Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/TextEventDispatcher.cpp
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
#include "mozilla/Preferences.h"
7
#include "mozilla/TextEvents.h"
8
#include "mozilla/TextEventDispatcher.h"
9
#include "nsIDocShell.h"
10
#include "nsIFrame.h"
11
#include "nsIPresShell.h"
12
#include "nsIWidget.h"
13
#include "nsPIDOMWindow.h"
14
#include "nsView.h"
15
#include "PuppetWidget.h"
16
17
namespace mozilla {
18
namespace widget {
19
20
/******************************************************************************
21
 * TextEventDispatcher
22
 *****************************************************************************/
23
24
bool TextEventDispatcher::sDispatchKeyEventsDuringComposition = false;
25
bool TextEventDispatcher::sDispatchKeyPressEventsOnlySystemGroupInContent =
26
       false;
27
28
TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
29
  : mWidget(aWidget)
30
  , mDispatchingEvent(0)
31
  , mInputTransactionType(eNoInputTransaction)
32
  , mIsComposing(false)
33
  , mIsHandlingComposition(false)
34
  , mHasFocus(false)
35
0
{
36
0
  MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
37
0
38
0
  static bool sInitialized = false;
39
0
  if (!sInitialized) {
40
0
    Preferences::AddBoolVarCache(
41
0
      &sDispatchKeyEventsDuringComposition,
42
0
      "dom.keyboardevent.dispatch_during_composition",
43
0
      true);
44
0
    Preferences::AddBoolVarCache(
45
0
      &sDispatchKeyPressEventsOnlySystemGroupInContent,
46
0
      "dom.keyboardevent.keypress."
47
0
        "dispatch_non_printable_keys_only_system_group_in_content",
48
0
      true);
49
0
    sInitialized = true;
50
0
  }
51
0
52
0
  ClearNotificationRequests();
53
0
}
54
55
nsresult
56
TextEventDispatcher::BeginInputTransaction(
57
                       TextEventDispatcherListener* aListener)
58
0
{
59
0
  return BeginInputTransactionInternal(aListener,
60
0
                                       eSameProcessSyncInputTransaction);
61
0
}
62
63
nsresult
64
TextEventDispatcher::BeginTestInputTransaction(
65
                       TextEventDispatcherListener* aListener,
66
                       bool aIsAPZAware)
67
0
{
68
0
  return BeginInputTransactionInternal(aListener,
69
0
           aIsAPZAware ? eAsyncTestInputTransaction :
70
0
                         eSameProcessSyncTestInputTransaction);
71
0
}
72
73
nsresult
74
TextEventDispatcher::BeginNativeInputTransaction()
75
0
{
76
0
  if (NS_WARN_IF(!mWidget)) {
77
0
    return NS_ERROR_FAILURE;
78
0
  }
79
0
  RefPtr<TextEventDispatcherListener> listener =
80
0
    mWidget->GetNativeTextEventDispatcherListener();
81
0
  if (NS_WARN_IF(!listener)) {
82
0
    return NS_ERROR_FAILURE;
83
0
  }
84
0
  return BeginInputTransactionInternal(listener, eNativeInputTransaction);
85
0
}
86
87
nsresult
88
TextEventDispatcher::BeginInputTransactionInternal(
89
                       TextEventDispatcherListener* aListener,
90
                       InputTransactionType aType)
91
0
{
92
0
  if (NS_WARN_IF(!aListener)) {
93
0
    return NS_ERROR_INVALID_ARG;
94
0
  }
95
0
  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
96
0
  if (listener) {
97
0
    if (listener == aListener && mInputTransactionType == aType) {
98
0
      UpdateNotificationRequests();
99
0
      return NS_OK;
100
0
    }
101
0
    // If this has composition or is dispatching an event, any other listener
102
0
    // can steal ownership.  Especially, if the latter case is allowed,
103
0
    // nobody cannot begin input transaction with this if a modal dialog is
104
0
    // opened during dispatching an event.
105
0
    if (IsComposing() || IsDispatchingEvent()) {
106
0
      return NS_ERROR_ALREADY_INITIALIZED;
107
0
    }
108
0
  }
109
0
  mListener = do_GetWeakReference(aListener);
110
0
  mInputTransactionType = aType;
111
0
  if (listener && listener != aListener) {
112
0
    listener->OnRemovedFrom(this);
113
0
  }
114
0
  UpdateNotificationRequests();
115
0
  return NS_OK;
116
0
}
117
118
nsresult
119
TextEventDispatcher::BeginInputTransactionFor(const WidgetGUIEvent* aEvent,
120
                                              PuppetWidget* aPuppetWidget)
121
0
{
122
0
  MOZ_ASSERT(XRE_IsContentProcess());
123
0
  MOZ_ASSERT(!IsDispatchingEvent());
124
0
125
0
  switch (aEvent->mMessage) {
126
0
    case eKeyDown:
127
0
    case eKeyPress:
128
0
    case eKeyUp:
129
0
      MOZ_ASSERT(aEvent->mClass == eKeyboardEventClass);
130
0
      break;
131
0
    case eCompositionStart:
132
0
    case eCompositionChange:
133
0
    case eCompositionCommit:
134
0
    case eCompositionCommitAsIs:
135
0
      MOZ_ASSERT(aEvent->mClass == eCompositionEventClass);
136
0
      break;
137
0
    default:
138
0
      return NS_ERROR_INVALID_ARG;
139
0
  }
140
0
141
0
  if (aEvent->mFlags.mIsSynthesizedForTests) {
142
0
    // If the event is for an automated test and this instance dispatched
143
0
    // an event to the parent process, we can assume that this is already
144
0
    // initialized properly.
145
0
    if (mInputTransactionType == eAsyncTestInputTransaction) {
146
0
      return NS_OK;
147
0
    }
148
0
    // Even if the event coming from the parent process is synthesized for
149
0
    // tests, this process should treat it as "sync" test here because
150
0
    // it won't be go back to the parent process.
151
0
    nsresult rv =
152
0
      BeginInputTransactionInternal(
153
0
        static_cast<TextEventDispatcherListener*>(aPuppetWidget),
154
0
        eSameProcessSyncTestInputTransaction);
155
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
156
0
      return rv;
157
0
    }
158
0
  } else {
159
0
    nsresult rv = BeginNativeInputTransaction();
160
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
161
0
      return rv;
162
0
    }
163
0
  }
164
0
165
0
  // Emulate modifying members which indicate the state of composition.
166
0
  // If we need to manage more states and/or more complexly, we should create
167
0
  // internal methods which are called by both here and each event dispatcher
168
0
  // method of this class.
169
0
  switch (aEvent->mMessage) {
170
0
    case eKeyDown:
171
0
    case eKeyPress:
172
0
    case eKeyUp:
173
0
      return NS_OK;
174
0
    case eCompositionStart:
175
0
      MOZ_ASSERT(!mIsComposing);
176
0
      mIsComposing = mIsHandlingComposition = true;
177
0
      return NS_OK;
178
0
    case eCompositionChange:
179
0
      MOZ_ASSERT(mIsComposing);
180
0
      MOZ_ASSERT(mIsHandlingComposition);
181
0
      mIsComposing = mIsHandlingComposition = true;
182
0
      return NS_OK;
183
0
    case eCompositionCommit:
184
0
    case eCompositionCommitAsIs:
185
0
      MOZ_ASSERT(mIsComposing);
186
0
      MOZ_ASSERT(mIsHandlingComposition);
187
0
      mIsComposing = false;
188
0
      mIsHandlingComposition = true;
189
0
      return NS_OK;
190
0
    default:
191
0
      MOZ_ASSERT_UNREACHABLE("You forgot to handle the event");
192
0
      return NS_ERROR_UNEXPECTED;
193
0
  }
194
0
}
195
void
196
TextEventDispatcher::EndInputTransaction(TextEventDispatcherListener* aListener)
197
0
{
198
0
  if (NS_WARN_IF(IsComposing()) || NS_WARN_IF(IsDispatchingEvent())) {
199
0
    return;
200
0
  }
201
0
202
0
  mInputTransactionType = eNoInputTransaction;
203
0
204
0
  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
205
0
  if (NS_WARN_IF(!listener)) {
206
0
    return;
207
0
  }
208
0
209
0
  if (NS_WARN_IF(listener != aListener)) {
210
0
    return;
211
0
  }
212
0
213
0
  mListener = nullptr;
214
0
  listener->OnRemovedFrom(this);
215
0
  UpdateNotificationRequests();
216
0
}
217
218
void
219
TextEventDispatcher::OnDestroyWidget()
220
0
{
221
0
  mWidget = nullptr;
222
0
  mHasFocus = false;
223
0
  ClearNotificationRequests();
224
0
  mPendingComposition.Clear();
225
0
  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
226
0
  mListener = nullptr;
227
0
  mInputTransactionType = eNoInputTransaction;
228
0
  if (listener) {
229
0
    listener->OnRemovedFrom(this);
230
0
  }
231
0
}
232
233
nsresult
234
TextEventDispatcher::GetState() const
235
0
{
236
0
  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
237
0
  if (!listener) {
238
0
    return NS_ERROR_NOT_INITIALIZED;
239
0
  }
240
0
  if (!mWidget || mWidget->Destroyed()) {
241
0
    return NS_ERROR_NOT_AVAILABLE;
242
0
  }
243
0
  return NS_OK;
244
0
}
245
246
void
247
TextEventDispatcher::InitEvent(WidgetGUIEvent& aEvent) const
248
0
{
249
0
  aEvent.mTime = PR_IntervalNow();
250
0
  aEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
251
0
  aEvent.mFlags.mIsSynthesizedForTests = IsForTests();
252
0
  if (aEvent.mClass != eCompositionEventClass) {
253
0
    return;
254
0
  }
255
0
  void* pseudoIMEContext = GetPseudoIMEContext();
256
0
  if (pseudoIMEContext) {
257
0
    aEvent.AsCompositionEvent()->mNativeIMEContext.
258
0
      InitWithRawNativeIMEContext(pseudoIMEContext);
259
0
  }
260
#ifdef DEBUG
261
  else {
262
    MOZ_ASSERT(!XRE_IsContentProcess(),
263
      "Why did the content process start native event transaction?");
264
    MOZ_ASSERT(aEvent.AsCompositionEvent()->mNativeIMEContext.IsValid(),
265
      "Native IME context shouldn't be invalid");
266
  }
267
#endif // #ifdef DEBUG
268
}
269
270
nsresult
271
TextEventDispatcher::DispatchEvent(nsIWidget* aWidget,
272
                                   WidgetGUIEvent& aEvent,
273
                                   nsEventStatus& aStatus)
