michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef TextInputHandler_h_ michael@0: #define TextInputHandler_h_ michael@0: michael@0: #include "nsCocoaUtils.h" michael@0: michael@0: #import michael@0: #import michael@0: #include "mozView.h" michael@0: #include "nsString.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsITimer.h" michael@0: #include "npapi.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/EventForwards.h" michael@0: michael@0: class nsChildView; michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: // Key code constants michael@0: enum michael@0: { michael@0: kVK_RightCommand = 0x36, // right command key michael@0: michael@0: kVK_PC_PrintScreen = kVK_F13, michael@0: kVK_PC_ScrollLock = kVK_F14, michael@0: kVK_PC_Pause = kVK_F15, michael@0: michael@0: kVK_PC_Insert = kVK_Help, michael@0: kVK_PC_Backspace = kVK_Delete, michael@0: kVK_PC_Delete = kVK_ForwardDelete, michael@0: michael@0: kVK_PC_ContextMenu = 0x6E, michael@0: michael@0: kVK_Powerbook_KeypadEnter = 0x34 // Enter on Powerbook's keyboard is different michael@0: }; michael@0: michael@0: /** michael@0: * TISInputSourceWrapper is a wrapper for the TISInputSourceRef. If we get the michael@0: * TISInputSourceRef from InputSourceID, we need to release the CFArray instance michael@0: * which is returned by TISCreateInputSourceList. However, when we release the michael@0: * list, we cannot access the TISInputSourceRef. So, it's not usable, and it michael@0: * may cause the memory leak bugs. nsTISInputSource automatically releases the michael@0: * list when the instance is destroyed. michael@0: */ michael@0: class TISInputSourceWrapper michael@0: { michael@0: public: michael@0: static TISInputSourceWrapper& CurrentInputSource(); michael@0: michael@0: TISInputSourceWrapper() michael@0: { michael@0: mInputSourceList = nullptr; michael@0: Clear(); michael@0: } michael@0: michael@0: TISInputSourceWrapper(const char* aID) michael@0: { michael@0: mInputSourceList = nullptr; michael@0: InitByInputSourceID(aID); michael@0: } michael@0: michael@0: TISInputSourceWrapper(SInt32 aLayoutID) michael@0: { michael@0: mInputSourceList = nullptr; michael@0: InitByLayoutID(aLayoutID); michael@0: } michael@0: michael@0: TISInputSourceWrapper(TISInputSourceRef aInputSource) michael@0: { michael@0: mInputSourceList = nullptr; michael@0: InitByTISInputSourceRef(aInputSource); michael@0: } michael@0: michael@0: ~TISInputSourceWrapper() { Clear(); } michael@0: michael@0: void InitByInputSourceID(const char* aID); michael@0: void InitByInputSourceID(const nsAFlatString &aID); michael@0: void InitByInputSourceID(const CFStringRef aID); michael@0: /** michael@0: * InitByLayoutID() initializes the keyboard layout by the layout ID. michael@0: * michael@0: * @param aLayoutID An ID of keyboard layout. michael@0: * 0: US michael@0: * 1: Greek michael@0: * 2: German michael@0: * 3: Swedish-Pro michael@0: * 4: Dvorak-Qwerty Cmd michael@0: * 5: Thai michael@0: * 6: Arabic michael@0: * 7: French michael@0: * 8: Hebrew michael@0: * 9: Lithuanian michael@0: * 10: Norwegian michael@0: * 11: Spanish michael@0: * @param aOverrideKeyboard When testing set to TRUE, otherwise, set to michael@0: * FALSE. When TRUE, we use an ANSI keyboard michael@0: * instead of the actual keyboard. michael@0: */ michael@0: void InitByLayoutID(SInt32 aLayoutID, bool aOverrideKeyboard = false); michael@0: void InitByCurrentInputSource(); michael@0: void InitByCurrentKeyboardLayout(); michael@0: void InitByCurrentASCIICapableInputSource(); michael@0: void InitByCurrentASCIICapableKeyboardLayout(); michael@0: void InitByCurrentInputMethodKeyboardLayoutOverride(); michael@0: void InitByTISInputSourceRef(TISInputSourceRef aInputSource); michael@0: void InitByLanguage(CFStringRef aLanguage); michael@0: michael@0: /** michael@0: * If the instance is initialized with a keyboard layout input source, michael@0: * returns it. michael@0: * If the instance is initialized with an IME mode input source, the result michael@0: * references the keyboard layout for the IME mode. However, this can be michael@0: * initialized only when the IME mode is actually selected. I.e, if IME mode michael@0: * input source is initialized with LayoutID or SourceID, this returns null. michael@0: */ michael@0: TISInputSourceRef GetKeyboardLayoutInputSource() const michael@0: { michael@0: return mKeyboardLayout; michael@0: } michael@0: const UCKeyboardLayout* GetUCKeyboardLayout(); michael@0: michael@0: bool IsOpenedIMEMode(); michael@0: bool IsIMEMode(); michael@0: bool IsKeyboardLayout(); michael@0: michael@0: bool IsASCIICapable() michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetBoolProperty(kTISPropertyInputSourceIsASCIICapable); michael@0: } michael@0: michael@0: bool IsEnabled() michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetBoolProperty(kTISPropertyInputSourceIsEnabled); michael@0: } michael@0: michael@0: bool GetLanguageList(CFArrayRef &aLanguageList); michael@0: bool GetPrimaryLanguage(CFStringRef &aPrimaryLanguage); michael@0: bool GetPrimaryLanguage(nsAString &aPrimaryLanguage); michael@0: michael@0: bool GetLocalizedName(CFStringRef &aName) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyLocalizedName, aName); michael@0: } michael@0: michael@0: bool GetLocalizedName(nsAString &aName) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyLocalizedName, aName); michael@0: } michael@0: michael@0: bool GetInputSourceID(CFStringRef &aID) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyInputSourceID, aID); michael@0: } michael@0: michael@0: bool GetInputSourceID(nsAString &aID) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyInputSourceID, aID); michael@0: } michael@0: michael@0: bool GetBundleID(CFStringRef &aBundleID) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyBundleID, aBundleID); michael@0: } michael@0: michael@0: bool GetBundleID(nsAString &aBundleID) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyBundleID, aBundleID); michael@0: } michael@0: michael@0: bool GetInputSourceType(CFStringRef &aType) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyInputSourceType, aType); michael@0: } michael@0: michael@0: bool GetInputSourceType(nsAString &aType) michael@0: { michael@0: NS_ENSURE_TRUE(mInputSource, false); michael@0: return GetStringProperty(kTISPropertyInputSourceType, aType); michael@0: } michael@0: michael@0: bool IsForRTLLanguage(); michael@0: bool IsInitializedByCurrentInputSource(); michael@0: michael@0: enum { michael@0: // 40 is an actual result of the ::LMGetKbdType() when we connect an michael@0: // unknown keyboard and set the keyboard type to ANSI manually on the michael@0: // set up dialog. michael@0: eKbdType_ANSI = 40 michael@0: }; michael@0: michael@0: void Select(); michael@0: void Clear(); michael@0: michael@0: /** michael@0: * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent. michael@0: * michael@0: * @param aNativeKeyEvent A native key event for which you want to michael@0: * dispatch a Gecko key event. michael@0: * @param aKeyEvent The result -- a Gecko key event initialized michael@0: * from the native key event. michael@0: * @param aInsertString If caller expects that the event will cause michael@0: * a character to be input (say in an editor), michael@0: * the caller should set this. Otherwise, michael@0: * if caller sets null to this, this method will michael@0: * compute the character to be input from michael@0: * characters of aNativeKeyEvent. michael@0: */ michael@0: void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent, michael@0: const nsAString *aInsertString = nullptr); michael@0: michael@0: /** michael@0: * ComputeGeckoKeyCode() returns Gecko keycode for aNativeKeyCode on current michael@0: * keyboard layout. michael@0: * michael@0: * @param aNativeKeyCode A native keycode. michael@0: * @param aKbType A native Keyboard Type value. Typically, michael@0: * this is a result of ::LMGetKbdType(). michael@0: * @param aCmdIsPressed TRUE if Cmd key is pressed. Otherwise, FALSE. michael@0: * @return The computed Gecko keycode. michael@0: */ michael@0: uint32_t ComputeGeckoKeyCode(UInt32 aNativeKeyCode, UInt32 aKbType, michael@0: bool aCmdIsPressed); michael@0: michael@0: /** michael@0: * ComputeGeckoKeyNameIndex() returns Gecko key name index for the key. michael@0: * michael@0: * @param aNativeKeyCode A native keycode. michael@0: */ michael@0: static KeyNameIndex ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode); michael@0: michael@0: protected: michael@0: /** michael@0: * TranslateToString() computes the inputted text from the native keyCode, michael@0: * modifier flags and keyboard type. michael@0: * michael@0: * @param aKeyCode A native keyCode. michael@0: * @param aModifiers Combination of native modifier flags. michael@0: * @param aKbType A native Keyboard Type value. Typically, michael@0: * this is a result of ::LMGetKbdType(). michael@0: * @param aStr Result, i.e., inputted text. michael@0: * The result can be two or more characters. michael@0: * @return If succeeded, TRUE. Otherwise, FALSE. michael@0: * Even if TRUE, aStr can be empty string. michael@0: */ michael@0: bool TranslateToString(UInt32 aKeyCode, UInt32 aModifiers, michael@0: UInt32 aKbType, nsAString &aStr); michael@0: michael@0: /** michael@0: * TranslateToChar() computes the inputted character from the native keyCode, michael@0: * modifier flags and keyboard type. If two or more characters would be michael@0: * input, this returns 0. michael@0: * michael@0: * @param aKeyCode A native keyCode. michael@0: * @param aModifiers Combination of native modifier flags. michael@0: * @param aKbType A native Keyboard Type value. Typically, michael@0: * this is a result of ::LMGetKbdType(). michael@0: * @return If succeeded and the result is one character, michael@0: * returns the charCode of it. Otherwise, michael@0: * returns 0. michael@0: */ michael@0: uint32_t TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType); michael@0: michael@0: /** michael@0: * InitKeyPressEvent() initializes aKeyEvent for aNativeKeyEvent. michael@0: * Don't call this method when aKeyEvent isn't NS_KEY_PRESS. michael@0: * michael@0: * @param aNativeKeyEvent A native key event for which you want to michael@0: * dispatch a Gecko key event. michael@0: * @param aInsertChar A character to be input in an editor by the michael@0: * event. michael@0: * @param aKeyEvent The result -- a Gecko key event initialized michael@0: * from the native key event. This must be michael@0: * NS_KEY_PRESS event. michael@0: * @param aKbType A native Keyboard Type value. Typically, michael@0: * this is a result of ::LMGetKbdType(). michael@0: */ michael@0: void InitKeyPressEvent(NSEvent *aNativeKeyEvent, michael@0: char16_t aInsertChar, michael@0: WidgetKeyboardEvent& aKeyEvent, michael@0: UInt32 aKbType); michael@0: michael@0: bool GetBoolProperty(const CFStringRef aKey); michael@0: bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr); michael@0: bool GetStringProperty(const CFStringRef aKey, nsAString &aStr); michael@0: michael@0: TISInputSourceRef mInputSource; michael@0: TISInputSourceRef mKeyboardLayout; michael@0: CFArrayRef mInputSourceList; michael@0: const UCKeyboardLayout* mUCKeyboardLayout; michael@0: int8_t mIsRTL; michael@0: michael@0: bool mOverrideKeyboard; michael@0: }; michael@0: michael@0: /** michael@0: * TextInputHandlerBase is a base class of PluginTextInputHandler, michael@0: * IMEInputHandler and TextInputHandler. Utility methods should be implemented michael@0: * this level. michael@0: */ michael@0: michael@0: class TextInputHandlerBase michael@0: { michael@0: public: michael@0: nsrefcnt AddRef() michael@0: { michael@0: NS_PRECONDITION(int32_t(mRefCnt) >= 0, "mRefCnt is negative"); michael@0: ++mRefCnt; michael@0: NS_LOG_ADDREF(this, mRefCnt, "TextInputHandlerBase", sizeof(*this)); michael@0: return mRefCnt; michael@0: } michael@0: nsrefcnt Release() michael@0: { michael@0: NS_PRECONDITION(mRefCnt != 0, "mRefCnt is alrady zero"); michael@0: --mRefCnt; michael@0: NS_LOG_RELEASE(this, mRefCnt, "TextInputHandlerBase"); michael@0: if (mRefCnt == 0) { michael@0: mRefCnt = 1; /* stabilize */ michael@0: delete this; michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: /** michael@0: * DispatchEvent() dispatches aEvent on mWidget. michael@0: * michael@0: * @param aEvent An event which you want to dispatch. michael@0: * @return TRUE if the event is consumed by web contents michael@0: * or chrome contents. Otherwise, FALSE. michael@0: */ michael@0: bool DispatchEvent(WidgetGUIEvent& aEvent); michael@0: michael@0: /** michael@0: * SetSelection() dispatches NS_SELECTION_SET event for the aRange. michael@0: * michael@0: * @param aRange The range which will be selected. michael@0: * @return TRUE if setting selection is succeeded and michael@0: * the widget hasn't been destroyed. michael@0: * Otherwise, FALSE. michael@0: */ michael@0: bool SetSelection(NSRange& aRange); michael@0: michael@0: /** michael@0: * InitKeyEvent() initializes aKeyEvent for aNativeKeyEvent. michael@0: * michael@0: * @param aNativeKeyEvent A native key event for which you want to michael@0: * dispatch a Gecko key event. michael@0: * @param aKeyEvent The result -- a Gecko key event initialized michael@0: * from the native key event. michael@0: * @param aInsertString If caller expects that the event will cause michael@0: * a character to be input (say in an editor), michael@0: * the caller should set this. Otherwise, michael@0: * if caller sets null to this, this method will michael@0: * compute the character to be input from michael@0: * characters of aNativeKeyEvent. michael@0: */ michael@0: void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent, michael@0: const nsAString *aInsertString = nullptr); michael@0: michael@0: /** michael@0: * SynthesizeNativeKeyEvent() is an implementation of michael@0: * nsIWidget::SynthesizeNativeKeyEvent(). See the document in nsIWidget.h michael@0: * for the detail. michael@0: */ michael@0: nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, michael@0: int32_t aNativeKeyCode, michael@0: uint32_t aModifierFlags, michael@0: const nsAString& aCharacters, michael@0: const nsAString& aUnmodifiedCharacters); michael@0: michael@0: /** michael@0: * Utility method intended for testing. Attempts to construct a native key michael@0: * event that would have been generated during an actual key press. This michael@0: * *does not dispatch* the native event. Instead, it is attached to the michael@0: * |mNativeKeyEvent| field of the Gecko event that is passed in. michael@0: * @param aKeyEvent Gecko key event to attach the native event to michael@0: */ michael@0: NS_IMETHOD AttachNativeKeyEvent(WidgetKeyboardEvent& aKeyEvent); michael@0: michael@0: /** michael@0: * GetWindowLevel() returns the window level of current focused (in Gecko) michael@0: * window. E.g., if an element in XUL panel has focus, this returns michael@0: * the XUL panel's window level. michael@0: */ michael@0: NSInteger GetWindowLevel(); michael@0: michael@0: /** michael@0: * IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special michael@0: * Gecko keyCode. A key is "special" if it isn't used for text input. michael@0: * michael@0: * @param aNativeKeyCode A native keycode. michael@0: * @return If the keycode is mapped to a special key, michael@0: * TRUE. Otherwise, FALSE. michael@0: */ michael@0: static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode); michael@0: michael@0: michael@0: /** michael@0: * EnableSecureEventInput() and DisableSecureEventInput() wrap the Carbon michael@0: * Event Manager APIs with the same names. In addition they keep track of michael@0: * how many times we've called them (in the same process) -- unlike the michael@0: * Carbon Event Manager APIs, which only keep track of how many times they've michael@0: * been called from any and all processes. michael@0: * michael@0: * The Carbon Event Manager's IsSecureEventInputEnabled() returns whether michael@0: * secure event input mode is enabled (in any process). This class's michael@0: * IsSecureEventInputEnabled() returns whether we've made any calls to michael@0: * EnableSecureEventInput() that are not (yet) offset by the calls we've michael@0: * made to DisableSecureEventInput(). michael@0: */ michael@0: static void EnableSecureEventInput(); michael@0: static void DisableSecureEventInput(); michael@0: static bool IsSecureEventInputEnabled(); michael@0: michael@0: /** michael@0: * EnsureSecureEventInputDisabled() calls DisableSecureEventInput() until michael@0: * our call count becomes 0. michael@0: */ michael@0: static void EnsureSecureEventInputDisabled(); michael@0: michael@0: protected: michael@0: nsAutoRefCnt mRefCnt; michael@0: michael@0: public: michael@0: /** michael@0: * mWidget must not be destroyed without OnDestroyWidget being called. michael@0: * michael@0: * @param aDestroyingWidget Destroying widget. This might not be mWidget. michael@0: * @return This result doesn't have any meaning for michael@0: * callers. When aDstroyingWidget isn't the same michael@0: * as mWidget, FALSE. Then, inherited methods in michael@0: * sub classes should return from this method michael@0: * without cleaning up. michael@0: */ michael@0: virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget); michael@0: michael@0: protected: michael@0: // The creater of this instance and client. michael@0: // This must not be null after initialized until OnDestroyWidget() is called. michael@0: nsChildView* mWidget; // [WEAK] michael@0: michael@0: // The native view for mWidget. michael@0: // This view handles the actual text inputting. michael@0: NSView* mView; // [STRONG] michael@0: michael@0: TextInputHandlerBase(nsChildView* aWidget, NSView *aNativeView); michael@0: virtual ~TextInputHandlerBase(); michael@0: michael@0: bool Destroyed() { return !mWidget; } michael@0: michael@0: /** michael@0: * mCurrentKeyEvent indicates what key event we are handling. While michael@0: * handling a native keydown event, we need to store the event for insertText, michael@0: * doCommandBySelector and various action message handlers of NSResponder michael@0: * such as [NSResponder insertNewline:sender]. michael@0: */ michael@0: struct KeyEventState michael@0: { michael@0: // Handling native key event michael@0: NSEvent* mKeyEvent; michael@0: // Whether keydown event was consumed by web contents or chrome contents. michael@0: bool mKeyDownHandled; michael@0: // Whether keypress event was dispatched for mKeyEvent. michael@0: bool mKeyPressDispatched; michael@0: // Whether keypress event was consumed by web contents or chrome contents. michael@0: bool mKeyPressHandled; michael@0: // Whether the key event causes other key events via IME or something. michael@0: bool mCausedOtherKeyEvents; michael@0: michael@0: KeyEventState() : mKeyEvent(nullptr) michael@0: { michael@0: Clear(); michael@0: } michael@0: michael@0: KeyEventState(NSEvent* aNativeKeyEvent) : mKeyEvent(nullptr) michael@0: { michael@0: Clear(); michael@0: Set(aNativeKeyEvent); michael@0: } michael@0: michael@0: KeyEventState(const KeyEventState &aOther) : mKeyEvent(nullptr) michael@0: { michael@0: Clear(); michael@0: if (aOther.mKeyEvent) { michael@0: mKeyEvent = [aOther.mKeyEvent retain]; michael@0: } michael@0: mKeyDownHandled = aOther.mKeyDownHandled; michael@0: mKeyPressDispatched = aOther.mKeyPressDispatched; michael@0: mKeyPressHandled = aOther.mKeyPressHandled; michael@0: mCausedOtherKeyEvents = aOther.mCausedOtherKeyEvents; michael@0: } michael@0: michael@0: ~KeyEventState() michael@0: { michael@0: Clear(); michael@0: } michael@0: michael@0: void Set(NSEvent* aNativeKeyEvent) michael@0: { michael@0: NS_PRECONDITION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL"); michael@0: Clear(); michael@0: mKeyEvent = [aNativeKeyEvent retain]; michael@0: } michael@0: michael@0: void Clear() michael@0: { michael@0: if (mKeyEvent) { michael@0: [mKeyEvent release]; michael@0: mKeyEvent = nullptr; michael@0: } michael@0: mKeyDownHandled = false; michael@0: mKeyPressDispatched = false; michael@0: mKeyPressHandled = false; michael@0: mCausedOtherKeyEvents = false; michael@0: } michael@0: michael@0: bool IsDefaultPrevented() const michael@0: { michael@0: return mKeyDownHandled || mKeyPressHandled || mCausedOtherKeyEvents; michael@0: } michael@0: michael@0: bool CanDispatchKeyPressEvent() const michael@0: { michael@0: return !mKeyPressDispatched && !IsDefaultPrevented(); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Helper class for guaranteeing cleaning mCurrentKeyEvent michael@0: */ michael@0: class AutoKeyEventStateCleaner michael@0: { michael@0: public: michael@0: AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) : michael@0: mHandler(aHandler) michael@0: { michael@0: } michael@0: michael@0: ~AutoKeyEventStateCleaner() michael@0: { michael@0: mHandler->RemoveCurrentKeyEvent(); michael@0: } michael@0: private: michael@0: nsRefPtr mHandler; michael@0: }; michael@0: michael@0: /** michael@0: * mCurrentKeyEvents stores all key events which are being processed. michael@0: * When we call interpretKeyEvents, IME may generate other key events. michael@0: * mCurrentKeyEvents[0] is the latest key event. michael@0: */ michael@0: nsTArray mCurrentKeyEvents; michael@0: michael@0: /** michael@0: * mFirstKeyEvent must be used for first key event. This member prevents michael@0: * memory fragmentation for most key events. michael@0: */ michael@0: KeyEventState mFirstKeyEvent; michael@0: michael@0: /** michael@0: * PushKeyEvent() adds the current key event to mCurrentKeyEvents. michael@0: */ michael@0: KeyEventState* PushKeyEvent(NSEvent* aNativeKeyEvent) michael@0: { michael@0: uint32_t nestCount = mCurrentKeyEvents.Length(); michael@0: for (uint32_t i = 0; i < nestCount; i++) { michael@0: // When the key event is caused by another key event, all key events michael@0: // which are being handled should be marked as "consumed". michael@0: mCurrentKeyEvents[i]->mCausedOtherKeyEvents = true; michael@0: } michael@0: michael@0: KeyEventState* keyEvent = nullptr; michael@0: if (nestCount == 0) { michael@0: mFirstKeyEvent.Set(aNativeKeyEvent); michael@0: keyEvent = &mFirstKeyEvent; michael@0: } else { michael@0: keyEvent = new KeyEventState(aNativeKeyEvent); michael@0: } michael@0: return *mCurrentKeyEvents.AppendElement(keyEvent); michael@0: } michael@0: michael@0: /** michael@0: * RemoveCurrentKeyEvent() removes the current key event from michael@0: * mCurrentKeyEvents. michael@0: */ michael@0: void RemoveCurrentKeyEvent() michael@0: { michael@0: NS_ASSERTION(mCurrentKeyEvents.Length() > 0, michael@0: "RemoveCurrentKeyEvent() is called unexpectedly"); michael@0: KeyEventState* keyEvent = GetCurrentKeyEvent(); michael@0: mCurrentKeyEvents.RemoveElementAt(mCurrentKeyEvents.Length() - 1); michael@0: if (keyEvent == &mFirstKeyEvent) { michael@0: keyEvent->Clear(); michael@0: } else { michael@0: delete keyEvent; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * GetCurrentKeyEvent() returns current processing key event. michael@0: */ michael@0: KeyEventState* GetCurrentKeyEvent() michael@0: { michael@0: if (mCurrentKeyEvents.Length() == 0) { michael@0: return nullptr; michael@0: } michael@0: return mCurrentKeyEvents[mCurrentKeyEvents.Length() - 1]; michael@0: } michael@0: michael@0: /** michael@0: * IsPrintableChar() checks whether the unicode character is michael@0: * a non-printable ASCII character or not. Note that this returns michael@0: * TRUE even if aChar is a non-printable UNICODE character. michael@0: * michael@0: * @param aChar A unicode character. michael@0: * @return TRUE if aChar is a printable ASCII character michael@0: * or a unicode character. Otherwise, i.e, michael@0: * if aChar is a non-printable ASCII character, michael@0: * FALSE. michael@0: */ michael@0: static bool IsPrintableChar(char16_t aChar); michael@0: michael@0: /** michael@0: * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input. michael@0: * michael@0: * @param aKeyEvent A key event. michael@0: * @return TRUE if the key event causes text input. michael@0: * Otherwise, FALSE. michael@0: */ michael@0: static bool IsNormalCharInputtingEvent(const WidgetKeyboardEvent& aKeyEvent); michael@0: michael@0: /** michael@0: * IsModifierKey() checks whether the native keyCode is for a modifier key. michael@0: * michael@0: * @param aNativeKeyCode A native keyCode. michael@0: * @return TRUE if aNativeKeyCode is for a modifier key. michael@0: * Otherwise, FALSE. michael@0: */ michael@0: static bool IsModifierKey(UInt32 aNativeKeyCode); michael@0: michael@0: private: michael@0: struct KeyboardLayoutOverride { michael@0: int32_t mKeyboardLayout; michael@0: bool mOverrideEnabled; michael@0: michael@0: KeyboardLayoutOverride() : michael@0: mKeyboardLayout(0), mOverrideEnabled(false) michael@0: { michael@0: } michael@0: }; michael@0: michael@0: KeyboardLayoutOverride mKeyboardOverride; michael@0: michael@0: static int32_t sSecureEventInputCount; michael@0: }; michael@0: michael@0: /** michael@0: * PluginTextInputHandler handles text input events for plugins. michael@0: */ michael@0: michael@0: class PluginTextInputHandler : public TextInputHandlerBase michael@0: { michael@0: public: michael@0: michael@0: /** michael@0: * When starting complex text input for current event on plugin, this is michael@0: * called. See also the comment of StartComplexTextInputForCurrentEvent() of michael@0: * nsIPluginWidget. michael@0: */ michael@0: nsresult StartComplexTextInputForCurrentEvent() michael@0: { michael@0: mPluginComplexTextInputRequested = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * HandleKeyDownEventForPlugin() handles aNativeKeyEvent. michael@0: * michael@0: * @param aNativeKeyEvent A native NSKeyDown event. michael@0: */ michael@0: void HandleKeyDownEventForPlugin(NSEvent* aNativeKeyEvent); michael@0: michael@0: /** michael@0: * HandleKeyUpEventForPlugin() handles aNativeKeyEvent. michael@0: * michael@0: * @param aNativeKeyEvent A native NSKeyUp event. michael@0: */ michael@0: void HandleKeyUpEventForPlugin(NSEvent* aNativeKeyEvent); michael@0: michael@0: /** michael@0: * ConvertCocoaKeyEventToNPCocoaEvent() converts aCocoaEvent to NPCocoaEvent. michael@0: * michael@0: * @param aCocoaEvent A native key event. michael@0: * @param aPluginEvent The result. michael@0: */ michael@0: static void ConvertCocoaKeyEventToNPCocoaEvent(NSEvent* aCocoaEvent, michael@0: NPCocoaEvent& aPluginEvent); michael@0: michael@0: #ifndef __LP64__ michael@0: michael@0: /** michael@0: * InstallPluginKeyEventsHandler() is called when initializing process. michael@0: * RemovePluginKeyEventsHandler() is called when finalizing process. michael@0: * These methods initialize/finalize global resource for handling events for michael@0: * plugins. michael@0: */ michael@0: static void InstallPluginKeyEventsHandler(); michael@0: static void RemovePluginKeyEventsHandler(); michael@0: michael@0: /** michael@0: * This must be called before first key/IME event for plugins. michael@0: * This method initializes IMKInputSession methods swizzling. michael@0: */ michael@0: static void SwizzleMethods(); michael@0: michael@0: /** michael@0: * When a composition starts or finishes, this is called. michael@0: */ michael@0: void SetPluginTSMInComposition(bool aInComposition) michael@0: { michael@0: mPluginTSMInComposition = aInComposition; michael@0: } michael@0: michael@0: #endif // #ifndef __LP64__ michael@0: michael@0: protected: michael@0: bool mIgnoreNextKeyUpEvent; michael@0: michael@0: PluginTextInputHandler(nsChildView* aWidget, NSView *aNativeView); michael@0: ~PluginTextInputHandler(); michael@0: michael@0: private: michael@0: michael@0: #ifndef __LP64__ michael@0: TSMDocumentID mPluginTSMDoc; michael@0: michael@0: bool mPluginTSMInComposition; michael@0: #endif // #ifndef __LP64__ michael@0: michael@0: bool mPluginComplexTextInputRequested; michael@0: michael@0: /** michael@0: * DispatchCocoaNPAPITextEvent() dispatches a text event for Cocoa plugin. michael@0: * michael@0: * @param aString A string inputted by the dispatching event. michael@0: * @return TRUE if the dispatched event was consumed. michael@0: * Otherwise, FALSE. michael@0: */ michael@0: bool DispatchCocoaNPAPITextEvent(NSString* aString); michael@0: michael@0: /** michael@0: * Whether the plugin is in composition or not. michael@0: * On 32bit build, this returns the state of mPluginTSMInComposition. michael@0: * On 64bit build, this returns ComplexTextInputPanel's state. michael@0: * michael@0: * @return TRUE if plugin is in composition. Otherwise, michael@0: * FALSE. michael@0: */ michael@0: bool IsInPluginComposition(); michael@0: michael@0: #ifndef __LP64__ michael@0: michael@0: /** michael@0: * Create a TSM document for use with plugins, so that we can support IME in michael@0: * them. Once it's created, if need be (re)activate it. Some plugins (e.g. michael@0: * the Flash plugin running in Camino) don't create their own TSM document -- michael@0: * without which IME can't work. Others (e.g. the Flash plugin running in michael@0: * Firefox) create a TSM document that (somehow) makes the input window behave michael@0: * badly when it contains more than one kind of input (say Hiragana and michael@0: * Romaji). (We can't just use the per-NSView TSM documents that Cocoa michael@0: * provides (those created and managed by the NSTSMInputContext class) -- for michael@0: * some reason TSMProcessRawKeyEvent() doesn't work with them.) michael@0: */ michael@0: void ActivatePluginTSMDocument(); michael@0: michael@0: /** michael@0: * HandleCarbonPluginKeyEvent() handles the aKeyEvent. This is called by michael@0: * PluginKeyEventsHandler(). michael@0: * michael@0: * @param aKeyEvent A native Carbon event. michael@0: */ michael@0: void HandleCarbonPluginKeyEvent(EventRef aKeyEvent); michael@0: michael@0: /** michael@0: * Target for text services events sent as the result of calls made to michael@0: * TSMProcessRawKeyEvent() in HandleKeyDownEventForPlugin() when a plugin has michael@0: * the focus. The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based michael@0: * IME (which would otherwise interfere with our efforts) and allow Carbon- michael@0: * based IME to work in plugins (via the NPAPI). This strategy doesn't cause michael@0: * trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI michael@0: * to get their keyboard events and do their own Cocoa-based IME. michael@0: */ michael@0: static OSStatus PluginKeyEventsHandler(EventHandlerCallRef aHandlerRef, michael@0: EventRef aEvent, michael@0: void *aUserData); michael@0: michael@0: static EventHandlerRef sPluginKeyEventsHandler; michael@0: michael@0: #endif // #ifndef __LP64__ michael@0: }; michael@0: michael@0: /** michael@0: * IMEInputHandler manages: michael@0: * 1. The IME/keyboard layout statement of nsChildView. michael@0: * 2. The IME composition statement of nsChildView. michael@0: * And also provides the methods which controls the current IME transaction of michael@0: * the instance. michael@0: * michael@0: * Note that an nsChildView handles one or more NSView's events. E.g., even if michael@0: * a text editor on XUL panel element, the input events handled on the parent michael@0: * (or its ancestor) widget handles it (the native focus is set to it). The michael@0: * actual focused view is notified by OnFocusChangeInGecko. michael@0: */ michael@0: michael@0: class IMEInputHandler : public PluginTextInputHandler michael@0: { michael@0: public: michael@0: virtual bool OnDestroyWidget(nsChildView* aDestroyingWidget); michael@0: michael@0: virtual void OnFocusChangeInGecko(bool aFocus); michael@0: michael@0: void OnSelectionChange() { mSelectedRange.location = NSNotFound; } michael@0: michael@0: /** michael@0: * DispatchTextEvent() dispatches a text event on mWidget. michael@0: * michael@0: * @param aText User text input. michael@0: * @param aAttrString An NSAttributedString instance which indicates michael@0: * current composition string. michael@0: * @param aSelectedRange Current selected range (or caret position). michael@0: * @param aDoCommit TRUE if the composition string should be michael@0: * committed. Otherwise, FALSE. michael@0: */ michael@0: bool DispatchTextEvent(const nsString& aText, michael@0: NSAttributedString* aAttrString, michael@0: NSRange& aSelectedRange, michael@0: bool aDoCommit); michael@0: michael@0: /** michael@0: * SetMarkedText() is a handler of setMarkedText of NSTextInput. michael@0: * michael@0: * @param aAttrString This mut be an instance of NSAttributedString. michael@0: * If the aString parameter to michael@0: * [ChildView setMarkedText:setSelectedRange:] michael@0: * isn't an instance of NSAttributedString, michael@0: * create an NSAttributedString from it and pass michael@0: * that instead. michael@0: * @param aSelectedRange Current selected range (or caret position). michael@0: * @param aReplacementRange The range which will be replaced with the michael@0: * aAttrString instead of current marked range. michael@0: */ michael@0: void SetMarkedText(NSAttributedString* aAttrString, michael@0: NSRange& aSelectedRange, michael@0: NSRange* aReplacementRange = nullptr); michael@0: michael@0: /** michael@0: * ConversationIdentifier() returns an ID for the current editor. The ID is michael@0: * guaranteed to be unique among currently existing editors. But it might be michael@0: * the same as the ID of an editor that has already been destroyed. michael@0: * michael@0: * @return An identifier of current focused editor. michael@0: */ michael@0: NSInteger ConversationIdentifier(); michael@0: michael@0: /** michael@0: * GetAttributedSubstringFromRange() returns an NSAttributedString instance michael@0: * which is allocated as autorelease for aRange. michael@0: * michael@0: * @param aRange The range of string which you want. michael@0: * @param aActualRange The actual range of the result. michael@0: * @return The string in aRange. If the string is empty, michael@0: * this returns nil. If succeeded, this returns michael@0: * an instance which is allocated as autorelease. michael@0: * If this has some troubles, returns nil. michael@0: */ michael@0: NSAttributedString* GetAttributedSubstringFromRange( michael@0: NSRange& aRange, michael@0: NSRange* aActualRange = nullptr); michael@0: michael@0: /** michael@0: * SelectedRange() returns current selected range. michael@0: * michael@0: * @return If an editor has focus, this returns selection michael@0: * range in the editor. Otherwise, this returns michael@0: * selection range in the focused document. michael@0: */ michael@0: NSRange SelectedRange(); michael@0: michael@0: /** michael@0: * FirstRectForCharacterRange() returns first *character* rect in the range. michael@0: * Cocoa needs the first line rect in the range, but we cannot compute it michael@0: * on current implementation. michael@0: * michael@0: * @param aRange A range of text to examine. Its position is michael@0: * an offset from the beginning of the focused michael@0: * editor or document. michael@0: * @param aActualRange If this is not null, this returns the actual michael@0: * range used for computing the result. michael@0: * @return An NSRect containing the first character in michael@0: * aRange, in screen coordinates. michael@0: * If the length of aRange is 0, the width will michael@0: * be 0. michael@0: */ michael@0: NSRect FirstRectForCharacterRange(NSRange& aRange, michael@0: NSRange* aActualRange = nullptr); michael@0: michael@0: /** michael@0: * CharacterIndexForPoint() returns an offset of a character at aPoint. michael@0: * XXX This isn't implemented, always returns 0. michael@0: * michael@0: * @param The point in screen coordinates. michael@0: * @return The offset of the character at aPoint from michael@0: * the beginning of the focused editor or michael@0: * document. michael@0: */ michael@0: NSUInteger CharacterIndexForPoint(NSPoint& aPoint); michael@0: michael@0: /** michael@0: * GetValidAttributesForMarkedText() returns attributes which we support. michael@0: * michael@0: * @return Always empty array for now. michael@0: */ michael@0: NSArray* GetValidAttributesForMarkedText(); michael@0: michael@0: bool HasMarkedText(); michael@0: NSRange MarkedRange(); michael@0: michael@0: bool IsIMEComposing() { return mIsIMEComposing; } michael@0: bool IsIMEOpened(); michael@0: bool IsIMEEnabled() { return mIsIMEEnabled; } michael@0: bool IsASCIICapableOnly() { return mIsASCIICapableOnly; } michael@0: bool IgnoreIMECommit() { return mIgnoreIMECommit; } michael@0: michael@0: bool IgnoreIMEComposition() michael@0: { michael@0: // Ignore the IME composition events when we're pending to discard the michael@0: // composition and we are not to handle the IME composition now. michael@0: return (mPendingMethods & kDiscardIMEComposition) && michael@0: (mIsInFocusProcessing || !IsFocused()); michael@0: } michael@0: michael@0: void CommitIMEComposition(); michael@0: void CancelIMEComposition(); michael@0: michael@0: void EnableIME(bool aEnableIME); michael@0: void SetIMEOpenState(bool aOpen); michael@0: void SetASCIICapableOnly(bool aASCIICapableOnly); michael@0: michael@0: bool IsFocused(); michael@0: michael@0: static CFArrayRef CreateAllIMEModeList(); michael@0: static void DebugPrintAllIMEModes(); michael@0: michael@0: // Don't use ::TSMGetActiveDocument() API directly, the document may not michael@0: // be what you want. michael@0: static TSMDocumentID GetCurrentTSMDocumentID(); michael@0: michael@0: protected: michael@0: // We cannot do some jobs in the given stack by some reasons. michael@0: // Following flags and the timer provide the execution pending mechanism, michael@0: // See the comment in nsCocoaTextInputHandler.mm. michael@0: nsCOMPtr mTimer; michael@0: enum { michael@0: kNotifyIMEOfFocusChangeInGecko = 1, michael@0: kDiscardIMEComposition = 2, michael@0: kSyncASCIICapableOnly = 4 michael@0: }; michael@0: uint32_t mPendingMethods; michael@0: michael@0: IMEInputHandler(nsChildView* aWidget, NSView *aNativeView); michael@0: virtual ~IMEInputHandler(); michael@0: michael@0: void ResetTimer(); michael@0: michael@0: virtual void ExecutePendingMethods(); michael@0: michael@0: /** michael@0: * InsertTextAsCommittingComposition() commits current composition. If there michael@0: * is no composition, this starts a composition and commits it immediately. michael@0: * michael@0: * @param aAttrString A string which is committed. michael@0: * @param aReplacementRange The range which will be replaced with the michael@0: * aAttrString instead of current selection. michael@0: */ michael@0: void InsertTextAsCommittingComposition(NSAttributedString* aAttrString, michael@0: NSRange* aReplacementRange); michael@0: michael@0: private: michael@0: // If mIsIMEComposing is true, the composition string is stored here. michael@0: NSString* mIMECompositionString; michael@0: // mLastDispatchedCompositionString stores the lastest dispatched composition michael@0: // string by compositionupdate event. michael@0: nsString mLastDispatchedCompositionString; michael@0: michael@0: NSRange mMarkedRange; michael@0: NSRange mSelectedRange; michael@0: michael@0: bool mIsIMEComposing; michael@0: bool mIsIMEEnabled; michael@0: bool mIsASCIICapableOnly; michael@0: bool mIgnoreIMECommit; michael@0: // This flag is enabled by OnFocusChangeInGecko, and will be cleared by michael@0: // ExecutePendingMethods. When this is true, IsFocus() returns TRUE. At michael@0: // that time, the focus processing in Gecko might not be finished yet. So, michael@0: // you cannot use WidgetQueryContentEvent or something. michael@0: bool mIsInFocusProcessing; michael@0: bool mIMEHasFocus; michael@0: michael@0: void KillIMEComposition(); michael@0: void SendCommittedText(NSString *aString); michael@0: void OpenSystemPreferredLanguageIME(); michael@0: michael@0: // Pending methods michael@0: void NotifyIMEOfFocusChangeInGecko(); michael@0: void DiscardIMEComposition(); michael@0: void SyncASCIICapableOnly(); michael@0: michael@0: static bool sStaticMembersInitialized; michael@0: static CFStringRef sLatestIMEOpenedModeInputSourceID; michael@0: static void InitStaticMembers(); michael@0: static void OnCurrentTextInputSourceChange(CFNotificationCenterRef aCenter, michael@0: void* aObserver, michael@0: CFStringRef aName, michael@0: const void* aObject, michael@0: CFDictionaryRef aUserInfo); michael@0: michael@0: static void FlushPendingMethods(nsITimer* aTimer, void* aClosure); michael@0: michael@0: /** michael@0: * ConvertToTextRangeStyle converts the given native underline style to michael@0: * our defined text range type. michael@0: * michael@0: * @param aUnderlineStyle NSUnderlineStyleSingle or michael@0: * NSUnderlineStyleThick. michael@0: * @param aSelectedRange Current selected range (or caret position). michael@0: * @return NS_TEXTRANGE_*. michael@0: */ michael@0: uint32_t ConvertToTextRangeType(uint32_t aUnderlineStyle, michael@0: NSRange& aSelectedRange); michael@0: michael@0: /** michael@0: * GetRangeCount() computes the range count of aAttrString. michael@0: * michael@0: * @param aAttrString An NSAttributedString instance whose number of michael@0: * NSUnderlineStyleAttributeName ranges you with michael@0: * to know. michael@0: * @return The count of NSUnderlineStyleAttributeName michael@0: * ranges in aAttrString. michael@0: */ michael@0: uint32_t GetRangeCount(NSAttributedString *aString); michael@0: michael@0: /** michael@0: * CreateTextRangeArray() returns text ranges for clauses and/or caret. michael@0: * michael@0: * @param aAttrString An NSAttributedString instance which indicates michael@0: * current composition string. michael@0: * @param aSelectedRange Current selected range (or caret position). michael@0: * @return The result is set to the michael@0: * NSUnderlineStyleAttributeName ranges in michael@0: * aAttrString. michael@0: */ michael@0: already_AddRefed michael@0: CreateTextRangeArray(NSAttributedString *aAttrString, michael@0: NSRange& aSelectedRange); michael@0: michael@0: /** michael@0: * InitCompositionEvent() initializes aCompositionEvent. michael@0: * michael@0: * @param aCompositionEvent A composition event which you want to michael@0: * initialize. michael@0: */ michael@0: void InitCompositionEvent(WidgetCompositionEvent& aCompositionEvent); michael@0: michael@0: /** michael@0: * When a composition starts, OnStartIMEComposition() is called. michael@0: */ michael@0: void OnStartIMEComposition(); michael@0: michael@0: /** michael@0: * When a composition is updated, OnUpdateIMEComposition() is called. michael@0: */ michael@0: void OnUpdateIMEComposition(NSString* aIMECompositionString); michael@0: michael@0: /** michael@0: * When a composition is finished, OnEndIMEComposition() is called. michael@0: */ michael@0: void OnEndIMEComposition(); michael@0: michael@0: // The focused IME handler. Please note that the handler might lost the michael@0: // actual focus by deactivating the application. If we are active, this michael@0: // must have the actual focused handle. michael@0: // We cannot access to the NSInputManager during we aren't active, so, the michael@0: // focused handler can have an IME transaction even if we are deactive. michael@0: static IMEInputHandler* sFocusedIMEHandler; michael@0: }; michael@0: michael@0: /** michael@0: * TextInputHandler implements the NSTextInput protocol. michael@0: */ michael@0: class TextInputHandler : public IMEInputHandler michael@0: { michael@0: public: michael@0: static NSUInteger sLastModifierState; michael@0: michael@0: static CFArrayRef CreateAllKeyboardLayoutList(); michael@0: static void DebugPrintAllKeyboardLayouts(); michael@0: michael@0: TextInputHandler(nsChildView* aWidget, NSView *aNativeView); michael@0: virtual ~TextInputHandler(); michael@0: michael@0: /** michael@0: * KeyDown event handler. michael@0: * michael@0: * @param aNativeEvent A native NSKeyDown event. michael@0: * @return TRUE if the event is consumed by web contents michael@0: * or chrome contents. Otherwise, FALSE. michael@0: */ michael@0: bool HandleKeyDownEvent(NSEvent* aNativeEvent); michael@0: michael@0: /** michael@0: * KeyUp event handler. michael@0: * michael@0: * @param aNativeEvent A native NSKeyUp event. michael@0: */ michael@0: void HandleKeyUpEvent(NSEvent* aNativeEvent); michael@0: michael@0: /** michael@0: * FlagsChanged event handler. michael@0: * michael@0: * @param aNativeEvent A native NSFlagsChanged event. michael@0: */ michael@0: void HandleFlagsChanged(NSEvent* aNativeEvent); michael@0: michael@0: /** michael@0: * Insert the string to content. I.e., this is a text input event handler. michael@0: * If this is called during keydown event handling, this may dispatch a michael@0: * NS_KEY_PRESS event. If this is called during composition, this commits michael@0: * the composition by the aAttrString. michael@0: * michael@0: * @param aAttrString An inserted string. michael@0: * @param aReplacementRange The range which will be replaced with the michael@0: * aAttrString instead of current selection. michael@0: */ michael@0: void InsertText(NSAttributedString *aAttrString, michael@0: NSRange* aReplacementRange = nullptr); michael@0: michael@0: /** michael@0: * doCommandBySelector event handler. michael@0: * michael@0: * @param aSelector A selector of the command. michael@0: * @return TRUE if the command is consumed. Otherwise, michael@0: * FALSE. michael@0: */ michael@0: bool DoCommandBySelector(const char* aSelector); michael@0: michael@0: /** michael@0: * KeyPressWasHandled() checks whether keypress event was handled or not. michael@0: * michael@0: * @return TRUE if keypress event for latest native key michael@0: * event was handled. Otherwise, FALSE. michael@0: * If this handler isn't handling any key events, michael@0: * always returns FALSE. michael@0: */ michael@0: bool KeyPressWasHandled() michael@0: { michael@0: KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); michael@0: return currentKeyEvent && currentKeyEvent->mKeyPressHandled; michael@0: } michael@0: michael@0: protected: michael@0: // Stores the association of device dependent modifier flags with a modifier michael@0: // keyCode. Being device dependent, this association may differ from one kind michael@0: // of hardware to the next. michael@0: struct ModifierKey michael@0: { michael@0: NSUInteger flags; michael@0: unsigned short keyCode; michael@0: michael@0: ModifierKey(NSUInteger aFlags, unsigned short aKeyCode) : michael@0: flags(aFlags), keyCode(aKeyCode) michael@0: { michael@0: } michael@0: michael@0: NSUInteger GetDeviceDependentFlags() const michael@0: { michael@0: return (flags & ~NSDeviceIndependentModifierFlagsMask); michael@0: } michael@0: michael@0: NSUInteger GetDeviceIndependentFlags() const michael@0: { michael@0: return (flags & NSDeviceIndependentModifierFlagsMask); michael@0: } michael@0: }; michael@0: typedef nsTArray ModifierKeyArray; michael@0: ModifierKeyArray mModifierKeys; michael@0: michael@0: /** michael@0: * GetModifierKeyForNativeKeyCode() returns the stored ModifierKey for michael@0: * the key. michael@0: */ michael@0: const ModifierKey* michael@0: GetModifierKeyForNativeKeyCode(unsigned short aKeyCode) const; michael@0: michael@0: /** michael@0: * GetModifierKeyForDeviceDependentFlags() returns the stored ModifierKey for michael@0: * the device dependent flags. michael@0: */ michael@0: const ModifierKey* michael@0: GetModifierKeyForDeviceDependentFlags(NSUInteger aFlags) const; michael@0: michael@0: /** michael@0: * DispatchKeyEventForFlagsChanged() dispatches keydown event or keyup event michael@0: * for the aNativeEvent. michael@0: * michael@0: * @param aNativeEvent A native flagschanged event which you want to michael@0: * dispatch our key event for. michael@0: * @param aDispatchKeyDown TRUE if you want to dispatch a keydown event. michael@0: * Otherwise, i.e., to dispatch keyup event, michael@0: * FALSE. michael@0: */ michael@0: void DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent, michael@0: bool aDispatchKeyDown); michael@0: }; michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: michael@0: #endif // TextInputHandler_h_