Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/TextInputProcessor.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "gfxPrefs.h"
8
#include "mozilla/dom/Event.h"
9
#include "mozilla/EventForwards.h"
10
#include "mozilla/TextEventDispatcher.h"
11
#include "mozilla/TextEvents.h"
12
#include "mozilla/TextInputProcessor.h"
13
#include "mozilla/widget/IMEData.h"
14
#include "mozilla/dom/KeyboardEvent.h"
15
#include "nsContentUtils.h"
16
#include "nsIDocShell.h"
17
#include "nsIWidget.h"
18
#include "nsPIDOMWindow.h"
19
#include "nsPresContext.h"
20
21
using mozilla::dom::Event;
22
using mozilla::dom::KeyboardEvent;
23
using namespace mozilla::widget;
24
25
namespace mozilla {
26
27
/******************************************************************************
28
 * TextInputProcessorNotification
29
 ******************************************************************************/
30
31
class TextInputProcessorNotification final :
32
        public nsITextInputProcessorNotification
33
{
34
  typedef IMENotification::SelectionChangeData SelectionChangeData;
35
  typedef IMENotification::SelectionChangeDataBase SelectionChangeDataBase;
36
  typedef IMENotification::TextChangeData TextChangeData;
37
  typedef IMENotification::TextChangeDataBase TextChangeDataBase;
38
39
public:
40
  explicit TextInputProcessorNotification(const char* aType)
41
    : mType(aType)
42
    , mTextChangeData()
43
0
  {
44
0
  }
45
46
  explicit TextInputProcessorNotification(
47
             const TextChangeDataBase& aTextChangeData)
48
    : mType("notify-text-change")
49
    , mTextChangeData(aTextChangeData)
50
0
  {
51
0
  }
52
53
  explicit TextInputProcessorNotification(
54
             const SelectionChangeDataBase& aSelectionChangeData)
55
    : mType("notify-selection-change")
56
    , mSelectionChangeData(aSelectionChangeData)
57
0
  {
58
0
    // SelectionChangeDataBase::mString still refers nsString instance owned
59
0
    // by aSelectionChangeData.  So, this needs to copy the instance.
60
0
    nsString* string = new nsString(aSelectionChangeData.String());
61
0
    mSelectionChangeData.mString = string;
62
0
  }
63
64
  NS_DECL_ISUPPORTS
65
66
  NS_IMETHOD GetType(nsACString& aType) final
67
0
  {
68
0
    aType = mType;
69
0
    return NS_OK;
70
0
  }
71
72
  // "notify-text-change" and "notify-selection-change"
73
  NS_IMETHOD GetOffset(uint32_t* aOffset) final
74
0
  {
75
0
    if (NS_WARN_IF(!aOffset)) {
76
0
      return NS_ERROR_INVALID_ARG;
77
0
    }
78
0
    if (IsSelectionChange()) {
79
0
      *aOffset = mSelectionChangeData.mOffset;
80
0
      return NS_OK;
81
0
    }
82
0
    if (IsTextChange()) {
83
0
      *aOffset = mTextChangeData.mStartOffset;
84
0
      return NS_OK;
85
0
    }
86
0
    return NS_ERROR_NOT_AVAILABLE;
87
0
  }
88
89
  // "notify-selection-change"
90
  NS_IMETHOD GetText(nsAString& aText) final
91
0
  {
92
0
    if (IsSelectionChange()) {
93
0
      aText = mSelectionChangeData.String();
94
0
      return NS_OK;
95
0
    }
96
0
    return NS_ERROR_NOT_AVAILABLE;
97
0
  }
98
99
  NS_IMETHOD GetCollapsed(bool* aCollapsed) final
100
0
  {
101
0
    if (NS_WARN_IF(!aCollapsed)) {
102
0
      return NS_ERROR_INVALID_ARG;
103
0
    }
104
0
    if (IsSelectionChange()) {
105
0
      *aCollapsed = mSelectionChangeData.IsCollapsed();
106
0
      return NS_OK;
107
0
    }
108
0
    return NS_ERROR_NOT_AVAILABLE;
109
0
  }
110
111
  NS_IMETHOD GetLength(uint32_t* aLength) final
112
0
  {
113
0
    if (NS_WARN_IF(!aLength)) {
114
0
      return NS_ERROR_INVALID_ARG;
115
0
    }
116
0
    if (IsSelectionChange()) {
117
0
      *aLength = mSelectionChangeData.Length();
118
0
      return NS_OK;
119
0
    }
120
0
    return NS_ERROR_NOT_AVAILABLE;
121
0
  }
122
123
  NS_IMETHOD GetReversed(bool* aReversed) final
124
0
  {
125
0
    if (NS_WARN_IF(!aReversed)) {
126
0
      return NS_ERROR_INVALID_ARG;
127
0
    }
128
0
    if (IsSelectionChange()) {
129
0
      *aReversed = mSelectionChangeData.mReversed;
130
0
      return NS_OK;
131
0
    }
132
0
    return NS_ERROR_NOT_AVAILABLE;
133
0
  }
134
135
  NS_IMETHOD GetWritingMode(nsACString& aWritingMode) final
136
0
  {
137
0
    if (IsSelectionChange()) {
138
0
      WritingMode writingMode = mSelectionChangeData.GetWritingMode();
139
0
      if (!writingMode.IsVertical()) {
140
0
        aWritingMode.AssignLiteral("horizontal-tb");
141
0
      } else if (writingMode.IsVerticalLR()) {
142
0
        aWritingMode.AssignLiteral("vertical-lr");
143
0
      } else {
144
0
        aWritingMode.AssignLiteral("vertical-rl");
145
0
      }
146
0
      return NS_OK;
147
0
    }
148
0
    return NS_ERROR_NOT_AVAILABLE;
149
0
  }
150
151
  NS_IMETHOD GetCausedByComposition(bool* aCausedByComposition) final
152
0
  {
153
0
    if (NS_WARN_IF(!aCausedByComposition)) {
154
0
      return NS_ERROR_INVALID_ARG;
155
0
    }
156
0
    if (IsSelectionChange()) {
157
0
      *aCausedByComposition = mSelectionChangeData.mCausedByComposition;
158
0
      return NS_OK;
159
0
    }
160
0
    return NS_ERROR_NOT_AVAILABLE;
161
0
  }
162
163
  NS_IMETHOD GetCausedBySelectionEvent(
164
               bool* aCausedBySelectionEvent) final
165
0
  {
166
0
    if (NS_WARN_IF(!aCausedBySelectionEvent)) {
167
0
      return NS_ERROR_INVALID_ARG;
168
0
    }
169
0
    if (IsSelectionChange()) {
170
0
      *aCausedBySelectionEvent = mSelectionChangeData.mCausedBySelectionEvent;
171
0
      return NS_OK;
172
0
    }
173
0
    return NS_ERROR_NOT_AVAILABLE;
174
0
  }
175
176
  NS_IMETHOD GetOccurredDuringComposition(
177
               bool* aOccurredDuringComposition) final
178
0
  {
179
0
    if (NS_WARN_IF(!aOccurredDuringComposition)) {
180
0
      return NS_ERROR_INVALID_ARG;
181
0
    }
182
0
    if (IsSelectionChange()) {
183
0
      *aOccurredDuringComposition =
184
0
        mSelectionChangeData.mOccurredDuringComposition;
185
0
      return NS_OK;
186
0
    }
187
0
    return NS_ERROR_NOT_AVAILABLE;
188
0
  }
189
190
  // "notify-text-change"
191
  NS_IMETHOD GetRemovedLength(uint32_t* aLength) final
192
0
  {
193
0
    if (NS_WARN_IF(!aLength)) {
194
0
      return NS_ERROR_INVALID_ARG;
195
0
    }
196
0
    if (IsTextChange()) {
197
0
      *aLength = mTextChangeData.OldLength();
198
0
      return NS_OK;
199
0
    }
200
0
    return NS_ERROR_NOT_AVAILABLE;
201
0
  }
202
203
  NS_IMETHOD GetAddedLength(uint32_t* aLength) final
204
0
  {
205
0
    if (NS_WARN_IF(!aLength)) {
206
0
      return NS_ERROR_INVALID_ARG;
207
0
    }
208
0
    if (IsTextChange()) {
209
0
      *aLength = mTextChangeData.NewLength();
210
0
      return NS_OK;
211
0
    }
212
0
    return NS_ERROR_NOT_AVAILABLE;
213
0
  }
214
215
  NS_IMETHOD GetCausedOnlyByComposition(
216
               bool* aCausedOnlyByComposition) final
217
0
  {
218
0
    if (NS_WARN_IF(!aCausedOnlyByComposition)) {
219
0
      return NS_ERROR_INVALID_ARG;
220
0
    }
221
0
    if (IsTextChange()) {
222
0
      *aCausedOnlyByComposition = mTextChangeData.mCausedOnlyByComposition;
223
0
      return NS_OK;
224
0
    }
225
0
    return NS_ERROR_NOT_AVAILABLE;
226
0
  }
227
228
  NS_IMETHOD GetIncludingChangesDuringComposition(
229
               bool* aIncludingChangesDuringComposition) final
230
0
  {
231
0
    if (NS_WARN_IF(!aIncludingChangesDuringComposition)) {
232
0
      return NS_ERROR_INVALID_ARG;
233
0
    }
234
0
    if (IsTextChange()) {
235
0
      *aIncludingChangesDuringComposition =
236
0
        mTextChangeData.mIncludingChangesDuringComposition;
237
0
      return NS_OK;
238
0
    }
239
0
    return NS_ERROR_NOT_AVAILABLE;
240
0
  }
241
242
  NS_IMETHOD GetIncludingChangesWithoutComposition(
243
               bool* aIncludingChangesWithoutComposition) final
244
0
  {
245
0
    if (NS_WARN_IF(!aIncludingChangesWithoutComposition)) {
246
0
      return NS_ERROR_INVALID_ARG;
247
0
    }
248
0
    if (IsTextChange()) {
249
0
      *aIncludingChangesWithoutComposition =
250
0
        mTextChangeData.mIncludingChangesWithoutComposition;
251
0
      return NS_OK;
252
0
    }
253
0
    return NS_ERROR_NOT_AVAILABLE;
254
0
  }
255
256
protected:
257
  virtual ~TextInputProcessorNotification()
258
0
  {
259
0
    if (IsSelectionChange()) {
260
0
      delete mSelectionChangeData.mString;
261
0
      mSelectionChangeData.mString = nullptr;
262
0
    }
263
0
  }
264
265
  bool IsTextChange() const
266
0
  {
267
0
    return mType.EqualsLiteral("notify-text-change");
268
0
  }
269
270
  bool IsSelectionChange() const
271
0
  {
272
0
    return mType.EqualsLiteral("notify-selection-change");
273
0
  }
274
275
private:
276
  nsAutoCString mType;
277
  union
278
  {
279
    TextChangeDataBase mTextChangeData;
280
    SelectionChangeDataBase mSelectionChangeData;
281
  };
282
283
  TextInputProcessorNotification()
284
    : mTextChangeData()
285
0
  {
286
0
  }
287
};
288
289
NS_IMPL_ISUPPORTS(TextInputProcessorNotification,
290
                  nsITextInputProcessorNotification)
291
292
/******************************************************************************
293
 * TextInputProcessor
294
 ******************************************************************************/
295
296
NS_IMPL_ISUPPORTS(TextInputProcessor,
297
                  nsITextInputProcessor,
298
                  TextEventDispatcherListener,
299
                  nsISupportsWeakReference)
300
301
TextInputProcessor::TextInputProcessor()
302
  : mDispatcher(nullptr)
303
  , mForTests(false)
304
0
{
305
0
}
306
307
TextInputProcessor::~TextInputProcessor()
308
0
{
309
0
  if (mDispatcher && mDispatcher->IsComposing()) {
310
0
    // If this is composing and not canceling the composition, nobody can steal
311
0
    // the rights of TextEventDispatcher from this instance.  Therefore, this
312
0
    // needs to cancel the composition here.
313
0
    if (NS_SUCCEEDED(IsValidStateForComposition())) {
314
0
      RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
315
0
      nsEventStatus status = nsEventStatus_eIgnore;
316
0
      kungFuDeathGrip->CommitComposition(status, &EmptyString());
317
0
    }
318
0
  }
319
0
}
320
321
bool
322
TextInputProcessor::IsComposing() const
323
0
{
324
0
  return mDispatcher && mDispatcher->IsComposing();
325
0
}
326
327
NS_IMETHODIMP
328
TextInputProcessor::GetHasComposition(bool* aHasComposition)
329
0
{
330
0
  MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr");
331
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
332
0
  *aHasComposition = IsComposing();
333
0
  return NS_OK;
334
0
}
335
336
NS_IMETHODIMP
337
TextInputProcessor::BeginInputTransaction(
338
                      mozIDOMWindow* aWindow,
339
                      nsITextInputProcessorCallback* aCallback,
340
                      bool* aSucceeded)
341
0
{
342
0
  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
343
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
344
0
  if (NS_WARN_IF(!aCallback)) {
345
0
    *aSucceeded = false;
346
0
    return NS_ERROR_INVALID_ARG;
347
0
  }
348
0
  return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
349
0
}
350
351
NS_IMETHODIMP
352
TextInputProcessor::BeginInputTransactionForTests(
353
                      mozIDOMWindow* aWindow,
354
                      nsITextInputProcessorCallback* aCallback,
355
                      uint8_t aOptionalArgc,
356
                      bool* aSucceeded)
357
0
{
358
0
  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
359
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
360
0
  nsITextInputProcessorCallback* callback =
361
0
    aOptionalArgc >= 1 ? aCallback : nullptr;
362
0
  return BeginInputTransactionInternal(aWindow, callback, true, *aSucceeded);
363
0
}
364
365
nsresult
366
TextInputProcessor::BeginInputTransactionInternal(
367
                      mozIDOMWindow* aWindow,
368
                      nsITextInputProcessorCallback* aCallback,
369
                      bool aForTests,
370
                      bool& aSucceeded)
371
0
{
372
0
  aSucceeded = false;
373
0
  if (NS_WARN_IF(!aWindow)) {
374
0
    return NS_ERROR_INVALID_ARG;
375
0
  }
376
0
  nsCOMPtr<nsPIDOMWindowInner> pWindow = nsPIDOMWindowInner::From(aWindow);
377
0
  if (NS_WARN_IF(!pWindow)) {
378
0
    return NS_ERROR_INVALID_ARG;
379
0
  }
380
0
  nsCOMPtr<nsIDocShell> docShell(pWindow->GetDocShell());
381
0
  if (NS_WARN_IF(!docShell)) {
382
0
    return NS_ERROR_FAILURE;
383
0
  }
384
0
  RefPtr<nsPresContext> presContext;
385
0
  nsresult rv = docShell->GetPresContext(getter_AddRefs(presContext));
386
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
387
0
    return rv;
388
0
  }
389
0
  if (NS_WARN_IF(!presContext)) {
390
0
    return NS_ERROR_FAILURE;
391
0
  }
392
0
  nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
393
0
  if (NS_WARN_IF(!widget)) {
394
0
    return NS_ERROR_FAILURE;
395
0
  }
396
0
397
0
  RefPtr<TextEventDispatcher> dispatcher = widget->GetTextEventDispatcher();
398
0
  MOZ_RELEASE_ASSERT(dispatcher, "TextEventDispatcher must not be null");
399
0
400
0
  // If the instance was initialized and is being initialized for same
401
0
  // dispatcher and same purpose, we don't need to initialize the dispatcher
402
0
  // again.
403
0
  if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback &&
404
0
      aForTests == mForTests) {
405
0
    aSucceeded = true;
406
0
    return NS_OK;
407
0
  }