274
0
{
275
0
  MOZ_ASSERT(!aEvent.AsInputEvent(), "Use DispatchInputEvent()");
276
0
277
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(this);
278
0
  nsCOMPtr<nsIWidget> widget(aWidget);
279
0
  mDispatchingEvent++;
280
0
  nsresult rv = widget->DispatchEvent(&aEvent, aStatus);
281
0
  mDispatchingEvent--;
282
0
  return rv;
283
0
}
284
285
nsresult
286
TextEventDispatcher::DispatchInputEvent(nsIWidget* aWidget,
287
                                        WidgetInputEvent& aEvent,
288
                                        nsEventStatus& aStatus)
289
0
{
290
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(this);
291
0
  nsCOMPtr<nsIWidget> widget(aWidget);
292
0
  mDispatchingEvent++;
293
0
294
0
  // If the event is dispatched via nsIWidget::DispatchInputEvent(), it
295
0
  // sends the event to the parent process first since APZ needs to handle it
296
0
  // first.  However, some callers (e.g., keyboard apps on B2G and tests
297
0
  // expecting synchronous dispatch) don't want this to do that.
298
0
  nsresult rv = NS_OK;
299
0
  if (ShouldSendInputEventToAPZ()) {
300
0
    aStatus = widget->DispatchInputEvent(&aEvent);
301
0
  } else {
302
0
    rv = widget->DispatchEvent(&aEvent, aStatus);
303
0
  }
304
0
305
0
  mDispatchingEvent--;
306
0
  return rv;
307
0
}
308
309
nsresult
310
TextEventDispatcher::StartComposition(nsEventStatus& aStatus,
311
                                      const WidgetEventTime* aEventTime)
