LCOV - code coverage report
Current view: top level - layout/xul - nsMenuPopupFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 98 1220 8.0 %
Date: 2017-07-14 16:53:18 Functions: 12 68 17.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 sw=2 et tw=78: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsMenuPopupFrame.h"
       8             : #include "nsGkAtoms.h"
       9             : #include "nsIContent.h"
      10             : #include "nsIAtom.h"
      11             : #include "nsPresContext.h"
      12             : #include "nsStyleContext.h"
      13             : #include "nsCSSRendering.h"
      14             : #include "nsNameSpaceManager.h"
      15             : #include "nsViewManager.h"
      16             : #include "nsWidgetsCID.h"
      17             : #include "nsMenuFrame.h"
      18             : #include "nsMenuBarFrame.h"
      19             : #include "nsPopupSetFrame.h"
      20             : #include "nsPIDOMWindow.h"
      21             : #include "nsIDOMEvent.h"
      22             : #include "nsIDOMKeyEvent.h"
      23             : #include "nsIDOMScreen.h"
      24             : #include "nsIDOMXULMenuListElement.h"
      25             : #include "nsIPresShell.h"
      26             : #include "nsFrameManager.h"
      27             : #include "nsIDocument.h"
      28             : #include "nsRect.h"
      29             : #include "nsIComponentManager.h"
      30             : #include "nsBoxLayoutState.h"
      31             : #include "nsIScrollableFrame.h"
      32             : #include "nsIRootBox.h"
      33             : #include "nsIDocShell.h"
      34             : #include "nsReadableUtils.h"
      35             : #include "nsUnicharUtils.h"
      36             : #include "nsLayoutUtils.h"
      37             : #include "nsContentUtils.h"
      38             : #include "nsCSSFrameConstructor.h"
      39             : #include "nsPIWindowRoot.h"
      40             : #include "nsIReflowCallback.h"
      41             : #include "nsBindingManager.h"
      42             : #include "nsIDocShellTreeOwner.h"
      43             : #include "nsIBaseWindow.h"
      44             : #include "nsISound.h"
      45             : #include "nsIScreenManager.h"
      46             : #include "nsIServiceManager.h"
      47             : #include "nsThemeConstants.h"
      48             : #include "nsTransitionManager.h"
      49             : #include "nsDisplayList.h"
      50             : #include "nsIDOMXULSelectCntrlItemEl.h"
      51             : #include "mozilla/EventDispatcher.h"
      52             : #include "mozilla/EventStateManager.h"
      53             : #include "mozilla/EventStates.h"
      54             : #include "mozilla/Preferences.h"
      55             : #include "mozilla/LookAndFeel.h"
      56             : #include "mozilla/MouseEvents.h"
      57             : #include "mozilla/dom/Element.h"
      58             : #include "mozilla/dom/Event.h"
      59             : #include "mozilla/dom/PopupBoxObject.h"
      60             : #include <algorithm>
      61             : 
      62             : using namespace mozilla;
      63             : using mozilla::dom::PopupBoxObject;
      64             : 
      65             : int8_t nsMenuPopupFrame::sDefaultLevelIsTop = -1;
      66             : 
      67             : DOMTimeStamp nsMenuPopupFrame::sLastKeyTime = 0;
      68             : 
      69             : // XXX, kyle.yuan@sun.com, there are 4 definitions for the same purpose:
      70             : //  nsMenuPopupFrame.h, nsListControlFrame.cpp, listbox.xml, tree.xml
      71             : //  need to find a good place to put them together.
      72             : //  if someone changes one, please also change the other.
      73             : uint32_t nsMenuPopupFrame::sTimeoutOfIncrementalSearch = 1000;
      74             : 
      75             : const char* kPrefIncrementalSearchTimeout =
      76             :   "ui.menu.incremental_search.timeout";
      77             : 
      78             : // NS_NewMenuPopupFrame
      79             : //
      80             : // Wrapper for creating a new menu popup container
      81             : //
      82             : nsIFrame*
      83          44 : NS_NewMenuPopupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
      84             : {
      85          44 :   return new (aPresShell) nsMenuPopupFrame(aContext);
      86             : }
      87             : 
      88          44 : NS_IMPL_FRAMEARENA_HELPERS(nsMenuPopupFrame)
      89             : 
      90         166 : NS_QUERYFRAME_HEAD(nsMenuPopupFrame)
      91          19 :   NS_QUERYFRAME_ENTRY(nsMenuPopupFrame)
      92         147 : NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
      93             : 
      94             : //
      95             : // nsMenuPopupFrame ctor
      96             : //
      97          44 : nsMenuPopupFrame::nsMenuPopupFrame(nsStyleContext* aContext)
      98             :   : nsBoxFrame(aContext, kClassID)
      99             :   , mCurrentMenu(nullptr)
     100             :   , mView(nullptr)
     101             :   , mPrefSize(-1, -1)
     102             :   , mXPos(0)
     103             :   , mYPos(0)
     104             :   , mLastClientOffset(0, 0)
     105             :   , mPopupType(ePopupTypePanel)
     106             :   , mPopupState(ePopupClosed)
     107             :   , mPopupAlignment(POPUPALIGNMENT_NONE)
     108             :   , mPopupAnchor(POPUPALIGNMENT_NONE)
     109             :   , mPosition(POPUPPOSITION_UNKNOWN)
     110             :   , mConsumeRollupEvent(PopupBoxObject::ROLLUP_DEFAULT)
     111             :   , mFlip(FlipType_Default)
     112             :   , mIsOpenChanged(false)
     113             :   , mIsContextMenu(false)
     114             :   , mAdjustOffsetForContextMenu(false)
     115             :   , mGeneratedChildren(false)
     116             :   , mMenuCanOverlapOSBar(false)
     117             :   , mShouldAutoPosition(true)
     118             :   , mInContentShell(true)
     119             :   , mIsMenuLocked(false)
     120             :   , mMouseTransparent(false)
     121             :   , mIsOffset(false)
     122             :   , mHFlip(false)
     123             :   , mVFlip(false)
     124          44 :   , mAnchorType(MenuPopupAnchorType_Node)
     125             : {
     126             :   // the preference name is backwards here. True means that the 'top' level is
     127             :   // the default, and false means that the 'parent' level is the default.
     128          44 :   if (sDefaultLevelIsTop >= 0)
     129          43 :     return;
     130           1 :   sDefaultLevelIsTop =
     131           1 :     Preferences::GetBool("ui.panel.default_level_parent", false);
     132             :   Preferences::AddUintVarCache(&sTimeoutOfIncrementalSearch,
     133           1 :                                kPrefIncrementalSearchTimeout, 1000);
     134             : } // ctor
     135             : 
     136             : void
     137          44 : nsMenuPopupFrame::Init(nsIContent*       aContent,
     138             :                        nsContainerFrame* aParent,
     139             :                        nsIFrame*         aPrevInFlow)
     140             : {
     141          44 :   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
     142             : 
     143             :   // lookup if we're allowed to overlap the OS bar (menubar/taskbar) from the
     144             :   // look&feel object
     145          44 :   mMenuCanOverlapOSBar =
     146          44 :     LookAndFeel::GetInt(LookAndFeel::eIntID_MenusCanOverlapOSBar) != 0;
     147             : 
     148          44 :   CreatePopupView();
     149             : 
     150             :   // XXX Hack. The popup's view should float above all other views,
     151             :   // so we use the nsView::SetFloating() to tell the view manager
     152             :   // about that constraint.
     153          44 :   nsView* ourView = GetView();
     154          44 :   nsViewManager* viewManager = ourView->GetViewManager();
     155          44 :   viewManager->SetViewFloating(ourView, true);
     156             : 
     157          44 :   mPopupType = ePopupTypePanel;
     158          44 :   nsIDocument* doc = aContent->OwnerDoc();
     159             :   int32_t namespaceID;
     160          88 :   nsCOMPtr<nsIAtom> tag = doc->BindingManager()->ResolveTag(aContent, &namespaceID);
     161          44 :   if (namespaceID == kNameSpaceID_XUL) {
     162          44 :     if (tag == nsGkAtoms::menupopup || tag == nsGkAtoms::popup)
     163          34 :       mPopupType = ePopupTypeMenu;
     164          10 :     else if (tag == nsGkAtoms::tooltip)
     165           9 :       mPopupType = ePopupTypeTooltip;
     166             :   }
     167             : 
     168          88 :   nsCOMPtr<nsIDocShellTreeItem> dsti = PresContext()->GetDocShell();
     169          44 :   if (dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
     170          44 :     mInContentShell = false;
     171             :   }
     172             : 
     173             :   // To improve performance, create the widget for the popup only if it is not
     174             :   // a leaf. Leaf popups such as menus will create their widgets later when
     175             :   // the popup opens.
     176          44 :   if (!IsLeaf() && !ourView->HasWidget()) {
     177           0 :     CreateWidgetForView(ourView);
     178             :   }
     179             : 
     180          53 :   if (aContent->NodeInfo()->Equals(nsGkAtoms::tooltip, kNameSpaceID_XUL) &&
     181           9 :       aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::_default,
     182             :                             nsGkAtoms::_true, eIgnoreCase)) {
     183             :     nsIRootBox* rootBox =
     184           1 :       nsIRootBox::GetRootBox(PresContext()->GetPresShell());
     185           1 :     if (rootBox) {
     186           1 :       rootBox->SetDefaultTooltip(aContent);
     187             :     }
     188             :   }
     189             : 
     190          44 :   AddStateBits(NS_FRAME_IN_POPUP);
     191          44 : }
     192             : 
     193             : bool
     194           0 : nsMenuPopupFrame::HasRemoteContent() const
     195             : {
     196           0 :   return (!mInContentShell && mPopupType == ePopupTypePanel &&
     197           0 :           mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
     198           0 :                                 nsGkAtoms::_true, eIgnoreCase));
     199             : }
     200             : 
     201             : bool
     202           0 : nsMenuPopupFrame::IsNoAutoHide() const
     203             : {
     204             :   // Panels with noautohide="true" don't hide when the mouse is clicked
     205             :   // outside of them, or when another application is made active. Non-autohide
     206             :   // panels cannot be used in content windows.
     207           0 :   return (!mInContentShell && mPopupType == ePopupTypePanel &&
     208           0 :            mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautohide,
     209           0 :                                  nsGkAtoms::_true, eIgnoreCase));
     210             : }
     211             : 
     212             : nsPopupLevel
     213           0 : nsMenuPopupFrame::PopupLevel(bool aIsNoAutoHide) const
     214             : {
     215             :   // The popup level is determined as follows, in this order:
     216             :   //   1. non-panels (menus and tooltips) are always topmost
     217             :   //   2. any specified level attribute
     218             :   //   3. if a titlebar attribute is set, use the 'floating' level
     219             :   //   4. if this is a noautohide panel, use the 'parent' level
     220             :   //   5. use the platform-specific default level
     221             : 
     222             :   // If this is not a panel, this is always a top-most popup.
     223           0 :   if (mPopupType != ePopupTypePanel)
     224           0 :     return ePopupLevelTop;
     225             : 
     226             :   // If the level attribute has been set, use that.
     227             :   static nsIContent::AttrValuesArray strings[] =
     228             :     {&nsGkAtoms::top, &nsGkAtoms::parent, &nsGkAtoms::floating, nullptr};
     229           0 :   switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::level,
     230           0 :                                     strings, eCaseMatters)) {
     231             :     case 0:
     232           0 :       return ePopupLevelTop;
     233             :     case 1:
     234           0 :       return ePopupLevelParent;
     235             :     case 2:
     236           0 :       return ePopupLevelFloating;
     237             :   }
     238             : 
     239             :   // Panels with titlebars most likely want to be floating popups.
     240           0 :   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::titlebar))
     241           0 :     return ePopupLevelFloating;
     242             : 
     243             :   // If this panel is a noautohide panel, the default is the parent level.
     244           0 :   if (aIsNoAutoHide)
     245           0 :     return ePopupLevelParent;
     246             : 
     247             :   // Otherwise, the result depends on the platform.
     248           0 :   return sDefaultLevelIsTop ? ePopupLevelTop : ePopupLevelParent;
     249             : }
     250             : 
     251             : void
     252           0 : nsMenuPopupFrame::EnsureWidget(bool aRecreate)
     253             : {
     254           0 :   nsView* ourView = GetView();
     255           0 :   if (aRecreate) {
     256           0 :     ourView->DestroyWidget();
     257             :   }
     258           0 :   if (!ourView->HasWidget()) {
     259           0 :     NS_ASSERTION(!mGeneratedChildren && !PrincipalChildList().FirstChild(),
     260             :                  "Creating widget for MenuPopupFrame with children");
     261           0 :     CreateWidgetForView(ourView);
     262             :   }
     263           0 : }
     264             : 
     265             : nsresult
     266           0 : nsMenuPopupFrame::CreateWidgetForView(nsView* aView)
     267             : {
     268             :   // Create a widget for ourselves.
     269           0 :   nsWidgetInitData widgetData;
     270           0 :   widgetData.mWindowType = eWindowType_popup;
     271           0 :   widgetData.mBorderStyle = eBorderStyle_default;
     272           0 :   widgetData.clipSiblings = true;
     273           0 :   widgetData.mPopupHint = mPopupType;
     274           0 :   widgetData.mNoAutoHide = IsNoAutoHide();
     275             : 
     276           0 :   if (!mInContentShell) {
     277             :     // A drag popup may be used for non-static translucent drag feedback
     278           0 :     if (mPopupType == ePopupTypePanel &&
     279           0 :         mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
     280             :                               nsGkAtoms::drag, eIgnoreCase)) {
     281           0 :       widgetData.mIsDragPopup = true;
     282             :     }
     283             : 
     284             :     // If mousethrough="always" is set directly on the popup, then the widget
     285             :     // should ignore mouse events, passing them through to the content behind.
     286           0 :     mMouseTransparent = GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS;
     287           0 :     widgetData.mMouseTransparent = mMouseTransparent;
     288             :   }
     289             : 
     290           0 :   nsAutoString title;
     291           0 :   if (mContent && widgetData.mNoAutoHide) {
     292           0 :     if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::titlebar,
     293             :                               nsGkAtoms::normal, eCaseMatters)) {
     294           0 :       widgetData.mBorderStyle = eBorderStyle_title;
     295             : 
     296           0 :       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, title);
     297             : 
     298           0 :       if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::close,
     299             :                                 nsGkAtoms::_true, eCaseMatters)) {
     300           0 :         widgetData.mBorderStyle =
     301           0 :           static_cast<enum nsBorderStyle>(widgetData.mBorderStyle | eBorderStyle_close);
     302             :       }
     303             :     }
     304             :   }
     305             : 
     306           0 :   bool remote = HasRemoteContent();
     307             : 
     308           0 :   nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(this, this);
     309             : #ifdef MOZ_WIDGET_GTK
     310           0 :   if (remote) {
     311             :     // Paradoxically, on Linux, setting the transparency mode to opaque will
     312             :     // give us proper transparency for composited popups. The shape-mask-based
     313             :     // pseudo-transparency that we use otherwise will render transparent areas
     314             :     // as opaque black when compositing is enabled.
     315             :     // See bug 630346.
     316           0 :     mode = eTransparencyOpaque;
     317             :   }
     318             : #endif
     319             : 
     320           0 :   nsIContent* parentContent = GetContent()->GetParent();
     321           0 :   nsIAtom *tag = nullptr;
     322           0 :   if (parentContent && parentContent->IsXULElement())
     323           0 :     tag = parentContent->NodeInfo()->NameAtom();
     324           0 :   widgetData.mHasRemoteContent = remote;
     325           0 :   widgetData.mSupportTranslucency = mode == eTransparencyTransparent;
     326           0 :   widgetData.mDropShadow = !(mode == eTransparencyTransparent || tag == nsGkAtoms::menulist);
     327           0 :   widgetData.mPopupLevel = PopupLevel(widgetData.mNoAutoHide);
     328             : 
     329             :   // panels which have a parent level need a parent widget. This allows them to
     330             :   // always appear in front of the parent window but behind other windows that
     331             :   // should be in front of it.
     332           0 :   nsCOMPtr<nsIWidget> parentWidget;
     333           0 :   if (widgetData.mPopupLevel != ePopupLevelTop) {
     334           0 :     nsCOMPtr<nsIDocShellTreeItem> dsti = PresContext()->GetDocShell();
     335           0 :     if (!dsti)
     336           0 :       return NS_ERROR_FAILURE;
     337             : 
     338           0 :     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
     339           0 :     dsti->GetTreeOwner(getter_AddRefs(treeOwner));
     340           0 :     if (!treeOwner) return NS_ERROR_FAILURE;
     341             : 
     342           0 :     nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(treeOwner));
     343           0 :     if (baseWindow)
     344           0 :       baseWindow->GetMainWidget(getter_AddRefs(parentWidget));
     345             :   }
     346             : 
     347           0 :   nsresult rv = aView->CreateWidgetForPopup(&widgetData, parentWidget,
     348           0 :                                             true, true);
     349           0 :   if (NS_FAILED(rv)) {
     350           0 :     return rv;
     351             :   }
     352             : 
     353           0 :   nsIWidget* widget = aView->GetWidget();
     354           0 :   widget->SetTransparencyMode(mode);
     355           0 :   widget->SetWindowShadowStyle(GetShadowStyle());
     356             : 
     357             :   // most popups don't have a title so avoid setting the title if there isn't one
     358           0 :   if (!title.IsEmpty()) {
     359           0 :     widget->SetTitle(title);
     360             :   }
     361             : 
     362           0 :   return NS_OK;
     363             : }
     364             : 
     365             : uint8_t
     366           0 : nsMenuPopupFrame::GetShadowStyle()
     367             : {
     368           0 :   uint8_t shadow = StyleUIReset()->mWindowShadow;
     369           0 :   if (shadow != NS_STYLE_WINDOW_SHADOW_DEFAULT)
     370           0 :     return shadow;
     371             : 
     372           0 :   switch (StyleDisplay()->mAppearance) {
     373             :     case NS_THEME_TOOLTIP:
     374           0 :       return NS_STYLE_WINDOW_SHADOW_TOOLTIP;
     375             :     case NS_THEME_MENUPOPUP:
     376           0 :       return NS_STYLE_WINDOW_SHADOW_MENU;
     377             :   }
     378           0 :   return NS_STYLE_WINDOW_SHADOW_DEFAULT;
     379             : }
     380             : 
     381           0 : NS_IMETHODIMP nsXULPopupShownEvent::Run()
     382             : {
     383           0 :   nsMenuPopupFrame* popup = do_QueryFrame(mPopup->GetPrimaryFrame());
     384             :   // Set the state to visible if the popup is still open.
     385           0 :   if (popup && popup->IsOpen()) {
     386           0 :     popup->SetPopupState(ePopupShown);
     387             :   }
     388             : 
     389             :   WidgetMouseEvent event(true, eXULPopupShown, nullptr,
     390           0 :                          WidgetMouseEvent::eReal);
     391           0 :   return EventDispatcher::Dispatch(mPopup, mPresContext, &event);
     392             : }
     393             : 
     394           0 : NS_IMETHODIMP nsXULPopupShownEvent::HandleEvent(nsIDOMEvent* aEvent)
     395             : {
     396           0 :   nsMenuPopupFrame* popup = do_QueryFrame(mPopup->GetPrimaryFrame());
     397           0 :   nsCOMPtr<nsIDOMEventTarget> eventTarget;
     398           0 :   aEvent->GetTarget(getter_AddRefs(eventTarget));
     399             :   // Ignore events not targeted at the popup itself (ie targeted at
     400             :   // descendants):
     401           0 :   if (!SameCOMIdentity(mPopup, eventTarget)) {
     402           0 :     return NS_OK;
     403             :   }
     404           0 :   if (popup) {
     405             :     // ResetPopupShownDispatcher will delete the reference to this, so keep
     406             :     // another one until Run is finished.
     407           0 :     RefPtr<nsXULPopupShownEvent> event = this;
     408             :     // Only call Run if it the dispatcher was assigned. This avoids calling the
     409             :     // Run method if the transitionend event fires multiple times.
     410           0 :     if (popup->ClearPopupShownDispatcher()) {
     411           0 :       return Run();
     412             :     }
     413             :   }
     414             : 
     415           0 :   CancelListener();
     416           0 :   return NS_OK;
     417             : }
     418             : 
     419           0 : void nsXULPopupShownEvent::CancelListener()
     420             : {
     421           0 :   mPopup->RemoveSystemEventListener(NS_LITERAL_STRING("transitionend"), this, false);
     422           0 : }
     423             : 
     424           0 : NS_IMPL_ISUPPORTS_INHERITED(nsXULPopupShownEvent, Runnable, nsIDOMEventListener);
     425             : 
     426             : void
     427          44 : nsMenuPopupFrame::SetInitialChildList(ChildListID  aListID,
     428             :                                       nsFrameList& aChildList)
     429             : {
     430             :   // unless the list is empty, indicate that children have been generated.
     431          44 :   if (aListID == kPrincipalList && aChildList.NotEmpty()) {
     432           0 :     mGeneratedChildren = true;
     433             :   }
     434          44 :   nsBoxFrame::SetInitialChildList(aListID, aChildList);
     435          44 : }
     436             : 
     437             : bool
     438          88 : nsMenuPopupFrame::IsLeafDynamic() const
     439             : {
     440          88 :   if (mGeneratedChildren)
     441           0 :     return false;
     442             : 
     443          88 :   if (mPopupType != ePopupTypeMenu) {
     444             :     // any panel with a type attribute, such as the autocomplete popup,
     445             :     // is always generated right away.
     446          20 :     return !mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::type);
     447             :   }
     448             : 
     449             :   // menu popups generate their child frames lazily only when opened, so
     450             :   // behave like a leaf frame. However, generate child frames normally if
     451             :   // the parent menu has a sizetopopup attribute. In this case the size of
     452             :   // the parent menu is dependent on the size of the popup, so the frames
     453             :   // need to exist in order to calculate this size.
     454          68 :   nsIContent* parentContent = mContent->GetParent();
     455         136 :   return (parentContent &&
     456         136 :           !parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup));
     457             : }
     458             : 
     459             : void
     460           0 : nsMenuPopupFrame::UpdateWidgetProperties()
     461             : {
     462           0 :   if (nsIWidget* widget = GetWidget()) {
     463           0 :     widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
     464           0 :     widget->SetWindowTransform(ComputeWidgetTransform());
     465             :   }
     466           0 : }
     467             : 
     468             : void
     469         105 : nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
     470             :                               nsIFrame* aAnchor, bool aSizedToPopup)
     471             : {
     472         105 :   if (!mGeneratedChildren)
     473         210 :     return;
     474             : 
     475           0 :   SchedulePaint();
     476             : 
     477           0 :   bool shouldPosition = true;
     478           0 :   bool isOpen = IsOpen();
     479           0 :   if (!isOpen) {
     480             :     // if the popup is not open, only do layout while showing or if the menu
     481             :     // is sized to the popup
     482           0 :     shouldPosition = (mPopupState == ePopupShowing || mPopupState == ePopupPositioning);
     483           0 :     if (!shouldPosition && !aSizedToPopup) {
     484           0 :       RemoveStateBits(NS_FRAME_FIRST_REFLOW);
     485           0 :       return;
     486             :     }
     487             :   }
     488             : 
     489             :   // if the popup has just been opened, make sure the scrolled window is at 0,0
     490             :   // Don't scroll menulists as they will scroll to their selected item on their own.
     491           0 :   if (mIsOpenChanged && !IsMenuList()) {
     492           0 :     nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
     493           0 :     if (scrollframe) {
     494           0 :       AutoWeakFrame weakFrame(this);
     495           0 :       scrollframe->ScrollTo(nsPoint(0,0), nsIScrollableFrame::INSTANT);
     496           0 :       if (!weakFrame.IsAlive()) {
     497           0 :         return;
     498             :       }
     499             :     }
     500             :   }
     501             : 
     502             :   // get the preferred, minimum and maximum size. If the menu is sized to the
     503             :   // popup, then the popup's width is the menu's width.
     504           0 :   nsSize prefSize = GetXULPrefSize(aState);
     505           0 :   nsSize minSize = GetXULMinSize(aState);
     506           0 :   nsSize maxSize = GetXULMaxSize(aState);
     507             : 
     508           0 :   if (aSizedToPopup) {
     509           0 :     prefSize.width = aParentMenu->GetRect().width;
     510             :   }
     511           0 :   prefSize = BoundsCheck(minSize, prefSize, maxSize);
     512             : 
     513             :   // if the size changed then set the bounds to be the preferred size
     514           0 :   bool sizeChanged = (mPrefSize != prefSize);
     515           0 :   if (sizeChanged) {
     516           0 :     SetXULBounds(aState, nsRect(0, 0, prefSize.width, prefSize.height), false);
     517           0 :     mPrefSize = prefSize;
     518             :   }
     519             : 
     520           0 :   bool needCallback = false;
     521           0 :   if (shouldPosition) {
     522           0 :     SetPopupPosition(aAnchor, false, aSizedToPopup, mPopupState == ePopupPositioning);
     523           0 :     needCallback = true;
     524             :   }
     525             : 
     526           0 :   nsRect bounds(GetRect());
     527           0 :   XULLayout(aState);
     528             : 
     529             :   // if the width or height changed, readjust the popup position. This is a
     530             :   // special case for tooltips where the preferred height doesn't include the
     531             :   // real height for its inline element, but does once it is laid out.
     532             :   // This is bug 228673 which doesn't have a simple fix.
     533           0 :   bool rePosition = shouldPosition && (mPosition == POPUPPOSITION_SELECTION);
     534           0 :   if (!aParentMenu) {
     535           0 :     nsSize newsize = GetSize();
     536           0 :     if (newsize.width > bounds.width || newsize.height > bounds.height) {
     537             :       // the size after layout was larger than the preferred size,
     538             :       // so set the preferred size accordingly
     539           0 :       mPrefSize = newsize;
     540           0 :       if (isOpen) {
     541           0 :         rePosition = true;
     542           0 :         needCallback = true;
     543             :       }
     544             :     }
     545             :   }
     546             : 
     547           0 :   if (rePosition) {
     548           0 :     SetPopupPosition(aAnchor, false, aSizedToPopup, false);
     549             :   }
     550             : 
     551           0 :   nsPresContext* pc = PresContext();
     552           0 :   nsView* view = GetView();
     553             : 
     554           0 :   if (sizeChanged) {
     555             :     // If the size of the popup changed, apply any size constraints.
     556           0 :     nsIWidget* widget = view->GetWidget();
     557           0 :     if (widget) {
     558           0 :       SetSizeConstraints(pc, widget, minSize, maxSize);
     559             :     }
     560             :   }
     561             : 
     562           0 :   if (isOpen) {
     563           0 :     nsViewManager* viewManager = view->GetViewManager();
     564           0 :     nsRect rect = GetRect();
     565           0 :     rect.x = rect.y = 0;
     566           0 :     viewManager->ResizeView(view, rect);
     567             : 
     568           0 :     if (mPopupState == ePopupOpening) {
     569           0 :       mPopupState = ePopupVisible;
     570             :     }
     571             : 
     572           0 :     viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
     573           0 :     SyncFrameViewProperties(view);
     574             :   }
     575             : 
     576             :   // finally, if the popup just opened, send a popupshown event
     577           0 :   if (mIsOpenChanged) {
     578           0 :     mIsOpenChanged = false;
     579             : 
     580             :     // Make sure the current selection in a menulist is visible.
     581           0 :     if (IsMenuList() && mCurrentMenu) {
     582           0 :       EnsureMenuItemIsVisible(mCurrentMenu);
     583             :     }
     584             : 
     585             : #ifndef MOZ_WIDGET_GTK
     586             :     // If the animate attribute is set to open, check for a transition and wait
     587             :     // for it to finish before firing the popupshown event.
     588             :     if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::animate,
     589             :                               nsGkAtoms::open, eCaseMatters) &&
     590             :         nsLayoutUtils::HasCurrentTransitions(this)) {
     591             :       mPopupShownDispatcher = new nsXULPopupShownEvent(mContent, pc);
     592             :       mContent->AddSystemEventListener(NS_LITERAL_STRING("transitionend"),
     593             :                                        mPopupShownDispatcher, false, false);
     594             :       return;
     595             :     }
     596             : #endif
     597             : 
     598             :     // If there are no transitions, fire the popupshown event right away.
     599           0 :     nsCOMPtr<nsIRunnable> event = new nsXULPopupShownEvent(GetContent(), pc);
     600           0 :     mContent->OwnerDoc()->Dispatch("nsXULPopupShownEvent",
     601             :                                    TaskCategory::Other,
     602           0 :                                    event.forget());
     603             :   }
     604             : 
     605           0 :   if (needCallback && !mReflowCallbackData.mPosted) {
     606           0 :     pc->PresShell()->PostReflowCallback(this);
     607           0 :     mReflowCallbackData.MarkPosted(aAnchor, aSizedToPopup);
     608             :   }
     609             : }
     610             : 
     611             : bool
     612           0 : nsMenuPopupFrame::ReflowFinished()
     613             : {
     614           0 :   SetPopupPosition(mReflowCallbackData.mAnchor, false, mReflowCallbackData.mSizedToPopup, true);
     615             : 
     616           0 :   mReflowCallbackData.Clear();
     617             : 
     618           0 :   return false;
     619             : }
     620             : 
     621             : void
     622           0 : nsMenuPopupFrame::ReflowCallbackCanceled()
     623             : {
     624           0 :   mReflowCallbackData.Clear();
     625           0 : }
     626             : 
     627             : bool
     628           0 : nsMenuPopupFrame::IsMenuList()
     629             : {
     630           0 :   nsIFrame* parentMenu = GetParent();
     631           0 :   if (!parentMenu) {
     632           0 :     return false;
     633             :   }
     634             : 
     635           0 :   nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(parentMenu->GetContent());
     636           0 :   return menulist != nullptr;
     637             : }
     638             : 
     639             : nsIContent*
     640           1 : nsMenuPopupFrame::GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame)
     641             : {
     642           1 :   while (aMenuPopupFrame) {
     643           1 :     if (aMenuPopupFrame->mTriggerContent)
     644           0 :       return aMenuPopupFrame->mTriggerContent;
     645             : 
     646             :     // check up the menu hierarchy until a popup with a trigger node is found
     647           1 :     nsMenuFrame* menuFrame = do_QueryFrame(aMenuPopupFrame->GetParent());
     648           1 :     if (!menuFrame)
     649           1 :       break;
     650             : 
     651           0 :     nsMenuParent* parentPopup = menuFrame->GetMenuParent();
     652           0 :     if (!parentPopup || !parentPopup->IsMenu())
     653           0 :       break;
     654             : 
     655           0 :     aMenuPopupFrame = static_cast<nsMenuPopupFrame *>(parentPopup);
     656             :   }
     657             : 
     658           1 :   return nullptr;
     659             : }
     660             : 
     661             : void
     662           0 : nsMenuPopupFrame::InitPositionFromAnchorAlign(const nsAString& aAnchor,
     663             :                                               const nsAString& aAlign)
     664             : {
     665           0 :   mTriggerContent = nullptr;
     666             : 
     667           0 :   if (aAnchor.EqualsLiteral("topleft"))
     668           0 :     mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
     669           0 :   else if (aAnchor.EqualsLiteral("topright"))
     670           0 :     mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
     671           0 :   else if (aAnchor.EqualsLiteral("bottomleft"))
     672           0 :     mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
     673           0 :   else if (aAnchor.EqualsLiteral("bottomright"))
     674           0 :     mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
     675           0 :   else if (aAnchor.EqualsLiteral("leftcenter"))
     676           0 :     mPopupAnchor = POPUPALIGNMENT_LEFTCENTER;
     677           0 :   else if (aAnchor.EqualsLiteral("rightcenter"))
     678           0 :     mPopupAnchor = POPUPALIGNMENT_RIGHTCENTER;
     679           0 :   else if (aAnchor.EqualsLiteral("topcenter"))
     680           0 :     mPopupAnchor = POPUPALIGNMENT_TOPCENTER;
     681           0 :   else if (aAnchor.EqualsLiteral("bottomcenter"))
     682           0 :     mPopupAnchor = POPUPALIGNMENT_BOTTOMCENTER;
     683             :   else
     684           0 :     mPopupAnchor = POPUPALIGNMENT_NONE;
     685             : 
     686           0 :   if (aAlign.EqualsLiteral("topleft"))
     687           0 :     mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
     688           0 :   else if (aAlign.EqualsLiteral("topright"))
     689           0 :     mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
     690           0 :   else if (aAlign.EqualsLiteral("bottomleft"))
     691           0 :     mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
     692           0 :   else if (aAlign.EqualsLiteral("bottomright"))
     693           0 :     mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
     694             :   else
     695           0 :     mPopupAlignment = POPUPALIGNMENT_NONE;
     696             : 
     697           0 :   mPosition = POPUPPOSITION_UNKNOWN;
     698           0 : }
     699             : 
     700             : void
     701           0 : nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent,
     702             :                                   nsIContent* aTriggerContent,
     703             :                                   const nsAString& aPosition,
     704             :                                   int32_t aXPos, int32_t aYPos,
     705             :                                   MenuPopupAnchorType aAnchorType,
     706             :                                   bool aAttributesOverride)
     707             : {
     708           0 :   EnsureWidget();
     709             : 
     710           0 :   mPopupState = ePopupShowing;
     711           0 :   mAnchorContent = aAnchorContent;
     712           0 :   mTriggerContent = aTriggerContent;
     713           0 :   mXPos = aXPos;
     714           0 :   mYPos = aYPos;
     715           0 :   mAdjustOffsetForContextMenu = false;
     716           0 :   mVFlip = false;
     717           0 :   mHFlip = false;
     718           0 :   mAlignmentOffset = 0;
     719             : 
     720           0 :   mAnchorType = aAnchorType;
     721             : 
     722             :   // if aAttributesOverride is true, then the popupanchor, popupalign and
     723             :   // position attributes on the <popup> override those values passed in.
     724             :   // If false, those attributes are only used if the values passed in are empty
     725           0 :   if (aAnchorContent || aAnchorType == MenuPopupAnchorType_Rect) {
     726           0 :     nsAutoString anchor, align, position, flip;
     727           0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupanchor, anchor);
     728           0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::popupalign, align);
     729           0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::position, position);
     730           0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::flip, flip);
     731             : 
     732           0 :     if (aAttributesOverride) {
     733             :       // if the attributes are set, clear the offset position. Otherwise,
     734             :       // the offset is used to adjust the position from the anchor point
     735           0 :       if (anchor.IsEmpty() && align.IsEmpty() && position.IsEmpty())
     736           0 :         position.Assign(aPosition);
     737             :       else
     738           0 :         mXPos = mYPos = 0;
     739             :     }
     740           0 :     else if (!aPosition.IsEmpty()) {
     741           0 :       position.Assign(aPosition);
     742             :     }
     743             : 
     744           0 :     if (flip.EqualsLiteral("none")) {
     745           0 :       mFlip = FlipType_None;
     746           0 :     } else if (flip.EqualsLiteral("both")) {
     747           0 :       mFlip = FlipType_Both;
     748           0 :     } else if (flip.EqualsLiteral("slide")) {
     749           0 :       mFlip = FlipType_Slide;
     750             :     }
     751             : 
     752           0 :     position.CompressWhitespace();
     753           0 :     int32_t spaceIdx = position.FindChar(' ');
     754             :     // if there is a space in the position, assume it is the anchor and
     755             :     // alignment as two separate tokens.
     756           0 :     if (spaceIdx >= 0) {
     757           0 :       InitPositionFromAnchorAlign(Substring(position, 0, spaceIdx), Substring(position, spaceIdx + 1));
     758             :     }
     759           0 :     else if (position.EqualsLiteral("before_start")) {
     760           0 :       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
     761           0 :       mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
     762           0 :       mPosition = POPUPPOSITION_BEFORESTART;
     763             :     }
     764           0 :     else if (position.EqualsLiteral("before_end")) {
     765           0 :       mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
     766           0 :       mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
     767           0 :       mPosition = POPUPPOSITION_BEFOREEND;
     768             :     }
     769           0 :     else if (position.EqualsLiteral("after_start")) {
     770           0 :       mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
     771           0 :       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
     772           0 :       mPosition = POPUPPOSITION_AFTERSTART;
     773             :     }
     774           0 :     else if (position.EqualsLiteral("after_end")) {
     775           0 :       mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
     776           0 :       mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
     777           0 :       mPosition = POPUPPOSITION_AFTEREND;
     778             :     }
     779           0 :     else if (position.EqualsLiteral("start_before")) {
     780           0 :       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
     781           0 :       mPopupAlignment = POPUPALIGNMENT_TOPRIGHT;
     782           0 :       mPosition = POPUPPOSITION_STARTBEFORE;
     783             :     }
     784           0 :     else if (position.EqualsLiteral("start_after")) {
     785           0 :       mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
     786           0 :       mPopupAlignment = POPUPALIGNMENT_BOTTOMRIGHT;
     787           0 :       mPosition = POPUPPOSITION_STARTAFTER;
     788             :     }
     789           0 :     else if (position.EqualsLiteral("end_before")) {
     790           0 :       mPopupAnchor = POPUPALIGNMENT_TOPRIGHT;
     791           0 :       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
     792           0 :       mPosition = POPUPPOSITION_ENDBEFORE;
     793             :     }
     794           0 :     else if (position.EqualsLiteral("end_after")) {
     795           0 :       mPopupAnchor = POPUPALIGNMENT_BOTTOMRIGHT;
     796           0 :       mPopupAlignment = POPUPALIGNMENT_BOTTOMLEFT;
     797           0 :       mPosition = POPUPPOSITION_ENDAFTER;
     798             :     }
     799           0 :     else if (position.EqualsLiteral("overlap")) {
     800           0 :       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
     801           0 :       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
     802           0 :       mPosition = POPUPPOSITION_OVERLAP;
     803             :     }
     804           0 :     else if (position.EqualsLiteral("after_pointer")) {
     805           0 :       mPopupAnchor = POPUPALIGNMENT_TOPLEFT;
     806           0 :       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
     807           0 :       mPosition = POPUPPOSITION_AFTERPOINTER;
     808             :       // XXXndeakin this is supposed to anchor vertically after, but with the
     809             :       // horizontal position as the mouse pointer.
     810           0 :       mYPos += 21;
     811             :     }
     812           0 :     else if (position.EqualsLiteral("selection")) {
     813           0 :       mPopupAnchor = POPUPALIGNMENT_BOTTOMLEFT;
     814           0 :       mPopupAlignment = POPUPALIGNMENT_TOPLEFT;
     815           0 :       mPosition = POPUPPOSITION_SELECTION;
     816             :     }
     817             :     else {
     818           0 :       InitPositionFromAnchorAlign(anchor, align);
     819             :     }
     820             :   }
     821             : 
     822           0 :   mScreenRect = nsIntRect(-1, -1, 0, 0);
     823             : 
     824           0 :   if (aAttributesOverride) {
     825             :     // Use |left| and |top| dimension attributes to position the popup if
     826             :     // present, as they may have been persisted.
     827           0 :     nsAutoString left, top;
     828           0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left);
     829           0 :     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top);
     830             : 
     831             :     nsresult err;
     832           0 :     if (!left.IsEmpty()) {
     833           0 :       int32_t x = left.ToInteger(&err);
     834           0 :       if (NS_SUCCEEDED(err))
     835           0 :         mScreenRect.x = x;
     836             :     }
     837           0 :     if (!top.IsEmpty()) {
     838           0 :       int32_t y = top.ToInteger(&err);
     839           0 :       if (NS_SUCCEEDED(err))
     840           0 :         mScreenRect.y = y;
     841             :     }
     842             :   }
     843           0 : }
     844             : 
     845             : void
     846           0 : nsMenuPopupFrame::InitializePopupAtScreen(nsIContent* aTriggerContent,
     847             :                                           int32_t aXPos, int32_t aYPos,
     848             :                                           bool aIsContextMenu)
     849             : {
     850           0 :   EnsureWidget();
     851             : 
     852           0 :   mPopupState = ePopupShowing;
     853           0 :   mAnchorContent = nullptr;
     854           0 :   mTriggerContent = aTriggerContent;
     855           0 :   mScreenRect = nsIntRect(aXPos, aYPos, 0, 0);
     856           0 :   mXPos = 0;
     857           0 :   mYPos = 0;
     858           0 :   mFlip = FlipType_Default;
     859           0 :   mPopupAnchor = POPUPALIGNMENT_NONE;
     860           0 :   mPopupAlignment = POPUPALIGNMENT_NONE;
     861           0 :   mPosition = POPUPPOSITION_UNKNOWN;
     862           0 :   mIsContextMenu = aIsContextMenu;
     863           0 :   mAdjustOffsetForContextMenu = aIsContextMenu;
     864           0 :   mAnchorType = MenuPopupAnchorType_Point;
     865           0 : }
     866             : 
     867             : void
     868           0 : nsMenuPopupFrame::InitializePopupAtRect(nsIContent* aTriggerContent,
     869             :                                         const nsAString& aPosition,
     870             :                                         const nsIntRect& aRect,
     871             :                                         bool aAttributesOverride)
     872             : {
     873           0 :   InitializePopup(nullptr, aTriggerContent, aPosition, 0, 0,
     874           0 :                   MenuPopupAnchorType_Rect, aAttributesOverride);
     875           0 :   mScreenRect = aRect;
     876           0 : }
     877             : 
     878             : void
     879           0 : nsMenuPopupFrame::InitializePopupWithAnchorAlign(nsIContent* aAnchorContent,
     880             :                                                  nsAString& aAnchor,
     881             :                                                  nsAString& aAlign,
     882             :                                                  int32_t aXPos, int32_t aYPos)
     883             : {
     884           0 :   EnsureWidget();
     885             : 
     886           0 :   mPopupState = ePopupShowing;
     887           0 :   mAdjustOffsetForContextMenu = false;
     888           0 :   mFlip = FlipType_Default;
     889             : 
     890             :   // this popup opening function is provided for backwards compatibility
     891             :   // only. It accepts either coordinates or an anchor and alignment value
     892             :   // but doesn't use both together.
     893           0 :   if (aXPos == -1 && aYPos == -1) {
     894           0 :     mAnchorContent = aAnchorContent;
     895           0 :     mAnchorType = MenuPopupAnchorType_Node;
     896           0 :     mScreenRect = nsIntRect(-1, -1, 0, 0);
     897           0 :     mXPos = 0;
     898           0 :     mYPos = 0;
     899           0 :     InitPositionFromAnchorAlign(aAnchor, aAlign);
     900             :   }
     901             :   else {
     902           0 :     mAnchorContent = nullptr;
     903           0 :     mAnchorType = MenuPopupAnchorType_Point;
     904           0 :     mPopupAnchor = POPUPALIGNMENT_NONE;
     905           0 :     mPopupAlignment = POPUPALIGNMENT_NONE;
     906           0 :     mPosition = POPUPPOSITION_UNKNOWN;
     907           0 :     mScreenRect = nsIntRect(aXPos, aYPos, 0, 0);
     908           0 :     mXPos = aXPos;
     909           0 :     mYPos = aYPos;
     910             :   }
     911           0 : }
     912             : 
     913             : void
     914           0 : nsMenuPopupFrame::ShowPopup(bool aIsContextMenu)
     915             : {
     916           0 :   mIsContextMenu = aIsContextMenu;
     917             : 
     918           0 :   InvalidateFrameSubtree();
     919             : 
     920           0 :   if (mPopupState == ePopupShowing || mPopupState == ePopupPositioning) {
     921           0 :     mPopupState = ePopupOpening;
     922           0 :     mIsOpenChanged = true;
     923             : 
     924             :     // Clear mouse capture when a popup is opened.
     925           0 :     if (mPopupType == ePopupTypeMenu) {
     926             :       EventStateManager* activeESM =
     927             :         static_cast<EventStateManager*>(
     928           0 :           EventStateManager::GetActiveEventStateManager());
     929           0 :       if (activeESM) {
     930           0 :         EventStateManager::ClearGlobalActiveContent(activeESM);
     931             :       }
     932             : 
     933           0 :       nsIPresShell::SetCapturingContent(nullptr, 0);
     934             :     }
     935             : 
     936           0 :     nsMenuFrame* menuFrame = do_QueryFrame(GetParent());
     937           0 :     if (menuFrame) {
     938           0 :       AutoWeakFrame weakFrame(this);
     939           0 :       menuFrame->PopupOpened();
     940           0 :       if (!weakFrame.IsAlive())
     941           0 :         return;
     942             :     }
     943             : 
     944             :     // do we need an actual reflow here?
     945             :     // is SetPopupPosition all that is needed?
     946           0 :     PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
     947           0 :                                                  NS_FRAME_HAS_DIRTY_CHILDREN);
     948             : 
     949           0 :     if (mPopupType == ePopupTypeMenu) {
     950           0 :       nsCOMPtr<nsISound> sound(do_CreateInstance("@mozilla.org/sound;1"));
     951           0 :       if (sound)
     952           0 :         sound->PlayEventSound(nsISound::EVENT_MENU_POPUP);
     953             :     }
     954             :   }
     955             : 
     956           0 :   mShouldAutoPosition = true;
     957             : }
     958             : 
     959             : void
     960           0 : nsMenuPopupFrame::HidePopup(bool aDeselectMenu, nsPopupState aNewState)
     961             : {
     962           0 :   NS_ASSERTION(aNewState == ePopupClosed || aNewState == ePopupInvisible,
     963             :                "popup being set to unexpected state");
     964             : 
     965           0 :   ClearPopupShownDispatcher();
     966             : 
     967             :   // don't hide the popup when it isn't open
     968           0 :   if (mPopupState == ePopupClosed || mPopupState == ePopupShowing ||
     969           0 :       mPopupState == ePopupPositioning)
     970           0 :     return;
     971             : 
     972             :   // clear the trigger content if the popup is being closed. But don't clear
     973             :   // it if the popup is just being made invisible as a popuphiding or command
     974             :   // event may want to retrieve it.
     975           0 :   if (aNewState == ePopupClosed) {
     976             :     // if the popup had a trigger node set, clear the global window popup node
     977             :     // as well
     978           0 :     if (mTriggerContent) {
     979           0 :       nsIDocument* doc = mContent->GetUncomposedDoc();
     980           0 :       if (doc) {
     981           0 :         if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
     982           0 :           nsCOMPtr<nsPIWindowRoot> root = win->GetTopWindowRoot();
     983           0 :           if (root) {
     984           0 :             root->SetPopupNode(nullptr);
     985             :           }
     986             :         }
     987             :       }
     988             :     }
     989           0 :     mTriggerContent = nullptr;
     990           0 :     mAnchorContent = nullptr;
     991             :   }
     992             : 
     993             :   // when invisible and about to be closed, HidePopup has already been called,
     994             :   // so just set the new state to closed and return
     995           0 :   if (mPopupState == ePopupInvisible) {
     996           0 :     if (aNewState == ePopupClosed)
     997           0 :       mPopupState = ePopupClosed;
     998           0 :     return;
     999             :   }
    1000             : 
    1001           0 :   mPopupState = aNewState;
    1002             : 
    1003           0 :   if (IsMenu())
    1004           0 :     SetCurrentMenuItem(nullptr);
    1005             : 
    1006           0 :   mIncrementalString.Truncate();
    1007             : 
    1008           0 :   LockMenuUntilClosed(false);
    1009             : 
    1010           0 :   mIsOpenChanged = false;
    1011           0 :   mCurrentMenu = nullptr; // make sure no current menu is set
    1012           0 :   mHFlip = mVFlip = false;
    1013             : 
    1014           0 :   nsView* view = GetView();
    1015           0 :   nsViewManager* viewManager = view->GetViewManager();
    1016           0 :   viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
    1017             : 
    1018           0 :   FireDOMEvent(NS_LITERAL_STRING("DOMMenuInactive"), mContent);
    1019             : 
    1020             :   // XXX, bug 137033, In Windows, if mouse is outside the window when the menupopup closes, no
    1021             :   // mouse_enter/mouse_exit event will be fired to clear current hover state, we should clear it manually.
    1022             :   // This code may not the best solution, but we can leave it here until we find the better approach.
    1023           0 :   NS_ASSERTION(mContent->IsElement(), "How do we have a non-element?");
    1024           0 :   EventStates state = mContent->AsElement()->State();
    1025             : 
    1026           0 :   if (state.HasState(NS_EVENT_STATE_HOVER)) {
    1027           0 :     EventStateManager* esm = PresContext()->EventStateManager();
    1028           0 :     esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER);
    1029             :   }
    1030             : 
    1031           0 :   nsMenuFrame* menuFrame = do_QueryFrame(GetParent());
    1032           0 :   if (menuFrame) {
    1033           0 :     menuFrame->PopupClosed(aDeselectMenu);
    1034             :   }
    1035             : }
    1036             : 
    1037             : uint32_t
    1038           0 : nsMenuPopupFrame::GetXULLayoutFlags()
    1039             : {
    1040           0 :   return NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_MOVE_VIEW | NS_FRAME_NO_VISIBILITY;
    1041             : }
    1042             : 
    1043             : ///////////////////////////////////////////////////////////////////////////////
    1044             : // GetRootViewForPopup
    1045             : //   Retrieves the view for the popup widget that contains the given frame.
    1046             : //   If the given frame is not contained by a popup widget, return the
    1047             : //   root view of the root viewmanager.
    1048             : nsView*
    1049           0 : nsMenuPopupFrame::GetRootViewForPopup(nsIFrame* aStartFrame)
    1050             : {
    1051           0 :   nsView* view = aStartFrame->GetClosestView();
    1052           0 :   NS_ASSERTION(view, "frame must have a closest view!");
    1053           0 :   while (view) {
    1054             :     // Walk up the view hierarchy looking for a view whose widget has a
    1055             :     // window type of eWindowType_popup - in other words a popup window
    1056             :     // widget. If we find one, this is the view we want.
    1057           0 :     nsIWidget* widget = view->GetWidget();
    1058           0 :     if (widget && widget->WindowType() == eWindowType_popup) {
    1059           0 :       return view;
    1060             :     }
    1061             : 
    1062           0 :     nsView* temp = view->GetParent();
    1063           0 :     if (!temp) {
    1064             :       // Otherwise, we've walked all the way up to the root view and not
    1065             :       // found a view for a popup window widget. Just return the root view.
    1066           0 :       return view;
    1067             :     }
    1068           0 :     view = temp;
    1069             :   }
    1070             : 
    1071           0 :   return nullptr;
    1072             : }
    1073             : 
    1074             : nsPoint
    1075           0 : nsMenuPopupFrame::AdjustPositionForAnchorAlign(nsRect& anchorRect,
    1076             :                                                FlipStyle& aHFlip, FlipStyle& aVFlip)
    1077             : {
    1078             :   // flip the anchor and alignment for right-to-left
    1079           0 :   int8_t popupAnchor(mPopupAnchor);
    1080           0 :   int8_t popupAlign(mPopupAlignment);
    1081           0 :   if (IsDirectionRTL()) {
    1082             :     // no need to flip the centered anchor types vertically
    1083           0 :     if (popupAnchor <= POPUPALIGNMENT_LEFTCENTER) {
    1084           0 :       popupAnchor = -popupAnchor;
    1085             :     }
    1086           0 :     popupAlign = -popupAlign;
    1087             :   }
    1088             : 
    1089           0 :   nsRect originalAnchorRect(anchorRect);
    1090             : 
    1091             :   // first, determine at which corner of the anchor the popup should appear
    1092           0 :   nsPoint pnt;
    1093           0 :   switch (popupAnchor) {
    1094             :     case POPUPALIGNMENT_LEFTCENTER:
    1095           0 :       pnt = nsPoint(anchorRect.x, anchorRect.y + anchorRect.height / 2);
    1096           0 :       anchorRect.y = pnt.y;
    1097           0 :       anchorRect.height = 0;
    1098           0 :       break;
    1099             :     case POPUPALIGNMENT_RIGHTCENTER:
    1100           0 :       pnt = nsPoint(anchorRect.XMost(), anchorRect.y + anchorRect.height / 2);
    1101           0 :       anchorRect.y = pnt.y;
    1102           0 :       anchorRect.height = 0;
    1103           0 :       break;
    1104             :     case POPUPALIGNMENT_TOPCENTER:
    1105           0 :       pnt = nsPoint(anchorRect.x + anchorRect.width / 2, anchorRect.y);
    1106           0 :       anchorRect.x = pnt.x;
    1107           0 :       anchorRect.width = 0;
    1108           0 :       break;
    1109             :     case POPUPALIGNMENT_BOTTOMCENTER:
    1110           0 :       pnt = nsPoint(anchorRect.x + anchorRect.width / 2, anchorRect.YMost());
    1111           0 :       anchorRect.x = pnt.x;
    1112           0 :       anchorRect.width = 0;
    1113           0 :       break;
    1114             :     case POPUPALIGNMENT_TOPRIGHT:
    1115           0 :       pnt = anchorRect.TopRight();
    1116           0 :       break;
    1117             :     case POPUPALIGNMENT_BOTTOMLEFT:
    1118           0 :       pnt = anchorRect.BottomLeft();
    1119           0 :       break;
    1120             :     case POPUPALIGNMENT_BOTTOMRIGHT:
    1121           0 :       pnt = anchorRect.BottomRight();
    1122           0 :       break;
    1123             :     case POPUPALIGNMENT_TOPLEFT:
    1124             :     default:
    1125           0 :       pnt = anchorRect.TopLeft();
    1126           0 :       break;
    1127             :   }
    1128             : 
    1129             :   // If the alignment is on the right edge of the popup, move the popup left
    1130             :   // by the width. Similarly, if the alignment is on the bottom edge of the
    1131             :   // popup, move the popup up by the height. In addition, account for the
    1132             :   // margins of the popup on the edge on which it is aligned.
    1133           0 :   nsMargin margin(0, 0, 0, 0);
    1134           0 :   StyleMargin()->GetMargin(margin);
    1135           0 :   switch (popupAlign) {
    1136             :     case POPUPALIGNMENT_TOPRIGHT:
    1137           0 :       pnt.MoveBy(-mRect.width - margin.right, margin.top);
    1138           0 :       break;
    1139             :     case POPUPALIGNMENT_BOTTOMLEFT:
    1140           0 :       pnt.MoveBy(margin.left, -mRect.height - margin.bottom);
    1141           0 :       break;
    1142             :     case POPUPALIGNMENT_BOTTOMRIGHT:
    1143           0 :       pnt.MoveBy(-mRect.width - margin.right, -mRect.height - margin.bottom);
    1144           0 :       break;
    1145             :     case POPUPALIGNMENT_TOPLEFT:
    1146             :     default:
    1147           0 :       pnt.MoveBy(margin.left, margin.top);
    1148           0 :       break;
    1149             :   }
    1150             : 
    1151             :   // If we aligning to the selected item in the popup, adjust the vertical
    1152             :   // position by the height of the menulist label and the selected item's
    1153             :   // position.
    1154           0 :   if (mPosition == POPUPPOSITION_SELECTION) {
    1155           0 :     MOZ_ASSERT(popupAnchor == POPUPALIGNMENT_BOTTOMLEFT ||
    1156             :               popupAnchor == POPUPALIGNMENT_BOTTOMRIGHT);
    1157           0 :     MOZ_ASSERT(popupAlign == POPUPALIGNMENT_TOPLEFT ||
    1158             :                popupAlign == POPUPALIGNMENT_TOPRIGHT);
    1159             : 
    1160           0 :     nsIFrame* selectedItemFrame = GetSelectedItemForAlignment();
    1161           0 :     if (selectedItemFrame) {
    1162           0 :       int32_t scrolly = 0;
    1163           0 :       nsIScrollableFrame *scrollframe = do_QueryFrame(nsBox::GetChildXULBox(this));
    1164           0 :       if (scrollframe) {
    1165           0 :         scrolly = scrollframe->GetScrollPosition().y;
    1166             :       }
    1167             : 
    1168           0 :       pnt.y -= originalAnchorRect.height + selectedItemFrame->GetRect().y - scrolly;
    1169             :     }
    1170             :   }
    1171             : 
    1172             :   // Flipping horizontally is allowed as long as the popup is above or below
    1173             :   // the anchor. This will happen if both the anchor and alignment are top or
    1174             :   // both are bottom, but different values. Similarly, flipping vertically is
    1175             :   // allowed if the popup is to the left or right of the anchor. In this case,
    1176             :   // the values of the constants are such that both must be positive or both
    1177             :   // must be negative. A special case, used for overlap, allows flipping
    1178             :   // vertically as well.
    1179             :   // If we are flipping in both directions, we want to set a flip style both
    1180             :   // horizontally and vertically. However, we want to flip on the inside edge
    1181             :   // of the anchor. Consider the example of a typical dropdown menu.
    1182             :   // Vertically, we flip the popup on the outside edges of the anchor menu,
    1183             :   // however horizontally, we want to to use the inside edges so the popup
    1184             :   // still appears underneath the anchor menu instead of floating off the
    1185             :   // side of the menu.
    1186           0 :   switch (popupAnchor) {
    1187             :     case POPUPALIGNMENT_LEFTCENTER:
    1188             :     case POPUPALIGNMENT_RIGHTCENTER:
    1189           0 :       aHFlip = FlipStyle_Outside;
    1190           0 :       aVFlip = FlipStyle_Inside;
    1191           0 :       break;
    1192             :     case POPUPALIGNMENT_TOPCENTER:
    1193             :     case POPUPALIGNMENT_BOTTOMCENTER:
    1194           0 :       aHFlip = FlipStyle_Inside;
    1195           0 :       aVFlip = FlipStyle_Outside;
    1196           0 :       break;
    1197             :     default:
    1198             :     {
    1199           0 :       FlipStyle anchorEdge = mFlip == FlipType_Both ? FlipStyle_Inside : FlipStyle_None;
    1200           0 :       aHFlip = (popupAnchor == -popupAlign) ? FlipStyle_Outside : anchorEdge;
    1201           0 :       if (((popupAnchor > 0) == (popupAlign > 0)) ||
    1202           0 :           (popupAnchor == POPUPALIGNMENT_TOPLEFT && popupAlign == POPUPALIGNMENT_TOPLEFT))
    1203           0 :         aVFlip = FlipStyle_Outside;
    1204             :       else
    1205           0 :         aVFlip = anchorEdge;
    1206           0 :       break;
    1207             :     }
    1208             :   }
    1209             : 
    1210           0 :   return pnt;
    1211             : }
    1212             : 
    1213           0 : nsIFrame* nsMenuPopupFrame::GetSelectedItemForAlignment()
    1214             : {
    1215             :   // This method adjusts a menulist's popup such that the selected item is under the cursor, aligned
    1216             :   // with the menulist label.
    1217           0 :   nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(mAnchorContent);
    1218           0 :   if (!select) {
    1219             :     // If there isn't an anchor, then try just getting the parent of the popup.
    1220           0 :     select = do_QueryInterface(mContent->GetParent());
    1221           0 :     if (!select) {
    1222           0 :       return nullptr;
    1223             :     }
    1224             :   }
    1225             : 
    1226           0 :   nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
    1227           0 :   select->GetSelectedItem(getter_AddRefs(item));
    1228             : 
    1229           0 :   nsCOMPtr<nsIContent> selectedElement = do_QueryInterface(item);
    1230           0 :   return selectedElement ? selectedElement->GetPrimaryFrame() : nullptr;
    1231             : }
    1232             : 
    1233             : nscoord
    1234           0 : nsMenuPopupFrame::SlideOrResize(nscoord& aScreenPoint, nscoord aSize,
    1235             :                                nscoord aScreenBegin, nscoord aScreenEnd,
    1236             :                                nscoord *aOffset)
    1237             : {
    1238             :   // The popup may be positioned such that either the left/top or bottom/right
    1239             :   // is outside the screen - but never both.
    1240             :   nscoord newPos =
    1241           0 :     std::max(aScreenBegin, std::min(aScreenEnd - aSize, aScreenPoint));
    1242           0 :   *aOffset = newPos - aScreenPoint;
    1243           0 :   aScreenPoint = newPos;
    1244           0 :   return std::min(aSize, aScreenEnd - aScreenPoint);
    1245             : }
    1246             : 
    1247             : nscoord
    1248           0 : nsMenuPopupFrame::FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
    1249             :                                nscoord aScreenBegin, nscoord aScreenEnd,
    1250             :                                nscoord aAnchorBegin, nscoord aAnchorEnd,
    1251             :                                nscoord aMarginBegin, nscoord aMarginEnd,
    1252             :                                nscoord aOffsetForContextMenu, FlipStyle aFlip,
    1253             :                                bool aEndAligned, bool* aFlipSide)
    1254             : {
    1255             :   // The flip side argument will be set to true if there wasn't room and we
    1256             :   // flipped to the opposite side.
    1257           0 :   *aFlipSide = false;
    1258             : 
    1259             :   // all of the coordinates used here are in app units relative to the screen
    1260           0 :   nscoord popupSize = aSize;
    1261           0 :   if (aScreenPoint < aScreenBegin) {
    1262             :     // at its current position, the popup would extend past the left or top
    1263             :     // edge of the screen, so it will have to be moved or resized.
    1264           0 :     if (aFlip) {
    1265             :       // for inside flips, we flip on the opposite side of the anchor
    1266           0 :       nscoord startpos = aFlip == FlipStyle_Outside ? aAnchorBegin : aAnchorEnd;
    1267           0 :       nscoord endpos = aFlip == FlipStyle_Outside ? aAnchorEnd : aAnchorBegin;
    1268             : 
    1269             :       // check whether there is more room to the left and right (or top and
    1270             :       // bottom) of the anchor and put the popup on the side with more room.
    1271           0 :       if (startpos - aScreenBegin >= aScreenEnd - endpos) {
    1272           0 :         aScreenPoint = aScreenBegin;
    1273           0 :         popupSize = startpos - aScreenPoint - aMarginEnd;
    1274           0 :         *aFlipSide = !aEndAligned;
    1275             :       }
    1276             :       else {
    1277             :         // If the newly calculated position is different than the existing
    1278             :         // position, flip such that the popup is to the right or bottom of the
    1279             :         // anchor point instead . However, when flipping use the same margin
    1280             :         // size.
    1281           0 :         nscoord newScreenPoint = endpos + aMarginEnd;
    1282           0 :         if (newScreenPoint != aScreenPoint) {
    1283           0 :           *aFlipSide = aEndAligned;
    1284           0 :           aScreenPoint = newScreenPoint;
    1285             :           // check if the new position is still off the right or bottom edge of
    1286             :           // the screen. If so, resize the popup.
    1287           0 :           if (aScreenPoint + aSize > aScreenEnd) {
    1288           0 :             popupSize = aScreenEnd - aScreenPoint;
    1289             :           }
    1290             :         }
    1291             :       }
    1292             :     }
    1293             :     else {
    1294           0 :       aScreenPoint = aScreenBegin;
    1295             :     }
    1296             :   }
    1297           0 :   else if (aScreenPoint + aSize > aScreenEnd) {
    1298             :     // at its current position, the popup would extend past the right or
    1299             :     // bottom edge of the screen, so it will have to be moved or resized.
    1300           0 :     if (aFlip) {
    1301             :       // for inside flips, we flip on the opposite side of the anchor
    1302           0 :       nscoord startpos = aFlip == FlipStyle_Outside ? aAnchorBegin : aAnchorEnd;
    1303           0 :       nscoord endpos = aFlip == FlipStyle_Outside ? aAnchorEnd : aAnchorBegin;
    1304             : 
    1305             :       // check whether there is more room to the left and right (or top and
    1306             :       // bottom) of the anchor and put the popup on the side with more room.
    1307           0 :       if (aScreenEnd - endpos >= startpos - aScreenBegin) {
    1308           0 :         *aFlipSide = aEndAligned;
    1309           0 :         if (mIsContextMenu) {
    1310           0 :           aScreenPoint = aScreenEnd - aSize;
    1311             :         }
    1312             :         else {
    1313           0 :           aScreenPoint = endpos + aMarginBegin;
    1314           0 :           popupSize = aScreenEnd - aScreenPoint;
    1315             :         }
    1316             :       }
    1317             :       else {
    1318             :         // if the newly calculated position is different than the existing
    1319             :         // position, we flip such that the popup is to the left or top of the
    1320             :         // anchor point instead.
    1321           0 :         nscoord newScreenPoint = startpos - aSize - aMarginBegin - std::max(aOffsetForContextMenu, 0);
    1322           0 :         if (newScreenPoint != aScreenPoint) {
    1323           0 :           *aFlipSide = !aEndAligned;
    1324           0 :           aScreenPoint = newScreenPoint;
    1325             : 
    1326             :           // check if the new position is still off the left or top edge of the
    1327             :           // screen. If so, resize the popup.
    1328           0 :           if (aScreenPoint < aScreenBegin) {
    1329           0 :             aScreenPoint = aScreenBegin;
    1330           0 :             if (!mIsContextMenu) {
    1331           0 :               popupSize = startpos - aScreenPoint - aMarginBegin;
    1332             :             }
    1333             :           }
    1334             :         }
    1335             :       }
    1336             :     }
    1337             :     else {
    1338           0 :       aScreenPoint = aScreenEnd - aSize;
    1339             :     }
    1340             :   }
    1341             : 
    1342             :   // Make sure that the point is within the screen boundaries and that the
    1343             :   // size isn't off the edge of the screen. This can happen when a large
    1344             :   // positive or negative margin is used.
    1345           0 :   if (aScreenPoint < aScreenBegin) {
    1346           0 :     aScreenPoint = aScreenBegin;
    1347             :   }
    1348           0 :   if (aScreenPoint > aScreenEnd) {
    1349           0 :     aScreenPoint = aScreenEnd - aSize;
    1350             :   }
    1351             : 
    1352             :   // If popupSize ended up being negative, or the original size was actually
    1353             :   // smaller than the calculated popup size, just use the original size instead.
    1354           0 :   if (popupSize <= 0 || aSize < popupSize) {
    1355           0 :     popupSize = aSize;
    1356             :   }
    1357           0 :   return std::min(popupSize, aScreenEnd - aScreenPoint);
    1358             : }
    1359             : 
    1360             : nsRect
    1361           0 : nsMenuPopupFrame::ComputeAnchorRect(nsPresContext* aRootPresContext, nsIFrame* aAnchorFrame)
    1362             : {
    1363             :   // Get the root frame for a reference
    1364           0 :   nsIFrame* rootFrame = aRootPresContext->FrameManager()->GetRootFrame();
    1365             : 
    1366             :   // The dimensions of the anchor
    1367           0 :   nsRect anchorRect = aAnchorFrame->GetRectRelativeToSelf();
    1368             : 
    1369             :   // Relative to the root
    1370           0 :   anchorRect = nsLayoutUtils::TransformFrameRectToAncestor(aAnchorFrame,
    1371             :                                                            anchorRect,
    1372             :                                                            rootFrame);
    1373             :   // Relative to the screen
    1374           0 :   anchorRect.MoveBy(rootFrame->GetScreenRectInAppUnits().TopLeft());
    1375             : 
    1376             :   // In its own app units
    1377             :   return anchorRect.ScaleToOtherAppUnitsRoundOut(aRootPresContext->AppUnitsPerDevPixel(),
    1378           0 :                                                  PresContext()->AppUnitsPerDevPixel());
    1379             : }
    1380             : 
    1381             : nsresult
    1382           0 : nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove, bool aSizedToPopup, bool aNotify)
    1383             : {
    1384           0 :   if (!mShouldAutoPosition)
    1385           0 :     return NS_OK;
    1386             : 
    1387             :   // If this is due to a move, return early if the popup hasn't been laid out
    1388             :   // yet. On Windows, this can happen when using a drag popup before it opens.
    1389           0 :   if (aIsMove && (mPrefSize.width == -1 || mPrefSize.height == -1)) {
    1390           0 :     return NS_OK;
    1391             :   }
    1392             : 
    1393           0 :   nsPresContext* presContext = PresContext();
    1394           0 :   nsIFrame* rootFrame = presContext->PresShell()->FrameManager()->GetRootFrame();
    1395           0 :   NS_ASSERTION(rootFrame->GetView() && GetView() &&
    1396             :                rootFrame->GetView() == GetView()->GetParent(),
    1397             :                "rootFrame's view is not our view's parent???");
    1398             : 
    1399             :   // For anchored popups, the anchor rectangle. For non-anchored popups, the
    1400             :   // size will be 0.
    1401           0 :   nsRect anchorRect;
    1402             : 
    1403             :   // Width of the parent, used when aSizedToPopup is true.
    1404           0 :   int32_t parentWidth = 0;
    1405             : 
    1406           0 :   bool anchored = IsAnchored();
    1407           0 :   if (anchored || aSizedToPopup) {
    1408             :     // In order to deal with transforms, we need the root prescontext:
    1409           0 :     nsPresContext* rootPresContext = presContext->GetRootPresContext();
    1410             : 
    1411             :     // If we can't reach a root pres context, don't bother continuing:
    1412           0 :     if (!rootPresContext) {
    1413           0 :       return NS_OK;
    1414             :     }
    1415             : 
    1416             :     // If anchored to a rectangle, use that rectangle. Otherwise, determine the
    1417             :     // rectangle from the anchor.
    1418           0 :     if (mAnchorType == MenuPopupAnchorType_Rect) {
    1419           0 :       anchorRect = ToAppUnits(mScreenRect, presContext->AppUnitsPerCSSPixel());
    1420             :     }
    1421             :     else {
    1422             :       // if the frame is not specified, use the anchor node passed to OpenPopup. If
    1423             :       // that wasn't specified either, use the root frame. Note that mAnchorContent
    1424             :       // might be a different document so its presshell must be used.
    1425           0 :       if (!aAnchorFrame) {
    1426           0 :         if (mAnchorContent) {
    1427           0 :           aAnchorFrame = mAnchorContent->GetPrimaryFrame();
    1428             :         }
    1429             : 
    1430           0 :         if (!aAnchorFrame) {
    1431           0 :           aAnchorFrame = rootFrame;
    1432           0 :           if (!aAnchorFrame)
    1433           0 :             return NS_OK;
    1434             :         }
    1435             :       }
    1436             : 
    1437           0 :       anchorRect = ComputeAnchorRect(rootPresContext, aAnchorFrame);
    1438             :     }
    1439             : 
    1440             :     // The width is needed when aSizedToPopup is true
    1441           0 :     parentWidth = anchorRect.width;
    1442             :   }
    1443             : 
    1444             :   // Set the popup's size to the preferred size. Below, this size will be
    1445             :   // adjusted to fit on the screen or within the content area. If the anchor
    1446             :   // is sized to the popup, use the anchor's width instead of the preferred
    1447             :   // width. The preferred size should already be set by the parent frame.
    1448           0 :   NS_ASSERTION(mPrefSize.width >= 0 || mPrefSize.height >= 0,
    1449             :                "preferred size of popup not set");
    1450           0 :   mRect.width = aSizedToPopup ? parentWidth : mPrefSize.width;
    1451           0 :   mRect.height = mPrefSize.height;
    1452             : 
    1453             :   // If we're anchoring to a rect, and the rect is smaller than the preferred size
    1454             :   // of the popup, change its width accordingly.
    1455           0 :   if (mAnchorType == MenuPopupAnchorType_Rect &&
    1456           0 :       parentWidth < mPrefSize.width) {
    1457           0 :     mRect.width = mPrefSize.width;
    1458             :   }
    1459             : 
    1460             :   // the screen position in app units where the popup should appear
    1461           0 :   nsPoint screenPoint;
    1462             : 
    1463             :   // indicators of whether the popup should be flipped or resized.
    1464           0 :   FlipStyle hFlip = FlipStyle_None, vFlip = FlipStyle_None;
    1465             : 
    1466           0 :   nsMargin margin(0, 0, 0, 0);
    1467           0 :   StyleMargin()->GetMargin(margin);
    1468             : 
    1469             :   // the screen rectangle of the root frame, in dev pixels.
    1470           0 :   nsRect rootScreenRect = rootFrame->GetScreenRectInAppUnits();
    1471             : 
    1472           0 :   nsDeviceContext* devContext = presContext->DeviceContext();
    1473           0 :   nsPoint offsetForContextMenu;
    1474             : 
    1475           0 :   bool isNoAutoHide = IsNoAutoHide();
    1476           0 :   nsPopupLevel popupLevel = PopupLevel(isNoAutoHide);
    1477             : 
    1478           0 :   if (anchored) {
    1479             :     // if we are anchored, there are certain things we don't want to do when
    1480             :     // repositioning the popup to fit on the screen, such as end up positioned
    1481             :     // over the anchor, for instance a popup appearing over the menu label.
    1482             :     // When doing this reposition, we want to move the popup to the side with
    1483             :     // the most room. The combination of anchor and alignment dictate if we
    1484             :     // readjust above/below or to the left/right.
    1485           0 :     if (mAnchorContent || mAnchorType == MenuPopupAnchorType_Rect) {
    1486             :       // move the popup according to the anchor and alignment. This will also
    1487             :       // tell us which axis the popup is flush against in case we have to move
    1488             :       // it around later. The AdjustPositionForAnchorAlign method accounts for
    1489             :       // the popup's margin.
    1490           0 :       screenPoint = AdjustPositionForAnchorAlign(anchorRect, hFlip, vFlip);
    1491             :     }
    1492             :     else {
    1493             :       // with no anchor, the popup is positioned relative to the root frame
    1494           0 :       anchorRect = rootScreenRect;
    1495           0 :       screenPoint = anchorRect.TopLeft() + nsPoint(margin.left, margin.top);
    1496             :     }
    1497             : 
    1498             :     // mXPos and mYPos specify an additonal offset passed to OpenPopup that
    1499             :     // should be added to the position.  We also add the offset to the anchor
    1500             :     // pos so a later flip/resize takes the offset into account.
    1501           0 :     nscoord anchorXOffset = presContext->CSSPixelsToAppUnits(mXPos);
    1502           0 :     if (IsDirectionRTL()) {
    1503           0 :       screenPoint.x -= anchorXOffset;
    1504           0 :       anchorRect.x -= anchorXOffset;
    1505             :     } else {
    1506           0 :       screenPoint.x += anchorXOffset;
    1507           0 :       anchorRect.x += anchorXOffset;
    1508             :     }
    1509           0 :     nscoord anchorYOffset = presContext->CSSPixelsToAppUnits(mYPos);
    1510           0 :     screenPoint.y += anchorYOffset;
    1511           0 :     anchorRect.y += anchorYOffset;
    1512             : 
    1513             :     // If this is a noautohide popup, set the screen coordinates of the popup.
    1514             :     // This way, the popup stays at the location where it was opened even when
    1515             :     // the window is moved. Popups at the parent level follow the parent
    1516             :     // window as it is moved and remained anchored, so we want to maintain the
    1517             :     // anchoring instead.
    1518           0 :     if (isNoAutoHide &&
    1519           0 :         (popupLevel != ePopupLevelParent || mAnchorType == MenuPopupAnchorType_Rect)) {
    1520             :       // Account for the margin that will end up being added to the screen coordinate
    1521             :       // the next time SetPopupPosition is called.
    1522           0 :       mAnchorType = MenuPopupAnchorType_Point;
    1523           0 :       mScreenRect.x = presContext->AppUnitsToIntCSSPixels(screenPoint.x - margin.left);
    1524           0 :       mScreenRect.y = presContext->AppUnitsToIntCSSPixels(screenPoint.y - margin.top);
    1525             :     }
    1526             :   }
    1527             :   else {
    1528             :     // The popup is positioned at a screen coordinate.
    1529             :     // First convert the screen position in mScreenRect from CSS pixels into
    1530             :     // device pixels, ignoring any zoom as mScreenRect holds unzoomed screen
    1531             :     // coordinates.
    1532           0 :     int32_t factor = devContext->AppUnitsPerDevPixelAtUnitFullZoom();
    1533             : 
    1534             :     // Depending on the platform, context menus should be offset by varying amounts
    1535             :     // to ensure that they don't appear directly where the cursor is. Otherwise,
    1536             :     // it is too easy to have the context menu close up again.
    1537           0 :     if (mAdjustOffsetForContextMenu) {
    1538           0 :       nsPoint offsetForContextMenuDev;
    1539           0 :       offsetForContextMenuDev.x = nsPresContext::CSSPixelsToAppUnits(LookAndFeel::GetInt(
    1540           0 :                                     LookAndFeel::eIntID_ContextMenuOffsetHorizontal)) / factor;
    1541           0 :       offsetForContextMenuDev.y = nsPresContext::CSSPixelsToAppUnits(LookAndFeel::GetInt(
    1542           0 :                                     LookAndFeel::eIntID_ContextMenuOffsetVertical)) / factor;
    1543           0 :       offsetForContextMenu.x = presContext->DevPixelsToAppUnits(offsetForContextMenuDev.x);
    1544           0 :       offsetForContextMenu.y = presContext->DevPixelsToAppUnits(offsetForContextMenuDev.y);
    1545             :     }
    1546             : 
    1547             :     // next, convert into app units accounting for the zoom
    1548           0 :     screenPoint.x = presContext->DevPixelsToAppUnits(
    1549           0 :                       nsPresContext::CSSPixelsToAppUnits(mScreenRect.x) / factor);
    1550           0 :     screenPoint.y = presContext->DevPixelsToAppUnits(
    1551           0 :                       nsPresContext::CSSPixelsToAppUnits(mScreenRect.y) / factor);
    1552           0 :     anchorRect = nsRect(screenPoint, nsSize(0, 0));
    1553             : 
    1554             :     // add the margins on the popup
    1555           0 :     screenPoint.MoveBy(margin.left + offsetForContextMenu.x,
    1556           0 :                        margin.top + offsetForContextMenu.y);
    1557             : 
    1558             : #ifdef XP_MACOSX
    1559             :     // OSX tooltips follow standard flip rule but other popups flip horizontally not vertically
    1560             :     if (mPopupType == ePopupTypeTooltip) {
    1561             :         vFlip = FlipStyle_Outside;
    1562             :     } else {
    1563             :         hFlip = FlipStyle_Outside;
    1564             :     }
    1565             : #else
    1566             :     // Other OS screen positioned popups can be flipped vertically but never horizontally
    1567           0 :     vFlip = FlipStyle_Outside;
    1568             : #endif // #ifdef XP_MACOSX
    1569             :   }
    1570             : 
    1571           0 :   nscoord oldAlignmentOffset = mAlignmentOffset;
    1572             : 
    1573             :   // If a panel is being moved or has flip="none", don't constrain or flip it, in order to avoid
    1574             :   // visual noise when moving windows between screens. However, if a panel is already constrained
    1575             :   // or flipped (mIsOffset), then we want to continue to calculate this. Also, always do this for
    1576             :   // content shells, so that the popup doesn't extend outside the containing frame.
    1577           0 :   if (mInContentShell || (mFlip != FlipType_None &&
    1578           0 :                           (!aIsMove || mIsOffset || mPopupType != ePopupTypePanel))) {
    1579           0 :     int32_t appPerDev = presContext->AppUnitsPerDevPixel();
    1580             :     LayoutDeviceIntRect anchorRectDevPix =
    1581           0 :       LayoutDeviceIntRect::FromAppUnitsToNearest(anchorRect, appPerDev);
    1582             :     LayoutDeviceIntRect rootScreenRectDevPix =
    1583           0 :       LayoutDeviceIntRect::FromAppUnitsToNearest(rootScreenRect, appPerDev);
    1584             :     LayoutDeviceIntRect screenRectDevPix =
    1585           0 :       GetConstraintRect(anchorRectDevPix, rootScreenRectDevPix, popupLevel);
    1586             :     nsRect screenRect =
    1587           0 :       LayoutDeviceIntRect::ToAppUnits(screenRectDevPix, appPerDev);
    1588             : 
    1589             :     // Ensure that anchorRect is on screen.
    1590           0 :     anchorRect = anchorRect.Intersect(screenRect);
    1591             : 
    1592             :     // shrink the the popup down if it is larger than the screen size
    1593           0 :     if (mRect.width > screenRect.width)
    1594           0 :       mRect.width = screenRect.width;
    1595           0 :     if (mRect.height > screenRect.height)
    1596           0 :       mRect.height = screenRect.height;
    1597             : 
    1598             :     // at this point the anchor (anchorRect) is within the available screen
    1599             :     // area (screenRect) and the popup is known to be no larger than the screen.
    1600             : 
    1601             :     // We might want to "slide" an arrow if the panel is of the correct type -
    1602             :     // but we can only slide on one axis - the other axis must be "flipped or
    1603             :     // resized" as normal.
    1604           0 :     bool slideHorizontal = false, slideVertical = false;
    1605           0 :     if (mFlip == FlipType_Slide) {
    1606           0 :       int8_t position = GetAlignmentPosition();
    1607           0 :       slideHorizontal = position >= POPUPPOSITION_BEFORESTART &&
    1608             :                         position <= POPUPPOSITION_AFTEREND;
    1609           0 :       slideVertical = position >= POPUPPOSITION_STARTBEFORE &&
    1610             :                       position <= POPUPPOSITION_ENDAFTER;
    1611             :     }
    1612             : 
    1613             :     // Next, check if there is enough space to show the popup at full size when
    1614             :     // positioned at screenPoint. If not, flip the popups to the opposite side
    1615             :     // of their anchor point, or resize them as necessary.
    1616           0 :     bool endAligned = IsDirectionRTL() ?
    1617           0 :       mPopupAlignment == POPUPALIGNMENT_TOPLEFT || mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT :
    1618           0 :       mPopupAlignment == POPUPALIGNMENT_TOPRIGHT || mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
    1619           0 :     nscoord preOffsetScreenPoint = screenPoint.x;
    1620           0 :     if (slideHorizontal) {
    1621           0 :       mRect.width = SlideOrResize(screenPoint.x, mRect.width, screenRect.x,
    1622             :                                   screenRect.XMost(), &mAlignmentOffset);
    1623             :     } else {
    1624           0 :       mRect.width = FlipOrResize(screenPoint.x, mRect.width, screenRect.x,
    1625             :                                  screenRect.XMost(), anchorRect.x, anchorRect.XMost(),
    1626             :                                  margin.left, margin.right, offsetForContextMenu.x, hFlip,
    1627             :                                  endAligned, &mHFlip);
    1628             :     }
    1629           0 :     mIsOffset = preOffsetScreenPoint != screenPoint.x;
    1630             : 
    1631           0 :     endAligned = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ||
    1632           0 :                  mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT;
    1633           0 :     preOffsetScreenPoint = screenPoint.y;
    1634           0 :     if (slideVertical) {
    1635           0 :       mRect.height = SlideOrResize(screenPoint.y, mRect.height, screenRect.y,
    1636             :                                   screenRect.YMost(), &mAlignmentOffset);
    1637             :     } else {
    1638           0 :       mRect.height = FlipOrResize(screenPoint.y, mRect.height, screenRect.y,
    1639             :                                   screenRect.YMost(), anchorRect.y, anchorRect.YMost(),
    1640             :                                   margin.top, margin.bottom, offsetForContextMenu.y, vFlip,
    1641             :                                   endAligned, &mVFlip);
    1642             :     }
    1643           0 :     mIsOffset = mIsOffset || (preOffsetScreenPoint != screenPoint.y);
    1644             : 
    1645           0 :     NS_ASSERTION(screenPoint.x >= screenRect.x && screenPoint.y >= screenRect.y &&
    1646             :                  screenPoint.x + mRect.width <= screenRect.XMost() &&
    1647             :                  screenPoint.y + mRect.height <= screenRect.YMost(),
    1648             :                  "Popup is offscreen");
    1649             :   }
    1650             : 
    1651             :   // snap the popup's position in screen coordinates to device pixels,
    1652             :   // see bug 622507, bug 961431
    1653           0 :   screenPoint.x = presContext->RoundAppUnitsToNearestDevPixels(screenPoint.x);
    1654           0 :   screenPoint.y = presContext->RoundAppUnitsToNearestDevPixels(screenPoint.y);
    1655             : 
    1656             :   // determine the x and y position of the view by subtracting the desired
    1657             :   // screen position from the screen position of the root frame.
    1658           0 :   nsPoint viewPoint = screenPoint - rootScreenRect.TopLeft();
    1659             : 
    1660           0 :   nsView* view = GetView();
    1661           0 :   NS_ASSERTION(view, "popup with no view");
    1662             : 
    1663             :   // Offset the position by the width and height of the borders and titlebar.
    1664             :   // Even though GetClientOffset should return (0, 0) when there is no
    1665             :   // titlebar or borders, we skip these calculations anyway for non-panels
    1666             :   // to save time since they will never have a titlebar.
    1667           0 :   nsIWidget* widget = view->GetWidget();
    1668           0 :   if (mPopupType == ePopupTypePanel && widget) {
    1669           0 :     mLastClientOffset = widget->GetClientOffset();
    1670           0 :     viewPoint.x += presContext->DevPixelsToAppUnits(mLastClientOffset.x);
    1671           0 :     viewPoint.y += presContext->DevPixelsToAppUnits(mLastClientOffset.y);
    1672             :   }
    1673             : 
    1674             :   presContext->GetPresShell()->GetViewManager()->
    1675           0 :     MoveViewTo(view, viewPoint.x, viewPoint.y);
    1676             : 
    1677             :   // Now that we've positioned the view, sync up the frame's origin.
    1678           0 :   nsBoxFrame::SetPosition(viewPoint - GetParent()->GetOffsetTo(rootFrame));
    1679             : 
    1680           0 :   if (aSizedToPopup) {
    1681           0 :     nsBoxLayoutState state(PresContext());
    1682             :     // XXXndeakin can parentSize.width still extend outside?
    1683           0 :     SetXULBounds(state, mRect);
    1684             :   }
    1685             : 
    1686             :   // If the popup is in the positioned state or if it is shown and the position
    1687             :   // or size changed, dispatch a popuppositioned event if the popup wants it.
    1688           0 :   nsIntRect newRect(screenPoint.x, screenPoint.y, mRect.width, mRect.height);
    1689           0 :   if (mPopupState == ePopupPositioning ||
    1690           0 :       (mPopupState == ePopupShown && !newRect.IsEqualEdges(mUsedScreenRect)) ||
    1691           0 :       (mPopupState == ePopupShown && oldAlignmentOffset != mAlignmentOffset)) {
    1692           0 :     mUsedScreenRect = newRect;
    1693           0 :     if (aNotify) {
    1694           0 :       nsXULPopupPositionedEvent::DispatchIfNeeded(mContent, false, false);
    1695             :     }
    1696             :   }
    1697             : 
    1698           0 :   return NS_OK;
    1699             : }
    1700             : 
    1701             : /* virtual */ nsMenuFrame*
    1702           0 : nsMenuPopupFrame::GetCurrentMenuItem()
    1703             : {
    1704           0 :   return mCurrentMenu;
    1705             : }
    1706             : 
    1707             : LayoutDeviceIntRect
    1708           0 : nsMenuPopupFrame::GetConstraintRect(const LayoutDeviceIntRect& aAnchorRect,
    1709             :                                     const LayoutDeviceIntRect& aRootScreenRect,
    1710             :                                     nsPopupLevel aPopupLevel)
    1711             : {
    1712           0 :   LayoutDeviceIntRect screenRectPixels;
    1713             : 
    1714             :   // determine the available screen space. It will be reduced by the OS chrome
    1715             :   // such as menubars. It addition, for content shells, it will be the area of
    1716             :   // the content rather than the screen.
    1717           0 :   nsCOMPtr<nsIScreen> screen;
    1718           0 :   nsCOMPtr<nsIScreenManager> sm(do_GetService("@mozilla.org/gfx/screenmanager;1"));
    1719           0 :   if (sm) {
    1720             :     // for content shells, get the screen where the root frame is located.
    1721             :     // This is because we need to constrain the content to this content area,
    1722             :     // so we should use the same screen. Otherwise, use the screen where the
    1723             :     // anchor is located.
    1724             :     DesktopToLayoutDeviceScale scale =
    1725           0 :       PresContext()->DeviceContext()->GetDesktopToDeviceScale();
    1726             :     DesktopRect rect =
    1727           0 :       (mInContentShell ? aRootScreenRect : aAnchorRect) / scale;
    1728           0 :     int32_t width = std::max(1, NSToIntRound(rect.width));
    1729           0 :     int32_t height = std::max(1, NSToIntRound(rect.height));
    1730           0 :     sm->ScreenForRect(rect.x, rect.y, width, height, getter_AddRefs(screen));
    1731           0 :     if (screen) {
    1732             :       // Non-top-level popups (which will always be panels)
    1733             :       // should never overlap the OS bar:
    1734           0 :       bool dontOverlapOSBar = aPopupLevel != ePopupLevelTop;
    1735             :       // get the total screen area if the popup is allowed to overlap it.
    1736           0 :       if (!dontOverlapOSBar && mMenuCanOverlapOSBar && !mInContentShell)
    1737           0 :         screen->GetRect(&screenRectPixels.x, &screenRectPixels.y,
    1738           0 :           &screenRectPixels.width, &screenRectPixels.height);
    1739             :       else
    1740           0 :         screen->GetAvailRect(&screenRectPixels.x, &screenRectPixels.y,
    1741           0 :           &screenRectPixels.width, &screenRectPixels.height);
    1742             :     }
    1743             :   }
    1744             : 
    1745           0 :   if (mInContentShell) {
    1746             :     // for content shells, clip to the client area rather than the screen area
    1747           0 :     screenRectPixels.IntersectRect(screenRectPixels, aRootScreenRect);
    1748             :   }
    1749           0 :   else if (!mOverrideConstraintRect.IsEmpty()) {
    1750             :     LayoutDeviceIntRect overrideConstrainRect =
    1751             :       LayoutDeviceIntRect::FromAppUnitsToNearest(mOverrideConstraintRect,
    1752           0 :                                                  PresContext()->AppUnitsPerDevPixel());
    1753             :     // This is currently only used for <select> elements where we want to constrain
    1754             :     // vertically to the screen but not horizontally, so do the intersection and then
    1755             :     // reset the horizontal values.
    1756           0 :     screenRectPixels.IntersectRect(screenRectPixels, overrideConstrainRect);
    1757           0 :     screenRectPixels.x = overrideConstrainRect.x;
    1758           0 :     screenRectPixels.width = overrideConstrainRect.width;
    1759             :   }
    1760             : 
    1761           0 :   return screenRectPixels;
    1762             : }
    1763             : 
    1764           0 : void nsMenuPopupFrame::CanAdjustEdges(Side aHorizontalSide,
    1765             :                                       Side aVerticalSide,
    1766             :                                       LayoutDeviceIntPoint& aChange)
    1767             : {
    1768           0 :   int8_t popupAlign(mPopupAlignment);
    1769           0 :   if (IsDirectionRTL()) {
    1770           0 :     popupAlign = -popupAlign;
    1771             :   }
    1772             : 
    1773           0 :   if (aHorizontalSide == (mHFlip ? eSideRight : eSideLeft)) {
    1774           0 :     if (popupAlign == POPUPALIGNMENT_TOPLEFT || popupAlign == POPUPALIGNMENT_BOTTOMLEFT) {
    1775           0 :       aChange.x = 0;
    1776             :     }
    1777             :   }
    1778           0 :   else if (aHorizontalSide == (mHFlip ? eSideLeft : eSideRight)) {
    1779           0 :     if (popupAlign == POPUPALIGNMENT_TOPRIGHT || popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
    1780           0 :       aChange.x = 0;
    1781             :     }
    1782             :   }
    1783             : 
    1784           0 :   if (aVerticalSide == (mVFlip ? eSideBottom : eSideTop)) {
    1785           0 :     if (popupAlign == POPUPALIGNMENT_TOPLEFT || popupAlign == POPUPALIGNMENT_TOPRIGHT) {
    1786           0 :       aChange.y = 0;
    1787             :     }
    1788             :   }
    1789           0 :   else if (aVerticalSide == (mVFlip ? eSideTop : eSideBottom)) {
    1790           0 :     if (popupAlign == POPUPALIGNMENT_BOTTOMLEFT || popupAlign == POPUPALIGNMENT_BOTTOMRIGHT) {
    1791           0 :       aChange.y = 0;
    1792             :     }
    1793             :   }
    1794           0 : }
    1795             : 
    1796           0 : ConsumeOutsideClicksResult nsMenuPopupFrame::ConsumeOutsideClicks()
    1797             : {
    1798             :   // If the popup has explicitly set a consume mode, honor that.
    1799           0 :   if (mConsumeRollupEvent != PopupBoxObject::ROLLUP_DEFAULT) {
    1800           0 :     return (mConsumeRollupEvent == PopupBoxObject::ROLLUP_CONSUME) ?
    1801           0 :            ConsumeOutsideClicks_True : ConsumeOutsideClicks_ParentOnly;
    1802             :   }
    1803             : 
    1804           0 :   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::consumeoutsideclicks,
    1805             :                             nsGkAtoms::_true, eCaseMatters)) {
    1806           0 :     return ConsumeOutsideClicks_True;
    1807             :   }
    1808           0 :   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::consumeoutsideclicks,
    1809             :                             nsGkAtoms::_false, eCaseMatters)) {
    1810           0 :     return ConsumeOutsideClicks_ParentOnly;
    1811             :   }
    1812           0 :   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::consumeoutsideclicks,
    1813             :                             nsGkAtoms::never, eCaseMatters)) {
    1814           0 :     return ConsumeOutsideClicks_Never;
    1815             :   }
    1816             : 
    1817           0 :   nsCOMPtr<nsIContent> parentContent = mContent->GetParent();
    1818           0 :   if (parentContent) {
    1819           0 :     dom::NodeInfo *ni = parentContent->NodeInfo();
    1820           0 :     if (ni->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL)) {
    1821           0 :       return ConsumeOutsideClicks_True;  // Consume outside clicks for combo boxes on all platforms
    1822             :     }
    1823             : #if defined(XP_WIN)
    1824             :     // Don't consume outside clicks for menus in Windows
    1825             :     if (ni->Equals(nsGkAtoms::menu, kNameSpaceID_XUL) ||
    1826             :         ni->Equals(nsGkAtoms::popupset, kNameSpaceID_XUL) ||
    1827             :         ((ni->Equals(nsGkAtoms::button, kNameSpaceID_XUL) ||
    1828             :           ni->Equals(nsGkAtoms::toolbarbutton, kNameSpaceID_XUL)) &&
    1829             :          (parentContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
    1830             :                                      nsGkAtoms::menu, eCaseMatters) ||
    1831             :           parentContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
    1832             :                                      nsGkAtoms::menuButton, eCaseMatters)))) {
    1833             :       return ConsumeOutsideClicks_Never;
    1834             :     }
    1835             : #endif
    1836           0 :     if (ni->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL)) {
    1837             :       // Don't consume outside clicks for autocomplete widget
    1838           0 :       if (parentContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
    1839             :                                      nsGkAtoms::autocomplete, eCaseMatters)) {
    1840           0 :         return ConsumeOutsideClicks_Never;
    1841             :       }
    1842             :     }
    1843             :   }
    1844             : 
    1845           0 :   return ConsumeOutsideClicks_True;
    1846             : }
    1847             : 
    1848             : // XXXroc this is megalame. Fossicking around for a frame of the right
    1849             : // type is a recipe for disaster in the long term.
    1850           0 : nsIScrollableFrame* nsMenuPopupFrame::GetScrollFrame(nsIFrame* aStart)
    1851             : {
    1852           0 :   if (!aStart)
    1853           0 :     return nullptr;
    1854             : 
    1855             :   // try start frame and siblings
    1856           0 :   nsIFrame* currFrame = aStart;
    1857           0 :   do {
    1858           0 :     nsIScrollableFrame* sf = do_QueryFrame(currFrame);
    1859           0 :     if (sf)
    1860           0 :       return sf;
    1861           0 :     currFrame = currFrame->GetNextSibling();
    1862           0 :   } while (currFrame);
    1863             : 
    1864             :   // try children
    1865           0 :   currFrame = aStart;
    1866           0 :   do {
    1867           0 :     nsIFrame* childFrame = currFrame->PrincipalChildList().FirstChild();
    1868           0 :     nsIScrollableFrame* sf = GetScrollFrame(childFrame);
    1869           0 :     if (sf)
    1870           0 :       return sf;
    1871           0 :     currFrame = currFrame->GetNextSibling();
    1872           0 :   } while (currFrame);
    1873             : 
    1874           0 :   return nullptr;
    1875             : }
    1876             : 
    1877           0 : void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsMenuFrame* aMenuItem)
    1878             : {
    1879           0 :   if (aMenuItem) {
    1880           0 :     aMenuItem->PresContext()->PresShell()->ScrollFrameRectIntoView(
    1881             :       aMenuItem,
    1882           0 :       nsRect(nsPoint(0,0), aMenuItem->GetRect().Size()),
    1883             :       nsIPresShell::ScrollAxis(),
    1884             :       nsIPresShell::ScrollAxis(),
    1885             :       nsIPresShell::SCROLL_OVERFLOW_HIDDEN |
    1886           0 :       nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY);
    1887             :   }
    1888           0 : }
    1889             : 
    1890           0 : void nsMenuPopupFrame::ChangeByPage(bool aIsUp)
    1891             : {
    1892             :   // Only scroll by page within menulists.
    1893           0 :   if (!IsMenuList()) {
    1894           0 :     return;
    1895             :   }
    1896             : 
    1897           0 :   nsMenuFrame* newMenu = nullptr;
    1898           0 :   nsIFrame* currentMenu = mCurrentMenu;
    1899           0 :   if (!currentMenu) {
    1900             :     // If there is no current menu item, get the first item. When moving up,
    1901             :     // just use this as the newMenu and leave currentMenu null so that no
    1902             :     // check for a later element is performed. When moving down, set currentMenu
    1903             :     // so that we look for one page down from the first item.
    1904           0 :     newMenu = nsXULPopupManager::GetNextMenuItem(this, nullptr, true, false);
    1905           0 :     if (!aIsUp) {
    1906           0 :       currentMenu = newMenu;
    1907             :     }
    1908             :   }
    1909             : 
    1910           0 :   if (currentMenu) {
    1911           0 :     nscoord scrollHeight = mRect.height;
    1912           0 :     nsIScrollableFrame *scrollframe = GetScrollFrame(this);
    1913           0 :     if (scrollframe) {
    1914           0 :       scrollHeight = scrollframe->GetScrollPortRect().height;
    1915             :     }
    1916             : 
    1917             :     // Get the position of the current item and add or subtract one popup's
    1918             :     // height to or from it.
    1919           0 :     nscoord targetPosition = aIsUp ? currentMenu->GetRect().YMost() - scrollHeight :
    1920           0 :                                      currentMenu->GetRect().y + scrollHeight;
    1921             : 
    1922             :     // Indicates that the last visible child was a valid menuitem.
    1923           0 :     bool lastWasValid = false;
    1924             : 
    1925             :     // Look for the next child which is just past the target position. This child
    1926             :     // will need to be selected.
    1927           0 :     while (currentMenu) {
    1928             :       // Only consider menu frames.
    1929           0 :       nsMenuFrame* menuFrame = do_QueryFrame(currentMenu);
    1930           0 :       if (menuFrame &&
    1931           0 :           nsXULPopupManager::IsValidMenuItem(menuFrame->GetContent(), true)) {
    1932             : 
    1933             :         // If the right position was found, break out. Otherwise, look for another item.
    1934           0 :         if ((!aIsUp && currentMenu->GetRect().YMost() > targetPosition) ||
    1935           0 :             (aIsUp && currentMenu->GetRect().y < targetPosition)) {
    1936             : 
    1937             :           // If the last visible child was not a valid menuitem or was disabled,
    1938             :           // use this as the menu to select, skipping over any non-valid items at
    1939             :           // the edge of the page.
    1940           0 :           if (!lastWasValid) {
    1941           0 :             newMenu = menuFrame;
    1942             :           }
    1943             : 
    1944           0 :           break;
    1945             :         }
    1946             : 
    1947             :         // Assign this item to newMenu. This item will be selected in case we
    1948             :         // don't find any more.
    1949           0 :         lastWasValid = true;
    1950           0 :         newMenu = menuFrame;
    1951             :       }
    1952             :       else {
    1953           0 :         lastWasValid = false;
    1954             :       }
    1955             : 
    1956           0 :       currentMenu = aIsUp ? currentMenu->GetPrevSibling() :
    1957             :                             currentMenu->GetNextSibling();
    1958             :     }
    1959             :   }
    1960             : 
    1961             :   // Select the new menuitem.
    1962           0 :   if (newMenu) {
    1963           0 :     ChangeMenuItem(newMenu, false, true);
    1964             :   }
    1965             : }
    1966             : 
    1967           0 : NS_IMETHODIMP nsMenuPopupFrame::SetCurrentMenuItem(nsMenuFrame* aMenuItem)
    1968             : {
    1969           0 :   if (mCurrentMenu == aMenuItem)
    1970           0 :     return NS_OK;
    1971             : 
    1972           0 :   if (mCurrentMenu) {
    1973           0 :     mCurrentMenu->SelectMenu(false);
    1974             :   }
    1975             : 
    1976           0 :   if (aMenuItem) {
    1977           0 :     EnsureMenuItemIsVisible(aMenuItem);
    1978           0 :     aMenuItem->SelectMenu(true);
    1979             :   }
    1980             : 
    1981           0 :   mCurrentMenu = aMenuItem;
    1982             : 
    1983           0 :   return NS_OK;
    1984             : }
    1985             : 
    1986             : void
    1987           0 : nsMenuPopupFrame::CurrentMenuIsBeingDestroyed()
    1988             : {
    1989           0 :   mCurrentMenu = nullptr;
    1990           0 : }
    1991             : 
    1992             : NS_IMETHODIMP
    1993           0 : nsMenuPopupFrame::ChangeMenuItem(nsMenuFrame* aMenuItem,
    1994             :                                  bool aSelectFirstItem,
    1995             :                                  bool aFromKey)
    1996             : {
    1997           0 :   if (mCurrentMenu == aMenuItem)
    1998           0 :     return NS_OK;
    1999             : 
    2000             :   // When a context menu is open, the current menu is locked, and no change
    2001             :   // to the menu is allowed.
    2002           0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2003           0 :   if (!mIsContextMenu && pm && pm->HasContextMenu(this))
    2004           0 :     return NS_OK;
    2005             : 
    2006             :   // Unset the current child.
    2007           0 :   if (mCurrentMenu) {
    2008           0 :     mCurrentMenu->SelectMenu(false);
    2009           0 :     nsMenuPopupFrame* popup = mCurrentMenu->GetPopup();
    2010           0 :     if (popup) {
    2011           0 :       if (mCurrentMenu->IsOpen()) {
    2012           0 :         if (pm)
    2013           0 :           pm->HidePopupAfterDelay(popup);
    2014             :       }
    2015             :     }
    2016             :   }
    2017             : 
    2018             :   // Set the new child.
    2019           0 :   if (aMenuItem) {
    2020           0 :     EnsureMenuItemIsVisible(aMenuItem);
    2021           0 :     aMenuItem->SelectMenu(true);
    2022             : 
    2023             :     // On Windows, a menulist should update its value whenever navigation was
    2024             :     // done by the keyboard.
    2025             : #ifdef XP_WIN
    2026             :     if (aFromKey && IsOpen() && IsMenuList()) {
    2027             :       // Fire a command event as the new item, but we don't want to close
    2028             :       // the menu, blink it, or update any other state of the menuitem. The
    2029             :       // command event will cause the item to be selected.
    2030             :       nsContentUtils::DispatchXULCommand(aMenuItem->GetContent(), /* aTrusted = */ true,
    2031             :                                          nullptr, PresContext()->PresShell(),
    2032             :                                          false, false, false, false);
    2033             :     }
    2034             : #endif
    2035             :   }
    2036             : 
    2037           0 :   mCurrentMenu = aMenuItem;
    2038             : 
    2039           0 :   return NS_OK;
    2040             : }
    2041             : 
    2042             : nsMenuFrame*
    2043           0 : nsMenuPopupFrame::Enter(WidgetGUIEvent* aEvent)
    2044             : {
    2045           0 :   mIncrementalString.Truncate();
    2046             : 
    2047             :   // Give it to the child.
    2048           0 :   if (mCurrentMenu)
    2049           0 :     return mCurrentMenu->Enter(aEvent);
    2050             : 
    2051           0 :   return nullptr;
    2052             : }
    2053             : 
    2054             : nsMenuFrame*
    2055           0 : nsMenuPopupFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent, bool& doAction)
    2056             : {
    2057             :   uint32_t charCode, keyCode;
    2058           0 :   aKeyEvent->GetCharCode(&charCode);
    2059           0 :   aKeyEvent->GetKeyCode(&keyCode);
    2060             : 
    2061           0 :   doAction = false;
    2062             : 
    2063             :   // Enumerate over our list of frames.
    2064             :   auto insertion = PresContext()->PresShell()->
    2065           0 :     FrameConstructor()->GetInsertionPoint(GetContent(), nullptr);
    2066           0 :   nsContainerFrame* immediateParent = insertion.mParentFrame;
    2067           0 :   if (!immediateParent)
    2068           0 :     immediateParent = this;
    2069             : 
    2070           0 :   uint32_t matchCount = 0, matchShortcutCount = 0;
    2071           0 :   bool foundActive = false;
    2072             :   bool isShortcut;
    2073           0 :   nsMenuFrame* frameBefore = nullptr;
    2074           0 :   nsMenuFrame* frameAfter = nullptr;
    2075           0 :   nsMenuFrame* frameShortcut = nullptr;
    2076             : 
    2077           0 :   nsIContent* parentContent = mContent->GetParent();
    2078             : 
    2079           0 :   bool isMenu = parentContent &&
    2080           0 :                   !parentContent->NodeInfo()->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL);
    2081             : 
    2082             :   DOMTimeStamp keyTime;
    2083           0 :   aKeyEvent->AsEvent()->GetTimeStamp(&keyTime);
    2084             : 
    2085           0 :   if (charCode == 0) {
    2086           0 :     if (keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) {
    2087           0 :       if (!isMenu && !mIncrementalString.IsEmpty()) {
    2088           0 :         mIncrementalString.SetLength(mIncrementalString.Length() - 1);
    2089           0 :         return nullptr;
    2090             :       }
    2091             :       else {
    2092             : #ifdef XP_WIN
    2093             :         nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
    2094             :         if (soundInterface)
    2095             :           soundInterface->Beep();
    2096             : #endif  // #ifdef XP_WIN
    2097             :       }
    2098             :     }
    2099           0 :     return nullptr;
    2100             :   }
    2101             :   else {
    2102           0 :     char16_t uniChar = ToLowerCase(static_cast<char16_t>(charCode));
    2103           0 :     if (isMenu) {
    2104             :       // Menu supports only first-letter navigation
    2105           0 :       mIncrementalString = uniChar;
    2106           0 :     } else if (IsWithinIncrementalTime(keyTime)) {
    2107           0 :       mIncrementalString.Append(uniChar);
    2108             :     } else {
    2109             :       // Interval too long, treat as new typing
    2110           0 :       mIncrementalString = uniChar;
    2111             :     }
    2112             :   }
    2113             : 
    2114             :   // See bug 188199 & 192346, if all letters in incremental string are same, just try to match the first one
    2115           0 :   nsAutoString incrementalString(mIncrementalString);
    2116           0 :   uint32_t charIndex = 1, stringLength = incrementalString.Length();
    2117           0 :   while (charIndex < stringLength && incrementalString[charIndex] == incrementalString[charIndex - 1]) {
    2118           0 :     charIndex++;
    2119             :   }
    2120           0 :   if (charIndex == stringLength) {
    2121           0 :     incrementalString.Truncate(1);
    2122           0 :     stringLength = 1;
    2123             :   }
    2124             : 
    2125           0 :   sLastKeyTime = keyTime;
    2126             : 
    2127             :   // NOTE: If you crashed here due to a bogus |immediateParent| it is
    2128             :   //       possible that the menu whose shortcut is being looked up has
    2129             :   //       been destroyed already.  One strategy would be to
    2130             :   //       setTimeout(<func>,0) as detailed in:
    2131             :   //       <http://bugzilla.mozilla.org/show_bug.cgi?id=126675#c32>
    2132           0 :   nsIFrame* firstMenuItem = nsXULPopupManager::GetNextMenuItem(immediateParent, nullptr, true, false);
    2133           0 :   nsIFrame* currFrame = firstMenuItem;
    2134             : 
    2135           0 :   int32_t menuAccessKey = -1;
    2136           0 :   nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
    2137             : 
    2138             :   // We start searching from first child. This process is divided into two parts
    2139             :   //   -- before current and after current -- by the current item
    2140           0 :   while (currFrame) {
    2141           0 :     nsIContent* current = currFrame->GetContent();
    2142           0 :     nsAutoString textKey;
    2143           0 :     if (menuAccessKey >= 0) {
    2144             :       // Get the shortcut attribute.
    2145           0 :       current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, textKey);
    2146             :     }
    2147           0 :     if (textKey.IsEmpty()) { // No shortcut, try first letter
    2148           0 :       isShortcut = false;
    2149           0 :       current->GetAttr(kNameSpaceID_None, nsGkAtoms::label, textKey);
    2150           0 :       if (textKey.IsEmpty()) // No label, try another attribute (value)
    2151           0 :         current->GetAttr(kNameSpaceID_None, nsGkAtoms::value, textKey);
    2152             :     }
    2153             :     else
    2154           0 :       isShortcut = true;
    2155             : 
    2156           0 :     if (StringBeginsWith(textKey, incrementalString,
    2157           0 :                          nsCaseInsensitiveStringComparator())) {
    2158             :       // mIncrementalString is a prefix of textKey
    2159           0 :       nsMenuFrame* menu = do_QueryFrame(currFrame);
    2160           0 :       if (menu) {
    2161             :         // There is one match
    2162           0 :         matchCount++;
    2163           0 :         if (isShortcut) {
    2164             :           // There is one shortcut-key match
    2165           0 :           matchShortcutCount++;
    2166             :           // Record the matched item. If there is only one matched shortcut item, do it
    2167           0 :           frameShortcut = menu;
    2168             :         }
    2169           0 :         if (!foundActive) {
    2170             :           // It's a first candidate item located before/on the current item
    2171           0 :           if (!frameBefore)
    2172           0 :             frameBefore = menu;
    2173             :         }
    2174             :         else {
    2175             :           // It's a first candidate item located after the current item
    2176           0 :           if (!frameAfter)
    2177           0 :             frameAfter = menu;
    2178             :         }
    2179             :       }
    2180             :       else
    2181           0 :         return nullptr;
    2182             :     }
    2183             : 
    2184             :     // Get the active status
    2185           0 :     if (current->AttrValueIs(kNameSpaceID_None, nsGkAtoms::menuactive,
    2186             :                              nsGkAtoms::_true, eCaseMatters)) {
    2187           0 :       foundActive = true;
    2188           0 :       if (stringLength > 1) {
    2189             :         // If there is more than one char typed, the current item has highest priority,
    2190             :         //   otherwise the item next to current has highest priority
    2191           0 :         if (currFrame == frameBefore)
    2192           0 :           return frameBefore;
    2193             :       }
    2194             :     }
    2195             : 
    2196           0 :     nsMenuFrame* menu = do_QueryFrame(currFrame);
    2197           0 :     currFrame = nsXULPopupManager::GetNextMenuItem(immediateParent, menu, true, true);
    2198           0 :     if (currFrame == firstMenuItem)
    2199           0 :       break;
    2200             :   }
    2201             : 
    2202           0 :   doAction = (isMenu && (matchCount == 1 || matchShortcutCount == 1));
    2203             : 
    2204           0 :   if (matchShortcutCount == 1) // We have one matched shortcut item
    2205           0 :     return frameShortcut;
    2206           0 :   if (frameAfter) // If we have matched item after the current, use it
    2207           0 :     return frameAfter;
    2208           0 :   else if (frameBefore) // If we haven't, use the item before the current
    2209           0 :     return frameBefore;
    2210             : 
    2211             :   // If we don't match anything, rollback the last typing
    2212           0 :   mIncrementalString.SetLength(mIncrementalString.Length() - 1);
    2213             : 
    2214             :   // didn't find a matching menu item
    2215             : #ifdef XP_WIN
    2216             :   // behavior on Windows - this item is in a menu popup off of the
    2217             :   // menu bar, so beep and do nothing else
    2218             :   if (isMenu) {
    2219             :     nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1");
    2220             :     if (soundInterface)
    2221             :       soundInterface->Beep();
    2222             :   }
    2223             : #endif  // #ifdef XP_WIN
    2224             : 
    2225           0 :   return nullptr;
    2226             : }
    2227             : 
    2228             : void
    2229           0 : nsMenuPopupFrame::LockMenuUntilClosed(bool aLock)
    2230             : {
    2231           0 :   mIsMenuLocked = aLock;
    2232             : 
    2233             :   // Lock / unlock the parent, too.
    2234           0 :   nsMenuFrame* menu = do_QueryFrame(GetParent());
    2235           0 :   if (menu) {
    2236           0 :     nsMenuParent* parentParent = menu->GetMenuParent();
    2237           0 :     if (parentParent) {
    2238           0 :       parentParent->LockMenuUntilClosed(aLock);
    2239             :     }
    2240             :   }
    2241           0 : }
    2242             : 
    2243             : nsIWidget*
    2244           0 : nsMenuPopupFrame::GetWidget()
    2245             : {
    2246           0 :   nsView * view = GetRootViewForPopup(this);
    2247           0 :   if (!view)
    2248           0 :     return nullptr;
    2249             : 
    2250           0 :   return view->GetWidget();
    2251             : }
    2252             : 
    2253             : void
    2254           0 : nsMenuPopupFrame::AttachedDismissalListener()
    2255             : {
    2256           0 :   mConsumeRollupEvent = PopupBoxObject::ROLLUP_DEFAULT;
    2257           0 : }
    2258             : 
    2259             : // helpers /////////////////////////////////////////////////////////////
    2260             : 
    2261             : nsresult
    2262           1 : nsMenuPopupFrame::AttributeChanged(int32_t aNameSpaceID,
    2263             :                                    nsIAtom* aAttribute,
    2264             :                                    int32_t aModType)
    2265             : 
    2266             : {
    2267           1 :   nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
    2268           1 :                                              aModType);
    2269             : 
    2270           1 :   if (aAttribute == nsGkAtoms::left || aAttribute == nsGkAtoms::top)
    2271           0 :     MoveToAttributePosition();
    2272             : 
    2273             : #ifndef MOZ_GTK2
    2274           1 :   if (aAttribute == nsGkAtoms::noautohide) {
    2275           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2276           0 :     if (pm)
    2277           0 :       pm->EnableRollup(mContent, !IsNoAutoHide());
    2278             :   }
    2279             : #endif
    2280             : 
    2281           1 :   if (aAttribute == nsGkAtoms::remote) {
    2282             :     // When the remote attribute changes, we need to create a new widget to
    2283             :     // ensure that it has the correct compositor and transparency settings to
    2284             :     // match the new value.
    2285           0 :     EnsureWidget(true);
    2286             :   }
    2287             : 
    2288           1 :   if (aAttribute == nsGkAtoms::followanchor) {
    2289           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2290           0 :     if (pm) {
    2291           0 :       pm->UpdateFollowAnchor(this);
    2292             :     }
    2293             :   }
    2294             : 
    2295           1 :   if (aAttribute == nsGkAtoms::label) {
    2296             :     // set the label for the titlebar
    2297           0 :     nsView* view = GetView();
    2298           0 :     if (view) {
    2299           0 :       nsIWidget* widget = view->GetWidget();
    2300           0 :       if (widget) {
    2301           0 :         nsAutoString title;
    2302           0 :         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, title);
    2303           0 :         if (!title.IsEmpty()) {
    2304           0 :           widget->SetTitle(title);
    2305             :         }
    2306             :       }
    2307             :     }
    2308           1 :   } else if (aAttribute == nsGkAtoms::ignorekeys) {
    2309           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2310           0 :     if (pm) {
    2311           0 :       nsAutoString ignorekeys;
    2312           0 :       mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::ignorekeys, ignorekeys);
    2313           0 :       pm->UpdateIgnoreKeys(ignorekeys.EqualsLiteral("true"));
    2314             :     }
    2315             :   }
    2316             : 
    2317           1 :   return rv;
    2318             : }
    2319             : 
    2320             : void
    2321           0 : nsMenuPopupFrame::MoveToAttributePosition()
    2322             : {
    2323             :   // Move the widget around when the user sets the |left| and |top| attributes.
    2324             :   // Note that this is not the best way to move the widget, as it results in lots
    2325             :   // of FE notifications and is likely to be slow as molasses. Use |moveTo| on
    2326             :   // PopupBoxObject if possible.
    2327           0 :   nsAutoString left, top;
    2328           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::left, left);
    2329           0 :   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::top, top);
    2330             :   nsresult err1, err2;
    2331           0 :   mozilla::CSSIntPoint pos(left.ToInteger(&err1), top.ToInteger(&err2));
    2332             : 
    2333           0 :   if (NS_SUCCEEDED(err1) && NS_SUCCEEDED(err2))
    2334           0 :     MoveTo(pos, false);
    2335           0 : }
    2336             : 
    2337             : void
    2338           7 : nsMenuPopupFrame::DestroyFrom(nsIFrame* aDestructRoot)
    2339             : {
    2340           7 :   if (mReflowCallbackData.mPosted) {
    2341           0 :     PresContext()->PresShell()->CancelReflowCallback(this);
    2342           0 :     mReflowCallbackData.Clear();
    2343             :   }
    2344             : 
    2345           7 :   nsMenuFrame* menu = do_QueryFrame(GetParent());
    2346           7 :   if (menu) {
    2347             :     // clear the open attribute on the parent menu
    2348             :     nsContentUtils::AddScriptRunner(
    2349           2 :       new nsUnsetAttrRunnable(menu->GetContent(), nsGkAtoms::open));
    2350             :   }
    2351             : 
    2352           7 :   ClearPopupShownDispatcher();
    2353             : 
    2354           7 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2355           7 :   if (pm)
    2356           7 :     pm->PopupDestroyed(this);
    2357             : 
    2358             :   nsIRootBox* rootBox =
    2359           7 :     nsIRootBox::GetRootBox(PresContext()->GetPresShell());
    2360           7 :   if (rootBox && rootBox->GetDefaultTooltip() == mContent) {
    2361           0 :     rootBox->SetDefaultTooltip(nullptr);
    2362             :   }
    2363             : 
    2364           7 :   nsBoxFrame::DestroyFrom(aDestructRoot);
    2365           7 : }
    2366             : 
    2367             : 
    2368             : void
    2369           0 : nsMenuPopupFrame::MoveTo(const CSSIntPoint& aPos, bool aUpdateAttrs)
    2370             : {
    2371           0 :   nsIWidget* widget = GetWidget();
    2372           0 :   if ((mScreenRect.x == aPos.x && mScreenRect.y == aPos.y) &&
    2373           0 :       (!widget || widget->GetClientOffset() == mLastClientOffset)) {
    2374           0 :     return;
    2375             :   }
    2376             : 
    2377             :   // reposition the popup at the specified coordinates. Don't clear the anchor
    2378             :   // and position, because the popup can be reset to its anchor position by
    2379             :   // using (-1, -1) as coordinates. Subtract off the margin as it will be
    2380             :   // added to the position when SetPopupPosition is called.
    2381           0 :   nsMargin margin(0, 0, 0, 0);
    2382           0 :   StyleMargin()->GetMargin(margin);
    2383             : 
    2384             :   // Workaround for bug 788189.  See also bug 708278 comment #25 and following.
    2385           0 :   if (mAdjustOffsetForContextMenu) {
    2386           0 :     margin.left += nsPresContext::CSSPixelsToAppUnits(LookAndFeel::GetInt(
    2387           0 :                      LookAndFeel::eIntID_ContextMenuOffsetHorizontal));
    2388           0 :     margin.top += nsPresContext::CSSPixelsToAppUnits(LookAndFeel::GetInt(
    2389           0 :                      LookAndFeel::eIntID_ContextMenuOffsetVertical));
    2390             :   }
    2391             : 
    2392           0 :   nsPresContext* presContext = PresContext();
    2393           0 :   mAnchorType = MenuPopupAnchorType_Point;
    2394           0 :   mScreenRect.x = aPos.x - presContext->AppUnitsToIntCSSPixels(margin.left);
    2395           0 :   mScreenRect.y = aPos.y - presContext->AppUnitsToIntCSSPixels(margin.top);
    2396             : 
    2397           0 :   SetPopupPosition(nullptr, true, false, true);
    2398             : 
    2399           0 :   nsCOMPtr<nsIContent> popup = mContent;
    2400           0 :   if (aUpdateAttrs && (popup->HasAttr(kNameSpaceID_None, nsGkAtoms::left) ||
    2401           0 :                        popup->HasAttr(kNameSpaceID_None, nsGkAtoms::top)))
    2402             :   {
    2403           0 :     nsAutoString left, top;
    2404           0 :     left.AppendInt(aPos.x);
    2405           0 :     top.AppendInt(aPos.y);
    2406           0 :     popup->SetAttr(kNameSpaceID_None, nsGkAtoms::left, left, false);
    2407           0 :     popup->SetAttr(kNameSpaceID_None, nsGkAtoms::top, top, false);
    2408             :   }
    2409             : }
    2410             : 
    2411             : void
    2412           0 : nsMenuPopupFrame::MoveToAnchor(nsIContent* aAnchorContent,
    2413             :                                const nsAString& aPosition,
    2414             :                                int32_t aXPos, int32_t aYPos,
    2415             :                                bool aAttributesOverride)
    2416             : {
    2417           0 :   NS_ASSERTION(IsVisible(), "popup must be visible to move it");
    2418             : 
    2419           0 :   nsPopupState oldstate = mPopupState;
    2420           0 :   InitializePopup(aAnchorContent, mTriggerContent, aPosition,
    2421           0 :                   aXPos, aYPos, MenuPopupAnchorType_Node, aAttributesOverride);
    2422             :   // InitializePopup changed the state so reset it.
    2423           0 :   mPopupState = oldstate;
    2424             : 
    2425             :   // Pass false here so that flipping and adjusting to fit on the screen happen.
    2426           0 :   SetPopupPosition(nullptr, false, false, true);
    2427           0 : }
    2428             : 
    2429             : bool
    2430           0 : nsMenuPopupFrame::GetAutoPosition()
    2431             : {
    2432           0 :   return mShouldAutoPosition;
    2433             : }
    2434             : 
    2435             : void
    2436           0 : nsMenuPopupFrame::SetAutoPosition(bool aShouldAutoPosition)
    2437             : {
    2438           0 :   mShouldAutoPosition = aShouldAutoPosition;
    2439           0 :   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2440           0 :   if (pm) {
    2441           0 :     pm->UpdateFollowAnchor(this);
    2442             :   }
    2443           0 : }
    2444             : 
    2445             : void
    2446           0 : nsMenuPopupFrame::SetConsumeRollupEvent(uint32_t aConsumeMode)
    2447             : {
    2448           0 :   mConsumeRollupEvent = aConsumeMode;
    2449           0 : }
    2450             : 
    2451             : int8_t
    2452           0 : nsMenuPopupFrame::GetAlignmentPosition() const
    2453             : {
    2454             :   // The code below handles most cases of alignment, anchor and position values. Those that are
    2455             :   // not handled just return POPUPPOSITION_UNKNOWN.
    2456             : 
    2457           0 :   if (mPosition == POPUPPOSITION_OVERLAP || mPosition == POPUPPOSITION_AFTERPOINTER ||
    2458           0 :       mPosition == POPUPPOSITION_SELECTION)
    2459           0 :     return mPosition;
    2460             : 
    2461           0 :   int8_t position = mPosition;
    2462             : 
    2463           0 :   if (position == POPUPPOSITION_UNKNOWN) {
    2464           0 :     switch (mPopupAnchor) {
    2465             :       case POPUPALIGNMENT_BOTTOMCENTER:
    2466           0 :         position = mPopupAlignment == POPUPALIGNMENT_TOPRIGHT ?
    2467           0 :                      POPUPPOSITION_AFTEREND : POPUPPOSITION_AFTERSTART;
    2468           0 :         break;
    2469             :       case POPUPALIGNMENT_TOPCENTER:
    2470           0 :         position = mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT ?
    2471           0 :                      POPUPPOSITION_BEFOREEND : POPUPPOSITION_BEFORESTART;
    2472           0 :         break;
    2473             :       case POPUPALIGNMENT_LEFTCENTER:
    2474           0 :         position = mPopupAlignment == POPUPALIGNMENT_BOTTOMRIGHT ?
    2475           0 :                      POPUPPOSITION_STARTAFTER : POPUPPOSITION_STARTBEFORE;
    2476           0 :         break;
    2477             :       case POPUPALIGNMENT_RIGHTCENTER:
    2478           0 :         position = mPopupAlignment == POPUPALIGNMENT_BOTTOMLEFT ?
    2479           0 :                      POPUPPOSITION_ENDAFTER : POPUPPOSITION_ENDBEFORE;
    2480           0 :         break;
    2481             :       default:
    2482           0 :         break;
    2483             :     }
    2484             :   }
    2485             : 
    2486           0 :   if (mHFlip) {
    2487           0 :     position = POPUPPOSITION_HFLIP(position);
    2488             :   }
    2489             : 
    2490           0 :   if (mVFlip) {
    2491           0 :     position = POPUPPOSITION_VFLIP(position);
    2492             :   }
    2493             : 
    2494           0 :   return position;
    2495             : }
    2496             : 
    2497             : /**
    2498             :  * KEEP THIS IN SYNC WITH nsFrame::CreateView
    2499             :  * as much as possible. Until we get rid of views finally...
    2500             :  */
    2501             : void
    2502          44 : nsMenuPopupFrame::CreatePopupView()
    2503             : {
    2504          44 :   if (HasView()) {
    2505           0 :     return;
    2506             :   }
    2507             : 
    2508          44 :   nsViewManager* viewManager = PresContext()->GetPresShell()->GetViewManager();
    2509          44 :   NS_ASSERTION(nullptr != viewManager, "null view manager");
    2510             : 
    2511             :   // Create a view
    2512          44 :   nsView* parentView = viewManager->GetRootView();
    2513          44 :   nsViewVisibility visibility = nsViewVisibility_kHide;
    2514          44 :   int32_t zIndex = INT32_MAX;
    2515          44 :   bool    autoZIndex = false;
    2516             : 
    2517          44 :   NS_ASSERTION(parentView, "no parent view");
    2518             : 
    2519             :   // Create a view
    2520          44 :   nsView *view = viewManager->CreateView(GetRect(), parentView, visibility);
    2521          44 :   viewManager->SetViewZIndex(view, autoZIndex, zIndex);
    2522             :   // XXX put view last in document order until we can do better
    2523          44 :   viewManager->InsertChild(parentView, view, nullptr, true);
    2524             : 
    2525             :   // Remember our view
    2526          44 :   SetView(view);
    2527             : 
    2528          44 :   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
    2529             :     ("nsMenuPopupFrame::CreatePopupView: frame=%p view=%p", this, view));
    2530             : }
    2531             : 
    2532             : bool
    2533           0 : nsMenuPopupFrame::ShouldFollowAnchor()
    2534             : {
    2535           0 :   if (!mShouldAutoPosition ||
    2536           0 :       mAnchorType != MenuPopupAnchorType_Node || !mAnchorContent) {
    2537           0 :     return false;
    2538             :   }
    2539             : 
    2540             :   // Follow anchor mode is used when followanchor="true" is set or for arrow panels.
    2541           0 :   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::followanchor,
    2542             :                             nsGkAtoms::_true, eCaseMatters)) {
    2543           0 :     return true;
    2544             :   }
    2545             : 
    2546           0 :   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::followanchor,
    2547             :                             nsGkAtoms::_false, eCaseMatters)) {
    2548           0 :     return false;
    2549             :   }
    2550             : 
    2551           0 :   return (mPopupType == ePopupTypePanel &&
    2552           0 :           mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
    2553           0 :                                 nsGkAtoms::arrow, eCaseMatters));
    2554             : }
    2555             : 
    2556             : bool
    2557           0 : nsMenuPopupFrame::ShouldFollowAnchor(nsRect& aRect)
    2558             : {
    2559           0 :   if (!ShouldFollowAnchor()) {
    2560           0 :     return false;
    2561             :   }
    2562             : 
    2563           0 :   nsIFrame* anchorFrame = mAnchorContent->GetPrimaryFrame();
    2564           0 :   if (anchorFrame) {
    2565           0 :     nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
    2566           0 :     if (rootPresContext) {
    2567           0 :       aRect = ComputeAnchorRect(rootPresContext, anchorFrame);
    2568             :     }
    2569             :   }
    2570             : 
    2571           0 :   return true;
    2572             : }
    2573             : 
    2574             : void
    2575           0 : nsMenuPopupFrame::CheckForAnchorChange(nsRect& aRect)
    2576             : {
    2577             :   // Don't update if the popup isn't visible or we shouldn't be following the anchor.
    2578           0 :   if (!IsVisible() || !ShouldFollowAnchor()) {
    2579           0 :     return;
    2580             :   }
    2581             : 
    2582           0 :   bool shouldHide = false;
    2583             : 
    2584           0 :   nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
    2585             : 
    2586             :   // If the frame for the anchor has gone away, hide the popup.
    2587           0 :   nsIFrame* anchor = mAnchorContent->GetPrimaryFrame();
    2588           0 :   if (!anchor || !rootPresContext) {
    2589           0 :     shouldHide = true;
    2590           0 :   } else if (!anchor->IsVisibleConsideringAncestors(VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
    2591             :     // If the anchor is now inside something that is invisible, hide the popup.
    2592           0 :     shouldHide = true;
    2593             :   } else {
    2594             :     // If the anchor is now inside a hidden parent popup, hide the popup.
    2595           0 :     nsIFrame* frame = anchor;
    2596           0 :     while (frame) {
    2597           0 :       nsMenuPopupFrame* popup = do_QueryFrame(frame);
    2598           0 :       if (popup && popup->PopupState() != ePopupShown) {
    2599           0 :         shouldHide = true;
    2600           0 :         break;
    2601             :       }
    2602             : 
    2603           0 :       frame = frame->GetParent();
    2604             :     }
    2605             :   }
    2606             : 
    2607           0 :   if (shouldHide) {
    2608           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    2609           0 :     if (pm) {
    2610             :       // As the caller will be iterating over the open popups, hide asyncronously.
    2611           0 :       pm->HidePopup(mContent, false, true, true, false);
    2612             :     }
    2613             : 
    2614           0 :     return;
    2615             :   }
    2616             : 
    2617           0 :   nsRect anchorRect = ComputeAnchorRect(rootPresContext, anchor);
    2618             : 
    2619             :   // If the rectangles are different, move the popup.
    2620           0 :   if (!anchorRect.IsEqualEdges(aRect)) {
    2621           0 :     aRect = anchorRect;
    2622           0 :     SetPopupPosition(nullptr, true, false, true);
    2623             :   }
    2624             : }

Generated by: LCOV version 1.13