Bug 489303, show resizer when statusbar is hidden, r=roc,gavin a=blocking
authorNeil Deakin <neil@mozilla.com>
Mon, 17 Jan 2011 09:35:32 -0500
changeset 60693 70f1fbe19abb1196bba45b335e2a35fbe512e7bb
parent 60692 48e1cb943266114c9e943dc756f1a8d7c0641d4e
child 60694 b27366868c807e07a11741a7a2eb404ebc08e5e7
push id18080
push userneil@mozilla.com
push dateMon, 17 Jan 2011 14:37:31 +0000
treeherdermozilla-central@b27366868c80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, gavin, blocking
bugs489303
milestone2.0b10pre
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 489303, show resizer when statusbar is hidden, r=roc,gavin a=blocking
accessible/tests/mochitest/events/test_scroll.xul
accessible/tests/mochitest/relations/test_tabbrowser.xul
accessible/tests/mochitest/tree/test_tabbrowser.xul
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
content/base/src/nsGkAtomList.h
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsSubDocumentFrame.cpp
layout/style/jar.mn
layout/style/ua.css
layout/xul/base/src/nsResizerFrame.cpp
toolkit/content/widgets/browser.xml
toolkit/content/widgets/resizer.xml
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -123,11 +123,12 @@
             setfocus="false">
         <tab class="tabbrowser-tab" selected="true"/>
       </tabs>
       <tabbrowser id="tabBrowser"
                   type="content-primary"
                   tabcontainer="tabbrowser-tabs"
                   flex="1"/>
     </vbox>
+    <toolbar id="addon-bar"/>
   </hbox>
 
 </window>
--- a/accessible/tests/mochitest/relations/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -115,12 +115,13 @@
           tabbrowser="tabbrowser"
           setfocus="false">
       <tab class="tabbrowser-tab" selected="true"/>
     </tabs>
     <tabbrowser id="tabbrowser"
                 type="content-primary"
                 tabcontainer="tabbrowser-tabs"
                 flex="1"/>
+    <toolbar id="addon-bar"/>
   </vbox>
 
 </window>
 
--- a/accessible/tests/mochitest/tree/test_tabbrowser.xul
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -149,12 +149,13 @@
           tabbrowser="tabbrowser"
           setfocus="false">
       <tab class="tabbrowser-tab" selected="true" fadein="true"/>
     </tabs>
     <tabbrowser id="tabbrowser"
                 type="content-primary"
                 tabcontainer="tabbrowser-tabs"
                 flex="1"/>
+    <toolbar id="addon-bar"/>
   </vbox>
 
 </window>
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -501,15 +501,15 @@ window[chromehidden~="toolbar"] toolbar:
 
 /* override hidden="true" for the status bar compatibility shim
    in case it was persisted for the real status bar */
 #status-bar {
   display: -moz-box;
 }
 
 /* Remove the resizer from the statusbar compatibility shim */
-#status-bar > .statusbar-resizerpanel {
+#status-bar[hideresizer] > .statusbar-resizerpanel {
   display: none;
 }
 
 browser[tabmodalPromptShowing] {
   -moz-user-focus: none !important;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1259,16 +1259,19 @@ function BrowserStartup() {
       // border.  Use 28px as a guess (titlebar + bottom window border)
       defaultHeight -= 28;
 #endif
     }
     document.documentElement.setAttribute("width", defaultWidth);
     document.documentElement.setAttribute("height", defaultHeight);
   }
 
+  if (!gShowPageResizers)
+    document.getElementById("status-bar").setAttribute("hideresizer", "true");
+
   if (!window.toolbar.visible) {
     // adjust browser UI for popups
     if (gURLBar) {
       gURLBar.setAttribute("readonly", "true");
       gURLBar.setAttribute("enablehistory", "false");
     }
     goSetCommandEnabled("Browser:OpenLocation", false);
     goSetCommandEnabled("cmd_newNavigatorTab", false);
@@ -2771,34 +2774,37 @@ var PrintPreviewListener = {
     var notificationBox = gBrowser.getNotificationBox();
     this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
     notificationBox.notificationsHidden = true;
 
     document.getElementById("sidebar").setAttribute("src", "about:blank");
     var addonBar = document.getElementById("addon-bar");
     this._chromeState.addonBarOpen = !addonBar.collapsed;
     addonBar.collapsed = true;
+    gBrowser.updateWindowResizers();
 
     this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
     if (gFindBarInitialized)
       gFindBar.close();
 
     this._chromeState.syncNotificationsOpen = false;
     var syncNotifications = document.getElementById("sync-notifications");
     if (syncNotifications) {
       this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
       syncNotifications.notificationsHidden = true;
     }
   },
   _showChrome: function () {
     if (this._chromeState.notificationsOpen)
       gBrowser.getNotificationBox().notificationsHidden = false;
 
-    if (this._chromeState.addonBarOpen)
+    if (this._chromeState.addonBarOpen) {
       document.getElementById("addon-bar").collapsed = false;
+      gBrowser.updateWindowResizers();
+    }
 
     if (this._chromeState.findOpen)
       gFindBar.open();
 
     if (this._chromeState.syncNotificationsOpen)
       document.getElementById("sync-notifications").notificationsHidden = false;
   }
 }
@@ -4762,16 +4768,17 @@ function setToolbarVisibility(toolbar, i
   var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
                         "autohide" : "collapsed";
 
   toolbar.setAttribute(hidingAttribute, !isVisible);
   document.persist(toolbar.id, hidingAttribute);
 
   PlacesToolbarHelper.init();
   BookmarksMenuButton.updatePosition();
+  gBrowser.updateWindowResizers();
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
   toggle: function () {
@@ -8324,8 +8331,20 @@ let AddonsMgrListener = {
   onUninstalling: function(aAddon) {
     this.lastAddonBarCount = this.getAddonBarItemCount();
   },
   onUninstalled: function(aAddon) {
     if (this.lastAddonBarCount > 0 && this.getAddonBarItemCount() == 0)
       setToolbarVisibility(this.addonBar, false);
   }
 };
+
+XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
+#ifdef XP_WIN
+  // Only show resizers on Windows 2000 and XP
+  let sysInfo = Components.classes["@mozilla.org/system-info;1"]
+                          .getService(Components.interfaces.nsIPropertyBag2);
+  return parseFloat(sysInfo.getProperty("version")) < 6;
+#else
+  return false;
+#endif
+});
+
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -172,16 +172,28 @@
           for (var i = 0; i < this.tabs.length; i++) {
             if (!this.tabs[i].pinned)
               break;
           }
           return i;
         ]]></getter>
       </property>
 
