Bug 1257759 part.5 PluginInstanceChild should post received native key event to chrome process if the key combination may be a shortcut key r=jimm
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 19 Apr 2016 20:09:37 +0900
changeset 294599 1952b7fec843cbb6e1b402c7a0e2a42ba9ba335f
parent 294598 6d2f821d43475cc6c1f75844fae3b8f8a7308b11
child 294600 2b6daf0220a4d81cdb4825f7261b5627c80c8f00
push id75605
push usermasayuki@d-toybox.com
push dateSat, 23 Apr 2016 09:56:31 +0000
treeherdermozilla-inbound@beab3af262e3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1257759
milestone48.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 1257759 part.5 PluginInstanceChild should post received native key event to chrome process if the key combination may be a shortcut key r=jimm When PluginInstanceChild receives native key events, it should post the events to the chrome process first for checking if the key combination is reserved. However, posting all key events to the chrome process may make damage to the performance of text input. Therefore, this patch starts to post a key event whose key combination may be a shortcut key. However, for avoiding to shuffle the event order, it posts following key events until all posted key events are handled by the chrome process. For receiving response from widget, this patch defines nsIKeyEventInPluginCallback. It's specified by nsIWidget::OnWindowedPluginKeyEvent() for ensuring the caller will receive the reply. Basically, the caller of nsIWidget::OnWindowedPluginKeyEvent() should reply to the child process. However, if the widget is a PuppetWidget, it cannot return the result synchronously. Therefore, PuppetWidget::OnWindowedPluginKeyEvent() returns NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY and stores the callback to mKeyEventInPluginCallbacks. Then, TabParent::HandledWindowedPluginKeyEvent() will call PuppetWidget::HandledWindowedPluginKeyEvent(). MozReview-Commit-ID: G6brOU26NwQ
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/plugins/base/PluginPRLibrary.cpp
dom/plugins/base/PluginPRLibrary.h
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
dom/plugins/ipc/PluginLibrary.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/plugins/ipc/PluginModuleParent.h
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/moz.build
widget/nsBaseWidget.cpp
widget/nsIKeyEventInPluginCallback.h
widget/nsIWidget.h
widget/windows/WinMessages.h
widget/windows/WinNativeEventData.h
widget/windows/moz.build
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
xpcom/base/ErrorList.h
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -62,16 +62,17 @@ using mozilla::WritingMode from "mozilla
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using nsIWidget::TouchPointerState from "nsIWidget.h";
 using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
 using class mozilla::dom::ipc::StructuredCloneData from "ipc/IPCMessageUtils.h";
 using mozilla::EventMessage from "mozilla/EventForwards.h";
 using nsEventStatus from "mozilla/EventForwards.h";
 using nsSizeMode from "nsIWidgetListener.h";
 using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
+using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
 
 namespace mozilla {
 namespace dom {
 
 struct NativeKeyBinding
 {
   CommandInt[] singleLineCommands;
   CommandInt[] multiLineCommands;
@@ -279,16 +280,26 @@ parent:
     prio(urgent) async SetPluginFocused(bool aFocused);
 
     /**
      * Set IME candidate window by windowless plugin if plugin has focus.
      */
     async SetCandidateWindowForPlugin(CandidateWindowPosition aPosition);
 
     /**
+     * Notifies the parent process of native key event data received in a
+     * plugin process directly.
+     *
+     * aKeyEventData    The native key event data.  The actual type copied into
+     *                  NativeEventData depending on the caller.  Please check
+     *                  PluginInstanceChild.
+     */
+    prio(urgent) async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
+
+    /**
      *  When plugin event isn't consumed, call this
      */
     async DefaultProcOfPluginEvent(WidgetPluginEvent aEvent);
 
     /**
      * 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.
      */
@@ -727,16 +738,28 @@ child:
 
     /**
      * Tells the root child docShell whether or not to use
      * global history. This is sent right after the PBrowser
      * is bound to a frameloader element.
      */
     async SetUseGlobalHistory(bool aUse);
 
+    /**
+     * HandledWindowedPluginKeyEvent() is always called after posting a native
+     * key event with OnWindowedPluginKeyEvent().
+     *
+     * @param aKeyEventData      The key event which was posted to the parent
+     *                           process.
+     * @param aIsConsumed        true if aKeyEventData is consumed in the
+     *                           parent process.  Otherwise, false.
+     */
+    async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
+                                        bool aIsConsumed);
+
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2478,16 +2478,28 @@ TabChild::RecvNavigateByKey(const bool& 
     }
 
     SendRequestFocus(false);
   }
 
   return true;
 }
 
+bool
+TabChild::RecvHandledWindowedPluginKeyEvent(
+            const NativeEventData& aKeyEventData,
+            const bool& aIsConsumed)
+{
+  if (NS_WARN_IF(!mPuppetWidget)) {
+    return true;
+  }
+  mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+  return true;
+}
+
 PRenderFrameChild*
 TabChild::AllocPRenderFrameChild()
 {
     return new RenderFrameChild();
 }
 
 bool
 TabChild::DeallocPRenderFrameChild(PRenderFrameChild* aFrame)
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -570,16 +570,20 @@ public:
                                    const int32_t& aModifierMask) override;
 
   virtual bool RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
                                                   const float& aVolume,
                                                   const bool& aMuted) override;
 
   virtual bool RecvSetUseGlobalHistory(const bool& aUse) override;
 
+  virtual bool RecvHandledWindowedPluginKeyEvent(
+                 const mozilla::NativeEventData& aKeyEventData,
+                 const bool& aIsConsumed) override;
+
   /**
    * Native widget remoting protocol for use with windowed plugins with e10s.
    */
   PPluginWidgetChild* AllocPPluginWidgetChild() override;
 
   bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
 
   nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1894,16 +1894,54 @@ TabParent::RecvOnEventNeedingAckHandled(
 
   // While calling OnEventNeedingAckHandled(), TabParent *might* be destroyed
   // since it may send notifications to IME.
   RefPtr<TabParent> kungFuDeathGrip(this);
   mContentCache.OnEventNeedingAckHandled(widget, aMessage);
   return true;
 }
 
