1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #define INPUTSCOPE_INIT_GUID
7 #define TEXTATTRS_INIT_GUID
8 #include "TSFTextStore.h"
14 #include "IMMHandler.h"
15 #include "KeyboardLayout.h"
16 #include "WinIMEHandler.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/WindowsVersion.h"
26 #include "nsPrintfCString.h"
31 static const char* kPrefNameEnableTSF
= "intl.tsf.enable";
34 * TSF related code should log its behavior even on release build especially
35 * in the interface methods.
37 * In interface methods, use LogLevel::Info.
38 * In internal methods, use LogLevel::Debug for logging normal behavior.
39 * For logging error, use LogLevel::Error.
41 * When an instance method is called, start with following text:
42 * "0x%p TSFFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
43 * after that, start with:
45 * In an internal method, start with following text:
47 * When a static method is called, start with following text:
51 LazyLogModule
sTextStoreLog("nsTextStoreWidgets");
53 enum class TextInputProcessorID
{
54 // Internal use only. This won't be returned by TSFStaticSink::ActiveTIP().
57 // Not a TIP. E.g., simple keyboard layout or IMM-IME.
60 // Used for other TIPs, i.e., any TIPs which we don't support specifically.
64 eMicrosoftIMEForJapanese
,
65 eMicrosoftOfficeIME2010ForJapanese
,
76 // TIP for Traditional Chinese.
81 eMicrosoftNewChangJie
,
82 eMicrosoftNewPhonetic
,
86 // TIP for Simplified Chinese.
88 eMicrosoftPinyinNewExperienceInputStyle
,
92 eMicrosoftIMEForKorean
,
96 static const char* GetBoolName(bool aBool
) { return aBool
? "true" : "false"; }
98 static void HandleSeparator(nsCString
& aDesc
) {
99 if (!aDesc
.IsEmpty()) {
100 aDesc
.AppendLiteral(" | ");
104 static const nsCString
GetFindFlagName(DWORD aFindFlag
) {
105 nsCString description
;
107 description
.AppendLiteral("no flags (0)");
110 if (aFindFlag
& TS_ATTR_FIND_BACKWARDS
) {
111 description
.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
113 if (aFindFlag
& TS_ATTR_FIND_WANT_OFFSET
) {
114 HandleSeparator(description
);
115 description
.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
117 if (aFindFlag
& TS_ATTR_FIND_UPDATESTART
) {
118 HandleSeparator(description
);
119 description
.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
121 if (aFindFlag
& TS_ATTR_FIND_WANT_VALUE
) {
122 HandleSeparator(description
);
123 description
.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
125 if (aFindFlag
& TS_ATTR_FIND_WANT_END
) {
126 HandleSeparator(description
);
127 description
.AppendLiteral("TS_ATTR_FIND_WANT_END");
129 if (aFindFlag
& TS_ATTR_FIND_HIDDEN
) {
130 HandleSeparator(description
);
131 description
.AppendLiteral("TS_ATTR_FIND_HIDDEN");
133 if (description
.IsEmpty()) {
134 description
.AppendLiteral("Unknown (");
135 description
.AppendInt(static_cast<uint32_t>(aFindFlag
));
136 description
.Append(')');
141 class GetACPFromPointFlagName
: public nsAutoCString
{
143 explicit GetACPFromPointFlagName(DWORD aFlags
) {
145 AppendLiteral("no flags (0)");
148 if (aFlags
& GXFPF_ROUND_NEAREST
) {
149 AppendLiteral("GXFPF_ROUND_NEAREST");
150 aFlags
&= ~GXFPF_ROUND_NEAREST
;
152 if (aFlags
& GXFPF_NEAREST
) {
153 HandleSeparator(*this);
154 AppendLiteral("GXFPF_NEAREST");
155 aFlags
&= ~GXFPF_NEAREST
;
158 HandleSeparator(*this);
159 AppendLiteral("Unknown(");
160 AppendInt(static_cast<uint32_t>(aFlags
));
164 virtual ~GetACPFromPointFlagName() {}
167 static const char* GetFocusChangeName(
168 InputContextAction::FocusChange aFocusChange
) {
169 switch (aFocusChange
) {
170 case InputContextAction::FOCUS_NOT_CHANGED
:
171 return "FOCUS_NOT_CHANGED";
172 case InputContextAction::GOT_FOCUS
:
174 case InputContextAction::LOST_FOCUS
:
176 case InputContextAction::MENU_GOT_PSEUDO_FOCUS
:
177 return "MENU_GOT_PSEUDO_FOCUS";
178 case InputContextAction::MENU_LOST_PSEUDO_FOCUS
:
179 return "MENU_LOST_PSEUDO_FOCUS";
180 case InputContextAction::WIDGET_CREATED
:
181 return "WIDGET_CREATED";
187 static nsCString
GetCLSIDNameStr(REFCLSID aCLSID
) {
188 LPOLESTR str
= nullptr;
189 HRESULT hr
= ::StringFromCLSID(aCLSID
, &str
);
190 if (FAILED(hr
) || !str
|| !str
[0]) {
191 return EmptyCString();
195 result
= NS_ConvertUTF16toUTF8(str
);
196 ::CoTaskMemFree(str
);
200 static nsCString
GetGUIDNameStr(REFGUID aGUID
) {
202 int len
= ::StringFromGUID2(aGUID
, str
, ArrayLength(str
));
203 if (!len
|| !str
[0]) {
204 return EmptyCString();
207 return NS_ConvertUTF16toUTF8(str
);
210 static nsCString
GetGUIDNameStrWithTable(REFGUID aGUID
) {
211 #define RETURN_GUID_NAME(aNamedGUID) \
212 if (IsEqualGUID(aGUID, aNamedGUID)) { \
213 return NS_LITERAL_CSTRING(#aNamedGUID); \
216 RETURN_GUID_NAME(GUID_PROP_INPUTSCOPE
)
217 RETURN_GUID_NAME(TSATTRID_OTHERS
)
218 RETURN_GUID_NAME(TSATTRID_Font
)
219 RETURN_GUID_NAME(TSATTRID_Font_FaceName
)
220 RETURN_GUID_NAME(TSATTRID_Font_SizePts
)
221 RETURN_GUID_NAME(TSATTRID_Font_Style
)
222 RETURN_GUID_NAME(TSATTRID_Font_Style_Bold
)
223 RETURN_GUID_NAME(TSATTRID_Font_Style_Italic
)
224 RETURN_GUID_NAME(TSATTRID_Font_Style_SmallCaps
)
225 RETURN_GUID_NAME(TSATTRID_Font_Style_Capitalize
)
226 RETURN_GUID_NAME(TSATTRID_Font_Style_Uppercase
)
227 RETURN_GUID_NAME(TSATTRID_Font_Style_Lowercase
)
228 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation
)
229 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_LasVegasLights
)
230 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_BlinkingBackground
)
231 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_SparkleText
)
232 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingBlackAnts
)
233 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingRedAnts
)
234 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_Shimmer
)
235 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeDown
)
236 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeRight
)
237 RETURN_GUID_NAME(TSATTRID_Font_Style_Emboss
)
238 RETURN_GUID_NAME(TSATTRID_Font_Style_Engrave
)
239 RETURN_GUID_NAME(TSATTRID_Font_Style_Hidden
)
240 RETURN_GUID_NAME(TSATTRID_Font_Style_Kerning
)
241 RETURN_GUID_NAME(TSATTRID_Font_Style_Outlined
)
242 RETURN_GUID_NAME(TSATTRID_Font_Style_Position
)
243 RETURN_GUID_NAME(TSATTRID_Font_Style_Protected
)
244 RETURN_GUID_NAME(TSATTRID_Font_Style_Shadow
)
245 RETURN_GUID_NAME(TSATTRID_Font_Style_Spacing
)
246 RETURN_GUID_NAME(TSATTRID_Font_Style_Weight
)
247 RETURN_GUID_NAME(TSATTRID_Font_Style_Height
)
248 RETURN_GUID_NAME(TSATTRID_Font_Style_Underline
)
249 RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Single
)
250 RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Double
)
251 RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough
)
252 RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Single
)
253 RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Double
)
254 RETURN_GUID_NAME(TSATTRID_Font_Style_Overline
)
255 RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Single
)
256 RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Double
)
257 RETURN_GUID_NAME(TSATTRID_Font_Style_Blink
)
258 RETURN_GUID_NAME(TSATTRID_Font_Style_Subscript
)
259 RETURN_GUID_NAME(TSATTRID_Font_Style_Superscript
)
260 RETURN_GUID_NAME(TSATTRID_Font_Style_Color
)
261 RETURN_GUID_NAME(TSATTRID_Font_Style_BackgroundColor
)
262 RETURN_GUID_NAME(TSATTRID_Text
)
263 RETURN_GUID_NAME(TSATTRID_Text_VerticalWriting
)
264 RETURN_GUID_NAME(TSATTRID_Text_RightToLeft
)
265 RETURN_GUID_NAME(TSATTRID_Text_Orientation
)
266 RETURN_GUID_NAME(TSATTRID_Text_Language
)
267 RETURN_GUID_NAME(TSATTRID_Text_ReadOnly
)
268 RETURN_GUID_NAME(TSATTRID_Text_EmbeddedObject
)
269 RETURN_GUID_NAME(TSATTRID_Text_Alignment
)
270 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Left
)
271 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Right
)
272 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Center
)
273 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Justify
)
274 RETURN_GUID_NAME(TSATTRID_Text_Link
)
275 RETURN_GUID_NAME(TSATTRID_Text_Hyphenation
)
276 RETURN_GUID_NAME(TSATTRID_Text_Para
)
277 RETURN_GUID_NAME(TSATTRID_Text_Para_FirstLineIndent
)
278 RETURN_GUID_NAME(TSATTRID_Text_Para_LeftIndent
)
279 RETURN_GUID_NAME(TSATTRID_Text_Para_RightIndent
)
280 RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceAfter
)
281 RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceBefore
)
282 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing
)
283 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Single
)
284 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_OnePtFive
)
285 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Double
)
286 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_AtLeast
)
287 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Exactly
)
288 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Multiple
)
289 RETURN_GUID_NAME(TSATTRID_List
)
290 RETURN_GUID_NAME(TSATTRID_List_LevelIndel
)
291 RETURN_GUID_NAME(TSATTRID_List_Type
)
292 RETURN_GUID_NAME(TSATTRID_List_Type_Bullet
)
293 RETURN_GUID_NAME(TSATTRID_List_Type_Arabic
)
294 RETURN_GUID_NAME(TSATTRID_List_Type_LowerLetter
)
295 RETURN_GUID_NAME(TSATTRID_List_Type_UpperLetter
)
296 RETURN_GUID_NAME(TSATTRID_List_Type_LowerRoman
)
297 RETURN_GUID_NAME(TSATTRID_List_Type_UpperRoman
)
298 RETURN_GUID_NAME(TSATTRID_App
)
299 RETURN_GUID_NAME(TSATTRID_App_IncorrectSpelling
)
300 RETURN_GUID_NAME(TSATTRID_App_IncorrectGrammar
)
302 #undef RETURN_GUID_NAME
304 return GetGUIDNameStr(aGUID
);
307 static nsCString
GetRIIDNameStr(REFIID aRIID
) {
308 LPOLESTR str
= nullptr;
309 HRESULT hr
= ::StringFromIID(aRIID
, &str
);
310 if (FAILED(hr
) || !str
|| !str
[0]) {
311 return EmptyCString();
314 nsAutoString
key(L
"Interface\\");
319 if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT
, key
.get(), nullptr, buf
,
321 result
= NS_ConvertUTF16toUTF8(buf
);
323 result
= NS_ConvertUTF16toUTF8(str
);
326 ::CoTaskMemFree(str
);
330 static const char* GetCommonReturnValueName(HRESULT aResult
) {
337 return "E_ACCESSDENIED";
343 return "E_INVALIDARG";
345 return "E_NOINTERFACE";
349 return "E_OUTOFMEMORY";
353 return "E_UNEXPECTED";
355 return SUCCEEDED(aResult
) ? "Succeeded" : "Failed";
359 static const char* GetTextStoreReturnValueName(HRESULT aResult
) {
362 return "TS_E_FORMAT";
363 case TS_E_INVALIDPOINT
:
364 return "TS_E_INVALIDPOINT";
365 case TS_E_INVALIDPOS
:
366 return "TS_E_INVALIDPOS";
367 case TS_E_NOINTERFACE
:
368 return "TS_E_NOINTERFACE";
370 return "TS_E_NOLAYOUT";
372 return "TS_E_NOLOCK";
374 return "TS_E_NOOBJECT";
375 case TS_E_NOSELECTION
:
376 return "TS_E_NOSELECTION";
378 return "TS_E_NOSERVICE";
380 return "TS_E_READONLY";
381 case TS_E_SYNCHRONOUS
:
382 return "TS_E_SYNCHRONOUS";
386 return GetCommonReturnValueName(aResult
);
390 static const nsCString
GetSinkMaskNameStr(DWORD aSinkMask
) {
391 nsCString description
;
392 if (aSinkMask
& TS_AS_TEXT_CHANGE
) {
393 description
.AppendLiteral("TS_AS_TEXT_CHANGE");
395 if (aSinkMask
& TS_AS_SEL_CHANGE
) {
396 HandleSeparator(description
);
397 description
.AppendLiteral("TS_AS_SEL_CHANGE");
399 if (aSinkMask
& TS_AS_LAYOUT_CHANGE
) {
400 HandleSeparator(description
);
401 description
.AppendLiteral("TS_AS_LAYOUT_CHANGE");
403 if (aSinkMask
& TS_AS_ATTR_CHANGE
) {
404 HandleSeparator(description
);
405 description
.AppendLiteral("TS_AS_ATTR_CHANGE");
407 if (aSinkMask
& TS_AS_STATUS_CHANGE
) {
408 HandleSeparator(description
);
409 description
.AppendLiteral("TS_AS_STATUS_CHANGE");
411 if (description
.IsEmpty()) {
412 description
.AppendLiteral("not-specified");
417 static const char* GetActiveSelEndName(TsActiveSelEnd aSelEnd
) {
418 return aSelEnd
== TS_AE_NONE
420 : aSelEnd
== TS_AE_START
422 : aSelEnd
== TS_AE_END
? "TS_AE_END" : "Unknown";
425 static const nsCString
GetLockFlagNameStr(DWORD aLockFlags
) {
426 nsCString description
;
427 if ((aLockFlags
& TS_LF_READWRITE
) == TS_LF_READWRITE
) {
428 description
.AppendLiteral("TS_LF_READWRITE");
429 } else if (aLockFlags
& TS_LF_READ
) {
430 description
.AppendLiteral("TS_LF_READ");
432 if (aLockFlags
& TS_LF_SYNC
) {
433 if (!description
.IsEmpty()) {
434 description
.AppendLiteral(" | ");
436 description
.AppendLiteral("TS_LF_SYNC");
438 if (description
.IsEmpty()) {
439 description
.AppendLiteral("not-specified");
444 static const char* GetTextRunTypeName(TsRunType aRunType
) {
447 return "TS_RT_PLAIN";
449 return "TS_RT_HIDDEN";
451 return "TS_RT_OPAQUE";
457 static nsCString
GetColorName(const TF_DA_COLOR
& aColor
) {
458 switch (aColor
.type
) {
460 return NS_LITERAL_CSTRING("TF_CT_NONE");
462 return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
463 static_cast<int32_t>(aColor
.nIndex
));
465 return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
466 static_cast<int32_t>(aColor
.cr
));
469 return nsPrintfCString("Unknown(%08X)",
470 static_cast<int32_t>(aColor
.type
));
474 static nsCString
GetLineStyleName(TF_DA_LINESTYLE aLineStyle
) {
475 switch (aLineStyle
) {
477 return NS_LITERAL_CSTRING("TF_LS_NONE");
479 return NS_LITERAL_CSTRING("TF_LS_SOLID");
481 return NS_LITERAL_CSTRING("TF_LS_DOT");
483 return NS_LITERAL_CSTRING("TF_LS_DASH");
485 return NS_LITERAL_CSTRING("TF_LS_SQUIGGLE");
487 return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle
));
492 static nsCString
GetClauseAttrName(TF_DA_ATTR_INFO aAttr
) {
495 return NS_LITERAL_CSTRING("TF_ATTR_INPUT");
496 case TF_ATTR_TARGET_CONVERTED
:
497 return NS_LITERAL_CSTRING("TF_ATTR_TARGET_CONVERTED");
498 case TF_ATTR_CONVERTED
:
499 return NS_LITERAL_CSTRING("TF_ATTR_CONVERTED");
500 case TF_ATTR_TARGET_NOTCONVERTED
:
501 return NS_LITERAL_CSTRING("TF_ATTR_TARGET_NOTCONVERTED");
502 case TF_ATTR_INPUT_ERROR
:
503 return NS_LITERAL_CSTRING("TF_ATTR_INPUT_ERROR");
504 case TF_ATTR_FIXEDCONVERTED
:
505 return NS_LITERAL_CSTRING("TF_ATTR_FIXEDCONVERTED");
507 return NS_LITERAL_CSTRING("TF_ATTR_OTHER");
509 return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr
));
514 static nsCString
GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE
& aDispAttr
) {
517 str
+= GetColorName(aDispAttr
.crText
);
518 str
+= " }, crBk:{ ";
519 str
+= GetColorName(aDispAttr
.crBk
);
520 str
+= " }, lsStyle: ";
521 str
+= GetLineStyleName(aDispAttr
.lsStyle
);
522 str
+= ", fBoldLine: ";
523 str
+= GetBoolName(aDispAttr
.fBoldLine
);
524 str
+= ", crLine:{ ";
525 str
+= GetColorName(aDispAttr
.crLine
);
526 str
+= " }, bAttr: ";
527 str
+= GetClauseAttrName(aDispAttr
.bAttr
);
531 static const char* GetMouseButtonName(int16_t aButton
) {
533 case MouseButton::eLeft
:
535 case MouseButton::eMiddle
:
536 return "MiddleButton";
537 case MouseButton::eRight
:
538 return "RightButton";
540 return "UnknownButton";
544 #define ADD_SEPARATOR_IF_NECESSARY(aStr) \
545 if (!aStr.IsEmpty()) { \
546 aStr.AppendLiteral(", "); \
549 static nsCString
GetMouseButtonsName(int16_t aButtons
) {
551 return NS_LITERAL_CSTRING("no buttons");
554 if (aButtons
& MouseButtonsFlag::eLeftFlag
) {
555 names
= "LeftButton";
557 if (aButtons
& MouseButtonsFlag::eRightFlag
) {
558 ADD_SEPARATOR_IF_NECESSARY(names
);
559 names
+= "RightButton";
561 if (aButtons
& MouseButtonsFlag::eMiddleFlag
) {
562 ADD_SEPARATOR_IF_NECESSARY(names
);
563 names
+= "MiddleButton";
565 if (aButtons
& MouseButtonsFlag::e4thFlag
) {
566 ADD_SEPARATOR_IF_NECESSARY(names
);
567 names
+= "4thButton";
569 if (aButtons
& MouseButtonsFlag::e5thFlag
) {
570 ADD_SEPARATOR_IF_NECESSARY(names
);
571 names
+= "5thButton";
576 static nsCString
GetModifiersName(Modifiers aModifiers
) {
577 if (aModifiers
== MODIFIER_NONE
) {
578 return NS_LITERAL_CSTRING("no modifiers");
581 if (aModifiers
& MODIFIER_ALT
) {
582 names
= NS_DOM_KEYNAME_ALT
;
584 if (aModifiers
& MODIFIER_ALTGRAPH
) {
585 ADD_SEPARATOR_IF_NECESSARY(names
);
586 names
+= NS_DOM_KEYNAME_ALTGRAPH
;
588 if (aModifiers
& MODIFIER_CAPSLOCK
) {
589 ADD_SEPARATOR_IF_NECESSARY(names
);
590 names
+= NS_DOM_KEYNAME_CAPSLOCK
;
592 if (aModifiers
& MODIFIER_CONTROL
) {
593 ADD_SEPARATOR_IF_NECESSARY(names
);
594 names
+= NS_DOM_KEYNAME_CONTROL
;
596 if (aModifiers
& MODIFIER_FN
) {
597 ADD_SEPARATOR_IF_NECESSARY(names
);
598 names
+= NS_DOM_KEYNAME_FN
;
600 if (aModifiers
& MODIFIER_FNLOCK
) {
601 ADD_SEPARATOR_IF_NECESSARY(names
);
602 names
+= NS_DOM_KEYNAME_FNLOCK
;
604 if (aModifiers
& MODIFIER_META
) {
605 ADD_SEPARATOR_IF_NECESSARY(names
);
606 names
+= NS_DOM_KEYNAME_META
;
608 if (aModifiers
& MODIFIER_NUMLOCK
) {
609 ADD_SEPARATOR_IF_NECESSARY(names
);
610 names
+= NS_DOM_KEYNAME_NUMLOCK
;
612 if (aModifiers
& MODIFIER_SCROLLLOCK
) {
613 ADD_SEPARATOR_IF_NECESSARY(names
);
614 names
+= NS_DOM_KEYNAME_SCROLLLOCK
;
616 if (aModifiers
& MODIFIER_SHIFT
) {
617 ADD_SEPARATOR_IF_NECESSARY(names
);
618 names
+= NS_DOM_KEYNAME_SHIFT
;
620 if (aModifiers
& MODIFIER_SYMBOL
) {
621 ADD_SEPARATOR_IF_NECESSARY(names
);
622 names
+= NS_DOM_KEYNAME_SYMBOL
;
624 if (aModifiers
& MODIFIER_SYMBOLLOCK
) {
625 ADD_SEPARATOR_IF_NECESSARY(names
);
626 names
+= NS_DOM_KEYNAME_SYMBOLLOCK
;
628 if (aModifiers
& MODIFIER_OS
) {
629 ADD_SEPARATOR_IF_NECESSARY(names
);
630 names
+= NS_DOM_KEYNAME_OS
;
635 class GetWritingModeName
: public nsAutoCString
{
637 explicit GetWritingModeName(const WritingMode
& aWritingMode
) {
638 if (!aWritingMode
.IsVertical()) {
639 AssignLiteral("Horizontal");
642 if (aWritingMode
.IsVerticalLR()) {
643 AssignLiteral("Vertical (LR)");
646 AssignLiteral("Vertical (RL)");
648 virtual ~GetWritingModeName() {}
651 class GetEscapedUTF8String final
: public NS_ConvertUTF16toUTF8
{
653 explicit GetEscapedUTF8String(const nsAString
& aString
)
654 : NS_ConvertUTF16toUTF8(aString
) {
657 explicit GetEscapedUTF8String(const char16ptr_t aString
)
658 : NS_ConvertUTF16toUTF8(aString
) {
661 GetEscapedUTF8String(const char16ptr_t aString
, uint32_t aLength
)
662 : NS_ConvertUTF16toUTF8(aString
, aLength
) {
668 ReplaceSubstring("\r", "\\r");
669 ReplaceSubstring("\n", "\\n");
670 ReplaceSubstring("\t", "\\t");
674 class GetIMEStateString
: public nsAutoCString
{
676 explicit GetIMEStateString(const IMEState
& aIMEState
) {
677 AppendLiteral("{ mEnabled=");
678 switch (aIMEState
.mEnabled
) {
679 case IMEState::DISABLED
:
680 AppendLiteral("DISABLED");
682 case IMEState::ENABLED
:
683 AppendLiteral("ENABLED");
685 case IMEState::PASSWORD
:
686 AppendLiteral("PASSWORD");
688 case IMEState::PLUGIN
:
689 AppendLiteral("PLUGIN");
691 case IMEState::UNKNOWN
:
692 AppendLiteral("UNKNOWN");
695 AppendPrintf("Unknown value (%d)", aIMEState
.mEnabled
);
698 AppendLiteral(", mOpen=");
699 switch (aIMEState
.mOpen
) {
700 case IMEState::OPEN_STATE_NOT_SUPPORTED
:
701 AppendLiteral("OPEN_STATE_NOT_SUPPORTED or DONT_CHANGE_OPEN_STATE");
704 AppendLiteral("OPEN");
706 case IMEState::CLOSED
:
707 AppendLiteral("CLOSED");
710 AppendPrintf("Unknown value (%d)", aIMEState
.mOpen
);
717 class GetInputContextString
: public nsAutoCString
{
719 explicit GetInputContextString(const InputContext
& aInputContext
) {
720 AppendPrintf("{ mIMEState=%s, ",
721 GetIMEStateString(aInputContext
.mIMEState
).get());
722 AppendLiteral("mOrigin=");
723 switch (aInputContext
.mOrigin
) {
724 case InputContext::ORIGIN_MAIN
:
725 AppendLiteral("ORIGIN_MAIN");
727 case InputContext::ORIGIN_CONTENT
:
728 AppendLiteral("ORIGIN_CONTENT");
731 AppendPrintf("Unknown value (%d)", aInputContext
.mOrigin
);
735 ", mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", "
736 "mActionHint=\"%s\", mMayBeIMEUnaware=%s }",
737 NS_ConvertUTF16toUTF8(aInputContext
.mHTMLInputType
).get(),
738 NS_ConvertUTF16toUTF8(aInputContext
.mHTMLInputInputmode
).get(),
739 NS_ConvertUTF16toUTF8(aInputContext
.mActionHint
).get(),
740 GetBoolName(aInputContext
.mMayBeIMEUnaware
));
744 class GetInputScopeString
: public nsAutoCString
{
746 explicit GetInputScopeString(const nsTArray
<InputScope
>& aList
) {
747 for (InputScope inputScope
: aList
) {
751 switch (inputScope
) {
753 AppendLiteral("IS_DEFAULT");
756 AppendLiteral("IS_URL");
758 case IS_FILE_FULLFILEPATH
:
759 AppendLiteral("IS_FILE_FULLFILEPATH");
761 case IS_FILE_FILENAME
:
762 AppendLiteral("IS_FILE_FILENAME");
764 case IS_EMAIL_USERNAME
:
765 AppendLiteral("IS_EMAIL_USERNAME");
767 case IS_EMAIL_SMTPEMAILADDRESS
:
768 AppendLiteral("IS_EMAIL_SMTPEMAILADDRESS");
771 AppendLiteral("IS_LOGINNAME");
773 case IS_PERSONALNAME_FULLNAME
:
774 AppendLiteral("IS_PERSONALNAME_FULLNAME");
776 case IS_PERSONALNAME_PREFIX
:
777 AppendLiteral("IS_PERSONALNAME_PREFIX");
779 case IS_PERSONALNAME_GIVENNAME
:
780 AppendLiteral("IS_PERSONALNAME_GIVENNAME");
782 case IS_PERSONALNAME_MIDDLENAME
:
783 AppendLiteral("IS_PERSONALNAME_MIDDLENAME");
785 case IS_PERSONALNAME_SURNAME
:
786 AppendLiteral("IS_PERSONALNAME_SURNAME");
788 case IS_PERSONALNAME_SUFFIX
:
789 AppendLiteral("IS_PERSONALNAME_SUFFIX");
791 case IS_ADDRESS_FULLPOSTALADDRESS
:
792 AppendLiteral("IS_ADDRESS_FULLPOSTALADDRESS");
794 case IS_ADDRESS_POSTALCODE
:
795 AppendLiteral("IS_ADDRESS_POSTALCODE");
797 case IS_ADDRESS_STREET
:
798 AppendLiteral("IS_ADDRESS_STREET");
800 case IS_ADDRESS_STATEORPROVINCE
:
801 AppendLiteral("IS_ADDRESS_STATEORPROVINCE");
803 case IS_ADDRESS_CITY
:
804 AppendLiteral("IS_ADDRESS_CITY");
806 case IS_ADDRESS_COUNTRYNAME
:
807 AppendLiteral("IS_ADDRESS_COUNTRYNAME");
809 case IS_ADDRESS_COUNTRYSHORTNAME
:
810 AppendLiteral("IS_ADDRESS_COUNTRYSHORTNAME");
812 case IS_CURRENCY_AMOUNTANDSYMBOL
:
813 AppendLiteral("IS_CURRENCY_AMOUNTANDSYMBOL");
815 case IS_CURRENCY_AMOUNT
:
816 AppendLiteral("IS_CURRENCY_AMOUNT");
818 case IS_DATE_FULLDATE
:
819 AppendLiteral("IS_DATE_FULLDATE");
822 AppendLiteral("IS_DATE_MONTH");
825 AppendLiteral("IS_DATE_DAY");
828 AppendLiteral("IS_DATE_YEAR");
830 case IS_DATE_MONTHNAME
:
831 AppendLiteral("IS_DATE_MONTHNAME");
833 case IS_DATE_DAYNAME
:
834 AppendLiteral("IS_DATE_DAYNAME");
837 AppendLiteral("IS_DIGITS");
840 AppendLiteral("IS_NUMBER");
843 AppendLiteral("IS_ONECHAR");
846 AppendLiteral("IS_PASSWORD");
848 case IS_TELEPHONE_FULLTELEPHONENUMBER
:
849 AppendLiteral("IS_TELEPHONE_FULLTELEPHONENUMBER");
851 case IS_TELEPHONE_COUNTRYCODE
:
852 AppendLiteral("IS_TELEPHONE_COUNTRYCODE");
854 case IS_TELEPHONE_AREACODE
:
855 AppendLiteral("IS_TELEPHONE_AREACODE");
857 case IS_TELEPHONE_LOCALNUMBER
:
858 AppendLiteral("IS_TELEPHONE_LOCALNUMBER");
860 case IS_TIME_FULLTIME
:
861 AppendLiteral("IS_TIME_FULLTIME");
864 AppendLiteral("IS_TIME_HOUR");
866 case IS_TIME_MINORSEC
:
867 AppendLiteral("IS_TIME_MINORSEC");
869 case IS_NUMBER_FULLWIDTH
:
870 AppendLiteral("IS_NUMBER_FULLWIDTH");
872 case IS_ALPHANUMERIC_HALFWIDTH
:
873 AppendLiteral("IS_ALPHANUMERIC_HALFWIDTH");
875 case IS_ALPHANUMERIC_FULLWIDTH
:
876 AppendLiteral("IS_ALPHANUMERIC_FULLWIDTH");
878 case IS_CURRENCY_CHINESE
:
879 AppendLiteral("IS_CURRENCY_CHINESE");
882 AppendLiteral("IS_BOPOMOFO");
885 AppendLiteral("IS_HIRAGANA");
887 case IS_KATAKANA_HALFWIDTH
:
888 AppendLiteral("IS_KATAKANA_HALFWIDTH");
890 case IS_KATAKANA_FULLWIDTH
:
891 AppendLiteral("IS_KATAKANA_FULLWIDTH");
894 AppendLiteral("IS_HANJA");
897 AppendLiteral("IS_PHRASELIST");
899 case IS_REGULAREXPRESSION
:
900 AppendLiteral("IS_REGULAREXPRESSION");
903 AppendLiteral("IS_SRGS");
906 AppendLiteral("IS_XML");
909 AppendLiteral("IS_PRIVATE");
912 AppendPrintf("Unknown Value(%d)", inputScope
);
919 /******************************************************************/
921 /******************************************************************/
923 class InputScopeImpl final
: public ITfInputScope
{
927 explicit InputScopeImpl(const nsTArray
<InputScope
>& aList
)
928 : mInputScopes(aList
) {
930 sTextStoreLog
, LogLevel::Info
,
931 ("0x%p InputScopeImpl(%s)", this, GetInputScopeString(aList
).get()));
934 NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl
)
936 STDMETHODIMP
QueryInterface(REFIID riid
, void** ppv
) {
938 if ((IID_IUnknown
== riid
) || (IID_ITfInputScope
== riid
)) {
939 *ppv
= static_cast<ITfInputScope
*>(this);
945 return E_NOINTERFACE
;
948 STDMETHODIMP
GetInputScopes(InputScope
** pprgInputScopes
, UINT
* pcCount
) {
949 uint32_t count
= (mInputScopes
.IsEmpty() ? 1 : mInputScopes
.Length());
952 (InputScope
*)CoTaskMemAlloc(sizeof(InputScope
) * count
);
953 NS_ENSURE_TRUE(pScope
, E_OUTOFMEMORY
);
955 if (mInputScopes
.IsEmpty()) {
956 *pScope
= IS_DEFAULT
;
958 *pprgInputScopes
= pScope
;
964 for (uint32_t idx
= 0; idx
< count
; idx
++) {
965 *(pScope
+ idx
) = mInputScopes
[idx
];
969 *pprgInputScopes
= pScope
;
973 STDMETHODIMP
GetPhrase(BSTR
** ppbstrPhrases
, UINT
* pcCount
) {
976 STDMETHODIMP
GetRegularExpression(BSTR
* pbstrRegExp
) { return E_NOTIMPL
; }
977 STDMETHODIMP
GetSRGS(BSTR
* pbstrSRGS
) { return E_NOTIMPL
; }
978 STDMETHODIMP
GetXML(BSTR
* pbstrXML
) { return E_NOTIMPL
; }
981 nsTArray
<InputScope
> mInputScopes
;
984 /******************************************************************/
986 /******************************************************************/
988 class TSFStaticSink final
: public ITfInputProcessorProfileActivationSink
{
990 static TSFStaticSink
* GetInstance() {
992 RefPtr
<ITfThreadMgr
> threadMgr
= TSFTextStore::GetThreadMgr();
993 if (NS_WARN_IF(!threadMgr
)) {
995 sTextStoreLog
, LogLevel::Error
,
996 ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
997 "instance due to no ThreadMgr instance"));
1000 RefPtr
<ITfInputProcessorProfiles
> inputProcessorProfiles
=
1001 TSFTextStore::GetInputProcessorProfiles();
1002 if (NS_WARN_IF(!inputProcessorProfiles
)) {
1004 sTextStoreLog
, LogLevel::Error
,
1005 ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
1006 "instance due to no InputProcessorProfiles instance"));
1009 RefPtr
<TSFStaticSink
> staticSink
= new TSFStaticSink();
1010 if (NS_WARN_IF(!staticSink
->Init(threadMgr
, inputProcessorProfiles
))) {
1011 staticSink
->Destroy();
1013 sTextStoreLog
, LogLevel::Error
,
1014 ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
1018 sInstance
= staticSink
.forget();
1023 static void Shutdown() {
1025 sInstance
->Destroy();
1026 sInstance
= nullptr;
1030 bool Init(ITfThreadMgr
* aThreadMgr
,
1031 ITfInputProcessorProfiles
* aInputProcessorProfiles
);
1032 STDMETHODIMP
QueryInterface(REFIID riid
, void** ppv
) {
1034 if (IID_IUnknown
== riid
||
1035 IID_ITfInputProcessorProfileActivationSink
== riid
) {
1036 *ppv
= static_cast<ITfInputProcessorProfileActivationSink
*>(this);
1042 return E_NOINTERFACE
;
1045 NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink
)
1047 const nsString
& GetActiveTIPKeyboardDescription() const {
1048 return mActiveTIPKeyboardDescription
;
1051 static bool IsIMM_IMEActive() {
1052 // Use IMM API until TSFStaticSink starts to work.
1053 if (!sInstance
|| !sInstance
->EnsureInitActiveTIPKeyboard()) {
1054 return IsIMM_IME(::GetKeyboardLayout(0));
1056 return sInstance
->mIsIMM_IME
;
1059 static bool IsIMM_IME(HKL aHKL
) {
1060 return (::ImmGetIMEFileNameW(aHKL
, nullptr, 0) > 0);
1063 static bool IsTraditionalChinese() {
1065 return sInstance
&& sInstance
->IsTraditionalChineseInternal();
1067 static bool IsSimplifiedChinese() {
1069 return sInstance
&& sInstance
->IsSimplifiedChineseInternal();
1071 static bool IsJapanese() {
1073 return sInstance
&& sInstance
->IsJapaneseInternal();
1075 static bool IsKorean() {
1077 return sInstance
&& sInstance
->IsKoreanInternal();
1081 * ActiveTIP() returns an ID for currently active TIP.
1082 * Please note that this method is expensive due to needs a lot of GUID
1083 * comparations if active language ID is one of CJKT. If you need to
1084 * check TIPs for a specific language, you should check current language
1087 static TextInputProcessorID
ActiveTIP() {
1089 if (!sInstance
|| !sInstance
->EnsureInitActiveTIPKeyboard()) {
1090 return TextInputProcessorID::eUnknown
;
1092 sInstance
->ComputeActiveTextInputProcessor();
1093 if (NS_WARN_IF(sInstance
->mActiveTIP
==
1094 TextInputProcessorID::eNotComputed
)) {
1095 return TextInputProcessorID::eUnknown
;
1097 return sInstance
->mActiveTIP
;
1100 static bool IsMSChangJieOrMSQuickActive() {
1101 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1102 // For avoiding unnecessary computation, we should check if the language
1103 // for current TIP is Traditional Chinese.
1104 if (!IsTraditionalChinese()) {
1107 switch (ActiveTIP()) {
1108 case TextInputProcessorID::eMicrosoftChangJie
:
1109 case TextInputProcessorID::eMicrosoftQuick
:
1116 static bool IsMSPinyinOrMSWubiActive() {
1117 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1118 // For avoiding unnecessary computation, we should check if the language
1119 // for current TIP is Simplified Chinese.
1120 if (!IsSimplifiedChinese()) {
1123 switch (ActiveTIP()) {
1124 case TextInputProcessorID::eMicrosoftPinyin
:
1125 case TextInputProcessorID::eMicrosoftWubi
:
1132 static bool IsMSJapaneseIMEActive() {
1133 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1134 // For avoiding unnecessary computation, we should check if the language
1135 // for current TIP is Japanese.
1136 if (!IsJapanese()) {
1139 return ActiveTIP() == TextInputProcessorID::eMicrosoftIMEForJapanese
;
1142 static bool IsGoogleJapaneseInputActive() {
1143 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1144 // For avoiding unnecessary computation, we should check if the language
1145 // for current TIP is Japanese.
1146 if (!IsJapanese()) {
1149 return ActiveTIP() == TextInputProcessorID::eGoogleJapaneseInput
;
1152 static bool IsATOKActive() {
1153 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1154 // For avoiding unnecessary computation, we should check if active TIP is
1155 // ATOK first since it's cheaper.
1156 return IsJapanese() && sInstance
->IsATOKActiveInternal();
1159 // Note that ATOK 2011 - 2016 refers native caret position for deciding its
1160 // popup window position.
1161 static bool IsATOKReferringNativeCaretActive() {
1162 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1163 // For avoiding unnecessary computation, we should check if active TIP is
1164 // ATOK first since it's cheaper.
1165 if (!IsJapanese() || !sInstance
->IsATOKActiveInternal()) {
1168 switch (ActiveTIP()) {
1169 case TextInputProcessorID::eATOK2011
:
1170 case TextInputProcessorID::eATOK2012
:
1171 case TextInputProcessorID::eATOK2013
:
1172 case TextInputProcessorID::eATOK2014
:
1173 case TextInputProcessorID::eATOK2015
:
1181 static void EnsureInstance() {
1183 RefPtr
<TSFStaticSink
> staticSink
= GetInstance();
1184 Unused
<< staticSink
;
1188 bool IsTraditionalChineseInternal() const { return mLangID
== 0x0404; }
1189 bool IsSimplifiedChineseInternal() const { return mLangID
== 0x0804; }
1190 bool IsJapaneseInternal() const { return mLangID
== 0x0411; }
1191 bool IsKoreanInternal() const { return mLangID
== 0x0412; }
1193 bool IsATOKActiveInternal() {
1194 EnsureInitActiveTIPKeyboard();
1195 // FYI: Name of packaged ATOK includes the release year like "ATOK 2015".
1196 // Name of ATOK Passport (subscription) equals "ATOK".
1197 return StringBeginsWith(mActiveTIPKeyboardDescription
,
1198 NS_LITERAL_STRING("ATOK ")) ||
1199 mActiveTIPKeyboardDescription
.EqualsLiteral("ATOK");
1202 void ComputeActiveTextInputProcessor() {
1203 if (mActiveTIP
!= TextInputProcessorID::eNotComputed
) {
1207 if (mActiveTIPGUID
== GUID_NULL
) {
1208 mActiveTIP
= TextInputProcessorID::eNone
;
1212 // Comparing GUID is slow. So, we should use language information to
1213 // reduce the comparing cost for TIP which is not we do not support
1214 // specifically since they are always compared with all supported TIPs.
1217 mActiveTIP
= ComputeActiveTIPAsTraditionalChinese();
1220 mActiveTIP
= ComputeActiveTIPAsJapanese();
1223 mActiveTIP
= ComputeActiveTIPAsKorean();
1226 mActiveTIP
= ComputeActiveTIPAsSimplifiedChinese();
1229 mActiveTIP
= TextInputProcessorID::eUnknown
;
1233 TextInputProcessorID
ComputeActiveTIPAsJapanese() {
1234 // {A76C93D9-5523-4E90-AAFA-4DB112F9AC76} (Win7, Win8.1, Win10)
1235 static const GUID kMicrosoftIMEForJapaneseGUID
= {
1239 {0xAA, 0xFA, 0x4D, 0xB1, 0x12, 0xF9, 0xAC, 0x76}};
1240 if (mActiveTIPGUID
== kMicrosoftIMEForJapaneseGUID
) {
1241 return TextInputProcessorID::eMicrosoftIMEForJapanese
;
1243 // {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
1244 static const GUID kMicrosoftOfficeIME2010ForJapaneseGUID
= {
1248 {0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64}};
1249 if (mActiveTIPGUID
== kMicrosoftOfficeIME2010ForJapaneseGUID
) {
1250 return TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese
;
1252 // {773EB24E-CA1D-4B1B-B420-FA985BB0B80D}
1253 static const GUID kGoogleJapaneseInputGUID
= {
1257 {0xB4, 0x20, 0xFA, 0x98, 0x5B, 0xB0, 0xB8, 0x0D}};
1258 if (mActiveTIPGUID
== kGoogleJapaneseInputGUID
) {
1259 return TextInputProcessorID::eGoogleJapaneseInput
;
1261 // {F9C24A5C-8A53-499D-9572-93B2FF582115}
1262 static const GUID kATOK2011GUID
= {
1266 {0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15}};
1267 if (mActiveTIPGUID
== kATOK2011GUID
) {
1268 return TextInputProcessorID::eATOK2011
;
1270 // {1DE01562-F445-401B-B6C3-E5B18DB79461}
1271 static const GUID kATOK2012GUID
= {
1275 {0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61}};
1276 if (mActiveTIPGUID
== kATOK2012GUID
) {
1277 return TextInputProcessorID::eATOK2012
;
1279 // {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
1280 static const GUID kATOK2013GUID
= {
1284 {0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15}};
1285 if (mActiveTIPGUID
== kATOK2013GUID
) {
1286 return TextInputProcessorID::eATOK2013
;
1288 // {4EF33B79-6AA9-4271-B4BF-9321C279381B}
1289 static const GUID kATOK2014GUID
= {
1293 {0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B}};
1294 if (mActiveTIPGUID
== kATOK2014GUID
) {
1295 return TextInputProcessorID::eATOK2014
;
1297 // {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
1298 static const GUID kATOK2015GUID
= {
1302 {0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A}};
1303 if (mActiveTIPGUID
== kATOK2015GUID
) {
1304 return TextInputProcessorID::eATOK2015
;
1306 // {0B557B4C-5740-4110-A60A-1493FA10BF2B}
1307 static const GUID kATOK2016GUID
= {
1311 {0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B}};
1312 if (mActiveTIPGUID
== kATOK2016GUID
) {
1313 return TextInputProcessorID::eATOK2016
;
1317 // - {6DBFD8F5-701D-11E6-920F-782BCBA6348F}
1318 // * ATOK Passport (confirmed with version 31.1.2)
1319 // - {A38F2FD9-7199-45E1-841C-BE0313D8052F}
1321 if (IsATOKActiveInternal()) {
1322 return TextInputProcessorID::eATOKUnknown
;
1325 // {E6D66705-1EDA-4373-8D01-1D0CB2D054C7}
1326 static const GUID kJapanist10GUID
= {
1330 {0x8D, 0x01, 0x1D, 0x0C, 0xB2, 0xD0, 0x54, 0xC7}};
1331 if (mActiveTIPGUID
== kJapanist10GUID
) {
1332 return TextInputProcessorID::eJapanist10
;
1335 return TextInputProcessorID::eUnknown
;
1338 TextInputProcessorID
ComputeActiveTIPAsTraditionalChinese() {
1339 // {B2F9C502-1742-11D4-9790-0080C882687E} (Win8.1, Win10)
1340 static const GUID kMicrosoftBopomofoGUID
= {
1344 {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1345 if (mActiveTIPGUID
== kMicrosoftBopomofoGUID
) {
1346 return TextInputProcessorID::eMicrosoftBopomofo
;
1348 // {4BDF9F03-C7D3-11D4-B2AB-0080C882687E} (Win7, Win8.1, Win10)
1349 static const GUID kMicrosoftChangJieGUID
= {
1353 {0xB2, 0xAB, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1354 if (mActiveTIPGUID
== kMicrosoftChangJieGUID
) {
1355 return TextInputProcessorID::eMicrosoftChangJie
;
1357 // {761309DE-317A-11D4-9B5D-0080C882687E} (Win7)
1358 static const GUID kMicrosoftPhoneticGUID
= {
1362 {0x9B, 0x5D, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1363 if (mActiveTIPGUID
== kMicrosoftPhoneticGUID
) {
1364 return TextInputProcessorID::eMicrosoftPhonetic
;
1366 // {6024B45F-5C54-11D4-B921-0080C882687E} (Win7, Win8.1, Win10)
1367 static const GUID kMicrosoftQuickGUID
= {
1371 {0xB9, 0x21, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1372 if (mActiveTIPGUID
== kMicrosoftQuickGUID
) {
1373 return TextInputProcessorID::eMicrosoftQuick
;
1375 // {F3BA907A-6C7E-11D4-97FA-0080C882687E} (Win7)
1376 static const GUID kMicrosoftNewChangJieGUID
= {
1380 {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1381 if (mActiveTIPGUID
== kMicrosoftNewChangJieGUID
) {
1382 return TextInputProcessorID::eMicrosoftNewChangJie
;
1384 // {B2F9C502-1742-11D4-9790-0080C882687E} (Win7)
1385 static const GUID kMicrosoftNewPhoneticGUID
= {
1389 {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1390 if (mActiveTIPGUID
== kMicrosoftNewPhoneticGUID
) {
1391 return TextInputProcessorID::eMicrosoftNewPhonetic
;
1393 // {0B883BA0-C1C7-11D4-87F9-0080C882687E} (Win7)
1394 static const GUID kMicrosoftNewQuickGUID
= {
1398 {0x87, 0xF9, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1399 if (mActiveTIPGUID
== kMicrosoftNewQuickGUID
) {
1400 return TextInputProcessorID::eMicrosoftNewQuick
;
1403 // NOTE: There are some other Traditional Chinese TIPs installed in Windows:
1404 // * Chinese Traditional Array (version 6.0)
1405 // - {D38EFF65-AA46-4FD5-91A7-67845FB02F5B} (Win7, Win8.1)
1406 // * Chinese Traditional DaYi (version 6.0)
1407 // - {037B2C25-480C-4D7F-B027-D6CA6B69788A} (Win7, Win8.1)
1409 // {B58630B5-0ED3-4335-BBC9-E77BBCB43CAD}
1410 static const GUID kFreeChangJieGUID
= {
1414 {0xBB, 0xC9, 0xE7, 0x7B, 0xBC, 0xB4, 0x3C, 0xAD}};
1415 if (mActiveTIPGUID
== kFreeChangJieGUID
) {
1416 return TextInputProcessorID::eFreeChangJie
;
1419 return TextInputProcessorID::eUnknown
;
1422 TextInputProcessorID
ComputeActiveTIPAsSimplifiedChinese() {
1423 // FYI: This matches with neither "Microsoft Pinyin ABC Input Style" nor
1424 // "Microsoft Pinyin New Experience Input Style" on Win7.
1425 // {FA550B04-5AD7-411F-A5AC-CA038EC515D7} (Win8.1, Win10)
1426 static const GUID kMicrosoftPinyinGUID
= {
1430 {0xA5, 0xAC, 0xCA, 0x03, 0x8E, 0xC5, 0x15, 0xD7}};
1431 if (mActiveTIPGUID
== kMicrosoftPinyinGUID
) {
1432 return TextInputProcessorID::eMicrosoftPinyin
;
1435 // {F3BA9077-6C7E-11D4-97FA-0080C882687E} (Win7)
1436 static const GUID kMicrosoftPinyinNewExperienceInputStyleGUID
= {
1440 {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1441 if (mActiveTIPGUID
== kMicrosoftPinyinNewExperienceInputStyleGUID
) {
1442 return TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle
;
1444 // {82590C13-F4DD-44F4-BA1D-8667246FDF8E} (Win8.1, Win10)
1445 static const GUID kMicrosoftWubiGUID
= {
1449 {0xBA, 0x1D, 0x86, 0x67, 0x24, 0x6F, 0xDF, 0x8E}};
1450 if (mActiveTIPGUID
== kMicrosoftWubiGUID
) {
1451 return TextInputProcessorID::eMicrosoftWubi
;
1453 // NOTE: There are some other Simplified Chinese TIPs installed in Windows:
1454 // * Chinese Simplified QuanPin (version 6.0)
1455 // - {54FC610E-6ABD-4685-9DDD-A130BDF1B170} (Win8.1)
1456 // * Chinese Simplified ZhengMa (version 6.0)
1457 // - {733B4D81-3BC3-4132-B91A-E9CDD5E2BFC9} (Win8.1)
1458 // * Chinese Simplified ShuangPin (version 6.0)
1459 // - {EF63706D-31C4-490E-9DBB-BD150ADC454B} (Win8.1)
1460 // * Microsoft Pinyin ABC Input Style
1461 // - {FCA121D2-8C6D-41FB-B2DE-A2AD110D4820} (Win7)
1462 return TextInputProcessorID::eUnknown
;
1465 TextInputProcessorID
ComputeActiveTIPAsKorean() {
1466 // {B5FE1F02-D5F2-4445-9C03-C568F23C99A1} (Win7, Win8.1, Win10)
1467 static const GUID kMicrosoftIMEForKoreanGUID
= {
1471 {0x9C, 0x03, 0xC5, 0x68, 0xF2, 0x3C, 0x99, 0xA1}};
1472 if (mActiveTIPGUID
== kMicrosoftIMEForKoreanGUID
) {
1473 return TextInputProcessorID::eMicrosoftIMEForKorean
;
1475 // {B60AF051-257A-46BC-B9D3-84DAD819BAFB} (Win8.1, Win10)
1476 static const GUID kMicrosoftOldHangulGUID
= {
1480 {0xB9, 0xD3, 0x84, 0xDA, 0xD8, 0x19, 0xBA, 0xFB}};
1481 if (mActiveTIPGUID
== kMicrosoftOldHangulGUID
) {
1482 return TextInputProcessorID::eMicrosoftOldHangul
;
1485 // NOTE: There is the other Korean TIP installed in Windows:
1486 // * Microsoft IME 2010
1487 // - {48878C45-93F9-4aaf-A6A1-272CD863C4F5} (Win7)
1489 return TextInputProcessorID::eUnknown
;
1492 public: // ITfInputProcessorProfileActivationSink
1493 STDMETHODIMP
OnActivated(DWORD
, LANGID
, REFCLSID
, REFGUID
, REFGUID
, HKL
,
1498 virtual ~TSFStaticSink() {}
1500 bool EnsureInitActiveTIPKeyboard();
1504 void GetTIPDescription(REFCLSID aTextService
, LANGID aLangID
,
1505 REFGUID aProfile
, nsAString
& aDescription
);
1506 bool IsTIPCategoryKeyboard(REFCLSID aTextService
, LANGID aLangID
,
1509 TextInputProcessorID mActiveTIP
;
1511 // Cookie of installing ITfInputProcessorProfileActivationSink
1512 DWORD mIPProfileCookie
;
1516 // True if current IME is implemented with IMM.
1518 // True if OnActivated() is already called
1519 bool mOnActivatedCalled
;
1521 RefPtr
<ITfThreadMgr
> mThreadMgr
;
1522 RefPtr
<ITfInputProcessorProfiles
> mInputProcessorProfiles
;
1524 // Active TIP keyboard's description. If active language profile isn't TIP,
1525 // i.e., IMM-IME or just a keyboard layout, this is empty.
1526 nsString mActiveTIPKeyboardDescription
;
1528 // Active TIP's GUID
1529 GUID mActiveTIPGUID
;
1531 static StaticRefPtr
<TSFStaticSink
> sInstance
;
1534 StaticRefPtr
<TSFStaticSink
> TSFStaticSink::sInstance
;
1536 TSFStaticSink::TSFStaticSink()
1537 : mActiveTIP(TextInputProcessorID::eNotComputed
),
1538 mIPProfileCookie(TF_INVALID_COOKIE
),
1541 mOnActivatedCalled(false),
1542 mActiveTIPGUID(GUID_NULL
) {}
1544 bool TSFStaticSink::Init(ITfThreadMgr
* aThreadMgr
,
1545 ITfInputProcessorProfiles
* aInputProcessorProfiles
) {
1546 MOZ_ASSERT(!mThreadMgr
&& !mInputProcessorProfiles
,
1547 "TSFStaticSink::Init() must be called only once");
1549 mThreadMgr
= aThreadMgr
;
1550 mInputProcessorProfiles
= aInputProcessorProfiles
;
1552 RefPtr
<ITfSource
> source
;
1554 mThreadMgr
->QueryInterface(IID_ITfSource
, getter_AddRefs(source
));
1556 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1557 ("0x%p TSFStaticSink::Init() FAILED to get ITfSource "
1558 "instance (0x%08X)",
1563 // NOTE: On Vista or later, Windows let us know activate IME changed only
1564 // with ITfInputProcessorProfileActivationSink.
1565 hr
= source
->AdviseSink(
1566 IID_ITfInputProcessorProfileActivationSink
,
1567 static_cast<ITfInputProcessorProfileActivationSink
*>(this),
1569 if (FAILED(hr
) || mIPProfileCookie
== TF_INVALID_COOKIE
) {
1570 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1571 ("0x%p TSFStaticSink::Init() FAILED to install "
1572 "ITfInputProcessorProfileActivationSink (0x%08X)",
1577 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1578 ("0x%p TSFStaticSink::Init(), "
1579 "mIPProfileCookie=0x%08X",
1580 this, mIPProfileCookie
));
1584 void TSFStaticSink::Destroy() {
1585 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1586 ("0x%p TSFStaticSink::Shutdown() "
1587 "mIPProfileCookie=0x%08X",
1588 this, mIPProfileCookie
));
1590 if (mIPProfileCookie
!= TF_INVALID_COOKIE
) {
1591 RefPtr
<ITfSource
> source
;
1593 mThreadMgr
->QueryInterface(IID_ITfSource
, getter_AddRefs(source
));
1595 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1596 ("0x%p TSFStaticSink::Shutdown() FAILED to get "
1597 "ITfSource instance (0x%08X)",
1600 hr
= source
->UnadviseSink(mIPProfileCookie
);
1602 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1603 ("0x%p TSFTextStore::Shutdown() FAILED to uninstall "
1604 "ITfInputProcessorProfileActivationSink (0x%08X)",
1610 mThreadMgr
= nullptr;
1611 mInputProcessorProfiles
= nullptr;
1615 TSFStaticSink::OnActivated(DWORD dwProfileType
, LANGID langid
, REFCLSID rclsid
,
1616 REFGUID catid
, REFGUID guidProfile
, HKL hkl
,
1618 if ((dwFlags
& TF_IPSINK_FLAG_ACTIVE
) &&
1619 (dwProfileType
== TF_PROFILETYPE_KEYBOARDLAYOUT
||
1620 catid
== GUID_TFCAT_TIP_KEYBOARD
)) {
1621 mOnActivatedCalled
= true;
1622 mActiveTIP
= TextInputProcessorID::eNotComputed
;
1623 mActiveTIPGUID
= guidProfile
;
1624 mLangID
= langid
& 0xFFFF;
1625 mIsIMM_IME
= IsIMM_IME(hkl
);
1626 GetTIPDescription(rclsid
, langid
, guidProfile
,
1627 mActiveTIPKeyboardDescription
);
1628 if (mActiveTIPGUID
!= GUID_NULL
) {
1629 // key should be "LocaleID|Description". Although GUID of the
1630 // profile is unique key since description may be localized for system
1631 // language, unfortunately, it's too long to record as key with its
1632 // description. Therefore, we should record only the description with
1633 // LocaleID because Microsoft IME may not include language information.
1634 // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
1636 key
.AppendPrintf("0x%04X|", mLangID
);
1637 nsAutoString
description(mActiveTIPKeyboardDescription
);
1638 static const uint32_t kMaxDescriptionLength
= 72 - key
.Length();
1639 if (description
.Length() > kMaxDescriptionLength
) {
1640 if (NS_IS_LOW_SURROGATE(description
[kMaxDescriptionLength
- 1]) &&
1641 NS_IS_HIGH_SURROGATE(description
[kMaxDescriptionLength
- 2])) {
1642 description
.Truncate(kMaxDescriptionLength
- 2);
1644 description
.Truncate(kMaxDescriptionLength
- 1);
1647 description
.Append(char16_t(0x2026));
1649 key
.Append(description
);
1650 Telemetry::ScalarSet(Telemetry::ScalarID::WIDGET_IME_NAME_ON_WINDOWS
, key
,
1653 // Notify IMEHandler of changing active keyboard layout.
1654 IMEHandler::OnKeyboardLayoutChanged();
1656 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1657 ("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), "
1658 "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
1659 "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
1660 "mActiveTIPDescription=\"%s\"",
1662 dwProfileType
== TF_PROFILETYPE_INPUTPROCESSOR
1663 ? "TF_PROFILETYPE_INPUTPROCESSOR"
1664 : dwProfileType
== TF_PROFILETYPE_KEYBOARDLAYOUT
1665 ? "TF_PROFILETYPE_KEYBOARDLAYOUT"
1667 dwProfileType
, langid
, GetCLSIDNameStr(rclsid
).get(),
1668 GetGUIDNameStr(catid
).get(), GetGUIDNameStr(guidProfile
).get(), hkl
,
1669 dwFlags
, GetBoolName(dwFlags
& TF_IPSINK_FLAG_ACTIVE
),
1670 GetBoolName(mIsIMM_IME
),
1671 NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription
).get()));
1675 bool TSFStaticSink::EnsureInitActiveTIPKeyboard() {
1676 if (mOnActivatedCalled
) {
1680 RefPtr
<ITfInputProcessorProfileMgr
> profileMgr
;
1681 HRESULT hr
= mInputProcessorProfiles
->QueryInterface(
1682 IID_ITfInputProcessorProfileMgr
, getter_AddRefs(profileMgr
));
1683 if (FAILED(hr
) || !profileMgr
) {
1684 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1685 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1686 "to get input processor profile manager, hr=0x%08X",
1691 TF_INPUTPROCESSORPROFILE profile
;
1692 hr
= profileMgr
->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD
, &profile
);
1693 if (hr
== S_FALSE
) {
1694 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1695 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1696 "to get active keyboard layout profile due to no active profile, "
1699 // XXX Should we call OnActivated() with arguments like non-TIP in this
1704 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1705 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1706 "to get active TIP keyboard, hr=0x%08X",
1711 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1712 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), "
1713 "calling OnActivated() manually...",
1715 OnActivated(profile
.dwProfileType
, profile
.langid
, profile
.clsid
,
1716 profile
.catid
, profile
.guidProfile
, ::GetKeyboardLayout(0),
1717 TF_IPSINK_FLAG_ACTIVE
);
1721 void TSFStaticSink::GetTIPDescription(REFCLSID aTextService
, LANGID aLangID
,
1723 nsAString
& aDescription
) {
1724 aDescription
.Truncate();
1726 if (aTextService
== CLSID_NULL
|| aProfile
== GUID_NULL
) {
1730 BSTR description
= nullptr;
1731 HRESULT hr
= mInputProcessorProfiles
->GetLanguageProfileDescription(
1732 aTextService
, aLangID
, aProfile
, &description
);
1734 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1735 ("0x%p TSFStaticSink::InitActiveTIPDescription() FAILED "
1736 "due to GetLanguageProfileDescription() failure, hr=0x%08X",
1741 if (description
&& description
[0]) {
1742 aDescription
.Assign(description
);
1744 ::SysFreeString(description
);
1747 bool TSFStaticSink::IsTIPCategoryKeyboard(REFCLSID aTextService
, LANGID aLangID
,
1749 if (aTextService
== CLSID_NULL
|| aProfile
== GUID_NULL
) {
1753 RefPtr
<IEnumTfLanguageProfiles
> enumLangProfiles
;
1754 HRESULT hr
= mInputProcessorProfiles
->EnumLanguageProfiles(
1755 aLangID
, getter_AddRefs(enumLangProfiles
));
1756 if (FAILED(hr
) || !enumLangProfiles
) {
1757 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1758 ("0x%p TSFStaticSink::IsTIPCategoryKeyboard(), FAILED "
1759 "to get language profiles enumerator, hr=0x%08X",
1764 TF_LANGUAGEPROFILE profile
;
1766 while (SUCCEEDED(enumLangProfiles
->Next(1, &profile
, &fetch
)) && fetch
) {
1767 // XXX We're not sure a profile is registered with two or more categories.
1768 if (profile
.clsid
== aTextService
&& profile
.guidProfile
== aProfile
&&
1769 profile
.catid
== GUID_TFCAT_TIP_KEYBOARD
) {
1776 /******************************************************************/
1778 /******************************************************************/
1780 class TSFPrefs final
{
1782 #define DECL_AND_IMPL_BOOL_PREF(aPref, aName, aDefaultValue) \
1783 static bool aName() { \
1784 static bool s##aName##Value = Preferences::GetBool(aPref, aDefaultValue); \
1785 return s##aName##Value; \
1788 DECL_AND_IMPL_BOOL_PREF("intl.ime.hack.set_input_scope_of_url_bar_to_default",
1789 ShouldSetInputScopeOfURLBarToDefault
, true)
1790 DECL_AND_IMPL_BOOL_PREF(
1791 "intl.tsf.hack.allow_to_stop_hacking_on_build_17643_or_later",
1792 AllowToStopHackingOnBuild17643OrLater
, false)
1793 DECL_AND_IMPL_BOOL_PREF("intl.tsf.hack.atok.create_native_caret",
1794 NeedToCreateNativeCaretForLegacyATOK
, true)
1795 DECL_AND_IMPL_BOOL_PREF(
1796 "intl.tsf.hack.atok.do_not_return_no_layout_error_of_composition_string",
1797 DoNotReturnNoLayoutErrorToATOKOfCompositionString
, true)
1798 DECL_AND_IMPL_BOOL_PREF(
1799 "intl.tsf.hack.japanist10."
1800 "do_not_return_no_layout_error_of_composition_string",
1801 DoNotReturnNoLayoutErrorToJapanist10OfCompositionString
, true)
1802 DECL_AND_IMPL_BOOL_PREF(
1803 "intl.tsf.hack.ms_simplified_chinese.do_not_return_no_layout_error",
1804 DoNotReturnNoLayoutErrorToMSSimplifiedTIP
, true)
1805 DECL_AND_IMPL_BOOL_PREF(
1806 "intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error",
1807 DoNotReturnNoLayoutErrorToMSTraditionalTIP
, true)
1808 DECL_AND_IMPL_BOOL_PREF(
1809 "intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error",
1810 DoNotReturnNoLayoutErrorToFreeChangJie
, true)
1811 DECL_AND_IMPL_BOOL_PREF(
1812 "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_first_"
1814 DoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar
, true)
1815 DECL_AND_IMPL_BOOL_PREF(
1816 "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_caret",
1817 DoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret
, true)
1818 DECL_AND_IMPL_BOOL_PREF(
1819 "intl.tsf.hack.ms_simplified_chinese.query_insert_result",
1820 NeedToHackQueryInsertForMSSimplifiedTIP
, true)
1821 DECL_AND_IMPL_BOOL_PREF(
1822 "intl.tsf.hack.ms_traditional_chinese.query_insert_result",
1823 NeedToHackQueryInsertForMSTraditionalTIP
, true)
1825 #undef DECL_AND_IMPL_BOOL_PREF
1828 /******************************************************************/
1830 /******************************************************************/
1832 StaticRefPtr
<ITfThreadMgr
> TSFTextStore::sThreadMgr
;
1833 StaticRefPtr
<ITfMessagePump
> TSFTextStore::sMessagePump
;
1834 StaticRefPtr
<ITfKeystrokeMgr
> TSFTextStore::sKeystrokeMgr
;
1835 StaticRefPtr
<ITfDisplayAttributeMgr
> TSFTextStore::sDisplayAttrMgr
;
1836 StaticRefPtr
<ITfCategoryMgr
> TSFTextStore::sCategoryMgr
;
1837 StaticRefPtr
<ITfCompartment
> TSFTextStore::sCompartmentForOpenClose
;
1838 StaticRefPtr
<ITfDocumentMgr
> TSFTextStore::sDisabledDocumentMgr
;
1839 StaticRefPtr
<ITfContext
> TSFTextStore::sDisabledContext
;
1840 StaticRefPtr
<ITfInputProcessorProfiles
> TSFTextStore::sInputProcessorProfiles
;
1841 StaticRefPtr
<TSFTextStore
> TSFTextStore::sEnabledTextStore
;
1842 const MSG
* TSFTextStore::sHandlingKeyMsg
= nullptr;
1843 DWORD
TSFTextStore::sClientId
= 0;
1844 bool TSFTextStore::sIsKeyboardEventDispatched
= false;
1846 #define TEXTSTORE_DEFAULT_VIEW (1)
1848 TSFTextStore::TSFTextStore()
1853 mHandlingKeyMessage(0),
1854 mContentForTSF(mComposition
, mSelectionForTSF
),
1855 mRequestedAttrValues(false),
1856 mIsRecordingActionsWithoutLock(false),
1857 mHasReturnedNoLayoutError(false),
1858 mWaitingQueryLayout(false),
1859 mPendingDestroy(false),
1860 mDeferClearingContentForTSF(false),
1861 mDeferNotifyingTSF(false),
1862 mDeferCommittingComposition(false),
1863 mDeferCancellingComposition(false),
1865 mBeingDestroyed(false) {
1866 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
1867 mRequestedAttrs
[i
] = false;
1870 // We hope that 5 or more actions don't occur at once.
1871 mPendingActions
.SetCapacity(5);
1873 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1874 ("0x%p TSFTextStore::TSFTextStore() SUCCEEDED", this));
1877 TSFTextStore::~TSFTextStore() {
1878 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1879 ("0x%p TSFTextStore instance is destroyed", this));
1882 bool TSFTextStore::Init(nsWindowBase
* aWidget
, const InputContext
& aContext
) {
1883 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1884 ("0x%p TSFTextStore::Init(aWidget=0x%p)", this, aWidget
));
1886 if (NS_WARN_IF(!aWidget
) || NS_WARN_IF(aWidget
->Destroyed())) {
1887 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1888 ("0x%p TSFTextStore::Init() FAILED due to being initialized with "
1895 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1896 ("0x%p TSFTextStore::Init() FAILED due to already initialized",
1902 if (NS_WARN_IF(!mWidget
)) {
1903 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1904 ("0x%p TSFTextStore::Init() FAILED "
1905 "due to aWidget is nullptr ",
1909 mDispatcher
= mWidget
->GetTextEventDispatcher();
1910 if (NS_WARN_IF(!mDispatcher
)) {
1911 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1912 ("0x%p TSFTextStore::Init() FAILED "
1913 "due to aWidget->GetTextEventDispatcher() failure",
1918 SetInputScope(aContext
.mHTMLInputType
, aContext
.mHTMLInputInputmode
,
1919 aContext
.mInPrivateBrowsing
);
1921 // Create document manager
1922 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
1923 RefPtr
<ITfDocumentMgr
> documentMgr
;
1924 HRESULT hr
= threadMgr
->CreateDocumentMgr(getter_AddRefs(documentMgr
));
1925 if (NS_WARN_IF(FAILED(hr
))) {
1926 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1927 ("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr "
1932 if (NS_WARN_IF(mDestroyed
)) {
1934 sTextStoreLog
, LogLevel::Error
,
1935 ("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr due to "
1936 "TextStore being destroyed during calling "
1937 "ITfThreadMgr::CreateDocumentMgr()",
1941 // Create context and add it to document manager
1942 RefPtr
<ITfContext
> context
;
1943 hr
= documentMgr
->CreateContext(sClientId
, 0,
1944 static_cast<ITextStoreACP
*>(this),
1945 getter_AddRefs(context
), &mEditCookie
);
1946 if (NS_WARN_IF(FAILED(hr
))) {
1947 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1948 ("0x%p TSFTextStore::Init() FAILED to create the context "
1953 if (NS_WARN_IF(mDestroyed
)) {
1954 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1955 ("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
1956 "TextStore being destroyed during calling "
1957 "ITfDocumentMgr::CreateContext()",
1962 hr
= documentMgr
->Push(context
);
1963 if (NS_WARN_IF(FAILED(hr
))) {
1964 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1965 ("0x%p TSFTextStore::Init() FAILED to push the context (0x%08X)",
1969 if (NS_WARN_IF(mDestroyed
)) {
1970 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1971 ("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
1972 "TextStore being destroyed during calling ITfDocumentMgr::Push()",
1974 documentMgr
->Pop(TF_POPF_ALL
);
1978 mDocumentMgr
= documentMgr
;
1981 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1982 ("0x%p TSFTextStore::Init() succeeded: "
1983 "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
1984 this, mDocumentMgr
.get(), mContext
.get(), mEditCookie
));
1989 void TSFTextStore::Destroy() {
1990 if (mBeingDestroyed
) {
1994 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1995 ("0x%p TSFTextStore::Destroy(), mLock=%s, "
1996 "mComposition.IsComposing()=%s, mHandlingKeyMessage=%u",
1997 this, GetLockFlagNameStr(mLock
).get(),
1998 GetBoolName(mComposition
.IsComposing()), mHandlingKeyMessage
));
2002 // Destroy native caret first because it's not directly related to TSF and
2003 // there may be another textstore which gets focus. So, we should avoid
2004 // to destroy caret after the new one recreates caret.
2005 IMEHandler::MaybeDestroyNativeCaret();
2008 mPendingDestroy
= true;
2012 AutoRestore
<bool> savedBeingDestroyed(mBeingDestroyed
);
2013 mBeingDestroyed
= true;
2015 // If there is composition, TSF keeps the composition even after the text
2016 // store destroyed. So, we should clear the composition here.
2017 if (mComposition
.IsComposing()) {
2018 CommitCompositionInternal(false);
2022 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2023 ("0x%p TSFTextStore::Destroy(), calling "
2024 "ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
2026 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
2027 sink
->OnLayoutChange(TS_LC_DESTROY
, TEXTSTORE_DEFAULT_VIEW
);
2030 // If this is called during handling a keydown or keyup message, we should
2031 // put off to release TSF objects until it completely finishes since
2032 // MS-IME for Japanese refers some objects without grabbing them.
2033 if (!mHandlingKeyMessage
) {
2034 ReleaseTSFObjects();
2037 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2038 ("0x%p TSFTextStore::Destroy() succeeded", this));
2041 void TSFTextStore::ReleaseTSFObjects() {
2042 MOZ_ASSERT(!mHandlingKeyMessage
);
2044 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2045 ("0x%p TSFTextStore::ReleaseTSFObjects()", this));
2049 RefPtr
<ITfDocumentMgr
> documentMgr
= mDocumentMgr
.forget();
2050 documentMgr
->Pop(TF_POPF_ALL
);
2054 mDispatcher
= nullptr;
2056 if (!mMouseTrackers
.IsEmpty()) {
2057 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2058 ("0x%p TSFTextStore::ReleaseTSFObjects(), "
2059 "removing a mouse tracker...",
2061 mMouseTrackers
.Clear();
2064 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2065 ("0x%p TSFTextStore::ReleaseTSFObjects() completed", this));
2069 TSFTextStore::QueryInterface(REFIID riid
, void** ppv
) {
2071 if ((IID_IUnknown
== riid
) || (IID_ITextStoreACP
== riid
)) {
2072 *ppv
= static_cast<ITextStoreACP
*>(this);
2073 } else if (IID_ITfContextOwnerCompositionSink
== riid
) {
2074 *ppv
= static_cast<ITfContextOwnerCompositionSink
*>(this);
2075 } else if (IID_ITfMouseTrackerACP
== riid
) {
2076 *ppv
= static_cast<ITfMouseTrackerACP
*>(this);
2083 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2084 ("0x%p TSFTextStore::QueryInterface() FAILED, riid=%s", this,
2085 GetRIIDNameStr(riid
).get()));
2086 return E_NOINTERFACE
;
2090 TSFTextStore::AdviseSink(REFIID riid
, IUnknown
* punk
, DWORD dwMask
) {
2092 sTextStoreLog
, LogLevel::Info
,
2093 ("0x%p TSFTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), "
2094 "mSink=0x%p, mSinkMask=%s",
2095 this, GetRIIDNameStr(riid
).get(), punk
, GetSinkMaskNameStr(dwMask
).get(),
2096 mSink
.get(), GetSinkMaskNameStr(mSinkMask
).get()));
2099 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2100 ("0x%p TSFTextStore::AdviseSink() FAILED due to the null punk",
2102 return E_UNEXPECTED
;
2105 if (IID_ITextStoreACPSink
!= riid
) {
2106 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2107 ("0x%p TSFTextStore::AdviseSink() FAILED due to "
2108 "unsupported interface",
2110 return E_INVALIDARG
; // means unsupported interface.
2115 punk
->QueryInterface(IID_ITextStoreACPSink
, getter_AddRefs(mSink
));
2117 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2118 ("0x%p TSFTextStore::AdviseSink() FAILED due to "
2119 "punk not having the interface",
2121 return E_UNEXPECTED
;
2124 // If sink is already installed we check to see if they are the same
2125 // Get IUnknown from both sides for comparison
2126 RefPtr
<IUnknown
> comparison1
, comparison2
;
2127 punk
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison1
));
2128 mSink
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison2
));
2129 if (comparison1
!= comparison2
) {
2130 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2131 ("0x%p TSFTextStore::AdviseSink() FAILED due to "
2132 "the sink being different from the stored sink",
2134 return CONNECT_E_ADVISELIMIT
;
2137 // Update mask either for a new sink or an existing sink
2143 TSFTextStore::UnadviseSink(IUnknown
* punk
) {
2144 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2145 ("0x%p TSFTextStore::UnadviseSink(punk=0x%p), mSink=0x%p", this, punk
,
2149 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2150 ("0x%p TSFTextStore::UnadviseSink() FAILED due to the null punk",
2152 return E_INVALIDARG
;
2155 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2156 ("0x%p TSFTextStore::UnadviseSink() FAILED due to "
2157 "any sink not stored",
2159 return CONNECT_E_NOCONNECTION
;
2161 // Get IUnknown from both sides for comparison
2162 RefPtr
<IUnknown
> comparison1
, comparison2
;
2163 punk
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison1
));
2164 mSink
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison2
));
2165 // Unadvise only if sinks are the same
2166 if (comparison1
!= comparison2
) {
2167 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2168 ("0x%p TSFTextStore::UnadviseSink() FAILED due to "
2169 "the sink being different from the stored sink",
2171 return CONNECT_E_NOCONNECTION
;
2179 TSFTextStore::RequestLock(DWORD dwLockFlags
, HRESULT
* phrSession
) {
2180 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2181 ("0x%p TSFTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), "
2182 "mLock=%s, mDestroyed=%s",
2183 this, GetLockFlagNameStr(dwLockFlags
).get(), phrSession
,
2184 GetLockFlagNameStr(mLock
).get(), GetBoolName(mDestroyed
)));
2187 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2188 ("0x%p TSFTextStore::RequestLock() FAILED due to "
2189 "any sink not stored",
2194 (!mContentForTSF
.IsInitialized() || mSelectionForTSF
.IsDirty())) {
2195 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2196 ("0x%p TSFTextStore::RequestLock() FAILED due to "
2197 "being destroyed and no information of the contents",
2202 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2203 ("0x%p TSFTextStore::RequestLock() FAILED due to "
2206 return E_INVALIDARG
;
2211 mLock
= dwLockFlags
& (~TS_LF_SYNC
);
2213 sTextStoreLog
, LogLevel::Info
,
2214 ("0x%p Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2215 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
2216 this, GetLockFlagNameStr(mLock
).get()));
2217 // Don't release this instance during this lock because this is called by
2218 // TSF but they don't grab us during this call.
2219 RefPtr
<TSFTextStore
> kungFuDeathGrip(this);
2220 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
2221 *phrSession
= sink
->OnLockGranted(mLock
);
2223 sTextStoreLog
, LogLevel::Info
,
2224 ("0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2225 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
2226 this, GetLockFlagNameStr(mLock
).get()));
2228 while (mLockQueued
) {
2229 mLock
= mLockQueued
;
2231 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2232 ("0x%p Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
2233 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2235 this, GetLockFlagNameStr(mLock
).get()));
2236 sink
->OnLockGranted(mLock
);
2237 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2238 ("0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2239 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2241 this, GetLockFlagNameStr(mLock
).get()));
2245 // The document is now completely unlocked.
2248 MaybeFlushPendingNotifications();
2250 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2251 ("0x%p TSFTextStore::RequestLock() succeeded: *phrSession=%s",
2252 this, GetTextStoreReturnValueName(*phrSession
)));
2256 // only time when reentrant lock is allowed is when caller holds a
2257 // read-only lock and is requesting an async write lock
2258 if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags
) &&
2259 !(dwLockFlags
& TS_LF_SYNC
)) {
2260 *phrSession
= TS_S_ASYNC
;
2261 mLockQueued
= dwLockFlags
& (~TS_LF_SYNC
);
2263 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2264 ("0x%p TSFTextStore::RequestLock() stores the request in the "
2265 "queue, *phrSession=TS_S_ASYNC",
2270 // no more locks allowed
2271 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2272 ("0x%p TSFTextStore::RequestLock() didn't allow to lock, "
2273 "*phrSession=TS_E_SYNCHRONOUS",
2275 *phrSession
= TS_E_SYNCHRONOUS
;
2279 void TSFTextStore::DidLockGranted() {
2280 if (IsReadWriteLocked()) {
2281 // FreeCJ (TIP for Traditional Chinese) calls SetSelection() to set caret
2282 // to the start of composition string and insert a full width space for
2283 // a placeholder with a call of SetText(). After that, it calls
2284 // OnUpdateComposition() without new range. Therefore, let's record the
2285 // composition update information here.
2286 CompleteLastActionIfStillIncomplete();
2288 FlushPendingActions();
2291 // If the widget has gone, we don't need to notify anything.
2292 if (mDestroyed
|| !mWidget
|| mWidget
->Destroyed()) {
2293 mPendingSelectionChangeData
.Clear();
2294 mHasReturnedNoLayoutError
= false;
2298 void TSFTextStore::DispatchEvent(WidgetGUIEvent
& aEvent
) {
2299 if (NS_WARN_IF(!mWidget
) || NS_WARN_IF(mWidget
->Destroyed())) {
2302 // If the event isn't a query content event, the event may be handled
2303 // asynchronously. So, we should put off to answer from GetTextExt() etc.
2304 if (!aEvent
.AsQueryContentEvent()) {
2305 mDeferNotifyingTSF
= true;
2307 mWidget
->DispatchWindowEvent(&aEvent
);
2310 void TSFTextStore::FlushPendingActions() {
2311 if (!mWidget
|| mWidget
->Destroyed()) {
2312 // Note that don't clear mContentForTSF because TIP may try to commit
2313 // composition with a document lock. In such case, TSFTextStore needs to
2314 // behave as expected by TIP.
2315 mPendingActions
.Clear();
2316 mPendingSelectionChangeData
.Clear();
2317 mHasReturnedNoLayoutError
= false;
2321 // Some TIP may request lock but does nothing during the lock. In such case,
2322 // this should do nothing. For example, when MS-IME for Japanese is active
2323 // and we're inactivating, this case occurs and causes different behavior
2324 // from the other TIPs.
2325 if (mPendingActions
.IsEmpty()) {
2329 RefPtr
<nsWindowBase
> widget(mWidget
);
2330 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2331 if (NS_WARN_IF(NS_FAILED(rv
))) {
2332 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2333 ("0x%p TSFTextStore::FlushPendingActions() "
2334 "FAILED due to BeginNativeInputTransaction() failure",
2338 for (uint32_t i
= 0; i
< mPendingActions
.Length(); i
++) {
2339 PendingAction
& action
= mPendingActions
[i
];
2340 switch (action
.mType
) {
2341 case PendingAction::Type::eKeyboardEvent
:
2344 sTextStoreLog
, LogLevel::Warning
,
2345 ("0x%p TSFTextStore::FlushPendingActions() "
2346 "IGNORED pending KeyboardEvent(%s) due to already destroyed",
2347 action
.mKeyMsg
->message
== WM_KEYDOWN
? "eKeyDown" : "eKeyUp",
2350 MOZ_DIAGNOSTIC_ASSERT(action
.mKeyMsg
);
2351 DispatchKeyboardEventAsProcessedByIME(*action
.mKeyMsg
);
2352 if (!widget
|| widget
->Destroyed()) {
2356 case PendingAction::Type::eCompositionStart
: {
2357 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2358 ("0x%p TSFTextStore::FlushPendingActions() "
2359 "flushing Type::eCompositionStart={ mSelectionStart=%d, "
2360 "mSelectionLength=%d }, mDestroyed=%s",
2361 this, action
.mSelectionStart
, action
.mSelectionLength
,
2362 GetBoolName(mDestroyed
)));
2365 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2366 ("0x%p TSFTextStore::FlushPendingActions() "
2367 "IGNORED pending compositionstart due to already destroyed",
2372 if (action
.mAdjustSelection
) {
2373 // Select composition range so the new composition replaces the range
2374 WidgetSelectionEvent
selectionSet(true, eSetSelection
, widget
);
2375 widget
->InitEvent(selectionSet
);
2376 selectionSet
.mOffset
= static_cast<uint32_t>(action
.mSelectionStart
);
2377 selectionSet
.mLength
= static_cast<uint32_t>(action
.mSelectionLength
);
2378 selectionSet
.mReversed
= false;
2379 DispatchEvent(selectionSet
);
2380 if (!selectionSet
.mSucceeded
) {
2381 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2382 ("0x%p TSFTextStore::FlushPendingActions() "
2383 "FAILED due to eSetSelection failure",
2389 // eCompositionStart always causes
2390 // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. Therefore, we should
2391 // wait to clear mContentForTSF until it's notified.
2392 mDeferClearingContentForTSF
= true;
2394 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2395 ("0x%p TSFTextStore::FlushPendingActions() "
2396 "dispatching compositionstart event...",
2398 WidgetEventTime eventTime
= widget
->CurrentMessageWidgetEventTime();
2399 nsEventStatus status
;
2400 rv
= mDispatcher
->StartComposition(status
, &eventTime
);
2401 if (NS_WARN_IF(NS_FAILED(rv
))) {
2402 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2403 ("0x%p TSFTextStore::FlushPendingActions() "
2404 "FAILED to dispatch compositionstart event, "
2405 "IsHandlingComposition()=%s",
2406 this, GetBoolName(IsHandlingComposition())));
2407 // XXX Is this right? If there is a composition in content,
2408 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2409 mDeferClearingContentForTSF
= !IsHandlingComposition();
2411 if (!widget
|| widget
->Destroyed()) {
2416 case PendingAction::Type::eCompositionUpdate
: {
2417 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2418 ("0x%p TSFTextStore::FlushPendingActions() "
2419 "flushing Type::eCompositionUpdate={ mData=\"%s\", "
2420 "mRanges=0x%p, mRanges->Length()=%d }",
2421 this, GetEscapedUTF8String(action
.mData
).get(),
2422 action
.mRanges
.get(),
2423 action
.mRanges
? action
.mRanges
->Length() : 0));
2425 // eCompositionChange causes a DOM text event, the IME will be notified
2426 // of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. In this case, we
2427 // should not clear mContentForTSF until we notify the IME of the
2428 // composition update.
2429 mDeferClearingContentForTSF
= true;
2431 rv
= mDispatcher
->SetPendingComposition(action
.mData
, action
.mRanges
);
2432 if (NS_WARN_IF(NS_FAILED(rv
))) {
2433 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2434 ("0x%p TSFTextStore::FlushPendingActions() "
2435 "FAILED to setting pending composition... "
2436 "IsHandlingComposition()=%s",
2437 this, GetBoolName(IsHandlingComposition())));
2438 // XXX Is this right? If there is a composition in content,
2439 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2440 mDeferClearingContentForTSF
= !IsHandlingComposition();
2442 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2443 ("0x%p TSFTextStore::FlushPendingActions() "
2444 "dispatching compositionchange event...",
2446 WidgetEventTime eventTime
= widget
->CurrentMessageWidgetEventTime();
2447 nsEventStatus status
;
2448 rv
= mDispatcher
->FlushPendingComposition(status
, &eventTime
);
2449 if (NS_WARN_IF(NS_FAILED(rv
))) {
2450 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2451 ("0x%p TSFTextStore::FlushPendingActions() "
2452 "FAILED to dispatch compositionchange event, "
2453 "IsHandlingComposition()=%s",
2454 this, GetBoolName(IsHandlingComposition())));
2455 // XXX Is this right? If there is a composition in content,
2456 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2457 mDeferClearingContentForTSF
= !IsHandlingComposition();
2459 // Be aware, the mWidget might already have been destroyed.
2463 case PendingAction::Type::eCompositionEnd
: {
2464 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2465 ("0x%p TSFTextStore::FlushPendingActions() "
2466 "flushing Type::eCompositionEnd={ mData=\"%s\" }",
2467 this, GetEscapedUTF8String(action
.mData
).get()));
2469 // Dispatching eCompositionCommit causes a DOM text event, then,
2470 // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
2471 // when focused content actually handles the event. For example,
2472 // when focused content is in a remote process, it's sent when
2473 // all dispatched composition events have been handled in the remote
2474 // process. So, until then, we don't have newer content information.
2475 // Therefore, we need to put off to clear mContentForTSF.
2476 mDeferClearingContentForTSF
= true;
2478 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2479 ("0x%p TSFTextStore::FlushPendingActions(), "
2480 "dispatching compositioncommit event...",
2482 WidgetEventTime eventTime
= widget
->CurrentMessageWidgetEventTime();
2483 nsEventStatus status
;
2484 rv
= mDispatcher
->CommitComposition(status
, &action
.mData
, &eventTime
);
2485 if (NS_WARN_IF(NS_FAILED(rv
))) {
2486 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2487 ("0x%p TSFTextStore::FlushPendingActions() "
2488 "FAILED to dispatch compositioncommit event, "
2489 "IsHandlingComposition()=%s",
2490 this, GetBoolName(IsHandlingComposition())));
2491 // XXX Is this right? If there is a composition in content,
2492 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2493 mDeferClearingContentForTSF
= !IsHandlingComposition();
2497 case PendingAction::Type::eSetSelection
: {
2499 sTextStoreLog
, LogLevel::Debug
,
2500 ("0x%p TSFTextStore::FlushPendingActions() "
2501 "flushing Type::eSetSelection={ mSelectionStart=%d, "
2502 "mSelectionLength=%d, mSelectionReversed=%s }, "
2504 this, action
.mSelectionStart
, action
.mSelectionLength
,
2505 GetBoolName(action
.mSelectionReversed
), GetBoolName(mDestroyed
)));
2508 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2509 ("0x%p TSFTextStore::FlushPendingActions() "
2510 "IGNORED pending selectionset due to already destroyed",
2515 WidgetSelectionEvent
selectionSet(true, eSetSelection
, widget
);
2516 selectionSet
.mOffset
= static_cast<uint32_t>(action
.mSelectionStart
);
2517 selectionSet
.mLength
= static_cast<uint32_t>(action
.mSelectionLength
);
2518 selectionSet
.mReversed
= action
.mSelectionReversed
;
2519 DispatchEvent(selectionSet
);
2520 if (!selectionSet
.mSucceeded
) {
2521 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2522 ("0x%p TSFTextStore::FlushPendingActions() "
2523 "FAILED due to eSetSelection failure",
2530 MOZ_CRASH("unexpected action type");
2533 if (widget
&& !widget
->Destroyed()) {
2537 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2538 ("0x%p TSFTextStore::FlushPendingActions(), "
2539 "qutting since the mWidget has gone",
2543 mPendingActions
.Clear();
2546 void TSFTextStore::MaybeFlushPendingNotifications() {
2547 if (IsReadLocked()) {
2548 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2549 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2550 "putting off flushing pending notifications due to being the "
2551 "document locked...",
2556 if (mDeferCommittingComposition
) {
2557 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2558 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2559 "calling TSFTextStore::CommitCompositionInternal(false)...",
2561 mDeferCommittingComposition
= mDeferCancellingComposition
= false;
2562 CommitCompositionInternal(false);
2563 } else if (mDeferCancellingComposition
) {
2564 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2565 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2566 "calling TSFTextStore::CommitCompositionInternal(true)...",
2568 mDeferCommittingComposition
= mDeferCancellingComposition
= false;
2569 CommitCompositionInternal(true);
2572 if (mDeferNotifyingTSF
) {
2573 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2574 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2575 "putting off flushing pending notifications due to being "
2576 "dispatching events...",
2581 if (mPendingDestroy
) {
2587 // If it's already been destroyed completely, this shouldn't notify TSF of
2588 // anything anymore.
2589 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2590 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2591 "does nothing because this has already destroyed completely...",
2596 if (!mDeferClearingContentForTSF
&& mContentForTSF
.IsInitialized()) {
2597 mContentForTSF
.Clear();
2598 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2599 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2600 "mContentForTSF is cleared",
2604 // When there is no cached content, we can sync actual contents and TSF/TIP
2605 // expecting contents.
2606 RefPtr
<TSFTextStore
> kungFuDeathGrip
= this;
2607 Unused
<< kungFuDeathGrip
;
2608 if (!mContentForTSF
.IsInitialized()) {
2609 if (mPendingTextChangeData
.IsValid()) {
2610 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2611 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2612 "calling TSFTextStore::NotifyTSFOfTextChange()...",
2614 NotifyTSFOfTextChange();
2616 if (mPendingSelectionChangeData
.IsValid()) {
2617 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2618 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2619 "calling TSFTextStore::NotifyTSFOfSelectionChange()...",
2621 NotifyTSFOfSelectionChange();
2625 if (mHasReturnedNoLayoutError
) {
2626 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2627 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2628 "calling TSFTextStore::NotifyTSFOfLayoutChange()...",
2630 NotifyTSFOfLayoutChange();
2634 void TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME() {
2635 // If we've already been destroyed, we cannot do anything.
2638 sTextStoreLog
, LogLevel::Debug
,
2639 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2640 "does nothing because it's already been destroyed",
2645 // If we're not handling key message or we've already dispatched a keyboard
2646 // event for the handling key message, we should do nothing anymore.
2647 if (!sHandlingKeyMsg
|| sIsKeyboardEventDispatched
) {
2649 sTextStoreLog
, LogLevel::Debug
,
2650 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2651 "does nothing because not necessary to dispatch keyboard event",
2656 sIsKeyboardEventDispatched
= true;
2657 // If the document is locked, just adding the task to dispatching an event
2659 if (IsReadLocked()) {
2661 sTextStoreLog
, LogLevel::Debug
,
2662 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2663 "adding to dispatch a keyboard event into the queue...",
2665 PendingAction
* action
= mPendingActions
.AppendElement();
2666 action
->mType
= PendingAction::Type::eKeyboardEvent
;
2667 action
->mKeyMsg
= sHandlingKeyMsg
;
2671 // Otherwise, dispatch a keyboard event.
2672 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2673 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2674 "trying to dispatch a keyboard event...",
2676 DispatchKeyboardEventAsProcessedByIME(*sHandlingKeyMsg
);
2679 void TSFTextStore::DispatchKeyboardEventAsProcessedByIME(const MSG
& aMsg
) {
2680 MOZ_ASSERT(mWidget
);
2681 MOZ_ASSERT(!mWidget
->Destroyed());
2682 MOZ_ASSERT(!mDestroyed
);
2684 ModifierKeyState modKeyState
;
2686 msg
.wParam
= VK_PROCESSKEY
;
2687 NativeKey
nativeKey(mWidget
, msg
, modKeyState
);
2688 switch (aMsg
.message
) {
2690 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2691 ("0x%p TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2692 "dispatching an eKeyDown event...",
2694 nativeKey
.HandleKeyDownMessage();
2697 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2698 ("0x%p TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2699 "dispatching an eKeyUp event...",
2701 nativeKey
.HandleKeyUpMessage();
2704 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2705 ("0x%p TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2706 "ERROR, it doesn't handle the message",
2713 TSFTextStore::GetStatus(TS_STATUS
* pdcs
) {
2714 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2715 ("0x%p TSFTextStore::GetStatus(pdcs=0x%p)", this, pdcs
));
2718 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2719 ("0x%p TSFTextStore::GetStatus() FAILED due to null pdcs", this));
2720 return E_INVALIDARG
;
2722 pdcs
->dwDynamicFlags
= 0;
2723 // we use a "flat" text model for TSF support so no hidden text
2724 pdcs
->dwStaticFlags
= TS_SS_NOHIDDENTEXT
;
2729 TSFTextStore::QueryInsert(LONG acpTestStart
, LONG acpTestEnd
, ULONG cch
,
2730 LONG
* pacpResultStart
, LONG
* pacpResultEnd
) {
2731 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2732 ("0x%p TSFTextStore::QueryInsert(acpTestStart=%ld, "
2733 "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)",
2734 this, acpTestStart
, acpTestEnd
, cch
, acpTestStart
, acpTestEnd
));
2736 if (!pacpResultStart
|| !pacpResultEnd
) {
2737 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2738 ("0x%p TSFTextStore::QueryInsert() FAILED due to "
2739 "the null argument",
2741 return E_INVALIDARG
;
2744 if (acpTestStart
< 0 || acpTestStart
> acpTestEnd
) {
2745 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2746 ("0x%p TSFTextStore::QueryInsert() FAILED due to "
2749 return E_INVALIDARG
;
2752 // XXX need to adjust to cluster boundary
2753 // Assume we are given good offsets for now
2754 if (IsWin8OrLater() && !mComposition
.IsComposing() &&
2755 ((TSFPrefs::NeedToHackQueryInsertForMSTraditionalTIP() &&
2756 TSFStaticSink::IsMSChangJieOrMSQuickActive()) ||
2757 (TSFPrefs::NeedToHackQueryInsertForMSSimplifiedTIP() &&
2758 TSFStaticSink::IsMSPinyinOrMSWubiActive()))) {
2759 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2760 ("0x%p TSFTextStore::QueryInsert() WARNING using different "
2761 "result for the TIP",
2763 // Chinese TIPs of Microsoft assume that QueryInsert() returns selected
2764 // range which should be removed.
2765 *pacpResultStart
= acpTestStart
;
2766 *pacpResultEnd
= acpTestEnd
;
2768 *pacpResultStart
= acpTestStart
;
2769 *pacpResultEnd
= acpTestStart
+ cch
;
2772 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2773 ("0x%p TSFTextStore::QueryInsert() succeeded: "
2774 "*pacpResultStart=%ld, *pacpResultEnd=%ld)",
2775 this, *pacpResultStart
, *pacpResultEnd
));
2780 TSFTextStore::GetSelection(ULONG ulIndex
, ULONG ulCount
,
2781 TS_SELECTION_ACP
* pSelection
, ULONG
* pcFetched
) {
2782 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2783 ("0x%p TSFTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, "
2784 "pSelection=0x%p, pcFetched=0x%p)",
2785 this, ulIndex
, ulCount
, pSelection
, pcFetched
));
2787 if (!IsReadLocked()) {
2789 sTextStoreLog
, LogLevel::Error
,
2790 ("0x%p TSFTextStore::GetSelection() FAILED due to not locked", this));
2793 if (!ulCount
|| !pSelection
|| !pcFetched
) {
2794 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2795 ("0x%p TSFTextStore::GetSelection() FAILED due to "
2798 return E_INVALIDARG
;
2803 if (ulIndex
!= static_cast<ULONG
>(TS_DEFAULT_SELECTION
) && ulIndex
!= 0) {
2804 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2805 ("0x%p TSFTextStore::GetSelection() FAILED due to "
2806 "unsupported selection",
2808 return TS_E_NOSELECTION
;
2811 Selection
& selectionForTSF
= SelectionForTSFRef();
2812 if (selectionForTSF
.IsDirty()) {
2813 if (DoNotReturnErrorFromGetSelection()) {
2814 AutoSetTemporarySelection
temprarySetter(selectionForTSF
);
2815 *pSelection
= selectionForTSF
.ACP();
2818 sTextStoreLog
, LogLevel::Info
,
2819 ("0x%p TSFTextStore::GetSelection() returns fake selection range "
2820 "for avoiding a crash in TSF, "
2821 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2822 this, selectionForTSF
.StartOffset(), selectionForTSF
.EndOffset(),
2823 selectionForTSF
.Length(),
2824 GetBoolName(selectionForTSF
.IsReversed())));
2827 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2828 ("0x%p TSFTextStore::GetSelection() FAILED due to "
2829 "SelectionForTSFRef() failure",
2833 *pSelection
= selectionForTSF
.ACP();
2836 sTextStoreLog
, LogLevel::Info
,
2837 ("0x%p TSFTextStore::GetSelection() succeeded, "
2838 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2839 this, selectionForTSF
.StartOffset(), selectionForTSF
.EndOffset(),
2840 selectionForTSF
.Length(), GetBoolName(selectionForTSF
.IsReversed())));
2845 bool TSFTextStore::DoNotReturnErrorFromGetSelection() {
2846 // There is a crash bug of TSF if we return error from GetSelection().
2847 // That was introduced in Anniversary Update (build 14393, see bug 1312302)
2848 // TODO: We should avoid to run this hack on fixed builds. When we get
2849 // exact build number, we should get back here.
2850 static bool sTSFMayCrashIfGetSelectionReturnsError
=
2851 IsWindows10BuildOrLater(14393);
2852 return sTSFMayCrashIfGetSelectionReturnsError
;
2855 TSFTextStore::Content
& TSFTextStore::ContentForTSFRef() {
2856 // This should be called when the document is locked or the content hasn't
2857 // been abandoned yet.
2858 if (NS_WARN_IF(!IsReadLocked() && !mContentForTSF
.IsInitialized())) {
2859 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2860 ("0x%p TSFTextStore::ContentForTSFRef(), FAILED, due to "
2861 "called wrong timing, IsReadLocked()=%s, "
2862 "mContentForTSF.IsInitialized()=%s",
2863 this, GetBoolName(IsReadLocked()),
2864 GetBoolName(mContentForTSF
.IsInitialized())));
2865 mContentForTSF
.Clear();
2866 return mContentForTSF
;
2869 Selection
& selectionForTSF
= SelectionForTSFRef();
2870 if (selectionForTSF
.IsDirty()) {
2871 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2872 ("0x%p TSFTextStore::ContentForTSFRef(), FAILED, due to "
2873 "SelectionForTSFRef() failure",
2875 mContentForTSF
.Clear();
2876 return mContentForTSF
;
2879 if (!mContentForTSF
.IsInitialized()) {
2881 if (NS_WARN_IF(!GetCurrentText(text
))) {
2882 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2883 ("0x%p TSFTextStore::ContentForTSFRef(), FAILED, due to "
2884 "GetCurrentText() failure",
2886 mContentForTSF
.Clear();
2887 return mContentForTSF
;
2890 mContentForTSF
.Init(text
);
2891 // Basically, the cached content which is expected by TSF/TIP should be
2892 // cleared after active composition is committed or the document lock is
2893 // unlocked. However, in e10s mode, content will be modified
2894 // asynchronously. In such case, mDeferClearingContentForTSF may be
2895 // true until whole dispatched events are handled by the focused editor.
2896 mDeferClearingContentForTSF
= false;
2899 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2900 ("0x%p TSFTextStore::ContentForTSFRef(): "
2901 "mContentForTSF={ mText=\"%s\" (Length()=%u), "
2902 "mLastCompositionString=\"%s\" (Length()=%u), "
2903 "mMinTextModifiedOffset=%u }",
2905 mContentForTSF
.Text().Length() <= 40
2906 ? GetEscapedUTF8String(mContentForTSF
.Text()).get()
2908 mContentForTSF
.Text().Length(),
2909 GetEscapedUTF8String(mContentForTSF
.LastCompositionString()).get(),
2910 mContentForTSF
.LastCompositionString().Length(),
2911 mContentForTSF
.MinTextModifiedOffset()));
2913 return mContentForTSF
;
2916 bool TSFTextStore::CanAccessActualContentDirectly() const {
2917 if (!mContentForTSF
.IsInitialized() || mSelectionForTSF
.IsDirty()) {
2921 // If the cached content has been changed by something except composition,
2922 // the content cache may be different from actual content.
2923 if (mPendingTextChangeData
.IsValid() &&
2924 !mPendingTextChangeData
.mCausedOnlyByComposition
) {
2928 // If the cached selection isn't changed, cached content and actual content
2930 if (!mPendingSelectionChangeData
.IsValid()) {
2934 return mSelectionForTSF
.EqualsExceptDirection(mPendingSelectionChangeData
);
2937 bool TSFTextStore::GetCurrentText(nsAString
& aTextContent
) {
2938 if (mContentForTSF
.IsInitialized()) {
2939 aTextContent
= mContentForTSF
.Text();
2943 MOZ_ASSERT(!mDestroyed
);
2944 MOZ_ASSERT(mWidget
&& !mWidget
->Destroyed());
2946 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2947 ("0x%p TSFTextStore::GetCurrentText(): "
2948 "retrieving text from the content...",
2951 WidgetQueryContentEvent
queryText(true, eQueryTextContent
, mWidget
);
2952 queryText
.InitForQueryTextContent(0, UINT32_MAX
);
2953 mWidget
->InitEvent(queryText
);
2954 DispatchEvent(queryText
);
2955 if (NS_WARN_IF(!queryText
.mSucceeded
)) {
2956 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2957 ("0x%p TSFTextStore::GetCurrentText(), FAILED, due to "
2958 "eQueryTextContent failure",
2960 aTextContent
.Truncate();
2964 aTextContent
= queryText
.mReply
.mString
;
2968 TSFTextStore::Selection
& TSFTextStore::SelectionForTSFRef() {
2969 if (mSelectionForTSF
.IsDirty()) {
2970 MOZ_ASSERT(!mDestroyed
);
2971 // If the window has never been available, we should crash since working
2972 // with broken values may make TIP confused.
2973 if (!mWidget
|| mWidget
->Destroyed()) {
2977 WidgetQueryContentEvent
querySelection(true, eQuerySelectedText
, mWidget
);
2978 mWidget
->InitEvent(querySelection
);
2979 DispatchEvent(querySelection
);
2980 if (NS_WARN_IF(!querySelection
.mSucceeded
)) {
2981 return mSelectionForTSF
;
2984 mSelectionForTSF
.SetSelection(
2985 querySelection
.mReply
.mOffset
, querySelection
.mReply
.mString
.Length(),
2986 querySelection
.mReply
.mReversed
, querySelection
.GetWritingMode());
2990 sTextStoreLog
, LogLevel::Debug
,
2991 ("0x%p TSFTextStore::SelectionForTSFRef(): "
2992 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2993 this, mSelectionForTSF
.StartOffset(), mSelectionForTSF
.EndOffset(),
2994 mSelectionForTSF
.Length(), GetBoolName(mSelectionForTSF
.IsReversed())));
2996 return mSelectionForTSF
;
2999 static HRESULT
GetRangeExtent(ITfRange
* aRange
, LONG
* aStart
, LONG
* aLength
) {
3000 RefPtr
<ITfRangeACP
> rangeACP
;
3001 aRange
->QueryInterface(IID_ITfRangeACP
, getter_AddRefs(rangeACP
));
3002 NS_ENSURE_TRUE(rangeACP
, E_FAIL
);
3003 return rangeACP
->GetExtent(aStart
, aLength
);
3006 static TextRangeType
GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE
& aDisplayAttr
) {
3007 switch (aDisplayAttr
.bAttr
) {
3008 case TF_ATTR_TARGET_CONVERTED
:
3009 return TextRangeType::eSelectedClause
;
3010 case TF_ATTR_CONVERTED
:
3011 return TextRangeType::eConvertedClause
;
3012 case TF_ATTR_TARGET_NOTCONVERTED
:
3013 return TextRangeType::eSelectedRawClause
;
3015 return TextRangeType::eRawClause
;
3020 TSFTextStore::GetDisplayAttribute(ITfProperty
* aAttrProperty
, ITfRange
* aRange
,
3021 TF_DISPLAYATTRIBUTE
* aResult
) {
3022 NS_ENSURE_TRUE(aAttrProperty
, E_FAIL
);
3023 NS_ENSURE_TRUE(aRange
, E_FAIL
);
3024 NS_ENSURE_TRUE(aResult
, E_FAIL
);
3028 if (MOZ_LOG_TEST(sTextStoreLog
, LogLevel::Debug
)) {
3029 LONG start
= 0, length
= 0;
3030 hr
= GetRangeExtent(aRange
, &start
, &length
);
3032 sTextStoreLog
, LogLevel::Debug
,
3033 ("0x%p TSFTextStore::GetDisplayAttribute(): "
3034 "GetDisplayAttribute range=%ld-%ld (hr=%s)",
3035 this, start
- mComposition
.mStart
,
3036 start
- mComposition
.mStart
+ length
, GetCommonReturnValueName(hr
)));
3040 ::VariantInit(&propValue
);
3041 hr
= aAttrProperty
->GetValue(TfEditCookie(mEditCookie
), aRange
, &propValue
);
3043 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3044 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3045 "ITfProperty::GetValue() failed",
3049 if (VT_I4
!= propValue
.vt
) {
3050 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3051 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3052 "ITfProperty::GetValue() returns non-VT_I4 value",
3054 ::VariantClear(&propValue
);
3058 RefPtr
<ITfCategoryMgr
> categoryMgr
= GetCategoryMgr();
3059 if (NS_WARN_IF(!categoryMgr
)) {
3063 hr
= categoryMgr
->GetGUID(DWORD(propValue
.lVal
), &guid
);
3064 ::VariantClear(&propValue
);
3066 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3067 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3068 "ITfCategoryMgr::GetGUID() failed",
3073 RefPtr
<ITfDisplayAttributeMgr
> displayAttrMgr
= GetDisplayAttributeMgr();
3074 if (NS_WARN_IF(!displayAttrMgr
)) {
3077 RefPtr
<ITfDisplayAttributeInfo
> info
;
3078 hr
= displayAttrMgr
->GetDisplayAttributeInfo(guid
, getter_AddRefs(info
),
3080 if (FAILED(hr
) || !info
) {
3081 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3082 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3083 "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed",
3088 hr
= info
->GetAttributeInfo(aResult
);
3090 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3091 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3092 "ITfDisplayAttributeInfo::GetAttributeInfo() failed",
3097 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3098 ("0x%p TSFTextStore::GetDisplayAttribute() succeeded: "
3100 this, GetDisplayAttrStr(*aResult
).get()));
3105 TSFTextStore::RestartCompositionIfNecessary(ITfRange
* aRangeNew
) {
3106 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3107 ("0x%p TSFTextStore::RestartCompositionIfNecessary("
3108 "aRangeNew=0x%p), mComposition.mView=0x%p",
3109 this, aRangeNew
, mComposition
.mView
.get()));
3111 if (!mComposition
.IsComposing()) {
3112 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3113 ("0x%p TSFTextStore::RestartCompositionIfNecessary() FAILED "
3114 "due to no composition view",
3120 RefPtr
<ITfCompositionView
> pComposition(mComposition
.mView
);
3121 RefPtr
<ITfRange
> composingRange(aRangeNew
);
3122 if (!composingRange
) {
3123 hr
= pComposition
->GetRange(getter_AddRefs(composingRange
));
3125 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3126 ("0x%p TSFTextStore::RestartCompositionIfNecessary() "
3127 "FAILED due to pComposition->GetRange() failure",
3133 // Get starting offset of the composition
3134 LONG compStart
= 0, compLength
= 0;
3135 hr
= GetRangeExtent(composingRange
, &compStart
, &compLength
);
3137 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3138 ("0x%p TSFTextStore::RestartCompositionIfNecessary() FAILED "
3139 "due to GetRangeExtent() failure",
3144 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3145 ("0x%p TSFTextStore::RestartCompositionIfNecessary(), "
3146 "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }",
3147 this, compStart
, compStart
+ compLength
, mComposition
.mStart
,
3148 mComposition
.mString
.Length()));
3150 if (mComposition
.mStart
!= compStart
||
3151 mComposition
.mString
.Length() != (ULONG
)compLength
) {
3152 // If the queried composition length is different from the length
3153 // of our composition string, OnUpdateComposition is being called
3154 // because a part of the original composition was committed.
3155 hr
= RestartComposition(pComposition
, composingRange
);
3157 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3158 ("0x%p TSFTextStore::RestartCompositionIfNecessary() "
3159 "FAILED due to RestartComposition() failure",
3166 sTextStoreLog
, LogLevel::Debug
,
3167 ("0x%p TSFTextStore::RestartCompositionIfNecessary() succeeded", this));
3172 TSFTextStore::RestartComposition(ITfCompositionView
* aCompositionView
,
3173 ITfRange
* aNewRange
) {
3174 Selection
& selectionForTSF
= SelectionForTSFRef();
3176 LONG newStart
, newLength
;
3177 HRESULT hr
= GetRangeExtent(aNewRange
, &newStart
, &newLength
);
3178 LONG newEnd
= newStart
+ newLength
;
3180 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3181 ("0x%p TSFTextStore::RestartComposition(aCompositionView=0x%p, "
3182 "aNewRange=0x%p { newStart=%d, newLength=%d }), "
3183 "mComposition={ mStart=%d, mCompositionString.Length()=%d }, "
3184 "selectionForTSF={ IsDirty()=%s, StartOffset()=%d, Length()=%d }",
3185 this, aCompositionView
, aNewRange
, newStart
, newLength
,
3186 mComposition
.mStart
, mComposition
.mString
.Length(),
3187 GetBoolName(selectionForTSF
.IsDirty()),
3188 selectionForTSF
.StartOffset(), selectionForTSF
.Length()));
3190 if (selectionForTSF
.IsDirty()) {
3191 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3192 ("0x%p TSFTextStore::RestartComposition() FAILED "
3193 "due to SelectionForTSFRef() failure",
3199 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3200 ("0x%p TSFTextStore::RestartComposition() FAILED "
3201 "due to GetRangeExtent() failure",
3206 // If the new range has no overlap with the crrent range, we just commit
3207 // the composition and restart new composition with the new range but
3208 // current selection range should be preserved.
3209 if (newStart
>= mComposition
.EndOffset() || newEnd
<= mComposition
.mStart
) {
3210 RecordCompositionEndAction();
3211 RecordCompositionStartAction(aCompositionView
, newStart
, newLength
, true);
3215 // If the new range has an overlap with the current one, we should not commit
3216 // the whole current range to avoid creating an odd undo transaction.
3217 // I.e., the overlapped range which is being composed should not appear in
3218 // undo transaction.
3220 // Backup current composition data and selection data.
3221 Composition oldComposition
= mComposition
;
3222 Selection oldSelection
= selectionForTSF
;
3224 // Commit only the part of composition.
3225 LONG keepComposingStartOffset
= std::max(mComposition
.mStart
, newStart
);
3226 LONG keepComposingEndOffset
= std::min(mComposition
.EndOffset(), newEnd
);
3228 keepComposingStartOffset
<= keepComposingEndOffset
,
3229 "Why keepComposingEndOffset is smaller than keepComposingStartOffset?");
3230 LONG keepComposingLength
= keepComposingEndOffset
- keepComposingStartOffset
;
3231 // Remove the overlapped part from the commit string.
3232 nsAutoString
commitString(mComposition
.mString
);
3233 commitString
.Cut(keepComposingStartOffset
- mComposition
.mStart
,
3234 keepComposingLength
);
3235 // Update the composition string.
3236 Content
& contentForTSF
= ContentForTSFRef();
3237 if (!contentForTSF
.IsInitialized()) {
3238 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3239 ("0x%p TSFTextStore::RestartComposition() FAILED "
3240 "due to ContentForTSFRef() failure",
3244 contentForTSF
.ReplaceTextWith(mComposition
.mStart
,
3245 mComposition
.mString
.Length(), commitString
);
3246 // Record a compositionupdate action for commit the part of composing string.
3247 PendingAction
* action
= LastOrNewPendingCompositionUpdate();
3248 action
->mData
= mComposition
.mString
;
3249 action
->mRanges
->Clear();
3250 // Note that we shouldn't append ranges when composition string
3251 // is empty because it may cause TextComposition confused.
3252 if (!action
->mData
.IsEmpty()) {
3253 TextRange caretRange
;
3254 caretRange
.mStartOffset
= caretRange
.mEndOffset
=
3255 uint32_t(oldComposition
.mStart
+ commitString
.Length());
3256 caretRange
.mRangeType
= TextRangeType::eCaret
;
3257 action
->mRanges
->AppendElement(caretRange
);
3259 action
->mIncomplete
= false;
3261 // Record compositionend action.
3262 RecordCompositionEndAction();
3264 // Record compositionstart action only with the new start since this method
3265 // hasn't restored composing string yet.
3266 RecordCompositionStartAction(aCompositionView
, newStart
, 0, false);
3268 // Restore the latest text content and selection.
3269 contentForTSF
.ReplaceSelectedTextWith(nsDependentSubstring(
3270 oldComposition
.mString
, keepComposingStartOffset
- oldComposition
.mStart
,
3271 keepComposingLength
));
3272 selectionForTSF
= oldSelection
;
3274 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3275 ("0x%p TSFTextStore::RestartComposition() succeeded, "
3276 "mComposition={ mStart=%d, mCompositionString.Length()=%d }, "
3277 "selectionForTSF={ IsDirty()=%s, StartOffset()=%d, Length()=%d }",
3278 this, mComposition
.mStart
, mComposition
.mString
.Length(),
3279 GetBoolName(selectionForTSF
.IsDirty()),
3280 selectionForTSF
.StartOffset(), selectionForTSF
.Length()));
3285 static bool GetColor(const TF_DA_COLOR
& aTSFColor
, nscolor
& aResult
) {
3286 switch (aTSFColor
.type
) {
3287 case TF_CT_SYSCOLOR
: {
3288 DWORD sysColor
= ::GetSysColor(aTSFColor
.nIndex
);
3290 NS_RGB(GetRValue(sysColor
), GetGValue(sysColor
), GetBValue(sysColor
));
3293 case TF_CT_COLORREF
:
3294 aResult
= NS_RGB(GetRValue(aTSFColor
.cr
), GetGValue(aTSFColor
.cr
),
3295 GetBValue(aTSFColor
.cr
));
3303 static bool GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle
,
3304 TextRangeStyle::LineStyle
& aTextRangeLineStyle
) {
3305 switch (aTSFLineStyle
) {
3307 aTextRangeLineStyle
= TextRangeStyle::LineStyle::None
;
3310 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Solid
;
3313 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Dotted
;
3316 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Dashed
;
3318 case TF_LS_SQUIGGLE
:
3319 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Wavy
;
3327 TSFTextStore::RecordCompositionUpdateAction() {
3328 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3329 ("0x%p TSFTextStore::RecordCompositionUpdateAction(), "
3330 "mComposition={ mView=0x%p, mStart=%d, mString=\"%s\" "
3332 this, mComposition
.mView
.get(), mComposition
.mStart
,
3333 GetEscapedUTF8String(mComposition
.mString
).get(),
3334 mComposition
.mString
.Length()));
3336 if (!mComposition
.IsComposing()) {
3337 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3338 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3339 "due to no composition view",
3344 // Getting display attributes is *really* complicated!
3345 // We first get the context and the property objects to query for
3346 // attributes, but since a big range can have a variety of values for
3347 // the attribute, we have to find out all the ranges that have distinct
3348 // attribute values. Then we query for what the value represents through
3349 // the display attribute manager and translate that to TextRange to be
3350 // sent in eCompositionChange
3352 RefPtr
<ITfProperty
> attrPropetry
;
3354 mContext
->GetProperty(GUID_PROP_ATTRIBUTE
, getter_AddRefs(attrPropetry
));
3355 if (FAILED(hr
) || !attrPropetry
) {
3356 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3357 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3358 "due to mContext->GetProperty() failure",
3360 return FAILED(hr
) ? hr
: E_FAIL
;
3363 RefPtr
<ITfRange
> composingRange
;
3364 hr
= mComposition
.mView
->GetRange(getter_AddRefs(composingRange
));
3366 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3367 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3368 "FAILED due to mComposition.mView->GetRange() failure",
3373 RefPtr
<IEnumTfRanges
> enumRanges
;
3374 hr
= attrPropetry
->EnumRanges(TfEditCookie(mEditCookie
),
3375 getter_AddRefs(enumRanges
), composingRange
);
3376 if (FAILED(hr
) || !enumRanges
) {
3377 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3378 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3379 "due to attrPropetry->EnumRanges() failure",
3381 return FAILED(hr
) ? hr
: E_FAIL
;
3384 // First, put the log of content and selection here.
3385 Selection
& selectionForTSF
= SelectionForTSFRef();
3386 if (selectionForTSF
.IsDirty()) {
3387 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3388 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3389 "due to SelectionForTSFRef() failure",
3394 PendingAction
* action
= LastOrNewPendingCompositionUpdate();
3395 action
->mData
= mComposition
.mString
;
3396 // The ranges might already have been initialized, however, if this is
3397 // called again, that means we need to overwrite the ranges with current
3399 action
->mRanges
->Clear();
3401 // Note that we shouldn't append ranges when composition string
3402 // is empty because it may cause TextComposition confused.
3403 if (!action
->mData
.IsEmpty()) {
3405 // No matter if we have display attribute info or not,
3406 // we always pass in at least one range to eCompositionChange
3407 newRange
.mStartOffset
= 0;
3408 newRange
.mEndOffset
= action
->mData
.Length();
3409 newRange
.mRangeType
= TextRangeType::eRawClause
;
3410 action
->mRanges
->AppendElement(newRange
);
3412 RefPtr
<ITfRange
> range
;
3413 while (enumRanges
->Next(1, getter_AddRefs(range
), nullptr) == S_OK
) {
3414 if (NS_WARN_IF(!range
)) {
3418 LONG rangeStart
= 0, rangeLength
= 0;
3419 if (FAILED(GetRangeExtent(range
, &rangeStart
, &rangeLength
))) {
3422 // The range may include out of composition string. We should ignore
3423 // outside of the composition string.
3424 LONG start
= std::min(std::max(rangeStart
, mComposition
.mStart
),
3425 mComposition
.EndOffset());
3427 std::max(std::min(rangeStart
+ rangeLength
, mComposition
.EndOffset()),
3428 mComposition
.mStart
);
3429 LONG length
= end
- start
;
3431 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3432 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3433 "ignores invalid range (%d-%d)",
3434 this, rangeStart
- mComposition
.mStart
,
3435 rangeStart
- mComposition
.mStart
+ rangeLength
));
3439 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3440 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3441 "ignores a range due to outside of the composition or empty "
3443 this, rangeStart
- mComposition
.mStart
,
3444 rangeStart
- mComposition
.mStart
+ rangeLength
));
3449 newRange
.mStartOffset
= uint32_t(start
- mComposition
.mStart
);
3450 // The end of the last range in the array is
3451 // always kept at the end of composition
3452 newRange
.mEndOffset
= mComposition
.mString
.Length();
3454 TF_DISPLAYATTRIBUTE attr
;
3455 hr
= GetDisplayAttribute(attrPropetry
, range
, &attr
);
3457 newRange
.mRangeType
= TextRangeType::eRawClause
;
3459 newRange
.mRangeType
= GetGeckoSelectionValue(attr
);
3460 if (GetColor(attr
.crText
, newRange
.mRangeStyle
.mForegroundColor
)) {
3461 newRange
.mRangeStyle
.mDefinedStyles
|=
3462 TextRangeStyle::DEFINED_FOREGROUND_COLOR
;
3464 if (GetColor(attr
.crBk
, newRange
.mRangeStyle
.mBackgroundColor
)) {
3465 newRange
.mRangeStyle
.mDefinedStyles
|=
3466 TextRangeStyle::DEFINED_BACKGROUND_COLOR
;
3468 if (GetColor(attr
.crLine
, newRange
.mRangeStyle
.mUnderlineColor
)) {
3469 newRange
.mRangeStyle
.mDefinedStyles
|=
3470 TextRangeStyle::DEFINED_UNDERLINE_COLOR
;
3472 if (GetLineStyle(attr
.lsStyle
, newRange
.mRangeStyle
.mLineStyle
)) {
3473 newRange
.mRangeStyle
.mDefinedStyles
|=
3474 TextRangeStyle::DEFINED_LINESTYLE
;
3475 newRange
.mRangeStyle
.mIsBoldLine
= attr
.fBoldLine
!= 0;
3479 TextRange
& lastRange
= action
->mRanges
->LastElement();
3480 if (lastRange
.mStartOffset
== newRange
.mStartOffset
) {
3481 // Replace range if last range is the same as this one
3482 // So that ranges don't overlap and confuse the editor
3483 lastRange
= newRange
;
3485 lastRange
.mEndOffset
= newRange
.mStartOffset
;
3486 action
->mRanges
->AppendElement(newRange
);
3490 // We need to hack for Korean Input System which is Korean standard TIP.
3491 // It sets no change style to IME selection (the selection is always only
3492 // one). So, the composition string looks like normal (or committed)
3493 // string. At this time, current selection range is same as the
3494 // composition string range. Other applications set a wide caret which
3495 // covers the composition string, however, Gecko doesn't support the wide
3496 // caret drawing now (Gecko doesn't support XOR drawing), unfortunately.
3497 // For now, we should change the range style to undefined.
3498 if (!selectionForTSF
.IsCollapsed() && action
->mRanges
->Length() == 1) {
3499 TextRange
& range
= action
->mRanges
->ElementAt(0);
3500 LONG start
= selectionForTSF
.MinOffset();
3501 LONG end
= selectionForTSF
.MaxOffset();
3502 if ((LONG
)range
.mStartOffset
== start
- mComposition
.mStart
&&
3503 (LONG
)range
.mEndOffset
== end
- mComposition
.mStart
&&
3504 range
.mRangeStyle
.IsNoChangeStyle()) {
3505 range
.mRangeStyle
.Clear();
3506 // The looks of selected type is better than others.
3507 range
.mRangeType
= TextRangeType::eSelectedRawClause
;
3511 // The caret position has to be collapsed.
3512 uint32_t caretPosition
= static_cast<uint32_t>(selectionForTSF
.MaxOffset() -
3513 mComposition
.mStart
);
3515 // If caret is in the target clause and it doesn't have specific style,
3516 // the target clause will be painted as normal selection range. Since
3517 // caret shouldn't be in selection range on Windows, we shouldn't append
3518 // caret range in such case.
3519 const TextRange
* targetClause
= action
->mRanges
->GetTargetClause();
3520 if (!targetClause
|| targetClause
->mRangeStyle
.IsDefined() ||
3521 caretPosition
< targetClause
->mStartOffset
||
3522 caretPosition
> targetClause
->mEndOffset
) {
3523 TextRange caretRange
;
3524 caretRange
.mStartOffset
= caretRange
.mEndOffset
= caretPosition
;
3525 caretRange
.mRangeType
= TextRangeType::eCaret
;
3526 action
->mRanges
->AppendElement(caretRange
);
3530 action
->mIncomplete
= false;
3532 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3533 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3541 TSFTextStore::SetSelectionInternal(const TS_SELECTION_ACP
* pSelection
,
3542 bool aDispatchCompositionChangeEvent
) {
3543 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3544 ("0x%p TSFTextStore::SetSelectionInternal(pSelection={ "
3545 "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, "
3546 "aDispatchCompositionChangeEvent=%s), mComposition.IsComposing()=%s",
3547 this, pSelection
->acpStart
, pSelection
->acpEnd
,
3548 GetActiveSelEndName(pSelection
->style
.ase
),
3549 GetBoolName(pSelection
->style
.fInterimChar
),
3550 GetBoolName(aDispatchCompositionChangeEvent
),
3551 GetBoolName(mComposition
.IsComposing())));
3553 MOZ_ASSERT(IsReadWriteLocked());
3555 Selection
& selectionForTSF
= SelectionForTSFRef();
3556 if (selectionForTSF
.IsDirty()) {
3557 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3558 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3559 "SelectionForTSFRef() failure",
3564 MaybeDispatchKeyboardEventAsProcessedByIME();
3566 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3567 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3568 "destroyed during dispatching a keyboard event",
3573 // If actually the range is not changing, we should do nothing.
3574 // Perhaps, we can ignore the difference change because it must not be
3575 // important for following edit.
3576 if (selectionForTSF
.EqualsExceptDirection(*pSelection
)) {
3577 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
3578 ("0x%p TSFTextStore::SetSelectionInternal() Succeeded but "
3579 "did nothing because the selection range isn't changing",
3581 selectionForTSF
.SetSelection(*pSelection
);
3585 if (mComposition
.IsComposing()) {
3586 if (aDispatchCompositionChangeEvent
) {
3587 HRESULT hr
= RestartCompositionIfNecessary();
3589 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3590 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3591 "RestartCompositionIfNecessary() failure",
3596 if (pSelection
->acpStart
< mComposition
.mStart
||
3597 pSelection
->acpEnd
> mComposition
.EndOffset()) {
3598 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3599 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3600 "the selection being out of the composition string",
3602 return TS_E_INVALIDPOS
;
3604 // Emulate selection during compositions
3605 selectionForTSF
.SetSelection(*pSelection
);
3606 if (aDispatchCompositionChangeEvent
) {
3607 HRESULT hr
= RecordCompositionUpdateAction();
3609 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3610 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3611 "RecordCompositionUpdateAction() failure",
3619 TS_SELECTION_ACP
selectionInContent(*pSelection
);
3621 // If mContentForTSF caches old contents which is now different from
3622 // actual contents, we need some complicated hack here...
3623 // Note that this hack assumes that this is used for reconversion.
3624 if (mContentForTSF
.IsInitialized() && mPendingTextChangeData
.IsValid() &&
3625 !mPendingTextChangeData
.mCausedOnlyByComposition
) {
3626 uint32_t startOffset
= static_cast<uint32_t>(selectionInContent
.acpStart
);
3627 uint32_t endOffset
= static_cast<uint32_t>(selectionInContent
.acpEnd
);
3628 if (mPendingTextChangeData
.mStartOffset
>= endOffset
) {
3629 // Setting selection before any changed ranges is fine.
3630 } else if (mPendingTextChangeData
.mRemovedEndOffset
<= startOffset
) {
3631 // Setting selection after removed range is fine with following
3633 selectionInContent
.acpStart
+= mPendingTextChangeData
.Difference();
3634 selectionInContent
.acpEnd
+= mPendingTextChangeData
.Difference();
3635 } else if (startOffset
== endOffset
) {
3636 // Moving caret position may be fine in most cases even if the insertion
3637 // point has already gone but in this case, composition will be inserted
3638 // to unexpected position, though.
3639 // It seems that moving caret into middle of the new text is odd.
3640 // Perhaps, end of it is expected by users in most cases.
3641 selectionInContent
.acpStart
= mPendingTextChangeData
.mAddedEndOffset
;
3642 selectionInContent
.acpEnd
= selectionInContent
.acpStart
;
3644 // Otherwise, i.e., setting range has already gone, we cannot set
3645 // selection properly.
3646 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3647 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3648 "there is unknown content change",
3654 CompleteLastActionIfStillIncomplete();
3655 PendingAction
* action
= mPendingActions
.AppendElement();
3656 action
->mType
= PendingAction::Type::eSetSelection
;
3657 action
->mSelectionStart
= selectionInContent
.acpStart
;
3658 action
->mSelectionLength
=
3659 selectionInContent
.acpEnd
- selectionInContent
.acpStart
;
3660 action
->mSelectionReversed
= (selectionInContent
.style
.ase
== TS_AE_START
);
3662 // Use TSF specified selection for updating mSelectionForTSF.
3663 selectionForTSF
.SetSelection(*pSelection
);
3669 TSFTextStore::SetSelection(ULONG ulCount
, const TS_SELECTION_ACP
* pSelection
) {
3670 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3671 ("0x%p TSFTextStore::SetSelection(ulCount=%lu, pSelection=%p { "
3672 "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), "
3673 "mComposition.IsComposing()=%s",
3674 this, ulCount
, pSelection
, pSelection
? pSelection
->acpStart
: 0,
3675 pSelection
? pSelection
->acpEnd
: 0,
3676 pSelection
? GetActiveSelEndName(pSelection
->style
.ase
) : "",
3677 pSelection
? GetBoolName(pSelection
->style
.fInterimChar
) : "",
3678 GetBoolName(mComposition
.IsComposing())));
3680 if (!IsReadWriteLocked()) {
3681 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3682 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3683 "not locked (read-write)",
3688 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3689 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3690 "trying setting multiple selection",
3692 return E_INVALIDARG
;
3695 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3696 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3699 return E_INVALIDARG
;
3702 HRESULT hr
= SetSelectionInternal(pSelection
, true);
3704 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3705 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3706 "SetSelectionInternal() failure",
3709 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3710 ("0x%p TSFTextStore::SetSelection() succeeded", this));
3716 TSFTextStore::GetText(LONG acpStart
, LONG acpEnd
, WCHAR
* pchPlain
,
3717 ULONG cchPlainReq
, ULONG
* pcchPlainOut
,
3718 TS_RUNINFO
* prgRunInfo
, ULONG ulRunInfoReq
,
3719 ULONG
* pulRunInfoOut
, LONG
* pacpNext
) {
3721 sTextStoreLog
, LogLevel::Info
,
3722 ("0x%p TSFTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, "
3723 "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, "
3724 "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, "
3725 "mString.Length()=%lu, IsComposing()=%s }",
3726 this, acpStart
, acpEnd
, pchPlain
, cchPlainReq
, pcchPlainOut
, prgRunInfo
,
3727 ulRunInfoReq
, pulRunInfoOut
, pacpNext
, mComposition
.mStart
,
3728 mComposition
.mString
.Length(), GetBoolName(mComposition
.IsComposing())));
3730 if (!IsReadLocked()) {
3731 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3732 ("0x%p TSFTextStore::GetText() FAILED due to "
3733 "not locked (read)",
3738 if (!pcchPlainOut
|| (!pchPlain
&& !prgRunInfo
) ||
3739 !cchPlainReq
!= !pchPlain
|| !ulRunInfoReq
!= !prgRunInfo
) {
3740 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3741 ("0x%p TSFTextStore::GetText() FAILED due to "
3744 return E_INVALIDARG
;
3747 if (acpStart
< 0 || acpEnd
< -1 || (acpEnd
!= -1 && acpStart
> acpEnd
)) {
3748 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3749 ("0x%p TSFTextStore::GetText() FAILED due to "
3752 return TS_E_INVALIDPOS
;
3755 // Making sure to null-terminate string just to be on the safe side
3757 if (pchPlain
&& cchPlainReq
) *pchPlain
= 0;
3758 if (pulRunInfoOut
) *pulRunInfoOut
= 0;
3759 if (pacpNext
) *pacpNext
= acpStart
;
3760 if (prgRunInfo
&& ulRunInfoReq
) {
3761 prgRunInfo
->uCount
= 0;
3762 prgRunInfo
->type
= TS_RT_PLAIN
;
3765 Content
& contentForTSF
= ContentForTSFRef();
3766 if (!contentForTSF
.IsInitialized()) {
3767 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3768 ("0x%p TSFTextStore::GetText() FAILED due to "
3769 "ContentForTSFRef() failure",
3773 if (contentForTSF
.Text().Length() < static_cast<uint32_t>(acpStart
)) {
3774 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3775 ("0x%p TSFTextStore::GetText() FAILED due to "
3776 "acpStart is larger offset than the actual text length",
3778 return TS_E_INVALIDPOS
;
3781 contentForTSF
.Text().Length() < static_cast<uint32_t>(acpEnd
)) {
3782 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3783 ("0x%p TSFTextStore::GetText() FAILED due to "
3784 "acpEnd is larger offset than the actual text length",
3786 return TS_E_INVALIDPOS
;
3788 uint32_t length
= (acpEnd
== -1) ? contentForTSF
.Text().Length() -
3789 static_cast<uint32_t>(acpStart
)
3790 : static_cast<uint32_t>(acpEnd
- acpStart
);
3791 if (cchPlainReq
&& cchPlainReq
- 1 < length
) {
3792 length
= cchPlainReq
- 1;
3795 if (pchPlain
&& cchPlainReq
) {
3796 const char16_t
* startChar
=
3797 contentForTSF
.Text().BeginReading() + acpStart
;
3798 memcpy(pchPlain
, startChar
, length
* sizeof(*pchPlain
));
3799 pchPlain
[length
] = 0;
3800 *pcchPlainOut
= length
;
3802 if (prgRunInfo
&& ulRunInfoReq
) {
3803 prgRunInfo
->uCount
= length
;
3804 prgRunInfo
->type
= TS_RT_PLAIN
;
3805 if (pulRunInfoOut
) *pulRunInfoOut
= 1;
3807 if (pacpNext
) *pacpNext
= acpStart
+ length
;
3810 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3811 ("0x%p TSFTextStore::GetText() succeeded: pcchPlainOut=0x%p, "
3812 "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, "
3814 this, pcchPlainOut
, prgRunInfo
? prgRunInfo
->uCount
: 0,
3815 prgRunInfo
? GetTextRunTypeName(prgRunInfo
->type
) : "N/A",
3816 pulRunInfoOut
? *pulRunInfoOut
: 0, pacpNext
? *pacpNext
: 0));
3821 TSFTextStore::SetText(DWORD dwFlags
, LONG acpStart
, LONG acpEnd
,
3822 const WCHAR
* pchText
, ULONG cch
, TS_TEXTCHANGE
* pChange
) {
3824 sTextStoreLog
, LogLevel::Info
,
3825 ("0x%p TSFTextStore::SetText(dwFlags=%s, acpStart=%ld, "
3826 "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
3827 "mComposition.IsComposing()=%s",
3828 this, dwFlags
== TS_ST_CORRECTION
? "TS_ST_CORRECTION" : "not-specified",
3829 acpStart
, acpEnd
, pchText
,
3830 pchText
&& cch
? GetEscapedUTF8String(pchText
, cch
).get() : "", cch
,
3831 pChange
, GetBoolName(mComposition
.IsComposing())));
3833 // Per SDK documentation, and since we don't have better
3834 // ways to do this, this method acts as a helper to
3835 // call SetSelection followed by InsertTextAtSelection
3836 if (!IsReadWriteLocked()) {
3837 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3838 ("0x%p TSFTextStore::SetText() FAILED due to "
3839 "not locked (read)",
3844 TS_SELECTION_ACP selection
;
3845 selection
.acpStart
= acpStart
;
3846 selection
.acpEnd
= acpEnd
;
3847 selection
.style
.ase
= TS_AE_END
;
3848 selection
.style
.fInterimChar
= 0;
3849 // Set selection to desired range
3850 HRESULT hr
= SetSelectionInternal(&selection
);
3852 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3853 ("0x%p TSFTextStore::SetText() FAILED due to "
3854 "SetSelectionInternal() failure",
3858 // Replace just selected text
3859 if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText
, cch
),
3861 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3862 ("0x%p TSFTextStore::SetText() FAILED due to "
3863 "InsertTextAtSelectionInternal() failure",
3868 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3869 ("0x%p TSFTextStore::SetText() succeeded: pChange={ "
3870 "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
3871 this, pChange
? pChange
->acpStart
: 0,
3872 pChange
? pChange
->acpOldEnd
: 0, pChange
? pChange
->acpNewEnd
: 0));
3877 TSFTextStore::GetFormattedText(LONG acpStart
, LONG acpEnd
,
3878 IDataObject
** ppDataObject
) {
3879 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3880 ("0x%p TSFTextStore::GetFormattedText() called "
3881 "but not supported (E_NOTIMPL)",
3884 // no support for formatted text
3889 TSFTextStore::GetEmbedded(LONG acpPos
, REFGUID rguidService
, REFIID riid
,
3891 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3892 ("0x%p TSFTextStore::GetEmbedded() called "
3893 "but not supported (E_NOTIMPL)",
3896 // embedded objects are not supported
3901 TSFTextStore::QueryInsertEmbedded(const GUID
* pguidService
,
3902 const FORMATETC
* pFormatEtc
,
3903 BOOL
* pfInsertable
) {
3904 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3905 ("0x%p TSFTextStore::QueryInsertEmbedded() called "
3906 "but not supported, *pfInsertable=FALSE (S_OK)",
3909 // embedded objects are not supported
3910 *pfInsertable
= FALSE
;
3915 TSFTextStore::InsertEmbedded(DWORD dwFlags
, LONG acpStart
, LONG acpEnd
,
3916 IDataObject
* pDataObject
, TS_TEXTCHANGE
* pChange
) {
3917 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3918 ("0x%p TSFTextStore::InsertEmbedded() called "
3919 "but not supported (E_NOTIMPL)",
3922 // embedded objects are not supported
3927 bool TSFTextStore::ShouldSetInputScopeOfURLBarToDefault() {
3928 // FYI: Google Japanese Input may be an IMM-IME. If it's installed on
3929 // Win7, it's always IMM-IME. Otherwise, basically, it's a TIP.
3930 // However, if it's installed on Win7 and has not been updated yet
3931 // after the OS is upgraded to Win8 or later, it's still an IMM-IME.
3932 // Therefore, we also need to check with IMMHandler here.
3933 if (!TSFPrefs::ShouldSetInputScopeOfURLBarToDefault()) {
3937 if (IMMHandler::IsGoogleJapaneseInputActive()) {
3941 switch (TSFStaticSink::ActiveTIP()) {
3942 case TextInputProcessorID::eMicrosoftIMEForJapanese
:
3943 case TextInputProcessorID::eGoogleJapaneseInput
:
3944 case TextInputProcessorID::eMicrosoftBopomofo
:
3945 case TextInputProcessorID::eMicrosoftChangJie
:
3946 case TextInputProcessorID::eMicrosoftPhonetic
:
3947 case TextInputProcessorID::eMicrosoftQuick
:
3948 case TextInputProcessorID::eMicrosoftNewChangJie
:
3949 case TextInputProcessorID::eMicrosoftNewPhonetic
:
3950 case TextInputProcessorID::eMicrosoftNewQuick
:
3951 case TextInputProcessorID::eMicrosoftPinyin
:
3952 case TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle
:
3953 case TextInputProcessorID::eMicrosoftOldHangul
:
3954 case TextInputProcessorID::eMicrosoftWubi
:
3956 case TextInputProcessorID::eMicrosoftIMEForKorean
:
3957 return IsWin8OrLater();
3963 void TSFTextStore::SetInputScope(const nsString
& aHTMLInputType
,
3964 const nsString
& aHTMLInputInputMode
,
3965 bool aInPrivateBrowsing
) {
3966 mInputScopes
.Clear();
3968 if (aInPrivateBrowsing
) {
3969 mInputScopes
.AppendElement(IS_PRIVATE
);
3972 if (aHTMLInputType
.IsEmpty() || aHTMLInputType
.EqualsLiteral("text")) {
3973 if (aHTMLInputInputMode
.EqualsLiteral("url")) {
3974 mInputScopes
.AppendElement(IS_URL
);
3975 } else if (aHTMLInputInputMode
.EqualsLiteral("mozAwesomebar")) {
3976 // Even if Awesomebar has focus, user may not input URL directly.
3977 // However, on-screen keyboard for URL should be shown because it has
3978 // some useful additional keys like ".com" and they are not hindrances
3979 // even when inputting non-URL text, e.g., words to search something in
3980 // the web. On the other hand, a lot of Microsoft's IMEs and Google
3981 // Japanese Input make their open state "closed" automatically if we
3982 // notify them of URL as the input scope. However, this is very annoying
3983 // for the users when they try to input some words to search the web or
3984 // bookmark/history items. Therefore, if they are active, we need to
3985 // notify them of the default input scope for avoiding this issue.
3986 if (TSFTextStore::ShouldSetInputScopeOfURLBarToDefault()) {
3989 // Don't append IS_SEARCH here for showing on-screen keyboard for URL.
3990 mInputScopes
.AppendElement(IS_URL
);
3991 } else if (aHTMLInputInputMode
.EqualsLiteral("email")) {
3992 mInputScopes
.AppendElement(IS_EMAIL_SMTPEMAILADDRESS
);
3993 } else if (aHTMLInputType
.EqualsLiteral("tel")) {
3994 mInputScopes
.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER
);
3995 mInputScopes
.AppendElement(IS_TELEPHONE_LOCALNUMBER
);
3996 } else if (aHTMLInputType
.EqualsLiteral("numeric")) {
3997 mInputScopes
.AppendElement(IS_NUMBER
);
4002 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html
4003 if (aHTMLInputType
.EqualsLiteral("url")) {
4004 mInputScopes
.AppendElement(IS_URL
);
4005 } else if (aHTMLInputType
.EqualsLiteral("search")) {
4006 mInputScopes
.AppendElement(IS_SEARCH
);
4007 } else if (aHTMLInputType
.EqualsLiteral("email")) {
4008 mInputScopes
.AppendElement(IS_EMAIL_SMTPEMAILADDRESS
);
4009 } else if (aHTMLInputType
.EqualsLiteral("password")) {
4010 mInputScopes
.AppendElement(IS_PASSWORD
);
4011 } else if (aHTMLInputType
.EqualsLiteral("datetime") ||
4012 aHTMLInputType
.EqualsLiteral("datetime-local")) {
4013 mInputScopes
.AppendElement(IS_DATE_FULLDATE
);
4014 mInputScopes
.AppendElement(IS_TIME_FULLTIME
);
4015 } else if (aHTMLInputType
.EqualsLiteral("date") ||
4016 aHTMLInputType
.EqualsLiteral("month") ||
4017 aHTMLInputType
.EqualsLiteral("week")) {
4018 mInputScopes
.AppendElement(IS_DATE_FULLDATE
);
4019 } else if (aHTMLInputType
.EqualsLiteral("time")) {
4020 mInputScopes
.AppendElement(IS_TIME_FULLTIME
);
4021 } else if (aHTMLInputType
.EqualsLiteral("tel")) {
4022 mInputScopes
.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER
);
4023 mInputScopes
.AppendElement(IS_TELEPHONE_LOCALNUMBER
);
4024 } else if (aHTMLInputType
.EqualsLiteral("number")) {
4025 mInputScopes
.AppendElement(IS_NUMBER
);
4029 int32_t TSFTextStore::GetRequestedAttrIndex(const TS_ATTRID
& aAttrID
) {
4030 if (IsEqualGUID(aAttrID
, GUID_PROP_INPUTSCOPE
)) {
4033 if (IsEqualGUID(aAttrID
, TSATTRID_Text_VerticalWriting
)) {
4034 return eTextVerticalWriting
;
4036 if (IsEqualGUID(aAttrID
, TSATTRID_Text_Orientation
)) {
4037 return eTextOrientation
;
4039 return eNotSupported
;
4043 TSFTextStore::GetAttrID(int32_t aIndex
) {
4046 return GUID_PROP_INPUTSCOPE
;
4047 case eTextVerticalWriting
:
4048 return TSATTRID_Text_VerticalWriting
;
4049 case eTextOrientation
:
4050 return TSATTRID_Text_Orientation
;
4052 MOZ_CRASH("Invalid index? Or not implemented yet?");
4058 TSFTextStore::HandleRequestAttrs(DWORD aFlags
, ULONG aFilterCount
,
4059 const TS_ATTRID
* aFilterAttrs
) {
4060 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4061 ("0x%p TSFTextStore::HandleRequestAttrs(aFlags=%s, "
4063 this, GetFindFlagName(aFlags
).get(), aFilterCount
));
4065 // This is a little weird! RequestSupportedAttrs gives us advanced notice
4066 // of a support query via RetrieveRequestedAttrs for a specific attribute.
4067 // RetrieveRequestedAttrs needs to return valid data for all attributes we
4068 // support, but the text service will only want the input scope object
4069 // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
4070 // TS_ATTR_FIND_WANT_VALUE.
4071 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
4072 mRequestedAttrs
[i
] = false;
4074 mRequestedAttrValues
= !!(aFlags
& TS_ATTR_FIND_WANT_VALUE
);
4076 for (uint32_t i
= 0; i
< aFilterCount
; i
++) {
4077 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4078 ("0x%p TSFTextStore::HandleRequestAttrs(), "
4079 "requested attr=%s",
4080 this, GetGUIDNameStrWithTable(aFilterAttrs
[i
]).get()));
4081 int32_t index
= GetRequestedAttrIndex(aFilterAttrs
[i
]);
4082 if (index
!= eNotSupported
) {
4083 mRequestedAttrs
[index
] = true;
4090 TSFTextStore::RequestSupportedAttrs(DWORD dwFlags
, ULONG cFilterAttrs
,
4091 const TS_ATTRID
* paFilterAttrs
) {
4092 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4093 ("0x%p TSFTextStore::RequestSupportedAttrs(dwFlags=%s, "
4094 "cFilterAttrs=%lu)",
4095 this, GetFindFlagName(dwFlags
).get(), cFilterAttrs
));
4097 return HandleRequestAttrs(dwFlags
, cFilterAttrs
, paFilterAttrs
);
4101 TSFTextStore::RequestAttrsAtPosition(LONG acpPos
, ULONG cFilterAttrs
,
4102 const TS_ATTRID
* paFilterAttrs
,
4104 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4105 ("0x%p TSFTextStore::RequestAttrsAtPosition(acpPos=%ld, "
4106 "cFilterAttrs=%lu, dwFlags=%s)",
4107 this, acpPos
, cFilterAttrs
, GetFindFlagName(dwFlags
).get()));
4109 return HandleRequestAttrs(dwFlags
| TS_ATTR_FIND_WANT_VALUE
, cFilterAttrs
,
4114 TSFTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos
,
4116 const TS_ATTRID
* paFilterAttr
,
4118 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4119 ("0x%p TSFTextStore::RequestAttrsTransitioningAtPosition("
4120 "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported "
4122 this, acpPos
, cFilterAttrs
, GetFindFlagName(dwFlags
).get()));
4124 // no per character attributes defined
4129 TSFTextStore::FindNextAttrTransition(LONG acpStart
, LONG acpHalt
,
4131 const TS_ATTRID
* paFilterAttrs
,
4132 DWORD dwFlags
, LONG
* pacpNext
,
4133 BOOL
* pfFound
, LONG
* plFoundOffset
) {
4134 if (!pacpNext
|| !pfFound
|| !plFoundOffset
) {
4135 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4136 (" 0x%p TSFTextStore::FindNextAttrTransition() FAILED due to "
4139 return E_INVALIDARG
;
4142 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4143 ("0x%p TSFTextStore::FindNextAttrTransition() called "
4144 "but not supported (S_OK)",
4147 // no per character attributes defined
4148 *pacpNext
= *plFoundOffset
= acpHalt
;
4154 TSFTextStore::RetrieveRequestedAttrs(ULONG ulCount
, TS_ATTRVAL
* paAttrVals
,
4156 if (!pcFetched
|| !paAttrVals
) {
4157 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4158 ("0x%p TSFTextStore::RetrieveRequestedAttrs() FAILED due to "
4161 return E_INVALIDARG
;
4164 ULONG expectedCount
= 0;
4165 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
4166 if (mRequestedAttrs
[i
]) {
4170 if (ulCount
< expectedCount
) {
4171 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4172 ("0x%p TSFTextStore::RetrieveRequestedAttrs() FAILED due to "
4173 "not enough count ulCount=%u, expectedCount=%u",
4174 this, ulCount
, expectedCount
));
4175 return E_INVALIDARG
;
4178 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4179 ("0x%p TSFTextStore::RetrieveRequestedAttrs() called "
4180 "ulCount=%d, mRequestedAttrValues=%s",
4181 this, ulCount
, GetBoolName(mRequestedAttrValues
)));
4184 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
4185 if (!mRequestedAttrs
[i
]) {
4188 mRequestedAttrs
[i
] = false;
4190 TS_ATTRID attrID
= GetAttrID(i
);
4192 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4193 ("0x%p TSFTextStore::RetrieveRequestedAttrs() for %s", this,
4194 GetGUIDNameStrWithTable(attrID
).get()));
4196 paAttrVals
[count
].idAttr
= attrID
;
4197 paAttrVals
[count
].dwOverlapId
= 0;
4199 if (!mRequestedAttrValues
) {
4200 paAttrVals
[count
].varValue
.vt
= VT_EMPTY
;
4204 paAttrVals
[count
].varValue
.vt
= VT_UNKNOWN
;
4205 RefPtr
<IUnknown
> inputScope
= new InputScopeImpl(mInputScopes
);
4206 paAttrVals
[count
].varValue
.punkVal
= inputScope
.forget().take();
4209 case eTextVerticalWriting
: {
4210 Selection
& selectionForTSF
= SelectionForTSFRef();
4211 paAttrVals
[count
].varValue
.vt
= VT_BOOL
;
4212 paAttrVals
[count
].varValue
.boolVal
=
4213 !selectionForTSF
.IsDirty() &&
4214 selectionForTSF
.GetWritingMode().IsVertical()
4219 case eTextOrientation
: {
4220 Selection
& selectionForTSF
= SelectionForTSFRef();
4221 paAttrVals
[count
].varValue
.vt
= VT_I4
;
4222 paAttrVals
[count
].varValue
.lVal
=
4223 !selectionForTSF
.IsDirty() &&
4224 selectionForTSF
.GetWritingMode().IsVertical()
4230 MOZ_CRASH("Invalid index? Or not implemented yet?");
4237 mRequestedAttrValues
= false;
4244 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4245 ("0x%p TSFTextStore::RetrieveRequestedAttrs() called "
4246 "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)",
4249 paAttrVals
->dwOverlapId
= 0;
4250 paAttrVals
->varValue
.vt
= VT_EMPTY
;
4256 TSFTextStore::GetEndACP(LONG
* pacp
) {
4257 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4258 ("0x%p TSFTextStore::GetEndACP(pacp=0x%p)", this, pacp
));
4260 if (!IsReadLocked()) {
4261 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4262 ("0x%p TSFTextStore::GetEndACP() FAILED due to "
4263 "not locked (read)",
4269 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4270 ("0x%p TSFTextStore::GetEndACP() FAILED due to "
4273 return E_INVALIDARG
;
4276 Content
& contentForTSF
= ContentForTSFRef();
4277 if (!contentForTSF
.IsInitialized()) {
4278 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4279 ("0x%p TSFTextStore::GetEndACP() FAILED due to "
4280 "ContentForTSFRef() failure",
4284 *pacp
= static_cast<LONG
>(contentForTSF
.Text().Length());
4289 TSFTextStore::GetActiveView(TsViewCookie
* pvcView
) {
4290 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4291 ("0x%p TSFTextStore::GetActiveView(pvcView=0x%p)", this, pvcView
));
4294 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4295 ("0x%p TSFTextStore::GetActiveView() FAILED due to "
4298 return E_INVALIDARG
;
4301 *pvcView
= TEXTSTORE_DEFAULT_VIEW
;
4303 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4304 ("0x%p TSFTextStore::GetActiveView() succeeded: *pvcView=%ld", this,
4310 TSFTextStore::GetACPFromPoint(TsViewCookie vcView
, const POINT
* pt
,
4311 DWORD dwFlags
, LONG
* pacp
) {
4312 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4313 ("0x%p TSFTextStore::GetACPFromPoint(pvcView=%d, pt=%p (x=%d, "
4314 "y=%d), dwFlags=%s, pacp=%p, mDeferNotifyingTSF=%s, "
4315 "mWaitingQueryLayout=%s",
4316 this, vcView
, pt
, pt
? pt
->x
: 0, pt
? pt
->y
: 0,
4317 GetACPFromPointFlagName(dwFlags
).get(), pacp
,
4318 GetBoolName(mDeferNotifyingTSF
), GetBoolName(mWaitingQueryLayout
)));
4320 if (!IsReadLocked()) {
4321 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4322 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4323 "not locked (read)",
4328 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4329 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4330 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4331 "called with invalid view",
4333 return E_INVALIDARG
;
4337 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4338 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4341 return E_INVALIDARG
;
4345 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4346 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4349 return E_INVALIDARG
;
4352 mWaitingQueryLayout
= false;
4354 if (mDestroyed
|| mContentForTSF
.IsLayoutChanged()) {
4355 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4356 ("0x%p TSFTextStore::GetACPFromPoint() returned "
4359 mHasReturnedNoLayoutError
= true;
4360 return TS_E_NOLAYOUT
;
4363 LayoutDeviceIntPoint
ourPt(pt
->x
, pt
->y
);
4364 // Convert to widget relative coordinates from screen's.
4365 ourPt
-= mWidget
->WidgetToScreenOffset();
4367 // NOTE: Don't check if the point is in the widget since the point can be
4368 // outside of the widget if focused editor is in a XUL <panel>.
4370 WidgetQueryContentEvent
charAtPt(true, eQueryCharacterAtPoint
, mWidget
);
4371 mWidget
->InitEvent(charAtPt
, &ourPt
);
4373 // FYI: WidgetQueryContentEvent may cause flushing pending layout and it
4374 // may cause focus change or something.
4375 RefPtr
<TSFTextStore
> kungFuDeathGrip(this);
4376 DispatchEvent(charAtPt
);
4377 if (!mWidget
|| mWidget
->Destroyed()) {
4378 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4379 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4380 "mWidget was destroyed during eQueryCharacterAtPoint",
4385 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
4386 ("0x%p TSFTextStore::GetACPFromPoint(), charAtPt={ "
4387 "mSucceeded=%s, mReply={ mOffset=%u, mTentativeCaretOffset=%u }}",
4388 this, GetBoolName(charAtPt
.mSucceeded
), charAtPt
.mReply
.mOffset
,
4389 charAtPt
.mReply
.mTentativeCaretOffset
));
4391 if (NS_WARN_IF(!charAtPt
.mSucceeded
)) {
4392 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4393 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4394 "eQueryCharacterAtPoint failure",
4399 // If dwFlags isn't set and the point isn't in any character's bounding box,
4400 // we should return TS_E_INVALIDPOINT.
4401 if (!(dwFlags
& GXFPF_NEAREST
) &&
4402 charAtPt
.mReply
.mOffset
== WidgetQueryContentEvent::NOT_FOUND
) {
4403 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4404 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to the "
4405 "point contained by no bounding box",
4407 return TS_E_INVALIDPOINT
;
4410 // Although, we're not sure if mTentativeCaretOffset becomes NOT_FOUND,
4411 // let's assume that there is no content in such case.
4412 if (NS_WARN_IF(charAtPt
.mReply
.mTentativeCaretOffset
==
4413 WidgetQueryContentEvent::NOT_FOUND
)) {
4414 charAtPt
.mReply
.mTentativeCaretOffset
= 0;
4419 // If dwFlags includes GXFPF_ROUND_NEAREST, we should return tentative
4420 // caret offset (MSDN calls it "range position").
4421 if (dwFlags
& GXFPF_ROUND_NEAREST
) {
4422 offset
= charAtPt
.mReply
.mTentativeCaretOffset
;
4423 } else if (charAtPt
.mReply
.mOffset
!= WidgetQueryContentEvent::NOT_FOUND
) {
4424 // Otherwise, we should return character offset whose bounding box contains
4426 offset
= charAtPt
.mReply
.mOffset
;
4428 // If the point isn't in any character's bounding box but we need to return
4429 // the nearest character from the point, we should *guess* the character
4430 // offset since there is no inexpensive API to check it strictly.
4431 // XXX If we retrieve 2 bounding boxes, one is before the offset and
4432 // the other is after the offset, we could resolve the offset.
4433 // However, dispatching 2 eQueryTextRect may be expensive.
4435 // So, use tentative offset for now.
4436 offset
= charAtPt
.mReply
.mTentativeCaretOffset
;
4438 // However, if it's after the last character, we need to decrement the
4440 Content
& contentForTSF
= ContentForTSFRef();
4441 if (!contentForTSF
.IsInitialized()) {
4442 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4443 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4444 "ContentForTSFRef() failure",
4448 if (contentForTSF
.Text().Length() <= offset
) {
4449 // If the tentative caret is after the last character, let's return
4450 // the last character's offset.
4451 offset
= contentForTSF
.Text().Length() - 1;
4455 if (NS_WARN_IF(offset
> LONG_MAX
)) {
4456 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4457 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to out of "
4458 "range of the result",
4460 return TS_E_INVALIDPOINT
;
4463 *pacp
= static_cast<LONG
>(offset
);
4464 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4465 ("0x%p TSFTextStore::GetACPFromPoint() succeeded: *pacp=%d", this,
4471 TSFTextStore::GetTextExt(TsViewCookie vcView
, LONG acpStart
, LONG acpEnd
,
4472 RECT
* prc
, BOOL
* pfClipped
) {
4474 sTextStoreLog
, LogLevel::Info
,
4475 ("0x%p TSFTextStore::GetTextExt(vcView=%ld, "
4476 "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p), "
4477 "IsHandlingComposition()=%s, "
4478 "mContentForTSF={ MinOffsetOfLayoutChanged()=%u, "
4479 "LatestCompositionStartOffset()=%d, LatestCompositionEndOffset()=%d }, "
4480 "mComposition= { IsComposing()=%s, mStart=%d, EndOffset()=%d }, "
4481 "mDeferNotifyingTSF=%s, mWaitingQueryLayout=%s, "
4482 "IMEHandler::IsA11yHandlingNativeCaret()=%s",
4483 this, vcView
, acpStart
, acpEnd
, prc
, pfClipped
,
4484 GetBoolName(IsHandlingComposition()),
4485 mContentForTSF
.MinOffsetOfLayoutChanged(),
4486 mContentForTSF
.HasOrHadComposition()
4487 ? mContentForTSF
.LatestCompositionStartOffset()
4489 mContentForTSF
.HasOrHadComposition()
4490 ? mContentForTSF
.LatestCompositionEndOffset()
4492 GetBoolName(mComposition
.IsComposing()), mComposition
.mStart
,
4493 mComposition
.EndOffset(), GetBoolName(mDeferNotifyingTSF
),
4494 GetBoolName(mWaitingQueryLayout
),
4495 GetBoolName(IMEHandler::IsA11yHandlingNativeCaret())));
4497 if (!IsReadLocked()) {
4498 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4499 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4500 "not locked (read)",
4505 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4506 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4507 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4508 "called with invalid view",
4510 return E_INVALIDARG
;
4513 if (!prc
|| !pfClipped
) {
4514 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4515 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4518 return E_INVALIDARG
;
4521 // According to MSDN, ITextStoreACP::GetTextExt() should return
4522 // TS_E_INVALIDARG when acpStart and acpEnd are same (i.e., collapsed range).
4523 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms538435(v=vs.85).aspx
4524 // > TS_E_INVALIDARG: The specified start and end character positions are
4526 // However, some TIPs (including Microsoft's Chinese TIPs!) call this with
4527 // collapsed range and if we return TS_E_INVALIDARG, they stops showing their
4528 // owning window or shows it but odd position. So, we should just return
4529 // error only when acpStart and/or acpEnd are really odd.
4531 if (acpStart
< 0 || acpEnd
< acpStart
) {
4532 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4533 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4536 return TS_E_INVALIDPOS
;
4539 mWaitingQueryLayout
= false;
4541 if (IsHandlingComposition() && mContentForTSF
.HasOrHadComposition() &&
4542 mContentForTSF
.IsLayoutChanged() &&
4543 mContentForTSF
.MinOffsetOfLayoutChanged() > LONG_MAX
) {
4544 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4545 ("0x%p TSFTextStore::GetTextExt(), FAILED due to the text "
4546 "is too big for TSF (cannot treat modified offset as LONG), "
4547 "mContentForTSF.MinOffsetOfLayoutChanged()=%u",
4548 this, mContentForTSF
.MinOffsetOfLayoutChanged()));
4552 // At Windows 10 build 17643 (an insider preview for RS5), Microsoft fixed
4553 // the bug of TS_E_NOLAYOUT (even when we returned TS_E_NOLAYOUT, TSF
4554 // returned E_FAIL to TIP). However, until we drop to support older Windows
4555 // and all TIPs are aware of TS_E_NOLAYOUT result, we need to keep returning
4556 // S_OK and available rectangle only for them.
4557 if (!MaybeHackNoErrorLayoutBugs(acpStart
, acpEnd
) &&
4558 mContentForTSF
.IsLayoutChangedAt(acpEnd
)) {
4559 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4560 ("0x%p TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
4563 mHasReturnedNoLayoutError
= true;
4564 return TS_E_NOLAYOUT
;
4568 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4569 ("0x%p TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
4570 "(acpEnd=%d) because this has already been destroyed",
4572 mHasReturnedNoLayoutError
= true;
4573 return TS_E_NOLAYOUT
;
4576 // use eQueryTextRect to get rect in system, screen coordinates
4577 WidgetQueryContentEvent
event(true, eQueryTextRect
, mWidget
);
4578 mWidget
->InitEvent(event
);
4580 WidgetQueryContentEvent::Options options
;
4581 int64_t startOffset
= acpStart
;
4582 if (mComposition
.IsComposing()) {
4583 // If there is a composition, TSF must want character rects related to
4584 // the composition. Therefore, we should use insertion point relative
4585 // query because the composition might be at different position from
4586 // the position where TSFTextStore believes it at.
4587 options
.mRelativeToInsertionPoint
= true;
4588 startOffset
-= mComposition
.mStart
;
4589 } else if (IsHandlingComposition() && mContentForTSF
.HasOrHadComposition()) {
4590 // If there was a composition and it hasn't been committed in the content
4591 // yet, ContentCacheInParent is still open for relative offset query from
4592 // the latest composition.
4593 options
.mRelativeToInsertionPoint
= true;
4594 startOffset
-= mContentForTSF
.LatestCompositionStartOffset();
4595 } else if (!CanAccessActualContentDirectly()) {
4596 // If TSF/TIP cannot access actual content directly, there may be pending
4597 // text and/or selection changes which have not been notified TSF yet.
4598 // Therefore, we should use relative to insertion point query since
4599 // TSF/TIP computes the offset from the cached selection.
4600 options
.mRelativeToInsertionPoint
= true;
4601 startOffset
-= mSelectionForTSF
.StartOffset();
4603 // ContentEventHandler and ContentCache return actual caret rect when
4604 // the queried range is collapsed and selection is collapsed at the
4605 // queried range. Then, its height (in horizontal layout, width in vertical
4606 // layout) may be different from actual font height of the line. In such
4607 // case, users see "dancing" of candidate or suggest window of TIP.
4608 // For preventing it, we should query text rect with at least 1 length.
4609 uint32_t length
= std::max(static_cast<int32_t>(acpEnd
- acpStart
), 1);
4610 event
.InitForQueryTextRect(startOffset
, length
, options
);
4612 DispatchEvent(event
);
4613 if (NS_WARN_IF(!event
.mSucceeded
)) {
4614 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4615 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4616 "eQueryTextRect failure",
4618 return TS_E_INVALIDPOS
; // but unexpected failure, maybe.
4621 // IMEs don't like empty rects, fix here
4622 if (event
.mReply
.mRect
.Width() <= 0) event
.mReply
.mRect
.SetWidth(1);
4623 if (event
.mReply
.mRect
.Height() <= 0) event
.mReply
.mRect
.SetHeight(1);
4625 // convert to unclipped screen rect
4626 nsWindow
* refWindow
= static_cast<nsWindow
*>(
4627 event
.mReply
.mFocusedWidget
? event
.mReply
.mFocusedWidget
: mWidget
);
4628 // Result rect is in top level widget coordinates
4629 refWindow
= refWindow
->GetTopLevelWindow(false);
4631 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4632 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4633 "no top level window",
4638 event
.mReply
.mRect
.MoveBy(refWindow
->WidgetToScreenOffset());
4640 // get bounding screen rect to test for clipping
4641 if (!GetScreenExtInternal(*prc
)) {
4642 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4643 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4644 "GetScreenExtInternal() failure",
4649 // clip text rect to bounding rect
4651 ::SetRect(&textRect
, event
.mReply
.mRect
.X(), event
.mReply
.mRect
.Y(),
4652 event
.mReply
.mRect
.XMost(), event
.mReply
.mRect
.YMost());
4653 if (!::IntersectRect(prc
, prc
, &textRect
))
4654 // Text is not visible
4655 ::SetRectEmpty(prc
);
4657 // not equal if text rect was clipped
4658 *pfClipped
= !::EqualRect(prc
, &textRect
);
4660 // ATOK 2011 - 2016 refers native caret position and size on windows whose
4661 // class name is one of Mozilla's windows for deciding candidate window
4662 // position. Additionally, ATOK 2015 and earlier behaves really odd when
4663 // we don't create native caret. Therefore, we need to create native caret
4664 // only when ATOK 2011 - 2015 is active (i.e., not necessary for ATOK 2016).
4665 // However, if a11y module is handling native caret, we shouldn't touch it.
4666 // Note that ATOK must require the latest information of the caret. So,
4667 // even if we'll create native caret later, we need to creat it here with
4668 // current information.
4669 if (!IMEHandler::IsA11yHandlingNativeCaret() &&
4670 TSFPrefs::NeedToCreateNativeCaretForLegacyATOK() &&
4671 TSFStaticSink::IsATOKReferringNativeCaretActive() &&
4672 mComposition
.IsComposing() && mComposition
.mStart
<= acpStart
&&
4673 mComposition
.EndOffset() >= acpStart
&& mComposition
.mStart
<= acpEnd
&&
4674 mComposition
.EndOffset() >= acpEnd
) {
4675 CreateNativeCaret();
4678 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4679 ("0x%p TSFTextStore::GetTextExt() succeeded: "
4680 "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s",
4681 this, prc
->left
, prc
->top
, prc
->right
, prc
->bottom
,
4682 GetBoolName(*pfClipped
)));
4687 bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG
& aACPStart
, LONG
& aACPEnd
) {
4688 // When ITextStoreACP::GetTextExt() returns TS_E_NOLAYOUT, TSF returns E_FAIL
4689 // to its caller (typically, active TIP). Then, most TIPs abort current job
4690 // or treat such application as non-GUI apps. E.g., some of them give up
4691 // showing candidate window, some others show candidate window at top-left of
4692 // the screen. For avoiding this issue, when there is composition (until
4693 // composition is actually committed in remote content), we should not
4694 // return TS_E_NOLAYOUT error for TIPs whose some features are broken by
4696 // Note that ideally, this issue should be avoided by each TIP since this
4697 // won't be fixed at least on non-latest Windows. Actually, Google Japanese
4698 // Input (based on Mozc) does it. When GetTextExt() returns E_FAIL, TIPs
4699 // should try to check result of GetRangeFromPoint() because TSF returns
4700 // TS_E_NOLAYOUT correctly in this case. See:
4701 // https://github.com/google/mozc/blob/6b878e31fb6ac4347dc9dfd8ccc1080fe718479f/src/win32/tip/tip_range_util.cc#L237-L257
4703 if (!IsHandlingComposition() || !mContentForTSF
.HasOrHadComposition() ||
4704 !mContentForTSF
.IsLayoutChangedAt(aACPEnd
)) {
4708 MOZ_ASSERT(!mComposition
.IsComposing() ||
4709 mComposition
.mStart
==
4710 mContentForTSF
.LatestCompositionStartOffset());
4711 MOZ_ASSERT(!mComposition
.IsComposing() ||
4712 mComposition
.EndOffset() ==
4713 mContentForTSF
.LatestCompositionEndOffset());
4715 // If TSF does not have the bug, we need to hack only with a few TIPs.
4716 static const bool sAlllowToStopHackingIfFine
=
4717 IsWindows10BuildOrLater(17643) &&
4718 TSFPrefs::AllowToStopHackingOnBuild17643OrLater();
4720 // We need to compute active TIP now. This may take a couple of milliseconds,
4721 // however, it'll be cached, so, must be faster than check active TIP every
4722 // GetTextExt() calls.
4723 const Selection
& selectionForTSF
= SelectionForTSFRef();
4724 switch (TSFStaticSink::ActiveTIP()) {
4725 // MS IME for Japanese doesn't support asynchronous handling at deciding
4726 // its suggest list window position. The feature was implemented
4727 // starting from Windows 8. And also we may meet same trouble in e10s
4728 // mode on Win7. So, we should never return TS_E_NOLAYOUT to MS IME for
4730 case TextInputProcessorID::eMicrosoftIMEForJapanese
:
4731 if (sAlllowToStopHackingIfFine
) {
4734 // Basically, MS-IME tries to retrieve whole composition string rect
4735 // at deciding suggest window immediately after unlocking the document.
4736 // However, in e10s mode, the content hasn't updated yet in most cases.
4737 // Therefore, if the first character at the retrieving range rect is
4738 // available, we should use it as the result.
4739 if (TSFPrefs::DoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar() &&
4740 aACPStart
< aACPEnd
) {
4741 aACPEnd
= aACPStart
;
4743 // Although, the condition is not clear, MS-IME sometimes retrieves the
4744 // caret rect immediately after modifying the composition string but
4745 // before unlocking the document. In such case, we should return the
4746 // nearest character rect.
4747 else if (TSFPrefs::DoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret() &&
4748 aACPStart
== aACPEnd
&& selectionForTSF
.IsCollapsed() &&
4749 selectionForTSF
.EndOffset() == aACPEnd
) {
4750 int32_t minOffsetOfLayoutChanged
=
4751 static_cast<int32_t>(mContentForTSF
.MinOffsetOfLayoutChanged());
4752 aACPEnd
= aACPStart
= std::max(minOffsetOfLayoutChanged
- 1, 0);
4757 // The bug of Microsoft Office IME 2010 for Japanese is similar to
4758 // MS-IME for Win 8.1 and Win 10. Newer version of MS Office IME is not
4759 // released yet. So, we can hack it without prefs because there must be
4760 // no developers who want to disable this hack for tests.
4761 // XXX We have not tested with Microsoft Office IME 2010 since it's
4762 // installable only with Win7 and Win8 (i.e., cannot install Win8.1
4763 // and Win10), and requires upgrade to Win10.
4764 case TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese
:
4765 // Basically, MS-IME tries to retrieve whole composition string rect
4766 // at deciding suggest window immediately after unlocking the document.
4767 // However, in e10s mode, the content hasn't updated yet in most cases.
4768 // Therefore, if the first character at the retrieving range rect is
4769 // available, we should use it as the result.
4770 if (aACPStart
< aACPEnd
) {
4771 aACPEnd
= aACPStart
;
4773 // Although, the condition is not clear, MS-IME sometimes retrieves the
4774 // caret rect immediately after modifying the composition string but
4775 // before unlocking the document. In such case, we should return the
4776 // nearest character rect.
4777 else if (aACPStart
== aACPEnd
&& selectionForTSF
.IsCollapsed() &&
4778 selectionForTSF
.EndOffset() == aACPEnd
) {
4779 int32_t minOffsetOfLayoutChanged
=
4780 static_cast<int32_t>(mContentForTSF
.MinOffsetOfLayoutChanged());
4781 aACPEnd
= aACPStart
= std::max(minOffsetOfLayoutChanged
- 1, 0);
4786 // ATOK fails to handle TS_E_NOLAYOUT only when it decides the position of
4787 // suggest window. In such case, ATOK tries to query rect of whole or a
4788 // part of composition string.
4789 // FYI: ATOK changes their implementation around candidate window and
4790 // suggest widget at ATOK 2016. Therefore, there are some differences
4791 // ATOK 2015 (or older) and ATOK 2016 (or newer).
4792 // FYI: ATOK 2017 stops referring our window class name. I.e., ATOK 2016
4793 // and older may behave differently only on Gecko but this must be
4794 // finished from ATOK 2017.
4795 // FYI: For testing with legacy ATOK, we should hack it even if current ATOK
4796 // refers native caret rect on windows whose window class is one of
4797 // Mozilla window classes and we stop creating native caret for ATOK
4798 // because creating native caret causes ATOK refers caret position
4799 // when GetTextExt() returns TS_E_NOLAYOUT.
4800 case TextInputProcessorID::eATOK2011
:
4801 case TextInputProcessorID::eATOK2012
:
4802 case TextInputProcessorID::eATOK2013
:
4803 case TextInputProcessorID::eATOK2014
:
4804 case TextInputProcessorID::eATOK2015
:
4805 // ATOK 2016 and later may temporarily show candidate window at odd
4806 // position when you convert a word quickly (e.g., keep pressing
4807 // space bar). So, on ATOK 2016 or later, we need to keep hacking the
4808 // result of GetTextExt().
4809 if (sAlllowToStopHackingIfFine
) {
4812 // If we'll create native caret where we paint our caret. Then, ATOK
4813 // will refer native caret. So, we don't need to hack anything in
4815 if (TSFPrefs::NeedToCreateNativeCaretForLegacyATOK()) {
4816 MOZ_ASSERT(TSFStaticSink::IsATOKReferringNativeCaretActive());
4820 case TextInputProcessorID::eATOK2016
:
4821 case TextInputProcessorID::eATOKUnknown
:
4822 if (!TSFPrefs::DoNotReturnNoLayoutErrorToATOKOfCompositionString()) {
4825 // If the range is in the composition string, we should return rectangle
4826 // in it as far as possible.
4827 if (aACPStart
< mContentForTSF
.LatestCompositionStartOffset() ||
4828 aACPStart
> mContentForTSF
.LatestCompositionEndOffset() ||
4829 aACPEnd
< mContentForTSF
.LatestCompositionStartOffset() ||
4830 aACPEnd
> mContentForTSF
.LatestCompositionEndOffset()) {
4834 // Japanist 10 fails to handle TS_E_NOLAYOUT when it decides the position
4835 // of candidate window. In such case, Japanist shows candidate window at
4836 // top-left of the screen. So, we should return the nearest caret rect
4837 // where we know. This is Japanist's bug. So, even after build 17643,
4838 // we need this hack.
4839 case TextInputProcessorID::eJapanist10
:
4841 DoNotReturnNoLayoutErrorToJapanist10OfCompositionString()) {
4844 if (aACPStart
< mContentForTSF
.LatestCompositionStartOffset() ||
4845 aACPStart
> mContentForTSF
.LatestCompositionEndOffset() ||
4846 aACPEnd
< mContentForTSF
.LatestCompositionStartOffset() ||
4847 aACPEnd
> mContentForTSF
.LatestCompositionEndOffset()) {
4851 // Free ChangJie 2010 doesn't handle ITfContextView::GetTextExt() properly.
4852 // This must be caused by the bug of TSF since Free ChangJie works fine on
4853 // build 17643 and later.
4854 case TextInputProcessorID::eFreeChangJie
:
4855 if (sAlllowToStopHackingIfFine
) {
4858 if (!TSFPrefs::DoNotReturnNoLayoutErrorToFreeChangJie()) {
4861 aACPEnd
= mContentForTSF
.LatestCompositionStartOffset();
4862 aACPStart
= std::min(aACPStart
, aACPEnd
);
4864 // Some Traditional Chinese TIPs of Microsoft don't show candidate window
4865 // in e10s mode on Win8 or later.
4866 case TextInputProcessorID::eMicrosoftChangJie
:
4867 case TextInputProcessorID::eMicrosoftQuick
:
4868 if (sAlllowToStopHackingIfFine
) {
4871 if (!IsWin8OrLater() ||
4872 !TSFPrefs::DoNotReturnNoLayoutErrorToMSTraditionalTIP()) {
4875 aACPEnd
= mContentForTSF
.LatestCompositionStartOffset();
4876 aACPStart
= std::min(aACPStart
, aACPEnd
);
4878 // Some Simplified Chinese TIPs of Microsoft don't show candidate window
4879 // in e10s mode on Win8 or later.
4880 // FYI: Only Simplified Chinese TIPs of Microsoft still require this hack
4881 // because they sometimes do not show candidate window when we return
4882 // TS_E_NOLAYOUT for first query. Note that even when they show
4883 // candidate window properly, we return TS_E_NOLAYOUT and following
4884 // log looks same as when they don't show candidate window. Perhaps,
4885 // there is stateful cause or race in them.
4886 case TextInputProcessorID::eMicrosoftPinyin
:
4887 case TextInputProcessorID::eMicrosoftWubi
:
4888 if (!IsWin8OrLater() ||
4889 !TSFPrefs::DoNotReturnNoLayoutErrorToMSSimplifiedTIP()) {
4892 aACPEnd
= mContentForTSF
.LatestCompositionStartOffset();
4893 aACPStart
= std::min(aACPStart
, aACPEnd
);
4899 // If we hack the queried range for active TIP, that means we should not
4900 // return TS_E_NOLAYOUT even if hacked offset is still modified. So, as
4901 // far as possible, we should adjust the offset.
4902 MOZ_ASSERT(mContentForTSF
.IsLayoutChanged());
4903 bool collapsed
= aACPStart
== aACPEnd
;
4904 // Note that even if all characters in the editor or the composition
4905 // string was modified, 0 or start offset of the composition string is
4906 // useful because it may return caret rect or old character's rect which
4907 // the user still see. That must be useful information for TIP.
4908 int32_t firstModifiedOffset
=
4909 static_cast<int32_t>(mContentForTSF
.MinOffsetOfLayoutChanged());
4910 LONG lastUnmodifiedOffset
= std::max(firstModifiedOffset
- 1, 0);
4911 if (mContentForTSF
.IsLayoutChangedAt(aACPStart
)) {
4912 if (aACPStart
>= mContentForTSF
.LatestCompositionStartOffset()) {
4913 // If mContentForTSF has last composition string and current
4914 // composition string, we can assume that ContentCacheInParent has
4915 // cached rects of composition string at least length of current
4916 // composition string. Otherwise, we can assume that rect for
4917 // first character of composition string is stored since it was
4918 // selection start or caret position.
4919 LONG maxCachedOffset
= mContentForTSF
.LatestCompositionEndOffset();
4920 if (mContentForTSF
.WasLastComposition()) {
4921 maxCachedOffset
= std::min(
4922 maxCachedOffset
, mContentForTSF
.LastCompositionStringEndOffset());
4924 aACPStart
= std::min(aACPStart
, maxCachedOffset
);
4926 // Otherwise, we don't know which character rects are cached. So, we
4927 // need to use first unmodified character's rect in this case. Even
4928 // if there is no character, the query event will return caret rect
4931 aACPStart
= lastUnmodifiedOffset
;
4933 MOZ_ASSERT(aACPStart
<= aACPEnd
);
4936 // If TIP requests caret rect with collapsed range, we should keep
4937 // collapsing the range.
4939 aACPEnd
= aACPStart
;
4941 // Let's set aACPEnd to larger offset of last unmodified offset or
4942 // aACPStart which may be the first character offset of the composition
4943 // string. However, some TIPs may want to know the right edge of the
4944 // range. Therefore, if aACPEnd is in composition string and active TIP
4945 // doesn't retrieve caret rect (i.e., the range isn't collapsed), we
4946 // should keep using the original aACPEnd. Otherwise, we should set
4947 // aACPEnd to larger value of aACPStart and lastUnmodifiedOffset.
4948 else if (mContentForTSF
.IsLayoutChangedAt(aACPEnd
) &&
4949 (aACPEnd
< mContentForTSF
.LatestCompositionStartOffset() ||
4950 aACPEnd
> mContentForTSF
.LatestCompositionEndOffset())) {
4951 aACPEnd
= std::max(aACPStart
, lastUnmodifiedOffset
);
4955 sTextStoreLog
, LogLevel::Debug
,
4956 ("0x%p TSFTextStore::HackNoErrorLayoutBugs() hacked the queried range "
4957 "for not returning TS_E_NOLAYOUT, new values are: "
4958 "aACPStart=%d, aACPEnd=%d",
4959 this, aACPStart
, aACPEnd
));
4965 TSFTextStore::GetScreenExt(TsViewCookie vcView
, RECT
* prc
) {
4966 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4967 ("0x%p TSFTextStore::GetScreenExt(vcView=%ld, prc=0x%p)", this,
4970 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4971 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4972 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4973 "called with invalid view",
4975 return E_INVALIDARG
;
4979 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4980 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4983 return E_INVALIDARG
;
4987 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4988 ("0x%p TSFTextStore::GetScreenExt() returns empty rect "
4989 "due to already destroyed",
4991 prc
->left
= prc
->top
= prc
->right
= prc
->bottom
= 0;
4995 if (!GetScreenExtInternal(*prc
)) {
4996 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4997 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4998 "GetScreenExtInternal() failure",
5003 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5004 ("0x%p TSFTextStore::GetScreenExt() succeeded: "
5005 "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
5006 this, prc
->left
, prc
->top
, prc
->right
, prc
->bottom
));
5010 bool TSFTextStore::GetScreenExtInternal(RECT
& aScreenExt
) {
5011 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5012 ("0x%p TSFTextStore::GetScreenExtInternal()", this));
5014 MOZ_ASSERT(!mDestroyed
);
5016 // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
5017 WidgetQueryContentEvent
event(true, eQueryEditorRect
, mWidget
);
5018 mWidget
->InitEvent(event
);
5019 DispatchEvent(event
);
5020 if (!event
.mSucceeded
) {
5021 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5022 ("0x%p TSFTextStore::GetScreenExtInternal() FAILED due to "
5023 "eQueryEditorRect failure",
5028 nsWindow
* refWindow
= static_cast<nsWindow
*>(
5029 event
.mReply
.mFocusedWidget
? event
.mReply
.mFocusedWidget
: mWidget
);
5030 // Result rect is in top level widget coordinates
5031 refWindow
= refWindow
->GetTopLevelWindow(false);
5033 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5034 ("0x%p TSFTextStore::GetScreenExtInternal() FAILED due to "
5035 "no top level window",
5040 LayoutDeviceIntRect boundRect
= refWindow
->GetClientBounds();
5041 boundRect
.MoveTo(0, 0);
5043 // Clip frame rect to window rect
5044 boundRect
.IntersectRect(event
.mReply
.mRect
, boundRect
);
5045 if (!boundRect
.IsEmpty()) {
5046 boundRect
.MoveBy(refWindow
->WidgetToScreenOffset());
5047 ::SetRect(&aScreenExt
, boundRect
.X(), boundRect
.Y(), boundRect
.XMost(),
5050 ::SetRectEmpty(&aScreenExt
);
5053 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5054 ("0x%p TSFTextStore::GetScreenExtInternal() succeeded: "
5055 "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
5056 this, aScreenExt
.left
, aScreenExt
.top
, aScreenExt
.right
,
5057 aScreenExt
.bottom
));
5062 TSFTextStore::GetWnd(TsViewCookie vcView
, HWND
* phwnd
) {
5063 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5064 ("0x%p TSFTextStore::GetWnd(vcView=%ld, phwnd=0x%p), "
5066 this, vcView
, phwnd
, mWidget
.get()));
5068 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
5069 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5070 ("0x%p TSFTextStore::GetWnd() FAILED due to "
5071 "called with invalid view",
5073 return E_INVALIDARG
;
5077 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5078 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
5081 return E_INVALIDARG
;
5084 *phwnd
= mWidget
? mWidget
->GetWindowHandle() : nullptr;
5086 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5087 ("0x%p TSFTextStore::GetWnd() succeeded: *phwnd=0x%p", this,
5088 static_cast<void*>(*phwnd
)));
5093 TSFTextStore::InsertTextAtSelection(DWORD dwFlags
, const WCHAR
* pchText
,
5094 ULONG cch
, LONG
* pacpStart
, LONG
* pacpEnd
,
5095 TS_TEXTCHANGE
* pChange
) {
5097 sTextStoreLog
, LogLevel::Info
,
5098 ("0x%p TSFTextStore::InsertTextAtSelection(dwFlags=%s, "
5099 "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
5100 "pChange=0x%p), IsComposing()=%s",
5104 : dwFlags
== TF_IAS_NOQUERY
5106 : dwFlags
== TF_IAS_QUERYONLY
? "TF_IAS_QUERYONLY" : "Unknown",
5107 pchText
, pchText
&& cch
? GetEscapedUTF8String(pchText
, cch
).get() : "",
5108 cch
, pacpStart
, pacpEnd
, pChange
,
5109 GetBoolName(mComposition
.IsComposing())));
5111 if (cch
&& !pchText
) {
5112 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5113 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5116 return E_INVALIDARG
;
5119 if (TS_IAS_QUERYONLY
== dwFlags
) {
5120 if (!IsReadLocked()) {
5121 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5122 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5123 "not locked (read)",
5128 if (!pacpStart
|| !pacpEnd
) {
5129 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5130 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5133 return E_INVALIDARG
;
5136 // Get selection first
5137 Selection
& selectionForTSF
= SelectionForTSFRef();
5138 if (selectionForTSF
.IsDirty()) {
5139 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5140 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5141 "SelectionForTSFRef() failure",
5146 // Simulate text insertion
5147 *pacpStart
= selectionForTSF
.StartOffset();
5148 *pacpEnd
= selectionForTSF
.EndOffset();
5150 pChange
->acpStart
= selectionForTSF
.StartOffset();
5151 pChange
->acpOldEnd
= selectionForTSF
.EndOffset();
5152 pChange
->acpNewEnd
=
5153 selectionForTSF
.StartOffset() + static_cast<LONG
>(cch
);
5156 if (!IsReadWriteLocked()) {
5157 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5158 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5159 "not locked (read-write)",
5165 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5166 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5169 return E_INVALIDARG
;
5172 if (TS_IAS_NOQUERY
!= dwFlags
&& (!pacpStart
|| !pacpEnd
)) {
5173 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5174 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5177 return E_INVALIDARG
;
5180 if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText
, cch
),
5182 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5183 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5184 "InsertTextAtSelectionInternal() failure",
5189 if (TS_IAS_NOQUERY
!= dwFlags
) {
5190 *pacpStart
= pChange
->acpStart
;
5191 *pacpEnd
= pChange
->acpNewEnd
;
5194 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5195 ("0x%p TSFTextStore::InsertTextAtSelection() succeeded: "
5196 "*pacpStart=%ld, *pacpEnd=%ld, "
5197 "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })",
5198 this, pacpStart
? *pacpStart
: 0, pacpEnd
? *pacpEnd
: 0,
5199 pChange
? pChange
->acpStart
: 0, pChange
? pChange
->acpOldEnd
: 0,
5200 pChange
? pChange
->acpNewEnd
: 0));
5204 bool TSFTextStore::InsertTextAtSelectionInternal(const nsAString
& aInsertStr
,
5205 TS_TEXTCHANGE
* aTextChange
) {
5206 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5207 ("0x%p TSFTextStore::InsertTextAtSelectionInternal("
5208 "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
5209 this, GetEscapedUTF8String(aInsertStr
).get(), aTextChange
,
5210 GetBoolName(mComposition
.IsComposing())));
5212 Content
& contentForTSF
= ContentForTSFRef();
5213 if (!contentForTSF
.IsInitialized()) {
5214 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5215 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() failed "
5216 "due to ContentForTSFRef() failure()",
5221 MaybeDispatchKeyboardEventAsProcessedByIME();
5224 sTextStoreLog
, LogLevel::Error
,
5225 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() FAILED due to "
5226 "destroyed during dispatching a keyboard event",
5231 TS_SELECTION_ACP oldSelection
= contentForTSF
.Selection().ACP();
5232 if (!mComposition
.IsComposing()) {
5233 // Use a temporary composition to contain the text
5234 PendingAction
* compositionStart
= mPendingActions
.AppendElements(2);
5235 PendingAction
* compositionEnd
= compositionStart
+ 1;
5237 compositionStart
->mType
= PendingAction::Type::eCompositionStart
;
5238 compositionStart
->mSelectionStart
= oldSelection
.acpStart
;
5239 compositionStart
->mSelectionLength
=
5240 oldSelection
.acpEnd
- oldSelection
.acpStart
;
5241 compositionStart
->mAdjustSelection
= false;
5243 compositionEnd
->mType
= PendingAction::Type::eCompositionEnd
;
5244 compositionEnd
->mData
= aInsertStr
;
5245 compositionEnd
->mSelectionStart
= compositionStart
->mSelectionStart
;
5247 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5248 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() "
5249 "appending pending compositionstart and compositionend... "
5250 "PendingCompositionStart={ mSelectionStart=%d, "
5251 "mSelectionLength=%d }, PendingCompositionEnd={ mData=\"%s\" "
5252 "(Length()=%u), mSelectionStart=%d }",
5253 this, compositionStart
->mSelectionStart
,
5254 compositionStart
->mSelectionLength
,
5255 GetEscapedUTF8String(compositionEnd
->mData
).get(),
5256 compositionEnd
->mData
.Length(), compositionEnd
->mSelectionStart
));
5259 contentForTSF
.ReplaceSelectedTextWith(aInsertStr
);
5262 aTextChange
->acpStart
= oldSelection
.acpStart
;
5263 aTextChange
->acpOldEnd
= oldSelection
.acpEnd
;
5264 aTextChange
->acpNewEnd
= contentForTSF
.Selection().EndOffset();
5268 sTextStoreLog
, LogLevel::Debug
,
5269 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() "
5270 "succeeded: mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ "
5271 "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
5272 this, mWidget
.get(), GetBoolName(mWidget
? mWidget
->Destroyed() : true),
5273 aTextChange
? aTextChange
->acpStart
: 0,
5274 aTextChange
? aTextChange
->acpOldEnd
: 0,
5275 aTextChange
? aTextChange
->acpNewEnd
: 0));
5280 TSFTextStore::InsertEmbeddedAtSelection(DWORD dwFlags
, IDataObject
* pDataObject
,
5281 LONG
* pacpStart
, LONG
* pacpEnd
,
5282 TS_TEXTCHANGE
* pChange
) {
5283 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5284 ("0x%p TSFTextStore::InsertEmbeddedAtSelection() called "
5285 "but not supported (E_NOTIMPL)",
5288 // embedded objects are not supported
5293 TSFTextStore::RecordCompositionStartAction(ITfCompositionView
* aComposition
,
5295 bool aPreserveSelection
) {
5296 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5297 ("0x%p TSFTextStore::RecordCompositionStartAction("
5298 "aComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), "
5299 "mComposition.mView=0x%p",
5300 this, aComposition
, aRange
, GetBoolName(aPreserveSelection
),
5301 mComposition
.mView
.get()));
5303 LONG start
= 0, length
= 0;
5304 HRESULT hr
= GetRangeExtent(aRange
, &start
, &length
);
5306 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5307 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED "
5308 "due to GetRangeExtent() failure",
5313 return RecordCompositionStartAction(aComposition
, start
, length
,
5314 aPreserveSelection
);
5318 TSFTextStore::RecordCompositionStartAction(ITfCompositionView
* aComposition
,
5319 LONG aStart
, LONG aLength
,
5320 bool aPreserveSelection
) {
5321 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5322 ("0x%p TSFTextStore::RecordCompositionStartAction("
5323 "aComposition=0x%p, aStart=%d, aLength=%d, aPreserveSelection=%s), "
5324 "mComposition.mView=0x%p",
5325 this, aComposition
, aStart
, aLength
, GetBoolName(aPreserveSelection
),
5326 mComposition
.mView
.get()));
5328 Content
& contentForTSF
= ContentForTSFRef();
5329 if (!contentForTSF
.IsInitialized()) {
5330 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5331 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED "
5332 "due to ContentForTSFRef() failure",
5337 MaybeDispatchKeyboardEventAsProcessedByIME();
5340 sTextStoreLog
, LogLevel::Error
,
5341 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED due to "
5342 "destroyed during dispatching a keyboard event",
5347 CompleteLastActionIfStillIncomplete();
5349 // TIP may have inserted text at selection before calling
5350 // OnStartComposition(). In this case, we've already created a pending
5351 // compositionend. If new composition replaces all commit string of the
5352 // pending compositionend, we should cancel the pending compositionend and
5353 // keep the previous composition normally.
5354 // On Windows 7, MS-IME for Korean, MS-IME 2010 for Korean and MS Old Hangul
5355 // may start composition with calling InsertTextAtSelection() and
5356 // OnStartComposition() with this order (bug 1208043).
5357 // On Windows 10, MS Pinyin, MS Wubi, MS ChangJie and MS Quick commits
5358 // last character and replace it with empty string with new composition
5359 // when user removes last character of composition string with Backspace
5360 // key (bug 1462257).
5361 if (!aPreserveSelection
&&
5362 IsLastPendingActionCompositionEndAt(aStart
, aLength
)) {
5363 const PendingAction
& pendingCompositionEnd
= mPendingActions
.LastElement();
5364 contentForTSF
.RestoreCommittedComposition(aComposition
,
5365 pendingCompositionEnd
);
5366 mPendingActions
.RemoveLastElement();
5367 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5368 ("0x%p TSFTextStore::RecordCompositionStartAction() "
5369 "succeeded: restoring the committed string as composing string, "
5370 "mComposition={ mStart=%ld, mString.Length()=%ld, "
5371 "mSelectionForTSF={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
5372 "style.fInterimChar=%s } }",
5373 this, mComposition
.mStart
, mComposition
.mString
.Length(),
5374 mSelectionForTSF
.StartOffset(), mSelectionForTSF
.EndOffset(),
5375 GetActiveSelEndName(mSelectionForTSF
.ActiveSelEnd()),
5376 GetBoolName(mSelectionForTSF
.IsInterimChar())));
5380 PendingAction
* action
= mPendingActions
.AppendElement();
5381 action
->mType
= PendingAction::Type::eCompositionStart
;
5382 action
->mSelectionStart
= aStart
;
5383 action
->mSelectionLength
= aLength
;
5385 Selection
& selectionForTSF
= SelectionForTSFRef();
5386 if (selectionForTSF
.IsDirty()) {
5387 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5388 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED "
5389 "due to SelectionForTSFRef() failure",
5391 action
->mAdjustSelection
= true;
5392 } else if (selectionForTSF
.MinOffset() != aStart
||
5393 selectionForTSF
.MaxOffset() != aStart
+ aLength
) {
5394 // If new composition range is different from current selection range,
5395 // we need to set selection before dispatching compositionstart event.
5396 action
->mAdjustSelection
= true;
5398 // We shouldn't dispatch selection set event before dispatching
5399 // compositionstart event because it may cause put caret different
5400 // position in HTML editor since generated flat text content and offset in
5401 // it are lossy data of HTML contents.
5402 action
->mAdjustSelection
= false;
5405 contentForTSF
.StartComposition(aComposition
, *action
, aPreserveSelection
);
5406 action
->mData
= mComposition
.mString
;
5408 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5409 ("0x%p TSFTextStore::RecordCompositionStartAction() succeeded: "
5410 "mComposition={ mStart=%ld, mString.Length()=%ld, "
5411 "mSelectionForTSF={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
5412 "style.fInterimChar=%s } }",
5413 this, mComposition
.mStart
, mComposition
.mString
.Length(),
5414 mSelectionForTSF
.StartOffset(), mSelectionForTSF
.EndOffset(),
5415 GetActiveSelEndName(mSelectionForTSF
.ActiveSelEnd()),
5416 GetBoolName(mSelectionForTSF
.IsInterimChar())));
5421 TSFTextStore::RecordCompositionEndAction() {
5422 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5423 ("0x%p TSFTextStore::RecordCompositionEndAction(), "
5424 "mComposition={ mView=0x%p, mString=\"%s\" }",
5425 this, mComposition
.mView
.get(),
5426 GetEscapedUTF8String(mComposition
.mString
).get()));
5428 MOZ_ASSERT(mComposition
.IsComposing());
5430 MaybeDispatchKeyboardEventAsProcessedByIME();
5432 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5433 ("0x%p TSFTextStore::RecordCompositionEndAction() FAILED due to "
5434 "destroyed during dispatching a keyboard event",
5439 // If we're handling incomplete composition update or already handled
5440 // composition update, we can forget them since composition end will send
5441 // the latest composition string and it overwrites the composition string
5442 // even if we dispatch eCompositionChange event before that. So, let's
5443 // forget all composition updates now.
5444 RemoveLastCompositionUpdateActions();
5445 PendingAction
* action
= mPendingActions
.AppendElement();
5446 action
->mType
= PendingAction::Type::eCompositionEnd
;
5447 action
->mData
= mComposition
.mString
;
5448 action
->mSelectionStart
= mComposition
.mStart
;
5450 Content
& contentForTSF
= ContentForTSFRef();
5451 if (!contentForTSF
.IsInitialized()) {
5452 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5453 ("0x%p TSFTextStore::RecordCompositionEndAction() FAILED due "
5454 "to ContentForTSFRef() failure",
5458 contentForTSF
.EndComposition(*action
);
5460 // If this composition was restart but the composition doesn't modify
5461 // anything, we should remove the pending composition for preventing to
5462 // dispatch redundant composition events.
5463 for (size_t i
= mPendingActions
.Length(), j
= 1; i
> 0; --i
, ++j
) {
5464 PendingAction
& pendingAction
= mPendingActions
[i
- 1];
5465 if (pendingAction
.mType
== PendingAction::Type::eCompositionStart
) {
5466 if (pendingAction
.mData
!= action
->mData
) {
5469 // When only setting selection is necessary, we should append it.
5470 if (pendingAction
.mAdjustSelection
) {
5471 LONG selectionStart
= pendingAction
.mSelectionStart
;
5472 LONG selectionLength
= pendingAction
.mSelectionLength
;
5474 PendingAction
* setSelection
= mPendingActions
.AppendElement();
5475 setSelection
->mType
= PendingAction::Type::eSetSelection
;
5476 setSelection
->mSelectionStart
= selectionStart
;
5477 setSelection
->mSelectionLength
= selectionLength
;
5478 setSelection
->mSelectionReversed
= false;
5480 // Remove the redundant pending composition.
5481 mPendingActions
.RemoveElementsAt(i
- 1, j
);
5482 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5483 ("0x%p TSFTextStore::RecordCompositionEndAction(), "
5484 "succeeded, but the composition was canceled due to redundant",
5491 sTextStoreLog
, LogLevel::Info
,
5492 ("0x%p TSFTextStore::RecordCompositionEndAction(), succeeded", this));
5497 TSFTextStore::OnStartComposition(ITfCompositionView
* pComposition
, BOOL
* pfOk
) {
5498 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5499 ("0x%p TSFTextStore::OnStartComposition(pComposition=0x%p, "
5500 "pfOk=0x%p), mComposition.mView=0x%p",
5501 this, pComposition
, pfOk
, mComposition
.mView
.get()));
5503 AutoPendingActionAndContentFlusher
flusher(this);
5507 // Only one composition at a time
5508 if (mComposition
.IsComposing()) {
5509 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5510 ("0x%p TSFTextStore::OnStartComposition() FAILED due to "
5511 "there is another composition already (but returns S_OK)",
5516 RefPtr
<ITfRange
> range
;
5517 HRESULT hr
= pComposition
->GetRange(getter_AddRefs(range
));
5519 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5520 ("0x%p TSFTextStore::OnStartComposition() FAILED due to "
5521 "pComposition->GetRange() failure",
5525 hr
= RecordCompositionStartAction(pComposition
, range
, false);
5527 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5528 ("0x%p TSFTextStore::OnStartComposition() FAILED due to "
5529 "RecordCompositionStartAction() failure",
5535 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5536 ("0x%p TSFTextStore::OnStartComposition() succeeded", this));
5541 TSFTextStore::OnUpdateComposition(ITfCompositionView
* pComposition
,
5542 ITfRange
* pRangeNew
) {
5543 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5544 ("0x%p TSFTextStore::OnUpdateComposition(pComposition=0x%p, "
5545 "pRangeNew=0x%p), mComposition.mView=0x%p",
5546 this, pComposition
, pRangeNew
, mComposition
.mView
.get()));
5548 AutoPendingActionAndContentFlusher
flusher(this);
5550 if (!mDocumentMgr
|| !mContext
) {
5551 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5552 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5553 "not ready for the composition",
5555 return E_UNEXPECTED
;
5557 if (!mComposition
.IsComposing()) {
5558 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5559 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5560 "no active composition",
5562 return E_UNEXPECTED
;
5564 if (mComposition
.mView
!= pComposition
) {
5565 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5566 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5567 "different composition view specified",
5569 return E_UNEXPECTED
;
5572 // pRangeNew is null when the update is not complete
5574 MaybeDispatchKeyboardEventAsProcessedByIME();
5576 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5577 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5578 "destroyed during dispatching a keyboard event",
5582 PendingAction
* action
= LastOrNewPendingCompositionUpdate();
5583 action
->mIncomplete
= true;
5584 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5585 ("0x%p TSFTextStore::OnUpdateComposition() succeeded but "
5591 HRESULT hr
= RestartCompositionIfNecessary(pRangeNew
);
5593 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5594 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5595 "RestartCompositionIfNecessary() failure",
5600 hr
= RecordCompositionUpdateAction();
5602 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5603 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5604 "RecordCompositionUpdateAction() failure",
5609 if (MOZ_LOG_TEST(sTextStoreLog
, LogLevel::Info
)) {
5610 Selection
& selectionForTSF
= SelectionForTSFRef();
5611 if (selectionForTSF
.IsDirty()) {
5612 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5613 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5614 "SelectionForTSFRef() failure",
5618 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5619 ("0x%p TSFTextStore::OnUpdateComposition() succeeded: "
5620 "mComposition={ mStart=%ld, mString=\"%s\" }, "
5621 "SelectionForTSFRef()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
5622 this, mComposition
.mStart
,
5623 GetEscapedUTF8String(mComposition
.mString
).get(),
5624 selectionForTSF
.StartOffset(), selectionForTSF
.EndOffset(),
5625 GetActiveSelEndName(selectionForTSF
.ActiveSelEnd())));
5631 TSFTextStore::OnEndComposition(ITfCompositionView
* pComposition
) {
5632 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5633 ("0x%p TSFTextStore::OnEndComposition(pComposition=0x%p), "
5634 "mComposition={ mView=0x%p, mString=\"%s\" }",
5635 this, pComposition
, mComposition
.mView
.get(),
5636 GetEscapedUTF8String(mComposition
.mString
).get()));
5638 AutoPendingActionAndContentFlusher
flusher(this);
5640 if (!mComposition
.IsComposing()) {
5641 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5642 ("0x%p TSFTextStore::OnEndComposition() FAILED due to "
5643 "no active composition",
5645 return E_UNEXPECTED
;
5648 if (mComposition
.mView
!= pComposition
) {
5649 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5650 ("0x%p TSFTextStore::OnEndComposition() FAILED due to "
5651 "different composition view specified",
5653 return E_UNEXPECTED
;
5656 HRESULT hr
= RecordCompositionEndAction();
5658 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5659 ("0x%p TSFTextStore::OnEndComposition() FAILED due to "
5660 "RecordCompositionEndAction() failure",
5665 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5666 ("0x%p TSFTextStore::OnEndComposition(), succeeded", this));
5671 TSFTextStore::AdviseMouseSink(ITfRangeACP
* range
, ITfMouseSink
* pSink
,
5673 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5674 ("0x%p TSFTextStore::AdviseMouseSink(range=0x%p, pSink=0x%p, "
5676 this, range
, pSink
, pdwCookie
));
5679 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5680 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to the "
5681 "pdwCookie is null",
5683 return E_INVALIDARG
;
5685 // Initialize the result with invalid cookie for safety.
5686 *pdwCookie
= MouseTracker::kInvalidCookie
;
5689 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5690 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to the "
5693 return E_INVALIDARG
;
5696 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5697 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to the "
5700 return E_INVALIDARG
;
5703 // Looking for an unusing tracker.
5704 MouseTracker
* tracker
= nullptr;
5705 for (size_t i
= 0; i
< mMouseTrackers
.Length(); i
++) {
5706 if (mMouseTrackers
[i
].IsUsing()) {
5709 tracker
= &mMouseTrackers
[i
];
5711 // If there is no unusing tracker, create new one.
5712 // XXX Should we make limitation of the number of installs?
5714 tracker
= mMouseTrackers
.AppendElement();
5715 HRESULT hr
= tracker
->Init(this);
5717 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5718 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to "
5719 "failure of MouseTracker::Init()",
5724 HRESULT hr
= tracker
->AdviseSink(this, range
, pSink
);
5726 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5727 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to failure "
5728 "of MouseTracker::Init()",
5732 *pdwCookie
= tracker
->Cookie();
5733 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5734 ("0x%p TSFTextStore::AdviseMouseSink(), succeeded, "
5741 TSFTextStore::UnadviseMouseSink(DWORD dwCookie
) {
5743 sTextStoreLog
, LogLevel::Info
,
5744 ("0x%p TSFTextStore::UnadviseMouseSink(dwCookie=%d)", this, dwCookie
));
5745 if (dwCookie
== MouseTracker::kInvalidCookie
) {
5746 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5747 ("0x%p TSFTextStore::UnadviseMouseSink() FAILED due to "
5748 "the cookie is invalid value",
5750 return E_INVALIDARG
;
5752 // The cookie value must be an index of mMouseTrackers.
5753 // We can use this shortcut for now.
5754 if (static_cast<size_t>(dwCookie
) >= mMouseTrackers
.Length()) {
5755 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5756 ("0x%p TSFTextStore::UnadviseMouseSink() FAILED due to "
5757 "the cookie is too large value",
5759 return E_INVALIDARG
;
5761 MouseTracker
& tracker
= mMouseTrackers
[dwCookie
];
5762 if (!tracker
.IsUsing()) {
5763 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5764 ("0x%p TSFTextStore::UnadviseMouseSink() FAILED due to "
5765 "the found tracker uninstalled already",
5767 return E_INVALIDARG
;
5769 tracker
.UnadviseSink();
5770 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5771 ("0x%p TSFTextStore::UnadviseMouseSink(), succeeded", this));
5776 nsresult
TSFTextStore::OnFocusChange(bool aGotFocus
,
5777 nsWindowBase
* aFocusedWidget
,
5778 const InputContext
& aContext
) {
5779 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5780 (" TSFTextStore::OnFocusChange(aGotFocus=%s, "
5781 "aFocusedWidget=0x%p, aContext=%s), "
5782 "sThreadMgr=0x%p, sEnabledTextStore=0x%p",
5783 GetBoolName(aGotFocus
), aFocusedWidget
,
5784 GetInputContextString(aContext
).get(), sThreadMgr
.get(),
5785 sEnabledTextStore
.get()));
5787 if (NS_WARN_IF(!IsInTSFMode())) {
5788 return NS_ERROR_NOT_AVAILABLE
;
5791 RefPtr
<ITfDocumentMgr
> prevFocusedDocumentMgr
;
5792 bool hasFocus
= ThinksHavingFocus();
5793 RefPtr
<TSFTextStore
> oldTextStore
= sEnabledTextStore
.forget();
5795 // If currently oldTextStore still has focus, notifies TSF of losing focus.
5797 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
5798 DebugOnly
<HRESULT
> hr
= threadMgr
->AssociateFocus(
5799 oldTextStore
->mWidget
->GetWindowHandle(), nullptr,
5800 getter_AddRefs(prevFocusedDocumentMgr
));
5801 NS_ASSERTION(SUCCEEDED(hr
), "Disassociating focus failed");
5802 NS_ASSERTION(prevFocusedDocumentMgr
== oldTextStore
->mDocumentMgr
,
5803 "different documentMgr has been associated with the window");
5806 // Even if there was a focused TextStore, we won't use it with new focused
5807 // editor. So, release it now.
5809 oldTextStore
->Destroy();
5812 if (NS_WARN_IF(!sThreadMgr
)) {
5813 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5814 (" TSFTextStore::OnFocusChange() FAILED, due to "
5815 "sThreadMgr being destroyed during calling "
5816 "ITfThreadMgr::AssociateFocus()"));
5817 return NS_ERROR_FAILURE
;
5819 if (NS_WARN_IF(sEnabledTextStore
)) {
5821 sTextStoreLog
, LogLevel::Error
,
5822 (" TSFTextStore::OnFocusChange() FAILED, due to "
5823 "nested event handling has created another focused TextStore during "
5824 "calling ITfThreadMgr::AssociateFocus()"));
5825 return NS_ERROR_FAILURE
;
5828 // If this is a notification of blur, move focus to the dummy document
5830 if (!aGotFocus
|| !aContext
.mIMEState
.IsEditable()) {
5831 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
5832 RefPtr
<ITfDocumentMgr
> disabledDocumentMgr
= sDisabledDocumentMgr
;
5833 HRESULT hr
= threadMgr
->SetFocus(disabledDocumentMgr
);
5834 if (NS_WARN_IF(FAILED(hr
))) {
5835 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5836 (" TSFTextStore::OnFocusChange() FAILED due to "
5837 "ITfThreadMgr::SetFocus() failure"));
5838 return NS_ERROR_FAILURE
;
5843 // If an editor is getting focus, create new TextStore and set focus.
5844 if (NS_WARN_IF(!CreateAndSetFocus(aFocusedWidget
, aContext
))) {
5845 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5846 (" TSFTextStore::OnFocusChange() FAILED due to "
5847 "ITfThreadMgr::CreateAndSetFocus() failure"));
5848 return NS_ERROR_FAILURE
;
5854 void TSFTextStore::EnsureToDestroyAndReleaseEnabledTextStoreIf(
5855 RefPtr
<TSFTextStore
>& aTextStore
) {
5856 aTextStore
->Destroy();
5857 if (sEnabledTextStore
== aTextStore
) {
5858 sEnabledTextStore
= nullptr;
5860 aTextStore
= nullptr;
5864 bool TSFTextStore::CreateAndSetFocus(nsWindowBase
* aFocusedWidget
,
5865 const InputContext
& aContext
) {
5866 // TSF might do something which causes that we need to access static methods
5867 // of TSFTextStore. At that time, sEnabledTextStore may be necessary.
5868 // So, we should set sEnabledTextStore directly.
5869 RefPtr
<TSFTextStore
> textStore
= new TSFTextStore();
5870 sEnabledTextStore
= textStore
;
5871 if (NS_WARN_IF(!textStore
->Init(aFocusedWidget
, aContext
))) {
5872 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5873 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5874 "TSFTextStore::Init() failure"));
5875 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5878 RefPtr
<ITfDocumentMgr
> newDocMgr
= textStore
->mDocumentMgr
;
5879 if (NS_WARN_IF(!newDocMgr
)) {
5880 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5881 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5882 "invalid TSFTextStore::mDocumentMgr"));
5883 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5886 if (aContext
.mIMEState
.mEnabled
== IMEState::PASSWORD
) {
5887 MarkContextAsKeyboardDisabled(textStore
->mContext
);
5888 RefPtr
<ITfContext
> topContext
;
5889 newDocMgr
->GetTop(getter_AddRefs(topContext
));
5890 if (topContext
&& topContext
!= textStore
->mContext
) {
5891 MarkContextAsKeyboardDisabled(topContext
);
5896 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
5897 hr
= threadMgr
->SetFocus(newDocMgr
);
5899 if (NS_WARN_IF(FAILED(hr
))) {
5900 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5901 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5902 "ITfTheadMgr::SetFocus() failure"));
5903 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5906 if (NS_WARN_IF(!sThreadMgr
)) {
5907 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5908 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5909 "sThreadMgr being destroyed during calling "
5910 "ITfTheadMgr::SetFocus()"));
5911 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5914 if (NS_WARN_IF(sEnabledTextStore
!= textStore
)) {
5915 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5916 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5917 "creating TextStore has lost focus during calling "
5918 "ITfThreadMgr::SetFocus()"));
5919 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5923 // Use AssociateFocus() for ensuring that any native focus event
5924 // never steal focus from our documentMgr.
5925 RefPtr
<ITfDocumentMgr
> prevFocusedDocumentMgr
;
5926 hr
= threadMgr
->AssociateFocus(aFocusedWidget
->GetWindowHandle(), newDocMgr
,
5927 getter_AddRefs(prevFocusedDocumentMgr
));
5928 if (NS_WARN_IF(FAILED(hr
))) {
5929 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5930 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5931 "ITfTheadMgr::AssociateFocus() failure"));
5932 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5935 if (NS_WARN_IF(!sThreadMgr
)) {
5936 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5937 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5938 "sThreadMgr being destroyed during calling "
5939 "ITfTheadMgr::AssociateFocus()"));
5940 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5943 if (NS_WARN_IF(sEnabledTextStore
!= textStore
)) {
5944 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5945 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5946 "creating TextStore has lost focus during calling "
5947 "ITfTheadMgr::AssociateFocus()"));
5948 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5952 if (textStore
->mSink
) {
5953 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5954 (" TSFTextStore::CreateAndSetFocus(), calling "
5955 "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE) for 0x%p...",
5957 RefPtr
<ITextStoreACPSink
> sink
= textStore
->mSink
;
5958 sink
->OnLayoutChange(TS_LC_CREATE
, TEXTSTORE_DEFAULT_VIEW
);
5959 if (NS_WARN_IF(sEnabledTextStore
!= textStore
)) {
5960 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5961 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5962 "creating TextStore has lost focus during calling "
5963 "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE)"));
5964 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5972 IMENotificationRequests
TSFTextStore::GetIMENotificationRequests() {
5973 if (!sEnabledTextStore
|| NS_WARN_IF(!sEnabledTextStore
->mDocumentMgr
)) {
5974 // If there is no active text store, we don't need any notifications
5975 // since there is no sink which needs notifications.
5976 return IMENotificationRequests();
5979 // Otherwise, requests all notifications since even if some of them may not
5980 // be required by the sink of active TIP, active TIP may be changed and
5981 // other TIPs may need all notifications.
5982 // Note that Windows temporarily steal focus from active window if the main
5983 // process which created the window becomes busy. In this case, we shouldn't
5984 // commit composition since user may want to continue to compose the
5985 // composition after becoming not busy. Therefore, we need notifications
5986 // even during deactive.
5987 // Be aware, we don't need to check actual focused text store. For example,
5988 // MS-IME for Japanese handles focus messages by themselves and sets focused
5989 // text store to nullptr when the process is being inactivated. However,
5990 // we still need to reuse sEnabledTextStore if the process is activated and
5991 // focused element isn't changed. Therefore, if sEnabledTextStore isn't
5992 // nullptr, we need to keep notifying the sink even when it is not focused
5993 // text store for the thread manager.
5994 return IMENotificationRequests(
5995 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
5996 IMENotificationRequests::NOTIFY_POSITION_CHANGE
|
5997 IMENotificationRequests::NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
|
5998 IMENotificationRequests::NOTIFY_DURING_DEACTIVE
);
6001 nsresult
TSFTextStore::OnTextChangeInternal(
6002 const IMENotification
& aIMENotification
) {
6003 const TextChangeDataBase
& textChangeData
= aIMENotification
.mTextChangeData
;
6005 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6006 ("0x%p TSFTextStore::OnTextChangeInternal(aIMENotification={ "
6007 "mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, "
6008 "mRemovedEndOffset=%lu, mAddedEndOffset=%lu, "
6009 "mCausedOnlyByComposition=%s, "
6010 "mIncludingChangesDuringComposition=%s, "
6011 "mIncludingChangesWithoutComposition=%s }), "
6012 "mDestroyed=%s, mSink=0x%p, mSinkMask=%s, "
6013 "mComposition.IsComposing()=%s",
6014 this, aIMENotification
.mMessage
, textChangeData
.mStartOffset
,
6015 textChangeData
.mRemovedEndOffset
, textChangeData
.mAddedEndOffset
,
6016 GetBoolName(textChangeData
.mCausedOnlyByComposition
),
6017 GetBoolName(textChangeData
.mIncludingChangesDuringComposition
),
6018 GetBoolName(textChangeData
.mIncludingChangesWithoutComposition
),
6019 GetBoolName(mDestroyed
), mSink
.get(),
6020 GetSinkMaskNameStr(mSinkMask
).get(),
6021 GetBoolName(mComposition
.IsComposing())));
6024 // If this instance is already destroyed, we shouldn't notify TSF of any
6029 mDeferNotifyingTSF
= false;
6031 // Different from selection change, we don't modify anything with text
6032 // change data. Therefore, if neither TSF not TIP wants text change
6033 // notifications, we don't need to store the changes.
6034 if (!mSink
|| !(mSinkMask
& TS_AS_TEXT_CHANGE
)) {
6038 // Merge any text change data even if it's caused by composition.
6039 mPendingTextChangeData
.MergeWith(textChangeData
);
6041 MaybeFlushPendingNotifications();
6046 void TSFTextStore::NotifyTSFOfTextChange() {
6047 MOZ_ASSERT(!mDestroyed
);
6048 MOZ_ASSERT(!IsReadLocked());
6049 MOZ_ASSERT(!mComposition
.IsComposing());
6050 MOZ_ASSERT(mPendingTextChangeData
.IsValid());
6052 // If the text changes are caused only by composition, we don't need to
6053 // notify TSF of the text changes.
6054 if (mPendingTextChangeData
.mCausedOnlyByComposition
) {
6055 mPendingTextChangeData
.Clear();
6059 // First, forget cached selection.
6060 mSelectionForTSF
.MarkDirty();
6062 // For making it safer, we should check if there is a valid sink to receive
6063 // text change notification.
6064 if (NS_WARN_IF(!mSink
) || NS_WARN_IF(!(mSinkMask
& TS_AS_TEXT_CHANGE
))) {
6065 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6066 ("0x%p TSFTextStore::NotifyTSFOfTextChange() FAILED due to "
6067 "mSink is not ready to call ITextStoreACPSink::OnTextChange()...",
6069 mPendingTextChangeData
.Clear();
6073 if (NS_WARN_IF(!mPendingTextChangeData
.IsInInt32Range())) {
6074 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6075 ("0x%p TSFTextStore::NotifyTSFOfTextChange() FAILED due to "
6076 "offset is too big for calling "
6077 "ITextStoreACPSink::OnTextChange()...",
6079 mPendingTextChangeData
.Clear();
6083 TS_TEXTCHANGE textChange
;
6084 textChange
.acpStart
= static_cast<LONG
>(mPendingTextChangeData
.mStartOffset
);
6085 textChange
.acpOldEnd
=
6086 static_cast<LONG
>(mPendingTextChangeData
.mRemovedEndOffset
);
6087 textChange
.acpNewEnd
=
6088 static_cast<LONG
>(mPendingTextChangeData
.mAddedEndOffset
);
6089 mPendingTextChangeData
.Clear();
6092 sTextStoreLog
, LogLevel::Info
,
6093 ("0x%p TSFTextStore::NotifyTSFOfTextChange(), calling "
6094 "ITextStoreACPSink::OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
6095 "acpNewEnd=%ld })...",
6096 this, textChange
.acpStart
, textChange
.acpOldEnd
, textChange
.acpNewEnd
));
6097 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6098 sink
->OnTextChange(0, &textChange
);
6101 nsresult
TSFTextStore::OnSelectionChangeInternal(
6102 const IMENotification
& aIMENotification
) {
6103 const SelectionChangeDataBase
& selectionChangeData
=
6104 aIMENotification
.mSelectionChangeData
;
6105 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6106 ("0x%p TSFTextStore::OnSelectionChangeInternal("
6107 "aIMENotification={ mSelectionChangeData={ mOffset=%lu, "
6108 "Length()=%lu, mReversed=%s, mWritingMode=%s, "
6109 "mCausedByComposition=%s, mCausedBySelectionEvent=%s, "
6110 "mOccurredDuringComposition=%s } }), mDestroyed=%s, "
6111 "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
6112 "mComposition.IsComposing()=%s",
6113 this, selectionChangeData
.mOffset
, selectionChangeData
.Length(),
6114 GetBoolName(selectionChangeData
.mReversed
),
6115 GetWritingModeName(selectionChangeData
.GetWritingMode()).get(),
6116 GetBoolName(selectionChangeData
.mCausedByComposition
),
6117 GetBoolName(selectionChangeData
.mCausedBySelectionEvent
),
6118 GetBoolName(selectionChangeData
.mOccurredDuringComposition
),
6119 GetBoolName(mDestroyed
), mSink
.get(),
6120 GetSinkMaskNameStr(mSinkMask
).get(),
6121 GetBoolName(mIsRecordingActionsWithoutLock
),
6122 GetBoolName(mComposition
.IsComposing())));
6125 // If this instance is already destroyed, we shouldn't notify TSF of any
6130 mDeferNotifyingTSF
= false;
6132 // Assign the new selection change data to the pending selection change data
6133 // because only the latest selection data is necessary.
6134 // Note that this is necessary to update mSelectionForTSF. Therefore, even if
6135 // neither TSF nor TIP wants selection change notifications, we need to
6136 // store the selection information.
6137 mPendingSelectionChangeData
.Assign(selectionChangeData
);
6139 // Flush remaining pending notifications here if it's possible.
6140 MaybeFlushPendingNotifications();
6142 // If we're available, we should create native caret instead of IMEHandler
6143 // because we may have some cache to do it.
6144 // Note that if we have composition, we'll notified composition-updated
6145 // later so that we don't need to create native caret in such case.
6146 if (!IsHandlingComposition() && IMEHandler::NeedsToCreateNativeCaret()) {
6147 CreateNativeCaret();
6153 void TSFTextStore::NotifyTSFOfSelectionChange() {
6154 MOZ_ASSERT(!mDestroyed
);
6155 MOZ_ASSERT(!IsReadLocked());
6156 MOZ_ASSERT(!mComposition
.IsComposing());
6157 MOZ_ASSERT(mPendingSelectionChangeData
.IsValid());
6159 // If selection range isn't actually changed, we don't need to notify TSF
6160 // of this selection change.
6161 if (!mSelectionForTSF
.SetSelection(
6162 mPendingSelectionChangeData
.mOffset
,
6163 mPendingSelectionChangeData
.Length(),
6164 mPendingSelectionChangeData
.mReversed
,
6165 mPendingSelectionChangeData
.GetWritingMode())) {
6166 mPendingSelectionChangeData
.Clear();
6167 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6168 ("0x%p TSFTextStore::NotifyTSFOfSelectionChange(), "
6169 "selection isn't actually changed.",
6174 mPendingSelectionChangeData
.Clear();
6176 if (!mSink
|| !(mSinkMask
& TS_AS_SEL_CHANGE
)) {
6180 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6181 ("0x%p TSFTextStore::NotifyTSFOfSelectionChange(), calling "
6182 "ITextStoreACPSink::OnSelectionChange()...",
6184 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6185 sink
->OnSelectionChange();
6188 nsresult
TSFTextStore::OnLayoutChangeInternal() {
6190 // If this instance is already destroyed, we shouldn't notify TSF of any
6195 NS_ENSURE_TRUE(mContext
, NS_ERROR_FAILURE
);
6196 NS_ENSURE_TRUE(mSink
, NS_ERROR_FAILURE
);
6198 mDeferNotifyingTSF
= false;
6200 nsresult rv
= NS_OK
;
6202 // We need to notify TSF of layout change even if the document is locked.
6203 // So, don't use MaybeFlushPendingNotifications() for flushing pending
6205 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6206 ("0x%p TSFTextStore::OnLayoutChangeInternal(), calling "
6207 "NotifyTSFOfLayoutChange()...",
6209 if (NS_WARN_IF(!NotifyTSFOfLayoutChange())) {
6210 rv
= NS_ERROR_FAILURE
;
6213 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6214 ("0x%p TSFTextStore::OnLayoutChangeInternal(), calling "
6215 "MaybeFlushPendingNotifications()...",
6217 MaybeFlushPendingNotifications();
6222 bool TSFTextStore::NotifyTSFOfLayoutChange() {
6223 MOZ_ASSERT(!mDestroyed
);
6225 // If we're waiting a query of layout information from TIP, it means that
6226 // we've returned TS_E_NOLAYOUT error.
6227 bool returnedNoLayoutError
= mHasReturnedNoLayoutError
|| mWaitingQueryLayout
;
6229 // If we returned TS_E_NOLAYOUT, TIP should query the computed layout again.
6230 mWaitingQueryLayout
= returnedNoLayoutError
;
6232 // For avoiding to call this method again at unlocking the document during
6233 // calls of OnLayoutChange(), reset mHasReturnedNoLayoutError.
6234 mHasReturnedNoLayoutError
= false;
6236 // Now, layout has been computed. We should notify mContentForTSF for
6237 // making GetTextExt() and GetACPFromPoint() not return TS_E_NOLAYOUT.
6238 if (mContentForTSF
.IsInitialized()) {
6239 mContentForTSF
.OnLayoutChanged();
6242 if (IMEHandler::NeedsToCreateNativeCaret()) {
6243 // If we're available, we should create native caret instead of IMEHandler
6244 // because we may have some cache to do it.
6245 CreateNativeCaret();
6247 // Now, the caret position is different from ours. Destroy the native caret
6248 // if we've create it only for GetTextExt().
6249 IMEHandler::MaybeDestroyNativeCaret();
6252 // This method should return true if either way succeeds.
6256 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6257 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6258 "calling ITextStoreACPSink::OnLayoutChange()...",
6260 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6261 HRESULT hr
= sink
->OnLayoutChange(TS_LC_CHANGE
, TEXTSTORE_DEFAULT_VIEW
);
6262 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6263 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6264 "called ITextStoreACPSink::OnLayoutChange()",
6266 ret
= SUCCEEDED(hr
);
6269 // The layout change caused by composition string change should cause
6270 // calling ITfContextOwnerServices::OnLayoutChange() too.
6271 if (returnedNoLayoutError
&& mContext
) {
6272 RefPtr
<ITfContextOwnerServices
> service
;
6273 mContext
->QueryInterface(IID_ITfContextOwnerServices
,
6274 getter_AddRefs(service
));
6276 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6277 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6278 "calling ITfContextOwnerServices::OnLayoutChange()...",
6280 HRESULT hr
= service
->OnLayoutChange();
6281 ret
= ret
&& SUCCEEDED(hr
);
6282 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6283 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6284 "called ITfContextOwnerServices::OnLayoutChange()",
6289 if (!mWidget
|| mWidget
->Destroyed()) {
6290 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6291 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6292 "the widget is destroyed during calling OnLayoutChange()",
6298 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6299 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6300 "the TSFTextStore instance is destroyed during calling "
6306 // If we returned TS_E_NOLAYOUT again, we need another call of
6307 // OnLayoutChange() later. So, let's wait a query from TIP.
6308 if (mHasReturnedNoLayoutError
) {
6309 mWaitingQueryLayout
= true;
6312 if (!mWaitingQueryLayout
) {
6313 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6314 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6315 "succeeded notifying TIP of our layout change",
6320 // If we believe that TIP needs to retry to retrieve our layout information
6321 // later, we should call it with ::PostMessage() hack.
6322 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6323 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6324 "posing MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE for calling "
6325 "OnLayoutChange() again...",
6327 ::PostMessage(mWidget
->GetWindowHandle(), MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE
,
6328 reinterpret_cast<WPARAM
>(this), 0);
6333 void TSFTextStore::NotifyTSFOfLayoutChangeAgain() {
6334 // Don't notify TSF of layout change after destroyed.
6336 mWaitingQueryLayout
= false;
6340 // Before preforming this method, TIP has accessed our layout information by
6341 // itself. In such case, we don't need to call OnLayoutChange() anymore.
6342 if (!mWaitingQueryLayout
) {
6346 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6347 ("0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6348 "calling NotifyTSFOfLayoutChange()...",
6350 NotifyTSFOfLayoutChange();
6352 // If TIP didn't retrieved our layout information during a call of
6353 // NotifyTSFOfLayoutChange(), it means that the TIP already gave up to
6354 // retry to retrieve layout information or doesn't necessary it anymore.
6355 // But don't forget that the call may have caused returning TS_E_NOLAYOUT
6356 // error again. In such case we still need to call OnLayoutChange() later.
6357 if (!mHasReturnedNoLayoutError
&& mWaitingQueryLayout
) {
6358 mWaitingQueryLayout
= false;
6359 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
6360 ("0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6361 "called NotifyTSFOfLayoutChange() but TIP didn't retry to "
6362 "retrieve the layout information",
6365 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6366 ("0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6367 "called NotifyTSFOfLayoutChange()",
6372 nsresult
TSFTextStore::OnUpdateCompositionInternal() {
6373 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6374 ("0x%p TSFTextStore::OnUpdateCompositionInternal(), "
6375 "mDestroyed=%s, mDeferNotifyingTSF=%s",
6376 this, GetBoolName(mDestroyed
), GetBoolName(mDeferNotifyingTSF
)));
6378 // There are nothing to do after destroyed.
6383 // Update cached data now because all pending events have been handled now.
6384 mContentForTSF
.OnCompositionEventsHandled();
6386 // If composition is completely finished both in TSF/TIP and the focused
6387 // editor which may be in a remote process, we can clear the cache and don't
6388 // have it until starting next composition.
6389 if (!mComposition
.IsComposing() && !IsHandlingComposition()) {
6390 mDeferClearingContentForTSF
= false;
6392 mDeferNotifyingTSF
= false;
6393 MaybeFlushPendingNotifications();
6395 // If we're available, we should create native caret instead of IMEHandler
6396 // because we may have some cache to do it.
6397 if (IMEHandler::NeedsToCreateNativeCaret()) {
6398 CreateNativeCaret();
6404 nsresult
TSFTextStore::OnMouseButtonEventInternal(
6405 const IMENotification
& aIMENotification
) {
6407 // If this instance is already destroyed, we shouldn't notify TSF of any
6412 if (mMouseTrackers
.IsEmpty()) {
6416 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6417 ("0x%p TSFTextStore::OnMouseButtonEventInternal("
6418 "aIMENotification={ mEventMessage=%s, mOffset=%u, mCursorPos={ "
6419 "mX=%d, mY=%d }, mCharRect={ mX=%d, mY=%d, mWidth=%d, mHeight=%d }, "
6420 "mButton=%s, mButtons=%s, mModifiers=%s })",
6421 this, ToChar(aIMENotification
.mMouseButtonEventData
.mEventMessage
),
6422 aIMENotification
.mMouseButtonEventData
.mOffset
,
6423 aIMENotification
.mMouseButtonEventData
.mCursorPos
.mX
,
6424 aIMENotification
.mMouseButtonEventData
.mCursorPos
.mY
,
6425 aIMENotification
.mMouseButtonEventData
.mCharRect
.mX
,
6426 aIMENotification
.mMouseButtonEventData
.mCharRect
.mY
,
6427 aIMENotification
.mMouseButtonEventData
.mCharRect
.mWidth
,
6428 aIMENotification
.mMouseButtonEventData
.mCharRect
.mHeight
,
6429 GetMouseButtonName(aIMENotification
.mMouseButtonEventData
.mButton
),
6430 GetMouseButtonsName(aIMENotification
.mMouseButtonEventData
.mButtons
)
6432 GetModifiersName(aIMENotification
.mMouseButtonEventData
.mModifiers
)
6435 uint32_t offset
= aIMENotification
.mMouseButtonEventData
.mOffset
;
6436 nsIntRect charRect
=
6437 aIMENotification
.mMouseButtonEventData
.mCharRect
.AsIntRect();
6438 nsIntPoint cursorPos
=
6439 aIMENotification
.mMouseButtonEventData
.mCursorPos
.AsIntPoint();
6441 if (charRect
.Width() > 0) {
6442 int32_t cursorXInChar
= cursorPos
.x
- charRect
.X();
6443 quadrant
= cursorXInChar
* 4 / charRect
.Width();
6444 quadrant
= (quadrant
+ 2) % 4;
6446 ULONG edge
= quadrant
< 2 ? offset
+ 1 : offset
;
6447 DWORD buttonStatus
= 0;
6449 aIMENotification
.mMouseButtonEventData
.mEventMessage
== eMouseUp
;
6451 switch (aIMENotification
.mMouseButtonEventData
.mButton
) {
6452 case MouseButton::eLeft
:
6453 buttonStatus
= MK_LBUTTON
;
6455 case MouseButton::eMiddle
:
6456 buttonStatus
= MK_MBUTTON
;
6458 case MouseButton::eRight
:
6459 buttonStatus
= MK_RBUTTON
;
6463 if (aIMENotification
.mMouseButtonEventData
.mModifiers
& MODIFIER_CONTROL
) {
6464 buttonStatus
|= MK_CONTROL
;
6466 if (aIMENotification
.mMouseButtonEventData
.mModifiers
& MODIFIER_SHIFT
) {
6467 buttonStatus
|= MK_SHIFT
;
6469 for (size_t i
= 0; i
< mMouseTrackers
.Length(); i
++) {
6470 MouseTracker
& tracker
= mMouseTrackers
[i
];
6471 if (!tracker
.IsUsing() || !tracker
.InRange(offset
)) {
6474 if (tracker
.OnMouseButtonEvent(edge
- tracker
.RangeStart(), quadrant
,
6476 return NS_SUCCESS_EVENT_CONSUMED
;
6482 void TSFTextStore::CreateNativeCaret() {
6483 MOZ_ASSERT(!IMEHandler::IsA11yHandlingNativeCaret());
6485 IMEHandler::MaybeDestroyNativeCaret();
6487 // Don't create native caret after destroyed.
6492 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6493 ("0x%p TSFTextStore::CreateNativeCaret(), "
6494 "mComposition.IsComposing()=%s",
6495 this, GetBoolName(mComposition
.IsComposing())));
6497 Selection
& selectionForTSF
= SelectionForTSFRef();
6498 if (selectionForTSF
.IsDirty()) {
6499 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6500 ("0x%p TSFTextStore::CreateNativeCaret() FAILED due to "
6501 "SelectionForTSFRef() failure",
6506 WidgetQueryContentEvent
queryCaretRect(true, eQueryCaretRect
, mWidget
);
6507 mWidget
->InitEvent(queryCaretRect
);
6509 WidgetQueryContentEvent::Options options
;
6510 // XXX If this is called without composition and the selection isn't
6511 // collapsed, is it OK?
6512 int64_t caretOffset
= selectionForTSF
.MaxOffset();
6513 if (mComposition
.IsComposing()) {
6514 // If there is a composition, use insertion point relative query for
6515 // deciding caret position because composition might be at different
6516 // position where TSFTextStore believes it at.
6517 options
.mRelativeToInsertionPoint
= true;
6518 caretOffset
-= mComposition
.mStart
;
6519 } else if (!CanAccessActualContentDirectly()) {
6520 // If TSF/TIP cannot access actual content directly, there may be pending
6521 // text and/or selection changes which have not been notified TSF yet.
6522 // Therefore, we should use relative to insertion point query since
6523 // TSF/TIP computes the offset from the cached selection.
6524 options
.mRelativeToInsertionPoint
= true;
6525 caretOffset
-= mSelectionForTSF
.StartOffset();
6527 queryCaretRect
.InitForQueryCaretRect(caretOffset
, options
);
6529 DispatchEvent(queryCaretRect
);
6530 if (NS_WARN_IF(!queryCaretRect
.mSucceeded
)) {
6531 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6532 ("0x%p TSFTextStore::CreateNativeCaret() FAILED due to "
6533 "eQueryCaretRect failure (offset=%d)",
6534 this, caretOffset
));
6538 if (!IMEHandler::CreateNativeCaret(static_cast<nsWindow
*>(mWidget
.get()),
6539 queryCaretRect
.mReply
.mRect
)) {
6540 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6541 ("0x%p TSFTextStore::CreateNativeCaret() FAILED due to "
6542 "IMEHandler::CreateNativeCaret() failure",
6548 void TSFTextStore::CommitCompositionInternal(bool aDiscard
) {
6549 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6550 ("0x%p TSFTextStore::CommitCompositionInternal(aDiscard=%s), "
6551 "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
6552 "mComposition.mString=\"%s\"",
6553 this, GetBoolName(aDiscard
), mSink
.get(), mContext
.get(),
6554 mComposition
.mView
.get(),
6555 GetEscapedUTF8String(mComposition
.mString
).get()));
6557 // If the document is locked, TSF will fail to commit composition since
6558 // TSF needs another document lock. So, let's put off the request.
6559 // Note that TextComposition will commit composition in the focused editor
6560 // with the latest composition string for web apps and waits asynchronous
6561 // committing messages. Therefore, we can and need to perform this
6563 if (IsReadLocked()) {
6564 if (mDeferCommittingComposition
|| mDeferCancellingComposition
) {
6565 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6566 ("0x%p TSFTextStore::CommitCompositionInternal(), "
6567 "does nothing because already called and waiting unlock...",
6572 mDeferCancellingComposition
= true;
6574 mDeferCommittingComposition
= true;
6576 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6577 ("0x%p TSFTextStore::CommitCompositionInternal(), "
6578 "putting off to request to %s composition after unlocking the "
6580 this, aDiscard
? "cancel" : "commit"));
6584 if (mComposition
.IsComposing() && aDiscard
) {
6585 LONG endOffset
= mComposition
.EndOffset();
6586 mComposition
.mString
.Truncate(0);
6587 // Note that don't notify TSF of text change after this is destroyed.
6588 if (mSink
&& !mDestroyed
) {
6589 TS_TEXTCHANGE textChange
;
6590 textChange
.acpStart
= mComposition
.mStart
;
6591 textChange
.acpOldEnd
= endOffset
;
6592 textChange
.acpNewEnd
= mComposition
.mStart
;
6593 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6594 ("0x%p TSFTextStore::CommitCompositionInternal(), calling"
6595 "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
6596 "acpNewEnd=%ld })...",
6597 this, textChange
.acpStart
, textChange
.acpOldEnd
,
6598 textChange
.acpNewEnd
));
6599 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6600 sink
->OnTextChange(0, &textChange
);
6603 // Terminate two contexts, the base context (mContext) and the top
6604 // if the top context is not the same as the base context
6605 RefPtr
<ITfContext
> context
= mContext
;
6608 RefPtr
<ITfContextOwnerCompositionServices
> services
;
6609 context
->QueryInterface(IID_ITfContextOwnerCompositionServices
,
6610 getter_AddRefs(services
));
6612 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6613 ("0x%p TSFTextStore::CommitCompositionInternal(), "
6614 "requesting TerminateComposition() for the context 0x%p...",
6615 this, context
.get()));
6616 services
->TerminateComposition(nullptr);
6619 if (context
!= mContext
) break;
6620 if (mDocumentMgr
) mDocumentMgr
->GetTop(getter_AddRefs(context
));
6621 } while (context
!= mContext
);
6624 static bool GetCompartment(IUnknown
* pUnk
, const GUID
& aID
,
6625 ITfCompartment
** aCompartment
) {
6626 if (!pUnk
) return false;
6628 RefPtr
<ITfCompartmentMgr
> compMgr
;
6629 pUnk
->QueryInterface(IID_ITfCompartmentMgr
, getter_AddRefs(compMgr
));
6630 if (!compMgr
) return false;
6632 return SUCCEEDED(compMgr
->GetCompartment(aID
, aCompartment
)) &&
6633 (*aCompartment
) != nullptr;
6637 void TSFTextStore::SetIMEOpenState(bool aState
) {
6638 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6639 ("TSFTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState
)));
6645 RefPtr
<ITfCompartment
> comp
= GetCompartmentForOpenClose();
6646 if (NS_WARN_IF(!comp
)) {
6647 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6648 (" TSFTextStore::SetIMEOpenState() FAILED due to"
6649 "no compartment available"));
6655 variant
.lVal
= aState
;
6656 HRESULT hr
= comp
->SetValue(sClientId
, &variant
);
6657 if (NS_WARN_IF(FAILED(hr
))) {
6658 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6659 (" TSFTextStore::SetIMEOpenState() FAILED due to "
6660 "ITfCompartment::SetValue() failure, hr=0x%08X",
6664 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6665 (" TSFTextStore::SetIMEOpenState(), setting "
6666 "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...",
6671 bool TSFTextStore::GetIMEOpenState() {
6676 RefPtr
<ITfCompartment
> comp
= GetCompartmentForOpenClose();
6677 if (NS_WARN_IF(!comp
)) {
6682 ::VariantInit(&variant
);
6683 HRESULT hr
= comp
->GetValue(&variant
);
6684 if (NS_WARN_IF(FAILED(hr
))) {
6685 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6686 ("TSFTextStore::GetIMEOpenState() FAILED due to "
6687 "ITfCompartment::GetValue() failure, hr=0x%08X",
6691 // Until IME is open in this process, the result may be empty.
6692 if (variant
.vt
== VT_EMPTY
) {
6695 if (NS_WARN_IF(variant
.vt
!= VT_I4
)) {
6696 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6697 ("TSFTextStore::GetIMEOpenState() FAILED due to "
6698 "invalid result of ITfCompartment::GetValue()"));
6699 ::VariantClear(&variant
);
6703 return variant
.lVal
!= 0;
6707 void TSFTextStore::SetInputContext(nsWindowBase
* aWidget
,
6708 const InputContext
& aContext
,
6709 const InputContextAction
& aAction
) {
6710 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6711 ("TSFTextStore::SetInputContext(aWidget=%p, "
6712 "aContext=%s, aAction.mFocusChange=%s), "
6713 "sEnabledTextStore(0x%p)={ mWidget=0x%p }, ThinksHavingFocus()=%s",
6714 aWidget
, GetInputContextString(aContext
).get(),
6715 GetFocusChangeName(aAction
.mFocusChange
), sEnabledTextStore
.get(),
6716 sEnabledTextStore
? sEnabledTextStore
->mWidget
.get() : nullptr,
6717 GetBoolName(ThinksHavingFocus())));
6719 // When this is called when the widget is created, there is nothing to do.
6720 if (aAction
.mFocusChange
== InputContextAction::WIDGET_CREATED
) {
6724 NS_ENSURE_TRUE_VOID(IsInTSFMode());
6726 if (aAction
.mFocusChange
!= InputContextAction::FOCUS_NOT_CHANGED
) {
6727 if (sEnabledTextStore
) {
6728 RefPtr
<TSFTextStore
> textStore(sEnabledTextStore
);
6729 textStore
->SetInputScope(aContext
.mHTMLInputType
,
6730 aContext
.mHTMLInputInputmode
,
6731 aContext
.mInPrivateBrowsing
);
6736 // If focus isn't actually changed but the enabled state is changed,
6737 // emulate the focus move.
6738 if (!ThinksHavingFocus() && aContext
.mIMEState
.IsEditable()) {
6739 OnFocusChange(true, aWidget
, aContext
);
6740 } else if (ThinksHavingFocus() && !aContext
.mIMEState
.IsEditable()) {
6741 OnFocusChange(false, aWidget
, aContext
);
6746 void TSFTextStore::MarkContextAsKeyboardDisabled(ITfContext
* aContext
) {
6747 VARIANT variant_int4_value1
;
6748 variant_int4_value1
.vt
= VT_I4
;
6749 variant_int4_value1
.lVal
= 1;
6751 RefPtr
<ITfCompartment
> comp
;
6752 if (!GetCompartment(aContext
, GUID_COMPARTMENT_KEYBOARD_DISABLED
,
6753 getter_AddRefs(comp
))) {
6754 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6755 ("TSFTextStore::MarkContextAsKeyboardDisabled() failed"
6761 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6762 ("TSFTextStore::MarkContextAsKeyboardDisabled(), setting "
6763 "to disable context 0x%p...",
6765 comp
->SetValue(sClientId
, &variant_int4_value1
);
6769 void TSFTextStore::MarkContextAsEmpty(ITfContext
* aContext
) {
6770 VARIANT variant_int4_value1
;
6771 variant_int4_value1
.vt
= VT_I4
;
6772 variant_int4_value1
.lVal
= 1;
6774 RefPtr
<ITfCompartment
> comp
;
6775 if (!GetCompartment(aContext
, GUID_COMPARTMENT_EMPTYCONTEXT
,
6776 getter_AddRefs(comp
))) {
6777 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6778 ("TSFTextStore::MarkContextAsEmpty() failed"
6784 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6785 ("TSFTextStore::MarkContextAsEmpty(), setting "
6786 "to mark empty context 0x%p...",
6788 comp
->SetValue(sClientId
, &variant_int4_value1
);
6792 void TSFTextStore::Initialize() {
6793 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6794 ("TSFTextStore::Initialize() is called..."));
6797 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6798 (" TSFTextStore::Initialize() FAILED due to already initialized"));
6802 bool enableTsf
= Preferences::GetBool(kPrefNameEnableTSF
, false);
6803 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6804 (" TSFTextStore::Initialize(), TSF is %s",
6805 enableTsf
? "enabled" : "disabled"));
6810 RefPtr
<ITfThreadMgr
> threadMgr
;
6812 ::CoCreateInstance(CLSID_TF_ThreadMgr
, nullptr, CLSCTX_INPROC_SERVER
,
6813 IID_ITfThreadMgr
, getter_AddRefs(threadMgr
));
6814 if (FAILED(hr
) || !threadMgr
) {
6815 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6816 (" TSFTextStore::Initialize() FAILED to "
6817 "create the thread manager, hr=0x%08X",
6822 hr
= threadMgr
->Activate(&sClientId
);
6824 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6825 (" TSFTextStore::Initialize() FAILED to activate, hr=0x%08X", hr
));
6829 RefPtr
<ITfDocumentMgr
> disabledDocumentMgr
;
6830 hr
= threadMgr
->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr
));
6831 if (FAILED(hr
) || !disabledDocumentMgr
) {
6832 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6833 (" TSFTextStore::Initialize() FAILED to create "
6834 "a document manager for disabled mode, hr=0x%08X",
6839 RefPtr
<ITfContext
> disabledContext
;
6840 DWORD editCookie
= 0;
6841 hr
= disabledDocumentMgr
->CreateContext(
6842 sClientId
, 0, nullptr, getter_AddRefs(disabledContext
), &editCookie
);
6843 if (FAILED(hr
) || !disabledContext
) {
6844 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6845 (" TSFTextStore::Initialize() FAILED to create "
6846 "a context for disabled mode, hr=0x%08X",
6851 MarkContextAsKeyboardDisabled(disabledContext
);
6852 MarkContextAsEmpty(disabledContext
);
6854 sThreadMgr
= threadMgr
;
6855 sDisabledDocumentMgr
= disabledDocumentMgr
;
6856 sDisabledContext
= disabledContext
;
6858 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6859 (" TSFTextStore::Initialize(), sThreadMgr=0x%p, "
6860 "sClientId=0x%08X, sDisabledDocumentMgr=0x%p, sDisabledContext=%p",
6861 sThreadMgr
.get(), sClientId
, sDisabledDocumentMgr
.get(),
6862 sDisabledContext
.get()));
6866 already_AddRefed
<ITfThreadMgr
> TSFTextStore::GetThreadMgr() {
6867 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
6868 return threadMgr
.forget();
6872 already_AddRefed
<ITfMessagePump
> TSFTextStore::GetMessagePump() {
6873 static bool sInitialized
= false;
6878 RefPtr
<ITfMessagePump
> messagePump
= sMessagePump
;
6879 return messagePump
.forget();
6881 // If it tried to retrieve ITfMessagePump from sThreadMgr but it failed,
6882 // we shouldn't retry it at every message due to performance reason.
6883 // Although this shouldn't occur actually.
6887 sInitialized
= true;
6889 RefPtr
<ITfMessagePump
> messagePump
;
6890 HRESULT hr
= sThreadMgr
->QueryInterface(IID_ITfMessagePump
,
6891 getter_AddRefs(messagePump
));
6892 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!messagePump
)) {
6893 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6894 ("TSFTextStore::GetMessagePump() FAILED to "
6895 "QI message pump from the thread manager, hr=0x%08X",
6899 sMessagePump
= messagePump
;
6900 return messagePump
.forget();
6904 already_AddRefed
<ITfDisplayAttributeMgr
>
6905 TSFTextStore::GetDisplayAttributeMgr() {
6906 RefPtr
<ITfDisplayAttributeMgr
> displayAttributeMgr
;
6907 if (sDisplayAttrMgr
) {
6908 displayAttributeMgr
= sDisplayAttrMgr
;
6909 return displayAttributeMgr
.forget();
6912 HRESULT hr
= ::CoCreateInstance(
6913 CLSID_TF_DisplayAttributeMgr
, nullptr, CLSCTX_INPROC_SERVER
,
6914 IID_ITfDisplayAttributeMgr
, getter_AddRefs(displayAttributeMgr
));
6915 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!displayAttributeMgr
)) {
6916 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6917 ("TSFTextStore::GetDisplayAttributeMgr() FAILED to create "
6918 "a display attribute manager instance, hr=0x%08X",
6922 sDisplayAttrMgr
= displayAttributeMgr
;
6923 return displayAttributeMgr
.forget();
6927 already_AddRefed
<ITfCategoryMgr
> TSFTextStore::GetCategoryMgr() {
6928 RefPtr
<ITfCategoryMgr
> categoryMgr
;
6930 categoryMgr
= sCategoryMgr
;
6931 return categoryMgr
.forget();
6934 ::CoCreateInstance(CLSID_TF_CategoryMgr
, nullptr, CLSCTX_INPROC_SERVER
,
6935 IID_ITfCategoryMgr
, getter_AddRefs(categoryMgr
));
6936 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!categoryMgr
)) {
6937 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6938 ("TSFTextStore::GetCategoryMgr() FAILED to create "
6939 "a category manager instance, hr=0x%08X",
6943 sCategoryMgr
= categoryMgr
;
6944 return categoryMgr
.forget();
6948 already_AddRefed
<ITfCompartment
> TSFTextStore::GetCompartmentForOpenClose() {
6949 if (sCompartmentForOpenClose
) {
6950 RefPtr
<ITfCompartment
> compartment
= sCompartmentForOpenClose
;
6951 return compartment
.forget();
6958 RefPtr
<ITfCompartmentMgr
> compartmentMgr
;
6959 HRESULT hr
= sThreadMgr
->QueryInterface(IID_ITfCompartmentMgr
,
6960 getter_AddRefs(compartmentMgr
));
6961 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!compartmentMgr
)) {
6962 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6963 ("TSFTextStore::GetCompartmentForOpenClose() FAILED due to"
6964 "sThreadMgr not having ITfCompartmentMgr, hr=0x%08X",
6969 RefPtr
<ITfCompartment
> compartment
;
6970 hr
= compartmentMgr
->GetCompartment(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE
,
6971 getter_AddRefs(compartment
));
6972 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!compartment
)) {
6973 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6974 ("TSFTextStore::GetCompartmentForOpenClose() FAILED due to"
6975 "ITfCompartmentMgr::GetCompartment() failuere, hr=0x%08X",
6980 sCompartmentForOpenClose
= compartment
;
6981 return compartment
.forget();
6985 already_AddRefed
<ITfInputProcessorProfiles
>
6986 TSFTextStore::GetInputProcessorProfiles() {
6987 RefPtr
<ITfInputProcessorProfiles
> inputProcessorProfiles
;
6988 if (sInputProcessorProfiles
) {
6989 inputProcessorProfiles
= sInputProcessorProfiles
;
6990 return inputProcessorProfiles
.forget();
6992 // XXX MSDN documents that ITfInputProcessorProfiles is available only on
6993 // desktop apps. However, there is no known way to obtain
6994 // ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
6996 HRESULT hr
= ::CoCreateInstance(
6997 CLSID_TF_InputProcessorProfiles
, nullptr, CLSCTX_INPROC_SERVER
,
6998 IID_ITfInputProcessorProfiles
, getter_AddRefs(inputProcessorProfiles
));
6999 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!inputProcessorProfiles
)) {
7000 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7001 ("TSFTextStore::GetInputProcessorProfiles() FAILED to create input "
7002 "processor profiles, hr=0x%08X",
7006 sInputProcessorProfiles
= inputProcessorProfiles
;
7007 return inputProcessorProfiles
.forget();
7011 void TSFTextStore::Terminate() {
7012 MOZ_LOG(sTextStoreLog
, LogLevel::Info
, ("TSFTextStore::Terminate()"));
7014 TSFStaticSink::Shutdown();
7016 sDisplayAttrMgr
= nullptr;
7017 sCategoryMgr
= nullptr;
7018 sEnabledTextStore
= nullptr;
7019 sDisabledDocumentMgr
= nullptr;
7020 sDisabledContext
= nullptr;
7021 sCompartmentForOpenClose
= nullptr;
7022 sInputProcessorProfiles
= nullptr;
7025 sThreadMgr
->Deactivate();
7026 sThreadMgr
= nullptr;
7027 sMessagePump
= nullptr;
7028 sKeystrokeMgr
= nullptr;
7033 bool TSFTextStore::ProcessRawKeyMessage(const MSG
& aMsg
) {
7035 return false; // not in TSF mode
7037 static bool sInitialized
= false;
7038 if (!sKeystrokeMgr
) {
7039 // If it tried to retrieve ITfKeystrokeMgr from sThreadMgr but it failed,
7040 // we shouldn't retry it at every keydown nor keyup due to performance
7041 // reason. Although this shouldn't occur actually.
7045 sInitialized
= true;
7046 RefPtr
<ITfKeystrokeMgr
> keystrokeMgr
;
7047 HRESULT hr
= sThreadMgr
->QueryInterface(IID_ITfKeystrokeMgr
,
7048 getter_AddRefs(keystrokeMgr
));
7049 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!keystrokeMgr
)) {
7050 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7051 ("TSFTextStore::ProcessRawKeyMessage() FAILED to "
7052 "QI keystroke manager from the thread manager, hr=0x%08X",
7056 sKeystrokeMgr
= keystrokeMgr
.forget();
7059 if (aMsg
.message
== WM_KEYDOWN
) {
7060 RefPtr
<TSFTextStore
> textStore(sEnabledTextStore
);
7062 textStore
->OnStartToHandleKeyMessage();
7063 if (NS_WARN_IF(textStore
!= sEnabledTextStore
)) {
7064 // Let's handle the key message with new focused TSFTextStore.
7065 textStore
= sEnabledTextStore
;
7068 AutoRestore
<const MSG
*> savePreviousKeyMsg(sHandlingKeyMsg
);
7069 AutoRestore
<bool> saveKeyEventDispatched(sIsKeyboardEventDispatched
);
7070 sHandlingKeyMsg
= &aMsg
;
7071 sIsKeyboardEventDispatched
= false;
7073 RefPtr
<ITfKeystrokeMgr
> keystrokeMgr
= sKeystrokeMgr
;
7074 HRESULT hr
= keystrokeMgr
->TestKeyDown(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
7075 if (FAILED(hr
) || !sKeystrokeMgr
|| !eaten
) {
7078 hr
= keystrokeMgr
->KeyDown(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
7080 textStore
->OnEndHandlingKeyMessage(!!eaten
);
7082 return SUCCEEDED(hr
) &&
7083 (eaten
|| !sKeystrokeMgr
|| sIsKeyboardEventDispatched
);
7085 if (aMsg
.message
== WM_KEYUP
) {
7086 RefPtr
<TSFTextStore
> textStore(sEnabledTextStore
);
7088 textStore
->OnStartToHandleKeyMessage();
7089 if (NS_WARN_IF(textStore
!= sEnabledTextStore
)) {
7090 // Let's handle the key message with new focused TSFTextStore.
7091 textStore
= sEnabledTextStore
;
7094 AutoRestore
<const MSG
*> savePreviousKeyMsg(sHandlingKeyMsg
);
7095 AutoRestore
<bool> saveKeyEventDispatched(sIsKeyboardEventDispatched
);
7096 sHandlingKeyMsg
= &aMsg
;
7097 sIsKeyboardEventDispatched
= false;
7099 RefPtr
<ITfKeystrokeMgr
> keystrokeMgr
= sKeystrokeMgr
;
7100 HRESULT hr
= keystrokeMgr
->TestKeyUp(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
7101 if (FAILED(hr
) || !sKeystrokeMgr
|| !eaten
) {
7104 hr
= keystrokeMgr
->KeyUp(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
7106 textStore
->OnEndHandlingKeyMessage(!!eaten
);
7108 return SUCCEEDED(hr
) &&
7109 (eaten
|| !sKeystrokeMgr
|| sIsKeyboardEventDispatched
);
7115 void TSFTextStore::ProcessMessage(nsWindowBase
* aWindow
, UINT aMessage
,
7116 WPARAM
& aWParam
, LPARAM
& aLParam
,
7117 MSGResult
& aResult
) {
7119 case WM_IME_SETCONTEXT
:
7120 // If a windowless plugin had focus and IME was handled on it, composition
7121 // window was set the position. After that, even in TSF mode, WinXP keeps
7122 // to use composition window at the position if the active IME is not
7123 // aware TSF. For avoiding this issue, we need to hide the composition
7126 aLParam
&= ~ISC_SHOWUICOMPOSITIONWINDOW
;
7130 // When an modal dialog such as a file picker is open, composition
7131 // should be committed because IME might be used on it.
7132 if (!IsComposingOn(aWindow
)) {
7135 CommitComposition(false);
7137 case MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE
: {
7138 TSFTextStore
* maybeTextStore
= reinterpret_cast<TSFTextStore
*>(aWParam
);
7139 if (maybeTextStore
== sEnabledTextStore
) {
7140 RefPtr
<TSFTextStore
> textStore(maybeTextStore
);
7141 textStore
->NotifyTSFOfLayoutChangeAgain();
7149 bool TSFTextStore::IsIMM_IMEActive() {
7150 return TSFStaticSink::IsIMM_IMEActive();
7154 bool TSFTextStore::IsMSJapaneseIMEActive() {
7155 return TSFStaticSink::IsMSJapaneseIMEActive();
7159 bool TSFTextStore::IsGoogleJapaneseInputActive() {
7160 return TSFStaticSink::IsGoogleJapaneseInputActive();
7163 /******************************************************************/
7164 /* TSFTextStore::Composition */
7165 /******************************************************************/
7167 void TSFTextStore::Composition::Start(ITfCompositionView
* aCompositionView
,
7168 LONG aCompositionStartOffset
,
7169 const nsAString
& aCompositionString
) {
7170 mView
= aCompositionView
;
7171 mString
= aCompositionString
;
7172 mStart
= aCompositionStartOffset
;
7175 void TSFTextStore::Composition::End() {
7180 /******************************************************************************
7181 * TSFTextStore::Content
7182 *****************************************************************************/
7184 const nsDependentSubstring
TSFTextStore::Content::GetSelectedText() const {
7185 MOZ_ASSERT(mInitialized
);
7186 return GetSubstring(static_cast<uint32_t>(mSelection
.StartOffset()),
7187 static_cast<uint32_t>(mSelection
.Length()));
7190 const nsDependentSubstring
TSFTextStore::Content::GetSubstring(
7191 uint32_t aStart
, uint32_t aLength
) const {
7192 MOZ_ASSERT(mInitialized
);
7193 return nsDependentSubstring(mText
, aStart
, aLength
);
7196 void TSFTextStore::Content::ReplaceSelectedTextWith(const nsAString
& aString
) {
7197 MOZ_ASSERT(mInitialized
);
7198 ReplaceTextWith(mSelection
.StartOffset(), mSelection
.Length(), aString
);
7201 inline uint32_t FirstDifferentCharOffset(const nsAString
& aStr1
,
7202 const nsAString
& aStr2
) {
7203 MOZ_ASSERT(aStr1
!= aStr2
);
7205 uint32_t minLength
= std::min(aStr1
.Length(), aStr2
.Length());
7206 for (; i
< minLength
&& aStr1
[i
] == aStr2
[i
]; i
++) {
7212 void TSFTextStore::Content::ReplaceTextWith(LONG aStart
, LONG aLength
,
7213 const nsAString
& aReplaceString
) {
7214 MOZ_ASSERT(mInitialized
);
7215 const nsDependentSubstring replacedString
= GetSubstring(
7216 static_cast<uint32_t>(aStart
), static_cast<uint32_t>(aLength
));
7217 if (aReplaceString
!= replacedString
) {
7218 uint32_t firstDifferentOffset
= mMinTextModifiedOffset
;
7219 if (mComposition
.IsComposing()) {
7220 // Emulate text insertion during compositions, because during a
7221 // composition, editor expects the whole composition string to
7222 // be sent in eCompositionChange, not just the inserted part.
7223 // The actual eCompositionChange will be sent in SetSelection
7224 // or OnUpdateComposition.
7225 MOZ_ASSERT(aStart
>= mComposition
.mStart
);
7226 MOZ_ASSERT(aStart
+ aLength
<= mComposition
.EndOffset());
7227 mComposition
.mString
.Replace(
7228 static_cast<uint32_t>(aStart
- mComposition
.mStart
),
7229 static_cast<uint32_t>(aLength
), aReplaceString
);
7230 // TIP may set composition string twice or more times during a document
7231 // lock. Therefore, we should compute the first difference offset with
7232 // mLastCompositionString.
7233 if (mComposition
.mString
!= mLastCompositionString
) {
7234 firstDifferentOffset
= mComposition
.mStart
+
7235 FirstDifferentCharOffset(mComposition
.mString
,
7236 mLastCompositionString
);
7237 // The previous change to the composition string is canceled.
7238 if (mMinTextModifiedOffset
>=
7239 static_cast<uint32_t>(mComposition
.mStart
) &&
7240 mMinTextModifiedOffset
< firstDifferentOffset
) {
7241 mMinTextModifiedOffset
= firstDifferentOffset
;
7243 } else if (mMinTextModifiedOffset
>=
7244 static_cast<uint32_t>(mComposition
.mStart
) &&
7245 mMinTextModifiedOffset
<
7246 static_cast<uint32_t>(mComposition
.EndOffset())) {
7247 // The previous change to the composition string is canceled.
7248 mMinTextModifiedOffset
= firstDifferentOffset
=
7249 mComposition
.EndOffset();
7251 mLatestCompositionEndOffset
= mComposition
.EndOffset();
7252 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7253 ("0x%p TSFTextStore::Content::ReplaceTextWith(aStart=%d, "
7254 "aLength=%d, aReplaceString=\"%s\"), mComposition={ mStart=%d, "
7255 "mString=\"%s\" }, mLastCompositionString=\"%s\", "
7256 "mMinTextModifiedOffset=%u, firstDifferentOffset=%u",
7257 this, aStart
, aLength
,
7258 GetEscapedUTF8String(aReplaceString
).get(), mComposition
.mStart
,
7259 GetEscapedUTF8String(mComposition
.mString
).get(),
7260 GetEscapedUTF8String(mLastCompositionString
).get(),
7261 mMinTextModifiedOffset
, firstDifferentOffset
));
7263 firstDifferentOffset
=
7264 static_cast<uint32_t>(aStart
) +
7265 FirstDifferentCharOffset(aReplaceString
, replacedString
);
7267 mMinTextModifiedOffset
=
7268 std::min(mMinTextModifiedOffset
, firstDifferentOffset
);
7269 mText
.Replace(static_cast<uint32_t>(aStart
), static_cast<uint32_t>(aLength
),
7272 // Selection should be collapsed at the end of the inserted string.
7273 mSelection
.CollapseAt(static_cast<uint32_t>(aStart
) +
7274 aReplaceString
.Length());
7277 void TSFTextStore::Content::StartComposition(
7278 ITfCompositionView
* aCompositionView
, const PendingAction
& aCompStart
,
7279 bool aPreserveSelection
) {
7280 MOZ_ASSERT(mInitialized
);
7281 MOZ_ASSERT(aCompositionView
);
7282 MOZ_ASSERT(!mComposition
.mView
);
7283 MOZ_ASSERT(aCompStart
.mType
== PendingAction::Type::eCompositionStart
);
7286 aCompositionView
, aCompStart
.mSelectionStart
,
7287 GetSubstring(static_cast<uint32_t>(aCompStart
.mSelectionStart
),
7288 static_cast<uint32_t>(aCompStart
.mSelectionLength
)));
7289 mLatestCompositionStartOffset
= mComposition
.mStart
;
7290 mLatestCompositionEndOffset
= mComposition
.EndOffset();
7291 if (!aPreserveSelection
) {
7292 // XXX Do we need to set a new writing-mode here when setting a new
7293 // selection? Currently, we just preserve the existing value.
7294 WritingMode writingMode
=
7295 mSelection
.IsDirty() ? WritingMode() : mSelection
.GetWritingMode();
7296 mSelection
.SetSelection(mComposition
.mStart
, mComposition
.mString
.Length(),
7297 false, writingMode
);
7301 void TSFTextStore::Content::RestoreCommittedComposition(
7302 ITfCompositionView
* aCompositionView
,
7303 const PendingAction
& aCanceledCompositionEnd
) {
7304 MOZ_ASSERT(mInitialized
);
7305 MOZ_ASSERT(aCompositionView
);
7306 MOZ_ASSERT(!mComposition
.mView
);
7307 MOZ_ASSERT(aCanceledCompositionEnd
.mType
==
7308 PendingAction::Type::eCompositionEnd
);
7311 static_cast<uint32_t>(aCanceledCompositionEnd
.mSelectionStart
),
7312 static_cast<uint32_t>(aCanceledCompositionEnd
.mData
.Length())) ==
7313 aCanceledCompositionEnd
.mData
);
7315 // Restore the committed string as composing string.
7316 mComposition
.Start(aCompositionView
, aCanceledCompositionEnd
.mSelectionStart
,
7317 aCanceledCompositionEnd
.mData
);
7318 mLatestCompositionStartOffset
= mComposition
.mStart
;
7319 mLatestCompositionEndOffset
= mComposition
.EndOffset();
7322 void TSFTextStore::Content::EndComposition(const PendingAction
& aCompEnd
) {
7323 MOZ_ASSERT(mInitialized
);
7324 MOZ_ASSERT(mComposition
.mView
);
7325 MOZ_ASSERT(aCompEnd
.mType
== PendingAction::Type::eCompositionEnd
);
7327 mSelection
.CollapseAt(mComposition
.mStart
+ aCompEnd
.mData
.Length());
7331 /******************************************************************************
7332 * TSFTextStore::MouseTracker
7333 *****************************************************************************/
7335 TSFTextStore::MouseTracker::MouseTracker()
7336 : mStart(-1), mLength(-1), mCookie(kInvalidCookie
) {}
7339 TSFTextStore::MouseTracker::Init(TSFTextStore
* aTextStore
) {
7340 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7341 ("0x%p TSFTextStore::MouseTracker::Init(aTextStore=0x%p), "
7342 "aTextStore->mMouseTrackers.Length()=%d",
7343 this, aTextStore
->mMouseTrackers
.Length()));
7345 if (&aTextStore
->mMouseTrackers
.LastElement() != this) {
7346 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7347 ("0x%p TSFTextStore::MouseTracker::Init() FAILED due to "
7348 "this is not the last element of mMouseTrackers",
7352 if (aTextStore
->mMouseTrackers
.Length() > kInvalidCookie
) {
7353 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7354 ("0x%p TSFTextStore::MouseTracker::Init() FAILED due to "
7355 "no new cookie available",
7359 MOZ_ASSERT(!aTextStore
->mMouseTrackers
.IsEmpty(),
7360 "This instance must be in TSFTextStore::mMouseTrackers");
7361 mCookie
= static_cast<DWORD
>(aTextStore
->mMouseTrackers
.Length() - 1);
7366 TSFTextStore::MouseTracker::AdviseSink(TSFTextStore
* aTextStore
,
7367 ITfRangeACP
* aTextRange
,
7368 ITfMouseSink
* aMouseSink
) {
7369 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7370 ("0x%p TSFTextStore::MouseTracker::AdviseSink(aTextStore=0x%p, "
7371 "aTextRange=0x%p, aMouseSink=0x%p), mCookie=%d, mSink=0x%p",
7372 this, aTextStore
, aTextRange
, aMouseSink
, mCookie
, mSink
.get()));
7373 MOZ_ASSERT(mCookie
!= kInvalidCookie
, "This hasn't been initalized?");
7376 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7377 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7378 "due to already being used",
7383 HRESULT hr
= aTextRange
->GetExtent(&mStart
, &mLength
);
7385 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7386 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7387 "due to failure of ITfRangeACP::GetExtent()",
7392 if (mStart
< 0 || mLength
<= 0) {
7393 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7394 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7395 "due to odd result of ITfRangeACP::GetExtent(), "
7396 "mStart=%d, mLength=%d",
7397 this, mStart
, mLength
));
7398 return E_INVALIDARG
;
7401 nsAutoString textContent
;
7402 if (NS_WARN_IF(!aTextStore
->GetCurrentText(textContent
))) {
7403 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7404 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7405 "due to failure of TSFTextStore::GetCurrentText()",
7410 if (textContent
.Length() <= static_cast<uint32_t>(mStart
) ||
7411 textContent
.Length() < static_cast<uint32_t>(mStart
+ mLength
)) {
7412 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7413 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7414 "due to out of range, mStart=%d, mLength=%d, "
7415 "textContent.Length()=%d",
7416 this, mStart
, mLength
, textContent
.Length()));
7417 return E_INVALIDARG
;
7422 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7423 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink(), "
7424 "succeeded, mStart=%d, mLength=%d, textContent.Length()=%d",
7425 this, mStart
, mLength
, textContent
.Length()));
7429 void TSFTextStore::MouseTracker::UnadviseSink() {
7430 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7431 ("0x%p TSFTextStore::MouseTracker::UnadviseSink(), "
7432 "mCookie=%d, mSink=0x%p, mStart=%d, mLength=%d",
7433 this, mCookie
, mSink
.get(), mStart
, mLength
));
7435 mStart
= mLength
= -1;
7438 bool TSFTextStore::MouseTracker::OnMouseButtonEvent(ULONG aEdge
,
7440 DWORD aButtonStatus
) {
7441 MOZ_ASSERT(IsUsing(), "The caller must check before calling OnMouseEvent()");
7444 RefPtr
<ITfMouseSink
> sink
= mSink
;
7445 HRESULT hr
= sink
->OnMouseEvent(aEdge
, aQuadrant
, aButtonStatus
, &eaten
);
7447 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7448 ("0x%p TSFTextStore::MouseTracker::OnMouseEvent(aEdge=%d, "
7449 "aQuadrant=%d, aButtonStatus=0x%08X), hr=0x%08X, eaten=%s",
7450 this, aEdge
, aQuadrant
, aButtonStatus
, hr
, GetBoolName(!!eaten
)));
7452 return SUCCEEDED(hr
) && eaten
;
7457 bool TSFTextStore::CurrentKeyboardLayoutHasIME() {
7458 RefPtr
<ITfInputProcessorProfiles
> inputProcessorProfiles
=
7459 TSFTextStore::GetInputProcessorProfiles();
7460 if (!inputProcessorProfiles
) {
7461 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7462 ("TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED due to "
7463 "there is no input processor profiles instance"));
7466 RefPtr
<ITfInputProcessorProfileMgr
> profileMgr
;
7467 HRESULT hr
= inputProcessorProfiles
->QueryInterface(
7468 IID_ITfInputProcessorProfileMgr
, getter_AddRefs(profileMgr
));
7469 if (FAILED(hr
) || !profileMgr
) {
7470 // On Windows Vista or later, ImmIsIME() API always returns true.
7471 // If we failed to obtain the profile manager, we cannot know if current
7472 // keyboard layout has IME.
7473 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7474 (" TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
7475 "ITfInputProcessorProfileMgr"));
7479 TF_INPUTPROCESSORPROFILE profile
;
7480 hr
= profileMgr
->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD
, &profile
);
7481 if (hr
== S_FALSE
) {
7482 return false; // not found or not active
7485 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7486 (" TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
7490 return (profile
.dwProfileType
== TF_PROFILETYPE_INPUTPROCESSOR
);
7492 #endif // #ifdef DEBUG
7494 } // namespace widget
7495 } // namespace mozilla