1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #ifndef IMContextWrapper_h_
9 #define IMContextWrapper_h_
10 
11 #include <gdk/gdk.h>
12 #include <gtk/gtk.h>
13 
14 #include "nsString.h"
15 #include "nsCOMPtr.h"
16 #include "nsTArray.h"
17 #include "nsIWidget.h"
18 #include "mozilla/CheckedInt.h"
19 #include "mozilla/EventForwards.h"
20 #include "mozilla/TextEventDispatcherListener.h"
21 #include "WritingModes.h"
22 
23 class nsWindow;
24 
25 namespace mozilla {
26 namespace widget {
27 
28 /**
29  * KeyHandlingState is result of IMContextWrapper::OnKeyEvent().
30  */
31 enum class KeyHandlingState {
32   // The native key event has not been handled by IMContextWrapper.
33   eNotHandled,
34   // The native key event was handled by IMContextWrapper.
35   eHandled,
36   // The native key event has not been handled by IMContextWrapper,
37   // but eKeyDown or eKeyUp event has been dispatched.
38   eNotHandledButEventDispatched,
39   // The native key event has not been handled by IMContextWrapper,
40   // but eKeyDown or eKeyUp event has been dispatched and consumed.
41   eNotHandledButEventConsumed,
42 };
43 
44 class IMContextWrapper final : public TextEventDispatcherListener {
45  public:
46   // TextEventDispatcherListener implementation
47   NS_DECL_ISUPPORTS
48 
49   NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher,
50                        const IMENotification& aNotification) override;
51   NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override;
52   NS_IMETHOD_(void)
53   OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override;
54   NS_IMETHOD_(void)
55   WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher,
56                             WidgetKeyboardEvent& aKeyboardEvent,
57                             uint32_t aIndexOfKeypress, void* aData) override;
58 
59  public:
60   // aOwnerWindow is a pointer of the owner window.  When aOwnerWindow is
61   // destroyed, the related IME contexts are released (i.e., IME cannot be
62   // used with the instance after that).
63   explicit IMContextWrapper(nsWindow* aOwnerWindow);
64 
65   // Called when the process is being shut down.
66   static void Shutdown();
67 
68   // "Enabled" means the users can use all IMEs.
69   // I.e., the focus is in the normal editors.
70   bool IsEnabled() const;
71 
72   // OnFocusWindow is a notification that aWindow is going to be focused.
73   void OnFocusWindow(nsWindow* aWindow);
74   // OnBlurWindow is a notification that aWindow is going to be unfocused.
75   void OnBlurWindow(nsWindow* aWindow);
76   // OnDestroyWindow is a notification that aWindow is going to be destroyed.
77   void OnDestroyWindow(nsWindow* aWindow);
78   // OnFocusChangeInGecko is a notification that an editor gets focus.
79   void OnFocusChangeInGecko(bool aFocus);
80   // OnSelectionChange is a notification that selection (caret) is changed
81   // in the focused editor.
82   void OnSelectionChange(nsWindow* aCaller,
83                          const IMENotification& aIMENotification);
84   // OnThemeChanged is called when desktop theme is changed.
85   static void OnThemeChanged();
86 
87   /**
88    * OnKeyEvent() is called when aWindow gets a native key press event or a
89    * native key release event.  If this returns true, the key event was
90    * filtered by IME.  Otherwise, this returns false.
91    * NOTE: When the native key press event starts composition, this returns
92    *       true but dispatches an eKeyDown event or eKeyUp event before
93    *       dispatching composition events or content command event.
94    *
95    * @param aWindow                       A window on which user operate the
96    *                                      key.
97    * @param aEvent                        A native key press or release
98    *                                      event.
99    * @param aKeyboardEventWasDispatched   true if eKeyDown or eKeyUp event
100    *                                      for aEvent has already been
101    *                                      dispatched.  In this case,
102    *                                      this class doesn't dispatch
103    *                                      keyboard event anymore.
104    */
105   KeyHandlingState OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent,
106                               bool aKeyboardEventWasDispatched = false);
107 
108   // IME related nsIWidget methods.
109   nsresult EndIMEComposition(nsWindow* aCaller);
110   void SetInputContext(nsWindow* aCaller, const InputContext* aContext,
111                        const InputContextAction* aAction);
112   InputContext GetInputContext();
113   void OnUpdateComposition();
114   void OnLayoutChange();
115 
116   TextEventDispatcher* GetTextEventDispatcher();
117 
118   // TODO: Typically, new IM comes every several years.  And now, our code
119   //       becomes really IM behavior dependent.  So, perhaps, we need prefs
120   //       to control related flags for IM developers.
121   enum class IMContextID : uint8_t {
122     Fcitx,  // 4.x or earlier
123     Fcitx5,
124     IBus,
125     IIIMF,
126     Scim,
127     Uim,
128     Wayland,
129     Unknown,
130   };
131 
132   friend std::ostream& operator<<(std::ostream& aStream,
133                                   const IMContextID& aIMContextID) {
134     switch (aIMContextID) {
135       case IMContextID::Fcitx:
136         return aStream << "Fcitx";
137       case IMContextID::Fcitx5:
138         return aStream << "Fcitx5";
139       case IMContextID::IBus:
140         return aStream << "IBus";
141       case IMContextID::IIIMF:
142         return aStream << "IIIMF";
143       case IMContextID::Scim:
144         return aStream << "Scim";
145       case IMContextID::Uim:
146         return aStream << "Uim";
147       case IMContextID::Wayland:
148         return aStream << "Wayland";
149       case IMContextID::Unknown:
150         return aStream << "Unknown";
151     }
152     MOZ_ASSERT_UNREACHABLE("Add new case for the new IM support");
153     return aStream << "Unknown";
154   }
155 
156   /**
157    * GetIMName() returns IM name associated with mContext.  If the context is
158    * xim, this look for actual engine from XMODIFIERS environment variable.
159    */
160   nsDependentCSubstring GetIMName() const;
161 
162   /**
163    * GetWaitingSynthesizedKeyPressHardwareKeyCode() returns hardware_keycode
164    * value of last handled GDK_KEY_PRESS event which is probable handled by
165    * IME asynchronously and we have not received synthesized GDK_KEY_PRESS
166    * event yet.
167    */
GetWaitingSynthesizedKeyPressHardwareKeyCode()168   static guint16 GetWaitingSynthesizedKeyPressHardwareKeyCode() {
169     return sWaitingSynthesizedKeyPressHardwareKeyCode;
170   }
171 
172  protected:
173   ~IMContextWrapper();
174 
175   // Owner of an instance of this class. This should be top level window.
176   // The owner window must release the contexts when it's destroyed because
177   // the IME contexts need the native window.  If OnDestroyWindow() is called
178   // with the owner window, it'll release IME contexts.  Otherwise, it'll
179   // just clean up any existing composition if it's related to the destroying
180   // child window.
181   nsWindow* mOwnerWindow;
182 
183   // A last focused window in this class's context.
184   nsWindow* mLastFocusedWindow;
185 
186   // Actual context. This is used for handling the user's input.
187   GtkIMContext* mContext;
188 
189   // mSimpleContext is used for the password field and
190   // the |ime-mode: disabled;| editors if sUseSimpleContext is true.
191   // These editors disable IME.  But dead keys should work.  Fortunately,
192   // the simple IM context of GTK2 support only them.
193   GtkIMContext* mSimpleContext;
194 
195   // mDummyContext is a dummy context and will be used in Focus()
196   // when the state of mEnabled means disabled.  This context's IME state is
197   // always "closed", so it closes IME forcedly.
198   GtkIMContext* mDummyContext;
199 
200   // mComposingContext is not nullptr while one of mContext, mSimpleContext
201   // and mDummyContext has composition.
202   // XXX: We don't assume that two or more context have composition same time.
203   GtkIMContext* mComposingContext;
204 
205   // IME enabled state and other things defined in InputContext.
206   // Use following helper methods if you don't need the detail of the status.
207   InputContext mInputContext;
208 
209   // mCompositionStart is the start offset of the composition string in the
210   // current content.  When <textarea> or <input> have focus, it means offset
211   // from the first character of them.  When a HTML editor has focus, it
212   // means offset from the first character of the root element of the editor.
213   uint32_t mCompositionStart;
214 
215   // mDispatchedCompositionString is the latest composition string which
216   // was dispatched by compositionupdate event.
217   nsString mDispatchedCompositionString;
218 
219   // mSelectedStringRemovedByComposition is the selected string which was
220   // removed by first compositionchange event.
221   nsString mSelectedStringRemovedByComposition;
222 
223   // OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native
224   // event.
225   GdkEventKey* mProcessingKeyEvent;
226 
227   /**
228    * GdkEventKeyQueue stores *copy* of GdkEventKey instances.  However, this
229    * must be safe to our usecase since it has |time| and the value should not
230    * be same as older event.
231    */
232   class GdkEventKeyQueue final {
233    public:
~GdkEventKeyQueue()234     ~GdkEventKeyQueue() { Clear(); }
235 
Clear()236     void Clear() {
237       if (!mEvents.IsEmpty()) {
238         RemoveEventsAt(0, mEvents.Length());
239       }
240     }
241 
242     /**
243      * PutEvent() puts new event into the queue.
244      */
PutEvent(const GdkEventKey * aEvent)245     void PutEvent(const GdkEventKey* aEvent) {
246       GdkEventKey* newEvent = reinterpret_cast<GdkEventKey*>(
247           gdk_event_copy(reinterpret_cast<const GdkEvent*>(aEvent)));
248       newEvent->state &= GDK_MODIFIER_MASK;
249       mEvents.AppendElement(newEvent);
250     }
251 
252     /**
253      * RemoveEvent() removes oldest same event and its preceding events
254      * from the queue.
255      */
RemoveEvent(const GdkEventKey * aEvent)256     void RemoveEvent(const GdkEventKey* aEvent) {
257       size_t index = IndexOf(aEvent);
258       if (NS_WARN_IF(index == GdkEventKeyQueue::NoIndex())) {
259         return;
260       }
261       RemoveEventsAt(0, index + 1);
262     }
263 
264     /**
265      * FirstEvent() returns oldest event in the queue.
266      */
GetFirstEvent()267     GdkEventKey* GetFirstEvent() const {
268       if (mEvents.IsEmpty()) {
269         return nullptr;
270       }
271       return mEvents[0];
272     }
273 
IsEmpty()274     bool IsEmpty() const { return mEvents.IsEmpty(); }
275 
NoIndex()276     static size_t NoIndex() { return nsTArray<GdkEventKey*>::NoIndex; }
Length()277     size_t Length() const { return mEvents.Length(); }
IndexOf(const GdkEventKey * aEvent)278     size_t IndexOf(const GdkEventKey* aEvent) const {
279       static_assert(!(GDK_MODIFIER_MASK & (1 << 24)),
280                     "We assumes 25th bit is used by some IM, but used by GDK");
281       static_assert(!(GDK_MODIFIER_MASK & (1 << 25)),
282                     "We assumes 26th bit is used by some IM, but used by GDK");
283       for (size_t i = 0; i < mEvents.Length(); i++) {
284         GdkEventKey* event = mEvents[i];
285         // It must be enough to compare only type, time, keyval and
286         // part of state.   Note that we cannot compaire two events
287         // simply since IME may have changed unused bits of state.
288         if (event->time == aEvent->time) {
289           if (NS_WARN_IF(event->type != aEvent->type) ||
290               NS_WARN_IF(event->keyval != aEvent->keyval) ||
291               NS_WARN_IF(event->state != (aEvent->state & GDK_MODIFIER_MASK))) {
292             continue;
293           }
294         }
295         return i;
296       }
297       return GdkEventKeyQueue::NoIndex();
298     }
299 
300    private:
301     nsTArray<GdkEventKey*> mEvents;
302 
RemoveEventsAt(size_t aStart,size_t aCount)303     void RemoveEventsAt(size_t aStart, size_t aCount) {
304       for (size_t i = aStart; i < aStart + aCount; i++) {
305         gdk_event_free(reinterpret_cast<GdkEvent*>(mEvents[i]));
306       }
307       mEvents.RemoveElementsAt(aStart, aCount);
308     }
309   };
310   // OnKeyEvent() append mPostingKeyEvents when it believes that a key event
311   // is posted to other IME process.
312   GdkEventKeyQueue mPostingKeyEvents;
313 
314   static guint16 sWaitingSynthesizedKeyPressHardwareKeyCode;
315 
316   struct Range {
317     uint32_t mOffset;
318     uint32_t mLength;
319 
RangeRange320     Range() : mOffset(UINT32_MAX), mLength(UINT32_MAX) {}
321 
IsValidRange322     bool IsValid() const { return mOffset != UINT32_MAX; }
ClearRange323     void Clear() {
324       mOffset = UINT32_MAX;
325       mLength = UINT32_MAX;
326     }
327   };
328 
329   // current target offset and length of IME composition
330   Range mCompositionTargetRange;
331 
332   // mCompositionState indicates current status of composition.
333   enum eCompositionState : uint8_t {
334     eCompositionState_NotComposing,
335     eCompositionState_CompositionStartDispatched,
336     eCompositionState_CompositionChangeEventDispatched
337   };
338   eCompositionState mCompositionState;
339 
IsComposing()340   bool IsComposing() const {
341     return (mCompositionState != eCompositionState_NotComposing);
342   }
343 
IsComposingOn(GtkIMContext * aContext)344   bool IsComposingOn(GtkIMContext* aContext) const {
345     return IsComposing() && mComposingContext == aContext;
346   }
347 
IsComposingOnCurrentContext()348   bool IsComposingOnCurrentContext() const {
349     return IsComposingOn(GetCurrentContext());
350   }
351 
EditorHasCompositionString()352   bool EditorHasCompositionString() {
353     return (mCompositionState ==
354             eCompositionState_CompositionChangeEventDispatched);
355   }
356 
357   /**
358    * Checks if aContext is valid context for handling composition.
359    *
360    * @param aContext          An IM context which is specified by native
361    *                          composition events.
362    * @return                  true if the context is valid context for
363    *                          handling composition.  Otherwise, false.
364    */
365   bool IsValidContext(GtkIMContext* aContext) const;
366 
GetCompositionStateName()367   const char* GetCompositionStateName() {
368     switch (mCompositionState) {
369       case eCompositionState_NotComposing:
370         return "NotComposing";
371       case eCompositionState_CompositionStartDispatched:
372         return "CompositionStartDispatched";
373       case eCompositionState_CompositionChangeEventDispatched:
374         return "CompositionChangeEventDispatched";
375       default:
376         return "InvaildState";
377     }
378   }
379 
380   // mIMContextID indicates the ID of mContext.  This is actually indicates
381   // IM which user selected.
382   IMContextID mIMContextID;
383 
384   struct Selection final {
385     nsString mString;
386     uint32_t mOffset;
387     WritingMode mWritingMode;
388 
Selectionfinal389     Selection() : mOffset(UINT32_MAX) {}
390 
Clearfinal391     void Clear() {
392       mString.Truncate();
393       mOffset = UINT32_MAX;
394       mWritingMode = WritingMode();
395     }
CollapseTofinal396     void CollapseTo(uint32_t aOffset, const WritingMode& aWritingMode) {
397       mWritingMode = aWritingMode;
398       mOffset = aOffset;
399       mString.Truncate();
400     }
401 
402     void Assign(const IMENotification& aIMENotification);
403     void Assign(const WidgetQueryContentEvent& aSelectedTextEvent);
404 
IsValidfinal405     bool IsValid() const { return mOffset != UINT32_MAX; }
Collapsedfinal406     bool Collapsed() const { return mString.IsEmpty(); }
Lengthfinal407     uint32_t Length() const { return mString.Length(); }
EndOffsetfinal408     uint32_t EndOffset() const {
409       if (NS_WARN_IF(!IsValid())) {
410         return UINT32_MAX;
411       }
412       CheckedInt<uint32_t> endOffset =
413           CheckedInt<uint32_t>(mOffset) + mString.Length();
414       if (NS_WARN_IF(!endOffset.isValid())) {
415         return UINT32_MAX;
416       }
417       return endOffset.value();
418     }
419   } mSelection;
420   bool EnsureToCacheSelection(nsAString* aSelectedString = nullptr);
421 
422   // mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And
423   // it's set to FALSE when we call gtk_im_context_focus_out().
424   bool mIsIMFocused;
425   // mFallbackToKeyEvent is set to false when this class starts to handle
426   // a native key event (at that time, mProcessingKeyEvent is set to the
427   // native event).  If active IME just commits composition with a character
428   // which is produced by the key with current keyboard layout, this is set
429   // to true.
430   bool mFallbackToKeyEvent;
431   // mKeyboardEventWasDispatched is used by OnKeyEvent() and
432   // MaybeDispatchKeyEventAsProcessedByIME().
433   // MaybeDispatchKeyEventAsProcessedByIME() dispatches an eKeyDown or
434   // eKeyUp event event if the composition is caused by a native
435   // key press event.  If this is true, a keyboard event has been dispatched
436   // for the native event.  If so, MaybeDispatchKeyEventAsProcessedByIME()
437   // won't dispatch keyboard event anymore.
438   bool mKeyboardEventWasDispatched;
439   // Whether the keyboard event which as dispatched at setting
440   // mKeyboardEventWasDispatched to true was consumed or not.
441   bool mKeyboardEventWasConsumed;
442   // mIsDeletingSurrounding is true while OnDeleteSurroundingNative() is
443   // trying to delete the surrounding text.
444   bool mIsDeletingSurrounding;
445   // mLayoutChanged is true after OnLayoutChange() is called.  This is reset
446   // when eCompositionChange is being dispatched.
447   bool mLayoutChanged;
448   // mSetCursorPositionOnKeyEvent true when caret rect or position is updated
449   // with no composition.  If true, we update candidate window position
450   // before key down
451   bool mSetCursorPositionOnKeyEvent;
452   // mPendingResettingIMContext becomes true if selection change notification
453   // is received during composition but the selection change occurred before
454   // starting the composition.  In such case, we cannot notify IME of
455   // selection change during composition because we don't want to commit
456   // the composition in such case.  However, we should notify IME of the
457   // selection change after the composition is committed.
458   bool mPendingResettingIMContext;
459   // mRetrieveSurroundingSignalReceived is true after "retrieve_surrounding"
460   // signal is received until selection is changed in Gecko.
461   bool mRetrieveSurroundingSignalReceived;
462   // mMaybeInDeadKeySequence is set to true when we detect a dead key press
463   // and set to false when we're sure dead key sequence has been finished.
464   // Note that we cannot detect which key event causes ending a dead key
465   // sequence.  For example, when you press dead key grave with ibus Spanish
466   // keyboard layout, it just consumes the key event when we call
467   // gtk_im_context_filter_keypress().  Then, pressing "Escape" key cancels
468   // the dead key sequence but we don't receive any signal and it's consumed
469   // by gtk_im_context_filter_keypress() normally.  On the other hand, when
470   // pressing "Shift" key causes exactly same behavior but dead key sequence
471   // isn't finished yet.
472   bool mMaybeInDeadKeySequence;
473   // mIsIMInAsyncKeyHandlingMode is set to true if we know that IM handles
474   // key events asynchronously.  I.e., filtered key event may come again
475   // later.
476   bool mIsIMInAsyncKeyHandlingMode;
477   // mIsKeySnooped is set to true if IM uses key snooper to listen key events.
478   // In such case, we won't receive key events if IME consumes the event.
479   bool mIsKeySnooped;
480 
481   // sLastFocusedContext is a pointer to the last focused instance of this
482   // class.  When a instance is destroyed and sLastFocusedContext refers it,
483   // this is cleared.  So, this refers valid pointer always.
484   static IMContextWrapper* sLastFocusedContext;
485 
486   // sUseSimpleContext indeicates if password editors and editors with
487   // |ime-mode: disabled;| should use GtkIMContextSimple.
488   // If true, they use GtkIMContextSimple.  Otherwise, not.
489   static bool sUseSimpleContext;
490 
491   // Callback methods for native IME events.  These methods should call
492   // the related instance methods simply.
493   static gboolean OnRetrieveSurroundingCallback(GtkIMContext* aContext,
494                                                 IMContextWrapper* aModule);
495   static gboolean OnDeleteSurroundingCallback(GtkIMContext* aContext,
496                                               gint aOffset, gint aNChars,
497                                               IMContextWrapper* aModule);
498   static void OnCommitCompositionCallback(GtkIMContext* aContext,
499                                           const gchar* aString,
500                                           IMContextWrapper* aModule);
501   static void OnChangeCompositionCallback(GtkIMContext* aContext,
502                                           IMContextWrapper* aModule);
503   static void OnStartCompositionCallback(GtkIMContext* aContext,
504                                          IMContextWrapper* aModule);
505   static void OnEndCompositionCallback(GtkIMContext* aContext,
506                                        IMContextWrapper* aModule);
507 
508   // The instance methods for the native IME events.
509   gboolean OnRetrieveSurroundingNative(GtkIMContext* aContext);
510   gboolean OnDeleteSurroundingNative(GtkIMContext* aContext, gint aOffset,
511                                      gint aNChars);
512   void OnCommitCompositionNative(GtkIMContext* aContext, const gchar* aString);
513   void OnChangeCompositionNative(GtkIMContext* aContext);
514   void OnStartCompositionNative(GtkIMContext* aContext);
515   void OnEndCompositionNative(GtkIMContext* aContext);
516 
517   /**
518    * GetCurrentContext() returns current IM context which is chosen with the
519    * enabled state.
520    * WARNING:
521    *     When this class receives some signals for a composition after focus
522    *     is moved in Gecko, the result of this may be different from given
523    *     context by the signals.
524    */
525   GtkIMContext* GetCurrentContext() const;
526 
527   /**
528    * GetActiveContext() returns a composing context or current context.
529    */
GetActiveContext()530   GtkIMContext* GetActiveContext() const {
531     return mComposingContext ? mComposingContext : GetCurrentContext();
532   }
533 
534   // If the owner window and IM context have been destroyed, returns TRUE.
IsDestroyed()535   bool IsDestroyed() { return !mOwnerWindow; }
536 
537   // Sets focus to the instance of this class.
538   void Focus();
539 
540   // Steals focus from the instance of this class.
541   void Blur();
542 
543   // Initializes the instance.
544   void Init();
545 
546   /**
547    * Reset the active context, i.e., if there is mComposingContext, reset it.
548    * Otherwise, reset current context.  Note that all native composition
549    * events during calling this will be ignored.
550    */
551   void ResetIME();
552 
553   // Gets the current composition string by the native APIs.
554   void GetCompositionString(GtkIMContext* aContext,
555                             nsAString& aCompositionString);
556 
557   /**
558    * Generates our text range array from current composition string.
559    *
560    * @param aContext              A GtkIMContext which is being handled.
561    * @param aCompositionString    The data to be dispatched with
562    *                              compositionchange event.
563    */
564   already_AddRefed<TextRangeArray> CreateTextRangeArray(
565       GtkIMContext* aContext, const nsAString& aCompositionString);
566 
567   /**
568    * SetTextRange() initializes aTextRange with aPangoAttrIter.
569    *
570    * @param aPangoAttrIter            An iter which represents a clause of the
571    *                                  composition string.
572    * @param aUTF8CompositionString    The whole composition string (UTF-8).
573    * @param aUTF16CaretOffset         The caret offset in the composition
574    *                                  string encoded as UTF-16.
575    * @param aTextRange                The result.
576    * @return                          true if this initializes aTextRange.
577    *                                  Otherwise, false.
578    */
579   bool SetTextRange(PangoAttrIterator* aPangoAttrIter,
580                     const gchar* aUTF8CompositionString,
581                     uint32_t aUTF16CaretOffset, TextRange& aTextRange) const;
582 
583   /**
584    * ToNscolor() converts the PangoColor in aPangoAttrColor to nscolor.
585    */
586   static nscolor ToNscolor(PangoAttrColor* aPangoAttrColor);
587 
588   /**
589    * Move the candidate window with "fake" cursor position.
590    *
591    * @param aContext              A GtkIMContext which is being handled.
592    */
593   void SetCursorPosition(GtkIMContext* aContext);
594 
595   // Queries the current selection offset of the window.
596   uint32_t GetSelectionOffset(nsWindow* aWindow);
597 
598   // Get current paragraph text content and cursor position
599   nsresult GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos);
600 
601   /**
602    * Delete text portion
603    *
604    * @param aContext              A GtkIMContext which is being handled.
605    * @param aOffset               Start offset of the range to delete.
606    * @param aNChars               Count of characters to delete.  It depends
607    *                              on |g_utf8_strlen()| what is one character.
608    */
609   nsresult DeleteText(GtkIMContext* aContext, int32_t aOffset,
610                       uint32_t aNChars);
611 
612   // Initializes the GUI event.
613   void InitEvent(WidgetGUIEvent& aEvent);
614 
615   // Called before destroying the context to work around some platform bugs.
616   void PrepareToDestroyContext(GtkIMContext* aContext);
617 
618   /**
619    *  WARNING:
620    *    Following methods dispatch gecko events.  Then, the focused widget
621    *    can be destroyed, and also it can be stolen focus.  If they returns
622    *    FALSE, callers cannot continue the composition.
623    *      - MaybeDispatchKeyEventAsProcessedByIME
624    *      - DispatchCompositionStart
625    *      - DispatchCompositionChangeEvent
626    *      - DispatchCompositionCommitEvent
627    */
628 
629   /**
630    * Dispatch an eKeyDown or eKeyUp event whose mKeyCode value is
631    * NS_VK_PROCESSKEY and mKeyNameIndex is KEY_NAME_INDEX_Process if
632    * we're not in a dead key sequence, mProcessingKeyEvent is nullptr
633    * but mPostingKeyEvents is not empty or mProcessingKeyEvent is not
634    * nullptr and mKeyboardEventWasDispatched is still false.  If this
635    * dispatches a keyboard event, this sets mKeyboardEventWasDispatched
636    * to true.
637    *
638    * @param aFollowingEvent       The following event message.
639    * @return                      If the caller can continue to handle
640    *                              composition, returns true.  Otherwise,
641    *                              false.  For example, if focus is moved
642    *                              by dispatched keyboard event, returns
643    *                              false.
644    */
645   bool MaybeDispatchKeyEventAsProcessedByIME(EventMessage aFollowingEvent);
646 
647   /**
648    * Dispatches a composition start event.
649    *
650    * @param aContext              A GtkIMContext which is being handled.
651    * @return                      true if the focused widget is neither
652    *                              destroyed nor changed.  Otherwise, false.
653    */
654   bool DispatchCompositionStart(GtkIMContext* aContext);
655 
656   /**
657    * Dispatches a compositionchange event.
658    *
659    * @param aContext              A GtkIMContext which is being handled.
660    * @param aCompositionString    New composition string.
661    * @return                      true if the focused widget is neither
662    *                              destroyed nor changed.  Otherwise, false.
663    */
664   bool DispatchCompositionChangeEvent(GtkIMContext* aContext,
665                                       const nsAString& aCompositionString);
666 
667   /**
668    * Dispatches a compositioncommit event or compositioncommitasis event.
669    *
670    * @param aContext              A GtkIMContext which is being handled.
671    * @param aCommitString         If this is nullptr, the composition will
672    *                              be committed with last dispatched data.
673    *                              Otherwise, the composition will be
674    *                              committed with this value.
675    * @return                      true if the focused widget is neither
676    *                              destroyed nor changed.  Otherwise, false.
677    */
678   bool DispatchCompositionCommitEvent(GtkIMContext* aContext,
679                                       const nsAString* aCommitString = nullptr);
680 };
681 
682 }  // namespace widget
683 }  // namespace mozilla
684 
685 #endif  // #ifndef IMContextWrapper_h_
686