+void
+TabParent::HandledWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+                                         bool aIsConsumed)
+{
+  bool ok = SendHandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+  NS_WARN_IF(!ok);
+}
+
+bool
+TabParent::RecvOnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData)
+{
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (NS_WARN_IF(!widget)) {
+    // Notifies the plugin process of the key event being not consumed by us.
+    HandledWindowedPluginKeyEvent(aKeyEventData, false);
+    return true;
+  }
+  nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    // Notifies the plugin process of the key event being not consumed by us.
+    HandledWindowedPluginKeyEvent(aKeyEventData, false);
+    return true;
+  }
+
+  // If the key event is posted to another process, we need to wait a call
+  // of HandledWindowedPluginKeyEvent().  So, nothing to do here in this case.
+  if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
+    return true;
+  }
+
+  // Otherwise, the key event is handled synchronously.  Let's notify the
+  // plugin process of the key event's result.
+  bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
+  HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
+
+  return true;
+}
+
 bool
 TabParent::RecvRequestFocus(const bool& aCanRaise)
 {
   nsCOMPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager();
   if (!fm) {
     return true;
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -17,16 +17,17 @@
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDOMEventListener.h"
+#include "nsIKeyEventInPluginCallback.h"
 #include "nsISecureBrowserUI.h"
 #include "nsITabParent.h"
 #include "nsIWebBrowserPersistable.h"
 #include "nsIXULBrowserWindow.h"
 #include "nsRefreshDriver.h"
 #include "nsWeakReference.h"
 #include "Units.h"
 #include "nsIWidget.h"
@@ -77,16 +78,17 @@ namespace ipc {
 class StructuredCloneData;
 } // ipc namespace
 
 class TabParent final : public PBrowserParent
                       , public nsIDOMEventListener
                       , public nsITabParent
                       , public nsIAuthPromptProvider
                       , public nsISecureBrowserUI
+                      , public nsIKeyEventInPluginCallback
                       , public nsSupportsWeakReference
                       , public TabContext
                       , public nsAPostRefreshObserver
                       , public nsIWebBrowserPersistable
 {
   typedef mozilla::dom::ClonedMessageData ClonedMessageData;
 
   virtual ~TabParent();
@@ -250,16 +252,25 @@ public:
   virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
                                    const int32_t& aIMEOpen,
                                    const nsString& aType,
                                    const nsString& aInputmode,
                                    const nsString& aActionHint,
                                    const int32_t& aCause,
                                    const int32_t& aFocusChange) override;
 
+
+  // See nsIKeyEventInPluginCallback
+  virtual void HandledWindowedPluginKeyEvent(
+                 const NativeEventData& aKeyEventData,
+                 bool aIsConsumed) override;
+
+  virtual bool RecvOnWindowedPluginKeyEvent(
+                 const NativeEventData& aKeyEventData) override;
+
   virtual bool RecvRequestFocus(const bool& aCanRaise) override;
 
   virtual bool
   RecvEnableDisableCommands(const nsString& aAction,
                             nsTArray<nsCString>&& aEnabledCommands,
                             nsTArray<nsCString>&& aDisabledCommands) override;
 
   virtual bool
--- a/dom/plugins/base/PluginPRLibrary.cpp
+++ b/dom/plugins/base/PluginPRLibrary.cpp
@@ -329,9 +329,22 @@ PluginPRLibrary::GetScrollCaptureContain
 }
 nsresult
 PluginPRLibrary::UpdateScrollState(NPP aInstance, bool aIsScrolling)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 #endif
 
+nsresult
+PluginPRLibrary::HandledWindowedPluginKeyEvent(
+                   NPP aInstance,
+                   const NativeEventData& aNativeKeyData,
+                   bool aIsConsumed)
+{
+  nsNPAPIPluginInstance* instance = (nsNPAPIPluginInstance*)aInstance->ndata;
+  if (NS_WARN_IF(!instance)) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/dom/plugins/base/PluginPRLibrary.h
+++ b/dom/plugins/base/PluginPRLibrary.h
@@ -124,16 +124,20 @@ public:
     virtual void DidComposite(NPP aInstance) override { }
     virtual void GetLibraryPath(nsACString& aPath) { aPath.Assign(mFilePath); }
     virtual nsresult GetRunID(uint32_t* aRunID) override { return NS_ERROR_NOT_IMPLEMENTED; }
     virtual void SetHasLocalInstance() override { }
 #if defined(XP_WIN)
     virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
     virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling) override;
 #endif
+    virtual nsresult HandledWindowedPluginKeyEvent(
+                       NPP aInstance,
+                       const mozilla::NativeEventData& aNativeKeyData,
+                       bool aIsCOnsumed) override;
 
 private:
     NP_InitializeFunc mNP_Initialize;
     NP_ShutdownFunc mNP_Shutdown;
     NP_GetMIMEDescriptionFunc mNP_GetMIMEDescription;
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
     NP_GetValueFunc mNP_GetValue;
 #endif
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1165,16 +1165,33 @@ nsNPAPIPluginInstance::UpdateScrollState
   if (RUNNING != mRunning)
     return NS_OK;
 
   AutoPluginLibraryCall library(this);
   return !library ? NS_ERROR_FAILURE : library->UpdateScrollState(&mNPP, aIsScrolling);
 }
 #endif
 
+nsresult
+nsNPAPIPluginInstance::HandledWindowedPluginKeyEvent(
+                         const NativeEventData& aKeyEventData,
+                         bool aIsConsumed)
+{
+  if (NS_WARN_IF(!mPlugin)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  PluginLibrary* library = mPlugin->GetLibrary();
+  if (NS_WARN_IF(!library)) {
+    return NS_ERROR_FAILURE;
+  }
+  return library->HandledWindowedPluginKeyEvent(&mNPP, aKeyEventData,
+                                                aIsConsumed);
+}
+
 void
 nsNPAPIPluginInstance::DidComposite()
 {
   if (RUNNING != mRunning)
     return;
 
   AutoPluginLibraryCall library(this);
   library->DidComposite(&mNPP);
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -23,16 +23,17 @@
 #include "nsIRunnable.h"
 #include "GLContextTypes.h"
 #include "AndroidSurfaceTexture.h"
 #include "AndroidBridge.h"
 #include <map>
 class PluginEventRunnable;
 #endif
 
+#include "mozilla/EventForwards.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/PluginLibrary.h"
 #include "mozilla/WeakPtr.h"
 
 class nsPluginStreamListenerPeer; // browser-initiated stream class
 class nsNPAPIPluginStreamListener; // plugin-initiated stream class
 class nsIPluginInstanceOwner;
 class nsIOutputStream;
@@ -120,16 +121,19 @@ public:
   nsresult InvalidateRect(NPRect *invalidRect);
   nsresult InvalidateRegion(NPRegion invalidRegion);
   nsresult GetMIMEType(const char* *result);
   nsresult GetJSContext(JSContext* *outContext);
 #if defined(XP_WIN)
   nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer **aContainer);
   nsresult UpdateScrollState(bool aIsScrolling);
 #endif
+  nsresult HandledWindowedPluginKeyEvent(
+             const mozilla::NativeEventData& aKeyEventData,
+             bool aIsConsumed);
   nsPluginInstanceOwner* GetOwner();
   void SetOwner(nsPluginInstanceOwner *aOwner);
   void DidComposite();
 
   bool HasAudioChannelAgent() const
   {
     return !!mAudioChannelAgent;
   }
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -413,16 +413,17 @@ nsPluginInstanceOwner::~nsPluginInstance
     mInstance->SetOwner(nullptr);
   }
 }
 
 NS_IMPL_ISUPPORTS(nsPluginInstanceOwner,
                   nsIPluginInstanceOwner,
                   nsIDOMEventListener,
                   nsIPrivacyTransitionObserver,
+                  nsIKeyEventInPluginCallback,
                   nsISupportsWeakReference)
 
 nsresult
 nsPluginInstanceOwner::SetInstance(nsNPAPIPluginInstance *aInstance)
 {
   NS_ASSERTION(!mInstance || !aInstance, "mInstance should only be set or unset!");
 
   // If we're going to null out mInstance after use, be sure to call
@@ -988,18 +989,68 @@ nsPluginInstanceOwner::RequestCommitOrCa
 
   if (aCommitted) {
     widget->NotifyIME(widget::REQUEST_TO_COMMIT_COMPOSITION);
   } else {
     widget->NotifyIME(widget::REQUEST_TO_CANCEL_COMPOSITION);
   }
   return true;
 }