312
0
{
313
0
  aStatus = nsEventStatus_eIgnore;
314
0
315
0
  nsresult rv = GetState();
316
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
317
0
    return rv;
318
0
  }
319
0
320
0
  if (NS_WARN_IF(mIsComposing)) {
321
0
    return NS_ERROR_FAILURE;
322
0
  }
323
0
324
0
  // When you change some members from here, you may need same change in
325
0
  // BeginInputTransactionFor().
326
0
  mIsComposing = mIsHandlingComposition = true;
327
0
  WidgetCompositionEvent compositionStartEvent(true, eCompositionStart,
328
0
                                               mWidget);
329
0
  InitEvent(compositionStartEvent);
330
0
  if (aEventTime) {
331
0
    compositionStartEvent.AssignEventTime(*aEventTime);
332
0
  }
333
0
  rv = DispatchEvent(mWidget, compositionStartEvent, aStatus);
334
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
335
0
    return rv;
336
0
  }
337
0
338
0
  return NS_OK;
339
0
}
340
341
nsresult
342
TextEventDispatcher::StartCompositionAutomaticallyIfNecessary(
343
                       nsEventStatus& aStatus,
344
                       const WidgetEventTime* aEventTime)
345
0
{
346
0
  if (IsComposing()) {
347
0
    return NS_OK;
348
0
  }
349
0
350
0
  nsresult rv = StartComposition(aStatus, aEventTime);
351
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
352
0
    return rv;
353
0
  }
354
0
355
0
  // If started composition has already been committed, we shouldn't dispatch
356
0
  // the compositionchange event.
357
0
  if (!IsComposing()) {
358
0
    aStatus = nsEventStatus_eConsumeNoDefault;
359
0
    return NS_OK;
360
0
  }
361
0
362
0
  // Note that the widget might be destroyed during a call of
363
0
  // StartComposition().  In such case, we shouldn't keep dispatching next
364
0
  // event.
365
0
  rv = GetState();
366
0
  if (NS_FAILED(rv)) {
367
0
    MOZ_ASSERT(rv != NS_ERROR_NOT_INITIALIZED,
368
0
               "aDispatcher must still be initialized in this case");
369
0
    aStatus = nsEventStatus_eConsumeNoDefault;
370
0
    return NS_OK; // Don't throw exception in this case
371
0
  }
372
0
373
0
  aStatus = nsEventStatus_eIgnore;
374
0
  return NS_OK;
375
0
}
376
377
nsresult
378
TextEventDispatcher::CommitComposition(nsEventStatus& aStatus,
379
                                       const nsAString* aCommitString,
380
                                       const WidgetEventTime* aEventTime)
381
0
{
382
0
  aStatus = nsEventStatus_eIgnore;
383
0
384
0
  nsresult rv = GetState();
385
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
386
0
    return rv;
387
0
  }
388
0
389
0
  // When there is no composition, caller shouldn't try to commit composition
390
0
  // with non-existing composition string nor commit composition with empty
391
0
  // string.
392
0
  if (NS_WARN_IF(!IsComposing() &&
393
0
                 (!aCommitString || aCommitString->IsEmpty()))) {
394
0
    return NS_ERROR_FAILURE;
395
0
  }
396
0
397
0
  nsCOMPtr<nsIWidget> widget(mWidget);
398
0
  rv = StartCompositionAutomaticallyIfNecessary(aStatus, aEventTime);
399
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
400
0
    return rv;
401
0
  }
