From 22c83aa50a9afbec07dd26b32bee08236c7f7efc Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Fri, 20 Feb 2015 10:37:02 -0600 Subject: [PATCH] Bug 1110888 - Always do plugin IME in main process, even with e10s. r=masayuki,smaug --- dom/html/HTMLObjectElement.cpp | 53 +++++++++++ dom/html/HTMLObjectElement.h | 6 ++ dom/html/HTMLSharedObjectElement.cpp | 16 ++++ dom/html/HTMLSharedObjectElement.h | 5 + dom/ipc/PBrowser.ipdl | 22 +++++ dom/ipc/TabParent.cpp | 27 ++++++ dom/ipc/TabParent.h | 5 + dom/plugins/base/nsPluginInstanceOwner.cpp | 106 ++++++++++----------- widget/PuppetWidget.cpp | 22 +++++ widget/PuppetWidget.h | 6 ++ widget/TextEvents.h | 11 +++ widget/cocoa/ComplexTextInputPanel.h | 4 +- widget/cocoa/ComplexTextInputPanel.mm | 39 ++------ widget/cocoa/TextInputHandler.mm | 23 ++++- widget/cocoa/nsChildView.h | 15 +++ widget/cocoa/nsChildView.mm | 75 +++++++++++++++ widget/nsBaseWidget.h | 6 ++ widget/nsGUIEventIPC.h | 2 + widget/nsIWidget.h | 26 ++++- 19 files changed, 377 insertions(+), 92 deletions(-) diff --git a/dom/html/HTMLObjectElement.cpp b/dom/html/HTMLObjectElement.cpp index be515683db6..2a5da583b49 100644 --- a/dom/html/HTMLObjectElement.cpp +++ b/dom/html/HTMLObjectElement.cpp @@ -20,6 +20,10 @@ #include "nsNPAPIPluginInstance.h" #include "nsIWidget.h" #include "nsContentUtils.h" +#ifdef XP_MACOSX +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/Event.h" +#endif namespace mozilla { namespace dom { @@ -104,6 +108,55 @@ NS_IMPL_ELEMENT_CLONE(HTMLObjectElement) // nsIConstraintValidation NS_IMPL_NSICONSTRAINTVALIDATION(HTMLObjectElement) +#ifdef XP_MACOSX + +static nsIWidget* GetWidget(Element* aElement) +{ + nsIWidget* retval = NULL; + nsIFrame* frame = aElement->GetPrimaryFrame(); + if (frame) { + retval = frame->GetNearestWidget(); + } + return retval; +} + +static void OnFocusBlurPlugin(Element* aElement, bool aFocus) +{ + nsIWidget* widget = GetWidget(aElement); + if (widget) { + bool value = aFocus; + widget->SetPluginFocused(value); + } +} + +void +HTMLObjectElement::HandleFocusBlurPlugin(Element* aElement, + WidgetEvent* aEvent) +{ + if (!aEvent->mFlags.mIsTrusted) { + return; + } + switch (aEvent->message) { + case NS_FOCUS_CONTENT: { + OnFocusBlurPlugin(aElement, true); + break; + } + case NS_BLUR_CONTENT: { + OnFocusBlurPlugin(aElement, false); + break; + } + } +} + +NS_IMETHODIMP +HTMLObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor) +{ + HandleFocusBlurPlugin(this, aVisitor.mEvent); + return NS_OK; +} + +#endif // #ifdef XP_MACOSX + NS_IMETHODIMP HTMLObjectElement::GetForm(nsIDOMHTMLFormElement **aForm) { diff --git a/dom/html/HTMLObjectElement.h b/dom/html/HTMLObjectElement.h index 9943c131b5f..eae048efb7b 100644 --- a/dom/html/HTMLObjectElement.h +++ b/dom/html/HTMLObjectElement.h @@ -30,6 +30,12 @@ public: virtual int32_t TabIndexDefault() MOZ_OVERRIDE; +#ifdef XP_MACOSX + // nsIDOMEventTarget + NS_IMETHOD PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; + static void HandleFocusBlurPlugin(Element* aElement, WidgetEvent* aEvent); +#endif + // Element virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE; diff --git a/dom/html/HTMLSharedObjectElement.cpp b/dom/html/HTMLSharedObjectElement.cpp index 744732e906d..bcf94f6a1e5 100644 --- a/dom/html/HTMLSharedObjectElement.cpp +++ b/dom/html/HTMLSharedObjectElement.cpp @@ -17,6 +17,11 @@ #include "nsIScriptError.h" #include "nsIWidget.h" #include "nsContentUtils.h" +#ifdef XP_MACOSX +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/HTMLObjectElement.h" +#endif NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(SharedObject) @@ -108,6 +113,17 @@ NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement) NS_IMPL_ELEMENT_CLONE(HTMLSharedObjectElement) +#ifdef XP_MACOSX + +NS_IMETHODIMP +HTMLSharedObjectElement::PostHandleEvent(EventChainPostVisitor& aVisitor) +{ + HTMLObjectElement::HandleFocusBlurPlugin(this, aVisitor.mEvent); + return NS_OK; +} + +#endif // #ifdef XP_MACOSX + nsresult HTMLSharedObjectElement::BindToTree(nsIDocument *aDocument, nsIContent *aParent, diff --git a/dom/html/HTMLSharedObjectElement.h b/dom/html/HTMLSharedObjectElement.h index c09caf3e3df..901ef99ef9f 100644 --- a/dom/html/HTMLSharedObjectElement.h +++ b/dom/html/HTMLSharedObjectElement.h @@ -32,6 +32,11 @@ public: virtual int32_t TabIndexDefault() MOZ_OVERRIDE; +#ifdef XP_MACOSX + // nsIDOMEventTarget + NS_IMETHOD PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE; +#endif + // nsIDOMHTMLAppletElement NS_DECL_NSIDOMHTMLAPPLETELEMENT diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 1309d03a804..fbc1e684fc9 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -280,6 +280,28 @@ parent: prio(urgent) sync EndIMEComposition(bool cancel) returns (bool noCompositionEvent, nsString composition); + /** + * Tells chrome to start plugin IME. If this results in a string getting + * committed, the result is in aCommitted (otherwise aCommitted is empty). + * + * aKeyboardEvent The event with which plugin IME is to be started + * panelX and panelY Location in screen coordinates of the IME input panel + * (should be just under the plugin) + * aCommitted The string committed during IME -- otherwise empty + */ + prio(urgent) sync StartPluginIME(WidgetKeyboardEvent aKeyboardEvent, + int32_t panelX, int32_t panelY) + returns (nsString aCommitted); + + /** + * Tells chrome (and specifically the appropriate widget) whether or not + * a plugin (inside the widget) has the keyboard focus. Should be sent + * when the keyboard focus changes too or from a plugin. + * + * aFocused Whether or not a plugin is focused + */ + prio(urgent) async SetPluginFocused(bool aFocused); + /** * Request that the parent process move focus to the browser's frame. If * canRaise is true, the window can be raised if it is inactive. diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index e8b934eb08b..094e2a3b7a1 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -2103,6 +2103,33 @@ TabParent::RecvEndIMEComposition(const bool& aCancel, return true; } +bool +TabParent::RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent, + const int32_t& aPanelX, const int32_t& aPanelY, + nsString* aCommitted) +{ + nsCOMPtr widget = GetWidget(); + if (!widget) { + return true; + } + widget->StartPluginIME(aKeyboardEvent, + (int32_t&)aPanelX, + (int32_t&)aPanelY, + *aCommitted); + return true; +} + +bool +TabParent::RecvSetPluginFocused(const bool& aFocused) +{ + nsCOMPtr widget = GetWidget(); + if (!widget) { + return true; + } + widget->SetPluginFocused((bool&)aFocused); + return true; +} + bool TabParent::RecvGetInputContext(int32_t* aIMEEnabled, int32_t* aIMEOpen, diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 04109daa670..f8c49232e8f 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -192,6 +192,11 @@ public: virtual bool RecvEndIMEComposition(const bool& aCancel, bool* aNoCompositionEvent, nsString* aComposition) MOZ_OVERRIDE; + virtual bool RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent, + const int32_t& aPanelX, + const int32_t& aPanelY, + nsString* aCommitted) MOZ_OVERRIDE; + virtual bool RecvSetPluginFocused(const bool& aFocused) MOZ_OVERRIDE; virtual bool RecvGetInputContext(int32_t* aIMEEnabled, int32_t* aIMEOpen, intptr_t* aNativeIMEContext) MOZ_OVERRIDE; diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 53020b03efd..8bdadc0f2d5 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -1837,19 +1837,30 @@ TranslateToNPCocoaEvent(WidgetGUIEvent* anEvent, nsIFrame* aObjectFrame) { WidgetKeyboardEvent* keyEvent = anEvent->AsKeyboardEvent(); - cocoaEvent.data.key.keyCode = keyEvent->mNativeKeyCode; - cocoaEvent.data.key.isARepeat = keyEvent->mIsRepeat; - cocoaEvent.data.key.modifierFlags = keyEvent->mNativeModifierFlags; - const char16_t* nativeChars = keyEvent->mNativeCharacters.get(); - cocoaEvent.data.key.characters = - (NPNSString*)::CFStringCreateWithCharacters(NULL, - reinterpret_cast(nativeChars), - keyEvent->mNativeCharacters.Length()); - const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get(); - cocoaEvent.data.key.charactersIgnoringModifiers = - (NPNSString*)::CFStringCreateWithCharacters(NULL, - reinterpret_cast(nativeCharsIgnoringModifiers), - keyEvent->mNativeCharactersIgnoringModifiers.Length()); + // That keyEvent->mPluginTextEventString is non-empty is a signal that we should + // create a text event for the plugin, instead of a key event. + if ((anEvent->message == NS_KEY_DOWN) && !keyEvent->mPluginTextEventString.IsEmpty()) { + cocoaEvent.type = NPCocoaEventTextInput; + const char16_t* pluginTextEventString = keyEvent->mPluginTextEventString.get(); + cocoaEvent.data.text.text = (NPNSString*) + ::CFStringCreateWithCharacters(NULL, + reinterpret_cast(pluginTextEventString), + keyEvent->mPluginTextEventString.Length()); + } else { + cocoaEvent.data.key.keyCode = keyEvent->mNativeKeyCode; + cocoaEvent.data.key.isARepeat = keyEvent->mIsRepeat; + cocoaEvent.data.key.modifierFlags = keyEvent->mNativeModifierFlags; + const char16_t* nativeChars = keyEvent->mNativeCharacters.get(); + cocoaEvent.data.key.characters = (NPNSString*) + ::CFStringCreateWithCharacters(NULL, + reinterpret_cast(nativeChars), + keyEvent->mNativeCharacters.Length()); + const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get(); + cocoaEvent.data.key.charactersIgnoringModifiers = (NPNSString*) + ::CFStringCreateWithCharacters(NULL, + reinterpret_cast(nativeCharsIgnoringModifiers), + keyEvent->mNativeCharactersIgnoringModifiers.Length()); + } break; } case NS_FOCUS_CONTENT: @@ -1921,56 +1932,37 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent) return nsEventStatus_eIgnore; } - if (cocoaEvent.type == NPCocoaEventKeyDown) { - ComplexTextInputPanel* ctiPanel = ComplexTextInputPanel::GetSharedComplexTextInputPanel(); - if (ctiPanel && ctiPanel->IsInComposition()) { - nsAutoString outText; - ctiPanel->InterpretKeyEvent(&cocoaEvent, outText); - if (!outText.IsEmpty()) { - CFStringRef cfString = ::CFStringCreateWithCharacters(kCFAllocatorDefault, - reinterpret_cast(outText.get()), - outText.Length()); - - NPCocoaEvent textEvent; - InitializeNPCocoaEvent(&textEvent); - textEvent.type = NPCocoaEventTextInput; - textEvent.data.text.text = (NPNSString*)cfString; - - mInstance->HandleEvent(&textEvent, nullptr); - } - return nsEventStatus_eConsumeNoDefault; - } + if (cocoaEvent.type == NPCocoaEventTextInput) { + mInstance->HandleEvent(&cocoaEvent, nullptr); + return nsEventStatus_eConsumeNoDefault; } int16_t response = kNPEventNotHandled; mInstance->HandleEvent(&cocoaEvent, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO); - if (response == kNPEventStartIME) { - nsAutoString outText; - ComplexTextInputPanel* ctiPanel = ComplexTextInputPanel::GetSharedComplexTextInputPanel(); - - // Place ctiPanel by passing the coordinates of the bottom-left of the plugin, - // in screen-coordinates. - double screenX, screenY; - ConvertPoint(0.0, mPluginFrame->GetScreenRect().height, NPCoordinateSpacePlugin, - &screenX, &screenY, NPCoordinateSpaceScreen); - - ctiPanel->PlacePanel(screenX, screenY); - ctiPanel->InterpretKeyEvent(&cocoaEvent, outText); - - if (!outText.IsEmpty()) { - CFStringRef cfString = ::CFStringCreateWithCharacters(kCFAllocatorDefault, - reinterpret_cast(outText.get()), - outText.Length()); - - NPCocoaEvent textEvent; - InitializeNPCocoaEvent(&textEvent); - textEvent.type = NPCocoaEventTextInput; - textEvent.data.text.text = (NPNSString*)cfString; - - mInstance->HandleEvent(&textEvent, nullptr); - } + if ((response == kNPEventStartIME) && (cocoaEvent.type == NPCocoaEventKeyDown)) { + nsIWidget* widget = mPluginFrame->GetNearestWidget(); + if (widget) { + const WidgetKeyboardEvent* keyEvent = anEvent.AsKeyboardEvent(); + double screenX, screenY; + ConvertPoint(0.0, mPluginFrame->GetScreenRect().height, + NPCoordinateSpacePlugin, &screenX, &screenY, + NPCoordinateSpaceScreen); + nsAutoString outText; + if (NS_SUCCEEDED(widget->StartPluginIME(*keyEvent, screenX, screenY, outText)) && + !outText.IsEmpty()) { + CFStringRef cfString = + ::CFStringCreateWithCharacters(kCFAllocatorDefault, + reinterpret_cast(outText.get()), + outText.Length()); + NPCocoaEvent textEvent; + InitializeNPCocoaEvent(&textEvent); + textEvent.type = NPCocoaEventTextInput; + textEvent.data.text.text = (NPNSString*)cfString; + mInstance->HandleEvent(&textEvent, nullptr); + } + } } bool handled = (response == kNPEventHandled || response == kNPEventStartIME); diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 1140f7b3bb1..acfe7001026 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -439,6 +439,28 @@ PuppetWidget::NotifyIMEInternal(const IMENotification& aIMENotification) } } +NS_IMETHODIMP +PuppetWidget::StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, + int32_t aPanelX, int32_t aPanelY, + nsString& aCommitted) +{ + if (!mTabChild || + !mTabChild->SendStartPluginIME(aKeyboardEvent, aPanelX, + aPanelY, &aCommitted)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +PuppetWidget::SetPluginFocused(bool& aFocused) +{ + if (!mTabChild || !mTabChild->SendSetPluginFocused(aFocused)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + NS_IMETHODIMP_(void) PuppetWidget::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index db3b7ed1ec8..f6f82b8f946 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -196,6 +196,12 @@ public: // Get the screen position of the application window. nsIntPoint GetWindowPosition(); + NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, + int32_t aPanelX, int32_t aPanelY, + nsString& aCommitted) MOZ_OVERRIDE; + + NS_IMETHOD SetPluginFocused(bool& aFocused) MOZ_OVERRIDE; + protected: bool mEnabled; bool mVisible; diff --git a/widget/TextEvents.h b/widget/TextEvents.h index 6234a2f2eb7..d0e8229a7cf 100644 --- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -165,6 +165,9 @@ public: uint32_t mNativeModifierFlags; nsString mNativeCharacters; nsString mNativeCharactersIgnoringModifiers; + // If this is non-empty, create a text event for plugins instead of a + // keyboard event. + nsString mPluginTextEventString; #endif // If the key should cause keypress events, this returns true. @@ -251,6 +254,14 @@ public: // is destroyed. mNativeKeyEvent = nullptr; mUniqueId = aEvent.mUniqueId; +#ifdef XP_MACOSX + mNativeKeyCode = aEvent.mNativeKeyCode; + mNativeModifierFlags = aEvent.mNativeModifierFlags; + mNativeCharacters.Assign(aEvent.mNativeCharacters); + mNativeCharactersIgnoringModifiers. + Assign(aEvent.mNativeCharactersIgnoringModifiers); + mPluginTextEventString.Assign(aEvent.mPluginTextEventString); +#endif } private: diff --git a/widget/cocoa/ComplexTextInputPanel.h b/widget/cocoa/ComplexTextInputPanel.h index 4cab3ba86ae..648e6d91143 100644 --- a/widget/cocoa/ComplexTextInputPanel.h +++ b/widget/cocoa/ComplexTextInputPanel.h @@ -36,8 +36,10 @@ class ComplexTextInputPanel public: static ComplexTextInputPanel* GetSharedComplexTextInputPanel(); virtual void PlacePanel(int32_t x, int32_t y) = 0; // Bottom left coordinate of plugin in screen coords - virtual void InterpretKeyEvent(NPCocoaEvent* aEvent, nsAString& aOutText) = 0; + virtual void InterpretKeyEvent(void* aEvent, nsAString& aOutText) = 0; virtual bool IsInComposition() = 0; + virtual void* GetInputContext() = 0; + virtual void CancelComposition() = 0; protected: virtual ~ComplexTextInputPanel() {}; diff --git a/widget/cocoa/ComplexTextInputPanel.mm b/widget/cocoa/ComplexTextInputPanel.mm index 6eebd44bc5b..52f714a0fec 100644 --- a/widget/cocoa/ComplexTextInputPanel.mm +++ b/widget/cocoa/ComplexTextInputPanel.mm @@ -132,23 +132,8 @@ extern "C" OSStatus TSMProcessRawKeyEvent(EventRef anEvent); { *string = nil; - if (!nsCocoaFeatures::OnMountainLionOrLater()) { - // This "works" on OS X 10.7 and below, but at the cost of breaking plugin - // IME, even in non-e10s mode: In an IME like Kotoeri Hiragana, every key - // gets sent to the plugin individually. - if (![[mInputTextView inputContext] handleEvent:event]) { - return; - } - } else { - // On OS X 10.8 and above we can't use -[NSTextInputContext handleEvent:] - // -- it doesn't work with a synthesized event. We need to activate the - // input context and call TSMProcessRawKeyEvent instead. This also allows - // plugin IME to work properly in non-e10s mode. - [[mInputTextView inputContext] activate]; - OSErr err = TSMProcessRawKeyEvent((EventRef)[event eventRef]); - if (err != noErr) { - return; - } + if (![[mInputTextView inputContext] handleEvent:event]) { + return; } if ([mInputTextView hasMarkedText]) { @@ -222,9 +207,12 @@ class ComplexTextInputPanelPrivate : public ComplexTextInputPanel public: ComplexTextInputPanelPrivate(); - virtual void InterpretKeyEvent(NPCocoaEvent* aEvent, nsAString& aOutText); + virtual void InterpretKeyEvent(void* aEvent, nsAString& aOutText); virtual bool IsInComposition(); virtual void PlacePanel(int32_t x, int32_t y); + virtual void* GetInputContext() { return [mPanel inputContext]; } + virtual void CancelComposition() { [mPanel cancelComposition]; } + private: ~ComplexTextInputPanelPrivate(); ComplexTextInputPanelImpl* mPanel; @@ -251,21 +239,10 @@ ComplexTextInputPanel::GetSharedComplexTextInputPanel() } void -ComplexTextInputPanelPrivate::InterpretKeyEvent(NPCocoaEvent* aEvent, nsAString& aOutText) +ComplexTextInputPanelPrivate::InterpretKeyEvent(void* aEvent, nsAString& aOutText) { - NSEvent* nativeEvent = [NSEvent keyEventWithType:NSKeyDown - location:NSMakePoint(0,0) - modifierFlags:aEvent->data.key.modifierFlags - timestamp:0 - windowNumber:[mPanel windowNumber] - context:[mPanel graphicsContext] - characters:(NSString*)aEvent->data.key.characters - charactersIgnoringModifiers:(NSString*)aEvent->data.key.charactersIgnoringModifiers - isARepeat:aEvent->data.key.isARepeat - keyCode:aEvent->data.key.keyCode]; - NSString* textString = nil; - [mPanel interpretKeyEvent:nativeEvent string:&textString]; + [mPanel interpretKeyEvent:(NSEvent*)aEvent string:&textString]; if (textString) { nsCocoaUtils::GetStringForNSString(textString, aOutText); diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm index 52a6158af1c..d934bf3aa96 100644 --- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -1523,7 +1523,20 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) KeyEventState* currentKeyEvent = PushKeyEvent(aNativeEvent); AutoKeyEventStateCleaner remover(this); - if (!IsIMEComposing()) { + ComplexTextInputPanel* ctiPanel = ComplexTextInputPanel::GetSharedComplexTextInputPanel(); + if (ctiPanel && ctiPanel->IsInComposition()) { + nsAutoString committed; + ctiPanel->InterpretKeyEvent(aNativeEvent, committed); + if (!committed.IsEmpty()) { + WidgetKeyboardEvent imeEvent(true, NS_KEY_DOWN, mWidget); + InitKeyEvent(aNativeEvent, imeEvent); + imeEvent.mPluginTextEventString.Assign(committed); + DispatchEvent(imeEvent); + } + return true; + } + + if (mWidget->IsPluginFocused() || !IsIMEComposing()) { NSResponder* firstResponder = [[mView window] firstResponder]; WidgetKeyboardEvent keydownEvent(true, NS_KEY_DOWN, mWidget); @@ -1555,6 +1568,14 @@ TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent) } } + // None of what follows is needed for plugin keyboard input. In fact it + // may cause trouble -- for example the call to [mView interpretKeyEvents:] + // can, in e10s mode, cause each key typed to appear twice in an IME + // composition. + if (mWidget->IsPluginFocused()) { + return true; + } + // Let Cocoa interpret the key events, caching IsIMEComposing first. bool wasComposing = IsIMEComposing(); bool interpretKeyEventsCalled = false; diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index b7c233b2eab..734869fe554 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -183,6 +183,9 @@ typedef NSInteger NSEventGestureAxis; // when mouseDown: is called, we store its event here (strong) NSEvent* mLastMouseDownEvent; + // Needed for IME support in e10s mode. Strong. + NSEvent* mLastKeyDownEvent; + // Whether the last mouse down event was blocked from Gecko. BOOL mBlockedLastMouseDown; @@ -312,6 +315,8 @@ typedef NSInteger NSEventGestureAxis; #endif - (void)setUsingOMTCompositor:(BOOL)aUseOMTC; + +- (NSEvent*)lastKeyDownEvent; @end class ChildViewMouseTracker { @@ -527,6 +532,14 @@ public: APZCTreeManager* APZCTM() { return mAPZC ; } + NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, + int32_t aPanelX, int32_t aPanelY, + nsString& aCommitted) MOZ_OVERRIDE; + + NS_IMETHOD SetPluginFocused(bool& aFocused); + + bool IsPluginFocused() { return mPluginFocused; } + protected: virtual ~nsChildView(); @@ -627,6 +640,8 @@ protected: bool mDrawing; bool mIsDispatchPaint; // Is a paint event being dispatched + bool mPluginFocused; + // Used in OMTC BasicLayers mode. Presents the BasicCompositor result // surface to the screen using an OpenGL context. nsAutoPtr mGLPresenter; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 173b006efcb..f4293134f6f 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -44,6 +44,7 @@ #include "nsMenuUtilsX.h" #include "nsMenuBarX.h" #include "NativeKeyBindings.h" +#include "ComplexTextInputPanel.h" #include "gfxContext.h" #include "gfxQuartzSurface.h" @@ -543,6 +544,8 @@ nsresult nsChildView::Create(nsIWidget *aParent, NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed"); mTextInputHandler = new TextInputHandler(this, mView); + mPluginFocused = false; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; @@ -1617,6 +1620,48 @@ nsChildView::NotifyIMEInternal(const IMENotification& aIMENotification) } } +NS_IMETHODIMP +nsChildView::StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, + int32_t aPanelX, int32_t aPanelY, + nsString& aCommitted) +{ + NS_ENSURE_TRUE(mView, NS_ERROR_NOT_AVAILABLE); + + ComplexTextInputPanel* ctiPanel = + ComplexTextInputPanel::GetSharedComplexTextInputPanel(); + + ctiPanel->PlacePanel(aPanelX, aPanelY); + // We deliberately don't use TextInputHandler::GetCurrentKeyEvent() to + // obtain the NSEvent* we pass to InterpretKeyEvent(). This works fine in + // non-e10s mode. But in e10s mode TextInputHandler::HandleKeyDownEvent() + // has already returned, so the relevant KeyEventState* (and its NSEvent*) + // is already out of scope. Furthermore we don't *need* to use it. + // StartPluginIME() is only ever called to start a new IME session when none + // currently exists. So nested IME should never reach here, and so it should + // be fine to use the last key-down event received by -[ChildView keyDown:] + // (as we currently do). + ctiPanel->InterpretKeyEvent([mView lastKeyDownEvent], aCommitted); + + return NS_OK; +} + +NS_IMETHODIMP +nsChildView::SetPluginFocused(bool& aFocused) +{ + if (aFocused == mPluginFocused) { + return NS_OK; + } + if (!aFocused) { + ComplexTextInputPanel* ctiPanel = + ComplexTextInputPanel::GetSharedComplexTextInputPanel(); + if (ctiPanel) { + ctiPanel->CancelComposition(); + } + } + mPluginFocused = aFocused; + return NS_OK; +} + NS_IMETHODIMP_(void) nsChildView::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) @@ -2901,6 +2946,7 @@ NSEvent* gLastDragMouseDownEvent = nil; mExpectingWheelStop = NO; mLastMouseDownEvent = nil; + mLastKeyDownEvent = nil; mClickThroughMouseDownEvent = nil; mDragService = nullptr; @@ -2955,6 +3001,26 @@ NSEvent* gLastDragMouseDownEvent = nil; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } +// ComplexTextInputPanel's interpretKeyEvent hack won't work without this. +// It makes calls to +[NSTextInputContext currentContext], deep in system +// code, return the appropriate context. +- (NSTextInputContext *)inputContext +{ + NSTextInputContext* pluginContext = NULL; + if (mGeckoChild && mGeckoChild->IsPluginFocused()) { + ComplexTextInputPanel* ctiPanel = + ComplexTextInputPanel::GetSharedComplexTextInputPanel(); + if (ctiPanel) { + pluginContext = (NSTextInputContext*) ctiPanel->GetInputContext(); + } + } + if (pluginContext) { + return pluginContext; + } else { + return [super inputContext]; + } +} + - (void)installTextInputHandler:(TextInputHandler*)aHandler { mTextInputHandler = aHandler; @@ -3032,6 +3098,7 @@ NSEvent* gLastDragMouseDownEvent = nil; [mGLContext release]; [mPendingDirtyRects release]; [mLastMouseDownEvent release]; + [mLastKeyDownEvent release]; [mClickThroughMouseDownEvent release]; CGImageRelease(mTopLeftCornerMask); ChildViewMouseTracker::OnDestroyView(self); @@ -5281,10 +5348,18 @@ static int32_t RoundUp(double aDouble) return YES; } +- (NSEvent*)lastKeyDownEvent +{ + return mLastKeyDownEvent; +} + - (void)keyDown:(NSEvent*)theEvent { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + [mLastKeyDownEvent release]; + mLastKeyDownEvent = [theEvent retain]; + // Weird things can happen on keyboard input if the key window isn't in the // current space. For example see bug 1056251. To get around this, always // make sure that, if our window is key, it's also made frontmost. Doing diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 485af8c071a..5dbf2430f1e 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -200,6 +200,12 @@ public: virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) MOZ_OVERRIDE MOZ_FINAL; + NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, + int32_t aPanelX, int32_t aPanelY, + nsString& aCommitted) MOZ_OVERRIDE + { return NS_ERROR_NOT_IMPLEMENTED; } + NS_IMETHOD SetPluginFocused(bool& aFocused) MOZ_OVERRIDE + { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD AttachNativeKeyEvent(mozilla::WidgetKeyboardEvent& aEvent) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD_(bool) ExecuteNativeKeyBinding( NativeKeyBindingsType aType, diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h index 8427526a0b8..345ae67c0bb 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -335,6 +335,7 @@ struct ParamTraits WriteParam(aMsg, aParam.mNativeModifierFlags); WriteParam(aMsg, aParam.mNativeCharacters); WriteParam(aMsg, aParam.mNativeCharactersIgnoringModifiers); + WriteParam(aMsg, aParam.mPluginTextEventString); #endif // An OS-specific native event might be attached in |mNativeKeyEvent|, but // that cannot be copied across process boundaries. @@ -361,6 +362,7 @@ struct ParamTraits && ReadParam(aMsg, aIter, &aResult->mNativeModifierFlags) && ReadParam(aMsg, aIter, &aResult->mNativeCharacters) && ReadParam(aMsg, aIter, &aResult->mNativeCharactersIgnoringModifiers) + && ReadParam(aMsg, aIter, &aResult->mPluginTextEventString) #endif ) { diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 723a16aef40..10bde973a5d 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -113,8 +113,8 @@ typedef void* nsNativeWidget; #define NS_NATIVE_PLUGIN_ID 105 #define NS_IWIDGET_IID \ -{ 0xa7db3e01, 0xb8fe, 0x4122, \ - { 0xbe, 0xa6, 0x45, 0x6c, 0xdd, 0x85, 0x30, 0x64 } }; +{ 0x316E4600, 0x15DB, 0x47AE, \ + { 0xBF, 0xE4, 0x5B, 0xCD, 0xFF, 0x80, 0x80, 0x83 } }; /* * Window shadow styles @@ -2008,6 +2008,28 @@ public: */ NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) = 0; + /** + * Start plugin IME. If this results in a string getting committed, the + * result is in aCommitted (otherwise aCommitted is empty). + * + * aKeyboardEvent The event with which plugin IME is to be started + * panelX and panelY Location in screen coordinates of the IME input panel + * (should be just under the plugin) + * aCommitted The string committed during IME -- otherwise empty + */ + NS_IMETHOD StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent, + int32_t aPanelX, int32_t aPanelY, + nsString& aCommitted) = 0; + + /** + * Tells the widget whether or not a plugin (inside the widget) has the + * keyboard focus. Should be sent when the keyboard focus changes too or + * from a plugin. + * + * aFocused Whether or not a plugin is focused + */ + NS_IMETHOD SetPluginFocused(bool& aFocused) = 0; + /* * Notifies the input context changes. */