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