Bug 1110888 - Always do plugin IME in main process, even with e10s. r=masayuki,smaug
authorSteven Michaud <smichaud@pobox.com>
Fri, 20 Feb 2015 10:37:02 -0600
changeset 257113 bc5168c2922d017babf46cab931dc7f8ad7c82c6
parent 257112 ca73b950e612af5a0395380a8e676ea2c993136d
child 257114 9ba87714f5cfda74e6dd2eb8fd73678429a9b3e6
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, smaug
bugs1110888
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1110888 - Always do plugin IME in main process, even with e10s. r=masayuki,smaug
dom/html/HTMLObjectElement.cpp
dom/html/HTMLObjectElement.h
dom/html/HTMLSharedObjectElement.cpp
dom/html/HTMLSharedObjectElement.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/plugins/base/nsPluginInstanceOwner.cpp
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/TextEvents.h
widget/cocoa/ComplexTextInputPanel.h
widget/cocoa/ComplexTextInputPanel.mm
widget/cocoa/TextInputHandler.mm
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/nsBaseWidget.h
widget/nsGUIEventIPC.h
widget/nsIWidget.h
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -15,16 +15,20 @@
 #include "nsIDocument.h"
 #include "nsIPluginDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsFormSubmission.h"
 #include "nsIObjectFrame.h"
 #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 {
 
 HTMLObjectElement::HTMLObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                      FromParser aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mIsDoneAddingChildren(!aFromParser)
@@ -99,16 +103,65 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
                                nsIConstraintValidation)
 NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLFormElement)
 
 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)
 {
   return nsGenericHTMLFormElement::GetForm(aForm);
 }
 
 void
 HTMLObjectElement::GetItemValueText(DOMString& aValue)
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -25,16 +25,22 @@ public:
   explicit HTMLObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                              FromParser aFromParser = NOT_FROM_PARSER);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   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;
 
   // nsIDOMHTMLObjectElement
   NS_DECL_NSIDOMHTMLOBJECTELEMENT
 
   virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                               nsIContent *aBindingParent,
--- a/dom/html/HTMLSharedObjectElement.cpp
+++ b/dom/html/HTMLSharedObjectElement.cpp
@@ -12,16 +12,21 @@
 
 #include "nsIDocument.h"
 #include "nsIPluginDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsThreadUtils.h"
 #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)
 
 namespace mozilla {
 namespace dom {
 
 HTMLSharedObjectElement::HTMLSharedObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                                  FromParser aFromParser)
@@ -103,16 +108,27 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
                                nsIChannelEventSink)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLAppletElement, applet)
   NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLEmbedElement, embed)
 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,
                                     nsIContent *aBindingParent,
                                     bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -27,16 +27,21 @@ public:
   explicit HTMLSharedObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                    mozilla::dom::FromParser aFromParser = mozilla::dom::NOT_FROM_PARSER);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
 
