Coverage Report

Created: 2018-09-25 14:53

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