+
 #endif // #ifdef XP_WIN
 
+void
+nsPluginInstanceOwner::HandledWindowedPluginKeyEvent(
+                         const NativeEventData& aKeyEventData,
+                         bool aIsConsumed)
+{
+  if (NS_WARN_IF(!mInstance)) {
+    return;
+  }
+  nsresult rv =
+    mInstance->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+  NS_WARN_IF(NS_FAILED(rv));
+}
+
+void
+nsPluginInstanceOwner::OnWindowedPluginKeyEvent(
+                         const NativeEventData& aKeyEventData)
+{
+  if (NS_WARN_IF(!mPluginFrame)) {
+    // Notifies the plugin process of the key event being not consumed by us.
+    HandledWindowedPluginKeyEvent(aKeyEventData, false);
+    return;
+  }
+
+  nsCOMPtr<nsIWidget> widget = mPluginFrame->PresContext()->GetRootWidget();
+  if (NS_WARN_IF(!widget)) {
+    // Notifies the plugin process of the key event being not consumed by us.
+    HandledWindowedPluginKeyEvent(aKeyEventData, false);
+    return;
+  }
+
+  nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    // Notifies the plugin process of the key event being not consumed by us.
+    HandledWindowedPluginKeyEvent(aKeyEventData, false);
+    return;
+  }
+
+  // If the key event is posted to another process, we need to wait a call
+  // of HandledWindowedPluginKeyEvent().  So, nothing to do here in this case.
+  if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
+    return;
+  }
+
+  // Otherwise, the key event is handled synchronously.  Let's notify the
+  // plugin process of the key event's result.
+  bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
+  HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
+}
+
 NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
 {
 #ifdef XP_MACOSX
   mEventModel = static_cast<NPEventModel>(eventModel);
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsPluginInstanceOwner_h_
 #define nsPluginInstanceOwner_h_
 
 #include "mozilla/Attributes.h"
 #include "npapi.h"
 #include "nsCOMPtr.h"
+#include "nsIKeyEventInPluginCallback.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsIPrivacyTransitionObserver.h"
 #include "nsIDOMEventListener.h"
 #include "nsPluginHost.h"
 #include "nsPluginNativeWindow.h"
 #include "nsWeakReference.h"
 #include "gfxRect.h"
 
@@ -47,20 +48,21 @@ using mozilla::widget::PuppetWidget;
 #ifdef MOZ_X11
 #ifdef MOZ_WIDGET_QT
 #include "gfxQtNativeRenderer.h"
 #else
 #include "gfxXlibNativeRenderer.h"
 #endif
 #endif
 
-class nsPluginInstanceOwner final : public nsIPluginInstanceOwner,
-                                    public nsIDOMEventListener,
-                                    public nsIPrivacyTransitionObserver,
-                                    public nsSupportsWeakReference
+class nsPluginInstanceOwner final : public nsIPluginInstanceOwner
+                                  , public nsIDOMEventListener
+                                  , public nsIPrivacyTransitionObserver
+                                  , public nsIKeyEventInPluginCallback
+                                  , public nsSupportsWeakReference
 {
 public:
   typedef mozilla::gfx::DrawTarget DrawTarget;
 
   nsPluginInstanceOwner();
   
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGININSTANCEOWNER
@@ -273,16 +275,31 @@ public:
   void NotifyDestroyPending();
 
   bool GetCompositionString(uint32_t aIndex, nsTArray<uint8_t>* aString,
                             int32_t* aLength);
   bool SetCandidateWindow(
            const mozilla::widget::CandidateWindowPosition& aPosition);
   bool RequestCommitOrCancel(bool aCommitted);
 
+  // See nsIKeyEventInPluginCallback
+  virtual void HandledWindowedPluginKeyEvent(
+                 const mozilla::NativeEventData& aKeyEventData,
+                 bool aIsConsumed) override;
+
+  /**
+   * OnWindowedPluginKeyEvent() is called when the plugin process receives
+   * native key event directly.
+   *
+   * @param aNativeKeyData      The key event which was received by the
+   *                            plugin process directly.
+   */
+  void OnWindowedPluginKeyEvent(
+         const mozilla::NativeEventData& aNativeKeyData);
+
   void GetCSSZoomFactor(float *result);
 private:
   virtual ~nsPluginInstanceOwner();
 
   // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
   bool IsUpToDate()
   {
     nsIntSize size;
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -27,16 +27,17 @@ using mozilla::gfx::IntRect from "mozill
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
 using mozilla::plugins::WindowsSharedMemoryHandle from "mozilla/plugins/PluginMessageUtils.h";
 using mozilla::layers::SurfaceDescriptorX11 from "gfxipc/ShadowLayerUtils.h";
 using nsIntRect from "nsRect.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h";
 using struct mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
+using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
 
 namespace mozilla {
 namespace plugins {
 
 struct IOSurfaceDescriptor {
   uint32_t surfaceId;
   double contentsScaleFactor;
 };
@@ -135,16 +136,26 @@ child:
   // refer to the existing background or a fresh descriptor.
   async UpdateBackground(SurfaceDescriptor background, nsIntRect rect);
 
   async NPP_DidComposite();
 
   intr NPP_Destroy()
     returns (NPError rv);
 
+  // HandledWindowedPluginKeyEvent() is always called after posting a native
+  // key event with OnWindowedPluginKeyEvent().
+  //
+  // @param aKeyEventData      The key event which was posted to the parent
+  //                           process.
+  // @param aIsConsumed        true if aKeyEventData is consumed in the
+  //                           parent process.  Otherwise, false.
+  async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
+                                      bool aIsConsumed);
+
 parent:
   intr NPN_GetValue_NPNVWindowNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
   intr NPN_GetValue_NPNVPluginElementNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
   intr NPN_GetValue_NPNVprivateModeBool()
     returns (bool value, NPError result);
   intr NPN_GetValue_NPNVnetscapeWindow()
@@ -266,16 +277,23 @@ parent:
   sync GetCompositionString(uint32_t aType)
                             returns (uint8_t[] aDist, int32_t aLength);
   // Set candidate window position.
   //
   // @param aPosition  position information of candidate window
   async SetCandidateWindow(CandidateWindowPosition aPosition);
   async RequestCommitOrCancel(bool aCommitted);
 
+  // Notifies the parent process of a plugin instance receiving key event
+  // directly.
+  //
+  // @param aKeyEventData       The native key event which will be sent to
+  //                            plugin from native event handler.
+  async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
+
 both:
   async PPluginScriptableObject();
 
 child:
   /* NPP_NewStream */
   async PBrowserStream(nsCString url,
                        uint32_t length,
                        uint32_t lastmodified,
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -27,27 +27,29 @@
 extern const wchar_t* kFlashFullscreenClass;
 using mozilla::gfx::SharedDIBSurface;
 #endif
 #include "gfxSharedImageSurface.h"
 #include "gfxUtils.h"
 #include "gfxAlphaRecovery.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/BasicEvents.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "ImageContainer.h"
 
 using namespace mozilla;
 using mozilla::ipc::ProcessChild;
 using namespace mozilla::plugins;
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
+using namespace mozilla::widget;
 using namespace std;
 
 #ifdef MOZ_WIDGET_GTK
 
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include <gdk/gdk.h>
 #include "gtk2xtbin.h"
@@ -56,16 +58,18 @@ using namespace std;
 #undef KeyPress
 #undef KeyRelease
 #elif defined(OS_WIN)
 
 #include <windows.h>
 #include <windowsx.h>
 
 #include "mozilla/widget/WinMessages.h"
+#include "mozilla/widget/WinModifierKeyState.h"
+#include "mozilla/widget/WinNativeEventData.h"
 #include "nsWindowsDllInterceptor.h"
 
 typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
                                             UINT uFlags,
                                             int x,
                                             int y,
                                             int nReserved,
                                             HWND hWnd,
@@ -144,16 +148,17 @@ PluginInstanceChild::PluginInstanceChild
     : mPluginIface(aPluginIface)
     , mMimeType(aMimeType)
     , mMode(aMode)
     , mNames(aNames)
     , mValues(aValues)
 #if defined(XP_DARWIN)
     , mContentsScaleFactor(1.0)
 #endif
+    , mPostingKeyEvents(0)
     , mDrawingModel(kDefaultDrawingModel)
     , mCurrentDirectSurface(nullptr)
     , mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex")
     , mAsyncInvalidateTask(0)
     , mCachedWindowActor(nullptr)
     , mCachedElementActor(nullptr)
 #ifdef MOZ_WIDGET_GTK
     , mXEmbed(false)
@@ -1425,16 +1430,71 @@ PluginInstanceChild::Initialize()
     else {
         mWsInfo.display = xt_client_get_display();
     }
 #endif 
 
     return true;
 }
 
+bool
+PluginInstanceChild::RecvHandledWindowedPluginKeyEvent(
+                       const NativeEventData& aKeyEventData,
+                       const bool& aIsConsumed)
+{
+    // Unknown key input shouldn't be sent to plugin for security.
+    // XXX Is this possible if a plugin process which posted the message
+    //     already crashed and this plugin process is recreated?
+    if (NS_WARN_IF(!mPostingKeyEvents)) {
+        return true;
+    }
+
+    mPostingKeyEvents--;
+    if (aIsConsumed) {
+        return true;
+    }
+
+#if defined(OS_WIN)
+    const WinNativeKeyEventData* eventData =
+        static_cast<const WinNativeKeyEventData*>(aKeyEventData);
+    UINT message = 0;
+    switch (eventData->mMessage) {
+        case WM_KEYDOWN:
+            message = MOZ_WM_KEYDOWN;
+            break;
+        case WM_SYSKEYDOWN:
+            message = MOZ_WM_SYSKEYDOWN;
+            break;
+        case WM_KEYUP:
+            message = MOZ_WM_KEYUP;
+            break;
+        case WM_SYSKEYUP:
+            message = MOZ_WM_SYSKEYUP;
+            break;
+        case WM_CHAR:
+            message = MOZ_WM_CHAR;
+            break;
+        case WM_SYSCHAR:
+            message = MOZ_WM_SYSCHAR;
+            break;
+        case WM_DEADCHAR:
+            message = MOZ_WM_DEADCHAR;
+            break;
+        case WM_SYSDEADCHAR:
+            message = MOZ_WM_SYSDEADCHAR;
+            break;
+        default:
+            MOZ_CRASH("Needs to handle all messages posted to the parent");
+    }
+    PluginWindowProcInternal(mPluginWindowHWND, message,
+                             eventData->mWParam, eventData->mLParam);
+#endif
+    return true;
+}
+
 #if defined(OS_WIN)
 
 static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
 static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty");
 static const TCHAR kFlashThrottleProperty[] = TEXT("MozillaFlashThrottleProperty");
 
 // static
 bool
@@ -1583,16 +1643,61 @@ PluginInstanceChild::PluginWindowProcInt
                 pos->x = pos->y = 0;
                 pos->cx = self->mPluginSize.x;
                 pos->cy = self->mPluginSize.y;
                 return res;
             }
             break;
         }
 
+        case WM_SETFOCUS:
+            // If this gets focus, ensure that there is no pending key events.
+            // Even if there were, we should ignore them for performance reason.
+            // Although, such case shouldn't occur.
+            NS_WARN_IF(self->mPostingKeyEvents > 0);
+            self->mPostingKeyEvents = 0;
+            break;
+
+        case WM_KEYDOWN:
+        case WM_SYSKEYDOWN:
+        case WM_KEYUP:
+        case WM_SYSKEYUP:
+            if (self->MaybePostKeyMessage(message, wParam, lParam)) {
+                // If PreHandleKeyMessage() posts the message to the parent
+                // process, we need to wait RecvOnKeyEventHandledBeforePlugin()
+                // to be called.
+                return 0; // Consume current message temporarily.
+            }
+            break;
+
+        case MOZ_WM_KEYDOWN:
+            message = WM_KEYDOWN;
+            break;
+        case MOZ_WM_SYSKEYDOWN:
+            message = WM_SYSKEYDOWN;
+            break;
+        case MOZ_WM_KEYUP:
+            message = WM_KEYUP;
+            break;
+        case MOZ_WM_SYSKEYUP:
+            message = WM_SYSKEYUP;
+            break;
+        case MOZ_WM_CHAR:
+            message = WM_CHAR;
+            break;
+        case MOZ_WM_SYSCHAR:
+            message = WM_SYSCHAR;
+            break;
+        case MOZ_WM_DEADCHAR:
+            message = WM_DEADCHAR;
+            break;
+        case MOZ_WM_SYSDEADCHAR:
+            message = WM_SYSDEADCHAR;
+            break;
+
         // The plugin received keyboard focus, let the parent know so the dom
         // is up to date.
         case WM_MOUSEACTIVATE:
             self->CallPluginFocusChange(true);
             break;
     }
 
     // Prevent lockups due to plugins making rpc calls when the parent
@@ -1646,16 +1751,97 @@ PluginInstanceChild::PluginWindowProcInt
 
     if (message == WM_NCDESTROY) {
         RemoveProp(hWnd, kPluginInstanceChildProperty);
     }
 
     return res;
 }
 
