зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1698997 - Make nsXULPopupManager::ShowPopupAtScreen open a native context menu, if preffed on. r=tnikkel
This is macOS only and behind the prefs widget.macos.native-context-menus and browser.proton.contextmenus.enabled . The big design question here is: Where do we put the fork in the road? How much should the existing non-native popup management machinery know about the state of the native menu? Which parts of the non-native state should a) reflect the true native state, b) enter a special "handled natively" state, or c) lie? This patch picks the following approach: - The nsMenuPopupFrame of the root menupopup knows about the native menu; it keeps it alive in its new mNativeMenu field. - If the context menu has submenus, i.e. nested <menupopup> elements, the nsMenuPopupFrames for those nested menupopups do not know anything about the native menu. - The mPopupState of natively-handled nsMenuPopupFrames is ePopupClosed. - XULPopupElement::GetState, which queries the frame's mPopupState, also returns "closed". This might cause problems in the future. - The XUL popup manager's mPopups "menu chain" does not know about any open native menus. - The rollup widget also does not know about the native popup. I've chosen to use ePopupClosed for native menus for the following reasons: 1. While mirroring / updating the state for the root menu would be doable without too much trouble, it would be much more annoying to do the same for nested menupopups of submenus. So if submenus will be ePopupClosed, it's consistent for the root menu to also be ePopupClosed. 2. nsXULPopupManager has assertions (for example in MayShowPopup) that make sure that the menu popup frame's mPopupState is consistent with the XUL popup manager's mPopups menu chain. Keeping the two both "closed" is the easiest way to achieve consistency. Unless there are grave concerns with this approach, I suggest we go with it for now and see what trouble arises. Differential Revision: https://phabricator.services.mozilla.com/D109183
This commit is contained in:
Родитель
ed5ee3edd2
Коммит
f92e83a7db
|
@ -61,6 +61,9 @@
|
|||
#ifdef MOZ_WAYLAND
|
||||
# include "mozilla/WidgetUtilsGtk.h"
|
||||
#endif /* MOZ_WAYLAND */
|
||||
#ifdef XP_MACOSX
|
||||
# include "mozilla/widget/NativeMenuSupport.h"
|
||||
#endif
|
||||
|
||||
#include "X11UndefineNone.h"
|
||||
|
||||
|
@ -69,6 +72,7 @@ using mozilla::dom::Document;
|
|||
using mozilla::dom::Element;
|
||||
using mozilla::dom::Event;
|
||||
using mozilla::dom::KeyboardEvent;
|
||||
using mozilla::widget::NativeMenu;
|
||||
|
||||
int8_t nsMenuPopupFrame::sDefaultLevelIsTop = -1;
|
||||
|
||||
|
@ -129,6 +133,12 @@ nsMenuPopupFrame::nsMenuPopupFrame(ComputedStyle* aStyle,
|
|||
Preferences::GetBool("ui.panel.default_level_parent", false);
|
||||
} // ctor
|
||||
|
||||
nsMenuPopupFrame::~nsMenuPopupFrame() {
|
||||
if (mNativeMenu) {
|
||||
mNativeMenu->RemoveObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
void nsMenuPopupFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
|
||||
nsIFrame* aPrevInFlow) {
|
||||
nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
|
||||
|
@ -903,6 +913,40 @@ void nsMenuPopupFrame::InitializePopupAtScreen(nsIContent* aTriggerContent,
|
|||
mPositionedOffset = 0;
|
||||
}
|
||||
|
||||
bool nsMenuPopupFrame::InitializePopupAsNativeContextMenu(
|
||||
nsIContent* aTriggerContent, int32_t aXPos, int32_t aYPos) {
|
||||
RefPtr<NativeMenu> menu;
|
||||
#ifdef XP_MACOSX
|
||||
if (mContent->IsElement()) {
|
||||
menu = mozilla::widget::NativeMenuSupport::CreateNativeContextMenu(
|
||||
mContent->AsElement());
|
||||
}
|
||||
#endif
|
||||
if (!menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mTriggerContent = aTriggerContent;
|
||||
mNativeMenu = menu;
|
||||
mPopupState = ePopupClosed; // Treat native popups as closed.
|
||||
mAnchorContent = nullptr;
|
||||
mScreenRect = nsIntRect(aXPos, aYPos, 0, 0);
|
||||
mXPos = 0;
|
||||
mYPos = 0;
|
||||
mFlip = FlipType_Default;
|
||||
mPopupAnchor = POPUPALIGNMENT_NONE;
|
||||
mPopupAlignment = POPUPALIGNMENT_NONE;
|
||||
mPosition = POPUPPOSITION_UNKNOWN;
|
||||
mIsContextMenu = true;
|
||||
mAdjustOffsetForContextMenu = true;
|
||||
mAnchorType = MenuPopupAnchorType_Point;
|
||||
mPositionedOffset = 0;
|
||||
|
||||
mNativeMenu->AddObserver(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void nsMenuPopupFrame::InitializePopupAtRect(nsIContent* aTriggerContent,
|
||||
const nsAString& aPosition,
|
||||
const nsIntRect& aRect,
|
||||
|
@ -912,6 +956,17 @@ void nsMenuPopupFrame::InitializePopupAtRect(nsIContent* aTriggerContent,
|
|||
mScreenRect = aRect;
|
||||
}
|
||||
|
||||
void nsMenuPopupFrame::ShowNativeMenu() {
|
||||
MOZ_RELEASE_ASSERT(mNativeMenu);
|
||||
bool succeeded = mNativeMenu->ShowAsContextMenu(
|
||||
DesktopPoint::FromUnknownPoint(mScreenRect.TopLeft()));
|
||||
if (!succeeded) {
|
||||
// preventDefault() was called on the popupshowing event.
|
||||
mNativeMenu->RemoveObserver(this);
|
||||
mNativeMenu = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void nsMenuPopupFrame::ShowPopup(bool aIsContextMenu) {
|
||||
mIsContextMenu = aIsContextMenu;
|
||||
|
||||
|
@ -953,6 +1008,18 @@ void nsMenuPopupFrame::ShowPopup(bool aIsContextMenu) {
|
|||
mShouldAutoPosition = true;
|
||||
}
|
||||
|
||||
void nsMenuPopupFrame::OnNativeMenuClosed() {
|
||||
if (!mNativeMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The native menu has closed.
|
||||
// Null out mNativeMenu so that we don't keep it (and mContent) alive
|
||||
// unnecessarily, and unregister ourselves first.
|
||||
mNativeMenu->RemoveObserver(this);
|
||||
mNativeMenu = nullptr;
|
||||
}
|
||||
|
||||
void nsMenuPopupFrame::HidePopup(bool aDeselectMenu, nsPopupState aNewState) {
|
||||
NS_ASSERTION(aNewState == ePopupClosed || aNewState == ePopupInvisible,
|
||||
"popup being set to unexpected state");
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/StaticPrefs_ui.h"
|
||||
#include "mozilla/widget/NativeMenu.h"
|
||||
#include "nsAtom.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -167,12 +168,14 @@ class nsXULPopupShownEvent final : public mozilla::Runnable,
|
|||
|
||||
class nsMenuPopupFrame final : public nsBoxFrame,
|
||||
public nsMenuParent,
|
||||
public nsIReflowCallback {
|
||||
public nsIReflowCallback,
|
||||
public mozilla::widget::NativeMenu::Observer {
|
||||
public:
|
||||
NS_DECL_QUERYFRAME
|
||||
NS_DECL_FRAMEARENA_HELPERS(nsMenuPopupFrame)
|
||||
|
||||
explicit nsMenuPopupFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
|
||||
~nsMenuPopupFrame();
|
||||
|
||||
// nsMenuParent interface
|
||||
virtual nsMenuFrame* GetCurrentMenuItem() override;
|
||||
|
@ -317,6 +320,16 @@ class nsMenuPopupFrame final : public nsBoxFrame,
|
|||
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.
|
||||
// This is achieved with the help of mNativeMenu.
|
||||
// Returns whether the native context menu was created successfully.
|
||||
bool InitializePopupAsNativeContextMenu(nsIContent* aTriggerContent,
|
||||
int32_t aXPos, int32_t aYPos);
|
||||
|
||||
// Must only be called after a call to InitializePopupAsNativeContextMenu
|
||||
// that returned true.
|
||||
void ShowNativeMenu();
|
||||
|
||||
// indicate that the popup should be opened
|
||||
void ShowPopup(bool aIsContextMenu);
|
||||
// indicate that the popup should be hidden. The new state should either be
|
||||
|
@ -443,6 +456,10 @@ class nsMenuPopupFrame final : public nsBoxFrame,
|
|||
virtual bool ReflowFinished() override;
|
||||
virtual void ReflowCallbackCanceled() override;
|
||||
|
||||
// NativeMenu::Observer
|
||||
void OnNativeMenuOpened() override {}
|
||||
void OnNativeMenuClosed() override;
|
||||
|
||||
protected:
|
||||
// returns the popup's level.
|
||||
nsPopupLevel PopupLevel(bool aIsNoAutoHide) const;
|
||||
|
@ -561,6 +578,11 @@ class nsMenuPopupFrame final : public nsBoxFrame,
|
|||
protected:
|
||||
nsString mIncrementalString; // for incremental typing navigation
|
||||
|
||||
// If this 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;
|
||||
|
||||
// the content that the popup is anchored to, if any, which may be in a
|
||||
// different document than the popup.
|
||||
nsCOMPtr<nsIContent> mAnchorContent;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPrefs_ui.h"
|
||||
#include "mozilla/StaticPrefs_widget.h"
|
||||
#include "mozilla/StaticPrefs_xul.h"
|
||||
#include "mozilla/widget/nsAutoRollup.h"
|
||||
|
||||
|
@ -708,6 +709,15 @@ void nsXULPopupManager::ShowPopup(nsIContent* aPopup,
|
|||
aTriggerEvent);
|
||||
}
|
||||
|
||||
static bool ShouldUseNativeContextMenus() {
|
||||
#ifdef XP_MACOSX
|
||||
return StaticPrefs::widget_macos_native_context_menus() &&
|
||||
Preferences::GetBool("browser.proton.contextmenus.enabled", false);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void nsXULPopupManager::ShowPopupAtScreen(nsIContent* aPopup, int32_t aXPos,
|
||||
int32_t aYPos, bool aIsContextMenu,
|
||||
Event* aTriggerEvent) {
|
||||
|
@ -717,6 +727,15 @@ void nsXULPopupManager::ShowPopupAtScreen(nsIContent* aPopup, int32_t aXPos,
|
|||
nsCOMPtr<nsIContent> triggerContent;
|
||||
InitTriggerEvent(aTriggerEvent, aPopup, getter_AddRefs(triggerContent));
|
||||
|
||||
if (aIsContextMenu && ShouldUseNativeContextMenus()) {
|
||||
bool haveNativeMenu = popupFrame->InitializePopupAsNativeContextMenu(
|
||||
triggerContent, aXPos, aYPos);
|
||||
if (haveNativeMenu) {
|
||||
popupFrame->ShowNativeMenu();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
popupFrame->InitializePopupAtScreen(triggerContent, aXPos, aYPos,
|
||||
aIsContextMenu);
|
||||
FirePopupShowingEvent(aPopup, aIsContextMenu, false, aTriggerEvent);
|
||||
|
|
|
@ -10829,6 +10829,11 @@
|
|||
mirror: always
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
- name: widget.macos.native-context-menus
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
- name: widget.macos.respect-system-appearance
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
|
|
Загрузка…
Ссылка в новой задаче