Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <olectl.h>
7 #include <algorithm>
9 #ifdef MOZ_LOGGING
10 #define FORCE_PR_LOG /* Allow logging in the release build */
11 #endif // MOZ_LOGGING
12 #include "prlog.h"
14 #include "nscore.h"
15 #include "nsWindow.h"
16 #ifdef MOZ_METRO
17 #include "winrt/MetroWidget.h"
18 #endif
19 #include "nsPrintfCString.h"
20 #include "WinUtils.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/TextEvents.h"
23 #include "mozilla/WindowsVersion.h"
25 #define INPUTSCOPE_INIT_GUID
26 #include "nsTextStore.h"
28 using namespace mozilla;
29 using namespace mozilla::widget;
31 static const char* kPrefNameTSFEnabled = "intl.tsf.enable";
33 static const char* kLegacyPrefNameTSFEnabled = "intl.enable_tsf_support";
35 #ifdef PR_LOGGING
36 /**
37 * TSF related code should log its behavior even on release build especially
38 * in the interface methods.
39 *
40 * In interface methods, use PR_LOG_ALWAYS.
41 * In internal methods, use PR_LOG_DEBUG for logging normal behavior.
42 * For logging error, use PR_LOG_ERROR.
43 *
44 * When an instance method is called, start with following text:
45 * "TSF: 0x%p nsFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
46 * after that, start with:
47 * "TSF: 0x%p nsFoo::Bar("
48 * In an internal method, start with following text:
49 * "TSF: 0x%p nsFoo::Bar("
50 * When a static method is called, start with following text:
51 * "TSF: nsFoo::Bar("
52 */
54 PRLogModuleInfo* sTextStoreLog = nullptr;
55 #endif // #ifdef PR_LOGGING
57 /******************************************************************/
58 /* InputScopeImpl */
59 /******************************************************************/
61 class InputScopeImpl MOZ_FINAL : public ITfInputScope
62 {
63 public:
64 InputScopeImpl(const nsTArray<InputScope>& aList) :
65 mRefCnt(1),
66 mInputScopes(aList)
67 {
68 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
69 ("TSF: 0x%p InputScopeImpl()", this));
70 }
72 STDMETHODIMP_(ULONG) AddRef(void) { return ++mRefCnt; }
74 STDMETHODIMP_(ULONG) Release(void)
75 {
76 --mRefCnt;
77 if (mRefCnt) {
78 return mRefCnt;
79 }
80 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
81 ("TSF: 0x%p InputScopeImpl::Release() final", this));
82 delete this;
83 return 0;
84 }
86 STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
87 {
88 *ppv=nullptr;
89 if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) {
90 *ppv = static_cast<ITfInputScope*>(this);
91 }
92 if (*ppv) {
93 AddRef();
94 return S_OK;
95 }
96 return E_NOINTERFACE;
97 }
99 STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount)
100 {
101 uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length());
103 InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count);
104 NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY);
106 if (mInputScopes.IsEmpty()) {
107 *pScope = IS_DEFAULT;
108 *pcCount = 1;
109 *pprgInputScopes = pScope;
110 return S_OK;
111 }
113 *pcCount = 0;
115 for (uint32_t idx = 0; idx < count; idx++) {
116 *(pScope + idx) = mInputScopes[idx];
117 (*pcCount)++;
118 }
120 *pprgInputScopes = pScope;
121 return S_OK;
122 }
124 STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; }
125 STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; }
126 STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; }
127 STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; }
129 private:
130 DWORD mRefCnt;
131 nsTArray<InputScope> mInputScopes;
132 };
134 /******************************************************************/
135 /* nsTextStore */
136 /******************************************************************/
138 ITfThreadMgr* nsTextStore::sTsfThreadMgr = nullptr;
139 ITfMessagePump* nsTextStore::sMessagePump = nullptr;
140 ITfKeystrokeMgr* nsTextStore::sKeystrokeMgr = nullptr;
141 ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = nullptr;
142 ITfCategoryMgr* nsTextStore::sCategoryMgr = nullptr;
143 ITfDocumentMgr* nsTextStore::sTsfDisabledDocumentMgr = nullptr;
144 ITfContext* nsTextStore::sTsfDisabledContext = nullptr;
145 ITfInputProcessorProfiles* nsTextStore::sInputProcessorProfiles = nullptr;
146 DWORD nsTextStore::sTsfClientId = 0;
147 nsTextStore* nsTextStore::sTsfTextStore = nullptr;
149 bool nsTextStore::sCreateNativeCaretForATOK = false;
151 UINT nsTextStore::sFlushTIPInputMessage = 0;
153 #define TEXTSTORE_DEFAULT_VIEW (1)
155 #ifdef PR_LOGGING
157 static const char*
158 GetBoolName(bool aBool)
159 {
160 return aBool ? "true" : "false";
161 }
163 static void
164 HandleSeparator(nsCString& aDesc)
165 {
166 if (!aDesc.IsEmpty()) {
167 aDesc.AppendLiteral(" | ");
168 }
169 }
171 static const nsCString
172 GetFindFlagName(DWORD aFindFlag)
173 {
174 nsAutoCString description;
175 if (!aFindFlag) {
176 description.AppendLiteral("no flags (0)");
177 return description;
178 }
179 if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
180 description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
181 }
182 if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
183 HandleSeparator(description);
184 description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
185 }
186 if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
187 HandleSeparator(description);
188 description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
189 }
190 if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
191 HandleSeparator(description);
192 description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
193 }
194 if (aFindFlag & TS_ATTR_FIND_WANT_END) {
195 HandleSeparator(description);
196 description.AppendLiteral("TS_ATTR_FIND_WANT_END");
197 }
198 if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
199 HandleSeparator(description);
200 description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
201 }
202 if (description.IsEmpty()) {
203 description.AppendLiteral("Unknown (");
204 description.AppendInt(static_cast<uint32_t>(aFindFlag));
205 description.AppendLiteral(")");
206 }
207 return description;
208 }
210 static const char*
211 GetIMEEnabledName(IMEState::Enabled aIMEEnabled)
212 {
213 switch (aIMEEnabled) {
214 case IMEState::DISABLED:
215 return "DISABLED";
216 case IMEState::ENABLED:
217 return "ENABLED";
218 case IMEState::PASSWORD:
219 return "PASSWORD";
220 case IMEState::PLUGIN:
221 return "PLUGIN";
222 default:
223 return "Invalid";
224 }
225 }
227 static const char*
228 GetFocusChangeName(InputContextAction::FocusChange aFocusChange)
229 {
230 switch (aFocusChange) {
231 case InputContextAction::FOCUS_NOT_CHANGED:
232 return "FOCUS_NOT_CHANGED";
233 case InputContextAction::GOT_FOCUS:
234 return "GOT_FOCUS";
235 case InputContextAction::LOST_FOCUS:
236 return "LOST_FOCUS";
237 case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
238 return "MENU_GOT_PSEUDO_FOCUS";
239 case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
240 return "MENU_LOST_PSEUDO_FOCUS";
241 default:
242 return "Unknown";
243 }
244 }
246 static nsCString
247 GetCLSIDNameStr(REFCLSID aCLSID)
248 {
249 LPOLESTR str = nullptr;
250 HRESULT hr = ::StringFromCLSID(aCLSID, &str);
251 if (FAILED(hr) || !str || !str[0]) {
252 return EmptyCString();
253 }
255 nsAutoCString result;
256 result = NS_ConvertUTF16toUTF8(str);
257 ::CoTaskMemFree(str);
258 return result;
259 }
261 static nsCString
262 GetGUIDNameStr(REFGUID aGUID)
263 {
264 OLECHAR str[40];
265 int len = ::StringFromGUID2(aGUID, str, ArrayLength(str));
266 if (!len || !str[0]) {
267 return EmptyCString();
268 }
270 return NS_ConvertUTF16toUTF8(str);
271 }
273 static nsCString
274 GetRIIDNameStr(REFIID aRIID)
275 {
276 LPOLESTR str = nullptr;
277 HRESULT hr = ::StringFromIID(aRIID, &str);
278 if (FAILED(hr) || !str || !str[0]) {
279 return EmptyCString();
280 }
282 nsAutoString key(L"Interface\\");
283 key += str;
285 nsAutoCString result;
286 wchar_t buf[256];
287 if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT, key.get(), nullptr,
288 buf, sizeof(buf))) {
289 result = NS_ConvertUTF16toUTF8(buf);
290 } else {
291 result = NS_ConvertUTF16toUTF8(str);
292 }
294 ::CoTaskMemFree(str);
295 return result;
296 }
298 static const char*
299 GetCommonReturnValueName(HRESULT aResult)
300 {
301 switch (aResult) {
302 case S_OK:
303 return "S_OK";
304 case E_ABORT:
305 return "E_ABORT";
306 case E_ACCESSDENIED:
307 return "E_ACCESSDENIED";
308 case E_FAIL:
309 return "E_FAIL";
310 case E_HANDLE:
311 return "E_HANDLE";
312 case E_INVALIDARG:
313 return "E_INVALIDARG";
314 case E_NOINTERFACE:
315 return "E_NOINTERFACE";
316 case E_NOTIMPL:
317 return "E_NOTIMPL";
318 case E_OUTOFMEMORY:
319 return "E_OUTOFMEMORY";
320 case E_POINTER:
321 return "E_POINTER";
322 case E_UNEXPECTED:
323 return "E_UNEXPECTED";
324 default:
325 return SUCCEEDED(aResult) ? "Succeeded" : "Failed";
326 }
327 }
329 static const char*
330 GetTextStoreReturnValueName(HRESULT aResult)
331 {
332 switch (aResult) {
333 case TS_E_FORMAT:
334 return "TS_E_FORMAT";
335 case TS_E_INVALIDPOINT:
336 return "TS_E_INVALIDPOINT";
337 case TS_E_INVALIDPOS:
338 return "TS_E_INVALIDPOS";
339 case TS_E_NOINTERFACE:
340 return "TS_E_NOINTERFACE";
341 case TS_E_NOLAYOUT:
342 return "TS_E_NOLAYOUT";
343 case TS_E_NOLOCK:
344 return "TS_E_NOLOCK";
345 case TS_E_NOOBJECT:
346 return "TS_E_NOOBJECT";
347 case TS_E_NOSELECTION:
348 return "TS_E_NOSELECTION";
349 case TS_E_NOSERVICE:
350 return "TS_E_NOSERVICE";
351 case TS_E_READONLY:
352 return "TS_E_READONLY";
353 case TS_E_SYNCHRONOUS:
354 return "TS_E_SYNCHRONOUS";
355 case TS_S_ASYNC:
356 return "TS_S_ASYNC";
357 default:
358 return GetCommonReturnValueName(aResult);
359 }
360 }
362 static const nsCString
363 GetSinkMaskNameStr(DWORD aSinkMask)
364 {
365 nsAutoCString description;
366 if (aSinkMask & TS_AS_TEXT_CHANGE) {
367 description.AppendLiteral("TS_AS_TEXT_CHANGE");
368 }
369 if (aSinkMask & TS_AS_SEL_CHANGE) {
370 HandleSeparator(description);
371 description.AppendLiteral("TS_AS_SEL_CHANGE");
372 }
373 if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
374 HandleSeparator(description);
375 description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
376 }
377 if (aSinkMask & TS_AS_ATTR_CHANGE) {
378 HandleSeparator(description);
379 description.AppendLiteral("TS_AS_ATTR_CHANGE");
380 }
381 if (aSinkMask & TS_AS_STATUS_CHANGE) {
382 HandleSeparator(description);
383 description.AppendLiteral("TS_AS_STATUS_CHANGE");
384 }
385 if (description.IsEmpty()) {
386 description.AppendLiteral("not-specified");
387 }
388 return description;
389 }
391 static const char*
392 GetActiveSelEndName(TsActiveSelEnd aSelEnd)
393 {
394 return aSelEnd == TS_AE_NONE ? "TS_AE_NONE" :
395 aSelEnd == TS_AE_START ? "TS_AE_START" :
396 aSelEnd == TS_AE_END ? "TS_AE_END" : "Unknown";
397 }
399 static const nsCString
400 GetLockFlagNameStr(DWORD aLockFlags)
401 {
402 nsAutoCString description;
403 if ((aLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE) {
404 description.AppendLiteral("TS_LF_READWRITE");
405 } else if (aLockFlags & TS_LF_READ) {
406 description.AppendLiteral("TS_LF_READ");
407 }
408 if (aLockFlags & TS_LF_SYNC) {
409 if (!description.IsEmpty()) {
410 description.AppendLiteral(" | ");
411 }
412 description.AppendLiteral("TS_LF_SYNC");
413 }
414 if (description.IsEmpty()) {
415 description.AppendLiteral("not-specified");
416 }
417 return description;
418 }
420 static const char*
421 GetTextRunTypeName(TsRunType aRunType)
422 {
423 switch (aRunType) {
424 case TS_RT_PLAIN:
425 return "TS_RT_PLAIN";
426 case TS_RT_HIDDEN:
427 return "TS_RT_HIDDEN";
428 case TS_RT_OPAQUE:
429 return "TS_RT_OPAQUE";
430 default:
431 return "Unknown";
432 }
433 }
435 static nsCString
436 GetColorName(const TF_DA_COLOR &aColor)
437 {
438 switch (aColor.type) {
439 case TF_CT_NONE:
440 return NS_LITERAL_CSTRING("TF_CT_NONE");
441 case TF_CT_SYSCOLOR:
442 return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
443 static_cast<int32_t>(aColor.nIndex));
444 case TF_CT_COLORREF:
445 return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
446 static_cast<int32_t>(aColor.cr));
447 break;
448 default:
449 return nsPrintfCString("Unknown(%08X)",
450 static_cast<int32_t>(aColor.type));
451 }
452 }
454 static nsCString
455 GetLineStyleName(TF_DA_LINESTYLE aLineStyle)
456 {
457 switch (aLineStyle) {
458 case TF_LS_NONE:
459 return NS_LITERAL_CSTRING("TF_LS_NONE");
460 case TF_LS_SOLID:
461 return NS_LITERAL_CSTRING("TF_LS_SOLID");
462 case TF_LS_DOT:
463 return NS_LITERAL_CSTRING("TF_LS_DOT");
464 case TF_LS_DASH:
465 return NS_LITERAL_CSTRING("TF_LS_DASH");
466 case TF_LS_SQUIGGLE:
467 return NS_LITERAL_CSTRING("TF_LS_SQUIGGLE");
468 default: {
469 return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle));
470 }
471 }
472 }
474 static nsCString
475 GetClauseAttrName(TF_DA_ATTR_INFO aAttr)
476 {
477 switch (aAttr) {
478 case TF_ATTR_INPUT:
479 return NS_LITERAL_CSTRING("TF_ATTR_INPUT");
480 case TF_ATTR_TARGET_CONVERTED:
481 return NS_LITERAL_CSTRING("TF_ATTR_TARGET_CONVERTED");
482 case TF_ATTR_CONVERTED:
483 return NS_LITERAL_CSTRING("TF_ATTR_CONVERTED");
484 case TF_ATTR_TARGET_NOTCONVERTED:
485 return NS_LITERAL_CSTRING("TF_ATTR_TARGET_NOTCONVERTED");
486 case TF_ATTR_INPUT_ERROR:
487 return NS_LITERAL_CSTRING("TF_ATTR_INPUT_ERROR");
488 case TF_ATTR_FIXEDCONVERTED:
489 return NS_LITERAL_CSTRING("TF_ATTR_FIXEDCONVERTED");
490 case TF_ATTR_OTHER:
491 return NS_LITERAL_CSTRING("TF_ATTR_OTHER");
492 default: {
493 return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr));
494 }
495 }
496 }
498 static nsCString
499 GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE &aDispAttr)
500 {
501 nsAutoCString str;
502 str = "crText:{ ";
503 str += GetColorName(aDispAttr.crText);
504 str += " }, crBk:{ ";
505 str += GetColorName(aDispAttr.crBk);
506 str += " }, lsStyle: ";
507 str += GetLineStyleName(aDispAttr.lsStyle);
508 str += ", fBoldLine: ";
509 str += GetBoolName(aDispAttr.fBoldLine);
510 str += ", crLine:{ ";
511 str += GetColorName(aDispAttr.crLine);
512 str += " }, bAttr: ";
513 str += GetClauseAttrName(aDispAttr.bAttr);
514 return str;
515 }
517 #endif // #ifdef PR_LOGGING
519 nsTextStore::nsTextStore()
520 : mContent(mComposition, mSelection)
521 {
522 mRefCnt = 1;
523 mEditCookie = 0;
524 mIPProfileCookie = TF_INVALID_COOKIE;
525 mLangProfileCookie = TF_INVALID_COOKIE;
526 mSinkMask = 0;
527 mLock = 0;
528 mLockQueued = 0;
529 mInputScopeDetected = false;
530 mInputScopeRequested = false;
531 mIsRecordingActionsWithoutLock = false;
532 mPendingOnSelectionChange = false;
533 mPendingOnLayoutChange = false;
534 mNativeCaretIsCreated = false;
535 mIsIMM_IME = false;
536 mOnActivatedCalled = false;
537 // We hope that 5 or more actions don't occur at once.
538 mPendingActions.SetCapacity(5);
540 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
541 ("TSF: 0x%p nsTextStore::nsTestStore() SUCCEEDED", this));
542 }
544 bool
545 nsTextStore::Init(ITfThreadMgr* aThreadMgr)
546 {
547 nsRefPtr<ITfSource> source;
548 HRESULT hr =
549 aThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
550 if (FAILED(hr)) {
551 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
552 ("TSF: 0x%p nsTextStore::Init() FAILED to get ITfSource instance "
553 "(0x%08X)", this, hr));
554 return false;
555 }
557 // On Vista or later, Windows let us know activate IME changed only with
558 // ITfInputProcessorProfileActivationSink. However, it's not available on XP.
559 // On XP, ITfActiveLanguageProfileNotifySink is available for it.
560 // NOTE: Each OnActivated() should be called when TSF becomes available.
561 if (IsVistaOrLater()) {
562 hr = source->AdviseSink(IID_ITfInputProcessorProfileActivationSink,
563 static_cast<ITfInputProcessorProfileActivationSink*>(this),
564 &mIPProfileCookie);
565 if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) {
566 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
567 ("TSF: 0x%p nsTextStore::Init() FAILED to install "
568 "ITfInputProcessorProfileActivationSink (0x%08X)", this, hr));
569 return false;
570 }
571 } else {
572 hr = source->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,
573 static_cast<ITfActiveLanguageProfileNotifySink*>(this),
574 &mLangProfileCookie);
575 if (FAILED(hr) || mLangProfileCookie == TF_INVALID_COOKIE) {
576 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
577 ("TSF: 0x%p nsTextStore::Init() FAILED to install "
578 "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr));
579 return false;
580 }
581 }
583 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
584 ("TSF: 0x%p nsTextStore::Init(), "
585 "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X",
586 this, mIPProfileCookie, mLangProfileCookie));
588 return true;
589 }
591 nsTextStore::~nsTextStore()
592 {
593 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
594 ("TSF: 0x%p nsTextStore instance is destroyed, "
595 "mWidget=0x%p, mDocumentMgr=0x%p, mContext=0x%p, mIPProfileCookie=0x%08X, "
596 "mLangProfileCookie=0x%08X",
597 this, mWidget.get(), mDocumentMgr.get(), mContext.get(),
598 mIPProfileCookie, mLangProfileCookie));
600 if (mIPProfileCookie != TF_INVALID_COOKIE) {
601 nsRefPtr<ITfSource> source;
602 HRESULT hr =
603 sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
604 if (FAILED(hr)) {
605 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
606 ("TSF: 0x%p ~nsTextStore FAILED to get ITfSource instance "
607 "(0x%08X)", this, hr));
608 } else {
609 hr = source->UnadviseSink(mIPProfileCookie);
610 if (FAILED(hr)) {
611 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
612 ("TSF: 0x%p ~nsTextStore FAILED to uninstall "
613 "ITfInputProcessorProfileActivationSink (0x%08X)",
614 this, hr));
615 }
616 }
617 }
619 if (mLangProfileCookie != TF_INVALID_COOKIE) {
620 nsRefPtr<ITfSource> source;
621 HRESULT hr =
622 sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
623 if (FAILED(hr)) {
624 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
625 ("TSF: 0x%p ~nsTextStore FAILED to get ITfSource instance "
626 "(0x%08X)", this, hr));
627 } else {
628 hr = source->UnadviseSink(mLangProfileCookie);
629 if (FAILED(hr)) {
630 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
631 ("TSF: 0x%p ~nsTextStore FAILED to uninstall "
632 "ITfActiveLanguageProfileNotifySink (0x%08X)",
633 this, hr));
634 }
635 }
636 }
637 }
639 bool
640 nsTextStore::Create(nsWindowBase* aWidget)
641 {
642 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
643 ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)",
644 this, aWidget));
646 EnsureInitActiveTIPKeyboard();
648 if (mDocumentMgr) {
649 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
650 ("TSF: 0x%p nsTextStore::Create() FAILED due to already initialized",
651 this));
652 return false;
653 }
655 // Create document manager
656 HRESULT hr = sTsfThreadMgr->CreateDocumentMgr(
657 getter_AddRefs(mDocumentMgr));
658 if (FAILED(hr)) {
659 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
660 ("TSF: 0x%p nsTextStore::Create() FAILED to create DocumentMgr "
661 "(0x%08X)", this, hr));
662 return false;
663 }
664 mWidget = aWidget;
666 // Create context and add it to document manager
667 hr = mDocumentMgr->CreateContext(sTsfClientId, 0,
668 static_cast<ITextStoreACP*>(this),
669 getter_AddRefs(mContext), &mEditCookie);
670 if (FAILED(hr)) {
671 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
672 ("TSF: 0x%p nsTextStore::Create() FAILED to create the context "
673 "(0x%08X)", this, hr));
674 mDocumentMgr = nullptr;
675 return false;
676 }
678 hr = mDocumentMgr->Push(mContext);
679 if (FAILED(hr)) {
680 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
681 ("TSF: 0x%p nsTextStore::Create() FAILED to push the context (0x%08X)",
682 this, hr));
683 // XXX Why don't we use NS_IF_RELEASE() here??
684 mContext = nullptr;
685 mDocumentMgr = nullptr;
686 return false;
687 }
689 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
690 ("TSF: 0x%p nsTextStore::Create() succeeded: "
691 "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
692 this, mDocumentMgr.get(), mContext.get(), mEditCookie));
694 return true;
695 }
697 bool
698 nsTextStore::Destroy(void)
699 {
700 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
701 ("TSF: 0x%p nsTextStore::Destroy(), mComposition.IsComposing()=%s",
702 this, GetBoolName(mComposition.IsComposing())));
704 // If there is composition, TSF keeps the composition even after the text
705 // store destroyed. So, we should clear the composition here.
706 if (mComposition.IsComposing()) {
707 NS_WARNING("Composition is still alive at destroying the text store");
708 CommitCompositionInternal(false);
709 }
711 mContent.Clear();
712 mSelection.MarkDirty();
714 if (mWidget) {
715 // When blurred, Tablet Input Panel posts "blur" messages
716 // and try to insert text when the message is retrieved later.
717 // But by that time the text store is already destroyed,
718 // so try to get the message early
719 MSG msg;
720 if (WinUtils::PeekMessage(&msg, mWidget->GetWindowHandle(),
721 sFlushTIPInputMessage, sFlushTIPInputMessage,
722 PM_REMOVE)) {
723 ::DispatchMessageW(&msg);
724 }
725 }
726 mContext = nullptr;
727 if (mDocumentMgr) {
728 mDocumentMgr->Pop(TF_POPF_ALL);
729 mDocumentMgr = nullptr;
730 }
731 mSink = nullptr;
732 mWidget = nullptr;
734 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
735 ("TSF: 0x%p nsTextStore::Destroy() succeeded", this));
736 return true;
737 }
739 STDMETHODIMP
740 nsTextStore::QueryInterface(REFIID riid,
741 void** ppv)
742 {
743 *ppv=nullptr;
744 if ( (IID_IUnknown == riid) || (IID_ITextStoreACP == riid) ) {
745 *ppv = static_cast<ITextStoreACP*>(this);
746 } else if (IID_ITfContextOwnerCompositionSink == riid) {
747 *ppv = static_cast<ITfContextOwnerCompositionSink*>(this);
748 } else if (IID_ITfActiveLanguageProfileNotifySink == riid) {
749 *ppv = static_cast<ITfActiveLanguageProfileNotifySink*>(this);
750 } else if (IID_ITfInputProcessorProfileActivationSink == riid) {
751 *ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this);
752 }
753 if (*ppv) {
754 AddRef();
755 return S_OK;
756 }
758 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
759 ("TSF: 0x%p nsTextStore::QueryInterface() FAILED, riid=%s",
760 this, GetRIIDNameStr(riid).get()));
761 return E_NOINTERFACE;
762 }
764 STDMETHODIMP_(ULONG) nsTextStore::AddRef()
765 {
766 return ++mRefCnt;
767 }
769 STDMETHODIMP_(ULONG) nsTextStore::Release()
770 {
771 --mRefCnt;
772 if (0 != mRefCnt)
773 return mRefCnt;
774 delete this;
775 return 0;
776 }
778 STDMETHODIMP
779 nsTextStore::AdviseSink(REFIID riid,
780 IUnknown *punk,
781 DWORD dwMask)
782 {
783 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
784 ("TSF: 0x%p nsTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), "
785 "mSink=0x%p, mSinkMask=%s",
786 this, GetRIIDNameStr(riid).get(), punk, GetSinkMaskNameStr(dwMask).get(),
787 mSink.get(), GetSinkMaskNameStr(mSinkMask).get()));
789 if (!punk) {
790 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
791 ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to the null punk",
792 this));
793 return E_UNEXPECTED;
794 }
796 if (IID_ITextStoreACPSink != riid) {
797 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
798 ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to "
799 "unsupported interface", this));
800 return E_INVALIDARG; // means unsupported interface.
801 }
803 if (!mSink) {
804 // Install sink
805 punk->QueryInterface(IID_ITextStoreACPSink, getter_AddRefs(mSink));
806 if (!mSink) {
807 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
808 ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to "
809 "punk not having the interface", this));
810 return E_UNEXPECTED;
811 }
812 } else {
813 // If sink is already installed we check to see if they are the same
814 // Get IUnknown from both sides for comparison
815 nsRefPtr<IUnknown> comparison1, comparison2;
816 punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
817 mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
818 if (comparison1 != comparison2) {
819 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
820 ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to "
821 "the sink being different from the stored sink", this));
822 return CONNECT_E_ADVISELIMIT;
823 }
824 }
825 // Update mask either for a new sink or an existing sink
826 mSinkMask = dwMask;
827 return S_OK;
828 }
830 STDMETHODIMP
831 nsTextStore::UnadviseSink(IUnknown *punk)
832 {
833 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
834 ("TSF: 0x%p nsTextStore::UnadviseSink(punk=0x%p), mSink=0x%p",
835 this, punk, mSink.get()));
837 if (!punk) {
838 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
839 ("TSF: 0x%p nsTextStore::UnadviseSink() FAILED due to the null punk",
840 this));
841 return E_INVALIDARG;
842 }
843 if (!mSink) {
844 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
845 ("TSF: 0x%p nsTextStore::UnadviseSink() FAILED due to "
846 "any sink not stored", this));
847 return CONNECT_E_NOCONNECTION;
848 }
849 // Get IUnknown from both sides for comparison
850 nsRefPtr<IUnknown> comparison1, comparison2;
851 punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
852 mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
853 // Unadvise only if sinks are the same
854 if (comparison1 != comparison2) {
855 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
856 ("TSF: 0x%p nsTextStore::UnadviseSink() FAILED due to "
857 "the sink being different from the stored sink", this));
858 return CONNECT_E_NOCONNECTION;
859 }
860 mSink = nullptr;
861 mSinkMask = 0;
862 return S_OK;
863 }
865 STDMETHODIMP
866 nsTextStore::RequestLock(DWORD dwLockFlags,
867 HRESULT *phrSession)
868 {
869 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
870 ("TSF: 0x%p nsTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), "
871 "mLock=%s", this, GetLockFlagNameStr(dwLockFlags).get(), phrSession,
872 GetLockFlagNameStr(mLock).get()));
874 if (!mSink) {
875 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
876 ("TSF: 0x%p nsTextStore::RequestLock() FAILED due to "
877 "any sink not stored", this));
878 return E_FAIL;
879 }
880 if (!phrSession) {
881 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
882 ("TSF: 0x%p nsTextStore::RequestLock() FAILED due to "
883 "null phrSession", this));
884 return E_INVALIDARG;
885 }
887 if (!mLock) {
888 // put on lock
889 mLock = dwLockFlags & (~TS_LF_SYNC);
890 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
891 ("TSF: 0x%p Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
892 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
893 this, GetLockFlagNameStr(mLock).get()));
894 *phrSession = mSink->OnLockGranted(mLock);
895 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
896 ("TSF: 0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
897 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
898 this, GetLockFlagNameStr(mLock).get()));
899 DidLockGranted();
900 while (mLockQueued) {
901 mLock = mLockQueued;
902 mLockQueued = 0;
903 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
904 ("TSF: 0x%p Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
905 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
906 this, GetLockFlagNameStr(mLock).get()));
907 mSink->OnLockGranted(mLock);
908 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
909 ("TSF: 0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
910 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
911 this, GetLockFlagNameStr(mLock).get()));
912 DidLockGranted();
913 }
915 // The document is now completely unlocked.
916 mLock = 0;
918 if (mPendingOnLayoutChange) {
919 mPendingOnLayoutChange = false;
920 if (mSink) {
921 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
922 ("TSF: 0x%p nsTextStore::RequestLock(), "
923 "calling ITextStoreACPSink::OnLayoutChange()...", this));
924 mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
925 }
926 // The layout change caused by composition string change should cause
927 // calling ITfContextOwnerServices::OnLayoutChange() too.
928 if (mContext) {
929 nsRefPtr<ITfContextOwnerServices> service;
930 mContext->QueryInterface(IID_ITfContextOwnerServices,
931 getter_AddRefs(service));
932 if (service) {
933 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
934 ("TSF: 0x%p nsTextStore::RequestLock(), "
935 "calling ITfContextOwnerServices::OnLayoutChange()...",
936 this));
937 service->OnLayoutChange();
938 }
939 }
940 }
942 if (mPendingOnSelectionChange) {
943 mPendingOnSelectionChange = false;
944 if (mSink) {
945 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
946 ("TSF: 0x%p nsTextStore::RequestLock(), "
947 "calling ITextStoreACPSink::OnSelectionChange()...", this));
948 mSink->OnSelectionChange();
949 }
950 }
952 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
953 ("TSF: 0x%p nsTextStore::RequestLock() succeeded: *phrSession=%s",
954 this, GetTextStoreReturnValueName(*phrSession)));
955 return S_OK;
956 }
958 // only time when reentrant lock is allowed is when caller holds a
959 // read-only lock and is requesting an async write lock
960 if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags) &&
961 !(dwLockFlags & TS_LF_SYNC)) {
962 *phrSession = TS_S_ASYNC;
963 mLockQueued = dwLockFlags & (~TS_LF_SYNC);
965 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
966 ("TSF: 0x%p nsTextStore::RequestLock() stores the request in the "
967 "queue, *phrSession=TS_S_ASYNC", this));
968 return S_OK;
969 }
971 // no more locks allowed
972 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
973 ("TSF: 0x%p nsTextStore::RequestLock() didn't allow to lock, "
974 "*phrSession=TS_E_SYNCHRONOUS", this));
975 *phrSession = TS_E_SYNCHRONOUS;
976 return E_FAIL;
977 }
979 void
980 nsTextStore::DidLockGranted()
981 {
982 if (mNativeCaretIsCreated) {
983 ::DestroyCaret();
984 mNativeCaretIsCreated = false;
985 }
986 if (IsReadWriteLocked()) {
987 FlushPendingActions();
988 }
990 // If the widget has gone, we don't need to notify anything.
991 if (!mWidget || mWidget->Destroyed()) {
992 mPendingOnSelectionChange = false;
993 mPendingOnLayoutChange = false;
994 }
995 }
997 void
998 nsTextStore::FlushPendingActions()
999 {
1000 if (!mWidget || mWidget->Destroyed()) {
1001 mPendingActions.Clear();
1002 mContent.Clear();
1003 mPendingOnSelectionChange = false;
1004 mPendingOnLayoutChange = false;
1005 return;
1006 }
1008 mContent.Clear();
1010 nsRefPtr<nsWindowBase> kungFuDeathGrip(mWidget);
1011 for (uint32_t i = 0; i < mPendingActions.Length(); i++) {
1012 PendingAction& action = mPendingActions[i];
1013 switch (action.mType) {
1014 case PendingAction::COMPOSITION_START: {
1015 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1016 ("TSF: 0x%p nsTextStore::FlushPendingActions() "
1017 "flushing COMPOSITION_START={ mSelectionStart=%d, "
1018 "mSelectionLength=%d }",
1019 this, action.mSelectionStart, action.mSelectionLength));
1021 MOZ_ASSERT(mComposition.mLastData.IsEmpty());
1023 // Select composition range so the new composition replaces the range
1024 WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
1025 mWidget->InitEvent(selectionSet);
1026 selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
1027 selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
1028 selectionSet.mReversed = false;
1029 mWidget->DispatchWindowEvent(&selectionSet);
1030 if (!selectionSet.mSucceeded) {
1031 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1032 ("TSF: 0x%p nsTextStore::FlushPendingActions() "
1033 "FAILED due to NS_SELECTION_SET failure", this));
1034 break;
1035 }
1036 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1037 ("TSF: 0x%p nsTextStore::FlushPendingActions() "
1038 "dispatching compositionstart event...", this));
1039 WidgetCompositionEvent compositionStart(true, NS_COMPOSITION_START,
1040 mWidget);
1041 mWidget->InitEvent(compositionStart);
1042 mWidget->DispatchWindowEvent(&compositionStart);
1043 if (!mWidget || mWidget->Destroyed()) {
1044 break;
1045 }
1046 break;
1047 }
1048 case PendingAction::COMPOSITION_UPDATE: {
1049 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1050 ("TSF: 0x%p nsTextStore::FlushPendingActions() "
1051 "flushing COMPOSITION_UPDATE={ mData=\"%s\", "
1052 "mRanges=0x%p, mRanges->Length()=%d }",
1053 this, NS_ConvertUTF16toUTF8(action.mData).get(), action.mRanges.get(),
1054 action.mRanges ? action.mRanges->Length() : 0));
1056 if (!action.mRanges) {
1057 NS_WARNING("How does this case occur?");
1058 action.mRanges = new TextRangeArray();
1059 }
1061 // Adjust offsets in the ranges for XP linefeed character (only \n).
1062 // XXX Following code is the safest approach. However, it wastes
1063 // a little performance. For ensuring the clauses do not
1064 // overlap each other, we should redesign TextRange later.
1065 for (uint32_t i = 0; i < action.mRanges->Length(); ++i) {
1066 TextRange& range = action.mRanges->ElementAt(i);
1067 TextRange nativeRange = range;
1068 if (nativeRange.mStartOffset > 0) {
1069 nsAutoString preText(
1070 Substring(action.mData, 0, nativeRange.mStartOffset));
1071 preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
1072 NS_LITERAL_STRING("\n"));
1073 range.mStartOffset = preText.Length();
1074 }
1075 if (nativeRange.Length() == 0) {
1076 range.mEndOffset = range.mStartOffset;
1077 } else {
1078 nsAutoString clause(
1079 Substring(action.mData,
1080 nativeRange.mStartOffset, nativeRange.Length()));
1081 clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
1082 NS_LITERAL_STRING("\n"));
1083 range.mEndOffset = range.mStartOffset + clause.Length();
1084 }
1085 }
1087 action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
1088 NS_LITERAL_STRING("\n"));
1090 if (action.mData != mComposition.mLastData) {
1091 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1092 ("TSF: 0x%p nsTextStore::FlushPendingActions(), "
1093 "dispatching compositionupdate event...", this));
1094 WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
1095 mWidget);
1096 mWidget->InitEvent(compositionUpdate);
1097 compositionUpdate.data = action.mData;
1098 mComposition.mLastData = compositionUpdate.data;
1099 mWidget->DispatchWindowEvent(&compositionUpdate);
1100 if (!mWidget || mWidget->Destroyed()) {
1101 break;
1102 }
1103 }
1105 MOZ_ASSERT(action.mData == mComposition.mLastData);
1107 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1108 ("TSF: 0x%p nsTextStore::FlushPendingActions(), "
1109 "dispatching text event...", this));
1110 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
1111 mWidget->InitEvent(textEvent);
1112 textEvent.theText = mComposition.mLastData;
1113 if (action.mRanges->IsEmpty()) {
1114 TextRange wholeRange;
1115 wholeRange.mStartOffset = 0;
1116 wholeRange.mEndOffset = textEvent.theText.Length();
1117 wholeRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
1118 action.mRanges->AppendElement(wholeRange);
1119 }
1120 textEvent.mRanges = action.mRanges;
1121 mWidget->DispatchWindowEvent(&textEvent);
1122 // Be aware, the mWidget might already have been destroyed.
1123 break;
1124 }
1125 case PendingAction::COMPOSITION_END: {
1126 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1127 ("TSF: 0x%p nsTextStore::FlushPendingActions() "
1128 "flushing COMPOSITION_END={ mData=\"%s\" }",
1129 this, NS_ConvertUTF16toUTF8(action.mData).get()));
1131 action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
1132 NS_LITERAL_STRING("\n"));
1133 if (action.mData != mComposition.mLastData) {
1134 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1135 ("TSF: 0x%p nsTextStore::FlushPendingActions(), "
1136 "dispatching compositionupdate event...", this));
1137 WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
1138 mWidget);
1139 mWidget->InitEvent(compositionUpdate);
1140 compositionUpdate.data = action.mData;
1141 mComposition.mLastData = compositionUpdate.data;
1142 mWidget->DispatchWindowEvent(&compositionUpdate);
1143 if (!mWidget || mWidget->Destroyed()) {
1144 break;
1145 }
1146 }
1148 MOZ_ASSERT(action.mData == mComposition.mLastData);
1150 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1151 ("TSF: 0x%p nsTextStore::FlushPendingActions(), "
1152 "dispatching text event...", this));
1153 WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
1154 mWidget->InitEvent(textEvent);
1155 textEvent.theText = mComposition.mLastData;
1156 mWidget->DispatchWindowEvent(&textEvent);
1157 if (!mWidget || mWidget->Destroyed()) {
1158 break;
1159 }
1161 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1162 ("TSF: 0x%p nsTextStore::FlushPendingActions(), "
1163 "dispatching compositionend event...", this));
1164 WidgetCompositionEvent compositionEnd(true, NS_COMPOSITION_END,
1165 mWidget);
1166 compositionEnd.data = mComposition.mLastData;
1167 mWidget->InitEvent(compositionEnd);
1168 mWidget->DispatchWindowEvent(&compositionEnd);
1169 if (!mWidget || mWidget->Destroyed()) {
1170 break;
1171 }
1172 mComposition.mLastData.Truncate();
1173 break;
1174 }
1175 case PendingAction::SELECTION_SET: {
1176 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1177 ("TSF: 0x%p nsTextStore::FlushPendingActions() "
1178 "flushing SELECTION_SET={ mSelectionStart=%d, "
1179 "mSelectionLength=%d, mSelectionReversed=%s }",
1180 this, action.mSelectionStart, action.mSelectionLength,
1181 GetBoolName(action.mSelectionReversed)));
1183 WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
1184 selectionSet.mOffset =
1185 static_cast<uint32_t>(action.mSelectionStart);
1186 selectionSet.mLength =
1187 static_cast<uint32_t>(action.mSelectionLength);
1188 selectionSet.mReversed = action.mSelectionReversed;
1189 break;
1190 }
1191 default:
1192 MOZ_CRASH("unexpected action type");
1193 }
1195 if (mWidget && !mWidget->Destroyed()) {
1196 continue;
1197 }
1199 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1200 ("TSF: 0x%p nsTextStore::FlushPendingActions(), "
1201 "qutting since the mWidget has gone", this));
1202 break;
1203 }
1204 mPendingActions.Clear();
1205 }
1207 STDMETHODIMP
1208 nsTextStore::GetStatus(TS_STATUS *pdcs)
1209 {
1210 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1211 ("TSF: 0x%p nsTextStore::GetStatus(pdcs=0x%p)", this, pdcs));
1213 if (!pdcs) {
1214 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1215 ("TSF: 0x%p nsTextStore::GetStatus() FAILED due to null pdcs", this));
1216 return E_INVALIDARG;
1217 }
1218 pdcs->dwDynamicFlags = 0;
1219 // we use a "flat" text model for TSF support so no hidden text
1220 pdcs->dwStaticFlags = TS_SS_NOHIDDENTEXT;
1221 return S_OK;
1222 }
1224 STDMETHODIMP
1225 nsTextStore::QueryInsert(LONG acpTestStart,
1226 LONG acpTestEnd,
1227 ULONG cch,
1228 LONG *pacpResultStart,
1229 LONG *pacpResultEnd)
1230 {
1231 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1232 ("TSF: 0x%p nsTextStore::QueryInsert(acpTestStart=%ld, "
1233 "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)",
1234 this, acpTestStart, acpTestEnd, cch, acpTestStart, acpTestEnd));
1236 if (!pacpResultStart || !pacpResultEnd) {
1237 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1238 ("TSF: 0x%p nsTextStore::QueryInsert() FAILED due to "
1239 "the null argument", this));
1240 return E_INVALIDARG;
1241 }
1243 if (acpTestStart < 0 || acpTestStart > acpTestEnd) {
1244 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1245 ("TSF: 0x%p nsTextStore::QueryInsert() FAILED due to "
1246 "wrong argument", this));
1247 return E_INVALIDARG;
1248 }
1250 // XXX need to adjust to cluster boundary
1251 // Assume we are given good offsets for now
1252 *pacpResultStart = acpTestStart;
1253 *pacpResultEnd = acpTestStart + cch;
1255 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1256 ("TSF: 0x%p nsTextStore::QueryInsert() succeeded: "
1257 "*pacpResultStart=%ld, *pacpResultEnd=%ld)",
1258 this, *pacpResultStart, *pacpResultEnd));
1259 return S_OK;
1260 }
1262 STDMETHODIMP
1263 nsTextStore::GetSelection(ULONG ulIndex,
1264 ULONG ulCount,
1265 TS_SELECTION_ACP *pSelection,
1266 ULONG *pcFetched)
1267 {
1268 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1269 ("TSF: 0x%p nsTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, "
1270 "pSelection=0x%p, pcFetched=0x%p)",
1271 this, ulIndex, ulCount, pSelection, pcFetched));
1273 if (!IsReadLocked()) {
1274 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1275 ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to not locked",
1276 this));
1277 return TS_E_NOLOCK;
1278 }
1279 if (!ulCount || !pSelection || !pcFetched) {
1280 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1281 ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to "
1282 "null argument", this));
1283 return E_INVALIDARG;
1284 }
1286 *pcFetched = 0;
1288 if (ulIndex != static_cast<ULONG>(TS_DEFAULT_SELECTION) &&
1289 ulIndex != 0) {
1290 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1291 ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to "
1292 "unsupported selection", this));
1293 return TS_E_NOSELECTION;
1294 }
1296 Selection& currentSel = CurrentSelection();
1297 if (currentSel.IsDirty()) {
1298 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1299 ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to "
1300 "CurrentSelection() failure", this));
1301 return E_FAIL;
1302 }
1303 *pSelection = currentSel.ACP();
1304 *pcFetched = 1;
1305 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1306 ("TSF: 0x%p nsTextStore::GetSelection() succeeded", this));
1307 return S_OK;
1308 }
1310 nsTextStore::Content&
1311 nsTextStore::CurrentContent()
1312 {
1313 Selection& currentSel = CurrentSelection();
1314 if (currentSel.IsDirty()) {
1315 mContent.Clear();
1316 return mContent;
1317 }
1319 if (!mContent.IsInitialized()) {
1320 MOZ_ASSERT(mWidget && !mWidget->Destroyed());
1322 WidgetQueryContentEvent queryText(true, NS_QUERY_TEXT_CONTENT, mWidget);
1323 queryText.InitForQueryTextContent(0, UINT32_MAX);
1324 mWidget->InitEvent(queryText);
1325 mWidget->DispatchWindowEvent(&queryText);
1326 NS_ENSURE_TRUE(queryText.mSucceeded, mContent);
1328 mContent.Init(queryText.mReply.mString);
1329 }
1331 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1332 ("TSF: 0x%p nsTextStore::CurrentContent(): "
1333 "mContent={ mText.Length()=%d }",
1334 this, mContent.Text().Length()));
1336 return mContent;
1337 }
1339 nsTextStore::Selection&
1340 nsTextStore::CurrentSelection()
1341 {
1342 if (mSelection.IsDirty()) {
1343 // If the window has never been available, we should crash since working
1344 // with broken values may make TIP confused.
1345 if (!mWidget || mWidget->Destroyed()) {
1346 MOZ_CRASH();
1347 }
1349 WidgetQueryContentEvent querySelection(true, NS_QUERY_SELECTED_TEXT,
1350 mWidget);
1351 mWidget->InitEvent(querySelection);
1352 mWidget->DispatchWindowEvent(&querySelection);
1353 NS_ENSURE_TRUE(querySelection.mSucceeded, mSelection);
1355 mSelection.SetSelection(querySelection.mReply.mOffset,
1356 querySelection.mReply.mString.Length(),
1357 querySelection.mReply.mReversed);
1358 }
1360 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1361 ("TSF: 0x%p nsTextStore::CurrentSelection(): "
1362 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
1363 this, mSelection.StartOffset(), mSelection.EndOffset(),
1364 mSelection.Length(),
1365 GetBoolName(mSelection.IsReversed())));
1367 return mSelection;
1368 }
1370 static HRESULT
1371 GetRangeExtent(ITfRange* aRange, LONG* aStart, LONG* aLength)
1372 {
1373 nsRefPtr<ITfRangeACP> rangeACP;
1374 aRange->QueryInterface(IID_ITfRangeACP, getter_AddRefs(rangeACP));
1375 NS_ENSURE_TRUE(rangeACP, E_FAIL);
1376 return rangeACP->GetExtent(aStart, aLength);
1377 }
1379 static uint32_t
1380 GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE &aDisplayAttr)
1381 {
1382 uint32_t result;
1383 switch (aDisplayAttr.bAttr) {
1384 case TF_ATTR_TARGET_CONVERTED:
1385 result = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
1386 break;
1387 case TF_ATTR_CONVERTED:
1388 result = NS_TEXTRANGE_CONVERTEDTEXT;
1389 break;
1390 case TF_ATTR_TARGET_NOTCONVERTED:
1391 result = NS_TEXTRANGE_SELECTEDRAWTEXT;
1392 break;
1393 default:
1394 result = NS_TEXTRANGE_RAWINPUT;
1395 break;
1396 }
1397 return result;
1398 }
1400 HRESULT
1401 nsTextStore::GetDisplayAttribute(ITfProperty* aAttrProperty,
1402 ITfRange* aRange,
1403 TF_DISPLAYATTRIBUTE* aResult)
1404 {
1405 NS_ENSURE_TRUE(aAttrProperty, E_FAIL);
1406 NS_ENSURE_TRUE(aRange, E_FAIL);
1407 NS_ENSURE_TRUE(aResult, E_FAIL);
1409 HRESULT hr;
1411 #ifdef PR_LOGGING
1412 if (PR_LOG_TEST(sTextStoreLog, PR_LOG_DEBUG)) {
1413 LONG start = 0, length = 0;
1414 hr = GetRangeExtent(aRange, &start, &length);
1415 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1416 ("TSF: 0x%p nsTextStore::GetDisplayAttribute(): "
1417 "GetDisplayAttribute range=%ld-%ld (hr=%s)",
1418 this, start - mComposition.mStart,
1419 start - mComposition.mStart + length,
1420 GetCommonReturnValueName(hr)));
1421 }
1422 #endif
1424 VARIANT propValue;
1425 ::VariantInit(&propValue);
1426 hr = aAttrProperty->GetValue(TfEditCookie(mEditCookie), aRange, &propValue);
1427 if (FAILED(hr)) {
1428 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1429 ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to "
1430 "ITfProperty::GetValue() failed", this));
1431 return hr;
1432 }
1433 if (VT_I4 != propValue.vt) {
1434 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1435 ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to "
1436 "ITfProperty::GetValue() returns non-VT_I4 value", this));
1437 ::VariantClear(&propValue);
1438 return E_FAIL;
1439 }
1441 NS_ENSURE_TRUE(sCategoryMgr, E_FAIL);
1442 GUID guid;
1443 hr = sCategoryMgr->GetGUID(DWORD(propValue.lVal), &guid);
1444 ::VariantClear(&propValue);
1445 if (FAILED(hr)) {
1446 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1447 ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to "
1448 "ITfCategoryMgr::GetGUID() failed", this));
1449 return hr;
1450 }
1452 NS_ENSURE_TRUE(sDisplayAttrMgr, E_FAIL);
1453 nsRefPtr<ITfDisplayAttributeInfo> info;
1454 hr = sDisplayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info),
1455 nullptr);
1456 if (FAILED(hr) || !info) {
1457 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1458 ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to "
1459 "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed", this));
1460 return hr;
1461 }
1463 hr = info->GetAttributeInfo(aResult);
1464 if (FAILED(hr)) {
1465 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1466 ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to "
1467 "ITfDisplayAttributeInfo::GetAttributeInfo() failed", this));
1468 return hr;
1469 }
1471 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1472 ("TSF: 0x%p nsTextStore::GetDisplayAttribute() succeeded: "
1473 "Result={ %s }", this, GetDisplayAttrStr(*aResult).get()));
1474 return S_OK;
1475 }
1477 HRESULT
1478 nsTextStore::RestartCompositionIfNecessary(ITfRange* aRangeNew)
1479 {
1480 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1481 ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary("
1482 "aRangeNew=0x%p), mComposition.mView=0x%p",
1483 this, aRangeNew, mComposition.mView.get()));
1485 if (!mComposition.IsComposing()) {
1486 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1487 ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() FAILED "
1488 "due to no composition view", this));
1489 return E_FAIL;
1490 }
1492 HRESULT hr;
1493 nsRefPtr<ITfCompositionView> pComposition(mComposition.mView);
1494 nsRefPtr<ITfRange> composingRange(aRangeNew);
1495 if (!composingRange) {
1496 hr = pComposition->GetRange(getter_AddRefs(composingRange));
1497 if (FAILED(hr)) {
1498 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1499 ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() FAILED "
1500 "due to pComposition->GetRange() failure", this));
1501 return hr;
1502 }
1503 }
1505 // Get starting offset of the composition
1506 LONG compStart = 0, compLength = 0;
1507 hr = GetRangeExtent(composingRange, &compStart, &compLength);
1508 if (FAILED(hr)) {
1509 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1510 ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() FAILED "
1511 "due to GetRangeExtent() failure", this));
1512 return hr;
1513 }
1515 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1516 ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary(), "
1517 "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }",
1518 this, compStart, compStart + compLength, mComposition.mStart,
1519 mComposition.mString.Length()));
1521 if (mComposition.mStart != compStart ||
1522 mComposition.mString.Length() != (ULONG)compLength) {
1523 // If the queried composition length is different from the length
1524 // of our composition string, OnUpdateComposition is being called
1525 // because a part of the original composition was committed.
1526 // Reflect that by committing existing composition and starting
1527 // a new one. RecordCompositionEndAction() followed by
1528 // RecordCompositionStartAction() will accomplish this automagically.
1529 RecordCompositionEndAction();
1530 RecordCompositionStartAction(pComposition, composingRange, true);
1531 }
1533 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1534 ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() succeeded",
1535 this));
1536 return S_OK;
1537 }
1539 static bool
1540 GetColor(const TF_DA_COLOR &aTSFColor, nscolor &aResult)
1541 {
1542 switch (aTSFColor.type) {
1543 case TF_CT_SYSCOLOR: {
1544 DWORD sysColor = ::GetSysColor(aTSFColor.nIndex);
1545 aResult = NS_RGB(GetRValue(sysColor), GetGValue(sysColor),
1546 GetBValue(sysColor));
1547 return true;
1548 }
1549 case TF_CT_COLORREF:
1550 aResult = NS_RGB(GetRValue(aTSFColor.cr), GetGValue(aTSFColor.cr),
1551 GetBValue(aTSFColor.cr));
1552 return true;
1553 case TF_CT_NONE:
1554 default:
1555 return false;
1556 }
1557 }
1559 static bool
1560 GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle, uint8_t &aTextRangeLineStyle)
1561 {
1562 switch (aTSFLineStyle) {
1563 case TF_LS_NONE:
1564 aTextRangeLineStyle = TextRangeStyle::LINESTYLE_NONE;
1565 return true;
1566 case TF_LS_SOLID:
1567 aTextRangeLineStyle = TextRangeStyle::LINESTYLE_SOLID;
1568 return true;
1569 case TF_LS_DOT:
1570 aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DOTTED;
1571 return true;
1572 case TF_LS_DASH:
1573 aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DASHED;
1574 return true;
1575 case TF_LS_SQUIGGLE:
1576 aTextRangeLineStyle = TextRangeStyle::LINESTYLE_WAVY;
1577 return true;
1578 default:
1579 return false;
1580 }
1581 }
1583 HRESULT
1584 nsTextStore::RecordCompositionUpdateAction()
1585 {
1586 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1587 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction(), "
1588 "mComposition={ mView=0x%p, mString=\"%s\" }",
1589 this, mComposition.mView.get(),
1590 NS_ConvertUTF16toUTF8(mComposition.mString).get()));
1592 if (!mComposition.IsComposing()) {
1593 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1594 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED "
1595 "due to no composition view", this));
1596 return E_FAIL;
1597 }
1599 // Getting display attributes is *really* complicated!
1600 // We first get the context and the property objects to query for
1601 // attributes, but since a big range can have a variety of values for
1602 // the attribute, we have to find out all the ranges that have distinct
1603 // attribute values. Then we query for what the value represents through
1604 // the display attribute manager and translate that to TextRange to be
1605 // sent in NS_TEXT_TEXT
1607 nsRefPtr<ITfProperty> attrPropetry;
1608 HRESULT hr = mContext->GetProperty(GUID_PROP_ATTRIBUTE,
1609 getter_AddRefs(attrPropetry));
1610 if (FAILED(hr) || !attrPropetry) {
1611 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1612 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED "
1613 "due to mContext->GetProperty() failure", this));
1614 return FAILED(hr) ? hr : E_FAIL;
1615 }
1617 nsRefPtr<ITfRange> composingRange;
1618 hr = mComposition.mView->GetRange(getter_AddRefs(composingRange));
1619 if (FAILED(hr)) {
1620 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1621 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() "
1622 "FAILED due to mComposition.mView->GetRange() failure", this));
1623 return hr;
1624 }
1626 nsRefPtr<IEnumTfRanges> enumRanges;
1627 hr = attrPropetry->EnumRanges(TfEditCookie(mEditCookie),
1628 getter_AddRefs(enumRanges), composingRange);
1629 if (FAILED(hr) || !enumRanges) {
1630 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1631 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED "
1632 "due to attrPropetry->EnumRanges() failure", this));
1633 return FAILED(hr) ? hr : E_FAIL;
1634 }
1636 // First, put the log of content and selection here.
1637 Selection& currentSel = CurrentSelection();
1638 if (currentSel.IsDirty()) {
1639 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1640 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED "
1641 "due to CurrentSelection() failure", this));
1642 return E_FAIL;
1643 }
1645 PendingAction* action = GetPendingCompositionUpdate();
1646 action->mData = mComposition.mString;
1647 // The ranges might already have been initialized, however, if this is
1648 // called again, that means we need to overwrite the ranges with current
1649 // information.
1650 action->mRanges->Clear();
1652 TextRange newRange;
1653 // No matter if we have display attribute info or not,
1654 // we always pass in at least one range to NS_TEXT_TEXT
1655 newRange.mStartOffset = 0;
1656 newRange.mEndOffset = action->mData.Length();
1657 newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
1658 action->mRanges->AppendElement(newRange);
1660 nsRefPtr<ITfRange> range;
1661 while (S_OK == enumRanges->Next(1, getter_AddRefs(range), nullptr) && range) {
1663 LONG start = 0, length = 0;
1664 if (FAILED(GetRangeExtent(range, &start, &length)))
1665 continue;
1667 TextRange newRange;
1668 newRange.mStartOffset = uint32_t(start - mComposition.mStart);
1669 // The end of the last range in the array is
1670 // always kept at the end of composition
1671 newRange.mEndOffset = mComposition.mString.Length();
1673 TF_DISPLAYATTRIBUTE attr;
1674 hr = GetDisplayAttribute(attrPropetry, range, &attr);
1675 if (FAILED(hr)) {
1676 newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
1677 } else {
1678 newRange.mRangeType = GetGeckoSelectionValue(attr);
1679 if (GetColor(attr.crText, newRange.mRangeStyle.mForegroundColor)) {
1680 newRange.mRangeStyle.mDefinedStyles |=
1681 TextRangeStyle::DEFINED_FOREGROUND_COLOR;
1682 }
1683 if (GetColor(attr.crBk, newRange.mRangeStyle.mBackgroundColor)) {
1684 newRange.mRangeStyle.mDefinedStyles |=
1685 TextRangeStyle::DEFINED_BACKGROUND_COLOR;
1686 }
1687 if (GetColor(attr.crLine, newRange.mRangeStyle.mUnderlineColor)) {
1688 newRange.mRangeStyle.mDefinedStyles |=
1689 TextRangeStyle::DEFINED_UNDERLINE_COLOR;
1690 }
1691 if (GetLineStyle(attr.lsStyle, newRange.mRangeStyle.mLineStyle)) {
1692 newRange.mRangeStyle.mDefinedStyles |=
1693 TextRangeStyle::DEFINED_LINESTYLE;
1694 newRange.mRangeStyle.mIsBoldLine = attr.fBoldLine != 0;
1695 }
1696 }
1698 TextRange& lastRange = action->mRanges->LastElement();
1699 if (lastRange.mStartOffset == newRange.mStartOffset) {
1700 // Replace range if last range is the same as this one
1701 // So that ranges don't overlap and confuse the editor
1702 lastRange = newRange;
1703 } else {
1704 lastRange.mEndOffset = newRange.mStartOffset;
1705 action->mRanges->AppendElement(newRange);
1706 }
1707 }
1709 // We need to hack for Korean Input System which is Korean standard TIP.
1710 // It sets no change style to IME selection (the selection is always only
1711 // one). So, the composition string looks like normal (or committed) string.
1712 // At this time, current selection range is same as the composition string
1713 // range. Other applications set a wide caret which covers the composition
1714 // string, however, Gecko doesn't support the wide caret drawing now (Gecko
1715 // doesn't support XOR drawing), unfortunately. For now, we should change
1716 // the range style to undefined.
1717 if (!currentSel.IsCollapsed() && action->mRanges->Length() == 1) {
1718 TextRange& range = action->mRanges->ElementAt(0);
1719 LONG start = currentSel.MinOffset();
1720 LONG end = currentSel.MaxOffset();
1721 if ((LONG)range.mStartOffset == start - mComposition.mStart &&
1722 (LONG)range.mEndOffset == end - mComposition.mStart &&
1723 range.mRangeStyle.IsNoChangeStyle()) {
1724 range.mRangeStyle.Clear();
1725 // The looks of selected type is better than others.
1726 range.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT;
1727 }
1728 }
1730 // The caret position has to be collapsed.
1731 LONG caretPosition = currentSel.MaxOffset();
1732 caretPosition -= mComposition.mStart;
1733 TextRange caretRange;
1734 caretRange.mStartOffset = caretRange.mEndOffset = uint32_t(caretPosition);
1735 caretRange.mRangeType = NS_TEXTRANGE_CARETPOSITION;
1736 action->mRanges->AppendElement(caretRange);
1738 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1739 ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() "
1740 "succeeded", this));
1742 return S_OK;
1743 }
1745 HRESULT
1746 nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
1747 bool aDispatchTextEvent)
1748 {
1749 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
1750 ("TSF: 0x%p nsTextStore::SetSelectionInternal(pSelection={ "
1751 "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, "
1752 "aDispatchTextEvent=%s), mComposition.IsComposing()=%s",
1753 this, pSelection->acpStart, pSelection->acpEnd,
1754 GetActiveSelEndName(pSelection->style.ase),
1755 GetBoolName(pSelection->style.fInterimChar),
1756 GetBoolName(aDispatchTextEvent),
1757 GetBoolName(mComposition.IsComposing())));
1759 MOZ_ASSERT(IsReadWriteLocked());
1761 Selection& currentSel = CurrentSelection();
1762 if (currentSel.IsDirty()) {
1763 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1764 ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to "
1765 "CurrentSelection() failure", this));
1766 return E_FAIL;
1767 }
1769 if (mComposition.IsComposing()) {
1770 if (aDispatchTextEvent) {
1771 HRESULT hr = RestartCompositionIfNecessary();
1772 if (FAILED(hr)) {
1773 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1774 ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to "
1775 "RestartCompositionIfNecessary() failure", this));
1776 return hr;
1777 }
1778 }
1779 if (pSelection->acpStart < mComposition.mStart ||
1780 pSelection->acpEnd > mComposition.EndOffset()) {
1781 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1782 ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to "
1783 "the selection being out of the composition string", this));
1784 return TS_E_INVALIDPOS;
1785 }
1786 // Emulate selection during compositions
1787 currentSel.SetSelection(*pSelection);
1788 if (aDispatchTextEvent) {
1789 HRESULT hr = RecordCompositionUpdateAction();
1790 if (FAILED(hr)) {
1791 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1792 ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to "
1793 "RecordCompositionUpdateAction() failure", this));
1794 return hr;
1795 }
1796 }
1797 return S_OK;
1798 }
1800 PendingAction* action = mPendingActions.AppendElement();
1801 action->mType = PendingAction::SELECTION_SET;
1802 action->mSelectionStart = pSelection->acpStart;
1803 action->mSelectionLength = pSelection->acpEnd - pSelection->acpStart;
1804 action->mSelectionReversed = (pSelection->style.ase == TS_AE_START);
1806 currentSel.SetSelection(*pSelection);
1808 return S_OK;
1809 }
1811 STDMETHODIMP
1812 nsTextStore::SetSelection(ULONG ulCount,
1813 const TS_SELECTION_ACP *pSelection)
1814 {
1815 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1816 ("TSF: 0x%p nsTextStore::SetSelection(ulCount=%lu, pSelection=%p { "
1817 "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), "
1818 "mComposition.IsComposing()=%s",
1819 this, ulCount, pSelection,
1820 pSelection ? pSelection->acpStart : 0,
1821 pSelection ? pSelection->acpEnd : 0,
1822 pSelection ? GetActiveSelEndName(pSelection->style.ase) : "",
1823 pSelection ? GetBoolName(pSelection->style.fInterimChar) : "",
1824 GetBoolName(mComposition.IsComposing())));
1826 if (!IsReadWriteLocked()) {
1827 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1828 ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to "
1829 "not locked (read-write)", this));
1830 return TS_E_NOLOCK;
1831 }
1832 if (ulCount != 1) {
1833 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1834 ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to "
1835 "trying setting multiple selection", this));
1836 return E_INVALIDARG;
1837 }
1838 if (!pSelection) {
1839 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1840 ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to "
1841 "null argument", this));
1842 return E_INVALIDARG;
1843 }
1845 HRESULT hr = SetSelectionInternal(pSelection, true);
1846 if (FAILED(hr)) {
1847 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1848 ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to "
1849 "SetSelectionInternal() failure", this));
1850 } else {
1851 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1852 ("TSF: 0x%p nsTextStore::SetSelection() succeeded", this));
1853 }
1854 return hr;
1855 }
1857 STDMETHODIMP
1858 nsTextStore::GetText(LONG acpStart,
1859 LONG acpEnd,
1860 WCHAR *pchPlain,
1861 ULONG cchPlainReq,
1862 ULONG *pcchPlainOut,
1863 TS_RUNINFO *prgRunInfo,
1864 ULONG ulRunInfoReq,
1865 ULONG *pulRunInfoOut,
1866 LONG *pacpNext)
1867 {
1868 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1869 ("TSF: 0x%p nsTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, "
1870 "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, "
1871 "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, "
1872 "mString.Length()=%lu, IsComposing()=%s }",
1873 this, acpStart, acpEnd, pchPlain, cchPlainReq, pcchPlainOut,
1874 prgRunInfo, ulRunInfoReq, pulRunInfoOut, pacpNext,
1875 mComposition.mStart, mComposition.mString.Length(),
1876 GetBoolName(mComposition.IsComposing())));
1878 if (!IsReadLocked()) {
1879 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1880 ("TSF: 0x%p nsTextStore::GetText() FAILED due to "
1881 "not locked (read)", this));
1882 return TS_E_NOLOCK;
1883 }
1885 if (!pcchPlainOut || (!pchPlain && !prgRunInfo) ||
1886 !cchPlainReq != !pchPlain || !ulRunInfoReq != !prgRunInfo) {
1887 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1888 ("TSF: 0x%p nsTextStore::GetText() FAILED due to "
1889 "invalid argument", this));
1890 return E_INVALIDARG;
1891 }
1893 if (acpStart < 0 || acpEnd < -1 || (acpEnd != -1 && acpStart > acpEnd)) {
1894 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1895 ("TSF: 0x%p nsTextStore::GetText() FAILED due to "
1896 "invalid position", this));
1897 return TS_E_INVALIDPOS;
1898 }
1900 // Making sure to null-terminate string just to be on the safe side
1901 *pcchPlainOut = 0;
1902 if (pchPlain && cchPlainReq) *pchPlain = 0;
1903 if (pulRunInfoOut) *pulRunInfoOut = 0;
1904 if (pacpNext) *pacpNext = acpStart;
1905 if (prgRunInfo && ulRunInfoReq) {
1906 prgRunInfo->uCount = 0;
1907 prgRunInfo->type = TS_RT_PLAIN;
1908 }
1910 Content& currentContent = CurrentContent();
1911 if (!currentContent.IsInitialized()) {
1912 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1913 ("TSF: 0x%p nsTextStore::GetText() FAILED due to "
1914 "CurrentContent() failure", this));
1915 return E_FAIL;
1916 }
1917 if (currentContent.Text().Length() < static_cast<uint32_t>(acpStart)) {
1918 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1919 ("TSF: 0x%p nsTextStore::GetText() FAILED due to "
1920 "acpStart is larger offset than the actual text length", this));
1921 return TS_E_INVALIDPOS;
1922 }
1923 if (acpEnd != -1 &&
1924 currentContent.Text().Length() < static_cast<uint32_t>(acpEnd)) {
1925 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1926 ("TSF: 0x%p nsTextStore::GetText() FAILED due to "
1927 "acpEnd is larger offset than the actual text length", this));
1928 return TS_E_INVALIDPOS;
1929 }
1930 uint32_t length = (acpEnd == -1) ?
1931 currentContent.Text().Length() - static_cast<uint32_t>(acpStart) :
1932 static_cast<uint32_t>(acpEnd - acpStart);
1933 if (cchPlainReq && cchPlainReq - 1 < length) {
1934 length = cchPlainReq - 1;
1935 }
1936 if (length) {
1937 if (pchPlain && cchPlainReq) {
1938 const char16_t* startChar =
1939 currentContent.Text().BeginReading() + acpStart;
1940 memcpy(pchPlain, startChar, length * sizeof(*pchPlain));
1941 pchPlain[length] = 0;
1942 *pcchPlainOut = length;
1943 }
1944 if (prgRunInfo && ulRunInfoReq) {
1945 prgRunInfo->uCount = length;
1946 prgRunInfo->type = TS_RT_PLAIN;
1947 if (pulRunInfoOut) *pulRunInfoOut = 1;
1948 }
1949 if (pacpNext) *pacpNext = acpStart + length;
1950 }
1952 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1953 ("TSF: 0x%p nsTextStore::GetText() succeeded: pcchPlainOut=0x%p, "
1954 "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, "
1955 "*pacpNext=%ld)",
1956 this, pcchPlainOut, prgRunInfo ? prgRunInfo->uCount : 0,
1957 prgRunInfo ? GetTextRunTypeName(prgRunInfo->type) : "N/A",
1958 pulRunInfoOut ? pulRunInfoOut : 0, pacpNext ? pacpNext : 0));
1959 return S_OK;
1960 }
1962 STDMETHODIMP
1963 nsTextStore::SetText(DWORD dwFlags,
1964 LONG acpStart,
1965 LONG acpEnd,
1966 const WCHAR *pchText,
1967 ULONG cch,
1968 TS_TEXTCHANGE *pChange)
1969 {
1970 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
1971 ("TSF: 0x%p nsTextStore::SetText(dwFlags=%s, acpStart=%ld, "
1972 "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
1973 "mComposition.IsComposing()=%s",
1974 this, dwFlags == TS_ST_CORRECTION ? "TS_ST_CORRECTION" :
1975 "not-specified",
1976 acpStart, acpEnd, pchText,
1977 pchText && cch ?
1978 NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
1979 cch, pChange, GetBoolName(mComposition.IsComposing())));
1981 // Per SDK documentation, and since we don't have better
1982 // ways to do this, this method acts as a helper to
1983 // call SetSelection followed by InsertTextAtSelection
1984 if (!IsReadWriteLocked()) {
1985 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
1986 ("TSF: 0x%p nsTextStore::SetText() FAILED due to "
1987 "not locked (read)", this));
1988 return TS_E_NOLOCK;
1989 }
1991 TS_SELECTION_ACP selection;
1992 selection.acpStart = acpStart;
1993 selection.acpEnd = acpEnd;
1994 selection.style.ase = TS_AE_END;
1995 selection.style.fInterimChar = 0;
1996 // Set selection to desired range
1997 HRESULT hr = SetSelectionInternal(&selection);
1998 if (FAILED(hr)) {
1999 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2000 ("TSF: 0x%p nsTextStore::SetText() FAILED due to "
2001 "SetSelectionInternal() failure", this));
2002 return hr;
2003 }
2004 // Replace just selected text
2005 if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
2006 pChange)) {
2007 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2008 ("TSF: 0x%p nsTextStore::SetText() FAILED due to "
2009 "InsertTextAtSelectionInternal() failure", this));
2010 return E_FAIL;
2011 }
2013 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2014 ("TSF: 0x%p nsTextStore::SetText() succeeded: pChange={ "
2015 "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
2016 this, pChange ? pChange->acpStart : 0,
2017 pChange ? pChange->acpOldEnd : 0, pChange ? pChange->acpNewEnd : 0));
2018 return S_OK;
2019 }
2021 STDMETHODIMP
2022 nsTextStore::GetFormattedText(LONG acpStart,
2023 LONG acpEnd,
2024 IDataObject **ppDataObject)
2025 {
2026 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2027 ("TSF: 0x%p nsTextStore::GetFormattedText() called "
2028 "but not supported (E_NOTIMPL)", this));
2030 // no support for formatted text
2031 return E_NOTIMPL;
2032 }
2034 STDMETHODIMP
2035 nsTextStore::GetEmbedded(LONG acpPos,
2036 REFGUID rguidService,
2037 REFIID riid,
2038 IUnknown **ppunk)
2039 {
2040 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2041 ("TSF: 0x%p nsTextStore::GetEmbedded() called "
2042 "but not supported (E_NOTIMPL)", this));
2044 // embedded objects are not supported
2045 return E_NOTIMPL;
2046 }
2048 STDMETHODIMP
2049 nsTextStore::QueryInsertEmbedded(const GUID *pguidService,
2050 const FORMATETC *pFormatEtc,
2051 BOOL *pfInsertable)
2052 {
2053 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2054 ("TSF: 0x%p nsTextStore::QueryInsertEmbedded() called "
2055 "but not supported, *pfInsertable=FALSE (S_OK)", this));
2057 // embedded objects are not supported
2058 *pfInsertable = FALSE;
2059 return S_OK;
2060 }
2062 STDMETHODIMP
2063 nsTextStore::InsertEmbedded(DWORD dwFlags,
2064 LONG acpStart,
2065 LONG acpEnd,
2066 IDataObject *pDataObject,
2067 TS_TEXTCHANGE *pChange)
2068 {
2069 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2070 ("TSF: 0x%p nsTextStore::InsertEmbedded() called "
2071 "but not supported (E_NOTIMPL)", this));
2073 // embedded objects are not supported
2074 return E_NOTIMPL;
2075 }
2077 void
2078 nsTextStore::SetInputScope(const nsString& aHTMLInputType)
2079 {
2080 mInputScopes.Clear();
2081 if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) {
2082 return;
2083 }
2085 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
2086 if (aHTMLInputType.EqualsLiteral("url")) {
2087 mInputScopes.AppendElement(IS_URL);
2088 } else if (aHTMLInputType.EqualsLiteral("search")) {
2089 mInputScopes.AppendElement(IS_SEARCH);
2090 } else if (aHTMLInputType.EqualsLiteral("email")) {
2091 mInputScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS);
2092 } else if (aHTMLInputType.EqualsLiteral("password")) {
2093 mInputScopes.AppendElement(IS_PASSWORD);
2094 } else if (aHTMLInputType.EqualsLiteral("datetime") ||
2095 aHTMLInputType.EqualsLiteral("datetime-local")) {
2096 mInputScopes.AppendElement(IS_DATE_FULLDATE);
2097 mInputScopes.AppendElement(IS_TIME_FULLTIME);
2098 } else if (aHTMLInputType.EqualsLiteral("date") ||
2099 aHTMLInputType.EqualsLiteral("month") ||
2100 aHTMLInputType.EqualsLiteral("week")) {
2101 mInputScopes.AppendElement(IS_DATE_FULLDATE);
2102 } else if (aHTMLInputType.EqualsLiteral("time")) {
2103 mInputScopes.AppendElement(IS_TIME_FULLTIME);
2104 } else if (aHTMLInputType.EqualsLiteral("tel")) {
2105 mInputScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER);
2106 mInputScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER);
2107 } else if (aHTMLInputType.EqualsLiteral("number")) {
2108 mInputScopes.AppendElement(IS_NUMBER);
2109 }
2110 }
2112 HRESULT
2113 nsTextStore::ProcessScopeRequest(DWORD dwFlags,
2114 ULONG cFilterAttrs,
2115 const TS_ATTRID *paFilterAttrs)
2116 {
2117 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2118 ("TSF: 0x%p nsTextStore::ProcessScopeRequest(dwFlags=%s, "
2119 "cFilterAttrs=%ld",
2120 this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
2122 // This is a little weird! RequestSupportedAttrs gives us advanced notice
2123 // of a support query via RetrieveRequestedAttrs for a specific attribute.
2124 // RetrieveRequestedAttrs needs to return valid data for all attributes we
2125 // support, but the text service will only want the input scope object
2126 // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
2127 // TS_ATTR_FIND_WANT_VALUE.
2128 mInputScopeDetected = mInputScopeRequested = false;
2130 // Currently we only support GUID_PROP_INPUTSCOPE
2131 for (uint32_t idx = 0; idx < cFilterAttrs; ++idx) {
2132 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2133 ("TSF: 0x%p nsTextStore::ProcessScopeRequest() "
2134 "requested attr=%s",
2135 this, GetCLSIDNameStr(paFilterAttrs[idx]).get()));
2136 if (IsEqualGUID(paFilterAttrs[idx], GUID_PROP_INPUTSCOPE)) {
2137 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2138 ("TSF: 0x%p nsTextStore::ProcessScopeRequest() "
2139 "GUID_PROP_INPUTSCOPE queried", this));
2140 mInputScopeDetected = true;
2141 if (dwFlags & TS_ATTR_FIND_WANT_VALUE) {
2142 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2143 ("TSF: 0x%p nsTextStore::ProcessScopeRequest() "
2144 "TS_ATTR_FIND_WANT_VALUE specified", this));
2145 mInputScopeRequested = true;
2146 }
2147 break;
2148 }
2149 }
2150 return S_OK;
2151 }
2153 STDMETHODIMP
2154 nsTextStore::RequestSupportedAttrs(DWORD dwFlags,
2155 ULONG cFilterAttrs,
2156 const TS_ATTRID *paFilterAttrs)
2157 {
2158 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2159 ("TSF: 0x%p nsTextStore::RequestSupportedAttrs(dwFlags=%s, "
2160 "cFilterAttrs=%lu)",
2161 this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
2163 return ProcessScopeRequest(dwFlags, cFilterAttrs, paFilterAttrs);
2164 }
2166 STDMETHODIMP
2167 nsTextStore::RequestAttrsAtPosition(LONG acpPos,
2168 ULONG cFilterAttrs,
2169 const TS_ATTRID *paFilterAttrs,
2170 DWORD dwFlags)
2171 {
2172 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2173 ("TSF: 0x%p nsTextStore::RequestAttrsAtPosition(acpPos=%ld, "
2174 "cFilterAttrs=%lu, dwFlags=%s)",
2175 this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
2177 return ProcessScopeRequest(dwFlags | TS_ATTR_FIND_WANT_VALUE,
2178 cFilterAttrs, paFilterAttrs);
2179 }
2181 STDMETHODIMP
2182 nsTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos,
2183 ULONG cFilterAttrs,
2184 const TS_ATTRID *paFilterAttr,
2185 DWORD dwFlags)
2186 {
2187 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2188 ("TSF: 0x%p nsTextStore::RequestAttrsTransitioningAtPosition("
2189 "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported "
2190 "(S_OK)",
2191 this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
2193 // no per character attributes defined
2194 return S_OK;
2195 }
2197 STDMETHODIMP
2198 nsTextStore::FindNextAttrTransition(LONG acpStart,
2199 LONG acpHalt,
2200 ULONG cFilterAttrs,
2201 const TS_ATTRID *paFilterAttrs,
2202 DWORD dwFlags,
2203 LONG *pacpNext,
2204 BOOL *pfFound,
2205 LONG *plFoundOffset)
2206 {
2207 if (!pacpNext || !pfFound || !plFoundOffset) {
2208 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2209 ("TSF: 0x%p nsTextStore::FindNextAttrTransition() FAILED due to "
2210 "null argument", this));
2211 return E_INVALIDARG;
2212 }
2214 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2215 ("TSF: 0x%p nsTextStore::FindNextAttrTransition() called "
2216 "but not supported (S_OK)", this));
2218 // no per character attributes defined
2219 *pacpNext = *plFoundOffset = acpHalt;
2220 *pfFound = FALSE;
2221 return S_OK;
2222 }
2224 STDMETHODIMP
2225 nsTextStore::RetrieveRequestedAttrs(ULONG ulCount,
2226 TS_ATTRVAL *paAttrVals,
2227 ULONG *pcFetched)
2228 {
2229 if (!pcFetched || !ulCount || !paAttrVals) {
2230 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2231 ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() FAILED due to "
2232 "null argument", this));
2233 return E_INVALIDARG;
2234 }
2236 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2237 ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() called "
2238 "ulCount=%d", this, ulCount));
2240 if (mInputScopeDetected || mInputScopeRequested) {
2241 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2242 ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() for "
2243 "GUID_PROP_INPUTSCOPE: "
2244 "mInputScopeDetected=%s mInputScopeRequested=%s",
2245 this, GetBoolName(mInputScopeDetected),
2246 GetBoolName(mInputScopeRequested)));
2248 paAttrVals->idAttr = GUID_PROP_INPUTSCOPE;
2249 paAttrVals->dwOverlapId = 0;
2250 paAttrVals->varValue.vt = VT_EMPTY;
2251 *pcFetched = 1;
2253 if (mInputScopeRequested) {
2254 paAttrVals->varValue.vt = VT_UNKNOWN;
2255 paAttrVals->varValue.punkVal = (IUnknown*) new InputScopeImpl(mInputScopes);
2256 }
2258 mInputScopeDetected = mInputScopeRequested = false;
2259 return S_OK;
2260 }
2262 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2263 ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() called "
2264 "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)", this));
2266 paAttrVals->dwOverlapId = 0;
2267 paAttrVals->varValue.vt = VT_EMPTY;
2268 *pcFetched = 0;
2269 return S_OK;
2270 }
2272 STDMETHODIMP
2273 nsTextStore::GetEndACP(LONG *pacp)
2274 {
2275 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2276 ("TSF: 0x%p nsTextStore::GetEndACP(pacp=0x%p)", this, pacp));
2278 if (!IsReadLocked()) {
2279 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2280 ("TSF: 0x%p nsTextStore::GetEndACP() FAILED due to "
2281 "not locked (read)", this));
2282 return TS_E_NOLOCK;
2283 }
2285 if (!pacp) {
2286 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2287 ("TSF: 0x%p nsTextStore::GetEndACP() FAILED due to "
2288 "null argument", this));
2289 return E_INVALIDARG;
2290 }
2292 Content& currentContent = CurrentContent();
2293 if (!currentContent.IsInitialized()) {
2294 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2295 ("TSF: 0x%p nsTextStore::GetEndACP() FAILED due to "
2296 "CurrentContent() failure", this));
2297 return E_FAIL;
2298 }
2299 *pacp = static_cast<LONG>(currentContent.Text().Length());
2300 return S_OK;
2301 }
2303 STDMETHODIMP
2304 nsTextStore::GetActiveView(TsViewCookie *pvcView)
2305 {
2306 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2307 ("TSF: 0x%p nsTextStore::GetActiveView(pvcView=0x%p)", this, pvcView));
2309 if (!pvcView) {
2310 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2311 ("TSF: 0x%p nsTextStore::GetActiveView() FAILED due to "
2312 "null argument", this));
2313 return E_INVALIDARG;
2314 }
2316 *pvcView = TEXTSTORE_DEFAULT_VIEW;
2318 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2319 ("TSF: 0x%p nsTextStore::GetActiveView() succeeded: *pvcView=%ld",
2320 this, *pvcView));
2321 return S_OK;
2322 }
2324 STDMETHODIMP
2325 nsTextStore::GetACPFromPoint(TsViewCookie vcView,
2326 const POINT *pt,
2327 DWORD dwFlags,
2328 LONG *pacp)
2329 {
2330 if (!IsReadLocked()) {
2331 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2332 ("TSF: 0x%p nsTextStore::GetACPFromPoint() FAILED due to "
2333 "not locked (read)", this));
2334 return TS_E_NOLOCK;
2335 }
2337 if (vcView != TEXTSTORE_DEFAULT_VIEW) {
2338 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2339 ("TSF: 0x%p nsTextStore::GetACPFromPoint() FAILED due to "
2340 "called with invalid view", this));
2341 return E_INVALIDARG;
2342 }
2344 if (mContent.IsLayoutChanged()) {
2345 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2346 ("TSF: 0x%p nsTextStore::GetACPFromPoint() FAILED due to "
2347 "layout not recomputed", this));
2348 mPendingOnLayoutChange = true;
2349 return TS_E_NOLAYOUT;
2350 }
2352 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2353 ("TSF: 0x%p nsTextStore::GetACPFromPoint(vcView=%ld, "
2354 "pt(0x%p)={ x=%ld, y=%ld }, dwFlags=%s, pacp=0x%p) called "
2355 "but not supported (E_NOTIMPL)", this));
2357 // not supported for now
2358 return E_NOTIMPL;
2359 }
2361 STDMETHODIMP
2362 nsTextStore::GetTextExt(TsViewCookie vcView,
2363 LONG acpStart,
2364 LONG acpEnd,
2365 RECT *prc,
2366 BOOL *pfClipped)
2367 {
2368 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2369 ("TSF: 0x%p nsTextStore::GetTextExt(vcView=%ld, "
2370 "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p)",
2371 this, vcView, acpStart, acpEnd, prc, pfClipped));
2373 if (!IsReadLocked()) {
2374 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2375 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2376 "not locked (read)", this));
2377 return TS_E_NOLOCK;
2378 }
2380 if (vcView != TEXTSTORE_DEFAULT_VIEW) {
2381 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2382 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2383 "called with invalid view", this));
2384 return E_INVALIDARG;
2385 }
2387 if (!prc || !pfClipped) {
2388 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2389 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2390 "null argument", this));
2391 return E_INVALIDARG;
2392 }
2394 if (acpStart < 0 || acpEnd < acpStart) {
2395 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2396 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2397 "invalid position", this));
2398 return TS_E_INVALIDPOS;
2399 }
2401 if (mContent.IsLayoutChangedAfter(acpEnd)) {
2402 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2403 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2404 "layout not recomputed at %d", this, acpEnd));
2405 mPendingOnLayoutChange = true;
2406 return TS_E_NOLAYOUT;
2407 }
2409 // use NS_QUERY_TEXT_RECT to get rect in system, screen coordinates
2410 WidgetQueryContentEvent event(true, NS_QUERY_TEXT_RECT, mWidget);
2411 mWidget->InitEvent(event);
2412 event.InitForQueryTextRect(acpStart, acpEnd - acpStart);
2413 mWidget->DispatchWindowEvent(&event);
2414 if (!event.mSucceeded) {
2415 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2416 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2417 "NS_QUERY_TEXT_RECT failure", this));
2418 return TS_E_INVALIDPOS; // but unexpected failure, maybe.
2419 }
2420 // IMEs don't like empty rects, fix here
2421 if (event.mReply.mRect.width <= 0)
2422 event.mReply.mRect.width = 1;
2423 if (event.mReply.mRect.height <= 0)
2424 event.mReply.mRect.height = 1;
2426 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
2427 // convert to unclipped screen rect
2428 nsWindow* refWindow = static_cast<nsWindow*>(
2429 event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWidget);
2430 // Result rect is in top level widget coordinates
2431 refWindow = refWindow->GetTopLevelWindow(false);
2432 if (!refWindow) {
2433 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2434 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2435 "no top level window", this));
2436 return E_FAIL;
2437 }
2439 event.mReply.mRect.MoveBy(refWindow->WidgetToScreenOffset());
2440 }
2442 // get bounding screen rect to test for clipping
2443 if (!GetScreenExtInternal(*prc)) {
2444 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2445 ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to "
2446 "GetScreenExtInternal() failure", this));
2447 return E_FAIL;
2448 }
2450 // clip text rect to bounding rect
2451 RECT textRect;
2452 ::SetRect(&textRect, event.mReply.mRect.x, event.mReply.mRect.y,
2453 event.mReply.mRect.XMost(), event.mReply.mRect.YMost());
2454 if (!::IntersectRect(prc, prc, &textRect))
2455 // Text is not visible
2456 ::SetRectEmpty(prc);
2458 // not equal if text rect was clipped
2459 *pfClipped = !::EqualRect(prc, &textRect);
2461 // ATOK refers native caret position and size on Desktop applications for
2462 // deciding candidate window. Therefore, we need to create native caret
2463 // for hacking the bug.
2464 if (sCreateNativeCaretForATOK &&
2465 StringBeginsWith(
2466 mActiveTIPKeyboardDescription, NS_LITERAL_STRING("ATOK ")) &&
2467 mComposition.IsComposing() &&
2468 mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart &&
2469 mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) {
2470 if (mNativeCaretIsCreated) {
2471 ::DestroyCaret();
2472 mNativeCaretIsCreated = false;
2473 }
2474 CreateNativeCaret();
2475 }
2477 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2478 ("TSF: 0x%p nsTextStore::GetTextExt() succeeded: "
2479 "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s",
2480 this, prc->left, prc->top, prc->right, prc->bottom,
2481 GetBoolName(*pfClipped)));
2483 return S_OK;
2484 }
2486 STDMETHODIMP
2487 nsTextStore::GetScreenExt(TsViewCookie vcView,
2488 RECT *prc)
2489 {
2490 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2491 ("TSF: 0x%p nsTextStore::GetScreenExt(vcView=%ld, prc=0x%p)",
2492 this, vcView, prc));
2494 if (vcView != TEXTSTORE_DEFAULT_VIEW) {
2495 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2496 ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to "
2497 "called with invalid view", this));
2498 return E_INVALIDARG;
2499 }
2501 if (!prc) {
2502 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2503 ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to "
2504 "null argument", this));
2505 return E_INVALIDARG;
2506 }
2508 if (!GetScreenExtInternal(*prc)) {
2509 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2510 ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to "
2511 "GetScreenExtInternal() failure", this));
2512 return E_FAIL;
2513 }
2515 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2516 ("TSF: 0x%p nsTextStore::GetScreenExt() succeeded: "
2517 "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
2518 this, prc->left, prc->top, prc->right, prc->bottom));
2519 return S_OK;
2520 }
2522 bool
2523 nsTextStore::GetScreenExtInternal(RECT &aScreenExt)
2524 {
2525 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
2526 ("TSF: 0x%p nsTextStore::GetScreenExtInternal()", this));
2528 // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
2529 WidgetQueryContentEvent event(true, NS_QUERY_EDITOR_RECT, mWidget);
2530 mWidget->InitEvent(event);
2531 mWidget->DispatchWindowEvent(&event);
2532 if (!event.mSucceeded) {
2533 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2534 ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to "
2535 "NS_QUERY_EDITOR_RECT failure", this));
2536 return false;
2537 }
2539 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
2540 nsIntRect boundRect;
2541 if (NS_FAILED(mWidget->GetClientBounds(boundRect))) {
2542 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2543 ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to "
2544 "failed to get the client bounds", this));
2545 return false;
2546 }
2547 ::SetRect(&aScreenExt, boundRect.x, boundRect.y,
2548 boundRect.XMost(), boundRect.YMost());
2549 } else {
2550 NS_ASSERTION(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop,
2551 "environment isn't WindowsEnvironmentType_Desktop!");
2552 nsWindow* refWindow = static_cast<nsWindow*>(
2553 event.mReply.mFocusedWidget ?
2554 event.mReply.mFocusedWidget : mWidget);
2555 // Result rect is in top level widget coordinates
2556 refWindow = refWindow->GetTopLevelWindow(false);
2557 if (!refWindow) {
2558 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2559 ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to "
2560 "no top level window", this));
2561 return false;
2562 }
2564 nsIntRect boundRect;
2565 if (NS_FAILED(refWindow->GetClientBounds(boundRect))) {
2566 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2567 ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to "
2568 "failed to get the client bounds", this));
2569 return false;
2570 }
2572 boundRect.MoveTo(0, 0);
2574 // Clip frame rect to window rect
2575 boundRect.IntersectRect(event.mReply.mRect, boundRect);
2576 if (!boundRect.IsEmpty()) {
2577 boundRect.MoveBy(refWindow->WidgetToScreenOffset());
2578 ::SetRect(&aScreenExt, boundRect.x, boundRect.y,
2579 boundRect.XMost(), boundRect.YMost());
2580 } else {
2581 ::SetRectEmpty(&aScreenExt);
2582 }
2583 }
2585 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
2586 ("TSF: 0x%p nsTextStore::GetScreenExtInternal() succeeded: "
2587 "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
2588 this, aScreenExt.left, aScreenExt.top,
2589 aScreenExt.right, aScreenExt.bottom));
2590 return true;
2591 }
2593 STDMETHODIMP
2594 nsTextStore::GetWnd(TsViewCookie vcView,
2595 HWND *phwnd)
2596 {
2597 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2598 ("TSF: 0x%p nsTextStore::GetWnd(vcView=%ld, phwnd=0x%p), "
2599 "mWidget=0x%p",
2600 this, vcView, phwnd, mWidget.get()));
2602 if (vcView != TEXTSTORE_DEFAULT_VIEW) {
2603 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2604 ("TSF: 0x%p nsTextStore::GetWnd() FAILED due to "
2605 "called with invalid view", this));
2606 return E_INVALIDARG;
2607 }
2609 if (!phwnd) {
2610 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2611 ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to "
2612 "null argument", this));
2613 return E_INVALIDARG;
2614 }
2616 *phwnd = mWidget->GetWindowHandle();
2618 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2619 ("TSF: 0x%p nsTextStore::GetWnd() succeeded: *phwnd=0x%p",
2620 this, static_cast<void*>(*phwnd)));
2621 return S_OK;
2622 }
2624 STDMETHODIMP
2625 nsTextStore::InsertTextAtSelection(DWORD dwFlags,
2626 const WCHAR *pchText,
2627 ULONG cch,
2628 LONG *pacpStart,
2629 LONG *pacpEnd,
2630 TS_TEXTCHANGE *pChange)
2631 {
2632 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2633 ("TSF: 0x%p nsTextStore::InsertTextAtSelection(dwFlags=%s, "
2634 "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
2635 "pChange=0x%p), IsComposing()=%s",
2636 this, dwFlags == 0 ? "0" :
2637 dwFlags == TF_IAS_NOQUERY ? "TF_IAS_NOQUERY" :
2638 dwFlags == TF_IAS_QUERYONLY ? "TF_IAS_QUERYONLY" : "Unknown",
2639 pchText,
2640 pchText && cch ? NS_ConvertUTF16toUTF8(pchText, cch).get() : "",
2641 cch, pacpStart, pacpEnd, pChange,
2642 GetBoolName(mComposition.IsComposing())));
2644 if (cch && !pchText) {
2645 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2646 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2647 "null pchText", this));
2648 return E_INVALIDARG;
2649 }
2651 if (TS_IAS_QUERYONLY == dwFlags) {
2652 if (!IsReadLocked()) {
2653 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2654 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2655 "not locked (read)", this));
2656 return TS_E_NOLOCK;
2657 }
2659 if (!pacpStart || !pacpEnd) {
2660 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2661 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2662 "null argument", this));
2663 return E_INVALIDARG;
2664 }
2666 // Get selection first
2667 Selection& currentSel = CurrentSelection();
2668 if (currentSel.IsDirty()) {
2669 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2670 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2671 "CurrentSelection() failure", this));
2672 return E_FAIL;
2673 }
2675 // Simulate text insertion
2676 *pacpStart = currentSel.StartOffset();
2677 *pacpEnd = currentSel.EndOffset();
2678 if (pChange) {
2679 pChange->acpStart = currentSel.StartOffset();
2680 pChange->acpOldEnd = currentSel.EndOffset();
2681 pChange->acpNewEnd = currentSel.StartOffset() + static_cast<LONG>(cch);
2682 }
2683 } else {
2684 if (!IsReadWriteLocked()) {
2685 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2686 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2687 "not locked (read-write)", this));
2688 return TS_E_NOLOCK;
2689 }
2691 if (!pChange) {
2692 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2693 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2694 "null pChange", this));
2695 return E_INVALIDARG;
2696 }
2698 if (TS_IAS_NOQUERY != dwFlags && (!pacpStart || !pacpEnd)) {
2699 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2700 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2701 "null argument", this));
2702 return E_INVALIDARG;
2703 }
2705 if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
2706 pChange)) {
2707 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2708 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to "
2709 "InsertTextAtSelectionInternal() failure", this));
2710 return E_FAIL;
2711 }
2713 if (TS_IAS_NOQUERY != dwFlags) {
2714 *pacpStart = pChange->acpStart;
2715 *pacpEnd = pChange->acpNewEnd;
2716 }
2717 }
2718 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2719 ("TSF: 0x%p nsTextStore::InsertTextAtSelection() succeeded: "
2720 "*pacpStart=%ld, *pacpEnd=%ld, "
2721 "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })",
2722 this, pacpStart ? *pacpStart : 0, pacpEnd ? *pacpEnd : 0,
2723 pChange ? pChange->acpStart: 0, pChange ? pChange->acpOldEnd : 0,
2724 pChange ? pChange->acpNewEnd : 0));
2725 return S_OK;
2726 }
2728 bool
2729 nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr,
2730 TS_TEXTCHANGE* aTextChange)
2731 {
2732 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
2733 ("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal("
2734 "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
2735 this, NS_ConvertUTF16toUTF8(aInsertStr).get(), aTextChange,
2736 GetBoolName(mComposition.IsComposing())));
2738 Content& currentContent = CurrentContent();
2739 if (!currentContent.IsInitialized()) {
2740 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2741 ("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() failed "
2742 "due to CurrentContent() failure()", this));
2743 return false;
2744 }
2746 TS_SELECTION_ACP oldSelection = currentContent.Selection().ACP();
2747 if (!mComposition.IsComposing()) {
2748 // Use a temporary composition to contain the text
2749 PendingAction* compositionStart = mPendingActions.AppendElement();
2750 compositionStart->mType = PendingAction::COMPOSITION_START;
2751 compositionStart->mSelectionStart = oldSelection.acpStart;
2752 compositionStart->mSelectionLength =
2753 oldSelection.acpEnd - oldSelection.acpStart;
2755 PendingAction* compositionEnd = mPendingActions.AppendElement();
2756 compositionEnd->mType = PendingAction::COMPOSITION_END;
2757 compositionEnd->mData = aInsertStr;
2758 }
2760 currentContent.ReplaceSelectedTextWith(aInsertStr);
2762 if (aTextChange) {
2763 aTextChange->acpStart = oldSelection.acpStart;
2764 aTextChange->acpOldEnd = oldSelection.acpEnd;
2765 aTextChange->acpNewEnd = currentContent.Selection().EndOffset();
2766 }
2768 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
2769 ("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() succeeded: "
2770 "mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ acpStart=%ld, "
2771 "acpOldEnd=%ld, acpNewEnd=%ld }",
2772 this, mWidget.get(),
2773 GetBoolName(mWidget ? mWidget->Destroyed() : true),
2774 aTextChange ? aTextChange->acpStart : 0,
2775 aTextChange ? aTextChange->acpOldEnd : 0,
2776 aTextChange ? aTextChange->acpNewEnd : 0));
2777 return true;
2778 }
2780 STDMETHODIMP
2781 nsTextStore::InsertEmbeddedAtSelection(DWORD dwFlags,
2782 IDataObject *pDataObject,
2783 LONG *pacpStart,
2784 LONG *pacpEnd,
2785 TS_TEXTCHANGE *pChange)
2786 {
2787 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2788 ("TSF: 0x%p nsTextStore::InsertEmbeddedAtSelection() called "
2789 "but not supported (E_NOTIMPL)", this));
2791 // embedded objects are not supported
2792 return E_NOTIMPL;
2793 }
2795 HRESULT
2796 nsTextStore::RecordCompositionStartAction(ITfCompositionView* pComposition,
2797 ITfRange* aRange,
2798 bool aPreserveSelection)
2799 {
2800 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
2801 ("TSF: 0x%p nsTextStore::RecordCompositionStartAction("
2802 "pComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), "
2803 "mComposition.mView=0x%p",
2804 this, pComposition, aRange, GetBoolName(aPreserveSelection),
2805 mComposition.mView.get()));
2807 LONG start = 0, length = 0;
2808 HRESULT hr = GetRangeExtent(aRange, &start, &length);
2809 if (FAILED(hr)) {
2810 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2811 ("TSF: 0x%p nsTextStore::RecordCompositionStartAction() FAILED "
2812 "due to GetRangeExtent() failure", this));
2813 return hr;
2814 }
2816 Content& currentContent = CurrentContent();
2817 if (!currentContent.IsInitialized()) {
2818 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2819 ("TSF: 0x%p nsTextStore::RecordCompositionStartAction() FAILED "
2820 "due to CurrentContent() failure", this));
2821 return E_FAIL;
2822 }
2824 PendingAction* action = mPendingActions.AppendElement();
2825 action->mType = PendingAction::COMPOSITION_START;
2826 action->mSelectionStart = start;
2827 action->mSelectionLength = length;
2829 currentContent.StartComposition(pComposition, *action, aPreserveSelection);
2831 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2832 ("TSF: 0x%p nsTextStore::RecordCompositionStartAction() succeeded: "
2833 "mComposition={ mStart=%ld, mString.Length()=%ld, "
2834 "mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
2835 "style.fInterimChar=%s } }",
2836 this, mComposition.mStart, mComposition.mString.Length(),
2837 mSelection.StartOffset(), mSelection.EndOffset(),
2838 GetActiveSelEndName(mSelection.ActiveSelEnd()),
2839 GetBoolName(mSelection.IsInterimChar())));
2840 return S_OK;
2841 }
2843 HRESULT
2844 nsTextStore::RecordCompositionEndAction()
2845 {
2846 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
2847 ("TSF: 0x%p nsTextStore::RecordCompositionEndAction(), "
2848 "mComposition={ mView=0x%p, mString=\"%s\" }",
2849 this, mComposition.mView.get(),
2850 NS_ConvertUTF16toUTF8(mComposition.mString).get()));
2852 MOZ_ASSERT(mComposition.IsComposing());
2854 PendingAction* action = mPendingActions.AppendElement();
2855 action->mType = PendingAction::COMPOSITION_END;
2856 action->mData = mComposition.mString;
2858 Content& currentContent = CurrentContent();
2859 if (!currentContent.IsInitialized()) {
2860 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2861 ("TSF: 0x%p nsTextStore::RecordCompositionEndAction() FAILED due "
2862 "to CurrentContent() failure", this));
2863 return E_FAIL;
2864 }
2865 currentContent.EndComposition(*action);
2867 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2868 ("TSF: 0x%p nsTextStore::RecordCompositionEndAction(), succeeded",
2869 this));
2870 return S_OK;
2871 }
2873 STDMETHODIMP
2874 nsTextStore::OnStartComposition(ITfCompositionView* pComposition,
2875 BOOL* pfOk)
2876 {
2877 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2878 ("TSF: 0x%p nsTextStore::OnStartComposition(pComposition=0x%p, "
2879 "pfOk=0x%p), mComposition.mView=0x%p",
2880 this, pComposition, pfOk, mComposition.mView.get()));
2882 AutoPendingActionAndContentFlusher flusher(this);
2884 *pfOk = FALSE;
2886 // Only one composition at a time
2887 if (mComposition.IsComposing()) {
2888 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2889 ("TSF: 0x%p nsTextStore::OnStartComposition() FAILED due to "
2890 "there is another composition already (but returns S_OK)", this));
2891 return S_OK;
2892 }
2894 nsRefPtr<ITfRange> range;
2895 HRESULT hr = pComposition->GetRange(getter_AddRefs(range));
2896 if (FAILED(hr)) {
2897 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2898 ("TSF: 0x%p nsTextStore::OnStartComposition() FAILED due to "
2899 "pComposition->GetRange() failure", this));
2900 return hr;
2901 }
2902 hr = RecordCompositionStartAction(pComposition, range, false);
2903 if (FAILED(hr)) {
2904 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2905 ("TSF: 0x%p nsTextStore::OnStartComposition() FAILED due to "
2906 "RecordCompositionStartAction() failure", this));
2907 return hr;
2908 }
2910 *pfOk = TRUE;
2911 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2912 ("TSF: 0x%p nsTextStore::OnStartComposition() succeeded", this));
2913 return S_OK;
2914 }
2916 STDMETHODIMP
2917 nsTextStore::OnUpdateComposition(ITfCompositionView* pComposition,
2918 ITfRange* pRangeNew)
2919 {
2920 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2921 ("TSF: 0x%p nsTextStore::OnUpdateComposition(pComposition=0x%p, "
2922 "pRangeNew=0x%p), mComposition.mView=0x%p",
2923 this, pComposition, pRangeNew, mComposition.mView.get()));
2925 AutoPendingActionAndContentFlusher flusher(this);
2927 if (!mDocumentMgr || !mContext) {
2928 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2929 ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
2930 "not ready for the composition", this));
2931 return E_UNEXPECTED;
2932 }
2933 if (!mComposition.IsComposing()) {
2934 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2935 ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
2936 "no active composition", this));
2937 return E_UNEXPECTED;
2938 }
2939 if (mComposition.mView != pComposition) {
2940 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2941 ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
2942 "different composition view specified", this));
2943 return E_UNEXPECTED;
2944 }
2946 // pRangeNew is null when the update is not complete
2947 if (!pRangeNew) {
2948 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2949 ("TSF: 0x%p nsTextStore::OnUpdateComposition() succeeded but "
2950 "not complete", this));
2951 return S_OK;
2952 }
2954 HRESULT hr = RestartCompositionIfNecessary(pRangeNew);
2955 if (FAILED(hr)) {
2956 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2957 ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
2958 "RestartCompositionIfNecessary() failure", this));
2959 return hr;
2960 }
2962 hr = RecordCompositionUpdateAction();
2963 if (FAILED(hr)) {
2964 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2965 ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
2966 "RecordCompositionUpdateAction() failure", this));
2967 return hr;
2968 }
2970 #ifdef PR_LOGGING
2971 if (PR_LOG_TEST(sTextStoreLog, PR_LOG_ALWAYS)) {
2972 Selection& currentSel = CurrentSelection();
2973 if (currentSel.IsDirty()) {
2974 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
2975 ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to "
2976 "CurrentSelection() failure", this));
2977 return E_FAIL;
2978 }
2979 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2980 ("TSF: 0x%p nsTextStore::OnUpdateComposition() succeeded: "
2981 "mComposition={ mStart=%ld, mString=\"%s\" }, "
2982 "CurrentSelection()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
2983 this, mComposition.mStart,
2984 NS_ConvertUTF16toUTF8(mComposition.mString).get(),
2985 currentSel.StartOffset(), currentSel.EndOffset(),
2986 GetActiveSelEndName(currentSel.ActiveSelEnd())));
2987 }
2988 #endif // #ifdef PR_LOGGING
2989 return S_OK;
2990 }
2992 STDMETHODIMP
2993 nsTextStore::OnEndComposition(ITfCompositionView* pComposition)
2994 {
2995 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
2996 ("TSF: 0x%p nsTextStore::OnEndComposition(pComposition=0x%p), "
2997 "mComposition={ mView=0x%p, mString=\"%s\" }",
2998 this, pComposition, mComposition.mView.get(),
2999 NS_ConvertUTF16toUTF8(mComposition.mString).get()));
3001 AutoPendingActionAndContentFlusher flusher(this);
3003 if (!mComposition.IsComposing()) {
3004 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3005 ("TSF: 0x%p nsTextStore::OnEndComposition() FAILED due to "
3006 "no active composition", this));
3007 return E_UNEXPECTED;
3008 }
3010 if (mComposition.mView != pComposition) {
3011 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3012 ("TSF: 0x%p nsTextStore::OnEndComposition() FAILED due to "
3013 "different composition view specified", this));
3014 return E_UNEXPECTED;
3015 }
3017 HRESULT hr = RecordCompositionEndAction();
3018 if (FAILED(hr)) {
3019 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3020 ("TSF: 0x%p nsTextStore::OnEndComposition() FAILED due to "
3021 "RecordCompositionEndAction() failure", this));
3022 return hr;
3023 }
3025 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3026 ("TSF: 0x%p nsTextStore::OnEndComposition(), succeeded", this));
3027 return S_OK;
3028 }
3030 STDMETHODIMP
3031 nsTextStore::OnActivated(REFCLSID clsid, REFGUID guidProfile,
3032 BOOL fActivated)
3033 {
3034 // NOTE: This is installed only on XP or Server 2003.
3035 if (fActivated) {
3036 // TODO: We should check if the profile's category is keyboard or not.
3037 mOnActivatedCalled = true;
3038 mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0));
3040 LANGID langID;
3041 HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID);
3042 if (FAILED(hr)) {
3043 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3044 ("TSF: nsTextStore::OnActivated() FAILED due to "
3045 "GetCurrentLanguage() failure, hr=0x%08X", hr));
3046 } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) {
3047 GetTIPDescription(clsid, langID, guidProfile,
3048 mActiveTIPKeyboardDescription);
3049 } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) {
3050 // Perhaps, this case is that keyboard layout without TIP is activated.
3051 mActiveTIPKeyboardDescription.Truncate();
3052 }
3053 }
3055 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3056 ("TSF: 0x%p nsTextStore::OnActivated(rclsid=%s, guidProfile=%s, "
3057 "fActivated=%s), mIsIMM_IME=%s, mActiveTIPDescription=\"%s\"",
3058 this, GetCLSIDNameStr(clsid).get(),
3059 GetGUIDNameStr(guidProfile).get(), GetBoolName(fActivated),
3060 GetBoolName(mIsIMM_IME),
3061 NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
3062 return S_OK;
3063 }
3065 STDMETHODIMP
3066 nsTextStore::OnActivated(DWORD dwProfileType,
3067 LANGID langid,
3068 REFCLSID rclsid,
3069 REFGUID catid,
3070 REFGUID guidProfile,
3071 HKL hkl,
3072 DWORD dwFlags)
3073 {
3074 // NOTE: This is installed only on Vista or later. However, this may be
3075 // called by EnsureInitActiveLanguageProfile() even on XP or Server
3076 // 2003.
3077 if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
3078 (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
3079 catid == GUID_TFCAT_TIP_KEYBOARD)) {
3080 mOnActivatedCalled = true;
3081 mIsIMM_IME = IsIMM_IME(hkl);
3082 GetTIPDescription(rclsid, langid, guidProfile,
3083 mActiveTIPKeyboardDescription);
3084 }
3085 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3086 ("TSF: 0x%p nsTextStore::OnActivated(dwProfileType=%s (0x%08X), "
3087 "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
3088 "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
3089 "mActiveTIPDescription=\"%s\"",
3090 this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ?
3091 "TF_PROFILETYPE_INPUTPROCESSOR" :
3092 dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ?
3093 "TF_PROFILETYPE_KEYBOARDLAYOUT" : "Unknown", dwProfileType,
3094 langid, GetCLSIDNameStr(rclsid).get(), GetGUIDNameStr(catid).get(),
3095 GetGUIDNameStr(guidProfile).get(), hkl, dwFlags,
3096 GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE),
3097 GetBoolName(mIsIMM_IME),
3098 NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
3099 return S_OK;
3100 }
3102 // static
3103 nsresult
3104 nsTextStore::OnFocusChange(bool aGotFocus,
3105 nsWindowBase* aFocusedWidget,
3106 IMEState::Enabled aIMEEnabled)
3107 {
3108 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3109 ("TSF: nsTextStore::OnFocusChange(aGotFocus=%s, "
3110 "aFocusedWidget=0x%p, aIMEEnabled=%s), sTsfThreadMgr=0x%p, "
3111 "sTsfTextStore=0x%p",
3112 GetBoolName(aGotFocus), aFocusedWidget,
3113 GetIMEEnabledName(aIMEEnabled), sTsfThreadMgr, sTsfTextStore));
3115 // no change notifications if TSF is disabled
3116 NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE);
3118 nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
3119 if (aGotFocus && (aIMEEnabled == IMEState::ENABLED ||
3120 aIMEEnabled == IMEState::PASSWORD)) {
3121 bool bRet = sTsfTextStore->Create(aFocusedWidget);
3122 NS_ENSURE_TRUE(bRet, NS_ERROR_FAILURE);
3123 NS_ENSURE_TRUE(sTsfTextStore->mDocumentMgr, NS_ERROR_FAILURE);
3124 if (aIMEEnabled == IMEState::PASSWORD) {
3125 MarkContextAsKeyboardDisabled(sTsfTextStore->mContext);
3126 nsRefPtr<ITfContext> topContext;
3127 sTsfTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
3128 if (topContext && topContext != sTsfTextStore->mContext) {
3129 MarkContextAsKeyboardDisabled(topContext);
3130 }
3131 }
3132 HRESULT hr = sTsfThreadMgr->SetFocus(sTsfTextStore->mDocumentMgr);
3133 NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
3134 // Use AssociateFocus() for ensuring that any native focus event
3135 // never steal focus from our documentMgr.
3136 hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
3137 sTsfTextStore->mDocumentMgr,
3138 getter_AddRefs(prevFocusedDocumentMgr));
3139 NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
3140 } else {
3141 if (ThinksHavingFocus()) {
3142 DebugOnly<HRESULT> hr = sTsfThreadMgr->AssociateFocus(
3143 sTsfTextStore->mWidget->GetWindowHandle(),
3144 nullptr, getter_AddRefs(prevFocusedDocumentMgr));
3145 NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
3146 NS_ASSERTION(prevFocusedDocumentMgr == sTsfTextStore->mDocumentMgr,
3147 "different documentMgr has been associated with the window");
3148 sTsfTextStore->Destroy();
3149 }
3150 HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr);
3151 NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
3152 }
3153 return NS_OK;
3154 }
3156 // static
3157 nsIMEUpdatePreference
3158 nsTextStore::GetIMEUpdatePreference()
3159 {
3160 if (sTsfThreadMgr && sTsfTextStore && sTsfTextStore->mDocumentMgr) {
3161 nsRefPtr<ITfDocumentMgr> docMgr;
3162 sTsfThreadMgr->GetFocus(getter_AddRefs(docMgr));
3163 if (docMgr == sTsfTextStore->mDocumentMgr) {
3164 nsIMEUpdatePreference updatePreference(
3165 nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE |
3166 nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE |
3167 nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE |
3168 nsIMEUpdatePreference::NOTIFY_DURING_DEACTIVE);
3169 // nsTextStore shouldn't notify TSF of selection change and text change
3170 // which are caused by composition.
3171 updatePreference.DontNotifyChangesCausedByComposition();
3172 return updatePreference;
3173 }
3174 }
3175 return nsIMEUpdatePreference();
3176 }
3178 nsresult
3179 nsTextStore::OnTextChangeInternal(const IMENotification& aIMENotification)
3180 {
3181 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3182 ("TSF: 0x%p nsTextStore::OnTextChangeInternal(aIMENotification={ "
3183 "mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, "
3184 "mOldEndOffset=%lu, mNewEndOffset=%lu}), mSink=0x%p, mSinkMask=%s, "
3185 "mComposition.IsComposing()=%s",
3186 this, aIMENotification.mMessage,
3187 aIMENotification.mTextChangeData.mStartOffset,
3188 aIMENotification.mTextChangeData.mOldEndOffset,
3189 aIMENotification.mTextChangeData.mNewEndOffset, mSink.get(),
3190 GetSinkMaskNameStr(mSinkMask).get(),
3191 GetBoolName(mComposition.IsComposing())));
3193 if (IsReadLocked()) {
3194 return NS_OK;
3195 }
3197 mSelection.MarkDirty();
3199 if (!mSink || !(mSinkMask & TS_AS_TEXT_CHANGE)) {
3200 return NS_OK;
3201 }
3203 if (!aIMENotification.mTextChangeData.IsInInt32Range()) {
3204 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3205 ("TSF: 0x%p nsTextStore::OnTextChangeInternal() FAILED due to "
3206 "offset is too big for calling mSink->OnTextChange()...",
3207 this));
3208 return NS_OK;
3209 }
3211 // Some TIPs are confused by text change notification during composition.
3212 // Especially, some of them stop working for composition in our process.
3213 // For preventing it, let's commit the composition.
3214 if (mComposition.IsComposing()) {
3215 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3216 ("TSF: 0x%p nsTextStore::OnTextChangeInternal(), "
3217 "committing the composition for avoiding making TIP confused...",
3218 this));
3219 CommitCompositionInternal(false);
3220 return NS_OK;
3221 }
3223 TS_TEXTCHANGE textChange;
3224 textChange.acpStart =
3225 static_cast<LONG>(aIMENotification.mTextChangeData.mStartOffset);
3226 textChange.acpOldEnd =
3227 static_cast<LONG>(aIMENotification.mTextChangeData.mOldEndOffset);
3228 textChange.acpNewEnd =
3229 static_cast<LONG>(aIMENotification.mTextChangeData.mNewEndOffset);
3231 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3232 ("TSF: 0x%p nsTextStore::OnTextChangeInternal(), calling "
3233 "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
3234 "acpNewEnd=%ld })...", this, textChange.acpStart,
3235 textChange.acpOldEnd, textChange.acpNewEnd));
3236 mSink->OnTextChange(0, &textChange);
3238 return NS_OK;
3239 }
3241 nsresult
3242 nsTextStore::OnSelectionChangeInternal(void)
3243 {
3244 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3245 ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), "
3246 "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
3247 "mComposition.IsComposing()=%s",
3248 this, mSink.get(), GetSinkMaskNameStr(mSinkMask).get(),
3249 GetBoolName(mIsRecordingActionsWithoutLock),
3250 GetBoolName(mComposition.IsComposing())));
3252 if (IsReadLocked()) {
3253 return NS_OK;
3254 }
3256 mSelection.MarkDirty();
3258 if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) {
3259 return NS_OK;
3260 }
3262 // Some TIPs are confused by selection change notification during composition.
3263 // Especially, some of them stop working for composition in our process.
3264 // For preventing it, let's commit the composition.
3265 if (mComposition.IsComposing()) {
3266 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3267 ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), "
3268 "committing the composition for avoiding making TIP confused...",
3269 this));
3270 CommitCompositionInternal(false);
3271 return NS_OK;
3272 }
3274 if (!mIsRecordingActionsWithoutLock) {
3275 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3276 ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), calling "
3277 "mSink->OnSelectionChange()...", this));
3278 mSink->OnSelectionChange();
3279 } else {
3280 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3281 ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), pending "
3282 "a call of mSink->OnSelectionChange()...", this));
3283 mPendingOnSelectionChange = true;
3284 }
3285 return NS_OK;
3286 }
3288 nsresult
3289 nsTextStore::OnLayoutChangeInternal()
3290 {
3291 NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
3292 NS_ENSURE_TRUE(mSink, NS_ERROR_FAILURE);
3294 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3295 ("TSF: 0x%p nsTextStore::OnLayoutChangeInternal(), calling "
3296 "mSink->OnLayoutChange()...", this));
3297 HRESULT hr = mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
3298 NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE);
3300 return NS_OK;
3301 }
3303 void
3304 nsTextStore::CreateNativeCaret()
3305 {
3306 // This method must work only on desktop application.
3307 if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Desktop) {
3308 return;
3309 }
3311 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3312 ("TSF: 0x%p nsTextStore::CreateNativeCaret(), "
3313 "mComposition.IsComposing()=%s",
3314 this, GetBoolName(mComposition.IsComposing())));
3316 Selection& currentSel = CurrentSelection();
3317 if (currentSel.IsDirty()) {
3318 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3319 ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to "
3320 "CurrentSelection() failure", this));
3321 return;
3322 }
3324 // XXX If this is called without composition and the selection isn't
3325 // collapsed, is it OK?
3326 uint32_t caretOffset = currentSel.MaxOffset();
3328 WidgetQueryContentEvent queryCaretRect(true, NS_QUERY_CARET_RECT, mWidget);
3329 queryCaretRect.InitForQueryCaretRect(caretOffset);
3330 mWidget->InitEvent(queryCaretRect);
3331 mWidget->DispatchWindowEvent(&queryCaretRect);
3332 if (!queryCaretRect.mSucceeded) {
3333 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3334 ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to "
3335 "NS_QUERY_CARET_RECT failure (offset=%d)", this, caretOffset));
3336 return;
3337 }
3339 nsIntRect& caretRect = queryCaretRect.mReply.mRect;
3340 mNativeCaretIsCreated = ::CreateCaret(mWidget->GetWindowHandle(), nullptr,
3341 caretRect.width, caretRect.height);
3342 if (!mNativeCaretIsCreated) {
3343 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3344 ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to "
3345 "CreateCaret() failure", this));
3346 return;
3347 }
3349 nsWindow* window = static_cast<nsWindow*>(mWidget.get());
3350 nsWindow* toplevelWindow = window->GetTopLevelWindow(false);
3351 if (!toplevelWindow) {
3352 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3353 ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to "
3354 "no top level window", this));
3355 return;
3356 }
3358 if (toplevelWindow != window) {
3359 caretRect.MoveBy(toplevelWindow->WidgetToScreenOffset());
3360 caretRect.MoveBy(-window->WidgetToScreenOffset());
3361 }
3363 ::SetCaretPos(caretRect.x, caretRect.y);
3364 }
3366 bool
3367 nsTextStore::EnsureInitActiveTIPKeyboard()
3368 {
3369 if (mOnActivatedCalled) {
3370 return true;
3371 }
3373 if (IsVistaOrLater()) {
3374 nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
3375 HRESULT hr =
3376 sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
3377 getter_AddRefs(profileMgr));
3378 if (FAILED(hr) || !profileMgr) {
3379 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3380 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
3381 "to get input processor profile manager, hr=0x%08X", this, hr));
3382 return false;
3383 }
3385 TF_INPUTPROCESSORPROFILE profile;
3386 hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
3387 if (hr == S_FALSE) {
3388 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3389 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
3390 "to get active keyboard layout profile due to no active profile, "
3391 "hr=0x%08X", this, hr));
3392 // XXX Should we call OnActivated() with arguments like non-TIP in this
3393 // case?
3394 return false;
3395 }
3396 if (FAILED(hr)) {
3397 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3398 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
3399 "to get active TIP keyboard, hr=0x%08X", this, hr));
3400 return false;
3401 }
3403 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3404 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), "
3405 "calling OnActivated() manually...", this));
3406 OnActivated(profile.dwProfileType, profile.langid, profile.clsid,
3407 profile.catid, profile.guidProfile, ::GetKeyboardLayout(0),
3408 TF_IPSINK_FLAG_ACTIVE);
3409 return true;
3410 }
3412 LANGID langID;
3413 HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID);
3414 if (FAILED(hr)) {
3415 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3416 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
3417 "to get current language ID, hr=0x%08X", this, hr));
3418 return false;
3419 }
3421 nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
3422 hr = sInputProcessorProfiles->EnumLanguageProfiles(langID,
3423 getter_AddRefs(enumLangProfiles));
3424 if (FAILED(hr) || !enumLangProfiles) {
3425 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3426 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED "
3427 "to get language profiles enumerator, hr=0x%08X", this, hr));
3428 return false;
3429 }
3431 TF_LANGUAGEPROFILE profile;
3432 ULONG fetch = 0;
3433 while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
3434 if (!profile.fActive || profile.catid != GUID_TFCAT_TIP_KEYBOARD) {
3435 continue;
3436 }
3437 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3438 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), "
3439 "calling OnActivated() manually...", this));
3440 bool isTIP = profile.guidProfile != GUID_NULL;
3441 OnActivated(isTIP ? TF_PROFILETYPE_INPUTPROCESSOR :
3442 TF_PROFILETYPE_KEYBOARDLAYOUT,
3443 profile.langid, profile.clsid, profile.catid,
3444 profile.guidProfile, ::GetKeyboardLayout(0),
3445 TF_IPSINK_FLAG_ACTIVE);
3446 return true;
3447 }
3449 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3450 ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), "
3451 "calling OnActivated() without active TIP manually...", this));
3452 OnActivated(TF_PROFILETYPE_KEYBOARDLAYOUT,
3453 langID, CLSID_NULL, GUID_TFCAT_TIP_KEYBOARD,
3454 GUID_NULL, ::GetKeyboardLayout(0),
3455 TF_IPSINK_FLAG_ACTIVE);
3456 return true;
3457 }
3459 void
3460 nsTextStore::CommitCompositionInternal(bool aDiscard)
3461 {
3462 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3463 ("TSF: 0x%p nsTextStore::CommitCompositionInternal(aDiscard=%s), "
3464 "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
3465 "mComposition.mString=\"%s\"",
3466 this, GetBoolName(aDiscard), mSink.get(), mContext.get(),
3467 mComposition.mView.get(),
3468 NS_ConvertUTF16toUTF8(mComposition.mString).get()));
3470 if (mComposition.IsComposing() && aDiscard) {
3471 LONG endOffset = mComposition.EndOffset();
3472 mComposition.mString.Truncate(0);
3473 if (mSink && !mLock) {
3474 TS_TEXTCHANGE textChange;
3475 textChange.acpStart = mComposition.mStart;
3476 textChange.acpOldEnd = endOffset;
3477 textChange.acpNewEnd = mComposition.mStart;
3478 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3479 ("TSF: 0x%p nsTextStore::CommitCompositionInternal(), calling"
3480 "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
3481 "acpNewEnd=%ld })...", this, textChange.acpStart,
3482 textChange.acpOldEnd, textChange.acpNewEnd));
3483 mSink->OnTextChange(0, &textChange);
3484 }
3485 }
3486 // Terminate two contexts, the base context (mContext) and the top
3487 // if the top context is not the same as the base context
3488 nsRefPtr<ITfContext> context = mContext;
3489 do {
3490 if (context) {
3491 nsRefPtr<ITfContextOwnerCompositionServices> services;
3492 context->QueryInterface(IID_ITfContextOwnerCompositionServices,
3493 getter_AddRefs(services));
3494 if (services) {
3495 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3496 ("TSF: 0x%p nsTextStore::CommitCompositionInternal(), "
3497 "requesting TerminateComposition() for the context 0x%p...",
3498 this, context.get()));
3499 services->TerminateComposition(nullptr);
3500 }
3501 }
3502 if (context != mContext)
3503 break;
3504 if (mDocumentMgr)
3505 mDocumentMgr->GetTop(getter_AddRefs(context));
3506 } while (context != mContext);
3507 }
3509 static
3510 bool
3511 GetCompartment(IUnknown* pUnk,
3512 const GUID& aID,
3513 ITfCompartment** aCompartment)
3514 {
3515 if (!pUnk) return false;
3517 nsRefPtr<ITfCompartmentMgr> compMgr;
3518 pUnk->QueryInterface(IID_ITfCompartmentMgr, getter_AddRefs(compMgr));
3519 if (!compMgr) return false;
3521 return SUCCEEDED(compMgr->GetCompartment(aID, aCompartment)) &&
3522 (*aCompartment) != nullptr;
3523 }
3525 // static
3526 void
3527 nsTextStore::SetIMEOpenState(bool aState)
3528 {
3529 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3530 ("TSF: nsTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState)));
3532 nsRefPtr<ITfCompartment> comp;
3533 if (!GetCompartment(sTsfThreadMgr,
3534 GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
3535 getter_AddRefs(comp))) {
3536 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3537 ("TSF: nsTextStore::SetIMEOpenState() FAILED due to"
3538 "no compartment available"));
3539 return;
3540 }
3542 VARIANT variant;
3543 variant.vt = VT_I4;
3544 variant.lVal = aState;
3545 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3546 ("TSF: nsTextStore::SetIMEOpenState(), setting "
3547 "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...",
3548 variant.lVal));
3549 comp->SetValue(sTsfClientId, &variant);
3550 }
3552 // static
3553 bool
3554 nsTextStore::GetIMEOpenState(void)
3555 {
3556 nsRefPtr<ITfCompartment> comp;
3557 if (!GetCompartment(sTsfThreadMgr,
3558 GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
3559 getter_AddRefs(comp)))
3560 return false;
3562 VARIANT variant;
3563 ::VariantInit(&variant);
3564 if (SUCCEEDED(comp->GetValue(&variant)) && variant.vt == VT_I4)
3565 return variant.lVal != 0;
3567 ::VariantClear(&variant); // clear up in case variant.vt != VT_I4
3568 return false;
3569 }
3571 // static
3572 void
3573 nsTextStore::SetInputContext(nsWindowBase* aWidget,
3574 const InputContext& aContext,
3575 const InputContextAction& aAction)
3576 {
3577 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3578 ("TSF: nsTextStore::SetInputContext(aWidget=%p, "
3579 "aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), "
3580 "ThinksHavingFocus()=%s",
3581 aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled),
3582 GetFocusChangeName(aAction.mFocusChange),
3583 GetBoolName(ThinksHavingFocus())));
3585 NS_ENSURE_TRUE_VOID(sTsfTextStore);
3586 sTsfTextStore->SetInputScope(aContext.mHTMLInputType);
3588 if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) {
3589 return;
3590 }
3592 // If focus isn't actually changed but the enabled state is changed,
3593 // emulate the focus move.
3594 if (!ThinksHavingFocus() &&
3595 aContext.mIMEState.mEnabled == IMEState::ENABLED) {
3596 OnFocusChange(true, aWidget, aContext.mIMEState.mEnabled);
3597 } else if (ThinksHavingFocus() &&
3598 aContext.mIMEState.mEnabled != IMEState::ENABLED) {
3599 OnFocusChange(false, aWidget, aContext.mIMEState.mEnabled);
3600 }
3601 }
3603 // static
3604 void
3605 nsTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext)
3606 {
3607 VARIANT variant_int4_value1;
3608 variant_int4_value1.vt = VT_I4;
3609 variant_int4_value1.lVal = 1;
3611 nsRefPtr<ITfCompartment> comp;
3612 if (!GetCompartment(aContext,
3613 GUID_COMPARTMENT_KEYBOARD_DISABLED,
3614 getter_AddRefs(comp))) {
3615 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3616 ("TSF: nsTextStore::MarkContextAsKeyboardDisabled() failed"
3617 "aContext=0x%p...", aContext));
3618 return;
3619 }
3621 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3622 ("TSF: nsTextStore::MarkContextAsKeyboardDisabled(), setting "
3623 "to disable context 0x%p...",
3624 aContext));
3625 comp->SetValue(sTsfClientId, &variant_int4_value1);
3626 }
3628 // static
3629 void
3630 nsTextStore::MarkContextAsEmpty(ITfContext* aContext)
3631 {
3632 VARIANT variant_int4_value1;
3633 variant_int4_value1.vt = VT_I4;
3634 variant_int4_value1.lVal = 1;
3636 nsRefPtr<ITfCompartment> comp;
3637 if (!GetCompartment(aContext,
3638 GUID_COMPARTMENT_EMPTYCONTEXT,
3639 getter_AddRefs(comp))) {
3640 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3641 ("TSF: nsTextStore::MarkContextAsEmpty() failed"
3642 "aContext=0x%p...", aContext));
3643 return;
3644 }
3646 PR_LOG(sTextStoreLog, PR_LOG_DEBUG,
3647 ("TSF: nsTextStore::MarkContextAsEmpty(), setting "
3648 "to mark empty context 0x%p...", aContext));
3649 comp->SetValue(sTsfClientId, &variant_int4_value1);
3650 }
3652 // static
3653 bool
3654 nsTextStore::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
3655 REFGUID aProfile)
3656 {
3657 if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
3658 return false;
3659 }
3661 nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
3662 HRESULT hr =
3663 sInputProcessorProfiles->EnumLanguageProfiles(aLangID,
3664 getter_AddRefs(enumLangProfiles));
3665 if (FAILED(hr) || !enumLangProfiles) {
3666 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3667 ("TSF: nsTextStore::IsTIPCategoryKeyboard(), FAILED "
3668 "to get language profiles enumerator, hr=0x%08X", hr));
3669 return false;
3670 }
3672 TF_LANGUAGEPROFILE profile;
3673 ULONG fetch = 0;
3674 while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
3675 // XXX We're not sure a profile is registered with two or more categories.
3676 if (profile.clsid == aTextService &&
3677 profile.guidProfile == aProfile &&
3678 profile.catid == GUID_TFCAT_TIP_KEYBOARD) {
3679 return true;
3680 }
3681 }
3682 return false;
3683 }
3685 // static
3686 void
3687 nsTextStore::GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
3688 REFGUID aProfile, nsAString& aDescription)
3689 {
3690 aDescription.Truncate();
3692 if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
3693 return;
3694 }
3696 BSTR description = nullptr;
3697 HRESULT hr =
3698 sInputProcessorProfiles->GetLanguageProfileDescription(aTextService,
3699 aLangID,
3700 aProfile,
3701 &description);
3702 if (FAILED(hr)) {
3703 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3704 ("TSF: nsTextStore::InitActiveTIPDescription() FAILED due to "
3705 "GetLanguageProfileDescription() failure, hr=0x%08X", hr));
3706 return;
3707 }
3709 if (description && description[0]) {
3710 aDescription.Assign(description);
3711 }
3712 ::SysFreeString(description);
3713 }
3715 // static
3716 void
3717 nsTextStore::Initialize()
3718 {
3719 #ifdef PR_LOGGING
3720 if (!sTextStoreLog) {
3721 sTextStoreLog = PR_NewLogModule("nsTextStoreWidgets");
3722 }
3723 #endif
3725 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3726 ("TSF: nsTextStore::Initialize() is called..."));
3728 if (sTsfThreadMgr) {
3729 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3730 ("TSF: nsTextStore::Initialize() FAILED due to already initialized"));
3731 return;
3732 }
3734 bool enableTsf = Preferences::GetBool(kPrefNameTSFEnabled, false);
3735 // Migrate legacy TSF pref to new pref. This should be removed in next
3736 // release cycle or later.
3737 if (!enableTsf && Preferences::GetBool(kLegacyPrefNameTSFEnabled, false)) {
3738 enableTsf = true;
3739 Preferences::SetBool(kPrefNameTSFEnabled, true);
3740 Preferences::ClearUser(kLegacyPrefNameTSFEnabled);
3741 }
3742 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3743 ("TSF: nsTextStore::Initialize(), TSF is %s",
3744 enableTsf ? "enabled" : "disabled"));
3745 if (!enableTsf) {
3746 return;
3747 }
3749 // XXX MSDN documents that ITfInputProcessorProfiles is available only on
3750 // desktop apps. However, there is no known way to obtain
3751 // ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
3752 // instance.
3753 nsRefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
3754 HRESULT hr =
3755 ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr,
3756 CLSCTX_INPROC_SERVER,
3757 IID_ITfInputProcessorProfiles,
3758 getter_AddRefs(inputProcessorProfiles));
3759 if (FAILED(hr) || !inputProcessorProfiles) {
3760 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3761 ("TSF: nsTextStore::Initialize() FAILED to create input processor "
3762 "profiles, hr=0x%08X", hr));
3763 return;
3764 }
3766 nsRefPtr<ITfThreadMgr> threadMgr;
3767 hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr,
3768 CLSCTX_INPROC_SERVER, IID_ITfThreadMgr,
3769 getter_AddRefs(threadMgr));
3770 if (FAILED(hr) || !threadMgr) {
3771 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3772 ("TSF: nsTextStore::Initialize() FAILED to "
3773 "create the thread manager, hr=0x%08X", hr));
3774 return;
3775 }
3777 nsRefPtr<ITfMessagePump> messagePump;
3778 hr = threadMgr->QueryInterface(IID_ITfMessagePump,
3779 getter_AddRefs(messagePump));
3780 if (FAILED(hr) || !messagePump) {
3781 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3782 ("TSF: nsTextStore::Initialize() FAILED to "
3783 "QI message pump from the thread manager, hr=0x%08X", hr));
3784 return;
3785 }
3787 nsRefPtr<ITfKeystrokeMgr> keystrokeMgr;
3788 hr = threadMgr->QueryInterface(IID_ITfKeystrokeMgr,
3789 getter_AddRefs(keystrokeMgr));
3790 if (FAILED(hr) || !keystrokeMgr) {
3791 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3792 ("TSF: nsTextStore::Initialize() FAILED to "
3793 "QI keystroke manager from the thread manager, hr=0x%08X", hr));
3794 return;
3795 }
3797 hr = threadMgr->Activate(&sTsfClientId);
3798 if (FAILED(hr)) {
3799 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3800 ("TSF: nsTextStore::Initialize() FAILED to activate, hr=0x%08X", hr));
3801 return;
3802 }
3804 nsRefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
3805 hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr,
3806 CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr,
3807 getter_AddRefs(displayAttributeMgr));
3808 if (FAILED(hr) || !displayAttributeMgr) {
3809 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3810 ("TSF: nsTextStore::Initialize() FAILED to create "
3811 "a display attribute manager instance, hr=0x%08X", hr));
3812 return;
3813 }
3815 nsRefPtr<ITfCategoryMgr> categoryMgr;
3816 hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr,
3817 CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr,
3818 getter_AddRefs(categoryMgr));
3819 if (FAILED(hr) || !categoryMgr) {
3820 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3821 ("TSF: nsTextStore::Initialize() FAILED to create "
3822 "a category manager instance, hr=0x%08X", hr));
3823 return;
3824 }
3826 nsRefPtr<ITfDocumentMgr> disabledDocumentMgr;
3827 hr = threadMgr->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr));
3828 if (FAILED(hr) || !disabledDocumentMgr) {
3829 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3830 ("TSF: nsTextStore::Initialize() FAILED to create "
3831 "a document manager for disabled mode, hr=0x%08X", hr));
3832 return;
3833 }
3835 nsRefPtr<ITfContext> disabledContext;
3836 DWORD editCookie = 0;
3837 hr = disabledDocumentMgr->CreateContext(sTsfClientId, 0, nullptr,
3838 getter_AddRefs(disabledContext),
3839 &editCookie);
3840 if (FAILED(hr) || !disabledContext) {
3841 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3842 ("TSF: nsTextStore::Initialize() FAILED to create "
3843 "a context for disabled mode, hr=0x%08X", hr));
3844 return;
3845 }
3847 MarkContextAsKeyboardDisabled(disabledContext);
3848 MarkContextAsEmpty(disabledContext);
3850 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3851 ("TSF: nsTextStore::Initialize() is creating "
3852 "an nsTextStore instance..."));
3853 nsRefPtr<nsTextStore> textStore = new nsTextStore();
3854 if (!textStore->Init(threadMgr)) {
3855 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
3856 ("TSF: nsTextStore::Initialize() FAILED to initialize nsTextStore "
3857 "instance"));
3858 return;
3859 }
3861 inputProcessorProfiles.swap(sInputProcessorProfiles);
3862 threadMgr.swap(sTsfThreadMgr);
3863 messagePump.swap(sMessagePump);
3864 keystrokeMgr.swap(sKeystrokeMgr);
3865 displayAttributeMgr.swap(sDisplayAttrMgr);
3866 categoryMgr.swap(sCategoryMgr);
3867 disabledDocumentMgr.swap(sTsfDisabledDocumentMgr);
3868 disabledContext.swap(sTsfDisabledContext);
3869 textStore.swap(sTsfTextStore);
3871 sCreateNativeCaretForATOK =
3872 Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true);
3874 MOZ_ASSERT(!sFlushTIPInputMessage);
3875 sFlushTIPInputMessage = ::RegisterWindowMessageW(L"Flush TIP Input Message");
3877 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
3878 ("TSF: nsTextStore::Initialize(), sTsfThreadMgr=0x%p, "
3879 "sTsfClientId=0x%08X, sTsfTextStore=0x%p, sDisplayAttrMgr=0x%p, "
3880 "sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p, "
3881 "sCreateNativeCaretForATOK=%s",
3882 sTsfThreadMgr, sTsfClientId, sTsfTextStore, sDisplayAttrMgr, sCategoryMgr,
3883 sTsfDisabledDocumentMgr, sTsfDisabledContext,
3884 GetBoolName(sCreateNativeCaretForATOK)));
3885 }
3887 // static
3888 void
3889 nsTextStore::Terminate(void)
3890 {
3891 PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Terminate()"));
3893 NS_IF_RELEASE(sDisplayAttrMgr);
3894 NS_IF_RELEASE(sCategoryMgr);
3895 NS_IF_RELEASE(sTsfTextStore);
3896 NS_IF_RELEASE(sTsfDisabledDocumentMgr);
3897 NS_IF_RELEASE(sTsfDisabledContext);
3898 NS_IF_RELEASE(sInputProcessorProfiles);
3899 sTsfClientId = 0;
3900 if (sTsfThreadMgr) {
3901 sTsfThreadMgr->Deactivate();
3902 NS_RELEASE(sTsfThreadMgr);
3903 NS_RELEASE(sMessagePump);
3904 NS_RELEASE(sKeystrokeMgr);
3905 }
3906 }
3908 // static
3909 bool
3910 nsTextStore::ProcessRawKeyMessage(const MSG& aMsg)
3911 {
3912 if (!sKeystrokeMgr) {
3913 return false; // not in TSF mode
3914 }
3916 if (aMsg.message == WM_KEYDOWN) {
3917 BOOL eaten;
3918 HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
3919 if (FAILED(hr) || !eaten) {
3920 return false;
3921 }
3922 hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
3923 return SUCCEEDED(hr) && eaten;
3924 }
3925 if (aMsg.message == WM_KEYUP) {
3926 BOOL eaten;
3927 HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
3928 if (FAILED(hr) || !eaten) {
3929 return false;
3930 }
3931 hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
3932 return SUCCEEDED(hr) && eaten;
3933 }
3934 return false;
3935 }
3937 // static
3938 void
3939 nsTextStore::ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
3940 WPARAM& aWParam, LPARAM& aLParam,
3941 MSGResult& aResult)
3942 {
3943 switch (aMessage) {
3944 case WM_IME_SETCONTEXT:
3945 // If a windowless plugin had focus and IME was handled on it, composition
3946 // window was set the position. After that, even in TSF mode, WinXP keeps
3947 // to use composition window at the position if the active IME is not
3948 // aware TSF. For avoiding this issue, we need to hide the composition
3949 // window here.
3950 if (aWParam) {
3951 aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
3952 }
3953 break;
3954 }
3955 }
3957 /******************************************************************/
3958 /* nsTextStore::Composition */
3959 /******************************************************************/
3961 void
3962 nsTextStore::Composition::Start(ITfCompositionView* aCompositionView,
3963 LONG aCompositionStartOffset,
3964 const nsAString& aCompositionString)
3965 {
3966 mView = aCompositionView;
3967 mString = aCompositionString;
3968 mStart = aCompositionStartOffset;
3969 }
3971 void
3972 nsTextStore::Composition::End()
3973 {
3974 mView = nullptr;
3975 mString.Truncate();
3976 }
3978 /******************************************************************************
3979 * nsTextStore::Content
3980 *****************************************************************************/
3982 const nsDependentSubstring
3983 nsTextStore::Content::GetSelectedText() const
3984 {
3985 MOZ_ASSERT(mInitialized);
3986 return GetSubstring(static_cast<uint32_t>(mSelection.StartOffset()),
3987 static_cast<uint32_t>(mSelection.Length()));
3988 }
3990 const nsDependentSubstring
3991 nsTextStore::Content::GetSubstring(uint32_t aStart, uint32_t aLength) const
3992 {
3993 MOZ_ASSERT(mInitialized);
3994 return nsDependentSubstring(mText, aStart, aLength);
3995 }
3997 void
3998 nsTextStore::Content::ReplaceSelectedTextWith(const nsAString& aString)
3999 {
4000 MOZ_ASSERT(mInitialized);
4001 ReplaceTextWith(mSelection.StartOffset(), mSelection.Length(), aString);
4002 }
4004 inline uint32_t
4005 FirstDifferentCharOffset(const nsAString& aStr1, const nsAString& aStr2)
4006 {
4007 MOZ_ASSERT(aStr1 != aStr2);
4008 uint32_t i = 0;
4009 uint32_t minLength = std::min(aStr1.Length(), aStr2.Length());
4010 for (; i < minLength && aStr1[i] == aStr2[i]; i++) {
4011 /* nothing to do */
4012 }
4013 return i;
4014 }
4016 void
4017 nsTextStore::Content::ReplaceTextWith(LONG aStart, LONG aLength,
4018 const nsAString& aReplaceString)
4019 {
4020 MOZ_ASSERT(mInitialized);
4021 const nsDependentSubstring replacedString =
4022 GetSubstring(static_cast<uint32_t>(aStart),
4023 static_cast<uint32_t>(aLength));
4024 if (aReplaceString != replacedString) {
4025 uint32_t firstDifferentOffset =
4026 static_cast<uint32_t>(aStart) + FirstDifferentCharOffset(aReplaceString,
4027 replacedString);
4028 mMinTextModifiedOffset =
4029 std::min(mMinTextModifiedOffset, firstDifferentOffset);
4030 if (mComposition.IsComposing()) {
4031 // Emulate text insertion during compositions, because during a
4032 // composition, editor expects the whole composition string to
4033 // be sent in NS_TEXT_TEXT, not just the inserted part.
4034 // The actual NS_TEXT_TEXT will be sent in SetSelection or
4035 // OnUpdateComposition.
4036 MOZ_ASSERT(aStart >= mComposition.mStart);
4037 MOZ_ASSERT(aStart + aLength <= mComposition.EndOffset());
4038 mComposition.mString.Replace(
4039 static_cast<uint32_t>(aStart - mComposition.mStart),
4040 static_cast<uint32_t>(aLength), aReplaceString);
4041 }
4042 mText.Replace(static_cast<uint32_t>(aStart),
4043 static_cast<uint32_t>(aLength), aReplaceString);
4044 }
4045 // Selection should be collapsed at the end of the inserted string.
4046 mSelection.CollapseAt(
4047 static_cast<uint32_t>(aStart) + aReplaceString.Length());
4048 }
4050 void
4051 nsTextStore::Content::StartComposition(ITfCompositionView* aCompositionView,
4052 const PendingAction& aCompStart,
4053 bool aPreserveSelection)
4054 {
4055 MOZ_ASSERT(mInitialized);
4056 MOZ_ASSERT(aCompositionView);
4057 MOZ_ASSERT(!mComposition.mView);
4058 MOZ_ASSERT(aCompStart.mType == PendingAction::COMPOSITION_START);
4060 mComposition.Start(aCompositionView, aCompStart.mSelectionStart,
4061 GetSubstring(static_cast<uint32_t>(aCompStart.mSelectionStart),
4062 static_cast<uint32_t>(aCompStart.mSelectionLength)));
4063 if (!aPreserveSelection) {
4064 mSelection.SetSelection(mComposition.mStart, mComposition.mString.Length(),
4065 false);
4066 }
4067 }
4069 void
4070 nsTextStore::Content::EndComposition(const PendingAction& aCompEnd)
4071 {
4072 MOZ_ASSERT(mInitialized);
4073 MOZ_ASSERT(mComposition.mView);
4074 MOZ_ASSERT(aCompEnd.mType == PendingAction::COMPOSITION_END);
4076 mSelection.CollapseAt(mComposition.mStart + aCompEnd.mData.Length());
4077 mComposition.End();
4078 }
4080 #ifdef DEBUG
4081 // static
4082 bool
4083 nsTextStore::CurrentKeyboardLayoutHasIME()
4084 {
4085 if (!sInputProcessorProfiles) {
4086 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
4087 ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED due to there is "
4088 "no input processor profiles instance"));
4089 return false;
4090 }
4091 nsRefPtr<ITfInputProcessorProfileMgr> profileMgr;
4092 HRESULT hr =
4093 sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr,
4094 getter_AddRefs(profileMgr));
4095 if (FAILED(hr) || !profileMgr) {
4096 // On Windows Vista or later, ImmIsIME() API always returns true.
4097 // If we failed to obtain the profile manager, we cannot know if current
4098 // keyboard layout has IME.
4099 if (IsVistaOrLater()) {
4100 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
4101 ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
4102 "ITfInputProcessorProfileMgr"));
4103 return false;
4104 }
4105 // If the profiles instance doesn't have ITfInputProcessorProfileMgr
4106 // interface, that means probably we're running on WinXP or WinServer2003
4107 // (except WinServer2003 R2). Then, we should use ImmIsIME().
4108 return ::ImmIsIME(::GetKeyboardLayout(0));
4109 }
4111 TF_INPUTPROCESSORPROFILE profile;
4112 hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
4113 if (hr == S_FALSE) {
4114 return false; // not found or not active
4115 }
4116 if (FAILED(hr)) {
4117 PR_LOG(sTextStoreLog, PR_LOG_ERROR,
4118 ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
4119 "active profile"));
4120 return false;
4121 }
4122 return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR);
4123 }
4124 #endif // #ifdef DEBUG