+bool
+PluginInstanceChild::ShouldPostKeyMessage(UINT message,
+                                          WPARAM wParam,
+                                          LPARAM lParam)
+{
+    // If there are some pending keyboard events which are not handled in
+    // the parent process, we should post the message for avoiding to shuffle
+    // the key event order.
+    if (mPostingKeyEvents) {
+        return true;
+    }
+
+    // If we are not waiting calls of RecvOnKeyEventHandledBeforePlugin(),
+    // we don't need to post WM_*CHAR messages.
+    switch (message) {
+        case WM_CHAR:
+        case WM_SYSCHAR:
+        case WM_DEADCHAR:
+        case WM_SYSDEADCHAR:
+            return false;
+    }
+
+    // Otherwise, we should post key message which might match with a
+    // shortcut key.
+    ModifierKeyState modifierState;
+    if (!modifierState.MaybeMatchShortcutKey()) {
+        // For better UX, we shouldn't use IPC when user tries to
+        // input character(s).
+        return false;
+    }
+
+    // Ignore modifier key events and keys already handled by IME.
+    switch (wParam) {
+        case VK_SHIFT:
+        case VK_CONTROL:
+        case VK_MENU:
+        case VK_LWIN:
+        case VK_RWIN:
+        case VK_CAPITAL:
+        case VK_NUMLOCK:
+        case VK_SCROLL:
+        // Following virtual keycodes shouldn't come with WM_(SYS)KEY* message
+        // but check it for avoiding unnecessary cross process communication.
+        case VK_LSHIFT:
+        case VK_RSHIFT:
+        case VK_LCONTROL:
+        case VK_RCONTROL:
+        case VK_LMENU:
+        case VK_RMENU:
+        case VK_PROCESSKEY:
+        case VK_PACKET:
+        case 0xFF: // 0xFF could be sent with unidentified key by the layout.
+            return false;
+        default:
+            break;
+    }
+    return true;
+}
+
+bool
+PluginInstanceChild::MaybePostKeyMessage(UINT message,
+                                         WPARAM wParam,
+                                         LPARAM lParam)
+{
+    if (!ShouldPostKeyMessage(message, wParam, lParam)) {
+        return false;
+    }
+
+    ModifierKeyState modifierState;
+    WinNativeKeyEventData winNativeKeyData(message, wParam, lParam,
+                                           modifierState);
+    NativeEventData nativeKeyData;
+    nativeKeyData.Copy(winNativeKeyData);
+    if (NS_WARN_IF(!SendOnWindowedPluginKeyEvent(nativeKeyData))) {
+        return false;
+     }
+
+    mPostingKeyEvents++;
+    return true;
+}
+
 /* set window long ptr hook for flash */
 
 /*
  * Flash will reset the subclass of our widget at various times.
  * (Notably when entering and exiting full screen mode.) This
  * occurs independent of the main plugin window event procedure.
  * We trap these subclass calls to prevent our subclass hook from
  * getting dropped.
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -2,16 +2,17 @@
  * vim: sw=4 ts=4 et :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef dom_plugins_PluginInstanceChild_h
 #define dom_plugins_PluginInstanceChild_h 1
 
+#include "mozilla/EventForwards.h"
 #include "mozilla/plugins/PPluginInstanceChild.h"
 #include "mozilla/plugins/PluginScriptableObjectChild.h"
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/PPluginSurfaceChild.h"
 #include "mozilla/ipc/CrossProcessMutex.h"
 #include "nsRefPtrHashtable.h"
 #if defined(OS_WIN)
 #include "mozilla/gfx/SharedDIBWin.h"
@@ -267,16 +268,21 @@ public:
 
     NPError NPN_InitAsyncSurface(NPSize *size, NPImageFormat format,
                                  void *initData, NPAsyncSurface *surface);
     NPError NPN_FinalizeAsyncSurface(NPAsyncSurface *surface);
 
     void NPN_SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed);
 
     void DoAsyncRedraw();
+
+    virtual bool RecvHandledWindowedPluginKeyEvent(
+                   const NativeEventData& aKeyEventData,
+                   const bool& aIsConsumed) override;
+
 private:
     friend class PluginModuleChild;
 
     NPError
     InternalGetNPObjectForValue(NPNVariable aValue,
                                 NPObject** aObject);
 
     bool IsUsingDirectDrawing();
@@ -389,28 +395,31 @@ private:
       private:
         HWND                 mWnd;
         UINT                 mMsg;
         WPARAM               mWParam;
         LPARAM               mLParam;
         bool                 mWindowed;
     };
 
-#endif
+    bool ShouldPostKeyMessage(UINT message, WPARAM wParam, LPARAM lParam);
+    bool MaybePostKeyMessage(UINT message, WPARAM wParam, LPARAM lParam);
+#endif // #if defined(OS_WIN)
     const NPPluginFuncs* mPluginIface;
     nsCString                   mMimeType;
     uint16_t                    mMode;
     InfallibleTArray<nsCString> mNames;
     InfallibleTArray<nsCString> mValues;
     NPP_t mData;
     NPWindow mWindow;
 #if defined(XP_DARWIN)
     double mContentsScaleFactor;
 #endif
     double mCSSZoomFactor;
+    uint32_t mPostingKeyEvents;
     int16_t               mDrawingModel;
 
     NPAsyncSurface* mCurrentDirectSurface;
 
     // The surface hashtables below serve a few purposes. They let us verify
     // and retain extra information about plugin surfaces, and they let us
     // free shared memory that the plugin might forget to release.
     struct DirectBitmap {
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -2,16 +2,17 @@
  * vim: sw=4 ts=4 et :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 #include <stdint.h> // for intptr_t
 
+#include "mozilla/BasicEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "PluginInstanceParent.h"
 #include "BrowserStreamParent.h"
 #include "PluginAsyncSurrogate.h"
 #include "PluginBackgroundDestroyer.h"
 #include "PluginModuleParent.h"
 #include "PluginStreamParent.h"
@@ -2657,16 +2658,43 @@ PluginInstanceParent::RecvRequestCommitO
     nsPluginInstanceOwner* owner = GetOwner();
     if (owner) {
         owner->RequestCommitOrCancel(aCommitted);
     }
 #endif
     return true;
 }
 
+nsresult
+PluginInstanceParent::HandledWindowedPluginKeyEvent(
+                        const NativeEventData& aKeyEventData,
+                        bool aIsConsumed)
+{
+    if (NS_WARN_IF(!SendHandledWindowedPluginKeyEvent(aKeyEventData,
+                                                      aIsConsumed))) {
+        return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
+}
+
+bool
+PluginInstanceParent::RecvOnWindowedPluginKeyEvent(
+                        const NativeEventData& aKeyEventData)
+{
+    nsPluginInstanceOwner* owner = GetOwner();
+    if (NS_WARN_IF(!owner)) {
+        // Notifies the plugin process of the key event being not consumed
+        // by us.
+        HandledWindowedPluginKeyEvent(aKeyEventData, false);
+        return true;
+    }
+    owner->OnWindowedPluginKeyEvent(aKeyEventData);
+    return true;
+}
+
 void
 PluginInstanceParent::RecordDrawingModel()
 {
     int mode = -1;
     switch (mWindowType) {
     case NPWindowTypeWindow:
         // We use 0=windowed since there is no specific NPDrawingModel value.
         mode = 0;
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -20,16 +20,17 @@
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRect.h"
 #include "PluginDataResolver.h"
 
 #include "mozilla/unused.h"
+#include "mozilla/EventForwards.h"
 
 class gfxASurface;
 class gfxContext;
 class nsPluginInstanceOwner;
 
 namespace mozilla {
 namespace layers {
 class Image;
@@ -351,16 +352,24 @@ public:
                              nsTArray<uint8_t>* aBuffer,
                              int32_t* aLength) override;
     virtual bool
     RecvSetCandidateWindow(
         const mozilla::widget::CandidateWindowPosition& aPosition) override;
     virtual bool
     RecvRequestCommitOrCancel(const bool& aCommitted) override;
 
+    // for reserved shortcut key handling with windowed plugin on Windows
+    nsresult HandledWindowedPluginKeyEvent(
+      const mozilla::NativeEventData& aKeyEventData,
+      bool aIsConsumed);
+    virtual bool
+    RecvOnWindowedPluginKeyEvent(
+      const mozilla::NativeEventData& aKeyEventData) override;
+
 private:
     // Create an appropriate platform surface for a background of size
     // |aSize|.  Return true if successful.
     bool CreateBackground(const nsIntSize& aSize);
     void DestroyBackground();
     SurfaceDescriptor BackgroundDescriptor() /*const*/;
 
     typedef mozilla::layers::ImageContainer ImageContainer;