402
0
  if (aStatus == nsEventStatus_eConsumeNoDefault) {
403
0
    return NS_OK;
404
0
  }
405
0
406
0
  // When you change some members from here, you may need same change in
407
0
  // BeginInputTransactionFor().
408
0
409
0
  // End current composition and make this free for other IMEs.
410
0
  mIsComposing = false;
411
0
412
0
  EventMessage message = aCommitString ? eCompositionCommit :
413
0
                                         eCompositionCommitAsIs;
414
0
  WidgetCompositionEvent compositionCommitEvent(true, message, widget);
415
0
  InitEvent(compositionCommitEvent);
416
0
  if (aEventTime) {
417
0
    compositionCommitEvent.AssignEventTime(*aEventTime);
418
0
  }
419
0
  if (message == eCompositionCommit) {
420
0
    compositionCommitEvent.mData = *aCommitString;
421
0
    // Don't send CRLF nor CR, replace it with LF here.
422
0
    compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
423
0
                                                  NS_LITERAL_STRING("\n"));
424
0
    compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r"),
425
0
                                                  NS_LITERAL_STRING("\n"));
426
0
  }
427
0
  rv = DispatchEvent(widget, compositionCommitEvent, aStatus);
428
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
429
0
    return rv;
430
0
  }
431
0
432
0
  return NS_OK;
433
0
}
434
435
nsresult
436
TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
437
0
{
438
0
  nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
439
0
440
0
  switch (aIMENotification.mMessage) {
441
0
    case NOTIFY_IME_OF_BLUR:
442
0
      mHasFocus = false;
443
0
      ClearNotificationRequests();
444
0
      break;
445
0
    case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
446
0
      // If content handles composition events when native IME doesn't have
447
0
      // composition, that means that we completely finished handling
448
0
      // composition(s).  Note that when focused content is in a remote
449
0
      // process, this is sent when all dispatched composition events
450
0
      // have been handled in the remote process.
451
0
      if (!IsComposing()) {
452
0
        mIsHandlingComposition = false;
453
0
      }
454
0
      break;
455
0
    default:
456
0
      break;
457
0
  }
458
0
459
0
460
0
  // First, send the notification to current input transaction's listener.
461
0
  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
462
0
  if (listener) {
463
0
    rv = listener->NotifyIME(this, aIMENotification);
464
0
  }
465
0
466
0
  if (!mWidget) {
467
0
    return rv;
468
0
  }
469
0
470
0
  // If current input transaction isn't for native event handler, we should
471
0
  // send the notification to the native text event dispatcher listener
472
0
  // since native event handler may need to do something from
473
0
  // TextEventDispatcherListener::NotifyIME() even before there is no
474
0
  // input transaction yet.  For example, native IME handler may need to
475
0
  // create new context at receiving NOTIFY_IME_OF_FOCUS.  In this case,
476
0
  // mListener may not be initialized since input transaction should be
477
0
  // initialized immediately before dispatching every WidgetKeyboardEvent
478
0
  // and WidgetCompositionEvent (dispatching events always occurs after
479
0
  // focus move).
480
0
  nsCOMPtr<TextEventDispatcherListener> nativeListener =
481
0
    mWidget->GetNativeTextEventDispatcherListener();
482
0
  if (listener != nativeListener && nativeListener) {
483
0
    switch (aIMENotification.mMessage) {
484
0
      case REQUEST_TO_COMMIT_COMPOSITION:
485
0
      case REQUEST_TO_CANCEL_COMPOSITION:
486
0
        // It's not necessary to notify native IME of requests.
487
0
        break;
488
0
      default: {
489
0
        // Even if current input transaction's listener returns NS_OK or
490
0
        // something, we need to notify native IME of notifications because
491
0
        // when user typing after TIP does something, the changed information
492
0
        // is necessary for them.
493
0
        nsresult rv2 =
494
0
          nativeListener->NotifyIME(this, aIMENotification);
495
0
        // But return the result from current listener except when the
496
0
        // notification isn't handled.
497
0
        if (rv == NS_ERROR_NOT_IMPLEMENTED) {
498
0
          rv = rv2;
499
0
        }
500
0
        break;
501
0
      }
502
0
    }
503
0
  }
504
0
505
0
  if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
506
0
    mHasFocus = true;
507
0
    UpdateNotificationRequests();
508
0
  }
509
0
510
0
  return rv;
511
0
}
512
513
void
514
TextEventDispatcher::ClearNotificationRequests()
515
0
{
516
0
  mIMENotificationRequests = IMENotificationRequests();
517
0
}
518
519
void
520
TextEventDispatcher::UpdateNotificationRequests()
521
0
{
522
0
  ClearNotificationRequests();
523
0
524
0
  // If it doesn't has focus, no notifications are available.
525
0
  if (!mHasFocus || !mWidget) {
526
0
    return;
527
0
  }
528
0
529
0
  // If there is a listener, its requests are necessary.
530
0
  nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
531
0
  if (listener) {
532
0
    mIMENotificationRequests = listener->GetIMENotificationRequests();
533
0
  }
534
0
535
0
  // Even if this is in non-native input transaction, native IME needs
536
0
  // requests.  So, add native IME requests too.
537
0
  if (!IsInNativeInputTransaction()) {
538
0
    nsCOMPtr<TextEventDispatcherListener> nativeListener =
539
0
      mWidget->GetNativeTextEventDispatcherListener();
540
0
    if (nativeListener) {
541
0
      mIMENotificationRequests |= nativeListener->GetIMENotificationRequests();
542
0
    }
543
0
  }
544
0
}
545
546
bool
547
TextEventDispatcher::DispatchKeyboardEvent(
548
                       EventMessage aMessage,
549
                       const WidgetKeyboardEvent& aKeyboardEvent,
550
                       nsEventStatus& aStatus,
551
                       void* aData)
