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