--- a/dom/plugins/ipc/PluginLibrary.h
+++ b/dom/plugins/ipc/PluginLibrary.h
@@ -86,16 +86,20 @@ public:
 #if defined(XP_MACOSX)
   virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) = 0;
   virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) = 0;
 #endif
 #if defined(XP_WIN)
     virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) = 0;
     virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling) = 0;
 #endif
+  virtual nsresult HandledWindowedPluginKeyEvent(
+                     NPP aInstance,
+                     const mozilla::NativeEventData& aNativeKeyData,
+                     bool aIsCOnsumed) = 0;
 
   /**
    * The next three methods are the third leg in the trip to
    * PluginInstanceParent.  They approximately follow the ReadbackSink
    * API.
    */
   virtual nsresult SetBackgroundUnknown(NPP instance) = 0;
   virtual nsresult BeginUpdateBackground(NPP instance,
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2009,16 +2009,29 @@ PluginModuleParent::GetScrollCaptureCont
 nsresult
 PluginModuleParent::UpdateScrollState(NPP aInstance, bool aIsScrolling)
 {
     PluginInstanceParent* inst = PluginInstanceParent::Cast(aInstance);
     return !inst ? NS_ERROR_FAILURE : inst->UpdateScrollState(aIsScrolling);
 }
 #endif
 
+nsresult
+PluginModuleParent::HandledWindowedPluginKeyEvent(
+                        NPP aInstance,
+                        const NativeEventData& aNativeKeyData,
+                        bool aIsConsumed)
+{
+    PluginInstanceParent* parent = PluginInstanceParent::Cast(aInstance);
+    if (NS_WARN_IF(!parent)) {
+        return NS_ERROR_FAILURE;
+    }
+    return parent->HandledWindowedPluginKeyEvent(aNativeKeyData, aIsConsumed);
+}
+
 void
 PluginModuleParent::OnInitFailure()
 {
     if (GetIPCChannel()->CanSend()) {
         Close();
     }
 
     mShutdown = true;
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -261,16 +261,21 @@ protected:
     virtual nsresult EndUpdateBackground(NPP instance,
                                          const nsIntRect& aRect) override;
 
 #if defined(XP_WIN)
     virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
     virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling);
 #endif
 
+    virtual nsresult HandledWindowedPluginKeyEvent(
+                       NPP aInstance,
+                       const mozilla::NativeEventData& aNativeKeyData,
+                       bool aIsConsumed) override;
+
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override;
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override;
 #endif
     virtual nsresult NP_Shutdown(NPError* error) override;
 
     virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) override;
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -1454,10 +1454,39 @@ PuppetWidget::HasPendingInputEvent()
       }
       return true;
     }
   );
 
   return ret;
 }
 
