tor-browser

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

commit 63275687e99f6b31ebb0fe64a713f17e8a98a62f
parent 65007e8566b88457a96096561eb59bda4cec3c46
Author: Markus Stange <mstange.moz@gmail.com>
Date:   Tue, 21 Oct 2025 21:55:01 +0000

Bug 1995621 - Enforce a maximum depth for native menu nesting on macOS. r=mac-reviewers,spohl

Differential Revision: https://phabricator.services.mozilla.com/D269452

Diffstat:
Mwidget/cocoa/nsMenuBarX.h | 1+
Mwidget/cocoa/nsMenuParentX.h | 3+++
Mwidget/cocoa/nsMenuX.h | 3+++
Mwidget/cocoa/nsMenuX.mm | 16+++++++++++++++-
4 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/widget/cocoa/nsMenuBarX.h b/widget/cocoa/nsMenuBarX.h @@ -118,6 +118,7 @@ class nsMenuBarX : public nsMenuParentX, // nsMenuParentX void MenuChildChangedVisibility(const MenuChild& aChild, bool aIsVisible) override; + size_t NestingDepth() override { return 0; } protected: virtual ~nsMenuBarX(); diff --git a/widget/cocoa/nsMenuParentX.h b/widget/cocoa/nsMenuParentX.h @@ -18,6 +18,9 @@ class nsMenuParentX { public: using MenuChild = mozilla::Variant<RefPtr<nsMenuX>, RefPtr<nsMenuItemX>>; + // The nesting depth; 0 for the root. + virtual size_t NestingDepth() = 0; + // XXXmstange double-check that this is still needed virtual nsMenuBarX* AsMenuBar() { return nullptr; } diff --git a/widget/cocoa/nsMenuX.h b/widget/cocoa/nsMenuX.h @@ -175,6 +175,7 @@ class nsMenuX final : public nsMenuParentX, // nsMenuParentX void MenuChildChangedVisibility(const MenuChild& aChild, bool aIsVisible) override; + size_t NestingDepth() override { return mNestingDepth; } void Dump(uint32_t aIndent) const; @@ -292,6 +293,8 @@ class nsMenuX final : public nsMenuParentX, // items. mozilla::Maybe<uint32_t> mHighlightedItemIndex; + size_t mNestingDepth = 0; + bool mIsEnabled = true; bool mNeedsRebuild = true; diff --git a/widget/cocoa/nsMenuX.mm b/widget/cocoa/nsMenuX.mm @@ -46,6 +46,11 @@ using namespace mozilla::dom; static bool gConstructingMenu = false; static bool gMenuMethodsSwizzled = false; +// Protect against really deep menu nestings, for example from recursive +// bookmark folders. This avoids hangs when the system enumerates the entire +// menu tree. +static const size_t kMaxMenuNestingDepth = 20; + int32_t nsMenuX::sIndexingMenuLevel = 0; // TODO: It is unclear whether this is still needed. @@ -86,7 +91,10 @@ static void SwizzleDynamicIndexingMethods() { nsMenuX::nsMenuX(nsMenuParentX* aParent, nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aContent) - : mContent(aContent), mParent(aParent), mMenuGroupOwner(aMenuGroupOwner) { + : mContent(aContent), + mParent(aParent), + mMenuGroupOwner(aMenuGroupOwner), + mNestingDepth(aParent ? aParent->NestingDepth() + 1 : 0) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; MOZ_COUNT_CTOR(nsMenuX); @@ -121,6 +129,12 @@ nsMenuX::nsMenuX(nsMenuParentX* aParent, nsMenuGroupOwnerX* aMenuGroupOwner, keyEquivalent:@""]; mNativeMenuItem.submenu = mNativeMenu; + if (mNestingDepth > kMaxMenuNestingDepth) { + // If we're nested too deep, turn this item into a regular item without a + // submenu. + mNativeMenuItem.submenu = nil; + } + SetEnabled(!mContent->IsElement() || !mContent->AsElement()->AttrValueIs( kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true,