552
0
{
553
0
  return DispatchKeyboardEventInternal(aMessage, aKeyboardEvent, aStatus,
554
0
                                       aData);
555
0
}
556
557
bool
558
TextEventDispatcher::DispatchKeyboardEventInternal(
559
                       EventMessage aMessage,
560
                       const WidgetKeyboardEvent& aKeyboardEvent,
561
                       nsEventStatus& aStatus,
562
                       void* aData,
563
                       uint32_t aIndexOfKeypress,
564
                       bool aNeedsCallback)
565
0
{
566
0
  // Note that this method is also used for dispatching key events on a plugin
567
0
  // because key events on a plugin should be dispatched same as normal key
568
0
  // events.  Then, only some handlers which need to intercept key events
569
0
  // before the focused plugin (e.g., reserved shortcut key handlers) can
570
0
  // consume the events.
571
0
  MOZ_ASSERT(WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
572
0
             WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage) ||
573
0
             aMessage == eKeyPress, "Invalid aMessage value");
574
0
  nsresult rv = GetState();
575
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
576
0
    return false;
577
0
  }
578
0
579
0
  // If the key shouldn't cause keypress events, don't this patch them.
580
0
  if (aMessage == eKeyPress && !aKeyboardEvent.ShouldCauseKeypressEvents()) {
581
0
    return false;
582
0
  }
583
0
584
0
  // Basically, key events shouldn't be dispatched during composition.
585
0
  // Note that plugin process has different IME context.  Therefore, we don't
586
0
  // need to check our composition state when the key event is fired on a
587
0
  // plugin.
588
0
  if (IsComposing() && !WidgetKeyboardEvent::IsKeyEventOnPlugin(aMessage)) {
589
0
    // However, if we need to behave like other browsers, we need the keydown
590
0
    // and keyup events.  Note that this behavior is also allowed by D3E spec.
591
0
    // FYI: keypress events must not be fired during composition.
592
0
    if (!sDispatchKeyEventsDuringComposition || aMessage == eKeyPress) {
593
0
      return false;
594
0
    }
595
0
    // XXX If there was mOnlyContentDispatch for this case, it might be useful
596
0
    //     because our chrome doesn't assume that key events are fired during
597
0
    //     composition.
598
0
  }
599
0
600
0
  WidgetKeyboardEvent keyEvent(true, aMessage, mWidget);
601
0
  InitEvent(keyEvent);
602
0
  keyEvent.AssignKeyEventData(aKeyboardEvent, false);
603
0
604
0
  if (aStatus == nsEventStatus_eConsumeNoDefault) {
605
0
    // If the key event should be dispatched as consumed event, marking it here.
606
0
    // This is useful to prevent double action.  This is intended to the system
607
0
    // has already consumed the event but we need to dispatch the event for
608
0
    // compatibility with older version and other browsers.  So, we should not
609
0
    // stop cross process forwarding of them.
610
0
    keyEvent.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow);
611
0
  }
612
0
613
0
  // Corrects each member for the specific key event type.
614
0
  if (keyEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
615
0
    MOZ_ASSERT(!aIndexOfKeypress,
616
0
      "aIndexOfKeypress must be 0 for non-printable key");
617
0
    // If the keyboard event isn't caused by printable key, its charCode should
618
0
    // be 0.
619
0
    keyEvent.SetCharCode(0);
620
0
  } else {
621
0
    if (WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
622
0
        WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage)) {
623
0
      MOZ_RELEASE_ASSERT(!aIndexOfKeypress,
624
0
        "aIndexOfKeypress must be 0 for either eKeyDown or eKeyUp");
625
0
    } else {
626
0
      MOZ_RELEASE_ASSERT(
627
0
        !aIndexOfKeypress || aIndexOfKeypress < keyEvent.mKeyValue.Length(),
628
0
        "aIndexOfKeypress must be 0 - mKeyValue.Length() - 1");
629
0
    }
630
0
    wchar_t ch =
631
0
      keyEvent.mKeyValue.IsEmpty() ? 0 : keyEvent.mKeyValue[aIndexOfKeypress];
632
0
    keyEvent.SetCharCode(static_cast<uint32_t>(ch));
633
0
    if (aMessage == eKeyPress) {
634
0
      // keyCode of eKeyPress events of printable keys should be always 0.
635
0
      keyEvent.mKeyCode = 0;
636
0
      // eKeyPress events are dispatched for every character.
637
0
      // So, each key value of eKeyPress events should be a character.
638
0
      if (ch) {
639
0
        keyEvent.mKeyValue.Assign(ch);
640
0
      } else {
641
0
        keyEvent.mKeyValue.Truncate();
642
0
      }
643
0
    }
644
0
  }
645
0
  if (WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage)) {
646
0
    // mIsRepeat of keyup event must be false.
647
0
    keyEvent.mIsRepeat = false;
648
0
  }
