TextInputProcessor.cpp (61295B)
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 "mozilla/TextInputProcessor.h" 8 9 #include "mozilla/EventForwards.h" 10 #include "mozilla/Maybe.h" 11 #include "mozilla/NativeKeyBindingsType.h" 12 #include "mozilla/StaticPrefs_test.h" 13 #include "mozilla/TextEventDispatcher.h" 14 #include "mozilla/TextEvents.h" 15 #include "mozilla/WritingModes.h" 16 #include "mozilla/dom/Event.h" 17 #include "mozilla/dom/KeyboardEvent.h" 18 #include "mozilla/widget/IMEData.h" 19 #include "nsContentUtils.h" 20 #include "nsIDocShell.h" 21 #include "nsIWidget.h" 22 #include "nsPIDOMWindow.h" 23 #include "nsPresContext.h" 24 25 using mozilla::dom::Event; 26 using mozilla::dom::KeyboardEvent; 27 using namespace mozilla::widget; 28 29 namespace mozilla { 30 31 /****************************************************************************** 32 * TextInputProcessorNotification 33 ******************************************************************************/ 34 35 class TextInputProcessorNotification final 36 : public nsITextInputProcessorNotification { 37 using SelectionChangeData = IMENotification::SelectionChangeData; 38 using SelectionChangeDataBase = IMENotification::SelectionChangeDataBase; 39 using TextChangeData = IMENotification::TextChangeData; 40 using TextChangeDataBase = IMENotification::TextChangeDataBase; 41 42 public: 43 explicit TextInputProcessorNotification(const char* aType) 44 : mType(aType), mTextChangeData() {} 45 46 explicit TextInputProcessorNotification( 47 const TextChangeDataBase& aTextChangeData) 48 : mType("notify-text-change"), mTextChangeData(aTextChangeData) {} 49 50 explicit TextInputProcessorNotification( 51 const SelectionChangeDataBase& aSelectionChangeData) 52 : mType("notify-selection-change"), 53 mSelectionChangeData(aSelectionChangeData) { 54 // SelectionChangeDataBase::mString still refers nsString instance owned 55 // by aSelectionChangeData. So, this needs to copy the instance. 56 if (aSelectionChangeData.HasRange()) { 57 mSelectionChangeData.mString = 58 new nsString(aSelectionChangeData.String()); 59 } else { 60 mSelectionChangeData.mString = nullptr; 61 } 62 } 63 64 NS_DECL_ISUPPORTS 65 66 NS_IMETHOD GetType(nsACString& aType) final { 67 aType = mType; 68 return NS_OK; 69 } 70 71 // "notify-text-change" and "notify-selection-change" 72 NS_IMETHOD GetOffset(uint32_t* aOffset) final { 73 if (NS_WARN_IF(!aOffset)) { 74 return NS_ERROR_INVALID_ARG; 75 } 76 if (IsSelectionChange()) { 77 if (!mSelectionChangeData.HasRange()) { 78 return NS_ERROR_NOT_AVAILABLE; 79 } 80 *aOffset = mSelectionChangeData.mOffset; 81 return NS_OK; 82 } 83 if (IsTextChange()) { 84 *aOffset = mTextChangeData.mStartOffset; 85 return NS_OK; 86 } 87 return NS_ERROR_NOT_AVAILABLE; 88 } 89 90 // "notify-selection-change" 91 NS_IMETHOD GetHasRange(bool* aHasRange) final { 92 if (IsSelectionChange()) { 93 *aHasRange = mSelectionChangeData.HasRange(); 94 return NS_OK; 95 } 96 return NS_ERROR_NOT_AVAILABLE; 97 } 98 NS_IMETHOD GetText(nsAString& aText) final { 99 if (IsSelectionChange()) { 100 if (!mSelectionChangeData.HasRange()) { 101 return NS_ERROR_NOT_AVAILABLE; 102 } 103 aText = mSelectionChangeData.String(); 104 return NS_OK; 105 } 106 return NS_ERROR_NOT_AVAILABLE; 107 } 108 109 NS_IMETHOD GetCollapsed(bool* aCollapsed) final { 110 if (NS_WARN_IF(!aCollapsed)) { 111 return NS_ERROR_INVALID_ARG; 112 } 113 if (IsSelectionChange()) { 114 *aCollapsed = mSelectionChangeData.IsCollapsed(); 115 return NS_OK; 116 } 117 return NS_ERROR_NOT_AVAILABLE; 118 } 119 120 NS_IMETHOD GetLength(uint32_t* aLength) final { 121 if (NS_WARN_IF(!aLength)) { 122 return NS_ERROR_INVALID_ARG; 123 } 124 if (IsSelectionChange()) { 125 if (!mSelectionChangeData.HasRange()) { 126 return NS_ERROR_NOT_AVAILABLE; 127 } 128 *aLength = mSelectionChangeData.Length(); 129 return NS_OK; 130 } 131 return NS_ERROR_NOT_AVAILABLE; 132 } 133 134 NS_IMETHOD GetReversed(bool* aReversed) final { 135 if (NS_WARN_IF(!aReversed)) { 136 return NS_ERROR_INVALID_ARG; 137 } 138 if (IsSelectionChange()) { 139 if (!mSelectionChangeData.HasRange()) { 140 return NS_ERROR_NOT_AVAILABLE; 141 } 142 *aReversed = mSelectionChangeData.mReversed; 143 return NS_OK; 144 } 145 return NS_ERROR_NOT_AVAILABLE; 146 } 147 148 NS_IMETHOD GetWritingMode(nsACString& aWritingMode) final { 149 if (IsSelectionChange()) { 150 WritingMode writingMode = mSelectionChangeData.GetWritingMode(); 151 if (!writingMode.IsVertical()) { 152 aWritingMode.AssignLiteral("horizontal-tb"); 153 } else if (writingMode.IsVerticalLR()) { 154 aWritingMode.AssignLiteral("vertical-lr"); 155 } else { 156 aWritingMode.AssignLiteral("vertical-rl"); 157 } 158 return NS_OK; 159 } 160 return NS_ERROR_NOT_AVAILABLE; 161 } 162 163 NS_IMETHOD GetCausedByComposition(bool* aCausedByComposition) final { 164 if (NS_WARN_IF(!aCausedByComposition)) { 165 return NS_ERROR_INVALID_ARG; 166 } 167 if (IsSelectionChange()) { 168 *aCausedByComposition = mSelectionChangeData.mCausedByComposition; 169 return NS_OK; 170 } 171 return NS_ERROR_NOT_AVAILABLE; 172 } 173 174 NS_IMETHOD GetCausedBySelectionEvent(bool* aCausedBySelectionEvent) final { 175 if (NS_WARN_IF(!aCausedBySelectionEvent)) { 176 return NS_ERROR_INVALID_ARG; 177 } 178 if (IsSelectionChange()) { 179 *aCausedBySelectionEvent = mSelectionChangeData.mCausedBySelectionEvent; 180 return NS_OK; 181 } 182 return NS_ERROR_NOT_AVAILABLE; 183 } 184 185 NS_IMETHOD GetOccurredDuringComposition( 186 bool* aOccurredDuringComposition) final { 187 if (NS_WARN_IF(!aOccurredDuringComposition)) { 188 return NS_ERROR_INVALID_ARG; 189 } 190 if (IsSelectionChange()) { 191 *aOccurredDuringComposition = 192 mSelectionChangeData.mOccurredDuringComposition; 193 return NS_OK; 194 } 195 return NS_ERROR_NOT_AVAILABLE; 196 } 197 198 // "notify-text-change" 199 NS_IMETHOD GetRemovedLength(uint32_t* aLength) final { 200 if (NS_WARN_IF(!aLength)) { 201 return NS_ERROR_INVALID_ARG; 202 } 203 if (IsTextChange()) { 204 *aLength = mTextChangeData.OldLength(); 205 return NS_OK; 206 } 207 return NS_ERROR_NOT_AVAILABLE; 208 } 209 210 NS_IMETHOD GetAddedLength(uint32_t* aLength) final { 211 if (NS_WARN_IF(!aLength)) { 212 return NS_ERROR_INVALID_ARG; 213 } 214 if (IsTextChange()) { 215 *aLength = mTextChangeData.NewLength(); 216 return NS_OK; 217 } 218 return NS_ERROR_NOT_AVAILABLE; 219 } 220 221 NS_IMETHOD GetCausedOnlyByComposition(bool* aCausedOnlyByComposition) final { 222 if (NS_WARN_IF(!aCausedOnlyByComposition)) { 223 return NS_ERROR_INVALID_ARG; 224 } 225 if (IsTextChange()) { 226 *aCausedOnlyByComposition = mTextChangeData.mCausedOnlyByComposition; 227 return NS_OK; 228 } 229 return NS_ERROR_NOT_AVAILABLE; 230 } 231 232 NS_IMETHOD GetIncludingChangesDuringComposition( 233 bool* aIncludingChangesDuringComposition) final { 234 if (NS_WARN_IF(!aIncludingChangesDuringComposition)) { 235 return NS_ERROR_INVALID_ARG; 236 } 237 if (IsTextChange()) { 238 *aIncludingChangesDuringComposition = 239 mTextChangeData.mIncludingChangesDuringComposition; 240 return NS_OK; 241 } 242 return NS_ERROR_NOT_AVAILABLE; 243 } 244 245 NS_IMETHOD GetIncludingChangesWithoutComposition( 246 bool* aIncludingChangesWithoutComposition) final { 247 if (NS_WARN_IF(!aIncludingChangesWithoutComposition)) { 248 return NS_ERROR_INVALID_ARG; 249 } 250 if (IsTextChange()) { 251 *aIncludingChangesWithoutComposition = 252 mTextChangeData.mIncludingChangesWithoutComposition; 253 return NS_OK; 254 } 255 return NS_ERROR_NOT_AVAILABLE; 256 } 257 258 protected: 259 virtual ~TextInputProcessorNotification() { 260 if (IsSelectionChange() && mSelectionChangeData.mString) { 261 delete mSelectionChangeData.mString; 262 mSelectionChangeData.mString = nullptr; 263 } 264 } 265 266 bool IsTextChange() const { 267 return mType.EqualsLiteral("notify-text-change"); 268 } 269 270 bool IsSelectionChange() const { 271 return mType.EqualsLiteral("notify-selection-change"); 272 } 273 274 private: 275 nsAutoCString mType; 276 union { 277 TextChangeDataBase mTextChangeData; 278 SelectionChangeDataBase mSelectionChangeData; 279 }; 280 281 TextInputProcessorNotification() : mTextChangeData() {} 282 }; 283 284 NS_IMPL_ISUPPORTS(TextInputProcessorNotification, 285 nsITextInputProcessorNotification) 286 287 /****************************************************************************** 288 * TextInputProcessor 289 ******************************************************************************/ 290 291 NS_IMPL_ISUPPORTS(TextInputProcessor, nsITextInputProcessor, 292 TextEventDispatcherListener, nsISupportsWeakReference) 293 294 TextInputProcessor::TextInputProcessor() 295 : mDispatcher(nullptr), mForTests(false) {} 296 297 TextInputProcessor::~TextInputProcessor() { 298 if (mDispatcher && mDispatcher->IsComposing()) { 299 // If this is composing and not canceling the composition, nobody can steal 300 // the rights of TextEventDispatcher from this instance. Therefore, this 301 // needs to cancel the composition here. 302 if (NS_SUCCEEDED(IsValidStateForComposition())) { 303 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 304 nsEventStatus status = nsEventStatus_eIgnore; 305 kungFuDeathGrip->CommitComposition(status, &EmptyString()); 306 } 307 } 308 } 309 310 bool TextInputProcessor::IsComposing() const { 311 return mDispatcher && mDispatcher->IsComposing(); 312 } 313 314 NS_IMETHODIMP 315 TextInputProcessor::GetHasComposition(bool* aHasComposition) { 316 MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr"); 317 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 318 *aHasComposition = IsComposing(); 319 return NS_OK; 320 } 321 322 NS_IMETHODIMP 323 TextInputProcessor::BeginInputTransaction( 324 mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback, 325 bool* aSucceeded) { 326 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); 327 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 328 if (NS_WARN_IF(!aCallback)) { 329 *aSucceeded = false; 330 return NS_ERROR_INVALID_ARG; 331 } 332 return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded); 333 } 334 335 NS_IMETHODIMP 336 TextInputProcessor::BeginInputTransactionForTests( 337 mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback, 338 uint8_t aOptionalArgc, bool* aSucceeded) { 339 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); 340 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 341 nsITextInputProcessorCallback* callback = 342 aOptionalArgc >= 1 ? aCallback : nullptr; 343 return BeginInputTransactionInternal(aWindow, callback, true, *aSucceeded); 344 } 345 346 nsresult TextInputProcessor::BeginInputTransactionForFuzzing( 347 nsPIDOMWindowInner* aWindow, nsITextInputProcessorCallback* aCallback, 348 bool* aSucceeded) { 349 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); 350 return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded); 351 } 352 353 nsresult TextInputProcessor::BeginInputTransactionInternal( 354 mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback, 355 bool aForTests, bool& aSucceeded) { 356 aSucceeded = false; 357 if (NS_WARN_IF(!aWindow)) { 358 return NS_ERROR_INVALID_ARG; 359 } 360 nsCOMPtr<nsPIDOMWindowInner> pWindow = nsPIDOMWindowInner::From(aWindow); 361 if (NS_WARN_IF(!pWindow)) { 362 return NS_ERROR_INVALID_ARG; 363 } 364 nsCOMPtr<nsIDocShell> docShell(pWindow->GetDocShell()); 365 if (NS_WARN_IF(!docShell)) { 366 return NS_ERROR_FAILURE; 367 } 368 RefPtr<nsPresContext> presContext = docShell->GetPresContext(); 369 if (NS_WARN_IF(!presContext)) { 370 return NS_ERROR_FAILURE; 371 } 372 nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget(); 373 if (NS_WARN_IF(!widget)) { 374 return NS_ERROR_FAILURE; 375 } 376 377 RefPtr<TextEventDispatcher> dispatcher = widget->GetTextEventDispatcher(); 378 MOZ_RELEASE_ASSERT(dispatcher, "TextEventDispatcher must not be null"); 379 380 // If the instance was initialized and is being initialized for same 381 // dispatcher and same purpose, we don't need to initialize the dispatcher 382 // again. 383 if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback && 384 aForTests == mForTests) { 385 aSucceeded = true; 386 return NS_OK; 387 } 388 389 // If this instance is composing or dispatching an event, don't allow to 390 // initialize again. Especially, if we allow to begin input transaction with 391 // another TextEventDispatcher during dispatching an event, it may cause that 392 // nobody cannot begin input transaction with it if the last event causes 393 // opening modal dialog. 394 if (mDispatcher && 395 (mDispatcher->IsComposing() || mDispatcher->IsDispatchingEvent())) { 396 return NS_ERROR_ALREADY_INITIALIZED; 397 } 398 399 // And also if another instance is composing with the new dispatcher or 400 // dispatching an event, it'll fail to steal its ownership. Then, we should 401 // not throw an exception, just return false. 402 if (dispatcher->IsComposing() || dispatcher->IsDispatchingEvent()) { 403 return NS_OK; 404 } 405 406 // This instance has finished preparing to link to the dispatcher. Therefore, 407 // let's forget the old dispatcher and purpose. 408 if (mDispatcher) { 409 mDispatcher->EndInputTransaction(this); 410 if (NS_WARN_IF(mDispatcher)) { 411 // Forcibly initialize the members if we failed to end the input 412 // transaction. 413 UnlinkFromTextEventDispatcher(); 414 } 415 } 416 417 nsresult rv = NS_OK; 418 if (aForTests) { 419 bool isAPZAware = StaticPrefs::test_events_async_enabled(); 420 rv = dispatcher->BeginTestInputTransaction(this, isAPZAware); 421 } else { 422 rv = dispatcher->BeginInputTransaction(this); 423 } 424 425 if (NS_WARN_IF(NS_FAILED(rv))) { 426 return rv; 427 } 428 429 mDispatcher = dispatcher; 430 mCallback = aCallback; 431 mForTests = aForTests; 432 aSucceeded = true; 433 return NS_OK; 434 } 435 436 void TextInputProcessor::UnlinkFromTextEventDispatcher() { 437 mDispatcher = nullptr; 438 mForTests = false; 439 if (mCallback) { 440 nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback); 441 mCallback = nullptr; 442 443 RefPtr<TextInputProcessorNotification> notification = 444 new TextInputProcessorNotification("notify-end-input-transaction"); 445 bool result = false; 446 callback->OnNotify(this, notification, &result); 447 } 448 } 449 450 nsresult TextInputProcessor::IsValidStateForComposition() { 451 if (NS_WARN_IF(!mDispatcher)) { 452 return NS_ERROR_NOT_INITIALIZED; 453 } 454 455 nsresult rv = mDispatcher->GetState(); 456 if (NS_WARN_IF(NS_FAILED(rv))) { 457 return rv; 458 } 459 460 return NS_OK; 461 } 462 463 bool TextInputProcessor::IsValidEventTypeForComposition( 464 const WidgetKeyboardEvent& aKeyboardEvent) const { 465 // The key event type of composition methods must be "", "keydown" or "keyup". 466 if (aKeyboardEvent.mMessage == eKeyDown || 467 aKeyboardEvent.mMessage == eKeyUp) { 468 return true; 469 } 470 if (aKeyboardEvent.mMessage == eUnidentifiedEvent && 471 aKeyboardEvent.mSpecifiedEventType && 472 nsDependentAtomString(aKeyboardEvent.mSpecifiedEventType) 473 .EqualsLiteral("on")) { 474 return true; 475 } 476 return false; 477 } 478 479 TextInputProcessor::EventDispatcherResult 480 TextInputProcessor::MaybeDispatchKeydownForComposition( 481 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) { 482 EventDispatcherResult result; 483 484 result.mResult = IsValidStateForComposition(); 485 if (NS_WARN_IF(NS_FAILED(result.mResult))) { 486 result.mCanContinue = false; 487 return result; 488 } 489 490 if (!aKeyboardEvent) { 491 return result; 492 } 493 494 // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch 495 // eKeyDown event. 496 if (aKeyboardEvent->mMessage == eKeyUp) { 497 return result; 498 } 499 500 // Modifier keys are not allowed because managing modifier state in this 501 // method makes this messy. 502 if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) { 503 result.mResult = NS_ERROR_INVALID_ARG; 504 result.mCanContinue = false; 505 return result; 506 } 507 508 uint32_t consumedFlags = 0; 509 510 result.mResult = 511 KeydownInternal(*aKeyboardEvent, aKeyFlags, false, consumedFlags); 512 result.mDoDefault = !consumedFlags; 513 if (NS_WARN_IF(NS_FAILED(result.mResult))) { 514 result.mCanContinue = false; 515 return result; 516 } 517 518 result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition()); 519 return result; 520 } 521 522 TextInputProcessor::EventDispatcherResult 523 TextInputProcessor::MaybeDispatchKeyupForComposition( 524 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) { 525 EventDispatcherResult result; 526 527 if (!aKeyboardEvent) { 528 return result; 529 } 530 531 // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch 532 // eKeyUp event. 533 if (aKeyboardEvent->mMessage == eKeyDown) { 534 return result; 535 } 536 537 // If the widget has been destroyed, we can do nothing here. 538 result.mResult = IsValidStateForComposition(); 539 if (NS_FAILED(result.mResult)) { 540 result.mCanContinue = false; 541 return result; 542 } 543 544 result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault); 545 if (NS_WARN_IF(NS_FAILED(result.mResult))) { 546 result.mCanContinue = false; 547 return result; 548 } 549 550 result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition()); 551 return result; 552 } 553 554 nsresult TextInputProcessor::PrepareKeyboardEventForComposition( 555 KeyboardEvent* aDOMKeyEvent, uint32_t& aKeyFlags, uint8_t aOptionalArgc, 556 WidgetKeyboardEvent*& aKeyboardEvent) { 557 aKeyboardEvent = nullptr; 558 559 aKeyboardEvent = aOptionalArgc && aDOMKeyEvent 560 ? aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent() 561 : nullptr; 562 if (!aKeyboardEvent || aOptionalArgc < 2) { 563 aKeyFlags = 0; 564 } 565 566 if (!aKeyboardEvent) { 567 return NS_OK; 568 } 569 570 if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) { 571 return NS_ERROR_INVALID_ARG; 572 } 573 574 return NS_OK; 575 } 576 577 NS_IMETHODIMP 578 TextInputProcessor::StartComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags, 579 uint8_t aOptionalArgc, bool* aSucceeded) { 580 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); 581 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 582 *aSucceeded = false; 583 584 RefPtr<KeyboardEvent> keyEvent; 585 if (aDOMKeyEvent) { 586 keyEvent = aDOMKeyEvent->AsKeyboardEvent(); 587 if (NS_WARN_IF(!keyEvent)) { 588 return NS_ERROR_INVALID_ARG; 589 } 590 } 591 592 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 593 594 WidgetKeyboardEvent* keyboardEvent; 595 nsresult rv = PrepareKeyboardEventForComposition( 596 keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent); 597 if (NS_WARN_IF(NS_FAILED(rv))) { 598 return rv; 599 } 600 601 EventDispatcherResult dispatcherResult = 602 MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags); 603 if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) || 604 !dispatcherResult.mCanContinue) { 605 return dispatcherResult.mResult; 606 } 607 608 if (dispatcherResult.mDoDefault) { 609 nsEventStatus status = nsEventStatus_eIgnore; 610 rv = kungFuDeathGrip->StartComposition(status); 611 *aSucceeded = status != nsEventStatus_eConsumeNoDefault && 612 kungFuDeathGrip && kungFuDeathGrip->IsComposing(); 613 } 614 615 MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags); 616 617 if (NS_WARN_IF(NS_FAILED(rv))) { 618 return rv; 619 } 620 return NS_OK; 621 } 622 623 NS_IMETHODIMP 624 TextInputProcessor::SetPendingCompositionString(const nsAString& aString) { 625 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 626 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 627 nsresult rv = IsValidStateForComposition(); 628 if (NS_WARN_IF(NS_FAILED(rv))) { 629 return rv; 630 } 631 return kungFuDeathGrip->SetPendingCompositionString(aString); 632 } 633 634 NS_IMETHODIMP 635 TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength, 636 uint32_t aAttribute) { 637 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 638 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 639 TextRangeType textRangeType; 640 switch (aAttribute) { 641 case ATTR_RAW_CLAUSE: 642 case ATTR_SELECTED_RAW_CLAUSE: 643 case ATTR_CONVERTED_CLAUSE: 644 case ATTR_SELECTED_CLAUSE: 645 textRangeType = ToTextRangeType(aAttribute); 646 break; 647 default: 648 return NS_ERROR_INVALID_ARG; 649 } 650 nsresult rv = IsValidStateForComposition(); 651 if (NS_WARN_IF(NS_FAILED(rv))) { 652 return rv; 653 } 654 return kungFuDeathGrip->AppendClauseToPendingComposition(aLength, 655 textRangeType); 656 } 657 658 NS_IMETHODIMP 659 TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset) { 660 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 661 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 662 nsresult rv = IsValidStateForComposition(); 663 if (NS_WARN_IF(NS_FAILED(rv))) { 664 return rv; 665 } 666 return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0); 667 } 668 669 NS_IMETHODIMP 670 TextInputProcessor::FlushPendingComposition(Event* aDOMKeyEvent, 671 uint32_t aKeyFlags, 672 uint8_t aOptionalArgc, 673 bool* aSucceeded) { 674 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); 675 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 676 677 // Even if this doesn't flush pending composition actually, we need to reset 678 // pending composition for starting next composition with new user input. 679 AutoPendingCompositionResetter resetter(this); 680 681 *aSucceeded = false; 682 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 683 bool wasComposing = IsComposing(); 684 685 RefPtr<KeyboardEvent> keyEvent; 686 if (aDOMKeyEvent) { 687 keyEvent = aDOMKeyEvent->AsKeyboardEvent(); 688 if (NS_WARN_IF(!keyEvent)) { 689 return NS_ERROR_INVALID_ARG; 690 } 691 } 692 693 WidgetKeyboardEvent* keyboardEvent; 694 nsresult rv = PrepareKeyboardEventForComposition( 695 keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent); 696 if (NS_WARN_IF(NS_FAILED(rv))) { 697 return rv; 698 } 699 700 EventDispatcherResult dispatcherResult = 701 MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags); 702 if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) || 703 !dispatcherResult.mCanContinue) { 704 return dispatcherResult.mResult; 705 } 706 707 // Even if the preceding keydown event was consumed, if the composition 708 // was already started, we shouldn't prevent the change of composition. 709 if (dispatcherResult.mDoDefault || wasComposing) { 710 // Preceding keydown event may cause destroying the widget. 711 if (NS_FAILED(IsValidStateForComposition())) { 712 return NS_OK; 713 } 714 nsEventStatus status = nsEventStatus_eIgnore; 715 rv = kungFuDeathGrip->FlushPendingComposition(status); 716 *aSucceeded = status != nsEventStatus_eConsumeNoDefault; 717 } 718 719 MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags); 720 721 if (NS_WARN_IF(NS_FAILED(rv))) { 722 return rv; 723 } 724 return NS_OK; 725 } 726 727 NS_IMETHODIMP 728 TextInputProcessor::CommitComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags, 729 uint8_t aOptionalArgc) { 730 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 731 732 RefPtr<KeyboardEvent> keyEvent; 733 if (aDOMKeyEvent) { 734 keyEvent = aDOMKeyEvent->AsKeyboardEvent(); 735 if (NS_WARN_IF(!keyEvent)) { 736 return NS_ERROR_INVALID_ARG; 737 } 738 } 739 740 WidgetKeyboardEvent* keyboardEvent; 741 nsresult rv = PrepareKeyboardEventForComposition( 742 keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent); 743 if (NS_WARN_IF(NS_FAILED(rv))) { 744 return rv; 745 } 746 747 return CommitCompositionInternal(keyboardEvent, aKeyFlags); 748 } 749 750 NS_IMETHODIMP 751 TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString, 752 Event* aDOMKeyEvent, 753 uint32_t aKeyFlags, 754 uint8_t aOptionalArgc, 755 bool* aSucceeded) { 756 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr"); 757 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 758 759 RefPtr<KeyboardEvent> keyEvent; 760 if (aDOMKeyEvent) { 761 keyEvent = aDOMKeyEvent->AsKeyboardEvent(); 762 if (NS_WARN_IF(!keyEvent)) { 763 return NS_ERROR_INVALID_ARG; 764 } 765 } 766 767 WidgetKeyboardEvent* keyboardEvent; 768 nsresult rv = PrepareKeyboardEventForComposition( 769 keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent); 770 if (NS_WARN_IF(NS_FAILED(rv))) { 771 return rv; 772 } 773 774 return CommitCompositionInternal(keyboardEvent, aKeyFlags, &aCommitString, 775 aSucceeded); 776 } 777 778 nsresult TextInputProcessor::CommitCompositionInternal( 779 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags, 780 const nsAString* aCommitString, bool* aSucceeded) { 781 if (aSucceeded) { 782 *aSucceeded = false; 783 } 784 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 785 bool wasComposing = IsComposing(); 786 787 EventDispatcherResult dispatcherResult = 788 MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags); 789 if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) || 790 !dispatcherResult.mCanContinue) { 791 return dispatcherResult.mResult; 792 } 793 794 // Even if the preceding keydown event was consumed, if the composition 795 // was already started, we shouldn't prevent the commit of composition. 796 nsresult rv = NS_OK; 797 if (dispatcherResult.mDoDefault || wasComposing) { 798 // Preceding keydown event may cause destroying the widget. 799 if (NS_FAILED(IsValidStateForComposition())) { 800 return NS_OK; 801 } 802 nsEventStatus status = nsEventStatus_eIgnore; 803 rv = kungFuDeathGrip->CommitComposition(status, aCommitString); 804 if (aSucceeded) { 805 *aSucceeded = status != nsEventStatus_eConsumeNoDefault; 806 } 807 } 808 809 MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags); 810 811 if (NS_WARN_IF(NS_FAILED(rv))) { 812 return rv; 813 } 814 return NS_OK; 815 } 816 817 NS_IMETHODIMP 818 TextInputProcessor::CancelComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags, 819 uint8_t aOptionalArgc) { 820 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 821 822 RefPtr<KeyboardEvent> keyEvent; 823 if (aDOMKeyEvent) { 824 keyEvent = aDOMKeyEvent->AsKeyboardEvent(); 825 if (NS_WARN_IF(!keyEvent)) { 826 return NS_ERROR_INVALID_ARG; 827 } 828 } 829 830 WidgetKeyboardEvent* keyboardEvent; 831 nsresult rv = PrepareKeyboardEventForComposition( 832 keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent); 833 if (NS_WARN_IF(NS_FAILED(rv))) { 834 return rv; 835 } 836 837 return CancelCompositionInternal(keyboardEvent, aKeyFlags); 838 } 839 840 nsresult TextInputProcessor::CancelCompositionInternal( 841 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) { 842 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 843 844 EventDispatcherResult dispatcherResult = 845 MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags); 846 if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) || 847 !dispatcherResult.mCanContinue) { 848 return dispatcherResult.mResult; 849 } 850 851 nsEventStatus status = nsEventStatus_eIgnore; 852 nsresult rv = kungFuDeathGrip->CommitComposition(status, &EmptyString()); 853 854 MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags); 855 856 if (NS_WARN_IF(NS_FAILED(rv))) { 857 return rv; 858 } 859 return NS_OK; 860 } 861 862 NS_IMETHODIMP 863 TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher, 864 const IMENotification& aNotification) { 865 // If This is called while this is being initialized, ignore the call. 866 // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because 867 // we can say, TextInputProcessor doesn't implement any handlers of the 868 // requests and notifications. 869 if (!mDispatcher) { 870 return NS_ERROR_NOT_IMPLEMENTED; 871 } 872 MOZ_ASSERT(aTextEventDispatcher == mDispatcher, 873 "Wrong TextEventDispatcher notifies this"); 874 NS_ASSERTION(mForTests || mCallback, 875 "mCallback can be null only when IME is initialized for tests"); 876 if (mCallback) { 877 RefPtr<TextInputProcessorNotification> notification; 878 switch (aNotification.mMessage) { 879 case REQUEST_TO_COMMIT_COMPOSITION: { 880 NS_ASSERTION(aTextEventDispatcher->IsComposing(), 881 "Why is this requested without composition?"); 882 notification = new TextInputProcessorNotification("request-to-commit"); 883 break; 884 } 885 case REQUEST_TO_CANCEL_COMPOSITION: { 886 NS_ASSERTION(aTextEventDispatcher->IsComposing(), 887 "Why is this requested without composition?"); 888 notification = new TextInputProcessorNotification("request-to-cancel"); 889 break; 890 } 891 case NOTIFY_IME_OF_FOCUS: 892 notification = new TextInputProcessorNotification("notify-focus"); 893 break; 894 case NOTIFY_IME_OF_BLUR: 895 notification = new TextInputProcessorNotification("notify-blur"); 896 break; 897 case NOTIFY_IME_OF_TEXT_CHANGE: 898 notification = 899 new TextInputProcessorNotification(aNotification.mTextChangeData); 900 break; 901 case NOTIFY_IME_OF_SELECTION_CHANGE: 902 notification = new TextInputProcessorNotification( 903 aNotification.mSelectionChangeData); 904 break; 905 case NOTIFY_IME_OF_POSITION_CHANGE: 906 notification = 907 new TextInputProcessorNotification("notify-position-change"); 908 break; 909 default: 910 return NS_ERROR_NOT_IMPLEMENTED; 911 } 912 MOZ_RELEASE_ASSERT(notification); 913 bool result = false; 914 nsresult rv = mCallback->OnNotify(this, notification, &result); 915 if (NS_WARN_IF(NS_FAILED(rv))) { 916 return rv; 917 } 918 return result ? NS_OK : NS_ERROR_FAILURE; 919 } 920 921 switch (aNotification.mMessage) { 922 case REQUEST_TO_COMMIT_COMPOSITION: { 923 NS_ASSERTION(aTextEventDispatcher->IsComposing(), 924 "Why is this requested without composition?"); 925 CommitCompositionInternal(); 926 return NS_OK; 927 } 928 case REQUEST_TO_CANCEL_COMPOSITION: { 929 NS_ASSERTION(aTextEventDispatcher->IsComposing(), 930 "Why is this requested without composition?"); 931 CancelCompositionInternal(); 932 return NS_OK; 933 } 934 default: 935 return NS_ERROR_NOT_IMPLEMENTED; 936 } 937 } 938 939 NS_IMETHODIMP_(IMENotificationRequests) 940 TextInputProcessor::GetIMENotificationRequests() { 941 // TextInputProcessor should support all change notifications. 942 return IMENotificationRequests( 943 IMENotificationRequests::NOTIFY_TEXT_CHANGE | 944 IMENotificationRequests::NOTIFY_POSITION_CHANGE); 945 } 946 947 NS_IMETHODIMP_(void) 948 TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) { 949 // If This is called while this is being initialized, ignore the call. 950 if (!mDispatcher) { 951 return; 952 } 953 MOZ_ASSERT(aTextEventDispatcher == mDispatcher, 954 "Wrong TextEventDispatcher notifies this"); 955 UnlinkFromTextEventDispatcher(); 956 } 957 958 NS_IMETHODIMP_(void) 959 TextInputProcessor::WillDispatchKeyboardEvent( 960 TextEventDispatcher* aTextEventDispatcher, 961 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress, 962 void* aData) { 963 // TextInputProcessor doesn't set alternative char code nor modify charCode 964 // even when Ctrl key is pressed. 965 } 966 967 nsresult TextInputProcessor::PrepareKeyboardEventToDispatch( 968 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aKeyFlags) { 969 if (NS_WARN_IF(aKeyboardEvent.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) { 970 return NS_ERROR_INVALID_ARG; 971 } 972 if ((aKeyFlags & KEY_NON_PRINTABLE_KEY) && 973 NS_WARN_IF(aKeyboardEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING)) { 974 return NS_ERROR_INVALID_ARG; 975 } 976 if ((aKeyFlags & KEY_FORCE_PRINTABLE_KEY) && 977 aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) { 978 aKeyboardEvent.GetDOMKeyName(aKeyboardEvent.mKeyValue); 979 aKeyboardEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; 980 } 981 if (aKeyFlags & KEY_KEEP_KEY_LOCATION_STANDARD) { 982 // If .location is initialized with specific value, using 983 // KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller. 984 // Let's throw an exception for notifying the developer of this bug. 985 if (NS_WARN_IF(aKeyboardEvent.mLocation)) { 986 return NS_ERROR_INVALID_ARG; 987 } 988 } else if (!aKeyboardEvent.mLocation) { 989 // If KeyboardEvent.mLocation is 0, it may be uninitialized. If so, we 990 // should compute proper mLocation value from its .code value. 991 aKeyboardEvent.mLocation = 992 WidgetKeyboardEvent::ComputeLocationFromCodeValue( 993 aKeyboardEvent.mCodeNameIndex); 994 } 995 996 if (aKeyFlags & KEY_KEEP_KEYCODE_ZERO) { 997 // If .keyCode is initialized with specific value, using 998 // KEY_KEEP_KEYCODE_ZERO must be a bug of the caller. Let's throw an 999 // exception for notifying the developer of such bug. 1000 if (NS_WARN_IF(aKeyboardEvent.mKeyCode)) { 1001 return NS_ERROR_INVALID_ARG; 1002 } 1003 } else if (!aKeyboardEvent.mKeyCode && 1004 aKeyboardEvent.mKeyNameIndex > KEY_NAME_INDEX_Unidentified && 1005 aKeyboardEvent.mKeyNameIndex < KEY_NAME_INDEX_USE_STRING) { 1006 // If KeyboardEvent.keyCode is 0, it may be uninitialized. If so, we may 1007 // be able to decide a good .keyCode value if the .key value is a 1008 // non-printable key. 1009 aKeyboardEvent.mKeyCode = 1010 WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex( 1011 aKeyboardEvent.mKeyNameIndex); 1012 } 1013 1014 aKeyboardEvent.mIsSynthesizedByTIP = true; 1015 aKeyboardEvent.mFlags.mIsSynthesizedForTests = mForTests; 1016 1017 return NS_OK; 1018 } 1019 1020 nsresult TextInputProcessor::InitEditCommands( 1021 WidgetKeyboardEvent& aKeyboardEvent) const { 1022 MOZ_ASSERT(XRE_IsContentProcess()); 1023 MOZ_ASSERT(aKeyboardEvent.mMessage == eKeyPress); 1024 1025 // When this emulates real input only in content process, we need to 1026 // initialize edit commands with the main process's widget via PuppetWidget 1027 // because they are initialized by BrowserParent before content process treats 1028 // them. 1029 // And also when this synthesizes keyboard events for tests, we need default 1030 // shortcut keys on the platform for making any developers get constant 1031 // results in any environments. 1032 1033 // Note that retrieving edit commands via PuppetWidget is expensive. 1034 // Let's skip it when the keyboard event is inputting text. 1035 if (aKeyboardEvent.IsInputtingText()) { 1036 aKeyboardEvent.PreventNativeKeyBindings(); 1037 return NS_OK; 1038 } 1039 1040 Maybe<WritingMode> writingMode; 1041 if (RefPtr<TextEventDispatcher> dispatcher = mDispatcher) { 1042 writingMode = dispatcher->MaybeQueryWritingModeAtSelection(); 1043 } 1044 1045 // FYI: WidgetKeyboardEvent::InitAllEditCommands() isn't available here 1046 // since it checks whether it's called in the main process to 1047 // avoid performance issues so that we need to initialize each 1048 // command manually here. 1049 if (NS_WARN_IF(!aKeyboardEvent.InitEditCommandsFor( 1050 NativeKeyBindingsType::SingleLineEditor, writingMode))) { 1051 return NS_ERROR_NOT_AVAILABLE; 1052 } 1053 if (NS_WARN_IF(!aKeyboardEvent.InitEditCommandsFor( 1054 NativeKeyBindingsType::MultiLineEditor, writingMode))) { 1055 return NS_ERROR_NOT_AVAILABLE; 1056 } 1057 if (NS_WARN_IF(!aKeyboardEvent.InitEditCommandsFor( 1058 NativeKeyBindingsType::RichTextEditor, writingMode))) { 1059 return NS_ERROR_NOT_AVAILABLE; 1060 } 1061 1062 return NS_OK; 1063 } 1064 1065 NS_IMETHODIMP 1066 TextInputProcessor::Keydown(Event* aDOMKeyEvent, uint32_t aKeyFlags, 1067 uint8_t aOptionalArgc, uint32_t* aConsumedFlags) { 1068 MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr"); 1069 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 1070 if (!aOptionalArgc) { 1071 aKeyFlags = 0; 1072 } 1073 if (NS_WARN_IF(!aDOMKeyEvent)) { 1074 return NS_ERROR_INVALID_ARG; 1075 } 1076 WidgetKeyboardEvent* originalKeyEvent = 1077 aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent(); 1078 if (NS_WARN_IF(!originalKeyEvent)) { 1079 return NS_ERROR_INVALID_ARG; 1080 } 1081 return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags); 1082 } 1083 1084 nsresult TextInputProcessor::Keydown(const WidgetKeyboardEvent& aKeyboardEvent, 1085 uint32_t aKeyFlags, 1086 uint32_t* aConsumedFlags) { 1087 uint32_t consumedFlags = 0; 1088 return KeydownInternal(aKeyboardEvent, aKeyFlags, true, 1089 aConsumedFlags ? *aConsumedFlags : consumedFlags); 1090 } 1091 1092 nsresult TextInputProcessor::KeydownInternal( 1093 const WidgetKeyboardEvent& aKeyboardEvent, uint32_t aKeyFlags, 1094 bool aAllowToDispatchKeypress, uint32_t& aConsumedFlags) { 1095 aConsumedFlags = KEYEVENT_NOT_CONSUMED; 1096 1097 // We shouldn't modify the internal WidgetKeyboardEvent. 1098 WidgetKeyboardEvent keyEvent(aKeyboardEvent); 1099 keyEvent.mFlags.mIsTrusted = true; 1100 keyEvent.mMessage = eKeyDown; 1101 nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags); 1102 if (NS_WARN_IF(NS_FAILED(rv))) { 1103 return rv; 1104 } 1105 1106 aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED 1107 : KEYEVENT_NOT_CONSUMED; 1108 1109 if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) { 1110 ModifierKeyData modifierKeyData(keyEvent); 1111 if (WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) { 1112 // If the modifier key is lockable modifier key such as CapsLock, 1113 // let's toggle modifier key state at keydown. 1114 ToggleModifierKey(modifierKeyData); 1115 } else { 1116 // Activate modifier flag before dispatching keydown event (i.e., keydown 1117 // event should indicate the releasing modifier is active. 1118 ActivateModifierKey(modifierKeyData); 1119 } 1120 if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) { 1121 return NS_OK; 1122 } 1123 } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) { 1124 return NS_ERROR_INVALID_ARG; 1125 } 1126 keyEvent.mModifiers = GetActiveModifiers(); 1127 1128 if (!aAllowToDispatchKeypress && 1129 !(aKeyFlags & KEY_DONT_MARK_KEYDOWN_AS_PROCESSED)) { 1130 keyEvent.mKeyCode = NS_VK_PROCESSKEY; 1131 keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process; 1132 } 1133 1134 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 1135 rv = IsValidStateForComposition(); 1136 if (NS_WARN_IF(NS_FAILED(rv))) { 1137 return rv; 1138 } 1139 1140 nsEventStatus status = 1141 aConsumedFlags ? nsEventStatus_eConsumeNoDefault : nsEventStatus_eIgnore; 1142 if (!kungFuDeathGrip->DispatchKeyboardEvent(eKeyDown, keyEvent, status)) { 1143 // If keydown event isn't dispatched, we don't need to dispatch keypress 1144 // events. 1145 return NS_OK; 1146 } 1147 1148 aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault) 1149 ? KEYDOWN_IS_CONSUMED 1150 : KEYEVENT_NOT_CONSUMED; 1151 1152 if (!aAllowToDispatchKeypress) { 1153 return NS_OK; 1154 } 1155 1156 keyEvent.mMessage = eKeyPress; 1157 1158 // Only `eKeyPress` events, editor wants to execute system default edit 1159 // commands mapped to the key combination. In e10s world, edit commands can 1160 // be retrieved only in the parent process due to the performance reason. 1161 // Therefore, BrowserParent initializes edit commands for all cases before 1162 // sending the event to focused content process. For emulating this, we 1163 // need to do it now for synthesizing `eKeyPress` events if and only if 1164 // we're dispatching the events in a content process. 1165 if (XRE_IsContentProcess()) { 1166 nsresult rv = InitEditCommands(keyEvent); 1167 if (NS_WARN_IF(NS_FAILED(rv))) { 1168 return rv; 1169 } 1170 } 1171 if (kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) { 1172 aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault) 1173 ? KEYPRESS_IS_CONSUMED 1174 : KEYEVENT_NOT_CONSUMED; 1175 } 1176 1177 return NS_OK; 1178 } 1179 1180 NS_IMETHODIMP 1181 TextInputProcessor::Keyup(Event* aDOMKeyEvent, uint32_t aKeyFlags, 1182 uint8_t aOptionalArgc, bool* aDoDefault) { 1183 MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr"); 1184 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 1185 if (!aOptionalArgc) { 1186 aKeyFlags = 0; 1187 } 1188 if (NS_WARN_IF(!aDOMKeyEvent)) { 1189 return NS_ERROR_INVALID_ARG; 1190 } 1191 WidgetKeyboardEvent* originalKeyEvent = 1192 aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent(); 1193 if (NS_WARN_IF(!originalKeyEvent)) { 1194 return NS_ERROR_INVALID_ARG; 1195 } 1196 return KeyupInternal(*originalKeyEvent, aKeyFlags, *aDoDefault); 1197 } 1198 1199 nsresult TextInputProcessor::Keyup(const WidgetKeyboardEvent& aKeyboardEvent, 1200 uint32_t aKeyFlags, bool* aDoDefault) { 1201 bool doDefault = false; 1202 return KeyupInternal(aKeyboardEvent, aKeyFlags, 1203 aDoDefault ? *aDoDefault : doDefault); 1204 } 1205 1206 nsresult TextInputProcessor::KeyupInternal( 1207 const WidgetKeyboardEvent& aKeyboardEvent, uint32_t aKeyFlags, 1208 bool& aDoDefault) { 1209 aDoDefault = false; 1210 1211 // We shouldn't modify the internal WidgetKeyboardEvent. 1212 WidgetKeyboardEvent keyEvent(aKeyboardEvent); 1213 keyEvent.mFlags.mIsTrusted = true; 1214 keyEvent.mMessage = eKeyUp; 1215 nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags); 1216 if (NS_WARN_IF(NS_FAILED(rv))) { 1217 return rv; 1218 } 1219 1220 aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED); 1221 1222 if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) { 1223 if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) { 1224 // Inactivate modifier flag before dispatching keyup event (i.e., keyup 1225 // event shouldn't indicate the releasing modifier is active. 1226 InactivateModifierKey(ModifierKeyData(keyEvent)); 1227 } 1228 if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) { 1229 return NS_OK; 1230 } 1231 } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) { 1232 return NS_ERROR_INVALID_ARG; 1233 } 1234 keyEvent.mModifiers = GetActiveModifiers(); 1235 1236 if (aKeyFlags & KEY_MARK_KEYUP_AS_PROCESSED) { 1237 keyEvent.mKeyCode = NS_VK_PROCESSKEY; 1238 keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process; 1239 } 1240 1241 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher); 1242 rv = IsValidStateForComposition(); 1243 if (NS_WARN_IF(NS_FAILED(rv))) { 1244 return rv; 1245 } 1246 1247 nsEventStatus status = 1248 aDoDefault ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault; 1249 kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status); 1250 aDoDefault = (status != nsEventStatus_eConsumeNoDefault); 1251 return NS_OK; 1252 } 1253 1254 NS_IMETHODIMP 1255 TextInputProcessor::InsertTextWithKeyPress(const nsAString& aString, 1256 Event* aDOMKeyEvent, 1257 uint32_t aKeyFlags, 1258 uint8_t aOptionalArgc, 1259 bool* aDoDefault) { 1260 MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr"); 1261 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 1262 1263 nsresult rv = IsValidStateForComposition(); 1264 if (NS_WARN_IF(NS_FAILED(rv))) { 1265 return rv; 1266 } 1267 1268 if (aOptionalArgc < 1) { 1269 aDOMKeyEvent = nullptr; 1270 } 1271 if (aOptionalArgc < 2) { 1272 aKeyFlags = 0; 1273 } 1274 *aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED); 1275 1276 WidgetKeyboardEvent* const originalKeyEvent = 1277 aDOMKeyEvent ? aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent() 1278 : nullptr; 1279 if (NS_WARN_IF(aDOMKeyEvent && !originalKeyEvent)) { 1280 return NS_ERROR_INVALID_ARG; 1281 } 1282 1283 WidgetKeyboardEvent keyEvent(true, eKeyPress, nullptr); 1284 if (originalKeyEvent) { 1285 keyEvent = WidgetKeyboardEvent(*originalKeyEvent); 1286 keyEvent.mFlags.mIsTrusted = true; 1287 keyEvent.mMessage = eKeyPress; 1288 } 1289 keyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; 1290 keyEvent.mKeyValue = aString; 1291 rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags); 1292 if (NS_WARN_IF(NS_FAILED(rv))) { 1293 return rv; 1294 } 1295 // Do not dispatch modifier key events even if the source event is a modifier 1296 // key event because modifier state should be changed before this. 1297 // TODO: In some test scenarios, we may need a new flag to use the given 1298 // modifier state as-is. 1299 keyEvent.mModifiers = GetActiveModifiers(); 1300 1301 // See KeyDownInternal() for the detail of this. 1302 if (XRE_IsContentProcess()) { 1303 nsresult rv = InitEditCommands(keyEvent); 1304 if (NS_WARN_IF(NS_FAILED(rv))) { 1305 return rv; 1306 } 1307 } 1308 1309 nsEventStatus status = 1310 *aDoDefault ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault; 1311 RefPtr<TextEventDispatcher> dispatcher(mDispatcher); 1312 if (dispatcher->MaybeDispatchKeypressEvents(keyEvent, status)) { 1313 *aDoDefault = (status != nsEventStatus_eConsumeNoDefault); 1314 } 1315 1316 return NS_OK; 1317 } 1318 1319 NS_IMETHODIMP 1320 TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName, 1321 bool* aActive) { 1322 MOZ_RELEASE_ASSERT(aActive, "aActive must not be null"); 1323 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 1324 Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName); 1325 *aActive = ((GetActiveModifiers() & modifier) != 0); 1326 return NS_OK; 1327 } 1328 1329 NS_IMETHODIMP 1330 TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther) { 1331 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); 1332 if (!aOther) { 1333 mModifierKeyDataArray = nullptr; 1334 return NS_OK; 1335 } 1336 TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther); 1337 if (!other->mModifierKeyDataArray) { 1338 other->mModifierKeyDataArray = new ModifierKeyDataArray(); 1339 } 1340 mModifierKeyDataArray = other->mModifierKeyDataArray; 1341 return NS_OK; 1342 } 1343 1344 NS_IMETHODIMP 1345 TextInputProcessor::ComputeCodeValueOfNonPrintableKey( 1346 const nsAString& aKeyValue, JS::Handle<JS::Value> aLocation, 1347 uint8_t aOptionalArgc, nsAString& aCodeValue) { 1348 aCodeValue.Truncate(); 1349 1350 Maybe<uint32_t> location; 1351 if (aOptionalArgc) { 1352 if (aLocation.isNullOrUndefined()) { 1353 // location should be nothing. 1354 } else if (aLocation.isInt32()) { 1355 location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32())); 1356 } else { 1357 NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(), 1358 "aLocation must be undefined, null or int"); 1359 return NS_ERROR_INVALID_ARG; 1360 } 1361 } 1362 1363 KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue); 1364 if (keyNameIndex == KEY_NAME_INDEX_Unidentified || 1365 keyNameIndex == KEY_NAME_INDEX_USE_STRING) { 1366 return NS_OK; 1367 } 1368 1369 CodeNameIndex codeNameIndex = 1370 WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex, 1371 location); 1372 if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) { 1373 return NS_OK; 1374 } 1375 MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING); 1376 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue); 1377 return NS_OK; 1378 } 1379 1380 NS_IMETHODIMP 1381 TextInputProcessor::GuessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout( 1382 const nsAString& aKeyValue, JS::Handle<JS::Value> aLocation, 1383 uint8_t aOptionalArgc, nsAString& aCodeValue) { 1384 aCodeValue.Truncate(); 1385 1386 Maybe<uint32_t> location; 1387 if (aOptionalArgc) { 1388 if (aLocation.isNullOrUndefined()) { 1389 // location should be nothing. 1390 } else if (aLocation.isInt32()) { 1391 location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32())); 1392 } else { 1393 NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(), 1394 "aLocation must be undefined, null or int"); 1395 return NS_ERROR_INVALID_ARG; 1396 } 1397 } 1398 CodeNameIndex codeNameIndex = 1399 GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(aKeyValue, location); 1400 if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) { 1401 return NS_OK; 1402 } 1403 MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING); 1404 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue); 1405 return NS_OK; 1406 } 1407 1408 // static 1409 CodeNameIndex 1410 TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout( 1411 const nsAString& aKeyValue, const Maybe<uint32_t>& aLocation) { 1412 if (aKeyValue.IsEmpty()) { 1413 return CODE_NAME_INDEX_UNKNOWN; 1414 } 1415 // US keyboard layout can input only one character per key. So, we can 1416 // assume that if the key value is 2 or more characters, it's a known 1417 // key name or not a usual key emulation. 1418 if (aKeyValue.Length() > 1) { 1419 return CODE_NAME_INDEX_UNKNOWN; 1420 } 1421 if (aLocation.isSome() && 1422 aLocation.value() == 1423 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) { 1424 switch (aKeyValue[0]) { 1425 case '+': 1426 return CODE_NAME_INDEX_NumpadAdd; 1427 case '-': 1428 return CODE_NAME_INDEX_NumpadSubtract; 1429 case '*': 1430 return CODE_NAME_INDEX_NumpadMultiply; 1431 case '/': 1432 return CODE_NAME_INDEX_NumpadDivide; 1433 case '.': 1434 return CODE_NAME_INDEX_NumpadDecimal; 1435 case '0': 1436 return CODE_NAME_INDEX_Numpad0; 1437 case '1': 1438 return CODE_NAME_INDEX_Numpad1; 1439 case '2': 1440 return CODE_NAME_INDEX_Numpad2; 1441 case '3': 1442 return CODE_NAME_INDEX_Numpad3; 1443 case '4': 1444 return CODE_NAME_INDEX_Numpad4; 1445 case '5': 1446 return CODE_NAME_INDEX_Numpad5; 1447 case '6': 1448 return CODE_NAME_INDEX_Numpad6; 1449 case '7': 1450 return CODE_NAME_INDEX_Numpad7; 1451 case '8': 1452 return CODE_NAME_INDEX_Numpad8; 1453 case '9': 1454 return CODE_NAME_INDEX_Numpad9; 1455 default: 1456 return CODE_NAME_INDEX_UNKNOWN; 1457 } 1458 } 1459 1460 if (aLocation.isSome() && 1461 aLocation.value() != 1462 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) { 1463 return CODE_NAME_INDEX_UNKNOWN; 1464 } 1465 1466 // TODO: Support characters inputted with option key on macOS. 1467 switch (aKeyValue[0]) { 1468 case 'a': 1469 case 'A': 1470 return CODE_NAME_INDEX_KeyA; 1471 case 'b': 1472 case 'B': 1473 return CODE_NAME_INDEX_KeyB; 1474 case 'c': 1475 case 'C': 1476 return CODE_NAME_INDEX_KeyC; 1477 case 'd': 1478 case 'D': 1479 return CODE_NAME_INDEX_KeyD; 1480 case 'e': 1481 case 'E': 1482 return CODE_NAME_INDEX_KeyE; 1483 case 'f': 1484 case 'F': 1485 return CODE_NAME_INDEX_KeyF; 1486 case 'g': 1487 case 'G': 1488 return CODE_NAME_INDEX_KeyG; 1489 case 'h': 1490 case 'H': 1491 return CODE_NAME_INDEX_KeyH; 1492 case 'i': 1493 case 'I': 1494 return CODE_NAME_INDEX_KeyI; 1495 case 'j': 1496 case 'J': 1497 return CODE_NAME_INDEX_KeyJ; 1498 case 'k': 1499 case 'K': 1500 return CODE_NAME_INDEX_KeyK; 1501 case 'l': 1502 case 'L': 1503 return CODE_NAME_INDEX_KeyL; 1504 case 'm': 1505 case 'M': 1506 return CODE_NAME_INDEX_KeyM; 1507 case 'n': 1508 case 'N': 1509 return CODE_NAME_INDEX_KeyN; 1510 case 'o': 1511 case 'O': 1512 return CODE_NAME_INDEX_KeyO; 1513 case 'p': 1514 case 'P': 1515 return CODE_NAME_INDEX_KeyP; 1516 case 'q': 1517 case 'Q': 1518 return CODE_NAME_INDEX_KeyQ; 1519 case 'r': 1520 case 'R': 1521 return CODE_NAME_INDEX_KeyR; 1522 case 's': 1523 case 'S': 1524 return CODE_NAME_INDEX_KeyS; 1525 case 't': 1526 case 'T': 1527 return CODE_NAME_INDEX_KeyT; 1528 case 'u': 1529 case 'U': 1530 return CODE_NAME_INDEX_KeyU; 1531 case 'v': 1532 case 'V': 1533 return CODE_NAME_INDEX_KeyV; 1534 case 'w': 1535 case 'W': 1536 return CODE_NAME_INDEX_KeyW; 1537 case 'x': 1538 case 'X': 1539 return CODE_NAME_INDEX_KeyX; 1540 case 'y': 1541 case 'Y': 1542 return CODE_NAME_INDEX_KeyY; 1543 case 'z': 1544 case 'Z': 1545 return CODE_NAME_INDEX_KeyZ; 1546 1547 case '`': 1548 case '~': 1549 return CODE_NAME_INDEX_Backquote; 1550 case '1': 1551 case '!': 1552 return CODE_NAME_INDEX_Digit1; 1553 case '2': 1554 case '@': 1555 return CODE_NAME_INDEX_Digit2; 1556 case '3': 1557 case '#': 1558 return CODE_NAME_INDEX_Digit3; 1559 case '4': 1560 case '$': 1561 return CODE_NAME_INDEX_Digit4; 1562 case '5': 1563 case '%': 1564 return CODE_NAME_INDEX_Digit5; 1565 case '6': 1566 case '^': 1567 return CODE_NAME_INDEX_Digit6; 1568 case '7': 1569 case '&': 1570 return CODE_NAME_INDEX_Digit7; 1571 case '8': 1572 case '*': 1573 return CODE_NAME_INDEX_Digit8; 1574 case '9': 1575 case '(': 1576 return CODE_NAME_INDEX_Digit9; 1577 case '0': 1578 case ')': 1579 return CODE_NAME_INDEX_Digit0; 1580 case '-': 1581 case '_': 1582 return CODE_NAME_INDEX_Minus; 1583 case '=': 1584 case '+': 1585 return CODE_NAME_INDEX_Equal; 1586 1587 case '[': 1588 case '{': 1589 return CODE_NAME_INDEX_BracketLeft; 1590 case ']': 1591 case '}': 1592 return CODE_NAME_INDEX_BracketRight; 1593 case '\\': 1594 case '|': 1595 return CODE_NAME_INDEX_Backslash; 1596 1597 case ';': 1598 case ':': 1599 return CODE_NAME_INDEX_Semicolon; 1600 case '\'': 1601 case '"': 1602 return CODE_NAME_INDEX_Quote; 1603 1604 case ',': 1605 case '<': 1606 return CODE_NAME_INDEX_Comma; 1607 case '.': 1608 case '>': 1609 return CODE_NAME_INDEX_Period; 1610 case '/': 1611 case '?': 1612 return CODE_NAME_INDEX_Slash; 1613 1614 case ' ': 1615 return CODE_NAME_INDEX_Space; 1616 1617 default: 1618 return CODE_NAME_INDEX_UNKNOWN; 1619 } 1620 } 1621 1622 NS_IMETHODIMP 1623 TextInputProcessor::GuessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout( 1624 const nsAString& aKeyValue, JS::Handle<JS::Value> aLocation, 1625 uint8_t aOptionalArgc, uint32_t* aKeyCodeValue) { 1626 if (NS_WARN_IF(!aKeyCodeValue)) { 1627 return NS_ERROR_INVALID_ARG; 1628 } 1629 1630 Maybe<uint32_t> location; 1631 if (aOptionalArgc) { 1632 if (aLocation.isNullOrUndefined()) { 1633 // location should be nothing. 1634 } else if (aLocation.isInt32()) { 1635 location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32())); 1636 } else { 1637 NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(), 1638 "aLocation must be undefined, null or int"); 1639 return NS_ERROR_INVALID_ARG; 1640 } 1641 } 1642 1643 *aKeyCodeValue = 1644 GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue, location); 1645 return NS_OK; 1646 } 1647 1648 // static 1649 uint32_t TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout( 1650 const nsAString& aKeyValue, const Maybe<uint32_t>& aLocation) { 1651 if (aKeyValue.IsEmpty()) { 1652 return 0; 1653 } 1654 // US keyboard layout can input only one character per key. So, we can 1655 // assume that if the key value is 2 or more characters, it's a known 1656 // key name of a non-printable key or not a usual key emulation. 1657 if (aKeyValue.Length() > 1) { 1658 return 0; 1659 } 1660 1661 if (aLocation.isSome() && 1662 aLocation.value() == 1663 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) { 1664 switch (aKeyValue[0]) { 1665 case '+': 1666 return dom::KeyboardEvent_Binding::DOM_VK_ADD; 1667 case '-': 1668 return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT; 1669 case '*': 1670 return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY; 1671 case '/': 1672 return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE; 1673 case '.': 1674 return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL; 1675 case '0': 1676 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0; 1677 case '1': 1678 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1; 1679 case '2': 1680 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2; 1681 case '3': 1682 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3; 1683 case '4': 1684 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4; 1685 case '5': 1686 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5; 1687 case '6': 1688 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6; 1689 case '7': 1690 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7; 1691 case '8': 1692 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8; 1693 case '9': 1694 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9; 1695 default: 1696 return 0; 1697 } 1698 } 1699 1700 if (aLocation.isSome() && 1701 aLocation.value() != 1702 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) { 1703 return 0; 1704 } 1705 1706 // TODO: Support characters inputted with option key on macOS. 1707 switch (aKeyValue[0]) { 1708 case 'a': 1709 case 'A': 1710 return dom::KeyboardEvent_Binding::DOM_VK_A; 1711 case 'b': 1712 case 'B': 1713 return dom::KeyboardEvent_Binding::DOM_VK_B; 1714 case 'c': 1715 case 'C': 1716 return dom::KeyboardEvent_Binding::DOM_VK_C; 1717 case 'd': 1718 case 'D': 1719 return dom::KeyboardEvent_Binding::DOM_VK_D; 1720 case 'e': 1721 case 'E': 1722 return dom::KeyboardEvent_Binding::DOM_VK_E; 1723 case 'f': 1724 case 'F': 1725 return dom::KeyboardEvent_Binding::DOM_VK_F; 1726 case 'g': 1727 case 'G': 1728 return dom::KeyboardEvent_Binding::DOM_VK_G; 1729 case 'h': 1730 case 'H': 1731 return dom::KeyboardEvent_Binding::DOM_VK_H; 1732 case 'i': 1733 case 'I': 1734 return dom::KeyboardEvent_Binding::DOM_VK_I; 1735 case 'j': 1736 case 'J': 1737 return dom::KeyboardEvent_Binding::DOM_VK_J; 1738 case 'k': 1739 case 'K': 1740 return dom::KeyboardEvent_Binding::DOM_VK_K; 1741 case 'l': 1742 case 'L': 1743 return dom::KeyboardEvent_Binding::DOM_VK_L; 1744 case 'm': 1745 case 'M': 1746 return dom::KeyboardEvent_Binding::DOM_VK_M; 1747 case 'n': 1748 case 'N': 1749 return dom::KeyboardEvent_Binding::DOM_VK_N; 1750 case 'o': 1751 case 'O': 1752 return dom::KeyboardEvent_Binding::DOM_VK_O; 1753 case 'p': 1754 case 'P': 1755 return dom::KeyboardEvent_Binding::DOM_VK_P; 1756 case 'q': 1757 case 'Q': 1758 return dom::KeyboardEvent_Binding::DOM_VK_Q; 1759 case 'r': 1760 case 'R': 1761 return dom::KeyboardEvent_Binding::DOM_VK_R; 1762 case 's': 1763 case 'S': 1764 return dom::KeyboardEvent_Binding::DOM_VK_S; 1765 case 't': 1766 case 'T': 1767 return dom::KeyboardEvent_Binding::DOM_VK_T; 1768 case 'u': 1769 case 'U': 1770 return dom::KeyboardEvent_Binding::DOM_VK_U; 1771 case 'v': 1772 case 'V': 1773 return dom::KeyboardEvent_Binding::DOM_VK_V; 1774 case 'w': 1775 case 'W': 1776 return dom::KeyboardEvent_Binding::DOM_VK_W; 1777 case 'x': 1778 case 'X': 1779 return dom::KeyboardEvent_Binding::DOM_VK_X; 1780 case 'y': 1781 case 'Y': 1782 return dom::KeyboardEvent_Binding::DOM_VK_Y; 1783 case 'z': 1784 case 'Z': 1785 return dom::KeyboardEvent_Binding::DOM_VK_Z; 1786 1787 case '`': 1788 case '~': 1789 return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE; 1790 case '1': 1791 case '!': 1792 return dom::KeyboardEvent_Binding::DOM_VK_1; 1793 case '2': 1794 case '@': 1795 return dom::KeyboardEvent_Binding::DOM_VK_2; 1796 case '3': 1797 case '#': 1798 return dom::KeyboardEvent_Binding::DOM_VK_3; 1799 case '4': 1800 case '$': 1801 return dom::KeyboardEvent_Binding::DOM_VK_4; 1802 case '5': 1803 case '%': 1804 return dom::KeyboardEvent_Binding::DOM_VK_5; 1805 case '6': 1806 case '^': 1807 return dom::KeyboardEvent_Binding::DOM_VK_6; 1808 case '7': 1809 case '&': 1810 return dom::KeyboardEvent_Binding::DOM_VK_7; 1811 case '8': 1812 case '*': 1813 return dom::KeyboardEvent_Binding::DOM_VK_8; 1814 case '9': 1815 case '(': 1816 return dom::KeyboardEvent_Binding::DOM_VK_9; 1817 case '0': 1818 case ')': 1819 return dom::KeyboardEvent_Binding::DOM_VK_0; 1820 case '-': 1821 case '_': 1822 return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS; 1823 case '=': 1824 case '+': 1825 return dom::KeyboardEvent_Binding::DOM_VK_EQUALS; 1826 1827 case '[': 1828 case '{': 1829 return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET; 1830 case ']': 1831 case '}': 1832 return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET; 1833 case '\\': 1834 case '|': 1835 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH; 1836 1837 case ';': 1838 case ':': 1839 return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON; 1840 case '\'': 1841 case '"': 1842 return dom::KeyboardEvent_Binding::DOM_VK_QUOTE; 1843 1844 case ',': 1845 case '<': 1846 return dom::KeyboardEvent_Binding::DOM_VK_COMMA; 1847 case '.': 1848 case '>': 1849 return dom::KeyboardEvent_Binding::DOM_VK_PERIOD; 1850 case '/': 1851 case '?': 1852 return dom::KeyboardEvent_Binding::DOM_VK_SLASH; 1853 1854 case ' ': 1855 return dom::KeyboardEvent_Binding::DOM_VK_SPACE; 1856 1857 default: 1858 return 0; 1859 } 1860 } 1861 1862 /****************************************************************************** 1863 * TextInputProcessor::AutoPendingCompositionResetter 1864 ******************************************************************************/ 1865 TextInputProcessor::AutoPendingCompositionResetter:: 1866 AutoPendingCompositionResetter(TextInputProcessor* aTIP) 1867 : mTIP(aTIP) { 1868 MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null"); 1869 } 1870 1871 TextInputProcessor::AutoPendingCompositionResetter:: 1872 ~AutoPendingCompositionResetter() { 1873 if (mTIP->mDispatcher) { 1874 mTIP->mDispatcher->ClearPendingComposition(); 1875 } 1876 } 1877 1878 /****************************************************************************** 1879 * TextInputProcessor::ModifierKeyData 1880 ******************************************************************************/ 1881 TextInputProcessor::ModifierKeyData::ModifierKeyData( 1882 const WidgetKeyboardEvent& aKeyboardEvent) 1883 : mKeyNameIndex(aKeyboardEvent.mKeyNameIndex), 1884 mCodeNameIndex(aKeyboardEvent.mCodeNameIndex) { 1885 mModifier = WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex); 1886 MOZ_ASSERT(mModifier, "mKeyNameIndex must be a modifier key name"); 1887 } 1888 1889 /****************************************************************************** 1890 * TextInputProcessor::ModifierKeyDataArray 1891 ******************************************************************************/ 1892 Modifiers TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const { 1893 Modifiers result = MODIFIER_NONE; 1894 for (uint32_t i = 0; i < Length(); i++) { 1895 result |= ElementAt(i).mModifier; 1896 } 1897 return result; 1898 } 1899 1900 void TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey( 1901 const TextInputProcessor::ModifierKeyData& aModifierKeyData) { 1902 if (Contains(aModifierKeyData)) { 1903 return; 1904 } 1905 AppendElement(aModifierKeyData); 1906 } 1907 1908 void TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey( 1909 const TextInputProcessor::ModifierKeyData& aModifierKeyData) { 1910 RemoveElement(aModifierKeyData); 1911 } 1912 1913 void TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey( 1914 const TextInputProcessor::ModifierKeyData& aModifierKeyData) { 1915 auto index = IndexOf(aModifierKeyData); 1916 if (index == NoIndex) { 1917 AppendElement(aModifierKeyData); 1918 return; 1919 } 1920 RemoveElementAt(index); 1921 } 1922 1923 } // namespace mozilla