Compare commits

..

3 Commits

Author SHA1 Message Date
mr-cheffy
690fff1ca0 chore: Sync upstream to Firefox 152.0 2026-06-11 07:52:02 +00:00
Ashvin Jangid
d5cbe55d2b gh-14099: fix boosts code editor force theming issue (gh-14098) 2026-06-10 13:28:44 +02:00
Rishab Shah
7c885218f7 gh-14100: restore workspace routing for extension-opened tabs with explicit containers (gh-14102)
Co-authored-by: pokeshah <89949620+pokeshah@users.noreply.github.com>
2026-06-10 13:23:54 +02:00
24 changed files with 750 additions and 274 deletions

View File

@@ -60,7 +60,7 @@ jobs:
brew install watchman
cargo install apple-codesign --locked --force
cargo install apple-codesign
- name: Force usage of gnu-tar
run: |

View File

@@ -35,7 +35,7 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne
### Firefox Versions
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `151.0.4`! 🚀
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 151.0.4`!
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 152.0`!
### Contributing

View File

@@ -1 +1 @@
9a6aa4c359d1fb6ac60decc82402f82d49a17cea
a58ad2d2952face15859068dd4421cf68d6a9dda

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008ede72076 100644
index 43fb79a3060e20f671ae6ffc26350c7abf497702..028dfcba9e23a17e4152071dd58eb97a70e59c10 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -502,6 +502,7 @@
@@ -79,17 +79,15 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
set selectedTab(val) {
if (
gSharedTabWarning.willShowSharedTabWarning(val) ||
@@ -592,6 +644,9 @@
@@ -592,6 +644,7 @@
) {
return;
}
+ if (gZenWorkspaces.onBeforeTabSelect(val)) {
+ return;
+ }
+ gZenWorkspaces.onBeforeTabSelect(val);
// Update the tab
this.tabbox.selectedTab = val;
}
@@ -659,6 +714,10 @@
@@ -659,6 +712,10 @@
userContextId = parseInt(tabArgument.getAttribute("usercontextid"), 10);
}
@@ -100,7 +98,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (tabArgument && tabArgument.linkedBrowser) {
remoteType = tabArgument.linkedBrowser.remoteType;
initialBrowsingContextGroupId =
@@ -751,6 +810,8 @@
@@ -751,6 +808,8 @@
this.tabpanels.appendChild(panel);
let tab = this.tabs[0];
@@ -109,7 +107,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
tab.linkedPanel = uniqueId;
this._selectedTab = tab;
this._selectedBrowser = browser;
@@ -1121,13 +1182,18 @@
@@ -1121,13 +1180,18 @@
}
this.showTab(aTab);
@@ -129,7 +127,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
aTab.setAttribute("pinned", "true");
this._updateTabBarForPinnedTabs();
@@ -1140,11 +1206,19 @@
@@ -1140,11 +1204,19 @@
}
this.#handleTabMove(aTab, () => {
@@ -150,7 +148,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
});
aTab.style.marginInlineStart = "";
@@ -1321,6 +1395,9 @@
@@ -1321,6 +1393,9 @@
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
@@ -160,7 +158,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (
aIconURL &&
!LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))
@@ -1330,6 +1407,9 @@
@@ -1330,6 +1405,9 @@
);
return;
}
@@ -170,7 +168,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aIconURL;
@@ -1652,7 +1732,6 @@
@@ -1652,7 +1730,6 @@
// Preview mode should not reset the owner
if (!this._previewMode && !oldTab.selected) {
@@ -178,7 +176,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
@@ -1743,6 +1822,7 @@
@@ -1743,6 +1820,7 @@
if (!this._previewMode) {
newTab.recordTimeFromUnloadToReload();
newTab.updateLastAccessed();
@@ -186,7 +184,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
oldTab.updateLastAccessed();
// if this is the foreground window, update the last-seen timestamps.
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
@@ -1957,6 +2037,9 @@
@@ -1957,6 +2035,9 @@
}
let activeEl = document.activeElement;
@@ -196,7 +194,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// If focus is on the old tab, move it to the new tab.
if (activeEl == oldTab) {
newTab.focus();
@@ -1995,7 +2078,7 @@
@@ -1995,7 +2076,7 @@
// Focus the location bar if it was previously focused for that tab.
// In full screen mode, only bother making the location bar visible
// if the tab is a blank one.
@@ -205,7 +203,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let selectURL = () => {
if (this._asyncTabSwitching) {
// Set _awaitingSetURI flag to suppress popup notification
@@ -2283,7 +2366,12 @@
@@ -2283,7 +2364,12 @@
return this._setTabLabel(aTab, aLabel);
}
@@ -219,7 +217,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (!aLabel || (isURL && /^about:reader\?url=/.test(aLabel))) {
return false;
}
@@ -2408,7 +2496,7 @@
@@ -2408,7 +2494,7 @@
newIndex = this.selectedTab._tPos + 1;
}
@@ -228,7 +226,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (this.isTabGroupLabel(targetTab)) {
throw new Error(
"Replacing a tab group label with a tab is not supported"
@@ -2685,6 +2773,7 @@
@@ -2685,6 +2771,7 @@
uriIsAboutBlank,
userContextId,
skipLoad,
@@ -236,7 +234,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} = {}) {
let b = document.createXULElement("browser");
// Use the JSM global to create the permanentKey, so that if the
@@ -2758,8 +2847,7 @@
@@ -2758,8 +2845,7 @@
// we use a different attribute name for this?
b.setAttribute("name", name);
}
@@ -246,7 +244,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
b.setAttribute("transparent", "true");
}
@@ -2929,7 +3017,7 @@
@@ -2929,7 +3015,7 @@
let panel = this.getPanel(browser);
let uniqueId = this._generateUniquePanelID();
@@ -255,7 +253,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
aTab.linkedPanel = uniqueId;
// Inject the <browser> into the DOM if necessary.
@@ -2989,8 +3077,8 @@
@@ -2989,8 +3075,8 @@
// If we transitioned from one browser to two browsers, we need to set
// hasSiblings=false on both the existing browser and the new browser.
if (this.tabs.length == 2) {
@@ -266,7 +264,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
@@ -3175,7 +3263,6 @@
@@ -3175,7 +3261,6 @@
this.selectedTab = this.addTrustedTab(BROWSER_NEW_TAB_URL, {
tabIndex: tab._tPos + 1,
userContextId: tab.userContextId,
@@ -274,7 +272,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
focusUrlBar: true,
});
resolve(this.selectedBrowser);
@@ -3285,6 +3372,10 @@
@@ -3285,6 +3370,10 @@
schemelessInput,
hasValidUserGestureActivation = false,
textDirectiveUserActivation = false,
@@ -285,7 +283,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} = {}
) {
// all callers of addTab that pass a params object need to pass
@@ -3295,10 +3386,25 @@
@@ -3295,10 +3384,25 @@
);
}
@@ -296,7 +294,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
+
+ let hasZenDefaultUserContextId = false;
+ let zenForcedWorkspaceId = undefined;
+ if (beforeRouteResult.isRouteFound) {
+ if (beforeRouteResult.userContextId) {
+ userContextId = beforeRouteResult.userContextId;
+ hasZenDefaultUserContextId = true;
+ } else if (typeof gZenWorkspaces !== "undefined" && !_forZenEmptyTab) {
@@ -311,7 +309,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// If we're opening a foreground tab, set the owner by default.
ownerTab ??= inBackground ? null : this.selectedTab;
@@ -3306,6 +3412,7 @@
@@ -3306,6 +3410,7 @@
if (this.selectedTab.owner) {
this.selectedTab.owner = null;
}
@@ -319,7 +317,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// Find the tab that opened this one, if any. This is used for
// determining positioning, and inherited attributes such as the
@@ -3358,6 +3465,22 @@
@@ -3358,6 +3463,22 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -342,7 +340,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (insertTab) {
// Insert the tab into the tab container in the correct position.
this.#insertTabAtIndex(t, {
@@ -3366,6 +3489,7 @@
@@ -3366,6 +3487,7 @@
ownerTab,
openerTab,
pinned,
@@ -350,7 +348,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
bulkOrderedOpen,
tabGroup: tabGroup ?? openerTab?.group,
});
@@ -3384,6 +3508,7 @@
@@ -3384,6 +3506,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -358,7 +356,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}));
if (focusUrlBar) {
@@ -3508,6 +3633,12 @@
@@ -3508,6 +3631,12 @@
}
}
@@ -371,7 +369,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// Additionally send pinned tab events
if (pinned) {
this.#notifyPinnedStatus(t);
@@ -3518,6 +3649,9 @@
@@ -3518,6 +3647,9 @@
if (!inBackground) {
this.selectedTab = t;
}
@@ -381,7 +379,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
return t;
}
@@ -3750,6 +3884,7 @@
@@ -3750,6 +3882,7 @@
isAdoptingGroup = false,
isUserTriggered = false,
telemetryUserCreateSource = "unknown",
@@ -389,7 +387,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} = {}
) {
if (
@@ -3760,9 +3895,6 @@
@@ -3760,9 +3893,6 @@
!this.isSplitViewWrapper(tabOrSplitView)
)
) {
@@ -399,7 +397,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
if (!color) {
@@ -3783,9 +3915,14 @@
@@ -3783,9 +3913,14 @@
label,
isAdoptingGroup
);
@@ -416,7 +414,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
);
group.addTabs(tabsAndSplitViews);
@@ -3906,7 +4043,7 @@
@@ -3906,7 +4041,7 @@
}
this.#handleTabMove(tab, () =>
@@ -425,7 +423,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
);
}
@@ -3990,6 +4127,7 @@
@@ -3990,6 +4125,7 @@
color: group.color,
insertBefore: newTabs[0],
isAdoptingGroup: true,
@@ -433,7 +431,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
});
}
@@ -4200,6 +4338,7 @@
@@ -4200,6 +4336,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -441,7 +439,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
) {
// If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
@@ -4269,6 +4408,7 @@
@@ -4269,6 +4406,7 @@
openWindowInfo,
name,
skipLoad,
@@ -449,7 +447,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
});
}
@@ -4482,9 +4622,9 @@
@@ -4482,9 +4620,9 @@
}
// Add a new tab if needed.
@@ -461,7 +459,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let url = "about:blank";
if (tabData.entries?.length) {
@@ -4521,8 +4661,10 @@
@@ -4521,8 +4659,10 @@
insertTab: false,
skipLoad: true,
preferredRemoteType,
@@ -473,7 +471,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (select) {
tabToSelect = tab;
}
@@ -4544,7 +4686,8 @@
@@ -4544,7 +4684,8 @@
this.pinTab(tab);
// Then ensure all the tab open/pinning information is sent.
this._fireTabOpen(tab, {});
@@ -483,7 +481,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let { groupId } = tabData;
const tabGroup = tabGroupWorkingData.get(groupId);
// if a tab refers to a tab group we don't know, skip any group
@@ -4564,7 +4707,10 @@
@@ -4564,7 +4705,10 @@
tabGroup.stateData.id,
tabGroup.stateData.color,
tabGroup.stateData.collapsed,
@@ -495,7 +493,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
);
tabsFragment.appendChild(tabGroup.node);
}
@@ -4619,9 +4765,21 @@
@@ -4619,9 +4763,21 @@
// to remove the old selected tab.
if (tabToSelect) {
let leftoverTab = this.selectedTab;
@@ -517,7 +515,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (tabs.length > 1 || !tabs[0].selected) {
this._updateTabsAfterInsert();
@@ -4812,11 +4970,14 @@
@@ -4812,11 +4968,14 @@
if (ownerTab) {
tab.owner = ownerTab;
}
@@ -533,7 +531,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -4828,7 +4989,7 @@
@@ -4828,7 +4987,7 @@
let lastRelatedTab =
openerTab && this._lastRelatedTabMap.get(openerTab);
let previousTab = lastRelatedTab || openerTab || this.selectedTab;
@@ -542,7 +540,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
tabGroup = previousTab.group;
}
if (
@@ -4844,7 +5005,7 @@
@@ -4844,7 +5003,7 @@
previousTab.splitview
) + 1;
} else if (previousTab.visible) {
@@ -551,7 +549,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} else if (previousTab == FirefoxViewHandler.tab) {
elementIndex = 0;
}
@@ -4872,14 +5033,14 @@
@@ -4872,14 +5031,14 @@
}
// Ensure index is within bounds.
if (tab.pinned) {
@@ -570,7 +568,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (pinned && !itemAfter?.pinned) {
itemAfter = null;
@@ -4896,7 +5057,7 @@
@@ -4896,7 +5055,7 @@
this.tabContainer._invalidateCachedTabs();
@@ -579,7 +577,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (
(this.isTab(itemAfter) && itemAfter.group == tabGroup) ||
this.isSplitViewWrapper(itemAfter)
@@ -4927,7 +5088,11 @@
@@ -4927,7 +5086,11 @@
const tabContainer = pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -591,7 +589,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
if (tab.group?.collapsed) {
@@ -4942,6 +5107,7 @@
@@ -4942,6 +5105,7 @@
if (pinned) {
this._updateTabBarForPinnedTabs();
}
@@ -599,7 +597,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
TabBarVisibility.update();
}
@@ -5490,6 +5656,7 @@
@@ -5490,6 +5654,7 @@
telemetrySource,
} = {}
) {
@@ -607,7 +605,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
// can be considered equivalent to closing the window.
if (
@@ -5579,6 +5746,7 @@
@@ -5579,6 +5744,7 @@
if (lastToClose) {
this.removeTab(lastToClose, aParams);
}
@@ -615,7 +613,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} catch (e) {
console.error(e);
}
@@ -5624,6 +5792,14 @@
@@ -5624,6 +5790,14 @@
return;
}
@@ -630,7 +628,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let isVisibleTab = aTab.visible;
// We have to sample the tab width now, since _beginRemoveTab might
// end up modifying the DOM in such a way that aTab gets a new
@@ -5631,6 +5807,9 @@
@@ -5631,6 +5805,9 @@
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
let isLastTab = this.#isLastTabInWindow(aTab);
@@ -640,7 +638,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -5642,13 +5821,14 @@
@@ -5642,13 +5819,14 @@
telemetrySource,
})
) {
@@ -656,7 +654,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let lockTabSizing =
!this.tabContainer.verticalMode &&
!aTab.pinned &&
@@ -5679,7 +5859,13 @@
@@ -5679,7 +5857,13 @@
// We're not animating, so we can cancel the animation stopwatch.
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
aTab._closeTimeAnimTimerId = null;
@@ -671,7 +669,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
return;
}
@@ -5813,7 +5999,7 @@
@@ -5813,7 +5997,7 @@
closeWindowWithLastTab != null
? closeWindowWithLastTab
: !window.toolbar.visible ||
@@ -680,7 +678,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
@@ -5837,6 +6023,7 @@
@@ -5837,6 +6021,7 @@
newTab = true;
}
@@ -688,7 +686,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -5877,13 +6064,7 @@
@@ -5877,13 +6062,7 @@
aTab._mouseleave();
if (newTab) {
@@ -703,7 +701,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} else {
TabBarVisibility.update();
}
@@ -6016,6 +6197,7 @@
@@ -6016,6 +6195,7 @@
this.tabs[i]._tPos = i;
}
@@ -711,7 +709,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (!this._windowIsClosing) {
// update tab close buttons state
this.tabContainer._updateCloseButtons();
@@ -6201,6 +6383,7 @@
@@ -6201,6 +6381,7 @@
memory_after: await getTotalMemoryUsage(),
time_to_unload_in_ms: timeElapsed,
});
@@ -719,7 +717,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
/**
@@ -6246,6 +6429,7 @@
@@ -6246,6 +6427,7 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -727,7 +725,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// If this tab has a successor, it should be selectable, since
// hiding or closing a tab removes that tab as a successor.
@@ -6258,15 +6442,22 @@
@@ -6258,15 +6440,22 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -752,7 +750,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let tab = this.tabContainer.findNextTab(aTab, {
direction: 1,
filter: _tab => remainingTabs.includes(_tab),
@@ -6280,7 +6471,7 @@
@@ -6280,7 +6469,7 @@
}
if (tab) {
@@ -761,7 +759,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -6301,7 +6492,7 @@
@@ -6301,7 +6490,7 @@
});
}
@@ -770,7 +768,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
_blurTab(aTab) {
@@ -6312,7 +6503,7 @@
@@ -6312,7 +6501,7 @@
* @returns {boolean}
* False if swapping isn't permitted, true otherwise.
*/
@@ -779,7 +777,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// Do not allow transfering a private tab to a non-private window
// and vice versa.
if (
@@ -6366,6 +6557,7 @@
@@ -6366,6 +6555,7 @@
// fire the beforeunload event in the process. Close the other
// window if this was its last tab.
if (
@@ -787,7 +785,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
!remoteBrowser._beginRemoveTab(aOtherTab, {
adoptedByTab: aOurTab,
closeWindowWithLastTab: true,
@@ -6377,7 +6569,7 @@
@@ -6377,7 +6567,7 @@
// If this is the last tab of the window, hide the window
// immediately without animation before the docshell swap, to avoid
// about:blank being painted.
@@ -796,7 +794,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (closeWindow) {
let win = aOtherTab.ownerGlobal;
win.windowUtils.suppressAnimation(true);
@@ -6511,11 +6703,13 @@
@@ -6511,11 +6701,13 @@
}
// Finish tearing down the tab that's going away.
@@ -810,7 +808,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
this.setTabTitle(aOurTab);
@@ -6717,10 +6911,10 @@
@@ -6717,10 +6909,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -823,7 +821,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -6780,7 +6974,8 @@
@@ -6780,7 +6972,8 @@
* @param {object} [aOptions={}]
* Key-value pairs that will be serialized into the features string.
*/
@@ -833,7 +831,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (this.tabs.length == 1) {
return null;
}
@@ -6797,7 +6992,7 @@
@@ -6797,7 +6990,7 @@
// tell a new window to take the "dropped" tab
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
args.appendElement(aTab.splitview ?? aTab);
@@ -842,7 +840,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
private: PrivateBrowsingUtils.isWindowPrivate(window),
features: Object.entries(aOptions)
.map(([key, value]) => `${key}=${value}`)
@@ -6805,6 +7000,8 @@
@@ -6805,6 +6998,8 @@
openerWindow: window,
args,
});
@@ -851,7 +849,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
/**
@@ -6917,7 +7114,7 @@
@@ -6917,7 +7112,7 @@
* `true` if element is a `<tab-group>`
*/
isTabGroup(element) {
@@ -860,7 +858,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
/**
@@ -7002,8 +7199,8 @@
@@ -7002,8 +7197,8 @@
}
// Don't allow mixing pinned and unpinned tabs.
@@ -871,7 +869,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -7049,8 +7246,8 @@
@@ -7049,8 +7244,8 @@
this.#handleTabMove(
element,
() => {
@@ -882,7 +880,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
neighbor = neighbor.group;
}
if (neighbor?.splitview) {
@@ -7061,6 +7258,12 @@
@@ -7061,6 +7256,12 @@
return;
}
}
@@ -895,7 +893,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
if (movingForwards && neighbor) {
neighbor.after(element);
@@ -7119,23 +7322,31 @@
@@ -7119,23 +7320,31 @@
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
if (this.isTabGroupLabel(targetElement)) {
targetElement = targetElement.group;
@@ -933,7 +931,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
} else if (!element.pinned && targetElement && targetElement.pinned) {
// If the caller asks to move an unpinned element next to a pinned
// tab, move the unpinned element to be the first unpinned element
@@ -7148,12 +7359,35 @@
@@ -7148,12 +7357,35 @@
// move the tab group right before the first unpinned tab.
// 4. Moving a tab group and the first unpinned tab is grouped:
// move the tab group right before the first unpinned tab's tab group.
@@ -970,7 +968,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// We want to include the splitview wrapper if it's the targetElement, but
// not in the case where we want to reverse tabs within the same splitview.
@@ -7162,6 +7396,7 @@
@@ -7162,6 +7394,7 @@
}
let getContainer = () =>
@@ -978,7 +976,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
element.pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -7170,11 +7405,15 @@
@@ -7170,11 +7403,15 @@
element,
() => {
if (moveBefore) {
@@ -995,7 +993,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
},
metricsContext
@@ -7248,11 +7487,15 @@
@@ -7248,11 +7485,15 @@
* @param {TabMetricsContext} [metricsContext]
*/
moveTabToExistingGroup(aTab, aGroup, metricsContext) {
@@ -1014,7 +1012,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
if (aTab.group && aTab.group.id === aGroup.id) {
return;
@@ -7324,6 +7567,7 @@
@@ -7324,6 +7565,7 @@
let state = {
tabIndex: tab._tPos,
@@ -1022,7 +1020,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
};
if (tab.visible) {
state.elementIndex = tab.elementIndex;
@@ -7355,7 +7599,7 @@
@@ -7355,7 +7597,7 @@
let changedSplitView =
previousTabState.splitViewId != currentTabState.splitViewId;
@@ -1031,7 +1029,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
tab.dispatchEvent(
new CustomEvent("TabMove", {
bubbles: true,
@@ -7402,6 +7646,10 @@
@@ -7402,6 +7644,10 @@
moveActionCallback();
@@ -1042,7 +1040,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -7452,7 +7700,22 @@
@@ -7452,7 +7698,22 @@
* @returns {object}
* The new tab in the current window, null if the tab couldn't be adopted.
*/
@@ -1066,7 +1064,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
// Swap the dropped tab with a new one we create and then close
// it in the other window (making it seem to have moved between
// windows). We also ensure that the tab we create to swap into has
@@ -7495,6 +7758,8 @@
@@ -7495,6 +7756,8 @@
}
params.skipLoad = true;
let newTab = this.addWebTab("about:blank", params);
@@ -1075,7 +1073,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
aTab.container.tabDragAndDrop.finishAnimateTabMove();
@@ -8205,7 +8470,7 @@
@@ -8205,7 +8468,7 @@
// preventDefault(). It will still raise the window if appropriate.
return;
}
@@ -1084,7 +1082,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
window.focus();
aEvent.preventDefault();
}
@@ -8222,7 +8487,6 @@
@@ -8222,7 +8485,6 @@
on_TabGroupCollapse(aEvent) {
aEvent.target.tabs.forEach(tab => {
@@ -1092,7 +1090,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
});
}
@@ -8556,7 +8820,9 @@
@@ -8556,7 +8818,9 @@
let filter = this._tabFilters.get(tab);
if (filter) {
@@ -1102,7 +1100,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
let listener = this._tabListeners.get(tab);
if (listener) {
@@ -9359,6 +9625,7 @@
@@ -9359,6 +9623,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -1110,7 +1108,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -9439,6 +9706,7 @@
@@ -9439,6 +9704,7 @@
// known defaults. Note we use the original URL since about:newtab
// redirects to a prerendered page.
const shouldRemoveFavicon =
@@ -1118,7 +1116,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
!this.mBrowser.mIconURL &&
!ignoreBlank &&
!(originalLocation.spec in FAVICON_DEFAULTS);
@@ -9613,13 +9881,6 @@
@@ -9613,13 +9879,6 @@
this.mBrowser.originalURI = aRequest.originalURI;
}
@@ -1132,7 +1130,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..68a037d5a0e3416f31ffcb163592f008
}
let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
@@ -10507,7 +10768,8 @@ var TabContextMenu = {
@@ -10507,7 +10766,8 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;

View File

@@ -0,0 +1,172 @@
diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -298,11 +298,13 @@
panic_on_gl_error, picTileWidth, picTileHeight,
gfx::gfxVars::WebRenderRequiresHardwareDriver(),
StaticPrefs::gfx_webrender_low_quality_pinch_zoom_AtStartup(),
StaticPrefs::gfx_webrender_max_shared_surface_size_AtStartup(),
StaticPrefs::gfx_webrender_enable_subpixel_aa_AtStartup(),
- compositor->ShouldUseLayerCompositor())) {
+ compositor->ShouldUseLayerCompositor(),
+ StaticPrefs::
+ gfx_webrender_opaque_backdrop_fallback_AtStartup())) {
// wr_window_new puts a message into gfxCriticalNote if it returns
// false
MOZ_ASSERT(errorMessage);
error.AssignASCII(errorMessage);
wr_api_free_error_msg(errorMessage);
diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1998,10 +1998,11 @@
reject_software_rasterizer: bool,
low_quality_pinch_zoom: bool,
max_shared_surface_size: i32,
enable_subpixel_aa: bool,
use_layer_compositor: bool,
+ opaque_backdrop_fallback: bool,
) -> bool {
assert!(unsafe { is_in_render_thread() });
// Ensure the WR profiler callbacks are hooked up to the Gecko profiler.
set_profiler_hooks(Some(&PROFILER_HOOKS));
@@ -2164,10 +2165,11 @@
texture_cache_config,
reject_software_rasterizer,
low_quality_pinch_zoom,
max_shared_surface_size,
enable_dithering,
+ opaque_backdrop_fallback,
..Default::default()
};
let window_size = DeviceIntSize::new(window_width, window_height);
let notifier = Box::new(CppNotifier { window_id });
diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -3982,10 +3982,14 @@
pub fn disable_color_write(&self) {
self.gl.color_mask(false, false, false, false);
}
+ pub fn set_color_mask(&self, r: bool, g: bool, b: bool, a: bool) {
+ self.gl.color_mask(r, g, b, a);
+ }
+
pub fn set_blend(&mut self, enable: bool) {
if enable {
self.gl.enable(gl::BLEND);
} else {
self.gl.disable(gl::BLEND);
diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/renderer/init.rs
--- a/gfx/wr/webrender/src/renderer/init.rs
+++ b/gfx/wr/webrender/src/renderer/init.rs
@@ -204,10 +204,12 @@
pub low_quality_pinch_zoom: bool,
pub max_shared_surface_size: i32,
/// If true, open a debug socket to listen for remote debugger.
/// Relies on `debugger` cargo feature being enabled.
pub enable_debugger: bool,
+ /// See explanation of `gfx.webrender.opaque-backdrop-fallback`.
+ pub opaque_backdrop_fallback: bool,
/// Use the new quad primitive path for box-shadow blur rendering.
pub use_quad_box_shadow: bool,
}
@@ -277,10 +279,11 @@
enable_instancing: true,
reject_software_rasterizer: false,
low_quality_pinch_zoom: false,
max_shared_surface_size: 2048,
enable_debugger: true,
+ opaque_backdrop_fallback: false,
use_quad_box_shadow: true,
}
}
}
@@ -802,10 +805,11 @@
allocated_native_surfaces: FastHashSet::default(),
debug_overlay_state: DebugOverlayState::new(),
buffer_damage_tracker: BufferDamageTracker::default(),
max_primitive_instance_count,
enable_instancing: options.enable_instancing,
+ opaque_backdrop_fallback: options.opaque_backdrop_fallback,
consecutive_oom_frames: 0,
target_frame_publish_id: None,
pending_result_msg: None,
layer_compositor_frame_state_in_prev_frame: None,
external_composite_debug_items: Vec::new(),
diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs
--- a/gfx/wr/webrender/src/renderer/mod.rs
+++ b/gfx/wr/webrender/src/renderer/mod.rs
@@ -867,10 +867,12 @@
buffer_damage_tracker: BufferDamageTracker,
max_primitive_instance_count: usize,
enable_instancing: bool,
+ opaque_backdrop_fallback: bool,
+
/// Count consecutive oom frames to detectif we are stuck unable to render
/// in a loop.
consecutive_oom_frames: u32,
/// update() defers processing of ResultMsg, if frame_publish_id of
@@ -2787,18 +2789,29 @@
let read_target = ReadTarget::from_texture(cache_texture);
// Should always be drawing to picture cache tiles or off-screen surface!
debug_assert!(!draw_target.is_default());
let device_to_framebuffer = Scale::new(1i32);
+ let dest_fb_rect = dest * device_to_framebuffer;
self.device.blit_render_target(
read_target,
src * device_to_framebuffer,
draw_target,
- dest * device_to_framebuffer,
+ dest_fb_rect,
TextureFilter::Linear,
);
+
+ if self.opaque_backdrop_fallback {
+ self.device.set_color_mask(false, false, false, true);
+ self.device.clear_target(
+ Some([0.0, 0.0, 0.0, 1.0]),
+ None,
+ Some(dest_fb_rect),
+ );
+ self.device.set_color_mask(true, true, true, true);
+ }
}
}
}
fn draw_picture_cache_target(
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
@@ -8439,10 +8439,17 @@
#else
value: false
#endif
mirror: once
+# Make backdrop-filter treat its captured backdrop as if it had been
+# composited over an opaque-black background. (See bug 2036640)
+- name: gfx.webrender.opaque-backdrop-fallback
+ type: bool
+ value: true
+ mirror: once
+
# Disable wait of GPU execution completion
- name: gfx.webrender.wait-gpu-finished.disabled
type: bool
value: false
mirror: once

View File

@@ -7,7 +7,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content
id="selection-shortcut-action-panel"
noautofocus="true"
consumeoutsideclicks="never"
+ nonnativepopover="true"
+ nonnative=""
type="arrow">
<hbox class="panel-subview-body">
<html:moz-button id="ai-action-button"/>
@@ -19,7 +19,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content
<panel class="panel-no-padding"
id="chat-shortcuts-options-panel"
noautofocus="true"
+ nonnativepopover="true"
+ nonnative=""
type="arrow">
<vbox class="panel-subview-body"/>
</panel>
@@ -31,7 +31,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content
noautofocus="true"
norolluponanchor="true"
consumeoutsideclicks="false"
+ nonnativepopover="true"
+ nonnative=""
role="tooltip">
<html:div class="tab-preview-content-interactive"></html:div>
<html:div class="tab-preview-content-main">
@@ -46,7 +46,7 @@ diff --git a/browser/components/asrouter/modules/FeatureCallout.sys.mjs b/browse
type="arrow"
consumeoutsideclicks="never"
norolluponanchor="true"
+
+ nonnative=""
position="${panel_position.panel_position_string}"
${hide_arrow ? "" : 'show-arrow=""'}
${autohide ? "" : 'noautohide="true"'}
@@ -61,7 +61,7 @@ diff --git a/browser/components/customizableui/content/panelUI.inc.xhtml b/brows
hidden="true"
flip="slide"
position="bottomright topright"
+ hidepopovertail="true"
+ hidepopovertail=""
noautofocus="true">
<panelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView"
viewCacheId="appMenu-viewCache">
@@ -70,31 +70,33 @@ 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
@@ -80,10 +80,14 @@
@@ -80,10 +80,15 @@
}
void XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
bool aIsContextMenu,
Event* aTriggerEvent) {
+ // 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);
+ if (NodeInfo()->NameAtom() == nsGkAtoms::panel) {
+ // TODO(bug 2038354): Remove this and make the front-end set the attribute
+ // explicitly.
+ SetAttr(kNameSpaceID_None, nsGkAtoms::nonnative, u"true"_ns, true);
+ }
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm) {
pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
}
}
@@ -93,10 +97,14 @@
@@ -93,10 +98,15 @@
int32_t aWidth, int32_t aHeight,
bool aIsContextMenu,
bool aAttributesOverride,
Event* aTriggerEvent) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ // TODO: See OpenPopupAtScreen. We should remove this and use the other
+ // implementation because this is a bit of a hacky way to determine whether to
+ // use a native popover or not.
+ SetAttr(kNameSpaceID_None, nsGkAtoms::nonnativepopover, u"true"_ns, true);
+ if (NodeInfo()->NameAtom() == nsGkAtoms::panel) {
+ // TODO(bug 2038354): Remove this and make the front-end set the attribute
+ // explicitly.
+ SetAttr(kNameSpaceID_None, nsGkAtoms::nonnative, u"true"_ns, true);
+ }
if (pm) {
pm->ShowPopupAtScreenRect(
this, aPosition, nsIntRect(aXPos, aYPos, aWidth, aHeight),
@@ -103,7 +105,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
@@ -528,18 +528,10 @@
@@ -516,18 +516,10 @@
// Move the popup to the position specified in its |left| and |top|
// attributes.
@@ -122,7 +124,7 @@ diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h
public:
/**
* Return whether the popup direction should be RTL.
@@ -548,10 +540,18 @@
@@ -536,10 +528,18 @@
*
* Return whether the popup direction should be RTL.
*/
@@ -144,7 +146,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
@@ -19672,10 +19672,19 @@
@@ -19840,10 +19840,19 @@
value: true
mirror: always
@@ -168,20 +170,20 @@ diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/glo
--- a/toolkit/themes/shared/global-shared.css
+++ b/toolkit/themes/shared/global-shared.css
@@ -72,10 +72,22 @@
--menuitem-border-radius: var(--panel-menuitem-border-radius);
--menuitem-padding: var(--panel-menuitem-padding);
--menuitem-margin: var(--panel-menuitem-margin);
--menuitem-border-radius: var(--arrowpanel-menuitem-border-radius);
--menuitem-padding: var(--arrowpanel-menuitem-padding);
--menuitem-margin: var(--arrowpanel-menuitem-margin);
}
+/* stylelint-disable-next-line media-query-no-invalid */
+@media -moz-pref("widget.macos.native-popovers") and (-moz-platform: macos) {
+ panel:not(:where([nonnativepopover="true"])) {
+ panel:not([nonnative]) {
+ background-color: transparent;
+ --panel-background: transparent;
+ --panel-shadow: none;
+ --panel-box-shadow: none;
+ --panel-border-color: transparent;
+ --panel-shadow-margin: 0px;
+
+ --panel-box-shadow-margin: 0px;
+ --panel-padding: 0px;
+ }
+}
+
@@ -241,7 +243,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h
+ // Check if this window should use NSPopover for popup/menu display
+ bool ShouldUseNSPopover() const;
+ bool ShouldShowAsNSPopover() const override;
+ bool ShouldShowAsNSPopover() const;
+
[[nodiscard]] nsresult Create(nsIWidget* aParent, const DesktopIntRect& aRect,
const InitData&) override;
@@ -265,7 +267,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
#include "nsIDOMWindowUtils.h"
#include "nsILocalFileMac.h"
#include "CocoaCompositorWidget.h"
@@ -5031,10 +5034,15 @@
@@ -5088,10 +5091,15 @@
if (mWindowType == WindowType::Popup) {
SetPopupWindowLevel();
mWindow.backgroundColor = NSColor.clearColor;
@@ -281,7 +283,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 |
@@ -5236,10 +5244,57 @@
@@ -5293,10 +5301,57 @@
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@@ -339,7 +341,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (!mWindow) {
@@ -5300,10 +5355,58 @@
@@ -5357,10 +5412,53 @@
mWindow.contentView.needsDisplay = YES;
if (!nativeParentWindow || mPopupLevel != PopupLevel::Parent) {
[mWindow orderFront:nil];
@@ -375,22 +377,17 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
+ // specific parent view
+ NSRect positioningRect = [parentView convertRect:windowRect
+ fromView:nil];
+ BOOL shouldHideAnchor = NO;
+ auto& element = popupFrame->PopupElement();
+ if (element.GetBoolAttr(nsGkAtoms::hidepopovertail)) {
+ shouldHideAnchor = YES;
+ }
+ bool shouldHideAnchor =
+ popupFrame->PopupElement().GetBoolAttr(nsGkAtoms::hidepopovertail);
+ [(PopupWindow*)mWindow showPopoverRelativeToRect:positioningRect
+ ofView:parentView
+ preferredEdge:preferredEdge
+ hiddenAnchor:shouldHideAnchor];
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-method-access"
+ SyncPopoverBounds([(PopupWindow*)mWindow popover], popupFrame);
+
+
+
+
+#pragma clang diagnostic pop
+ // Exit early here since the popover is now shown.
+
+ return;
+ }
// If our popup window is a non-native context menu, tell the OS (and
@@ -398,7 +395,7 @@ 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]) {
@@ -5373,11 +5476,15 @@
@@ -5430,11 +5528,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) {
@@ -415,7 +412,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]) {
@@ -5424,10 +5531,28 @@
@@ -5481,10 +5583,28 @@
return false;
}
return nsIWidget::ShouldUseOffMainThreadCompositing();
@@ -424,8 +421,8 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
+bool nsCocoaWindow::ShouldUseNSPopover() const {
+ // Use NSPopover for panel popups when the preference is enabled
+ // But not for detached popups - they should use traditional window logic
+ return (mWindowType == WindowType::Popup && mPopupType == PopupType::Panel &&
+ mozilla::StaticPrefs::widget_macos_native_popovers());
+ return mWindowType == WindowType::Popup && mPopupType == PopupType::Panel &&
+ mozilla::StaticPrefs::widget_macos_native_popovers();
+}
+
+bool nsCocoaWindow::ShouldShowAsNSPopover() const {
@@ -444,7 +441,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
return mWindow.isOpaque ? TransparencyMode::Opaque
: TransparencyMode::Transparent;
@@ -6378,10 +6503,19 @@
@@ -6442,10 +6562,22 @@
// We ignore aRepaint -- we have to call display:YES, otherwise the
// title bar doesn't immediately get repainted and is displayed in
@@ -454,17 +451,20 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
+ [(PopupWindow*)mWindow updatePopoverContent];
+ // A popover won't resize by setting the frame
+ // as it's size is calculated based on the content size
+ // Therefor the content size has to be changed as well
+ // Therefore the content size has to be changed as well
+ NSSize contentSize = NSMakeSize(aWidth, aHeight);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-method-access"
+ [[(PopupWindow*)mWindow popover] setContentSize:contentSize];
+ SyncPopoverBounds([(PopupWindow*)mWindow popover], GetPopupFrame());
+#pragma clang diagnostic pop
+ }
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
void nsCocoaWindow::Resize(const DesktopRect& aRect, bool aRepaint) {
@@ -8393,18 +8527,31 @@
@@ -8517,18 +8649,31 @@
backing:bufferingType
defer:deferCreation];
if (!self) {
@@ -497,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 {
@@ -8460,10 +8607,122 @@
@@ -8584,10 +8729,125 @@
- (void)setIsContextMenu:(BOOL)flag {
mIsContextMenu = flag;
@@ -537,7 +537,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
+}
+
+- (BOOL)usePopover {
+ return mUsePopover && !mIsContextMenu;
+ return mUsePopover;
+}
+
+- (void)showPopoverRelativeToRect:(NSRect)positioningRect
@@ -562,7 +562,10 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
+
+ // This is a hidden API that prevents the popover from showing its arrow
+ // pointing to the anchor.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-method-access"
+ [mPopover setShouldHideAnchor:hiddenAnchor];
+#pragma clang diagnostic pop
+
+ [mPopover showRelativeToRect:positioningRect
+ ofView:positioningView
@@ -620,25 +623,6 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
return NO;
}
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -829,10 +829,15 @@
virtual void SuppressAnimation(bool aSuppress) {}
/** Sets windows-specific mica backdrop on this widget. */
virtual void SetMicaBackdrop(bool) {}
+ /**
+ * Determine whether this widget should be shown as an NSPopover.
+ */
+ virtual bool ShouldShowAsNSPopover() const { return false; }
+
/**
* Return size mode (minimized, maximized, normalized).
* Returns a value from nsSizeMode (see nsIWidgetListener.h)
*/
virtual nsSizeMode SizeMode() = 0;
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -654,16 +638,4 @@ diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
Atom("highest", "highest"),
Atom("horizontal", "horizontal"),
Atom("hover", "hover"),
@@ -759,10 +760,11 @@
Atom("nohref", "nohref"),
Atom("noinitialselection", "noinitialselection"),
Atom("nomodule", "nomodule"),
Atom("nonce", "nonce"),
Atom("none", "none"),
+ Atom("nonnativepopover", "nonnativepopover"),
Atom("noresize", "noresize"),
Atom("normal", "normal"),
Atom("normalizeSpace", "normalize-space"),
Atom("noscript", "noscript"),
Atom("noshade", "noshade"),

View File

@@ -1,3 +1,18 @@
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
@@ -80,11 +80,10 @@
background-color: transparent;
--panel-background: transparent;
--panel-box-shadow: none;
--panel-border-color: transparent;
--panel-box-shadow-margin: 0px;
- --panel-padding: 0px;
}
}
/* Lightweight theme roots */
diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h

View File

@@ -630,7 +630,6 @@ ${cssSelector} {
this.currentBoostData.textCaseOverride = "uppercase";
}
this.currentBoostData.changeWasMade = true;
this.updateCaseButtonVisuals();
this.updateCurrentBoost();
}
@@ -652,7 +651,6 @@ ${cssSelector} {
await this.zenBoostsChild.sendQuery("ZenBoost:DisableSizeOverride");
}
this.currentBoostData.changeWasMade = true;
this.updateSizeButtonVisuals();
this.updateCurrentBoost();
}

