Bug 1368094 - Correct panel sliding on window resize r=bytesized

This is in response to an issue that's affecting the new app
update doorhangers on OSX, where the problem is more obvious.
On OSX, the panel styling makes it so that the doorhanger
overflows the window a little bit. This is fine until you enter
fullscreen with ctrl+command+F. At this point, the doorhanger
should come back onto the screen and the arrow should be rooted
to its anchor element (in our case the hamburger menu icon), but
instead it lags and the panel is not adjusted right away. This
is because right after the window is resized, which ends up
calling SetPopupPosition with aIsMove == false, SetPopupPosition
is called again from CheckForAnchorChange with aIsMove set to
true. There could be other solutions to this particular problem,
but since the aIsMove boolean is intended to limit the visual
noise when moving a window between screens, it seemed appropriate
for it to only prevent sliding or flipping if the panel isn't
already slid or flipped.

There was another issue affecting specifically the arrow, where
the logic for notifying observers of a positioning change in the
panel doesn't account for changes only to the position of the
anchor rect. This change adds tracking of that and sets aNotify
to true when called from ReflowFinished, since this is where
the position of the anchor element relative to the window can
need to change, even when the screen position of the panel rect
doesn't change.

MozReview-Commit-ID: Lpfokwkgl33

--HG--
extra : rebase_source : b05adc0b3f876196ff45499f0d70533f78cafb0e
This commit is contained in:
Doug Thayer 2017-06-09 10:49:53 -07:00
parent 0fbeea4132
commit bf49dba88f
2 changed files with 19 additions and 4 deletions

View File

@ -118,6 +118,7 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsStyleContext* aContext)
, mInContentShell(true)
, mIsMenuLocked(false)
, mMouseTransparent(false)
, mIsOffset(false)
, mHFlip(false)
, mVFlip(false)
, mAnchorType(MenuPopupAnchorType_Node)
@ -601,7 +602,7 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
bool
nsMenuPopupFrame::ReflowFinished()
{
SetPopupPosition(mReflowCallbackData.mAnchor, false, mReflowCallbackData.mSizedToPopup, false);
SetPopupPosition(mReflowCallbackData.mAnchor, false, mReflowCallbackData.mSizedToPopup, true);
mReflowCallbackData.Clear();
@ -1558,10 +1559,14 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
#endif // #ifdef XP_MACOSX
}
// If a panel is being moved or has flip="none", don't constrain or flip it. But always do this for
nscoord oldAlignmentOffset = mAlignmentOffset;
// If a panel is being moved or has flip="none", don't constrain or flip it, in order to avoid
// visual noise when moving windows between screens. However, if a panel is already constrained
// or flipped (mIsOffset), then we want to continue to calculate this. Also, always do this for
// content shells, so that the popup doesn't extend outside the containing frame.
if (mInContentShell || (mFlip != FlipType_None &&
(!aIsMove || mPopupType != ePopupTypePanel))) {
(!aIsMove || mIsOffset || mPopupType != ePopupTypePanel))) {
int32_t appPerDev = presContext->AppUnitsPerDevPixel();
LayoutDeviceIntRect anchorRectDevPix =
LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
@ -1602,6 +1607,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
bool endAligned = IsDirectionRTL() ?
mPopupAlignment == POPUPALIGNMENT_TOPLEFT || mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT :
mPopupAlignment == POPUPALIGNMENT_TOPRIGHT || mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
nscoord preOffsetScreenPoint = screenPoint.x;
if (slideHorizontal) {
mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
screenRect.XMost(), &mAlignmentOffset);
@ -1611,9 +1617,11 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
margin.left, margin.right, offsetForContextMenu.x, hFlip,
endAligned, &mHFlip);
}
mIsOffset = preOffsetScreenPoint != screenPoint.x;
endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
preOffsetScreenPoint = screenPoint.y;
if (slideVertical) {
mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
screenRect.YMost(), &mAlignmentOffset);
@ -1623,6 +1631,7 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
margin.top, margin.bottom, offsetForContextMenu.y, vFlip,
endAligned, &mVFlip);
}
mIsOffset = mIsOffset || (preOffsetScreenPoint != screenPoint.y);
NS_ASSERTION(screenPoint.x >= screenRect.x && screenPoint.y >= screenRect.y &&
screenPoint.x + mRect.width <= screenRect.XMost() &&
@ -1669,7 +1678,8 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aS
// or size changed, dispatch a popuppositioned event if the popup wants it.
nsIntRect newRect(screenPoint.x, screenPoint.y, mRect.width, mRect.height);
if (mPopupState == ePopupPositioning ||
(mPopupState == ePopupShown && !newRect.IsEqualEdges(mUsedScreenRect))) {
(mPopupState == ePopupShown && !newRect.IsEqualEdges(mUsedScreenRect)) ||
(mPopupState == ePopupShown && oldAlignmentOffset != mAlignmentOffset)) {
mUsedScreenRect = newRect;
if (aNotify) {
nsXULPopupPositionedEvent::DispatchIfNeeded(mContent, false, false);

View File

@ -636,6 +636,11 @@ protected:
bool mIsMenuLocked; // Should events inside this menu be ignored?
bool mMouseTransparent; // True if this is a popup is transparent to mouse events
// True if this popup has been offset due to moving off / near the edge of the screen.
// (This is useful for ensuring that a move, which can't offset the popup, doesn't undo
// a previously set offset.)
bool mIsOffset;
// the flip modes that were used when the popup was opened
bool mHFlip;
bool mVFlip;