Bug 1255138 - Window resize support from JS for e10s draft
authorMike Conley <mconley@mozilla.com>
Thu, 28 Apr 2016 14:29:04 -0400
changeset 363493 c62ef681dc398269af3533c78083bf29904e2373
parent 363492 2b49fa3fe5c8c01f0d284867222b4553b4c3f36c
child 363494 bfbd12dd3c0e1a8942687cb2875eeb2f86ad63bb
push id17222
push usermconley@mozilla.com
push dateWed, 04 May 2016 21:02:55 +0000
bugs1255138
milestone49.0a1
Bug 1255138 - Window resize support from JS for e10s MozReview-Commit-ID: 7cSuSRdE39L
dom/interfaces/base/nsITabChild.idl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/tests/mochitest/bugs/mochitest.ini
dom/tests/mochitest/bugs/test_resize_move_windows.html
dom/tests/mochitest/bugs/test_sizetocontent_clamp.html
embedding/browser/nsDocShellTreeOwner.cpp
xpfe/appshell/nsIXULWindow.idl
xpfe/appshell/nsXULWindow.cpp
--- a/dom/interfaces/base/nsITabChild.idl
+++ b/dom/interfaces/base/nsITabChild.idl
@@ -20,11 +20,14 @@ interface nsITabChild : nsISupports
   [notxpcom] void sendRequestFocus(in boolean canFocus);
 
   [notxpcom] void sendGetTabCount(out uint32_t tabCount);
 
   [noscript, notxpcom] void enableDisableCommands(in AString action,
                                                   in CommandsArrayRef enabledCommands,
                                                   in CommandsArrayRef disabledCommands);
 
+  [notxpcom] void remoteSizeShellTo(in int32_t width, in int32_t height,
+                                    in int32_t shellItemWidth, in int32_t shellItemHeight);
+
   readonly attribute uint64_t tabId;
 };
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -153,16 +153,19 @@ parent:
 
 parent:
     /**
      * When child sends this message, parent should move focus to
      * the next or previous focusable element or document.
      */
     async MoveFocus(bool forward, bool forDocumentNavigation);
 
+    async SizeShellTo(int32_t aWidth, int32_t aHeight,
+                      int32_t aShellItemWidth, int32_t aShellItemHeight);
+
     async Event(RemoteDOMEvent aEvent);
 
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
       returns (StructuredCloneData[] retval);
 
     prio(high) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
                                CpowEntry[] aCpows, Principal aPrincipal)
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -47,16 +47,17 @@
 #include "mozilla/StaticPtr.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 #include "mozIApplication.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsEmbedCID.h"
+#include "nsGlobalWindow.h"
 #include <algorithm>
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 #include "nsFilePickerProxy.h"
 #include "mozilla/dom/Element.h"
 #include "nsIBaseWindow.h"
 #include "nsIBrowserDOMWindow.h"
