forked from mirrors/gecko-dev
		
	-moz-window-shadow is a chrome-only property (not exposed to the web), and chrome stylesheets only use the none value to disable the default behavior. Split the style property (default/none) from the actual widget behavior. This is useful because I want to add a distinction between natively-styled menupopups and other panels in the following commit. While at it rename default to "auto" which is a more common name in CSS for something like this. We have no use case for removing the shadow of a top-level window, so remove it to simplify the code. Differential Revision: https://phabricator.services.mozilla.com/D197454
		
			
				
	
	
		
			647 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			647 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
//
 | 
						|
// nsMenuPopupFrame
 | 
						|
//
 | 
						|
 | 
						|
#ifndef nsMenuPopupFrame_h__
 | 
						|
#define nsMenuPopupFrame_h__
 | 
						|
 | 
						|
#include "mozilla/Attributes.h"
 | 
						|
#include "mozilla/gfx/Types.h"
 | 
						|
#include "mozilla/StaticPrefs_ui.h"
 | 
						|
#include "mozilla/TimeStamp.h"
 | 
						|
#include "nsAtom.h"
 | 
						|
#include "nsCOMPtr.h"
 | 
						|
#include "nsIDOMEventListener.h"
 | 
						|
#include "nsXULPopupManager.h"
 | 
						|
 | 
						|
#include "nsBlockFrame.h"
 | 
						|
 | 
						|
#include "Units.h"
 | 
						|
 | 
						|
class nsIWidget;
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
class PresShell;
 | 
						|
enum class WindowShadow : uint8_t;
 | 
						|
namespace dom {
 | 
						|
class KeyboardEvent;
 | 
						|
class XULButtonElement;
 | 
						|
class XULPopupElement;
 | 
						|
}  // namespace dom
 | 
						|
namespace widget {
 | 
						|
enum class PopupLevel : uint8_t;
 | 
						|
}
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
enum ConsumeOutsideClicksResult {
 | 
						|
  ConsumeOutsideClicks_ParentOnly =
 | 
						|
      0,                          // Only consume clicks on the parent anchor
 | 
						|
  ConsumeOutsideClicks_True = 1,  // Always consume clicks
 | 
						|
  ConsumeOutsideClicks_Never = 2  // Never consume clicks
 | 
						|
};
 | 
						|
 | 
						|
// How a popup may be flipped. Flipping to the outside edge is like how
 | 
						|
// a submenu would work. The entire popup is flipped to the opposite side
 | 
						|
// of the anchor.
 | 
						|
enum FlipStyle {
 | 
						|
  FlipStyle_None = 0,
 | 
						|
  FlipStyle_Outside = 1,
 | 
						|
  FlipStyle_Inside = 2
 | 
						|
};
 | 
						|
 | 
						|
// Values for the flip attribute
 | 
						|
enum FlipType {
 | 
						|
  FlipType_Default = 0,
 | 
						|
  FlipType_None = 1,  // don't try to flip or translate to stay onscreen
 | 
						|
  FlipType_Both = 2,  // flip in both directions
 | 
						|
  FlipType_Slide = 3  // allow the arrow to "slide" instead of resizing
 | 
						|
};
 | 
						|
 | 
						|
enum MenuPopupAnchorType {
 | 
						|
  MenuPopupAnchorType_Node = 0,   // anchored to a node
 | 
						|
  MenuPopupAnchorType_Point = 1,  // unanchored and positioned at a screen point
 | 
						|
  MenuPopupAnchorType_Rect = 2,   // anchored at a screen rectangle
 | 
						|
};
 | 
						|
 | 
						|
// values are selected so that the direction can be flipped just by
 | 
						|
// changing the sign
 | 
						|
#define POPUPALIGNMENT_NONE 0
 | 
						|
#define POPUPALIGNMENT_TOPLEFT 1
 | 
						|
#define POPUPALIGNMENT_TOPRIGHT -1
 | 
						|
#define POPUPALIGNMENT_BOTTOMLEFT 2
 | 
						|
#define POPUPALIGNMENT_BOTTOMRIGHT -2
 | 
						|
 | 
						|
#define POPUPALIGNMENT_LEFTCENTER 16
 | 
						|
#define POPUPALIGNMENT_RIGHTCENTER -16
 | 
						|
#define POPUPALIGNMENT_TOPCENTER 17
 | 
						|
#define POPUPALIGNMENT_BOTTOMCENTER 18
 | 
						|
 | 
						|
// The constants here are selected so that horizontally and vertically flipping
 | 
						|
// can be easily handled using the two flip macros below.
 | 
						|
#define POPUPPOSITION_UNKNOWN -1
 | 
						|
