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