1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef TSFTextStore_h_
7 #define TSFTextStore_h_
8 
9 #include "nsCOMPtr.h"
10 #include "nsIWidget.h"
11 #include "nsString.h"
12 #include "nsWindowBase.h"
13 
14 #include "WinUtils.h"
15 #include "WritingModes.h"
16 
17 #include "mozilla/Attributes.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/StaticPtr.h"
20 #include "mozilla/TextEventDispatcher.h"
21 #include "mozilla/TextRange.h"
22 #include "mozilla/WindowsVersion.h"
23 
24 #include <msctf.h>
25 #include <textstor.h>
26 
27 // GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID.
28 // With initguid.h, we get its instance instead of extern declaration.
29 #ifdef INPUTSCOPE_INIT_GUID
30 #  include <initguid.h>
31 #endif
32 #ifdef TEXTATTRS_INIT_GUID
33 #  include <tsattrs.h>
34 #endif
35 #include <inputscope.h>
36 
37 // TSF InputScope, for earlier SDK 8
38 #define IS_SEARCH static_cast<InputScope>(50)
39 
40 struct ITfThreadMgr;
41 struct ITfDocumentMgr;
42 struct ITfDisplayAttributeMgr;
43 struct ITfCategoryMgr;
44 class nsWindow;
45 
46 namespace mozilla {
47 namespace widget {
48 
49 class TSFStaticSink;
50 struct MSGResult;
51 
52 /*
53  * Text Services Framework text store
54  */
55 
56 class TSFTextStore final : public ITextStoreACP,
57                            public ITfContextOwnerCompositionSink,
58                            public ITfMouseTrackerACP {
59   friend class TSFStaticSink;
60 
61  private:
62   typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
63   typedef IMENotification::SelectionChangeData SelectionChangeData;
64   typedef IMENotification::TextChangeDataBase TextChangeDataBase;
65   typedef IMENotification::TextChangeData TextChangeData;
66 
67  public: /*IUnknown*/
68   STDMETHODIMP QueryInterface(REFIID, void**);
69 
70   NS_INLINE_DECL_IUNKNOWN_REFCOUNTING(TSFTextStore)
71 
72  public: /*ITextStoreACP*/
73   STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD);
74   STDMETHODIMP UnadviseSink(IUnknown*);
75   STDMETHODIMP RequestLock(DWORD, HRESULT*);
76   STDMETHODIMP GetStatus(TS_STATUS*);
77   STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*);
78   STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*);
79   STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*);
80   STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG,
81                        ULONG*, LONG*);
82   STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*);
83   STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**);
84   STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**);
85   STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*);
86   STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*);
87   STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*);
88   STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD);
89   STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG,
90                                                    const TS_ATTRID*, DWORD);
91   STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*,
92                                       DWORD, LONG*, BOOL*, LONG*);
93   STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*);
94   STDMETHODIMP GetEndACP(LONG*);
95   STDMETHODIMP GetActiveView(TsViewCookie*);
96   STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*);
97   STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*);
98   STDMETHODIMP GetScreenExt(TsViewCookie, RECT*);
99   STDMETHODIMP GetWnd(TsViewCookie, HWND*);
100   STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*,
101                                      TS_TEXTCHANGE*);
102   STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*,
103                                          TS_TEXTCHANGE*);
104 
105  public: /*ITfContextOwnerCompositionSink*/
106   STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*);
107   STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*);
108   STDMETHODIMP OnEndComposition(ITfCompositionView*);
109 
110  public: /*ITfMouseTrackerACP*/
111   STDMETHODIMP AdviseMouseSink(ITfRangeACP*, ITfMouseSink*, DWORD*);
112   STDMETHODIMP UnadviseMouseSink(DWORD);
113 
114  public:
115   static void Initialize(void);
116   static void Terminate(void);
117 
118   static bool ProcessRawKeyMessage(const MSG& aMsg);
119   static void ProcessMessage(nsWindowBase* aWindow, UINT aMessage,
120                              WPARAM& aWParam, LPARAM& aLParam,
121                              MSGResult& aResult);
122 
123   static void SetIMEOpenState(bool);
124   static bool GetIMEOpenState(void);
125 
CommitComposition(bool aDiscard)126   static void CommitComposition(bool aDiscard) {
127     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
128     if (!sEnabledTextStore) {
129       return;
130     }
131     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
132     textStore->CommitCompositionInternal(aDiscard);
133   }
134 
135   static void SetInputContext(nsWindowBase* aWidget,
136                               const InputContext& aContext,
137                               const InputContextAction& aAction);
138 
139   static nsresult OnFocusChange(bool aGotFocus, nsWindowBase* aFocusedWidget,
140                                 const InputContext& aContext);
OnTextChange(const IMENotification & aIMENotification)141   static nsresult OnTextChange(const IMENotification& aIMENotification) {
142     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
143     if (!sEnabledTextStore) {
144       return NS_OK;
145     }
146     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
147     return textStore->OnTextChangeInternal(aIMENotification);
148   }
149 
OnSelectionChange(const IMENotification & aIMENotification)150   static nsresult OnSelectionChange(const IMENotification& aIMENotification) {
151     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
152     if (!sEnabledTextStore) {
153       return NS_OK;
154     }
155     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
156     return textStore->OnSelectionChangeInternal(aIMENotification);
157   }
158 
OnLayoutChange()159   static nsresult OnLayoutChange() {
160     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
161     if (!sEnabledTextStore) {
162       return NS_OK;
163     }
164     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
165     return textStore->OnLayoutChangeInternal();
166   }
167 
OnUpdateComposition()168   static nsresult OnUpdateComposition() {
169     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
170     if (!sEnabledTextStore) {
171       return NS_OK;
172     }
173     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
174     return textStore->OnUpdateCompositionInternal();
175   }
176 
OnMouseButtonEvent(const IMENotification & aIMENotification)177   static nsresult OnMouseButtonEvent(const IMENotification& aIMENotification) {
178     NS_ASSERTION(IsInTSFMode(), "Not in TSF mode, shouldn't be called");
179     if (!sEnabledTextStore) {
180       return NS_OK;
181     }
182     RefPtr<TSFTextStore> textStore(sEnabledTextStore);
183     return textStore->OnMouseButtonEventInternal(aIMENotification);
184   }
185 
186   static IMENotificationRequests GetIMENotificationRequests();
187 
188   // Returns the address of the pointer so that the TSF automatic test can
189   // replace the system object with a custom implementation for testing.
190   // XXX TSF doesn't work now.  Should we remove it?
GetNativeData(uint32_t aDataType)191   static void* GetNativeData(uint32_t aDataType) {
192     switch (aDataType) {
193       case NS_NATIVE_TSF_THREAD_MGR:
194         Initialize();  // Apply any previous changes
195         return static_cast<void*>(&sThreadMgr);
196       case NS_NATIVE_TSF_CATEGORY_MGR:
197         return static_cast<void*>(&sCategoryMgr);
198       case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
199         return static_cast<void*>(&sDisplayAttrMgr);
200       default:
201         return nullptr;
202     }
203   }
204 
GetThreadManager()205   static void* GetThreadManager() { return static_cast<void*>(sThreadMgr); }
206 
ThinksHavingFocus()207   static bool ThinksHavingFocus() {
208     return (sEnabledTextStore && sEnabledTextStore->mContext);
209   }
210 
IsInTSFMode()211   static bool IsInTSFMode() { return sThreadMgr != nullptr; }
212 
IsComposing()213   static bool IsComposing() {
214     return (sEnabledTextStore && sEnabledTextStore->mComposition.IsComposing());
215   }
216 
IsComposingOn(nsWindowBase * aWidget)217   static bool IsComposingOn(nsWindowBase* aWidget) {
218     return (IsComposing() && sEnabledTextStore->mWidget == aWidget);
219   }
220 
GetEnabledWindowBase()221   static nsWindowBase* GetEnabledWindowBase() {
222     return sEnabledTextStore ? sEnabledTextStore->mWidget.get() : nullptr;
223   }
224 
225   /**
226    * Returns true if active keyboard layout is a legacy IMM-IME.
227    */
228   static bool IsIMM_IMEActive();
229 
230   /**
231    * Returns true if active TIP is MS-IME for Japanese.
232    */
233   static bool IsMSJapaneseIMEActive();
234 
235   /**
236    * Returns true if active TIP is Google Japanese Input.
237    * Note that if Google Japanese Input is installed as an IMM-IME,
238    * this return false even if Google Japanese Input is active.
239    * So, you may need to check IMMHandler::IsGoogleJapaneseInputActive() too.
240    */
241   static bool IsGoogleJapaneseInputActive();
242 
243   /**
244    * Returns true if active TIP or IME is a black listed one and we should
245    * set input scope of URL bar to IS_DEFAULT rather than IS_URL.
246    */
247   static bool ShouldSetInputScopeOfURLBarToDefault();
248 
249   /**
250    * Returns true if TSF may crash if GetSelection() returns E_FAIL.
251    */
252   static bool DoNotReturnErrorFromGetSelection();
253 
254 #ifdef DEBUG
255   // Returns true when keyboard layout has IME (TIP).
256   static bool CurrentKeyboardLayoutHasIME();
257 #endif  // #ifdef DEBUG
258 
259  protected:
260   TSFTextStore();
261   ~TSFTextStore();
262 
263   static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget,
264                                 const InputContext& aContext);
265   static void EnsureToDestroyAndReleaseEnabledTextStoreIf(
266       RefPtr<TSFTextStore>& aTextStore);
267   static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
268   static void MarkContextAsEmpty(ITfContext* aContext);
269 
270   bool Init(nsWindowBase* aWidget, const InputContext& aContext);
271   void Destroy();
272   void ReleaseTSFObjects();
273 
IsReadLock(DWORD aLock)274   bool IsReadLock(DWORD aLock) const {
275     return (TS_LF_READ == (aLock & TS_LF_READ));
276   }
IsReadWriteLock(DWORD aLock)277   bool IsReadWriteLock(DWORD aLock) const {
278     return (TS_LF_READWRITE == (aLock & TS_LF_READWRITE));
279   }
IsReadLocked()280   bool IsReadLocked() const { return IsReadLock(mLock); }
IsReadWriteLocked()281   bool IsReadWriteLocked() const { return IsReadWriteLock(mLock); }
282 
283   // This is called immediately after a call of OnLockGranted() of mSink.
284   // Note that mLock isn't cleared yet when this is called.
285   void DidLockGranted();
286 
287   bool GetScreenExtInternal(RECT& aScreenExt);
288   // If aDispatchCompositionChangeEvent is true, this method will dispatch
289   // compositionchange event if this is called during IME composing.
290   // aDispatchCompositionChangeEvent should be true only when this is called
291   // from SetSelection.  Because otherwise, the compositionchange event should
292   // not be sent from here.
293   HRESULT SetSelectionInternal(const TS_SELECTION_ACP*,
294                                bool aDispatchCompositionChangeEvent = false);
295   bool InsertTextAtSelectionInternal(const nsAString& aInsertStr,
296                                      TS_TEXTCHANGE* aTextChange);
297   void CommitCompositionInternal(bool);
298   HRESULT GetDisplayAttribute(ITfProperty* aProperty, ITfRange* aRange,
299                               TF_DISPLAYATTRIBUTE* aResult);
300   HRESULT RestartCompositionIfNecessary(ITfRange* pRangeNew = nullptr);
301   HRESULT RestartComposition(ITfCompositionView* aCompositionView,
302                              ITfRange* aNewRange);
303 
304   // Following methods record composing action(s) to mPendingActions.
305   // They will be flushed FlushPendingActions().
306   HRESULT RecordCompositionStartAction(ITfCompositionView* aCompositionView,
307                                        ITfRange* aRange,
308                                        bool aPreserveSelection);
309   HRESULT RecordCompositionStartAction(ITfCompositionView* aComposition,
310                                        LONG aStart, LONG aLength,
311                                        bool aPreserveSelection);
312   HRESULT RecordCompositionUpdateAction();
313   HRESULT RecordCompositionEndAction();
314 
315   // DispatchEvent() dispatches the event and if it may not be handled
316   // synchronously, this makes the instance not notify TSF of pending
317   // notifications until next notification from content.
318   void DispatchEvent(WidgetGUIEvent& aEvent);
319   void OnLayoutInformationAvaliable();
320 
321   // FlushPendingActions() performs pending actions recorded in mPendingActions
322   // and clear it.
323   void FlushPendingActions();
324   // MaybeFlushPendingNotifications() performs pending notifications to TSF.
325   void MaybeFlushPendingNotifications();
326 
327   nsresult OnTextChangeInternal(const IMENotification& aIMENotification);
328   nsresult OnSelectionChangeInternal(const IMENotification& aIMENotification);
329   nsresult OnMouseButtonEventInternal(const IMENotification& aIMENotification);
330   nsresult OnLayoutChangeInternal();
331   nsresult OnUpdateCompositionInternal();
332 
333   // mPendingSelectionChangeData stores selection change data until notifying
334   // TSF of selection change.  If two or more selection changes occur, this
335   // stores the latest selection change data because only it is necessary.
336   SelectionChangeData mPendingSelectionChangeData;
337 
338   // mPendingTextChangeData stores one or more text change data until notifying
339   // TSF of text change.  If two or more text changes occur, this merges
340   // every text change data.
341   TextChangeData mPendingTextChangeData;
342 
343   void NotifyTSFOfTextChange();
344   void NotifyTSFOfSelectionChange();
345   bool NotifyTSFOfLayoutChange();
346   void NotifyTSFOfLayoutChangeAgain();
347 
348   HRESULT HandleRequestAttrs(DWORD aFlags, ULONG aFilterCount,
349                              const TS_ATTRID* aFilterAttrs);
350   void SetInputScope(const nsString& aHTMLInputType,
351                      const nsString& aHTMLInputInputmode,
352                      bool aInPrivateBrowsing);
353 
354   // Creates native caret over our caret.  This method only works on desktop
355   // application.  Otherwise, this does nothing.
356   void CreateNativeCaret();
357   // Destroys native caret if there is.
358   void MaybeDestroyNativeCaret();
359 
360   /**
361    * MaybeHackNoErrorLayoutBugs() is a helper method of GetTextExt().  In
362    * strictly speaking, TSF is aware of asynchronous layout computation like us.
363    * However, Windows 10 version 1803 and older (including Windows 8.1 and
364    * older) Windows has a bug which is that the caller of GetTextExt() of TSF
365    * does not return TS_E_NOLAYOUT to TIP as is.  Additionally, even after
366    * fixing this bug, some TIPs are not work well when we return TS_E_NOLAYOUT.
367    * For avoiding this issue, this method checks current Windows version and
368    * active TIP, and if in case we cannot return TS_E_NOLAYOUT, this modifies
369    * aACPStart and aACPEnd to making sure that they are in range of unmodified
370    * characters.
371    *
372    * @param aACPStart   Initial value should be acpStart of GetTextExt().
373    *                    If this method returns true, this may be modified
374    *                    to be in range of unmodified characters.
375    * @param aACPEnd     Initial value should be acpEnd of GetTextExt().
376    *                    If this method returns true, this may be modified
377    *                    to be in range of unmodified characters.
378    *                    And also this may become same as aACPStart.
379    * @return            true if the caller shouldn't return TS_E_NOLAYOUT.
380    *                    In this case, this method modifies aACPStart and/or
381    *                    aASCPEnd to compute rectangle of unmodified characters.
382    *                    false if the caller can return TS_E_NOLAYOUT or
383    *                    we cannot have proper unmodified characters.
384    */
385   bool MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd);
386 
387   // Holds the pointer to our current win32 widget
388   RefPtr<nsWindowBase> mWidget;
389   // mDispatcher is a helper class to dispatch composition events.
390   RefPtr<TextEventDispatcher> mDispatcher;
391   // Document manager for the currently focused editor
392   RefPtr<ITfDocumentMgr> mDocumentMgr;
393   // Edit cookie associated with the current editing context
394   DWORD mEditCookie;
395   // Editing context at the bottom of mDocumentMgr's context stack
396   RefPtr<ITfContext> mContext;
397   // Currently installed notification sink
398   RefPtr<ITextStoreACPSink> mSink;
399   // TS_AS_* mask of what events to notify
400   DWORD mSinkMask;
401   // 0 if not locked, otherwise TS_LF_* indicating the current lock
402   DWORD mLock;
403   // 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock
404   DWORD mLockQueued;
405 
406   uint32_t mHandlingKeyMessage;
OnStartToHandleKeyMessage()407   void OnStartToHandleKeyMessage() {
408     // If we're starting to handle another key message during handling a
409     // key message, let's assume that the handling key message is handled by
410     // TIP and it sends another key message for hacking something.
411     // Let's try to dispatch a keyboard event now.
412     // FYI: All callers of this method grab this instance with local variable.
413     //      So, even after calling MaybeDispatchKeyboardEventAsProcessedByIME(),
414     //      we're safe to access any members.
415     if (!mDestroyed && sHandlingKeyMsg && !sIsKeyboardEventDispatched) {
416       MaybeDispatchKeyboardEventAsProcessedByIME();
417     }
418     ++mHandlingKeyMessage;
419   }
OnEndHandlingKeyMessage(bool aIsProcessedByTSF)420   void OnEndHandlingKeyMessage(bool aIsProcessedByTSF) {
421     // If sHandlingKeyMsg has been handled by TSF or TIP and we're still
422     // alive, but we haven't dispatch keyboard event for it, let's fire it now.
423     // FYI: All callers of this method grab this instance with local variable.
424     //      So, even after calling MaybeDispatchKeyboardEventAsProcessedByIME(),
425     //      we're safe to access any members.
426     if (!mDestroyed && sHandlingKeyMsg && aIsProcessedByTSF &&
427         !sIsKeyboardEventDispatched) {
428       MaybeDispatchKeyboardEventAsProcessedByIME();
429     }
430     MOZ_ASSERT(mHandlingKeyMessage);
431     if (--mHandlingKeyMessage) {
432       return;
433     }
434     // If TSFTextStore instance is destroyed during handling key message(s),
435     // release all TSF objects when all nested key messages have been handled.
436     if (mDestroyed) {
437       ReleaseTSFObjects();
438     }
439   }
440 
441   /**
442    * MaybeDispatchKeyboardEventAsProcessedByIME() tries to dispatch eKeyDown
443    * event or eKeyUp event for sHandlingKeyMsg and marking the dispatching
444    * event as "processed by IME".  Note that if the document is locked, this
445    * just adds a pending action into the queue and sets
446    * sIsKeyboardEventDispatched to true.
447    */
448   void MaybeDispatchKeyboardEventAsProcessedByIME();
449 
450   /**
451    * DispatchKeyboardEventAsProcessedByIME() dispatches an eKeyDown or
452    * eKeyUp event with NativeKey class and aMsg.
453    */
454   void DispatchKeyboardEventAsProcessedByIME(const MSG& aMsg);
455 
456   class Composition final {
457    public:
458     // nullptr if no composition is active, otherwise the current composition
459     RefPtr<ITfCompositionView> mView;
460 
461     // Current copy of the active composition string. Only mString is
462     // changed during a InsertTextAtSelection call if we have a composition.
463     // mString acts as a buffer until OnUpdateComposition is called
464     // and mString is flushed to editor through eCompositionChange.
465     // This way all changes are updated in batches to avoid
466     // inconsistencies/artifacts.
467     nsString mString;
468 
469     // The start of the current active composition, in ACP offsets
470     LONG mStart;
471 
IsComposing()472     bool IsComposing() const { return (mView != nullptr); }
473 
EndOffset()474     LONG EndOffset() const {
475       return mStart + static_cast<LONG>(mString.Length());
476     }
477 
478     // Start() and End() updates the members for emulating the latest state.
479     // Unless flush the pending actions, this data never matches the actual
480     // content.
481     void Start(ITfCompositionView* aCompositionView,
482                LONG aCompositionStartOffset,
483                const nsAString& aCompositionString);
484     void End();
485   };
486   // While the document is locked, we cannot dispatch any events which cause
487   // DOM events since the DOM events' handlers may modify the locked document.
488   // However, even while the document is locked, TSF may queries us.
489   // For that, TSFTextStore modifies mComposition even while the document is
490   // locked.  With mComposition, query methods can returns the text content
491   // information.
492   Composition mComposition;
493 
494   /**
495    * IsHandlingComposition() returns true if there is a composition in the
496    * focused editor.
497    */
IsHandlingComposition()498   bool IsHandlingComposition() const {
499     return mDispatcher && mDispatcher->IsHandlingComposition();
500   }
501 
502   class Selection {
503    public:
Selection()504     Selection() : mDirty(true) {}
505 
IsDirty()506     bool IsDirty() const { return mDirty; };
MarkDirty()507     void MarkDirty() { mDirty = true; }
508 
ACP()509     TS_SELECTION_ACP& ACP() {
510       MOZ_ASSERT(!mDirty);
511       return mACP;
512     }
513 
SetSelection(const TS_SELECTION_ACP & aSelection)514     void SetSelection(const TS_SELECTION_ACP& aSelection) {
515       mDirty = false;
516       mACP = aSelection;
517       // Selection end must be active in our editor.
518       if (mACP.style.ase != TS_AE_START) {
519         mACP.style.ase = TS_AE_END;
520       }
521       // We're not support interim char selection for now.
522       // XXX Probably, this is necessary for supporting South Asian languages.
523       mACP.style.fInterimChar = FALSE;
524     }
525 
SetSelection(uint32_t aStart,uint32_t aLength,bool aReversed,WritingMode aWritingMode)526     bool SetSelection(uint32_t aStart, uint32_t aLength, bool aReversed,
527                       WritingMode aWritingMode) {
528       bool changed = mDirty || mACP.acpStart != static_cast<LONG>(aStart) ||
529                      mACP.acpEnd != static_cast<LONG>(aStart + aLength);
530 
531       mDirty = false;
532       mACP.acpStart = static_cast<LONG>(aStart);
533       mACP.acpEnd = static_cast<LONG>(aStart + aLength);
534       mACP.style.ase = aReversed ? TS_AE_START : TS_AE_END;
535       mACP.style.fInterimChar = FALSE;
536       mWritingMode = aWritingMode;
537 
538       return changed;
539     }
540 
IsCollapsed()541     bool IsCollapsed() const {
542       MOZ_ASSERT(!mDirty);
543       return (mACP.acpStart == mACP.acpEnd);
544     }
545 
CollapseAt(uint32_t aOffset)546     void CollapseAt(uint32_t aOffset) {
547       // XXX This does not update the selection's mWritingMode.
548       // If it is ever used to "collapse" to an entirely new location,
549       // we may need to fix that.
550       mDirty = false;
551       mACP.acpStart = mACP.acpEnd = static_cast<LONG>(aOffset);
552       mACP.style.ase = TS_AE_END;
553       mACP.style.fInterimChar = FALSE;
554     }
555 
MinOffset()556     LONG MinOffset() const {
557       MOZ_ASSERT(!mDirty);
558       LONG min = std::min(mACP.acpStart, mACP.acpEnd);
559       MOZ_ASSERT(min >= 0);
560       return min;
561     }
562 
MaxOffset()563     LONG MaxOffset() const {
564       MOZ_ASSERT(!mDirty);
565       LONG max = std::max(mACP.acpStart, mACP.acpEnd);
566       MOZ_ASSERT(max >= 0);
567       return max;
568     }
569 
StartOffset()570     LONG StartOffset() const {
571       MOZ_ASSERT(!mDirty);
572       MOZ_ASSERT(mACP.acpStart >= 0);
573       return mACP.acpStart;
574     }
575 
EndOffset()576     LONG EndOffset() const {
577       MOZ_ASSERT(!mDirty);
578       MOZ_ASSERT(mACP.acpEnd >= 0);
579       return mACP.acpEnd;
580     }
581 
Length()582     LONG Length() const {
583       MOZ_ASSERT(!mDirty);
584       MOZ_ASSERT(mACP.acpEnd >= mACP.acpStart);
585       return std::abs(mACP.acpEnd - mACP.acpStart);
586     }
587 
IsReversed()588     bool IsReversed() const {
589       MOZ_ASSERT(!mDirty);
590       return (mACP.style.ase == TS_AE_START);
591     }
592 
ActiveSelEnd()593     TsActiveSelEnd ActiveSelEnd() const {
594       MOZ_ASSERT(!mDirty);
595       return mACP.style.ase;
596     }
597 
IsInterimChar()598     bool IsInterimChar() const {
599       MOZ_ASSERT(!mDirty);
600       return (mACP.style.fInterimChar != FALSE);
601     }
602 
GetWritingMode()603     WritingMode GetWritingMode() const {
604       MOZ_ASSERT(!mDirty);
605       return mWritingMode;
606     }
607 
EqualsExceptDirection(const TS_SELECTION_ACP & aACP)608     bool EqualsExceptDirection(const TS_SELECTION_ACP& aACP) const {
609       if (mACP.style.ase == aACP.style.ase) {
610         return mACP.acpStart == aACP.acpStart && mACP.acpEnd == aACP.acpEnd;
611       }
612       return mACP.acpStart == aACP.acpEnd && mACP.acpEnd == aACP.acpStart;
613     }
614 
EqualsExceptDirection(const SelectionChangeDataBase & aChangedSelection)615     bool EqualsExceptDirection(
616         const SelectionChangeDataBase& aChangedSelection) const {
617       MOZ_ASSERT(!mDirty);
618       MOZ_ASSERT(aChangedSelection.IsValid());
619       return aChangedSelection.Length() == static_cast<uint32_t>(Length()) &&
620              aChangedSelection.mOffset == static_cast<uint32_t>(StartOffset());
621     }
622 
623    private:
624     TS_SELECTION_ACP mACP;
625     WritingMode mWritingMode;
626     bool mDirty;
627   };
628   // Don't access mSelection directly except at calling MarkDirty().
629   // Use SelectionForTSFRef() instead.  This is modified immediately when
630   // TSF requests to set selection and not updated by selection change in
631   // content until mContentForTSF is cleared.
632   Selection mSelectionForTSF;
633 
634   /**
635    * Get the selection expected by TSF.  If mSelectionForTSF is already valid,
636    * this just return the reference to it.  Otherwise, this initializes it
637    * with eQuerySelectedText.  Please check if the result is valid before
638    * actually using it.
639    * Note that this is also called by ContentForTSFRef().
640    */
641   Selection& SelectionForTSFRef();
642 
643   class MOZ_STACK_CLASS AutoSetTemporarySelection final {
644    public:
AutoSetTemporarySelection(Selection & aSelection)645     explicit AutoSetTemporarySelection(Selection& aSelection)
646         : mSelection(aSelection) {
647       mDirty = mSelection.IsDirty();
648       if (mDirty) {
649         mSelection.CollapseAt(0);
650       }
651     }
652 
~AutoSetTemporarySelection()653     ~AutoSetTemporarySelection() {
654       if (mDirty) {
655         mSelection.MarkDirty();
656       }
657     }
658 
659    private:
660     Selection& mSelection;
661     bool mDirty;
662   };
663 
664   struct PendingAction final {
665     enum class Type : uint8_t {
666       eCompositionStart,
667       eCompositionUpdate,
668       eCompositionEnd,
669       eSetSelection,
670       eKeyboardEvent,
671     };
672     Type mType;
673     // For eCompositionStart, eCompositionEnd and eSetSelection
674     LONG mSelectionStart;
675     // For eCompositionStart and eSetSelection
676     LONG mSelectionLength;
677     // For eCompositionStart, eCompositionUpdate and eCompositionEnd
678     nsString mData;
679     // For eCompositionUpdate
680     RefPtr<TextRangeArray> mRanges;
681     // For eKeyboardEvent
682     MSG mKeyMsg;
683     // For eSetSelection
684     bool mSelectionReversed;
685     // For eCompositionUpdate
686     bool mIncomplete;
687     // For eCompositionStart
688     bool mAdjustSelection;
689   };
690   // Items of mPendingActions are appended when TSF tells us to need to dispatch
691   // DOM composition events.  However, we cannot dispatch while the document is
692   // locked because it can cause modifying the locked document.  So, the pending
693   // actions should be performed when document lock is unlocked.
694   nsTArray<PendingAction> mPendingActions;
695 
LastOrNewPendingCompositionUpdate()696   PendingAction* LastOrNewPendingCompositionUpdate() {
697     if (!mPendingActions.IsEmpty()) {
698       PendingAction& lastAction = mPendingActions.LastElement();
699       if (lastAction.mType == PendingAction::Type::eCompositionUpdate) {
700         return &lastAction;
701       }
702     }
703     PendingAction* newAction = mPendingActions.AppendElement();
704     newAction->mType = PendingAction::Type::eCompositionUpdate;
705     newAction->mRanges = new TextRangeArray();
706     newAction->mIncomplete = true;
707     return newAction;
708   }
709 
710   /**
711    * IsLastPendingActionCompositionEndAt() checks whether the previous pending
712    * action is committing composition whose range starts from aStart and its
713    * length is aLength.  In other words, this checks whether new composition
714    * which will replace same range as previous pending commit can be merged
715    * with the previous composition.
716    *
717    * @param aStart              The inserted offset you expected.
718    * @param aLength             The inserted text length you expected.
719    * @return                    true if the last pending action is
720    *                            eCompositionEnd and it inserted the text
721    *                            between aStart and aStart + aLength.
722    */
IsLastPendingActionCompositionEndAt(LONG aStart,LONG aLength)723   bool IsLastPendingActionCompositionEndAt(LONG aStart, LONG aLength) const {
724     if (mPendingActions.IsEmpty()) {
725       return false;
726     }
727     const PendingAction& pendingLastAction = mPendingActions.LastElement();
728     return pendingLastAction.mType == PendingAction::Type::eCompositionEnd &&
729            pendingLastAction.mSelectionStart == aStart &&
730            pendingLastAction.mData.Length() == static_cast<ULONG>(aLength);
731   }
732 
IsPendingCompositionUpdateIncomplete()733   bool IsPendingCompositionUpdateIncomplete() const {
734     if (mPendingActions.IsEmpty()) {
735       return false;
736     }
737     const PendingAction& lastAction = mPendingActions.LastElement();
738     return lastAction.mType == PendingAction::Type::eCompositionUpdate &&
739            lastAction.mIncomplete;
740   }
741 
CompleteLastActionIfStillIncomplete()742   void CompleteLastActionIfStillIncomplete() {
743     if (!IsPendingCompositionUpdateIncomplete()) {
744       return;
745     }
746     RecordCompositionUpdateAction();
747   }
748 
RemoveLastCompositionUpdateActions()749   void RemoveLastCompositionUpdateActions() {
750     while (!mPendingActions.IsEmpty()) {
751       const PendingAction& lastAction = mPendingActions.LastElement();
752       if (lastAction.mType != PendingAction::Type::eCompositionUpdate) {
753         break;
754       }
755       mPendingActions.RemoveLastElement();
756     }
757   }
758 
759   // When On*Composition() is called without document lock, we need to flush
760   // the recorded actions at quitting the method.
761   // AutoPendingActionAndContentFlusher class is usedful for it.
762   class MOZ_STACK_CLASS AutoPendingActionAndContentFlusher final {
763    public:
AutoPendingActionAndContentFlusher(TSFTextStore * aTextStore)764     explicit AutoPendingActionAndContentFlusher(TSFTextStore* aTextStore)
765         : mTextStore(aTextStore) {
766       MOZ_ASSERT(!mTextStore->mIsRecordingActionsWithoutLock);
767       if (!mTextStore->IsReadWriteLocked()) {
768         mTextStore->mIsRecordingActionsWithoutLock = true;
769       }
770     }
771 
~AutoPendingActionAndContentFlusher()772     ~AutoPendingActionAndContentFlusher() {
773       if (!mTextStore->mIsRecordingActionsWithoutLock) {
774         return;
775       }
776       mTextStore->FlushPendingActions();
777       mTextStore->mIsRecordingActionsWithoutLock = false;
778     }
779 
780    private:
AutoPendingActionAndContentFlusher()781     AutoPendingActionAndContentFlusher() {}
782 
783     RefPtr<TSFTextStore> mTextStore;
784   };
785 
786   class Content final {
787    public:
Content(TSFTextStore::Composition & aComposition,TSFTextStore::Selection & aSelection)788     Content(TSFTextStore::Composition& aComposition,
789             TSFTextStore::Selection& aSelection)
790         : mComposition(aComposition), mSelection(aSelection) {
791       Clear();
792     }
793 
Clear()794     void Clear() {
795       mText.Truncate();
796       mLastCompositionString.Truncate();
797       mLastCompositionStart = -1;
798       mInitialized = false;
799     }
800 
IsInitialized()801     bool IsInitialized() const { return mInitialized; }
802 
Init(const nsAString & aText)803     void Init(const nsAString& aText) {
804       mText = aText;
805       if (mComposition.IsComposing()) {
806         mLastCompositionString = mComposition.mString;
807         mLastCompositionStart = mComposition.mStart;
808       } else {
809         mLastCompositionString.Truncate();
810         mLastCompositionStart = -1;
811       }
812       mMinTextModifiedOffset = NOT_MODIFIED;
813       mLatestCompositionStartOffset = mLatestCompositionEndOffset = LONG_MAX;
814       mInitialized = true;
815     }
816 
OnLayoutChanged()817     void OnLayoutChanged() { mMinTextModifiedOffset = NOT_MODIFIED; }
818 
819     // OnCompositionEventsHandled() is called when all pending composition
820     // events are handled in the focused content which may be in a remote
821     // process.
OnCompositionEventsHandled()822     void OnCompositionEventsHandled() {
823       if (!mInitialized) {
824         return;
825       }
826       if (mComposition.IsComposing()) {
827         mLastCompositionString = mComposition.mString;
828         mLastCompositionStart = mComposition.mStart;
829       } else {
830         mLastCompositionString.Truncate();
831         mLastCompositionStart = -1;
832       }
833     }
834 
835     const nsDependentSubstring GetSelectedText() const;
836     const nsDependentSubstring GetSubstring(uint32_t aStart,
837                                             uint32_t aLength) const;
838     void ReplaceSelectedTextWith(const nsAString& aString);
839     void ReplaceTextWith(LONG aStart, LONG aLength, const nsAString& aString);
840 
841     void StartComposition(ITfCompositionView* aCompositionView,
842                           const PendingAction& aCompStart,
843                           bool aPreserveSelection);
844     /**
845      * RestoreCommittedComposition() restores the committed string as
846      * composing string.  If InsertTextAtSelection() or something is called
847      * before a call of OnStartComposition() or previous composition is
848      * committed and new composition is restarted to clean up the commited
849      * string, there is a pending compositionend.  In this case, we need to
850      * cancel the pending compositionend and continue the composition.
851      *
852      * @param aCompositionView          The composition view.
853      * @param aCanceledCompositionEnd   The pending compositionend which is
854      *                                  canceled for restarting the composition.
855      */
856     void RestoreCommittedComposition(
857         ITfCompositionView* aCompositionView,
858         const PendingAction& aCanceledCompositionEnd);
859     void EndComposition(const PendingAction& aCompEnd);
860 
Text()861     const nsString& Text() const {
862       MOZ_ASSERT(mInitialized);
863       return mText;
864     }
LastCompositionString()865     const nsString& LastCompositionString() const {
866       MOZ_ASSERT(mInitialized);
867       return mLastCompositionString;
868     }
LastCompositionStringEndOffset()869     LONG LastCompositionStringEndOffset() const {
870       MOZ_ASSERT(mInitialized);
871       MOZ_ASSERT(WasLastComposition());
872       return mLastCompositionStart + mLastCompositionString.Length();
873     }
WasLastComposition()874     bool WasLastComposition() const {
875       MOZ_ASSERT(mInitialized);
876       return mLastCompositionStart >= 0;
877     }
MinTextModifiedOffset()878     uint32_t MinTextModifiedOffset() const {
879       MOZ_ASSERT(mInitialized);
880       return mMinTextModifiedOffset;
881     }
LatestCompositionStartOffset()882     LONG LatestCompositionStartOffset() const {
883       MOZ_ASSERT(mInitialized);
884       MOZ_ASSERT(HasOrHadComposition());
885       return mLatestCompositionStartOffset;
886     }
LatestCompositionEndOffset()887     LONG LatestCompositionEndOffset() const {
888       MOZ_ASSERT(mInitialized);
889       MOZ_ASSERT(HasOrHadComposition());
890       return mLatestCompositionEndOffset;
891     }
892 
893     // Returns true if layout of the character at the aOffset has not been
894     // calculated.
IsLayoutChangedAt(uint32_t aOffset)895     bool IsLayoutChangedAt(uint32_t aOffset) const {
896       return IsLayoutChanged() && (mMinTextModifiedOffset <= aOffset);
897     }
898     // Returns true if layout of the content has been changed, i.e., the new
899     // layout has not been calculated.
IsLayoutChanged()900     bool IsLayoutChanged() const {
901       return mInitialized && (mMinTextModifiedOffset != NOT_MODIFIED);
902     }
903     // Returns minimum offset of modified text range.
MinOffsetOfLayoutChanged()904     uint32_t MinOffsetOfLayoutChanged() const {
905       return mInitialized ? mMinTextModifiedOffset : NOT_MODIFIED;
906     }
907 
HasOrHadComposition()908     bool HasOrHadComposition() const {
909       return mInitialized && mLatestCompositionStartOffset != LONG_MAX &&
910              mLatestCompositionEndOffset != LONG_MAX;
911     }
912 
Composition()913     TSFTextStore::Composition& Composition() { return mComposition; }
Selection()914     TSFTextStore::Selection& Selection() { return mSelection; }
915 
916    private:
917     nsString mText;
918     // mLastCompositionString stores the composition string when the document
919     // is locked. This is necessary to compute mMinTextModifiedOffset.
920     nsString mLastCompositionString;
921     TSFTextStore::Composition& mComposition;
922     TSFTextStore::Selection& mSelection;
923 
924     // mLastCompositionStart stores the start offset of composition when
925     // mLastCompositionString is set.
926     LONG mLastCompositionStart;
927 
928     // The latest composition's start and end offset.  If composition hasn't
929     // been started since this instance is initialized, they are LONG_MAX.
930     LONG mLatestCompositionStartOffset;
931     LONG mLatestCompositionEndOffset;
932 
933     // The minimum offset of modified part of the text.
934     enum : uint32_t { NOT_MODIFIED = UINT32_MAX };
935     uint32_t mMinTextModifiedOffset;
936 
937     bool mInitialized;
938   };
939   // mContentForTSF is cache of content.  The information is expected by TSF
940   // and TIP.  Therefore, this is useful for answering the query from TSF or
941   // TIP.
942   // This is initialized by ContentForTSFRef() automatically (therefore, don't
943   // access this member directly except at calling Clear(), IsInitialized(),
944   // IsLayoutChangeAfter() or IsLayoutChanged()).
945   // This is cleared when:
946   //  - When there is no composition, the document is unlocked.
947   //  - When there is a composition, all dispatched events are handled by
948   //    the focused editor which may be in a remote process.
949   // So, if two compositions are created very quickly, this cache may not be
950   // cleared between eCompositionCommit(AsIs) and eCompositionStart.
951   Content mContentForTSF;
952 
953   Content& ContentForTSFRef();
954 
955   // CanAccessActualContentDirectly() returns true when TSF/TIP can access
956   // actual content directly.  In other words, mContentForTSF and/or
957   // mSelectionForTSF doesn't cache content or they matches with actual
958   // contents due to no pending text/selection change notifications.
959   bool CanAccessActualContentDirectly() const;
960 
961   // While mContentForTSF is valid, this returns the text stored by it.
962   // Otherwise, return the current text content retrieved by eQueryTextContent.
963   bool GetCurrentText(nsAString& aTextContent);
964 
965   class MouseTracker final {
966    public:
967     static const DWORD kInvalidCookie = static_cast<DWORD>(-1);
968 
969     MouseTracker();
970 
971     HRESULT Init(TSFTextStore* aTextStore);
972     HRESULT AdviseSink(TSFTextStore* aTextStore, ITfRangeACP* aTextRange,
973                        ITfMouseSink* aMouseSink);
974     void UnadviseSink();
975 
IsUsing()976     bool IsUsing() const { return mSink != nullptr; }
InRange(uint32_t aOffset)977     bool InRange(uint32_t aOffset) const {
978       if (NS_WARN_IF(mStart < 0) || NS_WARN_IF(mLength <= 0)) {
979         return false;
980       }
981       return aOffset >= static_cast<uint32_t>(mStart) &&
982              aOffset < static_cast<uint32_t>(mStart + mLength);
983     }
Cookie()984     DWORD Cookie() const { return mCookie; }
985     bool OnMouseButtonEvent(ULONG aEdge, ULONG aQuadrant, DWORD aButtonStatus);
RangeStart()986     LONG RangeStart() const { return mStart; }
987 
988    private:
989     RefPtr<ITfMouseSink> mSink;
990     LONG mStart;
991     LONG mLength;
992     DWORD mCookie;
993   };
994   // mMouseTrackers is an array to store each information of installed
995   // ITfMouseSink instance.
996   nsTArray<MouseTracker> mMouseTrackers;
997 
998   // The input scopes for this context, defaults to IS_DEFAULT.
999   nsTArray<InputScope> mInputScopes;
1000 
1001   // Support retrieving attributes.
1002   // TODO: We should support RightToLeft, perhaps.
1003   enum {
1004     // Used for result of GetRequestedAttrIndex()
1005     eNotSupported = -1,
1006 
1007     // Supported attributes
1008     eInputScope = 0,
1009     eTextVerticalWriting,
1010     eTextOrientation,
1011 
1012     // Count of the supported attributes
1013     NUM_OF_SUPPORTED_ATTRS
1014   };
1015   bool mRequestedAttrs[NUM_OF_SUPPORTED_ATTRS];
1016 
1017   int32_t GetRequestedAttrIndex(const TS_ATTRID& aAttrID);
1018   TS_ATTRID GetAttrID(int32_t aIndex);
1019 
1020   bool mRequestedAttrValues;
1021 
1022   // If edit actions are being recorded without document lock, this is true.
1023   // Otherwise, false.
1024   bool mIsRecordingActionsWithoutLock;
1025   // If GetTextExt() or GetACPFromPoint() is called and the layout hasn't been
1026   // calculated yet, these methods return TS_E_NOLAYOUT.  At that time,
1027   // mHasReturnedNoLayoutError is set to true.
1028   bool mHasReturnedNoLayoutError;
1029   // Before calling ITextStoreACPSink::OnLayoutChange() and
1030   // ITfContextOwnerServices::OnLayoutChange(), mWaitingQueryLayout is set to
1031   // true.  This is set to  false when GetTextExt() or GetACPFromPoint() is
1032   // called.
1033   bool mWaitingQueryLayout;
1034   // During the documet is locked, we shouldn't destroy the instance.
1035   // If this is true, the instance will be destroyed after unlocked.
1036   bool mPendingDestroy;
1037   // If this is false, MaybeFlushPendingNotifications() will clear the
1038   // mContentForTSF.
1039   bool mDeferClearingContentForTSF;
1040   // While the instance is dispatching events, the event may not be handled
1041   // synchronously in e10s mode.  So, in such case, in strictly speaking,
1042   // we shouldn't query layout information.  However, TS_E_NOLAYOUT bugs of
1043   // ITextStoreAPC::GetTextExt() blocks us to behave ideally.
1044   // For preventing it to be called, we should put off notifying TSF of
1045   // anything until layout information becomes available.
1046   bool mDeferNotifyingTSF;
1047   // While the document is locked, committing composition always fails since
1048   // TSF needs another document lock for modifying the composition, selection
1049   // and etc.  So, committing composition should be performed after the
1050   // document is unlocked.
1051   bool mDeferCommittingComposition;
1052   bool mDeferCancellingComposition;
1053   // Immediately after a call of Destroy(), mDestroyed becomes true.  If this
1054   // is true, the instance shouldn't grant any requests from the TIP anymore.
1055   bool mDestroyed;
1056   // While the instance is being destroyed, this is set to true for avoiding
1057   // recursive Destroy() calls.
1058   bool mBeingDestroyed;
1059 
1060   // TSF thread manager object for the current application
1061   static StaticRefPtr<ITfThreadMgr> sThreadMgr;
1062   static already_AddRefed<ITfThreadMgr> GetThreadMgr();
1063   // sMessagePump is QI'ed from sThreadMgr
1064   static StaticRefPtr<ITfMessagePump> sMessagePump;
1065 
1066  public:
1067   // Expose GetMessagePump() for WinUtils.
1068   static already_AddRefed<ITfMessagePump> GetMessagePump();
1069 
1070  private:
1071   // sKeystrokeMgr is QI'ed from sThreadMgr
1072   static StaticRefPtr<ITfKeystrokeMgr> sKeystrokeMgr;
1073   // TSF display attribute manager
1074   static StaticRefPtr<ITfDisplayAttributeMgr> sDisplayAttrMgr;
1075   static already_AddRefed<ITfDisplayAttributeMgr> GetDisplayAttributeMgr();
1076   // TSF category manager
1077   static StaticRefPtr<ITfCategoryMgr> sCategoryMgr;
1078   static already_AddRefed<ITfCategoryMgr> GetCategoryMgr();
1079   // Compartment for (Get|Set)IMEOpenState()
1080   static StaticRefPtr<ITfCompartment> sCompartmentForOpenClose;
1081   static already_AddRefed<ITfCompartment> GetCompartmentForOpenClose();
1082 
1083   // Current text store which is managing a keyboard enabled editor (i.e.,
1084   // editable editor).  Currently only ONE TSFTextStore instance is ever used,
1085   // although Create is called when an editor is focused and Destroy called
1086   // when the focused editor is blurred.
1087   static StaticRefPtr<TSFTextStore> sEnabledTextStore;
1088 
1089   // For IME (keyboard) disabled state:
1090   static StaticRefPtr<ITfDocumentMgr> sDisabledDocumentMgr;
1091   static StaticRefPtr<ITfContext> sDisabledContext;
1092 
1093   static StaticRefPtr<ITfInputProcessorProfiles> sInputProcessorProfiles;
1094   static already_AddRefed<ITfInputProcessorProfiles>
1095   GetInputProcessorProfiles();
1096 
1097   // Handling key message.
1098   static const MSG* sHandlingKeyMsg;
1099 
1100   // TSF client ID for the current application
1101   static DWORD sClientId;
1102 
1103   // true if an eKeyDown or eKeyUp event for sHandlingKeyMsg has already
1104   // been dispatched.
1105   static bool sIsKeyboardEventDispatched;
1106 };
1107 
1108 }  // namespace widget
1109 }  // namespace mozilla
1110 
1111 #endif  // #ifndef TSFTextStore_h_
1112