#define POPUPPOSITION_BEFORESTART 0
 | 
						|
#define POPUPPOSITION_BEFOREEND 1
 | 
						|
#define POPUPPOSITION_AFTERSTART 2
 | 
						|
#define POPUPPOSITION_AFTEREND 3
 | 
						|
#define POPUPPOSITION_STARTBEFORE 4
 | 
						|
#define POPUPPOSITION_ENDBEFORE 5
 | 
						|
#define POPUPPOSITION_STARTAFTER 6
 | 
						|
#define POPUPPOSITION_ENDAFTER 7
 | 
						|
#define POPUPPOSITION_OVERLAP 8
 | 
						|
#define POPUPPOSITION_AFTERPOINTER 9
 | 
						|
#define POPUPPOSITION_SELECTION 10
 | 
						|
 | 
						|
#define POPUPPOSITION_HFLIP(v) (v ^ 1)
 | 
						|
#define POPUPPOSITION_VFLIP(v) (v ^ 2)
 | 
						|
 | 
						|
nsIFrame* NS_NewMenuPopupFrame(mozilla::PresShell* aPresShell,
 | 
						|
                               mozilla::ComputedStyle* aStyle);
 | 
						|
 | 
						|
class nsView;
 | 
						|
class nsMenuPopupFrame;
 | 
						|
 | 
						|
// this class is used for dispatching popupshown events asynchronously.
 | 
						|
class nsXULPopupShownEvent final : public mozilla::Runnable,
 | 
						|
                                   public nsIDOMEventListener {
 | 
						|
 public:
 | 
						|
  nsXULPopupShownEvent(nsIContent* aPopup, nsPresContext* aPresContext)
 | 
						|
      : mozilla::Runnable("nsXULPopupShownEvent"),
 | 
						|
        mPopup(aPopup),
 | 
						|
        mPresContext(aPresContext) {}
 | 
						|
 | 
						|
  NS_DECL_ISUPPORTS_INHERITED
 | 
						|
  NS_DECL_NSIRUNNABLE
 | 
						|
  NS_DECL_NSIDOMEVENTLISTENER
 | 
						|
 | 
						|
  void CancelListener();
 | 
						|
 | 
						|
 protected:
 | 
						|
  virtual ~nsXULPopupShownEvent() = default;
 | 
						|
 | 
						|
 private:
 | 
						|
  const nsCOMPtr<nsIContent> mPopup;
 | 
						|
  const RefPtr<nsPresContext> mPresContext;
 | 
						|
};
 | 
						|
 | 
						|