+void
+PuppetWidget::HandledWindowedPluginKeyEvent(
+                const NativeEventData& aKeyEventData,
+                bool aIsConsumed)
+{
+  if (NS_WARN_IF(mKeyEventInPluginCallbacks.IsEmpty())) {
+    return;
+  }
+  nsCOMPtr<nsIKeyEventInPluginCallback> callback =
+    mKeyEventInPluginCallbacks[0];
+  MOZ_ASSERT(callback);
+  mKeyEventInPluginCallbacks.RemoveElementAt(0);
+  callback->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+}
+
+nsresult
+PuppetWidget::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+                                       nsIKeyEventInPluginCallback* aCallback)
+{
+  if (NS_WARN_IF(!mTabChild)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  if (NS_WARN_IF(!mTabChild->SendOnWindowedPluginKeyEvent(aKeyEventData))) {
+    return NS_ERROR_FAILURE;
+  }
+  mKeyEventInPluginCallbacks.AppendElement(aCallback);
+  return NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY;
+}
+
 } // namespace widget
 } // namespace mozilla
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -14,16 +14,18 @@
 
 #ifndef mozilla_widget_PuppetWidget_h__
 #define mozilla_widget_PuppetWidget_h__
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsBaseScreen.h"
 #include "nsBaseWidget.h"
+#include "nsCOMArray.h"
+#include "nsIKeyEventInPluginCallback.h"
 #include "nsIScreenManager.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentCache.h"
 #include "mozilla/EventForwards.h"
 
 namespace mozilla {
 
@@ -257,16 +259,22 @@ public:
 
   virtual void ZoomToRect(const uint32_t& aPresShellId,
                           const FrameMetrics::ViewID& aViewId,
                           const CSSRect& aRect,
                           const uint32_t& aFlags) override;
 
   virtual bool HasPendingInputEvent() override;
 
+  void HandledWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+                                     bool aIsConsumed);
+  virtual nsresult OnWindowedPluginKeyEvent(
+                     const NativeEventData& aKeyEventData,
+                     nsIKeyEventInPluginCallback* aCallback) override;
+
 protected:
   virtual nsresult NotifyIMEInternal(
                      const IMENotification& aIMENotification) override;
 
 private:
   nsresult Paint();
 
   void SetChild(PuppetWidget* aChild);
