Bug 1165903: For Windows NPAPI do window re-parenting in the chrome process to allow for sandboxing. r=jimm
authorBob Owen <bobowencode@gmail.com>
Mon, 15 Jun 2015 16:08:51 +0100
changeset 267684 6a5137a37ec5e9ec6eda7eb787735357b759eb0f
parent 267683 fd2961b995ab2155e39ee16e623ec95d324fd11b
child 267685 b812439bc1ff0127a3225360d2615dbb97a70344
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1165903
milestone41.0a1
Bug 1165903: For Windows NPAPI do window re-parenting in the chrome process to allow for sandboxing. r=jimm
browser/app/profile/firefox.js
dom/ipc/PPluginWidget.ipdl
dom/plugins/base/nsPluginNativeWindow.h
dom/plugins/ipc/PPluginInstance.ipdl
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceChild.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginWidgetParent.cpp
dom/plugins/ipc/PluginWidgetParent.h
widget/PluginWidgetProxy.cpp
widget/PluginWidgetProxy.h
widget/nsIWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1202,16 +1202,17 @@ pref("security.sandbox.windows.log", fal
 // On windows these levels are:
 // 0 - no sandbox
 // 1 - sandbox with USER_NON_ADMIN access token level
 // 2 - a more strict sandbox, which might cause functionality issues. This now
 //     includes running at low integrity.
 // 3 - the strongest settings we seem to be able to use without breaking
 //     everything, but will probably cause some functionality restrictions
 pref("dom.ipc.plugins.sandbox-level.default", 0);
+pref("dom.ipc.plugins.sandbox-level.flash", 0);
 
 #if defined(MOZ_CONTENT_SANDBOX)
 // This controls the strength of the Windows content process sandbox for testing
 // purposes. This will require a restart.
 // On windows these levels are:
 // 0 - sandbox with USER_NON_ADMIN access token level
 // 1 - level 0 plus low integrity
 // 2 - a policy that we can reasonably call an effective sandbox
--- a/dom/ipc/PPluginWidget.ipdl
+++ b/dom/ipc/PPluginWidget.ipdl
@@ -36,12 +36,18 @@ parent:
 
   /**
    * Returns NS_NATIVE_PLUGIN_PORT and its variants: a sharable native
    * window for plugins. On Linux, this returns an XID for a socket widget
    * embedded in the chrome side native window. On Windows this returns the
    * native HWND of the plugin widget.
    */
   sync GetNativePluginPort() returns (uintptr_t value);
+
+  /**
+   * Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window
+   * on the chrome side. This is only currently used on Windows.
+   */
+  sync SetNativeChildWindow(uintptr_t childWindow);
 };
 
 }
 }
--- a/dom/plugins/base/nsPluginNativeWindow.h
+++ b/dom/plugins/base/nsPluginNativeWindow.h
@@ -44,17 +44,17 @@ public:
     return NS_OK;
   }
   nsresult SetPluginInstance(nsNPAPIPluginInstance *aPluginInstance) { 
     if (mPluginInstance != aPluginInstance)
       mPluginInstance = aPluginInstance;
     return NS_OK;
   }
 
-  nsresult GetPluginWidget(nsIWidget **aWidget) {
+  nsresult GetPluginWidget(nsIWidget **aWidget) const {
     NS_IF_ADDREF(*aWidget = mWidget);
     return NS_OK;
   }
   nsresult SetPluginWidget(nsIWidget *aWidget) { 
     mWidget = aWidget;
     return NS_OK;
   }
 
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -63,17 +63,20 @@ intr protocol PPluginInstance
   manages PBrowserStream;
   manages PPluginStream;
   manages PStreamNotify;
   manages PPluginSurface;
 
 child:
   intr __delete__();
 
-  intr NPP_SetWindow(NPRemoteWindow window);
+  // Return value is only used on Windows and only when the window needs its
+  // parent set to the chrome widget native window.
+  intr NPP_SetWindow(NPRemoteWindow window)
+    returns (NPRemoteWindow childWindowToBeAdopted);
 
   intr NPP_GetValue_NPPVpluginWantsAllNetworkStreams()
     returns (bool value, NPError result);
 
   // this message is not used on non-X platforms
   intr NPP_GetValue_NPPVpluginNeedsXEmbed()
     returns (bool value, NPError result);
 
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -1119,17 +1119,18 @@ void PluginInstanceChild::DeleteWindow()
 #endif
 
   // We don't have to keep the plug-in window ID any longer.
   mWindow.window = nullptr;
 }
 #endif
 
 bool
-PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
+PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow,
+                                         NPRemoteWindow* aChildWindowToBeAdopted)
 {
     PLUGIN_LOG_DEBUG(("%s (aWindow=<window: 0x%lx, x: %d, y: %d, width: %d, height: %d>)",
                       FULLFUNCTION,
                       aWindow.window,
                       aWindow.x, aWindow.y,
                       aWindow.width, aWindow.height));
     NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
                  "Shouldn't be receiving NPP_SetWindow with layer rendering");
@@ -1210,19 +1211,42 @@ PluginInstanceChild::AnswerNPP_SetWindow
               aWindow.height == 0) {
             // Skip SetWindow call for hidden QuickTime plugins
             return true;
           }
 
           if (!CreatePluginWindow())
               return false;
 
-          ReparentPluginWindow(reinterpret_cast<HWND>(aWindow.window));
           SizePluginWindow(aWindow.width, aWindow.height);
 
+          // If the window is not our parent set the return child window so that
+          // it can be re-parented in the chrome process. Re-parenting now
+          // happens there as we might not have sufficient permission.
+          // Also, this needs to be after SizePluginWindow because SetWindowPos
+          // relies on things that it sets.
+          HWND parentWindow = reinterpret_cast<HWND>(aWindow.window);
+          if (mPluginParentHWND != parentWindow  && IsWindow(parentWindow)) {
+              mPluginParentHWND = parentWindow;
+              aChildWindowToBeAdopted->window =
+                  reinterpret_cast<uint64_t>(mPluginWindowHWND);
+          } else {
+              // Now we know that the window has the correct parent we can show
+              // it. The actual visibility is controlled by its parent.
+              // First time round, these calls are made by our caller after the
+              // parent is set.
+              ShowWindow(mPluginWindowHWND, SW_SHOWNA);
+
+              // This used to be called in SizePluginWindow, but we need to make
+              // sure that mPluginWindowHWND has had it's parent set correctly,
+              // otherwise it can cause a focus issue.
+              SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, aWindow.width,
+                           aWindow.height, SWP_NOZORDER | SWP_NOREPOSITION);
+          }
+
           mWindow.window = (void*)mPluginWindowHWND;
           mWindow.x = aWindow.x;
           mWindow.y = aWindow.y;
           mWindow.width = aWindow.width;
           mWindow.height = aWindow.height;
           mWindow.type = aWindow.type;
 
           if (mPluginIface->setwindow) {
@@ -1401,43 +1425,22 @@ PluginInstanceChild::DestroyPluginWindow
             mPluginWndProc = 0;
         }
         DestroyWindow(mPluginWindowHWND);
         mPluginWindowHWND = 0;
     }
 }
 
 void
