Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/TextEventDispatcher.h
Line
Count
Source (jump to first uncovered line)
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 mozilla_textcompositionsynthesizer_h_
7
#define mozilla_textcompositionsynthesizer_h_
8
9
#include "mozilla/RefPtr.h"
10
#include "nsString.h"
11
#include "mozilla/Attributes.h"
12
#include "mozilla/EventForwards.h"
13
#include "mozilla/TextEventDispatcherListener.h"
14
#include "mozilla/TextRange.h"
15
#include "mozilla/widget/IMEData.h"
16
17
class nsIWidget;
18
19
namespace mozilla {
20
namespace widget {
21
22
class PuppetWidget;
23
24
/**
25
 * TextEventDispatcher is a helper class for dispatching widget events defined
26
 * in TextEvents.h.  Currently, this is a helper for dispatching
27
 * WidgetCompositionEvent and WidgetKeyboardEvent.  This manages the behavior
28
 * of them for conforming to DOM Level 3 Events.
29
 * An instance of this class is created by nsIWidget instance and owned by it.
30
 * This is typically created only by the top level widgets because only they
31
 * handle IME.
32
 */
33
34
class TextEventDispatcher final
35
{
36
  ~TextEventDispatcher()
37
0
  {
38
0
  }
39
40
  NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
41
42
public:
43
  explicit TextEventDispatcher(nsIWidget* aWidget);
44
45
  /**
46
   * Initializes the instance for IME or automated test.  Either IME or tests
47
   * need to call one of them before starting composition.  If they return
48
   * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
49
   * notifications from TextEventDispatcher for same purpose (for IME or tests).
50
   * If this returns another error, the caller shouldn't keep starting
51
   * composition.
52
   *
53
   * @param aListener       Specify the listener to listen notifications and
54
   *                        requests.  This must not be null.
55
   *                        NOTE: aListener is stored as weak reference in
56
   *                              TextEventDispatcher.  See mListener
57
   *                              definition below.
58
   */
59
  nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
60
  nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener,
61
                                     bool aIsAPZAware);
62
  nsresult BeginNativeInputTransaction();
63
64
  /**
65
   * BeginInputTransactionFor() should be used when aPuppetWidget dispatches
66
   * a composition or keyboard event coming from its parent process.
67
   */
68
  nsresult BeginInputTransactionFor(const WidgetGUIEvent* aEvent,
69
                                    PuppetWidget* aPuppetWidget);
70
71
  /**
72
   * EndInputTransaction() should be called when the listener stops using
73
   * the TextEventDispatcher.
74
   *
75
   * @param aListener       The listener using the TextEventDispatcher instance.
76
   */
77
  void EndInputTransaction(TextEventDispatcherListener* aListener);
78
79
  /**
80
   * OnDestroyWidget() is called when mWidget is being destroyed.
81
   */
82
  void OnDestroyWidget();
83
84
0
  nsIWidget* GetWidget() const { return mWidget; }
85
86
  const IMENotificationRequests& IMENotificationRequestsRef() const
87
0
  {
88
0
    return mIMENotificationRequests;
89
0
  }
90
91
  /**
92
   * GetState() returns current state of this class.
93
   *
94
   * @return        NS_OK: Fine to compose text.
95
   *                NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
96
   *                                          BeginInputTransactionForTests()
97
   *                                          should be called.
98
   *                NS_ERROR_NOT_AVAILABLE: The widget isn't available for
99
   *                                        composition.
100
   */
101
  nsresult GetState() const;
102
103
  /**
104
   * IsComposing() returns true after calling StartComposition() and before
105
   * calling CommitComposition().  In other words, native IME has composition
106
   * when this returns true.
107
   */
108
0
  bool IsComposing() const { return mIsComposing; }
109
110
  /**
111
   * IsHandlingComposition() returns true after calling StartComposition() and
112
   * content has not handled eCompositionCommit(AsIs) event.  In other words,
113
   * our content has composition when this returns true.
114
   */
115
0
  bool IsHandlingComposition() const { return mIsHandlingComposition; }
116
117
  /**
118
   * IsInNativeInputTransaction() returns true if native IME handler began a
119
   * transaction and it's not finished yet.
120
   */
121
  bool IsInNativeInputTransaction() const
122
0
  {
123
0
    return mInputTransactionType == eNativeInputTransaction;
124
0
  }
125
126
  /**
127
   * IsDispatchingEvent() returns true while this instance dispatching an event.
128
   */
129
0
  bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
130
131
  /**
132
   * GetPseudoIMEContext() returns pseudo native IME context if there is an
133
   * input transaction whose type is not for native event handler.
134
   * Otherwise, returns nullptr.
135
   */
136
  void* GetPseudoIMEContext() const
137
0
  {
138
0
    if (mInputTransactionType == eNoInputTransaction ||
139
0
        mInputTransactionType == eNativeInputTransaction) {
140
0
      return nullptr;
141
0
    }
142
0
    return const_cast<TextEventDispatcher*>(this);
143
0
  }
144
145
  /**
146
   * StartComposition() starts composition explicitly.
147
   *
148
   * @param aEventTime  If this is not nullptr, WidgetCompositionEvent will
149
   *                    be initialized with this.  Otherwise, initialized
150
   *                    with the time at initializing.
151
   */
152
  nsresult StartComposition(nsEventStatus& aStatus,
153
                            const WidgetEventTime* aEventTime = nullptr);
154
155
  /**
156
   * CommitComposition() commits composition.
157
   *
158
   * @param aCommitString   If this is null, commits with the last composition
159
   *                        string.  Otherwise, commits the composition with
160
   *                        this value.
161
   * @param aEventTime      If this is not nullptr, WidgetCompositionEvent will
162
   *                        be initialized with this.  Otherwise, initialized
163
   *                        with the time at initializing.
164
   */
165
   nsresult CommitComposition(nsEventStatus& aStatus,
166
                              const nsAString* aCommitString = nullptr,
167
                              const WidgetEventTime* aEventTime = nullptr);
168
169
  /**
170
   * SetPendingCompositionString() sets new composition string which will be
171
   * dispatched with eCompositionChange event by calling Flush().
172
   *
173
   * @param aString         New composition string.
174
   */
175
  nsresult SetPendingCompositionString(const nsAString& aString)
176
0
  {
177
0
    return mPendingComposition.SetString(aString);
178
0
  }
179
180
  /**
181
   * AppendClauseToPendingComposition() appends a clause information to
182
   * the pending composition string.
183
   *
184
   * @param aLength         Length of the clause.
185
   * @param aTextRangeType  One of TextRangeType::eRawClause,
186
   *                        TextRangeType::eSelectedRawClause,
187
   *                        TextRangeType::eConvertedClause or
188
   *                        TextRangeType::eSelectedClause.
189
   */
190
  nsresult AppendClauseToPendingComposition(uint32_t aLength,
191
                                            TextRangeType aTextRangeType)
192
0
  {
193
0
    return mPendingComposition.AppendClause(aLength, aTextRangeType);
194
0
  }
195
196
  /**
197
   * SetCaretInPendingComposition() sets caret position in the pending
198
   * composition string and its length.  This is optional.  If IME doesn't
199
   * want to show caret, it shouldn't need to call this.
200
   *
201
   * @param aOffset         Offset of the caret in the pending composition
202
   *                        string.  This should not be larger than the length
203
   *                        of the pending composition string.
204
   * @param aLength         Caret width.  If this is 0, caret will be collapsed.
205
   *                        Note that Gecko doesn't supported wide caret yet,
206
   *                        therefore, this is ignored for now.
207
   */
208
  nsresult SetCaretInPendingComposition(uint32_t aOffset,
209
                                        uint32_t aLength)
210
0
  {
211
0
    return mPendingComposition.SetCaret(aOffset, aLength);
212
0
  }
213
214
  /**
215
   * SetPendingComposition() is useful if native IME handler already creates
216
   * array of clauses and/or caret information.
217
   *
218
   * @param aString         Composition string.  This may include native line
219
   *                        breakers since they will be replaced with XP line
220
   *                        breakers automatically.
221
   * @param aRanges         This should include the ranges of clauses and/or
222
   *                        a range of caret.  Note that this method allows
223
   *                        some ranges overlap each other and the range order
224
   *                        is not from start to end.
225
   */
226
  nsresult SetPendingComposition(const nsAString& aString,
227
                                 const TextRangeArray* aRanges)
228
0
  {
229
0
    return mPendingComposition.Set(aString, aRanges);
230
0
  }
231
232
  /**
233
   * FlushPendingComposition() sends the pending composition string
234
   * to the widget of the store DOM window.  Before calling this, IME needs to
235
   * set pending composition string with SetPendingCompositionString(),
236
   * AppendClauseToPendingComposition() and/or
237
   * SetCaretInPendingComposition().
238
   *
239
   * @param aEventTime      If this is not nullptr, WidgetCompositionEvent will
240
   *                        be initialized with this.  Otherwise, initialized
241
   *                        with the time at initializing.
242
   */
243
  nsresult FlushPendingComposition(nsEventStatus& aStatus,
244
                                   const WidgetEventTime* aEventTime = nullptr)
245
0
  {
246
0
    return mPendingComposition.Flush(this, aStatus, aEventTime);
247
0
  }
248
249
  /**
250
   * ClearPendingComposition() makes this instance forget pending composition.
251
   */
252
  void ClearPendingComposition()
253
0
  {
254
0
    mPendingComposition.Clear();
255
0
  }
256
257
  /**
258
   * GetPendingCompositionClauses() returns text ranges which was appended by
259
   * AppendClauseToPendingComposition() or SetPendingComposition().
260
   */
261
  const TextRangeArray* GetPendingCompositionClauses() const
262
0
  {
263
0
    return mPendingComposition.GetClauses();
264
0
  }
265
266
  /**
267
   * @see nsIWidget::NotifyIME()
268
   */
269
  nsresult NotifyIME(const IMENotification& aIMENotification);
270
271
  /**
272
   * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
273
   *
274
   * @param aMessage        Must be eKeyDown or eKeyUp.
275
   *                        Use MaybeDispatchKeypressEvents() for dispatching
276
   *                        eKeyPress.
277
   * @param aKeyboardEvent  A keyboard event.
278
   * @param aStatus         If dispatching event should be marked as consumed,
279
   *                        set nsEventStatus_eConsumeNoDefault.  Otherwise,
280
   *                        set nsEventStatus_eIgnore.  After dispatching
281
   *                        a event and it's consumed this returns
282
   *                        nsEventStatus_eConsumeNoDefault.
283
   * @param aData           Calling this method may cause calling
284
   *                        WillDispatchKeyboardEvent() of the listener.
285
   *                        aData will be set to its argument.
286
   * @return                true if an event is dispatched.  Otherwise, false.
287
   */
288
  bool DispatchKeyboardEvent(EventMessage aMessage,
289
                             const WidgetKeyboardEvent& aKeyboardEvent,
290
                             nsEventStatus& aStatus,
291
                             void* aData = nullptr);
292
293
  /**
294
   * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
295
   * generated from aKeydownEvent.
296
   *
297
   * @param aKeyboardEvent  A keyboard event.
298
   * @param aStatus         Sets the result when the caller dispatches
299
   *                        aKeyboardEvent.  Note that if the value is
300
   *                        nsEventStatus_eConsumeNoDefault, this does NOT
301
   *                        dispatch keypress events.
302
   *                        When this method dispatches one or more keypress
303
   *                        events and one of them is consumed, this returns
304
   *                        nsEventStatus_eConsumeNoDefault.
305
   * @param aData           Calling this method may cause calling
306
   *                        WillDispatchKeyboardEvent() of the listener.
307
   *                        aData will be set to its argument.
308
   * @param aNeedsCallback  Set true when caller needs to initialize each
309
   *                        eKeyPress event immediately before dispatch.
310
   *                        Then, WillDispatchKeyboardEvent() is always called.
311
   * @return                true if one or more events are dispatched.
312
   *                        Otherwise, false.
313
   */
314
  bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
315
                                   nsEventStatus& aStatus,
316
                                   void* aData = nullptr,
317
                                   bool aNeedsCallback = false);
318
319
private:
320
  // mWidget is owner of the instance.  When this is created, this is set.
321
  // And when mWidget is released, this is cleared by OnDestroyWidget().
322
  // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
323
  // return true).
324
  nsIWidget* mWidget;
325
  // mListener is a weak reference to TextEventDispatcherListener.  That might
326
  // be referred by JS.  Therefore, the listener might be difficult to release
327
  // itself if this is a strong reference.  Additionally, it's difficult to
328
  // check if a method to uninstall the listener is called by valid instance.
329
  // So, using weak reference is the best way in this case.
330
  nsWeakPtr mListener;
331
  // mIMENotificationRequests should store current IME's notification requests.
332
  // So, this may be invalid when IME doesn't have focus.
333
  IMENotificationRequests mIMENotificationRequests;
334
335
  // mPendingComposition stores new composition string temporarily.
336
  // These values will be used for dispatching eCompositionChange event
337
  // in Flush().  When Flush() is called, the members will be cleared
338
  // automatically.
339
  class PendingComposition
340
  {
341
  public:
342
    PendingComposition();
343
    nsresult SetString(const nsAString& aString);
344
    nsresult AppendClause(uint32_t aLength, TextRangeType aTextRangeType);
345
    nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
346
    nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
347
    nsresult Flush(TextEventDispatcher* aDispatcher,
348
                   nsEventStatus& aStatus,
349
                   const WidgetEventTime* aEventTime);
350
0
    const TextRangeArray* GetClauses() const { return mClauses; }
351
    void Clear();
352
353
  private:
354
    nsString mString;
355
    RefPtr<TextRangeArray> mClauses;
356
    TextRange mCaret;
357
    bool mReplacedNativeLineBreakers;
358
359
    void EnsureClauseArray();
360
361
    /**
362
     * ReplaceNativeLineBreakers() replaces "\r\n" and "\r" to "\n" and adjust
363
     * each clause information and the caret information.
364
     */
365
    void ReplaceNativeLineBreakers();
366
367
    /**
368
     * AdjustRange() adjusts aRange as in the string with XP line breakers.
369
     *
370
     * @param aRange            The reference to a range in aNativeString.
371
     *                          This will be modified.
372
     * @param aNativeString     The string with native line breakers.
373
     *                          This may include "\r\n" and/or "\r".
374
     */
375
    static void AdjustRange(TextRange& aRange, const nsAString& aNativeString);
376
  };
377
  PendingComposition mPendingComposition;
378
379
  // While dispatching an event, this is incremented.
380
  uint16_t mDispatchingEvent;
381
382
  enum InputTransactionType : uint8_t
383
  {
384
    // No input transaction has been started.
385
    eNoInputTransaction,
386
    // Input transaction for native IME or keyboard event handler.  Note that
387
    // keyboard events may be dispatched via parent process if there is.
388
    // In remote processes, this is also used when events come from the parent
389
    // process and are not for tests because we cannot distinguish if
390
    // TextEventDispatcher has which type of transaction when it dispatches
391
    // (eNativeInputTransaction or eSameProcessSyncInputTransaction).
392
    eNativeInputTransaction,
393
    // Input transaction for automated tests which are APZ-aware.  Note that
394
    // keyboard events may be dispatched via parent process if there is.
395
    eAsyncTestInputTransaction,
396
    // Input transaction for automated tests which assume events are fired
397
    // synchronously.  I.e., keyboard events are always dispatched in the
398
    // current process.
399
    // In remote processes, this is also used when events come from the parent
400
    // process and are not dispatched by the instance itself for APZ-aware
401
    // tests because this instance won't dispatch the events via the parent
402
    // process again.
403
    eSameProcessSyncTestInputTransaction,
404
    // Input transaction for Others (must be IME on B2G).  Events are fired
405
    // synchronously because TextInputProcessor which is the only user of
406
    // this input transaction type supports only keyboard apps on B2G.
407
    // Keyboard apps on B2G doesn't want to dispatch keyboard events to
408
    // chrome process. Therefore, this should dispatch key events only in
409
    // the current process.
410
    eSameProcessSyncInputTransaction
411
  };
412
413
  InputTransactionType mInputTransactionType;
414
415
  bool IsForTests() const
416
0
  {
417
0
    return mInputTransactionType == eAsyncTestInputTransaction ||
418
0
           mInputTransactionType == eSameProcessSyncTestInputTransaction;
419
0
  }
420
421
  // ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should
422
  // be dispatched via its parent process (if there is) for APZ.  Otherwise,
423
  // when the input transaction is for IME of B2G or automated tests which
424
  // isn't APZ-aware, WidgetInputEvent should be dispatched form current
425
  // process directly.
426
  bool ShouldSendInputEventToAPZ() const
427
0
  {
428
0
    switch (mInputTransactionType) {
429
0
      case eNativeInputTransaction:
430
0
      case eAsyncTestInputTransaction:
431
0
        return true;
432
0
      case eSameProcessSyncTestInputTransaction:
433
0
      case eSameProcessSyncInputTransaction:
434
0
        return false;
435
0
      case eNoInputTransaction:
436
0
        NS_WARNING("Why does the caller need to dispatch an event when "
437
0
                   "there is no input transaction?");
438
0
        return true;
439
0
      default:
440
0
        MOZ_CRASH("Define the behavior of new InputTransactionType");
441
0
    }
442
0
  }
443
444
  // See IsComposing().
445
  bool mIsComposing;
446
447
  // See IsHandlingComposition().
448
  bool mIsHandlingComposition;
449
450
  // true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not
451
  // received yet.  Otherwise, false.
452
  bool mHasFocus;
453
454
  // If this is true, keydown and keyup events are dispatched even when there
455
  // is a composition.
456
  static bool sDispatchKeyEventsDuringComposition;
457
  // If this is true, keypress events for non-printable keys are dispatched only
458
  // for event listeners of the system event group in web content.
459
  static bool sDispatchKeyPressEventsOnlySystemGroupInContent;
460
461
  nsresult BeginInputTransactionInternal(
462
             TextEventDispatcherListener* aListener,
463
             InputTransactionType aType);
464
465
  /**
466
   * InitEvent() initializes aEvent.  This must be called before dispatching
467
   * the event.
468
   */
469
  void InitEvent(WidgetGUIEvent& aEvent) const;
470
471
472
  /**
473
   * DispatchEvent() dispatches aEvent on aWidget.
474
   */
475
  nsresult DispatchEvent(nsIWidget* aWidget,
476
                         WidgetGUIEvent& aEvent,
477
                         nsEventStatus& aStatus);
478
479
  /**
480
   * DispatchInputEvent() dispatches aEvent on aWidget.
481
   */
482
  nsresult DispatchInputEvent(nsIWidget* aWidget,
483
                              WidgetInputEvent& aEvent,
484
                              nsEventStatus& aStatus);
485
486
  /**
487
   * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
488
   * been started it yet.
489
   *
490
   * @param aStatus         If it succeeded to start composition normally, this
491
   *                        returns nsEventStatus_eIgnore.  Otherwise, e.g.,
492
   *                        the composition is canceled during dispatching
493
   *                        compositionstart event, this returns
494
   *                        nsEventStatus_eConsumeNoDefault.  In this case,
495
   *                        the caller shouldn't keep doing its job.
496
   * @param aEventTime      If this is not nullptr, WidgetCompositionEvent will
497
   *                        be initialized with this.  Otherwise, initialized
498
   *                        with the time at initializing.
499
   * @return                Only when something unexpected occurs, this returns
500
   *                        an error.  Otherwise, returns NS_OK even if aStatus
501
   *                        is nsEventStatus_eConsumeNoDefault.
502
   */
503
  nsresult StartCompositionAutomaticallyIfNecessary(
504
             nsEventStatus& aStatus,
505
             const WidgetEventTime* aEventTime);
506
507
  /**
508
   * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
509
   *
510
   * @param aMessage        Must be eKeyDown, eKeyUp or eKeyPress.
511
   * @param aKeyboardEvent  A keyboard event.  If aMessage is eKeyPress and
512
   *                        the event is for second or later character, its
513
   *                        mKeyValue should be empty string.
514
   * @param aStatus         If dispatching event should be marked as consumed,
515
   *                        set nsEventStatus_eConsumeNoDefault.  Otherwise,
516
   *                        set nsEventStatus_eIgnore.  After dispatching
517
   *                        a event and it's consumed this returns
518
   *                        nsEventStatus_eConsumeNoDefault.
519
   * @param aData           Calling this method may cause calling
520
   *                        WillDispatchKeyboardEvent() of the listener.
521
   *                        aData will be set to its argument.
522
   * @param aIndexOfKeypress    This must be 0 if aMessage isn't eKeyPress or
523
   *                            aKeyboard.mKeyNameIndex isn't
524
   *                            KEY_NAME_INDEX_USE_STRING.  Otherwise, i.e.,
525
   *                            when an eKeyPress event causes inputting
526
   *                            text, this must be between 0 and
527
   *                            mKeyValue.Length() - 1 since keypress events
528
   *                            sending only one character per event.
529
   * @param aNeedsCallback  Set true when caller needs to initialize each
530
   *                        eKeyPress event immediately before dispatch.
531
   *                        Then, WillDispatchKeyboardEvent() is always called.
532
   * @return                true if an event is dispatched.  Otherwise, false.
533
   */
534
  bool DispatchKeyboardEventInternal(EventMessage aMessage,
535
                                     const WidgetKeyboardEvent& aKeyboardEvent,
536
                                     nsEventStatus& aStatus,
537
                                     void* aData,
538
                                     uint32_t aIndexOfKeypress = 0,
539
                                     bool aNeedsCallback = false);
540
541
  /**
542
   * ClearNotificationRequests() clears mIMENotificationRequests.
543
   */
544
  void ClearNotificationRequests();
545
546
  /**
547
   * UpdateNotificationRequests() updates mIMENotificationRequests with
548
   * current state.  If the instance doesn't have focus, this clears
549
   * mIMENotificationRequests.  Otherwise, updates it with both requests of
550
   * current listener and native listener.
551
   */
552
  void UpdateNotificationRequests();
553
};
554
555
} // namespace widget
556
} // namespace mozilla
557
558
#endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_