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