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/ToString.h"
25 #include "mozilla/WindowsVersion.h"
27 #include "nsPrintfCString.h"
29 // Workaround for mingw32
30 #ifndef TS_SD_INPUTPANEMANUALDISPLAYENABLE
31 # define TS_SD_INPUTPANEMANUALDISPLAYENABLE 0x40
37 static const char* kPrefNameEnableTSF
= "intl.tsf.enable";
40 * TSF related code should log its behavior even on release build especially
41 * in the interface methods.
43 * In interface methods, use LogLevel::Info.
44 * In internal methods, use LogLevel::Debug for logging normal behavior.
45 * For logging error, use LogLevel::Error.
47 * When an instance method is called, start with following text:
48 * "0x%p TSFFoo::Bar(", the 0x%p should be the "this" of the nsFoo.
49 * after that, start with:
51 * In an internal method, start with following text:
53 * When a static method is called, start with following text:
57 LazyLogModule
sTextStoreLog("nsTextStoreWidgets");
59 enum class TextInputProcessorID
{
60 // Internal use only. This won't be returned by TSFStaticSink::ActiveTIP().
63 // Not a TIP. E.g., simple keyboard layout or IMM-IME.
66 // Used for other TIPs, i.e., any TIPs which we don't support specifically.
70 eMicrosoftIMEForJapanese
,
71 eMicrosoftOfficeIME2010ForJapanese
,
82 // TIP for Traditional Chinese.
87 eMicrosoftNewChangJie
,
88 eMicrosoftNewPhonetic
,
92 // TIP for Simplified Chinese.
94 eMicrosoftPinyinNewExperienceInputStyle
,
98 eMicrosoftIMEForKorean
,
102 static const char* GetBoolName(bool aBool
) { return aBool
? "true" : "false"; }
104 static void HandleSeparator(nsCString
& aDesc
) {
105 if (!aDesc
.IsEmpty()) {
106 aDesc
.AppendLiteral(" | ");
110 static const nsCString
GetFindFlagName(DWORD aFindFlag
) {
111 nsCString description
;
113 description
.AppendLiteral("no flags (0)");
116 if (aFindFlag
& TS_ATTR_FIND_BACKWARDS
) {
117 description
.AppendLiteral("TS_ATTR_FIND_BACKWARDS");
119 if (aFindFlag
& TS_ATTR_FIND_WANT_OFFSET
) {
120 HandleSeparator(description
);
121 description
.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET");
123 if (aFindFlag
& TS_ATTR_FIND_UPDATESTART
) {
124 HandleSeparator(description
);
125 description
.AppendLiteral("TS_ATTR_FIND_UPDATESTART");
127 if (aFindFlag
& TS_ATTR_FIND_WANT_VALUE
) {
128 HandleSeparator(description
);
129 description
.AppendLiteral("TS_ATTR_FIND_WANT_VALUE");
131 if (aFindFlag
& TS_ATTR_FIND_WANT_END
) {
132 HandleSeparator(description
);
133 description
.AppendLiteral("TS_ATTR_FIND_WANT_END");
135 if (aFindFlag
& TS_ATTR_FIND_HIDDEN
) {
136 HandleSeparator(description
);
137 description
.AppendLiteral("TS_ATTR_FIND_HIDDEN");
139 if (description
.IsEmpty()) {
140 description
.AppendLiteral("Unknown (");
141 description
.AppendInt(static_cast<uint32_t>(aFindFlag
));
142 description
.Append(')');
147 class GetACPFromPointFlagName
: public nsAutoCString
{
149 explicit GetACPFromPointFlagName(DWORD aFlags
) {
151 AppendLiteral("no flags (0)");
154 if (aFlags
& GXFPF_ROUND_NEAREST
) {
155 AppendLiteral("GXFPF_ROUND_NEAREST");
156 aFlags
&= ~GXFPF_ROUND_NEAREST
;
158 if (aFlags
& GXFPF_NEAREST
) {
159 HandleSeparator(*this);
160 AppendLiteral("GXFPF_NEAREST");
161 aFlags
&= ~GXFPF_NEAREST
;
164 HandleSeparator(*this);
165 AppendLiteral("Unknown(");
166 AppendInt(static_cast<uint32_t>(aFlags
));
170 virtual ~GetACPFromPointFlagName() {}
173 static const char* GetFocusChangeName(
174 InputContextAction::FocusChange aFocusChange
) {
175 switch (aFocusChange
) {
176 case InputContextAction::FOCUS_NOT_CHANGED
:
177 return "FOCUS_NOT_CHANGED";
178 case InputContextAction::GOT_FOCUS
:
180 case InputContextAction::LOST_FOCUS
:
182 case InputContextAction::MENU_GOT_PSEUDO_FOCUS
:
183 return "MENU_GOT_PSEUDO_FOCUS";
184 case InputContextAction::MENU_LOST_PSEUDO_FOCUS
:
185 return "MENU_LOST_PSEUDO_FOCUS";
186 case InputContextAction::WIDGET_CREATED
:
187 return "WIDGET_CREATED";
193 static nsCString
GetCLSIDNameStr(REFCLSID aCLSID
) {
194 LPOLESTR str
= nullptr;
195 HRESULT hr
= ::StringFromCLSID(aCLSID
, &str
);
196 if (FAILED(hr
) || !str
|| !str
[0]) {
201 result
= NS_ConvertUTF16toUTF8(str
);
202 ::CoTaskMemFree(str
);
206 static nsCString
GetGUIDNameStr(REFGUID aGUID
) {
208 int len
= ::StringFromGUID2(aGUID
, str
, ArrayLength(str
));
209 if (!len
|| !str
[0]) {
213 return NS_ConvertUTF16toUTF8(str
);
216 static nsCString
GetGUIDNameStrWithTable(REFGUID aGUID
) {
217 #define RETURN_GUID_NAME(aNamedGUID) \
218 if (IsEqualGUID(aGUID, aNamedGUID)) { \
219 return nsLiteralCString(#aNamedGUID); \
222 RETURN_GUID_NAME(GUID_PROP_INPUTSCOPE
)
223 RETURN_GUID_NAME(TSATTRID_OTHERS
)
224 RETURN_GUID_NAME(TSATTRID_Font
)
225 RETURN_GUID_NAME(TSATTRID_Font_FaceName
)
226 RETURN_GUID_NAME(TSATTRID_Font_SizePts
)
227 RETURN_GUID_NAME(TSATTRID_Font_Style
)
228 RETURN_GUID_NAME(TSATTRID_Font_Style_Bold
)
229 RETURN_GUID_NAME(TSATTRID_Font_Style_Italic
)
230 RETURN_GUID_NAME(TSATTRID_Font_Style_SmallCaps
)
231 RETURN_GUID_NAME(TSATTRID_Font_Style_Capitalize
)
232 RETURN_GUID_NAME(TSATTRID_Font_Style_Uppercase
)
233 RETURN_GUID_NAME(TSATTRID_Font_Style_Lowercase
)
234 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation
)
235 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_LasVegasLights
)
236 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_BlinkingBackground
)
237 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_SparkleText
)
238 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingBlackAnts
)
239 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_MarchingRedAnts
)
240 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_Shimmer
)
241 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeDown
)
242 RETURN_GUID_NAME(TSATTRID_Font_Style_Animation_WipeRight
)
243 RETURN_GUID_NAME(TSATTRID_Font_Style_Emboss
)
244 RETURN_GUID_NAME(TSATTRID_Font_Style_Engrave
)
245 RETURN_GUID_NAME(TSATTRID_Font_Style_Hidden
)
246 RETURN_GUID_NAME(TSATTRID_Font_Style_Kerning
)
247 RETURN_GUID_NAME(TSATTRID_Font_Style_Outlined
)
248 RETURN_GUID_NAME(TSATTRID_Font_Style_Position
)
249 RETURN_GUID_NAME(TSATTRID_Font_Style_Protected
)
250 RETURN_GUID_NAME(TSATTRID_Font_Style_Shadow
)
251 RETURN_GUID_NAME(TSATTRID_Font_Style_Spacing
)
252 RETURN_GUID_NAME(TSATTRID_Font_Style_Weight
)
253 RETURN_GUID_NAME(TSATTRID_Font_Style_Height
)
254 RETURN_GUID_NAME(TSATTRID_Font_Style_Underline
)
255 RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Single
)
256 RETURN_GUID_NAME(TSATTRID_Font_Style_Underline_Double
)
257 RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough
)
258 RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Single
)
259 RETURN_GUID_NAME(TSATTRID_Font_Style_Strikethrough_Double
)
260 RETURN_GUID_NAME(TSATTRID_Font_Style_Overline
)
261 RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Single
)
262 RETURN_GUID_NAME(TSATTRID_Font_Style_Overline_Double
)
263 RETURN_GUID_NAME(TSATTRID_Font_Style_Blink
)
264 RETURN_GUID_NAME(TSATTRID_Font_Style_Subscript
)
265 RETURN_GUID_NAME(TSATTRID_Font_Style_Superscript
)
266 RETURN_GUID_NAME(TSATTRID_Font_Style_Color
)
267 RETURN_GUID_NAME(TSATTRID_Font_Style_BackgroundColor
)
268 RETURN_GUID_NAME(TSATTRID_Text
)
269 RETURN_GUID_NAME(TSATTRID_Text_VerticalWriting
)
270 RETURN_GUID_NAME(TSATTRID_Text_RightToLeft
)
271 RETURN_GUID_NAME(TSATTRID_Text_Orientation
)
272 RETURN_GUID_NAME(TSATTRID_Text_Language
)
273 RETURN_GUID_NAME(TSATTRID_Text_ReadOnly
)
274 RETURN_GUID_NAME(TSATTRID_Text_EmbeddedObject
)
275 RETURN_GUID_NAME(TSATTRID_Text_Alignment
)
276 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Left
)
277 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Right
)
278 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Center
)
279 RETURN_GUID_NAME(TSATTRID_Text_Alignment_Justify
)
280 RETURN_GUID_NAME(TSATTRID_Text_Link
)
281 RETURN_GUID_NAME(TSATTRID_Text_Hyphenation
)
282 RETURN_GUID_NAME(TSATTRID_Text_Para
)
283 RETURN_GUID_NAME(TSATTRID_Text_Para_FirstLineIndent
)
284 RETURN_GUID_NAME(TSATTRID_Text_Para_LeftIndent
)
285 RETURN_GUID_NAME(TSATTRID_Text_Para_RightIndent
)
286 RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceAfter
)
287 RETURN_GUID_NAME(TSATTRID_Text_Para_SpaceBefore
)
288 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing
)
289 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Single
)
290 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_OnePtFive
)
291 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Double
)
292 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_AtLeast
)
293 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Exactly
)
294 RETURN_GUID_NAME(TSATTRID_Text_Para_LineSpacing_Multiple
)
295 RETURN_GUID_NAME(TSATTRID_List
)
296 RETURN_GUID_NAME(TSATTRID_List_LevelIndel
)
297 RETURN_GUID_NAME(TSATTRID_List_Type
)
298 RETURN_GUID_NAME(TSATTRID_List_Type_Bullet
)
299 RETURN_GUID_NAME(TSATTRID_List_Type_Arabic
)
300 RETURN_GUID_NAME(TSATTRID_List_Type_LowerLetter
)
301 RETURN_GUID_NAME(TSATTRID_List_Type_UpperLetter
)
302 RETURN_GUID_NAME(TSATTRID_List_Type_LowerRoman
)
303 RETURN_GUID_NAME(TSATTRID_List_Type_UpperRoman
)
304 RETURN_GUID_NAME(TSATTRID_App
)
305 RETURN_GUID_NAME(TSATTRID_App_IncorrectSpelling
)
306 RETURN_GUID_NAME(TSATTRID_App_IncorrectGrammar
)
308 #undef RETURN_GUID_NAME
310 return GetGUIDNameStr(aGUID
);
313 static nsCString
GetRIIDNameStr(REFIID aRIID
) {
314 LPOLESTR str
= nullptr;
315 HRESULT hr
= ::StringFromIID(aRIID
, &str
);
316 if (FAILED(hr
) || !str
|| !str
[0]) {
320 nsAutoString
key(L
"Interface\\");
325 if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT
, key
.get(), nullptr, buf
,
327 result
= NS_ConvertUTF16toUTF8(buf
);
329 result
= NS_ConvertUTF16toUTF8(str
);
332 ::CoTaskMemFree(str
);
336 static const char* GetCommonReturnValueName(HRESULT aResult
) {
343 return "E_ACCESSDENIED";
349 return "E_INVALIDARG";
351 return "E_NOINTERFACE";
355 return "E_OUTOFMEMORY";
359 return "E_UNEXPECTED";
361 return SUCCEEDED(aResult
) ? "Succeeded" : "Failed";
365 static const char* GetTextStoreReturnValueName(HRESULT aResult
) {
368 return "TS_E_FORMAT";
369 case TS_E_INVALIDPOINT
:
370 return "TS_E_INVALIDPOINT";
371 case TS_E_INVALIDPOS
:
372 return "TS_E_INVALIDPOS";
373 case TS_E_NOINTERFACE
:
374 return "TS_E_NOINTERFACE";
376 return "TS_E_NOLAYOUT";
378 return "TS_E_NOLOCK";
380 return "TS_E_NOOBJECT";
381 case TS_E_NOSELECTION
:
382 return "TS_E_NOSELECTION";
384 return "TS_E_NOSERVICE";
386 return "TS_E_READONLY";
387 case TS_E_SYNCHRONOUS
:
388 return "TS_E_SYNCHRONOUS";
392 return GetCommonReturnValueName(aResult
);
396 static const nsCString
GetSinkMaskNameStr(DWORD aSinkMask
) {
397 nsCString description
;
398 if (aSinkMask
& TS_AS_TEXT_CHANGE
) {
399 description
.AppendLiteral("TS_AS_TEXT_CHANGE");
401 if (aSinkMask
& TS_AS_SEL_CHANGE
) {
402 HandleSeparator(description
);
403 description
.AppendLiteral("TS_AS_SEL_CHANGE");
405 if (aSinkMask
& TS_AS_LAYOUT_CHANGE
) {
406 HandleSeparator(description
);
407 description
.AppendLiteral("TS_AS_LAYOUT_CHANGE");
409 if (aSinkMask
& TS_AS_ATTR_CHANGE
) {
410 HandleSeparator(description
);
411 description
.AppendLiteral("TS_AS_ATTR_CHANGE");
413 if (aSinkMask
& TS_AS_STATUS_CHANGE
) {
414 HandleSeparator(description
);
415 description
.AppendLiteral("TS_AS_STATUS_CHANGE");
417 if (description
.IsEmpty()) {
418 description
.AppendLiteral("not-specified");
423 static const char* GetActiveSelEndName(TsActiveSelEnd aSelEnd
) {
424 return aSelEnd
== TS_AE_NONE
426 : aSelEnd
== TS_AE_START
428 : aSelEnd
== TS_AE_END
? "TS_AE_END" : "Unknown";
431 static const nsCString
GetLockFlagNameStr(DWORD aLockFlags
) {
432 nsCString description
;
433 if ((aLockFlags
& TS_LF_READWRITE
) == TS_LF_READWRITE
) {
434 description
.AppendLiteral("TS_LF_READWRITE");
435 } else if (aLockFlags
& TS_LF_READ
) {
436 description
.AppendLiteral("TS_LF_READ");
438 if (aLockFlags
& TS_LF_SYNC
) {
439 if (!description
.IsEmpty()) {
440 description
.AppendLiteral(" | ");
442 description
.AppendLiteral("TS_LF_SYNC");
444 if (description
.IsEmpty()) {
445 description
.AppendLiteral("not-specified");
450 static const char* GetTextRunTypeName(TsRunType aRunType
) {
453 return "TS_RT_PLAIN";
455 return "TS_RT_HIDDEN";
457 return "TS_RT_OPAQUE";
463 static nsCString
GetColorName(const TF_DA_COLOR
& aColor
) {
464 switch (aColor
.type
) {
466 return "TF_CT_NONE"_ns
;
468 return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X",
469 static_cast<int32_t>(aColor
.nIndex
));
471 return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X",
472 static_cast<int32_t>(aColor
.cr
));
475 return nsPrintfCString("Unknown(%08X)",
476 static_cast<int32_t>(aColor
.type
));
480 static nsCString
GetLineStyleName(TF_DA_LINESTYLE aLineStyle
) {
481 switch (aLineStyle
) {
483 return "TF_LS_NONE"_ns
;
485 return "TF_LS_SOLID"_ns
;
487 return "TF_LS_DOT"_ns
;
489 return "TF_LS_DASH"_ns
;
491 return "TF_LS_SQUIGGLE"_ns
;
493 return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle
));
498 static nsCString
GetClauseAttrName(TF_DA_ATTR_INFO aAttr
) {
501 return "TF_ATTR_INPUT"_ns
;
502 case TF_ATTR_TARGET_CONVERTED
:
503 return "TF_ATTR_TARGET_CONVERTED"_ns
;
504 case TF_ATTR_CONVERTED
:
505 return "TF_ATTR_CONVERTED"_ns
;
506 case TF_ATTR_TARGET_NOTCONVERTED
:
507 return "TF_ATTR_TARGET_NOTCONVERTED"_ns
;
508 case TF_ATTR_INPUT_ERROR
:
509 return "TF_ATTR_INPUT_ERROR"_ns
;
510 case TF_ATTR_FIXEDCONVERTED
:
511 return "TF_ATTR_FIXEDCONVERTED"_ns
;
513 return "TF_ATTR_OTHER"_ns
;
515 return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr
));
520 static nsCString
GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE
& aDispAttr
) {
523 str
+= GetColorName(aDispAttr
.crText
);
524 str
+= " }, crBk:{ ";
525 str
+= GetColorName(aDispAttr
.crBk
);
526 str
+= " }, lsStyle: ";
527 str
+= GetLineStyleName(aDispAttr
.lsStyle
);
528 str
+= ", fBoldLine: ";
529 str
+= GetBoolName(aDispAttr
.fBoldLine
);
530 str
+= ", crLine:{ ";
531 str
+= GetColorName(aDispAttr
.crLine
);
532 str
+= " }, bAttr: ";
533 str
+= GetClauseAttrName(aDispAttr
.bAttr
);
537 static const char* GetMouseButtonName(int16_t aButton
) {
539 case MouseButton::ePrimary
:
541 case MouseButton::eMiddle
:
542 return "MiddleButton";
543 case MouseButton::eSecondary
:
544 return "RightButton";
546 return "UnknownButton";
550 #define ADD_SEPARATOR_IF_NECESSARY(aStr) \
551 if (!aStr.IsEmpty()) { \
552 aStr.AppendLiteral(", "); \
555 static nsCString
GetMouseButtonsName(int16_t aButtons
) {
557 return "no buttons"_ns
;
560 if (aButtons
& MouseButtonsFlag::ePrimaryFlag
) {
561 names
= "LeftButton";
563 if (aButtons
& MouseButtonsFlag::eSecondaryFlag
) {
564 ADD_SEPARATOR_IF_NECESSARY(names
);
565 names
+= "RightButton";
567 if (aButtons
& MouseButtonsFlag::eMiddleFlag
) {
568 ADD_SEPARATOR_IF_NECESSARY(names
);
569 names
+= "MiddleButton";
571 if (aButtons
& MouseButtonsFlag::e4thFlag
) {
572 ADD_SEPARATOR_IF_NECESSARY(names
);
573 names
+= "4thButton";
575 if (aButtons
& MouseButtonsFlag::e5thFlag
) {
576 ADD_SEPARATOR_IF_NECESSARY(names
);
577 names
+= "5thButton";
582 static nsCString
GetModifiersName(Modifiers aModifiers
) {
583 if (aModifiers
== MODIFIER_NONE
) {
584 return "no modifiers"_ns
;
587 if (aModifiers
& MODIFIER_ALT
) {
588 names
= NS_DOM_KEYNAME_ALT
;
590 if (aModifiers
& MODIFIER_ALTGRAPH
) {
591 ADD_SEPARATOR_IF_NECESSARY(names
);
592 names
+= NS_DOM_KEYNAME_ALTGRAPH
;
594 if (aModifiers
& MODIFIER_CAPSLOCK
) {
595 ADD_SEPARATOR_IF_NECESSARY(names
);
596 names
+= NS_DOM_KEYNAME_CAPSLOCK
;
598 if (aModifiers
& MODIFIER_CONTROL
) {
599 ADD_SEPARATOR_IF_NECESSARY(names
);
600 names
+= NS_DOM_KEYNAME_CONTROL
;
602 if (aModifiers
& MODIFIER_FN
) {
603 ADD_SEPARATOR_IF_NECESSARY(names
);
604 names
+= NS_DOM_KEYNAME_FN
;
606 if (aModifiers
& MODIFIER_FNLOCK
) {
607 ADD_SEPARATOR_IF_NECESSARY(names
);
608 names
+= NS_DOM_KEYNAME_FNLOCK
;
610 if (aModifiers
& MODIFIER_META
) {
611 ADD_SEPARATOR_IF_NECESSARY(names
);
612 names
+= NS_DOM_KEYNAME_META
;
614 if (aModifiers
& MODIFIER_NUMLOCK
) {
615 ADD_SEPARATOR_IF_NECESSARY(names
);
616 names
+= NS_DOM_KEYNAME_NUMLOCK
;
618 if (aModifiers
& MODIFIER_SCROLLLOCK
) {
619 ADD_SEPARATOR_IF_NECESSARY(names
);
620 names
+= NS_DOM_KEYNAME_SCROLLLOCK
;
622 if (aModifiers
& MODIFIER_SHIFT
) {
623 ADD_SEPARATOR_IF_NECESSARY(names
);
624 names
+= NS_DOM_KEYNAME_SHIFT
;
626 if (aModifiers
& MODIFIER_SYMBOL
) {
627 ADD_SEPARATOR_IF_NECESSARY(names
);
628 names
+= NS_DOM_KEYNAME_SYMBOL
;
630 if (aModifiers
& MODIFIER_SYMBOLLOCK
) {
631 ADD_SEPARATOR_IF_NECESSARY(names
);
632 names
+= NS_DOM_KEYNAME_SYMBOLLOCK
;
634 if (aModifiers
& MODIFIER_OS
) {
635 ADD_SEPARATOR_IF_NECESSARY(names
);
636 names
+= NS_DOM_KEYNAME_OS
;
641 class GetWritingModeName
: public nsAutoCString
{
643 explicit GetWritingModeName(const WritingMode
& aWritingMode
) {
644 if (!aWritingMode
.IsVertical()) {
645 AssignLiteral("Horizontal");
648 if (aWritingMode
.IsVerticalLR()) {
649 AssignLiteral("Vertical (LR)");
652 AssignLiteral("Vertical (RL)");
654 virtual ~GetWritingModeName() {}
657 class GetEscapedUTF8String final
: public NS_ConvertUTF16toUTF8
{
659 explicit GetEscapedUTF8String(const nsAString
& aString
)
660 : NS_ConvertUTF16toUTF8(aString
) {
663 explicit GetEscapedUTF8String(const char16ptr_t aString
)
664 : NS_ConvertUTF16toUTF8(aString
) {
667 GetEscapedUTF8String(const char16ptr_t aString
, uint32_t aLength
)
668 : NS_ConvertUTF16toUTF8(aString
, aLength
) {
674 ReplaceSubstring("\r", "\\r");
675 ReplaceSubstring("\n", "\\n");
676 ReplaceSubstring("\t", "\\t");
680 class GetInputScopeString
: public nsAutoCString
{
682 explicit GetInputScopeString(const nsTArray
<InputScope
>& aList
) {
683 for (InputScope inputScope
: aList
) {
687 switch (inputScope
) {
689 AppendLiteral("IS_DEFAULT");
692 AppendLiteral("IS_URL");
694 case IS_FILE_FULLFILEPATH
:
695 AppendLiteral("IS_FILE_FULLFILEPATH");
697 case IS_FILE_FILENAME
:
698 AppendLiteral("IS_FILE_FILENAME");
700 case IS_EMAIL_USERNAME
:
701 AppendLiteral("IS_EMAIL_USERNAME");
703 case IS_EMAIL_SMTPEMAILADDRESS
:
704 AppendLiteral("IS_EMAIL_SMTPEMAILADDRESS");
707 AppendLiteral("IS_LOGINNAME");
709 case IS_PERSONALNAME_FULLNAME
:
710 AppendLiteral("IS_PERSONALNAME_FULLNAME");
712 case IS_PERSONALNAME_PREFIX
:
713 AppendLiteral("IS_PERSONALNAME_PREFIX");
715 case IS_PERSONALNAME_GIVENNAME
:
716 AppendLiteral("IS_PERSONALNAME_GIVENNAME");
718 case IS_PERSONALNAME_MIDDLENAME
:
719 AppendLiteral("IS_PERSONALNAME_MIDDLENAME");
721 case IS_PERSONALNAME_SURNAME
:
722 AppendLiteral("IS_PERSONALNAME_SURNAME");
724 case IS_PERSONALNAME_SUFFIX
:
725 AppendLiteral("IS_PERSONALNAME_SUFFIX");
727 case IS_ADDRESS_FULLPOSTALADDRESS
:
728 AppendLiteral("IS_ADDRESS_FULLPOSTALADDRESS");
730 case IS_ADDRESS_POSTALCODE
:
731 AppendLiteral("IS_ADDRESS_POSTALCODE");
733 case IS_ADDRESS_STREET
:
734 AppendLiteral("IS_ADDRESS_STREET");
736 case IS_ADDRESS_STATEORPROVINCE
:
737 AppendLiteral("IS_ADDRESS_STATEORPROVINCE");
739 case IS_ADDRESS_CITY
:
740 AppendLiteral("IS_ADDRESS_CITY");
742 case IS_ADDRESS_COUNTRYNAME
:
743 AppendLiteral("IS_ADDRESS_COUNTRYNAME");
745 case IS_ADDRESS_COUNTRYSHORTNAME
:
746 AppendLiteral("IS_ADDRESS_COUNTRYSHORTNAME");
748 case IS_CURRENCY_AMOUNTANDSYMBOL
:
749 AppendLiteral("IS_CURRENCY_AMOUNTANDSYMBOL");
751 case IS_CURRENCY_AMOUNT
:
752 AppendLiteral("IS_CURRENCY_AMOUNT");
754 case IS_DATE_FULLDATE
:
755 AppendLiteral("IS_DATE_FULLDATE");
758 AppendLiteral("IS_DATE_MONTH");
761 AppendLiteral("IS_DATE_DAY");
764 AppendLiteral("IS_DATE_YEAR");
766 case IS_DATE_MONTHNAME
:
767 AppendLiteral("IS_DATE_MONTHNAME");
769 case IS_DATE_DAYNAME
:
770 AppendLiteral("IS_DATE_DAYNAME");
773 AppendLiteral("IS_DIGITS");
776 AppendLiteral("IS_NUMBER");
779 AppendLiteral("IS_ONECHAR");
782 AppendLiteral("IS_PASSWORD");
784 case IS_TELEPHONE_FULLTELEPHONENUMBER
:
785 AppendLiteral("IS_TELEPHONE_FULLTELEPHONENUMBER");
787 case IS_TELEPHONE_COUNTRYCODE
:
788 AppendLiteral("IS_TELEPHONE_COUNTRYCODE");
790 case IS_TELEPHONE_AREACODE
:
791 AppendLiteral("IS_TELEPHONE_AREACODE");
793 case IS_TELEPHONE_LOCALNUMBER
:
794 AppendLiteral("IS_TELEPHONE_LOCALNUMBER");
796 case IS_TIME_FULLTIME
:
797 AppendLiteral("IS_TIME_FULLTIME");
800 AppendLiteral("IS_TIME_HOUR");
802 case IS_TIME_MINORSEC
:
803 AppendLiteral("IS_TIME_MINORSEC");
805 case IS_NUMBER_FULLWIDTH
:
806 AppendLiteral("IS_NUMBER_FULLWIDTH");
808 case IS_ALPHANUMERIC_HALFWIDTH
:
809 AppendLiteral("IS_ALPHANUMERIC_HALFWIDTH");
811 case IS_ALPHANUMERIC_FULLWIDTH
:
812 AppendLiteral("IS_ALPHANUMERIC_FULLWIDTH");
814 case IS_CURRENCY_CHINESE
:
815 AppendLiteral("IS_CURRENCY_CHINESE");
818 AppendLiteral("IS_BOPOMOFO");
821 AppendLiteral("IS_HIRAGANA");
823 case IS_KATAKANA_HALFWIDTH
:
824 AppendLiteral("IS_KATAKANA_HALFWIDTH");
826 case IS_KATAKANA_FULLWIDTH
:
827 AppendLiteral("IS_KATAKANA_FULLWIDTH");
830 AppendLiteral("IS_HANJA");
833 AppendLiteral("IS_PHRASELIST");
835 case IS_REGULAREXPRESSION
:
836 AppendLiteral("IS_REGULAREXPRESSION");
839 AppendLiteral("IS_SRGS");
842 AppendLiteral("IS_XML");
845 AppendLiteral("IS_PRIVATE");
848 AppendPrintf("Unknown Value(%d)", inputScope
);
855 /******************************************************************/
857 /******************************************************************/
859 class InputScopeImpl final
: public ITfInputScope
{
863 explicit InputScopeImpl(const nsTArray
<InputScope
>& aList
)
864 : mInputScopes(aList
.Clone()) {
866 sTextStoreLog
, LogLevel::Info
,
867 ("0x%p InputScopeImpl(%s)", this, GetInputScopeString(aList
).get()));
870 NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(InputScopeImpl
)
872 STDMETHODIMP
QueryInterface(REFIID riid
, void** ppv
) {
874 if ((IID_IUnknown
== riid
) || (IID_ITfInputScope
== riid
)) {
875 *ppv
= static_cast<ITfInputScope
*>(this);
881 return E_NOINTERFACE
;
884 STDMETHODIMP
GetInputScopes(InputScope
** pprgInputScopes
, UINT
* pcCount
) {
885 uint32_t count
= (mInputScopes
.IsEmpty() ? 1 : mInputScopes
.Length());
888 (InputScope
*)CoTaskMemAlloc(sizeof(InputScope
) * count
);
889 NS_ENSURE_TRUE(pScope
, E_OUTOFMEMORY
);
891 if (mInputScopes
.IsEmpty()) {
892 *pScope
= IS_DEFAULT
;
894 *pprgInputScopes
= pScope
;
900 for (uint32_t idx
= 0; idx
< count
; idx
++) {
901 *(pScope
+ idx
) = mInputScopes
[idx
];
905 *pprgInputScopes
= pScope
;
909 STDMETHODIMP
GetPhrase(BSTR
** ppbstrPhrases
, UINT
* pcCount
) {
912 STDMETHODIMP
GetRegularExpression(BSTR
* pbstrRegExp
) { return E_NOTIMPL
; }
913 STDMETHODIMP
GetSRGS(BSTR
* pbstrSRGS
) { return E_NOTIMPL
; }
914 STDMETHODIMP
GetXML(BSTR
* pbstrXML
) { return E_NOTIMPL
; }
917 nsTArray
<InputScope
> mInputScopes
;
920 /******************************************************************/
922 /******************************************************************/
924 class TSFStaticSink final
: public ITfInputProcessorProfileActivationSink
{
926 static TSFStaticSink
* GetInstance() {
928 RefPtr
<ITfThreadMgr
> threadMgr
= TSFTextStore::GetThreadMgr();
929 if (NS_WARN_IF(!threadMgr
)) {
931 sTextStoreLog
, LogLevel::Error
,
932 ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
933 "instance due to no ThreadMgr instance"));
936 RefPtr
<ITfInputProcessorProfiles
> inputProcessorProfiles
=
937 TSFTextStore::GetInputProcessorProfiles();
938 if (NS_WARN_IF(!inputProcessorProfiles
)) {
940 sTextStoreLog
, LogLevel::Error
,
941 ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
942 "instance due to no InputProcessorProfiles instance"));
945 RefPtr
<TSFStaticSink
> staticSink
= new TSFStaticSink();
946 if (NS_WARN_IF(!staticSink
->Init(threadMgr
, inputProcessorProfiles
))) {
947 staticSink
->Destroy();
949 sTextStoreLog
, LogLevel::Error
,
950 ("TSFStaticSink::GetInstance() FAILED to initialize TSFStaticSink "
954 sInstance
= staticSink
.forget();
959 static void Shutdown() {
961 sInstance
->Destroy();
966 bool Init(ITfThreadMgr
* aThreadMgr
,
967 ITfInputProcessorProfiles
* aInputProcessorProfiles
);
968 STDMETHODIMP
QueryInterface(REFIID riid
, void** ppv
) {
970 if (IID_IUnknown
== riid
||
971 IID_ITfInputProcessorProfileActivationSink
== riid
) {
972 *ppv
= static_cast<ITfInputProcessorProfileActivationSink
*>(this);
978 return E_NOINTERFACE
;
981 NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFStaticSink
)
983 const nsString
& GetActiveTIPKeyboardDescription() const {
984 return mActiveTIPKeyboardDescription
;
987 static bool IsIMM_IMEActive() {
988 // Use IMM API until TSFStaticSink starts to work.
989 if (!sInstance
|| !sInstance
->EnsureInitActiveTIPKeyboard()) {
990 return IsIMM_IME(::GetKeyboardLayout(0));
992 return sInstance
->mIsIMM_IME
;
995 static bool IsIMM_IME(HKL aHKL
) {
996 return (::ImmGetIMEFileNameW(aHKL
, nullptr, 0) > 0);
999 static bool IsTraditionalChinese() {
1001 return sInstance
&& sInstance
->IsTraditionalChineseInternal();
1003 static bool IsSimplifiedChinese() {
1005 return sInstance
&& sInstance
->IsSimplifiedChineseInternal();
1007 static bool IsJapanese() {
1009 return sInstance
&& sInstance
->IsJapaneseInternal();
1011 static bool IsKorean() {
1013 return sInstance
&& sInstance
->IsKoreanInternal();
1017 * ActiveTIP() returns an ID for currently active TIP.
1018 * Please note that this method is expensive due to needs a lot of GUID
1019 * comparations if active language ID is one of CJKT. If you need to
1020 * check TIPs for a specific language, you should check current language
1023 static TextInputProcessorID
ActiveTIP() {
1025 if (!sInstance
|| !sInstance
->EnsureInitActiveTIPKeyboard()) {
1026 return TextInputProcessorID::eUnknown
;
1028 sInstance
->ComputeActiveTextInputProcessor();
1029 if (NS_WARN_IF(sInstance
->mActiveTIP
==
1030 TextInputProcessorID::eNotComputed
)) {
1031 return TextInputProcessorID::eUnknown
;
1033 return sInstance
->mActiveTIP
;
1036 static bool IsMSChangJieOrMSQuickActive() {
1037 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1038 // For avoiding unnecessary computation, we should check if the language
1039 // for current TIP is Traditional Chinese.
1040 if (!IsTraditionalChinese()) {
1043 switch (ActiveTIP()) {
1044 case TextInputProcessorID::eMicrosoftChangJie
:
1045 case TextInputProcessorID::eMicrosoftQuick
:
1052 static bool IsMSPinyinOrMSWubiActive() {
1053 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1054 // For avoiding unnecessary computation, we should check if the language
1055 // for current TIP is Simplified Chinese.
1056 if (!IsSimplifiedChinese()) {
1059 switch (ActiveTIP()) {
1060 case TextInputProcessorID::eMicrosoftPinyin
:
1061 case TextInputProcessorID::eMicrosoftWubi
:
1068 static bool IsMSJapaneseIMEActive() {
1069 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1070 // For avoiding unnecessary computation, we should check if the language
1071 // for current TIP is Japanese.
1072 if (!IsJapanese()) {
1075 return ActiveTIP() == TextInputProcessorID::eMicrosoftIMEForJapanese
;
1078 static bool IsGoogleJapaneseInputActive() {
1079 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1080 // For avoiding unnecessary computation, we should check if the language
1081 // for current TIP is Japanese.
1082 if (!IsJapanese()) {
1085 return ActiveTIP() == TextInputProcessorID::eGoogleJapaneseInput
;
1088 static bool IsATOKActive() {
1089 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1090 // For avoiding unnecessary computation, we should check if active TIP is
1091 // ATOK first since it's cheaper.
1092 return IsJapanese() && sInstance
->IsATOKActiveInternal();
1095 // Note that ATOK 2011 - 2016 refers native caret position for deciding its
1096 // popup window position.
1097 static bool IsATOKReferringNativeCaretActive() {
1098 // ActiveTIP() is expensive if it hasn't computed active TIP yet.
1099 // For avoiding unnecessary computation, we should check if active TIP is
1100 // ATOK first since it's cheaper.
1101 if (!IsJapanese() || !sInstance
->IsATOKActiveInternal()) {
1104 switch (ActiveTIP()) {
1105 case TextInputProcessorID::eATOK2011
:
1106 case TextInputProcessorID::eATOK2012
:
1107 case TextInputProcessorID::eATOK2013
:
1108 case TextInputProcessorID::eATOK2014
:
1109 case TextInputProcessorID::eATOK2015
:
1117 static void EnsureInstance() {
1119 RefPtr
<TSFStaticSink
> staticSink
= GetInstance();
1120 Unused
<< staticSink
;
1124 bool IsTraditionalChineseInternal() const { return mLangID
== 0x0404; }
1125 bool IsSimplifiedChineseInternal() const { return mLangID
== 0x0804; }
1126 bool IsJapaneseInternal() const { return mLangID
== 0x0411; }
1127 bool IsKoreanInternal() const { return mLangID
== 0x0412; }
1129 bool IsATOKActiveInternal() {
1130 EnsureInitActiveTIPKeyboard();
1131 // FYI: Name of packaged ATOK includes the release year like "ATOK 2015".
1132 // Name of ATOK Passport (subscription) equals "ATOK".
1133 return StringBeginsWith(mActiveTIPKeyboardDescription
, u
"ATOK "_ns
) ||
1134 mActiveTIPKeyboardDescription
.EqualsLiteral("ATOK");
1137 void ComputeActiveTextInputProcessor() {
1138 if (mActiveTIP
!= TextInputProcessorID::eNotComputed
) {
1142 if (mActiveTIPGUID
== GUID_NULL
) {
1143 mActiveTIP
= TextInputProcessorID::eNone
;
1147 // Comparing GUID is slow. So, we should use language information to
1148 // reduce the comparing cost for TIP which is not we do not support
1149 // specifically since they are always compared with all supported TIPs.
1152 mActiveTIP
= ComputeActiveTIPAsTraditionalChinese();
1155 mActiveTIP
= ComputeActiveTIPAsJapanese();
1158 mActiveTIP
= ComputeActiveTIPAsKorean();
1161 mActiveTIP
= ComputeActiveTIPAsSimplifiedChinese();
1164 mActiveTIP
= TextInputProcessorID::eUnknown
;
1168 TextInputProcessorID
ComputeActiveTIPAsJapanese() {
1169 // {A76C93D9-5523-4E90-AAFA-4DB112F9AC76} (Win7, Win8.1, Win10)
1170 static const GUID kMicrosoftIMEForJapaneseGUID
= {
1174 {0xAA, 0xFA, 0x4D, 0xB1, 0x12, 0xF9, 0xAC, 0x76}};
1175 if (mActiveTIPGUID
== kMicrosoftIMEForJapaneseGUID
) {
1176 return TextInputProcessorID::eMicrosoftIMEForJapanese
;
1178 // {54EDCC94-1524-4BB1-9FB7-7BABE4F4CA64}
1179 static const GUID kMicrosoftOfficeIME2010ForJapaneseGUID
= {
1183 {0x9F, 0xB7, 0x7B, 0xAB, 0xE4, 0xF4, 0xCA, 0x64}};
1184 if (mActiveTIPGUID
== kMicrosoftOfficeIME2010ForJapaneseGUID
) {
1185 return TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese
;
1187 // {773EB24E-CA1D-4B1B-B420-FA985BB0B80D}
1188 static const GUID kGoogleJapaneseInputGUID
= {
1192 {0xB4, 0x20, 0xFA, 0x98, 0x5B, 0xB0, 0xB8, 0x0D}};
1193 if (mActiveTIPGUID
== kGoogleJapaneseInputGUID
) {
1194 return TextInputProcessorID::eGoogleJapaneseInput
;
1196 // {F9C24A5C-8A53-499D-9572-93B2FF582115}
1197 static const GUID kATOK2011GUID
= {
1201 {0x95, 0x72, 0x93, 0xB2, 0xFF, 0x58, 0x21, 0x15}};
1202 if (mActiveTIPGUID
== kATOK2011GUID
) {
1203 return TextInputProcessorID::eATOK2011
;
1205 // {1DE01562-F445-401B-B6C3-E5B18DB79461}
1206 static const GUID kATOK2012GUID
= {
1210 {0xB6, 0xC3, 0xE5, 0xB1, 0x8D, 0xB7, 0x94, 0x61}};
1211 if (mActiveTIPGUID
== kATOK2012GUID
) {
1212 return TextInputProcessorID::eATOK2012
;
1214 // {3C4DB511-189A-4168-B6EA-BFD0B4C85615}
1215 static const GUID kATOK2013GUID
= {
1219 {0xB6, 0xEA, 0xBF, 0xD0, 0xB4, 0xC8, 0x56, 0x15}};
1220 if (mActiveTIPGUID
== kATOK2013GUID
) {
1221 return TextInputProcessorID::eATOK2013
;
1223 // {4EF33B79-6AA9-4271-B4BF-9321C279381B}
1224 static const GUID kATOK2014GUID
= {
1228 {0xB4, 0xBF, 0x93, 0x21, 0xC2, 0x79, 0x38, 0x1B}};
1229 if (mActiveTIPGUID
== kATOK2014GUID
) {
1230 return TextInputProcessorID::eATOK2014
;
1232 // {EAB4DC00-CE2E-483D-A86A-E6B99DA9599A}
1233 static const GUID kATOK2015GUID
= {
1237 {0xA8, 0x6A, 0xE6, 0xB9, 0x9D, 0xA9, 0x59, 0x9A}};
1238 if (mActiveTIPGUID
== kATOK2015GUID
) {
1239 return TextInputProcessorID::eATOK2015
;
1241 // {0B557B4C-5740-4110-A60A-1493FA10BF2B}
1242 static const GUID kATOK2016GUID
= {
1246 {0xA6, 0x0A, 0x14, 0x93, 0xFA, 0x10, 0xBF, 0x2B}};
1247 if (mActiveTIPGUID
== kATOK2016GUID
) {
1248 return TextInputProcessorID::eATOK2016
;
1252 // - {6DBFD8F5-701D-11E6-920F-782BCBA6348F}
1253 // * ATOK Passport (confirmed with version 31.1.2)
1254 // - {A38F2FD9-7199-45E1-841C-BE0313D8052F}
1256 if (IsATOKActiveInternal()) {
1257 return TextInputProcessorID::eATOKUnknown
;
1260 // {E6D66705-1EDA-4373-8D01-1D0CB2D054C7}
1261 static const GUID kJapanist10GUID
= {
1265 {0x8D, 0x01, 0x1D, 0x0C, 0xB2, 0xD0, 0x54, 0xC7}};
1266 if (mActiveTIPGUID
== kJapanist10GUID
) {
1267 return TextInputProcessorID::eJapanist10
;
1270 return TextInputProcessorID::eUnknown
;
1273 TextInputProcessorID
ComputeActiveTIPAsTraditionalChinese() {
1274 // {B2F9C502-1742-11D4-9790-0080C882687E} (Win8.1, Win10)
1275 static const GUID kMicrosoftBopomofoGUID
= {
1279 {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1280 if (mActiveTIPGUID
== kMicrosoftBopomofoGUID
) {
1281 return TextInputProcessorID::eMicrosoftBopomofo
;
1283 // {4BDF9F03-C7D3-11D4-B2AB-0080C882687E} (Win7, Win8.1, Win10)
1284 static const GUID kMicrosoftChangJieGUID
= {
1288 {0xB2, 0xAB, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1289 if (mActiveTIPGUID
== kMicrosoftChangJieGUID
) {
1290 return TextInputProcessorID::eMicrosoftChangJie
;
1292 // {761309DE-317A-11D4-9B5D-0080C882687E} (Win7)
1293 static const GUID kMicrosoftPhoneticGUID
= {
1297 {0x9B, 0x5D, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1298 if (mActiveTIPGUID
== kMicrosoftPhoneticGUID
) {
1299 return TextInputProcessorID::eMicrosoftPhonetic
;
1301 // {6024B45F-5C54-11D4-B921-0080C882687E} (Win7, Win8.1, Win10)
1302 static const GUID kMicrosoftQuickGUID
= {
1306 {0xB9, 0x21, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1307 if (mActiveTIPGUID
== kMicrosoftQuickGUID
) {
1308 return TextInputProcessorID::eMicrosoftQuick
;
1310 // {F3BA907A-6C7E-11D4-97FA-0080C882687E} (Win7)
1311 static const GUID kMicrosoftNewChangJieGUID
= {
1315 {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1316 if (mActiveTIPGUID
== kMicrosoftNewChangJieGUID
) {
1317 return TextInputProcessorID::eMicrosoftNewChangJie
;
1319 // {B2F9C502-1742-11D4-9790-0080C882687E} (Win7)
1320 static const GUID kMicrosoftNewPhoneticGUID
= {
1324 {0x97, 0x90, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1325 if (mActiveTIPGUID
== kMicrosoftNewPhoneticGUID
) {
1326 return TextInputProcessorID::eMicrosoftNewPhonetic
;
1328 // {0B883BA0-C1C7-11D4-87F9-0080C882687E} (Win7)
1329 static const GUID kMicrosoftNewQuickGUID
= {
1333 {0x87, 0xF9, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1334 if (mActiveTIPGUID
== kMicrosoftNewQuickGUID
) {
1335 return TextInputProcessorID::eMicrosoftNewQuick
;
1338 // NOTE: There are some other Traditional Chinese TIPs installed in Windows:
1339 // * Chinese Traditional Array (version 6.0)
1340 // - {D38EFF65-AA46-4FD5-91A7-67845FB02F5B} (Win7, Win8.1)
1341 // * Chinese Traditional DaYi (version 6.0)
1342 // - {037B2C25-480C-4D7F-B027-D6CA6B69788A} (Win7, Win8.1)
1344 // {B58630B5-0ED3-4335-BBC9-E77BBCB43CAD}
1345 static const GUID kFreeChangJieGUID
= {
1349 {0xBB, 0xC9, 0xE7, 0x7B, 0xBC, 0xB4, 0x3C, 0xAD}};
1350 if (mActiveTIPGUID
== kFreeChangJieGUID
) {
1351 return TextInputProcessorID::eFreeChangJie
;
1354 return TextInputProcessorID::eUnknown
;
1357 TextInputProcessorID
ComputeActiveTIPAsSimplifiedChinese() {
1358 // FYI: This matches with neither "Microsoft Pinyin ABC Input Style" nor
1359 // "Microsoft Pinyin New Experience Input Style" on Win7.
1360 // {FA550B04-5AD7-411F-A5AC-CA038EC515D7} (Win8.1, Win10)
1361 static const GUID kMicrosoftPinyinGUID
= {
1365 {0xA5, 0xAC, 0xCA, 0x03, 0x8E, 0xC5, 0x15, 0xD7}};
1366 if (mActiveTIPGUID
== kMicrosoftPinyinGUID
) {
1367 return TextInputProcessorID::eMicrosoftPinyin
;
1370 // {F3BA9077-6C7E-11D4-97FA-0080C882687E} (Win7)
1371 static const GUID kMicrosoftPinyinNewExperienceInputStyleGUID
= {
1375 {0x97, 0xFA, 0x00, 0x80, 0xC8, 0x82, 0x68, 0x7E}};
1376 if (mActiveTIPGUID
== kMicrosoftPinyinNewExperienceInputStyleGUID
) {
1377 return TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle
;
1379 // {82590C13-F4DD-44F4-BA1D-8667246FDF8E} (Win8.1, Win10)
1380 static const GUID kMicrosoftWubiGUID
= {
1384 {0xBA, 0x1D, 0x86, 0x67, 0x24, 0x6F, 0xDF, 0x8E}};
1385 if (mActiveTIPGUID
== kMicrosoftWubiGUID
) {
1386 return TextInputProcessorID::eMicrosoftWubi
;
1388 // NOTE: There are some other Simplified Chinese TIPs installed in Windows:
1389 // * Chinese Simplified QuanPin (version 6.0)
1390 // - {54FC610E-6ABD-4685-9DDD-A130BDF1B170} (Win8.1)
1391 // * Chinese Simplified ZhengMa (version 6.0)
1392 // - {733B4D81-3BC3-4132-B91A-E9CDD5E2BFC9} (Win8.1)
1393 // * Chinese Simplified ShuangPin (version 6.0)
1394 // - {EF63706D-31C4-490E-9DBB-BD150ADC454B} (Win8.1)
1395 // * Microsoft Pinyin ABC Input Style
1396 // - {FCA121D2-8C6D-41FB-B2DE-A2AD110D4820} (Win7)
1397 return TextInputProcessorID::eUnknown
;
1400 TextInputProcessorID
ComputeActiveTIPAsKorean() {
1401 // {B5FE1F02-D5F2-4445-9C03-C568F23C99A1} (Win7, Win8.1, Win10)
1402 static const GUID kMicrosoftIMEForKoreanGUID
= {
1406 {0x9C, 0x03, 0xC5, 0x68, 0xF2, 0x3C, 0x99, 0xA1}};
1407 if (mActiveTIPGUID
== kMicrosoftIMEForKoreanGUID
) {
1408 return TextInputProcessorID::eMicrosoftIMEForKorean
;
1410 // {B60AF051-257A-46BC-B9D3-84DAD819BAFB} (Win8.1, Win10)
1411 static const GUID kMicrosoftOldHangulGUID
= {
1415 {0xB9, 0xD3, 0x84, 0xDA, 0xD8, 0x19, 0xBA, 0xFB}};
1416 if (mActiveTIPGUID
== kMicrosoftOldHangulGUID
) {
1417 return TextInputProcessorID::eMicrosoftOldHangul
;
1420 // NOTE: There is the other Korean TIP installed in Windows:
1421 // * Microsoft IME 2010
1422 // - {48878C45-93F9-4aaf-A6A1-272CD863C4F5} (Win7)
1424 return TextInputProcessorID::eUnknown
;
1427 public: // ITfInputProcessorProfileActivationSink
1428 STDMETHODIMP
OnActivated(DWORD
, LANGID
, REFCLSID
, REFGUID
, REFGUID
, HKL
,
1433 virtual ~TSFStaticSink() {}
1435 bool EnsureInitActiveTIPKeyboard();
1439 void GetTIPDescription(REFCLSID aTextService
, LANGID aLangID
,
1440 REFGUID aProfile
, nsAString
& aDescription
);
1441 bool IsTIPCategoryKeyboard(REFCLSID aTextService
, LANGID aLangID
,
1444 TextInputProcessorID mActiveTIP
;
1446 // Cookie of installing ITfInputProcessorProfileActivationSink
1447 DWORD mIPProfileCookie
;
1451 // True if current IME is implemented with IMM.
1453 // True if OnActivated() is already called
1454 bool mOnActivatedCalled
;
1456 RefPtr
<ITfThreadMgr
> mThreadMgr
;
1457 RefPtr
<ITfInputProcessorProfiles
> mInputProcessorProfiles
;
1459 // Active TIP keyboard's description. If active language profile isn't TIP,
1460 // i.e., IMM-IME or just a keyboard layout, this is empty.
1461 nsString mActiveTIPKeyboardDescription
;
1463 // Active TIP's GUID
1464 GUID mActiveTIPGUID
;
1466 static StaticRefPtr
<TSFStaticSink
> sInstance
;
1469 StaticRefPtr
<TSFStaticSink
> TSFStaticSink::sInstance
;
1471 TSFStaticSink::TSFStaticSink()
1472 : mActiveTIP(TextInputProcessorID::eNotComputed
),
1473 mIPProfileCookie(TF_INVALID_COOKIE
),
1476 mOnActivatedCalled(false),
1477 mActiveTIPGUID(GUID_NULL
) {}
1479 bool TSFStaticSink::Init(ITfThreadMgr
* aThreadMgr
,
1480 ITfInputProcessorProfiles
* aInputProcessorProfiles
) {
1481 MOZ_ASSERT(!mThreadMgr
&& !mInputProcessorProfiles
,
1482 "TSFStaticSink::Init() must be called only once");
1484 mThreadMgr
= aThreadMgr
;
1485 mInputProcessorProfiles
= aInputProcessorProfiles
;
1487 RefPtr
<ITfSource
> source
;
1489 mThreadMgr
->QueryInterface(IID_ITfSource
, getter_AddRefs(source
));
1491 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1492 ("0x%p TSFStaticSink::Init() FAILED to get ITfSource "
1493 "instance (0x%08X)",
1498 // NOTE: On Vista or later, Windows let us know activate IME changed only
1499 // with ITfInputProcessorProfileActivationSink.
1500 hr
= source
->AdviseSink(
1501 IID_ITfInputProcessorProfileActivationSink
,
1502 static_cast<ITfInputProcessorProfileActivationSink
*>(this),
1504 if (FAILED(hr
) || mIPProfileCookie
== TF_INVALID_COOKIE
) {
1505 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1506 ("0x%p TSFStaticSink::Init() FAILED to install "
1507 "ITfInputProcessorProfileActivationSink (0x%08X)",
1512 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1513 ("0x%p TSFStaticSink::Init(), "
1514 "mIPProfileCookie=0x%08X",
1515 this, mIPProfileCookie
));
1519 void TSFStaticSink::Destroy() {
1520 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1521 ("0x%p TSFStaticSink::Shutdown() "
1522 "mIPProfileCookie=0x%08X",
1523 this, mIPProfileCookie
));
1525 if (mIPProfileCookie
!= TF_INVALID_COOKIE
) {
1526 RefPtr
<ITfSource
> source
;
1528 mThreadMgr
->QueryInterface(IID_ITfSource
, getter_AddRefs(source
));
1530 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1531 ("0x%p TSFStaticSink::Shutdown() FAILED to get "
1532 "ITfSource instance (0x%08X)",
1535 hr
= source
->UnadviseSink(mIPProfileCookie
);
1537 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1538 ("0x%p TSFTextStore::Shutdown() FAILED to uninstall "
1539 "ITfInputProcessorProfileActivationSink (0x%08X)",
1545 mThreadMgr
= nullptr;
1546 mInputProcessorProfiles
= nullptr;
1550 TSFStaticSink::OnActivated(DWORD dwProfileType
, LANGID langid
, REFCLSID rclsid
,
1551 REFGUID catid
, REFGUID guidProfile
, HKL hkl
,
1553 if ((dwFlags
& TF_IPSINK_FLAG_ACTIVE
) &&
1554 (dwProfileType
== TF_PROFILETYPE_KEYBOARDLAYOUT
||
1555 catid
== GUID_TFCAT_TIP_KEYBOARD
)) {
1556 mOnActivatedCalled
= true;
1557 mActiveTIP
= TextInputProcessorID::eNotComputed
;
1558 mActiveTIPGUID
= guidProfile
;
1559 mLangID
= langid
& 0xFFFF;
1560 mIsIMM_IME
= IsIMM_IME(hkl
);
1561 GetTIPDescription(rclsid
, langid
, guidProfile
,
1562 mActiveTIPKeyboardDescription
);
1563 if (mActiveTIPGUID
!= GUID_NULL
) {
1564 // key should be "LocaleID|Description". Although GUID of the
1565 // profile is unique key since description may be localized for system
1566 // language, unfortunately, it's too long to record as key with its
1567 // description. Therefore, we should record only the description with
1568 // LocaleID because Microsoft IME may not include language information.
1569 // 72 is kMaximumKeyStringLength in TelemetryScalar.cpp
1571 key
.AppendPrintf("0x%04X|", mLangID
);
1572 nsAutoString
description(mActiveTIPKeyboardDescription
);
1573 static const uint32_t kMaxDescriptionLength
= 72 - key
.Length();
1574 if (description
.Length() > kMaxDescriptionLength
) {
1575 if (NS_IS_LOW_SURROGATE(description
[kMaxDescriptionLength
- 1]) &&
1576 NS_IS_HIGH_SURROGATE(description
[kMaxDescriptionLength
- 2])) {
1577 description
.Truncate(kMaxDescriptionLength
- 2);
1579 description
.Truncate(kMaxDescriptionLength
- 1);
1582 description
.Append(char16_t(0x2026));
1584 key
.Append(description
);
1585 Telemetry::ScalarSet(Telemetry::ScalarID::WIDGET_IME_NAME_ON_WINDOWS
, key
,
1588 // Notify IMEHandler of changing active keyboard layout.
1589 IMEHandler::OnKeyboardLayoutChanged();
1591 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1592 ("0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), "
1593 "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
1594 "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
1595 "mActiveTIPDescription=\"%s\"",
1597 dwProfileType
== TF_PROFILETYPE_INPUTPROCESSOR
1598 ? "TF_PROFILETYPE_INPUTPROCESSOR"
1599 : dwProfileType
== TF_PROFILETYPE_KEYBOARDLAYOUT
1600 ? "TF_PROFILETYPE_KEYBOARDLAYOUT"
1602 dwProfileType
, langid
, GetCLSIDNameStr(rclsid
).get(),
1603 GetGUIDNameStr(catid
).get(), GetGUIDNameStr(guidProfile
).get(), hkl
,
1604 dwFlags
, GetBoolName(dwFlags
& TF_IPSINK_FLAG_ACTIVE
),
1605 GetBoolName(mIsIMM_IME
),
1606 NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription
).get()));
1610 bool TSFStaticSink::EnsureInitActiveTIPKeyboard() {
1611 if (mOnActivatedCalled
) {
1615 RefPtr
<ITfInputProcessorProfileMgr
> profileMgr
;
1616 HRESULT hr
= mInputProcessorProfiles
->QueryInterface(
1617 IID_ITfInputProcessorProfileMgr
, getter_AddRefs(profileMgr
));
1618 if (FAILED(hr
) || !profileMgr
) {
1619 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1620 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1621 "to get input processor profile manager, hr=0x%08X",
1626 TF_INPUTPROCESSORPROFILE profile
;
1627 hr
= profileMgr
->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD
, &profile
);
1628 if (hr
== S_FALSE
) {
1629 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1630 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1631 "to get active keyboard layout profile due to no active profile, "
1634 // XXX Should we call OnActivated() with arguments like non-TIP in this
1639 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1640 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), FAILED "
1641 "to get active TIP keyboard, hr=0x%08X",
1646 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1647 ("0x%p TSFStaticSink::EnsureInitActiveLanguageProfile(), "
1648 "calling OnActivated() manually...",
1650 OnActivated(profile
.dwProfileType
, profile
.langid
, profile
.clsid
,
1651 profile
.catid
, profile
.guidProfile
, ::GetKeyboardLayout(0),
1652 TF_IPSINK_FLAG_ACTIVE
);
1656 void TSFStaticSink::GetTIPDescription(REFCLSID aTextService
, LANGID aLangID
,
1658 nsAString
& aDescription
) {
1659 aDescription
.Truncate();
1661 if (aTextService
== CLSID_NULL
|| aProfile
== GUID_NULL
) {
1665 BSTR description
= nullptr;
1666 HRESULT hr
= mInputProcessorProfiles
->GetLanguageProfileDescription(
1667 aTextService
, aLangID
, aProfile
, &description
);
1669 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1670 ("0x%p TSFStaticSink::InitActiveTIPDescription() FAILED "
1671 "due to GetLanguageProfileDescription() failure, hr=0x%08X",
1676 if (description
&& description
[0]) {
1677 aDescription
.Assign(description
);
1679 ::SysFreeString(description
);
1682 bool TSFStaticSink::IsTIPCategoryKeyboard(REFCLSID aTextService
, LANGID aLangID
,
1684 if (aTextService
== CLSID_NULL
|| aProfile
== GUID_NULL
) {
1688 RefPtr
<IEnumTfLanguageProfiles
> enumLangProfiles
;
1689 HRESULT hr
= mInputProcessorProfiles
->EnumLanguageProfiles(
1690 aLangID
, getter_AddRefs(enumLangProfiles
));
1691 if (FAILED(hr
) || !enumLangProfiles
) {
1692 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1693 ("0x%p TSFStaticSink::IsTIPCategoryKeyboard(), FAILED "
1694 "to get language profiles enumerator, hr=0x%08X",
1699 TF_LANGUAGEPROFILE profile
;
1701 while (SUCCEEDED(enumLangProfiles
->Next(1, &profile
, &fetch
)) && fetch
) {
1702 // XXX We're not sure a profile is registered with two or more categories.
1703 if (profile
.clsid
== aTextService
&& profile
.guidProfile
== aProfile
&&
1704 profile
.catid
== GUID_TFCAT_TIP_KEYBOARD
) {
1711 /******************************************************************/
1713 /******************************************************************/
1715 class TSFPrefs final
{
1717 #define DECL_AND_IMPL_BOOL_PREF(aPref, aName, aDefaultValue) \
1718 static bool aName() { \
1719 static bool s##aName##Value = Preferences::GetBool(aPref, aDefaultValue); \
1720 return s##aName##Value; \
1723 DECL_AND_IMPL_BOOL_PREF("intl.ime.hack.set_input_scope_of_url_bar_to_default",
1724 ShouldSetInputScopeOfURLBarToDefault
, true)
1725 DECL_AND_IMPL_BOOL_PREF(
1726 "intl.tsf.hack.allow_to_stop_hacking_on_build_17643_or_later",
1727 AllowToStopHackingOnBuild17643OrLater
, false)
1728 DECL_AND_IMPL_BOOL_PREF("intl.tsf.hack.atok.create_native_caret",
1729 NeedToCreateNativeCaretForLegacyATOK
, true)
1730 DECL_AND_IMPL_BOOL_PREF(
1731 "intl.tsf.hack.atok.do_not_return_no_layout_error_of_composition_string",
1732 DoNotReturnNoLayoutErrorToATOKOfCompositionString
, true)
1733 DECL_AND_IMPL_BOOL_PREF(
1734 "intl.tsf.hack.japanist10."
1735 "do_not_return_no_layout_error_of_composition_string",
1736 DoNotReturnNoLayoutErrorToJapanist10OfCompositionString
, true)
1737 DECL_AND_IMPL_BOOL_PREF(
1738 "intl.tsf.hack.ms_simplified_chinese.do_not_return_no_layout_error",
1739 DoNotReturnNoLayoutErrorToMSSimplifiedTIP
, true)
1740 DECL_AND_IMPL_BOOL_PREF(
1741 "intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error",
1742 DoNotReturnNoLayoutErrorToMSTraditionalTIP
, true)
1743 DECL_AND_IMPL_BOOL_PREF(
1744 "intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error",
1745 DoNotReturnNoLayoutErrorToFreeChangJie
, true)
1746 DECL_AND_IMPL_BOOL_PREF(
1747 "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_first_"
1749 DoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar
, true)
1750 DECL_AND_IMPL_BOOL_PREF(
1751 "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_caret",
1752 DoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret
, true)
1753 DECL_AND_IMPL_BOOL_PREF(
1754 "intl.tsf.hack.ms_simplified_chinese.query_insert_result",
1755 NeedToHackQueryInsertForMSSimplifiedTIP
, true)
1756 DECL_AND_IMPL_BOOL_PREF(
1757 "intl.tsf.hack.ms_traditional_chinese.query_insert_result",
1758 NeedToHackQueryInsertForMSTraditionalTIP
, true)
1760 #undef DECL_AND_IMPL_BOOL_PREF
1763 /******************************************************************/
1765 /******************************************************************/
1767 StaticRefPtr
<ITfThreadMgr
> TSFTextStore::sThreadMgr
;
1768 StaticRefPtr
<ITfMessagePump
> TSFTextStore::sMessagePump
;
1769 StaticRefPtr
<ITfKeystrokeMgr
> TSFTextStore::sKeystrokeMgr
;
1770 StaticRefPtr
<ITfDisplayAttributeMgr
> TSFTextStore::sDisplayAttrMgr
;
1771 StaticRefPtr
<ITfCategoryMgr
> TSFTextStore::sCategoryMgr
;
1772 StaticRefPtr
<ITfCompartment
> TSFTextStore::sCompartmentForOpenClose
;
1773 StaticRefPtr
<ITfDocumentMgr
> TSFTextStore::sDisabledDocumentMgr
;
1774 StaticRefPtr
<ITfContext
> TSFTextStore::sDisabledContext
;
1775 StaticRefPtr
<ITfInputProcessorProfiles
> TSFTextStore::sInputProcessorProfiles
;
1776 StaticRefPtr
<TSFTextStore
> TSFTextStore::sEnabledTextStore
;
1777 const MSG
* TSFTextStore::sHandlingKeyMsg
= nullptr;
1778 DWORD
TSFTextStore::sClientId
= 0;
1779 bool TSFTextStore::sIsKeyboardEventDispatched
= false;
1781 #define TEXTSTORE_DEFAULT_VIEW (1)
1783 TSFTextStore::TSFTextStore()
1788 mHandlingKeyMessage(0),
1789 mContentForTSF(mComposition
, mSelectionForTSF
),
1790 mRequestedAttrValues(false),
1791 mIsRecordingActionsWithoutLock(false),
1792 mHasReturnedNoLayoutError(false),
1793 mWaitingQueryLayout(false),
1794 mPendingDestroy(false),
1795 mDeferClearingContentForTSF(false),
1796 mDeferNotifyingTSF(false),
1797 mDeferCommittingComposition(false),
1798 mDeferCancellingComposition(false),
1800 mBeingDestroyed(false) {
1801 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
1802 mRequestedAttrs
[i
] = false;
1805 // We hope that 5 or more actions don't occur at once.
1806 mPendingActions
.SetCapacity(5);
1808 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1809 ("0x%p TSFTextStore::TSFTextStore() SUCCEEDED", this));
1812 TSFTextStore::~TSFTextStore() {
1813 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1814 ("0x%p TSFTextStore instance is destroyed", this));
1817 bool TSFTextStore::Init(nsWindowBase
* aWidget
, const InputContext
& aContext
) {
1818 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1819 ("0x%p TSFTextStore::Init(aWidget=0x%p)", this, aWidget
));
1821 if (NS_WARN_IF(!aWidget
) || NS_WARN_IF(aWidget
->Destroyed())) {
1822 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1823 ("0x%p TSFTextStore::Init() FAILED due to being initialized with "
1830 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1831 ("0x%p TSFTextStore::Init() FAILED due to already initialized",
1837 if (NS_WARN_IF(!mWidget
)) {
1838 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1839 ("0x%p TSFTextStore::Init() FAILED "
1840 "due to aWidget is nullptr ",
1844 mDispatcher
= mWidget
->GetTextEventDispatcher();
1845 if (NS_WARN_IF(!mDispatcher
)) {
1846 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1847 ("0x%p TSFTextStore::Init() FAILED "
1848 "due to aWidget->GetTextEventDispatcher() failure",
1853 SetInputScope(aContext
.mHTMLInputType
, aContext
.mHTMLInputInputmode
,
1854 aContext
.mInPrivateBrowsing
);
1856 // Create document manager
1857 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
1858 RefPtr
<ITfDocumentMgr
> documentMgr
;
1859 HRESULT hr
= threadMgr
->CreateDocumentMgr(getter_AddRefs(documentMgr
));
1860 if (NS_WARN_IF(FAILED(hr
))) {
1861 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1862 ("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr "
1867 if (NS_WARN_IF(mDestroyed
)) {
1869 sTextStoreLog
, LogLevel::Error
,
1870 ("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr due to "
1871 "TextStore being destroyed during calling "
1872 "ITfThreadMgr::CreateDocumentMgr()",
1876 // Create context and add it to document manager
1877 RefPtr
<ITfContext
> context
;
1878 hr
= documentMgr
->CreateContext(sClientId
, 0,
1879 static_cast<ITextStoreACP
*>(this),
1880 getter_AddRefs(context
), &mEditCookie
);
1881 if (NS_WARN_IF(FAILED(hr
))) {
1882 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1883 ("0x%p TSFTextStore::Init() FAILED to create the context "
1888 if (NS_WARN_IF(mDestroyed
)) {
1889 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1890 ("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
1891 "TextStore being destroyed during calling "
1892 "ITfDocumentMgr::CreateContext()",
1897 hr
= documentMgr
->Push(context
);
1898 if (NS_WARN_IF(FAILED(hr
))) {
1899 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1900 ("0x%p TSFTextStore::Init() FAILED to push the context (0x%08X)",
1904 if (NS_WARN_IF(mDestroyed
)) {
1905 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
1906 ("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
1907 "TextStore being destroyed during calling ITfDocumentMgr::Push()",
1909 documentMgr
->Pop(TF_POPF_ALL
);
1913 mDocumentMgr
= documentMgr
;
1916 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1917 ("0x%p TSFTextStore::Init() succeeded: "
1918 "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
1919 this, mDocumentMgr
.get(), mContext
.get(), mEditCookie
));
1924 void TSFTextStore::Destroy() {
1925 if (mBeingDestroyed
) {
1929 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1930 ("0x%p TSFTextStore::Destroy(), mLock=%s, "
1931 "mComposition.IsComposing()=%s, mHandlingKeyMessage=%u",
1932 this, GetLockFlagNameStr(mLock
).get(),
1933 GetBoolName(mComposition
.IsComposing()), mHandlingKeyMessage
));
1937 // Destroy native caret first because it's not directly related to TSF and
1938 // there may be another textstore which gets focus. So, we should avoid
1939 // to destroy caret after the new one recreates caret.
1940 IMEHandler::MaybeDestroyNativeCaret();
1943 mPendingDestroy
= true;
1947 AutoRestore
<bool> savedBeingDestroyed(mBeingDestroyed
);
1948 mBeingDestroyed
= true;
1950 // If there is composition, TSF keeps the composition even after the text
1951 // store destroyed. So, we should clear the composition here.
1952 if (mComposition
.IsComposing()) {
1953 CommitCompositionInternal(false);
1957 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
1958 ("0x%p TSFTextStore::Destroy(), calling "
1959 "ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
1961 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
1962 sink
->OnLayoutChange(TS_LC_DESTROY
, TEXTSTORE_DEFAULT_VIEW
);
1965 // If this is called during handling a keydown or keyup message, we should
1966 // put off to release TSF objects until it completely finishes since
1967 // MS-IME for Japanese refers some objects without grabbing them.
1968 if (!mHandlingKeyMessage
) {
1969 ReleaseTSFObjects();
1972 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1973 ("0x%p TSFTextStore::Destroy() succeeded", this));
1976 void TSFTextStore::ReleaseTSFObjects() {
1977 MOZ_ASSERT(!mHandlingKeyMessage
);
1979 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
1980 ("0x%p TSFTextStore::ReleaseTSFObjects()", this));
1984 RefPtr
<ITfDocumentMgr
> documentMgr
= mDocumentMgr
.forget();
1985 documentMgr
->Pop(TF_POPF_ALL
);
1989 mDispatcher
= nullptr;
1991 if (!mMouseTrackers
.IsEmpty()) {
1992 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
1993 ("0x%p TSFTextStore::ReleaseTSFObjects(), "
1994 "removing a mouse tracker...",
1996 mMouseTrackers
.Clear();
1999 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2000 ("0x%p TSFTextStore::ReleaseTSFObjects() completed", this));
2004 TSFTextStore::QueryInterface(REFIID riid
, void** ppv
) {
2006 if ((IID_IUnknown
== riid
) || (IID_ITextStoreACP
== riid
)) {
2007 *ppv
= static_cast<ITextStoreACP
*>(this);
2008 } else if (IID_ITfContextOwnerCompositionSink
== riid
) {
2009 *ppv
= static_cast<ITfContextOwnerCompositionSink
*>(this);
2010 } else if (IID_ITfMouseTrackerACP
== riid
) {
2011 *ppv
= static_cast<ITfMouseTrackerACP
*>(this);
2018 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2019 ("0x%p TSFTextStore::QueryInterface() FAILED, riid=%s", this,
2020 GetRIIDNameStr(riid
).get()));
2021 return E_NOINTERFACE
;
2025 TSFTextStore::AdviseSink(REFIID riid
, IUnknown
* punk
, DWORD dwMask
) {
2027 sTextStoreLog
, LogLevel::Info
,
2028 ("0x%p TSFTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), "
2029 "mSink=0x%p, mSinkMask=%s",
2030 this, GetRIIDNameStr(riid
).get(), punk
, GetSinkMaskNameStr(dwMask
).get(),
2031 mSink
.get(), GetSinkMaskNameStr(mSinkMask
).get()));
2034 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2035 ("0x%p TSFTextStore::AdviseSink() FAILED due to the null punk",
2037 return E_UNEXPECTED
;
2040 if (IID_ITextStoreACPSink
!= riid
) {
2041 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2042 ("0x%p TSFTextStore::AdviseSink() FAILED due to "
2043 "unsupported interface",
2045 return E_INVALIDARG
; // means unsupported interface.
2050 punk
->QueryInterface(IID_ITextStoreACPSink
, getter_AddRefs(mSink
));
2052 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2053 ("0x%p TSFTextStore::AdviseSink() FAILED due to "
2054 "punk not having the interface",
2056 return E_UNEXPECTED
;
2059 // If sink is already installed we check to see if they are the same
2060 // Get IUnknown from both sides for comparison
2061 RefPtr
<IUnknown
> comparison1
, comparison2
;
2062 punk
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison1
));
2063 mSink
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison2
));
2064 if (comparison1
!= comparison2
) {
2065 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2066 ("0x%p TSFTextStore::AdviseSink() FAILED due to "
2067 "the sink being different from the stored sink",
2069 return CONNECT_E_ADVISELIMIT
;
2072 // Update mask either for a new sink or an existing sink
2078 TSFTextStore::UnadviseSink(IUnknown
* punk
) {
2079 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2080 ("0x%p TSFTextStore::UnadviseSink(punk=0x%p), mSink=0x%p", this, punk
,
2084 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2085 ("0x%p TSFTextStore::UnadviseSink() FAILED due to the null punk",
2087 return E_INVALIDARG
;
2090 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2091 ("0x%p TSFTextStore::UnadviseSink() FAILED due to "
2092 "any sink not stored",
2094 return CONNECT_E_NOCONNECTION
;
2096 // Get IUnknown from both sides for comparison
2097 RefPtr
<IUnknown
> comparison1
, comparison2
;
2098 punk
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison1
));
2099 mSink
->QueryInterface(IID_IUnknown
, getter_AddRefs(comparison2
));
2100 // Unadvise only if sinks are the same
2101 if (comparison1
!= comparison2
) {
2102 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2103 ("0x%p TSFTextStore::UnadviseSink() FAILED due to "
2104 "the sink being different from the stored sink",
2106 return CONNECT_E_NOCONNECTION
;
2114 TSFTextStore::RequestLock(DWORD dwLockFlags
, HRESULT
* phrSession
) {
2115 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2116 ("0x%p TSFTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), "
2117 "mLock=%s, mDestroyed=%s",
2118 this, GetLockFlagNameStr(dwLockFlags
).get(), phrSession
,
2119 GetLockFlagNameStr(mLock
).get(), GetBoolName(mDestroyed
)));
2122 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2123 ("0x%p TSFTextStore::RequestLock() FAILED due to "
2124 "any sink not stored",
2129 (!mContentForTSF
.IsInitialized() || mSelectionForTSF
.IsDirty())) {
2130 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2131 ("0x%p TSFTextStore::RequestLock() FAILED due to "
2132 "being destroyed and no information of the contents",
2137 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2138 ("0x%p TSFTextStore::RequestLock() FAILED due to "
2141 return E_INVALIDARG
;
2146 mLock
= dwLockFlags
& (~TS_LF_SYNC
);
2148 sTextStoreLog
, LogLevel::Info
,
2149 ("0x%p Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2150 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
2151 this, GetLockFlagNameStr(mLock
).get()));
2152 // Don't release this instance during this lock because this is called by
2153 // TSF but they don't grab us during this call.
2154 RefPtr
<TSFTextStore
> kungFuDeathGrip(this);
2155 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
2156 *phrSession
= sink
->OnLockGranted(mLock
);
2158 sTextStoreLog
, LogLevel::Info
,
2159 ("0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2160 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
2161 this, GetLockFlagNameStr(mLock
).get()));
2163 while (mLockQueued
) {
2164 mLock
= mLockQueued
;
2166 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2167 ("0x%p Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
2168 ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
2170 this, GetLockFlagNameStr(mLock
).get()));
2171 sink
->OnLockGranted(mLock
);
2172 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2173 ("0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2174 "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
2176 this, GetLockFlagNameStr(mLock
).get()));
2180 // The document is now completely unlocked.
2183 MaybeFlushPendingNotifications();
2185 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2186 ("0x%p TSFTextStore::RequestLock() succeeded: *phrSession=%s",
2187 this, GetTextStoreReturnValueName(*phrSession
)));
2191 // only time when reentrant lock is allowed is when caller holds a
2192 // read-only lock and is requesting an async write lock
2193 if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags
) &&
2194 !(dwLockFlags
& TS_LF_SYNC
)) {
2195 *phrSession
= TS_S_ASYNC
;
2196 mLockQueued
= dwLockFlags
& (~TS_LF_SYNC
);
2198 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2199 ("0x%p TSFTextStore::RequestLock() stores the request in the "
2200 "queue, *phrSession=TS_S_ASYNC",
2205 // no more locks allowed
2206 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2207 ("0x%p TSFTextStore::RequestLock() didn't allow to lock, "
2208 "*phrSession=TS_E_SYNCHRONOUS",
2210 *phrSession
= TS_E_SYNCHRONOUS
;
2214 void TSFTextStore::DidLockGranted() {
2215 if (IsReadWriteLocked()) {
2216 // FreeCJ (TIP for Traditional Chinese) calls SetSelection() to set caret
2217 // to the start of composition string and insert a full width space for
2218 // a placeholder with a call of SetText(). After that, it calls
2219 // OnUpdateComposition() without new range. Therefore, let's record the
2220 // composition update information here.
2221 CompleteLastActionIfStillIncomplete();
2223 FlushPendingActions();
2226 // If the widget has gone, we don't need to notify anything.
2227 if (mDestroyed
|| !mWidget
|| mWidget
->Destroyed()) {
2228 mPendingSelectionChangeData
.Clear();
2229 mHasReturnedNoLayoutError
= false;
2233 void TSFTextStore::DispatchEvent(WidgetGUIEvent
& aEvent
) {
2234 if (NS_WARN_IF(!mWidget
) || NS_WARN_IF(mWidget
->Destroyed())) {
2237 // If the event isn't a query content event, the event may be handled
2238 // asynchronously. So, we should put off to answer from GetTextExt() etc.
2239 if (!aEvent
.AsQueryContentEvent()) {
2240 mDeferNotifyingTSF
= true;
2242 mWidget
->DispatchWindowEvent(&aEvent
);
2245 void TSFTextStore::FlushPendingActions() {
2246 if (!mWidget
|| mWidget
->Destroyed()) {
2247 // Note that don't clear mContentForTSF because TIP may try to commit
2248 // composition with a document lock. In such case, TSFTextStore needs to
2249 // behave as expected by TIP.
2250 mPendingActions
.Clear();
2251 mPendingSelectionChangeData
.Clear();
2252 mHasReturnedNoLayoutError
= false;
2256 // Some TIP may request lock but does nothing during the lock. In such case,
2257 // this should do nothing. For example, when MS-IME for Japanese is active
2258 // and we're inactivating, this case occurs and causes different behavior
2259 // from the other TIPs.
2260 if (mPendingActions
.IsEmpty()) {
2264 RefPtr
<nsWindowBase
> widget(mWidget
);
2265 nsresult rv
= mDispatcher
->BeginNativeInputTransaction();
2266 if (NS_WARN_IF(NS_FAILED(rv
))) {
2267 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2268 ("0x%p TSFTextStore::FlushPendingActions() "
2269 "FAILED due to BeginNativeInputTransaction() failure",
2273 for (uint32_t i
= 0; i
< mPendingActions
.Length(); i
++) {
2274 PendingAction
& action
= mPendingActions
[i
];
2275 switch (action
.mType
) {
2276 case PendingAction::Type::eKeyboardEvent
:
2278 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2279 ("0x%p TSFTextStore::FlushPendingActions() "
2280 "IGNORED pending KeyboardEvent(%s) due to already destroyed",
2281 action
.mKeyMsg
.message
== WM_KEYDOWN
? "eKeyDown" : "eKeyUp",
2284 MOZ_DIAGNOSTIC_ASSERT(action
.mKeyMsg
.message
== WM_KEYDOWN
||
2285 action
.mKeyMsg
.message
== WM_KEYUP
);
2286 DispatchKeyboardEventAsProcessedByIME(action
.mKeyMsg
);
2287 if (!widget
|| widget
->Destroyed()) {
2291 case PendingAction::Type::eCompositionStart
: {
2292 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2293 ("0x%p TSFTextStore::FlushPendingActions() "
2294 "flushing Type::eCompositionStart={ mSelectionStart=%d, "
2295 "mSelectionLength=%d }, mDestroyed=%s",
2296 this, action
.mSelectionStart
, action
.mSelectionLength
,
2297 GetBoolName(mDestroyed
)));
2300 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2301 ("0x%p TSFTextStore::FlushPendingActions() "
2302 "IGNORED pending compositionstart due to already destroyed",
2307 if (action
.mAdjustSelection
) {
2308 // Select composition range so the new composition replaces the range
2309 WidgetSelectionEvent
selectionSet(true, eSetSelection
, widget
);
2310 widget
->InitEvent(selectionSet
);
2311 selectionSet
.mOffset
= static_cast<uint32_t>(action
.mSelectionStart
);
2312 selectionSet
.mLength
= static_cast<uint32_t>(action
.mSelectionLength
);
2313 selectionSet
.mReversed
= false;
2314 DispatchEvent(selectionSet
);
2315 if (!selectionSet
.mSucceeded
) {
2316 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2317 ("0x%p TSFTextStore::FlushPendingActions() "
2318 "FAILED due to eSetSelection failure",
2324 // eCompositionStart always causes
2325 // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. Therefore, we should
2326 // wait to clear mContentForTSF until it's notified.
2327 mDeferClearingContentForTSF
= true;
2329 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2330 ("0x%p TSFTextStore::FlushPendingActions() "
2331 "dispatching compositionstart event...",
2333 WidgetEventTime eventTime
= widget
->CurrentMessageWidgetEventTime();
2334 nsEventStatus status
;
2335 rv
= mDispatcher
->StartComposition(status
, &eventTime
);
2336 if (NS_WARN_IF(NS_FAILED(rv
))) {
2337 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2338 ("0x%p TSFTextStore::FlushPendingActions() "
2339 "FAILED to dispatch compositionstart event, "
2340 "IsHandlingComposition()=%s",
2341 this, GetBoolName(IsHandlingComposition())));
2342 // XXX Is this right? If there is a composition in content,
2343 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2344 mDeferClearingContentForTSF
= !IsHandlingComposition();
2346 if (!widget
|| widget
->Destroyed()) {
2351 case PendingAction::Type::eCompositionUpdate
: {
2352 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2353 ("0x%p TSFTextStore::FlushPendingActions() "
2354 "flushing Type::eCompositionUpdate={ mData=\"%s\", "
2355 "mRanges=0x%p, mRanges->Length()=%d }",
2356 this, GetEscapedUTF8String(action
.mData
).get(),
2357 action
.mRanges
.get(),
2358 action
.mRanges
? action
.mRanges
->Length() : 0));
2360 // eCompositionChange causes a DOM text event, the IME will be notified
2361 // of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. In this case, we
2362 // should not clear mContentForTSF until we notify the IME of the
2363 // composition update.
2364 mDeferClearingContentForTSF
= true;
2366 rv
= mDispatcher
->SetPendingComposition(action
.mData
, action
.mRanges
);
2367 if (NS_WARN_IF(NS_FAILED(rv
))) {
2368 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2369 ("0x%p TSFTextStore::FlushPendingActions() "
2370 "FAILED to setting pending composition... "
2371 "IsHandlingComposition()=%s",
2372 this, GetBoolName(IsHandlingComposition())));
2373 // XXX Is this right? If there is a composition in content,
2374 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2375 mDeferClearingContentForTSF
= !IsHandlingComposition();
2377 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2378 ("0x%p TSFTextStore::FlushPendingActions() "
2379 "dispatching compositionchange event...",
2381 WidgetEventTime eventTime
= widget
->CurrentMessageWidgetEventTime();
2382 nsEventStatus status
;
2383 rv
= mDispatcher
->FlushPendingComposition(status
, &eventTime
);
2384 if (NS_WARN_IF(NS_FAILED(rv
))) {
2385 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2386 ("0x%p TSFTextStore::FlushPendingActions() "
2387 "FAILED to dispatch compositionchange event, "
2388 "IsHandlingComposition()=%s",
2389 this, GetBoolName(IsHandlingComposition())));
2390 // XXX Is this right? If there is a composition in content,
2391 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2392 mDeferClearingContentForTSF
= !IsHandlingComposition();
2394 // Be aware, the mWidget might already have been destroyed.
2398 case PendingAction::Type::eCompositionEnd
: {
2399 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2400 ("0x%p TSFTextStore::FlushPendingActions() "
2401 "flushing Type::eCompositionEnd={ mData=\"%s\" }",
2402 this, GetEscapedUTF8String(action
.mData
).get()));
2404 // Dispatching eCompositionCommit causes a DOM text event, then,
2405 // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
2406 // when focused content actually handles the event. For example,
2407 // when focused content is in a remote process, it's sent when
2408 // all dispatched composition events have been handled in the remote
2409 // process. So, until then, we don't have newer content information.
2410 // Therefore, we need to put off to clear mContentForTSF.
2411 mDeferClearingContentForTSF
= true;
2413 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2414 ("0x%p TSFTextStore::FlushPendingActions(), "
2415 "dispatching compositioncommit event...",
2417 WidgetEventTime eventTime
= widget
->CurrentMessageWidgetEventTime();
2418 nsEventStatus status
;
2419 rv
= mDispatcher
->CommitComposition(status
, &action
.mData
, &eventTime
);
2420 if (NS_WARN_IF(NS_FAILED(rv
))) {
2421 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2422 ("0x%p TSFTextStore::FlushPendingActions() "
2423 "FAILED to dispatch compositioncommit event, "
2424 "IsHandlingComposition()=%s",
2425 this, GetBoolName(IsHandlingComposition())));
2426 // XXX Is this right? If there is a composition in content,
2427 // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED?
2428 mDeferClearingContentForTSF
= !IsHandlingComposition();
2432 case PendingAction::Type::eSetSelection
: {
2434 sTextStoreLog
, LogLevel::Debug
,
2435 ("0x%p TSFTextStore::FlushPendingActions() "
2436 "flushing Type::eSetSelection={ mSelectionStart=%d, "
2437 "mSelectionLength=%d, mSelectionReversed=%s }, "
2439 this, action
.mSelectionStart
, action
.mSelectionLength
,
2440 GetBoolName(action
.mSelectionReversed
), GetBoolName(mDestroyed
)));
2443 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2444 ("0x%p TSFTextStore::FlushPendingActions() "
2445 "IGNORED pending selectionset due to already destroyed",
2450 WidgetSelectionEvent
selectionSet(true, eSetSelection
, widget
);
2451 selectionSet
.mOffset
= static_cast<uint32_t>(action
.mSelectionStart
);
2452 selectionSet
.mLength
= static_cast<uint32_t>(action
.mSelectionLength
);
2453 selectionSet
.mReversed
= action
.mSelectionReversed
;
2454 DispatchEvent(selectionSet
);
2455 if (!selectionSet
.mSucceeded
) {
2456 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2457 ("0x%p TSFTextStore::FlushPendingActions() "
2458 "FAILED due to eSetSelection failure",
2465 MOZ_CRASH("unexpected action type");
2468 if (widget
&& !widget
->Destroyed()) {
2472 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2473 ("0x%p TSFTextStore::FlushPendingActions(), "
2474 "qutting since the mWidget has gone",
2478 mPendingActions
.Clear();
2481 void TSFTextStore::MaybeFlushPendingNotifications() {
2482 if (IsReadLocked()) {
2483 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2484 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2485 "putting off flushing pending notifications due to being the "
2486 "document locked...",
2491 if (mDeferCommittingComposition
) {
2492 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2493 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2494 "calling TSFTextStore::CommitCompositionInternal(false)...",
2496 mDeferCommittingComposition
= mDeferCancellingComposition
= false;
2497 CommitCompositionInternal(false);
2498 } else if (mDeferCancellingComposition
) {
2499 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2500 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2501 "calling TSFTextStore::CommitCompositionInternal(true)...",
2503 mDeferCommittingComposition
= mDeferCancellingComposition
= false;
2504 CommitCompositionInternal(true);
2507 if (mDeferNotifyingTSF
) {
2508 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2509 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2510 "putting off flushing pending notifications due to being "
2511 "dispatching events...",
2516 if (mPendingDestroy
) {
2522 // If it's already been destroyed completely, this shouldn't notify TSF of
2523 // anything anymore.
2524 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2525 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2526 "does nothing because this has already destroyed completely...",
2531 if (!mDeferClearingContentForTSF
&& mContentForTSF
.IsInitialized()) {
2532 mContentForTSF
.Clear();
2533 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2534 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2535 "mContentForTSF is cleared",
2539 // When there is no cached content, we can sync actual contents and TSF/TIP
2540 // expecting contents.
2541 RefPtr
<TSFTextStore
> kungFuDeathGrip
= this;
2542 Unused
<< kungFuDeathGrip
;
2543 if (!mContentForTSF
.IsInitialized()) {
2544 if (mPendingTextChangeData
.IsValid()) {
2545 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2546 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2547 "calling TSFTextStore::NotifyTSFOfTextChange()...",
2549 NotifyTSFOfTextChange();
2551 if (mPendingSelectionChangeData
.IsValid()) {
2552 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2553 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2554 "calling TSFTextStore::NotifyTSFOfSelectionChange()...",
2556 NotifyTSFOfSelectionChange();
2560 if (mHasReturnedNoLayoutError
) {
2561 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2562 ("0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
2563 "calling TSFTextStore::NotifyTSFOfLayoutChange()...",
2565 NotifyTSFOfLayoutChange();
2569 void TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME() {
2570 // If we've already been destroyed, we cannot do anything.
2573 sTextStoreLog
, LogLevel::Debug
,
2574 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2575 "does nothing because it's already been destroyed",
2580 // If we're not handling key message or we've already dispatched a keyboard
2581 // event for the handling key message, we should do nothing anymore.
2582 if (!sHandlingKeyMsg
|| sIsKeyboardEventDispatched
) {
2584 sTextStoreLog
, LogLevel::Debug
,
2585 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2586 "does nothing because not necessary to dispatch keyboard event",
2591 sIsKeyboardEventDispatched
= true;
2592 // If the document is locked, just adding the task to dispatching an event
2594 if (IsReadLocked()) {
2596 sTextStoreLog
, LogLevel::Debug
,
2597 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2598 "adding to dispatch a keyboard event into the queue...",
2600 PendingAction
* action
= mPendingActions
.AppendElement();
2601 action
->mType
= PendingAction::Type::eKeyboardEvent
;
2602 memcpy(&action
->mKeyMsg
, sHandlingKeyMsg
, sizeof(MSG
));
2606 // Otherwise, dispatch a keyboard event.
2607 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2608 ("0x%p TSFTextStore::MaybeDispatchKeyboardEventAsProcessedByIME(), "
2609 "trying to dispatch a keyboard event...",
2611 DispatchKeyboardEventAsProcessedByIME(*sHandlingKeyMsg
);
2614 void TSFTextStore::DispatchKeyboardEventAsProcessedByIME(const MSG
& aMsg
) {
2615 MOZ_ASSERT(mWidget
);
2616 MOZ_ASSERT(!mWidget
->Destroyed());
2617 MOZ_ASSERT(!mDestroyed
);
2619 ModifierKeyState modKeyState
;
2621 msg
.wParam
= VK_PROCESSKEY
;
2622 NativeKey
nativeKey(mWidget
, msg
, modKeyState
);
2623 switch (aMsg
.message
) {
2625 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2626 ("0x%p TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2627 "dispatching an eKeyDown event...",
2629 nativeKey
.HandleKeyDownMessage();
2632 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2633 ("0x%p TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2634 "dispatching an eKeyUp event...",
2636 nativeKey
.HandleKeyUpMessage();
2639 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2640 ("0x%p TSFTextStore::DispatchKeyboardEventAsProcessedByIME(), "
2641 "ERROR, it doesn't handle the message",
2648 TSFTextStore::GetStatus(TS_STATUS
* pdcs
) {
2649 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2650 ("0x%p TSFTextStore::GetStatus(pdcs=0x%p)", this, pdcs
));
2653 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2654 ("0x%p TSFTextStore::GetStatus() FAILED due to null pdcs", this));
2655 return E_INVALIDARG
;
2657 // We manage on-screen keyboard by own.
2658 pdcs
->dwDynamicFlags
= TS_SD_INPUTPANEMANUALDISPLAYENABLE
;
2659 // we use a "flat" text model for TSF support so no hidden text
2660 pdcs
->dwStaticFlags
= TS_SS_NOHIDDENTEXT
;
2665 TSFTextStore::QueryInsert(LONG acpTestStart
, LONG acpTestEnd
, ULONG cch
,
2666 LONG
* pacpResultStart
, LONG
* pacpResultEnd
) {
2667 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2668 ("0x%p TSFTextStore::QueryInsert(acpTestStart=%ld, "
2669 "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)",
2670 this, acpTestStart
, acpTestEnd
, cch
, acpTestStart
, acpTestEnd
));
2672 if (!pacpResultStart
|| !pacpResultEnd
) {
2673 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2674 ("0x%p TSFTextStore::QueryInsert() FAILED due to "
2675 "the null argument",
2677 return E_INVALIDARG
;
2680 if (acpTestStart
< 0 || acpTestStart
> acpTestEnd
) {
2681 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2682 ("0x%p TSFTextStore::QueryInsert() FAILED due to "
2685 return E_INVALIDARG
;
2688 // XXX need to adjust to cluster boundary
2689 // Assume we are given good offsets for now
2690 if (IsWin8OrLater() && !mComposition
.IsComposing() &&
2691 ((TSFPrefs::NeedToHackQueryInsertForMSTraditionalTIP() &&
2692 TSFStaticSink::IsMSChangJieOrMSQuickActive()) ||
2693 (TSFPrefs::NeedToHackQueryInsertForMSSimplifiedTIP() &&
2694 TSFStaticSink::IsMSPinyinOrMSWubiActive()))) {
2695 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
2696 ("0x%p TSFTextStore::QueryInsert() WARNING using different "
2697 "result for the TIP",
2699 // Chinese TIPs of Microsoft assume that QueryInsert() returns selected
2700 // range which should be removed.
2701 *pacpResultStart
= acpTestStart
;
2702 *pacpResultEnd
= acpTestEnd
;
2704 *pacpResultStart
= acpTestStart
;
2705 *pacpResultEnd
= acpTestStart
+ cch
;
2708 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2709 ("0x%p TSFTextStore::QueryInsert() succeeded: "
2710 "*pacpResultStart=%ld, *pacpResultEnd=%ld)",
2711 this, *pacpResultStart
, *pacpResultEnd
));
2716 TSFTextStore::GetSelection(ULONG ulIndex
, ULONG ulCount
,
2717 TS_SELECTION_ACP
* pSelection
, ULONG
* pcFetched
) {
2718 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
2719 ("0x%p TSFTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, "
2720 "pSelection=0x%p, pcFetched=0x%p)",
2721 this, ulIndex
, ulCount
, pSelection
, pcFetched
));
2723 if (!IsReadLocked()) {
2725 sTextStoreLog
, LogLevel::Error
,
2726 ("0x%p TSFTextStore::GetSelection() FAILED due to not locked", this));
2729 if (!ulCount
|| !pSelection
|| !pcFetched
) {
2730 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2731 ("0x%p TSFTextStore::GetSelection() FAILED due to "
2734 return E_INVALIDARG
;
2739 if (ulIndex
!= static_cast<ULONG
>(TS_DEFAULT_SELECTION
) && ulIndex
!= 0) {
2740 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2741 ("0x%p TSFTextStore::GetSelection() FAILED due to "
2742 "unsupported selection",
2744 return TS_E_NOSELECTION
;
2747 Selection
& selectionForTSF
= SelectionForTSFRef();
2748 if (selectionForTSF
.IsDirty()) {
2749 if (DoNotReturnErrorFromGetSelection()) {
2750 AutoSetTemporarySelection
temprarySetter(selectionForTSF
);
2751 *pSelection
= selectionForTSF
.ACP();
2754 sTextStoreLog
, LogLevel::Info
,
2755 ("0x%p TSFTextStore::GetSelection() returns fake selection range "
2756 "for avoiding a crash in TSF, "
2757 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2758 this, selectionForTSF
.StartOffset(), selectionForTSF
.EndOffset(),
2759 selectionForTSF
.Length(),
2760 GetBoolName(selectionForTSF
.IsReversed())));
2763 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2764 ("0x%p TSFTextStore::GetSelection() FAILED due to "
2765 "SelectionForTSFRef() failure",
2769 *pSelection
= selectionForTSF
.ACP();
2772 sTextStoreLog
, LogLevel::Info
,
2773 ("0x%p TSFTextStore::GetSelection() succeeded, "
2774 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2775 this, selectionForTSF
.StartOffset(), selectionForTSF
.EndOffset(),
2776 selectionForTSF
.Length(), GetBoolName(selectionForTSF
.IsReversed())));
2781 bool TSFTextStore::DoNotReturnErrorFromGetSelection() {
2782 // There is a crash bug of TSF if we return error from GetSelection().
2783 // That was introduced in Anniversary Update (build 14393, see bug 1312302)
2784 // TODO: We should avoid to run this hack on fixed builds. When we get
2785 // exact build number, we should get back here.
2786 static bool sTSFMayCrashIfGetSelectionReturnsError
=
2787 IsWindows10BuildOrLater(14393);
2788 return sTSFMayCrashIfGetSelectionReturnsError
;
2791 TSFTextStore::Content
& TSFTextStore::ContentForTSFRef() {
2792 // This should be called when the document is locked or the content hasn't
2793 // been abandoned yet.
2794 if (NS_WARN_IF(!IsReadLocked() && !mContentForTSF
.IsInitialized())) {
2795 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2796 ("0x%p TSFTextStore::ContentForTSFRef(), FAILED, due to "
2797 "called wrong timing, IsReadLocked()=%s, "
2798 "mContentForTSF.IsInitialized()=%s",
2799 this, GetBoolName(IsReadLocked()),
2800 GetBoolName(mContentForTSF
.IsInitialized())));
2801 mContentForTSF
.Clear();
2802 return mContentForTSF
;
2805 Selection
& selectionForTSF
= SelectionForTSFRef();
2806 if (selectionForTSF
.IsDirty()) {
2807 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2808 ("0x%p TSFTextStore::ContentForTSFRef(), FAILED, due to "
2809 "SelectionForTSFRef() failure",
2811 mContentForTSF
.Clear();
2812 return mContentForTSF
;
2815 if (!mContentForTSF
.IsInitialized()) {
2817 if (NS_WARN_IF(!GetCurrentText(text
))) {
2818 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2819 ("0x%p TSFTextStore::ContentForTSFRef(), FAILED, due to "
2820 "GetCurrentText() failure",
2822 mContentForTSF
.Clear();
2823 return mContentForTSF
;
2826 mContentForTSF
.Init(text
);
2827 // Basically, the cached content which is expected by TSF/TIP should be
2828 // cleared after active composition is committed or the document lock is
2829 // unlocked. However, in e10s mode, content will be modified
2830 // asynchronously. In such case, mDeferClearingContentForTSF may be
2831 // true until whole dispatched events are handled by the focused editor.
2832 mDeferClearingContentForTSF
= false;
2835 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2836 ("0x%p TSFTextStore::ContentForTSFRef(): "
2837 "mContentForTSF={ mText=\"%s\" (Length()=%u), "
2838 "mLastCompositionString=\"%s\" (Length()=%u), "
2839 "mMinTextModifiedOffset=%u }",
2841 mContentForTSF
.Text().Length() <= 40
2842 ? GetEscapedUTF8String(mContentForTSF
.Text()).get()
2844 mContentForTSF
.Text().Length(),
2845 GetEscapedUTF8String(mContentForTSF
.LastCompositionString()).get(),
2846 mContentForTSF
.LastCompositionString().Length(),
2847 mContentForTSF
.MinTextModifiedOffset()));
2849 return mContentForTSF
;
2852 bool TSFTextStore::CanAccessActualContentDirectly() const {
2853 if (!mContentForTSF
.IsInitialized() || mSelectionForTSF
.IsDirty()) {
2857 // If the cached content has been changed by something except composition,
2858 // the content cache may be different from actual content.
2859 if (mPendingTextChangeData
.IsValid() &&
2860 !mPendingTextChangeData
.mCausedOnlyByComposition
) {
2864 // If the cached selection isn't changed, cached content and actual content
2866 if (!mPendingSelectionChangeData
.IsValid()) {
2870 return mSelectionForTSF
.EqualsExceptDirection(mPendingSelectionChangeData
);
2873 bool TSFTextStore::GetCurrentText(nsAString
& aTextContent
) {
2874 if (mContentForTSF
.IsInitialized()) {
2875 aTextContent
= mContentForTSF
.Text();
2879 MOZ_ASSERT(!mDestroyed
);
2880 MOZ_ASSERT(mWidget
&& !mWidget
->Destroyed());
2882 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
2883 ("0x%p TSFTextStore::GetCurrentText(): "
2884 "retrieving text from the content...",
2887 WidgetQueryContentEvent
queryText(true, eQueryTextContent
, mWidget
);
2888 queryText
.InitForQueryTextContent(0, UINT32_MAX
);
2889 mWidget
->InitEvent(queryText
);
2890 DispatchEvent(queryText
);
2891 if (NS_WARN_IF(!queryText
.mSucceeded
)) {
2892 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2893 ("0x%p TSFTextStore::GetCurrentText(), FAILED, due to "
2894 "eQueryTextContent failure",
2896 aTextContent
.Truncate();
2900 aTextContent
= queryText
.mReply
.mString
;
2904 TSFTextStore::Selection
& TSFTextStore::SelectionForTSFRef() {
2905 if (mSelectionForTSF
.IsDirty()) {
2906 MOZ_ASSERT(!mDestroyed
);
2907 // If the window has never been available, we should crash since working
2908 // with broken values may make TIP confused.
2909 if (!mWidget
|| mWidget
->Destroyed()) {
2913 WidgetQueryContentEvent
querySelection(true, eQuerySelectedText
, mWidget
);
2914 mWidget
->InitEvent(querySelection
);
2915 DispatchEvent(querySelection
);
2916 if (NS_WARN_IF(!querySelection
.mSucceeded
)) {
2917 return mSelectionForTSF
;
2920 mSelectionForTSF
.SetSelection(
2921 querySelection
.mReply
.mOffset
, querySelection
.mReply
.mString
.Length(),
2922 querySelection
.mReply
.mReversed
, querySelection
.GetWritingMode());
2926 sTextStoreLog
, LogLevel::Debug
,
2927 ("0x%p TSFTextStore::SelectionForTSFRef(): "
2928 "acpStart=%d, acpEnd=%d (length=%d), reverted=%s",
2929 this, mSelectionForTSF
.StartOffset(), mSelectionForTSF
.EndOffset(),
2930 mSelectionForTSF
.Length(), GetBoolName(mSelectionForTSF
.IsReversed())));
2932 return mSelectionForTSF
;
2935 static HRESULT
GetRangeExtent(ITfRange
* aRange
, LONG
* aStart
, LONG
* aLength
) {
2936 RefPtr
<ITfRangeACP
> rangeACP
;
2937 aRange
->QueryInterface(IID_ITfRangeACP
, getter_AddRefs(rangeACP
));
2938 NS_ENSURE_TRUE(rangeACP
, E_FAIL
);
2939 return rangeACP
->GetExtent(aStart
, aLength
);
2942 static TextRangeType
GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE
& aDisplayAttr
) {
2943 switch (aDisplayAttr
.bAttr
) {
2944 case TF_ATTR_TARGET_CONVERTED
:
2945 return TextRangeType::eSelectedClause
;
2946 case TF_ATTR_CONVERTED
:
2947 return TextRangeType::eConvertedClause
;
2948 case TF_ATTR_TARGET_NOTCONVERTED
:
2949 return TextRangeType::eSelectedRawClause
;
2951 return TextRangeType::eRawClause
;
2956 TSFTextStore::GetDisplayAttribute(ITfProperty
* aAttrProperty
, ITfRange
* aRange
,
2957 TF_DISPLAYATTRIBUTE
* aResult
) {
2958 NS_ENSURE_TRUE(aAttrProperty
, E_FAIL
);
2959 NS_ENSURE_TRUE(aRange
, E_FAIL
);
2960 NS_ENSURE_TRUE(aResult
, E_FAIL
);
2964 if (MOZ_LOG_TEST(sTextStoreLog
, LogLevel::Debug
)) {
2965 LONG start
= 0, length
= 0;
2966 hr
= GetRangeExtent(aRange
, &start
, &length
);
2968 sTextStoreLog
, LogLevel::Debug
,
2969 ("0x%p TSFTextStore::GetDisplayAttribute(): "
2970 "GetDisplayAttribute range=%ld-%ld (hr=%s)",
2971 this, start
- mComposition
.mStart
,
2972 start
- mComposition
.mStart
+ length
, GetCommonReturnValueName(hr
)));
2976 ::VariantInit(&propValue
);
2977 hr
= aAttrProperty
->GetValue(TfEditCookie(mEditCookie
), aRange
, &propValue
);
2979 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2980 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
2981 "ITfProperty::GetValue() failed",
2985 if (VT_I4
!= propValue
.vt
) {
2986 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
2987 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
2988 "ITfProperty::GetValue() returns non-VT_I4 value",
2990 ::VariantClear(&propValue
);
2994 RefPtr
<ITfCategoryMgr
> categoryMgr
= GetCategoryMgr();
2995 if (NS_WARN_IF(!categoryMgr
)) {
2999 hr
= categoryMgr
->GetGUID(DWORD(propValue
.lVal
), &guid
);
3000 ::VariantClear(&propValue
);
3002 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3003 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3004 "ITfCategoryMgr::GetGUID() failed",
3009 RefPtr
<ITfDisplayAttributeMgr
> displayAttrMgr
= GetDisplayAttributeMgr();
3010 if (NS_WARN_IF(!displayAttrMgr
)) {
3013 RefPtr
<ITfDisplayAttributeInfo
> info
;
3014 hr
= displayAttrMgr
->GetDisplayAttributeInfo(guid
, getter_AddRefs(info
),
3016 if (FAILED(hr
) || !info
) {
3017 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3018 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3019 "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed",
3024 hr
= info
->GetAttributeInfo(aResult
);
3026 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3027 ("0x%p TSFTextStore::GetDisplayAttribute() FAILED due to "
3028 "ITfDisplayAttributeInfo::GetAttributeInfo() failed",
3033 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3034 ("0x%p TSFTextStore::GetDisplayAttribute() succeeded: "
3036 this, GetDisplayAttrStr(*aResult
).get()));
3041 TSFTextStore::RestartCompositionIfNecessary(ITfRange
* aRangeNew
) {
3042 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3043 ("0x%p TSFTextStore::RestartCompositionIfNecessary("
3044 "aRangeNew=0x%p), mComposition.mView=0x%p",
3045 this, aRangeNew
, mComposition
.mView
.get()));
3047 if (!mComposition
.IsComposing()) {
3048 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3049 ("0x%p TSFTextStore::RestartCompositionIfNecessary() FAILED "
3050 "due to no composition view",
3056 RefPtr
<ITfCompositionView
> pComposition(mComposition
.mView
);
3057 RefPtr
<ITfRange
> composingRange(aRangeNew
);
3058 if (!composingRange
) {
3059 hr
= pComposition
->GetRange(getter_AddRefs(composingRange
));
3061 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3062 ("0x%p TSFTextStore::RestartCompositionIfNecessary() "
3063 "FAILED due to pComposition->GetRange() failure",
3069 // Get starting offset of the composition
3070 LONG compStart
= 0, compLength
= 0;
3071 hr
= GetRangeExtent(composingRange
, &compStart
, &compLength
);
3073 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3074 ("0x%p TSFTextStore::RestartCompositionIfNecessary() FAILED "
3075 "due to GetRangeExtent() failure",
3080 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3081 ("0x%p TSFTextStore::RestartCompositionIfNecessary(), "
3082 "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }",
3083 this, compStart
, compStart
+ compLength
, mComposition
.mStart
,
3084 mComposition
.mString
.Length()));
3086 if (mComposition
.mStart
!= compStart
||
3087 mComposition
.mString
.Length() != (ULONG
)compLength
) {
3088 // If the queried composition length is different from the length
3089 // of our composition string, OnUpdateComposition is being called
3090 // because a part of the original composition was committed.
3091 hr
= RestartComposition(pComposition
, composingRange
);
3093 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3094 ("0x%p TSFTextStore::RestartCompositionIfNecessary() "
3095 "FAILED due to RestartComposition() failure",
3102 sTextStoreLog
, LogLevel::Debug
,
3103 ("0x%p TSFTextStore::RestartCompositionIfNecessary() succeeded", this));
3108 TSFTextStore::RestartComposition(ITfCompositionView
* aCompositionView
,
3109 ITfRange
* aNewRange
) {
3110 Selection
& selectionForTSF
= SelectionForTSFRef();
3112 LONG newStart
, newLength
;
3113 HRESULT hr
= GetRangeExtent(aNewRange
, &newStart
, &newLength
);
3114 LONG newEnd
= newStart
+ newLength
;
3116 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3117 ("0x%p TSFTextStore::RestartComposition(aCompositionView=0x%p, "
3118 "aNewRange=0x%p { newStart=%d, newLength=%d }), "
3119 "mComposition={ mStart=%d, mCompositionString.Length()=%d }, "
3120 "selectionForTSF={ IsDirty()=%s, StartOffset()=%d, Length()=%d }",
3121 this, aCompositionView
, aNewRange
, newStart
, newLength
,
3122 mComposition
.mStart
, mComposition
.mString
.Length(),
3123 GetBoolName(selectionForTSF
.IsDirty()),
3124 selectionForTSF
.StartOffset(), selectionForTSF
.Length()));
3126 if (selectionForTSF
.IsDirty()) {
3127 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3128 ("0x%p TSFTextStore::RestartComposition() FAILED "
3129 "due to SelectionForTSFRef() failure",
3135 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3136 ("0x%p TSFTextStore::RestartComposition() FAILED "
3137 "due to GetRangeExtent() failure",
3142 // If the new range has no overlap with the crrent range, we just commit
3143 // the composition and restart new composition with the new range but
3144 // current selection range should be preserved.
3145 if (newStart
>= mComposition
.EndOffset() || newEnd
<= mComposition
.mStart
) {
3146 RecordCompositionEndAction();
3147 RecordCompositionStartAction(aCompositionView
, newStart
, newLength
, true);
3151 // If the new range has an overlap with the current one, we should not commit
3152 // the whole current range to avoid creating an odd undo transaction.
3153 // I.e., the overlapped range which is being composed should not appear in
3154 // undo transaction.
3156 // Backup current composition data and selection data.
3157 Composition oldComposition
= mComposition
;
3158 Selection oldSelection
= selectionForTSF
;
3160 // Commit only the part of composition.
3161 LONG keepComposingStartOffset
= std::max(mComposition
.mStart
, newStart
);
3162 LONG keepComposingEndOffset
= std::min(mComposition
.EndOffset(), newEnd
);
3164 keepComposingStartOffset
<= keepComposingEndOffset
,
3165 "Why keepComposingEndOffset is smaller than keepComposingStartOffset?");
3166 LONG keepComposingLength
= keepComposingEndOffset
- keepComposingStartOffset
;
3167 // Remove the overlapped part from the commit string.
3168 nsAutoString
commitString(mComposition
.mString
);
3169 commitString
.Cut(keepComposingStartOffset
- mComposition
.mStart
,
3170 keepComposingLength
);
3171 // Update the composition string.
3172 Content
& contentForTSF
= ContentForTSFRef();
3173 if (!contentForTSF
.IsInitialized()) {
3174 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3175 ("0x%p TSFTextStore::RestartComposition() FAILED "
3176 "due to ContentForTSFRef() failure",
3180 contentForTSF
.ReplaceTextWith(mComposition
.mStart
,
3181 mComposition
.mString
.Length(), commitString
);
3182 // Record a compositionupdate action for commit the part of composing string.
3183 PendingAction
* action
= LastOrNewPendingCompositionUpdate();
3184 action
->mData
= mComposition
.mString
;
3185 action
->mRanges
->Clear();
3186 // Note that we shouldn't append ranges when composition string
3187 // is empty because it may cause TextComposition confused.
3188 if (!action
->mData
.IsEmpty()) {
3189 TextRange caretRange
;
3190 caretRange
.mStartOffset
= caretRange
.mEndOffset
=
3191 uint32_t(oldComposition
.mStart
+ commitString
.Length());
3192 caretRange
.mRangeType
= TextRangeType::eCaret
;
3193 action
->mRanges
->AppendElement(caretRange
);
3195 action
->mIncomplete
= false;
3197 // Record compositionend action.
3198 RecordCompositionEndAction();
3200 // Record compositionstart action only with the new start since this method
3201 // hasn't restored composing string yet.
3202 RecordCompositionStartAction(aCompositionView
, newStart
, 0, false);
3204 // Restore the latest text content and selection.
3205 contentForTSF
.ReplaceSelectedTextWith(nsDependentSubstring(
3206 oldComposition
.mString
, keepComposingStartOffset
- oldComposition
.mStart
,
3207 keepComposingLength
));
3208 selectionForTSF
= oldSelection
;
3210 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3211 ("0x%p TSFTextStore::RestartComposition() succeeded, "
3212 "mComposition={ mStart=%d, mCompositionString.Length()=%d }, "
3213 "selectionForTSF={ IsDirty()=%s, StartOffset()=%d, Length()=%d }",
3214 this, mComposition
.mStart
, mComposition
.mString
.Length(),
3215 GetBoolName(selectionForTSF
.IsDirty()),
3216 selectionForTSF
.StartOffset(), selectionForTSF
.Length()));
3221 static bool GetColor(const TF_DA_COLOR
& aTSFColor
, nscolor
& aResult
) {
3222 switch (aTSFColor
.type
) {
3223 case TF_CT_SYSCOLOR
: {
3224 DWORD sysColor
= ::GetSysColor(aTSFColor
.nIndex
);
3226 NS_RGB(GetRValue(sysColor
), GetGValue(sysColor
), GetBValue(sysColor
));
3229 case TF_CT_COLORREF
:
3230 aResult
= NS_RGB(GetRValue(aTSFColor
.cr
), GetGValue(aTSFColor
.cr
),
3231 GetBValue(aTSFColor
.cr
));
3239 static bool GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle
,
3240 TextRangeStyle::LineStyle
& aTextRangeLineStyle
) {
3241 switch (aTSFLineStyle
) {
3243 aTextRangeLineStyle
= TextRangeStyle::LineStyle::None
;
3246 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Solid
;
3249 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Dotted
;
3252 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Dashed
;
3254 case TF_LS_SQUIGGLE
:
3255 aTextRangeLineStyle
= TextRangeStyle::LineStyle::Wavy
;
3263 TSFTextStore::RecordCompositionUpdateAction() {
3264 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3265 ("0x%p TSFTextStore::RecordCompositionUpdateAction(), "
3266 "mComposition={ mView=0x%p, mStart=%d, mString=\"%s\" "
3268 this, mComposition
.mView
.get(), mComposition
.mStart
,
3269 GetEscapedUTF8String(mComposition
.mString
).get(),
3270 mComposition
.mString
.Length()));
3272 if (!mComposition
.IsComposing()) {
3273 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3274 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3275 "due to no composition view",
3280 // Getting display attributes is *really* complicated!
3281 // We first get the context and the property objects to query for
3282 // attributes, but since a big range can have a variety of values for
3283 // the attribute, we have to find out all the ranges that have distinct
3284 // attribute values. Then we query for what the value represents through
3285 // the display attribute manager and translate that to TextRange to be
3286 // sent in eCompositionChange
3288 RefPtr
<ITfProperty
> attrPropetry
;
3290 mContext
->GetProperty(GUID_PROP_ATTRIBUTE
, getter_AddRefs(attrPropetry
));
3291 if (FAILED(hr
) || !attrPropetry
) {
3292 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3293 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3294 "due to mContext->GetProperty() failure",
3296 return FAILED(hr
) ? hr
: E_FAIL
;
3299 RefPtr
<ITfRange
> composingRange
;
3300 hr
= mComposition
.mView
->GetRange(getter_AddRefs(composingRange
));
3302 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3303 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3304 "FAILED due to mComposition.mView->GetRange() failure",
3309 RefPtr
<IEnumTfRanges
> enumRanges
;
3310 hr
= attrPropetry
->EnumRanges(TfEditCookie(mEditCookie
),
3311 getter_AddRefs(enumRanges
), composingRange
);
3312 if (FAILED(hr
) || !enumRanges
) {
3313 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3314 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3315 "due to attrPropetry->EnumRanges() failure",
3317 return FAILED(hr
) ? hr
: E_FAIL
;
3320 // First, put the log of content and selection here.
3321 Selection
& selectionForTSF
= SelectionForTSFRef();
3322 if (selectionForTSF
.IsDirty()) {
3323 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3324 ("0x%p TSFTextStore::RecordCompositionUpdateAction() FAILED "
3325 "due to SelectionForTSFRef() failure",
3330 PendingAction
* action
= LastOrNewPendingCompositionUpdate();
3331 action
->mData
= mComposition
.mString
;
3332 // The ranges might already have been initialized, however, if this is
3333 // called again, that means we need to overwrite the ranges with current
3335 action
->mRanges
->Clear();
3337 // Note that we shouldn't append ranges when composition string
3338 // is empty because it may cause TextComposition confused.
3339 if (!action
->mData
.IsEmpty()) {
3341 // No matter if we have display attribute info or not,
3342 // we always pass in at least one range to eCompositionChange
3343 newRange
.mStartOffset
= 0;
3344 newRange
.mEndOffset
= action
->mData
.Length();
3345 newRange
.mRangeType
= TextRangeType::eRawClause
;
3346 action
->mRanges
->AppendElement(newRange
);
3348 RefPtr
<ITfRange
> range
;
3349 while (enumRanges
->Next(1, getter_AddRefs(range
), nullptr) == S_OK
) {
3350 if (NS_WARN_IF(!range
)) {
3354 LONG rangeStart
= 0, rangeLength
= 0;
3355 if (FAILED(GetRangeExtent(range
, &rangeStart
, &rangeLength
))) {
3358 // The range may include out of composition string. We should ignore
3359 // outside of the composition string.
3360 LONG start
= std::min(std::max(rangeStart
, mComposition
.mStart
),
3361 mComposition
.EndOffset());
3363 std::max(std::min(rangeStart
+ rangeLength
, mComposition
.EndOffset()),
3364 mComposition
.mStart
);
3365 LONG length
= end
- start
;
3367 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3368 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3369 "ignores invalid range (%d-%d)",
3370 this, rangeStart
- mComposition
.mStart
,
3371 rangeStart
- mComposition
.mStart
+ rangeLength
));
3375 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3376 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3377 "ignores a range due to outside of the composition or empty "
3379 this, rangeStart
- mComposition
.mStart
,
3380 rangeStart
- mComposition
.mStart
+ rangeLength
));
3385 newRange
.mStartOffset
= uint32_t(start
- mComposition
.mStart
);
3386 // The end of the last range in the array is
3387 // always kept at the end of composition
3388 newRange
.mEndOffset
= mComposition
.mString
.Length();
3390 TF_DISPLAYATTRIBUTE attr
;
3391 hr
= GetDisplayAttribute(attrPropetry
, range
, &attr
);
3393 newRange
.mRangeType
= TextRangeType::eRawClause
;
3395 newRange
.mRangeType
= GetGeckoSelectionValue(attr
);
3396 if (GetColor(attr
.crText
, newRange
.mRangeStyle
.mForegroundColor
)) {
3397 newRange
.mRangeStyle
.mDefinedStyles
|=
3398 TextRangeStyle::DEFINED_FOREGROUND_COLOR
;
3400 if (GetColor(attr
.crBk
, newRange
.mRangeStyle
.mBackgroundColor
)) {
3401 newRange
.mRangeStyle
.mDefinedStyles
|=
3402 TextRangeStyle::DEFINED_BACKGROUND_COLOR
;
3404 if (GetColor(attr
.crLine
, newRange
.mRangeStyle
.mUnderlineColor
)) {
3405 newRange
.mRangeStyle
.mDefinedStyles
|=
3406 TextRangeStyle::DEFINED_UNDERLINE_COLOR
;
3408 if (GetLineStyle(attr
.lsStyle
, newRange
.mRangeStyle
.mLineStyle
)) {
3409 newRange
.mRangeStyle
.mDefinedStyles
|=
3410 TextRangeStyle::DEFINED_LINESTYLE
;
3411 newRange
.mRangeStyle
.mIsBoldLine
= attr
.fBoldLine
!= 0;
3415 TextRange
& lastRange
= action
->mRanges
->LastElement();
3416 if (lastRange
.mStartOffset
== newRange
.mStartOffset
) {
3417 // Replace range if last range is the same as this one
3418 // So that ranges don't overlap and confuse the editor
3419 lastRange
= newRange
;
3421 lastRange
.mEndOffset
= newRange
.mStartOffset
;
3422 action
->mRanges
->AppendElement(newRange
);
3426 // We need to hack for Korean Input System which is Korean standard TIP.
3427 // It sets no change style to IME selection (the selection is always only
3428 // one). So, the composition string looks like normal (or committed)
3429 // string. At this time, current selection range is same as the
3430 // composition string range. Other applications set a wide caret which
3431 // covers the composition string, however, Gecko doesn't support the wide
3432 // caret drawing now (Gecko doesn't support XOR drawing), unfortunately.
3433 // For now, we should change the range style to undefined.
3434 if (!selectionForTSF
.IsCollapsed() && action
->mRanges
->Length() == 1) {
3435 TextRange
& range
= action
->mRanges
->ElementAt(0);
3436 LONG start
= selectionForTSF
.MinOffset();
3437 LONG end
= selectionForTSF
.MaxOffset();
3438 if ((LONG
)range
.mStartOffset
== start
- mComposition
.mStart
&&
3439 (LONG
)range
.mEndOffset
== end
- mComposition
.mStart
&&
3440 range
.mRangeStyle
.IsNoChangeStyle()) {
3441 range
.mRangeStyle
.Clear();
3442 // The looks of selected type is better than others.
3443 range
.mRangeType
= TextRangeType::eSelectedRawClause
;
3447 // The caret position has to be collapsed.
3448 uint32_t caretPosition
= static_cast<uint32_t>(selectionForTSF
.MaxOffset() -
3449 mComposition
.mStart
);
3451 // If caret is in the target clause and it doesn't have specific style,
3452 // the target clause will be painted as normal selection range. Since
3453 // caret shouldn't be in selection range on Windows, we shouldn't append
3454 // caret range in such case.
3455 const TextRange
* targetClause
= action
->mRanges
->GetTargetClause();
3456 if (!targetClause
|| targetClause
->mRangeStyle
.IsDefined() ||
3457 caretPosition
< targetClause
->mStartOffset
||
3458 caretPosition
> targetClause
->mEndOffset
) {
3459 TextRange caretRange
;
3460 caretRange
.mStartOffset
= caretRange
.mEndOffset
= caretPosition
;
3461 caretRange
.mRangeType
= TextRangeType::eCaret
;
3462 action
->mRanges
->AppendElement(caretRange
);
3466 action
->mIncomplete
= false;
3468 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3469 ("0x%p TSFTextStore::RecordCompositionUpdateAction() "
3477 TSFTextStore::SetSelectionInternal(const TS_SELECTION_ACP
* pSelection
,
3478 bool aDispatchCompositionChangeEvent
) {
3479 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
3480 ("0x%p TSFTextStore::SetSelectionInternal(pSelection={ "
3481 "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, "
3482 "aDispatchCompositionChangeEvent=%s), mComposition.IsComposing()=%s",
3483 this, pSelection
->acpStart
, pSelection
->acpEnd
,
3484 GetActiveSelEndName(pSelection
->style
.ase
),
3485 GetBoolName(pSelection
->style
.fInterimChar
),
3486 GetBoolName(aDispatchCompositionChangeEvent
),
3487 GetBoolName(mComposition
.IsComposing())));
3489 MOZ_ASSERT(IsReadWriteLocked());
3491 Selection
& selectionForTSF
= SelectionForTSFRef();
3492 if (selectionForTSF
.IsDirty()) {
3493 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3494 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3495 "SelectionForTSFRef() failure",
3500 MaybeDispatchKeyboardEventAsProcessedByIME();
3502 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3503 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3504 "destroyed during dispatching a keyboard event",
3509 // If actually the range is not changing, we should do nothing.
3510 // Perhaps, we can ignore the difference change because it must not be
3511 // important for following edit.
3512 if (selectionForTSF
.EqualsExceptDirection(*pSelection
)) {
3513 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
3514 ("0x%p TSFTextStore::SetSelectionInternal() Succeeded but "
3515 "did nothing because the selection range isn't changing",
3517 selectionForTSF
.SetSelection(*pSelection
);
3521 if (mComposition
.IsComposing()) {
3522 if (aDispatchCompositionChangeEvent
) {
3523 HRESULT hr
= RestartCompositionIfNecessary();
3525 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3526 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3527 "RestartCompositionIfNecessary() failure",
3532 if (pSelection
->acpStart
< mComposition
.mStart
||
3533 pSelection
->acpEnd
> mComposition
.EndOffset()) {
3534 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3535 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3536 "the selection being out of the composition string",
3538 return TS_E_INVALIDPOS
;
3540 // Emulate selection during compositions
3541 selectionForTSF
.SetSelection(*pSelection
);
3542 if (aDispatchCompositionChangeEvent
) {
3543 HRESULT hr
= RecordCompositionUpdateAction();
3545 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3546 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3547 "RecordCompositionUpdateAction() failure",
3555 TS_SELECTION_ACP
selectionInContent(*pSelection
);
3557 // If mContentForTSF caches old contents which is now different from
3558 // actual contents, we need some complicated hack here...
3559 // Note that this hack assumes that this is used for reconversion.
3560 if (mContentForTSF
.IsInitialized() && mPendingTextChangeData
.IsValid() &&
3561 !mPendingTextChangeData
.mCausedOnlyByComposition
) {
3562 uint32_t startOffset
= static_cast<uint32_t>(selectionInContent
.acpStart
);
3563 uint32_t endOffset
= static_cast<uint32_t>(selectionInContent
.acpEnd
);
3564 if (mPendingTextChangeData
.mStartOffset
>= endOffset
) {
3565 // Setting selection before any changed ranges is fine.
3566 } else if (mPendingTextChangeData
.mRemovedEndOffset
<= startOffset
) {
3567 // Setting selection after removed range is fine with following
3569 selectionInContent
.acpStart
+= mPendingTextChangeData
.Difference();
3570 selectionInContent
.acpEnd
+= mPendingTextChangeData
.Difference();
3571 } else if (startOffset
== endOffset
) {
3572 // Moving caret position may be fine in most cases even if the insertion
3573 // point has already gone but in this case, composition will be inserted
3574 // to unexpected position, though.
3575 // It seems that moving caret into middle of the new text is odd.
3576 // Perhaps, end of it is expected by users in most cases.
3577 selectionInContent
.acpStart
= mPendingTextChangeData
.mAddedEndOffset
;
3578 selectionInContent
.acpEnd
= selectionInContent
.acpStart
;
3580 // Otherwise, i.e., setting range has already gone, we cannot set
3581 // selection properly.
3582 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3583 ("0x%p TSFTextStore::SetSelectionInternal() FAILED due to "
3584 "there is unknown content change",
3590 CompleteLastActionIfStillIncomplete();
3591 PendingAction
* action
= mPendingActions
.AppendElement();
3592 action
->mType
= PendingAction::Type::eSetSelection
;
3593 action
->mSelectionStart
= selectionInContent
.acpStart
;
3594 action
->mSelectionLength
=
3595 selectionInContent
.acpEnd
- selectionInContent
.acpStart
;
3596 action
->mSelectionReversed
= (selectionInContent
.style
.ase
== TS_AE_START
);
3598 // Use TSF specified selection for updating mSelectionForTSF.
3599 selectionForTSF
.SetSelection(*pSelection
);
3605 TSFTextStore::SetSelection(ULONG ulCount
, const TS_SELECTION_ACP
* pSelection
) {
3606 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3607 ("0x%p TSFTextStore::SetSelection(ulCount=%lu, pSelection=%p { "
3608 "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), "
3609 "mComposition.IsComposing()=%s",
3610 this, ulCount
, pSelection
, pSelection
? pSelection
->acpStart
: 0,
3611 pSelection
? pSelection
->acpEnd
: 0,
3612 pSelection
? GetActiveSelEndName(pSelection
->style
.ase
) : "",
3613 pSelection
? GetBoolName(pSelection
->style
.fInterimChar
) : "",
3614 GetBoolName(mComposition
.IsComposing())));
3616 if (!IsReadWriteLocked()) {
3617 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3618 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3619 "not locked (read-write)",
3624 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3625 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3626 "trying setting multiple selection",
3628 return E_INVALIDARG
;
3631 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3632 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3635 return E_INVALIDARG
;
3638 HRESULT hr
= SetSelectionInternal(pSelection
, true);
3640 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3641 ("0x%p TSFTextStore::SetSelection() FAILED due to "
3642 "SetSelectionInternal() failure",
3645 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3646 ("0x%p TSFTextStore::SetSelection() succeeded", this));
3652 TSFTextStore::GetText(LONG acpStart
, LONG acpEnd
, WCHAR
* pchPlain
,
3653 ULONG cchPlainReq
, ULONG
* pcchPlainOut
,
3654 TS_RUNINFO
* prgRunInfo
, ULONG ulRunInfoReq
,
3655 ULONG
* pulRunInfoOut
, LONG
* pacpNext
) {
3657 sTextStoreLog
, LogLevel::Info
,
3658 ("0x%p TSFTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, "
3659 "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, "
3660 "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, "
3661 "mString.Length()=%lu, IsComposing()=%s }",
3662 this, acpStart
, acpEnd
, pchPlain
, cchPlainReq
, pcchPlainOut
, prgRunInfo
,
3663 ulRunInfoReq
, pulRunInfoOut
, pacpNext
, mComposition
.mStart
,
3664 mComposition
.mString
.Length(), GetBoolName(mComposition
.IsComposing())));
3666 if (!IsReadLocked()) {
3667 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3668 ("0x%p TSFTextStore::GetText() FAILED due to "
3669 "not locked (read)",
3674 if (!pcchPlainOut
|| (!pchPlain
&& !prgRunInfo
) ||
3675 !cchPlainReq
!= !pchPlain
|| !ulRunInfoReq
!= !prgRunInfo
) {
3676 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3677 ("0x%p TSFTextStore::GetText() FAILED due to "
3680 return E_INVALIDARG
;
3683 if (acpStart
< 0 || acpEnd
< -1 || (acpEnd
!= -1 && acpStart
> acpEnd
)) {
3684 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3685 ("0x%p TSFTextStore::GetText() FAILED due to "
3688 return TS_E_INVALIDPOS
;
3691 // Making sure to null-terminate string just to be on the safe side
3693 if (pchPlain
&& cchPlainReq
) *pchPlain
= 0;
3694 if (pulRunInfoOut
) *pulRunInfoOut
= 0;
3695 if (pacpNext
) *pacpNext
= acpStart
;
3696 if (prgRunInfo
&& ulRunInfoReq
) {
3697 prgRunInfo
->uCount
= 0;
3698 prgRunInfo
->type
= TS_RT_PLAIN
;
3701 Content
& contentForTSF
= ContentForTSFRef();
3702 if (!contentForTSF
.IsInitialized()) {
3703 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3704 ("0x%p TSFTextStore::GetText() FAILED due to "
3705 "ContentForTSFRef() failure",
3709 if (contentForTSF
.Text().Length() < static_cast<uint32_t>(acpStart
)) {
3710 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3711 ("0x%p TSFTextStore::GetText() FAILED due to "
3712 "acpStart is larger offset than the actual text length",
3714 return TS_E_INVALIDPOS
;
3717 contentForTSF
.Text().Length() < static_cast<uint32_t>(acpEnd
)) {
3718 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3719 ("0x%p TSFTextStore::GetText() FAILED due to "
3720 "acpEnd is larger offset than the actual text length",
3722 return TS_E_INVALIDPOS
;
3724 uint32_t length
= (acpEnd
== -1) ? contentForTSF
.Text().Length() -
3725 static_cast<uint32_t>(acpStart
)
3726 : static_cast<uint32_t>(acpEnd
- acpStart
);
3727 if (cchPlainReq
&& cchPlainReq
- 1 < length
) {
3728 length
= cchPlainReq
- 1;
3731 if (pchPlain
&& cchPlainReq
) {
3732 const char16_t
* startChar
=
3733 contentForTSF
.Text().BeginReading() + acpStart
;
3734 memcpy(pchPlain
, startChar
, length
* sizeof(*pchPlain
));
3735 pchPlain
[length
] = 0;
3736 *pcchPlainOut
= length
;
3738 if (prgRunInfo
&& ulRunInfoReq
) {
3739 prgRunInfo
->uCount
= length
;
3740 prgRunInfo
->type
= TS_RT_PLAIN
;
3741 if (pulRunInfoOut
) *pulRunInfoOut
= 1;
3743 if (pacpNext
) *pacpNext
= acpStart
+ length
;
3746 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3747 ("0x%p TSFTextStore::GetText() succeeded: pcchPlainOut=0x%p, "
3748 "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, "
3750 this, pcchPlainOut
, prgRunInfo
? prgRunInfo
->uCount
: 0,
3751 prgRunInfo
? GetTextRunTypeName(prgRunInfo
->type
) : "N/A",
3752 pulRunInfoOut
? *pulRunInfoOut
: 0, pacpNext
? *pacpNext
: 0));
3757 TSFTextStore::SetText(DWORD dwFlags
, LONG acpStart
, LONG acpEnd
,
3758 const WCHAR
* pchText
, ULONG cch
, TS_TEXTCHANGE
* pChange
) {
3760 sTextStoreLog
, LogLevel::Info
,
3761 ("0x%p TSFTextStore::SetText(dwFlags=%s, acpStart=%ld, "
3762 "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), "
3763 "mComposition.IsComposing()=%s",
3764 this, dwFlags
== TS_ST_CORRECTION
? "TS_ST_CORRECTION" : "not-specified",
3765 acpStart
, acpEnd
, pchText
,
3766 pchText
&& cch
? GetEscapedUTF8String(pchText
, cch
).get() : "", cch
,
3767 pChange
, GetBoolName(mComposition
.IsComposing())));
3769 // Per SDK documentation, and since we don't have better
3770 // ways to do this, this method acts as a helper to
3771 // call SetSelection followed by InsertTextAtSelection
3772 if (!IsReadWriteLocked()) {
3773 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3774 ("0x%p TSFTextStore::SetText() FAILED due to "
3775 "not locked (read)",
3780 TS_SELECTION_ACP selection
;
3781 selection
.acpStart
= acpStart
;
3782 selection
.acpEnd
= acpEnd
;
3783 selection
.style
.ase
= TS_AE_END
;
3784 selection
.style
.fInterimChar
= 0;
3785 // Set selection to desired range
3786 HRESULT hr
= SetSelectionInternal(&selection
);
3788 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3789 ("0x%p TSFTextStore::SetText() FAILED due to "
3790 "SetSelectionInternal() failure",
3794 // Replace just selected text
3795 if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText
, cch
),
3797 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
3798 ("0x%p TSFTextStore::SetText() FAILED due to "
3799 "InsertTextAtSelectionInternal() failure",
3804 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3805 ("0x%p TSFTextStore::SetText() succeeded: pChange={ "
3806 "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
3807 this, pChange
? pChange
->acpStart
: 0,
3808 pChange
? pChange
->acpOldEnd
: 0, pChange
? pChange
->acpNewEnd
: 0));
3813 TSFTextStore::GetFormattedText(LONG acpStart
, LONG acpEnd
,
3814 IDataObject
** ppDataObject
) {
3815 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3816 ("0x%p TSFTextStore::GetFormattedText() called "
3817 "but not supported (E_NOTIMPL)",
3820 // no support for formatted text
3825 TSFTextStore::GetEmbedded(LONG acpPos
, REFGUID rguidService
, REFIID riid
,
3827 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3828 ("0x%p TSFTextStore::GetEmbedded() called "
3829 "but not supported (E_NOTIMPL)",
3832 // embedded objects are not supported
3837 TSFTextStore::QueryInsertEmbedded(const GUID
* pguidService
,
3838 const FORMATETC
* pFormatEtc
,
3839 BOOL
* pfInsertable
) {
3840 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3841 ("0x%p TSFTextStore::QueryInsertEmbedded() called "
3842 "but not supported, *pfInsertable=FALSE (S_OK)",
3845 // embedded objects are not supported
3846 *pfInsertable
= FALSE
;
3851 TSFTextStore::InsertEmbedded(DWORD dwFlags
, LONG acpStart
, LONG acpEnd
,
3852 IDataObject
* pDataObject
, TS_TEXTCHANGE
* pChange
) {
3853 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3854 ("0x%p TSFTextStore::InsertEmbedded() called "
3855 "but not supported (E_NOTIMPL)",
3858 // embedded objects are not supported
3863 bool TSFTextStore::ShouldSetInputScopeOfURLBarToDefault() {
3864 // FYI: Google Japanese Input may be an IMM-IME. If it's installed on
3865 // Win7, it's always IMM-IME. Otherwise, basically, it's a TIP.
3866 // However, if it's installed on Win7 and has not been updated yet
3867 // after the OS is upgraded to Win8 or later, it's still an IMM-IME.
3868 // Therefore, we also need to check with IMMHandler here.
3869 if (!TSFPrefs::ShouldSetInputScopeOfURLBarToDefault()) {
3873 if (IMMHandler::IsGoogleJapaneseInputActive()) {
3877 switch (TSFStaticSink::ActiveTIP()) {
3878 case TextInputProcessorID::eMicrosoftIMEForJapanese
:
3879 case TextInputProcessorID::eGoogleJapaneseInput
:
3880 case TextInputProcessorID::eMicrosoftBopomofo
:
3881 case TextInputProcessorID::eMicrosoftChangJie
:
3882 case TextInputProcessorID::eMicrosoftPhonetic
:
3883 case TextInputProcessorID::eMicrosoftQuick
:
3884 case TextInputProcessorID::eMicrosoftNewChangJie
:
3885 case TextInputProcessorID::eMicrosoftNewPhonetic
:
3886 case TextInputProcessorID::eMicrosoftNewQuick
:
3887 case TextInputProcessorID::eMicrosoftPinyin
:
3888 case TextInputProcessorID::eMicrosoftPinyinNewExperienceInputStyle
:
3889 case TextInputProcessorID::eMicrosoftOldHangul
:
3890 case TextInputProcessorID::eMicrosoftWubi
:
3892 case TextInputProcessorID::eMicrosoftIMEForKorean
:
3893 return IsWin8OrLater();
3899 void TSFTextStore::SetInputScope(const nsString
& aHTMLInputType
,
3900 const nsString
& aHTMLInputInputMode
,
3901 bool aInPrivateBrowsing
) {
3902 mInputScopes
.Clear();
3904 // IME may refer only first input scope, but we will append inputmode's
3905 // input scopes too like Chrome since IME may refer it.
3906 IMEHandler::AppendInputScopeFromType(aHTMLInputType
, mInputScopes
);
3907 IMEHandler::AppendInputScopeFromInputmode(aHTMLInputInputMode
, mInputScopes
);
3909 if (aInPrivateBrowsing
) {
3910 mInputScopes
.AppendElement(IS_PRIVATE
);
3914 int32_t TSFTextStore::GetRequestedAttrIndex(const TS_ATTRID
& aAttrID
) {
3915 if (IsEqualGUID(aAttrID
, GUID_PROP_INPUTSCOPE
)) {
3918 if (IsEqualGUID(aAttrID
, TSATTRID_Text_VerticalWriting
)) {
3919 return eTextVerticalWriting
;
3921 if (IsEqualGUID(aAttrID
, TSATTRID_Text_Orientation
)) {
3922 return eTextOrientation
;
3924 return eNotSupported
;
3928 TSFTextStore::GetAttrID(int32_t aIndex
) {
3931 return GUID_PROP_INPUTSCOPE
;
3932 case eTextVerticalWriting
:
3933 return TSATTRID_Text_VerticalWriting
;
3934 case eTextOrientation
:
3935 return TSATTRID_Text_Orientation
;
3937 MOZ_CRASH("Invalid index? Or not implemented yet?");
3943 TSFTextStore::HandleRequestAttrs(DWORD aFlags
, ULONG aFilterCount
,
3944 const TS_ATTRID
* aFilterAttrs
) {
3945 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3946 ("0x%p TSFTextStore::HandleRequestAttrs(aFlags=%s, "
3948 this, GetFindFlagName(aFlags
).get(), aFilterCount
));
3950 // This is a little weird! RequestSupportedAttrs gives us advanced notice
3951 // of a support query via RetrieveRequestedAttrs for a specific attribute.
3952 // RetrieveRequestedAttrs needs to return valid data for all attributes we
3953 // support, but the text service will only want the input scope object
3954 // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains
3955 // TS_ATTR_FIND_WANT_VALUE.
3956 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
3957 mRequestedAttrs
[i
] = false;
3959 mRequestedAttrValues
= !!(aFlags
& TS_ATTR_FIND_WANT_VALUE
);
3961 for (uint32_t i
= 0; i
< aFilterCount
; i
++) {
3962 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3963 ("0x%p TSFTextStore::HandleRequestAttrs(), "
3964 "requested attr=%s",
3965 this, GetGUIDNameStrWithTable(aFilterAttrs
[i
]).get()));
3966 int32_t index
= GetRequestedAttrIndex(aFilterAttrs
[i
]);
3967 if (index
!= eNotSupported
) {
3968 mRequestedAttrs
[index
] = true;
3975 TSFTextStore::RequestSupportedAttrs(DWORD dwFlags
, ULONG cFilterAttrs
,
3976 const TS_ATTRID
* paFilterAttrs
) {
3977 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3978 ("0x%p TSFTextStore::RequestSupportedAttrs(dwFlags=%s, "
3979 "cFilterAttrs=%lu)",
3980 this, GetFindFlagName(dwFlags
).get(), cFilterAttrs
));
3982 return HandleRequestAttrs(dwFlags
, cFilterAttrs
, paFilterAttrs
);
3986 TSFTextStore::RequestAttrsAtPosition(LONG acpPos
, ULONG cFilterAttrs
,
3987 const TS_ATTRID
* paFilterAttrs
,
3989 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
3990 ("0x%p TSFTextStore::RequestAttrsAtPosition(acpPos=%ld, "
3991 "cFilterAttrs=%lu, dwFlags=%s)",
3992 this, acpPos
, cFilterAttrs
, GetFindFlagName(dwFlags
).get()));
3994 return HandleRequestAttrs(dwFlags
| TS_ATTR_FIND_WANT_VALUE
, cFilterAttrs
,
3999 TSFTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos
,
4001 const TS_ATTRID
* paFilterAttr
,
4003 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4004 ("0x%p TSFTextStore::RequestAttrsTransitioningAtPosition("
4005 "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported "
4007 this, acpPos
, cFilterAttrs
, GetFindFlagName(dwFlags
).get()));
4009 // no per character attributes defined
4014 TSFTextStore::FindNextAttrTransition(LONG acpStart
, LONG acpHalt
,
4016 const TS_ATTRID
* paFilterAttrs
,
4017 DWORD dwFlags
, LONG
* pacpNext
,
4018 BOOL
* pfFound
, LONG
* plFoundOffset
) {
4019 if (!pacpNext
|| !pfFound
|| !plFoundOffset
) {
4020 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4021 (" 0x%p TSFTextStore::FindNextAttrTransition() FAILED due to "
4024 return E_INVALIDARG
;
4027 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4028 ("0x%p TSFTextStore::FindNextAttrTransition() called "
4029 "but not supported (S_OK)",
4032 // no per character attributes defined
4033 *pacpNext
= *plFoundOffset
= acpHalt
;
4039 TSFTextStore::RetrieveRequestedAttrs(ULONG ulCount
, TS_ATTRVAL
* paAttrVals
,
4041 if (!pcFetched
|| !paAttrVals
) {
4042 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4043 ("0x%p TSFTextStore::RetrieveRequestedAttrs() FAILED due to "
4046 return E_INVALIDARG
;
4049 ULONG expectedCount
= 0;
4050 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
4051 if (mRequestedAttrs
[i
]) {
4055 if (ulCount
< expectedCount
) {
4056 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4057 ("0x%p TSFTextStore::RetrieveRequestedAttrs() FAILED due to "
4058 "not enough count ulCount=%u, expectedCount=%u",
4059 this, ulCount
, expectedCount
));
4060 return E_INVALIDARG
;
4063 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4064 ("0x%p TSFTextStore::RetrieveRequestedAttrs() called "
4065 "ulCount=%d, mRequestedAttrValues=%s",
4066 this, ulCount
, GetBoolName(mRequestedAttrValues
)));
4069 for (int32_t i
= 0; i
< NUM_OF_SUPPORTED_ATTRS
; i
++) {
4070 if (!mRequestedAttrs
[i
]) {
4073 mRequestedAttrs
[i
] = false;
4075 TS_ATTRID attrID
= GetAttrID(i
);
4077 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4078 ("0x%p TSFTextStore::RetrieveRequestedAttrs() for %s", this,
4079 GetGUIDNameStrWithTable(attrID
).get()));
4081 paAttrVals
[count
].idAttr
= attrID
;
4082 paAttrVals
[count
].dwOverlapId
= 0;
4084 if (!mRequestedAttrValues
) {
4085 paAttrVals
[count
].varValue
.vt
= VT_EMPTY
;
4089 paAttrVals
[count
].varValue
.vt
= VT_UNKNOWN
;
4090 RefPtr
<IUnknown
> inputScope
= new InputScopeImpl(mInputScopes
);
4091 paAttrVals
[count
].varValue
.punkVal
= inputScope
.forget().take();
4094 case eTextVerticalWriting
: {
4095 Selection
& selectionForTSF
= SelectionForTSFRef();
4096 paAttrVals
[count
].varValue
.vt
= VT_BOOL
;
4097 paAttrVals
[count
].varValue
.boolVal
=
4098 !selectionForTSF
.IsDirty() &&
4099 selectionForTSF
.GetWritingMode().IsVertical()
4104 case eTextOrientation
: {
4105 Selection
& selectionForTSF
= SelectionForTSFRef();
4106 paAttrVals
[count
].varValue
.vt
= VT_I4
;
4107 paAttrVals
[count
].varValue
.lVal
=
4108 !selectionForTSF
.IsDirty() &&
4109 selectionForTSF
.GetWritingMode().IsVertical()
4115 MOZ_CRASH("Invalid index? Or not implemented yet?");
4122 mRequestedAttrValues
= false;
4129 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4130 ("0x%p TSFTextStore::RetrieveRequestedAttrs() called "
4131 "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)",
4134 paAttrVals
->dwOverlapId
= 0;
4135 paAttrVals
->varValue
.vt
= VT_EMPTY
;
4141 TSFTextStore::GetEndACP(LONG
* pacp
) {
4142 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4143 ("0x%p TSFTextStore::GetEndACP(pacp=0x%p)", this, pacp
));
4145 if (!IsReadLocked()) {
4146 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4147 ("0x%p TSFTextStore::GetEndACP() FAILED due to "
4148 "not locked (read)",
4154 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4155 ("0x%p TSFTextStore::GetEndACP() FAILED due to "
4158 return E_INVALIDARG
;
4161 Content
& contentForTSF
= ContentForTSFRef();
4162 if (!contentForTSF
.IsInitialized()) {
4163 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4164 ("0x%p TSFTextStore::GetEndACP() FAILED due to "
4165 "ContentForTSFRef() failure",
4169 *pacp
= static_cast<LONG
>(contentForTSF
.Text().Length());
4174 TSFTextStore::GetActiveView(TsViewCookie
* pvcView
) {
4175 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4176 ("0x%p TSFTextStore::GetActiveView(pvcView=0x%p)", this, pvcView
));
4179 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4180 ("0x%p TSFTextStore::GetActiveView() FAILED due to "
4183 return E_INVALIDARG
;
4186 *pvcView
= TEXTSTORE_DEFAULT_VIEW
;
4188 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4189 ("0x%p TSFTextStore::GetActiveView() succeeded: *pvcView=%ld", this,
4195 TSFTextStore::GetACPFromPoint(TsViewCookie vcView
, const POINT
* pt
,
4196 DWORD dwFlags
, LONG
* pacp
) {
4197 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4198 ("0x%p TSFTextStore::GetACPFromPoint(pvcView=%d, pt=%p (x=%d, "
4199 "y=%d), dwFlags=%s, pacp=%p, mDeferNotifyingTSF=%s, "
4200 "mWaitingQueryLayout=%s",
4201 this, vcView
, pt
, pt
? pt
->x
: 0, pt
? pt
->y
: 0,
4202 GetACPFromPointFlagName(dwFlags
).get(), pacp
,
4203 GetBoolName(mDeferNotifyingTSF
), GetBoolName(mWaitingQueryLayout
)));
4205 if (!IsReadLocked()) {
4206 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4207 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4208 "not locked (read)",
4213 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4214 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4215 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4216 "called with invalid view",
4218 return E_INVALIDARG
;
4222 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4223 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4226 return E_INVALIDARG
;
4230 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4231 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4234 return E_INVALIDARG
;
4237 mWaitingQueryLayout
= false;
4239 if (mDestroyed
|| mContentForTSF
.IsLayoutChanged()) {
4240 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4241 ("0x%p TSFTextStore::GetACPFromPoint() returned "
4244 mHasReturnedNoLayoutError
= true;
4245 return TS_E_NOLAYOUT
;
4248 LayoutDeviceIntPoint
ourPt(pt
->x
, pt
->y
);
4249 // Convert to widget relative coordinates from screen's.
4250 ourPt
-= mWidget
->WidgetToScreenOffset();
4252 // NOTE: Don't check if the point is in the widget since the point can be
4253 // outside of the widget if focused editor is in a XUL <panel>.
4255 WidgetQueryContentEvent
charAtPt(true, eQueryCharacterAtPoint
, mWidget
);
4256 mWidget
->InitEvent(charAtPt
, &ourPt
);
4258 // FYI: WidgetQueryContentEvent may cause flushing pending layout and it
4259 // may cause focus change or something.
4260 RefPtr
<TSFTextStore
> kungFuDeathGrip(this);
4261 DispatchEvent(charAtPt
);
4262 if (!mWidget
|| mWidget
->Destroyed()) {
4263 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4264 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4265 "mWidget was destroyed during eQueryCharacterAtPoint",
4270 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
4271 ("0x%p TSFTextStore::GetACPFromPoint(), charAtPt={ "
4272 "mSucceeded=%s, mReply={ mOffset=%u, mTentativeCaretOffset=%u }}",
4273 this, GetBoolName(charAtPt
.mSucceeded
), charAtPt
.mReply
.mOffset
,
4274 charAtPt
.mReply
.mTentativeCaretOffset
));
4276 if (NS_WARN_IF(!charAtPt
.mSucceeded
)) {
4277 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4278 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4279 "eQueryCharacterAtPoint failure",
4284 // If dwFlags isn't set and the point isn't in any character's bounding box,
4285 // we should return TS_E_INVALIDPOINT.
4286 if (!(dwFlags
& GXFPF_NEAREST
) &&
4287 charAtPt
.mReply
.mOffset
== WidgetQueryContentEvent::NOT_FOUND
) {
4288 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4289 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to the "
4290 "point contained by no bounding box",
4292 return TS_E_INVALIDPOINT
;
4295 // Although, we're not sure if mTentativeCaretOffset becomes NOT_FOUND,
4296 // let's assume that there is no content in such case.
4297 if (NS_WARN_IF(charAtPt
.mReply
.mTentativeCaretOffset
==
4298 WidgetQueryContentEvent::NOT_FOUND
)) {
4299 charAtPt
.mReply
.mTentativeCaretOffset
= 0;
4304 // If dwFlags includes GXFPF_ROUND_NEAREST, we should return tentative
4305 // caret offset (MSDN calls it "range position").
4306 if (dwFlags
& GXFPF_ROUND_NEAREST
) {
4307 offset
= charAtPt
.mReply
.mTentativeCaretOffset
;
4308 } else if (charAtPt
.mReply
.mOffset
!= WidgetQueryContentEvent::NOT_FOUND
) {
4309 // Otherwise, we should return character offset whose bounding box contains
4311 offset
= charAtPt
.mReply
.mOffset
;
4313 // If the point isn't in any character's bounding box but we need to return
4314 // the nearest character from the point, we should *guess* the character
4315 // offset since there is no inexpensive API to check it strictly.
4316 // XXX If we retrieve 2 bounding boxes, one is before the offset and
4317 // the other is after the offset, we could resolve the offset.
4318 // However, dispatching 2 eQueryTextRect may be expensive.
4320 // So, use tentative offset for now.
4321 offset
= charAtPt
.mReply
.mTentativeCaretOffset
;
4323 // However, if it's after the last character, we need to decrement the
4325 Content
& contentForTSF
= ContentForTSFRef();
4326 if (!contentForTSF
.IsInitialized()) {
4327 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4328 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
4329 "ContentForTSFRef() failure",
4333 if (contentForTSF
.Text().Length() <= offset
) {
4334 // If the tentative caret is after the last character, let's return
4335 // the last character's offset.
4336 offset
= contentForTSF
.Text().Length() - 1;
4340 if (NS_WARN_IF(offset
> LONG_MAX
)) {
4341 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4342 ("0x%p TSFTextStore::GetACPFromPoint() FAILED due to out of "
4343 "range of the result",
4345 return TS_E_INVALIDPOINT
;
4348 *pacp
= static_cast<LONG
>(offset
);
4349 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4350 ("0x%p TSFTextStore::GetACPFromPoint() succeeded: *pacp=%d", this,
4356 TSFTextStore::GetTextExt(TsViewCookie vcView
, LONG acpStart
, LONG acpEnd
,
4357 RECT
* prc
, BOOL
* pfClipped
) {
4359 sTextStoreLog
, LogLevel::Info
,
4360 ("0x%p TSFTextStore::GetTextExt(vcView=%ld, "
4361 "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p), "
4362 "IsHandlingComposition()=%s, "
4363 "mContentForTSF={ MinOffsetOfLayoutChanged()=%u, "
4364 "LatestCompositionStartOffset()=%d, LatestCompositionEndOffset()=%d }, "
4365 "mComposition= { IsComposing()=%s, mStart=%d, EndOffset()=%d }, "
4366 "mDeferNotifyingTSF=%s, mWaitingQueryLayout=%s, "
4367 "IMEHandler::IsA11yHandlingNativeCaret()=%s",
4368 this, vcView
, acpStart
, acpEnd
, prc
, pfClipped
,
4369 GetBoolName(IsHandlingComposition()),
4370 mContentForTSF
.MinOffsetOfLayoutChanged(),
4371 mContentForTSF
.HasOrHadComposition()
4372 ? mContentForTSF
.LatestCompositionStartOffset()
4374 mContentForTSF
.HasOrHadComposition()
4375 ? mContentForTSF
.LatestCompositionEndOffset()
4377 GetBoolName(mComposition
.IsComposing()), mComposition
.mStart
,
4378 mComposition
.EndOffset(), GetBoolName(mDeferNotifyingTSF
),
4379 GetBoolName(mWaitingQueryLayout
),
4380 GetBoolName(IMEHandler::IsA11yHandlingNativeCaret())));
4382 if (!IsReadLocked()) {
4383 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4384 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4385 "not locked (read)",
4390 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4391 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4392 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4393 "called with invalid view",
4395 return E_INVALIDARG
;
4398 if (!prc
|| !pfClipped
) {
4399 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4400 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4403 return E_INVALIDARG
;
4406 // According to MSDN, ITextStoreACP::GetTextExt() should return
4407 // TS_E_INVALIDARG when acpStart and acpEnd are same (i.e., collapsed range).
4408 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms538435(v=vs.85).aspx
4409 // > TS_E_INVALIDARG: The specified start and end character positions are
4411 // However, some TIPs (including Microsoft's Chinese TIPs!) call this with
4412 // collapsed range and if we return TS_E_INVALIDARG, they stops showing their
4413 // owning window or shows it but odd position. So, we should just return
4414 // error only when acpStart and/or acpEnd are really odd.
4416 if (acpStart
< 0 || acpEnd
< acpStart
) {
4417 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4418 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4421 return TS_E_INVALIDPOS
;
4424 mWaitingQueryLayout
= false;
4426 if (IsHandlingComposition() && mContentForTSF
.HasOrHadComposition() &&
4427 mContentForTSF
.IsLayoutChanged() &&
4428 mContentForTSF
.MinOffsetOfLayoutChanged() > LONG_MAX
) {
4429 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4430 ("0x%p TSFTextStore::GetTextExt(), FAILED due to the text "
4431 "is too big for TSF (cannot treat modified offset as LONG), "
4432 "mContentForTSF.MinOffsetOfLayoutChanged()=%u",
4433 this, mContentForTSF
.MinOffsetOfLayoutChanged()));
4437 // At Windows 10 build 17643 (an insider preview for RS5), Microsoft fixed
4438 // the bug of TS_E_NOLAYOUT (even when we returned TS_E_NOLAYOUT, TSF
4439 // returned E_FAIL to TIP). However, until we drop to support older Windows
4440 // and all TIPs are aware of TS_E_NOLAYOUT result, we need to keep returning
4441 // S_OK and available rectangle only for them.
4442 if (!MaybeHackNoErrorLayoutBugs(acpStart
, acpEnd
) &&
4443 mContentForTSF
.IsLayoutChangedAt(acpEnd
)) {
4444 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4445 ("0x%p TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
4448 mHasReturnedNoLayoutError
= true;
4449 return TS_E_NOLAYOUT
;
4453 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4454 ("0x%p TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
4455 "(acpEnd=%d) because this has already been destroyed",
4457 mHasReturnedNoLayoutError
= true;
4458 return TS_E_NOLAYOUT
;
4461 // use eQueryTextRect to get rect in system, screen coordinates
4462 WidgetQueryContentEvent
event(true, eQueryTextRect
, mWidget
);
4463 mWidget
->InitEvent(event
);
4465 WidgetQueryContentEvent::Options options
;
4466 int64_t startOffset
= acpStart
;
4467 if (mComposition
.IsComposing()) {
4468 // If there is a composition, TSF must want character rects related to
4469 // the composition. Therefore, we should use insertion point relative
4470 // query because the composition might be at different position from
4471 // the position where TSFTextStore believes it at.
4472 options
.mRelativeToInsertionPoint
= true;
4473 startOffset
-= mComposition
.mStart
;
4474 } else if (IsHandlingComposition() && mContentForTSF
.HasOrHadComposition()) {
4475 // If there was a composition and it hasn't been committed in the content
4476 // yet, ContentCacheInParent is still open for relative offset query from
4477 // the latest composition.
4478 options
.mRelativeToInsertionPoint
= true;
4479 startOffset
-= mContentForTSF
.LatestCompositionStartOffset();
4480 } else if (!CanAccessActualContentDirectly()) {
4481 // If TSF/TIP cannot access actual content directly, there may be pending
4482 // text and/or selection changes which have not been notified TSF yet.
4483 // Therefore, we should use relative to insertion point query since
4484 // TSF/TIP computes the offset from the cached selection.
4485 options
.mRelativeToInsertionPoint
= true;
4486 startOffset
-= mSelectionForTSF
.StartOffset();
4488 // ContentEventHandler and ContentCache return actual caret rect when
4489 // the queried range is collapsed and selection is collapsed at the
4490 // queried range. Then, its height (in horizontal layout, width in vertical
4491 // layout) may be different from actual font height of the line. In such
4492 // case, users see "dancing" of candidate or suggest window of TIP.
4493 // For preventing it, we should query text rect with at least 1 length.
4494 uint32_t length
= std::max(static_cast<int32_t>(acpEnd
- acpStart
), 1);
4495 event
.InitForQueryTextRect(startOffset
, length
, options
);
4497 DispatchEvent(event
);
4498 if (NS_WARN_IF(!event
.mSucceeded
)) {
4499 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4500 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4501 "eQueryTextRect failure",
4503 return TS_E_INVALIDPOS
; // but unexpected failure, maybe.
4506 // IMEs don't like empty rects, fix here
4507 if (event
.mReply
.mRect
.Width() <= 0) event
.mReply
.mRect
.SetWidth(1);
4508 if (event
.mReply
.mRect
.Height() <= 0) event
.mReply
.mRect
.SetHeight(1);
4510 // convert to unclipped screen rect
4511 nsWindow
* refWindow
= static_cast<nsWindow
*>(
4512 event
.mReply
.mFocusedWidget
? event
.mReply
.mFocusedWidget
: mWidget
);
4513 // Result rect is in top level widget coordinates
4514 refWindow
= refWindow
->GetTopLevelWindow(false);
4516 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4517 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4518 "no top level window",
4523 event
.mReply
.mRect
.MoveBy(refWindow
->WidgetToScreenOffset());
4525 // get bounding screen rect to test for clipping
4526 if (!GetScreenExtInternal(*prc
)) {
4527 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4528 ("0x%p TSFTextStore::GetTextExt() FAILED due to "
4529 "GetScreenExtInternal() failure",
4534 // clip text rect to bounding rect
4536 ::SetRect(&textRect
, event
.mReply
.mRect
.X(), event
.mReply
.mRect
.Y(),
4537 event
.mReply
.mRect
.XMost(), event
.mReply
.mRect
.YMost());
4538 if (!::IntersectRect(prc
, prc
, &textRect
))
4539 // Text is not visible
4540 ::SetRectEmpty(prc
);
4542 // not equal if text rect was clipped
4543 *pfClipped
= !::EqualRect(prc
, &textRect
);
4545 // ATOK 2011 - 2016 refers native caret position and size on windows whose
4546 // class name is one of Mozilla's windows for deciding candidate window
4547 // position. Additionally, ATOK 2015 and earlier behaves really odd when
4548 // we don't create native caret. Therefore, we need to create native caret
4549 // only when ATOK 2011 - 2015 is active (i.e., not necessary for ATOK 2016).
4550 // However, if a11y module is handling native caret, we shouldn't touch it.
4551 // Note that ATOK must require the latest information of the caret. So,
4552 // even if we'll create native caret later, we need to creat it here with
4553 // current information.
4554 if (!IMEHandler::IsA11yHandlingNativeCaret() &&
4555 TSFPrefs::NeedToCreateNativeCaretForLegacyATOK() &&
4556 TSFStaticSink::IsATOKReferringNativeCaretActive() &&
4557 mComposition
.IsComposing() && mComposition
.mStart
<= acpStart
&&
4558 mComposition
.EndOffset() >= acpStart
&& mComposition
.mStart
<= acpEnd
&&
4559 mComposition
.EndOffset() >= acpEnd
) {
4560 CreateNativeCaret();
4563 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4564 ("0x%p TSFTextStore::GetTextExt() succeeded: "
4565 "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s",
4566 this, prc
->left
, prc
->top
, prc
->right
, prc
->bottom
,
4567 GetBoolName(*pfClipped
)));
4572 bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG
& aACPStart
, LONG
& aACPEnd
) {
4573 // When ITextStoreACP::GetTextExt() returns TS_E_NOLAYOUT, TSF returns E_FAIL
4574 // to its caller (typically, active TIP). Then, most TIPs abort current job
4575 // or treat such application as non-GUI apps. E.g., some of them give up
4576 // showing candidate window, some others show candidate window at top-left of
4577 // the screen. For avoiding this issue, when there is composition (until
4578 // composition is actually committed in remote content), we should not
4579 // return TS_E_NOLAYOUT error for TIPs whose some features are broken by
4581 // Note that ideally, this issue should be avoided by each TIP since this
4582 // won't be fixed at least on non-latest Windows. Actually, Google Japanese
4583 // Input (based on Mozc) does it. When GetTextExt() returns E_FAIL, TIPs
4584 // should try to check result of GetRangeFromPoint() because TSF returns
4585 // TS_E_NOLAYOUT correctly in this case. See:
4586 // https://github.com/google/mozc/blob/6b878e31fb6ac4347dc9dfd8ccc1080fe718479f/src/win32/tip/tip_range_util.cc#L237-L257
4588 if (!IsHandlingComposition() || !mContentForTSF
.HasOrHadComposition() ||
4589 !mContentForTSF
.IsLayoutChangedAt(aACPEnd
)) {
4593 MOZ_ASSERT(!mComposition
.IsComposing() ||
4594 mComposition
.mStart
==
4595 mContentForTSF
.LatestCompositionStartOffset());
4596 MOZ_ASSERT(!mComposition
.IsComposing() ||
4597 mComposition
.EndOffset() ==
4598 mContentForTSF
.LatestCompositionEndOffset());
4600 // If TSF does not have the bug, we need to hack only with a few TIPs.
4601 static const bool sAlllowToStopHackingIfFine
=
4602 IsWindows10BuildOrLater(17643) &&
4603 TSFPrefs::AllowToStopHackingOnBuild17643OrLater();
4605 // We need to compute active TIP now. This may take a couple of milliseconds,
4606 // however, it'll be cached, so, must be faster than check active TIP every
4607 // GetTextExt() calls.
4608 const Selection
& selectionForTSF
= SelectionForTSFRef();
4609 switch (TSFStaticSink::ActiveTIP()) {
4610 // MS IME for Japanese doesn't support asynchronous handling at deciding
4611 // its suggest list window position. The feature was implemented
4612 // starting from Windows 8. And also we may meet same trouble in e10s
4613 // mode on Win7. So, we should never return TS_E_NOLAYOUT to MS IME for
4615 case TextInputProcessorID::eMicrosoftIMEForJapanese
:
4616 // Basically, MS-IME tries to retrieve whole composition string rect
4617 // at deciding suggest window immediately after unlocking the document.
4618 // However, in e10s mode, the content hasn't updated yet in most cases.
4619 // Therefore, if the first character at the retrieving range rect is
4620 // available, we should use it as the result.
4621 // Note that according to bug 1609675, MS-IME for Japanese itself does
4622 // not handle TS_E_NOLAYOUT correctly at least on Build 18363.657 (1909).
4623 if (TSFPrefs::DoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar() &&
4624 aACPStart
< aACPEnd
) {
4625 aACPEnd
= aACPStart
;
4628 if (sAlllowToStopHackingIfFine
) {
4631 // Although, the condition is not clear, MS-IME sometimes retrieves the
4632 // caret rect immediately after modifying the composition string but
4633 // before unlocking the document. In such case, we should return the
4634 // nearest character rect.
4635 if (TSFPrefs::DoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret() &&
4636 aACPStart
== aACPEnd
&& selectionForTSF
.IsCollapsed() &&
4637 selectionForTSF
.EndOffset() == aACPEnd
) {
4638 int32_t minOffsetOfLayoutChanged
=
4639 static_cast<int32_t>(mContentForTSF
.MinOffsetOfLayoutChanged());
4640 aACPEnd
= aACPStart
= std::max(minOffsetOfLayoutChanged
- 1, 0);
4645 // The bug of Microsoft Office IME 2010 for Japanese is similar to
4646 // MS-IME for Win 8.1 and Win 10. Newer version of MS Office IME is not
4647 // released yet. So, we can hack it without prefs because there must be
4648 // no developers who want to disable this hack for tests.
4649 // XXX We have not tested with Microsoft Office IME 2010 since it's
4650 // installable only with Win7 and Win8 (i.e., cannot install Win8.1
4651 // and Win10), and requires upgrade to Win10.
4652 case TextInputProcessorID::eMicrosoftOfficeIME2010ForJapanese
:
4653 // Basically, MS-IME tries to retrieve whole composition string rect
4654 // at deciding suggest window immediately after unlocking the document.
4655 // However, in e10s mode, the content hasn't updated yet in most cases.
4656 // Therefore, if the first character at the retrieving range rect is
4657 // available, we should use it as the result.
4658 if (aACPStart
< aACPEnd
) {
4659 aACPEnd
= aACPStart
;
4661 // Although, the condition is not clear, MS-IME sometimes retrieves the
4662 // caret rect immediately after modifying the composition string but
4663 // before unlocking the document. In such case, we should return the
4664 // nearest character rect.
4665 else if (aACPStart
== aACPEnd
&& selectionForTSF
.IsCollapsed() &&
4666 selectionForTSF
.EndOffset() == aACPEnd
) {
4667 int32_t minOffsetOfLayoutChanged
=
4668 static_cast<int32_t>(mContentForTSF
.MinOffsetOfLayoutChanged());
4669 aACPEnd
= aACPStart
= std::max(minOffsetOfLayoutChanged
- 1, 0);
4674 // ATOK fails to handle TS_E_NOLAYOUT only when it decides the position of
4675 // suggest window. In such case, ATOK tries to query rect of whole or a
4676 // part of composition string.
4677 // FYI: ATOK changes their implementation around candidate window and
4678 // suggest widget at ATOK 2016. Therefore, there are some differences
4679 // ATOK 2015 (or older) and ATOK 2016 (or newer).
4680 // FYI: ATOK 2017 stops referring our window class name. I.e., ATOK 2016
4681 // and older may behave differently only on Gecko but this must be
4682 // finished from ATOK 2017.
4683 // FYI: For testing with legacy ATOK, we should hack it even if current ATOK
4684 // refers native caret rect on windows whose window class is one of
4685 // Mozilla window classes and we stop creating native caret for ATOK
4686 // because creating native caret causes ATOK refers caret position
4687 // when GetTextExt() returns TS_E_NOLAYOUT.
4688 case TextInputProcessorID::eATOK2011
:
4689 case TextInputProcessorID::eATOK2012
:
4690 case TextInputProcessorID::eATOK2013
:
4691 case TextInputProcessorID::eATOK2014
:
4692 case TextInputProcessorID::eATOK2015
:
4693 // ATOK 2016 and later may temporarily show candidate window at odd
4694 // position when you convert a word quickly (e.g., keep pressing
4695 // space bar). So, on ATOK 2016 or later, we need to keep hacking the
4696 // result of GetTextExt().
4697 if (sAlllowToStopHackingIfFine
) {
4700 // If we'll create native caret where we paint our caret. Then, ATOK
4701 // will refer native caret. So, we don't need to hack anything in
4703 if (TSFPrefs::NeedToCreateNativeCaretForLegacyATOK()) {
4704 MOZ_ASSERT(TSFStaticSink::IsATOKReferringNativeCaretActive());
4708 case TextInputProcessorID::eATOK2016
:
4709 case TextInputProcessorID::eATOKUnknown
:
4710 if (!TSFPrefs::DoNotReturnNoLayoutErrorToATOKOfCompositionString()) {
4713 // If the range is in the composition string, we should return rectangle
4714 // in it as far as possible.
4715 if (aACPStart
< mContentForTSF
.LatestCompositionStartOffset() ||
4716 aACPStart
> mContentForTSF
.LatestCompositionEndOffset() ||
4717 aACPEnd
< mContentForTSF
.LatestCompositionStartOffset() ||
4718 aACPEnd
> mContentForTSF
.LatestCompositionEndOffset()) {
4722 // Japanist 10 fails to handle TS_E_NOLAYOUT when it decides the position
4723 // of candidate window. In such case, Japanist shows candidate window at
4724 // top-left of the screen. So, we should return the nearest caret rect
4725 // where we know. This is Japanist's bug. So, even after build 17643,
4726 // we need this hack.
4727 case TextInputProcessorID::eJapanist10
:
4729 DoNotReturnNoLayoutErrorToJapanist10OfCompositionString()) {
4732 if (aACPStart
< mContentForTSF
.LatestCompositionStartOffset() ||
4733 aACPStart
> mContentForTSF
.LatestCompositionEndOffset() ||
4734 aACPEnd
< mContentForTSF
.LatestCompositionStartOffset() ||
4735 aACPEnd
> mContentForTSF
.LatestCompositionEndOffset()) {
4739 // Free ChangJie 2010 doesn't handle ITfContextView::GetTextExt() properly.
4740 // This must be caused by the bug of TSF since Free ChangJie works fine on
4741 // build 17643 and later.
4742 case TextInputProcessorID::eFreeChangJie
:
4743 if (sAlllowToStopHackingIfFine
) {
4746 if (!TSFPrefs::DoNotReturnNoLayoutErrorToFreeChangJie()) {
4749 aACPEnd
= mContentForTSF
.LatestCompositionStartOffset();
4750 aACPStart
= std::min(aACPStart
, aACPEnd
);
4752 // Some Traditional Chinese TIPs of Microsoft don't show candidate window
4753 // in e10s mode on Win8 or later.
4754 case TextInputProcessorID::eMicrosoftQuick
:
4755 if (sAlllowToStopHackingIfFine
) {
4756 return false; // MS Quick works fine with Win10 build 17643.
4759 case TextInputProcessorID::eMicrosoftChangJie
:
4760 if (!IsWin8OrLater() ||
4761 !TSFPrefs::DoNotReturnNoLayoutErrorToMSTraditionalTIP()) {
4764 aACPEnd
= mContentForTSF
.LatestCompositionStartOffset();
4765 aACPStart
= std::min(aACPStart
, aACPEnd
);
4767 // Some Simplified Chinese TIPs of Microsoft don't show candidate window
4768 // in e10s mode on Win8 or later.
4769 // FYI: Only Simplified Chinese TIPs of Microsoft still require this hack
4770 // because they sometimes do not show candidate window when we return
4771 // TS_E_NOLAYOUT for first query. Note that even when they show
4772 // candidate window properly, we return TS_E_NOLAYOUT and following
4773 // log looks same as when they don't show candidate window. Perhaps,
4774 // there is stateful cause or race in them.
4775 case TextInputProcessorID::eMicrosoftPinyin
:
4776 case TextInputProcessorID::eMicrosoftWubi
:
4777 if (!IsWin8OrLater() ||
4778 !TSFPrefs::DoNotReturnNoLayoutErrorToMSSimplifiedTIP()) {
4781 aACPEnd
= mContentForTSF
.LatestCompositionStartOffset();
4782 aACPStart
= std::min(aACPStart
, aACPEnd
);
4788 // If we hack the queried range for active TIP, that means we should not
4789 // return TS_E_NOLAYOUT even if hacked offset is still modified. So, as
4790 // far as possible, we should adjust the offset.
4791 MOZ_ASSERT(mContentForTSF
.IsLayoutChanged());
4792 bool collapsed
= aACPStart
== aACPEnd
;
4793 // Note that even if all characters in the editor or the composition
4794 // string was modified, 0 or start offset of the composition string is
4795 // useful because it may return caret rect or old character's rect which
4796 // the user still see. That must be useful information for TIP.
4797 int32_t firstModifiedOffset
=
4798 static_cast<int32_t>(mContentForTSF
.MinOffsetOfLayoutChanged());
4799 LONG lastUnmodifiedOffset
= std::max(firstModifiedOffset
- 1, 0);
4800 if (mContentForTSF
.IsLayoutChangedAt(aACPStart
)) {
4801 if (aACPStart
>= mContentForTSF
.LatestCompositionStartOffset()) {
4802 // If mContentForTSF has last composition string and current
4803 // composition string, we can assume that ContentCacheInParent has
4804 // cached rects of composition string at least length of current
4805 // composition string. Otherwise, we can assume that rect for
4806 // first character of composition string is stored since it was
4807 // selection start or caret position.
4808 LONG maxCachedOffset
= mContentForTSF
.LatestCompositionEndOffset();
4809 if (mContentForTSF
.WasLastComposition()) {
4810 maxCachedOffset
= std::min(
4811 maxCachedOffset
, mContentForTSF
.LastCompositionStringEndOffset());
4813 aACPStart
= std::min(aACPStart
, maxCachedOffset
);
4815 // Otherwise, we don't know which character rects are cached. So, we
4816 // need to use first unmodified character's rect in this case. Even
4817 // if there is no character, the query event will return caret rect
4820 aACPStart
= lastUnmodifiedOffset
;
4822 MOZ_ASSERT(aACPStart
<= aACPEnd
);
4825 // If TIP requests caret rect with collapsed range, we should keep
4826 // collapsing the range.
4828 aACPEnd
= aACPStart
;
4830 // Let's set aACPEnd to larger offset of last unmodified offset or
4831 // aACPStart which may be the first character offset of the composition
4832 // string. However, some TIPs may want to know the right edge of the
4833 // range. Therefore, if aACPEnd is in composition string and active TIP
4834 // doesn't retrieve caret rect (i.e., the range isn't collapsed), we
4835 // should keep using the original aACPEnd. Otherwise, we should set
4836 // aACPEnd to larger value of aACPStart and lastUnmodifiedOffset.
4837 else if (mContentForTSF
.IsLayoutChangedAt(aACPEnd
) &&
4838 (aACPEnd
< mContentForTSF
.LatestCompositionStartOffset() ||
4839 aACPEnd
> mContentForTSF
.LatestCompositionEndOffset())) {
4840 aACPEnd
= std::max(aACPStart
, lastUnmodifiedOffset
);
4844 sTextStoreLog
, LogLevel::Debug
,
4845 ("0x%p TSFTextStore::HackNoErrorLayoutBugs() hacked the queried range "
4846 "for not returning TS_E_NOLAYOUT, new values are: "
4847 "aACPStart=%d, aACPEnd=%d",
4848 this, aACPStart
, aACPEnd
));
4854 TSFTextStore::GetScreenExt(TsViewCookie vcView
, RECT
* prc
) {
4855 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4856 ("0x%p TSFTextStore::GetScreenExt(vcView=%ld, prc=0x%p)", this,
4859 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4860 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4861 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4862 "called with invalid view",
4864 return E_INVALIDARG
;
4868 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4869 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4872 return E_INVALIDARG
;
4876 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4877 ("0x%p TSFTextStore::GetScreenExt() returns empty rect "
4878 "due to already destroyed",
4880 prc
->left
= prc
->top
= prc
->right
= prc
->bottom
= 0;
4884 if (!GetScreenExtInternal(*prc
)) {
4885 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4886 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4887 "GetScreenExtInternal() failure",
4892 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4893 ("0x%p TSFTextStore::GetScreenExt() succeeded: "
4894 "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
4895 this, prc
->left
, prc
->top
, prc
->right
, prc
->bottom
));
4899 bool TSFTextStore::GetScreenExtInternal(RECT
& aScreenExt
) {
4900 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
4901 ("0x%p TSFTextStore::GetScreenExtInternal()", this));
4903 MOZ_ASSERT(!mDestroyed
);
4905 // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
4906 WidgetQueryContentEvent
event(true, eQueryEditorRect
, mWidget
);
4907 mWidget
->InitEvent(event
);
4908 DispatchEvent(event
);
4909 if (!event
.mSucceeded
) {
4910 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4911 ("0x%p TSFTextStore::GetScreenExtInternal() FAILED due to "
4912 "eQueryEditorRect failure",
4917 nsWindow
* refWindow
= static_cast<nsWindow
*>(
4918 event
.mReply
.mFocusedWidget
? event
.mReply
.mFocusedWidget
: mWidget
);
4919 // Result rect is in top level widget coordinates
4920 refWindow
= refWindow
->GetTopLevelWindow(false);
4922 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4923 ("0x%p TSFTextStore::GetScreenExtInternal() FAILED due to "
4924 "no top level window",
4929 LayoutDeviceIntRect boundRect
= refWindow
->GetClientBounds();
4930 boundRect
.MoveTo(0, 0);
4932 // Clip frame rect to window rect
4933 boundRect
.IntersectRect(event
.mReply
.mRect
, boundRect
);
4934 if (!boundRect
.IsEmpty()) {
4935 boundRect
.MoveBy(refWindow
->WidgetToScreenOffset());
4936 ::SetRect(&aScreenExt
, boundRect
.X(), boundRect
.Y(), boundRect
.XMost(),
4939 ::SetRectEmpty(&aScreenExt
);
4942 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
4943 ("0x%p TSFTextStore::GetScreenExtInternal() succeeded: "
4944 "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }",
4945 this, aScreenExt
.left
, aScreenExt
.top
, aScreenExt
.right
,
4946 aScreenExt
.bottom
));
4951 TSFTextStore::GetWnd(TsViewCookie vcView
, HWND
* phwnd
) {
4952 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4953 ("0x%p TSFTextStore::GetWnd(vcView=%ld, phwnd=0x%p), "
4955 this, vcView
, phwnd
, mWidget
.get()));
4957 if (vcView
!= TEXTSTORE_DEFAULT_VIEW
) {
4958 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4959 ("0x%p TSFTextStore::GetWnd() FAILED due to "
4960 "called with invalid view",
4962 return E_INVALIDARG
;
4966 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
4967 ("0x%p TSFTextStore::GetScreenExt() FAILED due to "
4970 return E_INVALIDARG
;
4973 *phwnd
= mWidget
? mWidget
->GetWindowHandle() : nullptr;
4975 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
4976 ("0x%p TSFTextStore::GetWnd() succeeded: *phwnd=0x%p", this,
4977 static_cast<void*>(*phwnd
)));
4982 TSFTextStore::InsertTextAtSelection(DWORD dwFlags
, const WCHAR
* pchText
,
4983 ULONG cch
, LONG
* pacpStart
, LONG
* pacpEnd
,
4984 TS_TEXTCHANGE
* pChange
) {
4986 sTextStoreLog
, LogLevel::Info
,
4987 ("0x%p TSFTextStore::InsertTextAtSelection(dwFlags=%s, "
4988 "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, "
4989 "pChange=0x%p), IsComposing()=%s",
4993 : dwFlags
== TF_IAS_NOQUERY
4995 : dwFlags
== TF_IAS_QUERYONLY
? "TF_IAS_QUERYONLY" : "Unknown",
4996 pchText
, pchText
&& cch
? GetEscapedUTF8String(pchText
, cch
).get() : "",
4997 cch
, pacpStart
, pacpEnd
, pChange
,
4998 GetBoolName(mComposition
.IsComposing())));
5000 if (cch
&& !pchText
) {
5001 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5002 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5005 return E_INVALIDARG
;
5008 if (TS_IAS_QUERYONLY
== dwFlags
) {
5009 if (!IsReadLocked()) {
5010 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5011 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5012 "not locked (read)",
5017 if (!pacpStart
|| !pacpEnd
) {
5018 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5019 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5022 return E_INVALIDARG
;
5025 // Get selection first
5026 Selection
& selectionForTSF
= SelectionForTSFRef();
5027 if (selectionForTSF
.IsDirty()) {
5028 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5029 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5030 "SelectionForTSFRef() failure",
5035 // Simulate text insertion
5036 *pacpStart
= selectionForTSF
.StartOffset();
5037 *pacpEnd
= selectionForTSF
.EndOffset();
5039 pChange
->acpStart
= selectionForTSF
.StartOffset();
5040 pChange
->acpOldEnd
= selectionForTSF
.EndOffset();
5041 pChange
->acpNewEnd
=
5042 selectionForTSF
.StartOffset() + static_cast<LONG
>(cch
);
5045 if (!IsReadWriteLocked()) {
5046 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5047 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5048 "not locked (read-write)",
5054 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5055 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5058 return E_INVALIDARG
;
5061 if (TS_IAS_NOQUERY
!= dwFlags
&& (!pacpStart
|| !pacpEnd
)) {
5062 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5063 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5066 return E_INVALIDARG
;
5069 if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText
, cch
),
5071 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5072 ("0x%p TSFTextStore::InsertTextAtSelection() FAILED due to "
5073 "InsertTextAtSelectionInternal() failure",
5078 if (TS_IAS_NOQUERY
!= dwFlags
) {
5079 *pacpStart
= pChange
->acpStart
;
5080 *pacpEnd
= pChange
->acpNewEnd
;
5083 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5084 ("0x%p TSFTextStore::InsertTextAtSelection() succeeded: "
5085 "*pacpStart=%ld, *pacpEnd=%ld, "
5086 "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })",
5087 this, pacpStart
? *pacpStart
: 0, pacpEnd
? *pacpEnd
: 0,
5088 pChange
? pChange
->acpStart
: 0, pChange
? pChange
->acpOldEnd
: 0,
5089 pChange
? pChange
->acpNewEnd
: 0));
5093 bool TSFTextStore::InsertTextAtSelectionInternal(const nsAString
& aInsertStr
,
5094 TS_TEXTCHANGE
* aTextChange
) {
5095 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5096 ("0x%p TSFTextStore::InsertTextAtSelectionInternal("
5097 "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s",
5098 this, GetEscapedUTF8String(aInsertStr
).get(), aTextChange
,
5099 GetBoolName(mComposition
.IsComposing())));
5101 Content
& contentForTSF
= ContentForTSFRef();
5102 if (!contentForTSF
.IsInitialized()) {
5103 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5104 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() failed "
5105 "due to ContentForTSFRef() failure()",
5110 MaybeDispatchKeyboardEventAsProcessedByIME();
5113 sTextStoreLog
, LogLevel::Error
,
5114 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() FAILED due to "
5115 "destroyed during dispatching a keyboard event",
5120 TS_SELECTION_ACP oldSelection
= contentForTSF
.Selection().ACP();
5121 if (!mComposition
.IsComposing()) {
5122 // Use a temporary composition to contain the text
5123 PendingAction
* compositionStart
= mPendingActions
.AppendElements(2);
5124 PendingAction
* compositionEnd
= compositionStart
+ 1;
5126 compositionStart
->mType
= PendingAction::Type::eCompositionStart
;
5127 compositionStart
->mSelectionStart
= oldSelection
.acpStart
;
5128 compositionStart
->mSelectionLength
=
5129 oldSelection
.acpEnd
- oldSelection
.acpStart
;
5130 compositionStart
->mAdjustSelection
= false;
5132 compositionEnd
->mType
= PendingAction::Type::eCompositionEnd
;
5133 compositionEnd
->mData
= aInsertStr
;
5134 compositionEnd
->mSelectionStart
= compositionStart
->mSelectionStart
;
5136 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5137 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() "
5138 "appending pending compositionstart and compositionend... "
5139 "PendingCompositionStart={ mSelectionStart=%d, "
5140 "mSelectionLength=%d }, PendingCompositionEnd={ mData=\"%s\" "
5141 "(Length()=%u), mSelectionStart=%d }",
5142 this, compositionStart
->mSelectionStart
,
5143 compositionStart
->mSelectionLength
,
5144 GetEscapedUTF8String(compositionEnd
->mData
).get(),
5145 compositionEnd
->mData
.Length(), compositionEnd
->mSelectionStart
));
5148 contentForTSF
.ReplaceSelectedTextWith(aInsertStr
);
5151 aTextChange
->acpStart
= oldSelection
.acpStart
;
5152 aTextChange
->acpOldEnd
= oldSelection
.acpEnd
;
5153 aTextChange
->acpNewEnd
= contentForTSF
.Selection().EndOffset();
5157 sTextStoreLog
, LogLevel::Debug
,
5158 ("0x%p TSFTextStore::InsertTextAtSelectionInternal() "
5159 "succeeded: mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ "
5160 "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }",
5161 this, mWidget
.get(), GetBoolName(mWidget
? mWidget
->Destroyed() : true),
5162 aTextChange
? aTextChange
->acpStart
: 0,
5163 aTextChange
? aTextChange
->acpOldEnd
: 0,
5164 aTextChange
? aTextChange
->acpNewEnd
: 0));
5169 TSFTextStore::InsertEmbeddedAtSelection(DWORD dwFlags
, IDataObject
* pDataObject
,
5170 LONG
* pacpStart
, LONG
* pacpEnd
,
5171 TS_TEXTCHANGE
* pChange
) {
5172 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5173 ("0x%p TSFTextStore::InsertEmbeddedAtSelection() called "
5174 "but not supported (E_NOTIMPL)",
5177 // embedded objects are not supported
5182 TSFTextStore::RecordCompositionStartAction(ITfCompositionView
* aComposition
,
5184 bool aPreserveSelection
) {
5185 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5186 ("0x%p TSFTextStore::RecordCompositionStartAction("
5187 "aComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), "
5188 "mComposition.mView=0x%p",
5189 this, aComposition
, aRange
, GetBoolName(aPreserveSelection
),
5190 mComposition
.mView
.get()));
5192 LONG start
= 0, length
= 0;
5193 HRESULT hr
= GetRangeExtent(aRange
, &start
, &length
);
5195 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5196 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED "
5197 "due to GetRangeExtent() failure",
5202 return RecordCompositionStartAction(aComposition
, start
, length
,
5203 aPreserveSelection
);
5207 TSFTextStore::RecordCompositionStartAction(ITfCompositionView
* aComposition
,
5208 LONG aStart
, LONG aLength
,
5209 bool aPreserveSelection
) {
5210 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5211 ("0x%p TSFTextStore::RecordCompositionStartAction("
5212 "aComposition=0x%p, aStart=%d, aLength=%d, aPreserveSelection=%s), "
5213 "mComposition.mView=0x%p",
5214 this, aComposition
, aStart
, aLength
, GetBoolName(aPreserveSelection
),
5215 mComposition
.mView
.get()));
5217 Content
& contentForTSF
= ContentForTSFRef();
5218 if (!contentForTSF
.IsInitialized()) {
5219 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5220 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED "
5221 "due to ContentForTSFRef() failure",
5226 MaybeDispatchKeyboardEventAsProcessedByIME();
5229 sTextStoreLog
, LogLevel::Error
,
5230 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED due to "
5231 "destroyed during dispatching a keyboard event",
5236 CompleteLastActionIfStillIncomplete();
5238 // TIP may have inserted text at selection before calling
5239 // OnStartComposition(). In this case, we've already created a pending
5240 // compositionend. If new composition replaces all commit string of the
5241 // pending compositionend, we should cancel the pending compositionend and
5242 // keep the previous composition normally.
5243 // On Windows 7, MS-IME for Korean, MS-IME 2010 for Korean and MS Old Hangul
5244 // may start composition with calling InsertTextAtSelection() and
5245 // OnStartComposition() with this order (bug 1208043).
5246 // On Windows 10, MS Pinyin, MS Wubi, MS ChangJie and MS Quick commits
5247 // last character and replace it with empty string with new composition
5248 // when user removes last character of composition string with Backspace
5249 // key (bug 1462257).
5250 if (!aPreserveSelection
&&
5251 IsLastPendingActionCompositionEndAt(aStart
, aLength
)) {
5252 const PendingAction
& pendingCompositionEnd
= mPendingActions
.LastElement();
5253 contentForTSF
.RestoreCommittedComposition(aComposition
,
5254 pendingCompositionEnd
);
5255 mPendingActions
.RemoveLastElement();
5256 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5257 ("0x%p TSFTextStore::RecordCompositionStartAction() "
5258 "succeeded: restoring the committed string as composing string, "
5259 "mComposition={ mStart=%ld, mString.Length()=%ld, "
5260 "mSelectionForTSF={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
5261 "style.fInterimChar=%s } }",
5262 this, mComposition
.mStart
, mComposition
.mString
.Length(),
5263 mSelectionForTSF
.StartOffset(), mSelectionForTSF
.EndOffset(),
5264 GetActiveSelEndName(mSelectionForTSF
.ActiveSelEnd()),
5265 GetBoolName(mSelectionForTSF
.IsInterimChar())));
5269 PendingAction
* action
= mPendingActions
.AppendElement();
5270 action
->mType
= PendingAction::Type::eCompositionStart
;
5271 action
->mSelectionStart
= aStart
;
5272 action
->mSelectionLength
= aLength
;
5274 Selection
& selectionForTSF
= SelectionForTSFRef();
5275 if (selectionForTSF
.IsDirty()) {
5276 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5277 ("0x%p TSFTextStore::RecordCompositionStartAction() FAILED "
5278 "due to SelectionForTSFRef() failure",
5280 action
->mAdjustSelection
= true;
5281 } else if (selectionForTSF
.MinOffset() != aStart
||
5282 selectionForTSF
.MaxOffset() != aStart
+ aLength
) {
5283 // If new composition range is different from current selection range,
5284 // we need to set selection before dispatching compositionstart event.
5285 action
->mAdjustSelection
= true;
5287 // We shouldn't dispatch selection set event before dispatching
5288 // compositionstart event because it may cause put caret different
5289 // position in HTML editor since generated flat text content and offset in
5290 // it are lossy data of HTML contents.
5291 action
->mAdjustSelection
= false;
5294 contentForTSF
.StartComposition(aComposition
, *action
, aPreserveSelection
);
5295 action
->mData
= mComposition
.mString
;
5297 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5298 ("0x%p TSFTextStore::RecordCompositionStartAction() succeeded: "
5299 "mComposition={ mStart=%ld, mString.Length()=%ld, "
5300 "mSelectionForTSF={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
5301 "style.fInterimChar=%s } }",
5302 this, mComposition
.mStart
, mComposition
.mString
.Length(),
5303 mSelectionForTSF
.StartOffset(), mSelectionForTSF
.EndOffset(),
5304 GetActiveSelEndName(mSelectionForTSF
.ActiveSelEnd()),
5305 GetBoolName(mSelectionForTSF
.IsInterimChar())));
5310 TSFTextStore::RecordCompositionEndAction() {
5311 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5312 ("0x%p TSFTextStore::RecordCompositionEndAction(), "
5313 "mComposition={ mView=0x%p, mString=\"%s\" }",
5314 this, mComposition
.mView
.get(),
5315 GetEscapedUTF8String(mComposition
.mString
).get()));
5317 MOZ_ASSERT(mComposition
.IsComposing());
5319 MaybeDispatchKeyboardEventAsProcessedByIME();
5321 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5322 ("0x%p TSFTextStore::RecordCompositionEndAction() FAILED due to "
5323 "destroyed during dispatching a keyboard event",
5328 // If we're handling incomplete composition update or already handled
5329 // composition update, we can forget them since composition end will send
5330 // the latest composition string and it overwrites the composition string
5331 // even if we dispatch eCompositionChange event before that. So, let's
5332 // forget all composition updates now.
5333 RemoveLastCompositionUpdateActions();
5334 PendingAction
* action
= mPendingActions
.AppendElement();
5335 action
->mType
= PendingAction::Type::eCompositionEnd
;
5336 action
->mData
= mComposition
.mString
;
5337 action
->mSelectionStart
= mComposition
.mStart
;
5339 Content
& contentForTSF
= ContentForTSFRef();
5340 if (!contentForTSF
.IsInitialized()) {
5341 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5342 ("0x%p TSFTextStore::RecordCompositionEndAction() FAILED due "
5343 "to ContentForTSFRef() failure",
5347 contentForTSF
.EndComposition(*action
);
5349 // If this composition was restart but the composition doesn't modify
5350 // anything, we should remove the pending composition for preventing to
5351 // dispatch redundant composition events.
5352 for (size_t i
= mPendingActions
.Length(), j
= 1; i
> 0; --i
, ++j
) {
5353 PendingAction
& pendingAction
= mPendingActions
[i
- 1];
5354 if (pendingAction
.mType
== PendingAction::Type::eCompositionStart
) {
5355 if (pendingAction
.mData
!= action
->mData
) {
5358 // When only setting selection is necessary, we should append it.
5359 if (pendingAction
.mAdjustSelection
) {
5360 LONG selectionStart
= pendingAction
.mSelectionStart
;
5361 LONG selectionLength
= pendingAction
.mSelectionLength
;
5363 PendingAction
* setSelection
= mPendingActions
.AppendElement();
5364 setSelection
->mType
= PendingAction::Type::eSetSelection
;
5365 setSelection
->mSelectionStart
= selectionStart
;
5366 setSelection
->mSelectionLength
= selectionLength
;
5367 setSelection
->mSelectionReversed
= false;
5369 // Remove the redundant pending composition.
5370 mPendingActions
.RemoveElementsAt(i
- 1, j
);
5371 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5372 ("0x%p TSFTextStore::RecordCompositionEndAction(), "
5373 "succeeded, but the composition was canceled due to redundant",
5380 sTextStoreLog
, LogLevel::Info
,
5381 ("0x%p TSFTextStore::RecordCompositionEndAction(), succeeded", this));
5386 TSFTextStore::OnStartComposition(ITfCompositionView
* pComposition
, BOOL
* pfOk
) {
5387 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5388 ("0x%p TSFTextStore::OnStartComposition(pComposition=0x%p, "
5389 "pfOk=0x%p), mComposition.mView=0x%p",
5390 this, pComposition
, pfOk
, mComposition
.mView
.get()));
5392 AutoPendingActionAndContentFlusher
flusher(this);
5396 // Only one composition at a time
5397 if (mComposition
.IsComposing()) {
5398 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5399 ("0x%p TSFTextStore::OnStartComposition() FAILED due to "
5400 "there is another composition already (but returns S_OK)",
5405 RefPtr
<ITfRange
> range
;
5406 HRESULT hr
= pComposition
->GetRange(getter_AddRefs(range
));
5408 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5409 ("0x%p TSFTextStore::OnStartComposition() FAILED due to "
5410 "pComposition->GetRange() failure",
5414 hr
= RecordCompositionStartAction(pComposition
, range
, false);
5416 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5417 ("0x%p TSFTextStore::OnStartComposition() FAILED due to "
5418 "RecordCompositionStartAction() failure",
5424 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5425 ("0x%p TSFTextStore::OnStartComposition() succeeded", this));
5430 TSFTextStore::OnUpdateComposition(ITfCompositionView
* pComposition
,
5431 ITfRange
* pRangeNew
) {
5432 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5433 ("0x%p TSFTextStore::OnUpdateComposition(pComposition=0x%p, "
5434 "pRangeNew=0x%p), mComposition.mView=0x%p",
5435 this, pComposition
, pRangeNew
, mComposition
.mView
.get()));
5437 AutoPendingActionAndContentFlusher
flusher(this);
5439 if (!mDocumentMgr
|| !mContext
) {
5440 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5441 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5442 "not ready for the composition",
5444 return E_UNEXPECTED
;
5446 if (!mComposition
.IsComposing()) {
5447 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5448 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5449 "no active composition",
5451 return E_UNEXPECTED
;
5453 if (mComposition
.mView
!= pComposition
) {
5454 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5455 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5456 "different composition view specified",
5458 return E_UNEXPECTED
;
5461 // pRangeNew is null when the update is not complete
5463 MaybeDispatchKeyboardEventAsProcessedByIME();
5465 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5466 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5467 "destroyed during dispatching a keyboard event",
5471 PendingAction
* action
= LastOrNewPendingCompositionUpdate();
5472 action
->mIncomplete
= true;
5473 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5474 ("0x%p TSFTextStore::OnUpdateComposition() succeeded but "
5480 HRESULT hr
= RestartCompositionIfNecessary(pRangeNew
);
5482 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5483 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5484 "RestartCompositionIfNecessary() failure",
5489 hr
= RecordCompositionUpdateAction();
5491 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5492 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5493 "RecordCompositionUpdateAction() failure",
5498 if (MOZ_LOG_TEST(sTextStoreLog
, LogLevel::Info
)) {
5499 Selection
& selectionForTSF
= SelectionForTSFRef();
5500 if (selectionForTSF
.IsDirty()) {
5501 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5502 ("0x%p TSFTextStore::OnUpdateComposition() FAILED due to "
5503 "SelectionForTSFRef() failure",
5507 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5508 ("0x%p TSFTextStore::OnUpdateComposition() succeeded: "
5509 "mComposition={ mStart=%ld, mString=\"%s\" }, "
5510 "SelectionForTSFRef()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }",
5511 this, mComposition
.mStart
,
5512 GetEscapedUTF8String(mComposition
.mString
).get(),
5513 selectionForTSF
.StartOffset(), selectionForTSF
.EndOffset(),
5514 GetActiveSelEndName(selectionForTSF
.ActiveSelEnd())));
5520 TSFTextStore::OnEndComposition(ITfCompositionView
* pComposition
) {
5521 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5522 ("0x%p TSFTextStore::OnEndComposition(pComposition=0x%p), "
5523 "mComposition={ mView=0x%p, mString=\"%s\" }",
5524 this, pComposition
, mComposition
.mView
.get(),
5525 GetEscapedUTF8String(mComposition
.mString
).get()));
5527 AutoPendingActionAndContentFlusher
flusher(this);
5529 if (!mComposition
.IsComposing()) {
5530 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5531 ("0x%p TSFTextStore::OnEndComposition() FAILED due to "
5532 "no active composition",
5534 return E_UNEXPECTED
;
5537 if (mComposition
.mView
!= pComposition
) {
5538 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5539 ("0x%p TSFTextStore::OnEndComposition() FAILED due to "
5540 "different composition view specified",
5542 return E_UNEXPECTED
;
5545 HRESULT hr
= RecordCompositionEndAction();
5547 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5548 ("0x%p TSFTextStore::OnEndComposition() FAILED due to "
5549 "RecordCompositionEndAction() failure",
5554 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5555 ("0x%p TSFTextStore::OnEndComposition(), succeeded", this));
5560 TSFTextStore::AdviseMouseSink(ITfRangeACP
* range
, ITfMouseSink
* pSink
,
5562 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5563 ("0x%p TSFTextStore::AdviseMouseSink(range=0x%p, pSink=0x%p, "
5565 this, range
, pSink
, pdwCookie
));
5568 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5569 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to the "
5570 "pdwCookie is null",
5572 return E_INVALIDARG
;
5574 // Initialize the result with invalid cookie for safety.
5575 *pdwCookie
= MouseTracker::kInvalidCookie
;
5578 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5579 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to the "
5582 return E_INVALIDARG
;
5585 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5586 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to the "
5589 return E_INVALIDARG
;
5592 // Looking for an unusing tracker.
5593 MouseTracker
* tracker
= nullptr;
5594 for (size_t i
= 0; i
< mMouseTrackers
.Length(); i
++) {
5595 if (mMouseTrackers
[i
].IsUsing()) {
5598 tracker
= &mMouseTrackers
[i
];
5600 // If there is no unusing tracker, create new one.
5601 // XXX Should we make limitation of the number of installs?
5603 tracker
= mMouseTrackers
.AppendElement();
5604 HRESULT hr
= tracker
->Init(this);
5606 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5607 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to "
5608 "failure of MouseTracker::Init()",
5613 HRESULT hr
= tracker
->AdviseSink(this, range
, pSink
);
5615 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5616 ("0x%p TSFTextStore::AdviseMouseSink() FAILED due to failure "
5617 "of MouseTracker::Init()",
5621 *pdwCookie
= tracker
->Cookie();
5622 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5623 ("0x%p TSFTextStore::AdviseMouseSink(), succeeded, "
5630 TSFTextStore::UnadviseMouseSink(DWORD dwCookie
) {
5632 sTextStoreLog
, LogLevel::Info
,
5633 ("0x%p TSFTextStore::UnadviseMouseSink(dwCookie=%d)", this, dwCookie
));
5634 if (dwCookie
== MouseTracker::kInvalidCookie
) {
5635 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5636 ("0x%p TSFTextStore::UnadviseMouseSink() FAILED due to "
5637 "the cookie is invalid value",
5639 return E_INVALIDARG
;
5641 // The cookie value must be an index of mMouseTrackers.
5642 // We can use this shortcut for now.
5643 if (static_cast<size_t>(dwCookie
) >= mMouseTrackers
.Length()) {
5644 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5645 ("0x%p TSFTextStore::UnadviseMouseSink() FAILED due to "
5646 "the cookie is too large value",
5648 return E_INVALIDARG
;
5650 MouseTracker
& tracker
= mMouseTrackers
[dwCookie
];
5651 if (!tracker
.IsUsing()) {
5652 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5653 ("0x%p TSFTextStore::UnadviseMouseSink() FAILED due to "
5654 "the found tracker uninstalled already",
5656 return E_INVALIDARG
;
5658 tracker
.UnadviseSink();
5659 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5660 ("0x%p TSFTextStore::UnadviseMouseSink(), succeeded", this));
5665 nsresult
TSFTextStore::OnFocusChange(bool aGotFocus
,
5666 nsWindowBase
* aFocusedWidget
,
5667 const InputContext
& aContext
) {
5668 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5669 (" TSFTextStore::OnFocusChange(aGotFocus=%s, "
5670 "aFocusedWidget=0x%p, aContext=%s), "
5671 "sThreadMgr=0x%p, sEnabledTextStore=0x%p",
5672 GetBoolName(aGotFocus
), aFocusedWidget
,
5673 mozilla::ToString(aContext
).c_str(), sThreadMgr
.get(),
5674 sEnabledTextStore
.get()));
5676 if (NS_WARN_IF(!IsInTSFMode())) {
5677 return NS_ERROR_NOT_AVAILABLE
;
5680 RefPtr
<ITfDocumentMgr
> prevFocusedDocumentMgr
;
5681 bool hasFocus
= ThinksHavingFocus();
5682 RefPtr
<TSFTextStore
> oldTextStore
= sEnabledTextStore
.forget();
5684 // If currently oldTextStore still has focus, notifies TSF of losing focus.
5686 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
5687 DebugOnly
<HRESULT
> hr
= threadMgr
->AssociateFocus(
5688 oldTextStore
->mWidget
->GetWindowHandle(), nullptr,
5689 getter_AddRefs(prevFocusedDocumentMgr
));
5690 NS_ASSERTION(SUCCEEDED(hr
), "Disassociating focus failed");
5691 NS_ASSERTION(prevFocusedDocumentMgr
== oldTextStore
->mDocumentMgr
,
5692 "different documentMgr has been associated with the window");
5695 // Even if there was a focused TextStore, we won't use it with new focused
5696 // editor. So, release it now.
5698 oldTextStore
->Destroy();
5701 if (NS_WARN_IF(!sThreadMgr
)) {
5702 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5703 (" TSFTextStore::OnFocusChange() FAILED, due to "
5704 "sThreadMgr being destroyed during calling "
5705 "ITfThreadMgr::AssociateFocus()"));
5706 return NS_ERROR_FAILURE
;
5708 if (NS_WARN_IF(sEnabledTextStore
)) {
5710 sTextStoreLog
, LogLevel::Error
,
5711 (" TSFTextStore::OnFocusChange() FAILED, due to "
5712 "nested event handling has created another focused TextStore during "
5713 "calling ITfThreadMgr::AssociateFocus()"));
5714 return NS_ERROR_FAILURE
;
5717 // If this is a notification of blur, move focus to the dummy document
5719 if (!aGotFocus
|| !aContext
.mIMEState
.IsEditable()) {
5720 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
5721 RefPtr
<ITfDocumentMgr
> disabledDocumentMgr
= sDisabledDocumentMgr
;
5722 HRESULT hr
= threadMgr
->SetFocus(disabledDocumentMgr
);
5723 if (NS_WARN_IF(FAILED(hr
))) {
5724 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5725 (" TSFTextStore::OnFocusChange() FAILED due to "
5726 "ITfThreadMgr::SetFocus() failure"));
5727 return NS_ERROR_FAILURE
;
5732 // If an editor is getting focus, create new TextStore and set focus.
5733 if (NS_WARN_IF(!CreateAndSetFocus(aFocusedWidget
, aContext
))) {
5734 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5735 (" TSFTextStore::OnFocusChange() FAILED due to "
5736 "ITfThreadMgr::CreateAndSetFocus() failure"));
5737 return NS_ERROR_FAILURE
;
5743 void TSFTextStore::EnsureToDestroyAndReleaseEnabledTextStoreIf(
5744 RefPtr
<TSFTextStore
>& aTextStore
) {
5745 aTextStore
->Destroy();
5746 if (sEnabledTextStore
== aTextStore
) {
5747 sEnabledTextStore
= nullptr;
5749 aTextStore
= nullptr;
5753 bool TSFTextStore::CreateAndSetFocus(nsWindowBase
* aFocusedWidget
,
5754 const InputContext
& aContext
) {
5755 // TSF might do something which causes that we need to access static methods
5756 // of TSFTextStore. At that time, sEnabledTextStore may be necessary.
5757 // So, we should set sEnabledTextStore directly.
5758 RefPtr
<TSFTextStore
> textStore
= new TSFTextStore();
5759 sEnabledTextStore
= textStore
;
5760 if (NS_WARN_IF(!textStore
->Init(aFocusedWidget
, aContext
))) {
5761 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5762 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5763 "TSFTextStore::Init() failure"));
5764 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5767 RefPtr
<ITfDocumentMgr
> newDocMgr
= textStore
->mDocumentMgr
;
5768 if (NS_WARN_IF(!newDocMgr
)) {
5769 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5770 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5771 "invalid TSFTextStore::mDocumentMgr"));
5772 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5775 if (aContext
.mIMEState
.mEnabled
== IMEState::PASSWORD
) {
5776 MarkContextAsKeyboardDisabled(textStore
->mContext
);
5777 RefPtr
<ITfContext
> topContext
;
5778 newDocMgr
->GetTop(getter_AddRefs(topContext
));
5779 if (topContext
&& topContext
!= textStore
->mContext
) {
5780 MarkContextAsKeyboardDisabled(topContext
);
5785 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
5786 hr
= threadMgr
->SetFocus(newDocMgr
);
5788 if (NS_WARN_IF(FAILED(hr
))) {
5789 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5790 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5791 "ITfTheadMgr::SetFocus() failure"));
5792 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5795 if (NS_WARN_IF(!sThreadMgr
)) {
5796 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5797 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5798 "sThreadMgr being destroyed during calling "
5799 "ITfTheadMgr::SetFocus()"));
5800 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5803 if (NS_WARN_IF(sEnabledTextStore
!= textStore
)) {
5804 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5805 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5806 "creating TextStore has lost focus during calling "
5807 "ITfThreadMgr::SetFocus()"));
5808 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5812 // Use AssociateFocus() for ensuring that any native focus event
5813 // never steal focus from our documentMgr.
5814 RefPtr
<ITfDocumentMgr
> prevFocusedDocumentMgr
;
5815 hr
= threadMgr
->AssociateFocus(aFocusedWidget
->GetWindowHandle(), newDocMgr
,
5816 getter_AddRefs(prevFocusedDocumentMgr
));
5817 if (NS_WARN_IF(FAILED(hr
))) {
5818 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5819 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5820 "ITfTheadMgr::AssociateFocus() failure"));
5821 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5824 if (NS_WARN_IF(!sThreadMgr
)) {
5825 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5826 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5827 "sThreadMgr being destroyed during calling "
5828 "ITfTheadMgr::AssociateFocus()"));
5829 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5832 if (NS_WARN_IF(sEnabledTextStore
!= textStore
)) {
5833 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5834 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5835 "creating TextStore has lost focus during calling "
5836 "ITfTheadMgr::AssociateFocus()"));
5837 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5841 if (textStore
->mSink
) {
5842 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
5843 (" TSFTextStore::CreateAndSetFocus(), calling "
5844 "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE) for 0x%p...",
5846 RefPtr
<ITextStoreACPSink
> sink
= textStore
->mSink
;
5847 sink
->OnLayoutChange(TS_LC_CREATE
, TEXTSTORE_DEFAULT_VIEW
);
5848 if (NS_WARN_IF(sEnabledTextStore
!= textStore
)) {
5849 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5850 (" TSFTextStore::CreateAndSetFocus() FAILED due to "
5851 "creating TextStore has lost focus during calling "
5852 "ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE)"));
5853 EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore
);
5861 IMENotificationRequests
TSFTextStore::GetIMENotificationRequests() {
5862 if (!sEnabledTextStore
|| NS_WARN_IF(!sEnabledTextStore
->mDocumentMgr
)) {
5863 // If there is no active text store, we don't need any notifications
5864 // since there is no sink which needs notifications.
5865 return IMENotificationRequests();
5868 // Otherwise, requests all notifications since even if some of them may not
5869 // be required by the sink of active TIP, active TIP may be changed and
5870 // other TIPs may need all notifications.
5871 // Note that Windows temporarily steal focus from active window if the main
5872 // process which created the window becomes busy. In this case, we shouldn't
5873 // commit composition since user may want to continue to compose the
5874 // composition after becoming not busy. Therefore, we need notifications
5875 // even during deactive.
5876 // Be aware, we don't need to check actual focused text store. For example,
5877 // MS-IME for Japanese handles focus messages by themselves and sets focused
5878 // text store to nullptr when the process is being inactivated. However,
5879 // we still need to reuse sEnabledTextStore if the process is activated and
5880 // focused element isn't changed. Therefore, if sEnabledTextStore isn't
5881 // nullptr, we need to keep notifying the sink even when it is not focused
5882 // text store for the thread manager.
5883 return IMENotificationRequests(
5884 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
5885 IMENotificationRequests::NOTIFY_POSITION_CHANGE
|
5886 IMENotificationRequests::NOTIFY_MOUSE_BUTTON_EVENT_ON_CHAR
|
5887 IMENotificationRequests::NOTIFY_DURING_DEACTIVE
);
5890 nsresult
TSFTextStore::OnTextChangeInternal(
5891 const IMENotification
& aIMENotification
) {
5892 const TextChangeDataBase
& textChangeData
= aIMENotification
.mTextChangeData
;
5894 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5895 ("0x%p TSFTextStore::OnTextChangeInternal(aIMENotification={ "
5896 "mMessage=0x%08X, mTextChangeData=%s }), "
5897 "mDestroyed=%s, mSink=0x%p, mSinkMask=%s, "
5898 "mComposition.IsComposing()=%s",
5899 this, aIMENotification
.mMessage
,
5900 mozilla::ToString(textChangeData
).c_str(), GetBoolName(mDestroyed
),
5901 mSink
.get(), GetSinkMaskNameStr(mSinkMask
).get(),
5902 GetBoolName(mComposition
.IsComposing())));
5905 // If this instance is already destroyed, we shouldn't notify TSF of any
5910 mDeferNotifyingTSF
= false;
5912 // Different from selection change, we don't modify anything with text
5913 // change data. Therefore, if neither TSF not TIP wants text change
5914 // notifications, we don't need to store the changes.
5915 if (!mSink
|| !(mSinkMask
& TS_AS_TEXT_CHANGE
)) {
5919 // Merge any text change data even if it's caused by composition.
5920 mPendingTextChangeData
.MergeWith(textChangeData
);
5922 MaybeFlushPendingNotifications();
5927 void TSFTextStore::NotifyTSFOfTextChange() {
5928 MOZ_ASSERT(!mDestroyed
);
5929 MOZ_ASSERT(!IsReadLocked());
5930 MOZ_ASSERT(!mComposition
.IsComposing());
5931 MOZ_ASSERT(mPendingTextChangeData
.IsValid());
5933 // If the text changes are caused only by composition, we don't need to
5934 // notify TSF of the text changes.
5935 if (mPendingTextChangeData
.mCausedOnlyByComposition
) {
5936 mPendingTextChangeData
.Clear();
5940 // First, forget cached selection.
5941 mSelectionForTSF
.MarkDirty();
5943 // For making it safer, we should check if there is a valid sink to receive
5944 // text change notification.
5945 if (NS_WARN_IF(!mSink
) || NS_WARN_IF(!(mSinkMask
& TS_AS_TEXT_CHANGE
))) {
5946 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5947 ("0x%p TSFTextStore::NotifyTSFOfTextChange() FAILED due to "
5948 "mSink is not ready to call ITextStoreACPSink::OnTextChange()...",
5950 mPendingTextChangeData
.Clear();
5954 if (NS_WARN_IF(!mPendingTextChangeData
.IsInInt32Range())) {
5955 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
5956 ("0x%p TSFTextStore::NotifyTSFOfTextChange() FAILED due to "
5957 "offset is too big for calling "
5958 "ITextStoreACPSink::OnTextChange()...",
5960 mPendingTextChangeData
.Clear();
5964 TS_TEXTCHANGE textChange
;
5965 textChange
.acpStart
= static_cast<LONG
>(mPendingTextChangeData
.mStartOffset
);
5966 textChange
.acpOldEnd
=
5967 static_cast<LONG
>(mPendingTextChangeData
.mRemovedEndOffset
);
5968 textChange
.acpNewEnd
=
5969 static_cast<LONG
>(mPendingTextChangeData
.mAddedEndOffset
);
5970 mPendingTextChangeData
.Clear();
5973 sTextStoreLog
, LogLevel::Info
,
5974 ("0x%p TSFTextStore::NotifyTSFOfTextChange(), calling "
5975 "ITextStoreACPSink::OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
5976 "acpNewEnd=%ld })...",
5977 this, textChange
.acpStart
, textChange
.acpOldEnd
, textChange
.acpNewEnd
));
5978 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
5979 sink
->OnTextChange(0, &textChange
);
5982 nsresult
TSFTextStore::OnSelectionChangeInternal(
5983 const IMENotification
& aIMENotification
) {
5984 const SelectionChangeDataBase
& selectionChangeData
=
5985 aIMENotification
.mSelectionChangeData
;
5986 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
5987 ("0x%p TSFTextStore::OnSelectionChangeInternal("
5988 "aIMENotification={ mSelectionChangeData=%s }), mDestroyed=%s, "
5989 "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
5990 "mComposition.IsComposing()=%s",
5991 this, mozilla::ToString(selectionChangeData
).c_str(),
5992 GetBoolName(mDestroyed
), mSink
.get(),
5993 GetSinkMaskNameStr(mSinkMask
).get(),
5994 GetBoolName(mIsRecordingActionsWithoutLock
),
5995 GetBoolName(mComposition
.IsComposing())));
5998 // If this instance is already destroyed, we shouldn't notify TSF of any
6003 mDeferNotifyingTSF
= false;
6005 // Assign the new selection change data to the pending selection change data
6006 // because only the latest selection data is necessary.
6007 // Note that this is necessary to update mSelectionForTSF. Therefore, even if
6008 // neither TSF nor TIP wants selection change notifications, we need to
6009 // store the selection information.
6010 mPendingSelectionChangeData
.Assign(selectionChangeData
);
6012 // Flush remaining pending notifications here if it's possible.
6013 MaybeFlushPendingNotifications();
6015 // If we're available, we should create native caret instead of IMEHandler
6016 // because we may have some cache to do it.
6017 // Note that if we have composition, we'll notified composition-updated
6018 // later so that we don't need to create native caret in such case.
6019 if (!IsHandlingComposition() && IMEHandler::NeedsToCreateNativeCaret()) {
6020 CreateNativeCaret();
6026 void TSFTextStore::NotifyTSFOfSelectionChange() {
6027 MOZ_ASSERT(!mDestroyed
);
6028 MOZ_ASSERT(!IsReadLocked());
6029 MOZ_ASSERT(!mComposition
.IsComposing());
6030 MOZ_ASSERT(mPendingSelectionChangeData
.IsValid());
6032 // If selection range isn't actually changed, we don't need to notify TSF
6033 // of this selection change.
6034 if (!mSelectionForTSF
.SetSelection(
6035 mPendingSelectionChangeData
.mOffset
,
6036 mPendingSelectionChangeData
.Length(),
6037 mPendingSelectionChangeData
.mReversed
,
6038 mPendingSelectionChangeData
.GetWritingMode())) {
6039 mPendingSelectionChangeData
.Clear();
6040 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6041 ("0x%p TSFTextStore::NotifyTSFOfSelectionChange(), "
6042 "selection isn't actually changed.",
6047 mPendingSelectionChangeData
.Clear();
6049 if (!mSink
|| !(mSinkMask
& TS_AS_SEL_CHANGE
)) {
6053 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6054 ("0x%p TSFTextStore::NotifyTSFOfSelectionChange(), calling "
6055 "ITextStoreACPSink::OnSelectionChange()...",
6057 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6058 sink
->OnSelectionChange();
6061 nsresult
TSFTextStore::OnLayoutChangeInternal() {
6063 // If this instance is already destroyed, we shouldn't notify TSF of any
6068 NS_ENSURE_TRUE(mContext
, NS_ERROR_FAILURE
);
6069 NS_ENSURE_TRUE(mSink
, NS_ERROR_FAILURE
);
6071 mDeferNotifyingTSF
= false;
6073 nsresult rv
= NS_OK
;
6075 // We need to notify TSF of layout change even if the document is locked.
6076 // So, don't use MaybeFlushPendingNotifications() for flushing pending
6078 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6079 ("0x%p TSFTextStore::OnLayoutChangeInternal(), calling "
6080 "NotifyTSFOfLayoutChange()...",
6082 if (NS_WARN_IF(!NotifyTSFOfLayoutChange())) {
6083 rv
= NS_ERROR_FAILURE
;
6086 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6087 ("0x%p TSFTextStore::OnLayoutChangeInternal(), calling "
6088 "MaybeFlushPendingNotifications()...",
6090 MaybeFlushPendingNotifications();
6095 bool TSFTextStore::NotifyTSFOfLayoutChange() {
6096 MOZ_ASSERT(!mDestroyed
);
6098 // If we're waiting a query of layout information from TIP, it means that
6099 // we've returned TS_E_NOLAYOUT error.
6100 bool returnedNoLayoutError
= mHasReturnedNoLayoutError
|| mWaitingQueryLayout
;
6102 // If we returned TS_E_NOLAYOUT, TIP should query the computed layout again.
6103 mWaitingQueryLayout
= returnedNoLayoutError
;
6105 // For avoiding to call this method again at unlocking the document during
6106 // calls of OnLayoutChange(), reset mHasReturnedNoLayoutError.
6107 mHasReturnedNoLayoutError
= false;
6109 // Now, layout has been computed. We should notify mContentForTSF for
6110 // making GetTextExt() and GetACPFromPoint() not return TS_E_NOLAYOUT.
6111 if (mContentForTSF
.IsInitialized()) {
6112 mContentForTSF
.OnLayoutChanged();
6115 if (IMEHandler::NeedsToCreateNativeCaret()) {
6116 // If we're available, we should create native caret instead of IMEHandler
6117 // because we may have some cache to do it.
6118 CreateNativeCaret();
6120 // Now, the caret position is different from ours. Destroy the native caret
6121 // if we've create it only for GetTextExt().
6122 IMEHandler::MaybeDestroyNativeCaret();
6125 // This method should return true if either way succeeds.
6129 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6130 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6131 "calling ITextStoreACPSink::OnLayoutChange()...",
6133 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6134 HRESULT hr
= sink
->OnLayoutChange(TS_LC_CHANGE
, TEXTSTORE_DEFAULT_VIEW
);
6135 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6136 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6137 "called ITextStoreACPSink::OnLayoutChange()",
6139 ret
= SUCCEEDED(hr
);
6142 // The layout change caused by composition string change should cause
6143 // calling ITfContextOwnerServices::OnLayoutChange() too.
6144 if (returnedNoLayoutError
&& mContext
) {
6145 RefPtr
<ITfContextOwnerServices
> service
;
6146 mContext
->QueryInterface(IID_ITfContextOwnerServices
,
6147 getter_AddRefs(service
));
6149 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6150 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6151 "calling ITfContextOwnerServices::OnLayoutChange()...",
6153 HRESULT hr
= service
->OnLayoutChange();
6154 ret
= ret
&& SUCCEEDED(hr
);
6155 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6156 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6157 "called ITfContextOwnerServices::OnLayoutChange()",
6162 if (!mWidget
|| mWidget
->Destroyed()) {
6163 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6164 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6165 "the widget is destroyed during calling OnLayoutChange()",
6171 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6172 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6173 "the TSFTextStore instance is destroyed during calling "
6179 // If we returned TS_E_NOLAYOUT again, we need another call of
6180 // OnLayoutChange() later. So, let's wait a query from TIP.
6181 if (mHasReturnedNoLayoutError
) {
6182 mWaitingQueryLayout
= true;
6185 if (!mWaitingQueryLayout
) {
6186 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6187 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6188 "succeeded notifying TIP of our layout change",
6193 // If we believe that TIP needs to retry to retrieve our layout information
6194 // later, we should call it with ::PostMessage() hack.
6195 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6196 ("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
6197 "posing MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE for calling "
6198 "OnLayoutChange() again...",
6200 ::PostMessage(mWidget
->GetWindowHandle(), MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE
,
6201 reinterpret_cast<WPARAM
>(this), 0);
6206 void TSFTextStore::NotifyTSFOfLayoutChangeAgain() {
6207 // Don't notify TSF of layout change after destroyed.
6209 mWaitingQueryLayout
= false;
6213 // Before preforming this method, TIP has accessed our layout information by
6214 // itself. In such case, we don't need to call OnLayoutChange() anymore.
6215 if (!mWaitingQueryLayout
) {
6219 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6220 ("0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6221 "calling NotifyTSFOfLayoutChange()...",
6223 NotifyTSFOfLayoutChange();
6225 // If TIP didn't retrieved our layout information during a call of
6226 // NotifyTSFOfLayoutChange(), it means that the TIP already gave up to
6227 // retry to retrieve layout information or doesn't necessary it anymore.
6228 // But don't forget that the call may have caused returning TS_E_NOLAYOUT
6229 // error again. In such case we still need to call OnLayoutChange() later.
6230 if (!mHasReturnedNoLayoutError
&& mWaitingQueryLayout
) {
6231 mWaitingQueryLayout
= false;
6232 MOZ_LOG(sTextStoreLog
, LogLevel::Warning
,
6233 ("0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6234 "called NotifyTSFOfLayoutChange() but TIP didn't retry to "
6235 "retrieve the layout information",
6238 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6239 ("0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
6240 "called NotifyTSFOfLayoutChange()",
6245 nsresult
TSFTextStore::OnUpdateCompositionInternal() {
6246 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6247 ("0x%p TSFTextStore::OnUpdateCompositionInternal(), "
6248 "mDestroyed=%s, mDeferNotifyingTSF=%s",
6249 this, GetBoolName(mDestroyed
), GetBoolName(mDeferNotifyingTSF
)));
6251 // There are nothing to do after destroyed.
6256 // Update cached data now because all pending events have been handled now.
6257 mContentForTSF
.OnCompositionEventsHandled();
6259 // If composition is completely finished both in TSF/TIP and the focused
6260 // editor which may be in a remote process, we can clear the cache and don't
6261 // have it until starting next composition.
6262 if (!mComposition
.IsComposing() && !IsHandlingComposition()) {
6263 mDeferClearingContentForTSF
= false;
6265 mDeferNotifyingTSF
= false;
6266 MaybeFlushPendingNotifications();
6268 // If we're available, we should create native caret instead of IMEHandler
6269 // because we may have some cache to do it.
6270 if (IMEHandler::NeedsToCreateNativeCaret()) {
6271 CreateNativeCaret();
6277 nsresult
TSFTextStore::OnMouseButtonEventInternal(
6278 const IMENotification
& aIMENotification
) {
6280 // If this instance is already destroyed, we shouldn't notify TSF of any
6285 if (mMouseTrackers
.IsEmpty()) {
6289 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6290 ("0x%p TSFTextStore::OnMouseButtonEventInternal("
6291 "aIMENotification={ mEventMessage=%s, mOffset=%u, mCursorPos={ "
6292 "mX=%d, mY=%d }, mCharRect={ mX=%d, mY=%d, mWidth=%d, mHeight=%d }, "
6293 "mButton=%s, mButtons=%s, mModifiers=%s })",
6294 this, ToChar(aIMENotification
.mMouseButtonEventData
.mEventMessage
),
6295 aIMENotification
.mMouseButtonEventData
.mOffset
,
6296 aIMENotification
.mMouseButtonEventData
.mCursorPos
.mX
,
6297 aIMENotification
.mMouseButtonEventData
.mCursorPos
.mY
,
6298 aIMENotification
.mMouseButtonEventData
.mCharRect
.mX
,
6299 aIMENotification
.mMouseButtonEventData
.mCharRect
.mY
,
6300 aIMENotification
.mMouseButtonEventData
.mCharRect
.mWidth
,
6301 aIMENotification
.mMouseButtonEventData
.mCharRect
.mHeight
,
6302 GetMouseButtonName(aIMENotification
.mMouseButtonEventData
.mButton
),
6303 GetMouseButtonsName(aIMENotification
.mMouseButtonEventData
.mButtons
)
6305 GetModifiersName(aIMENotification
.mMouseButtonEventData
.mModifiers
)
6308 uint32_t offset
= aIMENotification
.mMouseButtonEventData
.mOffset
;
6309 nsIntRect charRect
=
6310 aIMENotification
.mMouseButtonEventData
.mCharRect
.AsIntRect();
6311 nsIntPoint cursorPos
=
6312 aIMENotification
.mMouseButtonEventData
.mCursorPos
.AsIntPoint();
6314 if (charRect
.Width() > 0) {
6315 int32_t cursorXInChar
= cursorPos
.x
- charRect
.X();
6316 quadrant
= cursorXInChar
* 4 / charRect
.Width();
6317 quadrant
= (quadrant
+ 2) % 4;
6319 ULONG edge
= quadrant
< 2 ? offset
+ 1 : offset
;
6320 DWORD buttonStatus
= 0;
6322 aIMENotification
.mMouseButtonEventData
.mEventMessage
== eMouseUp
;
6324 switch (aIMENotification
.mMouseButtonEventData
.mButton
) {
6325 case MouseButton::ePrimary
:
6326 buttonStatus
= MK_LBUTTON
;
6328 case MouseButton::eMiddle
:
6329 buttonStatus
= MK_MBUTTON
;
6331 case MouseButton::eSecondary
:
6332 buttonStatus
= MK_RBUTTON
;
6336 if (aIMENotification
.mMouseButtonEventData
.mModifiers
& MODIFIER_CONTROL
) {
6337 buttonStatus
|= MK_CONTROL
;
6339 if (aIMENotification
.mMouseButtonEventData
.mModifiers
& MODIFIER_SHIFT
) {
6340 buttonStatus
|= MK_SHIFT
;
6342 for (size_t i
= 0; i
< mMouseTrackers
.Length(); i
++) {
6343 MouseTracker
& tracker
= mMouseTrackers
[i
];
6344 if (!tracker
.IsUsing() || !tracker
.InRange(offset
)) {
6347 if (tracker
.OnMouseButtonEvent(edge
- tracker
.RangeStart(), quadrant
,
6349 return NS_SUCCESS_EVENT_CONSUMED
;
6355 void TSFTextStore::CreateNativeCaret() {
6356 MOZ_ASSERT(!IMEHandler::IsA11yHandlingNativeCaret());
6358 IMEHandler::MaybeDestroyNativeCaret();
6360 // Don't create native caret after destroyed.
6365 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6366 ("0x%p TSFTextStore::CreateNativeCaret(), "
6367 "mComposition.IsComposing()=%s",
6368 this, GetBoolName(mComposition
.IsComposing())));
6370 Selection
& selectionForTSF
= SelectionForTSFRef();
6371 if (selectionForTSF
.IsDirty()) {
6372 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6373 ("0x%p TSFTextStore::CreateNativeCaret() FAILED due to "
6374 "SelectionForTSFRef() failure",
6379 WidgetQueryContentEvent
queryCaretRect(true, eQueryCaretRect
, mWidget
);
6380 mWidget
->InitEvent(queryCaretRect
);
6382 WidgetQueryContentEvent::Options options
;
6383 // XXX If this is called without composition and the selection isn't
6384 // collapsed, is it OK?
6385 int64_t caretOffset
= selectionForTSF
.MaxOffset();
6386 if (mComposition
.IsComposing()) {
6387 // If there is a composition, use insertion point relative query for
6388 // deciding caret position because composition might be at different
6389 // position where TSFTextStore believes it at.
6390 options
.mRelativeToInsertionPoint
= true;
6391 caretOffset
-= mComposition
.mStart
;
6392 } else if (!CanAccessActualContentDirectly()) {
6393 // If TSF/TIP cannot access actual content directly, there may be pending
6394 // text and/or selection changes which have not been notified TSF yet.
6395 // Therefore, we should use relative to insertion point query since
6396 // TSF/TIP computes the offset from the cached selection.
6397 options
.mRelativeToInsertionPoint
= true;
6398 caretOffset
-= mSelectionForTSF
.StartOffset();
6400 queryCaretRect
.InitForQueryCaretRect(caretOffset
, options
);
6402 DispatchEvent(queryCaretRect
);
6403 if (NS_WARN_IF(!queryCaretRect
.mSucceeded
)) {
6404 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6405 ("0x%p TSFTextStore::CreateNativeCaret() FAILED due to "
6406 "eQueryCaretRect failure (offset=%d)",
6407 this, caretOffset
));
6411 if (!IMEHandler::CreateNativeCaret(static_cast<nsWindow
*>(mWidget
.get()),
6412 queryCaretRect
.mReply
.mRect
)) {
6413 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6414 ("0x%p TSFTextStore::CreateNativeCaret() FAILED due to "
6415 "IMEHandler::CreateNativeCaret() failure",
6421 void TSFTextStore::CommitCompositionInternal(bool aDiscard
) {
6422 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6423 ("0x%p TSFTextStore::CommitCompositionInternal(aDiscard=%s), "
6424 "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, "
6425 "mComposition.mString=\"%s\"",
6426 this, GetBoolName(aDiscard
), mSink
.get(), mContext
.get(),
6427 mComposition
.mView
.get(),
6428 GetEscapedUTF8String(mComposition
.mString
).get()));
6430 // If the document is locked, TSF will fail to commit composition since
6431 // TSF needs another document lock. So, let's put off the request.
6432 // Note that TextComposition will commit composition in the focused editor
6433 // with the latest composition string for web apps and waits asynchronous
6434 // committing messages. Therefore, we can and need to perform this
6436 if (IsReadLocked()) {
6437 if (mDeferCommittingComposition
|| mDeferCancellingComposition
) {
6438 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6439 ("0x%p TSFTextStore::CommitCompositionInternal(), "
6440 "does nothing because already called and waiting unlock...",
6445 mDeferCancellingComposition
= true;
6447 mDeferCommittingComposition
= true;
6449 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6450 ("0x%p TSFTextStore::CommitCompositionInternal(), "
6451 "putting off to request to %s composition after unlocking the "
6453 this, aDiscard
? "cancel" : "commit"));
6457 if (mComposition
.IsComposing() && aDiscard
) {
6458 LONG endOffset
= mComposition
.EndOffset();
6459 mComposition
.mString
.Truncate(0);
6460 // Note that don't notify TSF of text change after this is destroyed.
6461 if (mSink
&& !mDestroyed
) {
6462 TS_TEXTCHANGE textChange
;
6463 textChange
.acpStart
= mComposition
.mStart
;
6464 textChange
.acpOldEnd
= endOffset
;
6465 textChange
.acpNewEnd
= mComposition
.mStart
;
6466 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6467 ("0x%p TSFTextStore::CommitCompositionInternal(), calling"
6468 "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
6469 "acpNewEnd=%ld })...",
6470 this, textChange
.acpStart
, textChange
.acpOldEnd
,
6471 textChange
.acpNewEnd
));
6472 RefPtr
<ITextStoreACPSink
> sink
= mSink
;
6473 sink
->OnTextChange(0, &textChange
);
6476 // Terminate two contexts, the base context (mContext) and the top
6477 // if the top context is not the same as the base context
6478 RefPtr
<ITfContext
> context
= mContext
;
6481 RefPtr
<ITfContextOwnerCompositionServices
> services
;
6482 context
->QueryInterface(IID_ITfContextOwnerCompositionServices
,
6483 getter_AddRefs(services
));
6485 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6486 ("0x%p TSFTextStore::CommitCompositionInternal(), "
6487 "requesting TerminateComposition() for the context 0x%p...",
6488 this, context
.get()));
6489 services
->TerminateComposition(nullptr);
6492 if (context
!= mContext
) break;
6493 if (mDocumentMgr
) mDocumentMgr
->GetTop(getter_AddRefs(context
));
6494 } while (context
!= mContext
);
6497 static bool GetCompartment(IUnknown
* pUnk
, const GUID
& aID
,
6498 ITfCompartment
** aCompartment
) {
6499 if (!pUnk
) return false;
6501 RefPtr
<ITfCompartmentMgr
> compMgr
;
6502 pUnk
->QueryInterface(IID_ITfCompartmentMgr
, getter_AddRefs(compMgr
));
6503 if (!compMgr
) return false;
6505 return SUCCEEDED(compMgr
->GetCompartment(aID
, aCompartment
)) &&
6506 (*aCompartment
) != nullptr;
6510 void TSFTextStore::SetIMEOpenState(bool aState
) {
6511 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6512 ("TSFTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState
)));
6518 RefPtr
<ITfCompartment
> comp
= GetCompartmentForOpenClose();
6519 if (NS_WARN_IF(!comp
)) {
6520 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6521 (" TSFTextStore::SetIMEOpenState() FAILED due to"
6522 "no compartment available"));
6528 variant
.lVal
= aState
;
6529 HRESULT hr
= comp
->SetValue(sClientId
, &variant
);
6530 if (NS_WARN_IF(FAILED(hr
))) {
6531 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6532 (" TSFTextStore::SetIMEOpenState() FAILED due to "
6533 "ITfCompartment::SetValue() failure, hr=0x%08X",
6537 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6538 (" TSFTextStore::SetIMEOpenState(), setting "
6539 "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...",
6544 bool TSFTextStore::GetIMEOpenState() {
6549 RefPtr
<ITfCompartment
> comp
= GetCompartmentForOpenClose();
6550 if (NS_WARN_IF(!comp
)) {
6555 ::VariantInit(&variant
);
6556 HRESULT hr
= comp
->GetValue(&variant
);
6557 if (NS_WARN_IF(FAILED(hr
))) {
6558 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6559 ("TSFTextStore::GetIMEOpenState() FAILED due to "
6560 "ITfCompartment::GetValue() failure, hr=0x%08X",
6564 // Until IME is open in this process, the result may be empty.
6565 if (variant
.vt
== VT_EMPTY
) {
6568 if (NS_WARN_IF(variant
.vt
!= VT_I4
)) {
6569 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6570 ("TSFTextStore::GetIMEOpenState() FAILED due to "
6571 "invalid result of ITfCompartment::GetValue()"));
6572 ::VariantClear(&variant
);
6576 return variant
.lVal
!= 0;
6580 void TSFTextStore::SetInputContext(nsWindowBase
* aWidget
,
6581 const InputContext
& aContext
,
6582 const InputContextAction
& aAction
) {
6583 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6584 ("TSFTextStore::SetInputContext(aWidget=%p, "
6585 "aContext=%s, aAction.mFocusChange=%s), "
6586 "sEnabledTextStore(0x%p)={ mWidget=0x%p }, ThinksHavingFocus()=%s",
6587 aWidget
, mozilla::ToString(aContext
).c_str(),
6588 GetFocusChangeName(aAction
.mFocusChange
), sEnabledTextStore
.get(),
6589 sEnabledTextStore
? sEnabledTextStore
->mWidget
.get() : nullptr,
6590 GetBoolName(ThinksHavingFocus())));
6592 switch (aAction
.mFocusChange
) {
6593 case InputContextAction::WIDGET_CREATED
:
6594 // If this is called when the widget is created, there is nothing to do.
6596 case InputContextAction::FOCUS_NOT_CHANGED
:
6597 case InputContextAction::MENU_LOST_PSEUDO_FOCUS
:
6598 if (NS_WARN_IF(!IsInTSFMode())) {
6601 // In these cases, `NOTIFY_IME_OF_FOCUS` won't be sent. Therefore,
6602 // we need to reset text store for new state right now.
6605 NS_WARNING_ASSERTION(IsInTSFMode(),
6606 "Why is this called when TSF is disabled?");
6607 if (sEnabledTextStore
) {
6608 RefPtr
<TSFTextStore
> textStore(sEnabledTextStore
);
6609 textStore
->SetInputScope(aContext
.mHTMLInputType
,
6610 aContext
.mHTMLInputInputmode
,
6611 aContext
.mInPrivateBrowsing
);
6616 // If focus isn't actually changed but the enabled state is changed,
6617 // emulate the focus move.
6618 if (!ThinksHavingFocus() && aContext
.mIMEState
.IsEditable()) {
6619 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6620 (" TSFTextStore::SetInputContent() emulates focus for IME "
6622 OnFocusChange(true, aWidget
, aContext
);
6623 } else if (ThinksHavingFocus() && !aContext
.mIMEState
.IsEditable()) {
6624 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6625 (" TSFTextStore::SetInputContent() emulates blur for IME "
6627 OnFocusChange(false, aWidget
, aContext
);
6632 void TSFTextStore::MarkContextAsKeyboardDisabled(ITfContext
* aContext
) {
6633 VARIANT variant_int4_value1
;
6634 variant_int4_value1
.vt
= VT_I4
;
6635 variant_int4_value1
.lVal
= 1;
6637 RefPtr
<ITfCompartment
> comp
;
6638 if (!GetCompartment(aContext
, GUID_COMPARTMENT_KEYBOARD_DISABLED
,
6639 getter_AddRefs(comp
))) {
6640 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6641 ("TSFTextStore::MarkContextAsKeyboardDisabled() failed"
6647 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6648 ("TSFTextStore::MarkContextAsKeyboardDisabled(), setting "
6649 "to disable context 0x%p...",
6651 comp
->SetValue(sClientId
, &variant_int4_value1
);
6655 void TSFTextStore::MarkContextAsEmpty(ITfContext
* aContext
) {
6656 VARIANT variant_int4_value1
;
6657 variant_int4_value1
.vt
= VT_I4
;
6658 variant_int4_value1
.lVal
= 1;
6660 RefPtr
<ITfCompartment
> comp
;
6661 if (!GetCompartment(aContext
, GUID_COMPARTMENT_EMPTYCONTEXT
,
6662 getter_AddRefs(comp
))) {
6663 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6664 ("TSFTextStore::MarkContextAsEmpty() failed"
6670 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
6671 ("TSFTextStore::MarkContextAsEmpty(), setting "
6672 "to mark empty context 0x%p...",
6674 comp
->SetValue(sClientId
, &variant_int4_value1
);
6678 void TSFTextStore::Initialize() {
6679 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6680 ("TSFTextStore::Initialize() is called..."));
6683 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6684 (" TSFTextStore::Initialize() FAILED due to already initialized"));
6688 bool enableTsf
= Preferences::GetBool(kPrefNameEnableTSF
, false);
6689 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6690 (" TSFTextStore::Initialize(), TSF is %s",
6691 enableTsf
? "enabled" : "disabled"));
6696 RefPtr
<ITfThreadMgr
> threadMgr
;
6698 ::CoCreateInstance(CLSID_TF_ThreadMgr
, nullptr, CLSCTX_INPROC_SERVER
,
6699 IID_ITfThreadMgr
, getter_AddRefs(threadMgr
));
6700 if (FAILED(hr
) || !threadMgr
) {
6701 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6702 (" TSFTextStore::Initialize() FAILED to "
6703 "create the thread manager, hr=0x%08X",
6708 hr
= threadMgr
->Activate(&sClientId
);
6710 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6711 (" TSFTextStore::Initialize() FAILED to activate, hr=0x%08X", hr
));
6715 RefPtr
<ITfDocumentMgr
> disabledDocumentMgr
;
6716 hr
= threadMgr
->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr
));
6717 if (FAILED(hr
) || !disabledDocumentMgr
) {
6718 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6719 (" TSFTextStore::Initialize() FAILED to create "
6720 "a document manager for disabled mode, hr=0x%08X",
6725 RefPtr
<ITfContext
> disabledContext
;
6726 DWORD editCookie
= 0;
6727 hr
= disabledDocumentMgr
->CreateContext(
6728 sClientId
, 0, nullptr, getter_AddRefs(disabledContext
), &editCookie
);
6729 if (FAILED(hr
) || !disabledContext
) {
6730 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6731 (" TSFTextStore::Initialize() FAILED to create "
6732 "a context for disabled mode, hr=0x%08X",
6737 MarkContextAsKeyboardDisabled(disabledContext
);
6738 MarkContextAsEmpty(disabledContext
);
6740 sThreadMgr
= threadMgr
;
6741 sDisabledDocumentMgr
= disabledDocumentMgr
;
6742 sDisabledContext
= disabledContext
;
6744 MOZ_LOG(sTextStoreLog
, LogLevel::Info
,
6745 (" TSFTextStore::Initialize(), sThreadMgr=0x%p, "
6746 "sClientId=0x%08X, sDisabledDocumentMgr=0x%p, sDisabledContext=%p",
6747 sThreadMgr
.get(), sClientId
, sDisabledDocumentMgr
.get(),
6748 sDisabledContext
.get()));
6752 already_AddRefed
<ITfThreadMgr
> TSFTextStore::GetThreadMgr() {
6753 RefPtr
<ITfThreadMgr
> threadMgr
= sThreadMgr
;
6754 return threadMgr
.forget();
6758 already_AddRefed
<ITfMessagePump
> TSFTextStore::GetMessagePump() {
6759 static bool sInitialized
= false;
6764 RefPtr
<ITfMessagePump
> messagePump
= sMessagePump
;
6765 return messagePump
.forget();
6767 // If it tried to retrieve ITfMessagePump from sThreadMgr but it failed,
6768 // we shouldn't retry it at every message due to performance reason.
6769 // Although this shouldn't occur actually.
6773 sInitialized
= true;
6775 RefPtr
<ITfMessagePump
> messagePump
;
6776 HRESULT hr
= sThreadMgr
->QueryInterface(IID_ITfMessagePump
,
6777 getter_AddRefs(messagePump
));
6778 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!messagePump
)) {
6779 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6780 ("TSFTextStore::GetMessagePump() FAILED to "
6781 "QI message pump from the thread manager, hr=0x%08X",
6785 sMessagePump
= messagePump
;
6786 return messagePump
.forget();
6790 already_AddRefed
<ITfDisplayAttributeMgr
>
6791 TSFTextStore::GetDisplayAttributeMgr() {
6792 RefPtr
<ITfDisplayAttributeMgr
> displayAttributeMgr
;
6793 if (sDisplayAttrMgr
) {
6794 displayAttributeMgr
= sDisplayAttrMgr
;
6795 return displayAttributeMgr
.forget();
6798 HRESULT hr
= ::CoCreateInstance(
6799 CLSID_TF_DisplayAttributeMgr
, nullptr, CLSCTX_INPROC_SERVER
,
6800 IID_ITfDisplayAttributeMgr
, getter_AddRefs(displayAttributeMgr
));
6801 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!displayAttributeMgr
)) {
6802 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6803 ("TSFTextStore::GetDisplayAttributeMgr() FAILED to create "
6804 "a display attribute manager instance, hr=0x%08X",
6808 sDisplayAttrMgr
= displayAttributeMgr
;
6809 return displayAttributeMgr
.forget();
6813 already_AddRefed
<ITfCategoryMgr
> TSFTextStore::GetCategoryMgr() {
6814 RefPtr
<ITfCategoryMgr
> categoryMgr
;
6816 categoryMgr
= sCategoryMgr
;
6817 return categoryMgr
.forget();
6820 ::CoCreateInstance(CLSID_TF_CategoryMgr
, nullptr, CLSCTX_INPROC_SERVER
,
6821 IID_ITfCategoryMgr
, getter_AddRefs(categoryMgr
));
6822 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!categoryMgr
)) {
6823 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6824 ("TSFTextStore::GetCategoryMgr() FAILED to create "
6825 "a category manager instance, hr=0x%08X",
6829 sCategoryMgr
= categoryMgr
;
6830 return categoryMgr
.forget();
6834 already_AddRefed
<ITfCompartment
> TSFTextStore::GetCompartmentForOpenClose() {
6835 if (sCompartmentForOpenClose
) {
6836 RefPtr
<ITfCompartment
> compartment
= sCompartmentForOpenClose
;
6837 return compartment
.forget();
6844 RefPtr
<ITfCompartmentMgr
> compartmentMgr
;
6845 HRESULT hr
= sThreadMgr
->QueryInterface(IID_ITfCompartmentMgr
,
6846 getter_AddRefs(compartmentMgr
));
6847 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!compartmentMgr
)) {
6848 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6849 ("TSFTextStore::GetCompartmentForOpenClose() FAILED due to"
6850 "sThreadMgr not having ITfCompartmentMgr, hr=0x%08X",
6855 RefPtr
<ITfCompartment
> compartment
;
6856 hr
= compartmentMgr
->GetCompartment(GUID_COMPARTMENT_KEYBOARD_OPENCLOSE
,
6857 getter_AddRefs(compartment
));
6858 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!compartment
)) {
6859 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6860 ("TSFTextStore::GetCompartmentForOpenClose() FAILED due to"
6861 "ITfCompartmentMgr::GetCompartment() failuere, hr=0x%08X",
6866 sCompartmentForOpenClose
= compartment
;
6867 return compartment
.forget();
6871 already_AddRefed
<ITfInputProcessorProfiles
>
6872 TSFTextStore::GetInputProcessorProfiles() {
6873 RefPtr
<ITfInputProcessorProfiles
> inputProcessorProfiles
;
6874 if (sInputProcessorProfiles
) {
6875 inputProcessorProfiles
= sInputProcessorProfiles
;
6876 return inputProcessorProfiles
.forget();
6878 // XXX MSDN documents that ITfInputProcessorProfiles is available only on
6879 // desktop apps. However, there is no known way to obtain
6880 // ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles
6882 HRESULT hr
= ::CoCreateInstance(
6883 CLSID_TF_InputProcessorProfiles
, nullptr, CLSCTX_INPROC_SERVER
,
6884 IID_ITfInputProcessorProfiles
, getter_AddRefs(inputProcessorProfiles
));
6885 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!inputProcessorProfiles
)) {
6886 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6887 ("TSFTextStore::GetInputProcessorProfiles() FAILED to create input "
6888 "processor profiles, hr=0x%08X",
6892 sInputProcessorProfiles
= inputProcessorProfiles
;
6893 return inputProcessorProfiles
.forget();
6897 void TSFTextStore::Terminate() {
6898 MOZ_LOG(sTextStoreLog
, LogLevel::Info
, ("TSFTextStore::Terminate()"));
6900 TSFStaticSink::Shutdown();
6902 sDisplayAttrMgr
= nullptr;
6903 sCategoryMgr
= nullptr;
6904 sEnabledTextStore
= nullptr;
6905 sDisabledDocumentMgr
= nullptr;
6906 sDisabledContext
= nullptr;
6907 sCompartmentForOpenClose
= nullptr;
6908 sInputProcessorProfiles
= nullptr;
6911 sThreadMgr
->Deactivate();
6912 sThreadMgr
= nullptr;
6913 sMessagePump
= nullptr;
6914 sKeystrokeMgr
= nullptr;
6919 bool TSFTextStore::ProcessRawKeyMessage(const MSG
& aMsg
) {
6921 return false; // not in TSF mode
6923 static bool sInitialized
= false;
6924 if (!sKeystrokeMgr
) {
6925 // If it tried to retrieve ITfKeystrokeMgr from sThreadMgr but it failed,
6926 // we shouldn't retry it at every keydown nor keyup due to performance
6927 // reason. Although this shouldn't occur actually.
6931 sInitialized
= true;
6932 RefPtr
<ITfKeystrokeMgr
> keystrokeMgr
;
6933 HRESULT hr
= sThreadMgr
->QueryInterface(IID_ITfKeystrokeMgr
,
6934 getter_AddRefs(keystrokeMgr
));
6935 if (NS_WARN_IF(FAILED(hr
)) || NS_WARN_IF(!keystrokeMgr
)) {
6936 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
6937 ("TSFTextStore::ProcessRawKeyMessage() FAILED to "
6938 "QI keystroke manager from the thread manager, hr=0x%08X",
6942 sKeystrokeMgr
= keystrokeMgr
.forget();
6945 if (aMsg
.message
== WM_KEYDOWN
) {
6946 RefPtr
<TSFTextStore
> textStore(sEnabledTextStore
);
6948 textStore
->OnStartToHandleKeyMessage();
6949 if (NS_WARN_IF(textStore
!= sEnabledTextStore
)) {
6950 // Let's handle the key message with new focused TSFTextStore.
6951 textStore
= sEnabledTextStore
;
6954 AutoRestore
<const MSG
*> savePreviousKeyMsg(sHandlingKeyMsg
);
6955 AutoRestore
<bool> saveKeyEventDispatched(sIsKeyboardEventDispatched
);
6956 sHandlingKeyMsg
= &aMsg
;
6957 sIsKeyboardEventDispatched
= false;
6959 RefPtr
<ITfKeystrokeMgr
> keystrokeMgr
= sKeystrokeMgr
;
6960 HRESULT hr
= keystrokeMgr
->TestKeyDown(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
6961 if (FAILED(hr
) || !sKeystrokeMgr
|| !eaten
) {
6964 hr
= keystrokeMgr
->KeyDown(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
6966 textStore
->OnEndHandlingKeyMessage(!!eaten
);
6968 return SUCCEEDED(hr
) &&
6969 (eaten
|| !sKeystrokeMgr
|| sIsKeyboardEventDispatched
);
6971 if (aMsg
.message
== WM_KEYUP
) {
6972 RefPtr
<TSFTextStore
> textStore(sEnabledTextStore
);
6974 textStore
->OnStartToHandleKeyMessage();
6975 if (NS_WARN_IF(textStore
!= sEnabledTextStore
)) {
6976 // Let's handle the key message with new focused TSFTextStore.
6977 textStore
= sEnabledTextStore
;
6980 AutoRestore
<const MSG
*> savePreviousKeyMsg(sHandlingKeyMsg
);
6981 AutoRestore
<bool> saveKeyEventDispatched(sIsKeyboardEventDispatched
);
6982 sHandlingKeyMsg
= &aMsg
;
6983 sIsKeyboardEventDispatched
= false;
6985 RefPtr
<ITfKeystrokeMgr
> keystrokeMgr
= sKeystrokeMgr
;
6986 HRESULT hr
= keystrokeMgr
->TestKeyUp(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
6987 if (FAILED(hr
) || !sKeystrokeMgr
|| !eaten
) {
6990 hr
= keystrokeMgr
->KeyUp(aMsg
.wParam
, aMsg
.lParam
, &eaten
);
6992 textStore
->OnEndHandlingKeyMessage(!!eaten
);
6994 return SUCCEEDED(hr
) &&
6995 (eaten
|| !sKeystrokeMgr
|| sIsKeyboardEventDispatched
);
7001 void TSFTextStore::ProcessMessage(nsWindowBase
* aWindow
, UINT aMessage
,
7002 WPARAM
& aWParam
, LPARAM
& aLParam
,
7003 MSGResult
& aResult
) {
7005 case WM_IME_SETCONTEXT
:
7006 // If a windowless plugin had focus and IME was handled on it, composition
7007 // window was set the position. After that, even in TSF mode, WinXP keeps
7008 // to use composition window at the position if the active IME is not
7009 // aware TSF. For avoiding this issue, we need to hide the composition
7012 aLParam
&= ~ISC_SHOWUICOMPOSITIONWINDOW
;
7016 // When an modal dialog such as a file picker is open, composition
7017 // should be committed because IME might be used on it.
7018 if (!IsComposingOn(aWindow
)) {
7021 CommitComposition(false);
7023 case MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE
: {
7024 TSFTextStore
* maybeTextStore
= reinterpret_cast<TSFTextStore
*>(aWParam
);
7025 if (maybeTextStore
== sEnabledTextStore
) {
7026 RefPtr
<TSFTextStore
> textStore(maybeTextStore
);
7027 textStore
->NotifyTSFOfLayoutChangeAgain();
7035 bool TSFTextStore::IsIMM_IMEActive() {
7036 return TSFStaticSink::IsIMM_IMEActive();
7040 bool TSFTextStore::IsMSJapaneseIMEActive() {
7041 return TSFStaticSink::IsMSJapaneseIMEActive();
7045 bool TSFTextStore::IsGoogleJapaneseInputActive() {
7046 return TSFStaticSink::IsGoogleJapaneseInputActive();
7049 /******************************************************************/
7050 /* TSFTextStore::Composition */
7051 /******************************************************************/
7053 void TSFTextStore::Composition::Start(ITfCompositionView
* aCompositionView
,
7054 LONG aCompositionStartOffset
,
7055 const nsAString
& aCompositionString
) {
7056 mView
= aCompositionView
;
7057 mString
= aCompositionString
;
7058 mStart
= aCompositionStartOffset
;
7061 void TSFTextStore::Composition::End() {
7066 /******************************************************************************
7067 * TSFTextStore::Content
7068 *****************************************************************************/
7070 const nsDependentSubstring
TSFTextStore::Content::GetSelectedText() const {
7071 MOZ_ASSERT(mInitialized
);
7072 return GetSubstring(static_cast<uint32_t>(mSelection
.StartOffset()),
7073 static_cast<uint32_t>(mSelection
.Length()));
7076 const nsDependentSubstring
TSFTextStore::Content::GetSubstring(
7077 uint32_t aStart
, uint32_t aLength
) const {
7078 MOZ_ASSERT(mInitialized
);
7079 return nsDependentSubstring(mText
, aStart
, aLength
);
7082 void TSFTextStore::Content::ReplaceSelectedTextWith(const nsAString
& aString
) {
7083 MOZ_ASSERT(mInitialized
);
7084 ReplaceTextWith(mSelection
.StartOffset(), mSelection
.Length(), aString
);
7087 inline uint32_t FirstDifferentCharOffset(const nsAString
& aStr1
,
7088 const nsAString
& aStr2
) {
7089 MOZ_ASSERT(aStr1
!= aStr2
);
7091 uint32_t minLength
= std::min(aStr1
.Length(), aStr2
.Length());
7092 for (; i
< minLength
&& aStr1
[i
] == aStr2
[i
]; i
++) {
7098 void TSFTextStore::Content::ReplaceTextWith(LONG aStart
, LONG aLength
,
7099 const nsAString
& aReplaceString
) {
7100 MOZ_ASSERT(mInitialized
);
7101 const nsDependentSubstring replacedString
= GetSubstring(
7102 static_cast<uint32_t>(aStart
), static_cast<uint32_t>(aLength
));
7103 if (aReplaceString
!= replacedString
) {
7104 uint32_t firstDifferentOffset
= mMinTextModifiedOffset
;
7105 if (mComposition
.IsComposing()) {
7106 // Emulate text insertion during compositions, because during a
7107 // composition, editor expects the whole composition string to
7108 // be sent in eCompositionChange, not just the inserted part.
7109 // The actual eCompositionChange will be sent in SetSelection
7110 // or OnUpdateComposition.
7111 MOZ_ASSERT(aStart
>= mComposition
.mStart
);
7112 MOZ_ASSERT(aStart
+ aLength
<= mComposition
.EndOffset());
7113 mComposition
.mString
.Replace(
7114 static_cast<uint32_t>(aStart
- mComposition
.mStart
),
7115 static_cast<uint32_t>(aLength
), aReplaceString
);
7116 // TIP may set composition string twice or more times during a document
7117 // lock. Therefore, we should compute the first difference offset with
7118 // mLastCompositionString.
7119 if (mComposition
.mString
!= mLastCompositionString
) {
7120 firstDifferentOffset
= mComposition
.mStart
+
7121 FirstDifferentCharOffset(mComposition
.mString
,
7122 mLastCompositionString
);
7123 // The previous change to the composition string is canceled.
7124 if (mMinTextModifiedOffset
>=
7125 static_cast<uint32_t>(mComposition
.mStart
) &&
7126 mMinTextModifiedOffset
< firstDifferentOffset
) {
7127 mMinTextModifiedOffset
= firstDifferentOffset
;
7129 } else if (mMinTextModifiedOffset
>=
7130 static_cast<uint32_t>(mComposition
.mStart
) &&
7131 mMinTextModifiedOffset
<
7132 static_cast<uint32_t>(mComposition
.EndOffset())) {
7133 // The previous change to the composition string is canceled.
7134 mMinTextModifiedOffset
= firstDifferentOffset
=
7135 mComposition
.EndOffset();
7137 mLatestCompositionEndOffset
= mComposition
.EndOffset();
7138 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7139 ("0x%p TSFTextStore::Content::ReplaceTextWith(aStart=%d, "
7140 "aLength=%d, aReplaceString=\"%s\"), mComposition={ mStart=%d, "
7141 "mString=\"%s\" }, mLastCompositionString=\"%s\", "
7142 "mMinTextModifiedOffset=%u, firstDifferentOffset=%u",
7143 this, aStart
, aLength
,
7144 GetEscapedUTF8String(aReplaceString
).get(), mComposition
.mStart
,
7145 GetEscapedUTF8String(mComposition
.mString
).get(),
7146 GetEscapedUTF8String(mLastCompositionString
).get(),
7147 mMinTextModifiedOffset
, firstDifferentOffset
));
7149 firstDifferentOffset
=
7150 static_cast<uint32_t>(aStart
) +
7151 FirstDifferentCharOffset(aReplaceString
, replacedString
);
7153 mMinTextModifiedOffset
=
7154 std::min(mMinTextModifiedOffset
, firstDifferentOffset
);
7155 mText
.Replace(static_cast<uint32_t>(aStart
), static_cast<uint32_t>(aLength
),
7158 // Selection should be collapsed at the end of the inserted string.
7159 mSelection
.CollapseAt(static_cast<uint32_t>(aStart
) +
7160 aReplaceString
.Length());
7163 void TSFTextStore::Content::StartComposition(
7164 ITfCompositionView
* aCompositionView
, const PendingAction
& aCompStart
,
7165 bool aPreserveSelection
) {
7166 MOZ_ASSERT(mInitialized
);
7167 MOZ_ASSERT(aCompositionView
);
7168 MOZ_ASSERT(!mComposition
.mView
);
7169 MOZ_ASSERT(aCompStart
.mType
== PendingAction::Type::eCompositionStart
);
7172 aCompositionView
, aCompStart
.mSelectionStart
,
7173 GetSubstring(static_cast<uint32_t>(aCompStart
.mSelectionStart
),
7174 static_cast<uint32_t>(aCompStart
.mSelectionLength
)));
7175 mLatestCompositionStartOffset
= mComposition
.mStart
;
7176 mLatestCompositionEndOffset
= mComposition
.EndOffset();
7177 if (!aPreserveSelection
) {
7178 // XXX Do we need to set a new writing-mode here when setting a new
7179 // selection? Currently, we just preserve the existing value.
7180 WritingMode writingMode
=
7181 mSelection
.IsDirty() ? WritingMode() : mSelection
.GetWritingMode();
7182 mSelection
.SetSelection(mComposition
.mStart
, mComposition
.mString
.Length(),
7183 false, writingMode
);
7187 void TSFTextStore::Content::RestoreCommittedComposition(
7188 ITfCompositionView
* aCompositionView
,
7189 const PendingAction
& aCanceledCompositionEnd
) {
7190 MOZ_ASSERT(mInitialized
);
7191 MOZ_ASSERT(aCompositionView
);
7192 MOZ_ASSERT(!mComposition
.mView
);
7193 MOZ_ASSERT(aCanceledCompositionEnd
.mType
==
7194 PendingAction::Type::eCompositionEnd
);
7197 static_cast<uint32_t>(aCanceledCompositionEnd
.mSelectionStart
),
7198 static_cast<uint32_t>(aCanceledCompositionEnd
.mData
.Length())) ==
7199 aCanceledCompositionEnd
.mData
);
7201 // Restore the committed string as composing string.
7202 mComposition
.Start(aCompositionView
, aCanceledCompositionEnd
.mSelectionStart
,
7203 aCanceledCompositionEnd
.mData
);
7204 mLatestCompositionStartOffset
= mComposition
.mStart
;
7205 mLatestCompositionEndOffset
= mComposition
.EndOffset();
7208 void TSFTextStore::Content::EndComposition(const PendingAction
& aCompEnd
) {
7209 MOZ_ASSERT(mInitialized
);
7210 MOZ_ASSERT(mComposition
.mView
);
7211 MOZ_ASSERT(aCompEnd
.mType
== PendingAction::Type::eCompositionEnd
);
7213 mSelection
.CollapseAt(mComposition
.mStart
+ aCompEnd
.mData
.Length());
7217 /******************************************************************************
7218 * TSFTextStore::MouseTracker
7219 *****************************************************************************/
7221 TSFTextStore::MouseTracker::MouseTracker()
7222 : mStart(-1), mLength(-1), mCookie(kInvalidCookie
) {}
7225 TSFTextStore::MouseTracker::Init(TSFTextStore
* aTextStore
) {
7226 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7227 ("0x%p TSFTextStore::MouseTracker::Init(aTextStore=0x%p), "
7228 "aTextStore->mMouseTrackers.Length()=%d",
7229 this, aTextStore
->mMouseTrackers
.Length()));
7231 if (&aTextStore
->mMouseTrackers
.LastElement() != this) {
7232 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7233 ("0x%p TSFTextStore::MouseTracker::Init() FAILED due to "
7234 "this is not the last element of mMouseTrackers",
7238 if (aTextStore
->mMouseTrackers
.Length() > kInvalidCookie
) {
7239 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7240 ("0x%p TSFTextStore::MouseTracker::Init() FAILED due to "
7241 "no new cookie available",
7245 MOZ_ASSERT(!aTextStore
->mMouseTrackers
.IsEmpty(),
7246 "This instance must be in TSFTextStore::mMouseTrackers");
7247 mCookie
= static_cast<DWORD
>(aTextStore
->mMouseTrackers
.Length() - 1);
7252 TSFTextStore::MouseTracker::AdviseSink(TSFTextStore
* aTextStore
,
7253 ITfRangeACP
* aTextRange
,
7254 ITfMouseSink
* aMouseSink
) {
7255 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7256 ("0x%p TSFTextStore::MouseTracker::AdviseSink(aTextStore=0x%p, "
7257 "aTextRange=0x%p, aMouseSink=0x%p), mCookie=%d, mSink=0x%p",
7258 this, aTextStore
, aTextRange
, aMouseSink
, mCookie
, mSink
.get()));
7259 MOZ_ASSERT(mCookie
!= kInvalidCookie
, "This hasn't been initalized?");
7262 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7263 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7264 "due to already being used",
7269 HRESULT hr
= aTextRange
->GetExtent(&mStart
, &mLength
);
7271 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7272 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7273 "due to failure of ITfRangeACP::GetExtent()",
7278 if (mStart
< 0 || mLength
<= 0) {
7279 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7280 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7281 "due to odd result of ITfRangeACP::GetExtent(), "
7282 "mStart=%d, mLength=%d",
7283 this, mStart
, mLength
));
7284 return E_INVALIDARG
;
7287 nsAutoString textContent
;
7288 if (NS_WARN_IF(!aTextStore
->GetCurrentText(textContent
))) {
7289 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7290 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7291 "due to failure of TSFTextStore::GetCurrentText()",
7296 if (textContent
.Length() <= static_cast<uint32_t>(mStart
) ||
7297 textContent
.Length() < static_cast<uint32_t>(mStart
+ mLength
)) {
7298 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7299 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink() FAILED "
7300 "due to out of range, mStart=%d, mLength=%d, "
7301 "textContent.Length()=%d",
7302 this, mStart
, mLength
, textContent
.Length()));
7303 return E_INVALIDARG
;
7308 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7309 ("0x%p TSFTextStore::MouseTracker::AdviseMouseSink(), "
7310 "succeeded, mStart=%d, mLength=%d, textContent.Length()=%d",
7311 this, mStart
, mLength
, textContent
.Length()));
7315 void TSFTextStore::MouseTracker::UnadviseSink() {
7316 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7317 ("0x%p TSFTextStore::MouseTracker::UnadviseSink(), "
7318 "mCookie=%d, mSink=0x%p, mStart=%d, mLength=%d",
7319 this, mCookie
, mSink
.get(), mStart
, mLength
));
7321 mStart
= mLength
= -1;
7324 bool TSFTextStore::MouseTracker::OnMouseButtonEvent(ULONG aEdge
,
7326 DWORD aButtonStatus
) {
7327 MOZ_ASSERT(IsUsing(), "The caller must check before calling OnMouseEvent()");
7330 RefPtr
<ITfMouseSink
> sink
= mSink
;
7331 HRESULT hr
= sink
->OnMouseEvent(aEdge
, aQuadrant
, aButtonStatus
, &eaten
);
7333 MOZ_LOG(sTextStoreLog
, LogLevel::Debug
,
7334 ("0x%p TSFTextStore::MouseTracker::OnMouseEvent(aEdge=%d, "
7335 "aQuadrant=%d, aButtonStatus=0x%08X), hr=0x%08X, eaten=%s",
7336 this, aEdge
, aQuadrant
, aButtonStatus
, hr
, GetBoolName(!!eaten
)));
7338 return SUCCEEDED(hr
) && eaten
;
7343 bool TSFTextStore::CurrentKeyboardLayoutHasIME() {
7344 RefPtr
<ITfInputProcessorProfiles
> inputProcessorProfiles
=
7345 TSFTextStore::GetInputProcessorProfiles();
7346 if (!inputProcessorProfiles
) {
7347 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7348 ("TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED due to "
7349 "there is no input processor profiles instance"));
7352 RefPtr
<ITfInputProcessorProfileMgr
> profileMgr
;
7353 HRESULT hr
= inputProcessorProfiles
->QueryInterface(
7354 IID_ITfInputProcessorProfileMgr
, getter_AddRefs(profileMgr
));
7355 if (FAILED(hr
) || !profileMgr
) {
7356 // On Windows Vista or later, ImmIsIME() API always returns true.
7357 // If we failed to obtain the profile manager, we cannot know if current
7358 // keyboard layout has IME.
7359 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7360 (" TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to query "
7361 "ITfInputProcessorProfileMgr"));
7365 TF_INPUTPROCESSORPROFILE profile
;
7366 hr
= profileMgr
->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD
, &profile
);
7367 if (hr
== S_FALSE
) {
7368 return false; // not found or not active
7371 MOZ_LOG(sTextStoreLog
, LogLevel::Error
,
7372 (" TSFTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive "
7376 return (profile
.dwProfileType
== TF_PROFILETYPE_INPUTPROCESSOR
);
7378 #endif // #ifdef DEBUG
7380 } // namespace widget
7381 } // namespace mozilla