+      <method name="updateWindowResizers">
+        <body><![CDATA[
+          if (!window.gShowPageResizers)
+            return;
+
+          var show = document.getElementById("addon-bar").collapsed;
+          for (let i = 0; i < this.browsers.length; i++) {
+            this.browsers[i].showWindowResizer = show;
+          }
+        ]]></body>
+      </method>
+
       <method name="pinTab">
         <parameter name="aTab"/>
         <body><![CDATA[
           if (aTab.pinned)
             return;
 
           if (aTab.hidden)
             this.showTab(aTab);
@@ -1231,16 +1243,21 @@
 
             var b = document.createElementNS(
               "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                              "browser");
             b.setAttribute("type", "content-targetable");
             b.setAttribute("message", "true");
             b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
             b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
+
+            if (window.gShowPageResizers && document.getElementById("addon-bar").collapsed) {
+              b.setAttribute("showresizer", "true");
+            }
+
             if (this.hasAttribute("autocompletepopup"))
               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
 
             // Create the browserStack container
             var stack = document.createElementNS(
                                     "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                     "stack");
@@ -2424,16 +2441,17 @@
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
           this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
+          this.updateWindowResizers();
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           for (var i = 0; i < this.mTabListeners.length; ++i) {
             let browser = this.getBrowserAtIndex(i);
             if (browser.registeredOpenURI) {
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -881,16 +881,17 @@ GK_ATOM(selectedIndex, "selectedIndex")
 GK_ATOM(selectedindex, "selectedindex")
 GK_ATOM(self, "self")
 GK_ATOM(seltype, "seltype")
 GK_ATOM(setcookie, "set-cookie")
 GK_ATOM(setter, "setter")
 GK_ATOM(shape, "shape")
 GK_ATOM(show, "show")
 GK_ATOM(showcaret, "showcaret")
+GK_ATOM(showresizer, "showresizer")
 GK_ATOM(simple, "simple")
 GK_ATOM(single, "single")
 GK_ATOM(size, "size")
 GK_ATOM(sizemode, "sizemode")
 GK_ATOM(sizetopopup, "sizetopopup")
 GK_ATOM(slider, "slider")
 GK_ATOM(small, "small")
 GK_ATOM(smooth, "smooth")
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -779,16 +779,37 @@ nsHTMLScrollFrame::GetPadding(nsMargin& 
 
 PRBool
 nsHTMLScrollFrame::IsCollapsed(nsBoxLayoutState& aBoxLayoutState)
 {
   // We're never collapsed in the box sense.
   return PR_FALSE;
 }
 
+// Return the <browser> if the scrollframe is for the root frame directly
+// inside a <browser>.
+static nsIContent*
+GetBrowserRoot(nsIContent* aContent)
+{
+  if (aContent) {
+    nsIDocument* doc = aContent->GetCurrentDoc();
+    nsPIDOMWindow* win = doc->GetWindow();
+    if (win) {
+      nsCOMPtr<nsIContent> frameContent =
+        do_QueryInterface(win->GetFrameElementInternal());
+      if (frameContent &&
+          frameContent->NodeInfo()->Equals(nsGkAtoms::browser, kNameSpaceID_XUL))
+        return frameContent;
+    }
+  }
+
+  return nsnull;
+}
+
+
 NS_IMETHODIMP
 nsHTMLScrollFrame::Reflow(nsPresContext*           aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
@@ -808,21 +829,33 @@ nsHTMLScrollFrame::Reflow(nsPresContext*
   PRBool reflowScrollCorner = PR_TRUE;
   if (!aReflowState.ShouldReflowAllKids()) {
     #define NEEDS_REFLOW(frame_) \
       ((frame_) && NS_SUBTREE_DIRTY(frame_))
 
     reflowContents = NEEDS_REFLOW(mInner.mScrolledFrame);
     reflowHScrollbar = NEEDS_REFLOW(mInner.mHScrollbarBox);
     reflowVScrollbar = NEEDS_REFLOW(mInner.mVScrollbarBox);
-    reflowScrollCorner = NEEDS_REFLOW(mInner.mScrollCornerBox);
+    reflowScrollCorner = NEEDS_REFLOW(mInner.mScrollCornerBox) ||
+                         NEEDS_REFLOW(mInner.mResizerBox);
 
     #undef NEEDS_REFLOW
   }
 
+  if (mInner.mIsRoot) {
+    mInner.mCollapsedResizer = PR_TRUE;
+
+    nsIContent* browserRoot = GetBrowserRoot(mContent);
+    if (browserRoot) {
+      PRBool showResizer = browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer);
+      reflowScrollCorner = showResizer == mInner.mCollapsedResizer;
+      mInner.mCollapsedResizer = !showResizer;
+    }
+  }
+
   nsRect oldScrollAreaBounds = mInner.mScrollPort;
   nsRect oldScrolledAreaBounds =
     mInner.mScrolledFrame->GetScrollableOverflowRectRelativeToParent();
   // Adjust to a multiple of device pixels to restore the invariant that
   // oldScrollPosition is a multiple of device pixels. This could have been
   // thrown out by a zoom change.
   nsIntPoint ptDevPx;
   nsPoint oldScrollPosition = mInner.GetScrollPosition();
@@ -1320,16 +1353,17 @@ static ScrollFrameActivityTracker *gScro
 
 nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter,
                                              PRBool aIsRoot,
                                              PRBool aIsXUL)
   : mHScrollbarBox(nsnull),
     mVScrollbarBox(nsnull),
     mScrolledFrame(nsnull),
     mScrollCornerBox(nsnull),
+    mResizerBox(nsnull),
     mOuter(aOuter),
     mAsyncScroll(nsnull),
     mDestination(0, 0),
     mScrollPosAtLastPaint(0, 0),
     mRestorePos(-1, -1),
     mLastPos(-1, -1),
     mNeverHasVerticalScrollbar(PR_FALSE),
     mNeverHasHorizontalScrollbar(PR_FALSE),
@@ -1341,17 +1375,18 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI
     mIsXUL(aIsXUL),
     mSupppressScrollbarUpdate(PR_FALSE),
     mSkippedScrollbarLayout(PR_FALSE),
     mHadNonInitialReflow(PR_FALSE),
     mHorizontalOverflow(PR_FALSE),
     mVerticalOverflow(PR_FALSE),
     mPostedReflowCallback(PR_FALSE),
     mMayHaveDirtyFixedChildren(PR_FALSE),
-    mUpdateScrollbarAttributes(PR_FALSE)
+    mUpdateScrollbarAttributes(PR_FALSE),
+    mCollapsedResizer(PR_FALSE)
 {
   // lookup if we're allowed to overlap the content from the look&feel object
   PRBool canOverlap;
   nsPresContext* presContext = mOuter->PresContext();
   presContext->LookAndFeel()->
     GetMetric(nsILookAndFeel::eMetric_ScrollbarsCanOverlapContent, canOverlap);
   mScrollbarsCanOverlapContent = canOverlap;
   mScrollingActive = IsAlwaysActive();
@@ -1763,17 +1798,17 @@ nsGfxScrollFrameInner::AppendScrollParts
                                            const nsDisplayListSet&        aLists,
                                            const nsDisplayListCollection& aDest,
                                            PRBool&                        aCreateLayer)
 {
   nsresult rv = NS_OK;
   PRBool hasResizer = HasResizer();
   for (nsIFrame* kid = mOuter->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) {
     if (kid != mScrolledFrame) {
-      if (kid == mScrollCornerBox && hasResizer) {
+      if (kid == mResizerBox && hasResizer) {
         // skip the resizer as this will be drawn later on top of the scrolled content
         continue;
       }
       rv = mOuter->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aDest,
                                             nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
       NS_ENSURE_SUCCESS(rv, rv);
       // DISPLAY_CHILD_FORCE_STACKING_CONTEXT put everything into the
       // PositionedDescendants list.
@@ -1867,24 +1902,24 @@ nsGfxScrollFrameInner::BuildDisplayList(
     AppendScrollPartsTo(aBuilder, aDirtyRect, aLists,
                        scrollParts, createLayersForScrollbars);
   }
 
   // Place the resizer in the display list in our Content() list above
   // scrolled content in the Content() list.
   // This ensures that the resizer appears above the content and the mouse can
   // still target the resizer even when scrollbars are hidden.
-  if (HasResizer() && mScrollCornerBox) {
-    rv = mOuter->BuildDisplayListForChild(aBuilder, mScrollCornerBox, aDirtyRect, scrollParts,
+  if (HasResizer() && mResizerBox) {
+    rv = mOuter->BuildDisplayListForChild(aBuilder, mResizerBox, aDirtyRect, scrollParts,
                                           nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
     NS_ENSURE_SUCCESS(rv, rv);
     // DISPLAY_CHILD_FORCE_STACKING_CONTEXT puts everything into the
     // PositionedDescendants list.
     ::AppendToTop(aBuilder, aLists.Content(),
-                  scrollParts.PositionedDescendants(), mScrollCornerBox,
+                  scrollParts.PositionedDescendants(), mResizerBox,
                   createLayersForScrollbars);
   }
 
   return NS_OK;
 }
 
 static void HandleScrollPref(nsIScrollable *aScrollable, PRInt32 aOrientation,
                              PRUint8& aValue)
@@ -2147,16 +2182,17 @@ nsGfxScrollFrameInner::FireScrollPortEve
 
 void
 nsGfxScrollFrameInner::ReloadChildFrames()
 {
   mScrolledFrame = nsnull;
   mHScrollbarBox = nsnull;
   mVScrollbarBox = nsnull;
   mScrollCornerBox = nsnull;
+  mResizerBox = nsnull;
 
   nsIFrame* frame = mOuter->GetFirstChild(nsnull);
   while (frame) {
     nsIContent* content = frame->GetContent();
     if (content == mOuter->GetContent()) {
       NS_ASSERTION(!mScrolledFrame, "Already found the scrolled frame");
       mScrolledFrame = frame;
     } else {
@@ -2166,16 +2202,19 @@ nsGfxScrollFrameInner::ReloadChildFrames
         // probably a scrollbar then
         if (value.LowerCaseEqualsLiteral("horizontal")) {
           NS_ASSERTION(!mHScrollbarBox, "Found multiple horizontal scrollbars?");
           mHScrollbarBox = frame;
         } else {
           NS_ASSERTION(!mVScrollbarBox, "Found multiple vertical scrollbars?");
           mVScrollbarBox = frame;
         }
+      } else if (content->Tag() == nsGkAtoms::resizer) {
+        NS_ASSERTION(!mResizerBox, "Found multiple resizers");
+        mResizerBox = frame;
       } else {
         // probably a scrollcorner
         NS_ASSERTION(!mScrollCornerBox, "Found multiple scrollcorners");
         mScrollCornerBox = frame;
       }
     }
 
     frame = frame->GetNextSibling();
@@ -2267,17 +2306,17 @@ nsGfxScrollFrameInner::CreateAnonymousCo
   }
 
   if (isResizable) {
     nsCOMPtr<nsINodeInfo> nodeInfo;
     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::resizer, nsnull,
                                             kNameSpaceID_XUL);
     NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
-    NS_TrustedNewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo.forget());
+    NS_TrustedNewXULElement(getter_AddRefs(mResizerContent), nodeInfo.forget());
 
     nsAutoString dir;
     switch (resizeStyle) {
       case NS_STYLE_RESIZE_HORIZONTAL:
         if (IsScrollbarOnRight()) {
           dir.AssignLiteral("right");
         }
         else {
@@ -2288,26 +2327,36 @@ nsGfxScrollFrameInner::CreateAnonymousCo
         dir.AssignLiteral("bottom");
         break;
       case NS_STYLE_RESIZE_BOTH:
         dir.AssignLiteral("bottomend");
         break;
       default:
         NS_WARNING("only resizable types should have resizers");
     }
-    mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, PR_FALSE);
-    mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
-                                  NS_LITERAL_STRING("_parent"), PR_FALSE);
-    mScrollCornerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
+    mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir, PR_FALSE);
+
+    if (mIsRoot) {
+      nsIContent* browserRoot = GetBrowserRoot(mOuter->GetContent());
+      mCollapsedResizer = !(browserRoot &&
+                            browserRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::showresizer));
+    }
+    else {
+      mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::element,
+                                    NS_LITERAL_STRING("_parent"), PR_FALSE);
+    }
+
+    mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
                                   NS_LITERAL_STRING("always"), PR_FALSE);
 
-    if (!aElements.AppendElement(mScrollCornerContent))
+    if (!aElements.AppendElement(mResizerContent))
       return NS_ERROR_OUT_OF_MEMORY;
   }
-  else if (canHaveHorizontal && canHaveVertical) {
+
+  if (canHaveHorizontal && canHaveVertical) {
     nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nsnull,
                                             kNameSpaceID_XUL);
     NS_TrustedNewXULElement(getter_AddRefs(mScrollCornerContent), nodeInfo.forget());
     if (!aElements.AppendElement(mScrollCornerContent))
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
@@ -2315,25 +2364,27 @@ nsGfxScrollFrameInner::CreateAnonymousCo
 
 void
 nsGfxScrollFrameInner::AppendAnonymousContentTo(nsBaseContentList& aElements,
                                                 PRUint32 aFilter)
 {
   aElements.MaybeAppendElement(mHScrollbarContent);
   aElements.MaybeAppendElement(mVScrollbarContent);
   aElements.MaybeAppendElement(mScrollCornerContent);
+  aElements.MaybeAppendElement(mResizerContent);
 }
 
 void
 nsGfxScrollFrameInner::Destroy()
 {
   // Unbind any content created in CreateAnonymousContent from the tree
   nsContentUtils::DestroyAnonymousContent(&mHScrollbarContent);
   nsContentUtils::DestroyAnonymousContent(&mVScrollbarContent);
   nsContentUtils::DestroyAnonymousContent(&mScrollCornerContent);
+  nsContentUtils::DestroyAnonymousContent(&mResizerContent);
 
   if (mPostedReflowCallback) {
     mOuter->PresContext()->PresShell()->CancelReflowCallback(this);
     mPostedReflowCallback = PR_FALSE;
   }
 }
 
 /**
@@ -3063,18 +3114,18 @@ nsGfxScrollFrameInner::AdjustScrollbarRe
                          nsRect& aRect, PRBool aHasResizer, PRBool aVertical)
 {
   if ((aVertical ? aRect.width : aRect.height) == 0)
     return;
 
   // if a content resizer is present, use its size. Otherwise, check if the
   // widget has a resizer.
   nsRect resizerRect;
-  if (aHasResizer && mScrollCornerBox) {
-    resizerRect = mScrollCornerBox->GetRect();
+  if (aHasResizer) {
+    resizerRect = mResizerBox->GetRect();
   }
   else {
     nsPoint offset;
     nsIWidget* widget = aFrame->GetNearestWidget(offset);
     nsIntRect widgetRect;
     if (!widget || !widget->ShowsResizeIndicator(&widgetRect))
       return;
 
@@ -3100,54 +3151,70 @@ nsGfxScrollFrameInner::LayoutScrollbars(
 {
   NS_ASSERTION(!mSupppressScrollbarUpdate,
                "This should have been suppressed");
 
   PRBool hasResizer = HasResizer();
   PRBool scrollbarOnLeft = !IsScrollbarOnRight();
 
   // place the scrollcorner
-  if (mScrollCornerBox) {
-    NS_PRECONDITION(mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
-
-    // if a resizer is present, get its size
-    nsSize resizerSize;
-    if (HasResizer()) {
-      // just assume a default size of 15 pixels
-      nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15);
-      resizerSize.width =
-        mVScrollbarBox ? mVScrollbarBox->GetMinSize(aState).width : defaultSize;
-      resizerSize.height =
-        mHScrollbarBox ? mHScrollbarBox->GetMinSize(aState).height : defaultSize;
-    }
-    else {
-      resizerSize = nsSize(0, 0);
-    }
+  if (mScrollCornerBox || mResizerBox) {
+    NS_PRECONDITION(!mScrollCornerBox || mScrollCornerBox->IsBoxFrame(), "Must be a box frame!");
 
     nsRect r(0, 0, 0, 0);
     if (aContentArea.x != mScrollPort.x || scrollbarOnLeft) {
       // scrollbar (if any) on left
       r.x = aContentArea.x;
-      r.width = PR_MAX(resizerSize.width, mScrollPort.x - aContentArea.x);
+      r.width = mScrollPort.x - aContentArea.x;
       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
     } else {
       // scrollbar (if any) on right
-      r.width = PR_MAX(resizerSize.width, aContentArea.XMost() - mScrollPort.XMost());
+      r.width = aContentArea.XMost() - mScrollPort.XMost();
       r.x = aContentArea.XMost() - r.width;
       NS_ASSERTION(r.width >= 0, "Scroll area should be inside client rect");
     }
     if (aContentArea.y != mScrollPort.y) {
       NS_ERROR("top scrollbars not supported");
     } else {
       // scrollbar (if any) on bottom
-      r.height = PR_MAX(resizerSize.height, aContentArea.YMost() - mScrollPort.YMost());
+      r.height = aContentArea.YMost() - mScrollPort.YMost();
       r.y = aContentArea.YMost() - r.height;
       NS_ASSERTION(r.height >= 0, "Scroll area should be inside client rect");
     }
-    LayoutAndInvalidate(aState, mScrollCornerBox, r, PR_FALSE);
+
+    if (mScrollCornerBox) {
+      LayoutAndInvalidate(aState, mScrollCornerBox, r, PR_FALSE);
+    }
+
+    if (hasResizer) {
+      // if a resizer is present, get its size. Assume a default size of 15 pixels.
+      nsSize resizerSize;
+      nscoord defaultSize = nsPresContext::CSSPixelsToAppUnits(15);
+      resizerSize.width =
+        mVScrollbarBox ? mVScrollbarBox->GetMinSize(aState).width : defaultSize;
+      if (resizerSize.width > r.width) {
+        r.width = resizerSize.width;
+        if (aContentArea.x == mScrollPort.x && !scrollbarOnLeft)
+          r.x = aContentArea.XMost() - r.width;
+      }
+
+      resizerSize.height =
+        mHScrollbarBox ? mHScrollbarBox->GetMinSize(aState).height : defaultSize;
+      if (resizerSize.height > r.height) {
+        r.height = resizerSize.height;
+        if (aContentArea.y == mScrollPort.y)
+          r.y = aContentArea.YMost() - r.height;
+      }
+
+      LayoutAndInvalidate(aState, mResizerBox, r, PR_FALSE);
+    }
+    else if (mResizerBox) {
+      // otherwise lay out the resizer with an empty rectangle
+      LayoutAndInvalidate(aState, mResizerBox, nsRect(), PR_FALSE);
+    }
   }
 
   nsPresContext* presContext = mScrolledFrame->PresContext();
   if (mVScrollbarBox) {
     NS_PRECONDITION(mVScrollbarBox->IsBoxFrame(), "Must be a box frame!");
     nsRect vRect(mScrollPort);
     vRect.width = aContentArea.width - mScrollPort.width;
     vRect.x = scrollbarOnLeft ? aContentArea.x : mScrollPort.XMost();
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -225,40 +225,40 @@ public:
   PRBool IsScrollbarOnRight() const;
   PRBool IsScrollingActive() const { return mScrollingActive; }
   // adjust the scrollbar rectangle aRect to account for any visible resizer.
   // aHasResizer specifies if there is a content resizer, however this method
   // will also check if a widget resizer is present as well.
   void AdjustScrollbarRectForResizer(nsIFrame* aFrame, nsPresContext* aPresContext,
                                      nsRect& aRect, PRBool aHasResizer, PRBool aVertical);
   // returns true if a resizer should be visible
-  PRBool HasResizer() {
-      return mScrollCornerContent && mScrollCornerContent->Tag() == nsGkAtoms::resizer;
-  }
+  PRBool HasResizer() { return mResizerBox && !mCollapsedResizer; }
   void LayoutScrollbars(nsBoxLayoutState& aState,
                         const nsRect& aContentArea,
                         const nsRect& aOldScrollArea);
 
   PRBool IsAlwaysActive() const;
   void MarkActive();
   void MarkInactive();
   nsExpirationState* GetExpirationState() { return &mActivityExpirationState; }
 
   // owning references to the nsIAnonymousContentCreator-built content
   nsCOMPtr<nsIContent> mHScrollbarContent;
   nsCOMPtr<nsIContent> mVScrollbarContent;
   nsCOMPtr<nsIContent> mScrollCornerContent;
+  nsCOMPtr<nsIContent> mResizerContent;
 
   nsRevocableEventPtr<ScrollEvent> mScrollEvent;
   nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent;
   nsRevocableEventPtr<ScrolledAreaEvent> mScrolledAreaEvent;
   nsIBox* mHScrollbarBox;
   nsIBox* mVScrollbarBox;
   nsIFrame* mScrolledFrame;
   nsIBox* mScrollCornerBox;
+  nsIBox* mResizerBox;
   nsContainerFrame* mOuter;
   AsyncScroll* mAsyncScroll;
   nsTArray<nsIScrollPositionListener*> mListeners;
   nsRect mScrollPort;
   // Where we're currently scrolling to, if we're scrolling asynchronously.
   // If we're not in the middle of an asynchronous scroll then this is
   // just the current scroll position. ScrollBy will choose its
   // destination based on this value.
@@ -301,16 +301,18 @@ public:
   // reflow callback.
   PRPackedBool mUpdateScrollbarAttributes:1;
   // If true, we should be prepared to scroll using this scrollframe
   // by placing descendant content into its own layer(s)
   PRPackedBool mScrollingActive:1;
   // If true, scrollbars are stacked on the top of the display list and can
   // float above the content as a result
   PRPackedBool mScrollbarsCanOverlapContent:1;
+  // If true, the resizer is collapsed and not displayed
+  PRPackedBool mCollapsedResizer:1;
 };
 
 /**
  * The scroll frame creates and manages the scrolling view
  *
  * It only supports having a single child frame that typically is an area
  * frame, but doesn't have to be. The child frame must have a view, though
  *
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -736,16 +736,23 @@ nsSubDocumentFrame::AttributeChanged(PRI
         // concrete class, yay!
         nsHTMLFramesetFrame* framesetFrame = do_QueryFrame(parentFrame);
         if (framesetFrame) {
           framesetFrame->RecalculateBorderResize();
         }
       }
     }
   }
+  else if (aAttribute == nsGkAtoms::showresizer) {
+    nsIFrame* rootFrame = GetSubdocumentRootFrame();
+    if (rootFrame) {
+      rootFrame->PresContext()->PresShell()->
+        FrameNeedsReflow(rootFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
+    }
+  }
   else if (aAttribute == nsGkAtoms::type) {
     if (!mFrameLoader) 
       return NS_OK;
 
     if (!mContent->IsXUL()) {
       return NS_OK;
     }
 
--- a/layout/style/jar.mn
+++ b/layout/style/jar.mn
@@ -1,8 +1,8 @@
 toolkit.jar:
-   res/ua.css    (ua.css)
+*  res/ua.css    (ua.css)
    res/html.css    (html.css)
    res/quirk.css    (quirk.css)
    res/viewsource.css    (viewsource.css)
 *  res/forms.css    (forms.css)
    res/arrow.gif    (arrow.gif)
    res/arrowd.gif   (arrowd.gif)
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -162,16 +162,19 @@
 
 *|*::-moz-viewport, *|*::-moz-viewport-scroll, *|*::-moz-canvas, *|*::-moz-scrolled-canvas {
   display: block !important;
   background-color: inherit;
 }
 
 *|*::-moz-viewport-scroll {
   overflow: auto;
+%ifdef XP_WIN
+  resize: both;
+%endif
 }
 
 *|*::-moz-column-content { 
   /* the column boxes inside a column-flowed block */
   /* make unicode-bidi inherit, otherwise it has no effect on column boxes */
   unicode-bidi: inherit;
   /* inherit the outer frame's display, otherwise we turn into an inline */
   display: inherit !important;
--- a/layout/xul/base/src/nsResizerFrame.cpp
+++ b/layout/xul/base/src/nsResizerFrame.cpp
@@ -356,18 +356,24 @@ nsResizerFrame::GetContentToResize(nsIPr
     nsCOMPtr<nsISupports> cont = aPresShell->GetPresContext()->GetContainer();
     nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(cont);
     if (dsti) {
       PRInt32 type = -1;
       isChromeShell = (NS_SUCCEEDED(dsti->GetItemType(&type)) &&
                        type == nsIDocShellTreeItem::typeChrome);
     }
 
-    if (!isChromeShell)
-      return nsnull;
+    if (!isChromeShell) {
+      // don't allow resizers in content shells, except for the viewport
+      // scrollbar which doesn't have a parent
+      nsIContent* nonNativeAnon = mContent->FindFirstNonNativeAnonymous();
+      if (nonNativeAnon && !nonNativeAnon->GetParent()) {
+        return nsnull;
+      }
+    }
 
     // get the document and the window - should this be cached?
     nsPIDOMWindow *domWindow = aPresShell->GetDocument()->GetWindow();
     if (domWindow) {
       nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
         do_QueryInterface(domWindow->GetDocShell());
       if (docShellAsItem) {
         nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -357,16 +357,22 @@
       <property name="contentTitle"
                 onget="return this.contentDocument.title;"
                 readonly="true"/>
 
       <property name="contentPrincipal"
                 onget="return this.contentDocument.nodePrincipal;"
                 readonly="true"/>
 
+      <property name="showWindowResizer"
+                onset="if (val) this.setAttribute('showresizer', 'true');
+                       else this.removeAttribute('showresizer');
+                       return val;"
+                onget="return this.getAttribute('showresizer') == 'true';"/>
+
       <field name="mPrefs" readonly="true">
         Components.classes['@mozilla.org/preferences-service;1']
                   .getService(Components.interfaces.nsIPrefService)
                   .getBranch(null);
       </field>
 
       <field name="mAtomService" readonly="true">
         Components.classes['@mozilla.org/atom-service;1']
--- a/toolkit/content/widgets/resizer.xml
+++ b/toolkit/content/widgets/resizer.xml
@@ -5,16 +5,21 @@
 
   <binding id="resizer">
     <resources>
       <stylesheet src="chrome://global/skin/resizer.css"/>
     </resources>
     <implementation>
       <constructor>
       <![CDATA[
+        // don't do this for viewport resizers; causes a crash related to
+        // bugs 563665 and 581536 otherwise
+        if (this.parentNode == this.ownerDocument.documentElement)
+          return;
+
         // if the direction is rtl, set the rtl attribute so that the
         // stylesheet can use this to make the cursor appear properly
         var direction = window.getComputedStyle(this, "").direction;
         if (direction == "rtl") {
           this.setAttribute("rtl", "true");
         }
       ]]>
       </constructor>