@@ -343,16 +351,18 @@ private:
   // Precomputed answers for ExecuteNativeKeyBinding
   InfallibleTArray<mozilla::CommandInt> mSingleLineCommands;
   InfallibleTArray<mozilla::CommandInt> mMultiLineCommands;
   InfallibleTArray<mozilla::CommandInt> mRichTextCommands;
 
   nsCOMPtr<imgIContainer> mCustomCursor;
   uint32_t mCursorHotspotX, mCursorHotspotY;
 
+  nsCOMArray<nsIKeyEventInPluginCallback> mKeyEventInPluginCallbacks;
+
 protected:
   bool mEnabled;
   bool mVisible;
 
 private:
   bool mNeedIMEStateInit;
   bool mNativeKeyCommandsValid;
 };
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -91,16 +91,17 @@ EXPORTS += [
     'GfxDriverInfo.h',
     'GfxInfoBase.h',
     'GfxInfoCollector.h',
     'InputData.h',
     'nsBaseScreen.h',
     'nsBaseWidget.h',
     'nsDeviceContextSpecProxy.h',
     'nsIDeviceContextSpec.h',
+    'nsIKeyEventInPluginCallback.h',
     'nsIPluginWidget.h',
     'nsIRollupListener.h',
     'nsIWidget.h',
     'nsIWidgetListener.h',
     'nsWidgetInitData.h',
     'nsWidgetsCID.h',
     'PluginWidgetProxy.h',
     'PuppetWidget.h',
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "nsBaseWidget.h"
 #include "nsDeviceContext.h"
 #include "nsCOMPtr.h"
 #include "nsGfxCIID.h"
 #include "nsWidgetsCID.h"
 #include "nsServiceManagerUtils.h"
+#include "nsIKeyEventInPluginCallback.h"
 #include "nsIScreenManager.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIServiceManager.h"
 #include "mozilla/Preferences.h"
@@ -2135,16 +2136,23 @@ nsIWidget::UpdateRegisteredPluginWindowV
 }
 
 NS_IMETHODIMP_(nsIWidget::NativeIMEContext)
 nsIWidget::GetNativeIMEContext()
 {
   return NativeIMEContext(this);
 }
 