View File

@@ -100,16 +100,7 @@ class ZenStartup {
delete this.promiseInitializedResolve;
setTimeout(() => {
// Wait for the natural PlacesToolbar rebuild before invalidating, so
// the two async rebuilds don't interleave and duplicate bookmarks.
// promiseRebuilt() returns undefined when no rebuild is in flight.
const rebuilt =
document
.getElementById("PlacesToolbar")
?._placesView?.promiseRebuilt() ?? Promise.resolve();
rebuilt
.catch(console.error)
.then(() => gZenWorkspaces._invalidateBookmarkContainers());
gZenWorkspaces._invalidateBookmarkContainers();
});
});
}

View File

@@ -2290,11 +2290,6 @@ class nsZenWorkspaces {
}
onBeforeTabSelect(aTab) {
if (this.#inChangingWorkspace) {
// Just in case, Let's not do these checks while we are
// in the middle of changing workspace,
return false;
}
const tabSpace = aTab?.getAttribute("zen-workspace-id");
if (
tabSpace &&
@@ -2302,12 +2297,8 @@ class nsZenWorkspaces {
!aTab.hasAttribute("zen-empty-tab") &&
!aTab.hasAttribute("zen-essential")
) {
this.lastSelectedWorkspaceTabs[tabSpace] =
gZenGlanceManager.getTabOrGlanceParent(aTab);
this.changeWorkspaceWithID(tabSpace);
return true;
}
return false;
}
_shouldShowTab(tab, workspaceUuid, containerId, workspaces) {

View File

@@ -40,7 +40,7 @@ add_task(async function testNormalBrowsing() {
browser: tab.linkedBrowser,
uriString: TEST_PAGE,
});
testWhitelistedPage(tab.ownerGlobal);
testWhitelistedPage(tab.documentGlobal);
info("Load a test page that's no longer whitelisted");
Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "");
@@ -54,5 +54,5 @@ add_task(async function testNormalBrowsing() {
);
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, TEST_PAGE);
await blockedLoaded;
testBlockedPage(tab.ownerGlobal);
testBlockedPage(tab.documentGlobal);
});

