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/. */
5 
6 #define INPUTSCOPE_INIT_GUID
7 #define TEXTATTRS_INIT_GUID
8 #include "TSFTextStore.h"
9 
10 #include <olectl.h>
11 #include <algorithm>
12 #include "nscore.h"
13 
14 #include "IMMHandler.h"
15 #include "KeyboardLayout.h"
16 #include "WinIMEHandler.h"
17 #include "WinUtils.h"
18 #include "mozilla/AutoRestore.h"
19 #include "mozilla/Logging.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/Telemetry.h"
22 #include "mozilla/TextEventDispatcher.h"
23 #include "mozilla/TextEvents.h"
24 #include "mozilla/ToString.h"
25 #include "mozilla/WindowsVersion.h"
26 #include "nsWindow.h"
27 #include "nsPrintfCString.h"
28 
29 // Workaround for mingw32
30 #ifndef TS_SD_INPUTPANEMANUALDISPLAYENABLE
31 #  define TS_SD_INPUTPANEMANUALDISPLAYENABLE 0x40
32 #endif
33 
34 namespace mozilla {
35 namespace widget {
36 
37 static const char* kPrefNameEnableTSF = "intl.tsf.enable";
38 
39 /**
40  * TSF related code should log its behavior even on release build especially
41  * in the interface methods.
42  *
43  * In interface methods, use LogLevel::Info.
44  * In internal methods, use LogLevel::Debug for logging normal behavior.
45  * For logging error, use LogLevel::Error.
46  *
47  * When an instance method is called, start with following text:
48  *   "0x%p TSFFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
49  * after that, start with:
50  *   "0x%p   TSFFoo::Bar("
51  * In an internal method, start with following text:
52  *   "0x%p   TSFFoo::Bar("
53  * When a static method is called, start with following text:
54  *   "TSFFoo::Bar("
55  */
56 
57 LazyLogModule sTextStoreLog("nsTextStoreWidgets");
58 
59 enum class TextInputProcessorID {
60   // Internal use only.  This won't be returned by TSFStaticSink::ActiveTIP().
61   eNotComputed,
62 
63   // Not a TIP.  E.g., simple keyboard layout or IMM-IME.
64   eNone,
65 
66   // Used for other TIPs, i.e., any TIPs which we don't support specifically.
67   eUnknown,
68 
69   // TIP for Japanese.
70   eMicrosoftIMEForJapanese,
71   eMicrosoftOfficeIME2010ForJapanese,
72   eGoogleJapaneseInput,
73   eATOK2011,
74   eATOK2012,
75   eATOK2013,
76   eATOK2014,
77   eATOK2015,
78   eATOK2016,
79   eATOKUnknown,
80   eJapanist10,
81 
82   // TIP for Traditional Chinese.
83   eMicrosoftBopomofo,
84   eMicrosoftChangJie,
85   eMicrosoftPhonetic,
86   eMicrosoftQuick,
87   eMicrosoftNewChangJie,
88   eMicrosoftNewPhonetic,
89   eMicrosoftNewQuick,
90   eFreeChangJie,
91 
92   // TIP for Simplified Chinese.
93   eMicrosoftPinyin,
94   eMicrosoftPinyinNewExperienceInputStyle,
95   eMicrosoftWubi,
96 
97   // TIP for Korean.
98   eMicrosoftIMEForKorean,
99   eMicrosoftOldHangul,
100 };
101 
GetBoolName(bool aBool)102 static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }
103 
HandleSeparator(nsCString & aDesc)104 static void HandleSeparator(nsCString& aDesc) {
105   if (!aDesc.IsEmpty()) {
106     aDesc.AppendLiteral(" | ");
107   }
108 }
109 
GetFindFlagName(DWORD aFindFlag)110 static const nsCString GetFindFlagName(DWORD aFindFlag) {
111   nsCString description;
112   if (!aFindFlag) {
113     description.AppendLiteral("no flags (0)");
114     return description;
115   }
116   if (aFindFlag & TS_ATTR_FIND_BACKWARDS) {
117     description.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
118   }
119   if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) {
120     HandleSeparator(description);
121     description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
122   }
123   if (aFindFlag & TS_ATTR_FIND_UPDATESTART) {
124     HandleSeparator(description);
125     description.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
126   }
127   if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) {
128     HandleSeparator(description);
129     description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
130   }
131   if (aFindFlag & TS_ATTR_FIND_WANT_END) {
132     HandleSeparator(description);
133     description.AppendLiteral("TS_ATTR_FIND_WANT_END");
134   }
135   if (aFindFlag & TS_ATTR_FIND_HIDDEN) {
136     HandleSeparator(description);
137     description.AppendLiteral("TS_ATTR_FIND_HIDDEN");
138   }
139   if (description.IsEmpty()) {
140     description.AppendLiteral("Unknown (");
141     description.AppendInt(static_cast<uint32_t>(aFindFlag));
142     description.Append(')');
143   }
144   return description;
145 }
146 
147 class GetACPFromPointFlagName : public nsAutoCString {
148  public:
GetACPFromPointFlagName(DWORD aFlags)149   explicit GetACPFromPointFlagName(DWORD aFlags) {
150     if (!aFlags) {
151       AppendLiteral("no flags (0)");
152       return;
153     }
154     if (aFlags & GXFPF_ROUND_NEAREST) {
155       AppendLiteral("GXFPF_ROUND_NEAREST");
156       aFlags &= ~GXFPF_ROUND_NEAREST;
157     }
158     if (aFlags & GXFPF_NEAREST) {
159       HandleSeparator(*this);
160       AppendLiteral("GXFPF_NEAREST");
161       aFlags &= ~GXFPF_NEAREST;
162     }
163     if (aFlags) {
164       HandleSeparator(*this);
165       AppendLiteral("Unknown(");
166       AppendInt(static_cast<uint32_t>(aFlags));
167       Append(')');
168     }
169   }
~GetACPFromPointFlagName()170   virtual ~GetACPFromPointFlagName() {}
171 };
172 
GetFocusChangeName(InputContextAction::FocusChange aFocusChange)173 static const char* GetFocusChangeName(
174     InputContextAction::FocusChange aFocusChange) {
175   switch (aFocusChange) {
176     case InputContextAction::FOCUS_NOT_CHANGED:
177       return "FOCUS_NOT_CHANGED";
178     case InputContextAction::GOT_FOCUS:
179       return "GOT_FOCUS";
180     case InputContextAction::LOST_FOCUS:
181       return "LOST_FOCUS";
182     case InputContextAction::MENU_GOT_PSEUDO_FOCUS:
183       return "MENU_GOT_PSEUDO_FOCUS";
184     case InputContextAction::MENU_LOST_PSEUDO_FOCUS:
185       return "MENU_LOST_PSEUDO_FOCUS";
186     case InputContextAction::WIDGET_CREATED:
187       return "WIDGET_CREATED";
188     default:
189       return "Unknown";
190   }
191 }
192 
GetCLSIDNameStr(REFCLSID aCLSID)193 static nsCString GetCLSIDNameStr(REFCLSID aCLSID) {
194   LPOLESTR str = nullptr;
195   HRESULT hr = ::StringFromCLSID(aCLSID, &str);
196   if (FAILED(hr) || !str || !str[0]) {
197     return EmptyCString();
198   }
199 
200   nsCString result;
201   result = NS_ConvertUTF16toUTF8(str);
202   ::CoTaskMemFree(str);
203   return result;
204 }
205 
GetGUIDNameStr(REFGUID aGUID)206 static nsCString GetGUIDNameStr(REFGUID aGUID) {
207   OLECHAR str[40];
208   int len = ::StringFromGUID2(aGUID, str, ArrayLength(str));
209   if (!len || !str[0]) {
210     return EmptyCString();
211   }
212 
213   return NS_ConvertUTF16toUTF8(str);
214 }
215 
GetGUIDNameStrWithTable(REFGUID aGUID)216 static nsCString GetGUIDNameStrWithTable(REFGUID aGUID) {
217 #define RETURN_GUID_NAME(aNamedGUID)        \
218   if (IsEqualGUID(aGUID, aNamedGUID)) {     \
219     return NS_LITERAL_CSTRING(#aNamedGUID); \
220   }
221 
222   RETURN_GUID_NAME(GUID_PROP_INPUTSCOPE)
223   RETURN_GUID_NAME(TSATTRID_OTHERS)
224   RETURN_GUID_NAME(TSATTRID_Font)
225   RETURN_GUID_NAME(TSATTRID_Font_FaceName)
226   RETURN_GUID_NAME(TSATTRID_Font_SizePts)
227   RETURN_GUID_NAME(TSATTRID_Font_Style)
228   RETURN_GUID_NAME(TSATTRID_Font_Style_Bold)
229   RETURN_GUID_NAME(TSATTRID_Font_Style_Italic)
230   RETURN_GUID_NAME(TSATTRID_Font_Style_SmallCaps)
231   RETURN_GUID_NAME(TSATTRID_Font_Style_Capitalize)
232   RETURN_GUID_NAME(TSATTRID_Font_Style_Uppercase)
233   RETURN_GUID_NAME(TSATTRID_Font_Style_Lowercase)
234   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation)
235   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_LasVegasLights)
236   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_BlinkingBackground)
237   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_SparkleText)
238   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingBlackAnts)
239   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingRedAnts)
240   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_Shimmer)
241   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeDown)
242   RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeRight)
243   RETURN_GUID_NAME(TSATTRID_Font_Style_Emboss)
244   RETURN_GUID_NAME(TSATTRID_Font_Style_Engrave)
245   RETURN_GUID_NAME(TSATTRID_Font_Style_Hidden)
246   RETURN_GUID_NAME(TSATTRID_Font_Style_Kerning)
247   RETURN_GUID_NAME(TSATTRID_Font_Style_Outlined)
248   RETURN_GUID_NAME(TSATTRID_Font_Style_Position)
249   RETURN_GUID_NAME(TSATTRID_Font_Style_Protected)
250   RETURN_GUID_NAME(TSATTRID_Font_Style_Shadow)
251   RETURN_GUID_NAME(TSATTRID_Font_Style_Spacing)
252   RETURN_GUID_NAME(TSATTRID_Font_Style_Weight)
253   RETURN_GUID_NAME(TSATTRID_Font_Style_Height)
254   RETURN_GUID_NAME(TSATTRID_Font_Style_Underline)
255   RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Single)
256   RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Double)
257   RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough)
258   RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Single)
259   RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Double)
260   RETURN_GUID_NAME(TSATTRID_Font_Style_Overline)
261   RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Single)
262   RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Double)
263   RETURN_GUID_NAME(TSATTRID_Font_Style_Blink)
264   RETURN_GUID_NAME(TSATTRID_Font_Style_Subscript)
265   RETURN_GUID_NAME(TSATTRID_Font_Style_Superscript)
266   RETURN_GUID_NAME(TSATTRID_Font_Style_Color)
267   RETURN_GUID_NAME(TSATTRID_Font_Style_BackgroundColor)
268   RETURN_GUID_NAME(TSATTRID_Text)
269   RETURN_GUID_NAME(TSATTRID_Text_VerticalWriting)
270   RETURN_GUID_NAME(TSATTRID_Text_RightToLeft)
271   RETURN_GUID_NAME(TSATTRID_Text_Orientation)
272   RETURN_GUID_NAME(TSATTRID_Text_Language)
273   RETURN_GUID_NAME(TSATTRID_Text_ReadOnly)
274   RETURN_GUID_NAME(TSATTRID_Text_EmbeddedObject)
275   RETURN_GUID_NAME(TSATTRID_Text_Alignment)
276   RETURN_GUID_NAME(TSATTRID_Text_Alignment_Left)
277   RETURN_GUID_NAME(TSATTRID_Text_Alignment_Right)
278   RETURN_GUID_NAME(TSATTRID_Text_Alignment_Center)
279   RETURN_GUID_NAME(TSATTRID_Text_Alignment_Justify)
280   RETURN_GUID_NAME(TSATTRID_Text_Link)
281   RETURN_GUID_NAME(TSATTRID_Text_Hyphenation)
282   RETURN_GUID_NAME(TSATTRID_Text_Para)
283   RETURN_GUID_NAME(TSATTRID_Text_Para_FirstLineIndent)
284   RETURN_GUID_NAME(TSATTRID_Text_Para_LeftIndent)
285   RETURN_GUID_NAME(TSATTRID_Text_Para_RightIndent)
286   RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceAfter)
287   RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceBefore)
288   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing)
289   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Single)
290   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_OnePtFive)
291   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Double)
292   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_AtLeast)
293   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Exactly)
294   RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Multiple)
295   RETURN_GUID_NAME(TSATTRID_List)
296   RETURN_GUID_NAME(TSATTRID_List_LevelIndel)
297   RETURN_GUID_NAME(TSATTRID_List_Type)
298   RETURN_GUID_NAME(TSATTRID_List_Type_Bullet)
299   RETURN_GUID_NAME(TSATTRID_List_Type_Arabic)
300   RETURN_GUID_NAME(TSATTRID_List_Type_LowerLetter)
301   RETURN_GUID_NAME(TSATTRID_List_Type_UpperLetter)
302   RETURN_GUID_NAME(TSATTRID_List_Type_LowerRoman)
303   RETURN_GUID_NAME(TSATTRID_List_Type_UpperRoman)
304   RETURN_GUID_NAME(TSATTRID_App)
305   RETURN_GUID_NAME(TSATTRID_App_IncorrectSpelling)
306   RETURN_GUID_NAME(TSATTRID_App_IncorrectGrammar)
307 
308 #undef RETURN_GUID_NAME
309 
310   return GetGUIDNameStr(aGUID);
311 }
312 
GetRIIDNameStr(REFIID aRIID)313 static nsCString GetRIIDNameStr(REFIID aRIID) {
314   LPOLESTR str = nullptr;
315   HRESULT hr = ::StringFromIID(aRIID, &str);
316   if (FAILED(hr) || !str || !str[0]) {
317     return EmptyCString();
318   }
319 
320   nsAutoString key(L"Interface\\");
321   key += str;
322 
323   nsCString result;
324   wchar_t buf[256];
325   if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT, key.get(), nullptr, buf,
326                                sizeof(buf))) {
327     result = NS_ConvertUTF16toUTF8(buf);
328   } else {
329     result = NS_ConvertUTF16toUTF8(str);
330   }
331 
332   ::CoTaskMemFree(str);
333   return result;
334 }
335 
GetCommonReturnValueName(HRESULT aResult)336 static const char* GetCommonReturnValueName(HRESULT aResult) {
337   switch (aResult) {
338     case S_OK:
339       return "S_OK";
340     case E_ABORT:
341       return "E_ABORT";
342     case E_ACCESSDENIED:
343       return "E_ACCESSDENIED";
344     case E_FAIL:
345       return "E_FAIL";
346     case E_HANDLE:
347       return "E_HANDLE";
348     case E_INVALIDARG:
349       return "E_INVALIDARG";
350     case E_NOINTERFACE:
351       return "E_NOINTERFACE";
352     case E_NOTIMPL:
353       return "E_NOTIMPL";
354     case E_OUTOFMEMORY:
355       return "E_OUTOFMEMORY";
356     case E_POINTER:
357       return "E_POINTER";
358     case E_UNEXPECTED:
359       return "E_UNEXPECTED";
360     default:
361       return SUCCEEDED(aResult) ? "Succeeded" : "Failed";
362   }
363 }
364 
GetTextStoreReturnValueName(HRESULT aResult)365 static const char* GetTextStoreReturnValueName(HRESULT aResult) {
366   switch (aResult) {
367     case TS_E_FORMAT:
368       return "TS_E_FORMAT";
369     case TS_E_INVALIDPOINT:
370       return "TS_E_INVALIDPOINT";
371     case TS_E_INVALIDPOS:
372       return "TS_E_INVALIDPOS";
373     case TS_E_NOINTERFACE:
374       return "TS_E_NOINTERFACE";
375     case TS_E_NOLAYOUT:
376       return "TS_E_NOLAYOUT";
377     case TS_E_NOLOCK:
378       return "TS_E_NOLOCK";
379     case TS_E_NOOBJECT:
380       return "TS_E_NOOBJECT";
381     case TS_E_NOSELECTION:
382       return "TS_E_NOSELECTION";
383     case TS_E_NOSERVICE:
384       return "TS_E_NOSERVICE";
385     case TS_E_READONLY:
386       return "TS_E_READONLY";
387     case TS_E_SYNCHRONOUS:
388       return "TS_E_SYNCHRONOUS";
389     case TS_S_ASYNC:
390       return "TS_S_ASYNC";
391     default:
392       return GetCommonReturnValueName(aResult);
393   }
394 }
395 
GetSinkMaskNameStr(DWORD aSinkMask)396 static const nsCString GetSinkMaskNameStr(DWORD aSinkMask) {
397   nsCString description;
398   if (aSinkMask & TS_AS_TEXT_CHANGE) {
399     description.AppendLiteral("TS_AS_TEXT_CHANGE");
400   }
401   if (aSinkMask & TS_AS_SEL_CHANGE) {
402     HandleSeparator(description);
403     description.AppendLiteral("TS_AS_SEL_CHANGE");
404   }
405   if (aSinkMask & TS_AS_LAYOUT_CHANGE) {
406     HandleSeparator(description);
407     description.AppendLiteral("TS_AS_LAYOUT_CHANGE");
408   }
409   if (aSinkMask & TS_AS_ATTR_CHANGE) {
410     HandleSeparator(description);
411     description.AppendLiteral("TS_AS_ATTR_CHANGE");
412   }
413   if (aSinkMask & TS_AS_STATUS_CHANGE) {
414     HandleSeparator(description);
415     description.AppendLiteral("TS_AS_STATUS_CHANGE");
416   }
417   if (description.IsEmpty()) {
418     description.AppendLiteral("not-specified");
419   }
420   return description;
421 }
422 
GetActiveSelEndName(TsActiveSelEnd aSelEnd)423 static const char* GetActiveSelEndName(TsActiveSelEnd aSelEnd) {
424   return aSelEnd == TS_AE_NONE
425              ? "TS_AE_NONE"
426              : aSelEnd == TS_AE_START
427                    ? "TS_AE_START"
428                    : aSelEnd == TS_AE_END ? "TS_AE_END" : "Unknown";
429 }
430 
GetLockFlagNameStr(DWORD aLockFlags)431 static const nsCString GetLockFlagNameStr(DWORD aLockFlags) {
432   nsCString description;
433   if ((aLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE) {
434     description.AppendLiteral("TS_LF_READWRITE");
435   } else if (aLockFlags & TS_LF_READ) {
436     description.AppendLiteral("TS_LF_READ");
437   }
438   if (aLockFlags & TS_LF_SYNC) {
439     if (!description.IsEmpty()) {
440       description.AppendLiteral(" | ");
441     }
442     description.AppendLiteral("TS_LF_SYNC");
443   }
444   if (description.IsEmpty()) {
445     description.AppendLiteral("not-specified");
446   }
447   return description;
448 }
449 
GetTextRunTypeName(TsRunType aRunType)450 static const char* GetTextRunTypeName(TsRunType aRunType) {
451   switch (aRunType) {
452     case TS_RT_PLAIN:
453       return "TS_RT_PLAIN";
454     case TS_RT_HIDDEN:
455       return "TS_RT_HIDDEN";
456     case TS_RT_OPAQUE:
457       return "TS_RT_OPAQUE";
458     default:
459       return "Unknown";
460   }
461 }
462 
GetColorName(const TF_DA_COLOR & aColor)463 static nsCString GetColorName(const TF_DA_COLOR& aColor) {
464   switch (aColor.type) {
465     case TF_CT_NONE:
466       return NS_LITERAL_CSTRING("TF_CT_NONE");
467     case TF_CT_SYSCOLOR:
468       return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
469                              static_cast<int32_t>(aColor.nIndex));
470     case TF_CT_COLORREF:
471       return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
472                              static_cast<int32_t>(aColor.cr));
473       break;
474     default:
475       return nsPrintfCString("Unknown(%08X)",
476                              static_cast<int32_t>(aColor.type));
477   }
478 }
479 
GetLineStyleName(TF_DA_LINESTYLE aLineStyle)480 static nsCString GetLineStyleName(TF_DA_LINESTYLE aLineStyle) {
481   switch (aLineStyle) {
482     case TF_LS_NONE:
483       return NS_LITERAL_CSTRING("TF_LS_NONE");
484     case TF_LS_SOLID:
485       return NS_LITERAL_CSTRING("TF_LS_SOLID");
486     case TF_LS_DOT:
487       return NS_LITERAL_CSTRING("TF_LS_DOT");
488     case TF_LS_DASH:
489       return NS_LITERAL_CSTRING("TF_LS_DASH");
490     case TF_LS_SQUIGGLE:
491       return NS_LITERAL_CSTRING("TF_LS_SQUIGGLE");
492     default: {
493       return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle));
494     }
495   }
496 }
497 
GetClauseAttrName(TF_DA_ATTR_INFO aAttr)498 static nsCString GetClauseAttrName(TF_DA_ATTR_INFO aAttr) {
499   switch (aAttr) {
500     case TF_ATTR_INPUT:
501       return NS_LITERAL_CSTRING("TF_ATTR_INPUT");
502     case TF_ATTR_TARGET_CONVERTED:
503       return NS_LITERAL_CSTRING("TF_ATTR_TARGET_CONVERTED");
504     case TF_ATTR_CONVERTED:
505       return NS_LITERAL_CSTRING("TF_ATTR_CONVERTED");
506     case TF_ATTR_TARGET_NOTCONVERTED:
507       return NS_LITERAL_CSTRING("TF_ATTR_TARGET_NOTCONVERTED");
508     case TF_ATTR_INPUT_ERROR:
509       return NS_LITERAL_CSTRING("TF_ATTR_INPUT_ERROR");
510     case TF_ATTR_FIXEDCONVERTED:
511       return NS_LITERAL_CSTRING("TF_ATTR_FIXEDCONVERTED");
512     case TF_ATTR_OTHER:
513       return NS_LITERAL_CSTRING("TF_ATTR_OTHER");
514     default: {
515       return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr));
516     }
517   }
518 }
519 
GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE & aDispAttr)520 static nsCString GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE& aDispAttr) {
521   nsCString str;
522   str = "crText:{ ";
523   str += GetColorName(aDispAttr.crText);
524   str += " }, crBk:{ ";
525   str += GetColorName(aDispAttr.crBk);
526   str += " }, lsStyle: ";
527   str += GetLineStyleName(aDispAttr.lsStyle);
528   str += ", fBoldLine: ";
529   str += GetBoolName(aDispAttr.fBoldLine);
530   str += ", crLine:{ ";
531   str += GetColorName(aDispAttr.crLine);
532   str += " }, bAttr: ";
533   str += GetClauseAttrName(aDispAttr.bAttr);
534   return str;
535 }
536 
GetMouseButtonName(int16_t aButton)537 static const char* GetMouseButtonName(int16_t aButton) {
538   switch (aButton) {
539     case MouseButton::eLeft:
540       return "LeftButton";
541     case MouseButton::eMiddle:
542       return "MiddleButton";
543     case MouseButton::eRight:
544       return "RightButton";
545     default:
546       return "UnknownButton";
547   }
548 }
549 
550 #define ADD_SEPARATOR_IF_NECESSARY(aStr) \
551   if (!aStr.IsEmpty()) {                 \
552     aStr.AppendLiteral(", ");            \
553   }
554 
GetMouseButtonsName(int16_t aButtons)555 static nsCString GetMouseButtonsName(int16_t aButtons) {
556   if (!aButtons) {
557     return NS_LITERAL_CSTRING("no buttons");
558   }
559   nsCString names;
560   if (aButtons & MouseButtonsFlag::eLeftFlag) {
561     names = "LeftButton";
562   }
563   if (aButtons & MouseButtonsFlag::eRightFlag) {
564     ADD_SEPARATOR_IF_NECESSARY(names);
565     names += "RightButton";
566   }
567   if (aButtons & MouseButtonsFlag::eMiddleFlag) {
568     ADD_SEPARATOR_IF_NECESSARY(names);
569     names += "MiddleButton";
570   }
571   if (aButtons & MouseButtonsFlag::e4thFlag) {
572     ADD_SEPARATOR_IF_NECESSARY(names);
573     names += "4thButton";
574   }
575   if (aButtons & MouseButtonsFlag::e5thFlag) {
576     ADD_SEPARATOR_IF_NECESSARY(names);
577     names += "5thButton";
578   }
579   return names;
580 }
581 
GetModifiersName(Modifiers aModifiers)582 static nsCString GetModifiersName(Modifiers aModifiers) {
583   if (aModifiers == MODIFIER_NONE) {
584     return NS_LITERAL_CSTRING("no modifiers");
585   }
586   nsCString names;
587   if (aModifiers & MODIFIER_ALT) {
588     names = NS_DOM_KEYNAME_ALT;
589   }
590   if (aModifiers & MODIFIER_ALTGRAPH) {
591     ADD_SEPARATOR_IF_NECESSARY(names);
592     names += NS_DOM_KEYNAME_ALTGRAPH;
593   }
594   if (aModifiers & MODIFIER_CAPSLOCK) {
595     ADD_SEPARATOR_IF_NECESSARY(names);
596     names += NS_DOM_KEYNAME_CAPSLOCK;
597   }
598   if (aModifiers & MODIFIER_CONTROL) {
599     ADD_SEPARATOR_IF_NECESSARY(names);
600     names += NS_DOM_KEYNAME_CONTROL;
601   }
602   if (aModifiers & MODIFIER_FN) {
603     ADD_SEPARATOR_IF_NECESSARY(names);
604     names += NS_DOM_KEYNAME_FN;
605   }
606   if (aModifiers & MODIFIER_FNLOCK) {
607     ADD_SEPARATOR_IF_NECESSARY(names);
608     names += NS_DOM_KEYNAME_FNLOCK;
609   }
610   if (aModifiers & MODIFIER_META) {
611     ADD_SEPARATOR_IF_NECESSARY(names);
612     names += NS_DOM_KEYNAME_META;
613   }
614   if (aModifiers & MODIFIER_NUMLOCK) {
615     ADD_SEPARATOR_IF_NECESSARY(names);
616     names += NS_DOM_KEYNAME_NUMLOCK;
617   }
618   if (aModifiers & MODIFIER_SCROLLLOCK) {
619     ADD_SEPARATOR_IF_NECESSARY(names);
620     names += NS_DOM_KEYNAME_SCROLLLOCK;
621   }
622   if (aModifiers & MODIFIER_SHIFT) {
623     ADD_SEPARATOR_IF_NECESSARY(names);
624     names += NS_DOM_KEYNAME_SHIFT;
625   }
626   if (aModifiers & MODIFIER_SYMBOL) {
627     ADD_SEPARATOR_IF_NECESSARY(names);
628     names += NS_DOM_KEYNAME_SYMBOL;
629   }
630   if (aModifiers & MODIFIER_SYMBOLLOCK) {
631     ADD_SEPARATOR_IF_NECESSARY(names);
632     names += NS_DOM_KEYNAME_SYMBOLLOCK;
633   }
634   if (aModifiers & MODIFIER_OS) {
635     ADD_SEPARATOR_IF_NECESSARY(names);
636     names += NS_DOM_KEYNAME_OS;
637   }
638   return names;
639 }
640 
641 class GetWritingModeName : public nsAutoCString {
642  public:
GetWritingModeName(const WritingMode & aWritingMode)643   explicit GetWritingModeName(const WritingMode& aWritingMode) {
644     if (!aWritingMode.IsVertical()) {
645       AssignLiteral("Horizontal");
646       return;
647     }
648     if (aWritingMode.IsVerticalLR()) {
649       AssignLiteral("Vertical (LR)");
650       return;
651     }
652     AssignLiteral("Vertical (RL)");
653   }
~GetWritingModeName()654   virtual ~GetWritingModeName() {}
655 };
656 
657 class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8 {
658  public:
GetEscapedUTF8String(const nsAString & aString)659   explicit GetEscapedUTF8String(const nsAString& aString)
660       : NS_ConvertUTF16toUTF8(aString) {
661     Escape();
662   }
GetEscapedUTF8String(const char16ptr_t aString)663   explicit GetEscapedUTF8String(const char16ptr_t aString)
664       : NS_ConvertUTF16toUTF8(aString) {
665     Escape();
666   }
GetEscapedUTF8String(const char16ptr_t aString,uint32_t aLength)667   GetEscapedUTF8String(const char16ptr_t aString, uint32_t aLength)
668       : NS_ConvertUTF16toUTF8(aString, aLength) {
669     Escape();
670   }
671 
672  private:
Escape()673   void Escape() {
674     ReplaceSubstring("\r", "\\r");
675     ReplaceSubstring("\n", "\\n");
676     ReplaceSubstring("\t", "\\t");
677   }
678 };
679 
680 class GetIMEStateString : public nsAutoCString {
681  public:
GetIMEStateString(const IMEState & aIMEState)682   explicit GetIMEStateString(const IMEState& aIMEState) {
683     AppendLiteral("{ mEnabled=");
684     switch (aIMEState.mEnabled) {
685       case IMEState::DISABLED:
686         AppendLiteral("DISABLED");
687         break;
688       case IMEState::ENABLED:
689         AppendLiteral("ENABLED");
690         break;
691       case IMEState::PASSWORD:
692         AppendLiteral("PASSWORD");
693         break;
694       case IMEState::PLUGIN:
695         AppendLiteral("PLUGIN");
696         break;
697       case IMEState::UNKNOWN:
698         AppendLiteral("UNKNOWN");
699         break;
700       default:
701         AppendPrintf("Unknown value (%d)", aIMEState.mEnabled);
702         break;
703     }
704     AppendLiteral(", mOpen=");
705     switch (aIMEState.mOpen) {
706       case IMEState::OPEN_STATE_NOT_SUPPORTED:
707         AppendLiteral("OPEN_STATE_NOT_SUPPORTED or DONT_CHANGE_OPEN_STATE");
708         break;
709       case IMEState::OPEN:
710         AppendLiteral("OPEN");
711         break;
712       case IMEState::CLOSED:
713         AppendLiteral("CLOSED");
714         break;
715       default:
716         AppendPrintf("Unknown value (%d)", aIMEState.mOpen);
717         break;
718     }
719     AppendLiteral(" }");
720   }
721 };
722 
723 class GetInputContextString : public nsAutoCString {
724  public:
GetInputContextString(const InputContext & aInputContext)725   explicit GetInputContextString(const InputContext& aInputContext) {
726     AppendPrintf("{ mIMEState=%s, ",
727                  GetIMEStateString(aInputContext.mIMEState).get());
728     AppendLiteral("mOrigin=");
729     switch (aInputContext.mOrigin) {
730       case InputContext::ORIGIN_MAIN:
731         AppendLiteral("ORIGIN_MAIN");
732         break;
733       case InputContext::ORIGIN_CONTENT:
734         AppendLiteral("ORIGIN_CONTENT");
735         break;
736       default:
737         AppendPrintf("Unknown value (%d)", aInputContext.mOrigin);
738         break;
739     }
740     AppendPrintf(
741         ", mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", "
742         "mActionHint=\"%s\", mMayBeIMEUnaware=%s }",
743         NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
744         NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
745         NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
746         GetBoolName(aInputContext.mMayBeIMEUnaware));
747   }
748 };
749 
750 class GetInputScopeString : public nsAutoCString {
751  public:
GetInputScopeString(const nsTArray<InputScope> & aList)752   explicit GetInputScopeString(const nsTArray<InputScope>& aList) {
753     for (InputScope inputScope : aList) {
754       if (!IsEmpty()) {
755         AppendLiteral(", ");
756       }
757       switch (inputScope) {
758         case IS_DEFAULT:
759           AppendLiteral("IS_DEFAULT");
760           break;
761         case IS_URL:
762           AppendLiteral("IS_URL");
763           break;
764         case IS_FILE_FULLFILEPATH:
765           AppendLiteral("IS_FILE_FULLFILEPATH");
766           break;
767         case IS_FILE_FILENAME:
768           AppendLiteral("IS_FILE_FILENAME");
769           break;
770         case IS_EMAIL_USERNAME:
771           AppendLiteral("IS_EMAIL_USERNAME");
772           break;
773         case IS_EMAIL_SMTPEMAILADDRESS:
774           AppendLiteral("IS_EMAIL_SMTPEMAILADDRESS");
775           break;
776         case IS_LOGINNAME:
777           AppendLiteral("IS_LOGINNAME");
778           break;
779         case IS_PERSONALNAME_FULLNAME:
780           AppendLiteral("IS_PERSONALNAME_FULLNAME");
781           break;
782         case IS_PERSONALNAME_PREFIX:
783           AppendLiteral("IS_PERSONALNAME_PREFIX");
784           break;
785         case IS_PERSONALNAME_GIVENNAME:
786           AppendLiteral("IS_PERSONALNAME_GIVENNAME");
787           break;
788         case IS_PERSONALNAME_MIDDLENAME:
789           AppendLiteral("IS_PERSONALNAME_MIDDLENAME");
790           break;
791         case IS_PERSONALNAME_SURNAME:
792           AppendLiteral("IS_PERSONALNAME_SURNAME");
793           break;
794         case IS_PERSONALNAME_SUFFIX:
795           AppendLiteral("IS_PERSONALNAME_SUFFIX");
796           break;
797         case IS_ADDRESS_FULLPOSTALADDRESS:
798           AppendLiteral("IS_ADDRESS_FULLPOSTALADDRESS");
799           break;
800         case IS_ADDRESS_POSTALCODE:
801           AppendLiteral("IS_ADDRESS_POSTALCODE");
802           break;
803         case IS_ADDRESS_STREET:
804           AppendLiteral("IS_ADDRESS_STREET");
805           break;
806         case IS_ADDRESS_STATEORPROVINCE:
807           AppendLiteral("IS_ADDRESS_STATEORPROVINCE");
808           break;
809         case IS_ADDRESS_CITY:
810           AppendLiteral("IS_ADDRESS_CITY");
811           break;
812         case IS_ADDRESS_COUNTRYNAME:
813           AppendLiteral("IS_ADDRESS_COUNTRYNAME");
814           break;
815         case IS_ADDRESS_COUNTRYSHORTNAME:
816           AppendLiteral("IS_ADDRESS_COUNTRYSHORTNAME");
817           break;
818         case IS_CURRENCY_AMOUNTANDSYMBOL:
819           AppendLiteral("IS_CURRENCY_AMOUNTANDSYMBOL");
820           break;
821         case IS_CURRENCY_AMOUNT:
822           AppendLiteral("IS_CURRENCY_AMOUNT");
823           break;
824         case IS_DATE_FULLDATE:
825           AppendLiteral("IS_DATE_FULLDATE");
826           break;
827         case IS_DATE_MONTH:
828           AppendLiteral("IS_DATE_MONTH");
829           break;
830         case IS_DATE_DAY:
831           AppendLiteral("IS_DATE_DAY");
832           break;
833         case IS_DATE_YEAR:
834           AppendLiteral("IS_DATE_YEAR");
835           break;
836         case IS_DATE_MONTHNAME:
837           AppendLiteral("IS_DATE_MONTHNAME");
838           break;
839         case IS_DATE_DAYNAME:
840           AppendLiteral("IS_DATE_DAYNAME");
841           break;
842         case IS_DIGITS:
843           AppendLiteral("IS_DIGITS");
844           break;
845         case IS_NUMBER:
846           AppendLiteral("IS_NUMBER");
847           break;
848         case IS_ONECHAR:
849           AppendLiteral("IS_ONECHAR");
850           break;
851         case IS_PASSWORD:
852           AppendLiteral("IS_PASSWORD");
853           break;
854         case IS_TELEPHONE_FULLTELEPHONENUMBER:
855           AppendLiteral("IS_TELEPHONE_FULLTELEPHONENUMBER");
856           break;
857         case IS_TELEPHONE_COUNTRYCODE:
858           AppendLiteral("IS_TELEPHONE_COUNTRYCODE");
859           break;
860         case IS_TELEPHONE_AREACODE:
861           AppendLiteral("IS_TELEPHONE_AREACODE");
862           break;
863         case IS_TELEPHONE_LOCALNUMBER:
864           AppendLiteral("IS_TELEPHONE_LOCALNUMBER");
865           break;
866         case IS_TIME_FULLTIME:
867           AppendLiteral("IS_TIME_FULLTIME");
868           break;
869         case IS_TIME_HOUR:
870           AppendLiteral("IS_TIME_HOUR");
871           break;
872         case IS_TIME_MINORSEC:
873           AppendLiteral("IS_TIME_MINORSEC");
874           break;
875         case IS_NUMBER_FULLWIDTH:
876           AppendLiteral("IS_NUMBER_FULLWIDTH");
877           break;
878         case IS_ALPHANUMERIC_HALFWIDTH:
879           AppendLiteral("IS_ALPHANUMERIC_HALFWIDTH");
880           break;
881         case IS_ALPHANUMERIC_FULLWIDTH:
882           AppendLiteral("IS_ALPHANUMERIC_FULLWIDTH");
883           break;
884         case IS_CURRENCY_CHINESE:
885           AppendLiteral("IS_CURRENCY_CHINESE");
886           break;
887         case IS_BOPOMOFO:
888           AppendLiteral("IS_BOPOMOFO");
889           break;
890         case IS_HIRAGANA:
891           AppendLiteral("IS_HIRAGANA");
892           break;
893         case IS_KATAKANA_HALFWIDTH:
894           AppendLiteral("IS_KATAKANA_HALFWIDTH");
895           break;
896         case IS_KATAKANA_FULLWIDTH:
897           AppendLiteral("IS_KATAKANA_FULLWIDTH");
898           break;
899         case IS_HANJA:
900           AppendLiteral("IS_HANJA");
901           break;
902         case IS_PHRASELIST:
903           AppendLiteral("IS_PHRASELIST");
904           break;
905         case IS_REGULAREXPRESSION:
906           AppendLiteral("IS_REGULAREXPRESSION");
907           break;
908         case IS_SRGS:
909           AppendLiteral("IS_SRGS");
910           break;
911         case IS_XML:
912           AppendLiteral("IS_XML");
913           break;
914         case IS_PRIVATE:
915           AppendLiteral("IS_PRIVATE");
916           break;
917         default:
918           AppendPrintf("Unknown Value(%d)", inputScope);
919           break;
920       }
921     }
922   }
923 };
924 
925 /******************************************************************/
926 /* InputScopeImpl                                                 */
927 /******************************************************************/
928 
929 class InputScopeImpl final : public ITfInputScope {
~InputScopeImpl()930   ~InputScopeImpl() {}
931 
932  public:
InputScopeImpl(const nsTArray<InputScope> & aList)933   explicit InputScopeImpl(const nsTArray<InputScope>& aList)
934       : mInputScopes(aList.Clone()) {
935     MOZ_LOG(
936         sTextStoreLog, LogLevel::Info,
937         ("0x%p InputScopeImpl(%s)", this, GetInputScopeString(aList).get()));
938   }
939 
NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl)940   NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl)
941 
942   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
943     *ppv = nullptr;
944     if ((IID_IUnknown == riid) || (IID_ITfInputScope == riid)) {
945       *ppv = static_cast<ITfInputScope*>(this);
946     }
947     if (*ppv) {
948       AddRef();
949       return S_OK;
950     }
951     return E_NOINTERFACE;
952   }
953 
GetInputScopes(InputScope ** pprgInputScopes,UINT * pcCount)954   STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) {
955     uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length());
956 
957     InputScope* pScope =
958         (InputScope*)CoTaskMemAlloc(sizeof(InputScope) * count);
959     NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY);
960 
961     if (mInputScopes.IsEmpty()) {
962       *pScope = IS_DEFAULT;
963       *pcCount = 1;
964       *pprgInputScopes = pScope;
965       return S_OK;
966     }
967 
968     *pcCount = 0;
969 
970     for (uint32_t idx = 0; idx < count; idx++) {
971       *(pScope + idx) = mInputScopes[idx];
972       (*pcCount)++;
973     }
974 
975     *pprgInputScopes = pScope;
976     return S_OK;
977   }
978 
GetPhrase(BSTR ** ppbstrPhrases,UINT * pcCount)979   STDMETHODIMP GetPhrase(BSTR** ppbstrPhrases, UINT* pcCount) {
980     return E_NOTIMPL;
981   }
GetRegularExpression(BSTR * pbstrRegExp)982   STDMETHODIMP GetRegularExpression(BSTR* pbstrRegExp) { return E_NOTIMPL; }
GetSRGS(BSTR * pbstrSRGS)983   STDMETHODIMP GetSRGS(BSTR* pbstrSRGS) { return E_NOTIMPL; }
GetXML(BSTR * pbstrXML)984   STDMETHODIMP GetXML(BSTR* pbstrXML) { return E_NOTIMPL; }
985 
986  private:
987   nsTArray<InputScope> mInputScopes;
988 };
989 
990 /******************************************************************/
991 /* TSFStaticSink                                                  */
992 /******************************************************************/
993 
994 class TSFStaticSink final : public ITfInputProcessorProfileActivationSink {
995  public:
GetInstance()996   static TSFStaticSink* GetInstance() {
997     if (!sInstance) {
998       RefPtr<ITfThreadMgr> threadMgr = TSFTextStore::GetThreadMgr();
999       if (NS_WARN_IF(!threadMgr)) {
1000         MOZ_LOG(
1001             sTextStoreLog, LogLevel::Error,
1002             ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
1003              "instance due to no ThreadMgr instance"));
1004         return nullptr;
1005       }
1006       RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
1007           TSFTextStore::GetInputProcessorProfiles();
1008       if (NS_WARN_IF(!inputProcessorProfiles)) {
1009         MOZ_LOG(
1010             sTextStoreLog, LogLevel::Error,
1011             ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
1012              "instance due to no InputProcessorProfiles instance"));
1013         return nullptr;
1014       }
1015       RefPtr<TSFStaticSink> staticSink = new TSFStaticSink();
1016       if (NS_WARN_IF(!staticSink->Init(threadMgr, inputProcessorProfiles))) {
1017         staticSink->Destroy();
1018         MOZ_LOG(
1019             sTextStoreLog, LogLevel::Error,
1020             ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
1021              "instance"));
1022         return nullptr;
1023       }
1024       sInstance = staticSink.forget();
1025     }
1026     return sInstance;
1027   }
1028 
Shutdown()1029   static void Shutdown() {
1030     if (sInstance) {
1031       sInstance->Destroy();
1032       sInstance = nullptr;
1033     }
1034   }
1035 
1036   bool Init(ITfThreadMgr* aThreadMgr,
1037             ITfInputProcessorProfiles* aInputProcessorProfiles);
QueryInterface(REFIID riid,void ** ppv)1038   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) {
1039     *ppv = nullptr;
1040     if (IID_IUnknown == riid ||
1041         IID_ITfInputProcessorProfileActivationSink == riid) {
1042       *ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this);
1043     }
1044     if (*ppv) {
1045       AddRef();
1046       return S_OK;
1047     }
1048     return E_NOINTERFACE;
1049   }
1050 
NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink) const1051   NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink)
1052 
1053   const nsString& GetActiveTIPKeyboardDescription() const {
1054     return mActiveTIPKeyboardDescription;
1055   }
1056 
IsIMM_IMEActive()1057   static bool IsIMM_IMEActive() {
1058     // Use IMM API until TSFStaticSink starts to work.
1059     if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
1060       return IsIMM_IME(::GetKeyboardLayout(0));
1061     }
1062     return sInstance->mIsIMM_IME;
1063   }
1064 
IsIMM_IME(HKL aHKL)1065   static bool IsIMM_IME(HKL aHKL) {
1066     return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0);
1067   }
1068 
IsTraditionalChinese()1069   static bool IsTraditionalChinese() {
1070     EnsureInstance();
1071     return sInstance && sInstance->IsTraditionalChineseInternal();
1072   }
IsSimplifiedChinese()1073   static bool IsSimplifiedChinese() {
1074     EnsureInstance();
1075     return sInstance && sInstance->IsSimplifiedChineseInternal();
1076   }
IsJapanese()1077   static bool IsJapanese() {
1078     EnsureInstance();
1079     return sInstance && sInstance->IsJapaneseInternal();
1080   }
IsKorean()1081   static bool IsKorean() {
1082     EnsureInstance();
1083     return sInstance && sInstance->IsKoreanInternal();
1084   }
1085 
1086   /**
1087    * ActiveTIP() returns an ID for currently active TIP.
1088    * Please note that this method is expensive due to needs a lot of GUID
1089    * comparations if active language ID is one of CJKT.  If you need to
1090    * check TIPs for a specific language, you should check current language
1091    * first.
1092    */
ActiveTIP()1093   static TextInputProcessorID ActiveTIP() {
1094     EnsureInstance();
1095     if (!sInstance || !sInstance->EnsureInitActiveTIPKeyboard()) {
1096       return TextInputProcessorID::eUnknown;
1097     }
1098     sInstance->ComputeActiveTextInputProcessor();
1099     if (NS_WARN_IF(sInstance->mActiveTIP ==
1100                    TextInputProcessorID::eNotComputed)) {
1101       return TextInputProcessorID::eUnknown;
1102     }
1103     return sInstance->mActiveTIP;
1104   }
1105 
IsMSChangJieOrMSQuickActive()1106   static bool IsMSChangJieOrMSQuickActive() {
1107     // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1108     // For avoiding unnecessary computation, we should check if the language
1109     // for current TIP is Traditional Chinese.
1110     if (!IsTraditionalChinese()) {
1111       return false;
1112     }
1113     switch (ActiveTIP()) {
1114       case TextInputProcessorID::eMicrosoftChangJie:
1115       case TextInputProcessorID::eMicrosoftQuick:
1116         return true;
1117       default:
1118         return false;
1119     }
1120   }
1121 
IsMSPinyinOrMSWubiActive()1122   static bool IsMSPinyinOrMSWubiActive() {
1123     // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1124     // For avoiding unnecessary computation, we should check if the language
1125     // for current TIP is Simplified Chinese.
1126     if (!IsSimplifiedChinese()) {
1127       return false;
1128     }
1129     switch (ActiveTIP()) {
1130       case TextInputProcessorID::eMicrosoftPinyin:
1131       case TextInputProcessorID::eMicrosoftWubi:
1132         return true;
1133       default:
1134         return false;
1135     }
1136   }
1137 
IsMSJapaneseIMEActive()1138   static bool IsMSJapaneseIMEActive() {
1139     // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1140     // For avoiding unnecessary computation, we should check if the language
1141     // for current TIP is Japanese.
1142     if (!IsJapanese()) {
1143       return false;
1144     }
1145     return ActiveTIP() == TextInputProcessorID::eMicrosoftIMEForJapanese;
1146   }
1147 
IsGoogleJapaneseInputActive()1148   static bool IsGoogleJapaneseInputActive() {
1149     // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1150     // For avoiding unnecessary computation, we should check if the language
1151     // for current TIP is Japanese.
1152     if (!IsJapanese()) {
1153       return false;
1154     }
1155     return ActiveTIP() == TextInputProcessorID::eGoogleJapaneseInput;
1156   }
1157 
IsATOKActive()1158   static bool IsATOKActive() {
1159     // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1160     // For avoiding unnecessary computation, we should check if active TIP is
1161     // ATOK first since it's cheaper.
1162     return IsJapanese() && sInstance->IsATOKActiveInternal();
1163   }
1164 
1165   // Note that ATOK 2011 - 2016 refers native caret position for deciding its
1166   // popup window position.
IsATOKReferringNativeCaretActive()1167   static bool IsATOKReferringNativeCaretActive() {
1168     // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1169     // For avoiding unnecessary computation, we should check if active TIP is
1170     // ATOK first since it's cheaper.
1171     if (!IsJapanese() || !sInstance->IsATOKActiveInternal()) {
1172       return false;
1173     }
1174     switch (ActiveTIP()) {
1175       case TextInputProcessorID::eATOK2011:
1176       case TextInputProcessorID::eATOK2012:
1177       case TextInputProcessorID::eATOK2013:
1178       case TextInputProcessorID::eATOK2014:
1179       case TextInputProcessorID::eATOK2015:
1180         return true;
1181       default:
1182         return false;
1183     }
1184   }
1185 
1186  private:
EnsureInstance()1187   static void EnsureInstance() {
1188     if (!sInstance) {
1189       RefPtr<TSFStaticSink> staticSink = GetInstance();
1190       Unused << staticSink;
1191     }
1192   }
1193 
IsTraditionalChineseInternal() const1194   bool IsTraditionalChineseInternal() const { return mLangID == 0x0404; }
IsSimplifiedChineseInternal() const1195   bool IsSimplifiedChineseInternal() const { return mLangID == 0x0804; }
IsJapaneseInternal() const1196   bool IsJapaneseInternal() const { return mLangID == 0x0411; }
IsKoreanInternal() const1197   bool IsKoreanInternal() const { return mLangID == 0x0412; }
1198 
IsATOKActiveInternal()1199   bool IsATOKActiveInternal() {
1200     EnsureInitActiveTIPKeyboard();
1201     // FYI: Name of packaged ATOK includes the release year like "ATOK 2015".
1202     //      Name of ATOK Passport (subscription) equals "ATOK".
1203     return StringBeginsWith(mActiveTIPKeyboardDescription,
1204                             NS_LITERAL_STRING("ATOK ")) ||
1205            mActiveTIPKeyboardDescription.EqualsLiteral("ATOK");
1206   }
1207 
ComputeActiveTextInputProcessor()1208   void ComputeActiveTextInputProcessor() {
1209     if (mActiveTIP != TextInputProcessorID::eNotComputed) {
1210       return;
1211     }
1212 
1213     if (mActiveTIPGUID == GUID_NULL) {
1214       mActiveTIP = TextInputProcessorID::eNone;
1215       return;
1216     }
1217 
1218     // Comparing GUID is slow. So, we should use language information to
1219     // reduce the comparing cost for TIP which is not we do not support
1220     // specifically since they are always compared with all supported TIPs.
1221     switch (mLangID) {
1222       case 0x0404:
1223         mActiveTIP = ComputeActiveTIPAsTraditionalChinese();
1224         break;
1225       case 0x0411:
1226         mActiveTIP = ComputeActiveTIPAsJapanese();
1227         break;
1228       case 0x0412:
1229         mActiveTIP = ComputeActiveTIPAsKorean();
1230         break;
1231       case 0x0804:
1232         mActiveTIP = ComputeActiveTIPAsSimplifiedChinese();
1233         break;
1234       default:
1235         mActiveTIP = TextInputProcessorID::eUnknown;
1236     }
1237   }
1238 
ComputeActiveTIPAsJapanese()1239   TextInputProcessorID ComputeActiveTIPAsJapanese() {
1240     // {A76C93D9-5523-4E90-AAFA-4DB112F9AC76} (Win7, Win8.1, Win10)
1241     static const GUID kMicrosoftIMEForJapaneseGUID = {
1242         0xA76C93D9,
1243         0x5523,
1244         0x4E90,
1245         {0xAA, 0xFA, 0x4D, 0xB1, 0x12, 0xF9, 0xAC, 0x76}};
1246     if (mActiveTIPGUID == kMicrosoftIMEForJapaneseGUID) {
1247       return TextInputProcessorID::eMicrosoftIMEForJapanese;
1248     }
1249     // {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
1250     static const GUID kMicrosoftOfficeIME2010ForJapaneseGUID = {
1251         0x54EDCC94,
1252         0x1524,
1253         0x4BB1,
1254         {0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64}};
1255     if (mActiveTIPGUID == kMicrosoftOfficeIME2010ForJapaneseGUID) {
1256       return TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese;
1257     }
1258     // {773EB24E-CA1D-4B1B-B420-FA985BB0B80D}
1259     static const GUID kGoogleJapaneseInputGUID = {
1260         0x773EB24E,
1261         0xCA1D,
1262         0x4B1B,
1263         {0xB4, 0x20, 0xFA, 0x98, 0x5B, 0xB0, 0xB8, 0x0D}};
1264     if (mActiveTIPGUID == kGoogleJapaneseInputGUID) {
1265       return TextInputProcessorID::eGoogleJapaneseInput;
1266     }
1267     // {F9C24A5C-8A53-499D-9572-93B2FF582115}
1268     static const GUID kATOK2011GUID = {
1269         0xF9C24A5C,
1270         0x8A53,
1271         0x499D,
1272         {0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15}};
1273     if (mActiveTIPGUID == kATOK2011GUID) {
1274       return TextInputProcessorID::eATOK2011;
1275     }
1276     // {1DE01562-F445-401B-B6C3-E5B18DB79461}
1277     static const GUID kATOK2012GUID = {
1278         0x1DE01562,
1279         0xF445,
1280         0x401B,
1281         {0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61}};
1282     if (mActiveTIPGUID == kATOK2012GUID) {
1283       return TextInputProcessorID::eATOK2012;
1284     }
1285     // {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
1286     static const GUID kATOK2013GUID = {
1287         0x3C4DB511,
1288         0x189A,
1289         0x4168,
1290         {0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15}};
1291     if (mActiveTIPGUID == kATOK2013GUID) {
1292       return TextInputProcessorID::eATOK2013;
1293     }
1294     // {4EF33B79-6AA9-4271-B4BF-9321C279381B}
1295     static const GUID kATOK2014GUID = {
1296         0x4EF33B79,
1297         0x6AA9,
1298         0x4271,
1299         {0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B}};
1300     if (mActiveTIPGUID == kATOK2014GUID) {
1301       return TextInputProcessorID::eATOK2014;
1302     }
1303     // {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
1304     static const GUID kATOK2015GUID = {
1305         0xEAB4DC00,
1306         0xCE2E,
1307         0x483D,
1308         {0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A}};
1309     if (mActiveTIPGUID == kATOK2015GUID) {
1310       return TextInputProcessorID::eATOK2015;
1311     }
1312     // {0B557B4C-5740-4110-A60A-1493FA10BF2B}
1313     static const GUID kATOK2016GUID = {
1314         0x0B557B4C,
1315         0x5740,
1316         0x4110,
1317         {0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B}};
1318     if (mActiveTIPGUID == kATOK2016GUID) {
1319       return TextInputProcessorID::eATOK2016;
1320     }
1321 
1322     // * ATOK 2017
1323     //   - {6DBFD8F5-701D-11E6-920F-782BCBA6348F}
1324     // * ATOK Passport (confirmed with version 31.1.2)
1325     //   - {A38F2FD9-7199-45E1-841C-BE0313D8052F}
1326 
1327     if (IsATOKActiveInternal()) {
1328       return TextInputProcessorID::eATOKUnknown;
1329     }
1330 
1331     // {E6D66705-1EDA-4373-8D01-1D0CB2D054C7}
1332     static const GUID kJapanist10GUID = {
1333         0xE6D66705,
1334         0x1EDA,
1335         0x4373,
1336         {0x8D, 0x01, 0x1D, 0x0C, 0xB2, 0xD0, 0x54, 0xC7}};
1337     if (mActiveTIPGUID == kJapanist10GUID) {
1338       return TextInputProcessorID::eJapanist10;
1339     }
1340 
1341     return TextInputProcessorID::eUnknown;
1342   }
1343 
ComputeActiveTIPAsTraditionalChinese()1344   TextInputProcessorID ComputeActiveTIPAsTraditionalChinese() {
1345     // {B2F9C502-1742-11D4-9790-0080C882687E} (Win8.1, Win10)
1346     static const GUID kMicrosoftBopomofoGUID = {
1347         0xB2F9C502,
1348         0x1742,
1349         0x11D4,
1350         {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1351     if (mActiveTIPGUID == kMicrosoftBopomofoGUID) {
1352       return TextInputProcessorID::eMicrosoftBopomofo;
1353     }
1354     // {4BDF9F03-C7D3-11D4-B2AB-0080C882687E} (Win7, Win8.1, Win10)
1355     static const GUID kMicrosoftChangJieGUID = {
1356         0x4BDF9F03,
1357         0xC7D3,
1358         0x11D4,
1359         {0xB2, 0xAB, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1360     if (mActiveTIPGUID == kMicrosoftChangJieGUID) {
1361       return TextInputProcessorID::eMicrosoftChangJie;
1362     }
1363     // {761309DE-317A-11D4-9B5D-0080C882687E} (Win7)
1364     static const GUID kMicrosoftPhoneticGUID = {
1365         0x761309DE,
1366         0x317A,
1367         0x11D4,
1368         {0x9B, 0x5D, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1369     if (mActiveTIPGUID == kMicrosoftPhoneticGUID) {
1370       return TextInputProcessorID::eMicrosoftPhonetic;
1371     }
1372     // {6024B45F-5C54-11D4-B921-0080C882687E} (Win7, Win8.1, Win10)
1373     static const GUID kMicrosoftQuickGUID = {
1374         0x6024B45F,
1375         0x5C54,
1376         0x11D4,
1377         {0xB9, 0x21, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1378     if (mActiveTIPGUID == kMicrosoftQuickGUID) {
1379       return TextInputProcessorID::eMicrosoftQuick;
1380     }
1381     // {F3BA907A-6C7E-11D4-97FA-0080C882687E} (Win7)
1382     static const GUID kMicrosoftNewChangJieGUID = {
1383         0xF3BA907A,
1384         0x6C7E,
1385         0x11D4,
1386         {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1387     if (mActiveTIPGUID == kMicrosoftNewChangJieGUID) {
1388       return TextInputProcessorID::eMicrosoftNewChangJie;
1389     }
1390     // {B2F9C502-1742-11D4-9790-0080C882687E} (Win7)
1391     static const GUID kMicrosoftNewPhoneticGUID = {
1392         0xB2F9C502,
1393         0x1742,
1394         0x11D4,
1395         {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1396     if (mActiveTIPGUID == kMicrosoftNewPhoneticGUID) {
1397       return TextInputProcessorID::eMicrosoftNewPhonetic;
1398     }
1399     // {0B883BA0-C1C7-11D4-87F9-0080C882687E} (Win7)
1400     static const GUID kMicrosoftNewQuickGUID = {
1401         0x0B883BA0,
1402         0xC1C7,
1403         0x11D4,
1404         {0x87, 0xF9, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1405     if (mActiveTIPGUID == kMicrosoftNewQuickGUID) {
1406       return TextInputProcessorID::eMicrosoftNewQuick;
1407     }
1408 
1409     // NOTE: There are some other Traditional Chinese TIPs installed in Windows:
1410     // * Chinese Traditional Array (version 6.0)
1411     //   - {D38EFF65-AA46-4FD5-91A7-67845FB02F5B} (Win7, Win8.1)
1412     // * Chinese Traditional DaYi (version 6.0)
1413     //   - {037B2C25-480C-4D7F-B027-D6CA6B69788A} (Win7, Win8.1)
1414 
1415     // {B58630B5-0ED3-4335-BBC9-E77BBCB43CAD}
1416     static const GUID kFreeChangJieGUID = {
1417         0xB58630B5,
1418         0x0ED3,
1419         0x4335,
1420         {0xBB, 0xC9, 0xE7, 0x7B, 0xBC, 0xB4, 0x3C, 0xAD}};
1421     if (mActiveTIPGUID == kFreeChangJieGUID) {
1422       return TextInputProcessorID::eFreeChangJie;
1423     }
1424 
1425     return TextInputProcessorID::eUnknown;
1426   }
1427 
ComputeActiveTIPAsSimplifiedChinese()1428   TextInputProcessorID ComputeActiveTIPAsSimplifiedChinese() {
1429     // FYI: This matches with neither "Microsoft Pinyin ABC Input Style" nor
1430     //      "Microsoft Pinyin New Experience Input Style" on Win7.
1431     // {FA550B04-5AD7-411F-A5AC-CA038EC515D7} (Win8.1, Win10)
1432     static const GUID kMicrosoftPinyinGUID = {
1433         0xFA550B04,
1434         0x5AD7,
1435         0x411F,
1436         {0xA5, 0xAC, 0xCA, 0x03, 0x8E, 0xC5, 0x15, 0xD7}};
1437     if (mActiveTIPGUID == kMicrosoftPinyinGUID) {
1438       return TextInputProcessorID::eMicrosoftPinyin;
1439     }
1440 
1441     // {F3BA9077-6C7E-11D4-97FA-0080C882687E} (Win7)
1442     static const GUID kMicrosoftPinyinNewExperienceInputStyleGUID = {
1443         0xF3BA9077,
1444         0x6C7E,
1445         0x11D4,
1446         {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1447     if (mActiveTIPGUID == kMicrosoftPinyinNewExperienceInputStyleGUID) {
1448       return TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle;
1449     }
1450     // {82590C13-F4DD-44F4-BA1D-8667246FDF8E} (Win8.1, Win10)
1451     static const GUID kMicrosoftWubiGUID = {
1452         0x82590C13,
1453         0xF4DD,
1454         0x44F4,
1455         {0xBA, 0x1D, 0x86, 0x67, 0x24, 0x6F, 0xDF, 0x8E}};
1456     if (mActiveTIPGUID == kMicrosoftWubiGUID) {
1457       return TextInputProcessorID::eMicrosoftWubi;
1458     }
1459     // NOTE: There are some other Simplified Chinese TIPs installed in Windows:
1460     // * Chinese Simplified QuanPin (version 6.0)
1461     //   - {54FC610E-6ABD-4685-9DDD-A130BDF1B170} (Win8.1)
1462     // * Chinese Simplified ZhengMa (version 6.0)
1463     //   - {733B4D81-3BC3-4132-B91A-E9CDD5E2BFC9} (Win8.1)
1464     // * Chinese Simplified ShuangPin (version 6.0)
1465     //   - {EF63706D-31C4-490E-9DBB-BD150ADC454B} (Win8.1)
1466     // * Microsoft Pinyin ABC Input Style
1467     //   - {FCA121D2-8C6D-41FB-B2DE-A2AD110D4820} (Win7)
1468     return TextInputProcessorID::eUnknown;
1469   }
1470 
ComputeActiveTIPAsKorean()1471   TextInputProcessorID ComputeActiveTIPAsKorean() {
1472     // {B5FE1F02-D5F2-4445-9C03-C568F23C99A1} (Win7, Win8.1, Win10)
1473     static const GUID kMicrosoftIMEForKoreanGUID = {
1474         0xB5FE1F02,
1475         0xD5F2,
1476         0x4445,
1477         {0x9C, 0x03, 0xC5, 0x68, 0xF2, 0x3C, 0x99, 0xA1}};
1478     if (mActiveTIPGUID == kMicrosoftIMEForKoreanGUID) {
1479       return TextInputProcessorID::eMicrosoftIMEForKorean;
1480     }
1481     // {B60AF051-257A-46BC-B9D3-84DAD819BAFB} (Win8.1, Win10)
1482     static const GUID kMicrosoftOldHangulGUID = {
1483         0xB60AF051,
1484         0x257A,
1485         0x46BC,
1486         {0xB9, 0xD3, 0x84, 0xDA, 0xD8, 0x19, 0xBA, 0xFB}};
1487     if (mActiveTIPGUID == kMicrosoftOldHangulGUID) {
1488       return TextInputProcessorID::eMicrosoftOldHangul;
1489     }
1490 
1491     // NOTE: There is the other Korean TIP installed in Windows:
1492     // * Microsoft IME 2010
1493     //   - {48878C45-93F9-4aaf-A6A1-272CD863C4F5} (Win7)
1494 
1495     return TextInputProcessorID::eUnknown;
1496   }
1497 
1498  public:  // ITfInputProcessorProfileActivationSink
1499   STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, HKL,
1500                            DWORD);
1501 
1502  private:
1503   TSFStaticSink();
~TSFStaticSink()1504   virtual ~TSFStaticSink() {}
1505 
1506   bool EnsureInitActiveTIPKeyboard();
1507 
1508   void Destroy();
1509 
1510   void GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
1511                          REFGUID aProfile, nsAString& aDescription);
1512   bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
1513                              REFGUID aProfile);
1514 
1515   TextInputProcessorID mActiveTIP;
1516 
1517   // Cookie of installing ITfInputProcessorProfileActivationSink
1518   DWORD mIPProfileCookie;
1519 
1520   LANGID mLangID;
1521 
1522   // True if current IME is implemented with IMM.
1523   bool mIsIMM_IME;
1524   // True if OnActivated() is already called
1525   bool mOnActivatedCalled;
1526 
1527   RefPtr<ITfThreadMgr> mThreadMgr;
1528   RefPtr<ITfInputProcessorProfiles> mInputProcessorProfiles;
1529 
1530   // Active TIP keyboard's description.  If active language profile isn't TIP,
1531   // i.e., IMM-IME or just a keyboard layout, this is empty.
1532   nsString mActiveTIPKeyboardDescription;
1533 
1534   // Active TIP's GUID
1535   GUID mActiveTIPGUID;
1536 
1537   static StaticRefPtr<TSFStaticSink> sInstance;
1538 };
1539 
1540 StaticRefPtr<TSFStaticSink> TSFStaticSink::sInstance;
1541 
TSFStaticSink()1542 TSFStaticSink::TSFStaticSink()
1543     : mActiveTIP(TextInputProcessorID::eNotComputed),
1544       mIPProfileCookie(TF_INVALID_COOKIE),
1545       mLangID(0),
1546       mIsIMM_IME(false),
1547       mOnActivatedCalled(false),
1548       mActiveTIPGUID(GUID_NULL) {}
1549 
Init(ITfThreadMgr * aThreadMgr,ITfInputProcessorProfiles * aInputProcessorProfiles)1550 bool TSFStaticSink::Init(ITfThreadMgr* aThreadMgr,
1551                          ITfInputProcessorProfiles* aInputProcessorProfiles) {
1552   MOZ_ASSERT(!mThreadMgr && !mInputProcessorProfiles,
1553              "TSFStaticSink::Init() must be called only once");
1554 
1555   mThreadMgr = aThreadMgr;
1556   mInputProcessorProfiles = aInputProcessorProfiles;
1557 
1558   RefPtr<ITfSource> source;
1559   HRESULT hr =
1560       mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
1561   if (FAILED(hr)) {
1562     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1563             ("0x%p TSFStaticSink::Init() FAILED to get ITfSource "
1564              "instance (0x%08X)",
1565              this, hr));
1566     return false;
1567   }
1568 
1569   // NOTE: On Vista or later, Windows let us know activate IME changed only
1570   //       with ITfInputProcessorProfileActivationSink.
1571   hr = source->AdviseSink(
1572       IID_ITfInputProcessorProfileActivationSink,
1573       static_cast<ITfInputProcessorProfileActivationSink*>(this),
1574       &mIPProfileCookie);
1575   if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) {
1576     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1577             ("0x%p TSFStaticSink::Init() FAILED to install "
1578              "ITfInputProcessorProfileActivationSink (0x%08X)",
1579              this, hr));
1580     return false;
1581   }
1582 
1583   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1584           ("0x%p TSFStaticSink::Init(), "
1585            "mIPProfileCookie=0x%08X",
1586            this, mIPProfileCookie));
1587   return true;
1588 }
1589 
Destroy()1590 void TSFStaticSink::Destroy() {
1591   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1592           ("0x%p TSFStaticSink::Shutdown() "
1593            "mIPProfileCookie=0x%08X",
1594            this, mIPProfileCookie));
1595 
1596   if (mIPProfileCookie != TF_INVALID_COOKIE) {
1597     RefPtr<ITfSource> source;
1598     HRESULT hr =
1599         mThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source));
1600     if (FAILED(hr)) {
1601       MOZ_LOG(sTextStoreLog, LogLevel::Error,
1602               ("0x%p   TSFStaticSink::Shutdown() FAILED to get "
1603                "ITfSource instance (0x%08X)",
1604                this, hr));
1605     } else {
1606       hr = source->UnadviseSink(mIPProfileCookie);
1607       if (FAILED(hr)) {
1608         MOZ_LOG(sTextStoreLog, LogLevel::Error,
1609                 ("0x%p   TSFTextStore::Shutdown() FAILED to uninstall "
1610                  "ITfInputProcessorProfileActivationSink (0x%08X)",
1611                  this, hr));
1612       }
1613     }
1614   }
1615 
1616   mThreadMgr = nullptr;
1617   mInputProcessorProfiles = nullptr;
1618 }
1619 
1620 STDMETHODIMP
OnActivated(DWORD dwProfileType,LANGID langid,REFCLSID rclsid,REFGUID catid,REFGUID guidProfile,HKL hkl,DWORD dwFlags)1621 TSFStaticSink::OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID rclsid,
1622                            REFGUID catid, REFGUID guidProfile, HKL hkl,
1623                            DWORD dwFlags) {
1624   if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
1625       (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
1626        catid == GUID_TFCAT_TIP_KEYBOARD)) {
1627     mOnActivatedCalled = true;
1628     mActiveTIP = TextInputProcessorID::eNotComputed;
1629     mActiveTIPGUID = guidProfile;
1630     mLangID = langid & 0xFFFF;
1631     mIsIMM_IME = IsIMM_IME(hkl);
1632     GetTIPDescription(rclsid, langid, guidProfile,
1633                       mActiveTIPKeyboardDescription);
1634     if (mActiveTIPGUID != GUID_NULL) {
1635       // key should be "LocaleID|Description".  Although GUID of the
1636       // profile is unique key since description may be localized for system
1637       // language, unfortunately, it's too long to record as key with its
1638       // description.  Therefore, we should record only the description with
1639       // LocaleID because Microsoft IME may not include language information.
1640       // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
1641       nsAutoString key;
1642       key.AppendPrintf("0x%04X|", mLangID);
1643       nsAutoString description(mActiveTIPKeyboardDescription);
1644       static const uint32_t kMaxDescriptionLength = 72 - key.Length();
1645       if (description.Length() > kMaxDescriptionLength) {
1646         if (NS_IS_LOW_SURROGATE(description[kMaxDescriptionLength - 1]) &&
1647             NS_IS_HIGH_SURROGATE(description[kMaxDescriptionLength - 2])) {
1648           description.Truncate(kMaxDescriptionLength - 2);
1649         } else {
1650           description.Truncate(kMaxDescriptionLength - 1);
1651         }
1652         // U+2026 is "..."
1653         description.Append(char16_t(0x2026));
1654       }
1655       key.Append(description);
1656       Telemetry::ScalarSet(Telemetry::ScalarID::WIDGET_IME_NAME_ON_WINDOWS, key,
1657                            true);
1658     }
1659     // Notify IMEHandler of changing active keyboard layout.
1660     IMEHandler::OnKeyboardLayoutChanged();
1661   }
1662   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1663           ("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), "
1664            "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
1665            "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
1666            "mActiveTIPDescription=\"%s\"",
1667            this,
1668            dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR
1669                ? "TF_PROFILETYPE_INPUTPROCESSOR"
1670                : dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT
1671                      ? "TF_PROFILETYPE_KEYBOARDLAYOUT"
1672                      : "Unknown",
1673            dwProfileType, langid, GetCLSIDNameStr(rclsid).get(),
1674            GetGUIDNameStr(catid).get(), GetGUIDNameStr(guidProfile).get(), hkl,
1675            dwFlags, GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE),
1676            GetBoolName(mIsIMM_IME),
1677            NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get()));
1678   return S_OK;
1679 }
1680 
EnsureInitActiveTIPKeyboard()1681 bool TSFStaticSink::EnsureInitActiveTIPKeyboard() {
1682   if (mOnActivatedCalled) {
1683     return true;
1684   }
1685 
1686   RefPtr<ITfInputProcessorProfileMgr> profileMgr;
1687   HRESULT hr = mInputProcessorProfiles->QueryInterface(
1688       IID_ITfInputProcessorProfileMgr, getter_AddRefs(profileMgr));
1689   if (FAILED(hr) || !profileMgr) {
1690     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1691             ("0x%p   TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1692              "to get input processor profile manager, hr=0x%08X",
1693              this, hr));
1694     return false;
1695   }
1696 
1697   TF_INPUTPROCESSORPROFILE profile;
1698   hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
1699   if (hr == S_FALSE) {
1700     MOZ_LOG(sTextStoreLog, LogLevel::Info,
1701             ("0x%p   TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1702              "to get active keyboard layout profile due to no active profile, "
1703              "hr=0x%08X",
1704              this, hr));
1705     // XXX Should we call OnActivated() with arguments like non-TIP in this
1706     //     case?
1707     return false;
1708   }
1709   if (FAILED(hr)) {
1710     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1711             ("0x%p   TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1712              "to get active TIP keyboard, hr=0x%08X",
1713              this, hr));
1714     return false;
1715   }
1716 
1717   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1718           ("0x%p   TSFStaticSink::EnsureInitActiveLanguageProfile(), "
1719            "calling OnActivated() manually...",
1720            this));
1721   OnActivated(profile.dwProfileType, profile.langid, profile.clsid,
1722               profile.catid, profile.guidProfile, ::GetKeyboardLayout(0),
1723               TF_IPSINK_FLAG_ACTIVE);
1724   return true;
1725 }
1726 
GetTIPDescription(REFCLSID aTextService,LANGID aLangID,REFGUID aProfile,nsAString & aDescription)1727 void TSFStaticSink::GetTIPDescription(REFCLSID aTextService, LANGID aLangID,
1728                                       REFGUID aProfile,
1729                                       nsAString& aDescription) {
1730   aDescription.Truncate();
1731 
1732   if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
1733     return;
1734   }
1735 
1736   BSTR description = nullptr;
1737   HRESULT hr = mInputProcessorProfiles->GetLanguageProfileDescription(
1738       aTextService, aLangID, aProfile, &description);
1739   if (FAILED(hr)) {
1740     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1741             ("0x%p   TSFStaticSink::InitActiveTIPDescription() FAILED "
1742              "due to GetLanguageProfileDescription() failure, hr=0x%08X",
1743              this, hr));
1744     return;
1745   }
1746 
1747   if (description && description[0]) {
1748     aDescription.Assign(description);
1749   }
1750   ::SysFreeString(description);
1751 }
1752 
IsTIPCategoryKeyboard(REFCLSID aTextService,LANGID aLangID,REFGUID aProfile)1753 bool TSFStaticSink::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
1754                                           REFGUID aProfile) {
1755   if (aTextService == CLSID_NULL || aProfile == GUID_NULL) {
1756     return false;
1757   }
1758 
1759   RefPtr<IEnumTfLanguageProfiles> enumLangProfiles;
1760   HRESULT hr = mInputProcessorProfiles->EnumLanguageProfiles(
1761       aLangID, getter_AddRefs(enumLangProfiles));
1762   if (FAILED(hr) || !enumLangProfiles) {
1763     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1764             ("0x%p   TSFStaticSink::IsTIPCategoryKeyboard(), FAILED "
1765              "to get language profiles enumerator, hr=0x%08X",
1766              this, hr));
1767     return false;
1768   }
1769 
1770   TF_LANGUAGEPROFILE profile;
1771   ULONG fetch = 0;
1772   while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) {
1773     // XXX We're not sure a profile is registered with two or more categories.
1774     if (profile.clsid == aTextService && profile.guidProfile == aProfile &&
1775         profile.catid == GUID_TFCAT_TIP_KEYBOARD) {
1776       return true;
1777     }
1778   }
1779   return false;
1780 }
1781 
1782 /******************************************************************/
1783 /* TSFPreference                                                  */
1784 /******************************************************************/
1785 
1786 class TSFPrefs final {
1787  public:
1788 #define DECL_AND_IMPL_BOOL_PREF(aPref, aName, aDefaultValue)                  \
1789   static bool aName() {                                                       \
1790     static bool s##aName##Value = Preferences::GetBool(aPref, aDefaultValue); \
1791     return s##aName##Value;                                                   \
1792   }
1793 
1794   DECL_AND_IMPL_BOOL_PREF("intl.ime.hack.set_input_scope_of_url_bar_to_default",
1795                           ShouldSetInputScopeOfURLBarToDefault, true)
1796   DECL_AND_IMPL_BOOL_PREF(
1797       "intl.tsf.hack.allow_to_stop_hacking_on_build_17643_or_later",
1798       AllowToStopHackingOnBuild17643OrLater, false)
1799   DECL_AND_IMPL_BOOL_PREF("intl.tsf.hack.atok.create_native_caret",
1800                           NeedToCreateNativeCaretForLegacyATOK, true)
1801   DECL_AND_IMPL_BOOL_PREF(
1802       "intl.tsf.hack.atok.do_not_return_no_layout_error_of_composition_string",
1803       DoNotReturnNoLayoutErrorToATOKOfCompositionString, true)
1804   DECL_AND_IMPL_BOOL_PREF(
1805       "intl.tsf.hack.japanist10."
1806       "do_not_return_no_layout_error_of_composition_string",
1807       DoNotReturnNoLayoutErrorToJapanist10OfCompositionString, true)
1808   DECL_AND_IMPL_BOOL_PREF(
1809       "intl.tsf.hack.ms_simplified_chinese.do_not_return_no_layout_error",
1810       DoNotReturnNoLayoutErrorToMSSimplifiedTIP, true)
1811   DECL_AND_IMPL_BOOL_PREF(
1812       "intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error",
1813       DoNotReturnNoLayoutErrorToMSTraditionalTIP, true)
1814   DECL_AND_IMPL_BOOL_PREF(
1815       "intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error",
1816       DoNotReturnNoLayoutErrorToFreeChangJie, true)
1817   DECL_AND_IMPL_BOOL_PREF(
1818       "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_first_"
1819       "char",
1820       DoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar, true)
1821   DECL_AND_IMPL_BOOL_PREF(
1822       "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_caret",
1823       DoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret, true)
1824   DECL_AND_IMPL_BOOL_PREF(
1825       "intl.tsf.hack.ms_simplified_chinese.query_insert_result",
1826       NeedToHackQueryInsertForMSSimplifiedTIP, true)
1827   DECL_AND_IMPL_BOOL_PREF(
1828       "intl.tsf.hack.ms_traditional_chinese.query_insert_result",
1829       NeedToHackQueryInsertForMSTraditionalTIP, true)
1830 
1831 #undef DECL_AND_IMPL_BOOL_PREF
1832 };
1833 
1834 /******************************************************************/
1835 /* TSFTextStore                                                   */
1836 /******************************************************************/
1837 
1838 StaticRefPtr<ITfThreadMgr> TSFTextStore::sThreadMgr;
1839 StaticRefPtr<ITfMessagePump> TSFTextStore::sMessagePump;
1840 StaticRefPtr<ITfKeystrokeMgr> TSFTextStore::sKeystrokeMgr;
1841 StaticRefPtr<ITfDisplayAttributeMgr> TSFTextStore::sDisplayAttrMgr;
1842 StaticRefPtr<ITfCategoryMgr> TSFTextStore::sCategoryMgr;
1843 StaticRefPtr<ITfCompartment> TSFTextStore::sCompartmentForOpenClose;
1844 StaticRefPtr<ITfDocumentMgr> TSFTextStore::sDisabledDocumentMgr;
1845 StaticRefPtr<ITfContext> TSFTextStore::sDisabledContext;
1846 StaticRefPtr<ITfInputProcessorProfiles> TSFTextStore::sInputProcessorProfiles;
1847 StaticRefPtr<TSFTextStore> TSFTextStore::sEnabledTextStore;
1848 const MSG* TSFTextStore::sHandlingKeyMsg = nullptr;
1849 DWORD TSFTextStore::sClientId = 0;
1850 bool TSFTextStore::sIsKeyboardEventDispatched = false;
1851 
1852 #define TEXTSTORE_DEFAULT_VIEW (1)
1853 
TSFTextStore()1854 TSFTextStore::TSFTextStore()
1855     : mEditCookie(0),
1856       mSinkMask(0),
1857       mLock(0),
1858       mLockQueued(0),
1859       mHandlingKeyMessage(0),
1860       mContentForTSF(mComposition, mSelectionForTSF),
1861       mRequestedAttrValues(false),
1862       mIsRecordingActionsWithoutLock(false),
1863       mHasReturnedNoLayoutError(false),
1864       mWaitingQueryLayout(false),
1865       mPendingDestroy(false),
1866       mDeferClearingContentForTSF(false),
1867       mDeferNotifyingTSF(false),
1868       mDeferCommittingComposition(false),
1869       mDeferCancellingComposition(false),
1870       mDestroyed(false),
1871       mBeingDestroyed(false) {
1872   for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) {
1873     mRequestedAttrs[i] = false;
1874   }
1875 
1876   // We hope that 5 or more actions don't occur at once.
1877   mPendingActions.SetCapacity(5);
1878 
1879   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1880           ("0x%p TSFTextStore::TSFTextStore() SUCCEEDED", this));
1881 }
1882 
~TSFTextStore()1883 TSFTextStore::~TSFTextStore() {
1884   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1885           ("0x%p TSFTextStore instance is destroyed", this));
1886 }
1887 
Init(nsWindowBase * aWidget,const InputContext & aContext)1888 bool TSFTextStore::Init(nsWindowBase* aWidget, const InputContext& aContext) {
1889   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1890           ("0x%p TSFTextStore::Init(aWidget=0x%p)", this, aWidget));
1891 
1892   if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
1893     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1894             ("0x%p   TSFTextStore::Init() FAILED due to being initialized with "
1895              "destroyed widget",
1896              this));
1897     return false;
1898   }
1899 
1900   if (mDocumentMgr) {
1901     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1902             ("0x%p   TSFTextStore::Init() FAILED due to already initialized",
1903              this));
1904     return false;
1905   }
1906 
1907   mWidget = aWidget;
1908   if (NS_WARN_IF(!mWidget)) {
1909     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1910             ("0x%p   TSFTextStore::Init() FAILED "
1911              "due to aWidget is nullptr ",
1912              this));
1913     return false;
1914   }
1915   mDispatcher = mWidget->GetTextEventDispatcher();
1916   if (NS_WARN_IF(!mDispatcher)) {
1917     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1918             ("0x%p   TSFTextStore::Init() FAILED "
1919              "due to aWidget->GetTextEventDispatcher() failure",
1920              this));
1921     return false;
1922   }
1923 
1924   SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputInputmode,
1925                 aContext.mInPrivateBrowsing);
1926 
1927   // Create document manager
1928   RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
1929   RefPtr<ITfDocumentMgr> documentMgr;
1930   HRESULT hr = threadMgr->CreateDocumentMgr(getter_AddRefs(documentMgr));
1931   if (NS_WARN_IF(FAILED(hr))) {
1932     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1933             ("0x%p   TSFTextStore::Init() FAILED to create ITfDocumentMgr "
1934              "(0x%08X)",
1935              this, hr));
1936     return false;
1937   }
1938   if (NS_WARN_IF(mDestroyed)) {
1939     MOZ_LOG(
1940         sTextStoreLog, LogLevel::Error,
1941         ("0x%p   TSFTextStore::Init() FAILED to create ITfDocumentMgr due to "
1942          "TextStore being destroyed during calling "
1943          "ITfThreadMgr::CreateDocumentMgr()",
1944          this));
1945     return false;
1946   }
1947   // Create context and add it to document manager
1948   RefPtr<ITfContext> context;
1949   hr = documentMgr->CreateContext(sClientId, 0,
1950                                   static_cast<ITextStoreACP*>(this),
1951                                   getter_AddRefs(context), &mEditCookie);
1952   if (NS_WARN_IF(FAILED(hr))) {
1953     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1954             ("0x%p   TSFTextStore::Init() FAILED to create the context "
1955              "(0x%08X)",
1956              this, hr));
1957     return false;
1958   }
1959   if (NS_WARN_IF(mDestroyed)) {
1960     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1961             ("0x%p   TSFTextStore::Init() FAILED to create ITfContext due to "
1962              "TextStore being destroyed during calling "
1963              "ITfDocumentMgr::CreateContext()",
1964              this));
1965     return false;
1966   }
1967 
1968   hr = documentMgr->Push(context);
1969   if (NS_WARN_IF(FAILED(hr))) {
1970     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1971             ("0x%p   TSFTextStore::Init() FAILED to push the context (0x%08X)",
1972              this, hr));
1973     return false;
1974   }
1975   if (NS_WARN_IF(mDestroyed)) {
1976     MOZ_LOG(sTextStoreLog, LogLevel::Error,
1977             ("0x%p   TSFTextStore::Init() FAILED to create ITfContext due to "
1978              "TextStore being destroyed during calling ITfDocumentMgr::Push()",
1979              this));
1980     documentMgr->Pop(TF_POPF_ALL);
1981     return false;
1982   }
1983 
1984   mDocumentMgr = documentMgr;
1985   mContext = context;
1986 
1987   MOZ_LOG(sTextStoreLog, LogLevel::Info,
1988           ("0x%p   TSFTextStore::Init() succeeded: "
1989            "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
1990            this, mDocumentMgr.get(), mContext.get(), mEditCookie));
1991 
1992   return true;
1993 }
1994 
Destroy()1995 void TSFTextStore::Destroy() {
1996   if (mBeingDestroyed) {
1997     return;
1998   }
1999 
2000   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2001           ("0x%p TSFTextStore::Destroy(), mLock=%s, "
2002            "mComposition.IsComposing()=%s, mHandlingKeyMessage=%u",
2003            this, GetLockFlagNameStr(mLock).get(),
2004            GetBoolName(mComposition.IsComposing()), mHandlingKeyMessage));
2005 
2006   mDestroyed = true;
2007 
2008   // Destroy native caret first because it's not directly related to TSF and
2009   // there may be another textstore which gets focus.  So, we should avoid
2010   // to destroy caret after the new one recreates caret.
2011   IMEHandler::MaybeDestroyNativeCaret();
2012 
2013   if (mLock) {
2014     mPendingDestroy = true;
2015     return;
2016   }
2017 
2018   AutoRestore<bool> savedBeingDestroyed(mBeingDestroyed);
2019   mBeingDestroyed = true;
2020 
2021   // If there is composition, TSF keeps the composition even after the text
2022   // store destroyed.  So, we should clear the composition here.
2023   if (mComposition.IsComposing()) {
2024     CommitCompositionInternal(false);
2025   }
2026 
2027   if (mSink) {
2028     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2029             ("0x%p   TSFTextStore::Destroy(), calling "
2030              "ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
2031              this));
2032     RefPtr<ITextStoreACPSink> sink = mSink;
2033     sink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
2034   }
2035 
2036   // If this is called during handling a keydown or keyup message, we should
2037   // put off to release TSF objects until it completely finishes since
2038   // MS-IME for Japanese refers some objects without grabbing them.
2039   if (!mHandlingKeyMessage) {
2040     ReleaseTSFObjects();
2041   }
2042 
2043   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2044           ("0x%p   TSFTextStore::Destroy() succeeded", this));
2045 }
2046 
ReleaseTSFObjects()2047 void TSFTextStore::ReleaseTSFObjects() {
2048   MOZ_ASSERT(!mHandlingKeyMessage);
2049 
2050   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2051           ("0x%p TSFTextStore::ReleaseTSFObjects()", this));
2052 
2053   mContext = nullptr;
2054   if (mDocumentMgr) {
2055     RefPtr<ITfDocumentMgr> documentMgr = mDocumentMgr.forget();
2056     documentMgr->Pop(TF_POPF_ALL);
2057   }
2058   mSink = nullptr;
2059   mWidget = nullptr;
2060   mDispatcher = nullptr;
2061 
2062   if (!mMouseTrackers.IsEmpty()) {
2063     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2064             ("0x%p   TSFTextStore::ReleaseTSFObjects(), "
2065              "removing a mouse tracker...",
2066              this));
2067     mMouseTrackers.Clear();
2068   }
2069 
2070   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2071           ("0x%p   TSFTextStore::ReleaseTSFObjects() completed", this));
2072 }
2073 
2074 STDMETHODIMP
QueryInterface(REFIID riid,void ** ppv)2075 TSFTextStore::QueryInterface(REFIID riid, void** ppv) {
2076   *ppv = nullptr;
2077   if ((IID_IUnknown == riid) || (IID_ITextStoreACP == riid)) {
2078     *ppv = static_cast<ITextStoreACP*>(this);
2079   } else if (IID_ITfContextOwnerCompositionSink == riid) {
2080     *ppv = static_cast<ITfContextOwnerCompositionSink*>(this);
2081   } else if (IID_ITfMouseTrackerACP == riid) {
2082     *ppv = static_cast<ITfMouseTrackerACP*>(this);
2083   }
2084   if (*ppv) {
2085     AddRef();
2086     return S_OK;
2087   }
2088 
2089   MOZ_LOG(sTextStoreLog, LogLevel::Error,
2090           ("0x%p TSFTextStore::QueryInterface() FAILED, riid=%s", this,
2091            GetRIIDNameStr(riid).get()));
2092   return E_NOINTERFACE;
2093 }
2094 
2095 STDMETHODIMP
AdviseSink(REFIID riid,IUnknown * punk,DWORD dwMask)2096 TSFTextStore::AdviseSink(REFIID riid, IUnknown* punk, DWORD dwMask) {
2097   MOZ_LOG(
2098       sTextStoreLog, LogLevel::Info,
2099       ("0x%p TSFTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), "
2100        "mSink=0x%p, mSinkMask=%s",
2101        this, GetRIIDNameStr(riid).get(), punk, GetSinkMaskNameStr(dwMask).get(),
2102        mSink.get(), GetSinkMaskNameStr(mSinkMask).get()));
2103 
2104   if (!punk) {
2105     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2106             ("0x%p   TSFTextStore::AdviseSink() FAILED due to the null punk",
2107              this));
2108     return E_UNEXPECTED;
2109   }
2110 
2111   if (IID_ITextStoreACPSink != riid) {
2112     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2113             ("0x%p   TSFTextStore::AdviseSink() FAILED due to "
2114              "unsupported interface",
2115              this));
2116     return E_INVALIDARG;  // means unsupported interface.
2117   }
2118 
2119   if (!mSink) {
2120     // Install sink
2121     punk->QueryInterface(IID_ITextStoreACPSink, getter_AddRefs(mSink));
2122     if (!mSink) {
2123       MOZ_LOG(sTextStoreLog, LogLevel::Error,
2124               ("0x%p   TSFTextStore::AdviseSink() FAILED due to "
2125                "punk not having the interface",
2126                this));
2127       return E_UNEXPECTED;
2128     }
2129   } else {
2130     // If sink is already installed we check to see if they are the same
2131     // Get IUnknown from both sides for comparison
2132     RefPtr<IUnknown> comparison1, comparison2;
2133     punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
2134     mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
2135     if (comparison1 != comparison2) {
2136       MOZ_LOG(sTextStoreLog, LogLevel::Error,
2137               ("0x%p   TSFTextStore::AdviseSink() FAILED due to "
2138                "the sink being different from the stored sink",
2139                this));
2140       return CONNECT_E_ADVISELIMIT;
2141     }
2142   }
2143   // Update mask either for a new sink or an existing sink
2144   mSinkMask = dwMask;
2145   return S_OK;
2146 }
2147 
2148 STDMETHODIMP
UnadviseSink(IUnknown * punk)2149 TSFTextStore::UnadviseSink(IUnknown* punk) {
2150   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2151           ("0x%p TSFTextStore::UnadviseSink(punk=0x%p), mSink=0x%p", this, punk,
2152            mSink.get()));
2153 
2154   if (!punk) {
2155     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2156             ("0x%p   TSFTextStore::UnadviseSink() FAILED due to the null punk",
2157              this));
2158     return E_INVALIDARG;
2159   }
2160   if (!mSink) {
2161     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2162             ("0x%p   TSFTextStore::UnadviseSink() FAILED due to "
2163              "any sink not stored",
2164              this));
2165     return CONNECT_E_NOCONNECTION;
2166   }
2167   // Get IUnknown from both sides for comparison
2168   RefPtr<IUnknown> comparison1, comparison2;
2169   punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1));
2170   mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2));
2171   // Unadvise only if sinks are the same
2172   if (comparison1 != comparison2) {
2173     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2174             ("0x%p   TSFTextStore::UnadviseSink() FAILED due to "
2175              "the sink being different from the stored sink",
2176              this));
2177     return CONNECT_E_NOCONNECTION;
2178   }
2179   mSink = nullptr;
2180   mSinkMask = 0;
2181   return S_OK;
2182 }
2183 
2184 STDMETHODIMP
RequestLock(DWORD dwLockFlags,HRESULT * phrSession)2185 TSFTextStore::RequestLock(DWORD dwLockFlags, HRESULT* phrSession) {
2186   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2187           ("0x%p TSFTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), "
2188            "mLock=%s, mDestroyed=%s",
2189            this, GetLockFlagNameStr(dwLockFlags).get(), phrSession,
2190            GetLockFlagNameStr(mLock).get(), GetBoolName(mDestroyed)));
2191 
2192   if (!mSink) {
2193     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2194             ("0x%p   TSFTextStore::RequestLock() FAILED due to "
2195              "any sink not stored",
2196              this));
2197     return E_FAIL;
2198   }
2199   if (mDestroyed &&
2200       (!mContentForTSF.IsInitialized() || mSelectionForTSF.IsDirty())) {
2201     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2202             ("0x%p   TSFTextStore::RequestLock() FAILED due to "
2203              "being destroyed and no information of the contents",
2204              this));
2205     return E_FAIL;
2206   }
2207   if (!phrSession) {
2208     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2209             ("0x%p   TSFTextStore::RequestLock() FAILED due to "
2210              "null phrSession",
2211              this));
2212     return E_INVALIDARG;
2213   }
2214 
2215   if (!mLock) {
2216     // put on lock
2217     mLock = dwLockFlags & (~TS_LF_SYNC);
2218     MOZ_LOG(
2219         sTextStoreLog, LogLevel::Info,
2220         ("0x%p   Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2221          ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
2222          this, GetLockFlagNameStr(mLock).get()));
2223     // Don't release this instance during this lock because this is called by
2224     // TSF but they don't grab us during this call.
2225     RefPtr<TSFTextStore> kungFuDeathGrip(this);
2226     RefPtr<ITextStoreACPSink> sink = mSink;
2227     *phrSession = sink->OnLockGranted(mLock);
2228     MOZ_LOG(
2229         sTextStoreLog, LogLevel::Info,
2230         ("0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2231          "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
2232          this, GetLockFlagNameStr(mLock).get()));
2233     DidLockGranted();
2234     while (mLockQueued) {
2235       mLock = mLockQueued;
2236       mLockQueued = 0;
2237       MOZ_LOG(sTextStoreLog, LogLevel::Info,
2238               ("0x%p   Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
2239                ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2240                ">>>>>",
2241                this, GetLockFlagNameStr(mLock).get()));
2242       sink->OnLockGranted(mLock);
2243       MOZ_LOG(sTextStoreLog, LogLevel::Info,
2244               ("0x%p   Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2245                "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2246                "<<<<<",
2247                this, GetLockFlagNameStr(mLock).get()));
2248       DidLockGranted();
2249     }
2250 
2251     // The document is now completely unlocked.
2252     mLock = 0;
2253 
2254     MaybeFlushPendingNotifications();
2255 
2256     MOZ_LOG(sTextStoreLog, LogLevel::Info,
2257             ("0x%p   TSFTextStore::RequestLock() succeeded: *phrSession=%s",
2258              this, GetTextStoreReturnValueName(*phrSession)));
2259     return S_OK;
2260   }
2261 
2262   // only time when reentrant lock is allowed is when caller holds a
2263   // read-only lock and is requesting an async write lock
2264   if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags) &&
2265       !(dwLockFlags & TS_LF_SYNC)) {
2266     *phrSession = TS_S_ASYNC;
2267     mLockQueued = dwLockFlags & (~TS_LF_SYNC);
2268 
2269     MOZ_LOG(sTextStoreLog, LogLevel::Info,
2270             ("0x%p   TSFTextStore::RequestLock() stores the request in the "
2271              "queue, *phrSession=TS_S_ASYNC",
2272              this));
2273     return S_OK;
2274   }
2275 
2276   // no more locks allowed
2277   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2278           ("0x%p   TSFTextStore::RequestLock() didn't allow to lock, "
2279            "*phrSession=TS_E_SYNCHRONOUS",
2280            this));
2281   *phrSession = TS_E_SYNCHRONOUS;
2282   return E_FAIL;
2283 }
2284 
DidLockGranted()2285 void TSFTextStore::DidLockGranted() {
2286   if (IsReadWriteLocked()) {
2287     // FreeCJ (TIP for Traditional Chinese) calls SetSelection() to set caret
2288     // to the start of composition string and insert a full width space for
2289     // a placeholder with a call of SetText().  After that, it calls
2290     // OnUpdateComposition() without new range.  Therefore, let's record the
2291     // composition update information here.
2292     CompleteLastActionIfStillIncomplete();
2293 
2294     FlushPendingActions();
2295   }
2296 
2297   // If the widget has gone, we don't need to notify anything.
2298   if (mDestroyed || !mWidget || mWidget->Destroyed()) {
2299     mPendingSelectionChangeData.Clear();
2300     mHasReturnedNoLayoutError = false;
2301   }
2302 }
2303 
DispatchEvent(WidgetGUIEvent & aEvent)2304 void TSFTextStore::DispatchEvent(WidgetGUIEvent& aEvent) {
2305   if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) {
2306     return;
2307   }
2308   // If the event isn't a query content event, the event may be handled
2309   // asynchronously.  So, we should put off to answer from GetTextExt() etc.
2310   if (!aEvent.AsQueryContentEvent()) {
2311     mDeferNotifyingTSF = true;
2312   }
2313   mWidget->DispatchWindowEvent(&aEvent);
2314 }
2315 
FlushPendingActions()2316 void TSFTextStore::FlushPendingActions() {
2317   if (!mWidget || mWidget->Destroyed()) {
2318     // Note that don't clear mContentForTSF because TIP may try to commit
2319     // composition with a document lock.  In such case, TSFTextStore needs to
2320     // behave as expected by TIP.
2321     mPendingActions.Clear();
2322     mPendingSelectionChangeData.Clear();
2323     mHasReturnedNoLayoutError = false;
2324     return;
2325   }
2326 
2327   // Some TIP may request lock but does nothing during the lock.  In such case,
2328   // this should do nothing.  For example, when MS-IME for Japanese is active
2329   // and we're inactivating, this case occurs and causes different behavior
2330   // from the other TIPs.
2331   if (mPendingActions.IsEmpty()) {
2332     return;
2333   }
2334 
2335   RefPtr<nsWindowBase> widget(mWidget);
2336   nsresult rv = mDispatcher->BeginNativeInputTransaction();
2337   if (NS_WARN_IF(NS_FAILED(rv))) {
2338     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2339             ("0x%p   TSFTextStore::FlushPendingActions() "
2340              "FAILED due to BeginNativeInputTransaction() failure",
2341              this));
2342     return;
2343   }
2344   for (uint32_t i = 0; i < mPendingActions.Length(); i++) {
2345     PendingAction& action = mPendingActions[i];
2346     switch (action.mType) {
2347       case PendingAction::Type::eKeyboardEvent:
2348         if (mDestroyed) {
2349           MOZ_LOG(sTextStoreLog, LogLevel::Warning,
2350                   ("0x%p   TSFTextStore::FlushPendingActions() "
2351                    "IGNORED pending KeyboardEvent(%s) due to already destroyed",
2352                    action.mKeyMsg.message == WM_KEYDOWN ? "eKeyDown" : "eKeyUp",
2353                    this));
2354         }
2355         MOZ_DIAGNOSTIC_ASSERT(action.mKeyMsg.message == WM_KEYDOWN ||
2356                               action.mKeyMsg.message == WM_KEYUP);
2357         DispatchKeyboardEventAsProcessedByIME(action.mKeyMsg);
2358         if (!widget || widget->Destroyed()) {
2359           break;
2360         }
2361         break;
2362       case PendingAction::Type::eCompositionStart: {
2363         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2364                 ("0x%p   TSFTextStore::FlushPendingActions() "
2365                  "flushing Type::eCompositionStart={ mSelectionStart=%d, "
2366                  "mSelectionLength=%d }, mDestroyed=%s",
2367                  this, action.mSelectionStart, action.mSelectionLength,
2368                  GetBoolName(mDestroyed)));
2369 
2370         if (mDestroyed) {
2371           MOZ_LOG(sTextStoreLog, LogLevel::Warning,
2372                   ("0x%p   TSFTextStore::FlushPendingActions() "
2373                    "IGNORED pending compositionstart due to already destroyed",
2374                    this));
2375           break;
2376         }
2377 
2378         if (action.mAdjustSelection) {
2379           // Select composition range so the new composition replaces the range
2380           WidgetSelectionEvent selectionSet(true, eSetSelection, widget);
2381           widget->InitEvent(selectionSet);
2382           selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
2383           selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
2384           selectionSet.mReversed = false;
2385           DispatchEvent(selectionSet);
2386           if (!selectionSet.mSucceeded) {
2387             MOZ_LOG(sTextStoreLog, LogLevel::Error,
2388                     ("0x%p   TSFTextStore::FlushPendingActions() "
2389                      "FAILED due to eSetSelection failure",
2390                      this));
2391             break;
2392           }
2393         }
2394 
2395         // eCompositionStart always causes
2396         // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED.  Therefore, we should
2397         // wait to clear mContentForTSF until it's notified.
2398         mDeferClearingContentForTSF = true;
2399 
2400         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2401                 ("0x%p   TSFTextStore::FlushPendingActions() "
2402                  "dispatching compositionstart event...",
2403                  this));
2404         WidgetEventTime eventTime = widget->CurrentMessageWidgetEventTime();
2405         nsEventStatus status;
2406         rv = mDispatcher->StartComposition(status, &eventTime);
2407         if (NS_WARN_IF(NS_FAILED(rv))) {
2408           MOZ_LOG(sTextStoreLog, LogLevel::Error,
2409                   ("0x%p   TSFTextStore::FlushPendingActions() "
2410                    "FAILED to dispatch compositionstart event, "
2411                    "IsHandlingComposition()=%s",
2412                    this, GetBoolName(IsHandlingComposition())));
2413           // XXX Is this right? If there is a composition in content,
2414           //     shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2415           mDeferClearingContentForTSF = !IsHandlingComposition();
2416         }
2417         if (!widget || widget->Destroyed()) {
2418           break;
2419         }
2420         break;
2421       }
2422       case PendingAction::Type::eCompositionUpdate: {
2423         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2424                 ("0x%p   TSFTextStore::FlushPendingActions() "
2425                  "flushing Type::eCompositionUpdate={ mData=\"%s\", "
2426                  "mRanges=0x%p, mRanges->Length()=%d }",
2427                  this, GetEscapedUTF8String(action.mData).get(),
2428                  action.mRanges.get(),
2429                  action.mRanges ? action.mRanges->Length() : 0));
2430 
2431         // eCompositionChange causes a DOM text event, the IME will be notified
2432         // of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED.  In this case, we
2433         // should not clear mContentForTSF until we notify the IME of the
2434         // composition update.
2435         mDeferClearingContentForTSF = true;
2436 
2437         rv = mDispatcher->SetPendingComposition(action.mData, action.mRanges);
2438         if (NS_WARN_IF(NS_FAILED(rv))) {
2439           MOZ_LOG(sTextStoreLog, LogLevel::Error,
2440                   ("0x%p   TSFTextStore::FlushPendingActions() "
2441                    "FAILED to setting pending composition... "
2442                    "IsHandlingComposition()=%s",
2443                    this, GetBoolName(IsHandlingComposition())));
2444           // XXX Is this right? If there is a composition in content,
2445           //     shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2446           mDeferClearingContentForTSF = !IsHandlingComposition();
2447         } else {
2448           MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2449                   ("0x%p   TSFTextStore::FlushPendingActions() "
2450                    "dispatching compositionchange event...",
2451                    this));
2452           WidgetEventTime eventTime = widget->CurrentMessageWidgetEventTime();
2453           nsEventStatus status;
2454           rv = mDispatcher->FlushPendingComposition(status, &eventTime);
2455           if (NS_WARN_IF(NS_FAILED(rv))) {
2456             MOZ_LOG(sTextStoreLog, LogLevel::Error,
2457                     ("0x%p   TSFTextStore::FlushPendingActions() "
2458                      "FAILED to dispatch compositionchange event, "
2459                      "IsHandlingComposition()=%s",
2460                      this, GetBoolName(IsHandlingComposition())));
2461             // XXX Is this right? If there is a composition in content,
2462             //     shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2463             mDeferClearingContentForTSF = !IsHandlingComposition();
2464           }
2465           // Be aware, the mWidget might already have been destroyed.
2466         }
2467         break;
2468       }
2469       case PendingAction::Type::eCompositionEnd: {
2470         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2471                 ("0x%p   TSFTextStore::FlushPendingActions() "
2472                  "flushing Type::eCompositionEnd={ mData=\"%s\" }",
2473                  this, GetEscapedUTF8String(action.mData).get()));
2474 
2475         // Dispatching eCompositionCommit causes a DOM text event, then,
2476         // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
2477         // when focused content actually handles the event.  For example,
2478         // when focused content is in a remote process, it's sent when
2479         // all dispatched composition events have been handled in the remote
2480         // process.  So, until then, we don't have newer content information.
2481         // Therefore, we need to put off to clear mContentForTSF.
2482         mDeferClearingContentForTSF = true;
2483 
2484         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2485                 ("0x%p   TSFTextStore::FlushPendingActions(), "
2486                  "dispatching compositioncommit event...",
2487                  this));
2488         WidgetEventTime eventTime = widget->CurrentMessageWidgetEventTime();
2489         nsEventStatus status;
2490         rv = mDispatcher->CommitComposition(status, &action.mData, &eventTime);
2491         if (NS_WARN_IF(NS_FAILED(rv))) {
2492           MOZ_LOG(sTextStoreLog, LogLevel::Error,
2493                   ("0x%p   TSFTextStore::FlushPendingActions() "
2494                    "FAILED to dispatch compositioncommit event, "
2495                    "IsHandlingComposition()=%s",
2496                    this, GetBoolName(IsHandlingComposition())));
2497           // XXX Is this right? If there is a composition in content,
2498           //     shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2499           mDeferClearingContentForTSF = !IsHandlingComposition();
2500         }
2501         break;
2502       }
2503       case PendingAction::Type::eSetSelection: {
2504         MOZ_LOG(
2505             sTextStoreLog, LogLevel::Debug,
2506             ("0x%p   TSFTextStore::FlushPendingActions() "
2507              "flushing Type::eSetSelection={ mSelectionStart=%d, "
2508              "mSelectionLength=%d, mSelectionReversed=%s }, "
2509              "mDestroyed=%s",
2510              this, action.mSelectionStart, action.mSelectionLength,
2511              GetBoolName(action.mSelectionReversed), GetBoolName(mDestroyed)));
2512 
2513         if (mDestroyed) {
2514           MOZ_LOG(sTextStoreLog, LogLevel::Warning,
2515                   ("0x%p   TSFTextStore::FlushPendingActions() "
2516                    "IGNORED pending selectionset due to already destroyed",
2517                    this));
2518           break;
2519         }
2520 
2521         WidgetSelectionEvent selectionSet(true, eSetSelection, widget);
2522         selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
2523         selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
2524         selectionSet.mReversed = action.mSelectionReversed;
2525         DispatchEvent(selectionSet);
2526         if (!selectionSet.mSucceeded) {
2527           MOZ_LOG(sTextStoreLog, LogLevel::Error,
2528                   ("0x%p   TSFTextStore::FlushPendingActions() "
2529                    "FAILED due to eSetSelection failure",
2530                    this));
2531           break;
2532         }
2533         break;
2534       }
2535       default:
2536         MOZ_CRASH("unexpected action type");
2537     }
2538 
2539     if (widget && !widget->Destroyed()) {
2540       continue;
2541     }
2542 
2543     MOZ_LOG(sTextStoreLog, LogLevel::Info,
2544             ("0x%p   TSFTextStore::FlushPendingActions(), "
2545              "qutting since the mWidget has gone",
2546              this));
2547     break;
2548   }
2549   mPendingActions.Clear();
2550 }
2551 
MaybeFlushPendingNotifications()2552 void TSFTextStore::MaybeFlushPendingNotifications() {
2553   if (IsReadLocked()) {
2554     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2555             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2556              "putting off flushing pending notifications due to being the "
2557              "document locked...",
2558              this));
2559     return;
2560   }
2561 
2562   if (mDeferCommittingComposition) {
2563     MOZ_LOG(sTextStoreLog, LogLevel::Info,
2564             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2565              "calling TSFTextStore::CommitCompositionInternal(false)...",
2566              this));
2567     mDeferCommittingComposition = mDeferCancellingComposition = false;
2568     CommitCompositionInternal(false);
2569   } else if (mDeferCancellingComposition) {
2570     MOZ_LOG(sTextStoreLog, LogLevel::Info,
2571             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2572              "calling TSFTextStore::CommitCompositionInternal(true)...",
2573              this));
2574     mDeferCommittingComposition = mDeferCancellingComposition = false;
2575     CommitCompositionInternal(true);
2576   }
2577 
2578   if (mDeferNotifyingTSF) {
2579     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2580             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2581              "putting off flushing pending notifications due to being "
2582              "dispatching events...",
2583              this));
2584     return;
2585   }
2586 
2587   if (mPendingDestroy) {
2588     Destroy();
2589     return;
2590   }
2591 
2592   if (mDestroyed) {
2593     // If it's already been destroyed completely, this shouldn't notify TSF of
2594     // anything anymore.
2595     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2596             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2597              "does nothing because this has already destroyed completely...",
2598              this));
2599     return;
2600   }
2601 
2602   if (!mDeferClearingContentForTSF && mContentForTSF.IsInitialized()) {
2603     mContentForTSF.Clear();
2604     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2605             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2606              "mContentForTSF is cleared",
2607              this));
2608   }
2609 
2610   // When there is no cached content, we can sync actual contents and TSF/TIP
2611   // expecting contents.
2612   RefPtr<TSFTextStore> kungFuDeathGrip = this;
2613   Unused << kungFuDeathGrip;
2614   if (!mContentForTSF.IsInitialized()) {
2615     if (mPendingTextChangeData.IsValid()) {
2616       MOZ_LOG(sTextStoreLog, LogLevel::Info,
2617               ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2618                "calling TSFTextStore::NotifyTSFOfTextChange()...",
2619                this));
2620       NotifyTSFOfTextChange();
2621     }
2622     if (mPendingSelectionChangeData.IsValid()) {
2623       MOZ_LOG(sTextStoreLog, LogLevel::Info,
2624               ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2625                "calling TSFTextStore::NotifyTSFOfSelectionChange()...",
2626                this));
2627       NotifyTSFOfSelectionChange();
2628     }
2629   }
2630 
2631   if (mHasReturnedNoLayoutError) {
2632     MOZ_LOG(sTextStoreLog, LogLevel::Info,
2633             ("0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
2634              "calling TSFTextStore::NotifyTSFOfLayoutChange()...",
2635              this));
2636     NotifyTSFOfLayoutChange();
2637   }
2638 }
2639 
MaybeDispatchKeyboardEventAsProcessedByIME()2640 void TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME() {
2641   // If we've already been destroyed, we cannot do anything.
2642   if (mDestroyed) {
2643     MOZ_LOG(
2644         sTextStoreLog, LogLevel::Debug,
2645         ("0x%p   TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2646          "does nothing because it's already been destroyed",
2647          this));
2648     return;
2649   }
2650 
2651   // If we're not handling key message or we've already dispatched a keyboard
2652   // event for the handling key message, we should do nothing anymore.
2653   if (!sHandlingKeyMsg || sIsKeyboardEventDispatched) {
2654     MOZ_LOG(
2655         sTextStoreLog, LogLevel::Debug,
2656         ("0x%p   TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2657          "does nothing because not necessary to dispatch keyboard event",
2658          this));
2659     return;
2660   }
2661 
2662   sIsKeyboardEventDispatched = true;
2663   // If the document is locked, just adding the task to dispatching an event
2664   // to the queue.
2665   if (IsReadLocked()) {
2666     MOZ_LOG(
2667         sTextStoreLog, LogLevel::Debug,
2668         ("0x%p   TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2669          "adding to dispatch a keyboard event into the queue...",
2670          this));
2671     PendingAction* action = mPendingActions.AppendElement();
2672     action->mType = PendingAction::Type::eKeyboardEvent;
2673     memcpy(&action->mKeyMsg, sHandlingKeyMsg, sizeof(MSG));
2674     return;
2675   }
2676 
2677   // Otherwise, dispatch a keyboard event.
2678   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2679           ("0x%p   TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2680            "trying to dispatch a keyboard event...",
2681            this));
2682   DispatchKeyboardEventAsProcessedByIME(*sHandlingKeyMsg);
2683 }
2684 
DispatchKeyboardEventAsProcessedByIME(const MSG & aMsg)2685 void TSFTextStore::DispatchKeyboardEventAsProcessedByIME(const MSG& aMsg) {
2686   MOZ_ASSERT(mWidget);
2687   MOZ_ASSERT(!mWidget->Destroyed());
2688   MOZ_ASSERT(!mDestroyed);
2689 
2690   ModifierKeyState modKeyState;
2691   MSG msg(aMsg);
2692   msg.wParam = VK_PROCESSKEY;
2693   NativeKey nativeKey(mWidget, msg, modKeyState);
2694   switch (aMsg.message) {
2695     case WM_KEYDOWN:
2696       MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2697               ("0x%p   TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2698                "dispatching an eKeyDown event...",
2699                this));
2700       nativeKey.HandleKeyDownMessage();
2701       break;
2702     case WM_KEYUP:
2703       MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2704               ("0x%p   TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2705                "dispatching an eKeyUp event...",
2706                this));
2707       nativeKey.HandleKeyUpMessage();
2708       break;
2709     default:
2710       MOZ_LOG(sTextStoreLog, LogLevel::Error,
2711               ("0x%p   TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2712                "ERROR, it doesn't handle the message",
2713                this));
2714       break;
2715   }
2716 }
2717 
2718 STDMETHODIMP
GetStatus(TS_STATUS * pdcs)2719 TSFTextStore::GetStatus(TS_STATUS* pdcs) {
2720   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2721           ("0x%p TSFTextStore::GetStatus(pdcs=0x%p)", this, pdcs));
2722 
2723   if (!pdcs) {
2724     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2725             ("0x%p   TSFTextStore::GetStatus() FAILED due to null pdcs", this));
2726     return E_INVALIDARG;
2727   }
2728   // We manage on-screen keyboard by own.
2729   pdcs->dwDynamicFlags = TS_SD_INPUTPANEMANUALDISPLAYENABLE;
2730   // we use a "flat" text model for TSF support so no hidden text
2731   pdcs->dwStaticFlags = TS_SS_NOHIDDENTEXT;
2732   return S_OK;
2733 }
2734 
2735 STDMETHODIMP
QueryInsert(LONG acpTestStart,LONG acpTestEnd,ULONG cch,LONG * pacpResultStart,LONG * pacpResultEnd)2736 TSFTextStore::QueryInsert(LONG acpTestStart, LONG acpTestEnd, ULONG cch,
2737                           LONG* pacpResultStart, LONG* pacpResultEnd) {
2738   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2739           ("0x%p TSFTextStore::QueryInsert(acpTestStart=%ld, "
2740            "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)",
2741            this, acpTestStart, acpTestEnd, cch, acpTestStart, acpTestEnd));
2742 
2743   if (!pacpResultStart || !pacpResultEnd) {
2744     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2745             ("0x%p   TSFTextStore::QueryInsert() FAILED due to "
2746              "the null argument",
2747              this));
2748     return E_INVALIDARG;
2749   }
2750 
2751   if (acpTestStart < 0 || acpTestStart > acpTestEnd) {
2752     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2753             ("0x%p   TSFTextStore::QueryInsert() FAILED due to "
2754              "wrong argument",
2755              this));
2756     return E_INVALIDARG;
2757   }
2758 
2759   // XXX need to adjust to cluster boundary
2760   // Assume we are given good offsets for now
2761   if (IsWin8OrLater() && !mComposition.IsComposing() &&
2762       ((TSFPrefs::NeedToHackQueryInsertForMSTraditionalTIP() &&
2763         TSFStaticSink::IsMSChangJieOrMSQuickActive()) ||
2764        (TSFPrefs::NeedToHackQueryInsertForMSSimplifiedTIP() &&
2765         TSFStaticSink::IsMSPinyinOrMSWubiActive()))) {
2766     MOZ_LOG(sTextStoreLog, LogLevel::Warning,
2767             ("0x%p   TSFTextStore::QueryInsert() WARNING using different "
2768              "result for the TIP",
2769              this));
2770     // Chinese TIPs of Microsoft assume that QueryInsert() returns selected
2771     // range which should be removed.
2772     *pacpResultStart = acpTestStart;
2773     *pacpResultEnd = acpTestEnd;
2774   } else {
2775     *pacpResultStart = acpTestStart;
2776     *pacpResultEnd = acpTestStart + cch;
2777   }
2778 
2779   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2780           ("0x%p  TSFTextStore::QueryInsert() succeeded: "
2781            "*pacpResultStart=%ld, *pacpResultEnd=%ld)",
2782            this, *pacpResultStart, *pacpResultEnd));
2783   return S_OK;
2784 }
2785 
2786 STDMETHODIMP
GetSelection(ULONG ulIndex,ULONG ulCount,TS_SELECTION_ACP * pSelection,ULONG * pcFetched)2787 TSFTextStore::GetSelection(ULONG ulIndex, ULONG ulCount,
2788                            TS_SELECTION_ACP* pSelection, ULONG* pcFetched) {
2789   MOZ_LOG(sTextStoreLog, LogLevel::Info,
2790           ("0x%p TSFTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, "
2791            "pSelection=0x%p, pcFetched=0x%p)",
2792            this, ulIndex, ulCount, pSelection, pcFetched));
2793 
2794   if (!IsReadLocked()) {
2795     MOZ_LOG(
2796         sTextStoreLog, LogLevel::Error,
2797         ("0x%p   TSFTextStore::GetSelection() FAILED due to not locked", this));
2798     return TS_E_NOLOCK;
2799   }
2800   if (!ulCount || !pSelection || !pcFetched) {
2801     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2802             ("0x%p   TSFTextStore::GetSelection() FAILED due to "
2803              "null argument",
2804              this));
2805     return E_INVALIDARG;
2806   }
2807 
2808   *pcFetched = 0;
2809 
2810   if (ulIndex != static_cast<ULONG>(TS_DEFAULT_SELECTION) && ulIndex != 0) {
2811     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2812             ("0x%p   TSFTextStore::GetSelection() FAILED due to "
2813              "unsupported selection",
2814              this));
2815     return TS_E_NOSELECTION;
2816   }
2817 
2818   Selection& selectionForTSF = SelectionForTSFRef();
2819   if (selectionForTSF.IsDirty()) {
2820     if (DoNotReturnErrorFromGetSelection()) {
2821       AutoSetTemporarySelection temprarySetter(selectionForTSF);
2822       *pSelection = selectionForTSF.ACP();
2823       *pcFetched = 1;
2824       MOZ_LOG(
2825           sTextStoreLog, LogLevel::Info,
2826           ("0x%p   TSFTextStore::GetSelection() returns fake selection range "
2827            "for avoiding a crash in TSF, "
2828            "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2829            this, selectionForTSF.StartOffset(), selectionForTSF.EndOffset(),
2830            selectionForTSF.Length(),
2831            GetBoolName(selectionForTSF.IsReversed())));
2832       return S_OK;
2833     }
2834     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2835             ("0x%p   TSFTextStore::GetSelection() FAILED due to "
2836              "SelectionForTSFRef() failure",
2837              this));
2838     return E_FAIL;
2839   }
2840   *pSelection = selectionForTSF.ACP();
2841   *pcFetched = 1;
2842   MOZ_LOG(
2843       sTextStoreLog, LogLevel::Info,
2844       ("0x%p   TSFTextStore::GetSelection() succeeded, "
2845        "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2846        this, selectionForTSF.StartOffset(), selectionForTSF.EndOffset(),
2847        selectionForTSF.Length(), GetBoolName(selectionForTSF.IsReversed())));
2848   return S_OK;
2849 }
2850 
2851 // static
DoNotReturnErrorFromGetSelection()2852 bool TSFTextStore::DoNotReturnErrorFromGetSelection() {
2853   // There is a crash bug of TSF if we return error from GetSelection().
2854   // That was introduced in Anniversary Update (build 14393, see bug 1312302)
2855   // TODO: We should avoid to run this hack on fixed builds.  When we get
2856   //       exact build number, we should get back here.
2857   static bool sTSFMayCrashIfGetSelectionReturnsError =
2858       IsWindows10BuildOrLater(14393);
2859   return sTSFMayCrashIfGetSelectionReturnsError;
2860 }
2861 
ContentForTSFRef()2862 TSFTextStore::Content& TSFTextStore::ContentForTSFRef() {
2863   // This should be called when the document is locked or the content hasn't
2864   // been abandoned yet.
2865   if (NS_WARN_IF(!IsReadLocked() && !mContentForTSF.IsInitialized())) {
2866     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2867             ("0x%p   TSFTextStore::ContentForTSFRef(), FAILED, due to "
2868              "called wrong timing, IsReadLocked()=%s, "
2869              "mContentForTSF.IsInitialized()=%s",
2870              this, GetBoolName(IsReadLocked()),
2871              GetBoolName(mContentForTSF.IsInitialized())));
2872     mContentForTSF.Clear();
2873     return mContentForTSF;
2874   }
2875 
2876   Selection& selectionForTSF = SelectionForTSFRef();
2877   if (selectionForTSF.IsDirty()) {
2878     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2879             ("0x%p   TSFTextStore::ContentForTSFRef(), FAILED, due to "
2880              "SelectionForTSFRef() failure",
2881              this));
2882     mContentForTSF.Clear();
2883     return mContentForTSF;
2884   }
2885 
2886   if (!mContentForTSF.IsInitialized()) {
2887     nsAutoString text;
2888     if (NS_WARN_IF(!GetCurrentText(text))) {
2889       MOZ_LOG(sTextStoreLog, LogLevel::Error,
2890               ("0x%p   TSFTextStore::ContentForTSFRef(), FAILED, due to "
2891                "GetCurrentText() failure",
2892                this));
2893       mContentForTSF.Clear();
2894       return mContentForTSF;
2895     }
2896 
2897     mContentForTSF.Init(text);
2898     // Basically, the cached content which is expected by TSF/TIP should be
2899     // cleared after active composition is committed or the document lock is
2900     // unlocked.  However, in e10s mode, content will be modified
2901     // asynchronously.  In such case, mDeferClearingContentForTSF may be
2902     // true until whole dispatched events are handled by the focused editor.
2903     mDeferClearingContentForTSF = false;
2904   }
2905 
2906   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2907           ("0x%p   TSFTextStore::ContentForTSFRef(): "
2908            "mContentForTSF={ mText=\"%s\" (Length()=%u), "
2909            "mLastCompositionString=\"%s\" (Length()=%u), "
2910            "mMinTextModifiedOffset=%u }",
2911            this,
2912            mContentForTSF.Text().Length() <= 40
2913                ? GetEscapedUTF8String(mContentForTSF.Text()).get()
2914                : "<omitted>",
2915            mContentForTSF.Text().Length(),
2916            GetEscapedUTF8String(mContentForTSF.LastCompositionString()).get(),
2917            mContentForTSF.LastCompositionString().Length(),
2918            mContentForTSF.MinTextModifiedOffset()));
2919 
2920   return mContentForTSF;
2921 }
2922 
CanAccessActualContentDirectly() const2923 bool TSFTextStore::CanAccessActualContentDirectly() const {
2924   if (!mContentForTSF.IsInitialized() || mSelectionForTSF.IsDirty()) {
2925     return true;
2926   }
2927 
2928   // If the cached content has been changed by something except composition,
2929   // the content cache may be different from actual content.
2930   if (mPendingTextChangeData.IsValid() &&
2931       !mPendingTextChangeData.mCausedOnlyByComposition) {
2932     return false;
2933   }
2934 
2935   // If the cached selection isn't changed, cached content and actual content
2936   // should be same.
2937   if (!mPendingSelectionChangeData.IsValid()) {
2938     return true;
2939   }
2940 
2941   return mSelectionForTSF.EqualsExceptDirection(mPendingSelectionChangeData);
2942 }
2943 
GetCurrentText(nsAString & aTextContent)2944 bool TSFTextStore::GetCurrentText(nsAString& aTextContent) {
2945   if (mContentForTSF.IsInitialized()) {
2946     aTextContent = mContentForTSF.Text();
2947     return true;
2948   }
2949 
2950   MOZ_ASSERT(!mDestroyed);
2951   MOZ_ASSERT(mWidget && !mWidget->Destroyed());
2952 
2953   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
2954           ("0x%p   TSFTextStore::GetCurrentText(): "
2955            "retrieving text from the content...",
2956            this));
2957 
2958   WidgetQueryContentEvent queryText(true, eQueryTextContent, mWidget);
2959   queryText.InitForQueryTextContent(0, UINT32_MAX);
2960   mWidget->InitEvent(queryText);
2961   DispatchEvent(queryText);
2962   if (NS_WARN_IF(!queryText.mSucceeded)) {
2963     MOZ_LOG(sTextStoreLog, LogLevel::Error,
2964             ("0x%p   TSFTextStore::GetCurrentText(), FAILED, due to "
2965              "eQueryTextContent failure",
2966              this));
2967     aTextContent.Truncate();
2968     return false;
2969   }
2970 
2971   aTextContent = queryText.mReply.mString;
2972   return true;
2973 }
2974 
SelectionForTSFRef()2975 TSFTextStore::Selection& TSFTextStore::SelectionForTSFRef() {
2976   if (mSelectionForTSF.IsDirty()) {
2977     MOZ_ASSERT(!mDestroyed);
2978     // If the window has never been available, we should crash since working
2979     // with broken values may make TIP confused.
2980     if (!mWidget || mWidget->Destroyed()) {
2981       MOZ_CRASH();
2982     }
2983 
2984     WidgetQueryContentEvent querySelection(true, eQuerySelectedText, mWidget);
2985     mWidget->InitEvent(querySelection);
2986     DispatchEvent(querySelection);
2987     if (NS_WARN_IF(!querySelection.mSucceeded)) {
2988       return mSelectionForTSF;
2989     }
2990 
2991     mSelectionForTSF.SetSelection(
2992         querySelection.mReply.mOffset, querySelection.mReply.mString.Length(),
2993         querySelection.mReply.mReversed, querySelection.GetWritingMode());
2994   }
2995 
2996   MOZ_LOG(
2997       sTextStoreLog, LogLevel::Debug,
2998       ("0x%p   TSFTextStore::SelectionForTSFRef(): "
2999        "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
3000        this, mSelectionForTSF.StartOffset(), mSelectionForTSF.EndOffset(),
3001        mSelectionForTSF.Length(), GetBoolName(mSelectionForTSF.IsReversed())));
3002 
3003   return mSelectionForTSF;
3004 }
3005 
GetRangeExtent(ITfRange * aRange,LONG * aStart,LONG * aLength)3006 static HRESULT GetRangeExtent(ITfRange* aRange, LONG* aStart, LONG* aLength) {
3007   RefPtr<ITfRangeACP> rangeACP;
3008   aRange->QueryInterface(IID_ITfRangeACP, getter_AddRefs(rangeACP));
3009   NS_ENSURE_TRUE(rangeACP, E_FAIL);
3010   return rangeACP->GetExtent(aStart, aLength);
3011 }
3012 
GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE & aDisplayAttr)3013 static TextRangeType GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE& aDisplayAttr) {
3014   switch (aDisplayAttr.bAttr) {
3015     case TF_ATTR_TARGET_CONVERTED:
3016       return TextRangeType::eSelectedClause;
3017     case TF_ATTR_CONVERTED:
3018       return TextRangeType::eConvertedClause;
3019     case TF_ATTR_TARGET_NOTCONVERTED:
3020       return TextRangeType::eSelectedRawClause;
3021     default:
3022       return TextRangeType::eRawClause;
3023   }
3024 }
3025 
3026 HRESULT
GetDisplayAttribute(ITfProperty * aAttrProperty,ITfRange * aRange,TF_DISPLAYATTRIBUTE * aResult)3027 TSFTextStore::GetDisplayAttribute(ITfProperty* aAttrProperty, ITfRange* aRange,
3028                                   TF_DISPLAYATTRIBUTE* aResult) {
3029   NS_ENSURE_TRUE(aAttrProperty, E_FAIL);
3030   NS_ENSURE_TRUE(aRange, E_FAIL);
3031   NS_ENSURE_TRUE(aResult, E_FAIL);
3032 
3033   HRESULT hr;
3034 
3035   if (MOZ_LOG_TEST(sTextStoreLog, LogLevel::Debug)) {
3036     LONG start = 0, length = 0;
3037     hr = GetRangeExtent(aRange, &start, &length);
3038     MOZ_LOG(
3039         sTextStoreLog, LogLevel::Debug,
3040         ("0x%p   TSFTextStore::GetDisplayAttribute(): "
3041          "GetDisplayAttribute range=%ld-%ld (hr=%s)",
3042          this, start - mComposition.mStart,
3043          start - mComposition.mStart + length, GetCommonReturnValueName(hr)));
3044   }
3045 
3046   VARIANT propValue;
3047   ::VariantInit(&propValue);
3048   hr = aAttrProperty->GetValue(TfEditCookie(mEditCookie), aRange, &propValue);
3049   if (FAILED(hr)) {
3050     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3051             ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
3052              "ITfProperty::GetValue() failed",
3053              this));
3054     return hr;
3055   }
3056   if (VT_I4 != propValue.vt) {
3057     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3058             ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
3059              "ITfProperty::GetValue() returns non-VT_I4 value",
3060              this));
3061     ::VariantClear(&propValue);
3062     return E_FAIL;
3063   }
3064 
3065   RefPtr<ITfCategoryMgr> categoryMgr = GetCategoryMgr();
3066   if (NS_WARN_IF(!categoryMgr)) {
3067     return E_FAIL;
3068   }
3069   GUID guid;
3070   hr = categoryMgr->GetGUID(DWORD(propValue.lVal), &guid);
3071   ::VariantClear(&propValue);
3072   if (FAILED(hr)) {
3073     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3074             ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
3075              "ITfCategoryMgr::GetGUID() failed",
3076              this));
3077     return hr;
3078   }
3079 
3080   RefPtr<ITfDisplayAttributeMgr> displayAttrMgr = GetDisplayAttributeMgr();
3081   if (NS_WARN_IF(!displayAttrMgr)) {
3082     return E_FAIL;
3083   }
3084   RefPtr<ITfDisplayAttributeInfo> info;
3085   hr = displayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info),
3086                                                nullptr);
3087   if (FAILED(hr) || !info) {
3088     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3089             ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
3090              "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed",
3091              this));
3092     return hr;
3093   }
3094 
3095   hr = info->GetAttributeInfo(aResult);
3096   if (FAILED(hr)) {
3097     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3098             ("0x%p   TSFTextStore::GetDisplayAttribute() FAILED due to "
3099              "ITfDisplayAttributeInfo::GetAttributeInfo() failed",
3100              this));
3101     return hr;
3102   }
3103 
3104   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3105           ("0x%p   TSFTextStore::GetDisplayAttribute() succeeded: "
3106            "Result={ %s }",
3107            this, GetDisplayAttrStr(*aResult).get()));
3108   return S_OK;
3109 }
3110 
3111 HRESULT
RestartCompositionIfNecessary(ITfRange * aRangeNew)3112 TSFTextStore::RestartCompositionIfNecessary(ITfRange* aRangeNew) {
3113   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3114           ("0x%p   TSFTextStore::RestartCompositionIfNecessary("
3115            "aRangeNew=0x%p), mComposition.mView=0x%p",
3116            this, aRangeNew, mComposition.mView.get()));
3117 
3118   if (!mComposition.IsComposing()) {
3119     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3120             ("0x%p   TSFTextStore::RestartCompositionIfNecessary() FAILED "
3121              "due to no composition view",
3122              this));
3123     return E_FAIL;
3124   }
3125 
3126   HRESULT hr;
3127   RefPtr<ITfCompositionView> pComposition(mComposition.mView);
3128   RefPtr<ITfRange> composingRange(aRangeNew);
3129   if (!composingRange) {
3130     hr = pComposition->GetRange(getter_AddRefs(composingRange));
3131     if (FAILED(hr)) {
3132       MOZ_LOG(sTextStoreLog, LogLevel::Error,
3133               ("0x%p   TSFTextStore::RestartCompositionIfNecessary() "
3134                "FAILED due to pComposition->GetRange() failure",
3135                this));
3136       return hr;
3137     }
3138   }
3139 
3140   // Get starting offset of the composition
3141   LONG compStart = 0, compLength = 0;
3142   hr = GetRangeExtent(composingRange, &compStart, &compLength);
3143   if (FAILED(hr)) {
3144     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3145             ("0x%p   TSFTextStore::RestartCompositionIfNecessary() FAILED "
3146              "due to GetRangeExtent() failure",
3147              this));
3148     return hr;
3149   }
3150 
3151   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3152           ("0x%p   TSFTextStore::RestartCompositionIfNecessary(), "
3153            "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }",
3154            this, compStart, compStart + compLength, mComposition.mStart,
3155            mComposition.mString.Length()));
3156 
3157   if (mComposition.mStart != compStart ||
3158       mComposition.mString.Length() != (ULONG)compLength) {
3159     // If the queried composition length is different from the length
3160     // of our composition string, OnUpdateComposition is being called
3161     // because a part of the original composition was committed.
3162     hr = RestartComposition(pComposition, composingRange);
3163     if (FAILED(hr)) {
3164       MOZ_LOG(sTextStoreLog, LogLevel::Error,
3165               ("0x%p   TSFTextStore::RestartCompositionIfNecessary() "
3166                "FAILED due to RestartComposition() failure",
3167                this));
3168       return hr;
3169     }
3170   }
3171 
3172   MOZ_LOG(
3173       sTextStoreLog, LogLevel::Debug,
3174       ("0x%p   TSFTextStore::RestartCompositionIfNecessary() succeeded", this));
3175   return S_OK;
3176 }
3177 
3178 HRESULT
RestartComposition(ITfCompositionView * aCompositionView,ITfRange * aNewRange)3179 TSFTextStore::RestartComposition(ITfCompositionView* aCompositionView,
3180                                  ITfRange* aNewRange) {
3181   Selection& selectionForTSF = SelectionForTSFRef();
3182 
3183   LONG newStart, newLength;
3184   HRESULT hr = GetRangeExtent(aNewRange, &newStart, &newLength);
3185   LONG newEnd = newStart + newLength;
3186 
3187   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3188           ("0x%p   TSFTextStore::RestartComposition(aCompositionView=0x%p, "
3189            "aNewRange=0x%p { newStart=%d, newLength=%d }), "
3190            "mComposition={ mStart=%d, mCompositionString.Length()=%d }, "
3191            "selectionForTSF={ IsDirty()=%s, StartOffset()=%d, Length()=%d }",
3192            this, aCompositionView, aNewRange, newStart, newLength,
3193            mComposition.mStart, mComposition.mString.Length(),
3194            GetBoolName(selectionForTSF.IsDirty()),
3195            selectionForTSF.StartOffset(), selectionForTSF.Length()));
3196 
3197   if (selectionForTSF.IsDirty()) {
3198     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3199             ("0x%p   TSFTextStore::RestartComposition() FAILED "
3200              "due to SelectionForTSFRef() failure",
3201              this));
3202     return E_FAIL;
3203   }
3204 
3205   if (FAILED(hr)) {
3206     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3207             ("0x%p   TSFTextStore::RestartComposition() FAILED "
3208              "due to GetRangeExtent() failure",
3209              this));
3210     return hr;
3211   }
3212 
3213   // If the new range has no overlap with the crrent range, we just commit
3214   // the composition and restart new composition with the new range but
3215   // current selection range should be preserved.
3216   if (newStart >= mComposition.EndOffset() || newEnd <= mComposition.mStart) {
3217     RecordCompositionEndAction();
3218     RecordCompositionStartAction(aCompositionView, newStart, newLength, true);
3219     return S_OK;
3220   }
3221 
3222   // If the new range has an overlap with the current one, we should not commit
3223   // the whole current range to avoid creating an odd undo transaction.
3224   // I.e., the overlapped range which is being composed should not appear in
3225   // undo transaction.
3226 
3227   // Backup current composition data and selection data.
3228   Composition oldComposition = mComposition;
3229   Selection oldSelection = selectionForTSF;
3230 
3231   // Commit only the part of composition.
3232   LONG keepComposingStartOffset = std::max(mComposition.mStart, newStart);
3233   LONG keepComposingEndOffset = std::min(mComposition.EndOffset(), newEnd);
3234   MOZ_ASSERT(
3235       keepComposingStartOffset <= keepComposingEndOffset,
3236       "Why keepComposingEndOffset is smaller than keepComposingStartOffset?");
3237   LONG keepComposingLength = keepComposingEndOffset - keepComposingStartOffset;
3238   // Remove the overlapped part from the commit string.
3239   nsAutoString commitString(mComposition.mString);
3240   commitString.Cut(keepComposingStartOffset - mComposition.mStart,
3241                    keepComposingLength);
3242   // Update the composition string.
3243   Content& contentForTSF = ContentForTSFRef();
3244   if (!contentForTSF.IsInitialized()) {
3245     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3246             ("0x%p   TSFTextStore::RestartComposition() FAILED "
3247              "due to ContentForTSFRef() failure",
3248              this));
3249     return E_FAIL;
3250   }
3251   contentForTSF.ReplaceTextWith(mComposition.mStart,
3252                                 mComposition.mString.Length(), commitString);
3253   // Record a compositionupdate action for commit the part of composing string.
3254   PendingAction* action = LastOrNewPendingCompositionUpdate();
3255   action->mData = mComposition.mString;
3256   action->mRanges->Clear();
3257   // Note that we shouldn't append ranges when composition string
3258   // is empty because it may cause TextComposition confused.
3259   if (!action->mData.IsEmpty()) {
3260     TextRange caretRange;
3261     caretRange.mStartOffset = caretRange.mEndOffset =
3262         uint32_t(oldComposition.mStart + commitString.Length());
3263     caretRange.mRangeType = TextRangeType::eCaret;
3264     action->mRanges->AppendElement(caretRange);
3265   }
3266   action->mIncomplete = false;
3267 
3268   // Record compositionend action.
3269   RecordCompositionEndAction();
3270 
3271   // Record compositionstart action only with the new start since this method
3272   // hasn't restored composing string yet.
3273   RecordCompositionStartAction(aCompositionView, newStart, 0, false);
3274 
3275   // Restore the latest text content and selection.
3276   contentForTSF.ReplaceSelectedTextWith(nsDependentSubstring(
3277       oldComposition.mString, keepComposingStartOffset - oldComposition.mStart,
3278       keepComposingLength));
3279   selectionForTSF = oldSelection;
3280 
3281   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3282           ("0x%p   TSFTextStore::RestartComposition() succeeded, "
3283            "mComposition={ mStart=%d, mCompositionString.Length()=%d }, "
3284            "selectionForTSF={ IsDirty()=%s, StartOffset()=%d, Length()=%d }",
3285            this, mComposition.mStart, mComposition.mString.Length(),
3286            GetBoolName(selectionForTSF.IsDirty()),
3287            selectionForTSF.StartOffset(), selectionForTSF.Length()));
3288 
3289   return S_OK;
3290 }
3291 
GetColor(const TF_DA_COLOR & aTSFColor,nscolor & aResult)3292 static bool GetColor(const TF_DA_COLOR& aTSFColor, nscolor& aResult) {
3293   switch (aTSFColor.type) {
3294     case TF_CT_SYSCOLOR: {
3295       DWORD sysColor = ::GetSysColor(aTSFColor.nIndex);
3296       aResult =
3297           NS_RGB(GetRValue(sysColor), GetGValue(sysColor), GetBValue(sysColor));
3298       return true;
3299     }
3300     case TF_CT_COLORREF:
3301       aResult = NS_RGB(GetRValue(aTSFColor.cr), GetGValue(aTSFColor.cr),
3302                        GetBValue(aTSFColor.cr));
3303       return true;
3304     case TF_CT_NONE:
3305     default:
3306       return false;
3307   }
3308 }
3309 
GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle,TextRangeStyle::LineStyle & aTextRangeLineStyle)3310 static bool GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle,
3311                          TextRangeStyle::LineStyle& aTextRangeLineStyle) {
3312   switch (aTSFLineStyle) {
3313     case TF_LS_NONE:
3314       aTextRangeLineStyle = TextRangeStyle::LineStyle::None;
3315       return true;
3316     case TF_LS_SOLID:
3317       aTextRangeLineStyle = TextRangeStyle::LineStyle::Solid;
3318       return true;
3319     case TF_LS_DOT:
3320       aTextRangeLineStyle = TextRangeStyle::LineStyle::Dotted;
3321       return true;
3322     case TF_LS_DASH:
3323       aTextRangeLineStyle = TextRangeStyle::LineStyle::Dashed;
3324       return true;
3325     case TF_LS_SQUIGGLE:
3326       aTextRangeLineStyle = TextRangeStyle::LineStyle::Wavy;
3327       return true;
3328     default:
3329       return false;
3330   }
3331 }
3332 
3333 HRESULT
RecordCompositionUpdateAction()3334 TSFTextStore::RecordCompositionUpdateAction() {
3335   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3336           ("0x%p   TSFTextStore::RecordCompositionUpdateAction(), "
3337            "mComposition={ mView=0x%p, mStart=%d, mString=\"%s\" "
3338            "(Length()=%d) }",
3339            this, mComposition.mView.get(), mComposition.mStart,
3340            GetEscapedUTF8String(mComposition.mString).get(),
3341            mComposition.mString.Length()));
3342 
3343   if (!mComposition.IsComposing()) {
3344     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3345             ("0x%p   TSFTextStore::RecordCompositionUpdateAction() FAILED "
3346              "due to no composition view",
3347              this));
3348     return E_FAIL;
3349   }
3350 
3351   // Getting display attributes is *really* complicated!
3352   // We first get the context and the property objects to query for
3353   // attributes, but since a big range can have a variety of values for
3354   // the attribute, we have to find out all the ranges that have distinct
3355   // attribute values. Then we query for what the value represents through
3356   // the display attribute manager and translate that to TextRange to be
3357   // sent in eCompositionChange
3358 
3359   RefPtr<ITfProperty> attrPropetry;
3360   HRESULT hr =
3361       mContext->GetProperty(GUID_PROP_ATTRIBUTE, getter_AddRefs(attrPropetry));
3362   if (FAILED(hr) || !attrPropetry) {
3363     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3364             ("0x%p   TSFTextStore::RecordCompositionUpdateAction() FAILED "
3365              "due to mContext->GetProperty() failure",
3366              this));
3367     return FAILED(hr) ? hr : E_FAIL;
3368   }
3369 
3370   RefPtr<ITfRange> composingRange;
3371   hr = mComposition.mView->GetRange(getter_AddRefs(composingRange));
3372   if (FAILED(hr)) {
3373     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3374             ("0x%p   TSFTextStore::RecordCompositionUpdateAction() "
3375              "FAILED due to mComposition.mView->GetRange() failure",
3376              this));
3377     return hr;
3378   }
3379 
3380   RefPtr<IEnumTfRanges> enumRanges;
3381   hr = attrPropetry->EnumRanges(TfEditCookie(mEditCookie),
3382                                 getter_AddRefs(enumRanges), composingRange);
3383   if (FAILED(hr) || !enumRanges) {
3384     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3385             ("0x%p   TSFTextStore::RecordCompositionUpdateAction() FAILED "
3386              "due to attrPropetry->EnumRanges() failure",
3387              this));
3388     return FAILED(hr) ? hr : E_FAIL;
3389   }
3390 
3391   // First, put the log of content and selection here.
3392   Selection& selectionForTSF = SelectionForTSFRef();
3393   if (selectionForTSF.IsDirty()) {
3394     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3395             ("0x%p   TSFTextStore::RecordCompositionUpdateAction() FAILED "
3396              "due to SelectionForTSFRef() failure",
3397              this));
3398     return E_FAIL;
3399   }
3400 
3401   PendingAction* action = LastOrNewPendingCompositionUpdate();
3402   action->mData = mComposition.mString;
3403   // The ranges might already have been initialized, however, if this is
3404   // called again, that means we need to overwrite the ranges with current
3405   // information.
3406   action->mRanges->Clear();
3407 
3408   // Note that we shouldn't append ranges when composition string
3409   // is empty because it may cause TextComposition confused.
3410   if (!action->mData.IsEmpty()) {
3411     TextRange newRange;
3412     // No matter if we have display attribute info or not,
3413     // we always pass in at least one range to eCompositionChange
3414     newRange.mStartOffset = 0;
3415     newRange.mEndOffset = action->mData.Length();
3416     newRange.mRangeType = TextRangeType::eRawClause;
3417     action->mRanges->AppendElement(newRange);
3418 
3419     RefPtr<ITfRange> range;
3420     while (enumRanges->Next(1, getter_AddRefs(range), nullptr) == S_OK) {
3421       if (NS_WARN_IF(!range)) {
3422         break;
3423       }
3424 
3425       LONG rangeStart = 0, rangeLength = 0;
3426       if (FAILED(GetRangeExtent(range, &rangeStart, &rangeLength))) {
3427         continue;
3428       }
3429       // The range may include out of composition string.  We should ignore
3430       // outside of the composition string.
3431       LONG start = std::min(std::max(rangeStart, mComposition.mStart),
3432                             mComposition.EndOffset());
3433       LONG end =
3434           std::max(std::min(rangeStart + rangeLength, mComposition.EndOffset()),
3435                    mComposition.mStart);
3436       LONG length = end - start;
3437       if (length < 0) {
3438         MOZ_LOG(sTextStoreLog, LogLevel::Error,
3439                 ("0x%p   TSFTextStore::RecordCompositionUpdateAction() "
3440                  "ignores invalid range (%d-%d)",
3441                  this, rangeStart - mComposition.mStart,
3442                  rangeStart - mComposition.mStart + rangeLength));
3443         continue;
3444       }
3445       if (!length) {
3446         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3447                 ("0x%p   TSFTextStore::RecordCompositionUpdateAction() "
3448                  "ignores a range due to outside of the composition or empty "
3449                  "(%d-%d)",
3450                  this, rangeStart - mComposition.mStart,
3451                  rangeStart - mComposition.mStart + rangeLength));
3452         continue;
3453       }
3454 
3455       TextRange newRange;
3456       newRange.mStartOffset = uint32_t(start - mComposition.mStart);
3457       // The end of the last range in the array is
3458       // always kept at the end of composition
3459       newRange.mEndOffset = mComposition.mString.Length();
3460 
3461       TF_DISPLAYATTRIBUTE attr;
3462       hr = GetDisplayAttribute(attrPropetry, range, &attr);
3463       if (FAILED(hr)) {
3464         newRange.mRangeType = TextRangeType::eRawClause;
3465       } else {
3466         newRange.mRangeType = GetGeckoSelectionValue(attr);
3467         if (GetColor(attr.crText, newRange.mRangeStyle.mForegroundColor)) {
3468           newRange.mRangeStyle.mDefinedStyles |=
3469               TextRangeStyle::DEFINED_FOREGROUND_COLOR;
3470         }
3471         if (GetColor(attr.crBk, newRange.mRangeStyle.mBackgroundColor)) {
3472           newRange.mRangeStyle.mDefinedStyles |=
3473               TextRangeStyle::DEFINED_BACKGROUND_COLOR;
3474         }
3475         if (GetColor(attr.crLine, newRange.mRangeStyle.mUnderlineColor)) {
3476           newRange.mRangeStyle.mDefinedStyles |=
3477               TextRangeStyle::DEFINED_UNDERLINE_COLOR;
3478         }
3479         if (GetLineStyle(attr.lsStyle, newRange.mRangeStyle.mLineStyle)) {
3480           newRange.mRangeStyle.mDefinedStyles |=
3481               TextRangeStyle::DEFINED_LINESTYLE;
3482           newRange.mRangeStyle.mIsBoldLine = attr.fBoldLine != 0;
3483         }
3484       }
3485 
3486       TextRange& lastRange = action->mRanges->LastElement();
3487       if (lastRange.mStartOffset == newRange.mStartOffset) {
3488         // Replace range if last range is the same as this one
3489         // So that ranges don't overlap and confuse the editor
3490         lastRange = newRange;
3491       } else {
3492         lastRange.mEndOffset = newRange.mStartOffset;
3493         action->mRanges->AppendElement(newRange);
3494       }
3495     }
3496 
3497     // We need to hack for Korean Input System which is Korean standard TIP.
3498     // It sets no change style to IME selection (the selection is always only
3499     // one).  So, the composition string looks like normal (or committed)
3500     // string.  At this time, current selection range is same as the
3501     // composition string range.  Other applications set a wide caret which
3502     // covers the composition string,  however, Gecko doesn't support the wide
3503     // caret drawing now (Gecko doesn't support XOR drawing), unfortunately.
3504     // For now, we should change the range style to undefined.
3505     if (!selectionForTSF.IsCollapsed() && action->mRanges->Length() == 1) {
3506       TextRange& range = action->mRanges->ElementAt(0);
3507       LONG start = selectionForTSF.MinOffset();
3508       LONG end = selectionForTSF.MaxOffset();
3509       if ((LONG)range.mStartOffset == start - mComposition.mStart &&
3510           (LONG)range.mEndOffset == end - mComposition.mStart &&
3511           range.mRangeStyle.IsNoChangeStyle()) {
3512         range.mRangeStyle.Clear();
3513         // The looks of selected type is better than others.
3514         range.mRangeType = TextRangeType::eSelectedRawClause;
3515       }
3516     }
3517 
3518     // The caret position has to be collapsed.
3519     uint32_t caretPosition = static_cast<uint32_t>(selectionForTSF.MaxOffset() -
3520                                                    mComposition.mStart);
3521 
3522     // If caret is in the target clause and it doesn't have specific style,
3523     // the target clause will be painted as normal selection range.  Since
3524     // caret shouldn't be in selection range on Windows, we shouldn't append
3525     // caret range in such case.
3526     const TextRange* targetClause = action->mRanges->GetTargetClause();
3527     if (!targetClause || targetClause->mRangeStyle.IsDefined() ||
3528         caretPosition < targetClause->mStartOffset ||
3529         caretPosition > targetClause->mEndOffset) {
3530       TextRange caretRange;
3531       caretRange.mStartOffset = caretRange.mEndOffset = caretPosition;
3532       caretRange.mRangeType = TextRangeType::eCaret;
3533       action->mRanges->AppendElement(caretRange);
3534     }
3535   }
3536 
3537   action->mIncomplete = false;
3538 
3539   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3540           ("0x%p   TSFTextStore::RecordCompositionUpdateAction() "
3541            "succeeded",
3542            this));
3543 
3544   return S_OK;
3545 }
3546 
3547 HRESULT
SetSelectionInternal(const TS_SELECTION_ACP * pSelection,bool aDispatchCompositionChangeEvent)3548 TSFTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection,
3549                                    bool aDispatchCompositionChangeEvent) {
3550   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
3551           ("0x%p   TSFTextStore::SetSelectionInternal(pSelection={ "
3552            "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, "
3553            "aDispatchCompositionChangeEvent=%s), mComposition.IsComposing()=%s",
3554            this, pSelection->acpStart, pSelection->acpEnd,
3555            GetActiveSelEndName(pSelection->style.ase),
3556            GetBoolName(pSelection->style.fInterimChar),
3557            GetBoolName(aDispatchCompositionChangeEvent),
3558            GetBoolName(mComposition.IsComposing())));
3559 
3560   MOZ_ASSERT(IsReadWriteLocked());
3561 
3562   Selection& selectionForTSF = SelectionForTSFRef();
3563   if (selectionForTSF.IsDirty()) {
3564     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3565             ("0x%p   TSFTextStore::SetSelectionInternal() FAILED due to "
3566              "SelectionForTSFRef() failure",
3567              this));
3568     return E_FAIL;
3569   }
3570 
3571   MaybeDispatchKeyboardEventAsProcessedByIME();
3572   if (mDestroyed) {
3573     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3574             ("0x%p   TSFTextStore::SetSelectionInternal() FAILED due to "
3575              "destroyed during dispatching a keyboard event",
3576              this));
3577     return E_FAIL;
3578   }
3579 
3580   // If actually the range is not changing, we should do nothing.
3581   // Perhaps, we can ignore the difference change because it must not be
3582   // important for following edit.
3583   if (selectionForTSF.EqualsExceptDirection(*pSelection)) {
3584     MOZ_LOG(sTextStoreLog, LogLevel::Warning,
3585             ("0x%p   TSFTextStore::SetSelectionInternal() Succeeded but "
3586              "did nothing because the selection range isn't changing",
3587              this));
3588     selectionForTSF.SetSelection(*pSelection);
3589     return S_OK;
3590   }
3591 
3592   if (mComposition.IsComposing()) {
3593     if (aDispatchCompositionChangeEvent) {
3594       HRESULT hr = RestartCompositionIfNecessary();
3595       if (FAILED(hr)) {
3596         MOZ_LOG(sTextStoreLog, LogLevel::Error,
3597                 ("0x%p   TSFTextStore::SetSelectionInternal() FAILED due to "
3598                  "RestartCompositionIfNecessary() failure",
3599                  this));
3600         return hr;
3601       }
3602     }
3603     if (pSelection->acpStart < mComposition.mStart ||
3604         pSelection->acpEnd > mComposition.EndOffset()) {
3605       MOZ_LOG(sTextStoreLog, LogLevel::Error,
3606               ("0x%p   TSFTextStore::SetSelectionInternal() FAILED due to "
3607                "the selection being out of the composition string",
3608                this));
3609       return TS_E_INVALIDPOS;
3610     }
3611     // Emulate selection during compositions
3612     selectionForTSF.SetSelection(*pSelection);
3613     if (aDispatchCompositionChangeEvent) {
3614       HRESULT hr = RecordCompositionUpdateAction();
3615       if (FAILED(hr)) {
3616         MOZ_LOG(sTextStoreLog, LogLevel::Error,
3617                 ("0x%p   TSFTextStore::SetSelectionInternal() FAILED due to "
3618                  "RecordCompositionUpdateAction() failure",
3619                  this));
3620         return hr;
3621       }
3622     }
3623     return S_OK;
3624   }
3625 
3626   TS_SELECTION_ACP selectionInContent(*pSelection);
3627 
3628   // If mContentForTSF caches old contents which is now different from
3629   // actual contents, we need some complicated hack here...
3630   // Note that this hack assumes that this is used for reconversion.
3631   if (mContentForTSF.IsInitialized() && mPendingTextChangeData.IsValid() &&
3632       !mPendingTextChangeData.mCausedOnlyByComposition) {
3633     uint32_t startOffset = static_cast<uint32_t>(selectionInContent.acpStart);
3634     uint32_t endOffset = static_cast<uint32_t>(selectionInContent.acpEnd);
3635     if (mPendingTextChangeData.mStartOffset >= endOffset) {
3636       // Setting selection before any changed ranges is fine.
3637     } else if (mPendingTextChangeData.mRemovedEndOffset <= startOffset) {
3638       // Setting selection after removed range is fine with following
3639       // adjustment.
3640       selectionInContent.acpStart += mPendingTextChangeData.Difference();
3641       selectionInContent.acpEnd += mPendingTextChangeData.Difference();
3642     } else if (startOffset == endOffset) {
3643       // Moving caret position may be fine in most cases even if the insertion
3644       // point has already gone but in this case, composition will be inserted
3645       // to unexpected position, though.
3646       // It seems that moving caret into middle of the new text is odd.
3647       // Perhaps, end of it is expected by users in most cases.
3648       selectionInContent.acpStart = mPendingTextChangeData.mAddedEndOffset;
3649       selectionInContent.acpEnd = selectionInContent.acpStart;
3650     } else {
3651       // Otherwise, i.e., setting range has already gone, we cannot set
3652       // selection properly.
3653       MOZ_LOG(sTextStoreLog, LogLevel::Error,
3654               ("0x%p   TSFTextStore::SetSelectionInternal() FAILED due to "
3655                "there is unknown content change",
3656                this));
3657       return E_FAIL;
3658     }
3659   }
3660 
3661   CompleteLastActionIfStillIncomplete();
3662   PendingAction* action = mPendingActions.AppendElement();
3663   action->mType = PendingAction::Type::eSetSelection;
3664   action->mSelectionStart = selectionInContent.acpStart;
3665   action->mSelectionLength =
3666       selectionInContent.acpEnd - selectionInContent.acpStart;
3667   action->mSelectionReversed = (selectionInContent.style.ase == TS_AE_START);
3668 
3669   // Use TSF specified selection for updating mSelectionForTSF.
3670   selectionForTSF.SetSelection(*pSelection);
3671 
3672   return S_OK;
3673 }
3674 
3675 STDMETHODIMP
SetSelection(ULONG ulCount,const TS_SELECTION_ACP * pSelection)3676 TSFTextStore::SetSelection(ULONG ulCount, const TS_SELECTION_ACP* pSelection) {
3677   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3678           ("0x%p TSFTextStore::SetSelection(ulCount=%lu, pSelection=%p { "
3679            "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), "
3680            "mComposition.IsComposing()=%s",
3681            this, ulCount, pSelection, pSelection ? pSelection->acpStart : 0,
3682            pSelection ? pSelection->acpEnd : 0,
3683            pSelection ? GetActiveSelEndName(pSelection->style.ase) : "",
3684            pSelection ? GetBoolName(pSelection->style.fInterimChar) : "",
3685            GetBoolName(mComposition.IsComposing())));
3686 
3687   if (!IsReadWriteLocked()) {
3688     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3689             ("0x%p   TSFTextStore::SetSelection() FAILED due to "
3690              "not locked (read-write)",
3691              this));
3692     return TS_E_NOLOCK;
3693   }
3694   if (ulCount != 1) {
3695     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3696             ("0x%p   TSFTextStore::SetSelection() FAILED due to "
3697              "trying setting multiple selection",
3698              this));
3699     return E_INVALIDARG;
3700   }
3701   if (!pSelection) {
3702     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3703             ("0x%p   TSFTextStore::SetSelection() FAILED due to "
3704              "null argument",
3705              this));
3706     return E_INVALIDARG;
3707   }
3708 
3709   HRESULT hr = SetSelectionInternal(pSelection, true);
3710   if (FAILED(hr)) {
3711     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3712             ("0x%p   TSFTextStore::SetSelection() FAILED due to "
3713              "SetSelectionInternal() failure",
3714              this));
3715   } else {
3716     MOZ_LOG(sTextStoreLog, LogLevel::Info,
3717             ("0x%p   TSFTextStore::SetSelection() succeeded", this));
3718   }
3719   return hr;
3720 }
3721 
3722 STDMETHODIMP
GetText(LONG acpStart,LONG acpEnd,WCHAR * pchPlain,ULONG cchPlainReq,ULONG * pcchPlainOut,TS_RUNINFO * prgRunInfo,ULONG ulRunInfoReq,ULONG * pulRunInfoOut,LONG * pacpNext)3723 TSFTextStore::GetText(LONG acpStart, LONG acpEnd, WCHAR* pchPlain,
3724                       ULONG cchPlainReq, ULONG* pcchPlainOut,
3725                       TS_RUNINFO* prgRunInfo, ULONG ulRunInfoReq,
3726                       ULONG* pulRunInfoOut, LONG* pacpNext) {
3727   MOZ_LOG(
3728       sTextStoreLog, LogLevel::Info,
3729       ("0x%p TSFTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, "
3730        "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, "
3731        "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, "
3732        "mString.Length()=%lu, IsComposing()=%s }",
3733        this, acpStart, acpEnd, pchPlain, cchPlainReq, pcchPlainOut, prgRunInfo,
3734        ulRunInfoReq, pulRunInfoOut, pacpNext, mComposition.mStart,
3735        mComposition.mString.Length(), GetBoolName(mComposition.IsComposing())));
3736 
3737   if (!IsReadLocked()) {
3738     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3739             ("0x%p   TSFTextStore::GetText() FAILED due to "
3740              "not locked (read)",
3741              this));
3742     return TS_E_NOLOCK;
3743   }
3744 
3745   if (!pcchPlainOut || (!pchPlain && !prgRunInfo) ||
3746       !cchPlainReq != !pchPlain || !ulRunInfoReq != !prgRunInfo) {
3747     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3748             ("0x%p   TSFTextStore::GetText() FAILED due to "
3749              "invalid argument",
3750              this));
3751     return E_INVALIDARG;
3752   }
3753 
3754   if (acpStart < 0 || acpEnd < -1 || (acpEnd != -1 && acpStart > acpEnd)) {
3755     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3756             ("0x%p   TSFTextStore::GetText() FAILED due to "
3757              "invalid position",
3758              this));
3759     return TS_E_INVALIDPOS;
3760   }
3761 
3762   // Making sure to null-terminate string just to be on the safe side
3763   *pcchPlainOut = 0;
3764   if (pchPlain && cchPlainReq) *pchPlain = 0;
3765   if (pulRunInfoOut) *pulRunInfoOut = 0;
3766   if (pacpNext) *pacpNext = acpStart;
3767   if (prgRunInfo && ulRunInfoReq) {
3768     prgRunInfo->uCount = 0;
3769     prgRunInfo->type = TS_RT_PLAIN;
3770   }
3771 
3772   Content& contentForTSF = ContentForTSFRef();
3773   if (!contentForTSF.IsInitialized()) {
3774     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3775             ("0x%p   TSFTextStore::GetText() FAILED due to "
3776              "ContentForTSFRef() failure",
3777              this));
3778     return E_FAIL;
3779   }
3780   if (contentForTSF.Text().Length() < static_cast<uint32_t>(acpStart)) {
3781     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3782             ("0x%p   TSFTextStore::GetText() FAILED due to "
3783              "acpStart is larger offset than the actual text length",
3784              this));
3785     return TS_E_INVALIDPOS;
3786   }
3787   if (acpEnd != -1 &&
3788       contentForTSF.Text().Length() < static_cast<uint32_t>(acpEnd)) {
3789     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3790             ("0x%p   TSFTextStore::GetText() FAILED due to "
3791              "acpEnd is larger offset than the actual text length",
3792              this));
3793     return TS_E_INVALIDPOS;
3794   }
3795   uint32_t length = (acpEnd == -1) ? contentForTSF.Text().Length() -
3796                                          static_cast<uint32_t>(acpStart)
3797                                    : static_cast<uint32_t>(acpEnd - acpStart);
3798   if (cchPlainReq && cchPlainReq - 1 < length) {
3799     length = cchPlainReq - 1;
3800   }
3801   if (length) {
3802     if (pchPlain && cchPlainReq) {
3803       const char16_t* startChar =
3804           contentForTSF.Text().BeginReading() + acpStart;
3805       memcpy(pchPlain, startChar, length * sizeof(*pchPlain));
3806       pchPlain[length] = 0;
3807       *pcchPlainOut = length;
3808     }
3809     if (prgRunInfo && ulRunInfoReq) {
3810       prgRunInfo->uCount = length;
3811       prgRunInfo->type = TS_RT_PLAIN;
3812       if (pulRunInfoOut) *pulRunInfoOut = 1;
3813     }
3814     if (pacpNext) *pacpNext = acpStart + length;
3815   }
3816 
3817   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3818           ("0x%p   TSFTextStore::GetText() succeeded: pcchPlainOut=0x%p, "
3819            "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, "
3820            "*pacpNext=%ld)",
3821            this, pcchPlainOut, prgRunInfo ? prgRunInfo->uCount : 0,
3822            prgRunInfo ? GetTextRunTypeName(prgRunInfo->type) : "N/A",
3823            pulRunInfoOut ? *pulRunInfoOut : 0, pacpNext ? *pacpNext : 0));
3824   return S_OK;
3825 }
3826 
3827 STDMETHODIMP
SetText(DWORD dwFlags,LONG acpStart,LONG acpEnd,const WCHAR * pchText,ULONG cch,TS_TEXTCHANGE * pChange)3828 TSFTextStore::SetText(DWORD dwFlags, LONG acpStart, LONG acpEnd,
3829                       const WCHAR* pchText, ULONG cch, TS_TEXTCHANGE* pChange) {
3830   MOZ_LOG(
3831       sTextStoreLog, LogLevel::Info,
3832       ("0x%p TSFTextStore::SetText(dwFlags=%s, acpStart=%ld, "
3833        "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
3834        "mComposition.IsComposing()=%s",
3835        this, dwFlags == TS_ST_CORRECTION ? "TS_ST_CORRECTION" : "not-specified",
3836        acpStart, acpEnd, pchText,
3837        pchText && cch ? GetEscapedUTF8String(pchText, cch).get() : "", cch,
3838        pChange, GetBoolName(mComposition.IsComposing())));
3839 
3840   // Per SDK documentation, and since we don't have better
3841   // ways to do this, this method acts as a helper to
3842   // call SetSelection followed by InsertTextAtSelection
3843   if (!IsReadWriteLocked()) {
3844     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3845             ("0x%p   TSFTextStore::SetText() FAILED due to "
3846              "not locked (read)",
3847              this));
3848     return TS_E_NOLOCK;
3849   }
3850 
3851   TS_SELECTION_ACP selection;
3852   selection.acpStart = acpStart;
3853   selection.acpEnd = acpEnd;
3854   selection.style.ase = TS_AE_END;
3855   selection.style.fInterimChar = 0;
3856   // Set selection to desired range
3857   HRESULT hr = SetSelectionInternal(&selection);
3858   if (FAILED(hr)) {
3859     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3860             ("0x%p   TSFTextStore::SetText() FAILED due to "
3861              "SetSelectionInternal() failure",
3862              this));
3863     return hr;
3864   }
3865   // Replace just selected text
3866   if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
3867                                      pChange)) {
3868     MOZ_LOG(sTextStoreLog, LogLevel::Error,
3869             ("0x%p   TSFTextStore::SetText() FAILED due to "
3870              "InsertTextAtSelectionInternal() failure",
3871              this));
3872     return E_FAIL;
3873   }
3874 
3875   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3876           ("0x%p   TSFTextStore::SetText() succeeded: pChange={ "
3877            "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
3878            this, pChange ? pChange->acpStart : 0,
3879            pChange ? pChange->acpOldEnd : 0, pChange ? pChange->acpNewEnd : 0));
3880   return S_OK;
3881 }
3882 
3883 STDMETHODIMP
GetFormattedText(LONG acpStart,LONG acpEnd,IDataObject ** ppDataObject)3884 TSFTextStore::GetFormattedText(LONG acpStart, LONG acpEnd,
3885                                IDataObject** ppDataObject) {
3886   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3887           ("0x%p TSFTextStore::GetFormattedText() called "
3888            "but not supported (E_NOTIMPL)",
3889            this));
3890 
3891   // no support for formatted text
3892   return E_NOTIMPL;
3893 }
3894 
3895 STDMETHODIMP
GetEmbedded(LONG acpPos,REFGUID rguidService,REFIID riid,IUnknown ** ppunk)3896 TSFTextStore::GetEmbedded(LONG acpPos, REFGUID rguidService, REFIID riid,
3897                           IUnknown** ppunk) {
3898   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3899           ("0x%p TSFTextStore::GetEmbedded() called "
3900            "but not supported (E_NOTIMPL)",
3901            this));
3902 
3903   // embedded objects are not supported
3904   return E_NOTIMPL;
3905 }
3906 
3907 STDMETHODIMP
QueryInsertEmbedded(const GUID * pguidService,const FORMATETC * pFormatEtc,BOOL * pfInsertable)3908 TSFTextStore::QueryInsertEmbedded(const GUID* pguidService,
3909                                   const FORMATETC* pFormatEtc,
3910                                   BOOL* pfInsertable) {
3911   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3912           ("0x%p TSFTextStore::QueryInsertEmbedded() called "
3913            "but not supported, *pfInsertable=FALSE (S_OK)",
3914            this));
3915 
3916   // embedded objects are not supported
3917   *pfInsertable = FALSE;
3918   return S_OK;
3919 }
3920 
3921 STDMETHODIMP
InsertEmbedded(DWORD dwFlags,LONG acpStart,LONG acpEnd,IDataObject * pDataObject,TS_TEXTCHANGE * pChange)3922 TSFTextStore::InsertEmbedded(DWORD dwFlags, LONG acpStart, LONG acpEnd,
3923                              IDataObject* pDataObject, TS_TEXTCHANGE* pChange) {
3924   MOZ_LOG(sTextStoreLog, LogLevel::Info,
3925           ("0x%p TSFTextStore::InsertEmbedded() called "
3926            "but not supported (E_NOTIMPL)",
3927            this));
3928 
3929   // embedded objects are not supported
3930   return E_NOTIMPL;
3931 }
3932 
3933 // static
ShouldSetInputScopeOfURLBarToDefault()3934 bool TSFTextStore::ShouldSetInputScopeOfURLBarToDefault() {
3935   // FYI: Google Japanese Input may be an IMM-IME.  If it's installed on
3936   //      Win7, it's always IMM-IME.  Otherwise, basically, it's a TIP.
3937   //      However, if it's installed on Win7 and has not been updated yet
3938   //      after the OS is upgraded to Win8 or later, it's still an IMM-IME.
3939   //      Therefore, we also need to check with IMMHandler here.
3940   if (!TSFPrefs::ShouldSetInputScopeOfURLBarToDefault()) {
3941     return false;
3942   }
3943 
3944   if (IMMHandler::IsGoogleJapaneseInputActive()) {
3945     return true;
3946   }
3947 
3948   switch (TSFStaticSink::ActiveTIP()) {
3949     case TextInputProcessorID::eMicrosoftIMEForJapanese:
3950     case TextInputProcessorID::eGoogleJapaneseInput:
3951     case TextInputProcessorID::eMicrosoftBopomofo:
3952     case TextInputProcessorID::eMicrosoftChangJie:
3953     case TextInputProcessorID::eMicrosoftPhonetic:
3954     case TextInputProcessorID::eMicrosoftQuick:
3955     case TextInputProcessorID::eMicrosoftNewChangJie:
3956     case TextInputProcessorID::eMicrosoftNewPhonetic:
3957     case TextInputProcessorID::eMicrosoftNewQuick:
3958     case TextInputProcessorID::eMicrosoftPinyin:
3959     case TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle:
3960     case TextInputProcessorID::eMicrosoftOldHangul:
3961     case TextInputProcessorID::eMicrosoftWubi:
3962       return true;
3963     case TextInputProcessorID::eMicrosoftIMEForKorean:
3964       return IsWin8OrLater();
3965     default:
3966       return false;
3967   }
3968 }
3969 
SetInputScope(const nsString & aHTMLInputType,const nsString & aHTMLInputInputMode,bool aInPrivateBrowsing)3970 void TSFTextStore::SetInputScope(const nsString& aHTMLInputType,
3971                                  const nsString& aHTMLInputInputMode,
3972                                  bool aInPrivateBrowsing) {
3973   mInputScopes.Clear();
3974 
3975   // IME may refer only first input scope, but we will append inputmode's
3976   // input scopes too like Chrome since IME may refer it.
3977   IMEHandler::AppendInputScopeFromType(aHTMLInputType, mInputScopes);
3978   IMEHandler::AppendInputScopeFromInputmode(aHTMLInputInputMode, mInputScopes);
3979 
3980   if (aInPrivateBrowsing) {
3981     mInputScopes.AppendElement(IS_PRIVATE);
3982   }
3983 }
3984 
GetRequestedAttrIndex(const TS_ATTRID & aAttrID)3985 int32_t TSFTextStore::GetRequestedAttrIndex(const TS_ATTRID& aAttrID) {
3986   if (IsEqualGUID(aAttrID, GUID_PROP_INPUTSCOPE)) {
3987     return eInputScope;
3988   }
3989   if (IsEqualGUID(aAttrID, TSATTRID_Text_VerticalWriting)) {
3990     return eTextVerticalWriting;
3991   }
3992   if (IsEqualGUID(aAttrID, TSATTRID_Text_Orientation)) {
3993     return eTextOrientation;
3994   }
3995   return eNotSupported;
3996 }
3997 
3998 TS_ATTRID
GetAttrID(int32_t aIndex)3999 TSFTextStore::GetAttrID(int32_t aIndex) {
4000   switch (aIndex) {
4001     case eInputScope:
4002       return GUID_PROP_INPUTSCOPE;
4003     case eTextVerticalWriting:
4004       return TSATTRID_Text_VerticalWriting;
4005     case eTextOrientation:
4006       return TSATTRID_Text_Orientation;
4007     default:
4008       MOZ_CRASH("Invalid index? Or not implemented yet?");
4009       return GUID_NULL;
4010   }
4011 }
4012 
4013 HRESULT
HandleRequestAttrs(DWORD aFlags,ULONG aFilterCount,const TS_ATTRID * aFilterAttrs)4014 TSFTextStore::HandleRequestAttrs(DWORD aFlags, ULONG aFilterCount,
4015                                  const TS_ATTRID* aFilterAttrs) {
4016   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4017           ("0x%p TSFTextStore::HandleRequestAttrs(aFlags=%s, "
4018            "aFilterCount=%u)",
4019            this, GetFindFlagName(aFlags).get(), aFilterCount));
4020 
4021   // This is a little weird! RequestSupportedAttrs gives us advanced notice
4022   // of a support query via RetrieveRequestedAttrs for a specific attribute.
4023   // RetrieveRequestedAttrs needs to return valid data for all attributes we
4024   // support, but the text service will only want the input scope object
4025   // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
4026   // TS_ATTR_FIND_WANT_VALUE.
4027   for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) {
4028     mRequestedAttrs[i] = false;
4029   }
4030   mRequestedAttrValues = !!(aFlags & TS_ATTR_FIND_WANT_VALUE);
4031 
4032   for (uint32_t i = 0; i < aFilterCount; i++) {
4033     MOZ_LOG(sTextStoreLog, LogLevel::Info,
4034             ("0x%p   TSFTextStore::HandleRequestAttrs(), "
4035              "requested attr=%s",
4036              this, GetGUIDNameStrWithTable(aFilterAttrs[i]).get()));
4037     int32_t index = GetRequestedAttrIndex(aFilterAttrs[i]);
4038     if (index != eNotSupported) {
4039       mRequestedAttrs[index] = true;
4040     }
4041   }
4042   return S_OK;
4043 }
4044 
4045 STDMETHODIMP
RequestSupportedAttrs(DWORD dwFlags,ULONG cFilterAttrs,const TS_ATTRID * paFilterAttrs)4046 TSFTextStore::RequestSupportedAttrs(DWORD dwFlags, ULONG cFilterAttrs,
4047                                     const TS_ATTRID* paFilterAttrs) {
4048   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4049           ("0x%p TSFTextStore::RequestSupportedAttrs(dwFlags=%s, "
4050            "cFilterAttrs=%lu)",
4051            this, GetFindFlagName(dwFlags).get(), cFilterAttrs));
4052 
4053   return HandleRequestAttrs(dwFlags, cFilterAttrs, paFilterAttrs);
4054 }
4055 
4056 STDMETHODIMP
RequestAttrsAtPosition(LONG acpPos,ULONG cFilterAttrs,const TS_ATTRID * paFilterAttrs,DWORD dwFlags)4057 TSFTextStore::RequestAttrsAtPosition(LONG acpPos, ULONG cFilterAttrs,
4058                                      const TS_ATTRID* paFilterAttrs,
4059                                      DWORD dwFlags) {
4060   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4061           ("0x%p TSFTextStore::RequestAttrsAtPosition(acpPos=%ld, "
4062            "cFilterAttrs=%lu, dwFlags=%s)",
4063            this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
4064 
4065   return HandleRequestAttrs(dwFlags | TS_ATTR_FIND_WANT_VALUE, cFilterAttrs,
4066                             paFilterAttrs);
4067 }
4068 
4069 STDMETHODIMP
RequestAttrsTransitioningAtPosition(LONG acpPos,ULONG cFilterAttrs,const TS_ATTRID * paFilterAttr,DWORD dwFlags)4070 TSFTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos,
4071                                                   ULONG cFilterAttrs,
4072                                                   const TS_ATTRID* paFilterAttr,
4073                                                   DWORD dwFlags) {
4074   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4075           ("0x%p TSFTextStore::RequestAttrsTransitioningAtPosition("
4076            "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported "
4077            "(S_OK)",
4078            this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get()));
4079 
4080   // no per character attributes defined
4081   return S_OK;
4082 }
4083 
4084 STDMETHODIMP
FindNextAttrTransition(LONG acpStart,LONG acpHalt,ULONG cFilterAttrs,const TS_ATTRID * paFilterAttrs,DWORD dwFlags,LONG * pacpNext,BOOL * pfFound,LONG * plFoundOffset)4085 TSFTextStore::FindNextAttrTransition(LONG acpStart, LONG acpHalt,
4086                                      ULONG cFilterAttrs,
4087                                      const TS_ATTRID* paFilterAttrs,
4088                                      DWORD dwFlags, LONG* pacpNext,
4089                                      BOOL* pfFound, LONG* plFoundOffset) {
4090   if (!pacpNext || !pfFound || !plFoundOffset) {
4091     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4092             ("  0x%p TSFTextStore::FindNextAttrTransition() FAILED due to "
4093              "null argument",
4094              this));
4095     return E_INVALIDARG;
4096   }
4097 
4098   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4099           ("0x%p   TSFTextStore::FindNextAttrTransition() called "
4100            "but not supported (S_OK)",
4101            this));
4102 
4103   // no per character attributes defined
4104   *pacpNext = *plFoundOffset = acpHalt;
4105   *pfFound = FALSE;
4106   return S_OK;
4107 }
4108 
4109 STDMETHODIMP
RetrieveRequestedAttrs(ULONG ulCount,TS_ATTRVAL * paAttrVals,ULONG * pcFetched)4110 TSFTextStore::RetrieveRequestedAttrs(ULONG ulCount, TS_ATTRVAL* paAttrVals,
4111                                      ULONG* pcFetched) {
4112   if (!pcFetched || !paAttrVals) {
4113     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4114             ("0x%p TSFTextStore::RetrieveRequestedAttrs() FAILED due to "
4115              "null argument",
4116              this));
4117     return E_INVALIDARG;
4118   }
4119 
4120   ULONG expectedCount = 0;
4121   for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) {
4122     if (mRequestedAttrs[i]) {
4123       expectedCount++;
4124     }
4125   }
4126   if (ulCount < expectedCount) {
4127     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4128             ("0x%p TSFTextStore::RetrieveRequestedAttrs() FAILED due to "
4129              "not enough count ulCount=%u, expectedCount=%u",
4130              this, ulCount, expectedCount));
4131     return E_INVALIDARG;
4132   }
4133 
4134   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4135           ("0x%p TSFTextStore::RetrieveRequestedAttrs() called "
4136            "ulCount=%d, mRequestedAttrValues=%s",
4137            this, ulCount, GetBoolName(mRequestedAttrValues)));
4138 
4139   int32_t count = 0;
4140   for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) {
4141     if (!mRequestedAttrs[i]) {
4142       continue;
4143     }
4144     mRequestedAttrs[i] = false;
4145 
4146     TS_ATTRID attrID = GetAttrID(i);
4147 
4148     MOZ_LOG(sTextStoreLog, LogLevel::Info,
4149             ("0x%p   TSFTextStore::RetrieveRequestedAttrs() for %s", this,
4150              GetGUIDNameStrWithTable(attrID).get()));
4151 
4152     paAttrVals[count].idAttr = attrID;
4153     paAttrVals[count].dwOverlapId = 0;
4154 
4155     if (!mRequestedAttrValues) {
4156       paAttrVals[count].varValue.vt = VT_EMPTY;
4157     } else {
4158       switch (i) {
4159         case eInputScope: {
4160           paAttrVals[count].varValue.vt = VT_UNKNOWN;
4161           RefPtr<IUnknown> inputScope = new InputScopeImpl(mInputScopes);
4162           paAttrVals[count].varValue.punkVal = inputScope.forget().take();
4163           break;
4164         }
4165         case eTextVerticalWriting: {
4166           Selection& selectionForTSF = SelectionForTSFRef();
4167           paAttrVals[count].varValue.vt = VT_BOOL;
4168           paAttrVals[count].varValue.boolVal =
4169               !selectionForTSF.IsDirty() &&
4170                       selectionForTSF.GetWritingMode().IsVertical()
4171                   ? VARIANT_TRUE
4172                   : VARIANT_FALSE;
4173           break;
4174         }
4175         case eTextOrientation: {
4176           Selection& selectionForTSF = SelectionForTSFRef();
4177           paAttrVals[count].varValue.vt = VT_I4;
4178           paAttrVals[count].varValue.lVal =
4179               !selectionForTSF.IsDirty() &&
4180                       selectionForTSF.GetWritingMode().IsVertical()
4181                   ? 2700
4182                   : 0;
4183           break;
4184         }
4185         default:
4186           MOZ_CRASH("Invalid index? Or not implemented yet?");
4187           break;
4188       }
4189     }
4190     count++;
4191   }
4192 
4193   mRequestedAttrValues = false;
4194 
4195   if (count) {
4196     *pcFetched = count;
4197     return S_OK;
4198   }
4199 
4200   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4201           ("0x%p   TSFTextStore::RetrieveRequestedAttrs() called "
4202            "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)",
4203            this));
4204 
4205   paAttrVals->dwOverlapId = 0;
4206   paAttrVals->varValue.vt = VT_EMPTY;
4207   *pcFetched = 0;
4208   return S_OK;
4209 }
4210 
4211 STDMETHODIMP
GetEndACP(LONG * pacp)4212 TSFTextStore::GetEndACP(LONG* pacp) {
4213   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4214           ("0x%p TSFTextStore::GetEndACP(pacp=0x%p)", this, pacp));
4215 
4216   if (!IsReadLocked()) {
4217     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4218             ("0x%p   TSFTextStore::GetEndACP() FAILED due to "
4219              "not locked (read)",
4220              this));
4221     return TS_E_NOLOCK;
4222   }
4223 
4224   if (!pacp) {
4225     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4226             ("0x%p   TSFTextStore::GetEndACP() FAILED due to "
4227              "null argument",
4228              this));
4229     return E_INVALIDARG;
4230   }
4231 
4232   Content& contentForTSF = ContentForTSFRef();
4233   if (!contentForTSF.IsInitialized()) {
4234     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4235             ("0x%p   TSFTextStore::GetEndACP() FAILED due to "
4236              "ContentForTSFRef() failure",
4237              this));
4238     return E_FAIL;
4239   }
4240   *pacp = static_cast<LONG>(contentForTSF.Text().Length());
4241   return S_OK;
4242 }
4243 
4244 STDMETHODIMP
GetActiveView(TsViewCookie * pvcView)4245 TSFTextStore::GetActiveView(TsViewCookie* pvcView) {
4246   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4247           ("0x%p TSFTextStore::GetActiveView(pvcView=0x%p)", this, pvcView));
4248 
4249   if (!pvcView) {
4250     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4251             ("0x%p   TSFTextStore::GetActiveView() FAILED due to "
4252              "null argument",
4253              this));
4254     return E_INVALIDARG;
4255   }
4256 
4257   *pvcView = TEXTSTORE_DEFAULT_VIEW;
4258 
4259   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4260           ("0x%p   TSFTextStore::GetActiveView() succeeded: *pvcView=%ld", this,
4261            *pvcView));
4262   return S_OK;
4263 }
4264 
4265 STDMETHODIMP
GetACPFromPoint(TsViewCookie vcView,const POINT * pt,DWORD dwFlags,LONG * pacp)4266 TSFTextStore::GetACPFromPoint(TsViewCookie vcView, const POINT* pt,
4267                               DWORD dwFlags, LONG* pacp) {
4268   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4269           ("0x%p TSFTextStore::GetACPFromPoint(pvcView=%d, pt=%p (x=%d, "
4270            "y=%d), dwFlags=%s, pacp=%p, mDeferNotifyingTSF=%s, "
4271            "mWaitingQueryLayout=%s",
4272            this, vcView, pt, pt ? pt->x : 0, pt ? pt->y : 0,
4273            GetACPFromPointFlagName(dwFlags).get(), pacp,
4274            GetBoolName(mDeferNotifyingTSF), GetBoolName(mWaitingQueryLayout)));
4275 
4276   if (!IsReadLocked()) {
4277     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4278             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4279              "not locked (read)",
4280              this));
4281     return TS_E_NOLOCK;
4282   }
4283 
4284   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
4285     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4286             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4287              "called with invalid view",
4288              this));
4289     return E_INVALIDARG;
4290   }
4291 
4292   if (!pt) {
4293     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4294             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4295              "null pt",
4296              this));
4297     return E_INVALIDARG;
4298   }
4299 
4300   if (!pacp) {
4301     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4302             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4303              "null pacp",
4304              this));
4305     return E_INVALIDARG;
4306   }
4307 
4308   mWaitingQueryLayout = false;
4309 
4310   if (mDestroyed || mContentForTSF.IsLayoutChanged()) {
4311     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4312             ("0x%p   TSFTextStore::GetACPFromPoint() returned "
4313              "TS_E_NOLAYOUT",
4314              this));
4315     mHasReturnedNoLayoutError = true;
4316     return TS_E_NOLAYOUT;
4317   }
4318 
4319   LayoutDeviceIntPoint ourPt(pt->x, pt->y);
4320   // Convert to widget relative coordinates from screen's.
4321   ourPt -= mWidget->WidgetToScreenOffset();
4322 
4323   // NOTE: Don't check if the point is in the widget since the point can be
4324   //       outside of the widget if focused editor is in a XUL <panel>.
4325 
4326   WidgetQueryContentEvent charAtPt(true, eQueryCharacterAtPoint, mWidget);
4327   mWidget->InitEvent(charAtPt, &ourPt);
4328 
4329   // FYI: WidgetQueryContentEvent may cause flushing pending layout and it
4330   //      may cause focus change or something.
4331   RefPtr<TSFTextStore> kungFuDeathGrip(this);
4332   DispatchEvent(charAtPt);
4333   if (!mWidget || mWidget->Destroyed()) {
4334     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4335             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4336              "mWidget was destroyed during eQueryCharacterAtPoint",
4337              this));
4338     return E_FAIL;
4339   }
4340 
4341   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
4342           ("0x%p   TSFTextStore::GetACPFromPoint(), charAtPt={ "
4343            "mSucceeded=%s, mReply={ mOffset=%u, mTentativeCaretOffset=%u }}",
4344            this, GetBoolName(charAtPt.mSucceeded), charAtPt.mReply.mOffset,
4345            charAtPt.mReply.mTentativeCaretOffset));
4346 
4347   if (NS_WARN_IF(!charAtPt.mSucceeded)) {
4348     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4349             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4350              "eQueryCharacterAtPoint failure",
4351              this));
4352     return E_FAIL;
4353   }
4354 
4355   // If dwFlags isn't set and the point isn't in any character's bounding box,
4356   // we should return TS_E_INVALIDPOINT.
4357   if (!(dwFlags & GXFPF_NEAREST) &&
4358       charAtPt.mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND) {
4359     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4360             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to the "
4361              "point contained by no bounding box",
4362              this));
4363     return TS_E_INVALIDPOINT;
4364   }
4365 
4366   // Although, we're not sure if mTentativeCaretOffset becomes NOT_FOUND,
4367   // let's assume that there is no content in such case.
4368   if (NS_WARN_IF(charAtPt.mReply.mTentativeCaretOffset ==
4369                  WidgetQueryContentEvent::NOT_FOUND)) {
4370     charAtPt.mReply.mTentativeCaretOffset = 0;
4371   }
4372 
4373   uint32_t offset;
4374 
4375   // If dwFlags includes GXFPF_ROUND_NEAREST, we should return tentative
4376   // caret offset (MSDN calls it "range position").
4377   if (dwFlags & GXFPF_ROUND_NEAREST) {
4378     offset = charAtPt.mReply.mTentativeCaretOffset;
4379   } else if (charAtPt.mReply.mOffset != WidgetQueryContentEvent::NOT_FOUND) {
4380     // Otherwise, we should return character offset whose bounding box contains
4381     // the point.
4382     offset = charAtPt.mReply.mOffset;
4383   } else {
4384     // If the point isn't in any character's bounding box but we need to return
4385     // the nearest character from the point, we should *guess* the character
4386     // offset since there is no inexpensive API to check it strictly.
4387     // XXX If we retrieve 2 bounding boxes, one is before the offset and
4388     //     the other is after the offset, we could resolve the offset.
4389     //     However, dispatching 2 eQueryTextRect may be expensive.
4390 
4391     // So, use tentative offset for now.
4392     offset = charAtPt.mReply.mTentativeCaretOffset;
4393 
4394     // However, if it's after the last character, we need to decrement the
4395     // offset.
4396     Content& contentForTSF = ContentForTSFRef();
4397     if (!contentForTSF.IsInitialized()) {
4398       MOZ_LOG(sTextStoreLog, LogLevel::Error,
4399               ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to "
4400                "ContentForTSFRef() failure",
4401                this));
4402       return E_FAIL;
4403     }
4404     if (contentForTSF.Text().Length() <= offset) {
4405       // If the tentative caret is after the last character, let's return
4406       // the last character's offset.
4407       offset = contentForTSF.Text().Length() - 1;
4408     }
4409   }
4410 
4411   if (NS_WARN_IF(offset > LONG_MAX)) {
4412     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4413             ("0x%p   TSFTextStore::GetACPFromPoint() FAILED due to out of "
4414              "range of the result",
4415              this));
4416     return TS_E_INVALIDPOINT;
4417   }
4418 
4419   *pacp = static_cast<LONG>(offset);
4420   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4421           ("0x%p   TSFTextStore::GetACPFromPoint() succeeded: *pacp=%d", this,
4422            *pacp));
4423   return S_OK;
4424 }
4425 
4426 STDMETHODIMP
GetTextExt(TsViewCookie vcView,LONG acpStart,LONG acpEnd,RECT * prc,BOOL * pfClipped)4427 TSFTextStore::GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd,
4428                          RECT* prc, BOOL* pfClipped) {
4429   MOZ_LOG(
4430       sTextStoreLog, LogLevel::Info,
4431       ("0x%p TSFTextStore::GetTextExt(vcView=%ld, "
4432        "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p), "
4433        "IsHandlingComposition()=%s, "
4434        "mContentForTSF={ MinOffsetOfLayoutChanged()=%u, "
4435        "LatestCompositionStartOffset()=%d, LatestCompositionEndOffset()=%d }, "
4436        "mComposition= { IsComposing()=%s, mStart=%d, EndOffset()=%d }, "
4437        "mDeferNotifyingTSF=%s, mWaitingQueryLayout=%s, "
4438        "IMEHandler::IsA11yHandlingNativeCaret()=%s",
4439        this, vcView, acpStart, acpEnd, prc, pfClipped,
4440        GetBoolName(IsHandlingComposition()),
4441        mContentForTSF.MinOffsetOfLayoutChanged(),
4442        mContentForTSF.HasOrHadComposition()
4443            ? mContentForTSF.LatestCompositionStartOffset()
4444            : -1,
4445        mContentForTSF.HasOrHadComposition()
4446            ? mContentForTSF.LatestCompositionEndOffset()
4447            : -1,
4448        GetBoolName(mComposition.IsComposing()), mComposition.mStart,
4449        mComposition.EndOffset(), GetBoolName(mDeferNotifyingTSF),
4450        GetBoolName(mWaitingQueryLayout),
4451        GetBoolName(IMEHandler::IsA11yHandlingNativeCaret())));
4452 
4453   if (!IsReadLocked()) {
4454     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4455             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4456              "not locked (read)",
4457              this));
4458     return TS_E_NOLOCK;
4459   }
4460 
4461   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
4462     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4463             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4464              "called with invalid view",
4465              this));
4466     return E_INVALIDARG;
4467   }
4468 
4469   if (!prc || !pfClipped) {
4470     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4471             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4472              "null argument",
4473              this));
4474     return E_INVALIDARG;
4475   }
4476 
4477   // According to MSDN, ITextStoreACP::GetTextExt() should return
4478   // TS_E_INVALIDARG when acpStart and acpEnd are same (i.e., collapsed range).
4479   // https://msdn.microsoft.com/en-us/library/windows/desktop/ms538435(v=vs.85).aspx
4480   // > TS_E_INVALIDARG: The specified start and end character positions are
4481   // >                  equal.
4482   // However, some TIPs (including Microsoft's Chinese TIPs!) call this with
4483   // collapsed range and if we return TS_E_INVALIDARG, they stops showing their
4484   // owning window or shows it but odd position.  So, we should just return
4485   // error only when acpStart and/or acpEnd are really odd.
4486 
4487   if (acpStart < 0 || acpEnd < acpStart) {
4488     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4489             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4490              "invalid position",
4491              this));
4492     return TS_E_INVALIDPOS;
4493   }
4494 
4495   mWaitingQueryLayout = false;
4496 
4497   if (IsHandlingComposition() && mContentForTSF.HasOrHadComposition() &&
4498       mContentForTSF.IsLayoutChanged() &&
4499       mContentForTSF.MinOffsetOfLayoutChanged() > LONG_MAX) {
4500     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4501             ("0x%p   TSFTextStore::GetTextExt(), FAILED due to the text "
4502              "is too big for TSF (cannot treat modified offset as LONG), "
4503              "mContentForTSF.MinOffsetOfLayoutChanged()=%u",
4504              this, mContentForTSF.MinOffsetOfLayoutChanged()));
4505     return E_FAIL;
4506   }
4507 
4508   // At Windows 10 build 17643 (an insider preview for RS5), Microsoft fixed
4509   // the bug of TS_E_NOLAYOUT (even when we returned TS_E_NOLAYOUT, TSF
4510   // returned E_FAIL to TIP).  However, until we drop to support older Windows
4511   // and all TIPs are aware of TS_E_NOLAYOUT result, we need to keep returning
4512   // S_OK and available rectangle only for them.
4513   if (!MaybeHackNoErrorLayoutBugs(acpStart, acpEnd) &&
4514       mContentForTSF.IsLayoutChangedAt(acpEnd)) {
4515     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4516             ("0x%p   TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
4517              "(acpEnd=%d)",
4518              this, acpEnd));
4519     mHasReturnedNoLayoutError = true;
4520     return TS_E_NOLAYOUT;
4521   }
4522 
4523   if (mDestroyed) {
4524     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4525             ("0x%p   TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
4526              "(acpEnd=%d) because this has already been destroyed",
4527              this, acpEnd));
4528     mHasReturnedNoLayoutError = true;
4529     return TS_E_NOLAYOUT;
4530   }
4531 
4532   // use eQueryTextRect to get rect in system, screen coordinates
4533   WidgetQueryContentEvent event(true, eQueryTextRect, mWidget);
4534   mWidget->InitEvent(event);
4535 
4536   WidgetQueryContentEvent::Options options;
4537   int64_t startOffset = acpStart;
4538   if (mComposition.IsComposing()) {
4539     // If there is a composition, TSF must want character rects related to
4540     // the composition.  Therefore, we should use insertion point relative
4541     // query because the composition might be at different position from
4542     // the position where TSFTextStore believes it at.
4543     options.mRelativeToInsertionPoint = true;
4544     startOffset -= mComposition.mStart;
4545   } else if (IsHandlingComposition() && mContentForTSF.HasOrHadComposition()) {
4546     // If there was a composition and it hasn't been committed in the content
4547     // yet, ContentCacheInParent is still open for relative offset query from
4548     // the latest composition.
4549     options.mRelativeToInsertionPoint = true;
4550     startOffset -= mContentForTSF.LatestCompositionStartOffset();
4551   } else if (!CanAccessActualContentDirectly()) {
4552     // If TSF/TIP cannot access actual content directly, there may be pending
4553     // text and/or selection changes which have not been notified TSF yet.
4554     // Therefore, we should use relative to insertion point query since
4555     // TSF/TIP computes the offset from the cached selection.
4556     options.mRelativeToInsertionPoint = true;
4557     startOffset -= mSelectionForTSF.StartOffset();
4558   }
4559   // ContentEventHandler and ContentCache return actual caret rect when
4560   // the queried range is collapsed and selection is collapsed at the
4561   // queried range.  Then, its height (in horizontal layout, width in vertical
4562   // layout) may be different from actual font height of the line.  In such
4563   // case, users see "dancing" of candidate or suggest window of TIP.
4564   // For preventing it, we should query text rect with at least 1 length.
4565   uint32_t length = std::max(static_cast<int32_t>(acpEnd - acpStart), 1);
4566   event.InitForQueryTextRect(startOffset, length, options);
4567 
4568   DispatchEvent(event);
4569   if (NS_WARN_IF(!event.mSucceeded)) {
4570     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4571             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4572              "eQueryTextRect failure",
4573              this));
4574     return TS_E_INVALIDPOS;  // but unexpected failure, maybe.
4575   }
4576 
4577   // IMEs don't like empty rects, fix here
4578   if (event.mReply.mRect.Width() <= 0) event.mReply.mRect.SetWidth(1);
4579   if (event.mReply.mRect.Height() <= 0) event.mReply.mRect.SetHeight(1);
4580 
4581   // convert to unclipped screen rect
4582   nsWindow* refWindow = static_cast<nsWindow*>(
4583       event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWidget);
4584   // Result rect is in top level widget coordinates
4585   refWindow = refWindow->GetTopLevelWindow(false);
4586   if (!refWindow) {
4587     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4588             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4589              "no top level window",
4590              this));
4591     return E_FAIL;
4592   }
4593 
4594   event.mReply.mRect.MoveBy(refWindow->WidgetToScreenOffset());
4595 
4596   // get bounding screen rect to test for clipping
4597   if (!GetScreenExtInternal(*prc)) {
4598     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4599             ("0x%p   TSFTextStore::GetTextExt() FAILED due to "
4600              "GetScreenExtInternal() failure",
4601              this));
4602     return E_FAIL;
4603   }
4604 
4605   // clip text rect to bounding rect
4606   RECT textRect;
4607   ::SetRect(&textRect, event.mReply.mRect.X(), event.mReply.mRect.Y(),
4608             event.mReply.mRect.XMost(), event.mReply.mRect.YMost());
4609   if (!::IntersectRect(prc, prc, &textRect))
4610     // Text is not visible
4611     ::SetRectEmpty(prc);
4612 
4613   // not equal if text rect was clipped
4614   *pfClipped = !::EqualRect(prc, &textRect);
4615 
4616   // ATOK 2011 - 2016 refers native caret position and size on windows whose
4617   // class name is one of Mozilla's windows for deciding candidate window
4618   // position.  Additionally, ATOK 2015 and earlier behaves really odd when
4619   // we don't create native caret.  Therefore, we need to create native caret
4620   // only when ATOK 2011 - 2015 is active (i.e., not necessary for ATOK 2016).
4621   // However, if a11y module is handling native caret, we shouldn't touch it.
4622   // Note that ATOK must require the latest information of the caret.  So,
4623   // even if we'll create native caret later, we need to creat it here with
4624   // current information.
4625   if (!IMEHandler::IsA11yHandlingNativeCaret() &&
4626       TSFPrefs::NeedToCreateNativeCaretForLegacyATOK() &&
4627       TSFStaticSink::IsATOKReferringNativeCaretActive() &&
4628       mComposition.IsComposing() && mComposition.mStart <= acpStart &&
4629       mComposition.EndOffset() >= acpStart && mComposition.mStart <= acpEnd &&
4630       mComposition.EndOffset() >= acpEnd) {
4631     CreateNativeCaret();
4632   }
4633 
4634   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4635           ("0x%p   TSFTextStore::GetTextExt() succeeded: "
4636            "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s",
4637            this, prc->left, prc->top, prc->right, prc->bottom,
4638            GetBoolName(*pfClipped)));
4639 
4640   return S_OK;
4641 }
4642 
MaybeHackNoErrorLayoutBugs(LONG & aACPStart,LONG & aACPEnd)4643 bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
4644   // When ITextStoreACP::GetTextExt() returns TS_E_NOLAYOUT, TSF returns E_FAIL
4645   // to its caller (typically, active TIP).  Then, most TIPs abort current job
4646   // or treat such application as non-GUI apps.  E.g., some of them give up
4647   // showing candidate window, some others show candidate window at top-left of
4648   // the screen.  For avoiding this issue, when there is composition (until
4649   // composition is actually committed in remote content), we should not
4650   // return TS_E_NOLAYOUT error for TIPs whose some features are broken by
4651   // this issue.
4652   // Note that ideally, this issue should be avoided by each TIP since this
4653   // won't be fixed at least on non-latest Windows.  Actually, Google Japanese
4654   // Input (based on Mozc) does it.  When GetTextExt() returns E_FAIL, TIPs
4655   // should try to check result of GetRangeFromPoint() because TSF returns
4656   // TS_E_NOLAYOUT correctly in this case. See:
4657   // https://github.com/google/mozc/blob/6b878e31fb6ac4347dc9dfd8ccc1080fe718479f/src/win32/tip/tip_range_util.cc#L237-L257
4658 
4659   if (!IsHandlingComposition() || !mContentForTSF.HasOrHadComposition() ||
4660       !mContentForTSF.IsLayoutChangedAt(aACPEnd)) {
4661     return false;
4662   }
4663 
4664   MOZ_ASSERT(!mComposition.IsComposing() ||
4665              mComposition.mStart ==
4666                  mContentForTSF.LatestCompositionStartOffset());
4667   MOZ_ASSERT(!mComposition.IsComposing() ||
4668              mComposition.EndOffset() ==
4669                  mContentForTSF.LatestCompositionEndOffset());
4670 
4671   // If TSF does not have the bug, we need to hack only with a few TIPs.
4672   static const bool sAlllowToStopHackingIfFine =
4673       IsWindows10BuildOrLater(17643) &&
4674       TSFPrefs::AllowToStopHackingOnBuild17643OrLater();
4675 
4676   // We need to compute active TIP now.  This may take a couple of milliseconds,
4677   // however, it'll be cached, so, must be faster than check active TIP every
4678   // GetTextExt() calls.
4679   const Selection& selectionForTSF = SelectionForTSFRef();
4680   switch (TSFStaticSink::ActiveTIP()) {
4681     // MS IME for Japanese doesn't support asynchronous handling at deciding
4682     // its suggest list window position.  The feature was implemented
4683     // starting from Windows 8.  And also we may meet same trouble in e10s
4684     // mode on Win7.  So, we should never return TS_E_NOLAYOUT to MS IME for
4685     // Japanese.
4686     case TextInputProcessorID::eMicrosoftIMEForJapanese:
4687       // Basically, MS-IME tries to retrieve whole composition string rect
4688       // at deciding suggest window immediately after unlocking the document.
4689       // However, in e10s mode, the content hasn't updated yet in most cases.
4690       // Therefore, if the first character at the retrieving range rect is
4691       // available, we should use it as the result.
4692       // Note that according to bug 1609675, MS-IME for Japanese itself does
4693       // not handle TS_E_NOLAYOUT correctly at least on Build 18363.657 (1909).
4694       if (TSFPrefs::DoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar() &&
4695           aACPStart < aACPEnd) {
4696         aACPEnd = aACPStart;
4697         break;
4698       }
4699       if (sAlllowToStopHackingIfFine) {
4700         return false;
4701       }
4702       // Although, the condition is not clear, MS-IME sometimes retrieves the
4703       // caret rect immediately after modifying the composition string but
4704       // before unlocking the document.  In such case, we should return the
4705       // nearest character rect.
4706       if (TSFPrefs::DoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret() &&
4707           aACPStart == aACPEnd && selectionForTSF.IsCollapsed() &&
4708           selectionForTSF.EndOffset() == aACPEnd) {
4709         int32_t minOffsetOfLayoutChanged =
4710             static_cast<int32_t>(mContentForTSF.MinOffsetOfLayoutChanged());
4711         aACPEnd = aACPStart = std::max(minOffsetOfLayoutChanged - 1, 0);
4712       } else {
4713         return false;
4714       }
4715       break;
4716     // The bug of Microsoft Office IME 2010 for Japanese is similar to
4717     // MS-IME for Win 8.1 and Win 10.  Newer version of MS Office IME is not
4718     // released yet.  So, we can hack it without prefs  because there must be
4719     // no developers who want to disable this hack for tests.
4720     // XXX We have not tested with Microsoft Office IME 2010 since it's
4721     //     installable only with Win7 and Win8 (i.e., cannot install Win8.1
4722     //     and Win10), and requires upgrade to Win10.
4723     case TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese:
4724       // Basically, MS-IME tries to retrieve whole composition string rect
4725       // at deciding suggest window immediately after unlocking the document.
4726       // However, in e10s mode, the content hasn't updated yet in most cases.
4727       // Therefore, if the first character at the retrieving range rect is
4728       // available, we should use it as the result.
4729       if (aACPStart < aACPEnd) {
4730         aACPEnd = aACPStart;
4731       }
4732       // Although, the condition is not clear, MS-IME sometimes retrieves the
4733       // caret rect immediately after modifying the composition string but
4734       // before unlocking the document.  In such case, we should return the
4735       // nearest character rect.
4736       else if (aACPStart == aACPEnd && selectionForTSF.IsCollapsed() &&
4737                selectionForTSF.EndOffset() == aACPEnd) {
4738         int32_t minOffsetOfLayoutChanged =
4739             static_cast<int32_t>(mContentForTSF.MinOffsetOfLayoutChanged());
4740         aACPEnd = aACPStart = std::max(minOffsetOfLayoutChanged - 1, 0);
4741       } else {
4742         return false;
4743       }
4744       break;
4745     // ATOK fails to handle TS_E_NOLAYOUT only when it decides the position of
4746     // suggest window.  In such case, ATOK tries to query rect of whole or a
4747     // part of composition string.
4748     // FYI: ATOK changes their implementation around candidate window and
4749     //      suggest widget at ATOK 2016.  Therefore, there are some differences
4750     //      ATOK 2015 (or older) and ATOK 2016 (or newer).
4751     // FYI: ATOK 2017 stops referring our window class name.  I.e., ATOK 2016
4752     //      and older may behave differently only on Gecko but this must be
4753     //      finished from ATOK 2017.
4754     // FYI: For testing with legacy ATOK, we should hack it even if current ATOK
4755     //      refers native caret rect on windows whose window class is one of
4756     //      Mozilla window classes and we stop creating native caret for ATOK
4757     //      because creating native caret causes ATOK refers caret position
4758     //      when GetTextExt() returns TS_E_NOLAYOUT.
4759     case TextInputProcessorID::eATOK2011:
4760     case TextInputProcessorID::eATOK2012:
4761     case TextInputProcessorID::eATOK2013:
4762     case TextInputProcessorID::eATOK2014:
4763     case TextInputProcessorID::eATOK2015:
4764       // ATOK 2016 and later may temporarily show candidate window at odd
4765       // position when you convert a word quickly (e.g., keep pressing
4766       // space bar).  So, on ATOK 2016 or later, we need to keep hacking the
4767       // result of GetTextExt().
4768       if (sAlllowToStopHackingIfFine) {
4769         return false;
4770       }
4771       // If we'll create native caret where we paint our caret.  Then, ATOK
4772       // will refer native caret.  So, we don't need to hack anything in
4773       // this case.
4774       if (TSFPrefs::NeedToCreateNativeCaretForLegacyATOK()) {
4775         MOZ_ASSERT(TSFStaticSink::IsATOKReferringNativeCaretActive());
4776         return false;
4777       }
4778       [[fallthrough]];
4779     case TextInputProcessorID::eATOK2016:
4780     case TextInputProcessorID::eATOKUnknown:
4781       if (!TSFPrefs::DoNotReturnNoLayoutErrorToATOKOfCompositionString()) {
4782         return false;
4783       }
4784       // If the range is in the composition string, we should return rectangle
4785       // in it as far as possible.
4786       if (aACPStart < mContentForTSF.LatestCompositionStartOffset() ||
4787           aACPStart > mContentForTSF.LatestCompositionEndOffset() ||
4788           aACPEnd < mContentForTSF.LatestCompositionStartOffset() ||
4789           aACPEnd > mContentForTSF.LatestCompositionEndOffset()) {
4790         return false;
4791       }
4792       break;
4793     // Japanist 10 fails to handle TS_E_NOLAYOUT when it decides the position
4794     // of candidate window.  In such case, Japanist shows candidate window at
4795     // top-left of the screen.  So, we should return the nearest caret rect
4796     // where we know.  This is Japanist's bug.  So, even after build 17643,
4797     // we need this hack.
4798     case TextInputProcessorID::eJapanist10:
4799       if (!TSFPrefs::
4800               DoNotReturnNoLayoutErrorToJapanist10OfCompositionString()) {
4801         return false;
4802       }
4803       if (aACPStart < mContentForTSF.LatestCompositionStartOffset() ||
4804           aACPStart > mContentForTSF.LatestCompositionEndOffset() ||
4805           aACPEnd < mContentForTSF.LatestCompositionStartOffset() ||
4806           aACPEnd > mContentForTSF.LatestCompositionEndOffset()) {
4807         return false;
4808       }
4809       break;
4810     // Free ChangJie 2010 doesn't handle ITfContextView::GetTextExt() properly.
4811     // This must be caused by the bug of TSF since Free ChangJie works fine on
4812     // build 17643 and later.
4813     case TextInputProcessorID::eFreeChangJie:
4814       if (sAlllowToStopHackingIfFine) {
4815         return false;
4816       }
4817       if (!TSFPrefs::DoNotReturnNoLayoutErrorToFreeChangJie()) {
4818         return false;
4819       }
4820       aACPEnd = mContentForTSF.LatestCompositionStartOffset();
4821       aACPStart = std::min(aACPStart, aACPEnd);
4822       break;
4823     // Some Traditional Chinese TIPs of Microsoft don't show candidate window
4824     // in e10s mode on Win8 or later.
4825     case TextInputProcessorID::eMicrosoftChangJie:
4826     case TextInputProcessorID::eMicrosoftQuick:
4827       if (sAlllowToStopHackingIfFine) {
4828         return false;
4829       }
4830       if (!IsWin8OrLater() ||
4831           !TSFPrefs::DoNotReturnNoLayoutErrorToMSTraditionalTIP()) {
4832         return false;
4833       }
4834       aACPEnd = mContentForTSF.LatestCompositionStartOffset();
4835       aACPStart = std::min(aACPStart, aACPEnd);
4836       break;
4837     // Some Simplified Chinese TIPs of Microsoft don't show candidate window
4838     // in e10s mode on Win8 or later.
4839     // FYI: Only Simplified Chinese TIPs of Microsoft still require this hack
4840     //      because they sometimes do not show candidate window when we return
4841     //      TS_E_NOLAYOUT for first query.  Note that even when they show
4842     //      candidate window properly, we return TS_E_NOLAYOUT and following
4843     //      log looks same as when they don't show candidate window.  Perhaps,
4844     //      there is stateful cause or race in them.
4845     case TextInputProcessorID::eMicrosoftPinyin:
4846     case TextInputProcessorID::eMicrosoftWubi:
4847       if (!IsWin8OrLater() ||
4848           !TSFPrefs::DoNotReturnNoLayoutErrorToMSSimplifiedTIP()) {
4849         return false;
4850       }
4851       aACPEnd = mContentForTSF.LatestCompositionStartOffset();
4852       aACPStart = std::min(aACPStart, aACPEnd);
4853       break;
4854     default:
4855       return false;
4856   }
4857 
4858   // If we hack the queried range for active TIP, that means we should not
4859   // return TS_E_NOLAYOUT even if hacked offset is still modified.  So, as
4860   // far as possible, we should adjust the offset.
4861   MOZ_ASSERT(mContentForTSF.IsLayoutChanged());
4862   bool collapsed = aACPStart == aACPEnd;
4863   // Note that even if all characters in the editor or the composition
4864   // string was modified, 0 or start offset of the composition string is
4865   // useful because it may return caret rect or old character's rect which
4866   // the user still see.  That must be useful information for TIP.
4867   int32_t firstModifiedOffset =
4868       static_cast<int32_t>(mContentForTSF.MinOffsetOfLayoutChanged());
4869   LONG lastUnmodifiedOffset = std::max(firstModifiedOffset - 1, 0);
4870   if (mContentForTSF.IsLayoutChangedAt(aACPStart)) {
4871     if (aACPStart >= mContentForTSF.LatestCompositionStartOffset()) {
4872       // If mContentForTSF has last composition string and current
4873       // composition string, we can assume that ContentCacheInParent has
4874       // cached rects of composition string at least length of current
4875       // composition string.  Otherwise, we can assume that rect for
4876       // first character of composition string is stored since it was
4877       // selection start or caret position.
4878       LONG maxCachedOffset = mContentForTSF.LatestCompositionEndOffset();
4879       if (mContentForTSF.WasLastComposition()) {
4880         maxCachedOffset = std::min(
4881             maxCachedOffset, mContentForTSF.LastCompositionStringEndOffset());
4882       }
4883       aACPStart = std::min(aACPStart, maxCachedOffset);
4884     }
4885     // Otherwise, we don't know which character rects are cached.  So, we
4886     // need to use first unmodified character's rect in this case.  Even
4887     // if there is no character, the query event will return caret rect
4888     // instead.
4889     else {
4890       aACPStart = lastUnmodifiedOffset;
4891     }
4892     MOZ_ASSERT(aACPStart <= aACPEnd);
4893   }
4894 
4895   // If TIP requests caret rect with collapsed range, we should keep
4896   // collapsing the range.
4897   if (collapsed) {
4898     aACPEnd = aACPStart;
4899   }
4900   // Let's set aACPEnd to larger offset of last unmodified offset or
4901   // aACPStart which may be the first character offset of the composition
4902   // string.  However, some TIPs may want to know the right edge of the
4903   // range.  Therefore, if aACPEnd is in composition string and active TIP
4904   // doesn't retrieve caret rect (i.e., the range isn't collapsed), we
4905   // should keep using the original aACPEnd.  Otherwise, we should set
4906   // aACPEnd to larger value of aACPStart and lastUnmodifiedOffset.
4907   else if (mContentForTSF.IsLayoutChangedAt(aACPEnd) &&
4908            (aACPEnd < mContentForTSF.LatestCompositionStartOffset() ||
4909             aACPEnd > mContentForTSF.LatestCompositionEndOffset())) {
4910     aACPEnd = std::max(aACPStart, lastUnmodifiedOffset);
4911   }
4912 
4913   MOZ_LOG(
4914       sTextStoreLog, LogLevel::Debug,
4915       ("0x%p   TSFTextStore::HackNoErrorLayoutBugs() hacked the queried range "
4916        "for not returning TS_E_NOLAYOUT, new values are: "
4917        "aACPStart=%d, aACPEnd=%d",
4918        this, aACPStart, aACPEnd));
4919 
4920   return true;
4921 }
4922 
4923 STDMETHODIMP
GetScreenExt(TsViewCookie vcView,RECT * prc)4924 TSFTextStore::GetScreenExt(TsViewCookie vcView, RECT* prc) {
4925   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4926           ("0x%p TSFTextStore::GetScreenExt(vcView=%ld, prc=0x%p)", this,
4927            vcView, prc));
4928 
4929   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
4930     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4931             ("0x%p   TSFTextStore::GetScreenExt() FAILED due to "
4932              "called with invalid view",
4933              this));
4934     return E_INVALIDARG;
4935   }
4936 
4937   if (!prc) {
4938     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4939             ("0x%p   TSFTextStore::GetScreenExt() FAILED due to "
4940              "null argument",
4941              this));
4942     return E_INVALIDARG;
4943   }
4944 
4945   if (mDestroyed) {
4946     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4947             ("0x%p   TSFTextStore::GetScreenExt() returns empty rect "
4948              "due to already destroyed",
4949              this));
4950     prc->left = prc->top = prc->right = prc->bottom = 0;
4951     return S_OK;
4952   }
4953 
4954   if (!GetScreenExtInternal(*prc)) {
4955     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4956             ("0x%p   TSFTextStore::GetScreenExt() FAILED due to "
4957              "GetScreenExtInternal() failure",
4958              this));
4959     return E_FAIL;
4960   }
4961 
4962   MOZ_LOG(sTextStoreLog, LogLevel::Info,
4963           ("0x%p   TSFTextStore::GetScreenExt() succeeded: "
4964            "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
4965            this, prc->left, prc->top, prc->right, prc->bottom));
4966   return S_OK;
4967 }
4968 
GetScreenExtInternal(RECT & aScreenExt)4969 bool TSFTextStore::GetScreenExtInternal(RECT& aScreenExt) {
4970   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
4971           ("0x%p   TSFTextStore::GetScreenExtInternal()", this));
4972 
4973   MOZ_ASSERT(!mDestroyed);
4974 
4975   // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
4976   WidgetQueryContentEvent event(true, eQueryEditorRect, mWidget);
4977   mWidget->InitEvent(event);
4978   DispatchEvent(event);
4979   if (!event.mSucceeded) {
4980     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4981             ("0x%p   TSFTextStore::GetScreenExtInternal() FAILED due to "
4982              "eQueryEditorRect failure",
4983              this));
4984     return false;
4985   }
4986 
4987   nsWindow* refWindow = static_cast<nsWindow*>(
4988       event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWidget);
4989   // Result rect is in top level widget coordinates
4990   refWindow = refWindow->GetTopLevelWindow(false);
4991   if (!refWindow) {
4992     MOZ_LOG(sTextStoreLog, LogLevel::Error,
4993             ("0x%p   TSFTextStore::GetScreenExtInternal() FAILED due to "
4994              "no top level window",
4995              this));
4996     return false;
4997   }
4998 
4999   LayoutDeviceIntRect boundRect = refWindow->GetClientBounds();
5000   boundRect.MoveTo(0, 0);
5001 
5002   // Clip frame rect to window rect
5003   boundRect.IntersectRect(event.mReply.mRect, boundRect);
5004   if (!boundRect.IsEmpty()) {
5005     boundRect.MoveBy(refWindow->WidgetToScreenOffset());
5006     ::SetRect(&aScreenExt, boundRect.X(), boundRect.Y(), boundRect.XMost(),
5007               boundRect.YMost());
5008   } else {
5009     ::SetRectEmpty(&aScreenExt);
5010   }
5011 
5012   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5013           ("0x%p   TSFTextStore::GetScreenExtInternal() succeeded: "
5014            "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
5015            this, aScreenExt.left, aScreenExt.top, aScreenExt.right,
5016            aScreenExt.bottom));
5017   return true;
5018 }
5019 
5020 STDMETHODIMP
GetWnd(TsViewCookie vcView,HWND * phwnd)5021 TSFTextStore::GetWnd(TsViewCookie vcView, HWND* phwnd) {
5022   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5023           ("0x%p TSFTextStore::GetWnd(vcView=%ld, phwnd=0x%p), "
5024            "mWidget=0x%p",
5025            this, vcView, phwnd, mWidget.get()));
5026 
5027   if (vcView != TEXTSTORE_DEFAULT_VIEW) {
5028     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5029             ("0x%p   TSFTextStore::GetWnd() FAILED due to "
5030              "called with invalid view",
5031              this));
5032     return E_INVALIDARG;
5033   }
5034 
5035   if (!phwnd) {
5036     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5037             ("0x%p   TSFTextStore::GetScreenExt() FAILED due to "
5038              "null argument",
5039              this));
5040     return E_INVALIDARG;
5041   }
5042 
5043   *phwnd = mWidget ? mWidget->GetWindowHandle() : nullptr;
5044 
5045   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5046           ("0x%p   TSFTextStore::GetWnd() succeeded: *phwnd=0x%p", this,
5047            static_cast<void*>(*phwnd)));
5048   return S_OK;
5049 }
5050 
5051 STDMETHODIMP
InsertTextAtSelection(DWORD dwFlags,const WCHAR * pchText,ULONG cch,LONG * pacpStart,LONG * pacpEnd,TS_TEXTCHANGE * pChange)5052 TSFTextStore::InsertTextAtSelection(DWORD dwFlags, const WCHAR* pchText,
5053                                     ULONG cch, LONG* pacpStart, LONG* pacpEnd,
5054                                     TS_TEXTCHANGE* pChange) {
5055   MOZ_LOG(
5056       sTextStoreLog, LogLevel::Info,
5057       ("0x%p TSFTextStore::InsertTextAtSelection(dwFlags=%s, "
5058        "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
5059        "pChange=0x%p), IsComposing()=%s",
5060        this,
5061        dwFlags == 0
5062            ? "0"
5063            : dwFlags == TF_IAS_NOQUERY
5064                  ? "TF_IAS_NOQUERY"
5065                  : dwFlags == TF_IAS_QUERYONLY ? "TF_IAS_QUERYONLY" : "Unknown",
5066        pchText, pchText && cch ? GetEscapedUTF8String(pchText, cch).get() : "",
5067        cch, pacpStart, pacpEnd, pChange,
5068        GetBoolName(mComposition.IsComposing())));
5069 
5070   if (cch && !pchText) {
5071     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5072             ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5073              "null pchText",
5074              this));
5075     return E_INVALIDARG;
5076   }
5077 
5078   if (TS_IAS_QUERYONLY == dwFlags) {
5079     if (!IsReadLocked()) {
5080       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5081               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5082                "not locked (read)",
5083                this));
5084       return TS_E_NOLOCK;
5085     }
5086 
5087     if (!pacpStart || !pacpEnd) {
5088       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5089               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5090                "null argument",
5091                this));
5092       return E_INVALIDARG;
5093     }
5094 
5095     // Get selection first
5096     Selection& selectionForTSF = SelectionForTSFRef();
5097     if (selectionForTSF.IsDirty()) {
5098       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5099               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5100                "SelectionForTSFRef() failure",
5101                this));
5102       return E_FAIL;
5103     }
5104 
5105     // Simulate text insertion
5106     *pacpStart = selectionForTSF.StartOffset();
5107     *pacpEnd = selectionForTSF.EndOffset();
5108     if (pChange) {
5109       pChange->acpStart = selectionForTSF.StartOffset();
5110       pChange->acpOldEnd = selectionForTSF.EndOffset();
5111       pChange->acpNewEnd =
5112           selectionForTSF.StartOffset() + static_cast<LONG>(cch);
5113     }
5114   } else {
5115     if (!IsReadWriteLocked()) {
5116       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5117               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5118                "not locked (read-write)",
5119                this));
5120       return TS_E_NOLOCK;
5121     }
5122 
5123     if (!pChange) {
5124       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5125               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5126                "null pChange",
5127                this));
5128       return E_INVALIDARG;
5129     }
5130 
5131     if (TS_IAS_NOQUERY != dwFlags && (!pacpStart || !pacpEnd)) {
5132       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5133               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5134                "null argument",
5135                this));
5136       return E_INVALIDARG;
5137     }
5138 
5139     if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch),
5140                                        pChange)) {
5141       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5142               ("0x%p   TSFTextStore::InsertTextAtSelection() FAILED due to "
5143                "InsertTextAtSelectionInternal() failure",
5144                this));
5145       return E_FAIL;
5146     }
5147 
5148     if (TS_IAS_NOQUERY != dwFlags) {
5149       *pacpStart = pChange->acpStart;
5150       *pacpEnd = pChange->acpNewEnd;
5151     }
5152   }
5153   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5154           ("0x%p   TSFTextStore::InsertTextAtSelection() succeeded: "
5155            "*pacpStart=%ld, *pacpEnd=%ld, "
5156            "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })",
5157            this, pacpStart ? *pacpStart : 0, pacpEnd ? *pacpEnd : 0,
5158            pChange ? pChange->acpStart : 0, pChange ? pChange->acpOldEnd : 0,
5159            pChange ? pChange->acpNewEnd : 0));
5160   return S_OK;
5161 }
5162 
InsertTextAtSelectionInternal(const nsAString & aInsertStr,TS_TEXTCHANGE * aTextChange)5163 bool TSFTextStore::InsertTextAtSelectionInternal(const nsAString& aInsertStr,
5164                                                  TS_TEXTCHANGE* aTextChange) {
5165   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5166           ("0x%p   TSFTextStore::InsertTextAtSelectionInternal("
5167            "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
5168            this, GetEscapedUTF8String(aInsertStr).get(), aTextChange,
5169            GetBoolName(mComposition.IsComposing())));
5170 
5171   Content& contentForTSF = ContentForTSFRef();
5172   if (!contentForTSF.IsInitialized()) {
5173     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5174             ("0x%p   TSFTextStore::InsertTextAtSelectionInternal() failed "
5175              "due to ContentForTSFRef() failure()",
5176              this));
5177     return false;
5178   }
5179 
5180   MaybeDispatchKeyboardEventAsProcessedByIME();
5181   if (mDestroyed) {
5182     MOZ_LOG(
5183         sTextStoreLog, LogLevel::Error,
5184         ("0x%p   TSFTextStore::InsertTextAtSelectionInternal() FAILED due to "
5185          "destroyed during dispatching a keyboard event",
5186          this));
5187     return false;
5188   }
5189 
5190   TS_SELECTION_ACP oldSelection = contentForTSF.Selection().ACP();
5191   if (!mComposition.IsComposing()) {
5192     // Use a temporary composition to contain the text
5193     PendingAction* compositionStart = mPendingActions.AppendElements(2);
5194     PendingAction* compositionEnd = compositionStart + 1;
5195 
5196     compositionStart->mType = PendingAction::Type::eCompositionStart;
5197     compositionStart->mSelectionStart = oldSelection.acpStart;
5198     compositionStart->mSelectionLength =
5199         oldSelection.acpEnd - oldSelection.acpStart;
5200     compositionStart->mAdjustSelection = false;
5201 
5202     compositionEnd->mType = PendingAction::Type::eCompositionEnd;
5203     compositionEnd->mData = aInsertStr;
5204     compositionEnd->mSelectionStart = compositionStart->mSelectionStart;
5205 
5206     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5207             ("0x%p   TSFTextStore::InsertTextAtSelectionInternal() "
5208              "appending pending compositionstart and compositionend... "
5209              "PendingCompositionStart={ mSelectionStart=%d, "
5210              "mSelectionLength=%d }, PendingCompositionEnd={ mData=\"%s\" "
5211              "(Length()=%u), mSelectionStart=%d }",
5212              this, compositionStart->mSelectionStart,
5213              compositionStart->mSelectionLength,
5214              GetEscapedUTF8String(compositionEnd->mData).get(),
5215              compositionEnd->mData.Length(), compositionEnd->mSelectionStart));
5216   }
5217 
5218   contentForTSF.ReplaceSelectedTextWith(aInsertStr);
5219 
5220   if (aTextChange) {
5221     aTextChange->acpStart = oldSelection.acpStart;
5222     aTextChange->acpOldEnd = oldSelection.acpEnd;
5223     aTextChange->acpNewEnd = contentForTSF.Selection().EndOffset();
5224   }
5225 
5226   MOZ_LOG(
5227       sTextStoreLog, LogLevel::Debug,
5228       ("0x%p   TSFTextStore::InsertTextAtSelectionInternal() "
5229        "succeeded: mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ "
5230        "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
5231        this, mWidget.get(), GetBoolName(mWidget ? mWidget->Destroyed() : true),
5232        aTextChange ? aTextChange->acpStart : 0,
5233        aTextChange ? aTextChange->acpOldEnd : 0,
5234        aTextChange ? aTextChange->acpNewEnd : 0));
5235   return true;
5236 }
5237 
5238 STDMETHODIMP
InsertEmbeddedAtSelection(DWORD dwFlags,IDataObject * pDataObject,LONG * pacpStart,LONG * pacpEnd,TS_TEXTCHANGE * pChange)5239 TSFTextStore::InsertEmbeddedAtSelection(DWORD dwFlags, IDataObject* pDataObject,
5240                                         LONG* pacpStart, LONG* pacpEnd,
5241                                         TS_TEXTCHANGE* pChange) {
5242   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5243           ("0x%p TSFTextStore::InsertEmbeddedAtSelection() called "
5244            "but not supported (E_NOTIMPL)",
5245            this));
5246 
5247   // embedded objects are not supported
5248   return E_NOTIMPL;
5249 }
5250 
5251 HRESULT
RecordCompositionStartAction(ITfCompositionView * aComposition,ITfRange * aRange,bool aPreserveSelection)5252 TSFTextStore::RecordCompositionStartAction(ITfCompositionView* aComposition,
5253                                            ITfRange* aRange,
5254                                            bool aPreserveSelection) {
5255   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5256           ("0x%p   TSFTextStore::RecordCompositionStartAction("
5257            "aComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), "
5258            "mComposition.mView=0x%p",
5259            this, aComposition, aRange, GetBoolName(aPreserveSelection),
5260            mComposition.mView.get()));
5261 
5262   LONG start = 0, length = 0;
5263   HRESULT hr = GetRangeExtent(aRange, &start, &length);
5264   if (FAILED(hr)) {
5265     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5266             ("0x%p   TSFTextStore::RecordCompositionStartAction() FAILED "
5267              "due to GetRangeExtent() failure",
5268              this));
5269     return hr;
5270   }
5271 
5272   return RecordCompositionStartAction(aComposition, start, length,
5273                                       aPreserveSelection);
5274 }
5275 
5276 HRESULT
RecordCompositionStartAction(ITfCompositionView * aComposition,LONG aStart,LONG aLength,bool aPreserveSelection)5277 TSFTextStore::RecordCompositionStartAction(ITfCompositionView* aComposition,
5278                                            LONG aStart, LONG aLength,
5279                                            bool aPreserveSelection) {
5280   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5281           ("0x%p   TSFTextStore::RecordCompositionStartAction("
5282            "aComposition=0x%p, aStart=%d, aLength=%d, aPreserveSelection=%s), "
5283            "mComposition.mView=0x%p",
5284            this, aComposition, aStart, aLength, GetBoolName(aPreserveSelection),
5285            mComposition.mView.get()));
5286 
5287   Content& contentForTSF = ContentForTSFRef();
5288   if (!contentForTSF.IsInitialized()) {
5289     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5290             ("0x%p   TSFTextStore::RecordCompositionStartAction() FAILED "
5291              "due to ContentForTSFRef() failure",
5292              this));
5293     return E_FAIL;
5294   }
5295 
5296   MaybeDispatchKeyboardEventAsProcessedByIME();
5297   if (mDestroyed) {
5298     MOZ_LOG(
5299         sTextStoreLog, LogLevel::Error,
5300         ("0x%p   TSFTextStore::RecordCompositionStartAction() FAILED due to "
5301          "destroyed during dispatching a keyboard event",
5302          this));
5303     return false;
5304   }
5305 
5306   CompleteLastActionIfStillIncomplete();
5307 
5308   // TIP may have inserted text at selection before calling
5309   // OnStartComposition().  In this case, we've already created a pending
5310   // compositionend.  If new composition replaces all commit string of the
5311   // pending compositionend, we should cancel the pending compositionend and
5312   // keep the previous composition normally.
5313   // On Windows 7, MS-IME for Korean, MS-IME 2010 for Korean and MS Old Hangul
5314   // may start composition with calling InsertTextAtSelection() and
5315   // OnStartComposition() with this order (bug 1208043).
5316   // On Windows 10, MS Pinyin, MS Wubi, MS ChangJie and MS Quick commits
5317   // last character and replace it with empty string with new composition
5318   // when user removes last character of composition string with Backspace
5319   // key (bug 1462257).
5320   if (!aPreserveSelection &&
5321       IsLastPendingActionCompositionEndAt(aStart, aLength)) {
5322     const PendingAction& pendingCompositionEnd = mPendingActions.LastElement();
5323     contentForTSF.RestoreCommittedComposition(aComposition,
5324                                               pendingCompositionEnd);
5325     mPendingActions.RemoveLastElement();
5326     MOZ_LOG(sTextStoreLog, LogLevel::Info,
5327             ("0x%p   TSFTextStore::RecordCompositionStartAction() "
5328              "succeeded: restoring the committed string as composing string, "
5329              "mComposition={ mStart=%ld, mString.Length()=%ld, "
5330              "mSelectionForTSF={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
5331              "style.fInterimChar=%s } }",
5332              this, mComposition.mStart, mComposition.mString.Length(),
5333              mSelectionForTSF.StartOffset(), mSelectionForTSF.EndOffset(),
5334              GetActiveSelEndName(mSelectionForTSF.ActiveSelEnd()),
5335              GetBoolName(mSelectionForTSF.IsInterimChar())));
5336     return S_OK;
5337   }
5338 
5339   PendingAction* action = mPendingActions.AppendElement();
5340   action->mType = PendingAction::Type::eCompositionStart;
5341   action->mSelectionStart = aStart;
5342   action->mSelectionLength = aLength;
5343 
5344   Selection& selectionForTSF = SelectionForTSFRef();
5345   if (selectionForTSF.IsDirty()) {
5346     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5347             ("0x%p   TSFTextStore::RecordCompositionStartAction() FAILED "
5348              "due to SelectionForTSFRef() failure",
5349              this));
5350     action->mAdjustSelection = true;
5351   } else if (selectionForTSF.MinOffset() != aStart ||
5352              selectionForTSF.MaxOffset() != aStart + aLength) {
5353     // If new composition range is different from current selection range,
5354     // we need to set selection before dispatching compositionstart event.
5355     action->mAdjustSelection = true;
5356   } else {
5357     // We shouldn't dispatch selection set event before dispatching
5358     // compositionstart event because it may cause put caret different
5359     // position in HTML editor since generated flat text content and offset in
5360     // it are lossy data of HTML contents.
5361     action->mAdjustSelection = false;
5362   }
5363 
5364   contentForTSF.StartComposition(aComposition, *action, aPreserveSelection);
5365   action->mData = mComposition.mString;
5366 
5367   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5368           ("0x%p   TSFTextStore::RecordCompositionStartAction() succeeded: "
5369            "mComposition={ mStart=%ld, mString.Length()=%ld, "
5370            "mSelectionForTSF={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
5371            "style.fInterimChar=%s } }",
5372            this, mComposition.mStart, mComposition.mString.Length(),
5373            mSelectionForTSF.StartOffset(), mSelectionForTSF.EndOffset(),
5374            GetActiveSelEndName(mSelectionForTSF.ActiveSelEnd()),
5375            GetBoolName(mSelectionForTSF.IsInterimChar())));
5376   return S_OK;
5377 }
5378 
5379 HRESULT
RecordCompositionEndAction()5380 TSFTextStore::RecordCompositionEndAction() {
5381   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5382           ("0x%p   TSFTextStore::RecordCompositionEndAction(), "
5383            "mComposition={ mView=0x%p, mString=\"%s\" }",
5384            this, mComposition.mView.get(),
5385            GetEscapedUTF8String(mComposition.mString).get()));
5386 
5387   MOZ_ASSERT(mComposition.IsComposing());
5388 
5389   MaybeDispatchKeyboardEventAsProcessedByIME();
5390   if (mDestroyed) {
5391     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5392             ("0x%p   TSFTextStore::RecordCompositionEndAction() FAILED due to "
5393              "destroyed during dispatching a keyboard event",
5394              this));
5395     return false;
5396   }
5397 
5398   // If we're handling incomplete composition update or already handled
5399   // composition update, we can forget them since composition end will send
5400   // the latest composition string and it overwrites the composition string
5401   // even if we dispatch eCompositionChange event before that.  So, let's
5402   // forget all composition updates now.
5403   RemoveLastCompositionUpdateActions();
5404   PendingAction* action = mPendingActions.AppendElement();
5405   action->mType = PendingAction::Type::eCompositionEnd;
5406   action->mData = mComposition.mString;
5407   action->mSelectionStart = mComposition.mStart;
5408 
5409   Content& contentForTSF = ContentForTSFRef();
5410   if (!contentForTSF.IsInitialized()) {
5411     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5412             ("0x%p   TSFTextStore::RecordCompositionEndAction() FAILED due "
5413              "to ContentForTSFRef() failure",
5414              this));
5415     return E_FAIL;
5416   }
5417   contentForTSF.EndComposition(*action);
5418 
5419   // If this composition was restart but the composition doesn't modify
5420   // anything, we should remove the pending composition for preventing to
5421   // dispatch redundant composition events.
5422   for (size_t i = mPendingActions.Length(), j = 1; i > 0; --i, ++j) {
5423     PendingAction& pendingAction = mPendingActions[i - 1];
5424     if (pendingAction.mType == PendingAction::Type::eCompositionStart) {
5425       if (pendingAction.mData != action->mData) {
5426         break;
5427       }
5428       // When only setting selection is necessary, we should append it.
5429       if (pendingAction.mAdjustSelection) {
5430         LONG selectionStart = pendingAction.mSelectionStart;
5431         LONG selectionLength = pendingAction.mSelectionLength;
5432 
5433         PendingAction* setSelection = mPendingActions.AppendElement();
5434         setSelection->mType = PendingAction::Type::eSetSelection;
5435         setSelection->mSelectionStart = selectionStart;
5436         setSelection->mSelectionLength = selectionLength;
5437         setSelection->mSelectionReversed = false;
5438       }
5439       // Remove the redundant pending composition.
5440       mPendingActions.RemoveElementsAt(i - 1, j);
5441       MOZ_LOG(sTextStoreLog, LogLevel::Info,
5442               ("0x%p   TSFTextStore::RecordCompositionEndAction(), "
5443                "succeeded, but the composition was canceled due to redundant",
5444                this));
5445       return S_OK;
5446     }
5447   }
5448 
5449   MOZ_LOG(
5450       sTextStoreLog, LogLevel::Info,
5451       ("0x%p   TSFTextStore::RecordCompositionEndAction(), succeeded", this));
5452   return S_OK;
5453 }
5454 
5455 STDMETHODIMP
OnStartComposition(ITfCompositionView * pComposition,BOOL * pfOk)5456 TSFTextStore::OnStartComposition(ITfCompositionView* pComposition, BOOL* pfOk) {
5457   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5458           ("0x%p TSFTextStore::OnStartComposition(pComposition=0x%p, "
5459            "pfOk=0x%p), mComposition.mView=0x%p",
5460            this, pComposition, pfOk, mComposition.mView.get()));
5461 
5462   AutoPendingActionAndContentFlusher flusher(this);
5463 
5464   *pfOk = FALSE;
5465 
5466   // Only one composition at a time
5467   if (mComposition.IsComposing()) {
5468     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5469             ("0x%p   TSFTextStore::OnStartComposition() FAILED due to "
5470              "there is another composition already (but returns S_OK)",
5471              this));
5472     return S_OK;
5473   }
5474 
5475   RefPtr<ITfRange> range;
5476   HRESULT hr = pComposition->GetRange(getter_AddRefs(range));
5477   if (FAILED(hr)) {
5478     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5479             ("0x%p   TSFTextStore::OnStartComposition() FAILED due to "
5480              "pComposition->GetRange() failure",
5481              this));
5482     return hr;
5483   }
5484   hr = RecordCompositionStartAction(pComposition, range, false);
5485   if (FAILED(hr)) {
5486     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5487             ("0x%p   TSFTextStore::OnStartComposition() FAILED due to "
5488              "RecordCompositionStartAction() failure",
5489              this));
5490     return hr;
5491   }
5492 
5493   *pfOk = TRUE;
5494   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5495           ("0x%p   TSFTextStore::OnStartComposition() succeeded", this));
5496   return S_OK;
5497 }
5498 
5499 STDMETHODIMP
OnUpdateComposition(ITfCompositionView * pComposition,ITfRange * pRangeNew)5500 TSFTextStore::OnUpdateComposition(ITfCompositionView* pComposition,
5501                                   ITfRange* pRangeNew) {
5502   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5503           ("0x%p TSFTextStore::OnUpdateComposition(pComposition=0x%p, "
5504            "pRangeNew=0x%p), mComposition.mView=0x%p",
5505            this, pComposition, pRangeNew, mComposition.mView.get()));
5506 
5507   AutoPendingActionAndContentFlusher flusher(this);
5508 
5509   if (!mDocumentMgr || !mContext) {
5510     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5511             ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5512              "not ready for the composition",
5513              this));
5514     return E_UNEXPECTED;
5515   }
5516   if (!mComposition.IsComposing()) {
5517     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5518             ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5519              "no active composition",
5520              this));
5521     return E_UNEXPECTED;
5522   }
5523   if (mComposition.mView != pComposition) {
5524     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5525             ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5526              "different composition view specified",
5527              this));
5528     return E_UNEXPECTED;
5529   }
5530 
5531   // pRangeNew is null when the update is not complete
5532   if (!pRangeNew) {
5533     MaybeDispatchKeyboardEventAsProcessedByIME();
5534     if (mDestroyed) {
5535       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5536               ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5537                "destroyed during dispatching a keyboard event",
5538                this));
5539       return E_FAIL;
5540     }
5541     PendingAction* action = LastOrNewPendingCompositionUpdate();
5542     action->mIncomplete = true;
5543     MOZ_LOG(sTextStoreLog, LogLevel::Info,
5544             ("0x%p   TSFTextStore::OnUpdateComposition() succeeded but "
5545              "not complete",
5546              this));
5547     return S_OK;
5548   }
5549 
5550   HRESULT hr = RestartCompositionIfNecessary(pRangeNew);
5551   if (FAILED(hr)) {
5552     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5553             ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5554              "RestartCompositionIfNecessary() failure",
5555              this));
5556     return hr;
5557   }
5558 
5559   hr = RecordCompositionUpdateAction();
5560   if (FAILED(hr)) {
5561     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5562             ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5563              "RecordCompositionUpdateAction() failure",
5564              this));
5565     return hr;
5566   }
5567 
5568   if (MOZ_LOG_TEST(sTextStoreLog, LogLevel::Info)) {
5569     Selection& selectionForTSF = SelectionForTSFRef();
5570     if (selectionForTSF.IsDirty()) {
5571       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5572               ("0x%p   TSFTextStore::OnUpdateComposition() FAILED due to "
5573                "SelectionForTSFRef() failure",
5574                this));
5575       return E_FAIL;
5576     }
5577     MOZ_LOG(sTextStoreLog, LogLevel::Info,
5578             ("0x%p   TSFTextStore::OnUpdateComposition() succeeded: "
5579              "mComposition={ mStart=%ld, mString=\"%s\" }, "
5580              "SelectionForTSFRef()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
5581              this, mComposition.mStart,
5582              GetEscapedUTF8String(mComposition.mString).get(),
5583              selectionForTSF.StartOffset(), selectionForTSF.EndOffset(),
5584              GetActiveSelEndName(selectionForTSF.ActiveSelEnd())));
5585   }
5586   return S_OK;
5587 }
5588 
5589 STDMETHODIMP
OnEndComposition(ITfCompositionView * pComposition)5590 TSFTextStore::OnEndComposition(ITfCompositionView* pComposition) {
5591   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5592           ("0x%p TSFTextStore::OnEndComposition(pComposition=0x%p), "
5593            "mComposition={ mView=0x%p, mString=\"%s\" }",
5594            this, pComposition, mComposition.mView.get(),
5595            GetEscapedUTF8String(mComposition.mString).get()));
5596 
5597   AutoPendingActionAndContentFlusher flusher(this);
5598 
5599   if (!mComposition.IsComposing()) {
5600     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5601             ("0x%p   TSFTextStore::OnEndComposition() FAILED due to "
5602              "no active composition",
5603              this));
5604     return E_UNEXPECTED;
5605   }
5606 
5607   if (mComposition.mView != pComposition) {
5608     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5609             ("0x%p   TSFTextStore::OnEndComposition() FAILED due to "
5610              "different composition view specified",
5611              this));
5612     return E_UNEXPECTED;
5613   }
5614 
5615   HRESULT hr = RecordCompositionEndAction();
5616   if (FAILED(hr)) {
5617     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5618             ("0x%p   TSFTextStore::OnEndComposition() FAILED due to "
5619              "RecordCompositionEndAction() failure",
5620              this));
5621     return hr;
5622   }
5623 
5624   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5625           ("0x%p   TSFTextStore::OnEndComposition(), succeeded", this));
5626   return S_OK;
5627 }
5628 
5629 STDMETHODIMP
AdviseMouseSink(ITfRangeACP * range,ITfMouseSink * pSink,DWORD * pdwCookie)5630 TSFTextStore::AdviseMouseSink(ITfRangeACP* range, ITfMouseSink* pSink,
5631                               DWORD* pdwCookie) {
5632   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5633           ("0x%p TSFTextStore::AdviseMouseSink(range=0x%p, pSink=0x%p, "
5634            "pdwCookie=0x%p)",
5635            this, range, pSink, pdwCookie));
5636 
5637   if (!pdwCookie) {
5638     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5639             ("0x%p   TSFTextStore::AdviseMouseSink() FAILED due to the "
5640              "pdwCookie is null",
5641              this));
5642     return E_INVALIDARG;
5643   }
5644   // Initialize the result with invalid cookie for safety.
5645   *pdwCookie = MouseTracker::kInvalidCookie;
5646 
5647   if (!range) {
5648     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5649             ("0x%p   TSFTextStore::AdviseMouseSink() FAILED due to the "
5650              "range is null",
5651              this));
5652     return E_INVALIDARG;
5653   }
5654   if (!pSink) {
5655     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5656             ("0x%p   TSFTextStore::AdviseMouseSink() FAILED due to the "
5657              "pSink is null",
5658              this));
5659     return E_INVALIDARG;
5660   }
5661 
5662   // Looking for an unusing tracker.
5663   MouseTracker* tracker = nullptr;
5664   for (size_t i = 0; i < mMouseTrackers.Length(); i++) {
5665     if (mMouseTrackers[i].IsUsing()) {
5666       continue;
5667     }
5668     tracker = &mMouseTrackers[i];
5669   }
5670   // If there is no unusing tracker, create new one.
5671   // XXX Should we make limitation of the number of installs?
5672   if (!tracker) {
5673     tracker = mMouseTrackers.AppendElement();
5674     HRESULT hr = tracker->Init(this);
5675     if (FAILED(hr)) {
5676       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5677               ("0x%p   TSFTextStore::AdviseMouseSink() FAILED due to "
5678                "failure of MouseTracker::Init()",
5679                this));
5680       return hr;
5681     }
5682   }
5683   HRESULT hr = tracker->AdviseSink(this, range, pSink);
5684   if (FAILED(hr)) {
5685     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5686             ("0x%p   TSFTextStore::AdviseMouseSink() FAILED due to failure "
5687              "of MouseTracker::Init()",
5688              this));
5689     return hr;
5690   }
5691   *pdwCookie = tracker->Cookie();
5692   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5693           ("0x%p   TSFTextStore::AdviseMouseSink(), succeeded, "
5694            "*pdwCookie=%d",
5695            this, *pdwCookie));
5696   return S_OK;
5697 }
5698 
5699 STDMETHODIMP
UnadviseMouseSink(DWORD dwCookie)5700 TSFTextStore::UnadviseMouseSink(DWORD dwCookie) {
5701   MOZ_LOG(
5702       sTextStoreLog, LogLevel::Info,
5703       ("0x%p TSFTextStore::UnadviseMouseSink(dwCookie=%d)", this, dwCookie));
5704   if (dwCookie == MouseTracker::kInvalidCookie) {
5705     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5706             ("0x%p   TSFTextStore::UnadviseMouseSink() FAILED due to "
5707              "the cookie is invalid value",
5708              this));
5709     return E_INVALIDARG;
5710   }
5711   // The cookie value must be an index of mMouseTrackers.
5712   // We can use this shortcut for now.
5713   if (static_cast<size_t>(dwCookie) >= mMouseTrackers.Length()) {
5714     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5715             ("0x%p   TSFTextStore::UnadviseMouseSink() FAILED due to "
5716              "the cookie is too large value",
5717              this));
5718     return E_INVALIDARG;
5719   }
5720   MouseTracker& tracker = mMouseTrackers[dwCookie];
5721   if (!tracker.IsUsing()) {
5722     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5723             ("0x%p   TSFTextStore::UnadviseMouseSink() FAILED due to "
5724              "the found tracker uninstalled already",
5725              this));
5726     return E_INVALIDARG;
5727   }
5728   tracker.UnadviseSink();
5729   MOZ_LOG(sTextStoreLog, LogLevel::Info,
5730           ("0x%p   TSFTextStore::UnadviseMouseSink(), succeeded", this));
5731   return S_OK;
5732 }
5733 
5734 // static
OnFocusChange(bool aGotFocus,nsWindowBase * aFocusedWidget,const InputContext & aContext)5735 nsresult TSFTextStore::OnFocusChange(bool aGotFocus,
5736                                      nsWindowBase* aFocusedWidget,
5737                                      const InputContext& aContext) {
5738   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5739           ("  TSFTextStore::OnFocusChange(aGotFocus=%s, "
5740            "aFocusedWidget=0x%p, aContext=%s), "
5741            "sThreadMgr=0x%p, sEnabledTextStore=0x%p",
5742            GetBoolName(aGotFocus), aFocusedWidget,
5743            GetInputContextString(aContext).get(), sThreadMgr.get(),
5744            sEnabledTextStore.get()));
5745 
5746   if (NS_WARN_IF(!IsInTSFMode())) {
5747     return NS_ERROR_NOT_AVAILABLE;
5748   }
5749 
5750   RefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
5751   bool hasFocus = ThinksHavingFocus();
5752   RefPtr<TSFTextStore> oldTextStore = sEnabledTextStore.forget();
5753 
5754   // If currently oldTextStore still has focus, notifies TSF of losing focus.
5755   if (hasFocus) {
5756     RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
5757     DebugOnly<HRESULT> hr = threadMgr->AssociateFocus(
5758         oldTextStore->mWidget->GetWindowHandle(), nullptr,
5759         getter_AddRefs(prevFocusedDocumentMgr));
5760     NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
5761     NS_ASSERTION(prevFocusedDocumentMgr == oldTextStore->mDocumentMgr,
5762                  "different documentMgr has been associated with the window");
5763   }
5764 
5765   // Even if there was a focused TextStore, we won't use it with new focused
5766   // editor.  So, release it now.
5767   if (oldTextStore) {
5768     oldTextStore->Destroy();
5769   }
5770 
5771   if (NS_WARN_IF(!sThreadMgr)) {
5772     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5773             ("  TSFTextStore::OnFocusChange() FAILED, due to "
5774              "sThreadMgr being destroyed during calling "
5775              "ITfThreadMgr::AssociateFocus()"));
5776     return NS_ERROR_FAILURE;
5777   }
5778   if (NS_WARN_IF(sEnabledTextStore)) {
5779     MOZ_LOG(
5780         sTextStoreLog, LogLevel::Error,
5781         ("  TSFTextStore::OnFocusChange() FAILED, due to "
5782          "nested event handling has created another focused TextStore during "
5783          "calling ITfThreadMgr::AssociateFocus()"));
5784     return NS_ERROR_FAILURE;
5785   }
5786 
5787   // If this is a notification of blur, move focus to the dummy document
5788   // manager.
5789   if (!aGotFocus || !aContext.mIMEState.IsEditable()) {
5790     RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
5791     RefPtr<ITfDocumentMgr> disabledDocumentMgr = sDisabledDocumentMgr;
5792     HRESULT hr = threadMgr->SetFocus(disabledDocumentMgr);
5793     if (NS_WARN_IF(FAILED(hr))) {
5794       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5795               ("  TSFTextStore::OnFocusChange() FAILED due to "
5796                "ITfThreadMgr::SetFocus() failure"));
5797       return NS_ERROR_FAILURE;
5798     }
5799     return NS_OK;
5800   }
5801 
5802   // If an editor is getting focus, create new TextStore and set focus.
5803   if (NS_WARN_IF(!CreateAndSetFocus(aFocusedWidget, aContext))) {
5804     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5805             ("  TSFTextStore::OnFocusChange() FAILED due to "
5806              "ITfThreadMgr::CreateAndSetFocus() failure"));
5807     return NS_ERROR_FAILURE;
5808   }
5809   return NS_OK;
5810 }
5811 
5812 // static
EnsureToDestroyAndReleaseEnabledTextStoreIf(RefPtr<TSFTextStore> & aTextStore)5813 void TSFTextStore::EnsureToDestroyAndReleaseEnabledTextStoreIf(
5814     RefPtr<TSFTextStore>& aTextStore) {
5815   aTextStore->Destroy();
5816   if (sEnabledTextStore == aTextStore) {
5817     sEnabledTextStore = nullptr;
5818   }
5819   aTextStore = nullptr;
5820 }
5821 
5822 // static
CreateAndSetFocus(nsWindowBase * aFocusedWidget,const InputContext & aContext)5823 bool TSFTextStore::CreateAndSetFocus(nsWindowBase* aFocusedWidget,
5824                                      const InputContext& aContext) {
5825   // TSF might do something which causes that we need to access static methods
5826   // of TSFTextStore.  At that time, sEnabledTextStore may be necessary.
5827   // So, we should set sEnabledTextStore directly.
5828   RefPtr<TSFTextStore> textStore = new TSFTextStore();
5829   sEnabledTextStore = textStore;
5830   if (NS_WARN_IF(!textStore->Init(aFocusedWidget, aContext))) {
5831     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5832             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5833              "TSFTextStore::Init() failure"));
5834     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5835     return false;
5836   }
5837   RefPtr<ITfDocumentMgr> newDocMgr = textStore->mDocumentMgr;
5838   if (NS_WARN_IF(!newDocMgr)) {
5839     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5840             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5841              "invalid TSFTextStore::mDocumentMgr"));
5842     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5843     return false;
5844   }
5845   if (aContext.mIMEState.mEnabled == IMEState::PASSWORD) {
5846     MarkContextAsKeyboardDisabled(textStore->mContext);
5847     RefPtr<ITfContext> topContext;
5848     newDocMgr->GetTop(getter_AddRefs(topContext));
5849     if (topContext && topContext != textStore->mContext) {
5850       MarkContextAsKeyboardDisabled(topContext);
5851     }
5852   }
5853 
5854   HRESULT hr;
5855   RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
5856   hr = threadMgr->SetFocus(newDocMgr);
5857 
5858   if (NS_WARN_IF(FAILED(hr))) {
5859     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5860             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5861              "ITfTheadMgr::SetFocus() failure"));
5862     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5863     return false;
5864   }
5865   if (NS_WARN_IF(!sThreadMgr)) {
5866     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5867             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5868              "sThreadMgr being destroyed during calling "
5869              "ITfTheadMgr::SetFocus()"));
5870     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5871     return false;
5872   }
5873   if (NS_WARN_IF(sEnabledTextStore != textStore)) {
5874     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5875             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5876              "creating TextStore has lost focus during calling "
5877              "ITfThreadMgr::SetFocus()"));
5878     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5879     return false;
5880   }
5881 
5882   // Use AssociateFocus() for ensuring that any native focus event
5883   // never steal focus from our documentMgr.
5884   RefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
5885   hr = threadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(), newDocMgr,
5886                                  getter_AddRefs(prevFocusedDocumentMgr));
5887   if (NS_WARN_IF(FAILED(hr))) {
5888     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5889             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5890              "ITfTheadMgr::AssociateFocus() failure"));
5891     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5892     return false;
5893   }
5894   if (NS_WARN_IF(!sThreadMgr)) {
5895     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5896             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5897              "sThreadMgr being destroyed during calling "
5898              "ITfTheadMgr::AssociateFocus()"));
5899     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5900     return false;
5901   }
5902   if (NS_WARN_IF(sEnabledTextStore != textStore)) {
5903     MOZ_LOG(sTextStoreLog, LogLevel::Error,
5904             ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5905              "creating TextStore has lost focus during calling "
5906              "ITfTheadMgr::AssociateFocus()"));
5907     EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5908     return false;
5909   }
5910 
5911   if (textStore->mSink) {
5912     MOZ_LOG(sTextStoreLog, LogLevel::Info,
5913             ("  TSFTextStore::CreateAndSetFocus(), calling "
5914              "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE) for 0x%p...",
5915              textStore.get()));
5916     RefPtr<ITextStoreACPSink> sink = textStore->mSink;
5917     sink->OnLayoutChange(TS_LC_CREATE, TEXTSTORE_DEFAULT_VIEW);
5918     if (NS_WARN_IF(sEnabledTextStore != textStore)) {
5919       MOZ_LOG(sTextStoreLog, LogLevel::Error,
5920               ("  TSFTextStore::CreateAndSetFocus() FAILED due to "
5921                "creating TextStore has lost focus during calling "
5922                "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE)"));
5923       EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
5924       return false;
5925     }
5926   }
5927   return true;
5928 }
5929 
5930 // static
GetIMENotificationRequests()5931 IMENotificationRequests TSFTextStore::GetIMENotificationRequests() {
5932   if (!sEnabledTextStore || NS_WARN_IF(!sEnabledTextStore->mDocumentMgr)) {
5933     // If there is no active text store, we don't need any notifications
5934     // since there is no sink which needs notifications.
5935     return IMENotificationRequests();
5936   }
5937 
5938   // Otherwise, requests all notifications since even if some of them may not
5939   // be required by the sink of active TIP, active TIP may be changed and
5940   // other TIPs may need all notifications.
5941   // Note that Windows temporarily steal focus from active window if the main
5942   // process which created the window becomes busy.  In this case, we shouldn't
5943   // commit composition since user may want to continue to compose the
5944   // composition after becoming not busy.  Therefore, we need notifications
5945   // even during deactive.
5946   // Be aware, we don't need to check actual focused text store.  For example,
5947   // MS-IME for Japanese handles focus messages by themselves and sets focused
5948   // text store to nullptr when the process is being inactivated.  However,
5949   // we still need to reuse sEnabledTextStore if the process is activated and
5950   // focused element isn't changed.  Therefore, if sEnabledTextStore isn't
5951   // nullptr, we need to keep notifying the sink even when it is not focused
5952   // text store for the thread manager.
5953   return IMENotificationRequests(
5954       IMENotificationRequests::NOTIFY_TEXT_CHANGE |
5955       IMENotificationRequests::NOTIFY_POSITION_CHANGE |
5956       IMENotificationRequests::NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR |
5957       IMENotificationRequests::NOTIFY_DURING_DEACTIVE);
5958 }
5959 
OnTextChangeInternal(const IMENotification & aIMENotification)5960 nsresult TSFTextStore::OnTextChangeInternal(
5961     const IMENotification& aIMENotification) {
5962   const TextChangeDataBase& textChangeData = aIMENotification.mTextChangeData;
5963 
5964   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
5965           ("0x%p   TSFTextStore::OnTextChangeInternal(aIMENotification={ "
5966            "mMessage=0x%08X, mTextChangeData=%s }), "
5967            "mDestroyed=%s, mSink=0x%p, mSinkMask=%s, "
5968            "mComposition.IsComposing()=%s",
5969            this, aIMENotification.mMessage,
5970            mozilla::ToString(textChangeData).c_str(), GetBoolName(mDestroyed),
5971            mSink.get(), GetSinkMaskNameStr(mSinkMask).get(),
5972            GetBoolName(mComposition.IsComposing())));
5973 
5974   if (mDestroyed) {
5975     // If this instance is already destroyed, we shouldn't notify TSF of any
5976     // changes.
5977     return NS_OK;
5978   }
5979 
5980   mDeferNotifyingTSF = false;
5981 
5982   // Different from selection change, we don't modify anything with text
5983   // change data.  Therefore, if neither TSF not TIP wants text change
5984   // notifications, we don't need to store the changes.
5985   if (!mSink || !(mSinkMask & TS_AS_TEXT_CHANGE)) {
5986     return NS_OK;
5987   }
5988 
5989   // Merge any text change data even if it's caused by composition.
5990   mPendingTextChangeData.MergeWith(textChangeData);
5991 
5992   MaybeFlushPendingNotifications();
5993 
5994   return NS_OK;
5995 }
5996 
NotifyTSFOfTextChange()5997 void TSFTextStore::NotifyTSFOfTextChange() {
5998   MOZ_ASSERT(!mDestroyed);
5999   MOZ_ASSERT(!IsReadLocked());
6000   MOZ_ASSERT(!mComposition.IsComposing());
6001   MOZ_ASSERT(mPendingTextChangeData.IsValid());
6002 
6003   // If the text changes are caused only by composition, we don't need to
6004   // notify TSF of the text changes.
6005   if (mPendingTextChangeData.mCausedOnlyByComposition) {
6006     mPendingTextChangeData.Clear();
6007     return;
6008   }
6009 
6010   // First, forget cached selection.
6011   mSelectionForTSF.MarkDirty();
6012 
6013   // For making it safer, we should check if there is a valid sink to receive
6014   // text change notification.
6015   if (NS_WARN_IF(!mSink) || NS_WARN_IF(!(mSinkMask & TS_AS_TEXT_CHANGE))) {
6016     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6017             ("0x%p   TSFTextStore::NotifyTSFOfTextChange() FAILED due to "
6018              "mSink is not ready to call ITextStoreACPSink::OnTextChange()...",
6019              this));
6020     mPendingTextChangeData.Clear();
6021     return;
6022   }
6023 
6024   if (NS_WARN_IF(!mPendingTextChangeData.IsInInt32Range())) {
6025     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6026             ("0x%p   TSFTextStore::NotifyTSFOfTextChange() FAILED due to "
6027              "offset is too big for calling "
6028              "ITextStoreACPSink::OnTextChange()...",
6029              this));
6030     mPendingTextChangeData.Clear();
6031     return;
6032   }
6033 
6034   TS_TEXTCHANGE textChange;
6035   textChange.acpStart = static_cast<LONG>(mPendingTextChangeData.mStartOffset);
6036   textChange.acpOldEnd =
6037       static_cast<LONG>(mPendingTextChangeData.mRemovedEndOffset);
6038   textChange.acpNewEnd =
6039       static_cast<LONG>(mPendingTextChangeData.mAddedEndOffset);
6040   mPendingTextChangeData.Clear();
6041 
6042   MOZ_LOG(
6043       sTextStoreLog, LogLevel::Info,
6044       ("0x%p   TSFTextStore::NotifyTSFOfTextChange(), calling "
6045        "ITextStoreACPSink::OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
6046        "acpNewEnd=%ld })...",
6047        this, textChange.acpStart, textChange.acpOldEnd, textChange.acpNewEnd));
6048   RefPtr<ITextStoreACPSink> sink = mSink;
6049   sink->OnTextChange(0, &textChange);
6050 }
6051 
OnSelectionChangeInternal(const IMENotification & aIMENotification)6052 nsresult TSFTextStore::OnSelectionChangeInternal(
6053     const IMENotification& aIMENotification) {
6054   const SelectionChangeDataBase& selectionChangeData =
6055       aIMENotification.mSelectionChangeData;
6056   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6057           ("0x%p   TSFTextStore::OnSelectionChangeInternal("
6058            "aIMENotification={ mSelectionChangeData=%s }), mDestroyed=%s, "
6059            "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
6060            "mComposition.IsComposing()=%s",
6061            this, mozilla::ToString(selectionChangeData).c_str(),
6062            GetBoolName(mDestroyed), mSink.get(),
6063            GetSinkMaskNameStr(mSinkMask).get(),
6064            GetBoolName(mIsRecordingActionsWithoutLock),
6065            GetBoolName(mComposition.IsComposing())));
6066 
6067   if (mDestroyed) {
6068     // If this instance is already destroyed, we shouldn't notify TSF of any
6069     // changes.
6070     return NS_OK;
6071   }
6072 
6073   mDeferNotifyingTSF = false;
6074 
6075   // Assign the new selection change data to the pending selection change data
6076   // because only the latest selection data is necessary.
6077   // Note that this is necessary to update mSelectionForTSF.  Therefore, even if
6078   // neither TSF nor TIP wants selection change notifications, we need to
6079   // store the selection information.
6080   mPendingSelectionChangeData.Assign(selectionChangeData);
6081 
6082   // Flush remaining pending notifications here if it's possible.
6083   MaybeFlushPendingNotifications();
6084 
6085   // If we're available, we should create native caret instead of IMEHandler
6086   // because we may have some cache to do it.
6087   // Note that if we have composition, we'll notified composition-updated
6088   // later so that we don't need to create native caret in such case.
6089   if (!IsHandlingComposition() && IMEHandler::NeedsToCreateNativeCaret()) {
6090     CreateNativeCaret();
6091   }
6092 
6093   return NS_OK;
6094 }
6095 
NotifyTSFOfSelectionChange()6096 void TSFTextStore::NotifyTSFOfSelectionChange() {
6097   MOZ_ASSERT(!mDestroyed);
6098   MOZ_ASSERT(!IsReadLocked());
6099   MOZ_ASSERT(!mComposition.IsComposing());
6100   MOZ_ASSERT(mPendingSelectionChangeData.IsValid());
6101 
6102   // If selection range isn't actually changed, we don't need to notify TSF
6103   // of this selection change.
6104   if (!mSelectionForTSF.SetSelection(
6105           mPendingSelectionChangeData.mOffset,
6106           mPendingSelectionChangeData.Length(),
6107           mPendingSelectionChangeData.mReversed,
6108           mPendingSelectionChangeData.GetWritingMode())) {
6109     mPendingSelectionChangeData.Clear();
6110     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6111             ("0x%p   TSFTextStore::NotifyTSFOfSelectionChange(), "
6112              "selection isn't actually changed.",
6113              this));
6114     return;
6115   }
6116 
6117   mPendingSelectionChangeData.Clear();
6118 
6119   if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) {
6120     return;
6121   }
6122 
6123   MOZ_LOG(sTextStoreLog, LogLevel::Info,
6124           ("0x%p   TSFTextStore::NotifyTSFOfSelectionChange(), calling "
6125            "ITextStoreACPSink::OnSelectionChange()...",
6126            this));
6127   RefPtr<ITextStoreACPSink> sink = mSink;
6128   sink->OnSelectionChange();
6129 }
6130 
OnLayoutChangeInternal()6131 nsresult TSFTextStore::OnLayoutChangeInternal() {
6132   if (mDestroyed) {
6133     // If this instance is already destroyed, we shouldn't notify TSF of any
6134     // changes.
6135     return NS_OK;
6136   }
6137 
6138   NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
6139   NS_ENSURE_TRUE(mSink, NS_ERROR_FAILURE);
6140 
6141   mDeferNotifyingTSF = false;
6142 
6143   nsresult rv = NS_OK;
6144 
6145   // We need to notify TSF of layout change even if the document is locked.
6146   // So, don't use MaybeFlushPendingNotifications() for flushing pending
6147   // layout change.
6148   MOZ_LOG(sTextStoreLog, LogLevel::Info,
6149           ("0x%p   TSFTextStore::OnLayoutChangeInternal(), calling "
6150            "NotifyTSFOfLayoutChange()...",
6151            this));
6152   if (NS_WARN_IF(!NotifyTSFOfLayoutChange())) {
6153     rv = NS_ERROR_FAILURE;
6154   }
6155 
6156   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6157           ("0x%p   TSFTextStore::OnLayoutChangeInternal(), calling "
6158            "MaybeFlushPendingNotifications()...",
6159            this));
6160   MaybeFlushPendingNotifications();
6161 
6162   return rv;
6163 }
6164 
NotifyTSFOfLayoutChange()6165 bool TSFTextStore::NotifyTSFOfLayoutChange() {
6166   MOZ_ASSERT(!mDestroyed);
6167 
6168   // If we're waiting a query of layout information from TIP, it means that
6169   // we've returned TS_E_NOLAYOUT error.
6170   bool returnedNoLayoutError = mHasReturnedNoLayoutError || mWaitingQueryLayout;
6171 
6172   // If we returned TS_E_NOLAYOUT, TIP should query the computed layout again.
6173   mWaitingQueryLayout = returnedNoLayoutError;
6174 
6175   // For avoiding to call this method again at unlocking the document during
6176   // calls of OnLayoutChange(), reset mHasReturnedNoLayoutError.
6177   mHasReturnedNoLayoutError = false;
6178 
6179   // Now, layout has been computed.  We should notify mContentForTSF for
6180   // making GetTextExt() and GetACPFromPoint() not return TS_E_NOLAYOUT.
6181   if (mContentForTSF.IsInitialized()) {
6182     mContentForTSF.OnLayoutChanged();
6183   }
6184 
6185   if (IMEHandler::NeedsToCreateNativeCaret()) {
6186     // If we're available, we should create native caret instead of IMEHandler
6187     // because we may have some cache to do it.
6188     CreateNativeCaret();
6189   } else {
6190     // Now, the caret position is different from ours.  Destroy the native caret
6191     // if we've create it only for GetTextExt().
6192     IMEHandler::MaybeDestroyNativeCaret();
6193   }
6194 
6195   // This method should return true if either way succeeds.
6196   bool ret = true;
6197 
6198   if (mSink) {
6199     MOZ_LOG(sTextStoreLog, LogLevel::Info,
6200             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6201              "calling ITextStoreACPSink::OnLayoutChange()...",
6202              this));
6203     RefPtr<ITextStoreACPSink> sink = mSink;
6204     HRESULT hr = sink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
6205     MOZ_LOG(sTextStoreLog, LogLevel::Info,
6206             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6207              "called ITextStoreACPSink::OnLayoutChange()",
6208              this));
6209     ret = SUCCEEDED(hr);
6210   }
6211 
6212   // The layout change caused by composition string change should cause
6213   // calling ITfContextOwnerServices::OnLayoutChange() too.
6214   if (returnedNoLayoutError && mContext) {
6215     RefPtr<ITfContextOwnerServices> service;
6216     mContext->QueryInterface(IID_ITfContextOwnerServices,
6217                              getter_AddRefs(service));
6218     if (service) {
6219       MOZ_LOG(sTextStoreLog, LogLevel::Info,
6220               ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6221                "calling ITfContextOwnerServices::OnLayoutChange()...",
6222                this));
6223       HRESULT hr = service->OnLayoutChange();
6224       ret = ret && SUCCEEDED(hr);
6225       MOZ_LOG(sTextStoreLog, LogLevel::Info,
6226               ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6227                "called ITfContextOwnerServices::OnLayoutChange()",
6228                this));
6229     }
6230   }
6231 
6232   if (!mWidget || mWidget->Destroyed()) {
6233     MOZ_LOG(sTextStoreLog, LogLevel::Info,
6234             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6235              "the widget is destroyed during calling OnLayoutChange()",
6236              this));
6237     return ret;
6238   }
6239 
6240   if (mDestroyed) {
6241     MOZ_LOG(sTextStoreLog, LogLevel::Info,
6242             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6243              "the TSFTextStore instance is destroyed during calling "
6244              "OnLayoutChange()",
6245              this));
6246     return ret;
6247   }
6248 
6249   // If we returned TS_E_NOLAYOUT again, we need another call of
6250   // OnLayoutChange() later.  So, let's wait a query from TIP.
6251   if (mHasReturnedNoLayoutError) {
6252     mWaitingQueryLayout = true;
6253   }
6254 
6255   if (!mWaitingQueryLayout) {
6256     MOZ_LOG(sTextStoreLog, LogLevel::Info,
6257             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6258              "succeeded notifying TIP of our layout change",
6259              this));
6260     return ret;
6261   }
6262 
6263   // If we believe that TIP needs to retry to retrieve our layout information
6264   // later, we should call it with ::PostMessage() hack.
6265   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6266           ("0x%p   TSFTextStore::NotifyTSFOfLayoutChange(), "
6267            "posing  MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE for calling "
6268            "OnLayoutChange() again...",
6269            this));
6270   ::PostMessage(mWidget->GetWindowHandle(), MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE,
6271                 reinterpret_cast<WPARAM>(this), 0);
6272 
6273   return true;
6274 }
6275 
NotifyTSFOfLayoutChangeAgain()6276 void TSFTextStore::NotifyTSFOfLayoutChangeAgain() {
6277   // Don't notify TSF of layout change after destroyed.
6278   if (mDestroyed) {
6279     mWaitingQueryLayout = false;
6280     return;
6281   }
6282 
6283   // Before preforming this method, TIP has accessed our layout information by
6284   // itself.  In such case, we don't need to call OnLayoutChange() anymore.
6285   if (!mWaitingQueryLayout) {
6286     return;
6287   }
6288 
6289   MOZ_LOG(sTextStoreLog, LogLevel::Info,
6290           ("0x%p   TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6291            "calling NotifyTSFOfLayoutChange()...",
6292            this));
6293   NotifyTSFOfLayoutChange();
6294 
6295   // If TIP didn't retrieved our layout information during a call of
6296   // NotifyTSFOfLayoutChange(), it means that the TIP already gave up to
6297   // retry to retrieve layout information or doesn't necessary it anymore.
6298   // But don't forget that the call may have caused returning TS_E_NOLAYOUT
6299   // error again.  In such case we still need to call OnLayoutChange() later.
6300   if (!mHasReturnedNoLayoutError && mWaitingQueryLayout) {
6301     mWaitingQueryLayout = false;
6302     MOZ_LOG(sTextStoreLog, LogLevel::Warning,
6303             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6304              "called NotifyTSFOfLayoutChange() but TIP didn't retry to "
6305              "retrieve the layout information",
6306              this));
6307   } else {
6308     MOZ_LOG(sTextStoreLog, LogLevel::Info,
6309             ("0x%p   TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6310              "called NotifyTSFOfLayoutChange()",
6311              this));
6312   }
6313 }
6314 
OnUpdateCompositionInternal()6315 nsresult TSFTextStore::OnUpdateCompositionInternal() {
6316   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6317           ("0x%p   TSFTextStore::OnUpdateCompositionInternal(), "
6318            "mDestroyed=%s, mDeferNotifyingTSF=%s",
6319            this, GetBoolName(mDestroyed), GetBoolName(mDeferNotifyingTSF)));
6320 
6321   // There are nothing to do after destroyed.
6322   if (mDestroyed) {
6323     return NS_OK;
6324   }
6325 
6326   // Update cached data now because all pending events have been handled now.
6327   mContentForTSF.OnCompositionEventsHandled();
6328 
6329   // If composition is completely finished both in TSF/TIP and the focused
6330   // editor which may be in a remote process, we can clear the cache and don't
6331   // have it until starting next composition.
6332   if (!mComposition.IsComposing() && !IsHandlingComposition()) {
6333     mDeferClearingContentForTSF = false;
6334   }
6335   mDeferNotifyingTSF = false;
6336   MaybeFlushPendingNotifications();
6337 
6338   // If we're available, we should create native caret instead of IMEHandler
6339   // because we may have some cache to do it.
6340   if (IMEHandler::NeedsToCreateNativeCaret()) {
6341     CreateNativeCaret();
6342   }
6343 
6344   return NS_OK;
6345 }
6346 
OnMouseButtonEventInternal(const IMENotification & aIMENotification)6347 nsresult TSFTextStore::OnMouseButtonEventInternal(
6348     const IMENotification& aIMENotification) {
6349   if (mDestroyed) {
6350     // If this instance is already destroyed, we shouldn't notify TSF of any
6351     // events.
6352     return NS_OK;
6353   }
6354 
6355   if (mMouseTrackers.IsEmpty()) {
6356     return NS_OK;
6357   }
6358 
6359   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6360           ("0x%p   TSFTextStore::OnMouseButtonEventInternal("
6361            "aIMENotification={ mEventMessage=%s, mOffset=%u, mCursorPos={ "
6362            "mX=%d, mY=%d }, mCharRect={ mX=%d, mY=%d, mWidth=%d, mHeight=%d }, "
6363            "mButton=%s, mButtons=%s, mModifiers=%s })",
6364            this, ToChar(aIMENotification.mMouseButtonEventData.mEventMessage),
6365            aIMENotification.mMouseButtonEventData.mOffset,
6366            aIMENotification.mMouseButtonEventData.mCursorPos.mX,
6367            aIMENotification.mMouseButtonEventData.mCursorPos.mY,
6368            aIMENotification.mMouseButtonEventData.mCharRect.mX,
6369            aIMENotification.mMouseButtonEventData.mCharRect.mY,
6370            aIMENotification.mMouseButtonEventData.mCharRect.mWidth,
6371            aIMENotification.mMouseButtonEventData.mCharRect.mHeight,
6372            GetMouseButtonName(aIMENotification.mMouseButtonEventData.mButton),
6373            GetMouseButtonsName(aIMENotification.mMouseButtonEventData.mButtons)
6374                .get(),
6375            GetModifiersName(aIMENotification.mMouseButtonEventData.mModifiers)
6376                .get()));
6377 
6378   uint32_t offset = aIMENotification.mMouseButtonEventData.mOffset;
6379   nsIntRect charRect =
6380       aIMENotification.mMouseButtonEventData.mCharRect.AsIntRect();
6381   nsIntPoint cursorPos =
6382       aIMENotification.mMouseButtonEventData.mCursorPos.AsIntPoint();
6383   ULONG quadrant = 1;
6384   if (charRect.Width() > 0) {
6385     int32_t cursorXInChar = cursorPos.x - charRect.X();
6386     quadrant = cursorXInChar * 4 / charRect.Width();
6387     quadrant = (quadrant + 2) % 4;
6388   }
6389   ULONG edge = quadrant < 2 ? offset + 1 : offset;
6390   DWORD buttonStatus = 0;
6391   bool isMouseUp =
6392       aIMENotification.mMouseButtonEventData.mEventMessage == eMouseUp;
6393   if (!isMouseUp) {
6394     switch (aIMENotification.mMouseButtonEventData.mButton) {
6395       case MouseButton::eLeft:
6396         buttonStatus = MK_LBUTTON;
6397         break;
6398       case MouseButton::eMiddle:
6399         buttonStatus = MK_MBUTTON;
6400         break;
6401       case MouseButton::eRight:
6402         buttonStatus = MK_RBUTTON;
6403         break;
6404     }
6405   }
6406   if (aIMENotification.mMouseButtonEventData.mModifiers & MODIFIER_CONTROL) {
6407     buttonStatus |= MK_CONTROL;
6408   }
6409   if (aIMENotification.mMouseButtonEventData.mModifiers & MODIFIER_SHIFT) {
6410     buttonStatus |= MK_SHIFT;
6411   }
6412   for (size_t i = 0; i < mMouseTrackers.Length(); i++) {
6413     MouseTracker& tracker = mMouseTrackers[i];
6414     if (!tracker.IsUsing() || !tracker.InRange(offset)) {
6415       continue;
6416     }
6417     if (tracker.OnMouseButtonEvent(edge - tracker.RangeStart(), quadrant,
6418                                    buttonStatus)) {
6419       return NS_SUCCESS_EVENT_CONSUMED;
6420     }
6421   }
6422   return NS_OK;
6423 }
6424 
CreateNativeCaret()6425 void TSFTextStore::CreateNativeCaret() {
6426   MOZ_ASSERT(!IMEHandler::IsA11yHandlingNativeCaret());
6427 
6428   IMEHandler::MaybeDestroyNativeCaret();
6429 
6430   // Don't create native caret after destroyed.
6431   if (mDestroyed) {
6432     return;
6433   }
6434 
6435   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6436           ("0x%p   TSFTextStore::CreateNativeCaret(), "
6437            "mComposition.IsComposing()=%s",
6438            this, GetBoolName(mComposition.IsComposing())));
6439 
6440   Selection& selectionForTSF = SelectionForTSFRef();
6441   if (selectionForTSF.IsDirty()) {
6442     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6443             ("0x%p   TSFTextStore::CreateNativeCaret() FAILED due to "
6444              "SelectionForTSFRef() failure",
6445              this));
6446     return;
6447   }
6448 
6449   WidgetQueryContentEvent queryCaretRect(true, eQueryCaretRect, mWidget);
6450   mWidget->InitEvent(queryCaretRect);
6451 
6452   WidgetQueryContentEvent::Options options;
6453   // XXX If this is called without composition and the selection isn't
6454   //     collapsed, is it OK?
6455   int64_t caretOffset = selectionForTSF.MaxOffset();
6456   if (mComposition.IsComposing()) {
6457     // If there is a composition, use insertion point relative query for
6458     // deciding caret position because composition might be at different
6459     // position where TSFTextStore believes it at.
6460     options.mRelativeToInsertionPoint = true;
6461     caretOffset -= mComposition.mStart;
6462   } else if (!CanAccessActualContentDirectly()) {
6463     // If TSF/TIP cannot access actual content directly, there may be pending
6464     // text and/or selection changes which have not been notified TSF yet.
6465     // Therefore, we should use relative to insertion point query since
6466     // TSF/TIP computes the offset from the cached selection.
6467     options.mRelativeToInsertionPoint = true;
6468     caretOffset -= mSelectionForTSF.StartOffset();
6469   }
6470   queryCaretRect.InitForQueryCaretRect(caretOffset, options);
6471 
6472   DispatchEvent(queryCaretRect);
6473   if (NS_WARN_IF(!queryCaretRect.mSucceeded)) {
6474     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6475             ("0x%p   TSFTextStore::CreateNativeCaret() FAILED due to "
6476              "eQueryCaretRect failure (offset=%d)",
6477              this, caretOffset));
6478     return;
6479   }
6480 
6481   if (!IMEHandler::CreateNativeCaret(static_cast<nsWindow*>(mWidget.get()),
6482                                      queryCaretRect.mReply.mRect)) {
6483     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6484             ("0x%p   TSFTextStore::CreateNativeCaret() FAILED due to "
6485              "IMEHandler::CreateNativeCaret() failure",
6486              this));
6487     return;
6488   }
6489 }
6490 
CommitCompositionInternal(bool aDiscard)6491 void TSFTextStore::CommitCompositionInternal(bool aDiscard) {
6492   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6493           ("0x%p   TSFTextStore::CommitCompositionInternal(aDiscard=%s), "
6494            "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
6495            "mComposition.mString=\"%s\"",
6496            this, GetBoolName(aDiscard), mSink.get(), mContext.get(),
6497            mComposition.mView.get(),
6498            GetEscapedUTF8String(mComposition.mString).get()));
6499 
6500   // If the document is locked, TSF will fail to commit composition since
6501   // TSF needs another document lock.  So, let's put off the request.
6502   // Note that TextComposition will commit composition in the focused editor
6503   // with the latest composition string for web apps and waits asynchronous
6504   // committing messages.  Therefore, we can and need to perform this
6505   // asynchronously.
6506   if (IsReadLocked()) {
6507     if (mDeferCommittingComposition || mDeferCancellingComposition) {
6508       MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6509               ("0x%p   TSFTextStore::CommitCompositionInternal(), "
6510                "does nothing because already called and waiting unlock...",
6511                this));
6512       return;
6513     }
6514     if (aDiscard) {
6515       mDeferCancellingComposition = true;
6516     } else {
6517       mDeferCommittingComposition = true;
6518     }
6519     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6520             ("0x%p   TSFTextStore::CommitCompositionInternal(), "
6521              "putting off to request to %s composition after unlocking the "
6522              "document",
6523              this, aDiscard ? "cancel" : "commit"));
6524     return;
6525   }
6526 
6527   if (mComposition.IsComposing() && aDiscard) {
6528     LONG endOffset = mComposition.EndOffset();
6529     mComposition.mString.Truncate(0);
6530     // Note that don't notify TSF of text change after this is destroyed.
6531     if (mSink && !mDestroyed) {
6532       TS_TEXTCHANGE textChange;
6533       textChange.acpStart = mComposition.mStart;
6534       textChange.acpOldEnd = endOffset;
6535       textChange.acpNewEnd = mComposition.mStart;
6536       MOZ_LOG(sTextStoreLog, LogLevel::Info,
6537               ("0x%p   TSFTextStore::CommitCompositionInternal(), calling"
6538                "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
6539                "acpNewEnd=%ld })...",
6540                this, textChange.acpStart, textChange.acpOldEnd,
6541                textChange.acpNewEnd));
6542       RefPtr<ITextStoreACPSink> sink = mSink;
6543       sink->OnTextChange(0, &textChange);
6544     }
6545   }
6546   // Terminate two contexts, the base context (mContext) and the top
6547   // if the top context is not the same as the base context
6548   RefPtr<ITfContext> context = mContext;
6549   do {
6550     if (context) {
6551       RefPtr<ITfContextOwnerCompositionServices> services;
6552       context->QueryInterface(IID_ITfContextOwnerCompositionServices,
6553                               getter_AddRefs(services));
6554       if (services) {
6555         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6556                 ("0x%p   TSFTextStore::CommitCompositionInternal(), "
6557                  "requesting TerminateComposition() for the context 0x%p...",
6558                  this, context.get()));
6559         services->TerminateComposition(nullptr);
6560       }
6561     }
6562     if (context != mContext) break;
6563     if (mDocumentMgr) mDocumentMgr->GetTop(getter_AddRefs(context));
6564   } while (context != mContext);
6565 }
6566 
GetCompartment(IUnknown * pUnk,const GUID & aID,ITfCompartment ** aCompartment)6567 static bool GetCompartment(IUnknown* pUnk, const GUID& aID,
6568                            ITfCompartment** aCompartment) {
6569   if (!pUnk) return false;
6570 
6571   RefPtr<ITfCompartmentMgr> compMgr;
6572   pUnk->QueryInterface(IID_ITfCompartmentMgr, getter_AddRefs(compMgr));
6573   if (!compMgr) return false;
6574 
6575   return SUCCEEDED(compMgr->GetCompartment(aID, aCompartment)) &&
6576          (*aCompartment) != nullptr;
6577 }
6578 
6579 // static
SetIMEOpenState(bool aState)6580 void TSFTextStore::SetIMEOpenState(bool aState) {
6581   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6582           ("TSFTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState)));
6583 
6584   if (!sThreadMgr) {
6585     return;
6586   }
6587 
6588   RefPtr<ITfCompartment> comp = GetCompartmentForOpenClose();
6589   if (NS_WARN_IF(!comp)) {
6590     MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6591             ("  TSFTextStore::SetIMEOpenState() FAILED due to"
6592              "no compartment available"));
6593     return;
6594   }
6595 
6596   VARIANT variant;
6597   variant.vt = VT_I4;
6598   variant.lVal = aState;
6599   HRESULT hr = comp->SetValue(sClientId, &variant);
6600   if (NS_WARN_IF(FAILED(hr))) {
6601     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6602             ("  TSFTextStore::SetIMEOpenState() FAILED due to "
6603              "ITfCompartment::SetValue() failure, hr=0x%08X",
6604              hr));
6605     return;
6606   }
6607   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6608           ("  TSFTextStore::SetIMEOpenState(), setting "
6609            "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...",
6610            variant.lVal));
6611 }
6612 
6613 // static
GetIMEOpenState()6614 bool TSFTextStore::GetIMEOpenState() {
6615   if (!sThreadMgr) {
6616     return false;
6617   }
6618 
6619   RefPtr<ITfCompartment> comp = GetCompartmentForOpenClose();
6620   if (NS_WARN_IF(!comp)) {
6621     return false;
6622   }
6623 
6624   VARIANT variant;
6625   ::VariantInit(&variant);
6626   HRESULT hr = comp->GetValue(&variant);
6627   if (NS_WARN_IF(FAILED(hr))) {
6628     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6629             ("TSFTextStore::GetIMEOpenState() FAILED due to "
6630              "ITfCompartment::GetValue() failure, hr=0x%08X",
6631              hr));
6632     return false;
6633   }
6634   // Until IME is open in this process, the result may be empty.
6635   if (variant.vt == VT_EMPTY) {
6636     return false;
6637   }
6638   if (NS_WARN_IF(variant.vt != VT_I4)) {
6639     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6640             ("TSFTextStore::GetIMEOpenState() FAILED due to "
6641              "invalid result of ITfCompartment::GetValue()"));
6642     ::VariantClear(&variant);
6643     return false;
6644   }
6645 
6646   return variant.lVal != 0;
6647 }
6648 
6649 // static
SetInputContext(nsWindowBase * aWidget,const InputContext & aContext,const InputContextAction & aAction)6650 void TSFTextStore::SetInputContext(nsWindowBase* aWidget,
6651                                    const InputContext& aContext,
6652                                    const InputContextAction& aAction) {
6653   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6654           ("TSFTextStore::SetInputContext(aWidget=%p, "
6655            "aContext=%s, aAction.mFocusChange=%s), "
6656            "sEnabledTextStore(0x%p)={ mWidget=0x%p }, ThinksHavingFocus()=%s",
6657            aWidget, GetInputContextString(aContext).get(),
6658            GetFocusChangeName(aAction.mFocusChange), sEnabledTextStore.get(),
6659            sEnabledTextStore ? sEnabledTextStore->mWidget.get() : nullptr,
6660            GetBoolName(ThinksHavingFocus())));
6661 
6662   // When this is called when the widget is created, there is nothing to do.
6663   if (aAction.mFocusChange == InputContextAction::WIDGET_CREATED) {
6664     return;
6665   }
6666 
6667   NS_ENSURE_TRUE_VOID(IsInTSFMode());
6668 
6669   if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) {
6670     if (sEnabledTextStore) {
6671       RefPtr<TSFTextStore> textStore(sEnabledTextStore);
6672       textStore->SetInputScope(aContext.mHTMLInputType,
6673                                aContext.mHTMLInputInputmode,
6674                                aContext.mInPrivateBrowsing);
6675     }
6676     return;
6677   }
6678 
6679   // If focus isn't actually changed but the enabled state is changed,
6680   // emulate the focus move.
6681   if (!ThinksHavingFocus() && aContext.mIMEState.IsEditable()) {
6682     OnFocusChange(true, aWidget, aContext);
6683   } else if (ThinksHavingFocus() && !aContext.mIMEState.IsEditable()) {
6684     OnFocusChange(false, aWidget, aContext);
6685   }
6686 }
6687 
6688 // static
MarkContextAsKeyboardDisabled(ITfContext * aContext)6689 void TSFTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext) {
6690   VARIANT variant_int4_value1;
6691   variant_int4_value1.vt = VT_I4;
6692   variant_int4_value1.lVal = 1;
6693 
6694   RefPtr<ITfCompartment> comp;
6695   if (!GetCompartment(aContext, GUID_COMPARTMENT_KEYBOARD_DISABLED,
6696                       getter_AddRefs(comp))) {
6697     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6698             ("TSFTextStore::MarkContextAsKeyboardDisabled() failed"
6699              "aContext=0x%p...",
6700              aContext));
6701     return;
6702   }
6703 
6704   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6705           ("TSFTextStore::MarkContextAsKeyboardDisabled(), setting "
6706            "to disable context 0x%p...",
6707            aContext));
6708   comp->SetValue(sClientId, &variant_int4_value1);
6709 }
6710 
6711 // static
MarkContextAsEmpty(ITfContext * aContext)6712 void TSFTextStore::MarkContextAsEmpty(ITfContext* aContext) {
6713   VARIANT variant_int4_value1;
6714   variant_int4_value1.vt = VT_I4;
6715   variant_int4_value1.lVal = 1;
6716 
6717   RefPtr<ITfCompartment> comp;
6718   if (!GetCompartment(aContext, GUID_COMPARTMENT_EMPTYCONTEXT,
6719                       getter_AddRefs(comp))) {
6720     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6721             ("TSFTextStore::MarkContextAsEmpty() failed"
6722              "aContext=0x%p...",
6723              aContext));
6724     return;
6725   }
6726 
6727   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
6728           ("TSFTextStore::MarkContextAsEmpty(), setting "
6729            "to mark empty context 0x%p...",
6730            aContext));
6731   comp->SetValue(sClientId, &variant_int4_value1);
6732 }
6733 
6734 // static
Initialize()6735 void TSFTextStore::Initialize() {
6736   MOZ_LOG(sTextStoreLog, LogLevel::Info,
6737           ("TSFTextStore::Initialize() is called..."));
6738 
6739   if (sThreadMgr) {
6740     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6741             ("  TSFTextStore::Initialize() FAILED due to already initialized"));
6742     return;
6743   }
6744 
6745   bool enableTsf = Preferences::GetBool(kPrefNameEnableTSF, false);
6746   MOZ_LOG(sTextStoreLog, LogLevel::Info,
6747           ("  TSFTextStore::Initialize(), TSF is %s",
6748            enableTsf ? "enabled" : "disabled"));
6749   if (!enableTsf) {
6750     return;
6751   }
6752 
6753   RefPtr<ITfThreadMgr> threadMgr;
6754   HRESULT hr =
6755       ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER,
6756                          IID_ITfThreadMgr, getter_AddRefs(threadMgr));
6757   if (FAILED(hr) || !threadMgr) {
6758     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6759             ("  TSFTextStore::Initialize() FAILED to "
6760              "create the thread manager, hr=0x%08X",
6761              hr));
6762     return;
6763   }
6764 
6765   hr = threadMgr->Activate(&sClientId);
6766   if (FAILED(hr)) {
6767     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6768             ("  TSFTextStore::Initialize() FAILED to activate, hr=0x%08X", hr));
6769     return;
6770   }
6771 
6772   RefPtr<ITfDocumentMgr> disabledDocumentMgr;
6773   hr = threadMgr->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr));
6774   if (FAILED(hr) || !disabledDocumentMgr) {
6775     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6776             ("  TSFTextStore::Initialize() FAILED to create "
6777              "a document manager for disabled mode, hr=0x%08X",
6778              hr));
6779     return;
6780   }
6781 
6782   RefPtr<ITfContext> disabledContext;
6783   DWORD editCookie = 0;
6784   hr = disabledDocumentMgr->CreateContext(
6785       sClientId, 0, nullptr, getter_AddRefs(disabledContext), &editCookie);
6786   if (FAILED(hr) || !disabledContext) {
6787     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6788             ("  TSFTextStore::Initialize() FAILED to create "
6789              "a context for disabled mode, hr=0x%08X",
6790              hr));
6791     return;
6792   }
6793 
6794   MarkContextAsKeyboardDisabled(disabledContext);
6795   MarkContextAsEmpty(disabledContext);
6796 
6797   sThreadMgr = threadMgr;
6798   sDisabledDocumentMgr = disabledDocumentMgr;
6799   sDisabledContext = disabledContext;
6800 
6801   MOZ_LOG(sTextStoreLog, LogLevel::Info,
6802           ("  TSFTextStore::Initialize(), sThreadMgr=0x%p, "
6803            "sClientId=0x%08X, sDisabledDocumentMgr=0x%p, sDisabledContext=%p",
6804            sThreadMgr.get(), sClientId, sDisabledDocumentMgr.get(),
6805            sDisabledContext.get()));
6806 }
6807 
6808 // static
GetThreadMgr()6809 already_AddRefed<ITfThreadMgr> TSFTextStore::GetThreadMgr() {
6810   RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
6811   return threadMgr.forget();
6812 }
6813 
6814 // static
GetMessagePump()6815 already_AddRefed<ITfMessagePump> TSFTextStore::GetMessagePump() {
6816   static bool sInitialized = false;
6817   if (!sThreadMgr) {
6818     return nullptr;
6819   }
6820   if (sMessagePump) {
6821     RefPtr<ITfMessagePump> messagePump = sMessagePump;
6822     return messagePump.forget();
6823   }
6824   // If it tried to retrieve ITfMessagePump from sThreadMgr but it failed,
6825   // we shouldn't retry it at every message due to performance reason.
6826   // Although this shouldn't occur actually.
6827   if (sInitialized) {
6828     return nullptr;
6829   }
6830   sInitialized = true;
6831 
6832   RefPtr<ITfMessagePump> messagePump;
6833   HRESULT hr = sThreadMgr->QueryInterface(IID_ITfMessagePump,
6834                                           getter_AddRefs(messagePump));
6835   if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!messagePump)) {
6836     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6837             ("TSFTextStore::GetMessagePump() FAILED to "
6838              "QI message pump from the thread manager, hr=0x%08X",
6839              hr));
6840     return nullptr;
6841   }
6842   sMessagePump = messagePump;
6843   return messagePump.forget();
6844 }
6845 
6846 // static
6847 already_AddRefed<ITfDisplayAttributeMgr>
GetDisplayAttributeMgr()6848 TSFTextStore::GetDisplayAttributeMgr() {
6849   RefPtr<ITfDisplayAttributeMgr> displayAttributeMgr;
6850   if (sDisplayAttrMgr) {
6851     displayAttributeMgr = sDisplayAttrMgr;
6852     return displayAttributeMgr.forget();
6853   }
6854 
6855   HRESULT hr = ::CoCreateInstance(
6856       CLSID_TF_DisplayAttributeMgr, nullptr, CLSCTX_INPROC_SERVER,
6857       IID_ITfDisplayAttributeMgr, getter_AddRefs(displayAttributeMgr));
6858   if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!displayAttributeMgr)) {
6859     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6860             ("TSFTextStore::GetDisplayAttributeMgr() FAILED to create "
6861              "a display attribute manager instance, hr=0x%08X",
6862              hr));
6863     return nullptr;
6864   }
6865   sDisplayAttrMgr = displayAttributeMgr;
6866   return displayAttributeMgr.forget();
6867 }
6868 
6869 // static
GetCategoryMgr()6870 already_AddRefed<ITfCategoryMgr> TSFTextStore::GetCategoryMgr() {
6871   RefPtr<ITfCategoryMgr> categoryMgr;
6872   if (sCategoryMgr) {
6873     categoryMgr = sCategoryMgr;
6874     return categoryMgr.forget();
6875   }
6876   HRESULT hr =
6877       ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_INPROC_SERVER,
6878                          IID_ITfCategoryMgr, getter_AddRefs(categoryMgr));
6879   if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!categoryMgr)) {
6880     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6881             ("TSFTextStore::GetCategoryMgr() FAILED to create "
6882              "a category manager instance, hr=0x%08X",
6883              hr));
6884     return nullptr;
6885   }
6886   sCategoryMgr = categoryMgr;
6887   return categoryMgr.forget();
6888 }
6889 
6890 // static
GetCompartmentForOpenClose()6891 already_AddRefed<ITfCompartment> TSFTextStore::GetCompartmentForOpenClose() {
6892   if (sCompartmentForOpenClose) {
6893     RefPtr<ITfCompartment> compartment = sCompartmentForOpenClose;
6894     return compartment.forget();
6895   }
6896 
6897   if (!sThreadMgr) {
6898     return nullptr;
6899   }
6900 
6901   RefPtr<ITfCompartmentMgr> compartmentMgr;
6902   HRESULT hr = sThreadMgr->QueryInterface(IID_ITfCompartmentMgr,
6903                                           getter_AddRefs(compartmentMgr));
6904   if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!compartmentMgr)) {
6905     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6906             ("TSFTextStore::GetCompartmentForOpenClose() FAILED due to"
6907              "sThreadMgr not having ITfCompartmentMgr, hr=0x%08X",
6908              hr));
6909     return nullptr;
6910   }
6911 
6912   RefPtr<ITfCompartment> compartment;
6913   hr = compartmentMgr->GetCompartment(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
6914                                       getter_AddRefs(compartment));
6915   if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!compartment)) {
6916     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6917             ("TSFTextStore::GetCompartmentForOpenClose() FAILED due to"
6918              "ITfCompartmentMgr::GetCompartment() failuere, hr=0x%08X",
6919              hr));
6920     return nullptr;
6921   }
6922 
6923   sCompartmentForOpenClose = compartment;
6924   return compartment.forget();
6925 }
6926 
6927 // static
6928 already_AddRefed<ITfInputProcessorProfiles>
GetInputProcessorProfiles()6929 TSFTextStore::GetInputProcessorProfiles() {
6930   RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles;
6931   if (sInputProcessorProfiles) {
6932     inputProcessorProfiles = sInputProcessorProfiles;
6933     return inputProcessorProfiles.forget();
6934   }
6935   // XXX MSDN documents that ITfInputProcessorProfiles is available only on
6936   //     desktop apps.  However, there is no known way to obtain
6937   //     ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
6938   //     instance.
6939   HRESULT hr = ::CoCreateInstance(
6940       CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_INPROC_SERVER,
6941       IID_ITfInputProcessorProfiles, getter_AddRefs(inputProcessorProfiles));
6942   if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!inputProcessorProfiles)) {
6943     MOZ_LOG(sTextStoreLog, LogLevel::Error,
6944             ("TSFTextStore::GetInputProcessorProfiles() FAILED to create input "
6945              "processor profiles, hr=0x%08X",
6946              hr));
6947     return nullptr;
6948   }
6949   sInputProcessorProfiles = inputProcessorProfiles;
6950   return inputProcessorProfiles.forget();
6951 }
6952 
6953 // static
Terminate()6954 void TSFTextStore::Terminate() {
6955   MOZ_LOG(sTextStoreLog, LogLevel::Info, ("TSFTextStore::Terminate()"));
6956 
6957   TSFStaticSink::Shutdown();
6958 
6959   sDisplayAttrMgr = nullptr;
6960   sCategoryMgr = nullptr;
6961   sEnabledTextStore = nullptr;
6962   sDisabledDocumentMgr = nullptr;
6963   sDisabledContext = nullptr;
6964   sCompartmentForOpenClose = nullptr;
6965   sInputProcessorProfiles = nullptr;
6966   sClientId = 0;
6967   if (sThreadMgr) {
6968     sThreadMgr->Deactivate();
6969     sThreadMgr = nullptr;
6970     sMessagePump = nullptr;
6971     sKeystrokeMgr = nullptr;
6972   }
6973 }
6974 
6975 // static
ProcessRawKeyMessage(const MSG & aMsg)6976 bool TSFTextStore::ProcessRawKeyMessage(const MSG& aMsg) {
6977   if (!sThreadMgr) {
6978     return false;  // not in TSF mode
6979   }
6980   static bool sInitialized = false;
6981   if (!sKeystrokeMgr) {
6982     // If it tried to retrieve ITfKeystrokeMgr from sThreadMgr but it failed,
6983     // we shouldn't retry it at every keydown nor keyup due to performance
6984     // reason.  Although this shouldn't occur actually.
6985     if (sInitialized) {
6986       return false;
6987     }
6988     sInitialized = true;
6989     RefPtr<ITfKeystrokeMgr> keystrokeMgr;
6990     HRESULT hr = sThreadMgr->QueryInterface(IID_ITfKeystrokeMgr,
6991                                             getter_AddRefs(keystrokeMgr));
6992     if (NS_WARN_IF(FAILED(hr)) || NS_WARN_IF(!keystrokeMgr)) {
6993       MOZ_LOG(sTextStoreLog, LogLevel::Error,
6994               ("TSFTextStore::ProcessRawKeyMessage() FAILED to "
6995                "QI keystroke manager from the thread manager, hr=0x%08X",
6996                hr));
6997       return false;
6998     }
6999     sKeystrokeMgr = keystrokeMgr.forget();
7000   }
7001 
7002   if (aMsg.message == WM_KEYDOWN) {
7003     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
7004     if (textStore) {
7005       textStore->OnStartToHandleKeyMessage();
7006       if (NS_WARN_IF(textStore != sEnabledTextStore)) {
7007         // Let's handle the key message with new focused TSFTextStore.
7008         textStore = sEnabledTextStore;
7009       }
7010     }
7011     AutoRestore<const MSG*> savePreviousKeyMsg(sHandlingKeyMsg);
7012     AutoRestore<bool> saveKeyEventDispatched(sIsKeyboardEventDispatched);
7013     sHandlingKeyMsg = &aMsg;
7014     sIsKeyboardEventDispatched = false;
7015     BOOL eaten;
7016     RefPtr<ITfKeystrokeMgr> keystrokeMgr = sKeystrokeMgr;
7017     HRESULT hr = keystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
7018     if (FAILED(hr) || !sKeystrokeMgr || !eaten) {
7019       return false;
7020     }
7021     hr = keystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
7022     if (textStore) {
7023       textStore->OnEndHandlingKeyMessage(!!eaten);
7024     }
7025     return SUCCEEDED(hr) &&
7026            (eaten || !sKeystrokeMgr || sIsKeyboardEventDispatched);
7027   }
7028   if (aMsg.message == WM_KEYUP) {
7029     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
7030     if (textStore) {
7031       textStore->OnStartToHandleKeyMessage();
7032       if (NS_WARN_IF(textStore != sEnabledTextStore)) {
7033         // Let's handle the key message with new focused TSFTextStore.
7034         textStore = sEnabledTextStore;
7035       }
7036     }
7037     AutoRestore<const MSG*> savePreviousKeyMsg(sHandlingKeyMsg);
7038     AutoRestore<bool> saveKeyEventDispatched(sIsKeyboardEventDispatched);
7039     sHandlingKeyMsg = &aMsg;
7040     sIsKeyboardEventDispatched = false;
7041     BOOL eaten;
7042     RefPtr<ITfKeystrokeMgr> keystrokeMgr = sKeystrokeMgr;
7043     HRESULT hr = keystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
7044     if (FAILED(hr) || !sKeystrokeMgr || !eaten) {
7045       return false;
7046     }
7047     hr = keystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
7048     if (textStore) {
7049       textStore->OnEndHandlingKeyMessage(!!eaten);
7050     }
7051     return SUCCEEDED(hr) &&
7052            (eaten || !sKeystrokeMgr || sIsKeyboardEventDispatched);
7053   }
7054   return false;
7055 }
7056 
7057 // static
ProcessMessage(nsWindowBase * aWindow,UINT aMessage,WPARAM & aWParam,LPARAM & aLParam,MSGResult & aResult)7058 void TSFTextStore::ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
7059                                   WPARAM& aWParam, LPARAM& aLParam,
7060                                   MSGResult& aResult) {
7061   switch (aMessage) {
7062     case WM_IME_SETCONTEXT:
7063       // If a windowless plugin had focus and IME was handled on it, composition
7064       // window was set the position.  After that, even in TSF mode, WinXP keeps
7065       // to use composition window at the position if the active IME is not
7066       // aware TSF.  For avoiding this issue, we need to hide the composition
7067       // window here.
7068       if (aWParam) {
7069         aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
7070       }
7071       break;
7072     case WM_ENTERIDLE:
7073       // When an modal dialog such as a file picker is open, composition
7074       // should be committed because IME might be used on it.
7075       if (!IsComposingOn(aWindow)) {
7076         break;
7077       }
7078       CommitComposition(false);
7079       break;
7080     case MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE: {
7081       TSFTextStore* maybeTextStore = reinterpret_cast<TSFTextStore*>(aWParam);
7082       if (maybeTextStore == sEnabledTextStore) {
7083         RefPtr<TSFTextStore> textStore(maybeTextStore);
7084         textStore->NotifyTSFOfLayoutChangeAgain();
7085       }
7086       break;
7087     }
7088   }
7089 }
7090 
7091 // static
IsIMM_IMEActive()7092 bool TSFTextStore::IsIMM_IMEActive() {
7093   return TSFStaticSink::IsIMM_IMEActive();
7094 }
7095 
7096 // static
IsMSJapaneseIMEActive()7097 bool TSFTextStore::IsMSJapaneseIMEActive() {
7098   return TSFStaticSink::IsMSJapaneseIMEActive();
7099 }
7100 
7101 // static
IsGoogleJapaneseInputActive()7102 bool TSFTextStore::IsGoogleJapaneseInputActive() {
7103   return TSFStaticSink::IsGoogleJapaneseInputActive();
7104 }
7105 
7106 /******************************************************************/
7107 /* TSFTextStore::Composition                                       */
7108 /******************************************************************/
7109 
Start(ITfCompositionView * aCompositionView,LONG aCompositionStartOffset,const nsAString & aCompositionString)7110 void TSFTextStore::Composition::Start(ITfCompositionView* aCompositionView,
7111                                       LONG aCompositionStartOffset,
7112                                       const nsAString& aCompositionString) {
7113   mView = aCompositionView;
7114   mString = aCompositionString;
7115   mStart = aCompositionStartOffset;
7116 }
7117 
End()7118 void TSFTextStore::Composition::End() {
7119   mView = nullptr;
7120   mString.Truncate();
7121 }
7122 
7123 /******************************************************************************
7124  *  TSFTextStore::Content
7125  *****************************************************************************/
7126 
GetSelectedText() const7127 const nsDependentSubstring TSFTextStore::Content::GetSelectedText() const {
7128   MOZ_ASSERT(mInitialized);
7129   return GetSubstring(static_cast<uint32_t>(mSelection.StartOffset()),
7130                       static_cast<uint32_t>(mSelection.Length()));
7131 }
7132 
GetSubstring(uint32_t aStart,uint32_t aLength) const7133 const nsDependentSubstring TSFTextStore::Content::GetSubstring(
7134     uint32_t aStart, uint32_t aLength) const {
7135   MOZ_ASSERT(mInitialized);
7136   return nsDependentSubstring(mText, aStart, aLength);
7137 }
7138 
ReplaceSelectedTextWith(const nsAString & aString)7139 void TSFTextStore::Content::ReplaceSelectedTextWith(const nsAString& aString) {
7140   MOZ_ASSERT(mInitialized);
7141   ReplaceTextWith(mSelection.StartOffset(), mSelection.Length(), aString);
7142 }
7143 
FirstDifferentCharOffset(const nsAString & aStr1,const nsAString & aStr2)7144 inline uint32_t FirstDifferentCharOffset(const nsAString& aStr1,
7145                                          const nsAString& aStr2) {
7146   MOZ_ASSERT(aStr1 != aStr2);
7147   uint32_t i = 0;
7148   uint32_t minLength = std::min(aStr1.Length(), aStr2.Length());
7149   for (; i < minLength && aStr1[i] == aStr2[i]; i++) {
7150     /* nothing to do */
7151   }
7152   return i;
7153 }
7154 
ReplaceTextWith(LONG aStart,LONG aLength,const nsAString & aReplaceString)7155 void TSFTextStore::Content::ReplaceTextWith(LONG aStart, LONG aLength,
7156                                             const nsAString& aReplaceString) {
7157   MOZ_ASSERT(mInitialized);
7158   const nsDependentSubstring replacedString = GetSubstring(
7159       static_cast<uint32_t>(aStart), static_cast<uint32_t>(aLength));
7160   if (aReplaceString != replacedString) {
7161     uint32_t firstDifferentOffset = mMinTextModifiedOffset;
7162     if (mComposition.IsComposing()) {
7163       // Emulate text insertion during compositions, because during a
7164       // composition, editor expects the whole composition string to
7165       // be sent in eCompositionChange, not just the inserted part.
7166       // The actual eCompositionChange will be sent in SetSelection
7167       // or OnUpdateComposition.
7168       MOZ_ASSERT(aStart >= mComposition.mStart);
7169       MOZ_ASSERT(aStart + aLength <= mComposition.EndOffset());
7170       mComposition.mString.Replace(
7171           static_cast<uint32_t>(aStart - mComposition.mStart),
7172           static_cast<uint32_t>(aLength), aReplaceString);
7173       // TIP may set composition string twice or more times during a document
7174       // lock.  Therefore, we should compute the first difference offset with
7175       // mLastCompositionString.
7176       if (mComposition.mString != mLastCompositionString) {
7177         firstDifferentOffset = mComposition.mStart +
7178                                FirstDifferentCharOffset(mComposition.mString,
7179                                                         mLastCompositionString);
7180         // The previous change to the composition string is canceled.
7181         if (mMinTextModifiedOffset >=
7182                 static_cast<uint32_t>(mComposition.mStart) &&
7183             mMinTextModifiedOffset < firstDifferentOffset) {
7184           mMinTextModifiedOffset = firstDifferentOffset;
7185         }
7186       } else if (mMinTextModifiedOffset >=
7187                      static_cast<uint32_t>(mComposition.mStart) &&
7188                  mMinTextModifiedOffset <
7189                      static_cast<uint32_t>(mComposition.EndOffset())) {
7190         // The previous change to the composition string is canceled.
7191         mMinTextModifiedOffset = firstDifferentOffset =
7192             mComposition.EndOffset();
7193       }
7194       mLatestCompositionEndOffset = mComposition.EndOffset();
7195       MOZ_LOG(sTextStoreLog, LogLevel::Debug,
7196               ("0x%p   TSFTextStore::Content::ReplaceTextWith(aStart=%d, "
7197                "aLength=%d, aReplaceString=\"%s\"), mComposition={ mStart=%d, "
7198                "mString=\"%s\" }, mLastCompositionString=\"%s\", "
7199                "mMinTextModifiedOffset=%u, firstDifferentOffset=%u",
7200                this, aStart, aLength,
7201                GetEscapedUTF8String(aReplaceString).get(), mComposition.mStart,
7202                GetEscapedUTF8String(mComposition.mString).get(),
7203                GetEscapedUTF8String(mLastCompositionString).get(),
7204                mMinTextModifiedOffset, firstDifferentOffset));
7205     } else {
7206       firstDifferentOffset =
7207           static_cast<uint32_t>(aStart) +
7208           FirstDifferentCharOffset(aReplaceString, replacedString);
7209     }
7210     mMinTextModifiedOffset =
7211         std::min(mMinTextModifiedOffset, firstDifferentOffset);
7212     mText.Replace(static_cast<uint32_t>(aStart), static_cast<uint32_t>(aLength),
7213                   aReplaceString);
7214   }
7215   // Selection should be collapsed at the end of the inserted string.
7216   mSelection.CollapseAt(static_cast<uint32_t>(aStart) +
7217                         aReplaceString.Length());
7218 }
7219 
StartComposition(ITfCompositionView * aCompositionView,const PendingAction & aCompStart,bool aPreserveSelection)7220 void TSFTextStore::Content::StartComposition(
7221     ITfCompositionView* aCompositionView, const PendingAction& aCompStart,
7222     bool aPreserveSelection) {
7223   MOZ_ASSERT(mInitialized);
7224   MOZ_ASSERT(aCompositionView);
7225   MOZ_ASSERT(!mComposition.mView);
7226   MOZ_ASSERT(aCompStart.mType == PendingAction::Type::eCompositionStart);
7227 
7228   mComposition.Start(
7229       aCompositionView, aCompStart.mSelectionStart,
7230       GetSubstring(static_cast<uint32_t>(aCompStart.mSelectionStart),
7231                    static_cast<uint32_t>(aCompStart.mSelectionLength)));
7232   mLatestCompositionStartOffset = mComposition.mStart;
7233   mLatestCompositionEndOffset = mComposition.EndOffset();
7234   if (!aPreserveSelection) {
7235     // XXX Do we need to set a new writing-mode here when setting a new
7236     // selection? Currently, we just preserve the existing value.
7237     WritingMode writingMode =
7238         mSelection.IsDirty() ? WritingMode() : mSelection.GetWritingMode();
7239     mSelection.SetSelection(mComposition.mStart, mComposition.mString.Length(),
7240                             false, writingMode);
7241   }
7242 }
7243 
RestoreCommittedComposition(ITfCompositionView * aCompositionView,const PendingAction & aCanceledCompositionEnd)7244 void TSFTextStore::Content::RestoreCommittedComposition(
7245     ITfCompositionView* aCompositionView,
7246     const PendingAction& aCanceledCompositionEnd) {
7247   MOZ_ASSERT(mInitialized);
7248   MOZ_ASSERT(aCompositionView);
7249   MOZ_ASSERT(!mComposition.mView);
7250   MOZ_ASSERT(aCanceledCompositionEnd.mType ==
7251              PendingAction::Type::eCompositionEnd);
7252   MOZ_ASSERT(
7253       GetSubstring(
7254           static_cast<uint32_t>(aCanceledCompositionEnd.mSelectionStart),
7255           static_cast<uint32_t>(aCanceledCompositionEnd.mData.Length())) ==
7256       aCanceledCompositionEnd.mData);
7257 
7258   // Restore the committed string as composing string.
7259   mComposition.Start(aCompositionView, aCanceledCompositionEnd.mSelectionStart,
7260                      aCanceledCompositionEnd.mData);
7261   mLatestCompositionStartOffset = mComposition.mStart;
7262   mLatestCompositionEndOffset = mComposition.EndOffset();
7263 }
7264 
EndComposition(const PendingAction & aCompEnd)7265 void TSFTextStore::Content::EndComposition(const PendingAction& aCompEnd) {
7266   MOZ_ASSERT(mInitialized);
7267   MOZ_ASSERT(mComposition.mView);
7268   MOZ_ASSERT(aCompEnd.mType == PendingAction::Type::eCompositionEnd);
7269 
7270   mSelection.CollapseAt(mComposition.mStart + aCompEnd.mData.Length());
7271   mComposition.End();
7272 }
7273 
7274 /******************************************************************************
7275  *  TSFTextStore::MouseTracker
7276  *****************************************************************************/
7277 
MouseTracker()7278 TSFTextStore::MouseTracker::MouseTracker()
7279     : mStart(-1), mLength(-1), mCookie(kInvalidCookie) {}
7280 
7281 HRESULT
Init(TSFTextStore * aTextStore)7282 TSFTextStore::MouseTracker::Init(TSFTextStore* aTextStore) {
7283   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
7284           ("0x%p   TSFTextStore::MouseTracker::Init(aTextStore=0x%p), "
7285            "aTextStore->mMouseTrackers.Length()=%d",
7286            this, aTextStore->mMouseTrackers.Length()));
7287 
7288   if (&aTextStore->mMouseTrackers.LastElement() != this) {
7289     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7290             ("0x%p   TSFTextStore::MouseTracker::Init() FAILED due to "
7291              "this is not the last element of mMouseTrackers",
7292              this));
7293     return E_FAIL;
7294   }
7295   if (aTextStore->mMouseTrackers.Length() > kInvalidCookie) {
7296     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7297             ("0x%p   TSFTextStore::MouseTracker::Init() FAILED due to "
7298              "no new cookie available",
7299              this));
7300     return E_FAIL;
7301   }
7302   MOZ_ASSERT(!aTextStore->mMouseTrackers.IsEmpty(),
7303              "This instance must be in TSFTextStore::mMouseTrackers");
7304   mCookie = static_cast<DWORD>(aTextStore->mMouseTrackers.Length() - 1);
7305   return S_OK;
7306 }
7307 
7308 HRESULT
AdviseSink(TSFTextStore * aTextStore,ITfRangeACP * aTextRange,ITfMouseSink * aMouseSink)7309 TSFTextStore::MouseTracker::AdviseSink(TSFTextStore* aTextStore,
7310                                        ITfRangeACP* aTextRange,
7311                                        ITfMouseSink* aMouseSink) {
7312   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
7313           ("0x%p   TSFTextStore::MouseTracker::AdviseSink(aTextStore=0x%p, "
7314            "aTextRange=0x%p, aMouseSink=0x%p), mCookie=%d, mSink=0x%p",
7315            this, aTextStore, aTextRange, aMouseSink, mCookie, mSink.get()));
7316   MOZ_ASSERT(mCookie != kInvalidCookie, "This hasn't been initalized?");
7317 
7318   if (mSink) {
7319     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7320             ("0x%p   TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7321              "due to already being used",
7322              this));
7323     return E_FAIL;
7324   }
7325 
7326   HRESULT hr = aTextRange->GetExtent(&mStart, &mLength);
7327   if (FAILED(hr)) {
7328     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7329             ("0x%p   TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7330              "due to failure of ITfRangeACP::GetExtent()",
7331              this));
7332     return hr;
7333   }
7334 
7335   if (mStart < 0 || mLength <= 0) {
7336     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7337             ("0x%p   TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7338              "due to odd result of ITfRangeACP::GetExtent(), "
7339              "mStart=%d, mLength=%d",
7340              this, mStart, mLength));
7341     return E_INVALIDARG;
7342   }
7343 
7344   nsAutoString textContent;
7345   if (NS_WARN_IF(!aTextStore->GetCurrentText(textContent))) {
7346     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7347             ("0x%p   TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7348              "due to failure of TSFTextStore::GetCurrentText()",
7349              this));
7350     return E_FAIL;
7351   }
7352 
7353   if (textContent.Length() <= static_cast<uint32_t>(mStart) ||
7354       textContent.Length() < static_cast<uint32_t>(mStart + mLength)) {
7355     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7356             ("0x%p   TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7357              "due to out of range, mStart=%d, mLength=%d, "
7358              "textContent.Length()=%d",
7359              this, mStart, mLength, textContent.Length()));
7360     return E_INVALIDARG;
7361   }
7362 
7363   mSink = aMouseSink;
7364 
7365   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
7366           ("0x%p   TSFTextStore::MouseTracker::AdviseMouseSink(), "
7367            "succeeded, mStart=%d, mLength=%d, textContent.Length()=%d",
7368            this, mStart, mLength, textContent.Length()));
7369   return S_OK;
7370 }
7371 
UnadviseSink()7372 void TSFTextStore::MouseTracker::UnadviseSink() {
7373   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
7374           ("0x%p   TSFTextStore::MouseTracker::UnadviseSink(), "
7375            "mCookie=%d, mSink=0x%p, mStart=%d, mLength=%d",
7376            this, mCookie, mSink.get(), mStart, mLength));
7377   mSink = nullptr;
7378   mStart = mLength = -1;
7379 }
7380 
OnMouseButtonEvent(ULONG aEdge,ULONG aQuadrant,DWORD aButtonStatus)7381 bool TSFTextStore::MouseTracker::OnMouseButtonEvent(ULONG aEdge,
7382                                                     ULONG aQuadrant,
7383                                                     DWORD aButtonStatus) {
7384   MOZ_ASSERT(IsUsing(), "The caller must check before calling OnMouseEvent()");
7385 
7386   BOOL eaten = FALSE;
7387   RefPtr<ITfMouseSink> sink = mSink;
7388   HRESULT hr = sink->OnMouseEvent(aEdge, aQuadrant, aButtonStatus, &eaten);
7389 
7390   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
7391           ("0x%p   TSFTextStore::MouseTracker::OnMouseEvent(aEdge=%d, "
7392            "aQuadrant=%d, aButtonStatus=0x%08X), hr=0x%08X, eaten=%s",
7393            this, aEdge, aQuadrant, aButtonStatus, hr, GetBoolName(!!eaten)));
7394 
7395   return SUCCEEDED(hr) && eaten;
7396 }
7397 
7398 #ifdef DEBUG
7399 // static
CurrentKeyboardLayoutHasIME()7400 bool TSFTextStore::CurrentKeyboardLayoutHasIME() {
7401   RefPtr<ITfInputProcessorProfiles> inputProcessorProfiles =
7402       TSFTextStore::GetInputProcessorProfiles();
7403   if (!inputProcessorProfiles) {
7404     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7405             ("TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED due to "
7406              "there is no input processor profiles instance"));
7407     return false;
7408   }
7409   RefPtr<ITfInputProcessorProfileMgr> profileMgr;
7410   HRESULT hr = inputProcessorProfiles->QueryInterface(
7411       IID_ITfInputProcessorProfileMgr, getter_AddRefs(profileMgr));
7412   if (FAILED(hr) || !profileMgr) {
7413     // On Windows Vista or later, ImmIsIME() API always returns true.
7414     // If we failed to obtain the profile manager, we cannot know if current
7415     // keyboard layout has IME.
7416     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7417             ("  TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
7418              "ITfInputProcessorProfileMgr"));
7419     return false;
7420   }
7421 
7422   TF_INPUTPROCESSORPROFILE profile;
7423   hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile);
7424   if (hr == S_FALSE) {
7425     return false;  // not found or not active
7426   }
7427   if (FAILED(hr)) {
7428     MOZ_LOG(sTextStoreLog, LogLevel::Error,
7429             ("  TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
7430              "active profile"));
7431     return false;
7432   }
7433   return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR);
7434 }
7435 #endif  // #ifdef DEBUG
7436 
7437 }  // namespace widget
7438 }  // namespace mozilla
7439