-PluginInstanceChild::ReparentPluginWindow(HWND hWndParent)
-{
-    if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) {
-        // Fix the child window's style to be a child window.
-        LONG_PTR style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE);
-        style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
-        style &= ~WS_POPUP;
-        SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style);
-
-        // Do the reparenting.
-        SetParent(mPluginWindowHWND, hWndParent);
-
-        // Make sure we're visible.
-        ShowWindow(mPluginWindowHWND, SW_SHOWNA);
-    }
-    mPluginParentHWND = hWndParent;
-}
-
-void
 PluginInstanceChild::SizePluginWindow(int width,
                                       int height)
 {
     if (mPluginWindowHWND) {
         mPluginSize.x = width;
         mPluginSize.y = height;
-        SetWindowPos(mPluginWindowHWND, nullptr, 0, 0, width, height,
-                     SWP_NOZORDER | SWP_NOREPOSITION);
     }
 }
 
 // See chromium's webplugin_delegate_impl.cc for explanation of this function.
 // static
 LRESULT CALLBACK
 PluginInstanceChild::DummyWindowProc(HWND hWnd,
                                      UINT message,
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -60,17 +60,18 @@ class PluginInstanceChild : public PPlug
                                              LPARAM lParam);
     static LRESULT CALLBACK PluginWindowProcInternal(HWND hWnd,
                                                      UINT message,
                                                      WPARAM wParam,
                                                      LPARAM lParam);
 #endif
 
 protected:
-    virtual bool AnswerNPP_SetWindow(const NPRemoteWindow& window) override;
+    bool AnswerNPP_SetWindow(const NPRemoteWindow& window,
+                             NPRemoteWindow* aChildWindowToBeAdopted) override;
 
     virtual bool
     AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(bool* wantsAllStreams, NPError* rv) override;
     virtual bool
     AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(bool* needs, NPError* rv) override;
     virtual bool
     AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
                                                     NPError* result) override;
@@ -273,17 +274,16 @@ private:
 
     virtual bool
     DeallocPPluginBackgroundDestroyerChild(PPluginBackgroundDestroyerChild* aActor) override;
 
 #if defined(OS_WIN)
     static bool RegisterWindowClass();
     bool CreatePluginWindow();
     void DestroyPluginWindow();
-    void ReparentPluginWindow(HWND hWndParent);
     void SizePluginWindow(int width, int height);
     int16_t WinlessHandleEvent(NPEvent& event);
     void CreateWinlessPopupSurrogate();
     void DestroyWinlessPopupSurrogate();
     void InitPopupMenuHook();
     void SetupFlashMsgThrottle();
     void UnhookWinlessFlashThrottle();
     void HookSetWindowLongPtr();
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -44,16 +44,18 @@
 #endif
 
 #if defined(OS_WIN)
 #include <windowsx.h>
 #include "gfxWindowsPlatform.h"
 #include "mozilla/plugins/PluginSurfaceParent.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
+#include "nsIWidget.h"
+#include "nsPluginNativeWindow.h"
 extern const wchar_t* kFlashFullscreenClass;
 #elif defined(MOZ_WIDGET_GTK)
 #include <gdk/gdk.h>
 #elif defined(XP_MACOSX)
 #include <ApplicationServices/ApplicationServices.h>
 #endif // defined(XP_MACOSX)
 
 // This is the pref used to determine whether to use Shumway on a Flash object