408
0
409
0
  // If this instance is composing or dispatching an event, don't allow to
410
0
  // initialize again.  Especially, if we allow to begin input transaction with
411
0
  // another TextEventDispatcher during dispatching an event, it may cause that
412
0
  // nobody cannot begin input transaction with it if the last event causes
413
0
  // opening modal dialog.
414
0
  if (mDispatcher &&
415
0
      (mDispatcher->IsComposing() || mDispatcher->IsDispatchingEvent())) {
416
0
    return NS_ERROR_ALREADY_INITIALIZED;
417
0
  }
418
0
419
0
  // And also if another instance is composing with the new dispatcher or
420
0
  // dispatching an event, it'll fail to steal its ownership.  Then, we should
421
0
  // not throw an exception, just return false.
422
0
  if (dispatcher->IsComposing() || dispatcher->IsDispatchingEvent()) {
423
0
    return NS_OK;
424
0
  }
425
0
426
0
  // This instance has finished preparing to link to the dispatcher.  Therefore,
427
0
  // let's forget the old dispatcher and purpose.
428
0
  if (mDispatcher) {
429
0
    mDispatcher->EndInputTransaction(this);
430
0
    if (NS_WARN_IF(mDispatcher)) {
431
0
      // Forcibly initialize the members if we failed to end the input
432
0
      // transaction.
433
0
      UnlinkFromTextEventDispatcher();
434
0
    }
435
0
  }
