diff --git a/README.md b/README.md
index 17eea24ba..7d1ae5931 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 `153.0`! 🚀
+- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `153.0.1`! 🚀
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 153.0`!
### Contributing
diff --git a/build/firefox-cache/l10n-last-commit-hash b/build/firefox-cache/l10n-last-commit-hash
index 4e1b64816..c712cf24e 100644
--- a/build/firefox-cache/l10n-last-commit-hash
+++ b/build/firefox-cache/l10n-last-commit-hash
@@ -1 +1 @@
-b1d3fe8f65751e6d695fa9ef94b11ad81e70dac1
\ No newline at end of file
+30959a45eaaf0bc499f3054793c76c6d7f0c8bc2
\ No newline at end of file
diff --git a/src/zen/tests/mochitests/shell/browser.toml b/src/zen/tests/mochitests/shell/browser.toml
index 79e0e5b60..b3c5bb1ca 100644
--- a/src/zen/tests/mochitests/shell/browser.toml
+++ b/src/zen/tests/mochitests/shell/browser.toml
@@ -118,7 +118,19 @@ run-if = [
]
tags = "os_integration"
+["browser_setDefaultProtocolHandler.js"]
+run-if = [
+ "os == 'win'",
+]
+tags = "os_integration"
+
["browser_setDesktopBackgroundPreview.js"]
disabled="Disabled by import_external_tests.py"
-support-files = ["large.png"]
+support-files = ["large.png", "canvas.html"]
+tags = "os_integration"
+
+["browser_windowsSetDefaultAppCmdHandler.js"]
+run-if = [
+ "os == 'win'",
+]
tags = "os_integration"
diff --git a/src/zen/tests/mochitests/shell/browser_headless_screenshot_1.js b/src/zen/tests/mochitests/shell/browser_headless_screenshot_1.js
index b5f84f0f6..798c37d45 100644
--- a/src/zen/tests/mochitests/shell/browser_headless_screenshot_1.js
+++ b/src/zen/tests/mochitests/shell/browser_headless_screenshot_1.js
@@ -71,4 +71,10 @@ add_task(async function () {
],
screenshotPath
);
+
+ // Regression test for bug 2003265, likely to become flaky if it regresses.
+ await testFileCreationPositive(
+ ["-url", "about:blank", "-screenshot", screenshotPath],
+ screenshotPath
+ );
});
diff --git a/src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js b/src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
index 04cc1f069..3ffa6cb57 100644
--- a/src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
+++ b/src/zen/tests/mochitests/shell/browser_setDefaultPDFHandler.js
@@ -8,6 +8,9 @@ ChromeUtils.defineESModuleGetters(this, {
sinon: "resource://testing-common/Sinon.sys.mjs",
});
+const confusedFoxPath = ShellService.getBundledPdfFile("confused_fox.pdf").path;
+const SET_HANDLER_WIN11 = Ci.nsIWindowsShellService.OPEN_WITH_SET_HANDLER;
+
const setDefaultBrowserUserChoiceStub = sinon.stub();
const setDefaultExtensionHandlersUserChoiceStub = sinon
.stub()
@@ -34,17 +37,15 @@ const _userChoiceImpossibleTelemetryResultStub = sinon
const setDefaultStub = sinon.stub();
// We'll dynamically update this as needed during the tests.
const queryCurrentDefaultHandlerForStub = sinon.stub();
-const launchOpenWithDefaultPickerForFileTypeStub = sinon.stub();
+const launchSetDefaultAppPickerStub = sinon.stub();
const launchModernSettingsDialogDefaultAppsStub = sinon.stub();
const shellStub = sinon.stub(ShellService, "shellService").value({
setDefaultBrowser: setDefaultStub,
queryCurrentDefaultHandlerFor: queryCurrentDefaultHandlerForStub,
- QueryInterface: () => ({
- launchOpenWithDefaultPickerForFileType:
- launchOpenWithDefaultPickerForFileTypeStub,
- launchModernSettingsDialogDefaultApps:
- launchModernSettingsDialogDefaultAppsStub,
- }),
+ launchSetDefaultAppPicker: launchSetDefaultAppPickerStub,
+ launchModernSettingsDialogDefaultApps:
+ launchModernSettingsDialogDefaultAppsStub,
+ QueryInterface: ChromeUtils.generateQI([]),
});
registerCleanupFunction(() => {
@@ -228,7 +229,7 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
const expectedArguments = [aumi, [".pdf", "FirefoxPDF"]];
const resetStubs = () => {
setDefaultExtensionHandlersUserChoiceStub.resetHistory();
- launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
+ launchSetDefaultAppPickerStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
};
@@ -250,7 +251,7 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
"Called default browser agent with expected arguments"
);
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.notCalled,
+ launchSetDefaultAppPickerStub.notCalled,
"Did not fall back to open-with picker"
);
Assert.ok(
@@ -271,7 +272,7 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
"Called default browser agent with expected arguments"
);
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.notCalled,
+ launchSetDefaultAppPickerStub.notCalled,
"Did not fall back to open-with picker"
);
Assert.ok(
@@ -289,7 +290,7 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
"Did not use userChoice"
);
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.notCalled,
+ launchSetDefaultAppPickerStub.notCalled,
"Did not fall back to open-with picker"
);
Assert.ok(
@@ -310,7 +311,7 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
"Called default browser agent with expected arguments"
);
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.notCalled,
+ launchSetDefaultAppPickerStub.notCalled,
"Did not fall back to open-with picker"
);
Assert.ok(
@@ -365,8 +366,11 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
Assert.ok(userChoiceStub.called, "Attempted userChoice");
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.calledWith(".pdf"),
- "Fell back to open-with picker for .pdf"
+ launchSetDefaultAppPickerStub.calledWith(
+ confusedFoxPath,
+ SET_HANDLER_WIN11
+ ),
+ "Fell back to open-with picker with bundled PDF path and Win11 flag"
);
Assert.ok(
launchModernSettingsDialogDefaultAppsStub.notCalled,
@@ -392,7 +396,7 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
);
userChoiceStub.resetHistory();
isDefaultHandlerForStub.resetHistory();
- launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
+ launchSetDefaultAppPickerStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
info(
@@ -413,22 +417,25 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
isDefaultHandlerForStub.returns(true);
userChoiceStub.resetHistory();
isDefaultHandlerForStub.resetHistory();
- launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
+ launchSetDefaultAppPickerStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
info(
"When userChoice fails and open-with picker fails, should fall back to settings dialog"
);
Services.fog.testResetFOG();
- launchOpenWithDefaultPickerForFileTypeStub.throws(
+ launchSetDefaultAppPickerStub.throws(
new Error("mock IOpenWithLauncher failure")
);
await ShellService.setAsDefaultPDFHandler(false);
Assert.ok(userChoiceStub.called, "Attempted userChoice");
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.calledWith(".pdf"),
- "Attempted open-with picker for .pdf"
+ launchSetDefaultAppPickerStub.calledWith(
+ confusedFoxPath,
+ SET_HANDLER_WIN11
+ ),
+ "Attempted open-with picker with bundled PDF path and Win11 flag"
);
Assert.ok(
launchModernSettingsDialogDefaultAppsStub.called,
@@ -463,7 +470,7 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
);
userChoiceStub.resetHistory();
isDefaultHandlerForStub.resetHistory();
- launchOpenWithDefaultPickerForFileTypeStub.resetHistory();
+ launchSetDefaultAppPickerStub.resetHistory();
launchModernSettingsDialogDefaultAppsStub.resetHistory();
info(
@@ -499,7 +506,7 @@ add_task(async function test_setAsDefaultPDFHandler_fallback() {
"Event result_is_default is false when no method set the default"
);
} finally {
- launchOpenWithDefaultPickerForFileTypeStub.reset();
+ launchSetDefaultAppPickerStub.reset();
launchModernSettingsDialogDefaultAppsStub.reset();
sandbox.restore();
await SpecialPowers.popPrefEnv();
@@ -529,7 +536,7 @@ add_task(async function test_setAsDefaultPDFHandler_useOpenWithDisabled() {
await ShellService.setAsDefaultPDFHandler(false);
Assert.ok(
- launchOpenWithDefaultPickerForFileTypeStub.notCalled,
+ launchSetDefaultAppPickerStub.notCalled,
"Did not invoke open-with picker when pref is disabled"
);
Assert.ok(
@@ -550,7 +557,7 @@ add_task(async function test_setAsDefaultPDFHandler_useOpenWithDisabled() {
"Event result_is_default reflects isDefaultHandlerFor"
);
} finally {
- launchOpenWithDefaultPickerForFileTypeStub.reset();
+ launchSetDefaultAppPickerStub.reset();
launchModernSettingsDialogDefaultAppsStub.reset();
sandbox.restore();
await SpecialPowers.popPrefEnv();
diff --git a/src/zen/tests/mochitests/shell/browser_setDefaultProtocolHandler.js b/src/zen/tests/mochitests/shell/browser_setDefaultProtocolHandler.js
new file mode 100644
index 000000000..cc4c5275c
--- /dev/null
+++ b/src/zen/tests/mochitests/shell/browser_setDefaultProtocolHandler.js
@@ -0,0 +1,321 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ChromeUtils.defineESModuleGetters(this, {
+ DEFAULT_PROTOCOL_URLS:
+ "moz-src:///browser/components/shell/ShellService.sys.mjs",
+ SET_DEFAULT_REDIRECT_PREF:
+ "moz-src:///browser/components/shell/WindowsSetDefaultRedirect.sys.mjs",
+ WindowsSetDefaultRedirect:
+ "moz-src:///browser/components/shell/WindowsSetDefaultRedirect.sys.mjs",
+ sinon: "resource://testing-common/Sinon.sys.mjs",
+});
+
+// Everything here is Windows.
+Assert.equal(AppConstants.platform, "win", "Platform is Windows");
+
+// Protocol defaults carry OPEN_WITH_PROTOCOL_MESSAGING on top of the
+// set-handler flag, unlike the file-type flags in browser_setDefaultPDFHandler.
+const SET_PROTOCOL_WIN11 =
+ Ci.nsIWindowsShellService.OPEN_WITH_SET_HANDLER |
+ Ci.nsIWindowsShellService.OPEN_WITH_PROTOCOL_MESSAGING;
+const SET_PROTOCOL_WIN10 =
+ Ci.nsIWindowsShellService.OPEN_WITH_SET_HANDLER_WIN10 |
+ Ci.nsIWindowsShellService.OPEN_WITH_PROTOCOL_MESSAGING;
+
+const launchSetDefaultAppPickerStub = sinon.stub();
+const launchModernSettingsDialogDefaultAppsStub = sinon.stub();
+const shellStub = sinon.stub(ShellService, "shellService").value({
+ launchSetDefaultAppPicker: launchSetDefaultAppPickerStub,
+ launchModernSettingsDialogDefaultApps:
+ launchModernSettingsDialogDefaultAppsStub,
+ QueryInterface: ChromeUtils.generateQI([]),
+});
+
+registerCleanupFunction(() => {
+ shellStub.restore();
+ Services.prefs.clearUserPref(SET_DEFAULT_REDIRECT_PREF);
+});
+
+function resetState() {
+ launchSetDefaultAppPickerStub.reset();
+ launchModernSettingsDialogDefaultAppsStub.reset();
+ Services.prefs.clearUserPref(SET_DEFAULT_REDIRECT_PREF);
+}
+
+function readPrefObject() {
+ return JSON.parse(
+ Services.prefs.getStringPref(SET_DEFAULT_REDIRECT_PREF, "")
+ );
+}
+
+add_task(async function test_default_https_uses_url_and_win11_flag() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+
+ try {
+ await ShellService.setAsDefaultProtocolHandler("https");
+
+ Assert.ok(
+ launchSetDefaultAppPickerStub.calledOnce,
+ "Picker invoked once for the https default"
+ );
+ Assert.deepEqual(
+ launchSetDefaultAppPickerStub.firstCall.args,
+ [DEFAULT_PROTOCOL_URLS.https, SET_PROTOCOL_WIN11],
+ "Picker called with https URL and SET_HANDLER | PROTOCOL_MESSAGING"
+ );
+ Assert.ok(
+ launchModernSettingsDialogDefaultAppsStub.notCalled,
+ "Modern settings not invoked when launcher succeeds"
+ );
+ Assert.deepEqual(
+ readPrefObject(),
+ {
+ openWithArg: DEFAULT_PROTOCOL_URLS.https,
+ overrideUri: null,
+ type: WindowsSetDefaultRedirect.TYPE.PROTOCOL,
+ },
+ "Pref records the URL; overrideUri null so the round-trip is suppressed"
+ );
+ } finally {
+ sandbox.restore();
+ }
+});
+
+add_task(async function test_custom_url_overrides_protocol_default() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+
+ try {
+ const customUrl = "https://custom.example.com/handler-check";
+ await ShellService.setAsDefaultProtocolHandler("https", customUrl, true);
+
+ Assert.deepEqual(
+ launchSetDefaultAppPickerStub.firstCall.args,
+ [customUrl, SET_PROTOCOL_WIN11],
+ "Picker called with caller-provided URL, not the protocol default"
+ );
+ Assert.deepEqual(
+ readPrefObject(),
+ {
+ openWithArg: customUrl,
+ overrideUri: DEFAULT_PROTOCOL_URLS.https,
+ type: WindowsSetDefaultRedirect.TYPE.PROTOCOL,
+ },
+ "Pref stashes the custom URL + protocol-default overrideUri for the round-trip"
+ );
+ } finally {
+ sandbox.restore();
+ }
+});
+
+add_task(async function test_win10_uses_set_handler_win10_flag() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(false);
+
+ try {
+ await ShellService.setAsDefaultProtocolHandler("https");
+ Assert.deepEqual(
+ launchSetDefaultAppPickerStub.firstCall.args,
+ [DEFAULT_PROTOCOL_URLS.https, SET_PROTOCOL_WIN10],
+ "Picker called with SET_HANDLER_WIN10 | PROTOCOL_MESSAGING when not on Win11"
+ );
+ } finally {
+ sandbox.restore();
+ }
+});
+
+add_task(async function test_falls_back_to_settings_when_picker_throws() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+ launchSetDefaultAppPickerStub.throws(new Error("mock launcher failure"));
+
+ try {
+ await ShellService.setAsDefaultProtocolHandler("https", undefined, true);
+
+ Assert.ok(launchSetDefaultAppPickerStub.called, "Tried picker first");
+ Assert.ok(
+ launchModernSettingsDialogDefaultAppsStub.called,
+ "Fell through to modern settings when launcher threw"
+ );
+ Assert.ok(
+ !Services.prefs.prefHasUserValue(SET_DEFAULT_REDIRECT_PREF),
+ "Pending redirect cleared on launcher failure so a later round-trip can't pick up stale intent"
+ );
+ } finally {
+ sandbox.restore();
+ }
+});
+
+add_task(async function test_unknown_protocol_with_no_url_throws() {
+ resetState();
+
+ await Assert.rejects(
+ ShellService.setAsDefaultProtocolHandler("notaprotocol"),
+ /No URL provided and no DEFAULT_PROTOCOL_URLS fallback/,
+ "Throws when neither URL nor protocol fallback resolves to a URL"
+ );
+ Assert.ok(
+ launchSetDefaultAppPickerStub.notCalled,
+ "Picker not invoked when args don't resolve to a URL"
+ );
+ Assert.ok(
+ !Services.prefs.prefHasUserValue(SET_DEFAULT_REDIRECT_PREF),
+ "Pref not touched when validation fails before any side effects"
+ );
+});
+
+// Wait for the deferred set_default_protocol_handler_attempt event to be
+// recorded, then return the single event emitted by the most recent call.
+async function awaitAttemptEvent() {
+ await TestUtils.waitForCondition(() => {
+ const events =
+ Glean.browser.setDefaultProtocolHandlerAttempt.testGetValue();
+ return events && events.length;
+ }, "Recorded set_default_protocol_handler_attempt event");
+ const events = Glean.browser.setDefaultProtocolHandlerAttempt.testGetValue();
+ Assert.equal(events.length, 1, "Recorded exactly one attempt event");
+ return events[0];
+}
+
+add_task(async function test_telemetry_records_open_with_success() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+ const isDefaultHandlerForStub = sandbox
+ .stub(ShellService, "isDefaultHandlerFor")
+ .returns(true);
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.shell.setDefaultProtocolHandler.attemptWaitTimeMs", 0]],
+ });
+
+ try {
+ Services.fog.testResetFOG();
+ await ShellService.setAsDefaultProtocolHandler("https");
+
+ const 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.protocol, "https", "Event protocol is https");
+ Assert.equal(
+ event.extra.result_is_default,
+ "true",
+ "Event result_is_default reflects isDefaultHandlerFor"
+ );
+ Assert.ok(
+ isDefaultHandlerForStub.calledWith("https"),
+ "Sampled isDefaultHandlerFor with the protocol"
+ );
+ } finally {
+ sandbox.restore();
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+add_task(async function test_telemetry_records_settings_fallback() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+ sandbox.stub(ShellService, "isDefaultHandlerFor").returns(true);
+ launchSetDefaultAppPickerStub.throws(new Error("mock launcher failure"));
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.shell.setDefaultProtocolHandler.attemptWaitTimeMs", 0]],
+ });
+
+ try {
+ Services.fog.testResetFOG();
+ await ShellService.setAsDefaultProtocolHandler("mailto");
+
+ Assert.equal(
+ Glean.browser.setDefaultProtocolHandlerModernSettingsResult.Success.testGetValue(),
+ 1,
+ "Recorded modern settings success"
+ );
+
+ const event = await awaitAttemptEvent();
+ Assert.equal(
+ 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.protocol, "mailto", "Event protocol is mailto");
+ } finally {
+ sandbox.restore();
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+add_task(async function test_telemetry_records_complete_failure() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+ sandbox.stub(ShellService, "isDefaultHandlerFor").returns(false);
+ launchSetDefaultAppPickerStub.throws(new Error("mock launcher failure"));
+ launchModernSettingsDialogDefaultAppsStub.throws(
+ new Error("mock modern settings failure")
+ );
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.shell.setDefaultProtocolHandler.attemptWaitTimeMs", 0]],
+ });
+
+ try {
+ Services.fog.testResetFOG();
+ await ShellService.setAsDefaultProtocolHandler("https");
+
+ Assert.equal(
+ Glean.browser.setDefaultProtocolHandlerModernSettingsResult.Failure.testGetValue(),
+ 1,
+ "Recorded modern settings failure"
+ );
+
+ const event = await awaitAttemptEvent();
+ Assert.equal(event.extra.method, "settings", "Event method is settings");
+ 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 {
+ sandbox.restore();
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+add_task(async function test_overwrites_existing_pending_redirect() {
+ resetState();
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(ShellService, "_isWindows11").returns(true);
+
+ try {
+ // A differently-typed leftover value on the pref must not trip the
+ // setStringPref in WindowsSetDefaultRedirect.arm (it clears first).
+ Services.prefs.setBoolPref(SET_DEFAULT_REDIRECT_PREF, true);
+ await ShellService.setAsDefaultProtocolHandler("https", undefined, false);
+ Assert.deepEqual(
+ readPrefObject(),
+ {
+ openWithArg: DEFAULT_PROTOCOL_URLS.https,
+ overrideUri: null,
+ type: WindowsSetDefaultRedirect.TYPE.PROTOCOL,
+ },
+ "Stale value replaced with the structured redirect on the next call"
+ );
+ } finally {
+ sandbox.restore();
+ }
+});
diff --git a/src/zen/tests/mochitests/shell/browser_setDesktopBackgroundPreview.js b/src/zen/tests/mochitests/shell/browser_setDesktopBackgroundPreview.js
index 733123e04..037d76623 100644
--- a/src/zen/tests/mochitests/shell/browser_setDesktopBackgroundPreview.js
+++ b/src/zen/tests/mochitests/shell/browser_setDesktopBackgroundPreview.js
@@ -3,7 +3,7 @@
/**
* Check whether the preview image for setDesktopBackground is rendered
- * correctly, without stretching
+ * correctly, without stretching, for both
and