649
0
  // mIsComposing should be initialized later.
650
0
  keyEvent.mIsComposing = false;
651
0
  if (mInputTransactionType == eNativeInputTransaction) {
652
0
    // Copy mNativeKeyEvent here because for safety for other users of
653
0
    // AssignKeyEventData(), it doesn't copy this.
654
0
    keyEvent.mNativeKeyEvent = aKeyboardEvent.mNativeKeyEvent;
655
0
  } else {
656
0
    // If it's not a keyboard event for native key event, we should ensure that
657
0
    // mNativeKeyEvent and mPluginEvent are null/empty.
658
0
    keyEvent.mNativeKeyEvent = nullptr;
659
0
    keyEvent.mPluginEvent.Clear();
660
0
  }
661
0
  // TODO: Manage mUniqueId here.
662
0
663
0
  // Request the alternative char codes for the key event.
664
0
  // eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
665
0
  // needs to check if a following keypress event is reserved by chrome for
666
0
  // stopping propagation of its preceding keydown event.
667
0
  keyEvent.mAlternativeCharCodes.Clear();
668
0
  if ((WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
669
0
       aMessage == eKeyPress) &&
670
0
      (aNeedsCallback || keyEvent.IsControl() || keyEvent.IsAlt() ||
671
0
       keyEvent.IsMeta() || keyEvent.IsOS())) {
672
0
    nsCOMPtr<TextEventDispatcherListener> listener =
673
0
      do_QueryReferent(mListener);
674
0
    if (listener) {
675
0
      DebugOnly<WidgetKeyboardEvent> original(keyEvent);
676
0
      listener->WillDispatchKeyboardEvent(this, keyEvent, aIndexOfKeypress,
677
0
                                          aData);
678
0
      MOZ_ASSERT(keyEvent.mMessage ==
679
0
                   static_cast<WidgetKeyboardEvent&>(original).mMessage);
680
0
      MOZ_ASSERT(keyEvent.mKeyCode ==
681
0
                   static_cast<WidgetKeyboardEvent&>(original).mKeyCode);
682
0
      MOZ_ASSERT(keyEvent.mLocation ==
683
0
                   static_cast<WidgetKeyboardEvent&>(original).mLocation);
684
0
      MOZ_ASSERT(keyEvent.mIsRepeat ==
685
0
                   static_cast<WidgetKeyboardEvent&>(original).mIsRepeat);
686
0
      MOZ_ASSERT(keyEvent.mIsComposing ==
687
0
                   static_cast<WidgetKeyboardEvent&>(original).mIsComposing);
688
0
      MOZ_ASSERT(keyEvent.mKeyNameIndex ==
689
0
                   static_cast<WidgetKeyboardEvent&>(original).mKeyNameIndex);
690
0
      MOZ_ASSERT(keyEvent.mCodeNameIndex ==
691
0
                   static_cast<WidgetKeyboardEvent&>(original).mCodeNameIndex);
692
0
      MOZ_ASSERT(keyEvent.mKeyValue ==
693
0
                   static_cast<WidgetKeyboardEvent&>(original).mKeyValue);
694
0
      MOZ_ASSERT(keyEvent.mCodeValue ==
695
0
                   static_cast<WidgetKeyboardEvent&>(original).mCodeValue);
696
0
    }
697
0
  }
698
0
699
0
  if (sDispatchKeyPressEventsOnlySystemGroupInContent &&
700
0
      keyEvent.mMessage == eKeyPress &&
701
0
      !keyEvent.ShouldKeyPressEventBeFiredOnContent()) {
702
0
    // Note that even if we set it to true, this may be overwritten by
703
0
    // PresShell::DispatchEventToDOM().
704
0
    keyEvent.mFlags.mOnlySystemGroupDispatchInContent = true;
705
0
  }
706
0
707
0
  DispatchInputEvent(mWidget, keyEvent, aStatus);
708
0
  return true;
709
0
}
710
711
bool
712
TextEventDispatcher::MaybeDispatchKeypressEvents(
713
                       const WidgetKeyboardEvent& aKeyboardEvent,
714
                       nsEventStatus& aStatus,
715
                       void* aData,
716
                       bool aNeedsCallback)
717
0
{
718
0
  // If the key event was consumed, keypress event shouldn't be fired.
719
0
  if (aStatus == nsEventStatus_eConsumeNoDefault) {
720
0
    return false;
721
0
  }
722
0
723
0
  // If the key shouldn't cause keypress events, don't fire them.
724
0
  if (!aKeyboardEvent.ShouldCauseKeypressEvents()) {
725
0
    return false;
726
0
  }
727
0
728
0
  // If the key isn't a printable key or just inputting one character or
729
0
  // no character, we should dispatch only one keypress.  Otherwise, i.e.,
730
0
  // if the key is a printable key and inputs multiple characters, keypress
731
0
  // event should be dispatched the count of inputting characters times.
732
0
  size_t keypressCount =
733
0
    aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ?
734
0
      1 : std::max(static_cast<nsAString::size_type>(1),
735
0
                   aKeyboardEvent.mKeyValue.Length());
736
0
  bool isDispatched = false;
737
0
  bool consumed = false;
738
0
  for (size_t i = 0; i < keypressCount; i++) {
739
0
    aStatus = nsEventStatus_eIgnore;
740
0
    if (!DispatchKeyboardEventInternal(eKeyPress, aKeyboardEvent,
741
0
                                       aStatus, aData, i, aNeedsCallback)) {
742
0
      // The widget must have been gone.
743
0
      break;
744
0
    }
745
0
    isDispatched = true;
746
0
    if (!consumed) {
747
0
      consumed = (aStatus == nsEventStatus_eConsumeNoDefault);
748
0
    }
749
0
  }
750
0
751
0
  // If one of the keypress event was consumed, return ConsumeNoDefault.
752
0
  if (consumed) {
753
0
    aStatus = nsEventStatus_eConsumeNoDefault;
754
0
  }
755
0
756
0
  return isDispatched;
757
0
}
758
759
/******************************************************************************
760
 * TextEventDispatcher::PendingComposition
761
 *****************************************************************************/
