1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 sw=2 et 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 #ifndef TextInputHandler_h_ 8 #define TextInputHandler_h_ 9 10 #include "nsCocoaUtils.h" 11 12 #import <Carbon/Carbon.h> 13 #import <Cocoa/Cocoa.h> 14 #include "mozView.h" 15 #include "nsString.h" 16 #include "nsCOMPtr.h" 17 #include "nsITimer.h" 18 #include "nsTArray.h" 19 #include "mozilla/EventForwards.h" 20 #include "mozilla/TextEventDispatcherListener.h" 21 #include "WritingModes.h" 22 23 class nsChildView; 24 25 namespace mozilla { 26 namespace widget { 27 28 // Key code constants 29 enum 30 { 31 #if !defined(MAC_OS_X_VERSION_10_12) || \ 32 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 33 kVK_RightCommand = 0x36, // right command key 34 #endif 35 36 kVK_PC_PrintScreen = kVK_F13, 37 kVK_PC_ScrollLock = kVK_F14, 38 kVK_PC_Pause = kVK_F15, 39 40 kVK_PC_Insert = kVK_Help, 41 kVK_PC_Backspace = kVK_Delete, 42 kVK_PC_Delete = kVK_ForwardDelete, 43 44 kVK_PC_ContextMenu = 0x6E, 45 46 kVK_Powerbook_KeypadEnter = 0x34 // Enter on Powerbook's keyboard is different 47 }; 48 49 /** 50 * TISInputSourceWrapper is a wrapper for the TISInputSourceRef. If we get the 51 * TISInputSourceRef from InputSourceID, we need to release the CFArray instance 52 * which is returned by TISCreateInputSourceList. However, when we release the 53 * list, we cannot access the TISInputSourceRef. So, it's not usable, and it 54 * may cause the memory leak bugs. nsTISInputSource automatically releases the 55 * list when the instance is destroyed. 56 */ 57 class TISInputSourceWrapper 58 { 59 public: 60 static TISInputSourceWrapper& CurrentInputSource(); 61 /** 62 * Shutdown() should be called when nobody doesn't need to use this class. 63 */ 64 static void Shutdown(); 65 TISInputSourceWrapper()66 TISInputSourceWrapper() 67 { 68 mInputSourceList = nullptr; 69 Clear(); 70 } 71 TISInputSourceWrapper(const char * aID)72 explicit TISInputSourceWrapper(const char* aID) 73 { 74 mInputSourceList = nullptr; 75 InitByInputSourceID(aID); 76 } 77 TISInputSourceWrapper(SInt32 aLayoutID)78 explicit TISInputSourceWrapper(SInt32 aLayoutID) 79 { 80 mInputSourceList = nullptr; 81 InitByLayoutID(aLayoutID); 82 } 83 TISInputSourceWrapper(TISInputSourceRef aInputSource)84 explicit TISInputSourceWrapper(TISInputSourceRef aInputSource) 85 { 86 mInputSourceList = nullptr; 87 InitByTISInputSourceRef(aInputSource); 88 } 89 ~TISInputSourceWrapper()90 ~TISInputSourceWrapper() { Clear(); } 91 92 void InitByInputSourceID(const char* aID); 93 void InitByInputSourceID(const nsAFlatString &aID); 94 void InitByInputSourceID(const CFStringRef aID); 95 /** 96 * InitByLayoutID() initializes the keyboard layout by the layout ID. 97 * 98 * @param aLayoutID An ID of keyboard layout. 99 * 0: US 100 * 1: Greek 101 * 2: German 102 * 3: Swedish-Pro 103 * 4: Dvorak-Qwerty Cmd 104 * 5: Thai 105 * 6: Arabic 106 * 7: French 107 * 8: Hebrew 108 * 9: Lithuanian 109 * 10: Norwegian 110 * 11: Spanish 111 * @param aOverrideKeyboard When testing set to TRUE, otherwise, set to 112 * FALSE. When TRUE, we use an ANSI keyboard 113 * instead of the actual keyboard. 114 */ 115 void InitByLayoutID(SInt32 aLayoutID, bool aOverrideKeyboard = false); 116 void InitByCurrentInputSource(); 117 void InitByCurrentKeyboardLayout(); 118 void InitByCurrentASCIICapableInputSource(); 119 void InitByCurrentASCIICapableKeyboardLayout(); 120 void InitByCurrentInputMethodKeyboardLayoutOverride(); 121 void InitByTISInputSourceRef(TISInputSourceRef aInputSource); 122 void InitByLanguage(CFStringRef aLanguage); 123 124 /** 125 * If the instance is initialized with a keyboard layout input source, 126 * returns it. 127 * If the instance is initialized with an IME mode input source, the result 128 * references the keyboard layout for the IME mode. However, this can be 129 * initialized only when the IME mode is actually selected. I.e, if IME mode 130 * input source is initialized with LayoutID or SourceID, this returns null. 131 */ GetKeyboardLayoutInputSource()132 TISInputSourceRef GetKeyboardLayoutInputSource() const 133 { 134 return mKeyboardLayout; 135 } 136 const UCKeyboardLayout* GetUCKeyboardLayout(); 137 138 bool IsOpenedIMEMode(); 139 bool IsIMEMode(); 140 bool IsKeyboardLayout(); 141 IsASCIICapable()142 bool IsASCIICapable() 143 { 144 NS_ENSURE_TRUE(mInputSource, false); 145 return GetBoolProperty(kTISPropertyInputSourceIsASCIICapable); 146 } 147 IsEnabled()148 bool IsEnabled() 149 { 150 NS_ENSURE_TRUE(mInputSource, false); 151 return GetBoolProperty(kTISPropertyInputSourceIsEnabled); 152 } 153 154 bool GetLanguageList(CFArrayRef &aLanguageList); 155 bool GetPrimaryLanguage(CFStringRef &aPrimaryLanguage); 156 bool GetPrimaryLanguage(nsAString &aPrimaryLanguage); 157 GetLocalizedName(CFStringRef & aName)158 bool GetLocalizedName(CFStringRef &aName) 159 { 160 NS_ENSURE_TRUE(mInputSource, false); 161 return GetStringProperty(kTISPropertyLocalizedName, aName); 162 } 163 GetLocalizedName(nsAString & aName)164 bool GetLocalizedName(nsAString &aName) 165 { 166 NS_ENSURE_TRUE(mInputSource, false); 167 return GetStringProperty(kTISPropertyLocalizedName, aName); 168 } 169 GetInputSourceID(CFStringRef & aID)170 bool GetInputSourceID(CFStringRef &aID) 171 { 172 NS_ENSURE_TRUE(mInputSource, false); 173 return GetStringProperty(kTISPropertyInputSourceID, aID); 174 } 175 GetInputSourceID(nsAString & aID)176 bool GetInputSourceID(nsAString &aID) 177 { 178 NS_ENSURE_TRUE(mInputSource, false); 179 return GetStringProperty(kTISPropertyInputSourceID, aID); 180 } 181 GetBundleID(CFStringRef & aBundleID)182 bool GetBundleID(CFStringRef &aBundleID) 183 { 184 NS_ENSURE_TRUE(mInputSource, false); 185 return GetStringProperty(kTISPropertyBundleID, aBundleID); 186 } 187 GetBundleID(nsAString & aBundleID)188 bool GetBundleID(nsAString &aBundleID) 189 { 190 NS_ENSURE_TRUE(mInputSource, false); 191 return GetStringProperty(kTISPropertyBundleID, aBundleID); 192 } 193 GetInputSourceType(CFStringRef & aType)194 bool GetInputSourceType(CFStringRef &aType) 195 { 196 NS_ENSURE_TRUE(mInputSource, false); 197 return GetStringProperty(kTISPropertyInputSourceType, aType); 198 } 199 GetInputSourceType(nsAString & aType)200 bool GetInputSourceType(nsAString &aType) 201 { 202 NS_ENSURE_TRUE(mInputSource, false); 203 return GetStringProperty(kTISPropertyInputSourceType, aType); 204 } 205 206 bool IsForRTLLanguage(); 207 bool IsInitializedByCurrentInputSource(); 208 209 enum { 210 // 40 is an actual result of the ::LMGetKbdType() when we connect an 211 // unknown keyboard and set the keyboard type to ANSI manually on the 212 // set up dialog. 213 eKbdType_ANSI = 40 214 }; 215 216 void Select(); 217 void Clear(); 218 219 /** 220 * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent. 221 * 222 * @param aNativeKeyEvent A native key event for which you want to 223 * dispatch a Gecko key event. 224 * @param aKeyEvent The result -- a Gecko key event initialized 225 * from the native key event. 226 * @param aInsertString If caller expects that the event will cause 227 * a character to be input (say in an editor), 228 * the caller should set this. Otherwise, 229 * if caller sets null to this, this method will 230 * compute the character to be input from 231 * characters of aNativeKeyEvent. 232 */ 233 void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent, 234 const nsAString *aInsertString = nullptr); 235 236 /** 237 * WillDispatchKeyboardEvent() computes aKeyEvent.mAlternativeCharCodes and 238 * recompute aKeyEvent.mCharCode if it's necessary. 239 * 240 * @param aNativeKeyEvent A native key event for which you want to 241 * dispatch a Gecko key event. 242 * @param aInsertString If caller expects that the event will cause 243 * a character to be input (say in an editor), 244 * the caller should set this. Otherwise, 245 * if caller sets null to this, this method will 246 * compute the character to be input from 247 * characters of aNativeKeyEvent. 248 * @param aKeyEvent The result -- a Gecko key event initialized 249 * from the native key event. This must be 250 * eKeyPress event. 251 */ 252 void WillDispatchKeyboardEvent(NSEvent* aNativeKeyEvent, 253 const nsAString* aInsertString, 254 WidgetKeyboardEvent& aKeyEvent); 255 256 /** 257 * ComputeGeckoKeyCode() returns Gecko keycode for aNativeKeyCode on current 258 * keyboard layout. 259 * 260 * @param aNativeKeyCode A native keycode. 261 * @param aKbType A native Keyboard Type value. Typically, 262 * this is a result of ::LMGetKbdType(). 263 * @param aCmdIsPressed TRUE if Cmd key is pressed. Otherwise, FALSE. 264 * @return The computed Gecko keycode. 265 */ 266 uint32_t ComputeGeckoKeyCode(UInt32 aNativeKeyCode, UInt32 aKbType, 267 bool aCmdIsPressed); 268 269 /** 270 * ComputeGeckoKeyNameIndex() returns Gecko key name index for the key. 271 * 272 * @param aNativeKeyCode A native keycode. 273 */ 274 static KeyNameIndex ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode); 275 276 /** 277 * ComputeGeckoCodeNameIndex() returns Gecko code name index for the key. 278 * 279 * @param aNativeKeyCode A native keycode. 280 */ 281 static CodeNameIndex ComputeGeckoCodeNameIndex(UInt32 aNativeKeyCode); 282 283 protected: 284 /** 285 * TranslateToString() computes the inputted text from the native keyCode, 286 * modifier flags and keyboard type. 287 * 288 * @param aKeyCode A native keyCode. 289 * @param aModifiers Combination of native modifier flags. 290 * @param aKbType A native Keyboard Type value. Typically, 291 * this is a result of ::LMGetKbdType(). 292 * @param aStr Result, i.e., inputted text. 293 * The result can be two or more characters. 294 * @return If succeeded, TRUE. Otherwise, FALSE. 295 * Even if TRUE, aStr can be empty string. 296 */ 297 bool TranslateToString(UInt32 aKeyCode, UInt32 aModifiers, 298 UInt32 aKbType, nsAString &aStr); 299 300 /** 301 * TranslateToChar() computes the inputted character from the native keyCode, 302 * modifier flags and keyboard type. If two or more characters would be 303 * input, this returns 0. 304 * 305 * @param aKeyCode A native keyCode. 306 * @param aModifiers Combination of native modifier flags. 307 * @param aKbType A native Keyboard Type value. Typically, 308 * this is a result of ::LMGetKbdType(). 309 * @return If succeeded and the result is one character, 310 * returns the charCode of it. Otherwise, 311 * returns 0. 312 */ 313 uint32_t TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType); 314 315 /** 316 * ComputeInsertString() computes string to be inserted with the key event. 317 * 318 * @param aNativeKeyEvent The native key event which causes our keyboard 319 * event(s). 320 * @param aKeyEvent A Gecko key event which was partially 321 * initialized with aNativeKeyEvent. 322 * @param aInsertString The string to be inputting by aNativeKeyEvent. 323 * This should be specified by InsertText(). 324 * In other words, if the key event doesn't cause 325 * a call of InsertText(), this can be nullptr. 326 * @param aResult The string which should be set to charCode of 327 * keypress event(s). 328 */ 329 void ComputeInsertStringForCharCode(NSEvent* aNativeKeyEvent, 330 const WidgetKeyboardEvent& aKeyEvent, 331 const nsAString* aInsertString, 332 nsAString& aResult); 333 334 /** 335 * IsPrintableKeyEvent() returns true if aNativeKeyEvent is caused by 336 * a printable key. Otherwise, returns false. 337 */ 338 bool IsPrintableKeyEvent(NSEvent* aNativeKeyEvent) const; 339 340 /** 341 * GetKbdType() returns physical keyboard type. 342 */ 343 UInt32 GetKbdType() const; 344 345 bool GetBoolProperty(const CFStringRef aKey); 346 bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr); 347 bool GetStringProperty(const CFStringRef aKey, nsAString &aStr); 348 349 TISInputSourceRef mInputSource; 350 TISInputSourceRef mKeyboardLayout; 351 CFArrayRef mInputSourceList; 352 const UCKeyboardLayout* mUCKeyboardLayout; 353 int8_t mIsRTL; 354 355 bool mOverrideKeyboard; 356 357 static TISInputSourceWrapper* sCurrentInputSource; 358 }; 359 360 /** 361 * TextInputHandlerBase is a base class of IMEInputHandler and TextInputHandler. 362 * Utility methods should be implemented this level. 363 */ 364 365 class TextInputHandlerBase : public TextEventDispatcherListener 366 { 367 public: 368 /** 369 * Other TextEventDispatcherListener methods should be implemented in 370 * IMEInputHandler. 371 */ 372 NS_DECL_ISUPPORTS 373 374 /** 375 * DispatchEvent() dispatches aEvent on mWidget. 376 * 377 * @param aEvent An event which you want to dispatch. 378 * @return TRUE if the event is consumed by web contents 379 * or chrome contents. Otherwise, FALSE. 380 */ 381 bool DispatchEvent(WidgetGUIEvent& aEvent); 382 383 /** 384 * SetSelection() dispatches eSetSelection event for the aRange. 385 * 386 * @param aRange The range which will be selected. 387 * @return TRUE if setting selection is succeeded and 388 * the widget hasn't been destroyed. 389 * Otherwise, FALSE. 390 */ 391 bool SetSelection(NSRange& aRange); 392 393 /** 394 * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent. 395 * 396 * @param aNativeKeyEvent A native key event for which you want to 397 * dispatch a Gecko key event. 398 * @param aKeyEvent The result -- a Gecko key event initialized 399 * from the native key event. 400 * @param aInsertString If caller expects that the event will cause 401 * a character to be input (say in an editor), 402 * the caller should set this. Otherwise, 403 * if caller sets null to this, this method will 404 * compute the character to be input from 405 * characters of aNativeKeyEvent. 406 */ 407 void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent, 408 const nsAString *aInsertString = nullptr); 409 410 /** 411 * SynthesizeNativeKeyEvent() is an implementation of 412 * nsIWidget::SynthesizeNativeKeyEvent(). See the document in nsIWidget.h 413 * for the detail. 414 */ 415 nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, 416 int32_t aNativeKeyCode, 417 uint32_t aModifierFlags, 418 const nsAString& aCharacters, 419 const nsAString& aUnmodifiedCharacters); 420 421 /** 422 * Utility method intended for testing. Attempts to construct a native key 423 * event that would have been generated during an actual key press. This 424 * *does not dispatch* the native event. Instead, it is attached to the 425 * |mNativeKeyEvent| field of the Gecko event that is passed in. 426 * @param aKeyEvent Gecko key event to attach the native event to 427 */ 428 NS_IMETHOD AttachNativeKeyEvent(WidgetKeyboardEvent& aKeyEvent); 429 430 /** 431 * GetWindowLevel() returns the window level of current focused (in Gecko) 432 * window. E.g., if an <input> element in XUL panel has focus, this returns 433 * the XUL panel's window level. 434 */ 435 NSInteger GetWindowLevel(); 436 437 /** 438 * IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special 439 * Gecko keyCode. A key is "special" if it isn't used for text input. 440 * 441 * @param aNativeKeyCode A native keycode. 442 * @return If the keycode is mapped to a special key, 443 * TRUE. Otherwise, FALSE. 444 */ 445 static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode); 446 447 448 /** 449 * EnableSecureEventInput() and DisableSecureEventInput() wrap the Carbon 450 * Event Manager APIs with the same names. In addition they keep track of 451 * how many times we've called them (in the same process) -- unlike the 452 * Carbon Event Manager APIs, which only keep track of how many times they've 453 * been called from any and all processes. 454 * 455 * The Carbon Event Manager's IsSecureEventInputEnabled() returns whether 456 * secure event input mode is enabled (in any process). This class's 457 * IsSecureEventInputEnabled() returns whether we've made any calls to 458 * EnableSecureEventInput() that are not (yet) offset by the calls we've 459 * made to DisableSecureEventInput(). 460 */ 461 static void EnableSecureEventInput(); 462 static void DisableSecureEventInput(); 463 static bool IsSecureEventInputEnabled(); 464 465 /** 466 * EnsureSecureEventInputDisabled() calls DisableSecureEventInput() until 467 * our call count becomes 0. 468 */ 469 static void EnsureSecureEventInputDisabled(); 470 471 public: 472 /** 473 * mWidget must not be destroyed without OnDestroyWidget being called. 474 * 475 * @param aDestroyingWidget Destroying widget. This might not be mWidget. 476 * @return This result doesn't have any meaning for 477 * callers. When aDstroyingWidget isn't the same 478 * as mWidget, FALSE. Then, inherited methods in 479 * sub classes should return from this method 480 * without cleaning up. 481 */ 482 virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget); 483 484 protected: 485 // The creator of this instance, client and its text event dispatcher. 486 // These members must not be nullptr after initialized until 487 // OnDestroyWidget() is called. 488 nsChildView* mWidget; // [WEAK] 489 RefPtr<TextEventDispatcher> mDispatcher; 490 491 // The native view for mWidget. 492 // This view handles the actual text inputting. 493 NSView<mozView>* mView; // [STRONG] 494 495 TextInputHandlerBase(nsChildView* aWidget, NSView<mozView> *aNativeView); 496 virtual ~TextInputHandlerBase(); 497 Destroyed()498 bool Destroyed() { return !mWidget; } 499 500 /** 501 * mCurrentKeyEvent indicates what key event we are handling. While 502 * handling a native keydown event, we need to store the event for insertText, 503 * doCommandBySelector and various action message handlers of NSResponder 504 * such as [NSResponder insertNewline:sender]. 505 */ 506 struct KeyEventState 507 { 508 // Handling native key event 509 NSEvent* mKeyEvent; 510 // String specified by InsertText(). This is not null only during a 511 // call of InsertText(). 512 nsAString* mInsertString; 513 // String which are included in [mKeyEvent characters] and already handled 514 // by InsertText() call(s). 515 nsString mInsertedString; 516 // Whether keydown event was consumed by web contents or chrome contents. 517 bool mKeyDownHandled; 518 // Whether keypress event was dispatched for mKeyEvent. 519 bool mKeyPressDispatched; 520 // Whether keypress event was consumed by web contents or chrome contents. 521 bool mKeyPressHandled; 522 // Whether the key event causes other key events via IME or something. 523 bool mCausedOtherKeyEvents; 524 // Whether the key event causes composition change or committing 525 // composition. So, even if InsertText() is called, this may be false 526 // if it dispatches keypress event. 527 bool mCompositionDispatched; 528 KeyEventStateKeyEventState529 KeyEventState() : mKeyEvent(nullptr) 530 { 531 Clear(); 532 } 533 KeyEventStateKeyEventState534 explicit KeyEventState(NSEvent* aNativeKeyEvent) : mKeyEvent(nullptr) 535 { 536 Clear(); 537 Set(aNativeKeyEvent); 538 } 539 540 KeyEventState(const KeyEventState &aOther) = delete; 541 ~KeyEventStateKeyEventState542 ~KeyEventState() 543 { 544 Clear(); 545 } 546 SetKeyEventState547 void Set(NSEvent* aNativeKeyEvent) 548 { 549 NS_PRECONDITION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL"); 550 Clear(); 551 mKeyEvent = [aNativeKeyEvent retain]; 552 } 553 ClearKeyEventState554 void Clear() 555 { 556 if (mKeyEvent) { 557 [mKeyEvent release]; 558 mKeyEvent = nullptr; 559 } 560 mInsertString = nullptr; 561 mInsertedString.Truncate(); 562 mKeyDownHandled = false; 563 mKeyPressDispatched = false; 564 mKeyPressHandled = false; 565 mCausedOtherKeyEvents = false; 566 mCompositionDispatched = false; 567 } 568 IsDefaultPreventedKeyEventState569 bool IsDefaultPrevented() const 570 { 571 return mKeyDownHandled || mKeyPressHandled || mCausedOtherKeyEvents || 572 mCompositionDispatched; 573 } 574 CanDispatchKeyPressEventKeyEventState575 bool CanDispatchKeyPressEvent() const 576 { 577 return !mKeyPressDispatched && !IsDefaultPrevented(); 578 } 579 580 void InitKeyEvent(TextInputHandlerBase* aHandler, 581 WidgetKeyboardEvent& aKeyEvent); 582 583 /** 584 * GetUnhandledString() returns characters of the event which have not been 585 * handled with InsertText() yet. For example, if there is a composition 586 * caused by a dead key press like '`' and it's committed by some key 587 * combinations like |Cmd+v|, then, the |v|'s KeyDown event's |characters| 588 * is |`v|. Then, after |`| is committed with a call of InsertString(), 589 * this returns only 'v'. 590 */ 591 void GetUnhandledString(nsAString& aUnhandledString) const; 592 }; 593 594 /** 595 * Helper classes for guaranteeing cleaning mCurrentKeyEvent 596 */ 597 class AutoKeyEventStateCleaner 598 { 599 public: AutoKeyEventStateCleaner(TextInputHandlerBase * aHandler)600 explicit AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) : 601 mHandler(aHandler) 602 { 603 } 604 ~AutoKeyEventStateCleaner()605 ~AutoKeyEventStateCleaner() 606 { 607 mHandler->RemoveCurrentKeyEvent(); 608 } 609 private: 610 RefPtr<TextInputHandlerBase> mHandler; 611 }; 612 613 class MOZ_STACK_CLASS AutoInsertStringClearer 614 { 615 public: AutoInsertStringClearer(KeyEventState * aState)616 explicit AutoInsertStringClearer(KeyEventState* aState) 617 : mState(aState) 618 { 619 } 620 ~AutoInsertStringClearer(); 621 622 private: 623 KeyEventState* mState; 624 }; 625 626 /** 627 * mCurrentKeyEvents stores all key events which are being processed. 628 * When we call interpretKeyEvents, IME may generate other key events. 629 * mCurrentKeyEvents[0] is the latest key event. 630 */ 631 nsTArray<KeyEventState*> mCurrentKeyEvents; 632 633 /** 634 * mFirstKeyEvent must be used for first key event. This member prevents 635 * memory fragmentation for most key events. 636 */ 637 KeyEventState mFirstKeyEvent; 638 639 /** 640 * PushKeyEvent() adds the current key event to mCurrentKeyEvents. 641 */ PushKeyEvent(NSEvent * aNativeKeyEvent)642 KeyEventState* PushKeyEvent(NSEvent* aNativeKeyEvent) 643 { 644 uint32_t nestCount = mCurrentKeyEvents.Length(); 645 for (uint32_t i = 0; i < nestCount; i++) { 646 // When the key event is caused by another key event, all key events 647 // which are being handled should be marked as "consumed". 648 mCurrentKeyEvents[i]->mCausedOtherKeyEvents = true; 649 } 650 651 KeyEventState* keyEvent = nullptr; 652 if (nestCount == 0) { 653 mFirstKeyEvent.Set(aNativeKeyEvent); 654 keyEvent = &mFirstKeyEvent; 655 } else { 656 keyEvent = new KeyEventState(aNativeKeyEvent); 657 } 658 return *mCurrentKeyEvents.AppendElement(keyEvent); 659 } 660 661 /** 662 * RemoveCurrentKeyEvent() removes the current key event from 663 * mCurrentKeyEvents. 664 */ RemoveCurrentKeyEvent()665 void RemoveCurrentKeyEvent() 666 { 667 NS_ASSERTION(mCurrentKeyEvents.Length() > 0, 668 "RemoveCurrentKeyEvent() is called unexpectedly"); 669 KeyEventState* keyEvent = GetCurrentKeyEvent(); 670 mCurrentKeyEvents.RemoveElementAt(mCurrentKeyEvents.Length() - 1); 671 if (keyEvent == &mFirstKeyEvent) { 672 keyEvent->Clear(); 673 } else { 674 delete keyEvent; 675 } 676 } 677 678 /** 679 * GetCurrentKeyEvent() returns current processing key event. 680 */ GetCurrentKeyEvent()681 KeyEventState* GetCurrentKeyEvent() 682 { 683 if (mCurrentKeyEvents.Length() == 0) { 684 return nullptr; 685 } 686 return mCurrentKeyEvents[mCurrentKeyEvents.Length() - 1]; 687 } 688 689 struct KeyboardLayoutOverride final 690 { 691 int32_t mKeyboardLayout; 692 bool mOverrideEnabled; 693 KeyboardLayoutOverridefinal694 KeyboardLayoutOverride() : 695 mKeyboardLayout(0), mOverrideEnabled(false) 696 { 697 } 698 }; 699 KeyboardLayoutOverrideRef()700 const KeyboardLayoutOverride& KeyboardLayoutOverrideRef() const 701 { 702 return mKeyboardOverride; 703 } 704 705 /** 706 * IsPrintableChar() checks whether the unicode character is 707 * a non-printable ASCII character or not. Note that this returns 708 * TRUE even if aChar is a non-printable UNICODE character. 709 * 710 * @param aChar A unicode character. 711 * @return TRUE if aChar is a printable ASCII character 712 * or a unicode character. Otherwise, i.e, 713 * if aChar is a non-printable ASCII character, 714 * FALSE. 715 */ 716 static bool IsPrintableChar(char16_t aChar); 717 718 /** 719 * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input. 720 * 721 * @param aKeyEvent A key event. 722 * @return TRUE if the key event causes text input. 723 * Otherwise, FALSE. 724 */ 725 static bool IsNormalCharInputtingEvent(const WidgetKeyboardEvent& aKeyEvent); 726 727 /** 728 * IsModifierKey() checks whether the native keyCode is for a modifier key. 729 * 730 * @param aNativeKeyCode A native keyCode. 731 * @return TRUE if aNativeKeyCode is for a modifier key. 732 * Otherwise, FALSE. 733 */ 734 static bool IsModifierKey(UInt32 aNativeKeyCode); 735 736 private: 737 KeyboardLayoutOverride mKeyboardOverride; 738 739 static int32_t sSecureEventInputCount; 740 }; 741 742 /** 743 * IMEInputHandler manages: 744 * 1. The IME/keyboard layout statement of nsChildView. 745 * 2. The IME composition statement of nsChildView. 746 * And also provides the methods which controls the current IME transaction of 747 * the instance. 748 * 749 * Note that an nsChildView handles one or more NSView's events. E.g., even if 750 * a text editor on XUL panel element, the input events handled on the parent 751 * (or its ancestor) widget handles it (the native focus is set to it). The 752 * actual focused view is notified by OnFocusChangeInGecko. 753 */ 754 755 class IMEInputHandler : public TextInputHandlerBase 756 { 757 public: 758 // TextEventDispatcherListener methods 759 NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, 760 const IMENotification& aNotification) override; 761 NS_IMETHOD_(void) OnRemovedFrom( 762 TextEventDispatcher* aTextEventDispatcher) override; 763 NS_IMETHOD_(void) WillDispatchKeyboardEvent( 764 TextEventDispatcher* aTextEventDispatcher, 765 WidgetKeyboardEvent& aKeyboardEvent, 766 uint32_t aIndexOfKeypress, 767 void* aData) override; 768 769 public: 770 virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget) override; 771 772 virtual void OnFocusChangeInGecko(bool aFocus); 773 774 void OnSelectionChange(const IMENotification& aIMENotification); 775 776 /** 777 * Call [NSTextInputContext handleEvent] for mouse event support of IME 778 */ 779 bool OnHandleEvent(NSEvent* aEvent); 780 781 /** 782 * SetMarkedText() is a handler of setMarkedText of NSTextInput. 783 * 784 * @param aAttrString This mut be an instance of NSAttributedString. 785 * If the aString parameter to 786 * [ChildView setMarkedText:setSelectedRange:] 787 * isn't an instance of NSAttributedString, 788 * create an NSAttributedString from it and pass 789 * that instead. 790 * @param aSelectedRange Current selected range (or caret position). 791 * @param aReplacementRange The range which will be replaced with the 792 * aAttrString instead of current marked range. 793 */ 794 void SetMarkedText(NSAttributedString* aAttrString, 795 NSRange& aSelectedRange, 796 NSRange* aReplacementRange = nullptr); 797 798 /** 799 * GetAttributedSubstringFromRange() returns an NSAttributedString instance 800 * which is allocated as autorelease for aRange. 801 * 802 * @param aRange The range of string which you want. 803 * @param aActualRange The actual range of the result. 804 * @return The string in aRange. If the string is empty, 805 * this returns nil. If succeeded, this returns 806 * an instance which is allocated as autorelease. 807 * If this has some troubles, returns nil. 808 */ 809 NSAttributedString* GetAttributedSubstringFromRange( 810 NSRange& aRange, 811 NSRange* aActualRange = nullptr); 812 813 /** 814 * SelectedRange() returns current selected range. 815 * 816 * @return If an editor has focus, this returns selection 817 * range in the editor. Otherwise, this returns 818 * selection range in the focused document. 819 */ 820 NSRange SelectedRange(); 821 822 /** 823 * DrawsVerticallyForCharacterAtIndex() returns whether the character at 824 * the given index is being rendered vertically. 825 * 826 * @param aCharIndex The character offset to query. 827 * 828 * @return True if writing-mode is vertical at the given 829 * character offset; otherwise false. 830 */ 831 bool DrawsVerticallyForCharacterAtIndex(uint32_t aCharIndex); 832 833 /** 834 * FirstRectForCharacterRange() returns first *character* rect in the range. 835 * Cocoa needs the first line rect in the range, but we cannot compute it 836 * on current implementation. 837 * 838 * @param aRange A range of text to examine. Its position is 839 * an offset from the beginning of the focused 840 * editor or document. 841 * @param aActualRange If this is not null, this returns the actual 842 * range used for computing the result. 843 * @return An NSRect containing the first character in 844 * aRange, in screen coordinates. 845 * If the length of aRange is 0, the width will 846 * be 0. 847 */ 848 NSRect FirstRectForCharacterRange(NSRange& aRange, 849 NSRange* aActualRange = nullptr); 850 851 /** 852 * CharacterIndexForPoint() returns an offset of a character at aPoint. 853 * XXX This isn't implemented, always returns 0. 854 * 855 * @param The point in screen coordinates. 856 * @return The offset of the character at aPoint from 857 * the beginning of the focused editor or 858 * document. 859 */ 860 NSUInteger CharacterIndexForPoint(NSPoint& aPoint); 861 862 /** 863 * GetValidAttributesForMarkedText() returns attributes which we support. 864 * 865 * @return Always empty array for now. 866 */ 867 NSArray* GetValidAttributesForMarkedText(); 868 869 bool HasMarkedText(); 870 NSRange MarkedRange(); 871 IsIMEComposing()872 bool IsIMEComposing() { return mIsIMEComposing; } 873 bool IsIMEOpened(); IsIMEEnabled()874 bool IsIMEEnabled() { return mIsIMEEnabled; } IsASCIICapableOnly()875 bool IsASCIICapableOnly() { return mIsASCIICapableOnly; } IgnoreIMECommit()876 bool IgnoreIMECommit() { return mIgnoreIMECommit; } 877 IgnoreIMEComposition()878 bool IgnoreIMEComposition() 879 { 880 // Ignore the IME composition events when we're pending to discard the 881 // composition and we are not to handle the IME composition now. 882 return (mPendingMethods & kDiscardIMEComposition) && 883 (mIsInFocusProcessing || !IsFocused()); 884 } 885 886 void CommitIMEComposition(); 887 void CancelIMEComposition(); 888 889 void EnableIME(bool aEnableIME); 890 void SetIMEOpenState(bool aOpen); 891 void SetASCIICapableOnly(bool aASCIICapableOnly); 892 893 /** 894 * True if OSX believes that our view has keyboard focus. 895 */ 896 bool IsFocused(); 897 898 static CFArrayRef CreateAllIMEModeList(); 899 static void DebugPrintAllIMEModes(); 900 901 // Don't use ::TSMGetActiveDocument() API directly, the document may not 902 // be what you want. 903 static TSMDocumentID GetCurrentTSMDocumentID(); 904 905 protected: 906 // We cannot do some jobs in the given stack by some reasons. 907 // Following flags and the timer provide the execution pending mechanism, 908 // See the comment in nsCocoaTextInputHandler.mm. 909 nsCOMPtr<nsITimer> mTimer; 910 enum { 911 kNotifyIMEOfFocusChangeInGecko = 1, 912 kDiscardIMEComposition = 2, 913 kSyncASCIICapableOnly = 4 914 }; 915 uint32_t mPendingMethods; 916 917 IMEInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView); 918 virtual ~IMEInputHandler(); 919 920 void ResetTimer(); 921 922 virtual void ExecutePendingMethods(); 923 924 /** 925 * InsertTextAsCommittingComposition() commits current composition. If there 926 * is no composition, this starts a composition and commits it immediately. 927 * 928 * @param aAttrString A string which is committed. 929 * @param aReplacementRange The range which will be replaced with the 930 * aAttrString instead of current selection. 931 */ 932 void InsertTextAsCommittingComposition(NSAttributedString* aAttrString, 933 NSRange* aReplacementRange); 934 935 private: 936 // If mIsIMEComposing is true, the composition string is stored here. 937 NSString* mIMECompositionString; 938 // If mIsIMEComposing is true, the start offset of the composition string. 939 uint32_t mIMECompositionStart; 940 941 NSRange mMarkedRange; 942 NSRange mSelectedRange; 943 944 NSRange mRangeForWritingMode; // range within which mWritingMode applies 945 mozilla::WritingMode mWritingMode; 946 947 bool mIsIMEComposing; 948 bool mIsIMEEnabled; 949 bool mIsASCIICapableOnly; 950 bool mIgnoreIMECommit; 951 // This flag is enabled by OnFocusChangeInGecko, and will be cleared by 952 // ExecutePendingMethods. When this is true, IsFocus() returns TRUE. At 953 // that time, the focus processing in Gecko might not be finished yet. So, 954 // you cannot use WidgetQueryContentEvent or something. 955 bool mIsInFocusProcessing; 956 bool mIMEHasFocus; 957 958 void KillIMEComposition(); 959 void SendCommittedText(NSString *aString); 960 void OpenSystemPreferredLanguageIME(); 961 962 // Pending methods 963 void NotifyIMEOfFocusChangeInGecko(); 964 void DiscardIMEComposition(); 965 void SyncASCIICapableOnly(); 966 967 static bool sStaticMembersInitialized; 968 static CFStringRef sLatestIMEOpenedModeInputSourceID; 969 static void InitStaticMembers(); 970 static void OnCurrentTextInputSourceChange(CFNotificationCenterRef aCenter, 971 void* aObserver, 972 CFStringRef aName, 973 const void* aObject, 974 CFDictionaryRef aUserInfo); 975 976 static void FlushPendingMethods(nsITimer* aTimer, void* aClosure); 977 978 /** 979 * ConvertToTextRangeStyle converts the given native underline style to 980 * our defined text range type. 981 * 982 * @param aUnderlineStyle NSUnderlineStyleSingle or 983 * NSUnderlineStyleThick. 984 * @param aSelectedRange Current selected range (or caret position). 985 * @return NS_TEXTRANGE_*. 986 */ 987 TextRangeType ConvertToTextRangeType(uint32_t aUnderlineStyle, 988 NSRange& aSelectedRange); 989 990 /** 991 * GetRangeCount() computes the range count of aAttrString. 992 * 993 * @param aAttrString An NSAttributedString instance whose number of 994 * NSUnderlineStyleAttributeName ranges you with 995 * to know. 996 * @return The count of NSUnderlineStyleAttributeName 997 * ranges in aAttrString. 998 */ 999 uint32_t GetRangeCount(NSAttributedString *aString); 1000 1001 /** 1002 * CreateTextRangeArray() returns text ranges for clauses and/or caret. 1003 * 1004 * @param aAttrString An NSAttributedString instance which indicates 1005 * current composition string. 1006 * @param aSelectedRange Current selected range (or caret position). 1007 * @return The result is set to the 1008 * NSUnderlineStyleAttributeName ranges in 1009 * aAttrString. 1010 */ 1011 already_AddRefed<mozilla::TextRangeArray> 1012 CreateTextRangeArray(NSAttributedString *aAttrString, 1013 NSRange& aSelectedRange); 1014 1015 /** 1016 * DispatchCompositionStartEvent() dispatches a compositionstart event and 1017 * initializes the members indicating composition state. 1018 * 1019 * @return true if it can continues handling composition. 1020 * Otherwise, e.g., canceled by the web page, 1021 * this returns false. 1022 */ 1023 bool DispatchCompositionStartEvent(); 1024 1025 /** 1026 * DispatchCompositionChangeEvent() dispatches a compositionchange event on 1027 * mWidget and modifies the members indicating composition state. 1028 * 1029 * @param aText User text input. 1030 * @param aAttrString An NSAttributedString instance which indicates 1031 * current composition string. 1032 * @param aSelectedRange Current selected range (or caret position). 1033 * 1034 * @return true if it can continues handling composition. 1035 * Otherwise, e.g., canceled by the web page, 1036 * this returns false. 1037 */ 1038 bool DispatchCompositionChangeEvent(const nsString& aText, 1039 NSAttributedString* aAttrString, 1040 NSRange& aSelectedRange); 1041 1042 /** 1043 * DispatchCompositionCommitEvent() dispatches a compositioncommit event or 1044 * compositioncommitasis event. If aCommitString is null, dispatches 1045 * compositioncommitasis event. I.e., if aCommitString is null, this 1046 * commits the composition with the last data. Otherwise, commits the 1047 * composition with aCommitString value. 1048 * 1049 * @return true if the widget isn't destroyed. 1050 * Otherwise, false. 1051 */ 1052 bool DispatchCompositionCommitEvent(const nsAString* aCommitString = nullptr); 1053 1054 // The focused IME handler. Please note that the handler might lost the 1055 // actual focus by deactivating the application. If we are active, this 1056 // must have the actual focused handle. 1057 // We cannot access to the NSInputManager during we aren't active, so, the 1058 // focused handler can have an IME transaction even if we are deactive. 1059 static IMEInputHandler* sFocusedIMEHandler; 1060 1061 static bool sCachedIsForRTLLangage; 1062 }; 1063 1064 /** 1065 * TextInputHandler implements the NSTextInput protocol. 1066 */ 1067 class TextInputHandler : public IMEInputHandler 1068 { 1069 public: 1070 static NSUInteger sLastModifierState; 1071 1072 static CFArrayRef CreateAllKeyboardLayoutList(); 1073 static void DebugPrintAllKeyboardLayouts(); 1074 1075 TextInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView); 1076 virtual ~TextInputHandler(); 1077 1078 /** 1079 * KeyDown event handler. 1080 * 1081 * @param aNativeEvent A native NSKeyDown event. 1082 * @return TRUE if the event is consumed by web contents 1083 * or chrome contents. Otherwise, FALSE. 1084 */ 1085 bool HandleKeyDownEvent(NSEvent* aNativeEvent); 1086 1087 /** 1088 * KeyUp event handler. 1089 * 1090 * @param aNativeEvent A native NSKeyUp event. 1091 */ 1092 void HandleKeyUpEvent(NSEvent* aNativeEvent); 1093 1094 /** 1095 * FlagsChanged event handler. 1096 * 1097 * @param aNativeEvent A native NSFlagsChanged event. 1098 */ 1099 void HandleFlagsChanged(NSEvent* aNativeEvent); 1100 1101 /** 1102 * Insert the string to content. I.e., this is a text input event handler. 1103 * If this is called during keydown event handling, this may dispatch a 1104 * eKeyPress event. If this is called during composition, this commits 1105 * the composition by the aAttrString. 1106 * 1107 * @param aAttrString An inserted string. 1108 * @param aReplacementRange The range which will be replaced with the 1109 * aAttrString instead of current selection. 1110 */ 1111 void InsertText(NSAttributedString *aAttrString, 1112 NSRange* aReplacementRange = nullptr); 1113 1114 /** 1115 * doCommandBySelector event handler. 1116 * 1117 * @param aSelector A selector of the command. 1118 * @return TRUE if the command is consumed. Otherwise, 1119 * FALSE. 1120 */ 1121 bool DoCommandBySelector(const char* aSelector); 1122 1123 /** 1124 * KeyPressWasHandled() checks whether keypress event was handled or not. 1125 * 1126 * @return TRUE if keypress event for latest native key 1127 * event was handled. Otherwise, FALSE. 1128 * If this handler isn't handling any key events, 1129 * always returns FALSE. 1130 */ KeyPressWasHandled()1131 bool KeyPressWasHandled() 1132 { 1133 KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); 1134 return currentKeyEvent && currentKeyEvent->mKeyPressHandled; 1135 } 1136 1137 protected: 1138 // Stores the association of device dependent modifier flags with a modifier 1139 // keyCode. Being device dependent, this association may differ from one kind 1140 // of hardware to the next. 1141 struct ModifierKey 1142 { 1143 NSUInteger flags; 1144 unsigned short keyCode; 1145 ModifierKeyModifierKey1146 ModifierKey(NSUInteger aFlags, unsigned short aKeyCode) : 1147 flags(aFlags), keyCode(aKeyCode) 1148 { 1149 } 1150 GetDeviceDependentFlagsModifierKey1151 NSUInteger GetDeviceDependentFlags() const 1152 { 1153 return (flags & ~NSDeviceIndependentModifierFlagsMask); 1154 } 1155 GetDeviceIndependentFlagsModifierKey1156 NSUInteger GetDeviceIndependentFlags() const 1157 { 1158 return (flags & NSDeviceIndependentModifierFlagsMask); 1159 } 1160 }; 1161 typedef nsTArray<ModifierKey> ModifierKeyArray; 1162 ModifierKeyArray mModifierKeys; 1163 1164 /** 1165 * GetModifierKeyForNativeKeyCode() returns the stored ModifierKey for 1166 * the key. 1167 */ 1168 const ModifierKey* 1169 GetModifierKeyForNativeKeyCode(unsigned short aKeyCode) const; 1170 1171 /** 1172 * GetModifierKeyForDeviceDependentFlags() returns the stored ModifierKey for 1173 * the device dependent flags. 1174 */ 1175 const ModifierKey* 1176 GetModifierKeyForDeviceDependentFlags(NSUInteger aFlags) const; 1177 1178 /** 1179 * DispatchKeyEventForFlagsChanged() dispatches keydown event or keyup event 1180 * for the aNativeEvent. 1181 * 1182 * @param aNativeEvent A native flagschanged event which you want to 1183 * dispatch our key event for. 1184 * @param aDispatchKeyDown TRUE if you want to dispatch a keydown event. 1185 * Otherwise, i.e., to dispatch keyup event, 1186 * FALSE. 1187 */ 1188 void DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent, 1189 bool aDispatchKeyDown); 1190 }; 1191 1192 } // namespace widget 1193 } // namespace mozilla 1194 1195 #endif // TextInputHandler_h_ 1196