436
0
437
0
  if (aForTests) {
438
0
    bool isAPZAware = gfxPrefs::TestEventsAsyncEnabled();
439
0
    rv = dispatcher->BeginTestInputTransaction(this, isAPZAware);
440
0
  } else {
441
0
    rv = dispatcher->BeginInputTransaction(this);
442
0
  }
443
0
444
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
445
0
    return rv;
446
0
  }
447
0
448
0
  mDispatcher = dispatcher;
449
0
  mCallback = aCallback;
450
0
  mForTests = aForTests;
451
0
  aSucceeded = true;
452
0
  return NS_OK;
453
0
}
454
455
void
456
TextInputProcessor::UnlinkFromTextEventDispatcher()
457
0
{
458
0
  mDispatcher = nullptr;
459
0
  mForTests = false;
460
0
  if (mCallback) {
461
0
    nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback);
462
0
    mCallback = nullptr;
463
0
464
0
    RefPtr<TextInputProcessorNotification> notification =
465
0
      new TextInputProcessorNotification("notify-end-input-transaction");
466
0
    bool result = false;
467
0
    callback->OnNotify(this, notification, &result);
468
0
  }
469
0
}
470
471
nsresult
472
TextInputProcessor::IsValidStateForComposition()
473
0
{
474
0
  if (NS_WARN_IF(!mDispatcher)) {
475
0
    return NS_ERROR_NOT_INITIALIZED;
476
0
  }
477
0
478
0
  nsresult rv = mDispatcher->GetState();
479
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
480
0
    return rv;
481
0
  }
482
0
483
0
  return NS_OK;
484
0
}
485
486
bool
487
TextInputProcessor::IsValidEventTypeForComposition(
488
                      const WidgetKeyboardEvent& aKeyboardEvent) const
489
0
{
490
0
  // The key event type of composition methods must be "", "keydown" or "keyup".
491
0
  if (aKeyboardEvent.mMessage == eKeyDown ||
492
0
      aKeyboardEvent.mMessage == eKeyUp) {
493
0
    return true;
494
0
  }
495
0
  if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
496
0
      aKeyboardEvent.mSpecifiedEventType &&
497
0
      nsDependentAtomString(
498
0
        aKeyboardEvent.mSpecifiedEventType).EqualsLiteral("on")) {
499
0
    return true;
500
0
  }
501
0
  return false;