762
763
TextEventDispatcher::PendingComposition::PendingComposition()
764
0
{
765
0
  Clear();
766
0
}
767
768
void
769
TextEventDispatcher::PendingComposition::Clear()
770
0
{
771
0
  mString.Truncate();
772
0
  mClauses = nullptr;
773
0
  mCaret.mRangeType = TextRangeType::eUninitialized;
774
0
  mReplacedNativeLineBreakers = false;
775
0
}
776
777
void
778
TextEventDispatcher::PendingComposition::EnsureClauseArray()
779
0
{
780
0
  if (mClauses) {
781
0
    return;
782
0
  }
783
0
  mClauses = new TextRangeArray();
784
0
}
785
786
nsresult
787
TextEventDispatcher::PendingComposition::SetString(const nsAString& aString)
788
0
{
789
0
  MOZ_ASSERT(!mReplacedNativeLineBreakers);
790
0
  mString = aString;
791
0
  return NS_OK;
792
0
}
793
794
nsresult
795
TextEventDispatcher::PendingComposition::AppendClause(
796
                                           uint32_t aLength,
797
                                           TextRangeType aTextRangeType)
798
0
{
799
0
  MOZ_ASSERT(!mReplacedNativeLineBreakers);
800
0
801
0
  if (NS_WARN_IF(!aLength)) {
802
0
    return NS_ERROR_INVALID_ARG;
803
0
  }
804
0
805
0
  switch (aTextRangeType) {
806
0
    case TextRangeType::eRawClause:
807
0
    case TextRangeType::eSelectedRawClause:
808
0
    case TextRangeType::eConvertedClause:
809
0
    case TextRangeType::eSelectedClause: {
810
0
      EnsureClauseArray();
811
0
      TextRange textRange;
812
0
      textRange.mStartOffset =
813
0
        mClauses->IsEmpty() ? 0 : mClauses->LastElement().mEndOffset;
814
0
      textRange.mEndOffset = textRange.mStartOffset + aLength;
815
0
      textRange.mRangeType = aTextRangeType;
816
0
      mClauses->AppendElement(textRange);
817
0
      return NS_OK;
818
0
    }
819
0
    default:
820
0
      return NS_ERROR_INVALID_ARG;
821
0
  }
822
0
}
823
824
nsresult
825
TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset,
826
                                                  uint32_t aLength)
827
0
{
828
0
  MOZ_ASSERT(!mReplacedNativeLineBreakers);
829
0
830
0
  mCaret.mStartOffset = aOffset;
831
0
  mCaret.mEndOffset = mCaret.mStartOffset + aLength;
832
0
  mCaret.mRangeType = TextRangeType::eCaret;
833
0
  return NS_OK;
834
0
}
835
836
nsresult
837
TextEventDispatcher::PendingComposition::Set(const nsAString& aString,
838
                                             const TextRangeArray* aRanges)
839
0
{
840
0
  Clear();
841
0
842
0
  nsresult rv = SetString(aString);
843
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
844
0
    return rv;
845
0
  }
846
0
847
0
  if (!aRanges || aRanges->IsEmpty()) {
848
0
    // Create dummy range if mString isn't empty.
849
0
    if (!mString.IsEmpty()) {
850
0
      rv = AppendClause(mString.Length(), TextRangeType::eRawClause);
851
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
852
0
        return rv;
853
0
      }
854
0
      ReplaceNativeLineBreakers();
855
0
    }
856
0
    return NS_OK;
857
0
  }
858
0
859
0
  // Adjust offsets in the ranges for XP linefeed character (only \n).
860
0
  for (uint32_t i = 0; i < aRanges->Length(); ++i) {
861
0
    TextRange range = aRanges->ElementAt(i);
862
0
    if (range.mRangeType == TextRangeType::eCaret) {
863
0
      mCaret = range;
864
0
    } else {
865
0
      EnsureClauseArray();
866
0
      mClauses->AppendElement(range);
867
0
    }
868
0
  }
869
0
  ReplaceNativeLineBreakers();
870
0
  return NS_OK;
