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