@@ -1020,18 +1022,40 @@ PluginInstanceParent::NPP_SetWindow(cons
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
     const NPSetWindowCallbackStruct* ws_info =
       static_cast<NPSetWindowCallbackStruct*>(aWindow->ws_info);
     window.visualID = ws_info->visual ? ws_info->visual->visualid : None;
     window.colormap = ws_info->colormap;
 #endif
 
-    if (!CallNPP_SetWindow(window))
+    NPRemoteWindow childWindow;
+    if (!CallNPP_SetWindow(window, &childWindow)) {
         return NPERR_GENERIC_ERROR;
+    }
+
+#if defined(XP_WIN)
+    // If a child window is returned it means that we need to re-parent it.
+    if (childWindow.window) {
+        nsCOMPtr<nsIWidget> widget;
+        static_cast<const nsPluginNativeWindow*>(aWindow)->
+            GetPluginWidget(getter_AddRefs(widget));
+        if (widget) {
+            widget->SetNativeData(NS_NATIVE_CHILD_WINDOW,
+                                  static_cast<uintptr_t>(childWindow.window));
+        }
+
+        // Now it has got the correct parent, make sure it is visible.
+        // In subsequent calls to SetWindow these calls happen in the Child.
+        HWND childHWND = reinterpret_cast<HWND>(childWindow.window);
+        ShowWindow(childHWND, SW_SHOWNA);
+        SetWindowPos(childHWND, nullptr, 0, 0, window.width, window.height,
+                     SWP_NOZORDER | SWP_NOREPOSITION);
+    }
+#endif
 
     return NPERR_NO_ERROR;
 }
 
 NPError
 PluginInstanceParent::NPP_GetValue(NPPVariable aVariable,
                                    void* _retval)
 {
--- a/dom/plugins/ipc/PluginWidgetParent.cpp
+++ b/dom/plugins/ipc/PluginWidgetParent.cpp
@@ -205,10 +205,25 @@ PluginWidgetParent::RecvGetNativePluginP
 #else
   *value = (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
   NS_ASSERTION(*value, "no native port??");
 #endif
   PWLOG("PluginWidgetParent::RecvGetNativeData() %p\n", (void*)*value);
   return true;
 }
 
+bool
+PluginWidgetParent::RecvSetNativeChildWindow(const uintptr_t& aChildWindow)
+{
+#if defined(XP_WIN)
+  ENSURE_CHANNEL;
+  PWLOG("PluginWidgetParent::RecvSetNativeChildWindow(%p)\n",
+        static_cast<void*>(aChildWindow));
+  mWidget->SetNativeData(NS_NATIVE_CHILD_WINDOW, aChildWindow);
+  return true;
+#else
+  NS_NOTREACHED("PluginWidgetParent::RecvSetNativeChildWindow not implemented!");
+  return false;
+#endif
+}
+
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginWidgetParent.h
+++ b/dom/plugins/ipc/PluginWidgetParent.h
@@ -26,16 +26,17 @@ class PluginWidgetParent : public PPlugi
 public:
   PluginWidgetParent();
   virtual ~PluginWidgetParent();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool RecvCreate(nsresult* aResult) override;
   virtual bool RecvSetFocus(const bool& aRaise) override;
   virtual bool RecvGetNativePluginPort(uintptr_t* value) override;
+  bool RecvSetNativeChildWindow(const uintptr_t& aChildWindow) override;
 
   // Helper for compositor checks on the channel
   bool ActorDestroyed() { return !mWidget; }
 
   // Called by PBrowser when it receives a Destroy() call from the child.
   void ParentDestroy();
 
   // Sets mWidget's parent
--- a/widget/PluginWidgetProxy.cpp
+++ b/widget/PluginWidgetProxy.cpp
@@ -136,16 +136,39 @@ PluginWidgetProxy::GetNativeData(uint32_
       return nullptr;
   }
   uintptr_t value = 0;
   mActor->SendGetNativePluginPort(&value);
   PWLOG("PluginWidgetProxy::GetNativeData %p\n", (void*)value);
   return (void*)value;
 }
 
+#if defined(XP_WIN)
+void
+PluginWidgetProxy::SetNativeData(uint32_t aDataType, uintptr_t aVal)
+{
+  if (!mActor) {
+    return;
+  }
+
+  auto tab = static_cast<mozilla::dom::TabChild*>(mActor->Manager());
+  if (tab && tab->IsDestroyed()) {
+    return;
+  }
+
+  switch (aDataType) {
+    case NS_NATIVE_CHILD_WINDOW:
+      mActor->SendSetNativeChildWindow(aVal);
+      break;
+    default:
+      NS_ERROR("SetNativeData called with unsupported data type.");
+  }
+}
+#endif
+
 NS_IMETHODIMP
 PluginWidgetProxy::SetFocus(bool aRaise)
 {
   ENSURE_CHANNEL;
   PWLOG("PluginWidgetProxy::SetFocus(%d)\n", aRaise);
   mActor->SendSetFocus(aRaise);
   return NS_OK;
 }