871
0
}
872
873
void
874
TextEventDispatcher::PendingComposition::ReplaceNativeLineBreakers()
875
0
{
876
0
  mReplacedNativeLineBreakers = true;
877
0
878
0
  // If the composition string is empty, we don't need to do anything.
879
0
  if (mString.IsEmpty()) {
880
0
    return;
881
0
  }
882
0
883
0
  nsAutoString nativeString(mString);
884
0
  // Don't expose CRLF nor CR to web contents, instead, use LF.
885
0
  mString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n"));
886
0
  mString.ReplaceSubstring(NS_LITERAL_STRING("\r"), NS_LITERAL_STRING("\n"));
887
0
888
0
  // If the length isn't changed, we don't need to adjust any offset and length
889
0
  // of mClauses nor mCaret.
890
0
  if (nativeString.Length() == mString.Length()) {
891
0
    return;
892
0
  }
893
0
894
0
  if (mClauses) {
895
0
    for (TextRange& clause : *mClauses) {
896
0
      AdjustRange(clause, nativeString);
897
0
    }
898
0
  }
899
0
  if (mCaret.mRangeType == TextRangeType::eCaret) {
900
0
    AdjustRange(mCaret, nativeString);
901
0
  }
902
0
}
903
904
// static
905
void
906
TextEventDispatcher::PendingComposition::AdjustRange(
907
                                           TextRange& aRange,
908
                                           const nsAString& aNativeString)
909
0
{
910
0
  TextRange nativeRange = aRange;
911
0
  // XXX Following code wastes runtime cost because this causes computing
912
0
  //     mStartOffset for each clause from the start of composition string.
913
0
  //     If we'd make TextRange have only its length, we don't need to do
914
0
  //     this.  However, this must not be so serious problem because
915
0
  //     composition string is usually short and separated as a few clauses.
916
0
  if (nativeRange.mStartOffset > 0) {
917
0
    nsAutoString preText(
918
0
      Substring(aNativeString, 0, nativeRange.mStartOffset));
919
0
    preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
920
0
                             NS_LITERAL_STRING("\n"));
921
0
    aRange.mStartOffset = preText.Length();
922
0
  }
923
0
  if (nativeRange.Length() == 0) {
924
0
    aRange.mEndOffset = aRange.mStartOffset;
925
0
  } else {
926
0
    nsAutoString clause(
927
0
      Substring(aNativeString, nativeRange.mStartOffset, nativeRange.Length()));
928
0
    clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
929
0
                            NS_LITERAL_STRING("\n"));
930
0
    aRange.mEndOffset = aRange.mStartOffset + clause.Length();
931
0
  }
932
0
}
933
934
nsresult
935
TextEventDispatcher::PendingComposition::Flush(
936
                                           TextEventDispatcher* aDispatcher,
937
                                           nsEventStatus& aStatus,
938
                                           const WidgetEventTime* aEventTime)
939
0
{
940
0
  aStatus = nsEventStatus_eIgnore;
941
0
942
0
  nsresult rv = aDispatcher->GetState();
943
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
944
0
    return rv;
945
0
  }
946
0
947
0
  if (mClauses && !mClauses->IsEmpty() &&
948
0
      mClauses->LastElement().mEndOffset != mString.Length()) {
949
0
    NS_WARNING("Sum of length of the all clauses must be same as the string "
950
0
               "length");
951
0
    Clear();
952
0
    return NS_ERROR_ILLEGAL_VALUE;
953
0
  }
954
0
  if (mCaret.mRangeType == TextRangeType::eCaret) {
955
0
    if (mCaret.mEndOffset > mString.Length()) {
956
0
      NS_WARNING("Caret position is out of the composition string");
957
0
      Clear();
958
0
      return NS_ERROR_ILLEGAL_VALUE;
959
0
    }
960
0
    EnsureClauseArray();
961
0
    mClauses->AppendElement(mCaret);
962
0
  }
963
0
964
0
  // If the composition string is set without Set(), we need to replace native
965
0
  // line breakers in the composition string with XP line breaker.
966
0
  if (!mReplacedNativeLineBreakers) {
967
0
    ReplaceNativeLineBreakers();
968
0
  }
969
0
970
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(aDispatcher);
971
0
  nsCOMPtr<nsIWidget> widget(aDispatcher->mWidget);
972
0
  WidgetCompositionEvent compChangeEvent(true, eCompositionChange, widget);
973
0
  aDispatcher->InitEvent(compChangeEvent);
974
0
  if (aEventTime) {
975
0
    compChangeEvent.AssignEventTime(*aEventTime);
976
0
  }
977
0
  compChangeEvent.mData = mString;
978
0
  if (mClauses) {
979
0
    MOZ_ASSERT(!mClauses->IsEmpty(),
980
0
               "mClauses must be non-empty array when it's not nullptr");
981
0
    compChangeEvent.mRanges = mClauses;
982
0
  }
983
0
984
0
  // While this method dispatches a composition event, some other event handler
985
0
  // cause more clauses to be added.  So, we should clear pending composition
986
0
  // before dispatching the event.
987
0
  Clear();
988
0
989
0
  rv = aDispatcher->StartCompositionAutomaticallyIfNecessary(aStatus,
990
0
                                                             aEventTime);
991
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
992
0
    return rv;
993
0
  }
994
0
  if (aStatus == nsEventStatus_eConsumeNoDefault) {
995
0
    return NS_OK;
996
0
  }
997
0
  rv = aDispatcher->DispatchEvent(widget, compChangeEvent, aStatus);
998
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
999
0
    return rv;
1000
0
  }
1001
0
1002
0
  return NS_OK;
1003
0
}
1004
1005
} // namespace widget
1006
} // namespace mozilla