Bug 536625, use menupopup's move instead of widget's, r=roc
authorNeil Deakin <neil@mozilla.com>
Mon, 01 Feb 2010 10:11:08 -0500
changeset 37792 0538dc4319643594cd6f685d30d67f1c9bd01050
parent 37791 653a6ad047ba34f4eb7a1a90d26caf6d435108bb
child 37793 4ab5e6695dc88305a402b1d5daaac4107c7022d8
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs536625
milestone1.9.3a1pre
Bug 536625, use menupopup's move instead of widget's, r=roc
layout/xul/base/src/nsMenuFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.cpp
layout/xul/base/src/nsMenuPopupFrame.h
layout/xul/base/src/nsPopupSetFrame.cpp
layout/xul/base/src/nsTitleBarFrame.cpp
layout/xul/base/src/nsXULPopupManager.cpp
toolkit/content/tests/chrome/Makefile.in
toolkit/content/tests/chrome/test_titlebar.xul
toolkit/content/tests/chrome/window_titlebar.xul
--- a/layout/xul/base/src/nsMenuFrame.cpp
+++ b/layout/xul/base/src/nsMenuFrame.cpp
@@ -757,17 +757,17 @@ nsMenuFrame::DoLayout(nsBoxLayoutState& 
     if (sizeChanged) {
       mPopupFrame->SetPreferredBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
     }
 
     // if the menu has just been opened, or its size changed, position
     // the popup. The flag that the popup checks in the HasOpenChanged
     // method will get cleared in AdjustView which is called below.
     if (IsOpen() && (sizeChanged || mPopupFrame->HasOpenChanged()))
-      mPopupFrame->SetPopupPosition(this);
+      mPopupFrame->SetPopupPosition(this, PR_FALSE);
 
     // is the new size too small? Make sure we handle scrollbars correctly
     nsIBox* child = mPopupFrame->GetChildBox();
 
     nsRect bounds(mPopupFrame->GetRect());
 
     nsIScrollableFrame *scrollframe = do_QueryFrame(child);
     if (scrollframe &&
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp
+++ b/layout/xul/base/src/nsMenuPopupFrame.cpp
@@ -900,17 +900,17 @@ nsMenuPopupFrame::FlipOrResize(nscoord& 
       aScreenPoint = aScreenEnd - aSize;
     }
   }
 
   return popupSize;
 }
 
 nsresult
-nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame)
+nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, PRBool aIsMove)
 {
   if (!mShouldAutoPosition)
     return NS_OK;
 
   nsPresContext* presContext = PresContext();
   nsIFrame* rootFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
   NS_ASSERTION(rootFrame->GetView() && GetView() &&
                rootFrame->GetView() == GetView()->GetParent(),
@@ -1040,16 +1040,22 @@ 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;
+  }
+
   nsRect screenRect = GetConstraintRect(anchorRect.TopLeft(), rootScreenRect);
 
   // ensure that anchorRect is on screen
   if (!anchorRect.IntersectRect(anchorRect, screenRect)) {
     anchorRect.width = anchorRect.height = 0;
     // if the anchor isn't within the screen, move it to the edge of the screen.
     if (anchorRect.x < screenRect.x)
       anchorRect.x = screenRect.x;
@@ -1578,17 +1584,17 @@ void
 nsMenuPopupFrame::MoveTo(PRInt32 aLeft, PRInt32 aTop, PRBool aUpdateAttrs)
 {
   // 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);
+  SetPopupPosition(nsnull, PR_TRUE);
 
   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
@@ -200,18 +200,19 @@ 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.
-  nsresult SetPopupPosition(nsIFrame* aAnchorFrame);
+  // will be adjusted so that it is on screen. If aIsMove is true, then the popup
+  // is being moved, and should not be flipped.
+  nsresult SetPopupPosition(nsIFrame* aAnchorFrame, PRBool aIsMove);
 
   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
--- a/layout/xul/base/src/nsPopupSetFrame.cpp
+++ b/layout/xul/base/src/nsPopupSetFrame.cpp
@@ -178,17 +178,17 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutSta
       // then get its preferred size
       nsSize prefSize = popupChild->GetPrefSize(aState);
       nsSize minSize = popupChild->GetMinSize(aState);
       nsSize maxSize = popupChild->GetMaxSize(aState);
 
       prefSize = BoundsCheck(minSize, prefSize, maxSize);
 
       popupChild->SetPreferredBounds(aState, nsRect(0,0,prefSize.width, prefSize.height));
-      popupChild->SetPopupPosition(nsnull);
+      popupChild->SetPopupPosition(nsnull, PR_FALSE);
 
       // is the new size too small? Make sure we handle scrollbars correctly
       nsIBox* child = popupChild->GetChildBox();
 
       nsRect bounds(popupChild->GetRect());
 
       nsIScrollableFrame *scrollframe = do_QueryFrame(child);
       if (scrollframe &&
@@ -213,17 +213,17 @@ nsPopupSetFrame::DoLayout(nsBoxLayoutSta
       // special case for tooltips where the preferred height doesn't include the
       // real height for its inline element, but does once it is laid out.
       // This is bug 228673 which doesn't have a simple fix.
       if (popupChild->GetRect().width > bounds.width ||
           popupChild->GetRect().height > bounds.height) {
         // the size after layout was larger than the preferred size,
         // so set the preferred size accordingly
         popupChild->SetPreferredSize(popupChild->GetSize());
-        popupChild->SetPopupPosition(nsnull);
+        popupChild->SetPopupPosition(nsnull, PR_FALSE);
       }
       popupChild->AdjustView();
     }
 
     currEntry = currEntry->mNextPopup;
   }
 
   return rv;
--- a/layout/xul/base/src/nsTitleBarFrame.cpp
+++ b/layout/xul/base/src/nsTitleBarFrame.cpp
@@ -151,25 +151,25 @@ nsTitleBarFrame::HandleEvent(nsPresConte
        if(mTrackingMouseMove)
        {
          nsIntPoint nsMoveBy = aEvent->refPoint - mLastPoint;
 
          nsIFrame* parent = GetParent();
          while (parent && parent->GetType() != nsGkAtoms::menuPopupFrame)
            parent = parent->GetParent();
 
-         // if the titlebar is in a popup, move the popup's widget, otherwise
+         // if the titlebar is in a popup, move the popup frame, otherwise
          // move the widget associated with the window
          if (parent) {
+           nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame*>(parent);
            nsCOMPtr<nsIWidget> widget;
-           (static_cast<nsMenuPopupFrame*>(parent))->
-             GetWidget(getter_AddRefs(widget));
+           menuPopupFrame->GetWidget(getter_AddRefs(widget));
            nsIntRect bounds;
            widget->GetScreenBounds(bounds);
-           widget->Move(bounds.x + nsMoveBy.x, bounds.y + nsMoveBy.y);
+           menuPopupFrame->MoveTo(bounds.x + nsMoveBy.x, bounds.y + nsMoveBy.y, PR_FALSE);
          }
          else {
            nsIPresShell* presShell = aPresContext->PresShell();
            nsPIDOMWindow *window = presShell->GetDocument()->GetWindow();
            if (window) {
              window->MoveBy(nsMoveBy.x, nsMoveBy.y);
            }
          }
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -264,17 +264,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);
+      item->Frame()->SetPopupPosition(nsnull, PR_TRUE);
     item = item->GetParent();
   }
 }
 
 nsIFrame*
 nsXULPopupManager::GetFrameOfTypeForContent(nsIContent* aContent,
                                             nsIAtom* aFrameType,
                                             PRBool aShouldFlush)
--- a/toolkit/content/tests/chrome/Makefile.in
+++ b/toolkit/content/tests/chrome/Makefile.in
@@ -90,16 +90,18 @@ include $(topsrcdir)/config/rules.mk
 		window_keys.xul \
 		test_showcaret.xul \
 		window_showcaret.xul \
 		test_righttoleft.xul \
 		test_dialogfocus.xul \
 		dialog_dialogfocus.xul \
 		test_screenPersistence.xul \
 		window_screenPosSize.xul \
+		test_titlebar.xul \
+		window_titlebar.xul \
 		$(NULL)
 
 # test_panel_focus.xul won't work if the Full Keyboard Access preference is set to
 # textboxes and lists only, so skip this test on Mac
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _TEST_FILES += test_panel_focus.xul \
                window_panel_focus.xul
 else
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/test_titlebar.xul
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+  XUL Widget Test for the titlebar element and window dragging
+  -->
+<window title="Titlebar" width="200" height="200"
+        onload="setTimeout(test_titlebar, 0);"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function test_titlebar()
+{
+  window.open("window_titlebar.xul", "_blank", "chrome,left=200,top=200");
+}
+
+function done(testWindow)
+{
+  testWindow.close();
+  SimpleTest.finish();
+}
+
+]]>
+</script>
+
+</window>
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/window_titlebar.xul
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<!--
+  XUL Widget Test for the titlebar element and window dragging
+  -->
+<window title="Titlebar" width="200" height="200"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+  <titlebar id="titlebar">
+    <label id="label" value="Titlebar"/>
+  </titlebar>
+
+  <panel id="panel" onpopupshown="popupshown()" onpopuphidden="popuphidden()">
+    <titlebar>
+      <label id="panellabel" value="Titlebar"/>
+    </titlebar>
+  </panel>
+
+  <button id="button" label="OK"/>
+  <statusbar id="statusbar">
+    <statusbarpanel>
+      <label id="statuslabel" value="Status"/>
+      <label id="statuslabelnodrag" value="No Drag" onmousedown="event.preventDefault()"/>
+    </statusbarpanel>
+  </statusbar>
+
+<script>
+<![CDATA[
+
+var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
+
+SimpleTest.waitForFocus(test_titlebar, window);
+
+function test_titlebar()
+{
+  var titlebar = document.getElementById("titlebar");
+  var label = document.getElementById("label");
+
+  var oldx, oldy;
+
+  // on Mac, the window can also be moved with the statusbar
+  if (navigator.platform.indexOf("Mac") >= 0) {
+    var statuslabel = document.getElementById("statuslabel");
+    var statuslabelnodrag = document.getElementById("statuslabelnodrag");
+
+    oldx = window.screenX;
+    oldy = window.screenY;
+
+    synthesizeMouse(statuslabel, 2, 2, { type: "mousedown" });
+    synthesizeMouse(statuslabel, 22, 22, { type: "mousemove" });
+    SimpleTest.is(window.screenX, oldx + 20, "move window with statusbar horizontal");
+    SimpleTest.is(window.screenY, oldy + 20, "move window with statusbar vertical");
+    synthesizeMouse(statuslabel, 22, 22, { type: "mouseup" });
+
+    // event was cancelled so the drag should not have occurred
+    synthesizeMouse(statuslabelnodrag, 2, 2, { type: "mousedown" });
+    synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mousemove" });
+    SimpleTest.is(window.screenX, oldx + 20, "move window with statusbar cancelled mousedown horizontal");
+    SimpleTest.is(window.screenY, oldy + 20, "move window with statusbar cancelled mousedown vertical");
+    synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mouseup" });
+  }
+
+  oldx = window.screenX;
+  oldy = window.screenY;
+
+  var target;
+  var mousedownListener = function (event) target = event.originalTarget;
+  window.addEventListener("mousedown", mousedownListener, false);
+  synthesizeMouse(label, 2, 2, { type: "mousedown" });
+  SimpleTest.is(target, titlebar, "movedown on titlebar");
+  synthesizeMouse(label, 22, 22, { type: "mousemove" });
+  SimpleTest.is(window.screenX, oldx + 20, "move window horizontal");
+  SimpleTest.is(window.screenY, oldy + 20, "move window vertical");
+  synthesizeMouse(label, 22, 22, { type: "mouseup" });
+
+  // with allowEvents set to true, the mouse should target the label instead
+  // and not move the window
+  titlebar.allowEvents = true;
+
+  synthesizeMouse(label, 2, 2, { type: "mousedown" });
+  SimpleTest.is(target, label, "movedown on titlebar with allowevents");
+  synthesizeMouse(label, 22, 22, { type: "mousemove" });
+  SimpleTest.is(window.screenX, oldx + 20, "mouse on label move window horizontal");
+  SimpleTest.is(window.screenY, oldy + 20, "mouse on label move window vertical");
+  synthesizeMouse(label, 22, 22, { type: "mouseup" });
+
+  window.removeEventListener("mousedown", mousedownListener, false);
+
+  document.getElementById("panel").openPopupAtScreen(window.screenX + 50, window.screenY + 60, false);
+}
+
+function popupshown()
+{
+  var panellabel = document.getElementById("panellabel");
+  var panel = document.getElementById("panel");
+  var oldrect = panel.getBoundingClientRect();
+
+  synthesizeMouse(panellabel, 2, 2, { type: "mousedown" });
+  synthesizeMouse(panellabel, 22, 22, { type: "mousemove" });
+
+  var newrect = panel.getBoundingClientRect();
+  SimpleTest.is(newrect.left, oldrect.left + 20, "move popup horizontal");
+  SimpleTest.is(newrect.top, oldrect.top + 20, "move popup vertical");
+  synthesizeMouse(panellabel, 22, 22, { type: "mouseup" });
+
+  synthesizeMouse(document.getElementById("button"), 5, 5, { type: "mousemove" });
+  SimpleTest.is(newrect.left, oldrect.left + 20, "popup horizontal after mouse on button");
+  SimpleTest.is(newrect.top, oldrect.top + 20, "popup vertical after mouse on button");
+
+  panel.hidePopup();
+}
+
+function popuphidden()
+{
+  window.opener.wrappedJSObject.done(window);
+}
+
+]]>
+</script>
+
+</window>