Bug 956162 - Implement @flip="none" for popups to allow them to appear off-screen without flipping or resizing. r=Enn
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Wed, 22 Jan 2014 20:09:03 +0000
changeset 164965 e61a79610e6894b0f9b4515f9a2b6bea4766f756
parent 164934 67d36c90a7ea74533ed7e029ebb41ce030edaed2
child 164966 822ce3170f0f8f457888c0ca8e9e47bce9ce4c9b
push id26068
push userkwierso@gmail.com
push dateFri, 24 Jan 2014 02:31:56 +0000
treeherdermozilla-central@9d650c07b547 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEnn
bugs956162
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 956162 - Implement @flip="none" for popups to allow them to appear off-screen without flipping or resizing. r=Enn [Australis]
browser/base/content/browser.xul
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsMenuPopupFrame.h
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -213,16 +213,17 @@
           <hbox id="UITourTooltipButtons" flex="1" align="end"/>
         </vbox>
       </hbox>
     </panel>
     <panel id="UITourHighlightContainer"
            hidden="true"
            noautofocus="true"
            noautohide="true"
+           flip="none"
            consumeoutsideclicks="false">
       <box id="UITourHighlight"></box>
     </panel>
 
     <panel id="social-share-panel"
            class="social-panel"
            type="arrow"
            orient="horizontal"
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -82,17 +82,17 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPr
   mPrefSize(-1, -1),
   mLastClientOffset(0, 0),
   mPopupType(ePopupTypePanel),
   mPopupState(ePopupClosed),
   mPopupAlignment(POPUPALIGNMENT_NONE),
   mPopupAnchor(POPUPALIGNMENT_NONE),
   mPosition(POPUPPOSITION_UNKNOWN),
   mConsumeRollupEvent(nsIPopupBoxObject::ROLLUP_DEFAULT),
-  mFlipBoth(false),
+  mFlip(FlipType_Default),
   mIsOpenChanged(false),
   mIsContextMenu(false),
   mAdjustOffsetForContextMenu(false),
   mGeneratedChildren(false),
   mMenuCanOverlapOSBar(false),
   mShouldAutoPosition(true),
   mInContentShell(true),
   mIsMenuLocked(false),
@@ -576,18 +576,23 @@ nsMenuPopupFrame::InitializePopup(nsICon
         position.Assign(aPosition);
       else
         mXPos = mYPos = 0;
     }
     else if (!aPosition.IsEmpty()) {
       position.Assign(aPosition);
     }
 
-    mFlipBoth = flip.EqualsLiteral("both");
-    mSlide = flip.EqualsLiteral("slide");
+    if (flip.EqualsLiteral("none")) {
+      mFlip = FlipType_None;
+    } else if (flip.EqualsLiteral("both")) {
+      mFlip = FlipType_Both;
+    } else if (flip.EqualsLiteral("slide")) {
+      mFlip = FlipType_Slide;
+    }
 
     position.CompressWhitespace();
     int32_t spaceIdx = position.FindChar(' ');
     // if there is a space in the position, assume it is the anchor and
     // alignment as two separate tokens.
     if (spaceIdx >= 0) {
       InitPositionFromAnchorAlign(Substring(position, 0, spaceIdx), Substring(position, spaceIdx + 1));
     }
