Bug 1110888 - Always do plugin IME in main process, even with e10s. r=masayuki,smaug a=lmandel
authorSteven Michaud <smichaud@pobox.com>
Fri, 20 Feb 2015 12:56:25 -0600
changeset 249829 5e7abc21fea7a438198ebcc616f854add2ad5bc9
parent 249828 8f5501a49164a46c3ef8211b255fe7e2de8e6e16
child 249830 915d0431328bb22ae8680eb948a7b3f7d68e6f33
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki, smaug, lmandel
bugs1110888
milestone37.0a2
Bug 1110888 - Always do plugin IME in main process, even with e10s. r=masayuki,smaug a=lmandel
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)
@@ -93,16 +97,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(nsAString& 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
+
   // nsIDOMHTMLObjectElement
   NS_DECL_NSIDOMHTMLOBJECTELEMENT
 
   virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                               nsIContent *aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) MOZ_OVERRIDE;
--- 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
@@ -253,16 +253,38 @@ parent:
      *  if cancel is PR_TRUE,
      *    widget should return empty string for composition
      *  if cancel is PR_FALSE,
      *    widget should return the current composition text
      */
     prio(urgent) sync EndIMEComposition(bool cancel) returns (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
@@ -1892,16 +1892,43 @@ TabParent::RecvEndIMEComposition(const b
 
   mIMECompositionEnding = false;
   *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
@@ -180,16 +180,21 @@ public:
                                                bool* aConsumedByIME) MOZ_OVERRIDE;
     virtual bool RecvNotifyIMEEditorRect(const nsIntRect& aRect) MOZ_OVERRIDE;
     virtual bool RecvNotifyIMEPositionChange(
                    const nsIntRect& aEditoRect,
                    const InfallibleTArray<nsIntRect>& aCompositionRects,
                    const nsIntRect& aCaretRect) MOZ_OVERRIDE;
     virtual bool RecvEndIMEComposition(const bool& aCancel,
                                        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
@@ -1838,29 +1838,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;
@@ -1922,66 +1933,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
@@ -450,16 +450,38 @@ PuppetWidget::NotifyIME(const IMENotific
       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
@@ -196,16 +196,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;
 
 private:
   nsresult Paint();
 
   void SetChild(PuppetWidget* aChild);
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -156,16 +156,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
 
   void GetDOMKeyName(nsAString& aKeyName)
   {
     if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
       aKeyName = mKeyValue;
       return;
     }
@@ -201,16 +204,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
   }
 };
 
 
 /******************************************************************************
  * mozilla::InternalBeforeAfterKeyboardEvent
  *
  * This is extended from WidgetKeyboardEvent and is mapped to DOM event
--- 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);
@@ -524,16 +529,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.
@@ -621,16 +634,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*
@@ -1610,16 +1613,58 @@ nsChildView::NotifyIME(const IMENotifica
     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->IsFocused()) {
     if (aContext.IsPasswordEditor()) {
@@ -2864,16 +2909,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
@@ -2918,16 +2964,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;
@@ -2995,16 +3061,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];
@@ -5235,20 +5302,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
@@ -191,16 +191,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 { return NS_ERROR_NOT_IMPLEMENTED; }
+  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
@@ -331,16 +331,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;
@@ -357,16 +358,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
@@ -98,18 +98,18 @@ typedef void* nsNativeWidget;
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #endif
 
 #define NS_IWIDGET_IID \
-{ 0x13239ca, 0xaf3f, 0x4f27, \
-  { 0xaf, 0x83, 0x47, 0xa9, 0x82, 0x3d, 0x99, 0xee } };
+{ 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
@@ -1936,16 +1936,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.