From eeb6716dfb8b15d5fc21e9e44ef83090685fa81c Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 5 Oct 2011 11:19:24 +0900 Subject: [PATCH] Bug 685073 part.1 Manage nested key events for IME r=smichaud --- widget/src/cocoa/TextInputHandler.h | 82 ++++++++++++++++++++++++++-- widget/src/cocoa/TextInputHandler.mm | 61 ++++++++++++--------- 2 files changed, 110 insertions(+), 33 deletions(-) diff --git a/widget/src/cocoa/TextInputHandler.h b/widget/src/cocoa/TextInputHandler.h index e3f23815abb2..c2377b9aef63 100644 --- a/widget/src/cocoa/TextInputHandler.h +++ b/widget/src/cocoa/TextInputHandler.h @@ -485,6 +485,23 @@ protected: KeyEventState() : mKeyEvent(nsnull) { Clear(); + } + + KeyEventState(NSEvent* aNativeKeyEvent) : mKeyEvent(nsnull) + { + Clear(); + Set(aNativeKeyEvent); + } + + KeyEventState(const KeyEventState &aOther) : mKeyEvent(nsnull) + { + Clear(); + if (aOther.mKeyEvent) { + mKeyEvent = [aOther.mKeyEvent retain]; + } + mKeyDownHandled = aOther.mKeyDownHandled; + mKeyPressDispatched = aOther.mKeyPressDispatched; + mKeyPressHandled = aOther.mKeyPressHandled; } ~KeyEventState() @@ -529,15 +546,67 @@ protected: ~AutoKeyEventStateCleaner() { - mHandler->mCurrentKeyEvent.Clear(); + mHandler->RemoveCurrentKeyEvent(); } private: - TextInputHandlerBase* mHandler; + nsRefPtr 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; + /** + * mCurrentKeyEvents stores all key events which are being processed. + * When we call interpretKeyEvents, IME may generate other key events. + * mCurrentKeyEvents[0] is the latest key event. + */ + nsTArray mCurrentKeyEvents; + + /** + * mFirstKeyEvent must be used for first key event. This member prevents + * memory fragmentation for most key events. + */ + KeyEventState mFirstKeyEvent; + + /** + * PushKeyEvent() adds the current key event to mCurrentKeyEvents. + */ + KeyEventState* PushKeyEvent(NSEvent* aNativeKeyEvent) + { + KeyEventState* keyEvent = nsnull; + if (mCurrentKeyEvents.Length() == 0) { + mFirstKeyEvent.Set(aNativeKeyEvent); + keyEvent = &mFirstKeyEvent; + } else { + keyEvent = new KeyEventState(aNativeKeyEvent); + } + return *mCurrentKeyEvents.AppendElement(keyEvent); + } + + /** + * RemoveCurrentKeyEvent() removes the current key event from + * mCurrentKeyEvents. + */ + void RemoveCurrentKeyEvent() + { + NS_ASSERTION(mCurrentKeyEvents.Length() > 0, + "RemoveCurrentKeyEvent() is called unexpectedly"); + KeyEventState* keyEvent = GetCurrentKeyEvent(); + mCurrentKeyEvents.RemoveElementAt(mCurrentKeyEvents.Length() - 1); + if (keyEvent == &mFirstKeyEvent) { + keyEvent->Clear(); + } else { + delete keyEvent; + } + } + + /** + * GetCurrentKeyEvent() returns current processing key event. + */ + KeyEventState* GetCurrentKeyEvent() + { + if (mCurrentKeyEvents.Length() == 0) { + return nsnull; + } + return mCurrentKeyEvents[mCurrentKeyEvents.Length() - 1]; + } /** * IsPrintableChar() checks whether the unicode character is @@ -1119,7 +1188,8 @@ public: */ bool KeyPressWasHandled() { - return mCurrentKeyEvent.mKeyPressHandled; + KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); + return currentKeyEvent && currentKeyEvent->mKeyPressHandled; } protected: diff --git a/widget/src/cocoa/TextInputHandler.mm b/widget/src/cocoa/TextInputHandler.mm index 062938f10571..46710dd862fb 100644 --- a/widget/src/cocoa/TextInputHandler.mm +++ b/widget/src/cocoa/TextInputHandler.mm @@ -997,7 +997,7 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) nsRefPtr kungFuDeathGrip(mWidget); - mCurrentKeyEvent.Set(aNativeEvent); + KeyEventState* currentKeyEvent = PushKeyEvent(aNativeEvent); AutoKeyEventStateCleaner remover(this); BOOL nonDeadKeyPress = [[aNativeEvent characters] length] > 0; @@ -1015,12 +1015,12 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) } #endif // #ifndef NP_NO_CARBON - mCurrentKeyEvent.mKeyDownHandled = DispatchEvent(keydownEvent); + currentKeyEvent->mKeyDownHandled = DispatchEvent(keydownEvent); if (Destroyed()) { PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::HandleKeyDownEvent, " "widget was destroyed by keydown event", this)); - return mCurrentKeyEvent.KeyDownOrPressHandled(); + return currentKeyEvent->KeyDownOrPressHandled(); } // The key down event may have shifted the focus, in which @@ -1030,7 +1030,7 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::HandleKeyDownEvent, " "view lost focus by keydown event", this)); - return mCurrentKeyEvent.KeyDownOrPressHandled(); + return currentKeyEvent->KeyDownOrPressHandled(); } // If this is the context menu key command, send a context menu key event. @@ -1052,7 +1052,7 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) Destroyed() ? " and widget was destroyed" : "")); [mView maybeInitContextMenuTracking]; // Bail, there is nothing else to do here. - return (cmEventHandled || mCurrentKeyEvent.KeyDownOrPressHandled()); + return (cmEventHandled || currentKeyEvent->KeyDownOrPressHandled()); } nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget); @@ -1066,16 +1066,16 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) // its owning shortcut key. See bug 477291. if ((!keypressEvent.isChar || keypressEvent.isControl) && !IsIMEComposing()) { - if (mCurrentKeyEvent.mKeyDownHandled) { + if (currentKeyEvent->mKeyDownHandled) { keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; } - mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent); - mCurrentKeyEvent.mKeyPressDispatched = true; + currentKeyEvent->mKeyPressHandled = DispatchEvent(keypressEvent); + currentKeyEvent->mKeyPressDispatched = true; if (Destroyed()) { PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::HandleKeyDownEvent, " "widget was destroyed by keypress event", this)); - return mCurrentKeyEvent.KeyDownOrPressHandled(); + return currentKeyEvent->KeyDownOrPressHandled(); } } } @@ -1098,7 +1098,7 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::HandleKeyDownEvent, widget was destroyed", this)); - return mCurrentKeyEvent.KeyDownOrPressHandled(); + return currentKeyEvent->KeyDownOrPressHandled(); } PR_LOG(gLog, PR_LOG_ALWAYS, @@ -1106,7 +1106,7 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) "IsIMEComposing()=%s", this, TrueOrFalse(wasComposing), TrueOrFalse(IsIMEComposing()))); - if (!mCurrentKeyEvent.mKeyPressDispatched && nonDeadKeyPress && + if (!currentKeyEvent->mKeyPressDispatched && nonDeadKeyPress && !wasComposing && !IsIMEComposing()) { nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget); InitKeyEvent(aNativeEvent, keypressEvent); @@ -1124,10 +1124,10 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) // our default action for this key. if (!(interpretKeyEventsCalled && IsNormalCharInputtingEvent(keypressEvent))) { - if (mCurrentKeyEvent.mKeyDownHandled) { + if (currentKeyEvent->mKeyDownHandled) { keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; } - mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent); + currentKeyEvent->mKeyPressHandled = DispatchEvent(keypressEvent); PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::HandleKeyDownEvent, keypress event dispatched", this)); @@ -1139,9 +1139,9 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::HandleKeyDownEvent, " "keydown handled=%s, keypress handled=%s", - this, TrueOrFalse(mCurrentKeyEvent.mKeyDownHandled), - TrueOrFalse(mCurrentKeyEvent.mKeyPressHandled))); - return mCurrentKeyEvent.KeyDownOrPressHandled(); + this, TrueOrFalse(currentKeyEvent->mKeyDownHandled), + TrueOrFalse(currentKeyEvent->mKeyPressHandled))); + return currentKeyEvent->KeyDownOrPressHandled(); NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); } @@ -1312,13 +1312,17 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString) return; } + KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); + PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::InsertText, aAttrString=\"%s\", " "IsIMEComposing()=%s, IgnoreIMEComposition()=%s, " "keyevent=%p, keypressDispatched=%s", this, GetCharacters([aAttrString string]), TrueOrFalse(IsIMEComposing()), - TrueOrFalse(IgnoreIMEComposition()), mCurrentKeyEvent.mKeyEvent, - TrueOrFalse(mCurrentKeyEvent.mKeyPressDispatched))); + TrueOrFalse(IgnoreIMEComposition()), + currentKeyEvent ? currentKeyEvent->mKeyEvent : nsnull, + currentKeyEvent ? + TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A")); if (IgnoreIMEComposition()) { return; @@ -1337,7 +1341,7 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString) // Don't let the same event be fired twice when hitting // enter/return! (Bug 420502) - if (mCurrentKeyEvent.mKeyPressDispatched) { + if (currentKeyEvent && currentKeyEvent->mKeyPressDispatched) { return; } @@ -1359,8 +1363,8 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString) EventRecord carbonEvent; #endif // #ifndef NP_NO_CARBON - if (mCurrentKeyEvent.mKeyEvent) { - NSEvent* keyEvent = mCurrentKeyEvent.mKeyEvent; + if (currentKeyEvent) { + NSEvent* keyEvent = currentKeyEvent->mKeyEvent; // XXX The ASCII characters inputting mode of egbridge (Japanese IME) // might send the keyDown event with wrong keyboard layout if other @@ -1373,7 +1377,7 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString) } #endif // #ifndef NP_NO_CARBON - if (mCurrentKeyEvent.mKeyDownHandled) { + if (currentKeyEvent->mKeyDownHandled) { keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT; } @@ -1400,9 +1404,9 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString) // Note: mWidget might have become null here. Don't count on it from here on. - if (mCurrentKeyEvent.mKeyEvent) { - mCurrentKeyEvent.mKeyPressHandled = keyPressHandled; - mCurrentKeyEvent.mKeyPressDispatched = true; + if (currentKeyEvent) { + currentKeyEvent->mKeyPressHandled = keyPressHandled; + currentKeyEvent->mKeyPressDispatched = true; } NS_OBJC_END_TRY_ABORT_BLOCK; @@ -1411,13 +1415,16 @@ TextInputHandler::InsertText(NSAttributedString *aAttrString) bool TextInputHandler::DoCommandBySelector(const char* aSelector) { + KeyEventState* currentKeyEvent = GetCurrentKeyEvent(); + PR_LOG(gLog, PR_LOG_ALWAYS, ("%p TextInputHandler::DoCommandBySelector, aSelector=\"%s\", " "Destroyed()=%s, keypressHandled=%s", this, aSelector ? aSelector : "", TrueOrFalse(Destroyed()), - TrueOrFalse(mCurrentKeyEvent.mKeyPressHandled))); + currentKeyEvent ? + TrueOrFalse(currentKeyEvent->mKeyPressHandled) : "N/A")); - return !Destroyed() && mCurrentKeyEvent.mKeyPressHandled; + return !Destroyed() && currentKeyEvent && currentKeyEvent->mKeyPressHandled; }