@@ -680,36 +685,34 @@ nsMenuPopupFrame::InitializePopupAtScree
 {
   EnsureWidget();
 
   mPopupState = ePopupShowing;
   mAnchorContent = nullptr;
   mTriggerContent = aTriggerContent;
   mScreenXPos = aXPos;
   mScreenYPos = aYPos;
-  mFlipBoth = false;
-  mSlide = false;
+  mFlip = FlipType_Default;
   mPopupAnchor = POPUPALIGNMENT_NONE;
   mPopupAlignment = POPUPALIGNMENT_NONE;
   mIsContextMenu = aIsContextMenu;
   mAdjustOffsetForContextMenu = aIsContextMenu;
 }
 
 void
 nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
                                                  nsAString& aAnchor,
                                                  nsAString& aAlign,
                                                  int32_t aXPos, int32_t aYPos)
 {
   EnsureWidget();
 
   mPopupState = ePopupShowing;
   mAdjustOffsetForContextMenu = false;
-  mFlipBoth = false;
-  mSlide = false;
+  mFlip = FlipType_Default;
 
   // this popup opening function is provided for backwards compatibility
   // only. It accepts either coordinates or an anchor and alignment value
   // but doesn't use both together.
   if (aXPos == -1 && aYPos == -1) {
     mAnchorContent = aAnchorContent;
     mScreenXPos = -1;
     mScreenYPos = -1;
@@ -976,17 +979,17 @@ nsMenuPopupFrame::AdjustPositionForAncho
       break;
     case POPUPALIGNMENT_TOPCENTER:
     case POPUPALIGNMENT_BOTTOMCENTER:
       aHFlip = FlipStyle_Inside;
       aVFlip = FlipStyle_Outside;
       break;
     default:
     {
-      FlipStyle anchorEdge = mFlipBoth ? FlipStyle_Inside : FlipStyle_None;
+      FlipStyle anchorEdge = mFlip == FlipType_Both ? FlipStyle_Inside : FlipStyle_None;
       aHFlip = (popupAnchor == -popupAlign) ? FlipStyle_Outside : anchorEdge;
       if (((popupAnchor > 0) == (popupAlign > 0)) ||
           (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT))
         aVFlip = FlipStyle_Outside;
       else
         aVFlip = anchorEdge;
       break;
     }
@@ -1266,19 +1269,19 @@ 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 = FlipStyle_Outside;
   }
 
-  // If a panel is being moved, don't constrain or flip it. But always do this for
+  // If a panel is being moved or has flip="none", don't constrain or flip it. But always do this for
   // content shells, so that the popup doesn't extend outside the containing frame.
-  if (mInContentShell || !aIsMove || mPopupType != ePopupTypePanel) {
+  if (mInContentShell || (mFlip != FlipType_None && (!aIsMove || mPopupType != ePopupTypePanel))) {
     nsRect screenRect = GetConstraintRect(anchorRect, 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;
@@ -1298,17 +1301,17 @@ 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.
 
     // We might want to "slide" an arrow if the panel is of the correct type -
     // but we can only slide on one axis - the other axis must be "flipped or
     // resized" as normal.
     bool slideHorizontal = false, slideVertical = false;
-    if (mSlide) {
+    if (mFlip == FlipType_Slide) {
       int8_t position = GetAlignmentPosition();
       slideHorizontal = position >= POPUPPOSITION_BEFORESTART &&
                         position <= POPUPPOSITION_AFTEREND;
       slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
                       position <= POPUPPOSITION_ENDAFTER;
     }
 
     // Next, check if there is enough space to show the popup at full size when
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -60,16 +60,24 @@ enum nsPopupState {
 // a submenu would work. The entire popup is flipped to the opposite side
 // of the anchor.
 enum FlipStyle {
   FlipStyle_None = 0,
   FlipStyle_Outside = 1,
   FlipStyle_Inside = 2
 };
 
+// Values for the flip attribute
+enum FlipType {
+  FlipType_Default = 0,
+  FlipType_None = 1,    // don't try to flip or translate to stay onscreen
+  FlipType_Both = 2,    // flip in both directions
+  FlipType_Slide = 3    // allow the arrow to "slide" instead of resizing
+};
+
 // values are selected so that the direction can be flipped just by
 // changing the sign
 #define POPUPALIGNMENT_NONE 0
 #define POPUPALIGNMENT_TOPLEFT 1
 #define POPUPALIGNMENT_TOPRIGHT -1
 #define POPUPALIGNMENT_BOTTOMLEFT 2
 #define POPUPALIGNMENT_BOTTOMRIGHT -2
 
@@ -454,18 +462,17 @@ protected:
 
   // popup alignment relative to the anchor node
   int8_t mPopupAlignment;
   int8_t mPopupAnchor;
   int8_t mPosition;
 
   // One of nsIPopupBoxObject::ROLLUP_DEFAULT/ROLLUP_CONSUME/ROLLUP_NO_CONSUME
   int8_t mConsumeRollupEvent;
-  bool mFlipBoth; // flip in both directions
-  bool mSlide; // allow the arrow to "slide" instead of resizing
+  FlipType mFlip; // Whether to flip
 
   bool mIsOpenChanged; // true if the open state changed since the last layout
   bool mIsContextMenu; // true for context menus
   // true if we need to offset the popup to ensure it's not under the mouse
   bool mAdjustOffsetForContextMenu;
   bool mGeneratedChildren; // true if the contents have been created
 
   bool mMenuCanOverlapOSBar;    // can we appear over the taskbar/menubar?