diff --git a/README.md b/README.md
index f391024f4..45db3aa51 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,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 `154.0.1`!
+- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `155.0`!
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 155.0`!
### Contributing
diff --git a/build/firefox-cache/l10n-last-commit-hash b/build/firefox-cache/l10n-last-commit-hash
index e668d9111..24e25043a 100644
--- a/build/firefox-cache/l10n-last-commit-hash
+++ b/build/firefox-cache/l10n-last-commit-hash
@@ -1 +1 @@
-6b4abe0093721c1be89eb41b3fb060dd6336923d
\ No newline at end of file
+20e5abbb642ab21d943bc1261864d9c531ba421a
\ No newline at end of file
diff --git a/src/external-patches/firefox/bug_2011236.patch b/src/external-patches/firefox/bug_2011236.patch
index 1a46a9c03..f853cfc36 100644
--- a/src/external-patches/firefox/bug_2011236.patch
+++ b/src/external-patches/firefox/bug_2011236.patch
@@ -150,8 +150,8 @@ diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/Sessi
aLoadState->TypeHint())) {
MOZ_DIAGNOSTIC_ASSERT(!mURI->SchemeIs("javascript"));
- // Pull the upload stream off of the channel instead of the load state, as
-@@ -124,11 +133,12 @@
+ // Verify the documented purpose of mBaseURI.
+@@ -130,11 +139,12 @@
loadInfo->GetPrincipalToInherit(
getter_AddRefs(mSharedState.Get()->mPrincipalToInherit));
diff --git a/src/external-patches/firefox/issue_14710.patch b/src/external-patches/firefox/issue_14710.patch
index 54e288ed8..da0f7160e 100644
--- a/src/external-patches/firefox/issue_14710.patch
+++ b/src/external-patches/firefox/issue_14710.patch
@@ -1,7 +1,24 @@
diff --git a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs b/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs
--- a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs
+++ b/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs
-@@ -119,10 +119,19 @@
+@@ -39,10 +39,16 @@
+ const TOGGLE_POSITION_PREF =
+ "media.videocontrols.picture-in-picture.video-toggle.position";
+ const TOGGLE_POSITION_RIGHT = "right";
+ const TOGGLE_POSITION_LEFT = "left";
+ const RESIZE_MARGIN_PX = 16;
++// Close reasons that should suppress auto-toggle when the originating tab is backgrounded
++const DELIBERATE_CLOSE_REASONS = [
++ "CloseButton",
++ "ClosePlayerShortcut",
++ "Shortcut",
++];
+
+ XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "PIP_ENABLED",
+ "media.videocontrols.picture-in-picture.enabled",
+@@ -119,10 +125,19 @@
}
case "PictureInPicture:VideoTabHidden": {
if (!lazy.PIP_ENABLED || !lazy.PIP_WHEN_SWITCHING_TABS) {
@@ -21,12 +38,13 @@ diff --git a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs b/tool
break;
}
let actor = browsingContext.currentWindowGlobal.getActor(
-@@ -261,10 +270,12 @@
+@@ -261,10 +276,13 @@
maxConcurrentPlayerCount: 0,
// Maps auto pip browser to PictureInPictureParent actor
weakAutoPipBrowserToParent: new WeakMap(),
++ // Browsers with deliberately closed PiP windows; suppresses auto-toggle.
+ weakAutoPipBrowserClosedDeliberately: new WeakSet(),
+
/**
@@ -34,7 +52,28 @@ diff --git a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs b/tool
*
* @param {PictureInPictureParent} pipActorRef
* Reference to the calling PictureInPictureParent actor
-@@ -899,10 +910,17 @@
+@@ -441,10 +459,20 @@
+ },
+
+ onPipSwappedBrowsers(event) {
+ let otherTab = event.detail;
+ if (otherTab) {
++ if (
++ this.weakAutoPipBrowserClosedDeliberately.has(
++ event.target.linkedBrowser
++ )
++ ) {
++ this.weakAutoPipBrowserClosedDeliberately.add(otherTab.linkedBrowser);
++ this.weakAutoPipBrowserClosedDeliberately.delete(
++ event.target.linkedBrowser
++ );
++ }
+ for (let win of Services.wm.getEnumerator(WINDOW_TYPE)) {
+ if (this.weakWinToBrowser.get(win) === event.target.linkedBrowser) {
+ this.weakWinToBrowser.set(win, otherTab.linkedBrowser);
+ this.removePiPBrowserFromWeakMap(event.target.linkedBrowser);
+ this.removeOriginatingWinFromWeakMap(event.target.linkedBrowser);
+@@ -899,10 +927,20 @@
const { reason, actorRef } = closeData;
const win = this.getWeakPipPlayer(actorRef);
if (!win) {
@@ -43,8 +82,11 @@ diff --git a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs b/tool
+
+ const browser = this.weakWinToBrowser.get(win);
+
-+ if (reason === "CloseButton") {
-+ PictureInPicture.weakAutoPipBrowserClosedDeliberately.add(browser);
++ if (browser && DELIBERATE_CLOSE_REASONS.includes(reason)) {
++ const tabbrowser = browser.getTabBrowser();
++ if (tabbrowser && tabbrowser.selectedBrowser != browser) {
++ PictureInPicture.weakAutoPipBrowserClosedDeliberately.add(browser);
++ }
+ }
+
this.removePiPBrowserFromWeakMap(this.weakWinToBrowser.get(win));
@@ -52,4 +94,69 @@ diff --git a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs b/tool
Glean.pictureinpicture["closedMethod" + reason].record();
await this.closePipWindow(win);
+diff --git a/toolkit/components/pictureinpicture/tests/browser_autotoggle.js b/toolkit/components/pictureinpicture/tests/browser_autotoggle.js
+--- a/toolkit/components/pictureinpicture/tests/browser_autotoggle.js
++++ b/toolkit/components/pictureinpicture/tests/browser_autotoggle.js
+@@ -297,5 +297,61 @@
+ await new Promise(resolve => setTimeout(resolve, 2000));
+ assertNoPiPWindowsOpen();
+ }
+ );
+ });
++
++/**
++ * Tests that closing a PiP window deliberately (via Shift+ESC) while the tab is backgrounded
++ * suppresses auto-toggle, but auto-toggle works normally on the next background.
++ */
++add_task(async function autopip_suppress_on_deliberate_close() {
++ await SpecialPowers.pushPrefEnv({
++ set: [[ALWAYS_SHOW_PREF, true]],
++ });
++
++ // Open a new window and save a handle for the first tab
++ let win1 = await BrowserTestUtils.openNewBrowserWindow();
++ let firstTab = win1.gBrowser.selectedTab;
++
++ // Open a new tab containing a video
++ let pipTab = await BrowserTestUtils.openNewForegroundTab(
++ win1.gBrowser,
++ TEST_PAGE
++ );
++ let browser = pipTab.linkedBrowser;
++
++ // Ensure the video is playing
++ let videoID = "with-controls";
++ await ensureVideosReady(browser);
++ await SpecialPowers.spawn(browser, [videoID], async videoID => {
++ await content.document.getElementById(videoID).play();
++ });
++
++ // Switch from the video tab to the first tab and check if the PiP window opened
++ let domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null);
++ await BrowserTestUtils.switchTab(win1.gBrowser, firstTab);
++ let pipWin = await domWindowOpened;
++ ok(pipWin, "PiP window automatically opened.");
++
++ // Close the PiP window deliberately while the tab is backgrounded and check that it doesn't reopen
++ domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null);
++ // A short delay is needed to make the test more robust
++ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
++ await new Promise(resolve => setTimeout(resolve, 2000));
++ EventUtils.synthesizeKey("KEY_Escape", { shiftKey: true }, pipWin);
++ let reopened = await Promise.race([
++ domWindowOpened.then(() => true),
++ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
++ new Promise(resolve => setTimeout(() => resolve(false), 2000)),
++ ]);
++ ok(!reopened, "PiP window should not reopen after deliberate close");
++
++ // Switch to the video tab and back to the first tab, which should automatically open the PiP window
++ domWindowOpened = BrowserTestUtils.domWindowOpenedAndLoaded(null);
++ await BrowserTestUtils.switchTab(win1.gBrowser, pipTab);
++ await BrowserTestUtils.switchTab(win1.gBrowser, firstTab);
++ pipWin = await domWindowOpened;
++ ok(pipWin, "PiP window automatically opened.");
++
++ await BrowserTestUtils.closeWindow(win1);
++});
diff --git a/src/zen/tests/mochitests/backforward/browser_history_menu.js b/src/zen/tests/mochitests/backforward/browser_history_menu.js
index 8623d5405..a0cff08b1 100644
--- a/src/zen/tests/mochitests/backforward/browser_history_menu.js
+++ b/src/zen/tests/mochitests/backforward/browser_history_menu.js
@@ -44,7 +44,7 @@ async function openHistoryMenu(useContextMenu) {
async function testBackForwardMenu(useContextMenu) {
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.com"
);
@@ -90,7 +90,7 @@ async function testBackForwardMenu(useContextMenu) {
);
let node = popupEvent.target.lastElementChild;
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
is(node.getAttribute("uri"), "http://example.com/", "'1' item uri");
is(node.getAttribute("index"), "0", "'1' item index");
is(
@@ -100,7 +100,7 @@ async function testBackForwardMenu(useContextMenu) {
);
node = node.previousElementSibling;
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
is(node.getAttribute("uri"), "http://example.com/2.html", "'2' item uri");
is(node.getAttribute("index"), "1", "'2' item index");
is(
@@ -111,7 +111,7 @@ async function testBackForwardMenu(useContextMenu) {
if (iter >= 3) {
node = node.previousElementSibling;
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
is(node.getAttribute("uri"), "http://example.com/3.html", "'3' item uri");
is(node.getAttribute("index"), "2", "'3' item index");
is(
@@ -133,7 +133,7 @@ async function testBackForwardMenu(useContextMenu) {
} else {
let newTabPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
url => url == "http://example.com/"
);
diff --git a/src/zen/tests/mochitests/caps/browser_principalSerialization_json.js b/src/zen/tests/mochitests/caps/browser_principalSerialization_json.js
index f79f269fd..bdc34988b 100644
--- a/src/zen/tests/mochitests/caps/browser_principalSerialization_json.js
+++ b/src/zen/tests/mochitests/caps/browser_principalSerialization_json.js
@@ -88,32 +88,32 @@ add_task(async function test_contentPrincipal() {
*/
let tests = [
{
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
input: { uri: "http://example.com/", OA: {} },
expected: `{"${contentId}":{"${content}":"http://example.com/"}}`,
},
{
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
input: { uri: "http://mozilla1.com/", OA: {} },
expected: `{"${contentId}":{"${content}":"http://mozilla1.com/"}}`,
},
{
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
input: { uri: "http://mozilla2.com/", OA: { userContextId: 0 } },
expected: `{"${contentId}":{"${content}":"http://mozilla2.com/"}}`,
},
{
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
input: { uri: "http://mozilla3.com/", OA: { userContextId: 2 } },
expected: `{"${contentId}":{"${content}":"http://mozilla3.com/","${suffix}":"^userContextId=2"}}`,
},
{
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
input: { uri: "http://mozilla4.com/", OA: { privateBrowsingId: 1 } },
expected: `{"${contentId}":{"${content}":"http://mozilla4.com/","${suffix}":"^privateBrowsingId=1"}}`,
},
{
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
input: { uri: "http://mozilla5.com/", OA: { privateBrowsingId: 0 } },
expected: `{"${contentId}":{"${content}":"http://mozilla5.com/"}}`,
},
diff --git a/src/zen/tests/mochitests/readermode/browser_bug1780350_readerModeSaveScroll.js b/src/zen/tests/mochitests/readermode/browser_bug1780350_readerModeSaveScroll.js
index 76add5511..dedeee53d 100644
--- a/src/zen/tests/mochitests/readermode/browser_bug1780350_readerModeSaveScroll.js
+++ b/src/zen/tests/mochitests/readermode/browser_bug1780350_readerModeSaveScroll.js
@@ -1,4 +1,4 @@
-/* eslint-disable @microsoft/sdl/no-insecure-url */
+/* eslint-disable sdl/no-insecure-url */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
diff --git a/src/zen/tests/mochitests/sessionstore/browser.toml b/src/zen/tests/mochitests/sessionstore/browser.toml
index f6ab3d6e8..bcc9ff324 100644
--- a/src/zen/tests/mochitests/sessionstore/browser.toml
+++ b/src/zen/tests/mochitests/sessionstore/browser.toml
@@ -16,8 +16,6 @@ support-files = [
"browser_frame_history_c2.html",
"browser_formdata_format_sample.html",
"browser_sessionHistory_slow.sjs",
- "browser_policy_container_sample.html",
- "browser_policy_container_sample_frame.html",
"browser_scrollPositions_sample.html",
"browser_scrollPositions_sample2.html",
"browser_scrollPositions_sample_frameset.html",
@@ -249,8 +247,6 @@ skip-if = [
"ccov", # Bug 1625525
]
-["browser_policy_container_not_stored.js"]
-
["browser_privatetabs.js"]
["browser_purge_domaindata.js"]
@@ -355,6 +351,7 @@ skip-if = [
["browser_sizemodeBeforeMinimized.js"]
skip-if = [
+ "os == 'linux' && os_version == '22.04' && arch == 'x86_64' && opt && nova", # Bug 2057856
"os == 'win' && os_version == '11.26200' && asan", # Bug 2019745
]
@@ -436,6 +433,7 @@ disabled="Disabled by import_external_tests.py"
skip-if = [
"os == 'mac' && os_version == '15.30' && arch == 'aarch64'", # Bug 2023967
]
+
["browser_tab_label_during_restore.js"]
disabled="Disabled by import_external_tests.py"
https_first_disabled = true
diff --git a/src/zen/tests/mochitests/sessionstore/browser_bfcache_telemetry.js b/src/zen/tests/mochitests/sessionstore/browser_bfcache_telemetry.js
index c1e987750..9e5d2bd36 100644
--- a/src/zen/tests/mochitests/sessionstore/browser_bfcache_telemetry.js
+++ b/src/zen/tests/mochitests/sessionstore/browser_bfcache_telemetry.js
@@ -1,44 +1,26 @@
const URL1 = "data:text/html;charset=utf-8,
Hello1
";
const URL2 = "data:text/html;charset=utf-8,Hello2
";
-async function getBFCacheComboTelemetry(probeInParent) {
- let bfcacheCombo;
- await TestUtils.waitForCondition(() => {
- let histograms;
- if (probeInParent) {
- histograms = Services.telemetry.getSnapshotForHistograms(
- "main",
- false /* clear */
- ).parent;
- } else {
- histograms = Services.telemetry.getSnapshotForHistograms(
- "main",
- false /* clear */
- ).content;
- }
- bfcacheCombo = histograms.BFCACHE_COMBO;
- return bfcacheCombo;
- });
- return bfcacheCombo;
+async function getBFCacheComboValue(label) {
+ await Services.fog.testFlushAllChildren();
+ return Glean.bfcache.combo[label].testGetValue();
}
-async function test_bfcache_telemetry(probeInParent) {
- Services.telemetry.getHistogramById("BFCACHE_COMBO").clear();
+add_task(async () => {
+ await Services.fog.testFlushAllChildren();
+ Services.fog.testResetFOG();
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL1);
BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, URL2);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
- let bfcacheCombo = await getBFCacheComboTelemetry(probeInParent);
+ await TestUtils.waitForCondition(
+ async () => (await getBFCacheComboValue("BFCache_Success")) !== null,
+ "Waiting for bfcache.combo 'BFCache_Success' to be recorded"
+ );
- is(bfcacheCombo.values[0], 1, "1 bfcache success");
+ is(await getBFCacheComboValue("BFCache_Success"), 1, "1 bfcache success");
gBrowser.removeTab(tab);
-}
-
-add_task(async () => {
- await test_bfcache_telemetry(
- Services.prefs.getBoolPref("fission.bfcacheInParent")
- );
});
diff --git a/src/zen/tests/mochitests/sessionstore/browser_oldformat.toml b/src/zen/tests/mochitests/sessionstore/browser_oldformat.toml
index 54842390e..348a0c004 100644
--- a/src/zen/tests/mochitests/sessionstore/browser_oldformat.toml
+++ b/src/zen/tests/mochitests/sessionstore/browser_oldformat.toml
@@ -153,6 +153,7 @@ skip-if = [
["browser_477657.js"]
skip-if = [
+ "os == 'linux' && os_version == '22.04' && arch == 'x86_64' && opt && nova", # Bug 2057857
"os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11'", # Bug 1610668
]
@@ -182,6 +183,9 @@ skip-if = [
]
["browser_524745.js"]
+skip-if = [
+ "os == 'linux' && os_version == '22.04' && arch == 'x86_64' && debug && nova", # Bug 2057858
+]
["browser_528776.js"]
diff --git a/src/zen/tests/mochitests/sessionstore/browser_policy_container_not_stored.js b/src/zen/tests/mochitests/sessionstore/browser_policy_container_not_stored.js
deleted file mode 100644
index 9258e3d54..000000000
--- a/src/zen/tests/mochitests/sessionstore/browser_policy_container_not_stored.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-/**
- * A session history entry only needs to retain the policy container for loads
- * that inherit their policies (about:blank, about:srcdoc, blob:, data:, ...).
- * Anything fetched over the network gets its policies from the response again,
- * so storing them would only bloat the session store (bug 2011236).
- */
-
-const TEST_ROOT = getRootDirectory(gTestPath).replace(
- "chrome://mochitests/content",
- "https://example.com"
-);
-const PARENT_URL = TEST_ROOT + "browser_policy_container_sample.html";
-const FRAME_URL = TEST_ROOT + "browser_policy_container_sample_frame.html";
-
-add_task(async function test_policy_container_only_for_inheriting_loads() {
- const tab = BrowserTestUtils.addTab(gBrowser, PARENT_URL);
- gBrowser.selectedTab = tab;
- await promiseBrowserLoaded(tab.linkedBrowser, true, PARENT_URL);
- await TabStateFlusher.flush(tab.linkedBrowser);
-
- const state = JSON.parse(ss.getTabState(tab));
- const topEntry = state.entries.at(-1);
- is(topEntry.url, PARENT_URL, "collected the parent entry");
-
- const children = topEntry.children ?? [];
- const networkFrame = children.find(child => child.url === FRAME_URL);
- const blankFrame = children.find(child => child.url === "about:blank");
-
- ok(networkFrame, "collected the network-scheme subframe entry");
- ok(blankFrame, "collected the about:blank subframe entry");
-
- // The parent has a CSP, so both subframes inherit a policy container onto
- // their load state. Only the one that cannot recover it from a response
- // should keep it in session history.
- ok(
- !("policyContainer" in networkFrame),
- "https subframe entry does not store a policyContainer"
- );
- ok(
- "policyContainer" in blankFrame,
- "about:blank subframe entry still stores its inherited policyContainer"
- );
-
- await BrowserTestUtils.removeTab(tab);
-});
diff --git a/src/zen/tests/mochitests/sessionstore/browser_policy_container_sample.html b/src/zen/tests/mochitests/sessionstore/browser_policy_container_sample.html
deleted file mode 100644
index 1b8ad8d29..000000000
--- a/src/zen/tests/mochitests/sessionstore/browser_policy_container_sample.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
- policyContainer session store sample
-
-
-
-
-
-
diff --git a/src/zen/tests/mochitests/sessionstore/browser_policy_container_sample_frame.html b/src/zen/tests/mochitests/sessionstore/browser_policy_container_sample_frame.html
deleted file mode 100644
index 4675a0cd4..000000000
--- a/src/zen/tests/mochitests/sessionstore/browser_policy_container_sample_frame.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- policyContainer session store sample frame
-
-
- frame
-
-
diff --git a/src/zen/tests/mochitests/sessionstore/browser_restored_window_features.js b/src/zen/tests/mochitests/sessionstore/browser_restored_window_features.js
index 20a1d7196..64aca7910 100644
--- a/src/zen/tests/mochitests/sessionstore/browser_restored_window_features.js
+++ b/src/zen/tests/mochitests/sessionstore/browser_restored_window_features.js
@@ -8,7 +8,6 @@ const BARPROP_NAMES = [
"menubar",
"personalbar",
"scrollbars",
- "statusbar",
"toolbar",
];
@@ -45,7 +44,6 @@ add_task(async function testRestoredWindowFeatures() {
menubar: true,
personalbar: true,
scrollbars: true,
- statusbar: true,
toolbar: true,
};
const TESTS = [
diff --git a/src/zen/tests/mochitests/sessionstore/browser_should_restore_tab.js b/src/zen/tests/mochitests/sessionstore/browser_should_restore_tab.js
index 07c699c31..318c846a6 100644
--- a/src/zen/tests/mochitests/sessionstore/browser_should_restore_tab.js
+++ b/src/zen/tests/mochitests/sessionstore/browser_should_restore_tab.js
@@ -18,8 +18,14 @@ async function check_tab_close_notification(openedTab, expectNotification) {
}
Services.obs.addObserver(topicObserver, NOTIFY_CLOSED_OBJECTS_CHANGED);
+ // (Bug 2058570) Intermittently, the tab state wasn't fully up to date before closing the tab.
+ // This caused test_navigated_about_home to think there was no meaningful history beyond about:home.
+ // Try flushing the tab state and then waiting for a notification if one is expected.
+ let flushedPromise = TabStateFlusher.flush(openedTab.linkedBrowser);
+
BrowserTestUtils.removeTab(openedTab);
await tabClosed;
+ await flushedPromise;
// SessionStore does a setTimeout(notify, 0) to notifyObservers when it handles TabClose
// We need to wait long enough to be confident the observer would have been notified
// if it was going to be.
diff --git a/src/zen/tests/mochitests/sessionstore/marionette/test_restore_aiwindow.py b/src/zen/tests/mochitests/sessionstore/marionette/test_restore_aiwindow.py
index 530bfdecb..b899a392d 100644
--- a/src/zen/tests/mochitests/sessionstore/marionette/test_restore_aiwindow.py
+++ b/src/zen/tests/mochitests/sessionstore/marionette/test_restore_aiwindow.py
@@ -385,3 +385,64 @@ class TestSmartWindowDefaultManualRestore(
tab_count,
msg="Tab count should be preserved after restore",
)
+
+
+class TestSmartWindowDefaultManualRestoreOverwritesNewTab(
+ SmartWindowDefaultMixin, SessionStoreTestCase
+):
+ """With Smart Window as default (startup.page=1), the startup window opens
+ showing the Smart new tab. Restoring a previously Smart session via
+ "Restore previous session" reuses that startup window (the types match),
+ and the restored tabs must *overwrite* the Smart new tab rather than being
+ appended alongside it."""
+
+ def test_smart_session_restored_overwrites_smart_new_tab(self):
+ self.marionette.execute_script(
+ """
+ Services.prefs.setBoolPref("browser.sessionstore.persist_closed_tabs_between_sessions", true);
+ """
+ )
+
+ self.wait_for_windows(
+ self.all_windows, "Not all requested windows have been opened"
+ )
+
+ # Make the saved session a Smart Window with the 3 content tabs from
+ # setUp (Tab 1/2/3), which are real pages, not the Smart new tab.
+ self.toggle_ai_window(True)
+ self.assertTrue(
+ self.is_ai_window(), msg="Window should be Smart before the restart"
+ )
+ tab_count = self.get_tab_count()
+ self.assertEqual(tab_count, 3, msg="Saved session should have 3 tabs")
+
+ self.marionette.quit()
+ self.marionette.start_session()
+ self.marionette.set_context("chrome")
+
+ # The new startup window opens Smart (Smart Window is the default) and
+ # shows a single Smart new tab.
+ self.assertTrue(
+ self.is_ai_window(),
+ msg="Startup window should open Smart when Smart Window is the default",
+ )
+
+ self.restore_last_session()
+
+ self.assertEqual(
+ len(self.marionette.chrome_window_handles),
+ 1,
+ msg="Smart session should restore into the existing startup window",
+ )
+ self.assertTrue(
+ self.is_ai_window(),
+ msg="Restored window should stay Smart",
+ )
+
+ # Asserts if Smart new tab was overwritten, not appended to.
+ self.assertEqual(
+ self.get_tab_count(),
+ tab_count,
+ msg="Restored tabs should overwrite the Smart new tab, not be "
+ "appended alongside it",
+ )
diff --git a/src/zen/tests/mochitests/sessionstore/unit/test_write_json_length_hint.js b/src/zen/tests/mochitests/sessionstore/unit/test_write_json_length_hint.js
deleted file mode 100644
index fd4a44848..000000000
--- a/src/zen/tests/mochitests/sessionstore/unit/test_write_json_length_hint.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-const { SessionWriter } = ChromeUtils.importESModule(
- "resource:///modules/sessionstore/SessionWriter.sys.mjs"
-);
-
-const profd = do_get_profile();
-const { SessionFile } = ChromeUtils.importESModule(
- "resource:///modules/sessionstore/SessionFile.sys.mjs"
-);
-
-const { updateAppInfo } = ChromeUtils.importESModule(
- "resource://testing-common/AppInfo.sys.mjs"
-);
-updateAppInfo({
- name: "SessionRestoreTest",
- ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
- version: "1",
- platformVersion: "",
-});
-
-add_setup(async function () {
- let source = do_get_file("data/sessionstore_valid.js");
- source.copyTo(profd, "sessionstore.js");
- await writeCompressedFile(
- SessionFile.Paths.clean.replace("jsonlz4", "js"),
- SessionFile.Paths.clean
- );
- await SessionFile.read();
-});
-
-add_task(async function test_length_hint_updates_after_write() {
- Assert.equal(
- SessionWriter._jsonLengthHint,
- 0,
- "Length hint starts at 0"
- );
-
- await SessionFile.write({});
-
- let hintAfterSmall = SessionWriter._jsonLengthHint;
- Assert.equal(
- hintAfterSmall,
- JSON.stringify({}).length,
- "Hint matches the uncompressed JSON byte length"
- );
-
- let largerState = await IOUtils.readJSON(
- PathUtils.join(do_get_cwd().path, "data", "sessionstore_complete.json")
- );
- await SessionFile.write(largerState);
-
- Assert.greater(
- SessionWriter._jsonLengthHint,
- hintAfterSmall,
- "Hint grows after writing a larger state"
- );
-});
-
-add_task(async function test_length_hint_resets_on_wipe() {
- await SessionFile.write({ windows: [{ tabs: [{ entries: [] }] }] });
- Assert.greater(SessionWriter._jsonLengthHint, 0, "Hint is nonzero");
-
- await SessionFile.wipe();
- Assert.equal(
- SessionWriter._jsonLengthHint,
- 0,
- "Hint resets to 0 after wipe"
- );
-});
diff --git a/src/zen/tests/mochitests/sessionstore/unit/xpcshell.toml b/src/zen/tests/mochitests/sessionstore/unit/xpcshell.toml
index 01b0fe1f2..661a6046d 100644
--- a/src/zen/tests/mochitests/sessionstore/unit/xpcshell.toml
+++ b/src/zen/tests/mochitests/sessionstore/unit/xpcshell.toml
@@ -41,8 +41,3 @@ skip-if = [
]
["test_startup_session_async.js"]
-
-["test_write_json_length_hint.js"]
-support-files = [
- "data/sessionstore_complete.json",
-]
diff --git a/src/zen/tests/mochitests/shell/unit/test_isOneClickSetDefaultEnabled.js b/src/zen/tests/mochitests/shell/unit/test_isOneClickSetDefaultEnabled.js
new file mode 100644
index 000000000..639d62ce8
--- /dev/null
+++ b/src/zen/tests/mochitests/shell/unit/test_isOneClickSetDefaultEnabled.js
@@ -0,0 +1,174 @@
+/* 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",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
+});
+
+const USER_CHOICE_PREF = "browser.shell.setDefaultBrowserUserChoice";
+const REG_RENAME_PREF = "browser.shell.setDefaultBrowserUserChoice.regRename";
+
+function mockNativeShellService(overrides) {
+ const mock = {
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowsShellService]),
+ checkAllProgIDsExist: () => true,
+ checkBrowserUserChoiceHashes: () => true,
+ isDefaultBrowser: () => false,
+ isUserChoiceProtectionDriverRunning: () => false,
+ canRenameUserChoiceAssociationKey: sinon.stub().returns(false),
+ ...overrides,
+ };
+ sinon.stub(ShellService, "shellService").value(mock);
+ return mock;
+}
+
+registerCleanupFunction(() => {
+ sinon.restore();
+ Services.prefs.clearUserPref(USER_CHOICE_PREF);
+ Services.prefs.clearUserPref(REG_RENAME_PREF);
+});
+
+add_task(function test_pref_disabled() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, false);
+ const native = mockNativeShellService({
+ checkAllProgIDsExist: () => {
+ Assert.ok(
+ false,
+ "Should not check capability when the user choice pref is off"
+ );
+ return true;
+ },
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ false,
+ "Should be false when the user choice pref is disabled, regardless of capability"
+ );
+ Assert.ok(
+ native.canRenameUserChoiceAssociationKey.notCalled,
+ "Should not probe the registry when the user choice pref is off"
+ );
+
+ sinon.restore();
+});
+
+add_task(function test_user_choice_impossible() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, true);
+ const native = mockNativeShellService({
+ checkBrowserUserChoiceHashes: () => false,
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ false,
+ "Should be false when the existing UserChoice hashes can't be reproduced"
+ );
+ Assert.ok(
+ native.canRenameUserChoiceAssociationKey.notCalled,
+ "Should not probe the registry when a UserChoice write can't succeed anyway"
+ );
+
+ sinon.restore();
+});
+
+add_task(function test_ucpd_not_running() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, true);
+ const native = mockNativeShellService({
+ isUserChoiceProtectionDriverRunning: () => false,
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ true,
+ "Should be true when UCPD isn't running to lock the UserChoice keys"
+ );
+ Assert.ok(
+ native.canRenameUserChoiceAssociationKey.notCalled,
+ "Should not probe the registry when UCPD isn't running"
+ );
+
+ sinon.restore();
+});
+
+add_task(function test_already_default_skips_probe() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, true);
+ const native = mockNativeShellService({
+ isUserChoiceProtectionDriverRunning: () => true,
+ isDefaultBrowser: () => true,
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ true,
+ "Should be true without probing when Firefox is already the default"
+ );
+ Assert.ok(
+ native.canRenameUserChoiceAssociationKey.notCalled,
+ "Should not probe the registry when Firefox is already the default"
+ );
+
+ sinon.restore();
+});
+
+add_task(function test_ucpd_running_rename_blocked() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, true);
+ const native = mockNativeShellService({
+ isUserChoiceProtectionDriverRunning: () => true,
+ canRenameUserChoiceAssociationKey: sinon.stub().returns(false),
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ false,
+ "Should be false when UCPD is running and blocks the registry rename"
+ );
+ Assert.ok(
+ native.canRenameUserChoiceAssociationKey.calledOnceWith("http"),
+ "Should probe the http association"
+ );
+
+ sinon.restore();
+});
+
+add_task(function test_ucpd_running_rename_permitted() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, true);
+ mockNativeShellService({
+ isUserChoiceProtectionDriverRunning: () => true,
+ canRenameUserChoiceAssociationKey: sinon.stub().returns(true),
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ true,
+ "Should be true when UCPD is running but permits the registry rename"
+ );
+
+ sinon.restore();
+});
+
+add_task(function test_reg_rename_disabled() {
+ Services.prefs.setBoolPref(USER_CHOICE_PREF, true);
+ Services.prefs.setBoolPref(REG_RENAME_PREF, false);
+ const native = mockNativeShellService({
+ isUserChoiceProtectionDriverRunning: () => true,
+ canRenameUserChoiceAssociationKey: sinon.stub().returns(true),
+ });
+
+ Assert.strictEqual(
+ ShellService.isOneClickSetDefaultEnabled(),
+ false,
+ "Should be false when the rename pref is false, as renaming is the only " +
+ "way to reach a locked UserChoice key"
+ );
+ Assert.ok(
+ native.canRenameUserChoiceAssociationKey.notCalled,
+ "Should not probe the registry when a successful rename wouldn't be used"
+ );
+
+ sinon.restore();
+ Services.prefs.clearUserPref(REG_RENAME_PREF);
+});
diff --git a/src/zen/tests/mochitests/shell/unit/test_taskbarPinningPolicy.js b/src/zen/tests/mochitests/shell/unit/test_taskbarPinningPolicy.js
new file mode 100644
index 000000000..a24d06f28
--- /dev/null
+++ b/src/zen/tests/mochitests/shell/unit/test_taskbarPinningPolicy.js
@@ -0,0 +1,166 @@
+/* Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { MockRegistry } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistry.sys.mjs"
+);
+
+const EXPLORER_POLICY_KEY = String.raw`Software\Policies\Microsoft\Windows\Explorer`;
+const POLICY_LOG_PREFIX = "Pinning disabled by policy";
+
+const HIVES = {
+ HKCU: Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ HKLM: Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+};
+const POLICIES = ["NoPinningToTaskbar", "TaskbarNoPinnedList"];
+
+const shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
+ Ci.nsIWindowsShellService
+);
+
+let gRegistry;
+let gLogFile;
+
+add_setup(async function () {
+ // The pinning policy diagnostic only reaches MOZ_LOG, never the console
+ // service, so route the module to a file and read it back.
+ // logging.config.sync flushes each line, so the message is on disk by the
+ // time canPinToTaskbar() returns.
+ const logBase = PathUtils.join(
+ do_get_profile().path,
+ "taskbar_policy_test.log"
+ );
+ gLogFile = `${logBase}-main.${Services.appinfo.processID}.moz_log`;
+
+ Services.prefs.setBoolPref("logging.config.sync", true);
+ Services.prefs.setCharPref("logging.config.LOG_FILE", logBase);
+ Services.prefs.setIntPref("logging.shell_windows::taskbar", 3);
+
+ gRegistry = new MockRegistry();
+
+ registerCleanupFunction(async () => {
+ gRegistry.shutdown();
+ Services.prefs.clearUserPref("logging.shell_windows::taskbar");
+ Services.prefs.clearUserPref("logging.config.LOG_FILE");
+ Services.prefs.clearUserPref("logging.config.sync");
+ await IOUtils.remove(gLogFile, { ignoreAbsent: true });
+ });
+});
+
+async function readLog() {
+ if (!(await IOUtils.exists(gLogFile))) {
+ return "";
+ }
+ return IOUtils.readUTF8(gLogFile);
+}
+
+function clearPolicies() {
+ for (const root of Object.values(HIVES)) {
+ for (const policy of POLICIES) {
+ gRegistry.setValue(root, EXPLORER_POLICY_KEY, policy, null);
+ }
+ }
+}
+
+function setPolicy(hive, policy, value) {
+ clearPolicies();
+ gRegistry.setValue(
+ HIVES[hive],
+ EXPLORER_POLICY_KEY,
+ policy,
+ value,
+ Ci.nsIWindowsRegKey.TYPE_INT
+ );
+}
+
+// Calls canPinToTaskbar and returns what it threw (if anything) along with the
+// log output it produced. The log file accumulates across tasks, so only the
+// appended portion is returned.
+async function callCanPinToTaskbar() {
+ const offset = (await readLog()).length;
+
+ let error = null;
+ try {
+ shellService.canPinToTaskbar();
+ } catch (e) {
+ error = e;
+ }
+
+ let log = (await readLog()).slice(offset);
+
+ return { error, log };
+}
+
+// Set of hive/group policy combinations known to block pinning.
+const BLOCKING_POLICIES = [
+ ["HKCU", "NoPinningToTaskbar"],
+ ["HKCU", "TaskbarNoPinnedList"],
+ ["HKLM", "TaskbarNoPinnedList"],
+];
+
+add_task(async function test_policiesBlockPinning() {
+ for (const [hive, policy] of BLOCKING_POLICIES) {
+ info(`Testing ${policy} in ${hive}`);
+ setPolicy(hive, policy, 1);
+
+ const { error, log } = await callCanPinToTaskbar();
+
+ Assert.equal(
+ error?.result,
+ Cr.NS_ERROR_NOT_AVAILABLE,
+ `canPinToTaskbar threw NS_ERROR_NOT_AVAILABLE for ${policy} in ${hive}`
+ );
+ Assert.stringContains(
+ log,
+ `${POLICY_LOG_PREFIX} ${policy} in hive ${hive}.`,
+ `The blocking policy ${policy} in ${hive} was logged`
+ );
+ }
+});
+
+// Policies that apply in other hives, but not the ones paired here.
+const INVALID_HIVE_FOR_POLICY = [["HKLM", "NoPinningToTaskbar"]];
+
+add_task(async function test_invalidPoliciesDoNotBlockPinning() {
+ for (const [hive, policy] of INVALID_HIVE_FOR_POLICY) {
+ info(`Testing ${policy} in ${hive}`);
+ setPolicy(hive, policy, 1);
+
+ // Whether pinning is actually available depends on the host machine, so only
+ // the absence of the policy diagnostic is asserted here.
+ const { log } = await callCanPinToTaskbar();
+
+ Assert.ok(
+ !log.includes(POLICY_LOG_PREFIX),
+ `The policy ${policy} in ${hive} is invalid and should not block pinning`
+ );
+ }
+});
+
+add_task(async function test_policySetToZeroDoesNotBlockPinning() {
+ setPolicy("HKCU", "NoPinningToTaskbar", 0);
+
+ // Whether pinning is actually available depends on the host machine, so only
+ // the absence of the policy diagnostic is asserted here.
+ const { log } = await callCanPinToTaskbar();
+
+ Assert.ok(
+ !log.includes(POLICY_LOG_PREFIX),
+ "A policy present but set to zero does not block pinning"
+ );
+});
+
+add_task(async function test_noPoliciesSet() {
+ clearPolicies();
+
+ // Whether pinning is actually available depends on the host machine, so only
+ // the absence of the policy diagnostic is asserted here.
+ const { log } = await callCanPinToTaskbar();
+
+ Assert.ok(
+ !log.includes(POLICY_LOG_PREFIX),
+ "No policy is reported when none are set"
+ );
+});
diff --git a/src/zen/tests/mochitests/shell/unit/xpcshell.toml b/src/zen/tests/mochitests/shell/unit/xpcshell.toml
index 106c8565a..d5f25a0c4 100644
--- a/src/zen/tests/mochitests/shell/unit/xpcshell.toml
+++ b/src/zen/tests/mochitests/shell/unit/xpcshell.toml
@@ -30,6 +30,11 @@ support-files = [
]
tags = ["portal"]
+["test_isOneClickSetDefaultEnabled.js"]
+run-if = [
+ "os == 'win'",
+]
+
["test_linuxDesktopEntry.js"]
run-if = [
"os == 'linux'",
@@ -45,6 +50,11 @@ run-if = [
"os == 'win'"
]
+["test_taskbarPinningPolicy.js"]
+run-if = [
+ "os == 'win'",
+]
+
["test_writeShortcutIcon.js"]
run-if = [
"os == 'win'",
diff --git a/src/zen/tests/mochitests/sync/head_helpers.js b/src/zen/tests/mochitests/sync/head_helpers.js
index d9383db21..17a726501 100644
--- a/src/zen/tests/mochitests/sync/head_helpers.js
+++ b/src/zen/tests/mochitests/sync/head_helpers.js
@@ -97,7 +97,10 @@ function loadWebExtensionTestFunctions() {
const path = webExtensionsTestPath("/head_sync.js");
let file = do_get_file(path);
let uri = Services.io.newFileURI(file);
- Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
+ Services.scriptloader.loadSubScriptWithOptions(uri.spec, {
+ target: gGlobalScope,
+ allowUnsafeURL: true,
+ });
}
/**
diff --git a/src/zen/tests/mochitests/sync/test_resource.js b/src/zen/tests/mochitests/sync/test_resource.js
index 5dee57b39..2e983a655 100644
--- a/src/zen/tests/mochitests/sync/test_resource.js
+++ b/src/zen/tests/mochitests/sync/test_resource.js
@@ -47,10 +47,10 @@ function server_404(metadata, response) {
response.bodyOutputStream.write(body, body.length);
}
-var pacFetched = false;
+var pacFetched;
function server_pac(metadata, response) {
_("Invoked PAC handler.");
- pacFetched = true;
+ pacFetched.resolve();
let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.setHeader(
@@ -189,12 +189,13 @@ add_task(async function test_proxy_auth_redirect() {
PACSystemSettings.PACURI = server.baseURI + "/pac2";
installFakePAC();
+ pacFetched = Promise.withResolvers();
let res = new Resource(server.baseURI + "/open");
let result = await res.get();
- Assert.ok(pacFetched);
+ await pacFetched.promise;
Assert.ok(fetched);
Assert.equal("This path exists", result.data);
- pacFetched = fetched = false;
+ fetched = false;
uninstallFakePAC();
await promiseStopServer(server);
});
diff --git a/src/zen/tests/mochitests/tabMediaIndicator/browser.toml b/src/zen/tests/mochitests/tabMediaIndicator/browser.toml
index a725df4df..ee21b0a23 100644
--- a/src/zen/tests/mochitests/tabMediaIndicator/browser.toml
+++ b/src/zen/tests/mochitests/tabMediaIndicator/browser.toml
@@ -35,16 +35,18 @@ https_first_disabled = true
["browser_mute.js"]
-["browser_mute2.js"]
-
["browser_mute_persist_navigation.js"]
["browser_mute_restore_closed_audible_tab.js"]
+["browser_mute2.js"]
+
["browser_mute_webAudio.js"]
["browser_sound_indicator_silent_video.js"]
+["browser_sound_indicator_tab_move.js"]
+
["browser_webAudio_hideSoundPlayingIcon.js"]
["browser_webAudio_silentData.js"]
diff --git a/src/zen/tests/mochitests/tabMediaIndicator/browser_mediaplayback_audibility_change.js b/src/zen/tests/mochitests/tabMediaIndicator/browser_mediaplayback_audibility_change.js
index 6c0c9de2b..4c50c2afe 100644
--- a/src/zen/tests/mochitests/tabMediaIndicator/browser_mediaplayback_audibility_change.js
+++ b/src/zen/tests/mochitests/tabMediaIndicator/browser_mediaplayback_audibility_change.js
@@ -179,31 +179,6 @@ add_task(async function testSoundIndicatorWhenChangingMediaVolume() {
/**
* Following are helper functions
*/
-function initMediaPlaybackDocument(
- tab,
- fileName,
- { preload, createVideo, muted = false, volume = 1.0 } = {}
-) {
- return SpecialPowers.spawn(
- tab.linkedBrowser,
- [fileName, preload, createVideo, muted, volume],
- // eslint-disable-next-line no-shadow
- async (fileName, preload, createVideo, muted, volume) => {
- if (createVideo) {
- content.media = content.document.createElement("video");
- } else {
- content.media = content.document.createElement("audio");
- }
- if (preload) {
- content.media.preload = preload;
- }
- content.media.muted = muted;
- content.media.volume = volume;
- content.media.src = fileName;
- }
- );
-}
-
function initMediaStreamPlaybackDocument(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
content.media = content.document.createElement("audio");
@@ -212,26 +187,6 @@ function initMediaStreamPlaybackDocument(tab) {
});
}
-function playMedia(tab, { resolveOnTimeupdate } = {}) {
- return SpecialPowers.spawn(
- tab.linkedBrowser,
- [resolveOnTimeupdate],
- // eslint-disable-next-line no-shadow
- async resolveOnTimeupdate => {
- await content.media.play();
- if (resolveOnTimeupdate) {
- await new Promise(r => (content.media.ontimeupdate = r));
- }
- }
- );
-}
-
-function pauseMedia(tab) {
- return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
- content.media.pause();
- });
-}
-
function assignNewSourceForAudio(tab, fileName) {
// eslint-disable-next-line no-shadow
return SpecialPowers.spawn(tab.linkedBrowser, [fileName], async fileName => {
diff --git a/src/zen/tests/mochitests/tabMediaIndicator/browser_sound_indicator_tab_move.js b/src/zen/tests/mochitests/tabMediaIndicator/browser_sound_indicator_tab_move.js
new file mode 100644
index 000000000..6731d69e4
--- /dev/null
+++ b/src/zen/tests/mochitests/tabMediaIndicator/browser_sound_indicator_tab_move.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Moving a tab to another window hands its browsing context to a different
+ * browser, which also changes the media controller the tab has to listen to.
+ * For each way a tab can reach another window, these tests check that the sound
+ * indicator still appears when audio starts and still clears when audio stops.
+ */
+
+const MOVE_MODES = [
+ { name: "moving it to a new window", moveTab: moveTabToNewWindow },
+ { name: "adopting it into another window", moveTab: adoptTabIntoOtherWindow },
+];
+
+for (const { name, moveTab } of MOVE_MODES) {
+ add_task(async function testSoundIndicatorAppearsAfterMovingTab() {
+ info("create a tab loading media document");
+ const tab = await createBlankForegroundTab();
+ await initMediaPlaybackDocument(tab, "audio.ogg");
+
+ info(`move the silent tab by ${name}`);
+ const { win, movedTab } = await moveTab(tab);
+
+ info("sound indicator should appear when audible audio starts playing");
+ await playMedia(movedTab);
+ await waitForTabSoundIndicatorAppears(movedTab);
+
+ info("stop media and close window");
+ await pauseMedia(movedTab);
+ await BrowserTestUtils.closeWindow(win);
+ });
+
+ add_task(async function testSoundIndicatorDisappearsAfterMovingTab() {
+ info("create a tab loading media document");
+ const tab = await createBlankForegroundTab();
+ await initMediaPlaybackDocument(tab, "audio.ogg");
+
+ info("sound indicator should appear when audible audio starts playing");
+ await playMedia(tab);
+ await waitForTabSoundIndicatorAppears(tab);
+
+ info(`move the audible tab by ${name}`);
+ const { win, movedTab } = await moveTab(tab);
+ ok(movedTab.soundPlaying, "Tab sound indicator is kept after moving");
+
+ info("sound indicator should disappear when audio stops playing");
+ await pauseMedia(movedTab);
+ await waitForTabSoundIndicatorDisappears(movedTab);
+
+ info("close window");
+ await BrowserTestUtils.closeWindow(win);
+ });
+}
+
+// Following are helper functions.
+
+async function moveTabToNewWindow(tab) {
+ const win = gBrowser.replaceTabWithWindow(tab);
+ await TestUtils.topicObserved(
+ "browser-delayed-startup-finished",
+ subject => subject == win
+ );
+ return { win, movedTab: win.gBrowser.selectedTab };
+}
+
+async function adoptTabIntoOtherWindow(tab) {
+ const win = await BrowserTestUtils.openNewBrowserWindow();
+ const movedTab = win.gBrowser.adoptTab(tab, { selectTab: true });
+ if (!movedTab) {
+ throw new Error("adoptTab refused to swap the tab into the other window");
+ }
+ return { win, movedTab };
+}
diff --git a/src/zen/tests/mochitests/tabMediaIndicator/head.js b/src/zen/tests/mochitests/tabMediaIndicator/head.js
index 8d58339a2..ce58e50ee 100644
--- a/src/zen/tests/mochitests/tabMediaIndicator/head.js
+++ b/src/zen/tests/mochitests/tabMediaIndicator/head.js
@@ -12,8 +12,8 @@ const gEMPTY_PAGE_URL = GetTestWebBasedURL("file_empty.html");
* if set, then return a url with different origin
*/
function GetTestWebBasedURL(fileName, cors = false) {
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
const origin = cors ? "http://example.org" : "http://example.com";
return (
getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
@@ -163,3 +163,52 @@ function disableNonTestMouse(disable) {
let utils = window.windowUtils;
utils.disableNonTestMouseEvents(disable);
}
+
+/**
+ * Create a media element in the given tab and point it at fileName. The element
+ * is stored on the content window as `content.media` for playMedia/pauseMedia.
+ */
+function initMediaPlaybackDocument(
+ tab,
+ fileName,
+ { preload, createVideo, muted = false, volume = 1.0 } = {}
+) {
+ return SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [fileName, preload, createVideo, muted, volume],
+ // eslint-disable-next-line no-shadow
+ async (fileName, preload, createVideo, muted, volume) => {
+ if (createVideo) {
+ content.media = content.document.createElement("video");
+ } else {
+ content.media = content.document.createElement("audio");
+ }
+ if (preload) {
+ content.media.preload = preload;
+ }
+ content.media.muted = muted;
+ content.media.volume = volume;
+ content.media.src = fileName;
+ }
+ );
+}
+
+function playMedia(tab, { resolveOnTimeupdate } = {}) {
+ return SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [resolveOnTimeupdate],
+ // eslint-disable-next-line no-shadow
+ async resolveOnTimeupdate => {
+ await content.media.play();
+ if (resolveOnTimeupdate) {
+ await new Promise(r => (content.media.ontimeupdate = r));
+ }
+ }
+ );
+}
+
+function pauseMedia(tab) {
+ return SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
+ content.media.pause();
+ });
+}
diff --git a/src/zen/tests/mochitests/zoom/browser_background_link_zoom_reset.js b/src/zen/tests/mochitests/zoom/browser_background_link_zoom_reset.js
index ac224cc4a..a0692bcf3 100644
--- a/src/zen/tests/mochitests/zoom/browser_background_link_zoom_reset.js
+++ b/src/zen/tests/mochitests/zoom/browser_background_link_zoom_reset.js
@@ -39,7 +39,7 @@ function test() {
(async function () {
gTestTab = BrowserTestUtils.addTab(gBrowser);
await FullZoomHelper.selectTabAndWaitForLocationChange(gTestTab);
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
await FullZoomHelper.load(gTestTab, "http://example.org" + TEST_PAGE);
})().then(testInitialZoom, FullZoomHelper.failAndContinue(finish));
}
diff --git a/src/zen/tests/mochitests/zoom/browser_background_zoom.js b/src/zen/tests/mochitests/zoom/browser_background_zoom.js
index d757d8e7b..10754e294 100644
--- a/src/zen/tests/mochitests/zoom/browser_background_zoom.js
+++ b/src/zen/tests/mochitests/zoom/browser_background_zoom.js
@@ -1,8 +1,8 @@
var gTestPage =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/zoom/zoom_test.html";
var gTestImage =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/general/moz.png";
var gTab1, gTab2, gTab3;
var gLevel;
diff --git a/src/zen/tests/mochitests/zoom/browser_default_zoom_multitab.js b/src/zen/tests/mochitests/zoom/browser_default_zoom_multitab.js
index d20402088..7dea43973 100644
--- a/src/zen/tests/mochitests/zoom/browser_default_zoom_multitab.js
+++ b/src/zen/tests/mochitests/zoom/browser_default_zoom_multitab.js
@@ -4,9 +4,9 @@
"use strict";
add_task(async function test_multidomain_global_zoom() {
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
const TEST_PAGE_URL_1 = "http://example.com/";
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
const TEST_PAGE_URL_2 = "http://example.org/";
// Prepare the test tabs
@@ -95,9 +95,9 @@ add_task(async function test_multidomain_global_zoom() {
});
add_task(async function test_site_specific_global_zoom() {
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
const TEST_PAGE_URL_1 = "http://example.net/";
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
const TEST_PAGE_URL_2 = "http://example.net/";
// Prepare the test tabs
diff --git a/src/zen/tests/mochitests/zoom/browser_image_zoom_tabswitch.js b/src/zen/tests/mochitests/zoom/browser_image_zoom_tabswitch.js
index b2357d813..84862abe3 100644
--- a/src/zen/tests/mochitests/zoom/browser_image_zoom_tabswitch.js
+++ b/src/zen/tests/mochitests/zoom/browser_image_zoom_tabswitch.js
@@ -6,7 +6,7 @@
function test() {
let tab1, tab2;
const TEST_IMAGE =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/general/moz.png";
waitForExplicitFinish();
diff --git a/src/zen/tests/mochitests/zoom/browser_mousewheel_zoom.js b/src/zen/tests/mochitests/zoom/browser_mousewheel_zoom.js
index a814f1dba..8ccbf3457 100644
--- a/src/zen/tests/mochitests/zoom/browser_mousewheel_zoom.js
+++ b/src/zen/tests/mochitests/zoom/browser_mousewheel_zoom.js
@@ -4,7 +4,7 @@
"use strict";
const TEST_PAGE =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/zoom/zoom_test.html";
var gTab1, gTab2, gLevel1;
diff --git a/src/zen/tests/mochitests/zoom/browser_sitespecific_background_pref.js b/src/zen/tests/mochitests/zoom/browser_sitespecific_background_pref.js
index 5756c4d8d..81440fde5 100644
--- a/src/zen/tests/mochitests/zoom/browser_sitespecific_background_pref.js
+++ b/src/zen/tests/mochitests/zoom/browser_sitespecific_background_pref.js
@@ -3,7 +3,7 @@ function test() {
(async function () {
let testPage =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/zoom/zoom_test.html";
let tab1 = BrowserTestUtils.addTab(gBrowser);
await FullZoomHelper.selectTabAndWaitForLocationChange(tab1);
diff --git a/src/zen/tests/mochitests/zoom/browser_sitespecific_video_zoom.js b/src/zen/tests/mochitests/zoom/browser_sitespecific_video_zoom.js
index 94fd0dee5..6f3ebc873 100644
--- a/src/zen/tests/mochitests/zoom/browser_sitespecific_video_zoom.js
+++ b/src/zen/tests/mochitests/zoom/browser_sitespecific_video_zoom.js
@@ -4,10 +4,10 @@
"use strict";
const TEST_PAGE =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/zoom/zoom_test.html";
const TEST_VIDEO =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/general/video.webm";
var gTab1, gTab2, gLevel1;
diff --git a/src/zen/tests/mochitests/zoom/browser_subframe_textzoom.js b/src/zen/tests/mochitests/zoom/browser_subframe_textzoom.js
index e5d40cf58..c456feaea 100644
--- a/src/zen/tests/mochitests/zoom/browser_subframe_textzoom.js
+++ b/src/zen/tests/mochitests/zoom/browser_subframe_textzoom.js
@@ -11,7 +11,7 @@ function test() {
waitForExplicitFinish();
const TEST_PAGE_URL = 'data:text/html,';
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
const TEST_IFRAME_URL = "http://test2.example.org/";
(async function () {
diff --git a/src/zen/tests/mochitests/zoom/browser_tabswitch_zoom_flicker.js b/src/zen/tests/mochitests/zoom/browser_tabswitch_zoom_flicker.js
index df1c3816a..6d5d12981 100644
--- a/src/zen/tests/mochitests/zoom/browser_tabswitch_zoom_flicker.js
+++ b/src/zen/tests/mochitests/zoom/browser_tabswitch_zoom_flicker.js
@@ -10,7 +10,7 @@ function test() {
Services.prefs.setBoolPref("browser.zoom.siteSpecific", true);
let uri =
- // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ // eslint-disable-next-line sdl/no-insecure-url
"http://example.org/browser/browser/base/content/test/zoom/zoom_test.html";
(async function () {
diff --git a/surfer.json b/surfer.json
index 340cc61c7..f144a9df1 100644
--- a/surfer.json
+++ b/surfer.json
@@ -5,7 +5,7 @@
"binaryName": "zen",
"version": {
"product": "firefox",
- "version": "154.0.1",
+ "version": "155.0",
"candidate": "155.0",
"candidateBuild": 1
},