--- a/widget/PluginWidgetProxy.h
+++ b/widget/PluginWidgetProxy.h
@@ -38,16 +38,19 @@ public:
                     const nsIntRect& aRect,
                     nsWidgetInitData* aInitData = nullptr) override;
   NS_IMETHOD Destroy() override;
   NS_IMETHOD SetFocus(bool aRaise = false) override;
   NS_IMETHOD SetParent(nsIWidget* aNewParent) override;
 
   virtual nsIWidget* GetParent(void) override;
   virtual void* GetNativeData(uint32_t aDataType) override;
+#if defined(XP_WIN)
+  void SetNativeData(uint32_t aDataType, uintptr_t aVal) override;
+#endif
   virtual nsTransparencyMode GetTransparencyMode() override
   { return eTransparencyOpaque; }
   virtual void GetWindowClipRegion(nsTArray<nsIntRect>* aRects) override;
 
 public:
   /**
    * When tabs are closed PPluginWidget can terminate before plugin code is
    * finished tearing us down. When this happens plugin calls over mActor
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -104,16 +104,17 @@ typedef void* nsNativeWidget;
 #define NS_NATIVE_PLUGIN_PORT_QD    100
 #define NS_NATIVE_PLUGIN_PORT_CG    101
 #endif
 #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
+#define NS_NATIVE_CHILD_WINDOW         104
 #endif
 #if defined(MOZ_WIDGET_GTK)
 // set/get nsPluginNativeWindowGtk, e10s specific
 #define NS_NATIVE_PLUGIN_OBJECT_PTR    104
 #endif
 // See RegisterPluginWindowForRemoteUpdates
 #define NS_NATIVE_PLUGIN_ID            105
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -2967,16 +2967,39 @@ void* nsWindow::GetNativeData(uint32_t a
 
     default:
       break;
   }
 
   return nullptr;
 }
 
+void
+nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal)
+{
+  switch (aDataType) {
+    case NS_NATIVE_CHILD_WINDOW:
+      {
+        HWND childWindow = reinterpret_cast<HWND>(aVal);
+
+        // Make sure the window is styled to be a child window.
+        LONG_PTR style = GetWindowLongPtr(childWindow, GWL_STYLE);
+        style |= WS_CHILD;
+        style &= ~WS_POPUP;
+        SetWindowLongPtr(childWindow, GWL_STYLE, style);
+
+        // Do the reparenting.
+        ::SetParent(childWindow, mWnd);
+        break;
+      }
+    default:
+      NS_ERROR("SetNativeData called with unsupported data type.");
+  }
+}
+
 // Free some native data according to aDataType
 void nsWindow::FreeNativeData(void * data, uint32_t aDataType)
 {
   switch (aDataType)
   {
     case NS_NATIVE_GRAPHIC:
 #ifdef MOZ_XUL
       if (eTransparencyTransparent != mTransparencyMode)
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -128,16 +128,17 @@ public:
   virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
   NS_IMETHOD              MakeFullScreen(bool aFullScreen, nsIScreen* aScreen = nullptr);
   NS_IMETHOD              HideWindowChrome(bool aShouldHide);
   NS_IMETHOD              Invalidate(bool aEraseBackground = false,
                                      bool aUpdateNCArea = false,
                                      bool aIncludeChildren = false);
   NS_IMETHOD              Invalidate(const nsIntRect & aRect);
   virtual void*           GetNativeData(uint32_t aDataType);
+  void                    SetNativeData(uint32_t aDataType, uintptr_t aVal) override;
   virtual void            FreeNativeData(void * data, uint32_t aDataType);
   NS_IMETHOD              SetTitle(const nsAString& aTitle);
   NS_IMETHOD              SetIcon(const nsAString& aIconSpec);
   virtual mozilla::LayoutDeviceIntPoint WidgetToScreenOffset();
   virtual mozilla::LayoutDeviceIntSize ClientToWindowSize(const mozilla::LayoutDeviceIntSize& aClientSize) override;
   NS_IMETHOD              DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                                         nsEventStatus& aStatus);
   NS_IMETHOD              EnableDragDrop(bool aEnable);