diff --git a/layout/xul/base/src/nsMenuPopupFrame.cpp b/layout/xul/base/src/nsMenuPopupFrame.cpp index 27d9814dee2..e0405c199a3 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -598,71 +598,37 @@ nsMenuPopupFrame::GetLayoutFlags(PRUint32& aFlags) aFlags = NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_VISIBILITY; } -/////////////////////////////////////////////////////////////////////////////// -// GetViewOffset -// Retrieves the offset of the given view with the root view, in the -// coordinate system of the root view. -void -nsMenuPopupFrame::GetViewOffset(nsIView* aView, nsPoint& aPoint) -{ - // Notes: - // 1) The root view is the client area of the toplevel window that - // this popup is anchored to. - // 2) Each menupopup is a child of the root view (see - // nsMenuPopupFrame::Init()) - // 3) The coordinates that we return are the total distance between - // the top left of the start view and the origin of the root view. - - // Keep track of the root view so that we know to stop there - nsIView* rootView; - aView->GetViewManager()->GetRootView(rootView); - aPoint = aView->GetOffsetTo(rootView); -} - /////////////////////////////////////////////////////////////////////////////// // GetRootViewForPopup // Retrieves the view for the popup widget that contains the given frame. // If the given frame is not contained by a popup widget, return the -// root view. This is the root view of the pres context's -// viewmanager if aStopAtViewManagerRoot is true; otherwise it's the // root view of the root viewmanager. nsIView* -nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame, - PRBool aStopAtViewManagerRoot) +nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame) { nsIView* view = aStartFrame->GetClosestView(); NS_ASSERTION(view, "frame must have a closest view!"); - if (view) { - nsIView* rootView = nsnull; - if (aStopAtViewManagerRoot) { - view->GetViewManager()->GetRootView(rootView); - } - - while (view) { - // Walk up the view hierarchy looking for a view whose widget has a - // window type of eWindowType_popup - in other words a popup window - // widget. If we find one, this is the view we want. - nsIWidget* widget = view->GetWidget(); - if (widget) { - nsWindowType wtype; - widget->GetWindowType(wtype); - if (wtype == eWindowType_popup) { - return view; - } - } - - if (aStopAtViewManagerRoot && view == rootView) { + nsIView* rootView = nsnull; + while (view) { + // Walk up the view hierarchy looking for a view whose widget has a + // window type of eWindowType_popup - in other words a popup window + // widget. If we find one, this is the view we want. + nsIWidget* widget = view->GetWidget(); + if (widget) { + nsWindowType wtype; + widget->GetWindowType(wtype); + if (wtype == eWindowType_popup) { return view; } - - nsIView* temp = view->GetParent(); - if (!temp) { - // Otherwise, we've walked all the way up to the root view and not - // found a view for a popup window widget. Just return the root view. - return view; - } - view = temp; } + + nsIView* temp = view->GetParent(); + if (!temp) { + // Otherwise, we've walked all the way up to the root view and not + // found a view for a popup window widget. Just return the root view. + return view; + } + view = temp; } return nsnull; @@ -854,6 +820,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) PRBool sizedToPopup = PR_FALSE; nsPresContext* presContext = PresContext(); + nsIFrame* rootFrame = presContext->PresShell()->FrameManager()->GetRootFrame(); // if the frame is not specified, use the anchor node passed to ShowPopup. If // that wasn't specified either, use the root frame. Note that mAnchorContent @@ -871,7 +838,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) } if (!aAnchorFrame) { - aAnchorFrame = presContext->PresShell()->FrameManager()->GetRootFrame(); + aAnchorFrame = rootFrame; if (!aAnchorFrame) return NS_OK; } @@ -882,96 +849,58 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) sizedToPopup = nsMenuFrame::IsSizedToPopup(aAnchorFrame->GetContent(), PR_FALSE); } - // |containingView| - // The view that contains the frame that is invoking this popup. This is - // the canvas view inside the scrollport view. It can have negative bounds - // if the canvas is scrolled so that part is off screen. - nsIView* containingView = nsnull; - nsPoint offset; - nsMargin margin; - containingView = aAnchorFrame->GetClosestView(&offset); - if (!containingView) - return NS_OK; - - // |parentPos| - // The distance between the containingView and the root view. This provides - // a hint as to where to position the menu relative to the window. - nsPoint parentPos; - GetViewOffset(containingView, parentPos); - // |parentRect| // The dimensions of the frame invoking the popup. nsRect parentRect = aAnchorFrame->GetRect(); - // get the document and the global script object - nsIPresShell *presShell = presContext->PresShell(); - nsIDocument *document = presShell->GetDocument(); - // If we stick to our parent's width, set it here before we move the // window around, because moving is done with respect to the width... if (sizedToPopup) { mRect.width = parentRect.width; } - // Use containingView instead of parentView, to account for the scrollarrows - // that a parent menu might have. - nsPoint parentViewWidgetOffset; - nsIWidget* parentViewWidget = containingView->GetNearestWidget(&parentViewWidgetOffset); - nsRect localParentWidgetRect(0,0,0,0), screenParentWidgetRect; - parentViewWidget->WidgetToScreen ( localParentWidgetRect, screenParentWidgetRect ); - // |xpos| and |ypos| hold the x and y positions of where the popup will be moved to, - // in _twips_, in the coordinate system of the _parent view_. + // in app units, in the coordinate system of the _parent view_. PRBool readjustAboveBelow = PR_FALSE; PRInt32 xpos = 0, ypos = 0; + nsMargin margin; + + // the positon in app units where the popup should appear. PRInt32 screenViewLocX, screenViewLocY; + // the screen rectangle of the anchor, or if null, the root frame, in dev pixels. + nsRect anchorScreenRect; + nsRect rootScreenRect = rootFrame->GetScreenRect(); + if (mScreenXPos == -1 && mScreenYPos == -1) { // if we are anchored to our parent, there are certain things we don't want to do // when repositioning the view to fit on the screen, such as end up positioned over // the parent. When doing this reposition, we want to move the popup to the side with // the most room. The combination of anchor and alignment dictate if we readjust // above/below or to the left/right. - if (mAnchorContent) { - xpos = parentPos.x + offset.x; - ypos = parentPos.y + offset.y; + anchorScreenRect = aAnchorFrame->GetScreenRect(); + xpos = presContext->DevPixelsToAppUnits(anchorScreenRect.x - rootScreenRect.x); + ypos = presContext->DevPixelsToAppUnits(anchorScreenRect.y - rootScreenRect.y); // move the popup according to the anchor and alignment. This will also tell us // which axis the popup is flush against in case we have to move it around later. AdjustPositionForAnchorAlign(&xpos, &ypos, parentRect, &readjustAboveBelow); - - // the x and y position may be used to offset the popup after it has been anchored - xpos += presContext->DevPixelsToAppUnits(mXPos); - ypos += presContext->DevPixelsToAppUnits(mYPos); } else { + // with no anchor, the popup is positioned relative to the root frame + anchorScreenRect = rootScreenRect; GetStyleMargin()->GetMargin(margin); - xpos = presContext->DevPixelsToAppUnits(mXPos) + margin.left; - ypos = presContext->DevPixelsToAppUnits(mYPos) + margin.top; + xpos = margin.left; + ypos = margin.top; } - // Recall that |xpos| and |ypos| are in the coordinate system of the parent view. In - // order to determine the screen coordinates of where our view will end up, we - // need to find the x/y position of the parent view in screen coords. That is done - // by getting the widget associated with the parent view and determining the offset - // based on converting (0,0) in its coordinate space to screen coords. We then - // offset that point by (|xpos|,|ypos|) to get the true screen coordinates of - // the view. *whew* + // add on the offset + xpos += presContext->CSSPixelsToAppUnits(mXPos); + ypos += presContext->CSSPixelsToAppUnits(mYPos); - // |parentView| - // The root view for the window that contains the frame, for frames inside - // menupopups this is the first view inside the popup window widget, for - // frames inside a toplevel window, this is the root view of the toplevel - // window. - nsIView* parentView = GetRootViewForPopup(aAnchorFrame, PR_FALSE); - if (!parentView) - return NS_OK; - - screenViewLocX = presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) + - (xpos - parentPos.x) + parentViewWidgetOffset.x; - screenViewLocY = presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) + - (ypos - parentPos.y) + parentViewWidgetOffset.y; + screenViewLocX = presContext->DevPixelsToAppUnits(rootScreenRect.x) + xpos; + screenViewLocY = presContext->DevPixelsToAppUnits(rootScreenRect.y) + ypos; } else { // positioned on screen @@ -979,19 +908,15 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) screenViewLocX = nsPresContext::CSSPixelsToAppUnits(mScreenXPos) + margin.left; screenViewLocY = nsPresContext::CSSPixelsToAppUnits(mScreenYPos) + margin.top; - xpos = screenViewLocX - presContext->DevPixelsToAppUnits(screenParentWidgetRect.x) - - parentViewWidgetOffset.x - parentPos.x; - ypos = screenViewLocY - presContext->DevPixelsToAppUnits(screenParentWidgetRect.y) - - parentViewWidgetOffset.y - parentPos.y; + // determine the x and y position by subtracting the desired screen + // position from the screen position of the root frame. + xpos = screenViewLocX - presContext->DevPixelsToAppUnits(rootScreenRect.x); + ypos = screenViewLocY - presContext->DevPixelsToAppUnits(rootScreenRect.y); } // Compute info about the screen dimensions. Because of multiple monitor systems, // the left or top sides of the screen may be in negative space (main monitor is on the // right, etc). We need to be sure to do the right thing. - nsPIDOMWindow *window = document->GetWindow(); - if (!window) - return NS_OK; - nsIDeviceContext* devContext = PresContext()->DeviceContext(); nsRect rect; if ( mMenuCanOverlapOSBar ) { @@ -1007,7 +932,6 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) // for content shells, clip to the client area rather than the screen area if (mInContentShell) { - nsRect rootScreenRect = presShell->GetRootFrame()->GetScreenRect(); rootScreenRect.ScaleRoundIn(presContext->AppUnitsPerDevPixel()); rect.IntersectRect(rect, rootScreenRect); } @@ -1020,6 +944,8 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) PRInt32 screenBottomTwips = rect.YMost(); if (mPopupAnchor != POPUPALIGNMENT_NONE) { + NS_ASSERTION(mScreenXPos == -1 && mScreenYPos == -1, + "screen position used with anchor"); // // Popup is anchored to the parent, guarantee that it does not cover the parent. We // shouldn't do anything funky if it will already fit on the screen as is. @@ -1042,16 +968,6 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) // | \/ || // +------------------------+ - - - // compute screen coordinates of parent frame so we can play with it. Make sure we put it - // into twips as everything else is as well. - nsRect screenParentFrameRect (presContext->AppUnitsToDevPixels(offset.x), presContext->AppUnitsToDevPixels(offset.y), - parentRect.width, parentRect.height ); - parentViewWidget->WidgetToScreen ( screenParentFrameRect, screenParentFrameRect ); - screenParentFrameRect.x = presContext->DevPixelsToAppUnits(screenParentFrameRect.x); - screenParentFrameRect.y = presContext->DevPixelsToAppUnits(screenParentFrameRect.y); - // Don't let it spill off the screen to the top if (screenViewLocY < screenTopTwips) { PRInt32 moveDist = screenTopTwips - screenViewLocY; @@ -1063,7 +979,9 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame) if ( (screenViewLocX + mRect.width) > screenRightTwips || screenViewLocX < screenLeftTwips || (screenViewLocY + mRect.height) > screenBottomTwips ) { - + nsRect screenParentFrameRect(anchorScreenRect); + screenParentFrameRect.ScaleRoundOut(PresContext()->AppUnitsPerDevPixel()); + // figure out which side of the parent has the most free space so we can move/resize // the popup there. This should still work if the parent frame is partially screen. PRBool switchSides = IsMoreRoomOnOtherSideOfParent ( readjustAboveBelow, screenViewLocX, screenViewLocY, @@ -1576,9 +1494,7 @@ nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, PRBool& doActi NS_IMETHODIMP nsMenuPopupFrame::GetWidget(nsIWidget **aWidget) { - // Get parent view - // XXX should this be passing PR_FALSE or PR_TRUE for aStopAtViewManagerRoot? - nsIView * view = GetRootViewForPopup(this, PR_FALSE); + nsIView * view = GetRootViewForPopup(this); if (!view) return NS_OK; diff --git a/layout/xul/base/src/nsMenuPopupFrame.h b/layout/xul/base/src/nsMenuPopupFrame.h index b39d4f16cf1..531d2387212 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.h +++ b/layout/xul/base/src/nsMenuPopupFrame.h @@ -204,9 +204,7 @@ public: // laid out, so that the view can be shown. void AdjustView(); - void GetViewOffset(nsIView* aView, nsPoint& aPoint); - nsIView* GetRootViewForPopup(nsIFrame* aStartFrame, - PRBool aStopAtViewManagerRoot); + 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 diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index 1ce9f35e4ff..c3902f63197 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -58,6 +58,9 @@ _TEST_FILES = bug288254_window.xul \ window_popup_preventdefault_chrome.xul \ test_largemenu.xul \ window_largemenu.xul \ + test_popup_anchor.xul \ + window_popup_anchor.xul \ + frame_popup_anchor.xul \ $(NULL) ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) diff --git a/toolkit/content/tests/chrome/frame_popup_anchor.xul b/toolkit/content/tests/chrome/frame_popup_anchor.xul new file mode 100644 index 00000000000..a6c02fd1764 --- /dev/null +++ b/toolkit/content/tests/chrome/frame_popup_anchor.xul @@ -0,0 +1,44 @@ + + + + + + + + + + + + + diff --git a/toolkit/content/tests/chrome/test_popup_anchor.xul b/toolkit/content/tests/chrome/test_popup_anchor.xul new file mode 100644 index 00000000000..5a719ce16dc --- /dev/null +++ b/toolkit/content/tests/chrome/test_popup_anchor.xul @@ -0,0 +1,32 @@ + + + + + + + + + +

+

+ +
+
+ + +
diff --git a/toolkit/content/tests/chrome/window_popup_anchor.xul b/toolkit/content/tests/chrome/window_popup_anchor.xul new file mode 100644 index 00000000000..693533a4562 --- /dev/null +++ b/toolkit/content/tests/chrome/window_popup_anchor.xul @@ -0,0 +1,27 @@ + + + + + + + + +