Bug 1261842 - Add methods to nsIDocShellTreeOwner for sizing the primary content. r=smaug
authorMike Conley <mconley@mozilla.com>
Thu, 14 Jul 2016 16:31:41 -0400
changeset 346282 b45911deab9bb3ec956228d7c5d26b6f4e7880d1
parent 346281 2fbe723f054e4a8feeaef12bcb0cf3c61a1bb94a
child 346283 9d70d39a5ebb2f2ed39ebebdd344dfcceccc363e
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1261842
milestone50.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 1261842 - Add methods to nsIDocShellTreeOwner for sizing the primary content. r=smaug MozReview-Commit-ID: CqiRTVd444n
docshell/base/nsIDocShellTreeOwner.idl
dom/ipc/ContentParent.cpp
embedding/browser/nsDocShellTreeOwner.cpp
embedding/components/windowwatcher/nsPIWindowWatcher.idl
embedding/components/windowwatcher/nsWindowWatcher.cpp
embedding/components/windowwatcher/nsWindowWatcher.h
xpfe/appshell/moz.build
xpfe/appshell/nsChromeTreeOwner.cpp
xpfe/appshell/nsContentTreeOwner.cpp
xpfe/appshell/nsIXULWindow.idl
xpfe/appshell/nsXULWindow.cpp
xpfe/appshell/nsXULWindow.h
--- a/docshell/base/nsIDocShellTreeOwner.idl
+++ b/docshell/base/nsIDocShellTreeOwner.idl
@@ -76,16 +76,36 @@ interface nsIDocShellTreeOwner : nsISupp
 
 	/*
 	Tells the tree owner to size its window or parent window in such a way
 	that the shell passed along will be the size specified.
 	*/
 	void sizeShellTo(in nsIDocShellTreeItem shell, in long cx, in long cy);
 
 	/*
+	Gets the size of the primary content area in CSS pixels. This should work
+	for both in-process and out-of-process content areas.
+	*/
+	void getPrimaryContentSize(out long width, out long height);
+	/*
+	Sets the size of the primary content area in CSS pixels. This should work
+	for both in-process and out-of-process content areas.
+	*/
+	void setPrimaryContentSize(in long width, in long height);
+
+	/*
+	Gets the size of the root docshell in CSS pixels.
+	*/
+	void getRootShellSize(out long width, out long height);
+	/*
+	Sets the size of the root docshell in CSS pixels.
+	*/
+	void setRootShellSize(in long width, in long height);
+
+	/*
 	Sets the persistence of different attributes of the window.
 	*/
 	void setPersistence(in boolean aPersistPosition,
                             in boolean aPersistSize,
                             in boolean aPersistSizeMode);
 
 	/*
 	Gets the current persistence states of the window.
@@ -93,9 +113,15 @@ interface nsIDocShellTreeOwner : nsISupp
 	void getPersistence(out boolean aPersistPosition,
                             out boolean aPersistSize,
                             out boolean aPersistSizeMode);
 
 	/*
 	Gets the number of targettable docshells.
 	*/
 	readonly attribute unsigned long targetableShellCount;
+
+	/*
+	Returns true if there is a primary content shell or a primary
+	tab parent.
+	*/
+	readonly attribute bool hasPrimaryContent;
 };
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5405,32 +5405,30 @@ ContentParent::RecvCreateWindow(PBrowser
     }
 
     return true;
   }
 
   nsCOMPtr<mozIDOMWindowProxy> window;
   TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
 
