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 249591 6a5137a37ec5e9ec6eda7eb787735357b759eb0f
parent 249590 fd2961b995ab2155e39ee16e623ec95d324fd11b
child 249592 b812439bc1ff0127a3225360d2615dbb97a70344
push id28931
push userryanvm@gmail.com
push dateThu, 18 Jun 2015 20:03:51 +0000
treeherdermozilla-central@4b0fb77f77a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1165903
milestone41.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 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);