502
0
}
503
504
TextInputProcessor::EventDispatcherResult
505
TextInputProcessor::MaybeDispatchKeydownForComposition(
506
                      const WidgetKeyboardEvent* aKeyboardEvent,
507
                      uint32_t aKeyFlags)
508
0
{
509
0
  EventDispatcherResult result;
510
0
511
0
  result.mResult = IsValidStateForComposition();
512
0
  if (NS_WARN_IF(NS_FAILED(result.mResult))) {
513
0
    result.mCanContinue = false;
514
0
    return result;
515
0
  }
516
0
517
0
  if (!aKeyboardEvent) {
518
0
    return result;
519
0
  }
520
0
521
0
  // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
522
0
  // eKeyDown event.
523
0
  if (aKeyboardEvent->mMessage == eKeyUp) {
524
0
    return result;
525
0
  }
526
0
527
0
  // Modifier keys are not allowed because managing modifier state in this
528
0
  // method makes this messy.
529
0
  if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) {
530
0
    result.mResult = NS_ERROR_INVALID_ARG;
531
0
    result.mCanContinue = false;
532
0
    return result;
533
0
  }
534
0
535
0
  uint32_t consumedFlags = 0;
536
0
537
0
  result.mResult = KeydownInternal(*aKeyboardEvent, aKeyFlags, false,
538
0
                                   consumedFlags);
539
0
  result.mDoDefault = !consumedFlags;
540
0
  if (NS_WARN_IF(NS_FAILED(result.mResult))) {
541
0
    result.mCanContinue = false;
542
0
    return result;
543
0
  }
544
0
545
0
  result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
546
0
  return result;
547
0
}
548
549
TextInputProcessor::EventDispatcherResult
550
TextInputProcessor::MaybeDispatchKeyupForComposition(
551
                      const WidgetKeyboardEvent* aKeyboardEvent,
552
                      uint32_t aKeyFlags)
553
0
{
554
0
  EventDispatcherResult result;
555
0
556
0
  if (!aKeyboardEvent) {
557
0
    return result;
558
0
  }
559
0
560
0
  // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
561
0
  // eKeyUp event.
562
0
  if (aKeyboardEvent->mMessage == eKeyDown) {
563
0
    return result;
564
0
  }
565
0
566
0
  // If the widget has been destroyed, we can do nothing here.
567
0
  result.mResult = IsValidStateForComposition();
568
0
  if (NS_FAILED(result.mResult)) {
569
0
    result.mCanContinue = false;
570
0
    return result;
571
0
  }
572
0
573
0
  result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault);
574
0
  if (NS_WARN_IF(NS_FAILED(result.mResult))) {
575
0
    result.mCanContinue = false;
576
0
    return result;
577
0
  }
578
0
579
0
  result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
580
0
  return result;
581
0
}
582
583
nsresult
584
TextInputProcessor::PrepareKeyboardEventForComposition(
585
                      KeyboardEvent* aDOMKeyEvent,
586
                      uint32_t& aKeyFlags,
587
                      uint8_t aOptionalArgc,
588
                      WidgetKeyboardEvent*& aKeyboardEvent)
589
0
{
590
0
  aKeyboardEvent = nullptr;
591
0
592
0
  aKeyboardEvent =
593
0
    aOptionalArgc && aDOMKeyEvent ?
594
0
      aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent() : nullptr;
595
0
  if (!aKeyboardEvent || aOptionalArgc < 2) {
596
0
    aKeyFlags = 0;
597
0
  }
598
0
599
0
  if (!aKeyboardEvent) {
600
0
    return NS_OK;
601
0
  }
602
0
603
0
  if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) {
604
0
    return NS_ERROR_INVALID_ARG;
605
0
  }
606
0
607
0
  return NS_OK;
608
0
}
609
610
NS_IMETHODIMP
611
TextInputProcessor::StartComposition(Event* aDOMKeyEvent,
612
                                     uint32_t aKeyFlags,
613
                                     uint8_t aOptionalArgc,
614
                                     bool* aSucceeded)
615
0
{
616
0
  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
617
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
618
0
  *aSucceeded = false;
619
0
620
0
  RefPtr<KeyboardEvent> keyEvent;
621
0
  if (aDOMKeyEvent) {
622
0
    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
623
0
    if (NS_WARN_IF(!keyEvent)) {
624
0
      return NS_ERROR_INVALID_ARG;
625
0
    }
626
0
  }
627
0
628
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
629
0
630
0
  WidgetKeyboardEvent* keyboardEvent;
631
0
  nsresult rv =
632
0
    PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
633
0
                                       keyboardEvent);
634
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
635
0
    return rv;
636
0
  }
637
0
638
0
  EventDispatcherResult dispatcherResult =
639
0
    MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
640
0
  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
641
0
      !dispatcherResult.mCanContinue) {
642
0
    return dispatcherResult.mResult;
643
0
  }
644
0
645
0
  if (dispatcherResult.mDoDefault) {
646
0
    nsEventStatus status = nsEventStatus_eIgnore;
647
0
    rv = kungFuDeathGrip->StartComposition(status);
648
0
    *aSucceeded = status != nsEventStatus_eConsumeNoDefault &&
649
0
                    kungFuDeathGrip && kungFuDeathGrip->IsComposing();
650
0
  }
651
0
652
0
  MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
653
0
654
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
655
0
    return rv;
656
0
  }
657
0
  return NS_OK;
658
0
}
659
660
NS_IMETHODIMP
661
TextInputProcessor::SetPendingCompositionString(const nsAString& aString)
662
0
{
663
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
664
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
665
0
  nsresult rv = IsValidStateForComposition();
666
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
667
0
    return rv;
668
0
  }
669
0
  return kungFuDeathGrip->SetPendingCompositionString(aString);
670
0
}
671
672
NS_IMETHODIMP
673
TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength,
674
                                                     uint32_t aAttribute)
675
0
{
676
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
677
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
678
0
  TextRangeType textRangeType;
679
0
  switch (aAttribute) {
680
0
    case ATTR_RAW_CLAUSE:
681
0
    case ATTR_SELECTED_RAW_CLAUSE:
682
0
    case ATTR_CONVERTED_CLAUSE:
683
0
    case ATTR_SELECTED_CLAUSE:
684
0
      textRangeType = ToTextRangeType(aAttribute);
685
0
      break;
686
0
    default:
687
0
      return NS_ERROR_INVALID_ARG;
688
0
  }
689
0
  nsresult rv = IsValidStateForComposition();
690
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
691
0
    return rv;
692
0
  }
693
0
  return kungFuDeathGrip->AppendClauseToPendingComposition(aLength, textRangeType);