-  const char* features = aFeatures.IsVoid() ? nullptr : aFeatures.get();
-
   nsCOMPtr<nsPIWindowWatcher> pwwatch =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
 
   if (NS_WARN_IF(NS_FAILED(*aResult))) {
     return true;
   }
 
   nsCOMPtr<nsITabParent> newRemoteTab;
   if (!thisTabParent) {
     // Because we weren't passed an opener tab, the content process has asked us
     // to open a new window that is unrelated to a pre-existing tab.
     *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab));
   } else {
-    *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, features, aCalledFromJS,
+    *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures, aCalledFromJS,
                                                 aFullZoom, getter_AddRefs(newRemoteTab));
   }
 
   if (NS_WARN_IF(NS_FAILED(*aResult))) {
     return true;
   }
 
   MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -439,16 +439,44 @@ nsDocShellTreeOwner::GetPrimaryTabParent
   }
 
   nsCOMPtr<nsITabParent> tab = mPrimaryTabParent;
   tab.forget(aTab);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShellTreeOwner::GetPrimaryContentSize(int32_t* aWidth,
+                                           int32_t* aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShellTreeOwner::SetPrimaryContentSize(int32_t aWidth,
+                                           int32_t aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShellTreeOwner::GetRootShellSize(int32_t* aWidth,
+                                      int32_t* aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDocShellTreeOwner::SetRootShellSize(int32_t aWidth,
+                                      int32_t aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
                                  int32_t aCX, int32_t aCY)
 {
   nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
 
   NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
 
   if (mTreeOwner) {
@@ -532,16 +560,23 @@ nsDocShellTreeOwner::GetTargetableShellC
     mTreeOwner->GetTargetableShellCount(aResult);
   } else {
     *aResult = 0;
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShellTreeOwner::GetHasPrimaryContent(bool* aResult)
+{
+  *aResult = mPrimaryTabParent || mPrimaryContentShell;
+  return NS_OK;
+}
+
 //*****************************************************************************
 // nsDocShellTreeOwner::nsIBaseWindow
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
                                 nsIWidget* aParentWidget, int32_t aX,
                                 int32_t aY, int32_t aCX, int32_t aCY)
--- a/embedding/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/nsPIWindowWatcher.idl
@@ -100,17 +100,17 @@ interface nsPIWindowWatcher : nsISupport
    * @param aOpenerFullZoom
    *        The current zoom multiplier for the opener tab. This is then
    *        applied to the newly opened window.
    *
    * @return the nsITabParent of the initial browser for the newly opened
    *         window.
    */
   nsITabParent openWindowWithTabParent(in nsITabParent aOpeningTab,
-                                       in string aFeatures,
+                                       in ACString aFeatures,
                                        in boolean aCalledFromJS,
                                        in float aOpenerFullZoom);
 
   /**
    * Find a named docshell tree item amongst all windows registered
    * with the window watcher.  This may be a subframe in some window,
    * for example.
    *
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -486,22 +486,22 @@ CheckUserContextCompatibility(nsIDocShel
   NS_ENSURE_SUCCESS(rv, false);
 
   return principalUserContextId == userContextId;
 }
 
 NS_IMETHODIMP
 nsWindowWatcher::OpenWindowWithoutParent(nsITabParent** aResult)
 {
-  return OpenWindowWithTabParent(nullptr, "", true, 1.0f, aResult);
+  return OpenWindowWithTabParent(nullptr, EmptyCString(), true, 1.0f, aResult);
 }
 
 NS_IMETHODIMP
 nsWindowWatcher::OpenWindowWithTabParent(nsITabParent* aOpeningTabParent,
-                                         const char* aFeatures,
+                                         const nsACString& aFeatures,
                                          bool aCalledFromJS,
                                          float aOpenerFullZoom,
                                          nsITabParent** aResult)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(mWindowCreator);
 
   if (!nsContentUtils::IsSafeToRunScript()) {
@@ -564,18 +564,17 @@ nsWindowWatcher::OpenWindowWithTabParent
 
   // B2G multi-screen support. mozDisplayId is returned from the
   // "display-changed" event, it is also platform-dependent.
 #ifdef MOZ_WIDGET_GONK
   int retval = WinHasOption(features, "mozDisplayId", 0, nullptr);
   windowCreator2->SetScreenId(retval);
 #endif
 
-  nsAutoCString features(aFeatures);
-  uint32_t chromeFlags = CalculateChromeFlagsForChild(features);
+  uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures);
 
   // A content process has asked for a new window, which implies
   // that the new window will need to be remote.
   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 
   bool cancel = false;
   nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
@@ -611,25 +610,25 @@ nsWindowWatcher::OpenWindowWithTabParent
   }
 
   chromeContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
 
   // Tabs opened from a content process can only open new windows
   // that will also run with out-of-process tabs.
   chromeContext->SetRemoteTabs(true);
 
-  if (PL_strcasestr(features.get(), "width=") ||
-      PL_strcasestr(features.get(), "height=")) {
+  if (PL_strcasestr(aFeatures.BeginReading(), "width=") ||
+      PL_strcasestr(aFeatures.BeginReading(), "height=")) {
     chromeTreeOwner->SetPersistence(false, false, false);
   }
 
   SizeSpec sizeSpec;
-  CalcSizeSpec(features, sizeSpec);
-  SizeOpenedDocShellItem(chromeTreeItem, parentWindowOuter, false, sizeSpec,
-                         &aOpenerFullZoom);
+  CalcSizeSpec(aFeatures, sizeSpec);
+  SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec,
+                   &aOpenerFullZoom);
 
   nsCOMPtr<nsITabParent> newTabParent;
   chromeTreeOwner->GetPrimaryTabParent(getter_AddRefs(newTabParent));
   if (NS_WARN_IF(!newTabParent)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   newTabParent.forget(aResult);
@@ -1223,18 +1222,20 @@ nsWindowWatcher::OpenWindowInternal(mozI
                                        getter_AddRefs(storage));
       if (storage) {
         newStorageManager->CloneStorage(storage);
       }
     }
   }
 
   if (isNewToplevelWindow) {
-    SizeOpenedDocShellItem(newDocShellItem, aParent, isCallerChrome, sizeSpec,
-                           aOpenerFullZoom);
+    nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
+    newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
+    SizeOpenedWindow(newTreeOwner, aParent, isCallerChrome, sizeSpec,
+                     aOpenerFullZoom);
   }
 
   // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
   if (windowIsModal || windowIsModalContentDialog) {
     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
     newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
     nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
 
@@ -2231,81 +2232,95 @@ nsWindowWatcher::CalcSizeSpec(const nsAC
       aResult.mUseDefaultHeight = true;
     } else {
       aResult.mInnerHeight = temp;
     }
     aResult.mInnerHeightSpecified = true;
   }
 }
 
-/* Size and position the new window according to aSizeSpec. This method
+/* Size and position a new window according to aSizeSpec. This method
    is assumed to be called after the window has already been given
    a default position and size; thus its current position and size are
    accurate defaults. The new window is made visible at method end.
+   @param aTreeOwner
+          The top-level nsIDocShellTreeOwner of the newly opened window.
+   @param aParent (optional)
+          The parent window from which to inherit zoom factors from if
+          aOpenerFullZoom isn't passed.
+   @param aIsCallerChrome
+          True if the code requesting the new window is privileged.
+   @param aSizeSpec
+          The size that the new window should be.
+   @param aOpenerFullZoom
+          An optional pointer to a zoom factor to scale the content
+          to.
 */
 void
-nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
-                                        mozIDOMWindowProxy* aParent,
-                                        bool aIsCallerChrome,
-                                        const SizeSpec& aSizeSpec,
-                                        float* aOpenerFullZoom)
+nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
+                                  mozIDOMWindowProxy* aParent,
+                                  bool aIsCallerChrome,
+                                  const SizeSpec& aSizeSpec,
+                                  float* aOpenerFullZoom)
 {
+  // We should only be sizing top-level windows if we're in the parent
+  // process.
+  MOZ_ASSERT(XRE_IsParentProcess());
+
   // position and size of window
   int32_t left = 0, top = 0, width = 100, height = 100;
   // difference between chrome and content size
   int32_t chromeWidth = 0, chromeHeight = 0;
   // whether the window size spec refers to chrome or content
   bool sizeChromeWidth = true, sizeChromeHeight = true;
 
   // get various interfaces for aDocShellItem, used throughout this method
-  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
-  aDocShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
-  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(treeOwner));
+  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(aTreeOwner));
   if (!treeOwnerAsWin) { // we'll need this to actually size the docshell
     return;
   }
 
   double openerZoom = aOpenerFullZoom ? *aOpenerFullZoom : 1.0;
   if (aParent && !aOpenerFullZoom) {
     nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(aParent);
     if (nsIDocument* doc = piWindow->GetDoc()) {
       if (nsIPresShell* shell = doc->GetShell()) {
         if (nsPresContext* presContext = shell->GetPresContext()) {
           openerZoom = presContext->GetFullZoom();
         }
       }
     }
   }
 
-  double scale;
+  double scale = 1.0;
   treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
 
   /* The current position and size will be unchanged if not specified
      (and they fit entirely onscreen). Also, calculate the difference
      between chrome and content sizes on aDocShellItem's window.
      This latter point becomes important if chrome and content
      specifications are mixed in aFeatures, and when bringing the window
      back from too far off the right or bottom edges of the screen. */
 
   treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
   left = NSToIntRound(left / scale);
   top = NSToIntRound(top / scale);
   width = NSToIntRound(width / scale);
   height = NSToIntRound(height / scale);
   {
-    // scope shellWindow why not
-    nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(aDocShellItem));
-    if (shellWindow) {
-      int32_t cox, coy;
-      double shellScale;
-      shellWindow->GetSize(&cox, &coy);
-      shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
-      chromeWidth = width - NSToIntRound(cox / shellScale);
-      chromeHeight = height - NSToIntRound(coy / shellScale);
+    int32_t contentWidth, contentHeight;
+    bool hasPrimaryContent = false;
+    aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
+    if (hasPrimaryContent) {
+      aTreeOwner->GetPrimaryContentSize(&contentWidth, &contentHeight);
+    } else {
+      aTreeOwner->GetRootShellSize(&contentWidth, &contentHeight);
     }
+    chromeWidth = width - contentWidth;
+    chromeHeight = height - contentHeight;
   }
 
   // Set up left/top
   if (aSizeSpec.mLeftSpecified) {
     left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
   }
 
   if (aSizeSpec.mTopSpecified) {
@@ -2347,17 +2362,16 @@ nsWindowWatcher::SizeOpenedDocShellItem(
   if (aIsCallerChrome) {
     // Only enable special priveleges for chrome when chrome calls
     // open() on a chrome window
     nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
     enabled = !aParent || chromeWin;
   }
 
   if (!enabled) {
-
     // Security check failed.  Ensure all args meet minimum reqs.
 
     int32_t oldTop = top, oldLeft = left;
 
     // We'll also need the screen dimensions
     nsCOMPtr<nsIScreen> screen;
     nsCOMPtr<nsIScreenManager> screenMgr(
       do_GetService("@mozilla.org/gfx/screenmanager;1"));
@@ -2450,17 +2464,23 @@ nsWindowWatcher::SizeOpenedDocShellItem(
     // wherever it really ended up
     treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
   }
   if (aSizeSpec.SizeSpecified()) {
     /* Prefer to trust the interfaces, which think in terms of pure
        chrome or content sizes. If we have a mix, use the chrome size
        adjusted by the chrome/content differences calculated earlier. */
     if (!sizeChromeWidth && !sizeChromeHeight) {
-      treeOwner->SizeShellTo(aDocShellItem, width * scale, height * scale);
+      bool hasPrimaryContent = false;
+      aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
+      if (hasPrimaryContent) {
+        aTreeOwner->SetPrimaryContentSize(width * scale, height * scale);
+      } else {
+        aTreeOwner->SetRootShellSize(width * scale, height * scale);
+      }
     } else {
       if (!sizeChromeWidth) {
         width += chromeWidth;
       }
       if (!sizeChromeHeight) {
         height += chromeHeight;
       }
       treeOwnerAsWin->SetSize(width * scale, height * scale, false);
--- a/embedding/components/windowwatcher/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/nsWindowWatcher.h
@@ -102,21 +102,21 @@ protected:
   static int32_t WinHasOption(const nsACString& aOptions, const char* aName,
                               int32_t aDefault, bool* aPresenceFlag);
   /* Compute the right SizeSpec based on aFeatures */
   static void CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult);
   static nsresult ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
                                           nsPIDOMWindowOuter* aParent,
                                           bool aWindowIsNew,
                                           mozIDOMWindowProxy** aOpenedWindow);
-  static void SizeOpenedDocShellItem(nsIDocShellTreeItem* aDocShellItem,
-                                     mozIDOMWindowProxy* aParent,
-                                     bool aIsCallerChrome,
-                                     const SizeSpec& aSizeSpec,
-                                     float* aOpenerFullZoom);
+  static void SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
+                               mozIDOMWindowProxy* aParent,
+                               bool aIsCallerChrome,
+                               const SizeSpec& aSizeSpec,
+                               float* aOpenerFullZoom);
   static void GetWindowTreeItem(mozIDOMWindowProxy* aWindow,
                                 nsIDocShellTreeItem** aResult);
   static void GetWindowTreeOwner(nsPIDOMWindowOuter* aWindow,
                                  nsIDocShellTreeOwner** aResult);
 
 private:
   static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags,
                                              const nsACString& aFeatures,
--- a/xpfe/appshell/moz.build
+++ b/xpfe/appshell/moz.build
@@ -33,8 +33,10 @@ UNIFIED_SOURCES += [
     'nsXULWindow.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
 
 FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild')
\ No newline at end of file
--- a/xpfe/appshell/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -264,16 +264,48 @@ nsChromeTreeOwner::TabParentRemoved(nsIT
 
 NS_IMETHODIMP
 nsChromeTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
 {
   NS_ENSURE_STATE(mXULWindow);
   return mXULWindow->GetPrimaryTabParent(aTab);
 }
 
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryContentSize(int32_t* aWidth,
+                                         int32_t* aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetPrimaryContentSize(int32_t aWidth,
+                                         int32_t aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->SetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetRootShellSize(int32_t* aWidth,
+                                    int32_t* aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetRootShellSize(int32_t aWidth,
+                                    int32_t aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->SetRootShellSize(aWidth, aHeight);
+}
+
 NS_IMETHODIMP nsChromeTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
    int32_t aCX, int32_t aCY)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SizeShellTo(aShellItem, aCX, aCY);
 }
 
 NS_IMETHODIMP
@@ -344,16 +376,23 @@ nsChromeTreeOwner::GetPersistence(bool* 
 
 NS_IMETHODIMP
 nsChromeTreeOwner::GetTargetableShellCount(uint32_t* aResult)
 {
   *aResult = 0;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsChromeTreeOwner::GetHasPrimaryContent(bool* aResult)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetHasPrimaryContent(aResult);
+}
+
 //*****************************************************************************
 // nsChromeTreeOwner::nsIBaseWindow
 //*****************************************************************************   
 
 NS_IMETHODIMP nsChromeTreeOwner::InitWindow(nativeWindow aParentNativeWindow,
    nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy)   
 {
    // Ignore widget parents for now.  Don't think those are a vaild thing to call.
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -329,16 +329,48 @@ nsContentTreeOwner::TabParentRemoved(nsI
 
 NS_IMETHODIMP
 nsContentTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
 {
   NS_ENSURE_STATE(mXULWindow);
   return mXULWindow->GetPrimaryTabParent(aTab);
 }
 
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryContentSize(int32_t* aWidth,
+                                          int32_t* aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetPrimaryContentSize(int32_t aWidth,
+                                          int32_t aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->SetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetRootShellSize(int32_t* aWidth,
+                                     int32_t* aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetRootShellSize(int32_t aWidth,
+                                     int32_t aHeight)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->SetRootShellSize(aWidth, aHeight);
+}
+
 NS_IMETHODIMP nsContentTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
    int32_t aCX, int32_t aCY)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SizeShellTo(aShellItem, aCX, aCY);
 }
 
 NS_IMETHODIMP
@@ -439,16 +471,23 @@ nsContentTreeOwner::GetPersistence(bool*
 NS_IMETHODIMP
 nsContentTreeOwner::GetTargetableShellCount(uint32_t* aResult)
 {
   NS_ENSURE_STATE(mXULWindow);
   *aResult = mXULWindow->mTargetableShells.Count();
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsContentTreeOwner::GetHasPrimaryContent(bool* aResult)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetHasPrimaryContent(aResult);
+}
+
 //*****************************************************************************
 // nsContentTreeOwner::nsIWebBrowserChrome3
 //*****************************************************************************   
 
 NS_IMETHODIMP nsContentTreeOwner::OnBeforeLinkTraversal(const nsAString &originalTarget,
                                                         nsIURI *linkURI,
                                                         nsIDOMNode *linkNode,
                                                         bool isAppTab,
--- a/xpfe/appshell/nsIXULWindow.idl
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -130,17 +130,36 @@ 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();
 
-
   /**
-   * Sometimes the child's nsDocShellTreeOwner needs to propogate a SizeShellTo call to the parent. But the
-   * shellItem argument of the call will not be available on the parent side, so we pass its dimensions here.
+   * Given the dimensions of some content area held within this
+   * XUL window, and assuming that that content area will change
+   * its dimensions in linear proportion to the dimensions of this
+   * XUL window, changes the size of the XUL window so that the
+   * content area reaches a particular size.
+   *
+   * We need to supply the content area dimensions because sometimes
+   * the child's nsDocShellTreeOwner needs to propagate a SizeShellTo
+   * call to the parent. But the shellItem argument of the call will
+   * not be available on the parent side.
+   *
    * Note: this is an internal method, other consumers should never call this.
+   *
+   * @param aDesiredWidth
+   *        The desired width of the content area in device pixels.
+   * @param aDesiredHeight
+   *        The desired height of the content area in device pixels.
+   * @param shellItemWidth
+   *        The current width of the content area.
+   * @param shellItemHeight
+   *        The current height of the content area.
    */
-  [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aCx, in int32_t aCy,
-                                                 in int32_t shellItemCx, in int32_t shellItemCy);
+  [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aDesiredWidth,
+                                                 in int32_t aDesiredHeight,
+                                                 in int32_t shellItemWidth,
+                                                 in int32_t shellItemHeight);
 };
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -56,16 +56,17 @@
 #include "prenv.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/BarProps.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TabParent.h"
 
 using namespace mozilla;
 using dom::AutoNoJSAPI;
 
 #define SIZEMODE_NORMAL     NS_LITERAL_STRING("normal")
 #define SIZEMODE_MAXIMIZED  NS_LITERAL_STRING("maximized")
 #define SIZEMODE_MINIMIZED  NS_LITERAL_STRING("minimized")
 #define SIZEMODE_FULLSCREEN NS_LITERAL_STRING("fullscreen")
@@ -1796,16 +1797,107 @@ nsresult nsXULWindow::ContentShellRemove
     if (!curItem || SameCOMIdentity(curItem, aContentShell)) {
       mTargetableShells.RemoveObjectAt(i);
     }
   }
   
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXULWindow::GetPrimaryContentSize(int32_t* aWidth,
+                                   int32_t* aHeight)
+{
+  if (mPrimaryTabParent) {
+    return GetPrimaryTabParentSize(aWidth, aHeight);
+  } else if (mPrimaryContentShell) {
+    return GetPrimaryContentShellSize(aWidth, aHeight);
+  }
+  return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+nsXULWindow::GetPrimaryTabParentSize(int32_t* aWidth,
+                                     int32_t* aHeight)
+{
+  TabParent* tabParent = TabParent::GetFrom(mPrimaryTabParent);
+  Element* element = tabParent->GetOwnerElement();
+  NS_ENSURE_STATE(element);
+
+  *aWidth = element->ClientWidth();
+  *aHeight = element->ClientHeight();
+  return NS_OK;
+}
+
+nsresult
+nsXULWindow::GetPrimaryContentShellSize(int32_t* aWidth,
+                                        int32_t* aHeight)
+{
+  NS_ENSURE_STATE(mPrimaryContentShell);
+
+  nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
+  NS_ENSURE_STATE(shellWindow);
+
+  int32_t devicePixelWidth, devicePixelHeight;
+  double shellScale = 1.0;
+  // We want to return CSS pixels. First, we get device pixels
+  // from the content area...
+  shellWindow->GetSize(&devicePixelWidth, &devicePixelHeight);
+  // And then get the device pixel scaling factor. Dividing device
+  // pixels by this scaling factor gives us CSS pixels.
+  shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
+  *aWidth = NSToIntRound(devicePixelWidth / shellScale);
+  *aHeight = NSToIntRound(devicePixelHeight / shellScale);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::SetPrimaryContentSize(int32_t aWidth,
+                                   int32_t aHeight)
+{
+  if (mPrimaryTabParent) {
+    return SetPrimaryTabParentSize(aWidth, aHeight);
+  } else if (mPrimaryContentShell) {
+    return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
+  }
+  return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+nsXULWindow::SetPrimaryTabParentSize(int32_t aWidth,
+                                     int32_t aHeight)
+{
+  int32_t shellWidth, shellHeight;
+  GetPrimaryTabParentSize(&shellWidth, &shellHeight);
+
+  double scale = 1.0;
+  GetUnscaledDevicePixelsPerCSSPixel(&scale);
+
+  SizeShellToWithLimit(aWidth, aHeight,
+                       shellWidth * scale, shellHeight * scale);
+  return NS_OK;
+}
+
+nsresult
+nsXULWindow::GetRootShellSize(int32_t* aWidth,
+                              int32_t* aHeight)
+{
+  nsCOMPtr<nsIBaseWindow> shellAsWin = do_QueryInterface(mDocShell);
+  NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
+  return shellAsWin->GetSize(aWidth, aHeight);
+}
+
+nsresult
+nsXULWindow::SetRootShellSize(int32_t aWidth,
+                              int32_t aHeight)
+{
+  nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = do_QueryInterface(mDocShell);
+  return SizeShellTo(docShellAsItem, aWidth, aHeight);
+}
+
 NS_IMETHODIMP nsXULWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
    int32_t aCX, int32_t aCY)
 {
   // XXXTAB This is wrong, we should actually reflow based on the passed in
   // shell.  For now we are hacking and doing delta sizing.  This is bad
   // because it assumes all size we add will go to the shell which probably
   // won't happen.
 
@@ -1924,16 +2016,22 @@ NS_IMETHODIMP nsXULWindow::CreateNewCont
   NS_ENSURE_STATE(xulWin->mPrimaryContentShell || xulWin->mPrimaryTabParent);
 
   *_retval = newWindow;
   NS_ADDREF(*_retval);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP nsXULWindow::GetHasPrimaryContent(bool* aResult)
+{
+  *aResult = mPrimaryTabParent || mPrimaryContentShell;
+  return NS_OK;
+}
+
 void nsXULWindow::EnableParent(bool aEnable)
 {
   nsCOMPtr<nsIBaseWindow> parentWindow;
   nsCOMPtr<nsIWidget>     parentWidget;
 
   parentWindow = do_QueryReferent(mParentWindow);
   if (parentWindow)
     parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
@@ -2177,34 +2275,36 @@ 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)
+void nsXULWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
+                                       int32_t aDesiredHeight,
+                                       int32_t shellItemWidth,
+                                       int32_t shellItemHeight)
 {
-  int32_t widthDelta = aCX - shellItemCx;
-  int32_t heightDelta = aCY - shellItemCy;
+  int32_t widthDelta = aDesiredWidth - shellItemWidth;
+  int32_t heightDelta = aDesiredHeight - shellItemHeight;
 
   if (widthDelta || heightDelta) {
-    int32_t winCX = 0;
-    int32_t winCY = 0;
+    int32_t winWidth = 0;
+    int32_t winHeight = 0;
 
-    GetSize(&winCX, &winCY);
+    GetSize(&winWidth, &winHeight);
     // There's no point in trying to make the window smaller than the
-    // desired docshell size --- that's not likely to work. This whole
+    // desired content area 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);
+    // "border" chrome to the content area.
+    winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
+    winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
+    SetSize(winWidth, winHeight, true);
   }
 }
 
 //*****************************************************************************
 //*** nsContentShellInfo: Object Management
 //*****************************************************************************   
 
 nsContentShellInfo::nsContentShellInfo(const nsAString& aID,
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -104,21 +104,31 @@ protected:
    NS_IMETHOD GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow);
    mozilla::dom::Element* GetWindowDOMElement() const;
 
    // See nsIDocShellTreeOwner for docs on next two methods
    nsresult ContentShellAdded(nsIDocShellTreeItem* aContentShell,
                                           bool aPrimary, bool aTargetable,
                                           const nsAString& aID);
    nsresult ContentShellRemoved(nsIDocShellTreeItem* aContentShell);
+   NS_IMETHOD GetPrimaryContentSize(int32_t* aWidth,
+                                    int32_t* aHeight);
+   NS_IMETHOD SetPrimaryContentSize(int32_t aWidth,
+                                    int32_t aHeight);
+   nsresult GetRootShellSize(int32_t* aWidth,
+                             int32_t* aHeight);
+   nsresult SetRootShellSize(int32_t aWidth,
+                             int32_t aHeight);
+
    NS_IMETHOD SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, 
       int32_t aCY);
    NS_IMETHOD ExitModalLoop(nsresult aStatus);
    NS_IMETHOD CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, nsIXULWindow **_retval);
    NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, nsIXULWindow **_retval);
+   NS_IMETHOD GetHasPrimaryContent(bool* aResult);
 
    void       EnableParent(bool aEnable);
    bool       ConstrainToZLevel(bool aImmediate, nsWindowZ *aPlacement,
                                 nsIWidget *aReqBelow, nsIWidget **aActualBelow);
    void       PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
                                       nsIXULWindow *aBehind);
    void       SetContentScrollbarVisibility(bool aVisible);
    bool       GetContentScrollbarVisibility();
@@ -158,16 +168,20 @@ protected:
    uint32_t                mPersistentAttributesMask;
    uint32_t                mChromeFlags;
    nsString                mTitle;
    nsIntRect               mOpenerScreenRect; // the screen rect of the opener
 
    nsCOMArray<nsIWeakReference> mTargetableShells; // targetable shells only
 
    nsCOMPtr<nsITabParent> mPrimaryTabParent;
+private:
+   nsresult GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight);
+   nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
+   nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)
 
 // nsContentShellInfo
 // Used to map shell IDs to nsIDocShellTreeItems.
 
 class nsContentShellInfo