View File

@@ -2,7 +2,10 @@
["browser_1119088.js"]
disabled="Disabled by import_external_tests.py"
support-files = ["mac_desktop_image.py"]
support-files = [
"large.png",
"mac_desktop_image.py"
]
run-if = [
"os == 'mac'",
]
@@ -12,6 +15,7 @@ skip-if = [
]
["browser_420786.js"]
support-files = ["large.png"]
run-if = [
"os == 'linux' && os_version == '22.04' && arch == 'x86_64' && display == 'wayland'",
"os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11'",
@@ -116,4 +120,5 @@ tags = "os_integration"
["browser_setDesktopBackgroundPreview.js"]
disabled="Disabled by import_external_tests.py"
support-files = ["large.png"]
tags = "os_integration"

View File

@@ -92,20 +92,19 @@ add_setup(async function () {
/**
* Tests "Set As Desktop Background" platform implementation on macOS.
*
* Sets the desktop background image to the browser logo from the about:logo
* page and verifies it was set successfully. Setting the desktop background
* (which uses the nsIShellService::setDesktopBackground() interface method)
* downloads the image to ~/Pictures using a unique file name and sets the
* desktop background to the downloaded file leaving the download in place.
* After setDesktopBackground() is called, the test uses a python script to
* validate that the current desktop background is in fact set to the
* downloaded logo.
* Sets the desktop background image to the large.png image and verifies it was
* set successfully. Setting the desktop background (which uses the
* nsIShellService::setDesktopBackground() interface method) downloads the
* image to ~/Pictures using a unique file name and sets the desktop background
* to the downloaded file leaving the download in place. After
* setDesktopBackground() is called, the test uses a python script to validate
* that the current desktop background is in fact set to the downloaded image.
*/
add_task(async function () {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:logo",
url: getRootDirectory(gTestPath) + "large.png",
},
async () => {
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(
@@ -140,7 +139,7 @@ add_task(async function () {
// For simplicity, we're going to reach in and access the image on the
// page directly, which means the page shouldn't be running in a remote
// browser. Thankfully, about:logo runs in the parent process for now.
// browser. Thankfully, chrome:// runs in the parent process for now.
Assert.ok(
!gBrowser.selectedBrowser.isRemoteBrowser,
"image can be accessed synchronously from the parent process"

View File

@@ -12,7 +12,7 @@ add_task(async function () {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:logo",
url: getRootDirectory(gTestPath) + "large.png",
},
() => {
var brandName = Services.strings
@@ -46,7 +46,7 @@ add_task(async function () {
// For simplicity, we're going to reach in and access the image on the
// page directly, which means the page shouldn't be running in a remote
// browser. Thankfully, about:logo runs in the parent process for now.
// browser. Thankfully, chrome:// runs in the parent process for now.
Assert.ok(
!gBrowser.selectedBrowser.isRemoteBrowser,
"image can be accessed synchronously from the parent process"

View File

@@ -323,14 +323,39 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
}
});
// Wait for the deferred set_default_pdf_handler_attempt event to be recorded,
// then return the single event that was emitted by the most recent call.
async function awaitAttemptEvent() {
await TestUtils.waitForCondition(() => {
const events = Glean.browser.setDefaultPdfHandlerAttempt.testGetValue();
return events && events.length;
}, "Recorded set_default_pdf_handler_attempt event");
const events = Glean.browser.setDefaultPdfHandlerAttempt.testGetValue();
Assert.equal(events.length, 1, "Recorded exactly one attempt event");
return events[0];
}
add_task(async function test_setAsDefaultPDFHandler_fallback() {
const sandbox = sinon.createSandbox();
// Enable the IOpenWithLauncher branch explicitly so the test does not
// depend on the build-channel default of
// browser.shell.setDefaultPDFHandler.useOpenWith, and use a 0ms wait so
// the deferred attempt event fires promptly.
await SpecialPowers.pushPrefEnv({
set: [
["browser.shell.setDefaultPDFHandler.useOpenWith", true],
["browser.shell.setDefaultPDFHandler.attemptWaitTimeMs", 0],
],
});
try {
const userChoiceStub = sandbox
.stub(ShellService, "setAsDefaultPDFHandlerUserChoice")
.rejects(new Error("mock userChoice failure"));
sandbox.stub(ShellService, "_isWindows11").returns(true);
const isDefaultHandlerForStub = sandbox
.stub(ShellService, "isDefaultHandlerFor")
.returns(true);
info(
"When userChoice fails and open-with picker succeeds, should not fall back to settings dialog"
@@ -352,27 +377,42 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
1,
"Recorded user-choice failure"
);
let event = await awaitAttemptEvent();
Assert.equal(event.extra.method, "open_with", "Event method is open_with");
Assert.equal(event.extra.success, "true", "Event success is true");
Assert.equal(
Glean.browser.setDefaultPdfHandlerUserChoiceResult.Success.testGetValue(),
undefined,
"Did not record user-choice success"
event.extra.result_is_default,
"true",
"Event result_is_default reflects isDefaultHandlerFor"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerOpenWithResult.Success.testGetValue(),
1,
"Recorded open-with success"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerOpenWithResult.Failure.testGetValue(),
undefined,
"Did not record open-with failure"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerModernSettingsResult.Success.testGetValue(),
undefined,
"Did not record modern settings result"
Assert.ok(
isDefaultHandlerForStub.calledWith(".pdf"),
"Sampled isDefaultHandlerFor after the delay"
);
userChoiceStub.resetHistory();
isDefaultHandlerForStub.resetHistory();
launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
info(
"When the picker succeeds but Firefox is not default after the delay, event records result_is_default=false"
);
Services.fog.testResetFOG();
isDefaultHandlerForStub.returns(false);
await ShellService.setAsDefaultPDFHandler(false);
event = await awaitAttemptEvent();
Assert.equal(event.extra.method, "open_with", "Event method is open_with");
Assert.equal(event.extra.success, "true", "Event success is true");
Assert.equal(
event.extra.result_is_default,
"false",
"Event result_is_default is false when Firefox did not become default"
);
isDefaultHandlerForStub.returns(true);
userChoiceStub.resetHistory();
isDefaultHandlerForStub.resetHistory();
launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
@@ -399,72 +439,120 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
1,
"Recorded user-choice failure"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerUserChoiceResult.Success.testGetValue(),
undefined,
"Did not record user-choice success"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerOpenWithResult.Failure.testGetValue(),
1,
"Recorded open-with failure"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerOpenWithResult.Success.testGetValue(),
undefined,
"Did not record open-with success"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerModernSettingsResult.Success.testGetValue(),
1,
"Recorded modern settings success"
);
event = await awaitAttemptEvent();
Assert.equal(
Glean.browser.setDefaultPdfHandlerModernSettingsResult.Failure.testGetValue(),
undefined,
"Did not record modern settings failure"
event.extra.method,
"settings",
"Event method is settings (last attempted)"
);
Assert.equal(
event.extra.success,
"true",
"Event success reflects modern settings launch"
);
Assert.equal(
event.extra.result_is_default,
"true",
"Event result_is_default reflects isDefaultHandlerFor"
);
userChoiceStub.resetHistory();
isDefaultHandlerForStub.resetHistory();
launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
info(
"When userChoice fails, open-with fails, and modern settings fails, should record all failures"
"When userChoice fails, open-with fails, and modern settings fails, event records success=false"
);
Services.fog.testResetFOG();
isDefaultHandlerForStub.returns(false);
launchModernSettingsDialogDefaultAppsStub.throws(
new Error("mock modern settings failure")
);
await ShellService.setAsDefaultPDFHandler(false);
Assert.equal(
Glean.browser.setDefaultPdfHandlerUserChoiceResult.ErrOther.testGetValue(),
1,
"Recorded user-choice failure"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerUserChoiceResult.Success.testGetValue(),
undefined,
"Did not record user-choice success"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerOpenWithResult.Failure.testGetValue(),
1,
"Recorded open-with failure"
);
Assert.equal(
Glean.browser.setDefaultPdfHandlerModernSettingsResult.Failure.testGetValue(),
1,
"Recorded modern settings failure"
);
event = await awaitAttemptEvent();
Assert.equal(
Glean.browser.setDefaultPdfHandlerModernSettingsResult.Success.testGetValue(),
undefined,
"Did not record modern settings success"
event.extra.method,
"settings",
"Event method is settings (last attempted)"
);
Assert.equal(
event.extra.success,
"false",
"Event success is false when every method failed"
);
Assert.equal(
event.extra.result_is_default,
"false",
"Event result_is_default is false when no method set the default"
);
} finally {
launchOpenWithDefaultPickerForFileTypeStub.reset();
launchModernSettingsDialogDefaultAppsStub.reset();
sandbox.restore();
await SpecialPowers.popPrefEnv();
}
});
add_task(async function test_setAsDefaultPDFHandler_useOpenWithDisabled() {
const sandbox = sinon.createSandbox();
// With useOpenWith disabled, a userChoice failure should skip the
// IOpenWithLauncher branch entirely and fall straight through to the
// modern settings dialog.
await SpecialPowers.pushPrefEnv({
set: [
["browser.shell.setDefaultPDFHandler.useOpenWith", false],
["browser.shell.setDefaultPDFHandler.attemptWaitTimeMs", 0],
],
});
try {
sandbox
.stub(ShellService, "setAsDefaultPDFHandlerUserChoice")
.rejects(new Error("mock userChoice failure"));
sandbox.stub(ShellService, "_isWindows11").returns(true);
sandbox.stub(ShellService, "isDefaultHandlerFor").returns(true);
Services.fog.testResetFOG();
await ShellService.setAsDefaultPDFHandler(false);
Assert.ok(
launchOpenWithDefaultPickerForFileTypeStub.notCalled,
"Did not invoke open-with picker when pref is disabled"
);
Assert.ok(
launchModernSettingsDialogDefaultAppsStub.called,
"Fell through to modern settings dialog"
);
const event = await awaitAttemptEvent();
Assert.equal(
event.extra.method,
"settings",
"Event method skipped open_with and recorded settings"
);
Assert.equal(event.extra.success, "true", "Event success is true");
Assert.equal(
event.extra.result_is_default,
"true",
"Event result_is_default reflects isDefaultHandlerFor"
);
} finally {
launchOpenWithDefaultPickerForFileTypeStub.reset();
launchModernSettingsDialogDefaultAppsStub.reset();
sandbox.restore();
await SpecialPowers.popPrefEnv();
}
});

View File

@@ -16,7 +16,7 @@ add_task(async function () {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:logo",
url: getRootDirectory(gTestPath) + "large.png",
},
async () => {
const dialogLoad = BrowserTestUtils.domWindowOpened(null, async win => {

View File

@@ -1,4 +1,3 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,131 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
ChromeUtils.defineESModuleGetters(this, {
ShellService: "moz-src:///browser/components/shell/ShellService.sys.mjs",
});
const BAREBONES_DESKTOP_ENTRY = `[Desktop Entry]
Version=1.5
Type=Application
Name=test_desktopEntryStatus.js test case
`;
let gHomeDir;
let gSystemDir;
const filename = what => `test_desktopEntryStatus_file_${what}.desktop`;
// GLib caches results for efficiency. Unfortunately, it doesn't really provide
// a way to invalidate that cache, aside from hoping that the file monitor
// picks up on it. Resolve this by setting up all of the desktop entries at the
// start, then doing checks, then exiting.
//
// (Some others are special-cased, namely absent and Hidden= checks.)
const kDesktopEntries = [
{
label: "visible",
content: BAREBONES_DESKTOP_ENTRY,
expected: Ci.nsIGNOMEShellService.DESKTOP_ENTRY_VISIBLE,
},
{
label: "nodisplay",
content: BAREBONES_DESKTOP_ENTRY + "NoDisplay=true\n",
expected: Ci.nsIGNOMEShellService.DESKTOP_ENTRY_INVISIBLE,
},
{
label: "onlyshowin-matching",
content: BAREBONES_DESKTOP_ENTRY + "OnlyShowIn=FirefoxOS\n",
expected: Ci.nsIGNOMEShellService.DESKTOP_ENTRY_VISIBLE,
},
{
label: "onlyshowin-notmatching",
content: BAREBONES_DESKTOP_ENTRY + "OnlyShowIn=another\n",
expected: Ci.nsIGNOMEShellService.DESKTOP_ENTRY_INVISIBLE,
},
{
label: "notshowin-matching",
content: BAREBONES_DESKTOP_ENTRY + "NotShowIn=FirefoxOS\n",
expected: Ci.nsIGNOMEShellService.DESKTOP_ENTRY_INVISIBLE,
},
{
label: "notshowin-notmatching",
content: BAREBONES_DESKTOP_ENTRY + "NotShowIn=another\n",
expected: Ci.nsIGNOMEShellService.DESKTOP_ENTRY_VISIBLE,
},
];
add_setup(async function setup() {
let unique = await IOUtils.createUniqueDirectory(
Services.dirsvc.get("TmpD", Ci.nsIFile).path,
"desktopEntryStatusTest"
);
let homeDir = PathUtils.join(unique, "data-home");
Services.env.set("XDG_DATA_HOME", homeDir);
gHomeDir = PathUtils.join(homeDir, "applications");
await IOUtils.makeDirectory(gHomeDir, { createAncestors: true });
let systemDir = PathUtils.join(unique, "data-system");
Services.env.set("XDG_DATA_DIRS", systemDir);
gSystemDir = PathUtils.join(systemDir, "applications");
await IOUtils.makeDirectory(gSystemDir, { createAncestors: true });
Services.env.set("XDG_CURRENT_DESKTOP", "FirefoxOS");
await IOUtils.writeUTF8(
PathUtils.join(gHomeDir, filename("deleted")),
BAREBONES_DESKTOP_ENTRY + "Hidden=true\n"
);
await IOUtils.writeUTF8(
PathUtils.join(gSystemDir, filename("deleted")),
BAREBONES_DESKTOP_ENTRY
);
for (const desktopEntry of kDesktopEntries) {
await IOUtils.writeUTF8(
PathUtils.join(gHomeDir, filename(desktopEntry.label + "-home")),
desktopEntry.content
);
await IOUtils.writeUTF8(
PathUtils.join(gSystemDir, filename(desktopEntry.label + "-system")),
desktopEntry.content
);
}
registerCleanupFunction(async () => {
return IOUtils.remove(unique, { recursive: true });
});
});
add_task(function test_desktopEntryStatus() {
Assert.equal(
ShellService.getDesktopEntryStatus(filename("absent")),
Ci.nsIGNOMEShellService.DESKTOP_ENTRY_ABSENT,
"A desktop entry that doesn't exist should be absent."
);
Assert.equal(
ShellService.getDesktopEntryStatus(filename("hidden")),
Ci.nsIGNOMEShellService.DESKTOP_ENTRY_ABSENT,
"A desktop entry shadowed by one with the Hidden= attribute should be absent."
);
for (const desktopEntry of kDesktopEntries) {
Assert.equal(
ShellService.getDesktopEntryStatus(
filename(desktopEntry.label + "-home")
),
desktopEntry.expected,
"Desktop entry matches when at the local level: " + desktopEntry.label
);
Assert.equal(
ShellService.getDesktopEntryStatus(
filename(desktopEntry.label + "-system")
),
desktopEntry.expected,
"Desktop entry matches when at the system level: " + desktopEntry.label
);
}
});

View File

@@ -0,0 +1,109 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
ChromeUtils.defineESModuleGetters(this, {
StartupOSIntegration:
"moz-src:///browser/components/shell/StartupOSIntegration.sys.mjs",
WindowsLaunchOnLogin: "resource://gre/modules/WindowsLaunchOnLogin.sys.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
});
const PREF = "browser.startup.windowsLaunchOnLogin.defaultEnabled";
async function runWith({ isFirstRun, prefValue, approved }) {
let sandbox = sinon.createSandbox();
let approvedStub = sandbox
.stub(WindowsLaunchOnLogin, "getLaunchOnLoginApproved")
.resolves(approved);
let createStub = sandbox
.stub(WindowsLaunchOnLogin, "createLaunchOnLogin")
.resolves();
if (prefValue === null) {
Services.prefs.clearUserPref(PREF);
} else {
Services.prefs.setBoolPref(PREF, prefValue);
}
try {
await StartupOSIntegration.maybeCreateLaunchOnLoginOnFirstRun(isFirstRun);
return { approvedStub, createStub };
} finally {
sandbox.restore();
Services.prefs.clearUserPref(PREF);
}
}
add_task(async function test_creates_when_all_conditions_true() {
let { createStub } = await runWith({
isFirstRun: true,
prefValue: true,
approved: true,
});
Assert.ok(
createStub.calledOnce,
"createLaunchOnLogin should be called when isFirstRun, pref, and approval are all true"
);
});
add_task(async function test_skips_when_not_first_run() {
let { createStub, approvedStub } = await runWith({
isFirstRun: false,
prefValue: true,
approved: true,
});
Assert.ok(
!createStub.called,
"createLaunchOnLogin should not be called when isFirstRun is false"
);
Assert.ok(
!approvedStub.called,
"getLaunchOnLoginApproved should be short-circuited when isFirstRun is false"
);
});
add_task(async function test_skips_when_pref_disabled() {
let { createStub, approvedStub } = await runWith({
isFirstRun: true,
prefValue: false,
approved: true,
});
Assert.ok(
!createStub.called,
"createLaunchOnLogin should not be called when pref is false"
);
Assert.ok(
!approvedStub.called,
"getLaunchOnLoginApproved should be short-circuited when pref is false"
);
});
add_task(async function test_skips_when_windows_policy_denies() {
let { createStub, approvedStub } = await runWith({
isFirstRun: true,
prefValue: true,
approved: false,
});
Assert.ok(
approvedStub.calledOnce,
"getLaunchOnLoginApproved should be consulted when pref and isFirstRun are true"
);
Assert.ok(
!createStub.called,
"createLaunchOnLogin should not be called when Windows policy denies"
);
});
add_task(async function test_uses_pref_default_when_unset() {
let { createStub } = await runWith({
isFirstRun: true,
prefValue: null,
approved: true,
});
Assert.ok(
createStub.calledOnce,
"createLaunchOnLogin should be called when pref is at its built-in default of true"
);
});

View File

@@ -5,6 +5,11 @@ run-if = [
firefox-appdir = "browser"
tags = "os_integration"
["test_desktopEntryStatus.js"]
run-if = [
"os == 'linux'",
]
["test_linuxDesktopEntry.js"]
run-if = [
"os == 'linux'",
@@ -15,6 +20,11 @@ run-if = [
"os == 'mac'",
]
["test_maybeCreateLaunchOnLoginOnFirstRun.js"]
run-if = [
"os == 'win'"
]
["test_secondaryTileJs.js"]
run-if = [
"os == 'win'"

View File

@@ -63,7 +63,7 @@ async function do_test(test) {
if (test.value) {
info("Creating mock filepicker to select files");
let MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window.browsingContext);
MockFilePicker.init();
MockFilePicker.returnValue = MockFilePicker.returnOK;
MockFilePicker.displayDirectory = FileUtils.getDir("TmpD", []);
MockFilePicker.setFiles([tempFile]);

View File

@@ -142,9 +142,7 @@ export class nsZenSiteDataPanel {
this.anchor.removeAttribute("boosting");
}
// Force a reflow to ensure the attribute change is applied before any potential animation.
if (this.unifiedPanel.state === "open") {
this.anchor.getBoundingClientRect();
}
this.anchor.getBoundingClientRect();
}
#initCopyUrlButton() {

View File

@@ -6,7 +6,7 @@
"version": {
"product": "firefox",
"version": "151.0.4",
"candidate": "151.0.4",
"candidate": "152.0",
"candidateBuild": 1
},
"buildOptions": {
@@ -20,7 +20,7 @@
"brandShortName": "Zen",
"brandFullName": "Zen Browser",
"release": {
"displayVersion": "1.21.1b",
"displayVersion": "1.21b",
"github": {
"repo": "zen-browser/desktop"
},