@@ -937,18 +938,38 @@ TabChild::SetChromeFlags(uint32_t aChrom
 NS_IMETHODIMP
 TabChild::DestroyBrowserWindow()
 {
   NS_WARNING("TabChild::DestroyBrowserWindow not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+static
+void SetOrReset(int32_t& aValue, int32_t aNew)
+{
+  aValue = aValue == aNew ? INT32_MIN : aNew;
+}
+
+void
+TabChild::RemoteSizeShellTo(int32_t aWidth, int32_t aHeight,
+                            int32_t aShellItemWidth, int32_t aShellItemHeight)
+{
+  nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+  nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(ourDocShell));
+  int32_t width, height;
+  docShellAsWin->GetSize(&width, &height);
+  SetOrReset(width, aWidth);
+  SetOrReset(height, aHeight);
+
+  Unused << SendSizeShellTo(width, height, aShellItemWidth, aShellItemHeight);
+}
+
 NS_IMETHODIMP
-TabChild::SizeBrowserTo(int32_t aCX, int32_t aCY)
+TabChild::SizeBrowserTo(int32_t aWidth, int32_t aHeight)
 {
   NS_WARNING("TabChild::SizeBrowserTo not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 TabChild::ShowAsModal()
@@ -982,19 +1003,35 @@ TabChild::SetStatusWithContext(uint32_t 
   // mRemoteFrame is a good indicator.
   if (mRemoteFrame)
     SendSetStatus(aStatusType, nsString(aStatusText));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY,
-                             int32_t aCx, int32_t aCy)
+                        int32_t aCx, int32_t aCy)
 {
-  Unused << PBrowserChild::SendSetDimensions(aFlags, aX, aY, aCx, aCy);
+  // The parent is in charge of the dimension changes. If JS code wants to
+  // change the dimensions (moveTo, screenX, etc.) we send a message to the
+  // parent about the new requested dimension, the parent does the resize/move
+  // then send a message to the child to update itself. For APIs like screenX
+  // this function is called with the current value for the non-changed values.
+  // In a series of calls like window.screenX = 10; window.screenY = 10; for
+  // the second call, since screenX is not yet updated we might accidentally
+  // reset back screeX to it's old value. To avoid this if a parameter did not
+  // change we want the parent to ignore its value. For that we will use a special
+  // value: INT32_MIN;
+  int32_t x, y, cx, cy;
+  GetDimensions(aFlags, &x, &y, &cx, &cy);
+  SetOrReset(x, aX);
+  SetOrReset(y, aY);
+  SetOrReset(cx, aCx);
+  SetOrReset(cy, aCy);
+  Unused << SendSetDimensions(aFlags, x, y, cx, cy);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::GetDimensions(uint32_t aFlags, int32_t* aX,
                              int32_t* aY, int32_t* aCx, int32_t* aCy)
 {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -614,16 +614,47 @@ TabParent::RecvMoveFocus(const bool& aFo
                                 static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD));
     nsCOMPtr<nsIDOMElement> frame = do_QueryInterface(mFrameElement);
     fm->MoveFocus(nullptr, frame, type, nsIFocusManager::FLAG_BYKEY,
                   getter_AddRefs(dummy));
   }
   return true;
 }
 
+static
+void SetOrIgnore(int32_t& aValue, int32_t aNew)
+{
+  aValue = INT32_MIN == aNew ? aValue : aNew;
+}
+
+bool
+TabParent::RecvSizeShellTo(const int32_t& aWidth, const int32_t& aHeight,
+                           const int32_t& aShellItemWidth, const int32_t& aShellItemHeight)
+{
+  NS_ENSURE_TRUE(mFrameElement, true);
+
+  nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+  NS_ENSURE_TRUE(docShell, true);
+
+  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+  nsresult rv = docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+  NS_ENSURE_SUCCESS(rv, true);
+
+  int32_t width = mDimensions.width;
+  int32_t height = mDimensions.height;
+  SetOrIgnore(width, aWidth);
+  SetOrIgnore(height, aHeight);
+
+  nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
+  NS_ENSURE_TRUE(xulWin, true);
+  xulWin->SizeShellToWithLimit(width, height, aShellItemWidth, aShellItemHeight);
+
+  return true;
+}
+
 bool
 TabParent::RecvEvent(const RemoteDOMEvent& aEvent)
 {
   nsCOMPtr<nsIDOMEvent> event = do_QueryInterface(aEvent.mEvent);
   NS_ENSURE_TRUE(event, true);
 
   nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
   NS_ENSURE_TRUE(target, true);
@@ -809,29 +840,41 @@ TabParent::RecvSetDimensions(const uint3
   NS_ENSURE_TRUE(mFrameElement, true);
   nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
   NS_ENSURE_TRUE(docShell, true);
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   docShell->GetTreeOwner(getter_AddRefs(treeOwner));
   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(treeOwner);
   NS_ENSURE_TRUE(treeOwnerAsWin, true);
 
+  // We only care about the parameters that actually changed, see more
+  // details in TabChild::SetDimensions.
+  int32_t x, y, cx, cy;
+  treeOwnerAsWin->GetPosition(&x, &y);
+  treeOwnerAsWin->GetSize(&cx, &cy);
+  SetOrIgnore(x, aX);
+  SetOrIgnore(y, aY);
+  SetOrIgnore(cx, aCx);
+  SetOrIgnore(cy, aCy);
+
   if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION &&
       aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
-    treeOwnerAsWin->SetPositionAndSize(aX, aY, aCx, aCy, true);
+    treeOwnerAsWin->SetPositionAndSize(x, y, cx, cy, true);
     return true;
   }
 
   if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) {
-    treeOwnerAsWin->SetPosition(aX, aY);
+    treeOwnerAsWin->SetPosition(x, y);
+    mUpdatedDimensions = false;
+    UpdatePosition();
     return true;
   }
 
   if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
-    treeOwnerAsWin->SetSize(aCx, aCy, true);
+    treeOwnerAsWin->SetSize(cx, cy, true);
     return true;
   }
 
   MOZ_ASSERT(false, "Unknown flags!");
   return false;
 }
 
 nsresult
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -156,16 +156,21 @@ public:
 
   void AddWindowListeners();
 
   void DidRefresh() override;
 
   virtual bool RecvMoveFocus(const bool& aForward,
                              const bool& aForDocumentNavigation) override;
 
