widget/cocoa/TextInputHandler.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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_

mercurial