From 3278a43751d266cce1402f756a9ae1dc31aa1c3e Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Sun, 3 May 2026 22:09:17 +0200 Subject: [PATCH] gh-13294: Fix macos crash with native popovers (gh-13546) --- .../firefox/native_macos_popovers.patch | 146 ++++++++++-------- src/external-patches/manifest.json | 14 +- src/zen/tabs/zen-tabs/vertical-tabs.css | 4 +- 3 files changed, 93 insertions(+), 71 deletions(-) diff --git a/src/external-patches/firefox/native_macos_popovers.patch b/src/external-patches/firefox/native_macos_popovers.patch index 8c23d2d7d..9029cc846 100644 --- a/src/external-patches/firefox/native_macos_popovers.patch +++ b/src/external-patches/firefox/native_macos_popovers.patch @@ -1,7 +1,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml --- a/browser/base/content/main-popupset.inc.xhtml +++ b/browser/base/content/main-popupset.inc.xhtml -@@ -196,10 +196,11 @@ +@@ -192,10 +192,11 @@ -@@ -207,10 +208,11 @@ +@@ -203,10 +204,11 @@ -@@ -560,10 +562,11 @@ +@@ -610,10 +612,11 @@ type="arrow" orient="vertical" noautofocus="true" @@ -40,7 +40,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content diff --git a/browser/components/asrouter/modules/FeatureCallout.sys.mjs b/browser/components/asrouter/modules/FeatureCallout.sys.mjs --- a/browser/components/asrouter/modules/FeatureCallout.sys.mjs +++ b/browser/components/asrouter/modules/FeatureCallout.sys.mjs -@@ -1046,10 +1046,11 @@ +@@ -1054,10 +1054,11 @@ noautofocus="true" flip="slide" type="arrow" @@ -70,22 +70,22 @@ diff --git a/browser/components/customizableui/content/panelUI.inc.xhtml b/brows diff --git a/dom/xul/XULPopupElement.cpp b/dom/xul/XULPopupElement.cpp --- a/dom/xul/XULPopupElement.cpp +++ b/dom/xul/XULPopupElement.cpp -@@ -82,10 +82,14 @@ +@@ -80,10 +80,14 @@ + } void XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos, bool aIsContextMenu, Event* aTriggerEvent) { - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + // TODO(cheff): At nsCocoaWindow::Show but we check for ShouldShowAsNSPopover + // to determine whether to use a native popover or not. This should sort of + // "replicate" that logic here, but it's a bit of a hacky way. + SetAttr(kNameSpaceID_None, nsGkAtoms::nonnativepopover, u"true"_ns, true); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) { pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent); } } - -@@ -94,10 +98,14 @@ +@@ -93,10 +97,14 @@ int32_t aWidth, int32_t aHeight, bool aIsContextMenu, bool aAttributesOverride, @@ -103,7 +103,7 @@ diff --git a/dom/xul/XULPopupElement.cpp b/dom/xul/XULPopupElement.cpp diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h --- a/layout/xul/nsMenuPopupFrame.h +++ b/layout/xul/nsMenuPopupFrame.h -@@ -530,18 +530,10 @@ +@@ -528,18 +528,10 @@ // Move the popup to the position specified in its |left| and |top| // attributes. @@ -122,7 +122,7 @@ diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h public: /** * Return whether the popup direction should be RTL. -@@ -550,10 +542,18 @@ +@@ -548,10 +540,18 @@ * * Return whether the popup direction should be RTL. */ @@ -144,7 +144,7 @@ diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml -@@ -19477,10 +19477,19 @@ +@@ -19672,10 +19672,19 @@ value: true mirror: always @@ -167,7 +167,7 @@ diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/Sta diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css --- a/toolkit/themes/shared/global-shared.css +++ b/toolkit/themes/shared/global-shared.css -@@ -85,10 +85,22 @@ +@@ -72,10 +72,22 @@ --menuitem-border-radius: var(--arrowpanel-menuitem-border-radius); --menuitem-padding: var(--arrowpanel-menuitem-padding); --menuitem-margin: var(--arrowpanel-menuitem-margin); @@ -251,7 +251,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm -@@ -3,10 +3,13 @@ +@@ -4,10 +4,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsCocoaWindow.h" @@ -265,7 +265,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm #include "nsIDOMWindowUtils.h" #include "nsILocalFileMac.h" #include "CocoaCompositorWidget.h" -@@ -4973,10 +4976,15 @@ +@@ -5031,10 +5034,15 @@ if (mWindowType == WindowType::Popup) { SetPopupWindowLevel(); mWindow.backgroundColor = NSColor.clearColor; @@ -281,7 +281,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // the active space. Does not work with multiple displays. See // NeedsRecreateToReshow() for multi-display with multi-space workaround. mWindow.collectionBehavior = mWindow.collectionBehavior | -@@ -5178,10 +5186,57 @@ +@@ -5236,10 +5244,57 @@ NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -339,54 +339,58 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; if (!mWindow) { -@@ -5242,10 +5297,54 @@ +@@ -5300,10 +5355,58 @@ mWindow.contentView.needsDisplay = YES; if (!nativeParentWindow || mPopupLevel != PopupLevel::Parent) { [mWindow orderFront:nil]; } NS_OBJC_END_TRY_IGNORE_BLOCK; -+ if (ShouldShowAsNSPopover()) { ++ if (ShouldShowAsNSPopover() && nativeParentWindow) { + nsMenuPopupFrame* popupFrame = GetPopupFrame(); -+ if (nativeParentWindow) { -+ NSRectEdge preferredEdge = -+ AlignmentPositionToNSRectEdge(popupFrame->GetAlignmentPosition()); -+ nsRect anchorRectAppUnits = popupFrame->GetUntransformedAnchorRect(); -+ nsPresContext* pc = popupFrame->PresContext(); -+ int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel(); -+ mozilla::DesktopToLayoutDeviceScale desktopToLayoutScale = -+ pc->DeviceContext()->GetDesktopToDeviceScale(); -+ mozilla::DesktopIntRect popupAnchorRectScaled = -+ mozilla::DesktopIntRect::RoundOut( -+ mozilla::LayoutDeviceRect::FromAppUnits(anchorRectAppUnits, -+ appUnitsPerDevPixel) / -+ desktopToLayoutScale); -+ // Taking the now correctly scaled anchor rect and turning it into a -+ // gecko rect this accounts for the y-axis inversion that cocoa needs, -+ // as the origin is in the bottom left. This rect is in screen space -+ NSRect cocoaScreenRect = -+ nsCocoaUtils::GeckoRectToCocoaRect(popupAnchorRectScaled); -+ // We take the screen space rect and convert it to window space -+ // coordinates, as NSPopover requires the coordinates to be in view -+ // space and inside the view. If the coordinates are outside our view, -+ // the popover will fail silently -+ NSRect windowRect = -+ [nativeParentWindow convertRectFromScreen:cocoaScreenRect]; -+ NSView* parentView = [nativeParentWindow contentView]; -+ // We take the window space rect and convert it to view space for the -+ // specific parent view -+ NSRect positioningRect = [parentView convertRect:windowRect -+ fromView:nil]; -+ BOOL shouldHideAnchor = NO; -+ auto& element = popupFrame->PopupElement(); -+ if (element.GetBoolAttr(nsGkAtoms::hidepopovertail)) { -+ shouldHideAnchor = YES; -+ } -+ [(PopupWindow*)mWindow showPopoverRelativeToRect:positioningRect -+ ofView:parentView -+ preferredEdge:preferredEdge -+ hiddenAnchor:shouldHideAnchor]; -+ SyncPopoverBounds([(PopupWindow*)mWindow popover], popupFrame); ++ NSRectEdge preferredEdge = ++ AlignmentPositionToNSRectEdge(popupFrame->GetAlignmentPosition()); ++ nsRect anchorRectAppUnits = popupFrame->GetUntransformedAnchorRect(); ++ nsPresContext* pc = popupFrame->PresContext(); ++ int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel(); ++ mozilla::DesktopToLayoutDeviceScale desktopToLayoutScale = ++ pc->DeviceContext()->GetDesktopToDeviceScale(); ++ mozilla::DesktopIntRect popupAnchorRectScaled = ++ mozilla::DesktopIntRect::RoundOut( ++ mozilla::LayoutDeviceRect::FromAppUnits(anchorRectAppUnits, ++ appUnitsPerDevPixel) / ++ desktopToLayoutScale); ++ // Taking the now correctly scaled anchor rect and turning it into a ++ // gecko rect this accounts for the y-axis inversion that cocoa needs, ++ // as the origin is in the bottom left. This rect is in screen space ++ NSRect cocoaScreenRect = ++ nsCocoaUtils::GeckoRectToCocoaRect(popupAnchorRectScaled); ++ // We take the screen space rect and convert it to window space ++ // coordinates, as NSPopover requires the coordinates to be in view ++ // space and inside the view. If the coordinates are outside our view, ++ // the popover will fail silently ++ NSRect windowRect = ++ [nativeParentWindow convertRectFromScreen:cocoaScreenRect]; ++ NSView* parentView = [nativeParentWindow contentView]; ++ // We take the window space rect and convert it to view space for the ++ // specific parent view ++ NSRect positioningRect = [parentView convertRect:windowRect ++ fromView:nil]; ++ BOOL shouldHideAnchor = NO; ++ auto& element = popupFrame->PopupElement(); ++ if (element.GetBoolAttr(nsGkAtoms::hidepopovertail)) { ++ shouldHideAnchor = YES; + } ++ [(PopupWindow*)mWindow showPopoverRelativeToRect:positioningRect ++ ofView:parentView ++ preferredEdge:preferredEdge ++ hiddenAnchor:shouldHideAnchor]; ++ SyncPopoverBounds([(PopupWindow*)mWindow popover], popupFrame); ++ if (mPopupLevel == PopupLevel::Parent) { ++ [nativeParentWindow addChildWindow:mWindow ordered:NSWindowAbove]; ++ } ++ ++ // Exit early here since the popover is now shown. ++ mWindow.isBeingShown = NO; + return; + } // If our popup window is a non-native context menu, tell the OS (and @@ -394,12 +398,13 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // close other programs' context menus when ours open. if ([mWindow isKindOfClass:[PopupWindow class]] && [(PopupWindow*)mWindow isContextMenu]) { -@@ -5316,10 +5415,15 @@ +@@ -5373,11 +5476,15 @@ + // unhook it here before ordering it out. When you order out the child // of a window it hides the parent window. if (mWindowType == WindowType::Popup && nativeParentWindow) { [nativeParentWindow removeChildWindow:mWindow]; } - +- + // Handle NSPopover hiding or traditional window hiding + if ([mWindow isKindOfClass:[PopupWindow class]] && + [(PopupWindow*)mWindow usePopover]) { @@ -410,7 +415,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // other programs) that a menu has closed. if ([mWindow isKindOfClass:[PopupWindow class]] && [(PopupWindow*)mWindow isContextMenu]) { -@@ -5366,10 +5470,28 @@ +@@ -5424,10 +5531,28 @@ return false; } return nsIWidget::ShouldUseOffMainThreadCompositing(); @@ -439,12 +444,12 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm return mWindow.isOpaque ? TransparencyMode::Opaque : TransparencyMode::Transparent; -@@ -6328,10 +6450,20 @@ +@@ -6378,10 +6503,19 @@ + // We ignore aRepaint -- we have to call display:YES, otherwise the // title bar doesn't immediately get repainted and is displayed in // the wrong place, leading to a visual jump. [mWindow setFrame:newFrame display:YES]; - + if (ShouldUseNSPopover() && [(PopupWindow*)mWindow usePopover]) { + [(PopupWindow*)mWindow updatePopoverContent]; + // A popover won't resize by setting the frame @@ -454,18 +459,18 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm + [[(PopupWindow*)mWindow popover] setContentSize:contentSize]; + SyncPopoverBounds([(PopupWindow*)mWindow popover], GetPopupFrame()); + } -+ + NS_OBJC_END_TRY_IGNORE_BLOCK; } void nsCocoaWindow::Resize(const DesktopRect& aRect, bool aRepaint) { - DoResize(aRect.x, aRect.y, aRect.width, aRect.height, aRepaint, false); -@@ -8314,17 +8446,26 @@ +@@ -8393,18 +8527,31 @@ + backing:bufferingType defer:deferCreation]; if (!self) { return nil; } - +- + mPopover = nil; + mPopoverViewController = nil; + mUsePopover = NO; @@ -477,6 +482,11 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm } +- (void)dealloc { ++ if (mPopover) { ++ ChildViewMouseTracker::OnDestroyWindow( ++ mPopover.contentViewController.view.window); ++ } ++ + [mPopover release]; + [mPopoverViewController release]; + [super dealloc]; @@ -487,7 +497,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // Return 0 in order to match what the system does for sheet windows and // _NSPopoverWindows. - (CGFloat)_backdropBleedAmount { -@@ -8378,10 +8519,122 @@ +@@ -8460,10 +8607,122 @@ - (void)setIsContextMenu:(BOOL)flag { mIsContextMenu = flag; @@ -613,7 +623,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h -@@ -843,10 +843,15 @@ +@@ -829,10 +829,15 @@ virtual void SuppressAnimation(bool aSuppress) {} /** Sets windows-specific mica backdrop on this widget. */ @@ -644,7 +654,7 @@ diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py Atom("highest", "highest"), Atom("horizontal", "horizontal"), Atom("hover", "hover"), -@@ -757,10 +758,11 @@ +@@ -759,10 +760,11 @@ Atom("nohref", "nohref"), Atom("noinitialselection", "noinitialselection"), Atom("nomodule", "nomodule"), diff --git a/src/external-patches/manifest.json b/src/external-patches/manifest.json index ea0199ba6..671ab5007 100644 --- a/src/external-patches/manifest.json +++ b/src/external-patches/manifest.json @@ -10,7 +10,19 @@ // Specifically trying to target FeatureCallout.sys.mjs's change. // IMPORTANT: Make sure Feature callouts STILL use native popopvers when // syncing from upstream, as this is a critical part of the patch. - "+ nonnativepopover=\"true\"": "+ " + "+ nonnativepopover=\"true\"": "+ ", + + // Fix conflicts with upstream changes. + "--menuitem-border-radius: var(--panel-menuitem-border-radius)": "--menuitem-border-radius: var(--arrowpanel-menuitem-border-radius)", + "--menuitem-padding: var(--panel-menuitem-padding)": "--menuitem-padding: var(--arrowpanel-menuitem-padding)", + "--menuitem-margin: var(--panel-menuitem-margin)": "--menuitem-margin: var(--arrowpanel-menuitem-margin)", + + " \n #include \"nsCocoaWindow.h\"\n \n #include \"nsISupportsPrimitives.h\"\n #include \"nsArrayUtils.h\"": + " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n \n #include \"nsCocoaWindow.h\"\n \n #include \"nsISupportsPrimitives.h\"\n #include \"nsArrayUtils.h\"", + " #include \"nsISupportsPrimitives.h\"\n": "", + + " Atom(\"nonnative\", \"nonnative\"),\n": "", + "Atom(\"noscript\", \"noscript\"),": "Atom(\"noscript\", \"noscript\"),\n Atom(\"noshade\", \"noshade\")," } }, { diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index b0fdebe7a..2052aaa5e 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -318,7 +318,7 @@ &, & .tab-content > image { transition: - scale 0.2s ease, + scale 0.1s ease, var(--zen-tabbox-element-indent-transition); } } @@ -1077,7 +1077,7 @@ #tabs-newtab-button { max-height: var(--tab-min-height); display: flex !important; - transition: scale 0.2s ease; + transition: scale 0.1s ease; #tabbrowser-tabs[movingtab] & { transition: transform 0.1s ease; }