694
0
}
695
696
NS_IMETHODIMP
697
TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset)
698
0
{
699
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
700
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
701
0
  nsresult rv = IsValidStateForComposition();
702
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
703
0
    return rv;
704
0
  }
705
0
  return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0);
706
0
}
707
708
NS_IMETHODIMP
709
TextInputProcessor::FlushPendingComposition(Event* aDOMKeyEvent,
710
                                            uint32_t aKeyFlags,
711
                                            uint8_t aOptionalArgc,
712
                                            bool* aSucceeded)
713
0
{
714
0
  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
715
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
716
0
717
0
  // Even if this doesn't flush pending composition actually, we need to reset
718
0
  // pending composition for starting next composition with new user input.
719
0
  AutoPendingCompositionResetter resetter(this);
720
0
721
0
  *aSucceeded = false;
722
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
723
0
  bool wasComposing = IsComposing();
724
0
725
0
  RefPtr<KeyboardEvent> keyEvent;
726
0
  if (aDOMKeyEvent) {
727
0
    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
728
0
    if (NS_WARN_IF(!keyEvent)) {
729
0
      return NS_ERROR_INVALID_ARG;
730
0
    }
731
0
  }
732
0
733
0
  WidgetKeyboardEvent* keyboardEvent;
734
0
  nsresult rv =
735
0
    PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
736
0
                                       keyboardEvent);
737
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
738
0
    return rv;
739
0
  }
740
0
741
0
  EventDispatcherResult dispatcherResult =
742
0
    MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
743
0
  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
744
0
      !dispatcherResult.mCanContinue) {
745
0
    return dispatcherResult.mResult;
746
0
  }
747
0
748
0
  // Even if the preceding keydown event was consumed, if the composition
749
0
  // was already started, we shouldn't prevent the change of composition.
750
0
  if (dispatcherResult.mDoDefault || wasComposing) {
751
0
    // Preceding keydown event may cause destroying the widget.
752
0
    if (NS_FAILED(IsValidStateForComposition())) {
753
0
      return NS_OK;
754
0
    }
755
0
    nsEventStatus status = nsEventStatus_eIgnore;
756
0
    rv = kungFuDeathGrip->FlushPendingComposition(status);
757
0
    *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
758
0
  }
759
0
760
0
  MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
761
0
762
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
763
0
    return rv;
764
0
  }
765
0
  return NS_OK;
766
0
}
767
768
NS_IMETHODIMP
769
TextInputProcessor::CommitComposition(Event* aDOMKeyEvent,
770
                                      uint32_t aKeyFlags,
771
                                      uint8_t aOptionalArgc)
772
0
{
773
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
774
0
775
0
  RefPtr<KeyboardEvent> keyEvent;
776
0
  if (aDOMKeyEvent) {
777
0
    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
778
0
    if (NS_WARN_IF(!keyEvent)) {
779
0
      return NS_ERROR_INVALID_ARG;
780
0
    }
781
0
  }
782
0
783
0
  WidgetKeyboardEvent* keyboardEvent;
784
0
  nsresult rv =
785
0
    PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
786
0
                                       keyboardEvent);
787
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
788
0
    return rv;
789
0
  }
790
0
791
0
  return CommitCompositionInternal(keyboardEvent, aKeyFlags);
792
0
}
793
794
NS_IMETHODIMP
795
TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString,
796
                                          Event* aDOMKeyEvent,
797
                                          uint32_t aKeyFlags,
798
                                          uint8_t aOptionalArgc,
799
                                          bool* aSucceeded)
800
0
{
801
0
  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
802
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
803
0
804
0
  RefPtr<KeyboardEvent> keyEvent;
805
0
  if (aDOMKeyEvent) {
806
0
    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
807
0
    if (NS_WARN_IF(!keyEvent)) {
808
0
      return NS_ERROR_INVALID_ARG;
809
0
    }
810
0
  }
811
0
812
0
  WidgetKeyboardEvent* keyboardEvent;
813
0
  nsresult rv =
814
0
    PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
815
0
                                       keyboardEvent);
816
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
817
0
    return rv;
818
0
  }
819
0
820
0
  return CommitCompositionInternal(keyboardEvent, aKeyFlags,
821
0
                                   &aCommitString, aSucceeded);
822
0
}
823
824
nsresult
825
TextInputProcessor::CommitCompositionInternal(
826
                      const WidgetKeyboardEvent* aKeyboardEvent,
827
                      uint32_t aKeyFlags,
828
                      const nsAString* aCommitString,
829
                      bool* aSucceeded)
830
0
{
831
0
  if (aSucceeded) {
832
0
    *aSucceeded = false;
833
0
  }
834
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
835
0
  bool wasComposing = IsComposing();
836
0
837
0
  EventDispatcherResult dispatcherResult =
838
0
    MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
839
0
  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
840
0
      !dispatcherResult.mCanContinue) {
841
0
    return dispatcherResult.mResult;
842
0
  }
843
0
844
0
  // Even if the preceding keydown event was consumed, if the composition
845
0
  // was already started, we shouldn't prevent the commit of composition.
846
0
  nsresult rv = NS_OK;
847
0
  if (dispatcherResult.mDoDefault || wasComposing) {
848
0
    // Preceding keydown event may cause destroying the widget.
849
0
    if (NS_FAILED(IsValidStateForComposition())) {
850
0
      return NS_OK;
851
0
    }
852
0
    nsEventStatus status = nsEventStatus_eIgnore;
853
0
    rv = kungFuDeathGrip->CommitComposition(status, aCommitString);
854
0
    if (aSucceeded) {
855
0
      *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
856
0
    }
857
0
  }
858
0
859
0
  MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
860
0
861
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
862
0
    return rv;
863
0
  }
864
0
  return NS_OK;
865
0
}
866
867
NS_IMETHODIMP
868
TextInputProcessor::CancelComposition(Event* aDOMKeyEvent,
869
                                      uint32_t aKeyFlags,
870
                                      uint8_t aOptionalArgc)
871
0
{
872
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
873
0
874
0
  RefPtr<KeyboardEvent> keyEvent;
875
0
  if (aDOMKeyEvent) {
876
0
    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
877
0
    if (NS_WARN_IF(!keyEvent)) {
878
0
      return NS_ERROR_INVALID_ARG;
879
0
    }
880
0
  }
881
0
882
0
  WidgetKeyboardEvent* keyboardEvent;
883
0
  nsresult rv =
884
0
    PrepareKeyboardEventForComposition(keyEvent, aKeyFlags, aOptionalArgc,
885
0
                                       keyboardEvent);
886
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
887
0
    return rv;
888
0
  }