+#ifdef XP_MACOSX
+  // nsIDOMEventTarget
+  NS_IMETHOD PostHandleEvent(EventChainPostVisitor& aVisitor) MOZ_OVERRIDE;
+#endif
+
   // nsIDOMHTMLAppletElement
   NS_DECL_NSIDOMHTMLAPPLETELEMENT
 
   // Can't use macro for nsIDOMHTMLEmbedElement because it has conflicts with
   // NS_DECL_NSIDOMHTMLAPPLETELEMENT.
 
   // nsIDOMHTMLEmbedElement
   NS_IMETHOD GetSrc(nsAString &aSrc) MOZ_OVERRIDE;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -276,16 +276,38 @@ parent:
      *    widget should return empty string for composition
      *  if cancel is false,
      *    widget should return the current composition text
      */
     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.
      */
     RequestFocus(bool canRaise);
 
     /**
      * Indicate, based on the current state, that some commands are enabled and
      * some are disabled.
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2099,16 +2099,43 @@ TabParent::RecvEndIMEComposition(const b
   mIMECompositionEnding = false;
   *aNoCompositionEvent = !mIMEEventCountAfterEnding;
   *aComposition = mIMECompositionText;
   mIMECompositionText.Truncate(0);  
   return true;
 }
 
 bool
+TabParent::RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent,
+                              const int32_t& aPanelX, const int32_t& aPanelY,
+                              nsString* aCommitted)
+{
+  nsCOMPtr<nsIWidget> 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<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    return true;
+  }
+  widget->SetPluginFocused((bool&)aFocused);
+  return true;
+}
+
+bool
 TabParent::RecvGetInputContext(int32_t* aIMEEnabled,
                                int32_t* aIMEOpen,
                                intptr_t* aNativeIMEContext)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     *aIMEEnabled = IMEState::DISABLED;
     *aIMEOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -187,16 +187,21 @@ public:
     virtual bool RecvNotifyIMEEditorRect(const LayoutDeviceIntRect& aRect) MOZ_OVERRIDE;
     virtual bool RecvNotifyIMEPositionChange(
                    const LayoutDeviceIntRect& aEditorRect,
                    InfallibleTArray<LayoutDeviceIntRect>&& aCompositionRects,
                    const LayoutDeviceIntRect& aCaretRect) MOZ_OVERRIDE;
     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;
     virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
                                      const int32_t& aIMEOpen,
                                      const nsString& aType,
                                      const nsString& aInputmode,
                                      const nsString& aActionHint,
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1832,29 +1832,40 @@ TranslateToNPCocoaEvent(WidgetGUIEvent* 
       }
       break;
     }
     case NS_KEY_DOWN:
     case NS_KEY_UP:
     {
       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<const UniChar*>(nativeChars),
-                                                    keyEvent->mNativeCharacters.Length());
-      const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get();
-      cocoaEvent.data.key.charactersIgnoringModifiers =
-        (NPNSString*)::CFStringCreateWithCharacters(NULL,
-                                                    reinterpret_cast<const UniChar*>(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<const UniChar*>(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<const UniChar*>(nativeChars),
+                                         keyEvent->mNativeCharacters.Length());
+        const char16_t* nativeCharsIgnoringModifiers = keyEvent->mNativeCharactersIgnoringModifiers.get();
+        cocoaEvent.data.key.charactersIgnoringModifiers = (NPNSString*)
+          ::CFStringCreateWithCharacters(NULL,
+                                         reinterpret_cast<const UniChar*>(nativeCharsIgnoringModifiers),
+                                         keyEvent->mNativeCharactersIgnoringModifiers.Length());
+      }
       break;
     }
     case NS_FOCUS_CONTENT:
     case NS_BLUR_CONTENT:
       cocoaEvent.data.focus.hasFocus = (anEvent->message == NS_FOCUS_CONTENT);
       break;
     default:
       break;
@@ -1916,66 +1927,47 @@ nsEventStatus nsPluginInstanceOwner::Pro
     PerformDelayedBlurs();
   }
 
   NPCocoaEvent cocoaEvent = TranslateToNPCocoaEvent(const_cast<WidgetGUIEvent*>(&anEvent), mPluginFrame);
   if (cocoaEvent.type == (NPCocoaEventType)0) {
     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<const UniChar*>(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<const UniChar*>(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<const UniChar*>(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);
   bool leftMouseButtonDown = (anEvent.message == NS_MOUSE_BUTTON_DOWN) &&
                              (anEvent.AsMouseEvent()->button == WidgetMouseEvent::eLeftButton);
   if (handled && !(leftMouseButtonDown && !mContentFocused)) {
     rv = nsEventStatus_eConsumeNoDefault;
   }
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -434,16 +434,38 @@ PuppetWidget::NotifyIMEInternal(const IM
       return NotifyIMEOfMouseButtonEvent(aIMENotification);
     case NOTIFY_IME_OF_POSITION_CHANGE:
       return NotifyIMEOfPositionChange();
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
+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)
 {
 #ifndef MOZ_CROSS_PROCESS_IME
   return;
 #endif
 
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -191,16 +191,22 @@ public:
   nsIntSize GetScreenDimensions();
 
   // Get the size of the chrome of the window that this tab belongs to.
   nsIntPoint GetChromeDimensions();
 
   // 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;
 
   virtual nsresult NotifyIMEInternal(
                      const IMENotification& aIMENotification) MOZ_OVERRIDE;
 
 private:
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -160,16 +160,19 @@ public:
   uint32_t mUniqueId;
 
 #ifdef XP_MACOSX
   // Values given by a native NSEvent, for use with Cocoa NPAPI plugins.
   uint16_t mNativeKeyCode;
   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.
   // Otherwise, false.
   bool ShouldCauseKeypressEvents() const;
 
   void GetDOMKeyName(nsAString& aKeyName)
   {
@@ -246,16 +249,24 @@ public:
     mKeyNameIndex = aEvent.mKeyNameIndex;
     mCodeNameIndex = aEvent.mCodeNameIndex;
     mKeyValue = aEvent.mKeyValue;
     mCodeValue = aEvent.mCodeValue;
     // Don't copy mNativeKeyEvent because it may be referred after its instance
     // 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:
   static const char16_t* kKeyNames[];
   static const char16_t* kCodeNames[];
   typedef nsDataHashtable<nsStringHashKey,
                           KeyNameIndex> KeyNameIndexHashtable;
   typedef nsDataHashtable<nsStringHashKey,
--- a/widget/cocoa/ComplexTextInputPanel.h
+++ b/widget/cocoa/ComplexTextInputPanel.h
@@ -31,16 +31,18 @@
 #include "nsString.h"
 #include "npapi.h"
 
 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() {};
 };
 
 #endif // ComplexTextInputPanel_h_
--- a/widget/cocoa/ComplexTextInputPanel.mm
+++ b/widget/cocoa/ComplexTextInputPanel.mm
@@ -127,33 +127,18 @@ extern "C" OSStatus TSMProcessRawKeyEven
     [self cancelComposition];
   }
 }
 
 - (void)interpretKeyEvent:(NSEvent*)event string:(NSString**)string
 {
   *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]) {
     // Don't show the input method window for dead keys
     if ([[event characters] length] > 0) {
       [self orderFront:nil];
     }
     return;
@@ -217,19 +202,22 @@ extern "C" OSStatus TSMProcessRawKeyEven
 
 @end
 
 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;
 };
 
 ComplexTextInputPanelPrivate::ComplexTextInputPanelPrivate()
 {
   mPanel = [[ComplexTextInputPanelImpl alloc] init];
@@ -246,31 +234,20 @@ ComplexTextInputPanel::GetSharedComplexT
   static ComplexTextInputPanelPrivate *sComplexTextInputPanelPrivate;
   if (!sComplexTextInputPanelPrivate) {
     sComplexTextInputPanelPrivate = new ComplexTextInputPanelPrivate();
   }
   return sComplexTextInputPanelPrivate;
 }
 
 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);
   }
 }
 
 bool
 ComplexTextInputPanelPrivate::IsInComposition()
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -1518,17 +1518,30 @@ TextInputHandler::HandleKeyDownEvent(NSE
      [aNativeEvent modifierFlags], GetCharacters([aNativeEvent characters]),
      GetCharacters([aNativeEvent charactersIgnoringModifiers])));
 
   nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
 
   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);
     InitKeyEvent(aNativeEvent, keydownEvent);
 
     currentKeyEvent->mKeyDownHandled = DispatchEvent(keydownEvent);
     if (Destroyed()) {
       PR_LOG(gLog, PR_LOG_ALWAYS,
@@ -1550,16 +1563,24 @@ TextInputHandler::HandleKeyDownEvent(NSE
     if (currentKeyEvent->IsDefaultPrevented()) {
       PR_LOG(gLog, PR_LOG_ALWAYS,
         ("%p TextInputHandler::HandleKeyDownEvent, "
          "keydown event's default is prevented", this));
       return true;
     }
   }
 
+  // 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;
   if (IsIMEEnabled() || IsASCIICapableOnly()) {
     PR_LOG(gLog, PR_LOG_ALWAYS,
       ("%p TextInputHandler::HandleKeyDownEvent, calling interpretKeyEvents",
        this));
     [mView interpretKeyEvents:[NSArray arrayWithObject:aNativeEvent]];
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -178,16 +178,19 @@ typedef NSInteger NSEventGestureAxis;
   // for Gecko's leak detector to detect leaked TextInputHandler objects.
   // This is initialized by [mozView installTextInputHandler:aHandler] and
   // cleared by [mozView uninstallTextInputHandler].
   mozilla::widget::TextInputHandler* mTextInputHandler;  // [WEAK]
 
   // 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;
 
   // when acceptsFirstMouse: is called, we store the event here (strong)
   NSEvent* mClickThroughMouseDownEvent;
 
   // rects that were invalidated during a draw, so have pending drawing
   NSMutableArray* mPendingDirtyRects;
@@ -307,16 +310,18 @@ typedef NSInteger NSEventGestureAxis;
 #ifdef __LP64__
 - (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
                      scrollOverflowX:(double)anOverflowX
                      scrollOverflowY:(double)anOverflowY
               viewPortIsOverscrolled:(BOOL)aViewPortIsOverscrolled;
 #endif
 
 - (void)setUsingOMTCompositor:(BOOL)aUseOMTC;
+
+- (NSEvent*)lastKeyDownEvent;
 @end
 
 class ChildViewMouseTracker {
 
 public:
 
   static void MouseMoved(NSEvent* aEvent);
   static void MouseScrolled(NSEvent* aEvent);
@@ -522,16 +527,24 @@ public:
   }
 
   mozilla::TemporaryRef<mozilla::gfx::DrawTarget> StartRemoteDrawing() MOZ_OVERRIDE;
   void EndRemoteDrawing() MOZ_OVERRIDE;
   void CleanupRemoteDrawing() MOZ_OVERRIDE;
 
   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();
 
   void              ReportMoveEvent();
   void              ReportSizeEvent();
 
   // override to create different kinds of child views. Autoreleases, so
   // caller must retain.
@@ -622,16 +635,18 @@ protected:
   // use it.
   // ** We'll need to reinitialize this if the backing resolution changes. **
   mutable CGFloat       mBackingScaleFactor;
 
   bool                  mVisible;
   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<GLPresenter> mGLPresenter;
 
   mozilla::UniquePtr<mozilla::VibrancyManager> mVibrancyManager;
 
   static uint32_t sLastInputEventCount;
 
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -39,16 +39,17 @@
 #include "nsClipboard.h"
 #include "nsCursorManager.h"
 #include "nsWindowMap.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaUtils.h"
 #include "nsMenuUtilsX.h"
 #include "nsMenuBarX.h"
 #include "NativeKeyBindings.h"
+#include "ComplexTextInputPanel.h"
 
 #include "gfxContext.h"
 #include "gfxQuartzSurface.h"
 #include "gfxUtils.h"
 #include "nsRegion.h"
 #include "Layers.h"
 #include "ClientLayerManager.h"
 #include "mozilla/layers/LayerManagerComposite.h"
@@ -538,16 +539,18 @@ nsresult nsChildView::Create(nsIWidget *
   // if this is a ChildView, make sure that our per-window data
   // is set up
   if ([mView isKindOfClass:[ChildView class]])
     [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:[mView window]];
 
   NS_ASSERTION(!mTextInputHandler, "mTextInputHandler has already existed");
   mTextInputHandler = new TextInputHandler(this, mView);
 
+  mPluginFocused = false;
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 // Creates the appropriate child view. Override to create something other than
 // our |ChildView| object. Autoreleases, so caller must retain.
 NSView*
@@ -1612,16 +1615,58 @@ nsChildView::NotifyIMEInternal(const IME
     case NOTIFY_IME_OF_SELECTION_CHANGE:
       NS_ENSURE_TRUE(mTextInputHandler, NS_ERROR_NOT_AVAILABLE);
       mTextInputHandler->OnSelectionChange();
     default:
       return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
+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)
 {
   NS_ENSURE_TRUE_VOID(mTextInputHandler);
 
   if (mTextInputHandler->IsOrWouldBeFocused()) {
     if (aContext.IsPasswordEditor()) {
@@ -2896,16 +2941,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
   if ((self = [super initWithFrame:inFrame])) {
     mGeckoChild = inChild;
     mPendingDisplay = NO;
     mBlockedLastMouseDown = NO;
     mExpectingWheelStop = NO;
 
     mLastMouseDownEvent = nil;
+    mLastKeyDownEvent = nil;
     mClickThroughMouseDownEvent = nil;
     mDragService = nullptr;
 
     mGestureState = eGestureState_None;
     mCumulativeMagnification = 0.0;
     mCumulativeRotation = 0.0;
 
     // We can't call forceRefreshOpenGL here because, in order to work around
@@ -2950,16 +2996,36 @@ NSEvent* gLastDragMouseDownEvent = nil;
                                                name:NSViewGlobalFrameDidChangeNotification
                                              object:self];
 
   return self;
 
   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;
 }
 
 - (void)uninstallTextInputHandler
 {
   mTextInputHandler = nullptr;
@@ -3027,16 +3093,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
 - (void)dealloc
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [mGLContext release];
   [mPendingDirtyRects release];
   [mLastMouseDownEvent release];
+  [mLastKeyDownEvent release];
   [mClickThroughMouseDownEvent release];
   CGImageRelease(mTopLeftCornerMask);
   ChildViewMouseTracker::OnDestroyView(self);
 
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
 
   [super dealloc];
@@ -5276,20 +5343,28 @@ static int32_t RoundUp(double aDouble)
 // Cocoa will call this after the menu system returns "NO" for "performKeyEquivalent:".
 // We want all they key events we can get so just return YES. In particular, this fixes
 // ctrl-tab - we don't get a "keyDown:" call for that without this.
 - (BOOL)_wantsKeyDownForEvent:(NSEvent*)event
 {
   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
   // this automatically switches to whatever space our window is in.  Safari
   // does something similar.  Our window should normally always be key --
   // otherwise why is the OS sending us a key down event?  But it's just
   // possible we're in Gecko's hidden window, so we check first.
   NSWindow *viewWindow = [self window];
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -195,16 +195,22 @@ public:
   virtual void            FreeNativeData(void * data, uint32_t aDataType) MOZ_OVERRIDE {}
   NS_IMETHOD              BeginResizeDrag(mozilla::WidgetGUIEvent* aEvent,
                                           int32_t aHorizontal,
                                           int32_t aVertical) MOZ_OVERRIDE;
   NS_IMETHOD              BeginMoveDrag(mozilla::WidgetMouseEvent* aEvent) MOZ_OVERRIDE;
   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,
                             const mozilla::WidgetKeyboardEvent& aEvent,
                             DoCommandCallback aCallback,
                             void* aCallbackData) MOZ_OVERRIDE { return false; }
   NS_IMETHOD              SetLayersAcceleration(bool aEnabled) MOZ_OVERRIDE;
   virtual bool            ComputeShouldAccelerate(bool aDefault);
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -330,16 +330,17 @@ struct ParamTraits<mozilla::WidgetKeyboa
     WriteParam(aMsg, aParam.mIsRepeat);
     WriteParam(aMsg, aParam.location);
     WriteParam(aMsg, aParam.mUniqueId);
 #ifdef XP_MACOSX
     WriteParam(aMsg, aParam.mNativeKeyCode);
     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.
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     uint32_t keyNameIndex = 0, codeNameIndex = 0;
@@ -356,16 +357,17 @@ struct ParamTraits<mozilla::WidgetKeyboa
         ReadParam(aMsg, aIter, &aResult->mIsRepeat) &&
         ReadParam(aMsg, aIter, &aResult->location) &&
         ReadParam(aMsg, aIter, &aResult->mUniqueId)
 #ifdef XP_MACOSX
         && ReadParam(aMsg, aIter, &aResult->mNativeKeyCode)
         && ReadParam(aMsg, aIter, &aResult->mNativeModifierFlags)
         && ReadParam(aMsg, aIter, &aResult->mNativeCharacters)
         && ReadParam(aMsg, aIter, &aResult->mNativeCharactersIgnoringModifiers)
+        && ReadParam(aMsg, aIter, &aResult->mPluginTextEventString)
 #endif
         )
     {
       aResult->mKeyNameIndex = static_cast<mozilla::KeyNameIndex>(keyNameIndex);
       aResult->mCodeNameIndex =
         static_cast<mozilla::CodeNameIndex>(codeNameIndex);
       aResult->mNativeKeyEvent = nullptr;
       return true;
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -108,18 +108,18 @@ typedef void* nsNativeWidget;
 #if defined(MOZ_WIDGET_GTK)
 // set/get nsPluginNativeWindowGtk, e10s specific
 #define NS_NATIVE_PLUGIN_OBJECT_PTR    104
 #endif
 // See RegisterPluginWindowForRemoteUpdates
 #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
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -2003,16 +2003,38 @@ public:
     /**
      * Notify IME of the specified notification.
      *
      * @return If the notification is mouse button event and it's consumed by
      *         IME, this returns NS_SUCCESS_EVENT_CONSUMED.
      */
     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.
      */
     NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
                                       const InputContextAction& aAction) = 0;
 
     /*
      * Get current input context.