class nsMenuPopupFrame final : public nsBlockFrame {
 | 
						|
  using PopupLevel = mozilla::widget::PopupLevel;
 | 
						|
  using PopupType = mozilla::widget::PopupType;
 | 
						|
 | 
						|
 public:
 | 
						|
  NS_DECL_QUERYFRAME
 | 
						|
  NS_DECL_FRAMEARENA_HELPERS(nsMenuPopupFrame)
 | 
						|
 | 
						|
  explicit nsMenuPopupFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
 | 
						|
  ~nsMenuPopupFrame();
 | 
						|
 | 
						|
  // as popups are opened asynchronously, the popup pending state is used to
 | 
						|
  // prevent multiple requests from attempting to open the same popup twice
 | 
						|
  nsPopupState PopupState() const { return mPopupState; }
 | 
						|
  void SetPopupState(nsPopupState);
 | 
						|
 | 
						|
  /*
 | 
						|
   * When this popup is open, should clicks outside of it be consumed?
 | 
						|
   * Return true if the popup should rollup on an outside click,
 | 
						|
   * but consume that click so it can't be used for anything else.
 | 
						|
   * Return false to allow clicks outside the popup to activate content
 | 
						|
   * even when the popup is open.
 | 
						|
   * ---------------------------------------------------------------------
 | 
						|
   *
 | 
						|
   * Should clicks outside of a popup be eaten?
 | 
						|
   *
 | 
						|
   *       Menus     Autocomplete     Comboboxes
 | 
						|
   * Mac     Eat           No              Eat
 | 
						|
   * Win     No            No              Eat
 | 
						|
   * Unix    Eat           No              Eat
 | 
						|
   *
 | 
						|
   */
 | 
						|
  ConsumeOutsideClicksResult ConsumeOutsideClicks();
 | 
						|
 | 
						|
  mozilla::dom::XULPopupElement& PopupElement() const;
 | 
						|
 | 
						|
  nscoord GetPrefISize(gfxContext*) final;
 | 
						|
  nscoord GetMinISize(gfxContext*) final;
 | 
						|
  void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
 | 
						|
              const ReflowInput& aReflowInput,
 | 
						|
              nsReflowStatus& aStatus) override;
 | 
						|
 | 
						|
  nsIWidget* GetWidget() const;
 | 
						|
 | 
						|
  enum class WidgetStyle : uint8_t {
 | 
						|
    ColorScheme,
 | 
						|
    InputRegion,
 | 
						|
    Opacity,
 | 
						|
    Shadow,
 | 
						|
    Transform,
 | 
						|
  };
 | 
						|
  using WidgetStyleFlags = mozilla::EnumSet<WidgetStyle>;
 | 
						|
  static constexpr WidgetStyleFlags AllWidgetStyleFlags() {
 | 
						|
    return {WidgetStyle::ColorScheme, WidgetStyle::InputRegion,
 | 
						|
            WidgetStyle::Opacity, WidgetStyle::Shadow, WidgetStyle::Transform};
 | 
						|
  }
 | 
						|
  void PropagateStyleToWidget(WidgetStyleFlags = AllWidgetStyleFlags()) const;
 | 
						|
 | 
						|
  // Overridden methods
 | 
						|
  void Init(nsIContent* aContent, nsContainerFrame* aParent,
 | 
						|
            nsIFrame* aPrevInFlow) override;
 | 
						|
 | 
						|
  nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
 | 
						|
                            int32_t aModType) override;
 | 
						|
 | 
						|
  // FIXME: This shouldn't run script (this can end up calling HidePopup).
 | 
						|
  MOZ_CAN_RUN_SCRIPT_BOUNDARY void Destroy(DestroyContext&) override;
 | 
						|
 | 
						|
  bool HasRemoteContent() const;
 | 
						|
 | 
						|
  // Whether we should create a widget on Init().
 | 
						|
  bool ShouldCreateWidgetUpfront() const;
 | 
						|
 | 
						|
  // Whether we should expand the menu to take the size of the parent menulist.
 | 
						|
  bool ShouldExpandToInflowParentOrAnchor() const;
 | 
						|
 | 
						|
  // Returns true if the popup is a panel with the noautohide attribute set to
 | 
						|
  // true. These panels do not roll up automatically.
 | 
						|
  bool IsNoAutoHide() const;
 | 
						|
 | 
						|
  PopupLevel GetPopupLevel() const { return GetPopupLevel(IsNoAutoHide()); }
 | 
						|
 | 
						|
  // Ensure that a widget has already been created for this view, and create
 | 
						|
  // one if it hasn't. If aRecreate is true, destroys any existing widget and
 | 
						|
  // creates a new one, regardless of whether one has already been created.
 | 
						|
  void PrepareWidget(bool aRecreate = false);
 | 
						|
 | 
						|
  MOZ_CAN_RUN_SCRIPT void EnsureActiveMenuListItemIsVisible();
 | 
						|
 | 
						|
  nsresult CreateWidgetForView(nsView* aView);
 | 
						|
  mozilla::WindowShadow GetShadowStyle() const;
 | 
						|
 | 
						|
  void DidSetComputedStyle(ComputedStyle* aOldStyle) override;
 | 
						|
 | 
						|
  // layout, position and display the popup as needed
 | 
						|
  MOZ_CAN_RUN_SCRIPT_BOUNDARY
 | 
						|
  void LayoutPopup(nsPresContext*, ReflowOutput&, const ReflowInput&,
 | 
						|
                   nsReflowStatus&);
 | 
						|
 | 
						|
  // Set the position of the popup relative to the anchor content, anchored at a
 | 
						|
  // rectangle, or at a specific point if a screen position is set. The popup
 | 
						|
  // will be adjusted so that it is on screen. If aIsMove is true, then the
 | 
						|
  // popup is being moved, and should not be flipped.
 | 
						|
  void SetPopupPosition(bool aIsMove);
 | 
						|
 | 
						|
  // Called when the Enter key is pressed while the popup is open. This will
 | 
						|
  // just pass the call down to the current menu, if any.
 | 
						|
  // Also, calling Enter will reset the current incremental search string,
 | 
						|
  // calculated in FindMenuWithShortcut.
 | 
						|
  MOZ_CAN_RUN_SCRIPT void HandleEnterKeyPress(mozilla::WidgetEvent&);
 | 
						|
 | 
						|
  // Locate and return the menu frame that should be activated for the supplied
 | 
						|
  // key event. If aDoAction is set to true by this method, then the menu's
 | 
						|
  // action should be carried out, as if the user had pressed the Enter key. If
 | 
						|
  // aDoAction is false, the menu should just be highlighted.
 | 
						|
  // This method also handles incremental searching in menus so the user can
 | 
						|
  // type the first few letters of an item/s name to select it.
 | 
						|
  mozilla::dom::XULButtonElement* FindMenuWithShortcut(
 | 
						|
      mozilla::dom::KeyboardEvent& aKeyEvent, bool& aDoAction);
 | 
						|
 | 
						|
  mozilla::dom::XULButtonElement* GetCurrentMenuItem() const;
 | 
						|
  nsIFrame* GetCurrentMenuItemFrame() const;
 | 
						|
 | 
						|
  PopupType GetPopupType() const { return mPopupType; }
 | 
						|
  bool IsContextMenu() const { return mIsContextMenu; }
 | 
						|
 | 
						|
  bool IsOpen() const {
 | 
						|
    return mPopupState == ePopupOpening || mPopupState == ePopupVisible ||
 | 
						|
           mPopupState == ePopupShown;
 | 
						|
  }
 | 
						|
  bool IsVisible() {
 | 
						|
    return mPopupState == ePopupVisible || mPopupState == ePopupShown;
 | 
						|
  }
 | 
						|
  bool IsVisibleOrShowing() {
 | 
						|
    return IsOpen() || mPopupState == ePopupPositioning ||
 | 
						|
           mPopupState == ePopupShowing;
 | 
						|
  }
 | 
						|
  bool IsNativeMenu() const { return mIsNativeMenu; }
 | 
						|
  bool IsMouseTransparent() const;
 | 
						|
 | 
						|
  // Return true if the popup is for a menulist.
 | 
						|
  bool IsMenuList() const;
 | 
						|
 | 
						|
  bool IsDragSource() const { return mIsDragSource; }
 | 
						|
  void SetIsDragSource(bool aIsDragSource) { mIsDragSource = aIsDragSource; }
 | 
						|
 | 
						|
  static nsIContent* GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame);
 | 
						|
  void ClearTriggerContent() { mTriggerContent = nullptr; }
 | 
						|
  void ClearTriggerContentIncludingDocument();
 | 
						|
 | 
						|
  // returns true if the popup is in a content shell, or false for a popup in
 | 
						|
  // a chrome shell
 | 
						|
  bool IsInContentShell() const { return mInContentShell; }
 | 
						|
 | 
						|
  // the Initialize methods are used to set the anchor position for
 | 
						|
  // each way of opening a popup.
 | 
						|
  void InitializePopup(nsIContent* aAnchorContent, nsIContent* aTriggerContent,
 | 
						|
                       const nsAString& aPosition, int32_t aXPos, int32_t aYPos,
 | 
						|
                       MenuPopupAnchorType aAnchorType,
 | 
						|
                       bool aAttributesOverride);
 | 
						|
 | 
						|
  void InitializePopupAtRect(nsIContent* aTriggerContent,
 | 
						|
                             const nsAString& aPosition, const nsIntRect& aRect,
 | 
						|
                             bool aAttributesOverride);
 | 
						|
 | 
						|
  /**
 | 
						|
   * @param aIsContextMenu if true, then the popup is
 | 
						|
   * positioned at a slight offset from aXPos/aYPos to ensure the
 | 
						|
   * (presumed) mouse position is not over the menu.
 | 
						|
   */
 | 
						|
  void InitializePopupAtScreen(nsIContent* aTriggerContent, int32_t aXPos,
 | 
						|
                               int32_t aYPos, bool aIsContextMenu);
 | 
						|
 | 
						|
  // Called if this popup should be displayed as an OS-native context menu.
 | 
						|
  void InitializePopupAsNativeContextMenu(nsIContent* aTriggerContent,
 | 
						|
                                          int32_t aXPos, int32_t aYPos);
 | 
						|
 | 
						|
  // indicate that the popup should be opened
 | 
						|
  void ShowPopup(bool aIsContextMenu);
 | 
						|
  // indicate that the popup should be hidden. The new state should either be
 | 
						|
  // ePopupClosed or ePopupInvisible.
 | 
						|
  MOZ_CAN_RUN_SCRIPT void HidePopup(bool aDeselectMenu, nsPopupState aNewState,
 | 
						|
                                    bool aFromFrameDestruction = false);
 | 
						|
 | 
						|
  void ClearIncrementalString() { mIncrementalString.Truncate(); }
 | 
						|
  static bool IsWithinIncrementalTime(mozilla::TimeStamp time) {
 | 
						|
    return !sLastKeyTime.IsNull() &&
 | 
						|
           ((time - sLastKeyTime).ToMilliseconds() <=
 | 
						|
            mozilla::StaticPrefs::ui_menu_incremental_search_timeout());
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef DEBUG_FRAME_DUMP
 | 
						|
  virtual nsresult GetFrameName(nsAString& aResult) const override {
 | 
						|
    return MakeFrameName(u"MenuPopup"_ns, aResult);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  MOZ_CAN_RUN_SCRIPT void ChangeByPage(bool aIsUp);
 | 
						|
 | 
						|
  // Move the popup to the screen coordinate |aPos| in CSS pixels.
 | 
						|
  // If aUpdateAttrs is true, and the popup already has left or top attributes,
 | 
						|
  // then those attributes are updated to the new location.
 | 
						|
  // The frame may be destroyed by this method.
 | 
						|
  void MoveTo(const mozilla::CSSPoint& aPos, bool aUpdateAttrs,
 | 
						|
              bool aByMoveToRect = false);
 | 
						|
 | 
						|
  void MoveToAnchor(nsIContent* aAnchorContent, const nsAString& aPosition,
 | 
						|
                    int32_t aXPos, int32_t aYPos, bool aAttributesOverride);
 | 
						|
 | 
						|
  nsIScrollableFrame* GetScrollFrame() const;
 | 
						|
 | 
						|
  void SetOverrideConstraintRect(const mozilla::CSSIntRect& aRect) {
 | 
						|
    mOverrideConstraintRect = mozilla::CSSIntRect::ToAppUnits(aRect);
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsConstrainedByLayout() const { return mConstrainedByLayout; }
 | 
						|
 | 
						|
  struct Rects {
 | 
						|
    // For anchored popups, the anchor rectangle. For non-anchored popups, the
 | 
						|
    // size will be 0.
 | 
						|
    nsRect mAnchorRect;
 | 
						|
    // mAnchorRect before accounting for flipping / resizing / intersecting with
 | 
						|
    // the screen. This is needed for Wayland, which flips / resizes at the
 | 
						|
    // widget level.
 | 
						|
    nsRect mUntransformedAnchorRect;
 | 
						|
    // The final used rect we want to occupy.
 | 
						|
    nsRect mUsedRect;
 | 
						|
    // The alignment offset for sliding the panel, see
 | 
						|
    // nsMenuPopupFrame::mAlignmentOffset.
 | 
						|
    nscoord mAlignmentOffset = 0;
 | 
						|
    bool mHFlip = false;
 | 
						|
    bool mVFlip = false;
 | 
						|
    bool mConstrainedByLayout = false;
 | 
						|
    // The client offset of our widget.
 | 
						|
    mozilla::LayoutDeviceIntPoint mClientOffset;
 | 
						|
    nsPoint mViewPoint;
 | 
						|
  };
 | 
						|
 | 
						|
  // For a popup that should appear anchored at the given rect, gets the anchor
 | 
						|
  // and constraint rects for that popup.
 | 
						|
  // This will be the available area of the screen the popup should be displayed
 | 
						|
  // on. Content popups, however, will also be constrained by the content area.
 | 
						|
  //
 | 
						|
  // For non-toplevel popups (which will always be panels), we will also
 | 
						|
  // constrain them to the available screen rect, ie they will not fall
 | 
						|
  // underneath the taskbar, dock or other fixed OS elements.
 | 
						|
  Rects GetRects(const nsSize& aPrefSize) const;
 | 
						|
  Maybe<nsRect> GetConstraintRect(const nsRect& aAnchorRect,
 | 
						|
                                  const nsRect& aRootScreenRect,
 | 
						|
                                  PopupLevel) const;
 | 
						|
  void PerformMove(const Rects&);
 | 
						|
 | 
						|
  // Return true if the popup is positioned relative to an anchor.
 | 
						|
  bool IsAnchored() const { return mAnchorType != MenuPopupAnchorType_Point; }
 | 
						|
 | 
						|
  // Return the anchor if there is one.
 | 
						|
  nsIContent* GetAnchor() const { return mAnchorContent; }
 | 
						|
 | 
						|
  // Return the screen coordinates in CSS pixels of the popup,
 | 
						|
  // or (-1, -1, 0, 0) if anchored.
 | 
						|
  mozilla::CSSIntRect GetScreenAnchorRect() const {
 | 
						|
    return mozilla::CSSRect::FromAppUnitsRounded(mScreenRect);
 | 
						|
  }
 | 
						|
 | 
						|
  mozilla::LayoutDeviceIntPoint GetLastClientOffset() const {
 | 
						|
    return mLastClientOffset;
 | 
						|
  }
 | 
						|
 | 
						|
  // Return the alignment of the popup
 | 
						|
  int8_t GetAlignmentPosition() const;
 | 
						|
 | 
						|
  // Return the offset applied to the alignment of the popup
 | 
						|
  nscoord GetAlignmentOffset() const { return mAlignmentOffset; }
 | 
						|
 | 
						|
  // Clear the mPopupShownDispatcher, remove the listener and return true if
 | 
						|
  // mPopupShownDispatcher was non-null.
 | 
						|
  bool ClearPopupShownDispatcher() {
 | 
						|
    if (mPopupShownDispatcher) {
 | 
						|
      mPopupShownDispatcher->CancelListener();
 | 
						|
      mPopupShownDispatcher = nullptr;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  void ShowWithPositionedEvent() { mPopupState = ePopupPositioning; }
 | 
						|
 | 
						|
  // Checks for the anchor to change and either moves or hides the popup
 | 
						|
  // accordingly. The original position of the anchor should be supplied as
 | 
						|
  // the argument. If the popup needs to be hidden, HidePopup will be called by
 | 
						|
  // CheckForAnchorChange. If the popup needs to be moved, aRect will be updated
 | 
						|
  // with the new rectangle.
 | 
						|
  void CheckForAnchorChange(nsRect& aRect);
 | 
						|
 | 
						|
  void WillDispatchPopupPositioned() { mPendingPositionedEvent = false; }
 | 
						|
 | 
						|
 protected:
 | 
						|
  // returns the popup's level.
 | 
						|
  PopupLevel GetPopupLevel(bool aIsNoAutoHide) const;
 | 
						|
  void TweakMinPrefISize(nscoord&);
 | 
						|
 | 
						|
  void InitPositionFromAnchorAlign(const nsAString& aAnchor,
 | 
						|
                                   const nsAString& aAlign);
 | 
						|
 | 
						|
  // return the position where the popup should be, when it should be
 | 
						|
  // anchored at anchorRect. aHFlip and aVFlip will be set if the popup may be
 | 
						|
  // flipped in that direction if there is not enough space available.
 | 
						|
  nsPoint AdjustPositionForAnchorAlign(nsRect& aAnchorRect,
 | 
						|
                                       const nsSize& aPrefSize,
 | 
						|
                                       FlipStyle& aHFlip,
 | 
						|
                                       FlipStyle& aVFlip) const;
 | 
						|
 | 
						|
  // For popups that are going to align to their selected item, get the frame of
 | 
						|
  // the selected item.
 | 
						|
  nsIFrame* GetSelectedItemForAlignment() const;
 | 
						|
 | 
						|
  // check if the popup will fit into the available space and resize it. This
 | 
						|
  // method handles only one axis at a time so is called twice, once for
 | 
						|
  // horizontal and once for vertical. All arguments are specified for this
 | 
						|
  // one axis. All coordinates are in app units relative to the screen.
 | 
						|
  //   aScreenPoint - the point where the popup should appear
 | 
						|
  //   aSize - the size of the popup
 | 
						|
  //   aScreenBegin - the left or top edge of the screen
 | 
						|
  //   aScreenEnd - the right or bottom edge of the screen
 | 
						|
  //   aAnchorBegin - the left or top edge of the anchor rectangle
 | 
						|
  //   aAnchorEnd - the right or bottom edge of the anchor rectangle
 | 
						|
  //   aMarginBegin - the left or top margin of the popup
 | 
						|
  //   aMarginEnd - the right or bottom margin of the popup
 | 
						|
  //   aFlip - how to flip or resize the popup when there isn't space
 | 
						|
  //   aFlipSide - pointer to where current flip mode is stored
 | 
						|
  nscoord FlipOrResize(nscoord& aScreenPoint, nscoord aSize,
 | 
						|
                       nscoord aScreenBegin, nscoord aScreenEnd,
 | 
						|
                       nscoord aAnchorBegin, nscoord aAnchorEnd,
 | 
						|
                       nscoord aMarginBegin, nscoord aMarginEnd,
 | 
						|
                       FlipStyle aFlip, bool aIsOnEnd, bool* aFlipSide) const;
 | 
						|
 | 
						|
  // check if the popup can fit into the available space by "sliding" (i.e.,
 | 
						|
  // by having the anchor arrow slide along one axis and only resizing if that
 | 
						|
  // can't provide the requested size). Only one axis can be slid - the other
 | 
						|
  // axis is "flipped" as normal. This method can handle either axis, but is
 | 
						|
  // only called for the sliding axis. All coordinates are in app units
 | 
						|
  // relative to the screen.
 | 
						|
  //   aScreenPoint - the point where the popup should appear
 | 
						|
  //   aSize - the size of the popup
 | 
						|
  //   aScreenBegin - the left or top edge of the screen
 | 
						|
  //   aScreenEnd - the right or bottom edge of the screen
 | 
						|
  //   aOffset - the amount by which the arrow must be slid such that it is
 | 
						|
  //             still aligned with the anchor.
 | 
						|
  // Result is the new size of the popup, which will typically be the same
 | 
						|
  // as aSize, unless aSize is greater than the screen width/height.
 | 
						|
  nscoord SlideOrResize(nscoord& aScreenPoint, nscoord aSize,
 | 
						|
                        nscoord aScreenBegin, nscoord aScreenEnd,
 | 
						|
                        nscoord* aOffset) const;
 | 
						|
 | 
						|
  // Given an anchor frame, compute the anchor rectangle relative to the screen,
 | 
						|
  // using the popup frame's app units, and taking into account transforms.
 | 
						|
  nsRect ComputeAnchorRect(nsPresContext* aRootPresContext,
 | 
						|
                           nsIFrame* aAnchorFrame) const;
 | 
						|
 | 
						|
  // Move the popup to the position specified in its |left| and |top|
 | 
						|
  // attributes.
 | 
						|
  void MoveToAttributePosition();
 | 
						|
 | 
						|
  // Create a popup view for this frame. The view is added a child of the root
 | 
						|
  // view, and is initially hidden.
 | 
						|
  void CreatePopupView();
 | 
						|
 | 
						|
  nsView* GetViewInternal() const override { return mView; }
 | 
						|
  void SetViewInternal(nsView* aView) override { mView = aView; }
 | 
						|
 | 
						|
  // Returns true if the popup should try to remain at the same relative
 | 
						|
  // location as the anchor while it is open. If the anchor becomes hidden
 | 
						|
  // either directly or indirectly because a parent popup or other element
 | 
						|
  // is no longer visible, or a parent deck page is changed, the popup hides
 | 
						|
  // as well. The second variation also sets the anchor rectangle, relative to
 | 
						|
  // the popup frame.
 | 
						|
  bool ShouldFollowAnchor() const;
 | 
						|
 | 
						|
  nsIFrame* GetAnchorFrame() const;
 | 
						|
 | 
						|
 public:
 | 
						|
  /**
 | 
						|
   * Return whether the popup direction should be RTL.
 | 
						|
   * If the popup has an anchor, its direction is the anchor direction.
 | 
						|
   * Otherwise, its the general direction of the UI.
 | 
						|
   *
 | 
						|
   * Return whether the popup direction should be RTL.
 | 
						|
   */
 | 
						|
  bool IsDirectionRTL() const;
 | 
						|
 | 
						|
  bool ShouldFollowAnchor(nsRect& aRect);
 | 
						|
 | 
						|
  // Returns parent menu widget for submenus that are in the same
 | 
						|
  // frame hierarchy, it's needed for Linux/Wayland which demands
 | 
						|
  // strict popup windows hierarchy.
 | 
						|
  nsIWidget* GetParentMenuWidget();
 | 
						|
 | 
						|
  // Returns the effective margin for this popup. This is the CSS margin plus
 | 
						|
  // the context-menu shift, if needed.
 | 
						|
  nsMargin GetMargin() const;
 | 
						|
 | 
						|
  // These are used by Wayland backend.
 | 
						|
  const nsRect& GetUntransformedAnchorRect() const {
 | 
						|
    return mUntransformedAnchorRect;
 | 
						|
  }
 | 
						|
  int GetPopupAlignment() const { return mPopupAlignment; }
 | 
						|
  int GetPopupAnchor() const { return mPopupAnchor; }
 | 
						|
  FlipType GetFlipType() const { return mFlip; }
 | 
						|
 | 
						|
  void WidgetPositionOrSizeDidChange();
 | 
						|
 | 
						|
 protected:
 | 
						|
  nsString mIncrementalString;  // for incremental typing navigation
 | 
						|
 | 
						|
  // the content that the popup is anchored to, if any, which may be in a
 | 
						|
  // different document than the popup.
 | 
						|
  nsCOMPtr<nsIContent> mAnchorContent;
 | 
						|
 | 
						|
  // the content that triggered the popup, typically the node where the mouse
 | 
						|
  // was clicked. It will be cleared when the popup is hidden.
 | 
						|
  nsCOMPtr<nsIContent> mTriggerContent;
 | 
						|
 | 
						|
  nsView* mView = nullptr;
 | 
						|
 | 
						|
  RefPtr<nsXULPopupShownEvent> mPopupShownDispatcher;
 | 
						|
 | 
						|
  // The popup's screen rectangle in app units.
 | 
						|
  nsRect mUsedScreenRect;
 | 
						|
 | 
						|
  // A popup's preferred size may be different than its actual size stored in
 | 
						|
  // mRect in the case where the popup was resized because it was too large
 | 
						|
  // for the screen. The preferred size mPrefSize holds the full size the popup
 | 
						|
  // would be before resizing. Computations are performed using this size.
 | 
						|
  nsSize mPrefSize{-1, -1};
 | 
						|
 | 
						|
  // The position of the popup, in CSS pixels.
 | 
						|
  // The screen coordinates, if set to values other than -1,
 | 
						|
  // override mXPos and mYPos.
 | 
						|
  int32_t mXPos = 0;
 | 
						|
  int32_t mYPos = 0;
 | 
						|
  nsRect mScreenRect;
 | 
						|
  // Used for store rectangle which the popup is going to be anchored to, we
 | 
						|
  // need that for Wayland. It's important that this rect is unflipped, and
 | 
						|
  // without margins applied, as GTK is what takes care of determining how to
 | 
						|
  // flip etc. on Wayland.
 | 
						|
  nsRect mUntransformedAnchorRect;
 | 
						|
 | 
						|
  // If the panel prefers to "slide" rather than resize, then the arrow gets
 | 
						|
  // positioned at this offset (along either the x or y axis, depending on
 | 
						|
  // mPosition)
 | 
						|
  nscoord mAlignmentOffset = 0;
 | 
						|
 | 
						|
  // The value of the client offset of our widget the last time we positioned
 | 
						|
  // ourselves. We store this so that we can detect when it changes but the
 | 
						|
  // position of our widget didn't change.
 | 
						|
  mozilla::LayoutDeviceIntPoint mLastClientOffset;
 | 
						|
 | 
						|
  PopupType mPopupType = PopupType::Panel;  // type of popup
 | 
						|
  nsPopupState mPopupState = ePopupClosed;  // open state of the popup
 | 
						|
 | 
						|
  // popup alignment relative to the anchor node
 | 
						|
  int8_t mPopupAlignment = POPUPALIGNMENT_NONE;
 | 
						|
  int8_t mPopupAnchor = POPUPALIGNMENT_NONE;
 | 
						|
  int8_t mPosition = POPUPPOSITION_UNKNOWN;
 | 
						|
 | 
						|
  FlipType mFlip = FlipType_Default;  // Whether to flip
 | 
						|
 | 
						|
  // Whether we were moved by the move-to-rect Wayland callback. In that case,
 | 
						|
  // we stop updating the anchor so that we can end up with a stable position.
 | 
						|
  bool mPositionedByMoveToRect = false;
 | 
						|
  // true if the open state changed since the last layout.
 | 
						|
  bool mIsOpenChanged = false;
 | 
						|
  // true for context menus and their submenus.
 | 
						|
  bool mIsContextMenu = false;
 | 
						|
  // true for the topmost context menu.
 | 
						|
  bool mIsTopLevelContextMenu = false;
 | 
						|
  // true if the popup is in a content shell.
 | 
						|
  bool mInContentShell = true;
 | 
						|
 | 
						|
  // The flip modes that were used when the popup was opened
 | 
						|
  bool mHFlip = false;
 | 
						|
  bool mVFlip = false;
 | 
						|
  // Whether layout has constrained this popup in some way.
 | 
						|
  bool mConstrainedByLayout = false;
 | 
						|
 | 
						|
  // Whether the most recent initialization of this menupopup happened via
 | 
						|
  // InitializePopupAsNativeContextMenu.
 | 
						|
  bool mIsNativeMenu = false;
 | 
						|
 | 
						|
  // Whether we have a pending `popuppositioned` event.
 | 
						|
  bool mPendingPositionedEvent = false;
 | 
						|
 | 
						|
  // Whether this popup is source of D&D operation. We can't close such
 | 
						|
  // popup on Wayland as it cancel whole D&D operation.
 | 
						|
  bool mIsDragSource = false;
 | 
						|
 | 
						|
  // When POPUPPOSITION_SELECTION is used, this indicates the vertical offset
 | 
						|
  // that the original selected item was. This needs to be used in case the
 | 
						|
  // popup gets changed so that we can keep the popup at the same vertical
 | 
						|
  // offset.
 | 
						|
  // TODO(emilio): try to make this not mutable.
 | 
						|
  mutable nscoord mPositionedOffset = 0;
 | 
						|
 | 
						|
  // How the popup is anchored.
 | 
						|
  MenuPopupAnchorType mAnchorType = MenuPopupAnchorType_Node;
 | 
						|
 | 
						|
  nsRect mOverrideConstraintRect;
 | 
						|
 | 
						|
  static int8_t sDefaultLevelIsTop;
 | 
						|
 | 
						|
  static mozilla::TimeStamp sLastKeyTime;
 | 
						|
 | 
						|
};  // class nsMenuPopupFrame
 | 
						|
 | 
						|
#endif
 |