889
0
890
0
  return CancelCompositionInternal(keyboardEvent, aKeyFlags);
891
0
}
892
893
nsresult
894
TextInputProcessor::CancelCompositionInternal(
895
                      const WidgetKeyboardEvent* aKeyboardEvent,
896
                      uint32_t aKeyFlags)
897
0
{
898
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
899
0
900
0
  EventDispatcherResult dispatcherResult =
901
0
    MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
902
0
  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
903
0
      !dispatcherResult.mCanContinue) {
904
0
    return dispatcherResult.mResult;
905
0
  }
906
0
907
0
  nsEventStatus status = nsEventStatus_eIgnore;
908
0
  nsresult rv = kungFuDeathGrip->CommitComposition(status, &EmptyString());
909
0
910
0
  MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
911
0
912
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
913
0
    return rv;
914
0
  }
915
0
  return NS_OK;
916
0
}
917
918
NS_IMETHODIMP
919
TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
920
                              const IMENotification& aNotification)
921
0
{
922
0
  // If This is called while this is being initialized, ignore the call.
923
0
  // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
924
0
  // we can say, TextInputProcessor doesn't implement any handlers of the
925
0
  // requests and notifications.
926
0
  if (!mDispatcher) {
927
0
    return NS_ERROR_NOT_IMPLEMENTED;
928
0
  }
929
0
  MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
930
0
             "Wrong TextEventDispatcher notifies this");
931
0
  NS_ASSERTION(mForTests || mCallback,
932
0
               "mCallback can be null only when IME is initialized for tests");
933
0
  if (mCallback) {
934
0
    RefPtr<TextInputProcessorNotification> notification;
935
0
    switch (aNotification.mMessage) {
936
0
      case REQUEST_TO_COMMIT_COMPOSITION: {
937
0
        NS_ASSERTION(aTextEventDispatcher->IsComposing(),
938
0
                     "Why is this requested without composition?");
939
0
        notification = new TextInputProcessorNotification("request-to-commit");
940
0
        break;
941
0
      }
942
0
      case REQUEST_TO_CANCEL_COMPOSITION: {
943
0
        NS_ASSERTION(aTextEventDispatcher->IsComposing(),
944
0
                     "Why is this requested without composition?");
945
0
        notification = new TextInputProcessorNotification("request-to-cancel");
946
0
        break;
947
0
      }
948
0
      case NOTIFY_IME_OF_FOCUS:
949
0
        notification = new TextInputProcessorNotification("notify-focus");
950
0
        break;
951
0
      case NOTIFY_IME_OF_BLUR:
952
0
        notification = new TextInputProcessorNotification("notify-blur");
953
0
        break;
954
0
      case NOTIFY_IME_OF_TEXT_CHANGE:
955
0
        notification = new TextInputProcessorNotification(
956
0
                             aNotification.mTextChangeData);
957
0
        break;
958
0
      case NOTIFY_IME_OF_SELECTION_CHANGE:
959
0
        notification = new TextInputProcessorNotification(
960
0
                             aNotification.mSelectionChangeData);
961
0
        break;
962
0
      case NOTIFY_IME_OF_POSITION_CHANGE:
963
0
        notification = new TextInputProcessorNotification(
964
0
                             "notify-position-change");
965
0
        break;
966
0
      default:
967
0
        return NS_ERROR_NOT_IMPLEMENTED;
968
0
    }
969
0
    MOZ_RELEASE_ASSERT(notification);
970
0
    bool result = false;
971
0
    nsresult rv = mCallback->OnNotify(this, notification, &result);
972
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
973
0
      return rv;
974
0
    }
975
0
    return result ? NS_OK : NS_ERROR_FAILURE;
976
0
  }
977
0
978
0
  switch (aNotification.mMessage) {
979
0
    case REQUEST_TO_COMMIT_COMPOSITION: {
980
0
      NS_ASSERTION(aTextEventDispatcher->IsComposing(),
981
0
                   "Why is this requested without composition?");
982
0
      CommitCompositionInternal();
983
0
      return NS_OK;
984
0
    }
985
0
    case REQUEST_TO_CANCEL_COMPOSITION: {
986
0
      NS_ASSERTION(aTextEventDispatcher->IsComposing(),
987
0
                   "Why is this requested without composition?");
988
0
      CancelCompositionInternal();
989
0
      return NS_OK;
990
0
    }
991
0
    default:
992
0
      return NS_ERROR_NOT_IMPLEMENTED;
993
0
  }
994
0
}
995
996
NS_IMETHODIMP_(IMENotificationRequests)
997
TextInputProcessor::GetIMENotificationRequests()
998
0
{
999
0
  // TextInputProcessor should support all change notifications.
1000
0
  return IMENotificationRequests(
1001
0
           IMENotificationRequests::NOTIFY_TEXT_CHANGE |
1002
0
           IMENotificationRequests::NOTIFY_POSITION_CHANGE);
1003
0
}
1004
1005
NS_IMETHODIMP_(void)
1006
TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher)
1007
0
{
1008
0
  // If This is called while this is being initialized, ignore the call.
1009
0
  if (!mDispatcher) {
1010
0
    return;
1011
0
  }
1012
0
  MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
1013
0
             "Wrong TextEventDispatcher notifies this");
1014
0
  UnlinkFromTextEventDispatcher();
1015
0
}
1016
1017
NS_IMETHODIMP_(void)
1018
TextInputProcessor::WillDispatchKeyboardEvent(
1019
                      TextEventDispatcher* aTextEventDispatcher,
1020
                      WidgetKeyboardEvent& aKeyboardEvent,
1021
                      uint32_t aIndexOfKeypress,
1022
                      void* aData)
1023
0
{
1024
0
  // TextInputProcessor doesn't set alternative char code nor modify charCode
1025
0
  // even when Ctrl key is pressed.
1026
0
}
1027
1028
nsresult
1029
TextInputProcessor::PrepareKeyboardEventToDispatch(
1030
                      WidgetKeyboardEvent& aKeyboardEvent,
1031
                      uint32_t aKeyFlags)