+  virtual bool RecvSizeShellTo(const int32_t& aWidth,
+                               const int32_t& aHeight,
+                               const int32_t& aShellItemWidth,
+                               const int32_t& aShellItemHeight) override;
+
   virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override;
 
   virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override;
 
   virtual bool
   RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) override;
 
   virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -155,18 +155,18 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_bug927901.html]
 [test_devicemotion_multiple_listeners.html]
 skip-if = toolkit == 'android' #bug 775227
 [test_domparser_after_blank.html]
 [test_errorReporting.html]
 [test_onerror_message.html]
 [test_protochains.html]
 [test_resize_move_windows.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #Windows can't change size and position on Android # b2g(Windows can't change size and position on B2G) b2g-debug(Windows can't change size and position on B2G) b2g-desktop(Windows can't change size and position on B2G)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Windows can't change size and position on Android # b2g(Windows can't change size and position on B2G) b2g-debug(Windows can't change size and position on B2G) b2g-desktop(Windows can't change size and position on B2G)
 [test_sizetocontent_clamp.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #Windows can't change size on Android # b2g(Windows can't change size on B2G) b2g-debug(Windows can't change size on B2G) b2g-desktop(Windows can't change size on B2G)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Windows can't change size on Android # b2g(Windows can't change size on B2G) b2g-debug(Windows can't change size on B2G) b2g-desktop(Windows can't change size on B2G)
 [test_toJSON.html]
 [test_window_bar.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1022869.html]
 [test_bug1112040.html]
 [test_bug1160342_marquee.html]
 [test_bug1171215.html]
--- a/dom/tests/mochitest/bugs/test_resize_move_windows.html
+++ b/dom/tests/mochitest/bugs/test_resize_move_windows.html
@@ -232,95 +232,86 @@ function checkChangeIsEnabled(aWindow, a
 
   function outerChangeCondition() {
     return aWindow.outerWidth != oWidth && aWindow.outerHeight != oHeight;
   }
 
   function outerChangeTest() {
     isnot(aWindow.outerWidth, oWidth, "Window outerWidth should have changed");
     isnot(aWindow.outerHeight, oHeight, "Window outerHeight should have changed");
-
-    aWindow.outerWidth = oWidth;
-    aWindow.outerHeight = oHeight;
   }
 
   /**
    * Size checks.
    */
   prevWidth = aWindow.innerWidth;
   prevHeight = aWindow.innerHeight;
-
   aWindow.innerWidth = getNewWidth(aWindow);
   aWindow.innerHeight = getNewHeight(aWindow);
 
   hitEventLoop(sizeChangeCondition, sizeChangeTest, hits)
   .then(function() {
     aWindow.resizeTo(getNewWidth(aWindow), getNewHeight(aWindow));
-  })
-  .then(function() {
     return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
   })
   .then(function () {
     aWindow.resizeBy(getNewWidth(aWindow) - aWindow.innerWidth,
                      getNewHeight(aWindow) - aWindow.innerHeight);
-  })
-  .then(function() {
     return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
   })
-  .then(function () {
-    prevWidth = aWindow.innerWidth = getNewWidth(aWindow);
-    prevHeight = aWindow.innerHeight = getNewHeight(aWindow);
+  .then(function() {
     aWindow.sizeToContent();
-  })
-  .then(function() {
-    hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
+    return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
   })
   .then(function() {
     /**
      * Position checks.
      */
     prevX = aWindow.screenX;
     prevY = aWindow.screenY;
 
     aWindow.screenX = getNewX(aWindow);
     aWindow.screenY = getNewY(aWindow);
-  })
-  .then(function() {
     return hitEventLoop(posChangeCondition, posChangeTest, hits);
   })
   .then(function() {
     prevX = aWindow.screenX;
     prevY = aWindow.screenY;
 
     aWindow.moveTo(getNewX(aWindow), getNewY(aWindow));
-  })
-  .then(function() {
     return hitEventLoop(posChangeCondition, posChangeTest, hits);
   })
   .then(function() {
     prevX = aWindow.screenX;
     prevY = aWindow.screenY;
 
     aWindow.moveBy(getNewX(aWindow) - aWindow.screenX,
                    getNewY(aWindow) - aWindow.screenY);
-  })
-  .then(function() {
     return hitEventLoop(posChangeCondition, posChangeTest, hits);
   })
   .then(function() {
     /**
      * Outer width/height checks.
      */
     oWidth = aWindow.outerWidth;
     oHeight = aWindow.outerHeight;
 
     aWindow.outerWidth = oWidth * 2;
     aWindow.outerHeight = oHeight * 2;
+    return hitEventLoop(outerChangeCondition, outerChangeTest, hits);
   })
   .then(function() {
+    let origWidth = oWidth;
+    let origHeight = oHeight;
+
+    oWidth = aWindow.outerWidth;
+    oHeight = aWindow.outerHeight;
+
+    aWindow.outerWidth = origWidth;
+    aWindow.outerHeight = origHeight;
     return hitEventLoop(outerChangeCondition, outerChangeTest, hits);
   })
   .then(aNext);
 }
 
 SpecialPowers.pushPrefEnv({"set": [["dom.disable_window_move_resize", false]]}, function() {
 SimpleTest.waitForFocus(function() {
   if (screen.width <= 200 || screen.height <= 200) {
--- a/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html
+++ b/dom/tests/mochitest/bugs/test_sizetocontent_clamp.html
@@ -30,35 +30,34 @@ var epsilon =  navigator.platform.indexO
 
 // Windows 8 has a minimum 122 pixel inner window width due to
 // outer window chrome.
 var isWin8 = (navigator.userAgent.indexOf("Windows NT 6.2") != -1);
 
 var innerWidthMin = (isWin8 ? 120 : 100);
 var innerWidthMax = (isWin8 ? 125 : 100);
 
+var isExecuted = false;
+
 function test() {
   var w = window.open('data:text/html,null', null, 'width=300,height=300');
-  var nbResize = 0;
 
   SimpleTest.waitForFocus(function() {
     w.onresize = function() {
-      nbResize++;
 
-      if (nbResize == 1) {
+      if (w.innerWidth > 300 - epsilon || isExecuted) {
         return;
       }
 
+      isExecuted = true;
+
       ok(w.innerWidth + epsilon >= innerWidthMin && w.innerWidth - epsilon <= innerWidthMax,
-         "innerWidth should be between " + innerWidthMin + " and " + innerWidthMax);
+         "innerWidth should be between " + innerWidthMin + " and " + innerWidthMax + " but it was: " + w.innerWidth);
       ok(w.innerHeight + epsilon >= 100 && w.innerHeight - epsilon <= 100,
-         "innerHeight should be around 100");
-
-      // It's not clear why 2 events are coming...
-      is(nbResize, 2, "We should get 2 events.");
+         "innerHeight should be around 100" + " but it was: " + w.innerHeight);
 
       w.close();
 
       SimpleTest.waitForFocus(function() {
         SimpleTest.finish();
       });
     };
     w.sizeToContent();
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -52,16 +52,17 @@
 #include "nsIStringBundle.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsIWindowWatcher.h"
 #include "nsPIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsITabParent.h"
+#include "nsITabChild.h"
 #include "nsRect.h"
 #include "nsIWebBrowserChromeFocus.h"
 #include "nsIContent.h"
 #include "imgIContainer.h"
 #include "nsContextMenuInfo.h"
 #include "nsPresContext.h"
 #include "nsViewManager.h"
 #include "nsView.h"
@@ -450,16 +451,30 @@ nsDocShellTreeOwner::SizeShellTo(nsIDocS
 
   NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
 
   if (mTreeOwner) {
     return mTreeOwner->SizeShellTo(aShellItem, aCX, aCY);
   }
 
   if (aShellItem == mWebBrowser->mDocShell) {
+    nsCOMPtr<nsITabChild> tabChild = do_QueryInterface(webBrowserChrome);
+    if (tabChild) {
+      // The XUL window to resize is in the parent process, but there we
+      // won't be able to get aShellItem to do the hack in nsXULWindow::SizeShellTo,
+      // so let's send the width and height of aShellItem too.
+      nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
+      NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
+
+      int32_t width = 0;
+      int32_t height = 0;
+      shellAsWin->GetSize(&width, &height);
+      tabChild->RemoteSizeShellTo(aCX, aCY, width, height);
+      return NS_OK;
+    }
     return webBrowserChrome->SizeBrowserTo(aCX, aCY);
   }
 
   nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(aShellItem));
   NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDOMDocument> domDocument;
   webNav->GetDocument(getter_AddRefs(domDocument));
@@ -573,17 +588,18 @@ nsDocShellTreeOwner::GetDevicePixelsPerD
   *aScale = 1.0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY)
 {
   if (mWebBrowser) {
-    return mWebBrowser->SetPositionDesktopPix(aX, aY);
+    nsresult rv = mWebBrowser->SetPositionDesktopPix(aX, aY);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   double scale = 1.0;
   GetDevicePixelsPerDesktopPixel(&scale);
   return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
 }
 
 NS_IMETHODIMP
--- a/xpfe/appshell/nsIXULWindow.idl
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -13,17 +13,17 @@
  * notification through the global observer service.
  */
 
 interface nsIDocShell;
 interface nsIDocShellTreeItem;
 interface nsIXULBrowserWindow;
 interface nsITabParent;
 
-[scriptable, uuid(d6d7a014-e28d-4c9d-8727-1cf6d870619b)]
+[scriptable, uuid(0d2760ac-636e-4b80-a8ec-11385bc47f0e)]
 interface nsIXULWindow : nsISupports
 {
   /**
    * The docshell owning the XUL for this window.
    */
   readonly attribute nsIDocShell docShell;
 
   /**
@@ -129,9 +129,12 @@ interface nsIXULWindow : nsISupports
 
   /**
    * Back-door method to force application of chrome flags at a particular
    * time.  Do NOT call this unless you know what you're doing!  In particular,
    * calling this when this XUL window doesn't yet have a document in its
    * docshell could cause problems.
    */
   [noscript] void applyChromeFlags();
+
+  [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aCx, in int32_t aCy,
+                                                 in int32_t shellItemCx, in int32_t shellItemCy);
 };
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -1810,32 +1810,17 @@ NS_IMETHODIMP nsXULWindow::SizeShellTo(n
 
   nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
   NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
 
   int32_t width = 0;
   int32_t height = 0;
   shellAsWin->GetSize(&width, &height);
 
-  int32_t widthDelta = aCX - width;
-  int32_t heightDelta = aCY - height;
-
-  if (widthDelta || heightDelta) {
-    int32_t winCX = 0;
-    int32_t winCY = 0;
-
-    GetSize(&winCX, &winCY);
-    // There's no point in trying to make the window smaller than the
-    // desired docshell size --- that's not likely to work. This whole
-    // function assumes that the outer docshell is adding some constant
-    // "border" chrome to aShellItem.
-    winCX = std::max(winCX + widthDelta, aCX);
-    winCY = std::max(winCY + heightDelta, aCY);
-    SetSize(winCX, winCY, true);
-  }
+  SizeShellToWithLimit(aCX, aCY, width, height);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsXULWindow::ExitModalLoop(nsresult aStatus)
 {
   if (mContinueModalLoop)
     EnableParent(true);
@@ -2191,16 +2176,37 @@ NS_IMETHODIMP nsXULWindow::GetXULBrowser
 }
 
 NS_IMETHODIMP nsXULWindow::SetXULBrowserWindow(nsIXULBrowserWindow * aXULBrowserWindow)
 {
   mXULBrowserWindow = aXULBrowserWindow;
   return NS_OK;
 }
 
+void nsXULWindow::SizeShellToWithLimit(int32_t aCX, int32_t aCY,
+                                       int32_t shellItemCx, int32_t shellItemCy)
+{
+  int32_t widthDelta = aCX - shellItemCx;
+  int32_t heightDelta = aCY - shellItemCy;
+
+  if (widthDelta || heightDelta) {
+    int32_t winCX = 0;
+    int32_t winCY = 0;
+
+    GetSize(&winCX, &winCY);
+    // There's no point in trying to make the window smaller than the
+    // desired docshell size --- that's not likely to work. This whole
+    // function assumes that the outer docshell is adding some constant
+    // "border" chrome to aShellItem.
+    winCX = std::max(winCX + widthDelta, aCX);
+    winCY = std::max(winCY + heightDelta, aCY);
+    SetSize(winCX, winCY, true);
+  }
+}
+
 //*****************************************************************************
 //*** nsContentShellInfo: Object Management
 //*****************************************************************************   
 
 nsContentShellInfo::nsContentShellInfo(const nsAString& aID,
                                        nsIWeakReference* aContentShell)
   : id(aID),
     child(aContentShell)