mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
6674caa946
Differential Revision: https://phabricator.services.mozilla.com/D195549
910 lines
33 KiB
C++
910 lines
33 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/. */
|
|
|
|
/**
|
|
* The XUL Popup Manager keeps track of all open popups.
|
|
*/
|
|
|
|
#ifndef nsXULPopupManager_h__
|
|
#define nsXULPopupManager_h__
|
|
|
|
#include "mozilla/Logging.h"
|
|
#include "nsHashtablesFwd.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIRollupListener.h"
|
|
#include "nsIDOMEventListener.h"
|
|
#include "Units.h"
|
|
#include "nsPoint.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsTArray.h"
|
|
#include "nsIObserver.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/FunctionRef.h"
|
|
#include "mozilla/widget/InitData.h"
|
|
#include "mozilla/widget/NativeMenu.h"
|
|
|
|
// XXX Avoid including this here by moving function bodies to the cpp file.
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
// X.h defines KeyPress
|
|
#ifdef KeyPress
|
|
# undef KeyPress
|
|
#endif
|
|
|
|
/**
|
|
* There are two types that are used:
|
|
* - dismissable popups such as menus, which should close up when there is a
|
|
* click outside the popup. In this situation, the entire chain of menus
|
|
* above should also be closed.
|
|
* - panels, which stay open until a request is made to close them. This
|
|
* type is used by tooltips.
|
|
*
|
|
* When a new popup is opened, it is appended to the popup chain, stored in a
|
|
* linked list in mPopups.
|
|
* Popups are stored in this list linked from newest to oldest. When a click
|
|
* occurs outside one of the open dismissable popups, the chain is closed by
|
|
* calling Rollup.
|
|
*/
|
|
|
|
class nsContainerFrame;
|
|
class nsITimer;
|
|
class nsIDocShellTreeItem;
|
|
class nsMenuPopupFrame;
|
|
class nsPIDOMWindowOuter;
|
|
class nsRefreshDriver;
|
|
|
|
namespace mozilla {
|
|
class PresShell;
|
|
namespace dom {
|
|
class Event;
|
|
class KeyboardEvent;
|
|
class UIEvent;
|
|
class XULButtonElement;
|
|
class XULMenuBarElement;
|
|
class XULPopupElement;
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
// XUL popups can be in several different states. When opening a popup, the
|
|
// state changes as follows:
|
|
// ePopupClosed - initial state
|
|
// ePopupShowing - during the period when the popupshowing event fires
|
|
// ePopupOpening - between the popupshowing event and being visible. Creation
|
|
// of the child frames, layout and reflow occurs in this
|
|
// state. The popup is stored in the popup manager's list of
|
|
// open popups during this state.
|
|
// ePopupVisible - layout is done and the popup's view and widget are made
|
|
// visible. The popup is visible on screen but may be
|
|
// transitioning. The popupshown event has not yet fired.
|
|
// ePopupShown - the popup has been shown and is fully ready. This state is
|
|
// assigned just before the popupshown event fires.
|
|
// When closing a popup:
|
|
// ePopupHidden - during the period when the popuphiding event fires and
|
|
// the popup is removed.
|
|
// ePopupClosed - the popup's widget is made invisible.
|
|
enum nsPopupState {
|
|
// state when a popup is not open
|
|
ePopupClosed,
|
|
// state from when a popup is requested to be shown to after the
|
|
// popupshowing event has been fired.
|
|
ePopupShowing,
|
|
// state while a popup is waiting to be laid out and positioned
|
|
ePopupPositioning,
|
|
// state while a popup is open but the widget is not yet visible
|
|
ePopupOpening,
|
|
// state while a popup is visible and waiting for the popupshown event
|
|
ePopupVisible,
|
|
// state while a popup is open and visible on screen
|
|
ePopupShown,
|
|
// state from when a popup is requested to be hidden to when it is closed.
|
|
ePopupHiding,
|
|
// state which indicates that the popup was hidden without firing the
|
|
// popuphiding or popuphidden events. It is used when executing a menu
|
|
// command because the menu needs to be hidden before the command event
|
|
// fires, yet the popuphiding and popuphidden events are fired after. This
|
|
// state can also occur when the popup is removed because the document is
|
|
// unloaded.
|
|
ePopupInvisible
|
|
};
|
|
|
|
// when a menu command is executed, the closemenu attribute may be used
|
|
// to define how the menu should be closed up
|
|
enum CloseMenuMode {
|
|
CloseMenuMode_Auto, // close up the chain of menus, default value
|
|
CloseMenuMode_None, // don't close up any menus
|
|
CloseMenuMode_Single // close up only the menu the command is inside
|
|
};
|
|
|
|
/**
|
|
* nsNavigationDirection: an enum expressing navigation through the menus in
|
|
* terms which are independent of the directionality of the chrome. The
|
|
* terminology, derived from XSL-FO and CSS3 (e.g.
|
|
* http://www.w3.org/TR/css3-text/#TextLayout), is BASE (Before, After, Start,
|
|
* End), with the addition of First and Last (mapped to Home and End
|
|
* respectively).
|
|
*
|
|
* In languages such as English where the inline progression is left-to-right
|
|
* and the block progression is top-to-bottom (lr-tb), these terms will map out
|
|
* as in the following diagram
|
|
*
|
|
* --- inline progression --->
|
|
*
|
|
* First |
|
|
* ... |
|
|
* Before |
|
|
* +--------+ block
|
|
* Start | | End progression
|
|
* +--------+ |
|
|
* After |
|
|
* ... |
|
|
* Last V
|
|
*
|
|
*/
|
|
|
|
enum nsNavigationDirection {
|
|
eNavigationDirection_Last,
|
|
eNavigationDirection_First,
|
|
eNavigationDirection_Start,
|
|
eNavigationDirection_Before,
|
|
eNavigationDirection_End,
|
|
eNavigationDirection_After
|
|
};
|
|
|
|
enum nsIgnoreKeys {
|
|
eIgnoreKeys_False,
|
|
eIgnoreKeys_True,
|
|
eIgnoreKeys_Shortcuts,
|
|
};
|
|
|
|
enum class HidePopupOption : uint8_t {
|
|
// If the entire chain of menus should be closed.
|
|
HideChain,
|
|
// If the parent <menu> of the popup should not be deselected. This will not
|
|
// be set when the menu is closed by pressing the Escape key.
|
|
DeselectMenu,
|
|
// If the first popuphiding event should be sent asynchrously. This should
|
|
// be set if HidePopup is called from a frame.
|
|
Async,
|
|
// If this popup is hiding due to being cancelled.
|
|
IsRollup,
|
|
// Whether animations should be disabled for rolled-up popups.
|
|
DisableAnimations,
|
|
};
|
|
|
|
using HidePopupOptions = mozilla::EnumSet<HidePopupOption>;
|
|
|
|
/**
|
|
* DirectionFromKeyCodeTable: two arrays, the first for left-to-right and the
|
|
* other for right-to-left, that map keycodes to values of
|
|
* nsNavigationDirection.
|
|
*/
|
|
extern const nsNavigationDirection DirectionFromKeyCodeTable[2][6];
|
|
|
|
#define NS_DIRECTION_FROM_KEY_CODE(frame, keycode) \
|
|
(DirectionFromKeyCodeTable[static_cast<uint8_t>( \
|
|
(frame)->StyleVisibility()->mDirection)][( \
|
|
keycode)-mozilla::dom::KeyboardEvent_Binding::DOM_VK_END])
|
|
|
|
// Used to hold information about a popup that is about to be opened.
|
|
struct PendingPopup {
|
|
using Element = mozilla::dom::Element;
|
|
using Event = mozilla::dom::Event;
|
|
|
|
PendingPopup(Element* aPopup, Event* aEvent);
|
|
|
|
const RefPtr<Element> mPopup;
|
|
const RefPtr<Event> mEvent;
|
|
|
|
// Device pixels relative to the showing popup's presshell's
|
|
// root prescontext's root frame.
|
|
mozilla::LayoutDeviceIntPoint mMousePoint;
|
|
|
|
// Cached modifiers used to trigger the popup.
|
|
mozilla::Modifiers mModifiers;
|
|
|
|
already_AddRefed<nsIContent> GetTriggerContent() const;
|
|
|
|
void InitMousePoint();
|
|
|
|
void SetMousePoint(mozilla::LayoutDeviceIntPoint aMousePoint) {
|
|
mMousePoint = aMousePoint;
|
|
}
|
|
|
|
uint16_t MouseInputSource() const;
|
|
};
|
|
|
|
// nsMenuChainItem holds info about an open popup. Items are stored in a
|
|
// doubly linked list. Note that the linked list is stored beginning from
|
|
// the lowest child in a chain of menus, as this is the active submenu.
|
|
class nsMenuChainItem {
|
|
using PopupType = mozilla::widget::PopupType;
|
|
|
|
nsMenuPopupFrame* mFrame; // the popup frame
|
|
PopupType mPopupType; // the popup type of the frame
|
|
bool mNoAutoHide; // true for noautohide panels
|
|
bool mIsContext; // true for context menus
|
|
bool mOnMenuBar; // true if the menu is on a menu bar
|
|
nsIgnoreKeys mIgnoreKeys; // indicates how keyboard listeners should be used
|
|
|
|
// True if the popup should maintain its position relative to the anchor when
|
|
// the anchor moves.
|
|
bool mFollowAnchor;
|
|
|
|
// The last seen position of the anchor, relative to the screen.
|
|
nsRect mCurrentRect;
|
|
|
|
mozilla::UniquePtr<nsMenuChainItem> mParent;
|
|
// Back pointer, safe because mChild keeps us alive.
|
|
nsMenuChainItem* mChild = nullptr;
|
|
|
|
public:
|
|
nsMenuChainItem(nsMenuPopupFrame* aFrame, bool aNoAutoHide, bool aIsContext,
|
|
PopupType aPopupType)
|
|
: mFrame(aFrame),
|
|
mPopupType(aPopupType),
|
|
mNoAutoHide(aNoAutoHide),
|
|
mIsContext(aIsContext),
|
|
mOnMenuBar(false),
|
|
mIgnoreKeys(eIgnoreKeys_False),
|
|
mFollowAnchor(false) {
|
|
NS_ASSERTION(aFrame, "null frame passed to nsMenuChainItem constructor");
|
|
MOZ_COUNT_CTOR(nsMenuChainItem);
|
|
}
|
|
|
|
MOZ_COUNTED_DTOR(nsMenuChainItem)
|
|
|
|
mozilla::dom::XULPopupElement* Element();
|
|
nsMenuPopupFrame* Frame() { return mFrame; }
|
|
PopupType GetPopupType() { return mPopupType; }
|
|
bool IsNoAutoHide() { return mNoAutoHide; }
|
|
void SetNoAutoHide(bool aNoAutoHide) { mNoAutoHide = aNoAutoHide; }
|
|
bool IsMenu() { return mPopupType == PopupType::Menu; }
|
|
bool IsContextMenu() { return mIsContext; }
|
|
nsIgnoreKeys IgnoreKeys() { return mIgnoreKeys; }
|
|
void SetIgnoreKeys(nsIgnoreKeys aIgnoreKeys) { mIgnoreKeys = aIgnoreKeys; }
|
|
bool IsOnMenuBar() { return mOnMenuBar; }
|
|
void SetOnMenuBar(bool aOnMenuBar) { mOnMenuBar = aOnMenuBar; }
|
|
nsMenuChainItem* GetParent() { return mParent.get(); }
|
|
nsMenuChainItem* GetChild() { return mChild; }
|
|
bool FollowsAnchor() { return mFollowAnchor; }
|
|
void UpdateFollowAnchor();
|
|
void CheckForAnchorChange();
|
|
|
|
// set the parent of this item to aParent, also changing the parent
|
|
// to have this as a child.
|
|
void SetParent(mozilla::UniquePtr<nsMenuChainItem> aParent);
|
|
// Removes the parent pointer and returns it.
|
|
mozilla::UniquePtr<nsMenuChainItem> Detach();
|
|
};
|
|
|
|
// this class is used for dispatching popuphiding events asynchronously.
|
|
class nsXULPopupHidingEvent : public mozilla::Runnable {
|
|
using PopupType = mozilla::widget::PopupType;
|
|
using Element = mozilla::dom::Element;
|
|
|
|
public:
|
|
nsXULPopupHidingEvent(Element* aPopup, Element* aNextPopup,
|
|
Element* aLastPopup, PopupType aPopupType,
|
|
HidePopupOptions aOptions)
|
|
: mozilla::Runnable("nsXULPopupHidingEvent"),
|
|
mPopup(aPopup),
|
|
mNextPopup(aNextPopup),
|
|
mLastPopup(aLastPopup),
|
|
mPopupType(aPopupType),
|
|
mOptions(aOptions) {
|
|
NS_ASSERTION(aPopup,
|
|
"null popup supplied to nsXULPopupHidingEvent constructor");
|
|
// aNextPopup and aLastPopup may be null
|
|
}
|
|
|
|
NS_IMETHOD Run() override;
|
|
|
|
private:
|
|
nsCOMPtr<Element> mPopup;
|
|
nsCOMPtr<Element> mNextPopup;
|
|
nsCOMPtr<Element> mLastPopup;
|
|
PopupType mPopupType;
|
|
HidePopupOptions mOptions;
|
|
};
|
|
|
|
// this class is used for dispatching popuppositioned events asynchronously.
|
|
class nsXULPopupPositionedEvent : public mozilla::Runnable {
|
|
using Element = mozilla::dom::Element;
|
|
|
|
public:
|
|
explicit nsXULPopupPositionedEvent(Element* aPopup)
|
|
: mozilla::Runnable("nsXULPopupPositionedEvent"), mPopup(aPopup) {
|
|
MOZ_ASSERT(aPopup);
|
|
}
|
|
|
|
NS_IMETHOD Run() override;
|
|
|
|
// Asynchronously dispatch a popuppositioned event at aPopup if this is a
|
|
// panel that should receieve such events. Return true if the event was sent.
|
|
static bool DispatchIfNeeded(Element* aPopup);
|
|
|
|
private:
|
|
const nsCOMPtr<Element> mPopup;
|
|
};
|
|
|
|
// this class is used for dispatching menu command events asynchronously.
|
|
class nsXULMenuCommandEvent : public mozilla::Runnable {
|
|
using Element = mozilla::dom::Element;
|
|
|
|
public:
|
|
nsXULMenuCommandEvent(Element* aMenu, bool aIsTrusted,
|
|
mozilla::Modifiers aModifiers, bool aUserInput,
|
|
bool aFlipChecked, int16_t aButton)
|
|
: mozilla::Runnable("nsXULMenuCommandEvent"),
|
|
mMenu(aMenu),
|
|
mModifiers(aModifiers),
|
|
mButton(aButton),
|
|
mIsTrusted(aIsTrusted),
|
|
mUserInput(aUserInput),
|
|
mFlipChecked(aFlipChecked),
|
|
mCloseMenuMode(CloseMenuMode_Auto) {
|
|
NS_ASSERTION(aMenu,
|
|
"null menu supplied to nsXULMenuCommandEvent constructor");
|
|
}
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
|
|
|
|
void SetCloseMenuMode(CloseMenuMode aCloseMenuMode) {
|
|
mCloseMenuMode = aCloseMenuMode;
|
|
}
|
|
|
|
private:
|
|
RefPtr<Element> mMenu;
|
|
|
|
mozilla::Modifiers mModifiers;
|
|
int16_t mButton;
|
|
bool mIsTrusted;
|
|
bool mUserInput;
|
|
bool mFlipChecked;
|
|
CloseMenuMode mCloseMenuMode;
|
|
};
|
|
|
|
class nsXULPopupManager final : public nsIDOMEventListener,
|
|
public nsIRollupListener,
|
|
public nsIObserver,
|
|
public mozilla::widget::NativeMenu::Observer {
|
|
public:
|
|
friend class nsXULPopupHidingEvent;
|
|
friend class nsXULPopupPositionedEvent;
|
|
friend class nsXULMenuCommandEvent;
|
|
friend class TransitionEnder;
|
|
|
|
using PopupType = mozilla::widget::PopupType;
|
|
using Element = mozilla::dom::Element;
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
NS_DECL_NSIDOMEVENTLISTENER
|
|
|
|
// nsIRollupListener
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
|
bool Rollup(const RollupOptions&,
|
|
nsIContent** aLastRolledUp = nullptr) override;
|
|
bool ShouldRollupOnMouseWheelEvent() override;
|
|
bool ShouldConsumeOnMouseWheelEvent() override;
|
|
bool ShouldRollupOnMouseActivate() override;
|
|
uint32_t GetSubmenuWidgetChain(nsTArray<nsIWidget*>* aWidgetChain) override;
|
|
nsIWidget* GetRollupWidget() override;
|
|
bool RollupNativeMenu() override;
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool RollupTooltips();
|
|
|
|
enum class RollupKind { Tooltip, Menu };
|
|
MOZ_CAN_RUN_SCRIPT
|
|
bool RollupInternal(RollupKind, const RollupOptions&,
|
|
nsIContent** aLastRolledUp);
|
|
|
|
// NativeMenu::Observer
|
|
void OnNativeMenuOpened() override;
|
|
void OnNativeMenuClosed() override;
|
|
void OnNativeSubMenuWillOpen(mozilla::dom::Element* aPopupElement) override;
|
|
void OnNativeSubMenuDidOpen(mozilla::dom::Element* aPopupElement) override;
|
|
void OnNativeSubMenuClosed(mozilla::dom::Element* aPopupElement) override;
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY void OnNativeMenuWillActivateItem(
|
|
mozilla::dom::Element* aMenuItemElement) override;
|
|
|
|
static nsXULPopupManager* sInstance;
|
|
|
|
// initialize and shutdown methods called by nsLayoutStatics
|
|
static nsresult Init();
|
|
static void Shutdown();
|
|
|
|
// returns a weak reference to the popup manager instance, could return null
|
|
// if a popup manager could not be allocated
|
|
static nsXULPopupManager* GetInstance();
|
|
|
|
// This should be called when a window is moved or resized to adjust the
|
|
// popups accordingly.
|
|
void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter* aWindow);
|
|
void AdjustPopupsOnWindowChange(mozilla::PresShell* aPresShell);
|
|
|
|
// inform the popup manager that a menu bar has been activated or deactivated,
|
|
// either because one of its menus has opened or closed, or that the menubar
|
|
// has been focused such that its menus may be navigated with the keyboard.
|
|
// aActivate should be true when the menubar should be focused, and false
|
|
// when the active menu bar should be defocused. In the latter case, if
|
|
// aMenuBar isn't currently active, yet another menu bar is, that menu bar
|
|
// will remain active.
|
|
void SetActiveMenuBar(mozilla::dom::XULMenuBarElement* aMenuBar,
|
|
bool aActivate);
|
|
|
|
struct MayShowMenuResult {
|
|
const bool mIsNative = false;
|
|
mozilla::dom::XULButtonElement* const mMenuButton = nullptr;
|
|
nsMenuPopupFrame* const mMenuPopupFrame = nullptr;
|
|
|
|
explicit operator bool() const {
|
|
MOZ_ASSERT(!!mMenuButton == !!mMenuPopupFrame);
|
|
return mIsNative || mMenuButton;
|
|
}
|
|
};
|
|
|
|
MayShowMenuResult MayShowMenu(nsIContent* aMenu);
|
|
|
|
/**
|
|
* Open a <menu> given its content node. If aSelectFirstItem is
|
|
* set to true, the first item on the menu will automatically be
|
|
* selected.
|
|
*/
|
|
void ShowMenu(nsIContent* aMenu, bool aSelectFirstItem);
|
|
|
|
/**
|
|
* Open a popup, either anchored or unanchored. If aSelectFirstItem is
|
|
* true, then the first item in the menu is selected. The arguments are
|
|
* similar to those for XULPopupElement::OpenPopup.
|
|
*
|
|
* aTriggerEvent should be the event that triggered the event. This is used
|
|
* to determine the coordinates and trigger node for the popup. This may be
|
|
* null if the popup was not triggered by an event.
|
|
*
|
|
* This fires the popupshowing event synchronously.
|
|
*/
|
|
void ShowPopup(Element* aPopup, nsIContent* aAnchorContent,
|
|
const nsAString& aPosition, int32_t aXPos, int32_t aYPos,
|
|
bool aIsContextMenu, bool aAttributesOverride,
|
|
bool aSelectFirstItem, mozilla::dom::Event* aTriggerEvent);
|
|
|
|
/**
|
|
* Open a popup at a specific screen position specified by aXPos and aYPos,
|
|
* measured in CSS pixels.
|
|
*
|
|
* This fires the popupshowing event synchronously.
|
|
*
|
|
* If aIsContextMenu is true, the popup is positioned at a slight
|
|
* offset from aXPos/aYPos to ensure that it is not under the mouse
|
|
* cursor.
|
|
*/
|
|
void ShowPopupAtScreen(Element* aPopup, int32_t aXPos, int32_t aYPos,
|
|
bool aIsContextMenu,
|
|
mozilla::dom::Event* aTriggerEvent);
|
|
|
|
/* Open a popup anchored at a screen rectangle specified by aRect.
|
|
* The remaining arguments are similar to ShowPopup.
|
|
*/
|
|
void ShowPopupAtScreenRect(Element* aPopup, const nsAString& aPosition,
|
|
const nsIntRect& aRect, bool aIsContextMenu,
|
|
bool aAttributesOverride,
|
|
mozilla::dom::Event* aTriggerEvent);
|
|
|
|
/**
|
|
* Open a popup as a native menu, at a specific screen position specified by
|
|
* aXPos and aYPos, measured in CSS pixels.
|
|
*
|
|
* This fires the popupshowing event synchronously.
|
|
*
|
|
* Returns whether native menus are supported for aPopup on this platform.
|
|
* TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY bool ShowPopupAsNativeMenu(
|
|
Element* aPopup, int32_t aXPos, int32_t aYPos, bool aIsContextMenu,
|
|
mozilla::dom::Event* aTriggerEvent);
|
|
|
|
/**
|
|
* Open a tooltip at a specific screen position specified by aXPos and aYPos,
|
|
* measured in device pixels. This fires the popupshowing event synchronously.
|
|
*/
|
|
void ShowTooltipAtScreen(Element* aPopup, nsIContent* aTriggerContent,
|
|
const mozilla::LayoutDeviceIntPoint&);
|
|
|
|
/*
|
|
* Hide a popup aPopup. If the popup is in a <menu>, then also inform the
|
|
* menu that the popup is being hidden.
|
|
* aLastPopup - optional popup to close last when hiding a chain of menus.
|
|
* If null, then all popups will be closed.
|
|
*/
|
|
void HidePopup(Element* aPopup, HidePopupOptions,
|
|
Element* aLastPopup = nullptr);
|
|
|
|
/*
|
|
* Hide the popup of a <menu>.
|
|
*/
|
|
void HideMenu(nsIContent* aMenu);
|
|
|
|
/**
|
|
* Hide a popup after a short delay. This is used when rolling over menu
|
|
* items. This timer is stored in mCloseTimer. The timer may be cancelled and
|
|
* the popup closed by calling KillMenuTimer.
|
|
*/
|
|
void HidePopupAfterDelay(nsMenuPopupFrame* aPopup, int32_t aDelay);
|
|
|
|
/**
|
|
* Hide all of the popups from a given docshell. This should be called when
|
|
* the document is hidden.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
|
void HidePopupsInDocShell(nsIDocShellTreeItem* aDocShellToHide);
|
|
|
|
/**
|
|
* Check if any popups need to be repositioned or hidden after a style or
|
|
* layout change. This will update, for example, any arrow type panels when
|
|
* the anchor that is is pointing to has moved, resized or gone away.
|
|
* Only those popups that pertain to the supplied aRefreshDriver are updated.
|
|
*/
|
|
void UpdatePopupPositions(nsRefreshDriver* aRefreshDriver);
|
|
|
|
/**
|
|
* Get the first nsMenuChainItem that is matched by the matching callback
|
|
* function provided.
|
|
*/
|
|
nsMenuChainItem* FirstMatchingPopup(
|
|
mozilla::FunctionRef<bool(nsMenuChainItem*)> aMatcher) const;
|
|
|
|
/**
|
|
* Enable or disable anchor following on the popup if needed.
|
|
*/
|
|
void UpdateFollowAnchor(nsMenuPopupFrame* aPopup);
|
|
|
|
/**
|
|
* Execute a menu command from the triggering event aEvent.
|
|
*
|
|
* aMenu - a menuitem to execute
|
|
* aEvent - an nsXULMenuCommandEvent that contains all the info from the mouse
|
|
* event which triggered the menu to be executed, may not be null
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT void ExecuteMenu(nsIContent* aMenu,
|
|
nsXULMenuCommandEvent* aEvent);
|
|
|
|
/**
|
|
* If a native menu is open, and aItem is an item in the menu's subtree,
|
|
* execute the item with the help of the native menu and close the menu.
|
|
* Returns true if a native menu was open.
|
|
*/
|
|
bool ActivateNativeMenuItem(nsIContent* aItem, mozilla::Modifiers aModifiers,
|
|
int16_t aButton, mozilla::ErrorResult& aRv);
|
|
|
|
/**
|
|
* Return true if the popup for the supplied content node is open.
|
|
*/
|
|
bool IsPopupOpen(Element* aPopup);
|
|
|
|
/**
|
|
* Return the frame for the topmost open popup of a given type, or null if
|
|
* no popup of that type is open. If aType is PopupType::Any, a menu of any
|
|
* type is returned.
|
|
*/
|
|
nsIFrame* GetTopPopup(PopupType aType);
|
|
|
|
/**
|
|
* Returns the topmost active menuitem that's currently visible, if any.
|
|
*/
|
|
nsIContent* GetTopActiveMenuItemContent();
|
|
|
|
/**
|
|
* Return an array of all the open and visible popup frames for
|
|
* menus, in order from top to bottom.
|
|
*/
|
|
void GetVisiblePopups(nsTArray<nsIFrame*>& aPopups);
|
|
|
|
/**
|
|
* Get the node that last triggered a popup or tooltip in the document
|
|
* aDocument. aDocument must be non-null and be a document contained within
|
|
* the same window hierarchy as the popup to retrieve.
|
|
*/
|
|
already_AddRefed<nsINode> GetLastTriggerPopupNode(
|
|
mozilla::dom::Document* aDocument) {
|
|
return GetLastTriggerNode(aDocument, false);
|
|
}
|
|
|
|
already_AddRefed<nsINode> GetLastTriggerTooltipNode(
|
|
mozilla::dom::Document* aDocument) {
|
|
return GetLastTriggerNode(aDocument, true);
|
|
}
|
|
|
|
/**
|
|
* Return false if a popup may not be opened. This will return false if the
|
|
* popup is already open, if the popup is in a content shell that is not
|
|
* focused, or if it is a submenu of another menu that isn't open.
|
|
*/
|
|
bool MayShowPopup(nsMenuPopupFrame* aFrame);
|
|
|
|
/**
|
|
* Indicate that the popup associated with aView has been moved to the
|
|
* specified device pixel coordinates.
|
|
*/
|
|
void PopupMoved(nsIFrame* aFrame, const mozilla::LayoutDeviceIntPoint& aPoint,
|
|
bool aByMoveToRect);
|
|
|
|
/**
|
|
* Indicate that the popup associated with aView has been resized to the
|
|
* given device pixel size aSize.
|
|
*/
|
|
void PopupResized(nsIFrame* aFrame,
|
|
const mozilla::LayoutDeviceIntSize& aSize);
|
|
|
|
/**
|
|
* Called when a popup frame is destroyed. In this case, just remove the
|
|
* item and later popups from the list. No point going through HidePopup as
|
|
* the frames have gone away.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT void PopupDestroyed(nsMenuPopupFrame* aFrame);
|
|
|
|
/**
|
|
* Returns true if there is a context menu open. If aPopup is specified,
|
|
* then the context menu must be later in the chain than aPopup. If aPopup
|
|
* is null, returns true if any context menu at all is open.
|
|
*/
|
|
bool HasContextMenu(nsMenuPopupFrame* aPopup);
|
|
|
|
/**
|
|
* Update the commands for the menus within the menu popup for a given
|
|
* content node. aPopup should be a XUL menupopup element. This method
|
|
* changes attributes on the children of aPopup, and deals only with the
|
|
* content of the popup, not the frames.
|
|
*/
|
|
void UpdateMenuItems(Element* aPopup);
|
|
|
|
/**
|
|
* Stop the timer which hides a popup after a delay, started by a previous
|
|
* call to HidePopupAfterDelay. In addition, the popup awaiting to be hidden
|
|
* is closed asynchronously.
|
|
*/
|
|
void KillMenuTimer();
|
|
|
|
/**
|
|
* Cancel the timer which closes menus after delay, but only if the menu to
|
|
* close is aMenuParent. When a submenu is opened, the user might move the
|
|
* mouse over a sibling menuitem which would normally close the menu. This
|
|
* menu is closed via a timer. However, if the user moves the mouse over the
|
|
* submenu before the timer fires, we should instead cancel the timer. This
|
|
* ensures that the user can move the mouse diagonally over a menu.
|
|
*/
|
|
void CancelMenuTimer(nsMenuPopupFrame*);
|
|
|
|
/**
|
|
* Handles navigation for menu accelkeys. If aFrame is specified, then the
|
|
* key is handled by that popup, otherwise if aFrame is null, the key is
|
|
* handled by the active popup or menubar.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT bool HandleShortcutNavigation(
|
|
mozilla::dom::KeyboardEvent& aKeyEvent, nsMenuPopupFrame* aFrame);
|
|
|
|
/**
|
|
* Handles cursor navigation within a menu. Returns true if the key has
|
|
* been handled.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT bool HandleKeyboardNavigation(uint32_t aKeyCode);
|
|
|
|
/**
|
|
* Handle keyboard navigation within a menu popup specified by aFrame.
|
|
* Returns true if the key was handled and other default handling
|
|
* should not occur.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT bool HandleKeyboardNavigationInPopup(
|
|
nsMenuPopupFrame* aFrame, nsNavigationDirection aDir) {
|
|
return HandleKeyboardNavigationInPopup(nullptr, aFrame, aDir);
|
|
}
|
|
|
|
/**
|
|
* Handles the keyboard event with keyCode value. Returns true if the event
|
|
* has been handled.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT bool HandleKeyboardEventWithKeyCode(
|
|
mozilla::dom::KeyboardEvent* aKeyEvent,
|
|
nsMenuChainItem* aTopVisibleMenuItem);
|
|
|
|
// Sets mIgnoreKeys of the Top Visible Menu Item
|
|
nsresult UpdateIgnoreKeys(bool aIgnoreKeys);
|
|
|
|
nsPopupState GetPopupState(mozilla::dom::Element* aPopupElement);
|
|
|
|
mozilla::dom::Event* GetOpeningPopupEvent() const {
|
|
return mPendingPopup->mEvent.get();
|
|
}
|
|
|
|
MOZ_CAN_RUN_SCRIPT nsresult KeyUp(mozilla::dom::KeyboardEvent* aKeyEvent);
|
|
MOZ_CAN_RUN_SCRIPT nsresult KeyDown(mozilla::dom::KeyboardEvent* aKeyEvent);
|
|
MOZ_CAN_RUN_SCRIPT nsresult KeyPress(mozilla::dom::KeyboardEvent* aKeyEvent);
|
|
|
|
protected:
|
|
nsXULPopupManager();
|
|
~nsXULPopupManager();
|
|
|
|
// get the nsMenuPopupFrame, if any, for the given content node
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
|
nsMenuPopupFrame* GetPopupFrameForContent(nsIContent* aContent,
|
|
bool aShouldFlush);
|
|
|
|
// Get the menu to start rolling up.
|
|
nsMenuChainItem* GetRollupItem(RollupKind);
|
|
|
|
// Return the topmost menu, skipping over invisible popups
|
|
nsMenuChainItem* GetTopVisibleMenu() {
|
|
return GetRollupItem(RollupKind::Menu);
|
|
}
|
|
|
|
// Add the chain item to the chain and update mPopups to point to it.
|
|
void AddMenuChainItem(mozilla::UniquePtr<nsMenuChainItem>);
|
|
|
|
// Removes the chain item from the chain and deletes it.
|
|
void RemoveMenuChainItem(nsMenuChainItem*);
|
|
|
|
// Hide all of the visible popups from the given list. This function can
|
|
// cause style changes and frame destruction.
|
|
MOZ_CAN_RUN_SCRIPT void HidePopupsInList(
|
|
const nsTArray<nsMenuPopupFrame*>& aFrames);
|
|
|
|
// Hide, but don't close, visible menus. Called before executing a menu item.
|
|
// The caller promises to close the menus properly (with a call to HidePopup)
|
|
// once the item has been executed.
|
|
MOZ_CAN_RUN_SCRIPT void HideOpenMenusBeforeExecutingMenu(CloseMenuMode aMode);
|
|
|
|
// callbacks for ShowPopup and HidePopup as events may be done asynchronously
|
|
MOZ_CAN_RUN_SCRIPT void ShowPopupCallback(Element* aPopup,
|
|
nsMenuPopupFrame* aPopupFrame,
|
|
bool aIsContextMenu,
|
|
bool aSelectFirstItem);
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY void HidePopupCallback(
|
|
Element* aPopup, nsMenuPopupFrame* aPopupFrame, Element* aNextPopup,
|
|
Element* aLastPopup, PopupType aPopupType, HidePopupOptions);
|
|
|
|
/**
|
|
* Trigger frame construction and reflow in the popup, fire a popupshowing
|
|
* event on the popup and then open the popup.
|
|
*
|
|
* aPendingPopup - information about the popup to open
|
|
* aIsContextMenu - true for context menus
|
|
* aSelectFirstItem - true to select the first item in the menu
|
|
* TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY void BeginShowingPopup(
|
|
const PendingPopup& aPendingPopup, bool aIsContextMenu,
|
|
bool aSelectFirstItem);
|
|
|
|
/**
|
|
* Fire a popuphiding event and then hide the popup. This will be called
|
|
* recursively if aNextPopup and aLastPopup are set in order to hide a chain
|
|
* of open menus. If these are not set, only one popup is closed. However,
|
|
* if the popup type indicates a menu, yet the next popup is not a menu,
|
|
* then this ends the closing of popups. This allows a menulist inside a
|
|
* non-menu to close up the menu but not close up the panel it is contained
|
|
* within.
|
|
*
|
|
* The caller must keep a strong reference to aPopup, aNextPopup and
|
|
* aLastPopup.
|
|
*
|
|
* aPopup - the popup to hide
|
|
* aNextPopup - the next popup to hide
|
|
* aLastPopup - the last popup in the chain to hide
|
|
* aPresContext - nsPresContext for the popup's frame
|
|
* aPopupType - the PopupType of the frame.
|
|
* aOptions - the relevant options to hide the popup. Only a subset is looked
|
|
* at.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
|
void FirePopupHidingEvent(Element* aPopup, Element* aNextPopup,
|
|
Element* aLastPopup, nsPresContext* aPresContext,
|
|
PopupType aPopupType, HidePopupOptions aOptions);
|
|
|
|
/**
|
|
* Handle keyboard navigation within a menu popup specified by aItem.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT
|
|
bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
|
|
nsNavigationDirection aDir) {
|
|
return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* Handle keyboard navigation within a menu popup aFrame. If aItem is
|
|
* supplied, then it is expected to have a frame equal to aFrame.
|
|
* If aItem is non-null, then the navigation may be redirected to
|
|
* an open submenu if one exists. Returns true if the key was
|
|
* handled and other default handling should not occur.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT bool HandleKeyboardNavigationInPopup(
|
|
nsMenuChainItem* aItem, nsMenuPopupFrame* aFrame,
|
|
nsNavigationDirection aDir);
|
|
|
|
protected:
|
|
already_AddRefed<nsINode> GetLastTriggerNode(
|
|
mozilla::dom::Document* aDocument, bool aIsTooltip);
|
|
|
|
/**
|
|
* Fire a popupshowing event for aPopup.
|
|
*/
|
|
MOZ_CAN_RUN_SCRIPT nsEventStatus FirePopupShowingEvent(
|
|
const PendingPopup& aPendingPopup, nsPresContext* aPresContext);
|
|
|
|
/**
|
|
* Set mouse capturing for the current popup. This traps mouse clicks that
|
|
* occur outside the popup so that it can be closed up. aOldPopup should be
|
|
* set to the popup that was previously the current popup.
|
|
*/
|
|
void SetCaptureState(nsIContent* aOldPopup);
|
|
|
|
/**
|
|
* Key event listeners are attached to the document containing the current
|
|
* menu for menu and shortcut navigation. Only one listener is needed at a
|
|
* time, stored in mKeyListener, so switch it only if the document changes.
|
|
* Having menus in different documents is very rare, so the listeners will
|
|
* usually only be attached when the first menu opens and removed when all
|
|
* menus have closed.
|
|
*
|
|
* This is also used when only a menubar is active without any open menus,
|
|
* so that keyboard navigation between menus on the menubar may be done.
|
|
*/
|
|
// TODO: Convert UpdateKeyboardListeners() to MOZ_CAN_RUN_SCRIPT and get rid
|
|
// of the kungFuDeathGrip in it.
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY void UpdateKeyboardListeners();
|
|
|
|
/*
|
|
* Returns true if the docshell for aDoc is aExpected or a child of aExpected.
|
|
*/
|
|
bool IsChildOfDocShell(mozilla::dom::Document* aDoc,
|
|
nsIDocShellTreeItem* aExpected);
|
|
|
|
// Finds a chain item in mPopups.
|
|
nsMenuChainItem* FindPopup(Element* aPopup) const;
|
|
|
|
// the document the key event listener is attached to
|
|
nsCOMPtr<mozilla::dom::EventTarget> mKeyListener;
|
|
|
|
// widget that is currently listening to rollup events
|
|
nsCOMPtr<nsIWidget> mWidget;
|
|
|
|
// set to the currently active menu bar, if any
|
|
mozilla::dom::XULMenuBarElement* mActiveMenuBar;
|
|
|
|
// linked list of normal menus and panels. mPopups points to the innermost
|
|
// popup, which keeps alive all their parents.
|
|
mozilla::UniquePtr<nsMenuChainItem> mPopups;
|
|
|
|
// timer used for HidePopupAfterDelay
|
|
nsCOMPtr<nsITimer> mCloseTimer;
|
|
nsMenuPopupFrame* mTimerMenu = nullptr;
|
|
|
|
// Information about the popup that is currently firing a popupshowing event.
|
|
const PendingPopup* mPendingPopup;
|
|
|
|
// If a popup is displayed as a native menu, this is non-null while the
|
|
// native menu is open.
|
|
// mNativeMenu has a strong reference to the menupopup nsIContent.
|
|
RefPtr<mozilla::widget::NativeMenu> mNativeMenu;
|
|
|
|
// If the currently open native menu activated an item, this is the item's
|
|
// close menu mode. Nothing() if mNativeMenu is null or if no item was
|
|
// activated.
|
|
mozilla::Maybe<CloseMenuMode> mNativeMenuActivatedItemCloseMenuMode;
|
|
|
|
// If a popup is displayed as a native menu, this map contains the popup state
|
|
// for any of its non-closed submenus. This state cannot be stored on the
|
|
// submenus' nsMenuPopupFrames, because we usually don't generate frames for
|
|
// the contents of native menus.
|
|
// If a submenu is not present in this map, it means it's closed.
|
|
// This map is empty if mNativeMenu is null.
|
|
nsTHashMap<RefPtr<mozilla::dom::Element>, nsPopupState>
|
|
mNativeMenuSubmenuStates;
|
|
};
|
|
|
|
#endif
|