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