+nsresult
+nsIWidget::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+                                    nsIKeyEventInPluginCallback* aCallback)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 namespace mozilla {
 namespace widget {
 
 void
 NativeIMEContext::Init(nsIWidget* aWidget)
 {
   if (!aWidget) {
     mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
new file mode 100644
--- /dev/null
+++ b/widget/nsIKeyEventInPluginCallback.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIKeyEventInPluginCallback_h_
+#define nsIKeyEventInPluginCallback_h_
+
+#include "mozilla/EventForwards.h"
+
+#include "nsISupports.h"
+
+#define NS_IKEYEVENTINPLUGINCALLBACK_IID \
+{ 0x543c5a8a, 0xc50e, 0x4cf9, \
+  { 0xa6, 0xba, 0x29, 0xa1, 0xc5, 0xa5, 0x47, 0x07 } }
+
+
+class nsIKeyEventInPluginCallback : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IKEYEVENTINPLUGINCALLBACK_IID)
+
+  /**
+   * HandledWindowedPluginKeyEvent() is a callback method of
+   * nsIWidget::OnWindowedPluginKeyEvent().  When it returns
+   * NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY, it should call this method
+   * when the key event is handled.
+   *
+   * @param aKeyEventData      The key event which was posted to the parent
+   *                           process from a plugin process.
+   * @param aIsConsumed        true if aKeyEventData is consumed in the
+   *                           parent process.  Otherwise, false.
+   */
+  virtual void HandledWindowedPluginKeyEvent(
+                 const mozilla::NativeEventData& aKeyEventData,
+                 bool aIsConsumed) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIKeyEventInPluginCallback,
+                              NS_IKEYEVENTINPLUGINCALLBACK_IID)
+
+#endif // #ifndef nsIKeyEventInPluginCallback_h_
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -34,16 +34,17 @@
 
 // forward declarations
 class   nsIRollupListener;
 class   imgIContainer;
 class   nsIContent;
 class   ViewWrapper;
 class   nsIScreen;
 class   nsIRunnable;
+class   nsIKeyEventInPluginCallback;
 
 namespace mozilla {
 class CompositorVsyncDispatcher;
 namespace dom {
 class TabChild;
 } // namespace dom
 namespace plugins {
 class PluginWidgetChild;
@@ -2049,16 +2050,38 @@ public:
     NS_IMETHOD_(TextEventDispatcherListener*)
       GetNativeTextEventDispatcherListener() = 0;
 
     virtual void ZoomToRect(const uint32_t& aPresShellId,
                             const FrameMetrics::ViewID& aViewId,
                             const CSSRect& aRect,
                             const uint32_t& aFlags) = 0;
 
+    /**
+     * OnWindowedPluginKeyEvent() is called when native key event is
+     * received in the focused plugin process directly in PluginInstanceChild.
+     *
+     * @param aKeyEventData     The native key event data.  The actual type
+     *                          copied into NativeEventData depends on the
+     *                          caller.  Please check PluginInstanceChild.
+     * @param aCallback         Callback interface.  When this returns
+     *                          NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY,
+     *                          the event handler has to call this callback.
+     *                          Otherwise, the caller should do that instead.
+     * @return                  NS_ERROR_* if this fails to handle the event.
+     *                          NS_SUCCESS_EVENT_CONSUMED if the key event is
+     *                          consumed.
+     *                          NS_OK if the key event isn't consumed.
+     *                          NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY if the
+     *                          key event will be handled asynchronously.
+     */
+    virtual nsresult OnWindowedPluginKeyEvent(
+                       const mozilla::NativeEventData& aKeyEventData,
+                       nsIKeyEventInPluginCallback* aCallback);
+
 protected:
     /**
      * Like GetDefaultScale, but taking into account only the system settings
      * and ignoring Gecko preferences.
      */
     virtual double GetDefaultScaleInternal() { return 1.0; }
 
     // keep the list of children.  We also keep track of our siblings.
--- a/widget/windows/WinMessages.h
+++ b/widget/windows/WinMessages.h
@@ -29,16 +29,27 @@
 // If TSFTextStore needs to notify TSF/TIP of layout change later, this
 // message is posted.
 #define MOZ_WM_NOTIY_TSF_OF_LAYOUT_CHANGE (WM_APP+0x0315)
 // Internal message used in correcting backwards clock skew
 #define MOZ_WM_SKEWFIX                    (WM_APP+0x0316)
 // Internal message used for hiding the on-screen keyboard
 #define MOZ_WM_DISMISS_ONSCREEN_KEYBOARD  (WM_APP+0x0317)
 
+// Following MOZ_WM_*KEY* messages are used by PluginInstanceChild internally.
+// (never posted to the queue)
+#define MOZ_WM_KEYDOWN                    (WM_APP+0x0318)
+#define MOZ_WM_KEYUP                      (WM_APP+0x0319)
+#define MOZ_WM_SYSKEYDOWN                 (WM_APP+0x031A)
+#define MOZ_WM_SYSKEYUP                   (WM_APP+0x031B)
+#define MOZ_WM_CHAR                       (WM_APP+0x031C)
+#define MOZ_WM_SYSCHAR                    (WM_APP+0x031D)
+#define MOZ_WM_DEADCHAR                   (WM_APP+0x031E)
+#define MOZ_WM_SYSDEADCHAR                (WM_APP+0x031F)
+
 // Internal message for ensuring the file picker is visible on multi monitor
 // systems, and when the screen resolution changes.
 #define MOZ_WM_ENSUREVISIBLE              (WM_APP+0x374F)
 
 // XXX Should rename them to MOZ_WM_* and use safer values!
 // Messages for fullscreen transition window
 #define WM_FULLSCREEN_TRANSITION_BEFORE   (WM_USER + 0)
 #define WM_FULLSCREEN_TRANSITION_AFTER    (WM_USER + 1)
new file mode 100644
--- /dev/null
+++ b/widget/windows/WinNativeEventData.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_widget_WinNativeEventData_h_
+#define mozilla_widget_WinNativeEventData_h_
+
+#include <windows.h>
+
+#include "mozilla/EventForwards.h"
+#include "mozilla/widget/WinModifierKeyState.h"
+
+namespace mozilla {
+namespace widget {
+
+/**
+ * WinNativeKeyEventData is used by nsIWidget::OnWindowedPluginKeyEvent() and
+ * related IPC methods.  This class cannot hold any pointers and references
+ * since which are not available in different process.
+ */
+
+class WinNativeKeyEventData final
+{
+public:
+  UINT mMessage;
+  WPARAM mWParam;
+  LPARAM mLParam;
+  Modifiers mModifiers;
+
+private:
+  uintptr_t mKeyboardLayout;
+
+public:
+  WinNativeKeyEventData(UINT aMessage,
+                        WPARAM aWParam,
+                        LPARAM aLParam,
+                        const ModifierKeyState& aModifierKeyState)
+    : mMessage(aMessage)
+    , mWParam(aWParam)
+    , mLParam(aLParam)
+    , mModifiers(aModifierKeyState.GetModifiers())
+    , mKeyboardLayout(reinterpret_cast<uintptr_t>(::GetKeyboardLayout(0)))
+  {
+  }
+
+  HKL GetKeyboardLayout() const
+  {
+    return reinterpret_cast<HKL>(mKeyboardLayout);
+  }
+};
+
+} // namespace widget
+} // namespace mozilla
+
+#endif // #ifndef mozilla_widget_WinNativeEventData_h_
--- a/widget/windows/moz.build
+++ b/widget/windows/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
     'WindowHook.h',
     'WinUtils.h',
 ]
 
 EXPORTS.mozilla.widget += [
     'AudioSession.h',
     'WinMessages.h',
     'WinModifierKeyState.h',
+    'WinNativeEventData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AudioSession.cpp',
     'GfxInfo.cpp',
     'IEnumFE.cpp',
     'IMMHandler.cpp',
     'InkCollector.cpp',
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -81,16 +81,17 @@
 #include "prprf.h"
 #include "prmem.h"
 #include "prenv.h"
 
 #include "mozilla/WidgetTraceEvent.h"
 #include "nsIAppShell.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIDOMMouseEvent.h"
+#include "nsIKeyEventInPluginCallback.h"
 #include "nsITheme.h"
 #include "nsIObserverService.h"
 #include "nsIScreenManager.h"
 #include "imgIContainer.h"
 #include "nsIFile.h"
 #include "nsIRollupListener.h"
 #include "nsIServiceManager.h"
 #include "nsIClipboard.h"
@@ -7864,16 +7865,24 @@ nsWindow::DefaultProcOfPluginEvent(const
 
   // For WM_IME_*COMPOSITION
   IMEHandler::DefaultProcOfPluginEvent(this, pPluginEvent);
 
   CallWindowProcW(GetPrevWindowProc(), mWnd, pPluginEvent->event,
                   pPluginEvent->wParam, pPluginEvent->lParam);
 }
 
+nsresult
+nsWindow::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+                                   nsIKeyEventInPluginCallback* aCallback)
+{
+  // TODO: Implement in the following patch.
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 /**************************************************************
  **************************************************************
  **
  ** BLOCK: ChildWindow impl.
  **
  ** Child window overrides.
  **
  **************************************************************
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -306,16 +306,19 @@ public:
 
   const IMEContext& DefaultIMC() const { return mDefaultIMC; }
 
   virtual void SetCandidateWindowForPlugin(
                  const mozilla::widget::CandidateWindowPosition&
                    aPosition) override;
   virtual void DefaultProcOfPluginEvent(
                  const mozilla::WidgetPluginEvent& aEvent) override;
+  virtual nsresult OnWindowedPluginKeyEvent(
+                     const mozilla::NativeEventData& aKeyEventData,
+                     nsIKeyEventInPluginCallback* aCallback) override;
 
 protected:
   virtual ~nsWindow();
 
   virtual void WindowUsesOMTC() override;
   virtual void RegisterTouchWindow() override;
 
   // A magic number to identify the FAKETRACKPOINTSCROLLABLE window created
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -113,19 +113,27 @@
   ERROR(NS_ERROR_GFX_CMAP_MALFORMED,                      FAILURE(51)),
 #undef MODULE
 
 
   /* ======================================================================= */
   /* 4:  NS_ERROR_MODULE_WIDGET */
   /* ======================================================================= */
 #define MODULE  NS_ERROR_MODULE_WIDGET
-  /* Used by nsIWidget::NotifyIME(). Returned when the notification is handled
-   * and the notified event is consumed by IME. */
+  /* Used by:
+   *   - nsIWidget::NotifyIME()
+   *   - nsIWidget::OnWindowedPluginKeyEvent()
+   * Returned when the notification or the event is handled and it's consumed
+   * by somebody. */
   ERROR(NS_SUCCESS_EVENT_CONSUMED,                        SUCCESS(1)),
+  /* Used by:
+   *   - nsIWidget::OnWindowedPluginKeyEvent()
+   * Returned when the event is handled correctly but the result will be
+   * notified asynchronously. */
+  ERROR(NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY,          SUCCESS(2)),
 #undef MODULE
 
 
   /* ======================================================================= */
   /* 6: NS_ERROR_MODULE_NETWORK */
   /* ======================================================================= */
 #define MODULE NS_ERROR_MODULE_NETWORK
   /* General async request error codes: