Bug 511180, resizer should be able to resizer any element, and work inside popups, r=neil,sr=roc
authorNeil Deakin <neil@mozilla.com>
Wed, 23 Dec 2009 13:45:44 -0500
changeset 36614 4c8a5f1f1fef675eb5b89ddf8113b54c30d2569c
parent 36613 b0cc8c093877cab0ebb367671f9638e0027ee8d3
child 36615 3a81a1e824bb23f11daaaddf106ccdff2d952bf8
push idunknown
push userunknown
push dateunknown
reviewersneil, roc
bugs511180
milestone1.9.3a1pre
Bug 511180, resizer should be able to resizer any element, and work inside popups, r=neil,sr=roc
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.h
layout/xul/base/src/nsResizerFrame.cpp
layout/xul/base/src/nsResizerFrame.h
layout/xul/base/src/nsXULPopupManager.cpp
layout/xul/base/test/Makefile.in
layout/xul/base/test/test_resizer.xul
layout/xul/base/test/window_resizer.xul
layout/xul/base/test/window_resizer_element.xul
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -115,17 +115,19 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPr
   mPopupAnchor(POPUPALIGNMENT_NONE),
   mIsOpenChanged(PR_FALSE),
   mIsContextMenu(PR_FALSE),
   mAdjustOffsetForContextMenu(PR_FALSE),
   mGeneratedChildren(PR_FALSE),
   mMenuCanOverlapOSBar(PR_FALSE),
   mShouldAutoPosition(PR_TRUE),
   mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
-  mInContentShell(PR_TRUE)
+  mInContentShell(PR_TRUE),
+  mHFlip(PR_FALSE),
+  mVFlip(PR_FALSE)
 {
   if (sDefaultLevelParent >= 0)
     return;
   sDefaultLevelParent =
     nsContentUtils::GetBoolPref("ui.panel.default_level_parent", PR_FALSE);
 } // ctor
 
 
@@ -669,17 +671,18 @@ nsMenuPopupFrame::HidePopup(PRBool aDese
 
   if (IsMenu())
     SetCurrentMenuItem(nsnull);
 
   mIncrementalString.Truncate();
 
   mIsOpenChanged = PR_FALSE;
   mCurrentMenu = nsnull; // make sure no current menu is set
- 
+  mHFlip = mVFlip = PR_FALSE;
+
   nsIView* view = GetView();
   nsIViewManager* viewManager = view->GetViewManager();
   viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
   viewManager->ResizeView(view, nsRect(0, 0, 0, 0));
 
   FireDOMEvent(NS_LITERAL_STRING("DOMMenuInactive"), mContent);
 
   // XXX, bug 137033, In Windows, if mouse is outside the window when the menupopup closes, no
@@ -812,34 +815,35 @@ nsMenuPopupFrame::AdjustPositionForAncho
   return pnt;
 }
 
 nscoord
 nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize, 
                                nscoord aScreenBegin, nscoord aScreenEnd,
                                nscoord aAnchorBegin, nscoord aAnchorEnd,
                                nscoord aMarginBegin, nscoord aMarginEnd,
-                               nscoord aOffsetForContextMenu, PRBool aFlip)
+                               nscoord aOffsetForContextMenu, PRBool aFlip,
+                               PRPackedBool* aFlipSide)
 {
   // all of the coordinates used here are in app units relative to the screen
-
   nscoord popupSize = aSize;
   if (aScreenPoint < aScreenBegin) {
     // at its current position, the popup would extend past the left or top
     // edge of the screen, so it will have to be moved or resized.
     if (aFlip) {
       // check whether there is more room to the left and right (or top and
       // bottom) of the anchor and put the popup on the side with more room.
       if (aAnchorBegin - aScreenBegin >= aScreenEnd - aAnchorEnd) {
         aScreenPoint = aScreenBegin;
         popupSize = aAnchorBegin - aScreenPoint - aMarginEnd;
       }
       else {
         // flip such that the popup is to the right or bottom of the anchor
         // point instead. However, when flipping use the same margin size.
+        *aFlipSide = PR_TRUE;
         aScreenPoint = aAnchorEnd + aMarginEnd;
         // check if the new position is still off the right or bottom edge of
         // the screen. If so, resize the popup.
         if (aScreenPoint + aSize > aScreenEnd) {
           popupSize = aScreenEnd - aScreenPoint;
         }
       }
     }
@@ -859,16 +863,17 @@ nsMenuPopupFrame::FlipOrResize(nscoord& 
         }
         else {
           popupSize = aScreenEnd - aScreenPoint;
         }
       }
       else {
         // flip such that the popup is to the left or top of the anchor point
         // instead.
+        *aFlipSide = PR_TRUE;
         aScreenPoint = aAnchorBegin - aSize - aMarginBegin - aOffsetForContextMenu;
         // check if the new position is still off the left or top edge of the
         // screen. If so, resize the popup.
         if (aScreenPoint < aScreenBegin) {
           aScreenPoint = aScreenBegin;
           if (!mIsContextMenu) {
             popupSize = aAnchorBegin - aScreenPoint - aMarginBegin;
           }
@@ -879,19 +884,19 @@ nsMenuPopupFrame::FlipOrResize(nscoord& 
       aScreenPoint = aScreenEnd - aSize;
     }
   }
 
   return popupSize;
 }
 
 nsresult
-nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, PRBool aIsMove)
+nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
 {
-  if (!mShouldAutoPosition && !aIsMove && !mInContentShell)
+  if (!mShouldAutoPosition)
     return NS_OK;
 
   nsPresContext* presContext = PresContext();
   nsIFrame* rootFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
   NS_ASSERTION(rootFrame->GetView() && GetView() &&
                rootFrame->GetView() == GetView()->GetParent(),
                "rootFrame's view is not our view's parent???");
 
@@ -1019,55 +1024,17 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
     // add the margins on the popup
     screenPoint.MoveBy(margin.left + offsetForContextMenu,
                        margin.top + offsetForContextMenu);
 
     // screen positioned popups can be flipped vertically but never horizontally
     vFlip = PR_TRUE;
   }
 
-  // if a panel is being moved, don't flip it. But always do this for content
-  // shells, so that the popup doesn't extend outside the containing frame.
-  if (aIsMove && mPopupType == ePopupTypePanel && !mInContentShell)
-    hFlip = vFlip = PR_FALSE;
-
-  // screenRect will hold the rectangle of the available screen space. It
-  // will be reduced by the OS chrome such as menubars. It addition, for
-  // content shells, it will be the area of the content rather than the
-  // screen.
-  nsIntRect screenRectPixels;
-  nsCOMPtr<nsIScreen> screen;
-  nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
-  if (sm) {
-    // for context shells, get the screen where the root frame is located.
-    // This is because we need to constrain the content to this content area,
-    // so we should use the same screen. Otherwise, use the screen where the
-    // anchor is located.
-    nsPoint pnt = mInContentShell ? rootScreenRect.TopLeft() : anchorRect.TopLeft();
-    sm->ScreenForRect(presContext->AppUnitsToDevPixels(pnt.x),
-                      presContext->AppUnitsToDevPixels(pnt.y),
-                      1, 1, getter_AddRefs(screen));
-    if (screen) {
-      if (mMenuCanOverlapOSBar)
-        screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
-                        &screenRectPixels.width, &screenRectPixels.height);
-      else
-        screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
-                             &screenRectPixels.width, &screenRectPixels.height);
-    }
-  }
-  nsRect screenRect = screenRectPixels.ToAppUnits(presContext->AppUnitsPerDevPixel());
-
-  // keep a 3 pixel margin to the right and bottom of the screen for the WinXP dropshadow
-  screenRect.SizeBy(-nsPresContext::CSSPixelsToAppUnits(3),
-                    -nsPresContext::CSSPixelsToAppUnits(3));
-
-  // for content shells, clip to the client area rather than the screen area
-  if (mInContentShell)
-    screenRect.IntersectRect(screenRect, rootScreenRect);
+  nsRect screenRect = GetConstraintRect(anchorRect.TopLeft(), rootScreenRect);
 
   // ensure that anchorRect is on screen
   if (!anchorRect.IntersectRect(anchorRect, screenRect)) {
     // if the anchor isn't within the screen, move it to the edge of the screen.
     // IntersectRect will have set both the width and height of anchorRect to 0.
     if (anchorRect.x < screenRect.x)
       anchorRect.x = screenRect.x;
     if (anchorRect.XMost() > screenRect.XMost())
@@ -1086,20 +1053,21 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
 
   // at this point the anchor (anchorRect) is within the available screen
   // area (screenRect) and the popup is known to be no larger than the screen.
   // Next, check if there is enough space to show the popup at full size when
   // positioned at screenPoint. If not, flip the popups to the opposite side
   // of their anchor point, or resize them as necessary.
   mRect.width = FlipOrResize(screenPoint.x, mRect.width, screenRect.x,
                              screenRect.XMost(), anchorRect.x, anchorRect.XMost(),
-                             margin.left, margin.right, offsetForContextMenu, hFlip);
+                             margin.left, margin.right, offsetForContextMenu, hFlip, &mHFlip);
+
   mRect.height = FlipOrResize(screenPoint.y, mRect.height, screenRect.y,
                               screenRect.YMost(), anchorRect.y, anchorRect.YMost(),
-                              margin.top, margin.bottom, offsetForContextMenu, vFlip);
+                              margin.top, margin.bottom, offsetForContextMenu, vFlip, &mVFlip);
 
   NS_ASSERTION(screenPoint.x >= screenRect.x && screenPoint.y >= screenRect.y &&
                screenPoint.x + mRect.width <= screenRect.XMost() &&
                screenPoint.y + mRect.height <= screenRect.YMost(),
                "Popup is offscreen");
 
   // determine the x and y position of the view by subtracting the desired
   // screen position from the screen position of the root frame.
@@ -1120,16 +1088,89 @@ nsMenuPopupFrame::SetPopupPosition(nsIFr
 }
 
 /* virtual */ nsMenuFrame*
 nsMenuPopupFrame::GetCurrentMenuItem()
 {
   return mCurrentMenu;
 }
 
+nsRect
+nsMenuPopupFrame::GetConstraintRect(nsPoint aAnchorPoint, nsRect& aRootScreenRect)
+{
+  nsIntRect screenRectPixels;
+  nsPresContext* presContext = PresContext();
+
+  // determine the available screen space. It will be reduced by the OS chrome
+  // such as menubars. It addition, for content shells, it will be the area of
+  // the content rather than the screen.
+  nsCOMPtr<nsIScreen> screen;
+  nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
+  if (sm) {
+    // for content shells, get the screen where the root frame is located.
+    // This is because we need to constrain the content to this content area,
+    // so we should use the same screen. Otherwise, use the screen where the
+    // anchor is located.
+    nsPoint pnt = mInContentShell ? aRootScreenRect.TopLeft() : aAnchorPoint;
+    sm->ScreenForRect(presContext->AppUnitsToDevPixels(pnt.x),
+                      presContext->AppUnitsToDevPixels(pnt.y),
+                      1, 1, getter_AddRefs(screen));
+    if (screen) {
+      // get the total screen area if the popup is allowed to overlap it.
+      if (mMenuCanOverlapOSBar && !mInContentShell)
+        screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
+                        &screenRectPixels.width, &screenRectPixels.height);
+      else
+        screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
+                             &screenRectPixels.width, &screenRectPixels.height);
+    }
+  }
+
+  // keep a 3 pixel margin to the right and bottom of the screen for the WinXP dropshadow
+  screenRectPixels.SizeBy(-3, -3);
+
+  nsRect screenRect = screenRectPixels.ToAppUnits(presContext->AppUnitsPerDevPixel());
+  if (mInContentShell) {
+    // for content shells, clip to the client area rather than the screen area
+    screenRect.IntersectRect(screenRect, aRootScreenRect);
+  }
+
+  return screenRect;
+}
+
+void nsMenuPopupFrame::CanAdjustEdges(PRInt8 aHorizontalSide, PRInt8 aVerticalSide, nsIntPoint& aChange)
+{
+  PRInt8 popupAlign(mPopupAlignment);
+  if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
+    popupAlign = -popupAlign;
+  }
+
+  if (aHorizontalSide == (mHFlip ? NS_SIDE_RIGHT : NS_SIDE_LEFT)) {
+    if (popupAlign == POPUPALIGNMENT_TOPLEFT || popupAlign == POPUPALIGNMENT_BOTTOMLEFT) {
+      aChange.x = 0;
+    }
+  }
+  else if (aHorizontalSide == (mHFlip ? NS_SIDE_LEFT : NS_SIDE_RIGHT)) {
+    if (popupAlign == POPUPALIGNMENT_TOPRIGHT || popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
+      aChange.x = 0;
+    }
+  }
+
+  if (aVerticalSide == (mVFlip ? NS_SIDE_BOTTOM : NS_SIDE_TOP)) {
+    if (popupAlign == POPUPALIGNMENT_TOPLEFT || popupAlign == POPUPALIGNMENT_TOPRIGHT) {
+      aChange.y = 0;
+    }
+  }
+  else if (aVerticalSide == (mVFlip ? NS_SIDE_TOP : NS_SIDE_BOTTOM)) {
+    if (popupAlign == POPUPALIGNMENT_BOTTOMLEFT || popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
+      aChange.y = 0;
+    }
+  }
+}
+
 PRBool nsMenuPopupFrame::ConsumeOutsideClicks()
 {
   // If the popup has explicitly set a consume mode, honor that.
   if (mConsumeRollupEvent != nsIPopupBoxObject::ROLLUP_DEFAULT)
     return (mConsumeRollupEvent == nsIPopupBoxObject::ROLLUP_CONSUME);
 
   nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
   if (parentContent) {
@@ -1539,27 +1580,23 @@ nsMenuPopupFrame::Destroy()
 
   nsBoxFrame::Destroy();
 }
 
 
 void
 nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop, PRBool aUpdateAttrs)
 {
-  // just don't support moving popups for content shells
-  if (mInContentShell)
-    return;
-
   // reposition the popup at the specified coordinates. Don't clear the anchor
   // and position, because the popup can be reset to its anchor position by
   // using (-1, -1) as coordinates.
   mScreenXPos = aLeft;
   mScreenYPos = aTop;
 
-  SetPopupPosition(nsnull, PR_TRUE);
+  SetPopupPosition(nsnull);
 
   nsCOMPtr<nsIContent> popup = mContent;
   if (aUpdateAttrs && (popup->HasAttr(kNameSpaceID_None, nsGkAtoms::left) ||
                        popup->HasAttr(kNameSpaceID_None, nsGkAtoms::top)))
   {
     nsAutoString left, top;
     left.AppendInt(aLeft);
     top.AppendInt(aTop);
--- a/layout/xul/base/src/nsMenuPopupFrame.h
+++ b/layout/xul/base/src/nsMenuPopupFrame.h
@@ -199,19 +199,18 @@ public:
   // laid out, so that the view can be shown.
   void AdjustView();
 
   nsIView* GetRootViewForPopup(nsIFrame* aStartFrame);
 
   // set the position of the popup either relative to the anchor aAnchorFrame
   // (or the frame for mAnchorContent if aAnchorFrame is null) or at a specific
   // point if a screen position (mScreenXPos and mScreenYPos) are set. The popup
-  // will be adjusted so that it is on screen. If aIsMove is true, then the popup
-  // is being moved.
-  nsresult SetPopupPosition(nsIFrame* aAnchorFrame, PRBool aIsMove = PR_FALSE);
+  // will be adjusted so that it is on screen.
+  nsresult SetPopupPosition(nsIFrame* aAnchorFrame);
 
   PRBool HasGeneratedChildren() { return mGeneratedChildren; }
   void SetGeneratedChildren() { mGeneratedChildren = PR_TRUE; }
 
   // called when the Enter key is pressed while the popup is open. This will
   // just pass the call down to the current menu, if any. If a current menu
   // should be opened as a result, this method should return the frame for
   // that menu, or null if no menu should be opened. Also, calling Enter will
@@ -290,16 +289,35 @@ public:
   // same as SetBounds except the preferred size mPrefSize is also set.
   void SetPreferredBounds(nsBoxLayoutState& aState, const nsRect& aRect);
 
   // retrieve the last preferred size
   nsSize PreferredSize() { return mPrefSize; }
   // set the last preferred size
   void SetPreferredSize(nsSize aSize) { mPrefSize = aSize; }
 
+  // For a popup that should appear at the given anchor point, determine
+  // the screen area that it is constrained by. This will be the available
+  // area of the screen the popup should be displayed on. Content popups,
+  // however, will also be constrained by the content area, given by
+  // aRootScreenRect. All coordinates are in app units.
+  nsRect GetConstraintRect(nsPoint aAnchorPoint, nsRect& aRootScreenRect);
+
+  // Determines whether the given edges of the popup may be moved, where
+  // aHorizontalSide and aVerticalSide are one of the NS_SIDE_* constants, or
+  // 0 for no movement in that direction. aChange is the distance to move on
+  // those sides. If will be reset to 0 if the side cannot be adjusted at all
+  // in that direction. For example, a popup cannot be moved if it is anchored
+  // on a particular side.
+  //
+  // Later, when bug 357725 is implemented, we can make this adjust aChange by
+  // the amount that the side can be resized, so that minimums and maximums
+  // can be taken into account.
+  void CanAdjustEdges(PRInt8 aHorizontalSide, PRInt8 aVerticalSide, nsIntPoint& aChange);
+
 protected:
 
   // redefine to tell the box system not to move the views.
   virtual void GetLayoutFlags(PRUint32& aFlags);
 
   void InitPositionFromAnchorAlign(const nsAString& aAnchor,
                                    const nsAString& aAlign);
 
@@ -318,21 +336,23 @@ protected:
   //   aScreenBegin - the left or top edge of the screen
   //   aScreenEnd - the right or bottom edge of the screen
   //   aAnchorBegin - the left or top edge of the anchor rectangle
   //   aAnchorEnd - the right or bottom edge of the anchor rectangle
   //   aMarginBegin - the left or top margin of the popup
   //   aMarginEnd - the right or bottom margin of the popup
   //   aOffsetForContextMenu - the additional offset to add for context menus
   //   aFlip - whether to flip or resize the popup when there isn't space
+  //   aFlipSide - pointer to where current flip mode is stored
   nscoord FlipOrResize(nscoord& aScreenPoint, nscoord aSize, 
                        nscoord aScreenBegin, nscoord aScreenEnd,
                        nscoord aAnchorBegin, nscoord aAnchorEnd,
                        nscoord aMarginBegin, nscoord aMarginEnd,
-                       nscoord aOffsetForContextMenu, PRBool aFlip);
+                       nscoord aOffsetForContextMenu, PRBool aFlip,
+                       PRPackedBool* aFlipSide);
 
   // Move the popup to the position specified in its |left| and |top| attributes.
   void MoveToAttributePosition();
 
   nsString     mIncrementalString;  // for incremental typing navigation
 
   // the content that the popup is anchored to, if any, which may be in a
   // different document than the popup.
@@ -369,12 +389,16 @@ protected:
   PRPackedBool mAdjustOffsetForContextMenu;
   PRPackedBool mGeneratedChildren; // true if the contents have been created
 
   PRPackedBool mMenuCanOverlapOSBar;    // can we appear over the taskbar/menubar?
   PRPackedBool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup?
   PRPackedBool mConsumeRollupEvent; // Should the rollup event be consumed?
   PRPackedBool mInContentShell; // True if the popup is in a content shell
 
+  // the flip modes that were used when the popup was opened
+  PRPackedBool mHFlip;
+  PRPackedBool mVFlip;
+
   static PRInt8 sDefaultLevelParent;
 }; // class nsMenuPopupFrame
 
 #endif
--- a/layout/xul/base/src/nsResizerFrame.cpp
+++ b/layout/xul/base/src/nsResizerFrame.cpp
@@ -32,33 +32,39 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
 #include "nsResizerFrame.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMNodeList.h"
 #include "nsGkAtoms.h"
 #include "nsINameSpaceManager.h"
+#include "nsIDOMElementCSSInlineStyle.h"
+#include "nsIDOMCSSStyleDeclaration.h"
 
 #include "nsPresContext.h"
+#include "nsFrameManager.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIBaseWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsContentUtils.h"
+#include "nsMenuPopupFrame.h"
+#include "nsIScreenManager.h"
 
 //
 // NS_NewResizerFrame
 //
 // Creates a new Resizer frame and returns it
 //
 nsIFrame*
 NS_NewResizerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@@ -81,146 +87,303 @@ nsResizerFrame::HandleEvent(nsPresContex
   NS_ENSURE_ARG_POINTER(aEventStatus);
   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
     return NS_OK;
   }
 
   nsWeakFrame weakFrame(this);
   PRBool doDefault = PR_TRUE;
 
-  // what direction should we go in? 
-  Direction direction = GetDirection();
-
   switch (aEvent->message) {
-
-   case NS_MOUSE_BUTTON_DOWN: {
-       if (aEvent->eventStructType == NS_MOUSE_EVENT &&
-           static_cast<nsMouseEvent*>(aEvent)->button ==
-             nsMouseEvent::eLeftButton)
-       {
+    case NS_MOUSE_BUTTON_DOWN: {
+      if (aEvent->eventStructType == NS_MOUSE_EVENT &&
+        static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton)
+      {
+        nsCOMPtr<nsIBaseWindow> window;
+        nsIPresShell* presShell = aPresContext->GetPresShell();
+        nsIContent* contentToResize =
+          GetContentToResize(presShell, getter_AddRefs(window));
+        if (contentToResize) {
+          nsIFrame* frameToResize = presShell->GetPrimaryFrameFor(contentToResize);
+          if (!frameToResize)
+            break;
 
-         nsresult rv = NS_OK;
-
-         // ask the widget implementation to begin a resize drag if it can
-         rv = aEvent->widget->BeginResizeDrag(aEvent, 
-             direction.mHorizontal, direction.mVertical);
+          mMouseDownRect = frameToResize->GetScreenRect();
+        }
+        else {
+          // ask the widget implementation to begin a resize drag if it can
+          Direction direction = GetDirection();
+          nsresult rv = aEvent->widget->BeginResizeDrag(aEvent,
+                        direction.mHorizontal, direction.mVertical);
+          if (rv == NS_ERROR_NOT_IMPLEMENTED && window) {
+            // if there's no native resize support, we need to do window
+            // resizing ourselves
+            window->GetPositionAndSize(&mMouseDownRect.x, &mMouseDownRect.y,
+                                       &mMouseDownRect.width, &mMouseDownRect.height);
+          }
+          else {
+            // for native drags, don't set the fields below
+            doDefault = PR_FALSE;
+            break;
+          }
+        }
 
-         if (rv == NS_ERROR_NOT_IMPLEMENTED) {
-           // there's no native resize support, 
-           // we need to window resizing ourselves
+        // we're tracking
+        mTrackingMouseMove = PR_TRUE;
 
-           // we're tracking.
-           mTrackingMouseMove = PR_TRUE;
+        // remember current mouse coordinates
+        mMouseDownPoint = aEvent->refPoint + aEvent->widget->WidgetToScreenOffset();
+
+        nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
 
-           // start capture.
-           nsIPresShell::SetCapturingContent(GetContent(), CAPTURE_IGNOREALLOWED);
+        doDefault = PR_FALSE;
+      }
+    }
+    break;
+
+  case NS_MOUSE_BUTTON_UP: {
 
-           // remember current mouse coordinates.
-           mLastPoint = aEvent->refPoint;
-           aEvent->widget->GetScreenBounds(mWidgetRect);
-         }
+    if (mTrackingMouseMove && aEvent->eventStructType == NS_MOUSE_EVENT &&
+        static_cast<nsMouseEvent*>(aEvent)->button == nsMouseEvent::eLeftButton)
+    {
+      // we're done tracking.
+      mTrackingMouseMove = PR_FALSE;
+
+      nsIPresShell::SetCapturingContent(nsnull, 0);
+
+      doDefault = PR_FALSE;
+    }
+  }
+  break;
 
-         *aEventStatus = nsEventStatus_eConsumeNoDefault;
-         doDefault = PR_FALSE;
-       }
-     }
-     break;
-
-
-   case NS_MOUSE_BUTTON_UP: {
+  case NS_MOUSE_MOVE: {
+    if (mTrackingMouseMove)
+    {
+      nsCOMPtr<nsIBaseWindow> window;
+      nsIPresShell* presShell = aPresContext->GetPresShell();
+      nsCOMPtr<nsIContent> contentToResize =
+        GetContentToResize(presShell, getter_AddRefs(window));
 
-       if(mTrackingMouseMove && aEvent->eventStructType == NS_MOUSE_EVENT &&
-          static_cast<nsMouseEvent*>(aEvent)->button ==
-            nsMouseEvent::eLeftButton)
-       {
-         // we're done tracking.
-         mTrackingMouseMove = PR_FALSE;
+      // check if the returned content really is a menupopup
+      nsMenuPopupFrame* menuPopupFrame = nsnull;
+      if (contentToResize) {
+        nsIFrame* frameToResize = presShell->GetPrimaryFrameFor(contentToResize);
+        if (frameToResize && frameToResize->GetType() == nsGkAtoms::menuPopupFrame) {
+          menuPopupFrame = static_cast<nsMenuPopupFrame *>(frameToResize);
+        }
+      }
 
-         // end capture
-         nsIPresShell::SetCapturingContent(nsnull, 0);
+      // both MouseMove and direction are negative when pointing to the
+      // top and left, and positive when pointing to the bottom and right
+
+      // retrieve the offset of the mousemove event relative to the mousedown.
+      // The difference is how much the resize needs to be
+      nsIntPoint screenPoint(aEvent->refPoint + aEvent->widget->WidgetToScreenOffset());
+      nsIntPoint mouseMove(screenPoint - mMouseDownPoint);
 
-         *aEventStatus = nsEventStatus_eConsumeNoDefault;
-         doDefault = PR_FALSE;
-       }
-     }
-     break;
+      // what direction should we go in? For content resizing, always use
+      // 'bottomend'. For other windows, check the dir attribute.
+      Direction direction;
+      if (window || menuPopupFrame) {
+        direction = GetDirection();
+        if (menuPopupFrame) {
+          menuPopupFrame->CanAdjustEdges(
+            (direction.mHorizontal == -1) ? NS_SIDE_LEFT : NS_SIDE_RIGHT,
+            (direction.mVertical == -1) ? NS_SIDE_TOP : NS_SIDE_BOTTOM, mouseMove);
+        }
+      }
+      else if (contentToResize) {
+        direction.mHorizontal =
+          GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL ? -1 : 1;
+        direction.mVertical = 1;
+      }
+      else {
+        break; // don't do anything if there's nothing to resize
+      }
+
+      nsIntRect rect = mMouseDownRect;
+      AdjustDimensions(&rect.x, &rect.width, mouseMove.x, direction.mHorizontal);
+      AdjustDimensions(&rect.y, &rect.height, mouseMove.y, direction.mVertical);
 
-   case NS_MOUSE_MOVE: {
-       if(mTrackingMouseMove)
-       {
-         // get the document and the window - should this be cached?
-         nsPIDOMWindow *domWindow =
-           aPresContext->PresShell()->GetDocument()->GetWindow();
-         NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+      // Don't allow resizing a window or a popup past the edge of the screen,
+      // so adjust the rectangle to fit within the available screen area.
+      if (window) {
+        nsCOMPtr<nsIScreen> screen;
+        nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
+        if (sm) {
+          nsIntRect frameRect = GetScreenRect();
+          sm->ScreenForRect(frameRect.x, frameRect.y, 1, 1, getter_AddRefs(screen));
+          if (screen) {
+            nsIntRect screenRect;
+            screen->GetRect(&screenRect.x, &screenRect.y,
+                            &screenRect.width, &screenRect.height);
+            rect.IntersectRect(rect, screenRect);
+          }
+        }
+      }
+      else if (menuPopupFrame) {
+        nsPoint framePoint = menuPopupFrame->GetScreenRectInAppUnits().TopLeft();
+        nsIFrame* rootFrame = aPresContext->PresShell()->FrameManager()->GetRootFrame();
+        nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();
 
-         nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
-           do_QueryInterface(domWindow->GetDocShell());
-         NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
+        nsRect screenRect = menuPopupFrame->GetConstraintRect(framePoint, rootScreenRect);
+        // round using ToInsidePixels as it's better to be a pixel too small
+        // than be too large. If the popup is too large it could get flipped
+        // to the opposite side of the anchor point while resizing.
+        nsIntRect screenRectPixels = screenRect.ToInsidePixels(aPresContext->AppUnitsPerDevPixel());
+        rect.IntersectRect(rect, screenRectPixels);
+      }
 
-         nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
-         docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
-
-         nsCOMPtr<nsIBaseWindow> window(do_QueryInterface(treeOwner));
-
-         if (!window) {
-           return NS_OK;
-         }
+      if (contentToResize) {
+        nsIntRect cssRect =
+          rect.ToAppUnits(aPresContext->AppUnitsPerDevPixel())
+              .ToInsidePixels(nsPresContext::AppUnitsPerCSSPixel());
 
-         PRInt32 x,y,cx,cy;
-         window->GetPositionAndSize(&x,&y,&cx,&cy);
-         nsIntPoint oldWindowTopLeft(x, y);
+        nsAutoString widthstr, heightstr;
+        widthstr.AppendInt(cssRect.width);
+        heightstr.AppendInt(cssRect.height);
 
-         // both MouseMove and direction are negative when pointing to the
-         // top and left, and positive when pointing to the bottom and right
-         nsIntPoint mouseMove(aEvent->refPoint - mLastPoint);
-         
-         AdjustDimensions(&x, &cx, mouseMove.x, direction.mHorizontal);
-         AdjustDimensions(&y, &cy, mouseMove.y, direction.mVertical);
+        // for XUL elements, just set the width and height attributes. For
+        // other elements, set style.width and style.height
+        if (contentToResize->IsXUL()) {
+          nsIntRect oldRect;
+          nsWeakFrame weakFrame(menuPopupFrame);
+          if (menuPopupFrame) {
+            nsCOMPtr<nsIWidget> widget;
+            menuPopupFrame->GetWidget(getter_AddRefs(widget));
+            if (widget)
+              widget->GetScreenBounds(oldRect);
+          }
 
-         // remember the last mouse point, relative to the *new* window
-         mLastPoint = aEvent->refPoint + (oldWindowTopLeft - nsIntPoint(x, y));
-
-         window->SetPositionAndSize(x,y,cx,cy,PR_TRUE); // do the repaint.
+          contentToResize->SetAttr(kNameSpaceID_None, nsGkAtoms::width, widthstr, PR_TRUE);
+          contentToResize->SetAttr(kNameSpaceID_None, nsGkAtoms::height, heightstr, PR_TRUE);
 
-         *aEventStatus = nsEventStatus_eConsumeNoDefault;
-
-         doDefault = PR_FALSE;
-       }
-     }
-     break;
-
+          if (weakFrame.IsAlive() &&
+              (oldRect.x != rect.x || oldRect.y != rect.y)) {
+            // XXX This might go very wrong, since menu popups may add
+            // offsets (e.g. from margins) to this position, so the popup's
+            // widget won't end up at the desired position.
+            menuPopupFrame->MoveTo(rect.x, rect.y, PR_TRUE);
+          }
+        }
+        else {
+          nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyleContent =
+            do_QueryInterface(contentToResize);
+          if (inlineStyleContent) {
+            widthstr += NS_LITERAL_STRING("px");
+            heightstr += NS_LITERAL_STRING("px");
 
+            nsCOMPtr<nsIDOMCSSStyleDeclaration> decl;
+            inlineStyleContent->GetStyle(getter_AddRefs(decl));
+            decl->SetProperty(NS_LITERAL_STRING("width"), widthstr, EmptyString());
+            decl->SetProperty(NS_LITERAL_STRING("height"), heightstr, EmptyString());
+          }
+        }
+      }
+      else {
+        window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, PR_TRUE); // do the repaint.
+      }
 
-    case NS_MOUSE_CLICK:
-      if (NS_IS_MOUSE_LEFT_CLICK(aEvent))
-      {
-        MouseClicked(aPresContext, aEvent);
-      }
-      break;
+      doDefault = PR_FALSE;
+    }
   }
+  break;
+
+  case NS_MOUSE_CLICK:
+    if (NS_IS_MOUSE_LEFT_CLICK(aEvent))
+    {
+      MouseClicked(aPresContext, aEvent);
+    }
+    break;
+  }
+
+  if (!doDefault)
+    *aEventStatus = nsEventStatus_eConsumeNoDefault;
 
   if (doDefault && weakFrame.IsAlive())
     return nsTitleBarFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
   else
     return NS_OK;
 }
 
+nsIContent*
+nsResizerFrame::GetContentToResize(nsIPresShell* aPresShell, nsIBaseWindow** aWindow)
+{
+  *aWindow = nsnull;
+
+  nsAutoString elementid;
+  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::element, elementid);
+  if (elementid.IsEmpty()) {
+    // If the resizer is in a popup, resize the popup's widget, otherwise
+    // resize the widget associated with the window.
+    nsIFrame* popup = GetParent();
+    while (popup) {
+      if (popup->GetType() == nsGkAtoms::menuPopupFrame) {
+        return popup->GetContent();
+      }
+      popup = popup->GetParent();
+    }
+
+    // don't allow resizing windows in content shells
+    PRBool isChromeShell = PR_FALSE;
+    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;
+
+    // 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;
+        docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
+        if (treeOwner) {
+          CallQueryInterface(treeOwner, aWindow);
+        }
+      }
+    }
+
+    return nsnull;
+  }
+
+  if (elementid.EqualsLiteral("_parent")) {
+    return mContent->GetParent();
+  }
+
+  nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aPresShell->GetDocument());
+  nsCOMPtr<nsIDOMElement> element;
+  doc->GetElementById(elementid, getter_AddRefs(element));
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(element);
+  return content.get();
+}
+
 /* adjust the window position and size according to the mouse movement and
  * the resizer direction
  */
 void
 nsResizerFrame::AdjustDimensions(PRInt32* aPos, PRInt32* aSize,
                                  PRInt32 aMovement, PRInt8 aResizerDirection)
 {
   switch(aResizerDirection)
   {
     case -1: // only move the window when the direction is top and/or left
       *aPos+= aMovement;
     case 1: // falling through: the window is resized in both cases
       *aSize+= aResizerDirection*aMovement;
+      if (*aSize < 1) // use one as a minimum size or the element could disappear
+        *aSize = 1;
   }
 }
 
 /* returns a Direction struct containing the horizontal and vertical direction
  */
 nsResizerFrame::Direction
 nsResizerFrame::GetDirection()
 {
--- a/layout/xul/base/src/nsResizerFrame.h
+++ b/layout/xul/base/src/nsResizerFrame.h
@@ -35,16 +35,19 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef nsResizerFrame_h___
 #define nsResizerFrame_h___
 
 #include "nsTitleBarFrame.h"
 
+class nsIBaseWindow;
+class nsMenuPopupFrame;
+
 class nsResizerFrame : public nsTitleBarFrame 
 {
 protected:
   struct Direction {
     PRInt8 mHorizontal;
     PRInt8 mVertical;
   };
 
@@ -57,17 +60,20 @@ public:
 
   NS_IMETHOD HandleEvent(nsPresContext* aPresContext, 
                                       nsGUIEvent* aEvent,
                                       nsEventStatus* aEventStatus);
 
   virtual void MouseClicked(nsPresContext* aPresContext, nsGUIEvent *aEvent);
 
 protected:
+  nsIContent* GetContentToResize(nsIPresShell* aPresShell, nsIBaseWindow** aWindow);
+
   Direction GetDirection();
   static void AdjustDimensions(PRInt32* aPos, PRInt32* aSize,
                         PRInt32 aMovement, PRInt8 aResizerDirection);
 
 protected:
-	nsIntRect mWidgetRect;
+	nsIntRect mMouseDownRect;
+	nsIntPoint mMouseDownPoint;
 }; // class nsResizerFrame
 
 #endif /* nsResizerFrame_h___ */
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -265,17 +265,17 @@ nsXULPopupManager::AdjustPopupsOnWindowC
 {
   // Panels with noautohide="true" are moved and kept aligned with the anchor
   // when the parent window moves. Dismissable menus and panels are expected
   // to roll up when a window is moved, so there is no need to check these.
   nsMenuChainItem* item = mNoHidePanels;
   while (item) {
     // if the auto positioning has been disabled, don't move the popup
     if (item->Frame()->GetAutoPosition())
-      item->Frame()->SetPopupPosition(nsnull, PR_TRUE);
+      item->Frame()->SetPopupPosition(nsnull);
     item = item->GetParent();
   }
 }
 
 nsIFrame*
 nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
                                             nsIAtom* aFrameType,
                                             PRBool aShouldFlush)