1032
0
{
1033
0
  if (NS_WARN_IF(aKeyboardEvent.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
1034
0
    return NS_ERROR_INVALID_ARG;
1035
0
  }
1036
0
  if ((aKeyFlags & KEY_NON_PRINTABLE_KEY) &&
1037
0
      NS_WARN_IF(aKeyboardEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING)) {
1038
0
    return NS_ERROR_INVALID_ARG;
1039
0
  }
1040
0
  if ((aKeyFlags & KEY_FORCE_PRINTABLE_KEY) &&
1041
0
      aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
1042
0
    aKeyboardEvent.GetDOMKeyName(aKeyboardEvent.mKeyValue);
1043
0
    aKeyboardEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
1044
0
  }
1045
0
  if (aKeyFlags & KEY_KEEP_KEY_LOCATION_STANDARD) {
1046
0
    // If .location is initialized with specific value, using
1047
0
    // KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller.
1048
0
    // Let's throw an exception for notifying the developer of this bug.
1049
0
    if (NS_WARN_IF(aKeyboardEvent.mLocation)) {
1050
0
      return NS_ERROR_INVALID_ARG;
1051
0
    }
1052
0
  } else if (!aKeyboardEvent.mLocation) {
1053
0
    // If KeyboardEvent.mLocation is 0, it may be uninitialized.  If so, we
1054
0
    // should compute proper mLocation value from its .code value.
1055
0
    aKeyboardEvent.mLocation =
1056
0
      WidgetKeyboardEvent::ComputeLocationFromCodeValue(
1057
0
        aKeyboardEvent.mCodeNameIndex);
1058
0
  }
1059
0
1060
0
  if (aKeyFlags & KEY_KEEP_KEYCODE_ZERO) {
1061
0
    // If .keyCode is initialized with specific value, using
1062
0
    // KEY_KEEP_KEYCODE_ZERO must be a bug of the caller.  Let's throw an
1063
0
    // exception for notifying the developer of such bug.
1064
0
    if (NS_WARN_IF(aKeyboardEvent.mKeyCode)) {
1065
0
      return NS_ERROR_INVALID_ARG;
1066
0
    }
1067
0
  } else if (!aKeyboardEvent.mKeyCode &&
1068
0
             aKeyboardEvent.mKeyNameIndex > KEY_NAME_INDEX_Unidentified &&
1069
0
             aKeyboardEvent.mKeyNameIndex < KEY_NAME_INDEX_USE_STRING) {
1070
0
    // If KeyboardEvent.keyCode is 0, it may be uninitialized.  If so, we may
1071
0
    // be able to decide a good .keyCode value if the .key value is a
1072
0
    // non-printable key.
1073
0
    aKeyboardEvent.mKeyCode =
1074
0
      WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
1075
0
        aKeyboardEvent.mKeyNameIndex);
1076
0
  }
1077
0
1078
0
  aKeyboardEvent.mIsSynthesizedByTIP = (mForTests)? false : true;
1079
0
1080
0
  return NS_OK;
1081
0
}
1082
1083
NS_IMETHODIMP
1084
TextInputProcessor::Keydown(Event* aDOMKeyEvent,
1085
                            uint32_t aKeyFlags,
1086
                            uint8_t aOptionalArgc,
1087
                            uint32_t* aConsumedFlags)
1088
0
{
1089
0
  MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr");
1090
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1091
0
  if (!aOptionalArgc) {
1092
0
    aKeyFlags = 0;
1093
0
  }
1094
0
  if (NS_WARN_IF(!aDOMKeyEvent)) {
1095
0
    return NS_ERROR_INVALID_ARG;
1096
0
  }
1097
0
  WidgetKeyboardEvent* originalKeyEvent =
1098
0
    aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
1099
0
  if (NS_WARN_IF(!originalKeyEvent)) {
1100
0
    return NS_ERROR_INVALID_ARG;
1101
0
  }
1102
0
  return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
1103
0
}
1104
1105
nsresult
1106
TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
1107
                                    uint32_t aKeyFlags,
1108
                                    bool aAllowToDispatchKeypress,
1109
                                    uint32_t& aConsumedFlags)
1110
0
{
1111
0
  aConsumedFlags = KEYEVENT_NOT_CONSUMED;
1112
0
1113
0
  // We shouldn't modify the internal WidgetKeyboardEvent.
1114
0
  WidgetKeyboardEvent keyEvent(aKeyboardEvent);
1115
0
  nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
1116
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1117
0
    return rv;
1118
0
  }
1119
0
1120
0
  aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED :
1121
0
                                                         KEYEVENT_NOT_CONSUMED;
1122
0
1123
0
  if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
1124
0
    ModifierKeyData modifierKeyData(keyEvent);
1125
0
    if (WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
1126
0
      // If the modifier key is lockable modifier key such as CapsLock,
1127
0
      // let's toggle modifier key state at keydown.
1128
0
      ToggleModifierKey(modifierKeyData);
1129
0
    } else {
1130
0
      // Activate modifier flag before dispatching keydown event (i.e., keydown
1131
0
      // event should indicate the releasing modifier is active.
1132
0
      ActivateModifierKey(modifierKeyData);
1133
0
    }
1134
0
    if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
1135
0
      return NS_OK;
1136
0
    }
1137
0
  } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
1138
0
    return NS_ERROR_INVALID_ARG;
1139
0
  }
1140
0
  keyEvent.mModifiers = GetActiveModifiers();
1141
0
1142
0
  if (!aAllowToDispatchKeypress &&
1143
0
      !(aKeyFlags & KEY_DONT_MARK_KEYDOWN_AS_PROCESSED)) {
1144
0
    keyEvent.mKeyCode = NS_VK_PROCESSKEY;
1145
0
    keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
1146
0
  }
1147
0
1148
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
1149
0
  rv = IsValidStateForComposition();
1150
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1151
0
    return rv;
1152
0
  }
1153
0
1154
0
  nsEventStatus status = aConsumedFlags ? nsEventStatus_eConsumeNoDefault :
1155
0
                                          nsEventStatus_eIgnore;
1156
0
  if (!kungFuDeathGrip->DispatchKeyboardEvent(eKeyDown, keyEvent, status)) {
1157
0
    // If keydown event isn't dispatched, we don't need to dispatch keypress
1158
0
    // events.
1159
0
    return NS_OK;
1160
0
  }
1161
0
1162
0
  aConsumedFlags |=
1163
0
    (status == nsEventStatus_eConsumeNoDefault) ? KEYDOWN_IS_CONSUMED :
1164
0
                                                  KEYEVENT_NOT_CONSUMED;
1165
0
1166
0
  if (aAllowToDispatchKeypress &&
1167
0
      kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) {
1168
0
    aConsumedFlags |=
1169
0
      (status == nsEventStatus_eConsumeNoDefault) ? KEYPRESS_IS_CONSUMED :
1170
0
                                                    KEYEVENT_NOT_CONSUMED;
1171
0
  }
1172
0
1173
0
  return NS_OK;
