diff --git a/widget/src/cocoa/TextInputHandler.h b/widget/src/cocoa/TextInputHandler.h index d5c3f6a4bc89..ee2c79d68aff 100644 --- a/widget/src/cocoa/TextInputHandler.h +++ b/widget/src/cocoa/TextInputHandler.h @@ -407,29 +407,6 @@ public: const nsAString& aCharacters, const nsAString& aUnmodifiedCharacters); - /** - * IsPrintableChar() checks whether the unicode character is - * a non-printable ASCII character or not. Note that this returns - * TRUE even if aChar is a non-printable UNICODE character. - * - * @param aChar A unicode character. - * @return TRUE if aChar is a printable ASCII character - * or a unicode character. Otherwise, i.e, - * if aChar is a non-printable ASCII character, - * FALSE. - */ - static PRBool IsPrintableChar(PRUnichar aChar); - - /** - * ComputeGeckoKeyCodeFromChar() computes Gecko defined keyCode value from - * aChar. If aChar is not an ASCII character, this always returns FALSE. - * - * @param aChar A unicode character. - * @return A Gecko defined keyCode. Or zero if aChar - * is a unicode character. - */ - static PRUint32 ComputeGeckoKeyCodeFromChar(PRUnichar aChar); - /** * ComputeGeckoKeyCode() computes Gecko defined keyCode from the native * keyCode or the characters. @@ -459,24 +436,6 @@ public: */ static PRBool IsSpecialGeckoKey(UInt32 aNativeKeyCode); - /** - * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input. - * - * @param aKeyEvent A key event. - * @return TRUE if the key event causes text input. - * Otherwise, FALSE. - */ - static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aKeyEvent); - - /** - * IsModifierKey() checks whether the native keyCode is for a modifier key. - * - * @param aNativeKeyCode A native keyCode. - * @return TRUE if aNativeKeyCode is for a modifier key. - * Otherwise, FALSE. - */ - static PRBool IsModifierKey(UInt32 aNativeKeyCode); - protected: nsAutoRefCnt mRefCnt; @@ -507,6 +466,121 @@ protected: PRBool Destroyed() { return !mWidget; } + /** + * mCurrentKeyEvent indicates what key event we are handling. While + * handling a native keydown event, we need to store the event for insertText, + * doCommandBySelector and various action message handlers of NSResponder + * such as [NSResponder insertNewline:sender]. + */ + struct KeyEventState + { + // Handling native key event + NSEvent* mKeyEvent; + // Whether keydown event was consumed by web contents or chrome contents. + PRPackedBool mKeyDownHandled; + // Whether keypress event was dispatched for mKeyEvent. + PRPackedBool mKeyPressDispatched; + // Whether keypress event was consumed by web contents or chrome contents. + PRPackedBool mKeyPressHandled; + + KeyEventState() : mKeyEvent(nsnull) + { + Clear(); + } + + ~KeyEventState() + { + Clear(); + } + + void Set(NSEvent* aNativeKeyEvent) + { + NS_PRECONDITION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL"); + Clear(); + mKeyEvent = [aNativeKeyEvent retain]; + } + + void Clear() + { + if (mKeyEvent) { + [mKeyEvent release]; + mKeyEvent = nsnull; + } + mKeyDownHandled = PR_FALSE; + mKeyPressDispatched = PR_FALSE; + mKeyPressHandled = PR_FALSE; + } + + PRBool KeyDownOrPressHandled() + { + return mKeyDownHandled || mKeyPressHandled; + } + }; + + /** + * Helper class for guaranteeing cleaning mCurrentKeyEvent + */ + class AutoKeyEventStateCleaner + { + public: + AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) : + mHandler(aHandler) + { + } + + ~AutoKeyEventStateCleaner() + { + mHandler->mCurrentKeyEvent.Clear(); + } + private: + TextInputHandlerBase* mHandler; + }; + + // XXX If keydown event was nested, the key event is overwritten by newer + // event. This is wrong behavior. Some IMEs are making such situation. + KeyEventState mCurrentKeyEvent; + + /** + * IsPrintableChar() checks whether the unicode character is + * a non-printable ASCII character or not. Note that this returns + * TRUE even if aChar is a non-printable UNICODE character. + * + * @param aChar A unicode character. + * @return TRUE if aChar is a printable ASCII character + * or a unicode character. Otherwise, i.e, + * if aChar is a non-printable ASCII character, + * FALSE. + */ + static PRBool IsPrintableChar(PRUnichar aChar); + + /** + * ComputeGeckoKeyCodeFromChar() computes Gecko defined keyCode value from + * aChar. If aChar is not an ASCII character, this always returns FALSE. + * + * @param aChar A unicode character. + * @return A Gecko defined keyCode. Or zero if aChar + * is a unicode character. + */ + static PRUint32 ComputeGeckoKeyCodeFromChar(PRUnichar aChar); + + /** + * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input. + * + * @param aKeyEvent A key event. + * @return TRUE if the key event causes text input. + * Otherwise, FALSE. + */ + static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aKeyEvent); + + /** + * IsModifierKey() checks whether the native keyCode is for a modifier key. + * + * @param aNativeKeyCode A native keyCode. + * @return TRUE if aNativeKeyCode is for a modifier key. + * Otherwise, FALSE. + */ + static PRBool IsModifierKey(UInt32 aNativeKeyCode); + private: struct KeyboardLayoutOverride { PRInt32 mKeyboardLayout; @@ -527,9 +601,46 @@ private: class PluginTextInputHandler : public TextInputHandlerBase { +public: + +#ifndef NP_NO_CARBON + /** + * ConvertCocoaKeyEventToCarbonEvent() converts aCocoaKeyEvent to + * aCarbonKeyEvent. + * + * @param aCocoaKeyEvent A Cocoa key event. + * @param aCarbonKeyEvent Converted Carbon event from aCocoaEvent. + * @param aMakeKeyDownEventIfNSFlagsChanged + * If aCocoaKeyEvent isn't NSFlagsChanged event, + * this is ignored. Otherwise, i.e., if + * aCocoaKeyEvent is NSFlagsChanged event, + * set TRUE if you need a keydown event. + * Otherwise, Set FALSE for a keyup event. + */ + static void ConvertCocoaKeyEventToCarbonEvent( + NSEvent* aCocoaKeyEvent, + EventRecord& aCarbonKeyEvent, + PRBool aMakeKeyDownEventIfNSFlagsChanged = PR_FALSE); +#endif // NP_NO_CARBON + protected: PluginTextInputHandler(nsChildView* aWidget, NSView *aNativeView); ~PluginTextInputHandler(); + +#ifndef NP_NO_CARBON + + /** + * ConvertUnicodeToCharCode() converts aUnichar to native encoded string. + * + * @param aUniChar A unicode character. + * @param aOutChar Native encoded string for aUniChar. + * @return TRUE if the converting succeeded. + * Otherwise, FALSE. + */ + static PRBool ConvertUnicodeToCharCode(PRUnichar aUniChar, + unsigned char* aOutChar); + +#endif // NP_NO_CARBON }; /** @@ -581,14 +692,6 @@ public: void SetMarkedText(NSAttributedString* aAttrString, NSRange& aSelectedRange); - /** - * InsertTextAsCommittingComposition() commits current composition. If there - * is no composition, this starts a composition and commits it immediately. - * - * @param aAttrString A string which is committed. - */ - void InsertTextAsCommittingComposition(NSAttributedString* aAttrString); - /** * ConversationIdentifier() returns an ID for the current editor. The ID is * guaranteed to be unique among currently existing editors. But it might be @@ -713,6 +816,14 @@ protected: virtual void ExecutePendingMethods(); + /** + * InsertTextAsCommittingComposition() commits current composition. If there + * is no composition, this starts a composition and commits it immediately. + * + * @param aAttrString A string which is committed. + */ + void InsertTextAsCommittingComposition(NSAttributedString* aAttrString); + private: // If mIsIMEComposing is true, the composition string is stored here. NSString* mIMECompositionString; @@ -831,6 +942,39 @@ public: TextInputHandler(nsChildView* aWidget, NSView *aNativeView); virtual ~TextInputHandler(); + + /** + * KeyDown event handler. + * + * @param aNativeEvent A native keydown event which you want to + * handle. + * @return TRUE if the event is consumed by web contents + * or chrome contents. Otherwise, FALSE. + */ + PRBool HandleKeyDownEvent(NSEvent* aNativeEvent); + + /** + * Insert the string to content. I.e., this is a text input event handler. + * If this is called during keydown event handling, this may dispatch a + * NS_KEY_PRESS event. If this is called during composition, this commits + * the composition by the aAttrString. + * + * @param aAttrString An inserted string. + */ + void InsertText(NSAttributedString *aAttrString); + + /** + * KeyPressWasHandled() checks whether keypress event was handled or not. + * + * @return TRUE if keypress event for latest native key + * event was handled. Otherwise, FALSE. + * If this handler isn't handling any key events, + * always returns FALSE. + */ + PRBool KeyPressWasHandled() + { + return mCurrentKeyEvent.mKeyPressHandled; + } }; } // namespace widget diff --git a/widget/src/cocoa/TextInputHandler.mm b/widget/src/cocoa/TextInputHandler.mm index 3fb9cb48e0b5..9e7c3a53f20d 100644 --- a/widget/src/cocoa/TextInputHandler.mm +++ b/widget/src/cocoa/TextInputHandler.mm @@ -804,6 +804,227 @@ TextInputHandler::~TextInputHandler() [mView uninstallTextInputHandler]; } +PRBool +TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + if (Destroyed()) { + return PR_FALSE; + } + + nsRefPtr kungFuDeathGrip(mWidget); + + mCurrentKeyEvent.Set(aNativeEvent); + AutoKeyEventStateCleaner remover(this); + + BOOL nonDeadKeyPress = [[aNativeEvent characters] length] > 0; + if (nonDeadKeyPress && !IsIMEComposing()) { + NSResponder* firstResponder = [[mView window] firstResponder]; + + nsKeyEvent keydownEvent(PR_TRUE, NS_KEY_DOWN, mWidget); + InitKeyEvent(aNativeEvent, keydownEvent); + +#ifndef NP_NO_CARBON + EventRecord carbonEvent; + if ([mView pluginEventModel] == NPEventModelCarbon) { + ConvertCocoaKeyEventToCarbonEvent(aNativeEvent, carbonEvent, PR_TRUE); + keydownEvent.pluginEvent = &carbonEvent; + } +#endif // #ifndef NP_NO_CARBON + + mCurrentKeyEvent.mKeyDownHandled = DispatchEvent(keydownEvent); + if (Destroyed()) { + return mCurrentKeyEvent.KeyDownOrPressHandled(); + } + + // The key down event may have shifted the focus, in which + // case we should not fire the key press. + // XXX This is a special code only on Cocoa widget, why is this needed? + if (firstResponder != [[mView window] firstResponder]) { + return mCurrentKeyEvent.KeyDownOrPressHandled(); + } + + // If this is the context menu key command, send a context menu key event. + NSUInteger modifierFlags = + [aNativeEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask; + if (modifierFlags == NSControlKeyMask && + [[aNativeEvent charactersIgnoringModifiers] isEqualToString:@" "]) { + nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, + [mView widget], nsMouseEvent::eReal, + nsMouseEvent::eContextMenuKey); + contextMenuEvent.isShift = contextMenuEvent.isControl = + contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE; + + PRBool cmEventHandled = DispatchEvent(contextMenuEvent); + [mView maybeInitContextMenuTracking]; + // Bail, there is nothing else to do here. + return (cmEventHandled || mCurrentKeyEvent.KeyDownOrPressHandled()); + } + + nsKeyEvent keypressEvent(PR_TRUE, NS_KEY_PRESS, mWidget); + InitKeyEvent(aNativeEvent, keypressEvent); + + // if this is a non-letter keypress, or the control key is down, + // dispatch the keydown to gecko, so that we trap delete, + // control-letter combinations etc before Cocoa tries to use + // them for keybindings. + // XXX This is wrong. IME may be handle the non-letter keypress event as + // its owning shortcut key. See bug 477291. + if ((!keypressEvent.isChar || keypressEvent.isControl) && + !IsIMEComposing()) { + if (mCurrentKeyEvent.mKeyDownHandled) { + keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; + } + mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent); + mCurrentKeyEvent.mKeyPressDispatched = PR_TRUE; + if (Destroyed()) { + return mCurrentKeyEvent.KeyDownOrPressHandled(); + } + } + } + + // Let Cocoa interpret the key events, caching IsIMEComposing first. + PRBool wasComposing = IsIMEComposing(); + PRBool interpretKeyEventsCalled = PR_FALSE; + if (IsIMEEnabled() || IsASCIICapableOnly()) { + [mView interpretKeyEvents:[NSArray arrayWithObject:aNativeEvent]]; + interpretKeyEventsCalled = PR_TRUE; + } + + if (Destroyed()) { + return mCurrentKeyEvent.KeyDownOrPressHandled(); + } + + if (!mCurrentKeyEvent.mKeyPressDispatched && nonDeadKeyPress && + !wasComposing && !IsIMEComposing()) { + nsKeyEvent keypressEvent(PR_TRUE, NS_KEY_PRESS, mWidget); + InitKeyEvent(aNativeEvent, keypressEvent); + + // If we called interpretKeyEvents and this isn't normal character input + // then IME probably ate the event for some reason. We do not want to + // send a key press event in that case. + // TODO: + // There are some other cases which IME eats the current event. + // 1. If key events were nested during calling interpretKeyEvents, it means + // that IME did something. Then, we should do nothing. + // 2. If one or more commands are called like "deleteBackward", we should + // dispatch keypress event at that time. Note that the command may have + // been a converted or generated action by IME. Then, we shouldn't do + // our default action for this key. + if (!(interpretKeyEventsCalled && + IsNormalCharInputtingEvent(keypressEvent))) { + if (mCurrentKeyEvent.mKeyDownHandled) { + keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; + } + mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent); + } + } + + // Note: mWidget might have become null here. Don't count on it from here on. + + return mCurrentKeyEvent.KeyDownOrPressHandled(); + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE); +} + +void +TextInputHandler::InsertText(NSAttributedString *aAttrString) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + if (Destroyed()) { + return; + } + + if (IgnoreIMEComposition()) { + return; + } + + nsString str; + GetStringForNSString([aAttrString string], str); + if (!IsIMEComposing() && str.IsEmpty()) { + return; // nothing to do + } + + if (str.Length() != 1 || IsIMEComposing()) { + InsertTextAsCommittingComposition(aAttrString); + return; + } + + // Don't let the same event be fired twice when hitting + // enter/return! (Bug 420502) + if (mCurrentKeyEvent.mKeyPressDispatched) { + return; + } + + nsRefPtr kungFuDeathGrip(mWidget); + + // Dispatch keypress event with char instead of textEvent + nsKeyEvent keypressEvent(PR_TRUE, NS_KEY_PRESS, mWidget); + keypressEvent.time = PR_IntervalNow(); + keypressEvent.charCode = str.CharAt(0); + keypressEvent.keyCode = 0; + keypressEvent.isChar = PR_TRUE; + + // Don't set other modifiers from the current event, because here in + // -insertText: they've already been taken into account in creating + // the input string. + + // create event for use by plugins +#ifndef NP_NO_CARBON + EventRecord carbonEvent; +#endif // #ifndef NP_NO_CARBON + + if (mCurrentKeyEvent.mKeyEvent) { + NSEvent* keyEvent = mCurrentKeyEvent.mKeyEvent; + + // XXX The ASCII characters inputting mode of egbridge (Japanese IME) + // might send the keyDown event with wrong keyboard layout if other + // keyboard layouts are already loaded. In that case, the native event + // doesn't match to this gecko event... +#ifndef NP_NO_CARBON + if ([mView pluginEventModel] == NPEventModelCarbon) { + ConvertCocoaKeyEventToCarbonEvent(keyEvent, carbonEvent, PR_TRUE); + keypressEvent.pluginEvent = &carbonEvent; + } +#endif // #ifndef NP_NO_CARBON + + if (mCurrentKeyEvent.mKeyDownHandled) { + keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; + } + + keypressEvent.isShift = ([keyEvent modifierFlags] & NSShiftKeyMask) != 0; + if (!IsPrintableChar(keypressEvent.charCode)) { + keypressEvent.keyCode = + ComputeGeckoKeyCode([keyEvent keyCode], + [keyEvent charactersIgnoringModifiers]); + keypressEvent.charCode = 0; + } + } else { + // Note that insertText is not called only at key pressing. + if (!IsPrintableChar(keypressEvent.charCode)) { + keypressEvent.keyCode = + ComputeGeckoKeyCodeFromChar(keypressEvent.charCode); + keypressEvent.charCode = 0; + } + } + + // TODO: + // If mCurrentKeyEvent.mKeyEvent is null and when we implement textInput + // event of DOM3 Events, we should dispatch it instead of keypress event. + PRBool keyPressHandled = DispatchEvent(keypressEvent); + + // Note: mWidget might have become null here. Don't count on it from here on. + + if (mCurrentKeyEvent.mKeyEvent) { + mCurrentKeyEvent.mKeyPressHandled = keyPressHandled; + mCurrentKeyEvent.mKeyPressDispatched = PR_TRUE; + } + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + #pragma mark - @@ -949,6 +1170,7 @@ IMEInputHandler::GetCurrentTSMDocumentID() return ::TSMGetActiveDocument(); } + #pragma mark - @@ -2015,6 +2237,103 @@ PluginTextInputHandler::~PluginTextInputHandler() { } +#ifndef NP_NO_CARBON + +/* static */ PRBool +PluginTextInputHandler::ConvertUnicodeToCharCode(PRUnichar aUniChar, + unsigned char* aOutChar) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + UnicodeToTextInfo converterInfo; + TextEncoding systemEncoding; + Str255 convertedString; + + *aOutChar = nsnull; + + OSStatus err = + ::UpgradeScriptInfoToTextEncoding(smSystemScript, + kTextLanguageDontCare, + kTextRegionDontCare, + NULL, + &systemEncoding); + NS_ENSURE_TRUE(err == noErr, PR_FALSE); + + err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo); + NS_ENSURE_TRUE(err == noErr, PR_FALSE); + + err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), + &aUniChar, convertedString); + NS_ENSURE_TRUE(err == noErr, PR_FALSE); + + *aOutChar = convertedString[1]; + ::DisposeUnicodeToTextInfo(&converterInfo); + return PR_TRUE; + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE); +} + +/* static */ void +PluginTextInputHandler::ConvertCocoaKeyEventToCarbonEvent( + NSEvent* aCocoaKeyEvent, + EventRecord& aCarbonKeyEvent, + PRBool aMakeKeyDownEventIfNSFlagsChanged) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + UInt32 charCode = 0; + if ([aCocoaKeyEvent type] == NSFlagsChanged) { + aCarbonKeyEvent.what = aMakeKeyDownEventIfNSFlagsChanged ? keyDown : keyUp; + } else { + if ([[aCocoaKeyEvent characters] length] > 0) { + charCode = [[aCocoaKeyEvent characters] characterAtIndex:0]; + } + if ([aCocoaKeyEvent type] == NSKeyDown) { + aCarbonKeyEvent.what = [aCocoaKeyEvent isARepeat] ? autoKey : keyDown; + } else { + aCarbonKeyEvent.what = keyUp; + } + } + + if (charCode >= 0x0080) { + switch (charCode) { + case NSUpArrowFunctionKey: + charCode = kUpArrowCharCode; + break; + case NSDownArrowFunctionKey: + charCode = kDownArrowCharCode; + break; + case NSLeftArrowFunctionKey: + charCode = kLeftArrowCharCode; + break; + case NSRightArrowFunctionKey: + charCode = kRightArrowCharCode; + break; + default: + unsigned char convertedCharCode; + if (ConvertUnicodeToCharCode(charCode, &convertedCharCode)) { + charCode = convertedCharCode; + } + //NSLog(@"charcode is %d, converted to %c, char is %@", + // charCode, convertedCharCode, [aCocoaKeyEvent characters]); + break; + } + } + + aCarbonKeyEvent.message = + (charCode & 0x00FF) | ([aCocoaKeyEvent keyCode] << 8); + aCarbonKeyEvent.when = ::TickCount(); + ::GetGlobalMouse(&aCarbonKeyEvent.where); + // XXX Is this correct? If ::GetCurrentKeyModifiers() returns "current" + // state and there is one or more pending modifier key events, + // the result is mismatch with the state at current key event. + aCarbonKeyEvent.modifiers = ::GetCurrentKeyModifiers(); + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + +#endif // NP_NO_CARBON + #pragma mark - diff --git a/widget/src/cocoa/mozView.h b/widget/src/cocoa/mozView.h index abdd139d803c..869cdf2950db 100644 --- a/widget/src/cocoa/mozView.h +++ b/widget/src/cocoa/mozView.h @@ -38,6 +38,8 @@ #ifndef mozView_h_ #define mozView_h_ +#include "npapi.h" + #undef DARWIN #import class nsIWidget; @@ -76,6 +78,15 @@ class TextInputHandler; - (BOOL)isDragInProgress; + // Gets the plugin event model for the view +- (NPEventModel)pluginEventModel; + + // Checks whether the view is first responder or not +- (BOOL)isFirstResponder; + + // Call when you dispatch an event which may cause to open context menu. +- (void)maybeInitContextMenuTracking; + @end // An informal protocol implemented by the NSWindow of the host application. diff --git a/widget/src/cocoa/nsChildView.h b/widget/src/cocoa/nsChildView.h index bb820d322ade..c044408680df 100644 --- a/widget/src/cocoa/nsChildView.h +++ b/widget/src/cocoa/nsChildView.h @@ -192,20 +192,6 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent); NPEventModel mPluginEventModel; NPDrawingModel mPluginDrawingModel; - // The following variables are only valid during key down event processing. - // Their current usage needs to be fixed to avoid problems with nested event - // loops that can confuse them. Once a variable is set during key down event - // processing, if an event spawns a nested event loop the previously set value - // will be wiped out. - NSEvent* mCurKeyEvent; - PRBool mKeyDownHandled; - // While we process key down events we need to keep track of whether or not - // we sent a key press event. This helps us make sure we do send one - // eventually. - BOOL mKeyPressSent; - // Valid when mKeyPressSent is true. - PRBool mKeyPressHandled; - // when mouseDown: is called, we store its event here (strong) NSEvent* mLastMouseDownEvent; diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index 3383b2da7f9c..de7847860587 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -169,7 +169,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0; - (BOOL)isPluginView; - (void)setPluginEventModel:(NPEventModel)eventModel; - (void)setPluginDrawingModel:(NPDrawingModel)drawingModel; -- (NPEventModel)pluginEventModel; - (NPDrawingModel)pluginDrawingModel; #ifndef NP_NO_CARBON @@ -180,8 +179,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0; - (void)processPendingRedraws; -- (void)maybeInitContextMenuTracking; - - (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext; // Called using performSelector:withObject:afterDelay:0 to release @@ -198,8 +195,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0; - (id)accessible; #endif -- (BOOL)isFirstResponder; - - (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown; - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent; @@ -2069,10 +2064,6 @@ NSEvent* gLastDragMouseDownEvent = nil; #else mPluginDrawingModel = NPDrawingModelCoreGraphics; #endif - mCurKeyEvent = nil; - mKeyDownHandled = PR_FALSE; - mKeyPressHandled = NO; - mKeyPressSent = NO; mPendingDisplay = NO; mBlockedLastMouseDown = NO; @@ -3853,38 +3844,6 @@ NSEvent* gLastDragMouseDownEvent = nil; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -#ifndef NP_NO_CARBON -static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; - - UnicodeToTextInfo converterInfo; - TextEncoding systemEncoding; - Str255 convertedString; - OSStatus err; - - *outChar = 0; - - err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding); - if (err != noErr) - return PR_FALSE; - - err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo); - if (err != noErr) - return PR_FALSE; - - err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString); - if (err != noErr) - return PR_FALSE; - - *outChar = convertedString[1]; - ::DisposeUnicodeToTextInfo(&converterInfo); - return PR_TRUE; - - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE); -} -#endif // NP_NO_CARBON - static void ConvertCocoaKeyEventToNPCocoaEvent(NSEvent* cocoaEvent, NPCocoaEvent& pluginEvent, PRUint32 keyType = 0) { InitNPCocoaEvent(&pluginEvent); @@ -3912,54 +3871,6 @@ static void ConvertCocoaKeyEventToNPCocoaEvent(NSEvent* cocoaEvent, NPCocoaEvent } } -#ifndef NP_NO_CARBON -static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord& pluginEvent, PRUint32 keyType = 0) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - UInt32 charCode = 0; - if ([cocoaEvent type] == NSFlagsChanged) { - pluginEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp; - } else { - if ([[cocoaEvent characters] length] > 0) - charCode = [[cocoaEvent characters] characterAtIndex:0]; - if ([cocoaEvent type] == NSKeyDown) - pluginEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown; - else - pluginEvent.what = keyUp; - } - - if (charCode >= 0x0080) { - switch (charCode) { - case NSUpArrowFunctionKey: - charCode = kUpArrowCharCode; - break; - case NSDownArrowFunctionKey: - charCode = kDownArrowCharCode; - break; - case NSLeftArrowFunctionKey: - charCode = kLeftArrowCharCode; - break; - case NSRightArrowFunctionKey: - charCode = kRightArrowCharCode; - break; - default: - unsigned char convertedCharCode; - if (ConvertUnicodeToCharCode(charCode, &convertedCharCode)) - charCode = convertedCharCode; - //NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]); - break; - } - } - pluginEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8); - pluginEvent.when = ::TickCount(); - ::GetGlobalMouse(&pluginEvent.where); - pluginEvent.modifiers = ::GetCurrentKeyModifiers(); - - NS_OBJC_END_TRY_ABORT_BLOCK; -} -#endif // NP_NO_CARBON - // Basic conversion for cocoa to gecko events, common to all conversions. // Note that it is OK for inEvent to be nil. - (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent @@ -4128,8 +4039,6 @@ static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord& #pragma mark - // NSTextInput implementation -#define MAX_BUFFER_SIZE 32 - - (void)insertText:(id)insertString { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; @@ -4137,93 +4046,20 @@ static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord& #if DEBUG_IME NSLog(@"****in insertText: '%@'", insertString); #endif - if (!mGeckoChild || !mTextInputHandler) - return; - if (mTextInputHandler->IgnoreIMEComposition()) - return; + NS_ENSURE_TRUE(mGeckoChild, ); nsAutoRetainCocoaObject kungFuDeathGrip(self); - if (![insertString isKindOfClass:[NSAttributedString class]]) - insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease]; - - NSString *tmpStr = [insertString string]; - unsigned int len = [tmpStr length]; - if (!mTextInputHandler->IsIMEComposing() && len == 0) - return; // nothing to do - PRUnichar buffer[MAX_BUFFER_SIZE]; - PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer; - [tmpStr getCharacters:bufPtr]; - bufPtr[len] = PRUnichar('\0'); - - if (len == 1 && !mTextInputHandler->IsIMEComposing()) { - // don't let the same event be fired twice when hitting - // enter/return! (Bug 420502) - if (mKeyPressSent) - return; - - // dispatch keypress event with char instead of textEvent - nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild); - geckoEvent.time = PR_IntervalNow(); - geckoEvent.charCode = bufPtr[0]; // gecko expects OS-translated unicode - geckoEvent.keyCode = 0; - geckoEvent.isChar = PR_TRUE; - if (mKeyDownHandled) - geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; - // don't set other modifiers from the current event, because here in - // -insertText: they've already been taken into account in creating - // the input string. - - // create event for use by plugins -#ifndef NP_NO_CARBON - EventRecord carbonEvent; -#endif - if (mCurKeyEvent) { - // XXX The ASCII characters inputting mode of egbridge (Japanese IME) - // might send the keyDown event with wrong keyboard layout if other - // keyboard layouts are already loaded. In that case, the native event - // doesn't match to this gecko event... -#ifndef NP_NO_CARBON - if (mPluginEventModel == NPEventModelCarbon) { - ConvertCocoaKeyEventToCarbonEvent(mCurKeyEvent, carbonEvent); - geckoEvent.pluginEvent = &carbonEvent; - } -#endif - - geckoEvent.isShift = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0; - if (!TextInputHandler::IsPrintableChar(geckoEvent.charCode)) { - geckoEvent.keyCode = - TextInputHandler::ComputeGeckoKeyCode([mCurKeyEvent keyCode], - [mCurKeyEvent charactersIgnoringModifiers]); - geckoEvent.charCode = 0; - } - } else { - // Note that insertText is not called only at key pressing. - if (!TextInputHandler::IsPrintableChar(geckoEvent.charCode)) { - geckoEvent.keyCode = - TextInputHandler::ComputeGeckoKeyCodeFromChar(geckoEvent.charCode); - geckoEvent.charCode = 0; - } - } - - PRBool keyPressHandled = mTextInputHandler->DispatchEvent(geckoEvent); - // Note: mGeckoChild might have become null here. Don't count on it from here on. - // Only record the results of dispatching geckoEvent if we're currently - // processing a keyDown event. - if (mCurKeyEvent) { - mKeyPressHandled = keyPressHandled; - mKeyPressSent = YES; - } - } - else { - NSAttributedString* attrStr = - static_cast(insertString); - mTextInputHandler->InsertTextAsCommittingComposition(attrStr); + NSAttributedString* attrStr; + if ([insertString isKindOfClass:[NSAttributedString class]]) { + attrStr = static_cast(insertString); + } else { + attrStr = + [[[NSAttributedString alloc] initWithString:insertString] autorelease]; } - if (bufPtr != buffer) - delete[] bufPtr; + mTextInputHandler->InsertText(attrStr); NS_OBJC_END_TRY_ABORT_BLOCK; } @@ -4237,12 +4073,15 @@ static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord& { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + PRBool ignore = mGeckoChild && mTextInputHandler->KeyPressWasHandled(); + #if DEBUG_IME - NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled); + NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, ignore); #endif - if (!mKeyPressHandled) + if (!ignore) { [super doCommandBySelector:aSelector]; + } NS_OBJC_END_TRY_ABORT_BLOCK; } @@ -4374,110 +4213,7 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf) ToEscapedString([theEvent charactersIgnoringModifiers], str2))); nsAutoRetainCocoaObject kungFuDeathGrip(self); - mCurKeyEvent = theEvent; - - BOOL nonDeadKeyPress = [[theEvent characters] length] > 0; - if (nonDeadKeyPress && !mTextInputHandler->IsIMEComposing()) { - NSResponder* firstResponder = [[self window] firstResponder]; - - nsKeyEvent geckoKeydown(PR_TRUE, NS_KEY_DOWN, mGeckoChild); - mTextInputHandler->InitKeyEvent(theEvent, geckoKeydown); - -#ifndef NP_NO_CARBON - EventRecord carbonEvent; - if (mPluginEventModel == NPEventModelCarbon) { - ConvertCocoaKeyEventToCarbonEvent(theEvent, carbonEvent); - geckoKeydown.pluginEvent = &carbonEvent; - } -#endif - - mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoKeydown); - if (!mGeckoChild) { - return mKeyDownHandled; - } - - // The key down event may have shifted the focus, in which - // case we should not fire the key press. - if (firstResponder != [[self window] firstResponder]) { - PRBool handled = mKeyDownHandled; - mCurKeyEvent = nil; - mKeyDownHandled = PR_FALSE; - return handled; - } - - // If this is the context menu key command, send a context menu key event. - unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask; - if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) { - nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey); - contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE; - PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent); - [self maybeInitContextMenuTracking]; - // Bail, there is nothing else to do here. - PRBool handled = (cmEventHandled || mKeyDownHandled); - mCurKeyEvent = nil; - mKeyDownHandled = PR_FALSE; - return handled; - } - - nsKeyEvent geckoKeypress(PR_TRUE, NS_KEY_PRESS, mGeckoChild); - mTextInputHandler->InitKeyEvent(theEvent, geckoKeypress); - - // if this is a non-letter keypress, or the control key is down, - // dispatch the keydown to gecko, so that we trap delete, - // control-letter combinations etc before Cocoa tries to use - // them for keybindings. - if ((!geckoKeypress.isChar || geckoKeypress.isControl) && - !mTextInputHandler->IsIMEComposing()) { - if (mKeyDownHandled) - geckoKeypress.flags |= NS_EVENT_FLAG_NO_DEFAULT; - mKeyPressHandled = mTextInputHandler->DispatchEvent(geckoKeypress); - mKeyPressSent = YES; - if (!mGeckoChild) - return (mKeyDownHandled || mKeyPressHandled); - } - } - - // Let Cocoa interpret the key events, caching IsIMEComposing first. - PRBool wasComposing = mTextInputHandler->IsIMEComposing(); - PRBool interpretKeyEventsCalled = PR_FALSE; - if (mTextInputHandler->IsIMEEnabled() || - mTextInputHandler->IsASCIICapableOnly()) { - [super interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; - interpretKeyEventsCalled = PR_TRUE; - } - - if (!mGeckoChild) { - return (mKeyDownHandled || mKeyPressHandled); - } - - if (!mKeyPressSent && nonDeadKeyPress && !wasComposing && - !mTextInputHandler->IsIMEComposing()) { - nsKeyEvent geckoKeypress(PR_TRUE, NS_KEY_PRESS, mGeckoChild); - mTextInputHandler->InitKeyEvent(theEvent, geckoKeypress); - - // If we called interpretKeyEvents and this isn't normal character input - // then IME probably ate the event for some reason. We do not want to - // send a key press event in that case. - if (!(interpretKeyEventsCalled && - TextInputHandler::IsNormalCharInputtingEvent(geckoKeypress))) { - if (mKeyDownHandled) { - geckoKeypress.flags |= NS_EVENT_FLAG_NO_DEFAULT; - } - mKeyPressHandled = mTextInputHandler->DispatchEvent(geckoKeypress); - } - } - - // Note: mGeckoChild might have become null here. Don't count on it from here on. - - PRBool handled = (mKeyDownHandled || mKeyPressHandled); - - // See note about nested event loops where these variables are declared in header. - mKeyPressHandled = NO; - mKeyPressSent = NO; - mCurKeyEvent = nil; - mKeyDownHandled = PR_FALSE; - - return handled; + return mTextInputHandler->HandleKeyDownEvent(theEvent); NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); } @@ -4746,7 +4482,9 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf) nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, mGeckoChild); mTextInputHandler->InitKeyEvent(theEvent, keyUpEvent); EventRecord macKeyUpEvent; - ConvertCocoaKeyEventToCarbonEvent(theEvent, macKeyUpEvent); + TextInputHandler::ConvertCocoaKeyEventToCarbonEvent(theEvent, + macKeyUpEvent, + PR_FALSE); keyUpEvent.pluginEvent = &macKeyUpEvent; mGeckoChild->DispatchWindowEvent(keyUpEvent); } @@ -4864,7 +4602,9 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf) if (mIsPluginView) { #ifndef NP_NO_CARBON if (mPluginEventModel == NPEventModelCarbon) { - ConvertCocoaKeyEventToCarbonEvent(theEvent, carbonEvent, message); + TextInputHandler::ConvertCocoaKeyEventToCarbonEvent(theEvent, + carbonEvent, + isKeyDown); geckoEvent.pluginEvent = &carbonEvent; } #endif @@ -5474,8 +5214,7 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf) if (NS_FAILED(rv)) return NO; - if (!mGeckoChild) - return NO; + NS_ENSURE_TRUE(mGeckoChild, PR_FALSE); nsContentCommandEvent command(PR_TRUE, NS_CONTENT_COMMAND_PASTE_TRANSFERABLE,