tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsMenuPopupFrame.h (26870B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 //
      8 // nsMenuPopupFrame
      9 //
     10 
     11 #ifndef nsMenuPopupFrame_h__
     12 #define nsMenuPopupFrame_h__
     13 
     14 #include "Units.h"
     15 #include "mozilla/Attributes.h"
     16 #include "mozilla/StaticPrefs_ui.h"
     17 #include "mozilla/TimeStamp.h"
     18 #include "mozilla/gfx/Types.h"
     19 #include "nsAtom.h"
     20 #include "nsBlockFrame.h"
     21 #include "nsCOMPtr.h"
     22 #include "nsExpirationState.h"
     23 #include "nsIDOMEventListener.h"
     24 #include "nsIWidgetListener.h"
     25 #include "nsXULPopupManager.h"
     26 
     27 class nsIWidget;
     28 
     29 namespace mozilla {
     30 class PresShell;
     31 enum class WindowShadow : uint8_t;
     32 namespace dom {
     33 class KeyboardEvent;
     34 class XULButtonElement;
     35 class XULPopupElement;
     36 }  // namespace dom
     37 namespace widget {
     38 enum class PopupLevel : uint8_t;
     39 }
     40 }  // namespace mozilla
     41 
     42 enum ConsumeOutsideClicksResult {
     43  ConsumeOutsideClicks_ParentOnly =
     44      0,                          // Only consume clicks on the parent anchor
     45  ConsumeOutsideClicks_True = 1,  // Always consume clicks
     46  ConsumeOutsideClicks_Never = 2  // Never consume clicks
     47 };
     48 
     49 // How a popup may be flipped. Flipping to the outside edge is like how a
     50 // submenu would work. The entire popup is flipped to the opposite side of the
     51 // anchor.
     52 enum class FlipStyle {
     53  None = 0,
     54  Outside = 1,
     55  Inside = 2,
     56 };
     57 
     58 // Values for the flip attribute
     59 enum class FlipType {
     60  Default = 0,
     61  None = 1,   // don't try to flip or translate to stay onscreen
     62  Both = 2,   // flip in both directions
     63  Slide = 3,  // allow the arrow to "slide" instead of resizing
     64 };
     65 
     66 enum class MenuPopupAnchorType : uint8_t {
     67  Node = 0,   // anchored to a node
     68  Point = 1,  // unanchored, and positioned at a screen point
     69  Rect = 2,   // anchored at a screen rectangle
     70 };
     71 
     72 // values are selected so that the direction can be flipped just by
     73 // changing the sign
     74 #define POPUPALIGNMENT_NONE 0
     75 #define POPUPALIGNMENT_TOPLEFT 1
     76 #define POPUPALIGNMENT_TOPRIGHT -1
     77 #define POPUPALIGNMENT_BOTTOMLEFT 2
     78 #define POPUPALIGNMENT_BOTTOMRIGHT -2
     79 
     80 #define POPUPALIGNMENT_LEFTCENTER 16
     81 #define POPUPALIGNMENT_RIGHTCENTER -16
     82 #define POPUPALIGNMENT_TOPCENTER 17
     83 #define POPUPALIGNMENT_BOTTOMCENTER 18
     84 
     85 // The constants here are selected so that horizontally and vertically flipping
     86 // can be easily handled using the two flip macros below.
     87 #define POPUPPOSITION_UNKNOWN -1
     88 #define POPUPPOSITION_BEFORESTART 0
     89 #define POPUPPOSITION_BEFOREEND 1
     90 #define POPUPPOSITION_AFTERSTART 2
     91 #define POPUPPOSITION_AFTEREND 3
     92 #define POPUPPOSITION_STARTBEFORE 4
     93 #define POPUPPOSITION_ENDBEFORE 5
     94 #define POPUPPOSITION_STARTAFTER 6
     95 #define POPUPPOSITION_ENDAFTER 7
     96 #define POPUPPOSITION_OVERLAP 8
     97 #define POPUPPOSITION_AFTERPOINTER 9
     98 #define POPUPPOSITION_SELECTION 10
     99 
    100 #define POPUPPOSITION_HFLIP(v) (v ^ 1)
    101 #define POPUPPOSITION_VFLIP(v) (v ^ 2)
    102 
    103 nsIFrame* NS_NewMenuPopupFrame(mozilla::PresShell* aPresShell,
    104                               mozilla::ComputedStyle* aStyle);
    105 
    106 class nsMenuPopupFrame;
    107 
    108 // this class is used for dispatching popupshown events asynchronously.
    109 class nsXULPopupShownEvent final : public mozilla::Runnable,
    110                                   public nsIDOMEventListener {
    111 public:
    112  nsXULPopupShownEvent(nsIContent* aPopup, nsPresContext* aPresContext)
    113      : mozilla::Runnable("nsXULPopupShownEvent"),
    114        mPopup(aPopup),
    115        mPresContext(aPresContext) {}
    116 
    117  NS_DECL_ISUPPORTS_INHERITED
    118  NS_DECL_NSIRUNNABLE
    119  NS_DECL_NSIDOMEVENTLISTENER
    120 
    121  void CancelListener();
    122 
    123 protected:
    124  virtual ~nsXULPopupShownEvent() = default;
    125 
    126 private:
    127  const nsCOMPtr<nsIContent> mPopup;
    128  const RefPtr<nsPresContext> mPresContext;
    129 };
    130 
    131 class nsMenuPopupFrame final : public nsBlockFrame, public nsIWidgetListener {
    132  using PopupLevel = mozilla::widget::PopupLevel;
    133  using PopupType = mozilla::widget::PopupType;
    134 
    135 public:
    136  NS_DECL_QUERYFRAME
    137  NS_DECL_FRAMEARENA_HELPERS(nsMenuPopupFrame)
    138 
    139  explicit nsMenuPopupFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
    140  ~nsMenuPopupFrame();
    141 
    142  // as popups are opened asynchronously, the popup pending state is used to
    143  // prevent multiple requests from attempting to open the same popup twice
    144  nsPopupState PopupState() const { return mPopupState; }
    145  void SetPopupState(nsPopupState);
    146 
    147  /*
    148   * When this popup is open, should clicks outside of it be consumed?
    149   * Return true if the popup should rollup on an outside click,
    150   * but consume that click so it can't be used for anything else.
    151   * Return false to allow clicks outside the popup to activate content
    152   * even when the popup is open.
    153   * ---------------------------------------------------------------------
    154   *
    155   * Should clicks outside of a popup be eaten?
    156   *
    157   *       Menus     Autocomplete     Comboboxes
    158   * Mac     Eat           No              Eat
    159   * Win     No            No              Eat
    160   * Unix    Eat           No              Eat
    161   *
    162   */
    163  ConsumeOutsideClicksResult ConsumeOutsideClicks();
    164 
    165  mozilla::dom::XULPopupElement& PopupElement() const;
    166 
    167  nscoord IntrinsicISize(const mozilla::IntrinsicSizeInput& aInput,
    168                         mozilla::IntrinsicISizeType aType) override;
    169 
    170  void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
    171              const ReflowInput& aReflowInput,
    172              nsReflowStatus& aStatus) override;
    173 
    174  nsIWidget* GetWidget() const;
    175  already_AddRefed<nsIWidget> ComputeParentWidget() const;
    176 
    177  enum class WidgetStyle : uint8_t {
    178    ColorScheme,
    179    InputRegion,
    180    Opacity,
    181    Shadow,
    182    Transform,
    183    MicaBackdrop,
    184  };
    185  using WidgetStyleFlags = mozilla::EnumSet<WidgetStyle>;
    186  static constexpr WidgetStyleFlags AllWidgetStyleFlags() {
    187    return {WidgetStyle::ColorScheme, WidgetStyle::InputRegion,
    188            WidgetStyle::Opacity,     WidgetStyle::Shadow,
    189            WidgetStyle::Transform,   WidgetStyle::MicaBackdrop};
    190  }
    191  void PropagateStyleToWidget(WidgetStyleFlags = AllWidgetStyleFlags()) const;
    192 
    193  // Overridden methods
    194  void Init(nsIContent* aContent, nsContainerFrame* aParent,
    195            nsIFrame* aPrevInFlow) override;
    196 
    197  nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
    198                            AttrModType aModType) override;
    199 
    200  // nsIWidgetListener
    201  mozilla::PresShell* GetPresShell() override { return PresShell(); }
    202  nsMenuPopupFrame* GetAsMenuPopupFrame() override { return this; }
    203  void WindowMoved(nsIWidget*, const mozilla::LayoutDeviceIntPoint&,
    204                   ByMoveToRect) override;
    205  void WindowResized(nsIWidget*, const mozilla::LayoutDeviceIntSize&) override;
    206  bool RequestWindowClose(nsIWidget*) override;
    207  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    208  nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent) override;
    209  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    210  void PaintWindow(nsIWidget* aWidget) override;
    211  void DidCompositeWindow(mozilla::layers::TransactionId aTransactionId,
    212                          const mozilla::TimeStamp& aCompositeStart,
    213                          const mozilla::TimeStamp& aCompositeEnd) override;
    214  bool ShouldNotBeVisible() override { return !IsOpen(); }
    215  using nsIFrame::HandleEvent;  // Needed to silence warning.
    216 
    217  // FIXME: This shouldn't run script (this can end up calling HidePopup).
    218  MOZ_CAN_RUN_SCRIPT_BOUNDARY void Destroy(DestroyContext&) override;
    219 
    220  bool HasRemoteContent() const;
    221 
    222  // Whether this is a drag popup to show drag feedback.
    223  bool IsDragPopup() const;
    224 
    225  // Whether we should have a widget even when we're not shown.
    226  bool ShouldHaveWidgetWhenHidden() const;
    227 
    228  // Whether we should expand the menu to take the size of the parent menulist.
    229  bool ShouldExpandToInflowParentOrAnchor() const;
    230 
    231  // Returns true if the popup is a panel with the noautohide attribute set to
    232  // true. These panels do not roll up automatically.
    233  bool IsNoAutoHide() const;
    234 
    235  PopupLevel GetPopupLevel() const { return GetPopupLevel(IsNoAutoHide()); }
    236 
    237  // Ensure that a widget has already been created for this view, and create
    238  // one if it hasn't. If aForceRecreate is true, destroys any existing widget
    239  // and creates a new one, regardless of whether one has already been created.
    240  // Otherwise does so only if needed.
    241  void PrepareWidget(bool aForceRecreate = false);
    242 
    243  MOZ_CAN_RUN_SCRIPT void EnsureActiveMenuListItemIsVisible();
    244 
    245  void CreateWidget();
    246  void DestroyWidget();
    247  mozilla::WindowShadow GetShadowStyle() const;
    248 
    249  void DidSetComputedStyle(ComputedStyle* aOldStyle) override;
    250 
    251  // layout, position and display the popup as needed
    252  MOZ_CAN_RUN_SCRIPT_BOUNDARY
    253  void LayoutPopup(nsPresContext*, ReflowOutput&, const ReflowInput&,
    254                   nsReflowStatus&);
    255 
    256  // Set the position of the popup relative to the anchor content, anchored at a
    257  // rectangle, or at a specific point if a screen position is set. The popup
    258  // will be adjusted so that it is on screen. If aIsMove is true, then the
    259  // popup is being moved, and should not be flipped.
    260  void SetPopupPosition(bool aIsMove);
    261 
    262  // Called when the Enter key is pressed while the popup is open. This will
    263  // just pass the call down to the current menu, if any.
    264  // Also, calling Enter will reset the current incremental search string,
    265  // calculated in FindMenuWithShortcut.
    266  MOZ_CAN_RUN_SCRIPT void HandleEnterKeyPress(mozilla::WidgetEvent&);
    267 
    268  // Locate and return the menu frame that should be activated for the supplied
    269  // key event. If aDoAction is set to true by this method, then the menu's
    270  // action should be carried out, as if the user had pressed the Enter key. If
    271  // aDoAction is false, the menu should just be highlighted.
    272  // This method also handles incremental searching in menus so the user can
    273  // type the first few letters of an item/s name to select it.
    274  mozilla::dom::XULButtonElement* FindMenuWithShortcut(
    275      mozilla::dom::KeyboardEvent& aKeyEvent, bool& aDoAction);
    276 
    277  mozilla::dom::XULButtonElement* GetCurrentMenuItem() const;
    278  nsIFrame* GetCurrentMenuItemFrame() const;
    279 
    280  PopupType GetPopupType() const { return mPopupType; }
    281  bool IsContextMenu() const { return mIsContextMenu; }
    282 
    283  bool IsOpen() const {
    284    return mPopupState == ePopupOpening || mPopupState == ePopupVisible ||
    285           mPopupState == ePopupShown;
    286  }
    287  bool IsVisible() const {
    288    return mPopupState == ePopupVisible || mPopupState == ePopupShown;
    289  }
    290  bool IsVisibleOrShowing() const {
    291    return IsOpen() || mPopupState == ePopupPositioning ||
    292           mPopupState == ePopupShowing;
    293  }
    294  bool IsNativeMenu() const { return mIsNativeMenu; }
    295  bool CanSkipLayout() const;
    296  bool IsMouseTransparent() const;
    297 
    298  // Return true if the popup is for a menulist.
    299  bool IsMenuList() const;
    300 
    301  bool IsDragSource() const { return mIsDragSource; }
    302  void SetIsDragSource(bool aIsDragSource) { mIsDragSource = aIsDragSource; }
    303 
    304  bool PendingWidgetMoveResize() const { return mPendingWidgetMoveResize; }
    305  void ClearPendingWidgetMoveResize() { mPendingWidgetMoveResize = false; }
    306  void SchedulePendingWidgetMoveResize();
    307 
    308  static nsIContent* GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame);
    309  void ClearTriggerContent() { mTriggerContent = nullptr; }
    310  void ClearTriggerContentIncludingDocument();
    311 
    312  // returns true if the popup is in a content shell, or false for a popup in
    313  // a chrome shell
    314  bool IsInContentShell() const { return mInContentShell; }
    315 
    316  // the Initialize methods are used to set the anchor position for
    317  // each way of opening a popup.
    318  void InitializePopup(nsIContent* aAnchorContent, nsIContent* aTriggerContent,
    319                       const nsAString& aPosition, int32_t aXPos, int32_t aYPos,
    320                       MenuPopupAnchorType aAnchorType,
    321                       bool aAttributesOverride);
    322 
    323  void InitializePopupAtRect(nsIContent* aTriggerContent,
    324                             const nsAString& aPosition, const nsIntRect& aRect,
    325                             bool aAttributesOverride);
    326 
    327  /**
    328   * @param aIsContextMenu if true, then the popup is
    329   * positioned at a slight offset from aXPos/aYPos to ensure the
    330   * (presumed) mouse position is not over the menu.
    331   */
    332  void InitializePopupAtScreen(nsIContent* aTriggerContent, int32_t aXPos,
    333                               int32_t aYPos, bool aIsContextMenu);
    334 
    335  // Called if this popup should be displayed as an OS-native context menu.
    336  void InitializePopupAsNativeContextMenu(nsIContent* aTriggerContent,
    337                                          int32_t aXPos, int32_t aYPos);
    338 
    339  // indicate that the popup should be opened
    340  void ShowPopup(bool aIsContextMenu);
    341  // indicate that the popup should be hidden. The new state should either be
    342  // ePopupClosed or ePopupInvisible.
    343  MOZ_CAN_RUN_SCRIPT void HidePopup(bool aDeselectMenu, nsPopupState aNewState,
    344                                    bool aFromFrameDestruction = false);
    345 
    346  void ClearIncrementalString() { mIncrementalString.Truncate(); }
    347  static bool IsWithinIncrementalTime(mozilla::TimeStamp time) {
    348    return !sLastKeyTime.IsNull() &&
    349           ((time - sLastKeyTime).ToMilliseconds() <=
    350            mozilla::StaticPrefs::ui_menu_incremental_search_timeout());
    351  }
    352 
    353 #ifdef DEBUG_FRAME_DUMP
    354  virtual nsresult GetFrameName(nsAString& aResult) const override {
    355    return MakeFrameName(u"MenuPopup"_ns, aResult);
    356  }
    357 #endif
    358 
    359  MOZ_CAN_RUN_SCRIPT void ChangeByPage(bool aIsUp);
    360 
    361  // Move the popup to the screen coordinate |aPos| in CSS pixels.
    362  // If aUpdateAttrs is true, and the popup already has left or top attributes,
    363  // then those attributes are updated to the new location.
    364  // The frame may be destroyed by this method.
    365  void MoveTo(const mozilla::CSSPoint& aPos, bool aUpdateAttrs,
    366              bool aByMoveToRect = false);
    367 
    368  void MoveToAnchor(nsIContent* aAnchorContent, const nsAString& aPosition,
    369                    int32_t aXPos, int32_t aYPos, bool aAttributesOverride);
    370 
    371  mozilla::ScrollContainerFrame* GetScrollContainerFrame() const;
    372 
    373  void SetOverrideConstraintRect(const mozilla::CSSIntRect& aRect) {
    374    mOverrideConstraintRect = mozilla::CSSIntRect::ToAppUnits(aRect);
    375  }
    376 
    377  bool IsConstrainedByLayout() const { return mConstrainedByLayout; }
    378 
    379  struct Rects {
    380    // For anchored popups, the anchor rectangle. For non-anchored popups, the
    381    // size will be 0.
    382    nsRect mAnchorRect;
    383    // mAnchorRect before accounting for flipping / resizing / intersecting with
    384    // the screen. This is needed for Wayland, which flips / resizes at the
    385    // widget level.
    386    nsRect mUntransformedAnchorRect;
    387    // The final used rect we want to occupy.
    388    nsRect mUsedRect;
    389    // The alignment offset for sliding the panel, see
    390    // nsMenuPopupFrame::mAlignmentOffset.
    391    nscoord mAlignmentOffset = 0;
    392    bool mHFlip = false;
    393    bool mVFlip = false;
    394    bool mConstrainedByLayout = false;
    395    nsPoint mViewPoint;
    396  };
    397 
    398  // For a popup that should appear anchored at the given rect, gets the anchor
    399  // and constraint rects for that popup.
    400  // This will be the available area of the screen the popup should be displayed
    401  // on. Content popups, however, will also be constrained by the content area.
    402  //
    403  // For non-toplevel popups (which will always be panels), we will also
    404  // constrain them to the available screen rect, ie they will not fall
    405  // underneath the taskbar, dock or other fixed OS elements.
    406  Rects GetRects(const nsSize& aPrefSize) const;
    407  Maybe<nsRect> GetConstraintRect(const nsRect& aAnchorRect,
    408                                  const nsRect& aRootScreenRect,
    409                                  PopupLevel) const;
    410  void PerformMove(const Rects&);
    411 
    412  // Return true if the popup is positioned relative to an anchor.
    413  bool IsAnchored() const { return mAnchorType != MenuPopupAnchorType::Point; }
    414 
    415  // Return the anchor if there is one.
    416  nsIContent* GetAnchor() const { return mAnchorContent; }
    417 
    418  // Return the screen coordinates in CSS pixels of the popup,
    419  // or (-1, -1, 0, 0) if anchored.
    420  mozilla::CSSIntRect GetScreenAnchorRect() const {
    421    return mozilla::CSSRect::FromAppUnitsRounded(mScreenRect);
    422  }
    423 
    424  mozilla::LayoutDeviceIntRect CalcWidgetBounds() const;
    425 
    426  // Return the alignment of the popup
    427  int8_t GetAlignmentPosition() const;
    428 
    429  // Return the offset applied to the alignment of the popup
    430  nscoord GetAlignmentOffset() const { return mAlignmentOffset; }
    431 
    432  // Clear the mPopupShownDispatcher, remove the listener and return true if
    433  // mPopupShownDispatcher was non-null.
    434  bool ClearPopupShownDispatcher() {
    435    if (mPopupShownDispatcher) {
    436      mPopupShownDispatcher->CancelListener();
    437      mPopupShownDispatcher = nullptr;
    438      return true;
    439    }
    440 
    441    return false;
    442  }
    443 
    444  void ShowWithPositionedEvent() { mPopupState = ePopupPositioning; }
    445 
    446  // Checks for the anchor to change and either moves or hides the popup
    447  // accordingly. The original position of the anchor should be supplied as
    448  // the argument. If the popup needs to be hidden, HidePopup will be called by
    449  // CheckForAnchorChange. If the popup needs to be moved, aRect will be updated
    450  // with the new rectangle.
    451  void CheckForAnchorChange(nsRect& aRect);
    452 
    453  void WillDispatchPopupPositioned() { mPendingPositionedEvent = false; }
    454 
    455 protected:
    456  // returns the popup's level.
    457  PopupLevel GetPopupLevel(bool aIsNoAutoHide) const;
    458 
    459  void InitPositionFromAnchorAlign(const nsAString& aAnchor,
    460                                   const nsAString& aAlign);
    461 
    462  // return the position where the popup should be, when it should be
    463  // anchored at anchorRect. aHFlip and aVFlip will be set if the popup may be
    464  // flipped in that direction if there is not enough space available.
    465  nsPoint AdjustPositionForAnchorAlign(nsRect& aAnchorRect,
    466                                       const nsSize& aPrefSize,
    467                                       FlipStyle& aHFlip,
    468                                       FlipStyle& aVFlip) const;
    469 
    470  // For popups that are going to align to their selected item, get the frame of
    471  // the selected item.
    472  nsIFrame* GetSelectedItemForAlignment() const;
    473 
    474  // check if the popup will fit into the available space and resize it. This
    475  // method handles only one axis at a time so is called twice, once for
    476  // horizontal and once for vertical. All arguments are specified for this
    477  // one axis. All coordinates are in app units relative to the screen.
    478  //   aScreenPoint - the point where the popup should appear
    479  //   aSize - the size of the popup
    480  //   aScreenBegin - the left or top edge of the screen
    481  //   aScreenEnd - the right or bottom edge of the screen
    482  //   aAnchorBegin - the left or top edge of the anchor rectangle
    483  //   aAnchorEnd - the right or bottom edge of the anchor rectangle
    484  //   aMarginBegin - the left or top margin of the popup
    485  //   aMarginEnd - the right or bottom margin of the popup
    486  //   aFlip - how to flip or resize the popup when there isn't space
    487  //   aFlipSide - pointer to where current flip mode is stored
    488  nscoord FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
    489                       nscoord aScreenBegin, nscoord aScreenEnd,
    490                       nscoord aAnchorBegin, nscoord aAnchorEnd,
    491                       nscoord aMarginBegin, nscoord aMarginEnd,
    492                       FlipStyle aFlip, bool aIsOnEnd, bool* aFlipSide) const;
    493 
    494  // check if the popup can fit into the available space by "sliding" (i.e.,
    495  // by having the anchor arrow slide along one axis and only resizing if that
    496  // can't provide the requested size). Only one axis can be slid - the other
    497  // axis is "flipped" as normal. This method can handle either axis, but is
    498  // only called for the sliding axis. All coordinates are in app units
    499  // relative to the screen.
    500  //   aScreenPoint - the point where the popup should appear
    501  //   aSize - the size of the popup
    502  //   aScreenBegin - the left or top edge of the screen
    503  //   aScreenEnd - the right or bottom edge of the screen
    504  //   aOffset - the amount by which the arrow must be slid such that it is
    505  //             still aligned with the anchor.
    506  // Result is the new size of the popup, which will typically be the same
    507  // as aSize, unless aSize is greater than the screen width/height.
    508  nscoord SlideOrResize(nscoord& aScreenPoint, nscoord aSize,
    509                        nscoord aScreenBegin, nscoord aScreenEnd,
    510                        nscoord* aOffset) const;
    511 
    512  // Given an anchor frame, compute the anchor rectangle relative to the screen,
    513  // using the popup frame's app units, and taking into account transforms.
    514  nsRect ComputeAnchorRect(nsPresContext* aRootPresContext,
    515                           nsIFrame* aAnchorFrame) const;
    516 
    517  // Move the popup to the position specified in its |left| and |top|
    518  // attributes.
    519  void MoveToAttributePosition();
    520 
    521  // Returns true if the popup should try to remain at the same relative
    522  // location as the anchor while it is open. If the anchor becomes hidden
    523  // either directly or indirectly because a parent popup or other element
    524  // is no longer visible, or a parent deck page is changed, the popup hides
    525  // as well. The second variation also sets the anchor rectangle, relative to
    526  // the popup frame.
    527  bool ShouldFollowAnchor() const;
    528 
    529  nsIFrame* GetAnchorFrame() const;
    530 
    531 public:
    532  /**
    533   * Return whether the popup direction should be RTL.
    534   * If the popup has an anchor, its direction is the anchor direction.
    535   * Otherwise, its the general direction of the UI.
    536   *
    537   * Return whether the popup direction should be RTL.
    538   */
    539  bool IsDirectionRTL() const;
    540 
    541  bool ShouldFollowAnchor(nsRect& aRect);
    542 
    543  // Returns parent menu widget for submenus that are in the same
    544  // frame hierarchy, it's needed for Linux/Wayland which demands
    545  // strict popup windows hierarchy.
    546  nsIWidget* GetParentMenuWidget();
    547 
    548  // Returns the effective margin for this popup. This is the CSS margin plus
    549  // the context-menu shift, if needed.
    550  nsMargin GetMargin() const;
    551 
    552  // These are used by Wayland backend.
    553  const nsRect& GetUntransformedAnchorRect() const {
    554    return mUntransformedAnchorRect;
    555  }
    556  int8_t GetUntransformedPopupAlignment() const {
    557    return mUntransformedPopupAlignment;
    558  }
    559  int8_t GetUntransformedPopupAnchor() const {
    560    return mUntransformedPopupAnchor;
    561  }
    562 
    563  int8_t GetPopupAlignment() const { return mPopupAlignment; }
    564  int8_t GetPopupAnchor() const { return mPopupAnchor; }
    565  FlipType GetFlipType() const { return mFlip; }
    566 
    567  uint64_t GetAPZFocusSequenceNumber() const { return mAPZFocusSequenceNumber; }
    568 
    569  void UpdateAPZFocusSequenceNumber(uint64_t aNewNumber) {
    570    mAPZFocusSequenceNumber = aNewNumber;
    571  }
    572 
    573  void DestroyWidgetIfNeeded();
    574  nsExpirationState* GetExpirationState() { return &mExpirationState; }
    575 
    576 protected:
    577  nsString mIncrementalString;  // for incremental typing navigation
    578 
    579  // the content that the popup is anchored to, if any, which may be in a
    580  // different document than the popup.
    581  nsCOMPtr<nsIContent> mAnchorContent;
    582 
    583  // the content that triggered the popup, typically the node where the mouse
    584  // was clicked. It will be cleared when the popup is hidden.
    585  nsCOMPtr<nsIContent> mTriggerContent;
    586 
    587  RefPtr<nsIWidget> mWidget;
    588 
    589  RefPtr<nsXULPopupShownEvent> mPopupShownDispatcher;
    590 
    591  // The popup's screen rectangle in app units.
    592  nsRect mUsedScreenRect;
    593 
    594  // A popup's preferred size may be different than its actual size stored in
    595  // mRect in the case where the popup was resized because it was too large
    596  // for the screen. The preferred size mPrefSize holds the full size the popup
    597  // would be before resizing. Computations are performed using this size.
    598  nsSize mPrefSize{-1, -1};
    599 
    600  // A point with extra offsets to apply in the horizontal and vertical axes. We
    601  // don't use an nsMargin because the values would be the same for the same
    602  // axis.
    603  nsPoint mExtraMargin;
    604 
    605  nsRect mScreenRect;
    606  // Used for store rectangle which the popup is going to be anchored to, we
    607  // need that for Wayland. It's important that this rect is unflipped, and
    608  // without margins applied, as GTK is what takes care of determining how to
    609  // flip etc. on Wayland.
    610  nsRect mUntransformedAnchorRect;
    611 
    612  // If the panel prefers to "slide" rather than resize, then the arrow gets
    613  // positioned at this offset (along either the x or y axis, depending on
    614  // mPosition)
    615  nscoord mAlignmentOffset = 0;
    616 
    617  // The focus sequence number of the last processed input event
    618  uint64_t mAPZFocusSequenceNumber = 0;
    619 
    620  PopupType mPopupType = PopupType::Panel;  // type of popup
    621  nsPopupState mPopupState = ePopupClosed;  // open state of the popup
    622 
    623  // popup alignment relative to the anchor node
    624  // The untransformed variants are needed for Wayland
    625  int8_t mUntransformedPopupAlignment = POPUPALIGNMENT_NONE;
    626  int8_t mUntransformedPopupAnchor = POPUPALIGNMENT_NONE;
    627  int8_t mPopupAlignment = POPUPALIGNMENT_NONE;
    628  int8_t mPopupAnchor = POPUPALIGNMENT_NONE;
    629  int8_t mPosition = POPUPPOSITION_UNKNOWN;
    630 
    631  FlipType mFlip = FlipType::Default;  // Whether to flip
    632 
    633  // Whether we were moved by the move-to-rect Wayland callback. In that case,
    634  // we stop updating the anchor so that we can end up with a stable position.
    635  bool mPositionedByMoveToRect = false;
    636  // true if the open state changed since the last layout.
    637  bool mIsOpenChanged = false;
    638  // true for context menus and their submenus.
    639  bool mIsContextMenu = false;
    640  // true for the topmost context menu.
    641  bool mIsTopLevelContextMenu = false;
    642  // true if the popup is in a content shell.
    643  bool mInContentShell = true;
    644 
    645  // The flip modes that were used when the popup was opened
    646  bool mHFlip = false;
    647  bool mVFlip = false;
    648  // Whether layout has constrained this popup in some way.
    649  bool mConstrainedByLayout = false;
    650 
    651  // Whether the most recent initialization of this menupopup happened via
    652  // InitializePopupAsNativeContextMenu.
    653  bool mIsNativeMenu = false;
    654 
    655  // Whether we have a pending `popuppositioned` event.
    656  bool mPendingPositionedEvent = false;
    657 
    658  // Whether this popup is source of D&D operation. We can't close such
    659  // popup on Wayland as it cancel whole D&D operation.
    660  bool mIsDragSource = false;
    661 
    662  // Whether there's a pending move-resize of our widget.
    663  bool mPendingWidgetMoveResize = false;
    664 
    665  // When POPUPPOSITION_SELECTION is used, this indicates the vertical offset
    666  // that the original selected item was. This needs to be used in case the
    667  // popup gets changed so that we can keep the popup at the same vertical
    668  // offset.
    669  // TODO(emilio): try to make this not mutable.
    670  mutable nscoord mPositionedOffset = 0;
    671 
    672  // How the popup is anchored.
    673  MenuPopupAnchorType mAnchorType = MenuPopupAnchorType::Node;
    674 
    675  nsRect mOverrideConstraintRect;
    676 
    677  nsExpirationState mExpirationState;
    678  static mozilla::TimeStamp sLastKeyTime;
    679 };  // class nsMenuPopupFrame
    680 
    681 #endif