--- a/layout/xul/base/test/Makefile.in
+++ b/layout/xul/base/test/Makefile.in
@@ -43,14 +43,15 @@ relativesrcdir  = layout/xul/base/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = 	test_bug381167.xhtml \
 		test_bug393970.xul \
 		test_resizer.xul \
 		window_resizer.xul \
+		window_resizer_element.xul \
 		test_bug477754.xul \
 		test_stack.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
--- a/layout/xul/base/test/test_resizer.xul
+++ b/layout/xul/base/test/test_resizer.xul
@@ -1,26 +1,104 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
 <?xml-stylesheet href="data:text/css,description {min-width: 1px; padding: 2px;}" type="text/css"?>
 <!--
 XUL <resizer> tests
 -->
 <window title="XUL resizer tests"
-  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript" src="/MochiKit/packed.js" />
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
     // we can't test GTK and Windows native drag resizing implementations
-    if (/Mac/.test(navigator.platform)) {
-      SimpleTest.waitForExplicitFinish();
+    SimpleTest.waitForExplicitFinish();
+
+    function openPopup()
+    {
+      document.getElementById("panel").
+        openPopupAtScreen(Math.round(window.mozInnerScreenX) + window.innerWidth - 130,
+                          Math.round(window.mozInnerScreenY) + window.innerHeight - 130);
+    }
+
+    var step = 0;
+    function popupShown(event)
+    {
+      if (step == 0) {
+        // check to make sure that the popup cannot be resized past the edges of
+        // the content area
+        var resizerrect = document.getElementById("resizer").getBoundingClientRect();
+        synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mousedown" });
+        synthesizeMouse(document.documentElement, resizerrect.left + 2000, resizerrect.top + 2000, { type:"mousemove" });
+
+        // allow a one pixel variance as rounding is always done to the inside
+        // of a rectangle.
+        var popuprect = document.getElementById("panel").getBoundingClientRect();
+        ok(Math.round(popuprect.right) == window.innerWidth ||
+           Math.round(popuprect.right) == window.innerWidth - 1,
+           "resized to content edge width");
+        ok(Math.round(popuprect.bottom) == window.innerHeight ||
+           Math.round(popuprect.bottom) == window.innerHeight - 1,
+           "resized to content edge height");
+
+        resizerrect = document.getElementById("resizer").getBoundingClientRect();
+        synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mouseup" });
+      }
+      else {
+        // the popup is opened twice. Make sure that for the second time, the
+        // resized popup opens in the same direction as there should still be
+        // room for it
+        var popuprect = document.getElementById("panel").getBoundingClientRect();
+        is(Math.round(popuprect.left), window.innerWidth - 130, "reopen popup left");
+        is(Math.round(popuprect.top), window.innerHeight - 130, "reopen popup top");
+      }
 
-      window.open("window_resizer.xul", "_blank", "left=200,top=200,outerWidth=300,outerHeight=300");
+      event.target.hidePopup();
+    }
+
+    function doResizerWindowTests() {
+      step++;
+      if (step == 1) {
+        openPopup();
+        return;
+      }
+
+      if (/Mac/.test(navigator.platform)) {
+        netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserWrite");
+        window.open("window_resizer.xul", "_blank", "left=200,top=200,outerWidth=300,outerHeight=300,chrome");
+      }
+      else {
+        lastResizerTest();
+      }
     }
+
+    function nextResizerTest()
+    {
+      // try opening the test again as a chrome window
+      if (step++ == 2)
+        window.open("window_resizer.xul", "_blank", "left=200,top=200,outerWidth=300,outerHeight=300");
+      else
+        lastResizerTest();
+    }
+
+    function lastResizerTest()
+    {
+      netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserWrite");
+      window.open("window_resizer_element.xul", "_blank", "left=200,top=200,outerWidth=300,outerHeight=300,chrome");
+    }
+
+    SimpleTest.waitForFocus(openPopup);
    ]]></script>