1174
0
}
1175
1176
NS_IMETHODIMP
1177
TextInputProcessor::Keyup(Event* aDOMKeyEvent,
1178
                          uint32_t aKeyFlags,
1179
                          uint8_t aOptionalArgc,
1180
                          bool* aDoDefault)
1181
0
{
1182
0
  MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
1183
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1184
0
  if (!aOptionalArgc) {
1185
0
    aKeyFlags = 0;
1186
0
  }
1187
0
  if (NS_WARN_IF(!aDOMKeyEvent)) {
1188
0
    return NS_ERROR_INVALID_ARG;
1189
0
  }
1190
0
  WidgetKeyboardEvent* originalKeyEvent =
1191
0
    aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
1192
0
  if (NS_WARN_IF(!originalKeyEvent)) {
1193
0
    return NS_ERROR_INVALID_ARG;
1194
0
  }
1195
0
  return KeyupInternal(*originalKeyEvent, aKeyFlags, *aDoDefault);
1196
0
}
1197
1198
nsresult
1199
TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
1200
                                  uint32_t aKeyFlags,
1201
                                  bool& aDoDefault)
1202
0
{
1203
0
  aDoDefault = false;
1204
0
1205
0
  // We shouldn't modify the internal WidgetKeyboardEvent.
1206
0
  WidgetKeyboardEvent keyEvent(aKeyboardEvent);
1207
0
  nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
1208
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1209
0
    return rv;
1210
0
  }
1211
0
1212
0
  aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
1213
0
1214
0
  if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
1215
0
    if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
1216
0
      // Inactivate modifier flag before dispatching keyup event (i.e., keyup
1217
0
      // event shouldn't indicate the releasing modifier is active.
1218
0
      InactivateModifierKey(ModifierKeyData(keyEvent));
1219
0
    }
1220
0
    if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
1221
0
      return NS_OK;
1222
0
    }
1223
0
  } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
1224
0
    return NS_ERROR_INVALID_ARG;
1225
0
  }
1226
0
  keyEvent.mModifiers = GetActiveModifiers();
1227
0
1228
0
  if (aKeyFlags & KEY_MARK_KEYUP_AS_PROCESSED) {
1229
0
    keyEvent.mKeyCode = NS_VK_PROCESSKEY;
1230
0
    keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
1231
0
  }
1232
0
1233
0
  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
1234
0
  rv = IsValidStateForComposition();
1235
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1236
0
    return rv;
1237
0
  }
1238
0
1239
0
  nsEventStatus status = aDoDefault ? nsEventStatus_eIgnore :
1240
0
                                      nsEventStatus_eConsumeNoDefault;
1241
0
  kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status);
1242
0
  aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
1243
0
  return NS_OK;
1244
0
}
1245
1246
NS_IMETHODIMP
1247
TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
1248
                                     bool* aActive)
1249
0
{
1250
0
  MOZ_RELEASE_ASSERT(aActive, "aActive must not be null");
1251
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1252
0
  if (!mModifierKeyDataArray) {
1253
0
    *aActive = false;
1254
0
    return NS_OK;
1255
0
  }
1256
0
  Modifiers activeModifiers = mModifierKeyDataArray->GetActiveModifiers();
1257
0
  Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName);
1258
0
  *aActive = ((activeModifiers & modifier) != 0);
1259
0
  return NS_OK;
1260
0
}
1261
1262
NS_IMETHODIMP
1263
TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther)
1264
0
{
1265
0
  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1266
0
  if (!aOther) {
1267
0
    mModifierKeyDataArray = nullptr;
1268
0
    return NS_OK;
1269
0
  }
1270
0
  TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
1271
0
  if (!other->mModifierKeyDataArray) {
1272
0
    other->mModifierKeyDataArray = new ModifierKeyDataArray();
1273
0
  }
1274
0
  mModifierKeyDataArray = other->mModifierKeyDataArray;
1275
0
  return NS_OK;
1276
0
}
1277
1278
/******************************************************************************
1279
 * TextInputProcessor::AutoPendingCompositionResetter
1280
 ******************************************************************************/
1281
TextInputProcessor::AutoPendingCompositionResetter::
1282
  AutoPendingCompositionResetter(TextInputProcessor* aTIP)
1283
  : mTIP(aTIP)
1284
0
{
1285
0
  MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null");
1286
0
}
1287
1288
TextInputProcessor::AutoPendingCompositionResetter::
1289
  ~AutoPendingCompositionResetter()
1290
0
{
1291
0
  if (mTIP->mDispatcher) {
1292
0
    mTIP->mDispatcher->ClearPendingComposition();
1293
0
  }
1294
0
}
1295
1296
/******************************************************************************
1297
 * TextInputProcessor::ModifierKeyData
1298
 ******************************************************************************/
1299
TextInputProcessor::ModifierKeyData::ModifierKeyData(
1300
  const WidgetKeyboardEvent& aKeyboardEvent)
1301
  : mKeyNameIndex(aKeyboardEvent.mKeyNameIndex)
1302
  , mCodeNameIndex(aKeyboardEvent.mCodeNameIndex)
1303
0
{
1304
0
  mModifier = WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex);
1305
0
  MOZ_ASSERT(mModifier, "mKeyNameIndex must be a modifier key name");
1306
0
}
1307
1308
/******************************************************************************
1309
 * TextInputProcessor::ModifierKeyDataArray
1310
 ******************************************************************************/
1311
Modifiers
1312
TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const
1313
0
{
1314
0
  Modifiers result = MODIFIER_NONE;
1315
0
  for (uint32_t i = 0; i < Length(); i++) {
1316
0
    result |= ElementAt(i).mModifier;
1317
0
  }
1318
0
  return result;
1319
0
}
1320
1321
void
1322
TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
1323
  const TextInputProcessor::ModifierKeyData& aModifierKeyData)
1324
0
{
1325
0
  if (Contains(aModifierKeyData)) {
1326
0
    return;
1327
0
  }
1328
0
  AppendElement(aModifierKeyData);
1329
0
}
1330
1331
void
1332
TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey(
1333
  const TextInputProcessor::ModifierKeyData& aModifierKeyData)
1334
0
{
1335
0
  RemoveElement(aModifierKeyData);
1336
0
}
1337
1338
void
1339
TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey(
1340
  const TextInputProcessor::ModifierKeyData& aModifierKeyData)
1341
0
{
1342
0
  auto index = IndexOf(aModifierKeyData);
1343
0
  if (index == NoIndex) {
1344
0
    AppendElement(aModifierKeyData);
1345
0
    return;
1346
0
  }
1347
0
  RemoveElementAt(index);
1348
0
}
1349
1350
} // namespace mozilla