+
+<panel id="panel" onpopupshown="popupShown(event)" onpopuphidden="doResizerWindowTests()">
+  <resizer id="resizer" dir="bottomend" width="16" height="16"/>
+  <hbox width="50" height="50" flex="1"/>
+</panel>
+
 </window>
--- a/layout/xul/base/test/window_resizer.xul
+++ b/layout/xul/base/test/window_resizer.xul
@@ -1,37 +1,39 @@
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-	hidechrome="true"
-	windowtype="main"
-	orient="vertical"
-	sizemode="normal"
-	onload="setTimeout(doTest, 0)"
-	persist="screenX screenY width height">
+    screenX="200" screenY="200" width="300" height="300"
+	onload="setTimeout(doTest, 0)">
 <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
 <script><![CDATA[
 var is = window.opener.SimpleTest.is;
 
 function doTest() {
   // from test_resizer.xul
   var expectX = 200;
   var expectY = 200;
   var expectXMost = 500;
   var expectYMost = 500;
   var screenScale = expectX/window.screenX;
   var root = document.documentElement;
 
+  var oldScreenX = window.screenX;
+  var oldScreenY = window.screenY;
+  var oldWidth = window.outerWidth;
+  var oldHeight = window.outerHeight;
+
   function testResizer(dx, dy) {
     var offset = 20;
     var scale = 5;
     // target the centre of the resizer
     var offsetX = window.innerWidth/2 + (window.innerWidth/3)*dx;
     var offsetY = window.innerHeight/2 + (window.innerHeight/3)*dy;
 
-    for (mouseX = -1; mouseX <= 1; ++mouseX) {
-      for (mouseY = -1; mouseY <= 1; ++mouseY) {
+    for (var mouseX = -1; mouseX <= 1; ++mouseX) {
+      for (var mouseY = -1; mouseY <= 1; ++mouseY) {
         var newExpectX = expectX;
         var newExpectXMost = expectXMost;
         var newExpectY = expectY;
         var newExpectYMost = expectYMost;
         if (dx < 0) {
           newExpectX += mouseX*scale;
         } else if (dx > 0) {
           newExpectXMost += mouseX*scale;
@@ -39,42 +41,59 @@ function doTest() {
         if (dy < 0) {
           newExpectY += mouseY*scale;
         } else if (dy > 0) {
           newExpectYMost += mouseY*scale;
         }
 
         synthesizeMouse(root, offsetX, offsetY, { type:"mousedown" });
         synthesizeMouse(root, offsetX + mouseX*scale, offsetY + mouseY*scale, { type:"mousemove" });
-        is(window.screenX*screenScale, newExpectX,
-           "Bad x for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
-        is(window.screenY*screenScale, newExpectY,
-           "Bad y for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
-        is(window.outerWidth, newExpectXMost - newExpectX,
-           "Bad width for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
-        is(window.outerHeight, newExpectYMost - newExpectY,
-           "Bad height for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
+        if (window instanceof Components.interfaces.nsIDOMChromeWindow) {
+          is(window.screenX*screenScale, newExpectX,
+             "Bad x for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
+          is(window.screenY*screenScale, newExpectY,
+             "Bad y for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
+          is(window.outerWidth, newExpectXMost - newExpectX,
+             "Bad width for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
+          is(window.outerHeight, newExpectYMost - newExpectY,
+             "Bad height for " + dx + "," + dy + " moving " + mouseX + "," + mouseY);
+        }
+        else {
+          is(window.screenX, oldScreenX, "window not moved for non-chrome window screenX");
+          is(window.screenY, oldScreenY, "window not moved for non-chrome window screenY");
+          is(window.outerWidth, oldWidth, "window not moved for non-chrome window outerWidth");
+          is(window.outerHeight, oldHeight, "window not moved for non-chrome window outerHeight");
+        }
+
         // move it back before we release! Adjust for any window movement
         synthesizeMouse(root, offsetX - (newExpectX - expectX),
                               offsetY - (newExpectY - expectY), { type:"mousemove" });
         synthesizeMouse(root, offsetX, offsetY, { type:"mouseup" });
       }
     }
   }
 
+  function testElementResizer()
+  {
+    var resizers = document.getElementsByTagName("resizer");
+    for (var r = 0; r < resizers.length; r++) {
+      resizers.setAttribute("element", "container");
+    }
+  }
+
   testResizer(-1, -1);
   testResizer(-1, 0);
   testResizer(-1, 1);
   testResizer(0, -1);
   testResizer(0, 1);
   testResizer(1, -1);
   testResizer(1, 0);
   testResizer(1, 1);
   window.close();
-  window.opener.SimpleTest.finish();
+  window.opener.nextResizerTest();
 }
 ]]></script>
 	<hbox flex="1">
 		<vbox flex="1">
 			<resizer dir="topleft" flex="1"/>
 			<resizer dir="left" flex="1"/>
 			<resizer dir="bottomleft" flex="1"/>
 		</vbox>
new file mode 100644
--- /dev/null
+++ b/layout/xul/base/test/window_resizer_element.xul
@@ -0,0 +1,179 @@
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        align="start">
+<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<script><![CDATA[
+var is = window.opener.SimpleTest.is;
+
+const anchorPositions =
+  [ "before_start", "before_end", "after_start", "after_end",
+    "start_before", "start_after", "end_before", "end_after", "overlap", "screen"];
+var currentPosition;
+
+function testResizer(resizerid, noShrink, hResize, vResize, testid)
+{
+  var rect = document.getElementById(resizerid + "-container").getBoundingClientRect();
+  var resizer = document.getElementById(resizerid);
+  var resizerrect = resizer.getBoundingClientRect();
+
+  var originalX = resizerrect.left;
+  var originalY = resizerrect.top;
+
+  const scale = 20;
+  for (var mouseX = -1; mouseX <= 1; ++mouseX) {
+    for (var mouseY = -1; mouseY <= 1; ++mouseY) {
+      var expectedWidth = rect.width + hResize * mouseX * scale;
+      var expectedHeight = rect.height + vResize * mouseY * scale;
+
+      if (noShrink) {
+        if (mouseX == -1)
+          expectedWidth = rect.width;
+        if (mouseY == -1)
+          expectedHeight = rect.height;
+      }
+
+      synthesizeMouse(document.documentElement, originalX + 5, originalY + 5, { type:"mousedown" });
+      synthesizeMouse(document.documentElement, originalX + 5 + mouseX * scale,
+                      originalY + 5 + mouseY * scale, { type:"mousemove" });
+
+      var newrect = document.getElementById(resizerid + "-container").getBoundingClientRect();
+      is(Math.round(newrect.width), Math.round(expectedWidth), "resize element " + resizerid +
+         " " + testid + " width moving " + mouseX + "," + mouseY + ",,," + hResize);
+      is(Math.round(newrect.height), Math.round(expectedHeight), "resize element " + resizerid +
+         " " + testid + " height moving " + mouseX + "," + mouseY);
+      // move it back before we release!
+      synthesizeMouse(document.documentElement, originalX + 5, originalY + 5, { type:"mousemove" });
+      synthesizeMouse(document.documentElement, originalX + 5, originalY + 5, { type:"mouseup" });
+    }
+  }
+}
+
+function doTest() {
+  // first, check if a resizer with a element attribute set to an element that
+  // does not exist does not cause a problem
+  var resizer = document.getElementById("notfound");
+  synthesizeMouse(resizer, 5, 5, { type:"mousedown" });
+  synthesizeMouse(resizer, 10, 10, { type:"mousemove" });
+  synthesizeMouse(resizer, 5, 5, { type:"mouseup" });
+
+  testResizer("outside", true, 1, 1, "");
+  testResizer("html", true, 1, 1, "");
+  testResizer("inside", true, 1, 1, "");
+  testResizer("inside-large", false, 1, 1, "");
+  testResizer("inside-with-border", true, 1, 1, "");
+
+  document.getElementById("inside-popup-container").
+    openPopupAtScreen(Math.ceil(window.mozInnerScreenX) + 100, Math.ceil(window.mozInnerScreenY) + 100);
+}
+
+function popupShown(event)
+{
+  testResizer("inside-popup", false, 1, 1, "");
+  document.getElementById("inside-popup-container").id = "outside-popup-container";
+  testResizer("outside-popup", false, 1, 1, "");
+
+  var resizerrect = document.getElementById("inside-popup").getBoundingClientRect();
+  synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mousedown" });
+  synthesizeMouse(document.documentElement, resizerrect.left + 2000, resizerrect.top + 2000, { type:"mousemove" });
+
+  var isMac = (navigator.platform.indexOf("Mac") >= 0);
+  var popuprect = document.getElementById("outside-popup-container").getBoundingClientRect();
+  // subtract 3 due to space left for panel dropshadow
+  is(Math.ceil(window.mozInnerScreenX) + popuprect.right,
+     (isMac ? screen.availLeft + screen.availWidth : screen.left + screen.width) - 3, "resized to edge width");
+  is(Math.ceil(window.mozInnerScreenY) + popuprect.bottom,
+     (isMac ? screen.availTop + screen.availHeight : screen.top + screen.height) - 3, "resized to edge height");
+
+  resizerrect = document.getElementById("inside-popup").getBoundingClientRect();
+  synthesizeMouse(document.documentElement, resizerrect.left + 5, resizerrect.top + 5, { type:"mouseup" });
+
+  event.target.hidePopup();
+}
+
+function popupHidden()
+{
+  if (anchorPositions.length == 0) {
+    window.close();
+    window.opener.SimpleTest.finish();
+    return;
+  }
+
+  currentPosition = anchorPositions.shift();
+  var anchor = document.getElementById("anchor");
+  var popup = document.getElementById("anchored-panel-container");
+
+  if (currentPosition == "screen")
+    popup.openPopupAtScreen(window.screenX + 100, window.screenY + 100);
+  else
+    popup.openPopup(anchor, currentPosition);
+}
+
+function anchoredPopupShown(event)
+{
+  var leftAllowed = (currentPosition.indexOf("end_") == -1 && currentPosition.indexOf("_start") == -1);
+  var rightAllowed = (currentPosition.indexOf("start_") == -1 && currentPosition.indexOf("_end") == -1);
+  var topAllowed = (currentPosition.indexOf("after_") == -1 && currentPosition.indexOf("_before") == -1);
+  var bottomAllowed = (currentPosition.indexOf("before_") == -1 && currentPosition.indexOf("_after") == -1);
+
+  if (currentPosition == "overlap") {
+    leftAllowed = topAllowed = false;
+    rightAllowed = bottomAllowed = true;
+  }
+
+  var resizerTypes = [ "topleft", "top", "topright", "left", "right",
+                       "bottomleft", "bottom", "bottomright", "bottomend" ];
+  for (var r = 0; r < resizerTypes.length; r++) {
+    var resizerType = resizerTypes[r];
+    var horiz = 0, vert = 0;
+    if (leftAllowed && resizerType.indexOf("left") >= 0) horiz = -1;
+    else if (rightAllowed && (resizerType.indexOf("right") >= 0 || resizerType == "bottomend")) horiz = 1;
+
+    if (topAllowed && resizerType.indexOf("top") >= 0) vert = -1;
+    else if (bottomAllowed && resizerType.indexOf("bottom") >= 0) vert = 1;
+
+    document.getElementById("anchored-panel").dir = resizerType;
+    testResizer("anchored-panel", false, horiz, vert, currentPosition + " " + resizerType);
+  }
+
+  event.target.hidePopup();
+}
+
+window.opener.SimpleTest.waitForFocus(doTest, window);
+]]></script>
+
+<resizer id="outside" dir="bottomend" element="outside-container"/>
+<resizer id="notfound" dir="bottomend" element="nothing"/>
+<hbox id="outside-container">
+  <hbox minwidth="46" minheight="39"/>
+</hbox>
+<html:div id="html-container" xmlns:html="http://www.w3.org/1999/xhtml">
+  <html:button>One</html:button><html:br/>
+  <resizer id="html" dir="bottomend" element="_parent"/>
+</html:div>
+<hbox id="anchor" align="start" style="margin-left: 100px;">
+  <hbox id="inside-container">
+    <hbox minwidth="45" minheight="41"/>
+    <resizer id="inside" dir="bottomend" element="_parent"/>
+  </hbox>
+  <hbox id="inside-large-container" width="70" height="70">
+    <resizer id="inside-large" dir="bottomend" element="_parent"/>
+  </hbox>
+  <hbox id="inside-with-border-container" style="border: 5px solid red; padding: 2px; margin: 2px;">
+    <hbox minwidth="35" minheight="30"/>
+    <resizer id="inside-with-border" dir="bottomend" element="_parent"/>
+  </hbox>
+</hbox>
+
+<panel id="inside-popup-container" onpopupshown="popupShown(event)" onpopuphidden="popupHidden()">
+  <resizer id="inside-popup" dir="bottomend"/>
+  <hbox width="50" height="50" flex="1"/>
+</panel>
+<resizer id="outside-popup" dir="bottomend" element="outside-popup-container"/>
+
+<panel id="anchored-panel-container" onpopupshown="anchoredPopupShown(event)"
+       onpopuphidden="popupHidden()">
+  <hbox width="50" height="50" flex="1"/>
+  <resizer id="anchored-panel" width="20" height="20"/>
+</panel>
+
+</window>