tbb-commits
Threads by month
- ----- 2026 -----
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- 1 participants
- 19931 discussions
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
f8f4dbce by Henry Wilkes at 2025-09-09T17:33:53+00:00
fixup! BB 40925: Implemented the Security Level component
TB 43966: Add a warning notification for users when they enter the
custom security level state.
- - - - -
4 changed files:
- browser/components/BrowserComponents.manifest
- browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
- browser/modules/moz.build
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -51,7 +51,7 @@ category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs
#endif
#endif
category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
-category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelNotification.sys.mjs SecurityLevelNotification.ready
category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
=====================================
browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
=====================================
@@ -15,7 +15,7 @@ ChromeUtils.defineLazyGetter(lazy, "NotificationStrings", function () {
/**
* Interface for showing the security level restart notification on desktop.
*/
-export const SecurityLevelRestartNotification = {
+export const SecurityLevelNotification = {
/**
* Whether we have already been initialised.
*
@@ -31,11 +31,13 @@ export const SecurityLevelRestartNotification = {
return;
}
this._initialized = true;
- lazy.SecurityLevelPrefs.setRestartNotificationHandler(this);
+ lazy.SecurityLevelPrefs.setNotificationHandler(this);
},
/**
* Show the restart notification, and perform the restart if the user agrees.
+ *
+ * @returns {boolean} - Whether we are restarting the browser.
*/
async tryRestartBrowser() {
const [titleText, bodyText, primaryButtonText, secondaryButtonText] =
@@ -69,6 +71,49 @@ export const SecurityLevelRestartNotification = {
Services.startup.quit(
Services.startup.eAttemptQuit | Services.startup.eRestart
);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Show or re-show the custom security notification.
+ *
+ * @param {Function} userDismissedCallback - The callback for when the user
+ * dismisses the notification.
+ */
+ async showCustomWarning(userDismissedCallback) {
+ const win = lazy.BrowserWindowTracker.getTopWindow();
+ if (!win) {
+ return;
+ }
+ const typeName = "security-level-custom";
+ const existing = win.gNotificationBox.getNotificationWithValue(typeName);
+ if (existing) {
+ win.gNotificationBox.removeNotification(existing);
}
+
+ const buttons = [
+ {
+ "l10n-id": "security-level-panel-open-settings-button",
+ callback() {
+ win.openPreferences("privacy-securitylevel");
+ },
+ },
+ ];
+
+ win.gNotificationBox.appendNotification(
+ typeName,
+ {
+ label: { "l10n-id": "security-level-summary-custom" },
+ priority: win.gNotificationBox.PRIORITY_WARNING_HIGH,
+ eventCallback: event => {
+ if (event === "dismissed") {
+ userDismissedCallback();
+ }
+ },
+ },
+ buttons
+ );
},
};
=====================================
browser/modules/moz.build
=====================================
@@ -149,7 +149,7 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
"ClipboardPrivacy.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
+ "SecurityLevelNotification.sys.mjs",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -226,7 +226,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.error("Could not apply NoScript settings", e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
waitForExtensionMessage(noscriptID, a => a.__meta.name === "started").then(
@@ -236,7 +236,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.exception(e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
@@ -389,7 +389,12 @@ var initializeSecurityPrefs = function () {
// particular, for the NoScript addon.
Services.prefs.setBoolPref(kCustomPref, false);
Services.prefs.setIntPref(kSliderPref, effectiveIndex);
- } else if (!wasCustom && effectiveIndex !== desiredIndex) {
+ }
+ // Warn the user if they have booted the browser in a custom state, and have
+ // not yet acknowledged it in a previous session.
+ SecurityLevelPrefs.maybeWarnCustom();
+
+ if (!wasCustom && effectiveIndex !== desiredIndex) {
// NOTE: We assume all our controlled preferences require a restart.
// In practice, only a subset of these preferences may actually require a
// restart, so we could switch their values. But we treat them all the same
@@ -436,7 +441,7 @@ var initializeSecurityPrefs = function () {
// properly applied. See tor-browser#43783.
// In the case where it does match a pre-set security level, the custom
// flag will be cleared at the next startup.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}),
});
}
@@ -505,12 +510,27 @@ export class SecurityLevel {
}
/**
- * @typedef {object} SecurityLevelRestartNotificationHandler
+ * @callback SecurityLevelTryRestartBrowserCallback
+ *
+ * @returns {Promise<boolean>} - A promise that resolves when the user has made
+ * a decision. Should return `true` when the browser is now restarting.
+ */
+/**
+ * @callback SecurityLevelShowCustomWarningCallback
*
- * An object that can serve the user a restart notification.
+ * @param {Function} userDismissedCallback - A callback that should be called
+ * if the user has acknowledged and dismissed the notification.
+ */
+/**
+ * @typedef {object} SecurityLevelNotificationHandler
*
- * @property {Function} tryRestartBrowser - The method that should be called to
- * ask the user to restart the browser.
+ * An object that can serve the user notifications.
+ *
+ * @property {SecurityLevelTryRestartBrowserCallback} tryRestartBrowser - The
+ * method that should be called to ask the user to restart the browser.
+ * @property {SecurityLevelShowCustomWarningCallback} showCustomWarning - The
+ * method that should be called to let the user know they have a custom
+ * security level.
*/
/*
@@ -526,6 +546,8 @@ export const SecurityLevelPrefs = {
}),
security_slider_pref: "browser.security_level.security_slider",
security_custom_pref: "browser.security_level.security_custom",
+ _customWarningDismissedPref:
+ "browser.security_level.custom_warning_dismissed",
/**
* The current security level preference.
@@ -581,18 +603,18 @@ export const SecurityLevelPrefs = {
},
/**
- * Whether the browser should be restarted to apply the security level.
+ * The external handler that can show a notification to the user, if any.
*
- * @type {boolean}
+ * @type {?SecurityLevelNotificationHandler}
*/
- _needRestart: false,
+ _notificationHandler: null,
/**
- * The external handler that can show a notification to the user, if any.
+ * The notifications we are waiting for a handler to show.
*
- * @type {?SecurityLevelRestartNotificationHandler}
+ * @type {Set}
*/
- _restartNotificationHandler: null,
+ _pendingNotifications: {},
/**
* Set the external handler for showing notifications to the user.
@@ -600,49 +622,73 @@ export const SecurityLevelPrefs = {
* This should only be called once per session once the handler is ready to
* show a notification, which may occur immediately during this call.
*
- * @param {SecurityLevelRestartNotificationHandler} handler - The new handler
- * to use.
+ * @param {SecurityLevelNotificationHandler} handler - The new handler to use.
*/
- setRestartNotificationHandler(handler) {
+ setNotificationHandler(handler) {
logger.info("Restart notification handler is set");
- this._restartNotificationHandler = handler;
- if (this._needRestart) {
- // Show now using the new handler.
- this._tryShowRestartNotification();
- }
+ this._notificationHandler = handler;
+ this._tryShowNotifications(this._pendingNotifications);
},
/**
* A promise for any ongoing notification prompt task.
*
- * @type {Promise}
+ * Resolves with whether the browser is restarting.
+ *
+ * @type {Promise<boolean>}
*/
_restartNotificationPromise: null,
/**
- * Try show a notification to the user.
+ * Try show notifications to the user.
+ *
+ * If no notification handler has been attached yet, this will queue the
+ * notification for when it is added, if ever.
*
- * If no notification handler has been attached yet, this will do nothing.
+ * @param {object} notifications - The notifications to try and show.
+ * @param {boolean} notifications.restart - Whether to show the restart
+ * notification.
+ * @param {boolean} notifications.custom - Whether to show the custom security
+ * level notification.
*/
- async _tryShowRestartNotification() {
- if (!this._restartNotificationHandler) {
- logger.info("Missing a restart notification handler");
+ async _tryShowNotifications(notifications) {
+ if (!this._notificationHandler) {
+ logger.info("Missing a notification handler", notifications);
// This may be added later in the session.
+ if (notifications.custom) {
+ this._pendingNotifications.custom = true;
+ }
+ if (notifications.restart) {
+ this._pendingNotifications.restart = true;
+ }
return;
}
- const prevPromise = this._restartNotificationPromise;
- let resolve;
- ({ promise: this._restartNotificationPromise, resolve } =
- Promise.withResolvers());
- await prevPromise;
-
- try {
- await this._restartNotificationHandler?.tryRestartBrowser();
- } finally {
- // Allow the notification to be shown again.
- resolve();
+ let isRestarting = false;
+ if (notifications.restart) {
+ const prevPromise = this._restartNotificationPromise;
+ let resolve;
+ ({ promise: this._restartNotificationPromise, resolve } =
+ Promise.withResolvers());
+ await prevPromise;
+
+ try {
+ isRestarting = await this._notificationHandler?.tryRestartBrowser();
+ } finally {
+ // Allow the notification to be shown again.
+ resolve();
+ }
+ }
+ // NOTE: We wait for the restart notification to resolve before showing the
+ // custom warning. We do not show the warning if we are already restarting.
+ if (!isRestarting && notifications.custom) {
+ this._notificationHandler?.showCustomWarning(() => {
+ // User has acknowledged and dismissed the notification.
+ Services.prefs.setBoolPref(this._customWarningDismissedPref, true);
+ });
}
+
+ this._pendingNotifications = {};
},
/**
@@ -658,7 +704,6 @@ export const SecurityLevelPrefs = {
// At the next startup, the custom flag may be cleared if the settings are
// as expected.
Services.prefs.setBoolPref(kCustomPref, true);
- this._needRestart = true;
// NOTE: We need to change the controlled security level preferences in
// response to the desired change in security level. We could either:
@@ -684,6 +729,39 @@ export const SecurityLevelPrefs = {
// still be marked as "custom" because:
// 1. Some preferences require a browser restart to be applied.
// 2. NoScript has not been updated with the new settings.
- this._tryShowRestartNotification();
+
+ this._tryShowNotifications({ restart: true, custom: true });
+ },
+
+ /**
+ * Put the user in the custom security level state and show them a warning
+ * about this state.
+ */
+ setCustomAndWarn() {
+ Services.prefs.setBoolPref(kCustomPref, true);
+ // NOTE: We clear _customWarningDismissedPref because the entry points
+ // for this method imply we should re-warn the user each time.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ this._tryShowNotifications({ custom: true });
+ },
+
+ /**
+ * If the user is in a custom state, try and notify them of this state.
+ */
+ maybeWarnCustom() {
+ const isCustom = Services.prefs.getBoolPref(kCustomPref, false);
+ if (!isCustom) {
+ // Clear the dismissed preference so the user will be re-shown the
+ // notification when they re-enter the custom state.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ return;
+ }
+ if (Services.prefs.getBoolPref(this._customWarningDismissedPref, false)) {
+ // Do not warn the user of the custom state if they have already
+ // acknowledged and dismissed this in a previous session.
+ return;
+ }
+
+ this._tryShowNotifications({ custom: true });
},
}; /* Security Level Prefs */
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f8f4dbc…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/f8f4dbc…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! BB 40925: Implemented the Security Level component
by morgan (@morgan) 09 Sep '25
by morgan (@morgan) 09 Sep '25
09 Sep '25
morgan pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
c1b85777 by Henry Wilkes at 2025-09-09T17:27:56+00:00
fixup! BB 40925: Implemented the Security Level component
TB 43966: Add a warning notification for users when they enter the
custom security level state.
- - - - -
4 changed files:
- browser/components/BrowserComponents.manifest
- browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
- browser/modules/moz.build
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -52,7 +52,7 @@ category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs
#endif
#endif
category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
-category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelNotification.sys.mjs SecurityLevelNotification.ready
category browser-first-window-ready moz-src:///toolkit/modules/DragDropFilter.sys.mjs DragDropFilter.init
category browser-first-window-ready moz-src:///browser/modules/TorSettingsNotification.sys.mjs TorSettingsNotification.ready
category browser-first-window-ready moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs OnionAliasStore.init
=====================================
browser/modules/SecurityLevelRestartNotification.sys.mjs → browser/modules/SecurityLevelNotification.sys.mjs
=====================================
@@ -15,7 +15,7 @@ ChromeUtils.defineLazyGetter(lazy, "NotificationStrings", function () {
/**
* Interface for showing the security level restart notification on desktop.
*/
-export const SecurityLevelRestartNotification = {
+export const SecurityLevelNotification = {
/**
* Whether we have already been initialised.
*
@@ -31,11 +31,13 @@ export const SecurityLevelRestartNotification = {
return;
}
this._initialized = true;
- lazy.SecurityLevelPrefs.setRestartNotificationHandler(this);
+ lazy.SecurityLevelPrefs.setNotificationHandler(this);
},
/**
* Show the restart notification, and perform the restart if the user agrees.
+ *
+ * @returns {boolean} - Whether we are restarting the browser.
*/
async tryRestartBrowser() {
const [titleText, bodyText, primaryButtonText, secondaryButtonText] =
@@ -69,6 +71,49 @@ export const SecurityLevelRestartNotification = {
Services.startup.quit(
Services.startup.eAttemptQuit | Services.startup.eRestart
);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Show or re-show the custom security notification.
+ *
+ * @param {Function} userDismissedCallback - The callback for when the user
+ * dismisses the notification.
+ */
+ async showCustomWarning(userDismissedCallback) {
+ const win = lazy.BrowserWindowTracker.getTopWindow();
+ if (!win) {
+ return;
+ }
+ const typeName = "security-level-custom";
+ const existing = win.gNotificationBox.getNotificationWithValue(typeName);
+ if (existing) {
+ win.gNotificationBox.removeNotification(existing);
}
+
+ const buttons = [
+ {
+ "l10n-id": "security-level-panel-open-settings-button",
+ callback() {
+ win.openPreferences("privacy-securitylevel");
+ },
+ },
+ ];
+
+ win.gNotificationBox.appendNotification(
+ typeName,
+ {
+ label: { "l10n-id": "security-level-summary-custom" },
+ priority: win.gNotificationBox.PRIORITY_WARNING_HIGH,
+ eventCallback: event => {
+ if (event === "dismissed") {
+ userDismissedCallback();
+ }
+ },
+ },
+ buttons
+ );
},
};
=====================================
browser/modules/moz.build
=====================================
@@ -150,7 +150,7 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
"ClipboardPrivacy.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
+ "SecurityLevelNotification.sys.mjs",
"TorSettingsNotification.sys.mjs",
]
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -226,7 +226,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.error("Could not apply NoScript settings", e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
waitForExtensionMessage(noscriptID, a => a.__meta.name === "started").then(
@@ -236,7 +236,7 @@ var initializeNoScriptControl = () => {
} catch (e) {
logger.exception(e);
// Treat as a custom security level for the rest of the session.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}
};
@@ -389,7 +389,12 @@ var initializeSecurityPrefs = function () {
// particular, for the NoScript addon.
Services.prefs.setBoolPref(kCustomPref, false);
Services.prefs.setIntPref(kSliderPref, effectiveIndex);
- } else if (!wasCustom && effectiveIndex !== desiredIndex) {
+ }
+ // Warn the user if they have booted the browser in a custom state, and have
+ // not yet acknowledged it in a previous session.
+ SecurityLevelPrefs.maybeWarnCustom();
+
+ if (!wasCustom && effectiveIndex !== desiredIndex) {
// NOTE: We assume all our controlled preferences require a restart.
// In practice, only a subset of these preferences may actually require a
// restart, so we could switch their values. But we treat them all the same
@@ -436,7 +441,7 @@ var initializeSecurityPrefs = function () {
// properly applied. See tor-browser#43783.
// In the case where it does match a pre-set security level, the custom
// flag will be cleared at the next startup.
- Services.prefs.setBoolPref(kCustomPref, true);
+ SecurityLevelPrefs.setCustomAndWarn();
}),
});
}
@@ -505,12 +510,27 @@ export class SecurityLevel {
}
/**
- * @typedef {object} SecurityLevelRestartNotificationHandler
+ * @callback SecurityLevelTryRestartBrowserCallback
+ *
+ * @returns {Promise<boolean>} - A promise that resolves when the user has made
+ * a decision. Should return `true` when the browser is now restarting.
+ */
+/**
+ * @callback SecurityLevelShowCustomWarningCallback
*
- * An object that can serve the user a restart notification.
+ * @param {Function} userDismissedCallback - A callback that should be called
+ * if the user has acknowledged and dismissed the notification.
+ */
+/**
+ * @typedef {object} SecurityLevelNotificationHandler
*
- * @property {Function} tryRestartBrowser - The method that should be called to
- * ask the user to restart the browser.
+ * An object that can serve the user notifications.
+ *
+ * @property {SecurityLevelTryRestartBrowserCallback} tryRestartBrowser - The
+ * method that should be called to ask the user to restart the browser.
+ * @property {SecurityLevelShowCustomWarningCallback} showCustomWarning - The
+ * method that should be called to let the user know they have a custom
+ * security level.
*/
/*
@@ -526,6 +546,8 @@ export const SecurityLevelPrefs = {
}),
security_slider_pref: "browser.security_level.security_slider",
security_custom_pref: "browser.security_level.security_custom",
+ _customWarningDismissedPref:
+ "browser.security_level.custom_warning_dismissed",
/**
* The current security level preference.
@@ -581,18 +603,18 @@ export const SecurityLevelPrefs = {
},
/**
- * Whether the browser should be restarted to apply the security level.
+ * The external handler that can show a notification to the user, if any.
*
- * @type {boolean}
+ * @type {?SecurityLevelNotificationHandler}
*/
- _needRestart: false,
+ _notificationHandler: null,
/**
- * The external handler that can show a notification to the user, if any.
+ * The notifications we are waiting for a handler to show.
*
- * @type {?SecurityLevelRestartNotificationHandler}
+ * @type {Set}
*/
- _restartNotificationHandler: null,
+ _pendingNotifications: {},
/**
* Set the external handler for showing notifications to the user.
@@ -600,49 +622,73 @@ export const SecurityLevelPrefs = {
* This should only be called once per session once the handler is ready to
* show a notification, which may occur immediately during this call.
*
- * @param {SecurityLevelRestartNotificationHandler} handler - The new handler
- * to use.
+ * @param {SecurityLevelNotificationHandler} handler - The new handler to use.
*/
- setRestartNotificationHandler(handler) {
+ setNotificationHandler(handler) {
logger.info("Restart notification handler is set");
- this._restartNotificationHandler = handler;
- if (this._needRestart) {
- // Show now using the new handler.
- this._tryShowRestartNotification();
- }
+ this._notificationHandler = handler;
+ this._tryShowNotifications(this._pendingNotifications);
},
/**
* A promise for any ongoing notification prompt task.
*
- * @type {Promise}
+ * Resolves with whether the browser is restarting.
+ *
+ * @type {Promise<boolean>}
*/
_restartNotificationPromise: null,
/**
- * Try show a notification to the user.
+ * Try show notifications to the user.
+ *
+ * If no notification handler has been attached yet, this will queue the
+ * notification for when it is added, if ever.
*
- * If no notification handler has been attached yet, this will do nothing.
+ * @param {object} notifications - The notifications to try and show.
+ * @param {boolean} notifications.restart - Whether to show the restart
+ * notification.
+ * @param {boolean} notifications.custom - Whether to show the custom security
+ * level notification.
*/
- async _tryShowRestartNotification() {
- if (!this._restartNotificationHandler) {
- logger.info("Missing a restart notification handler");
+ async _tryShowNotifications(notifications) {
+ if (!this._notificationHandler) {
+ logger.info("Missing a notification handler", notifications);
// This may be added later in the session.
+ if (notifications.custom) {
+ this._pendingNotifications.custom = true;
+ }
+ if (notifications.restart) {
+ this._pendingNotifications.restart = true;
+ }
return;
}
- const prevPromise = this._restartNotificationPromise;
- let resolve;
- ({ promise: this._restartNotificationPromise, resolve } =
- Promise.withResolvers());
- await prevPromise;
-
- try {
- await this._restartNotificationHandler?.tryRestartBrowser();
- } finally {
- // Allow the notification to be shown again.
- resolve();
+ let isRestarting = false;
+ if (notifications.restart) {
+ const prevPromise = this._restartNotificationPromise;
+ let resolve;
+ ({ promise: this._restartNotificationPromise, resolve } =
+ Promise.withResolvers());
+ await prevPromise;
+
+ try {
+ isRestarting = await this._notificationHandler?.tryRestartBrowser();
+ } finally {
+ // Allow the notification to be shown again.
+ resolve();
+ }
+ }
+ // NOTE: We wait for the restart notification to resolve before showing the
+ // custom warning. We do not show the warning if we are already restarting.
+ if (!isRestarting && notifications.custom) {
+ this._notificationHandler?.showCustomWarning(() => {
+ // User has acknowledged and dismissed the notification.
+ Services.prefs.setBoolPref(this._customWarningDismissedPref, true);
+ });
}
+
+ this._pendingNotifications = {};
},
/**
@@ -658,7 +704,6 @@ export const SecurityLevelPrefs = {
// At the next startup, the custom flag may be cleared if the settings are
// as expected.
Services.prefs.setBoolPref(kCustomPref, true);
- this._needRestart = true;
// NOTE: We need to change the controlled security level preferences in
// response to the desired change in security level. We could either:
@@ -684,6 +729,39 @@ export const SecurityLevelPrefs = {
// still be marked as "custom" because:
// 1. Some preferences require a browser restart to be applied.
// 2. NoScript has not been updated with the new settings.
- this._tryShowRestartNotification();
+
+ this._tryShowNotifications({ restart: true, custom: true });
+ },
+
+ /**
+ * Put the user in the custom security level state and show them a warning
+ * about this state.
+ */
+ setCustomAndWarn() {
+ Services.prefs.setBoolPref(kCustomPref, true);
+ // NOTE: We clear _customWarningDismissedPref because the entry points
+ // for this method imply we should re-warn the user each time.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ this._tryShowNotifications({ custom: true });
+ },
+
+ /**
+ * If the user is in a custom state, try and notify them of this state.
+ */
+ maybeWarnCustom() {
+ const isCustom = Services.prefs.getBoolPref(kCustomPref, false);
+ if (!isCustom) {
+ // Clear the dismissed preference so the user will be re-shown the
+ // notification when they re-enter the custom state.
+ Services.prefs.clearUserPref(this._customWarningDismissedPref);
+ return;
+ }
+ if (Services.prefs.getBoolPref(this._customWarningDismissedPref, false)) {
+ // Do not warn the user of the custom state if they have already
+ // acknowledged and dismissed this in a previous session.
+ return;
+ }
+
+ this._tryShowNotifications({ custom: true });
},
}; /* Security Level Prefs */
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c1b8577…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c1b8577…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag FIREFOX_140_3_0esr_BUILD1
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed new tag FIREFOX_140_3_0esr_BUILD1 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/FIREFOX_1…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][maint-14.5] MB 463: Fix typo in MimeType entry in .desktop file included in deb/rpm package
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch maint-14.5 at The Tor Project / Applications / tor-browser-build
Commits:
207612f3 by Nicolas Vigier at 2025-09-09T17:44:21+02:00
MB 463: Fix typo in MimeType entry in .desktop file included in deb/rpm package
- - - - -
1 changed file:
- projects/linux-packages/browser.desktop.in
Changes:
=====================================
projects/linux-packages/browser.desktop.in
=====================================
@@ -10,6 +10,6 @@ Icon=[% c("var/system_pkg/pkg_name") %]
StartupNotify=true
StartupWMClass=[% c("var/display_name") %]
[% IF ! c("var/tor-browser") -%]
-MimeType=text/html;text/xml;application/xhtml_xml;x-scheme-handler/http;x-scheme-handler/https;
+MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
[% END -%]
Terminal=false
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/2…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! TB 41435: Add a Tor Browser migration function
by henry (@henry) 09 Sep '25
by henry (@henry) 09 Sep '25
09 Sep '25
henry pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
86b25200 by Henry Wilkes at 2025-09-09T15:34:21+00:00
fixup! TB 41435: Add a Tor Browser migration function
TB 44180: Clear YEC 2024 preference.
- - - - -
1 changed file:
- browser/components/ProfileDataUpgrader.sys.mjs
Changes:
=====================================
browser/components/ProfileDataUpgrader.sys.mjs
=====================================
@@ -993,7 +993,9 @@ export let ProfileDataUpgrader = {
// (tor-browser#43567).
// Version 8: Tor Browser 15.0a2: Remove legacy search addons
// (tor-browser#43111).
- const TBB_MIGRATION_VERSION = 8;
+ // Version 9: Tor Browser 15.0a3: Remove YEC 2024 preference.
+ // (tor-browser#44180)
+ const TBB_MIGRATION_VERSION = 9;
const MIGRATION_PREF = "torbrowser.migration.version";
// If we decide to force updating users to pass through any version
@@ -1093,6 +1095,9 @@ export let ProfileDataUpgrader = {
"wikipedia(a)search.mozilla.org",
]);
}
+ if (currentVersion < 9) {
+ Services.prefs.clearUserPref("torbrowser.homepage.yec2024.message");
+ }
Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
},
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/86b2520…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/86b2520…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
8998794a by Pier Angelo Vendrame at 2025-09-09T16:00:06+02:00
fixup! Firefox preference overrides.
BB 43959: Switch to Noto Color Emoji on Linux and Windows.
It has better compatibility than Twemoji Mozilla and upstream also did
it in Bug 1939359 (for Linux).
We don't use Segoe UI Emoji on Windows, so we enable Noto Color Emoji
also on Windows.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -883,8 +883,9 @@ pref("font.name-list.monospace.x-unicode", "Menlo, Courier New, Noto Sans Baline
#endif
#ifdef XP_WIN
-pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Twemoji Mozilla, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu");
+pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Noto Color Emoji, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Times New Roman, Noto Naskh Arabic");
pref("font.name-list.sans-serif.ar", "Segoe UI, Tahoma, Arial, Noto Naskh Arabic");
@@ -960,8 +961,9 @@ pref("font.name-list.monospace.x-unicode", "Consolas, Noto Sans Balinese, Noto S
#endif
#ifdef XP_LINUX
-pref("font.system.whitelist", "Arimo, Cousine, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.system.whitelist", "Arimo, Cousine, Noto Color Emoji, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Noto Naskh Arabic, Tinos");
pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Arimo");
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/899…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/899…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
8dcb50e7 by Pier Angelo Vendrame at 2025-09-09T15:59:25+02:00
fixup! Firefox preference overrides.
BB 43959: Switch to Noto Color Emoji on Linux and Windows.
It has better compatibility than Twemoji Mozilla and upstream also did
it in Bug 1939359 (for Linux).
We don't use Segoe UI Emoji on Windows, so we enable Noto Color Emoji
also on Windows.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -887,8 +887,9 @@ pref("font.name-list.monospace.x-unicode", "Menlo, Courier New, Noto Sans Baline
#endif
#ifdef XP_WIN
-pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Twemoji Mozilla, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu");
+pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Noto Color Emoji, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Times New Roman, Noto Naskh Arabic");
pref("font.name-list.sans-serif.ar", "Segoe UI, Tahoma, Arial, Noto Naskh Arabic");
@@ -964,8 +965,9 @@ pref("font.name-list.monospace.x-unicode", "Consolas, Noto Sans Balinese, Noto S
#endif
#ifdef XP_LINUX
-pref("font.system.whitelist", "Arimo, Cousine, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.system.whitelist", "Arimo, Cousine, Noto Color Emoji, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Noto Naskh Arabic, Tinos");
pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Arimo");
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/8dcb50e…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/8dcb50e…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
8cf95921 by Pier Angelo Vendrame at 2025-09-09T15:58:27+02:00
fixup! Firefox preference overrides.
BB 43959: Switch to Noto Color Emoji on Linux and Windows.
It has better compatibility than Twemoji Mozilla and upstream also did
it in Bug 1939359 (for Linux).
We don't use Segoe UI Emoji on Windows, so we enable Noto Color Emoji
also on Windows.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -887,8 +887,9 @@ pref("font.name-list.monospace.x-unicode", "Menlo, Courier New, Noto Sans Baline
#endif
#ifdef XP_WIN
-pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Twemoji Mozilla, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu");
+pref("font.system.whitelist", "Arial, Cambria Math, Consolas, Courier New, Georgia, Lucida Console, MS Gothic, MS ゴシック, MS PGothic, MS Pゴシック, MV Boli, Malgun Gothic, Microsoft Himalaya, Microsoft JhengHei, Microsoft YaHei, 微软雅黑, Segoe UI, SimSun, 宋体, Sylfaen, Tahoma, Times New Roman, Verdana, Noto Color Emoji, Noto Sans Adlam, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Kannada, Noto Sans Khmer, Noto Sans Javanese, Noto Sans Kayah Li, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans Newa, Noto Sans New Tai Lue, Noto Sans NKo, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols 2, Noto Sans Symbols, Noto Sans Syriac, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh APT, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Tibetan, Noto Serif Yezidi, Noto Naskh Arabic, Noto Sans, Noto Serif, Pyidaungsu, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Times New Roman, Noto Naskh Arabic");
pref("font.name-list.sans-serif.ar", "Segoe UI, Tahoma, Arial, Noto Naskh Arabic");
@@ -964,8 +965,9 @@ pref("font.name-list.monospace.x-unicode", "Consolas, Noto Sans Balinese, Noto S
#endif
#ifdef XP_LINUX
-pref("font.system.whitelist", "Arimo, Cousine, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.system.whitelist", "Arimo, Cousine, Noto Color Emoji, Noto Naskh Arabic, Noto Sans Adlam, Noto Sans Armenian, Noto Sans Balinese, Noto Sans Bamum, Noto Sans Bassa Vah, Noto Sans Batak, Noto Sans Bengali, Noto Sans Buginese, Noto Sans Buhid, Noto Sans Canadian Aboriginal, Noto Sans Chakma, Noto Sans Cham, Noto Sans Cherokee, Noto Sans Coptic, Noto Sans Deseret, Noto Sans Devanagari, Noto Sans Elbasan, Noto Sans Ethiopic, Noto Sans Georgian, Noto Sans Grantha, Noto Sans Gujarati, Noto Sans Gunjala Gondi, Noto Sans Gurmukhi, Noto Sans Hanifi Rohingya, Noto Sans Hanunoo, Noto Sans Hebrew, Noto Sans JP, Noto Sans Javanese, Noto Sans KR, Noto Sans Kannada, Noto Sans Kayah Li, Noto Sans Khmer, Noto Sans Khojki, Noto Sans Khudawadi, Noto Sans Lao, Noto Sans Lepcha, Noto Sans Limbu, Noto Sans Lisu, Noto Sans Mahajani, Noto Sans Malayalam, Noto Sans Mandaic, Noto Sans Masaram Gondi, Noto Sans Medefaidrin, Noto Sans Meetei Mayek, Noto Sans Mende Kikakui, Noto Sans Miao, Noto Sans Modi, Noto Sans Mongolian, Noto Sans Mro, Noto Sans Multani, Noto Sans NKo, Noto Sans New Tai Lue, Noto Sans Newa, Noto Sans Ol Chiki, Noto Sans Oriya, Noto Sans Osage, Noto Sans Osmanya, Noto Sans Pahawh Hmong, Noto Sans Pau Cin Hau, Noto Sans Rejang, Noto Sans Runic, Noto Sans SC, Noto Sans Samaritan, Noto Sans Saurashtra, Noto Sans Sharada, Noto Sans Shavian, Noto Sans Sinhala, Noto Sans Sora Sompeng, Noto Sans Soyombo, Noto Sans Sundanese, Noto Sans Syloti Nagri, Noto Sans Symbols, Noto Sans Symbols 2, Noto Sans Syriac, Noto Sans TC, Noto Sans Tagalog, Noto Sans Tagbanwa, Noto Sans Tai Le, Noto Sans Tai Tham, Noto Sans Tai Viet, Noto Sans Takri, Noto Sans Tamil, Noto Sans Telugu, Noto Sans Thaana, Noto Sans Thai, Noto Sans Tifinagh, Noto Sans Tifinagh APT, Noto Sans Tifinagh Adrar, Noto Sans Tifinagh Agraw Imazighen, Noto Sans Tifinagh Ahaggar, Noto Sans Tifinagh Air, Noto Sans Tifinagh Azawagh, Noto Sans Tifinagh Ghat, Noto Sans Tifinagh Hawad, Noto Sans Tifinagh Rhissa Ixa, Noto Sans Tifinagh SIL, Noto Sans Tifinagh Tawellemmet, Noto Sans Tirhuta, Noto Sans Vai, Noto Sans Wancho, Noto Sans Warang Citi, Noto Sans Yi, Noto Sans Zanabazar Square, Noto Serif Armenian, Noto Serif Balinese, Noto Serif Bengali, Noto Serif Devanagari, Noto Serif Dogra, Noto Serif Ethiopic, Noto Serif Georgian, Noto Serif Grantha, Noto Serif Gujarati, Noto Serif Gurmukhi, Noto Serif Hebrew, Noto Serif Kannada, Noto Serif Khmer, Noto Serif Khojki, Noto Serif Lao, Noto Serif Malayalam, Noto Serif Myanmar, Noto Serif NP Hmong, Noto Serif Sinhala, Noto Serif Tamil, Noto Serif Telugu, Noto Serif Thai, Noto Serif Tibetan, Noto Serif Yezidi, Pyidaungsu, STIX Two Math, Tinos, Twemoji Mozilla");
+pref("font.name-list.emoji", "Noto Color Emoji, Twemoji Mozilla");
// Arabic
pref("font.name-list.serif.ar", "Noto Naskh Arabic, Tinos");
pref("font.name-list.sans-serif.ar", "Noto Naskh Arabic, Arimo");
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/8cf9592…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/8cf9592…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser-build][main] Bug 41561: Ship Noto Color Emoji on Linux and Windows.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch main at The Tor Project / Applications / tor-browser-build
Commits:
7be3d4ea by Pier Angelo Vendrame at 2025-09-08T09:35:31+02:00
Bug 41561: Ship Noto Color Emoji on Linux and Windows.
- - - - -
2 changed files:
- projects/fonts/build
- projects/fonts/config
Changes:
=====================================
projects/fonts/build
=====================================
@@ -31,6 +31,10 @@ mv noto-fonts-* noto-fonts
cp Arimo-*/fonts/ttf/*.ttf Cousine-* Tinos-* NotoSans{JP,KR,SC,TC}-Regular.otf $distdir/
[% END %]
+[% IF c("var/linux") || c("var/windows") %]
+ cp NotoColorEmoji.ttf $distdir/
+[% END %]
+
cp "$rootdir/[% c('input_files_by_name/Pyidaungsu') %]" $distdir/
cp README.txt "$distdir/000_README.txt"
=====================================
projects/fonts/config
=====================================
@@ -188,6 +188,9 @@ input_files:
- URL: https://github.com/googlefonts/noto-cjk/raw/523d033d6cb47f4a80c58a35753646f…
sha256sum: 5bab0cb3c1cf89dde07c4a95a4054b195afbcfe784d69d75c340780712237537
enable: '[% c("var/linux") %]'
+ - URL: https://github.com/googlefonts/noto-emoji/raw/b3e3051a088047d19fd4d49b1c3ac…
+ sha256sum: 3ed77810c203e1a67735dc19d395f32c23f2d7c0c3696690f4f78e15e57ab816
+ enable: '[% c("var/linux") || c("var/windows") %]'
- URL: https://github.com/stipub/stixfonts/raw/v2.13b171/fonts/static_otf/STIXTwoM…
sha256sum: 3a5f3f26f40d5698b3c62dd085d48d6663696a3f80825aab8b553d5097518e8c
name: stix
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/7…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/commit/7…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! Add CI for Base Browser
by brizental (@brizental) 09 Sep '25
by brizental (@brizental) 09 Sep '25
09 Sep '25
brizental pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
081b9f68 by Beatriz Rizental at 2025-09-09T13:10:41+02:00
fixup! Add CI for Base Browser
- - - - -
5 changed files:
- .gitlab-ci.yml
- .gitlab/ci/jobs/lint/helpers.py → .gitlab/ci/jobs/helpers.py
- .gitlab/ci/jobs/lint/lint.yml
- + .gitlab/ci/jobs/test/python-test.yml
- .gitlab/ci/jobs/update-translations.yml
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -1,5 +1,6 @@
stages:
- lint
+ - test
- update-translations
variables:
@@ -9,4 +10,5 @@ variables:
include:
- local: '.gitlab/ci/mixins.yml'
- local: '.gitlab/ci/jobs/lint/lint.yml'
+ - local: '.gitlab/ci/jobs/test/python-test.yml'
- local: '.gitlab/ci/jobs/update-translations.yml'
=====================================
.gitlab/ci/jobs/lint/helpers.py → .gitlab/ci/jobs/helpers.py
=====================================
@@ -112,7 +112,7 @@ if __name__ == "__main__":
parser.add_argument(
"--get-changed-files",
help="Get list of changed files."
- "When running from a merge request get sthe list of changed files since the merge-base of the current branch."
+ "When running from a merge request gets the list of changed files since the merge-base of the current branch."
"When running from a protected branch i.e. any branch that starts with <something>-browser-, gets the list of files changed since the FIREFOX_ tag.",
action="store_true",
)
=====================================
.gitlab/ci/jobs/lint/lint.yml
=====================================
@@ -18,7 +18,7 @@ lint-all:
- firefox
script:
- ./mach configure --with-base-browser-version=0.0.0
- - .gitlab/ci/jobs/lint/helpers.py --get-changed-files | xargs -0 --no-run-if-empty ./mach lint -v
+ - .gitlab/ci/jobs/helpers.py --get-changed-files | xargs -0 --no-run-if-empty ./mach lint -v
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
# Run job whenever a commit is merged to a protected branch
=====================================
.gitlab/ci/jobs/test/python-test.yml
=====================================
@@ -0,0 +1,24 @@
+python-test:
+ extends: .with-local-repo-bash
+ stage: test
+ image: $IMAGE_PATH
+ interruptible: true
+ variables:
+ MOZBUILD_STATE_PATH: "/var/tmp/mozbuild"
+ cache:
+ paths:
+ - node_modules
+ # Store the cache regardless on job outcome
+ when: 'always'
+ # Share the cache throughout all pipelines running for a given branch
+ key: $CI_COMMIT_REF_SLUG
+ tags:
+ # Run these jobs in the browser dedicated runners.
+ - firefox
+ script:
+ - ./mach configure --with-base-browser-version=0.0.0
+ - .gitlab/ci/jobs/helpers.py --get-changed-files | xargs -0 --no-run-if-empty ./mach python-test -v
+ rules:
+ - if: $CI_PIPELINE_SOURCE == 'merge_request_event' || ($CI_COMMIT_BRANCH && $CI_COMMIT_REF_PROTECTED == 'true' && $CI_PIPELINE_SOURCE == 'push')
+ changes:
+ - "**/test_*.py"
=====================================
.gitlab/ci/jobs/update-translations.yml
=====================================
@@ -81,7 +81,7 @@ combine-en-US-translations:
# push-en-US-translations job.
- echo 'COMBINE_TRANSLATIONS_JOB_ID='"$CI_JOB_ID" >job_id.env
- pip install compare_locales
- - python ./tools/base-browser/l10n/combine-translation-versions.py "$CI_COMMIT_BRANCH" "$TRANSLATION_FILES" "$COMBINED_FILES_JSON"
+ - python ./tools/base_browser/l10n/combine-translation-versions.py "$CI_COMMIT_BRANCH" "$TRANSLATION_FILES" "$COMBINED_FILES_JSON"
push-en-US-translations:
extends: .update-translation-base
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/081…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/081…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-115.27.0esr-13.5-1] fixup! Bug 43125: Extend the 13.5 EOL expiry date for tor-browser.
by Pier Angelo Vendrame (@pierov) 09 Sep '25
by Pier Angelo Vendrame (@pierov) 09 Sep '25
09 Sep '25
Pier Angelo Vendrame pushed to branch tor-browser-115.27.0esr-13.5-1 at The Tor Project / Applications / Tor Browser
Commits:
55d160b3 by Henry Wilkes at 2025-09-08T17:57:36+01:00
fixup! Bug 43125: Extend the 13.5 EOL expiry date for tor-browser.
TB 43168: Extend the 13.5 EOL to 24th March 2026.
- - - - -
1 changed file:
- browser/base/content/droppedSupportNotification.js
Changes:
=====================================
browser/base/content/droppedSupportNotification.js
=====================================
@@ -3,8 +3,8 @@
// Show a prompt that a user's system will no longer be supported.
window.addEventListener("load", () => {
let labelId;
- // ESR 115 EOL pushed to 14th October 2025.
- const isExpired = Date.now() > Date.UTC(2025, 9, 14);
+ // ESR 115 EOL pushed to 24th March 2026.
+ const isExpired = Date.now() > Date.UTC(2026, 2, 24);
if (
AppConstants.platform === "macosx" &&
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/55d160b…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/55d160b…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser] Pushed new tag tor-browser-143.0a1-16.0-1-build1
by brizental (@brizental) 08 Sep '25
by brizental (@brizental) 08 Sep '25
08 Sep '25
brizental pushed new tag tor-browser-143.0a1-16.0-1-build1 at The Tor Project / Applications / Tor Browser
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/tree/tor-brows…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] 6 commits: BB 43564: Modify ./mach bootstrap for Base Browser
by brizental (@brizental) 08 Sep '25
by brizental (@brizental) 08 Sep '25
08 Sep '25
brizental pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
c3546be5 by Beatriz Rizental at 2025-09-08T19:32:45+02:00
BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
802cceaf by Beatriz Rizental at 2025-09-08T19:32:46+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
EXTRA: Stop asking to configure git during bootstrap.
- - - - -
856dc4ee by Beatriz Rizental at 2025-09-08T19:32:47+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
a1b3a8cf by Beatriz Rizental at 2025-09-08T19:32:47+02:00
fixup! BB 41803: Add some developer tools for working on tor-browser.
- - - - -
4d66d017 by Beatriz Rizental at 2025-09-08T19:32:48+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
642024c9 by Beatriz Rizental at 2025-09-08T19:32:49+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
25 changed files:
- + build/moz.configure/basebrowser-resources.configure
- build/moz.configure/bootstrap.configure
- build/moz.configure/init.configure
- moz.configure
- python/mozboot/mozboot/bootstrap.py
- python/mozbuild/mozbuild/action/tooltool.py
- python/mozbuild/mozbuild/artifact_commands.py
- python/mozbuild/mozbuild/backend/base.py
- + python/mozbuild/mozbuild/tbbutils.py
- python/mozbuild/mozbuild/test/python.toml
- + python/mozbuild/mozbuild/test/test_tbbutils.py
- − tools/base-browser/l10n/combine/tests/README
- tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor
- tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py
- tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py
- tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py
- tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py
- + tools/base_browser/l10n/combine/tests/python.toml
- tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py
- tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py
- tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py
- tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py
- tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py
- tools/base-browser/tb-dev → tools/base_browser/tb-dev
- tools/moz.build
Changes:
=====================================
build/moz.configure/basebrowser-resources.configure
=====================================
@@ -0,0 +1,88 @@
+# Helpers
+# -------------------------------------------------
+
+
+@depends(build_project)
+def is_desktop_build(build_project):
+ return build_project == "browser"
+
+
+# Bootstrap resources
+# -------------------------------------------------
+
+
+option(
+ "--with-noscript",
+ env="NOSCRIPT",
+ nargs=1,
+ default=None,
+ help="Path to noscript .xpi extension archive.",
+)
+
+
+@depends(
+ "--with-noscript",
+ mozbuild_state_path,
+ bootstrap_path(
+ "noscript", no_unpack=True, when=depends("--with-noscript")(lambda x: not x)
+ ),
+)
+@checking("for noscript")
+@imports(_from="pathlib", _import="Path")
+def noscript(value, mozbuild_state_path, _bootstrapped):
+ if value:
+ path = Path(value[0])
+ if path.is_file() and path.suffix == ".xpi":
+ return value[0]
+ else:
+ die("--with-noscript must be an existing .xpi file")
+
+ bootstrapped_location = Path(mozbuild_state_path) / "browser"
+ for file in bootstrapped_location.glob(f"*.xpi"):
+ if "noscript" in file.name:
+ return str(bootstrapped_location / file)
+
+ # noscript is not required for building.
+ return None
+
+
+set_config("NOSCRIPT", noscript)
+
+
+option(
+ "--with-tor-browser-fonts",
+ env="TOR_BROWSER_FONTS",
+ nargs=1,
+ default=None,
+ help="Path to location of fonts directory.",
+)
+
+
+@depends(
+ "--with-tor-browser-fonts",
+ mozbuild_state_path,
+ bootstrap_path(
+ "fonts",
+ when=depends("--with-tor-browser-fonts")(lambda x: not x) & is_desktop_build,
+ ),
+)
+@checking("for tor-browser fonts directory")
+@imports(_from="pathlib", _import="Path")
+def tor_browser_fonts(value, mozbuild_state_path, _bootstrapped):
+ if value:
+ path = Path(value[0])
+ # TODO: Do a more thorough check on the directory.
+ if path.is_dir():
+ return value[0]
+ else:
+ die("--with-tor-browser-fonts must point to a real directory.")
+
+ bootstrapped_location = Path(mozbuild_state_path) / "fonts"
+ if bootstrapped_location.is_dir():
+ return str(bootstrapped_location)
+
+ # tor browser fonts directory is not required for building.
+ return None
+
+
+set_config("TOR_BROWSER_FONTS", tor_browser_fonts)
=====================================
build/moz.configure/bootstrap.configure
=====================================
@@ -4,6 +4,29 @@
# 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/.
+option(
+ "--with-tor-browser-build-out",
+ env="TOR_BROWSER_BUILD_OUT",
+ nargs=1,
+ default="https://tb-build-06.torproject.org/~tb-builder/tor-browser-build/out",
+ help="URL pointing to a Tor Browser Build out folder, served over HTTP[S].",
+)
+
+
+@depends("--with-tor-browser-build-out")
+def tor_browser_build_out(value):
+ if value:
+ return value[0]
+
+
+option(
+ "--enable-tor-browser-build-only-bootstrap",
+ env="TBB_ONLY_BOOTSTRAP",
+ default=False,
+ help="Flag that disables bootstrapping any artifact from Mozilla's Taskcluster. Will only bootstrap artifacts from tor-browser-build.",
+)
+
+
option(
env="MOZ_FETCHES_DIR",
nargs=1,
@@ -115,9 +138,10 @@ def bootstrap_toolchain_tasks(host):
def bootstrap_path(path, **kwargs):
when = kwargs.pop("when", None)
allow_failure = kwargs.pop("allow_failure", None)
+ no_unpack = kwargs.pop("no_unpack", False)
if kwargs:
configure_error(
- "bootstrap_path only takes `when` and `allow_failure` as a keyword argument"
+ "bootstrap_path only takes `when`, `allow_failure` and `no_unpack` as keyword arguments"
)
@depends(
@@ -129,11 +153,16 @@ def bootstrap_path(path, **kwargs):
build_environment,
dependable(path),
dependable(allow_failure),
+ dependable(no_unpack),
+ tor_browser_build_out,
+ "--enable-tor-browser-build-only-bootstrap",
+ target,
when=when,
)
@imports("os")
@imports("subprocess")
@imports("sys")
+ @imports("mozbuild.tbbutils")
@imports(_from="mozbuild.dirutils", _import="ensureParentDir")
@imports(_from="importlib", _import="import_module")
@imports(_from="shutil", _import="rmtree")
@@ -148,6 +177,10 @@ def bootstrap_path(path, **kwargs):
build_env,
path,
allow_failure,
+ no_unpack,
+ tor_browser_build_out,
+ tbb_only_bootstrap,
+ target,
):
if not path:
return
@@ -158,6 +191,83 @@ def bootstrap_path(path, **kwargs):
if path_parts[0] == "clang-tools":
path_prefix = path_parts.pop(0)
+ # Small hack because noscript is inside the browser folder.
+ if path_parts[0] == "noscript":
+ path_prefix = "browser"
+
+ def try_tbb_bootstrap(exists):
+ if not tor_browser_build_out:
+ return False
+
+ # Tor browser build doesn't have artifacts for all targets supported
+ # by the Firefox build system. When this is empty it means we are
+ # building for a platform which tbb doesn't support.
+ if not target.tor_browser_build_alias:
+ return False
+
+ artifact = mozbuild.tbbutils.get_artifact_name(path_parts[0], tasks.prefix)
+ if not artifact:
+ log.info("%s is not mapped to a tbb artifact", path_parts[0])
+ return False
+
+ artifact_path = mozbuild.tbbutils.get_artifact_path(
+ tor_browser_build_out,
+ artifact,
+ target,
+ prefix=path_prefix,
+ log=log.warning,
+ )
+ if not artifact_path:
+ log.info("no path found in tbb/out for %s", artifact)
+ return False
+
+ # We will use the name of the artifact as the index.
+ #
+ # It's usually unique to the artifact version, but each artifact follows
+ # a different naming convention, so we can't really get more specific here.
+ artifact_index = artifact_path.rsplit("/", 1)[-1]
+ index_file = os.path.join(toolchains_base_dir, "indices", artifact)
+ try:
+ with open(index_file) as fh:
+ index = fh.read().strip()
+ except Exception:
+ index = None
+ if index == artifact_index and exists:
+ log.debug("%s is up-to-date", artifact)
+ return True
+
+ command = ["artifact", "toolchain", "--from-url", artifact_path]
+
+ if no_unpack:
+ command.append("--no-unpack")
+
+ # Note to rebasers:
+ # From here on, it's a slightly modified copy/paste
+ # from the end of the try_bootstrap function
+ log.info(
+ "%s bootstrapped toolchain from TBB in %s",
+ "Updating" if exists else "Installing",
+ os.path.join(toolchains_base_dir, path_prefix, artifact),
+ )
+ os.makedirs(os.path.join(toolchains_base_dir, path_prefix), exist_ok=True)
+ proc = subprocess.run(
+ [
+ sys.executable,
+ os.path.join(build_env.topsrcdir, "mach"),
+ "--log-no-times",
+ ]
+ + command,
+ cwd=os.path.join(toolchains_base_dir, path_prefix),
+ check=not allow_failure,
+ )
+ if proc.returncode != 0 and allow_failure:
+ return False
+ ensureParentDir(index_file)
+ with open(index_file, "w") as fh:
+ fh.write(artifact_index)
+
+ return True
+
def try_bootstrap(exists):
if not tasks:
return False
@@ -280,9 +390,10 @@ def bootstrap_path(path, **kwargs):
try:
# With --enable-bootstrap=no-update, we don't `try_bootstrap`, except
# when the toolchain can't be found.
- if (
- "no-update" not in enable_bootstrap or not exists
- ) and not try_bootstrap(exists):
+ if ("no-update" not in enable_bootstrap or not exists) and not (
+ try_tbb_bootstrap(exists)
+ or (not tbb_only_bootstrap and try_bootstrap(exists))
+ ):
# If there aren't toolchain artifacts to use for this build,
# don't return a path.
return None
=====================================
build/moz.configure/init.configure
=====================================
@@ -590,6 +590,21 @@ def split_triplet(triplet, allow_wasi=False):
else:
toolchain = "%s-%s" % (cpu, os)
+ # In tor-browser-build we use slightly different terminology for
+ # the supported platforms. Let's prepare that OS string here.
+ #
+ # Not all possible platforms listed here are supported in tbb,
+ # so this value will be empty sometimes.
+ tor_browser_build_alias = None
+ if canonical_os == "Android" and canonical_kernel == "Linux":
+ tor_browser_build_alias = f"android"
+ elif canonical_os == "GNU" and canonical_kernel == "Linux":
+ tor_browser_build_alias = f"linux"
+ elif canonical_os == "OSX" and canonical_kernel == "Darwin":
+ tor_browser_build_alias = f"macos"
+ elif canonical_os == "WINNT" and canonical_kernel == "WINNT":
+ tor_browser_build_alias = f"windows"
+
return namespace(
alias=triplet,
cpu=CPU(canonical_cpu),
@@ -604,6 +619,7 @@ def split_triplet(triplet, allow_wasi=False):
toolchain=toolchain,
vendor=vendor,
sub_configure_alias=sub_configure_alias,
+ tor_browser_build_alias=tor_browser_build_alias,
)
=====================================
moz.configure
=====================================
@@ -229,6 +229,7 @@ check_prog("WGET", ("wget",), allow_missing=True)
include("build/moz.configure/toolchain.configure", when="--enable-compile-environment")
+include("build/moz.configure/basebrowser-resources.configure")
include("build/moz.configure/pkg.configure")
include("build/moz.configure/memory.configure", when="--enable-compile-environment")
=====================================
python/mozboot/mozboot/bootstrap.py
=====================================
@@ -52,21 +52,28 @@ Note on Artifact Mode:
Artifact builds download prebuilt C++ components rather than building
them locally. Artifact builds are faster!
-Artifact builds are recommended for people working on Firefox or
-Firefox for Android frontends, or the GeckoView Java API. They are unsuitable
+Artifact builds are recommended for people working on Tor Browser or
+Base Browser for Android frontends, or the GeckoView Java API. They are unsuitable
for those working on C++ code. For more information see:
https://firefox-source-docs.mozilla.org/contributing/build/artifact_builds.….
-Please choose the version of Firefox you want to build (see note above):
+# Note to Base Browser developers
+
+This is still highly experimental. Expect bugs!
+
+Please choose the version of Base Browser you want to build (see note above):
%s
Your choice: """
APPLICATIONS = OrderedDict(
[
- ("Firefox for Desktop Artifact Mode", "browser_artifact_mode"),
- ("Firefox for Desktop", "browser"),
- ("GeckoView/Firefox for Android Artifact Mode", "mobile_android_artifact_mode"),
- ("GeckoView/Firefox for Android", "mobile_android"),
+ ("Base Browser for Desktop Artifact Mode", "browser_artifact_mode"),
+ ("Base Browser for Desktop", "browser"),
+ (
+ "GeckoView/Base Browser for Android Artifact Mode",
+ "mobile_android_artifact_mode",
+ ),
+ ("GeckoView/Base Browser for Android", "mobile_android"),
("SpiderMonkey JavaScript engine", "js"),
]
)
@@ -360,6 +367,8 @@ class Bootstrapper:
getattr(self.instance, "ensure_%s_packages" % application)()
def check_code_submission(self, checkout_root: Path):
+ return
+
if self.instance.no_interactive or which("moz-phab"):
return
@@ -474,8 +483,7 @@ class Bootstrapper:
configure_mercurial(hg, state_dir)
# Offer to configure Git, if the current checkout or repo type is Git.
- elif git and checkout_type == "git":
- should_configure_git = False
+ elif False and git and checkout_type == "git":
if not self.instance.no_interactive:
should_configure_git = self.instance.prompt_yesno(prompt=CONFIGURE_GIT)
else:
=====================================
python/mozbuild/mozbuild/action/tooltool.py
=====================================
@@ -1029,14 +1029,29 @@ def unpack_file(filename):
"""Untar `filename`, assuming it is uncompressed or compressed with bzip2,
xz, gzip, zst, or unzip a zip file. The file is assumed to contain a single
directory with a name matching the base of the given filename.
- Xz support is handled by shelling out to 'tar'."""
+ Xz support is handled by shelling out to 'tar'.
+
+ tor-browser#41564 - For supporting tor-browser-build artifacts that contain
+ multiple directories, the archive is extracted into a directory with the
+ same name as the base of the filename. This modification is only applied to
+ tar archives, because that is all that was necessary.
+ """
if os.path.isfile(filename) and tarfile.is_tarfile(filename):
tar_file, zip_ext = os.path.splitext(filename)
base_file, tar_ext = os.path.splitext(tar_file)
clean_path(base_file)
log.info('untarring "%s"' % filename)
with TarFile.open(filename) as tar:
- safe_extract(tar)
+ top_level_directories = set()
+ for name in tar.getnames():
+ dir = name.split("/", 1)[0]
+ top_level_directories.add(dir)
+ if len(top_level_directories) == 1:
+ safe_extract(tar)
+ else:
+ safe_extract(
+ tar, path=os.path.join(os.path.dirname(filename), base_file)
+ )
elif os.path.isfile(filename) and filename.endswith(".tar.zst"):
import zstandard
=====================================
python/mozbuild/mozbuild/artifact_commands.py
=====================================
@@ -244,6 +244,12 @@ def artifact_clear_cache(command_context, tree=None, job=None, verbose=False):
nargs="+",
help="Download toolchain artifact from a given task.",
)
+@CommandArgument(
+ "--from-url",
+ metavar="URL",
+ nargs="+",
+ help="Download toolchain artifact from an arbitrary address.",
+)
@CommandArgument(
"--tooltool-manifest",
metavar="MANIFEST",
@@ -273,6 +279,7 @@ def artifact_toolchain(
skip_cache=False,
from_build=(),
from_task=(),
+ from_url=[],
tooltool_manifest=None,
no_unpack=False,
retry=0,
@@ -504,6 +511,13 @@ def artifact_toolchain(
record = ArtifactRecord(task_id, name)
records[record.filename] = record
+ if from_url:
+ for file in from_url:
+ record = DownloadRecord(
+ file, file.rsplit("/", 1)[-1], None, None, None, True
+ )
+ records[record.filename] = record
+
for record in records.values():
command_context.log(
logging.INFO,
=====================================
python/mozbuild/mozbuild/backend/base.py
=====================================
@@ -2,11 +2,14 @@
# 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/.
+import errno
import itertools
+import logging
import os
import time
from abc import ABCMeta, abstractmethod
from contextlib import contextmanager
+from pathlib import Path
import mozpack.path as mozpath
from mach.mixin.logging import LoggingMixin
@@ -239,6 +242,72 @@ class BuildBackend(LoggingMixin):
with open(mozpath.join(dir, ".purgecaches"), "w") as f:
f.write("\n")
+ def _setup_tor_browser_environment(self, config):
+ app = config.substs["MOZ_BUILD_APP"]
+
+ noscript_target_filename = "{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi"
+ noscript_location = config.substs.get("NOSCRIPT")
+
+ def _infallible_symlink(src, dst):
+ try:
+ os.symlink(src, dst)
+ except OSError as e:
+ if e.errno == errno.EEXIST:
+ # If the symlink already exists, remove it and try again.
+ os.remove(dst)
+ os.symlink(src, dst)
+ else:
+ return
+
+ if app == "browser":
+ tbdir = Path(config.topobjdir) / "dist" / "bin"
+
+ if config.substs.get("OS_TARGET") == "Darwin":
+ tbdir = next(tbdir.glob("*.app"))
+ paths = {
+ "docs": tbdir / "Contents/Resources/TorBrowser/Docs",
+ "exts": tbdir / "Contents/Resources/distribution/extensions",
+ "fonts": tbdir / "Resources/fonts",
+ }
+ else:
+ paths = {
+ "docs": tbdir / "TorBrowser/Docs",
+ "exts": tbdir / "distribution/extensions",
+ "fonts": tbdir / "fonts",
+ }
+
+ fonts_location = config.substs.get("TOR_BROWSER_FONTS")
+ if fonts_location:
+ self.log(
+ logging.INFO,
+ "_setup_tor_browser_environment",
+ {
+ "fonts_location": fonts_location,
+ "fonts_target": str(paths["fonts"]),
+ },
+ "Creating symlink for fonts files from {fonts_location} to {fonts_target}",
+ )
+
+ for file in Path(fonts_location).iterdir():
+ target = paths["fonts"] / file.name
+ _infallible_symlink(file, target)
+
+ # Set up NoScript extension
+ if noscript_location:
+ noscript_target = paths["exts"] / noscript_target_filename
+ self.log(
+ logging.INFO,
+ "_setup_tor_browser_environment",
+ {
+ "noscript_location": noscript_location,
+ "noscript_target": str(noscript_target),
+ },
+ "Creating symlink for NoScript from {noscript_location} to {noscript_target}",
+ )
+
+ paths["exts"].mkdir(parents=True, exist_ok=True)
+ _infallible_symlink(noscript_location, noscript_target)
+
def post_build(self, config, output, jobs, verbose, status):
"""Called late during 'mach build' execution, after `build(...)` has finished.
@@ -257,6 +326,9 @@ class BuildBackend(LoggingMixin):
"""
self._write_purgecaches(config)
+ if status == 0:
+ self._setup_tor_browser_environment(config)
+
return status
@contextmanager
=====================================
python/mozbuild/mozbuild/tbbutils.py
=====================================
@@ -0,0 +1,103 @@
+import re
+from urllib.request import Request, urlopen
+
+
+def list_files_http(url):
+ try:
+ req = Request(url, method="GET")
+ with urlopen(req) as response:
+ if response.status != 200:
+ return []
+ html = response.read().decode()
+ except Exception:
+ return []
+
+ links = []
+ for href in re.findall(r'<a href="([^"]+)"', html):
+ if href == "../":
+ continue
+
+ links.append(href)
+
+ return links
+
+
+TOR_BROWSER_BUILD_ARTIFACTS = [
+ # Tor Browser Build-only artifacts, these artifacts are not common with Firefox.
+ "noscript",
+ "fonts",
+]
+
+# Mapping of artifacts from taskcluster to tor-browser-build.
+ARTIFACT_NAME_MAP = {
+ "cbindgen": "cbindgen",
+ # FIXME (tor-browser-build#41471): nasm is more or less ready to go, but it needs to have the
+ # executable in the root of the artifact folder instead of nasm/bin.
+ # "nasm": "nasm",
+ # FIXME (tor-browser-build#41421): the clang project as is, is not ready to use. It needs
+ # to be repackaged with a bunch of things that differ per platform. Fun stuff.
+ # "clang": "clang",
+ "node": "node",
+}
+
+
+def get_artifact_name(original_artifact_name, host):
+ # These are not build artifacts, they are pre-built artifacts to be added to the final build,
+ # therefore this check can come before the host check.
+ if original_artifact_name in TOR_BROWSER_BUILD_ARTIFACTS:
+ return original_artifact_name
+
+ if host != "linux64":
+ # Tor browser build only has development artifacts for linux64 host systems.
+ return None
+
+ return ARTIFACT_NAME_MAP.get(original_artifact_name)
+
+
+def get_artifact_path(url, artifact, target, prefix="", log=lambda *args, **kwargs: {}):
+ if prefix:
+ path = prefix
+ else:
+ path = artifact
+
+ # The `?C=M;O=D` parameters make it so links are ordered by
+ # the last modified date. This here to make us get the latest
+ # version of file in the case there are multiple and we just
+ # grab the first one.
+ files = list_files_http(f"{url}/{path}?C=M;O=D")
+
+ if not files:
+ log(f"No files found in {url} for {artifact}.")
+ return None
+
+ def filter_files(files, keyword):
+ return [file for file in files if keyword in file]
+
+ artifact_files = [file for file in files if file.startswith(artifact)]
+
+ if len(artifact_files) == 0:
+ log(f"No files found in {url} for {artifact}.")
+ return None
+
+ if len(artifact_files) == 1:
+ return f"{url}/{path}/{artifact_files[0]}"
+
+ files_per_os = filter_files(artifact_files, target.tor_browser_build_alias)
+
+ # If there are files in the folder, but they don't have the OS in the name
+ # it probably means we can get any of them because they can be used to build
+ # for any OS. So let's just get the first one.
+ #
+ # Note: It could be the case that the artifact _is_ OS dependant, but there
+ # just are no files for the OS we are looking for. In that case, this will
+ # return an incorrect artifact. This should not happen often though and is
+ # something we cannot address until artifact names are standardized on tbb.
+ if len(files_per_os) == 0:
+ return f"{url}/{artifact}/{artifact_files[0]}"
+
+ elif len(files_per_os) == 1:
+ return f"{url}/{artifact}/{files_per_os[0]}"
+
+ matches = filter_files(files_per_os, target.cpu)
+
+ return f"{url}/{artifact}/{matches[0]}" if matches else None
=====================================
python/mozbuild/mozbuild/test/python.toml
=====================================
@@ -111,6 +111,9 @@ subsuite = "mozbuild"
["test_rewrite_mozbuild.py"]
+["test_tbbutils.py"]
+subsuite = "base-browser"
+
["test_telemetry.py"]
["test_telemetry_settings.py"]
=====================================
python/mozbuild/mozbuild/test/test_tbbutils.py
=====================================
@@ -0,0 +1,139 @@
+import unittest
+from types import SimpleNamespace
+from unittest.mock import MagicMock, patch
+
+import mozunit
+
+from mozbuild.tbbutils import get_artifact_path, list_files_http
+
+
+class TestGetArtifactName(unittest.TestCase):
+ def setUp(self):
+ self.artifact = "artifact"
+ self.host = "linux64"
+
+ @patch("mozbuild.tbbutils.TOR_BROWSER_BUILD_ARTIFACTS", new=["artifact"])
+ def test_artifact_in_tbb_artifacts(self):
+ from mozbuild.tbbutils import get_artifact_name
+
+ result = get_artifact_name(self.artifact, self.host)
+ self.assertEqual(result, self.artifact)
+
+ @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"})
+ def test_host_is_not_linux64(self):
+ from mozbuild.tbbutils import get_artifact_name
+
+ result = get_artifact_name(self.artifact, "linux64-aarch64")
+ self.assertIsNone(result)
+
+ @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"})
+ def test_mapped_artifact(self):
+ from mozbuild.tbbutils import get_artifact_name
+
+ result = get_artifact_name(self.artifact, self.host)
+ self.assertEqual(result, self.artifact[::-1])
+
+
+class TestGetArtifactPath(unittest.TestCase):
+ def setUp(self):
+ self.url = "http://example.com"
+ self.artifact = "artifact"
+ # This is just an example target which is valid. But it doesn't make
+ # any difference and could be anything for these tests.
+ self.target = SimpleNamespace(tor_browser_build_alias="linux", cpu="x86_64")
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_no_files_returns_none(self, mock_list_files):
+ mock_list_files.return_value = []
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertIsNone(result)
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_no_matching_files_returns_none(self, mock_list_files):
+ mock_list_files.return_value = ["somethingelse.zip", "yetanotherthing.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertIsNone(result)
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_single_artifact_match(self, mock_list_files):
+ mock_list_files.return_value = ["artifact-1.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-1.zip")
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_without_os_returns_first(self, mock_list_files):
+ mock_list_files.return_value = ["artifact-1.zip", "artifact-2.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertTrue(result.startswith(f"{self.url}/{self.artifact}/"))
+ self.assertIn("artifact-", result)
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_with_os_match(self, mock_list_files):
+ mock_list_files.return_value = [
+ "artifact-windows.zip",
+ "artifact-linux.zip",
+ ]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-linux.zip")
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_with_cpu_match(self, mock_list_files):
+ mock_list_files.return_value = [
+ "artifact-linux-arm.zip",
+ "artifact-linux-x86_64.zip",
+ ]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertEqual(
+ result, f"{self.url}/{self.artifact}/artifact-linux-x86_64.zip"
+ )
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_with_prefix(self, mock_list_files):
+ mock_list_files.return_value = ["artifact-1.zip"]
+
+ prefix = "prefix"
+ result = get_artifact_path(self.url, self.artifact, self.target, prefix=prefix)
+ self.assertEqual(result, f"{self.url}/{prefix}/artifact-1.zip")
+ mock_list_files.assert_called_with(f"{self.url}/{prefix}?C=M;O=D")
+
+
+class TestListFilesHttp(unittest.TestCase):
+ def setUp(self):
+ self.url = "http://example.com"
+
+ @patch("mozbuild.tbbutils.urlopen")
+ def test_non_200_status_returns_empty(self, mock_urlopen):
+ mock_resp = MagicMock()
+ mock_resp.status = 404
+ mock_resp.read.return_value = b""
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
+
+ result = list_files_http(self.url)
+ self.assertEqual(result, [])
+
+ @patch("mozbuild.tbbutils.urlopen")
+ def test_exception_returns_empty(self, mock_urlopen):
+ mock_urlopen.side_effect = Exception("network error")
+ result = list_files_http(self.url)
+ self.assertEqual(result, [])
+
+ @patch("mozbuild.tbbutils.urlopen")
+ def test_regular_links(self, mock_urlopen):
+ html = b"""
+ <html><body>
+ <a href="../">Parent</a>
+ <a href="file1.zip">file1</a>
+ <a href="file2.zip">file2</a>
+ </body></html>
+ """
+ mock_resp = MagicMock()
+ mock_resp.status = 200
+ mock_resp.read.return_value = html
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
+
+ result = list_files_http(self.url)
+ self.assertEqual(result, ["file1.zip", "file2.zip"])
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/README deleted
=====================================
@@ -1,2 +0,0 @@
-python tests to be run with pytest.
-Requires the compare-locales package.
=====================================
tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor
=====================================
=====================================
tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py
=====================================
=====================================
tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py
=====================================
=====================================
tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py
=====================================
=====================================
tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py
=====================================
=====================================
tools/base_browser/l10n/combine/tests/python.toml
=====================================
@@ -0,0 +1,10 @@
+[DEFAULT]
+subsuite = "base-browser"
+
+["test_android.py"]
+
+["test_dtd.py"]
+
+["test_fluent.py"]
+
+["test_properties.py"]
=====================================
tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def wrap_in_xml(content):
@@ -413,3 +414,7 @@ def test_alternatives():
<string name="string_4_alt">Other string</string>
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def assert_result(new_content, old_content, expect):
@@ -411,3 +412,7 @@ def test_alternatives():
<!ENTITY string.4.alt "Other string">
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def assert_result(new_content, old_content, expect):
@@ -475,3 +476,7 @@ def test_alternatives():
-string-4-alt = Other string
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def assert_result(new_content, old_content, expect):
@@ -408,3 +409,7 @@ def test_alternatives():
string.4.alt = Other string
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py
=====================================
=====================================
tools/base-browser/tb-dev → tools/base_browser/tb-dev
=====================================
=====================================
tools/moz.build
=====================================
@@ -71,6 +71,7 @@ with Files("tryselect/docs/**"):
SCHEDULES.exclusive = ["docs"]
PYTHON_UNITTEST_MANIFESTS += [
+ "base_browser/l10n/combine/tests/python.toml",
"fuzzing/smoke/python.toml",
"lint/test/python.toml",
"tryselect/test/python.toml",
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/a5…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/a5…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] 6 commits: BB 43564: Modify ./mach bootstrap for Base Browser
by brizental (@brizental) 08 Sep '25
by brizental (@brizental) 08 Sep '25
08 Sep '25
brizental pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
85b4d8d5 by Beatriz Rizental at 2025-09-08T19:22:50+02:00
BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
02a18b4d by Beatriz Rizental at 2025-09-08T19:22:58+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
EXTRA: Stop asking to configure git during bootstrap.
- - - - -
4937c1b6 by Beatriz Rizental at 2025-09-08T19:23:06+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
98a01dbd by Beatriz Rizental at 2025-09-08T19:23:32+02:00
fixup! BB 41803: Add some developer tools for working on tor-browser.
- - - - -
10ce4ee0 by Beatriz Rizental at 2025-09-08T19:28:44+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
e841a083 by Beatriz Rizental at 2025-09-08T19:28:49+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
25 changed files:
- + build/moz.configure/basebrowser-resources.configure
- build/moz.configure/bootstrap.configure
- build/moz.configure/init.configure
- moz.configure
- python/mozboot/mozboot/bootstrap.py
- python/mozbuild/mozbuild/action/tooltool.py
- python/mozbuild/mozbuild/artifact_commands.py
- python/mozbuild/mozbuild/backend/base.py
- + python/mozbuild/mozbuild/tbbutils.py
- python/mozbuild/mozbuild/test/python.toml
- + python/mozbuild/mozbuild/test/test_tbbutils.py
- − tools/base-browser/l10n/combine/tests/README
- tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor
- tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py
- tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py
- tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py
- tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py
- + tools/base_browser/l10n/combine/tests/python.toml
- tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py
- tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py
- tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py
- tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py
- tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py
- tools/base-browser/tb-dev → tools/base_browser/tb-dev
- tools/moz.build
Changes:
=====================================
build/moz.configure/basebrowser-resources.configure
=====================================
@@ -0,0 +1,88 @@
+# Helpers
+# -------------------------------------------------
+
+
+@depends(build_project)
+def is_desktop_build(build_project):
+ return build_project == "browser"
+
+
+# Bootstrap resources
+# -------------------------------------------------
+
+
+option(
+ "--with-noscript",
+ env="NOSCRIPT",
+ nargs=1,
+ default=None,
+ help="Path to noscript .xpi extension archive.",
+)
+
+
+@depends(
+ "--with-noscript",
+ mozbuild_state_path,
+ bootstrap_path(
+ "noscript", no_unpack=True, when=depends("--with-noscript")(lambda x: not x)
+ ),
+)
+@checking("for noscript")
+@imports(_from="pathlib", _import="Path")
+def noscript(value, mozbuild_state_path, _bootstrapped):
+ if value:
+ path = Path(value[0])
+ if path.is_file() and path.suffix == ".xpi":
+ return value[0]
+ else:
+ die("--with-noscript must be an existing .xpi file")
+
+ bootstrapped_location = Path(mozbuild_state_path) / "browser"
+ for file in bootstrapped_location.glob(f"*.xpi"):
+ if "noscript" in file.name:
+ return str(bootstrapped_location / file)
+
+ # noscript is not required for building.
+ return None
+
+
+set_config("NOSCRIPT", noscript)
+
+
+option(
+ "--with-tor-browser-fonts",
+ env="TOR_BROWSER_FONTS",
+ nargs=1,
+ default=None,
+ help="Path to location of fonts directory.",
+)
+
+
+@depends(
+ "--with-tor-browser-fonts",
+ mozbuild_state_path,
+ bootstrap_path(
+ "fonts",
+ when=depends("--with-tor-browser-fonts")(lambda x: not x) & is_desktop_build,
+ ),
+)
+@checking("for tor-browser fonts directory")
+@imports(_from="pathlib", _import="Path")
+def tor_browser_fonts(value, mozbuild_state_path, _bootstrapped):
+ if value:
+ path = Path(value[0])
+ # TODO: Do a more thorough check on the directory.
+ if path.is_dir():
+ return value[0]
+ else:
+ die("--with-tor-browser-fonts must point to a real directory.")
+
+ bootstrapped_location = Path(mozbuild_state_path) / "fonts"
+ if bootstrapped_location.is_dir():
+ return str(bootstrapped_location)
+
+ # tor browser fonts directory is not required for building.
+ return None
+
+
+set_config("TOR_BROWSER_FONTS", tor_browser_fonts)
=====================================
build/moz.configure/bootstrap.configure
=====================================
@@ -4,6 +4,29 @@
# 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/.
+option(
+ "--with-tor-browser-build-out",
+ env="TOR_BROWSER_BUILD_OUT",
+ nargs=1,
+ default="https://tb-build-06.torproject.org/~tb-builder/tor-browser-build/out",
+ help="URL pointing to a Tor Browser Build out folder, served over HTTP[S].",
+)
+
+
+@depends("--with-tor-browser-build-out")
+def tor_browser_build_out(value):
+ if value:
+ return value[0]
+
+
+option(
+ "--enable-tor-browser-build-only-bootstrap",
+ env="TBB_ONLY_BOOTSTRAP",
+ default=False,
+ help="Flag that disables bootstrapping any artifact from Mozilla's Taskcluster. Will only bootstrap artifacts from tor-browser-build.",
+)
+
+
option(
env="MOZ_FETCHES_DIR",
nargs=1,
@@ -115,9 +138,10 @@ def bootstrap_toolchain_tasks(host):
def bootstrap_path(path, **kwargs):
when = kwargs.pop("when", None)
allow_failure = kwargs.pop("allow_failure", None)
+ no_unpack = kwargs.pop("no_unpack", False)
if kwargs:
configure_error(
- "bootstrap_path only takes `when` and `allow_failure` as a keyword argument"
+ "bootstrap_path only takes `when`, `allow_failure` and `no_unpack` as keyword arguments"
)
@depends(
@@ -129,11 +153,16 @@ def bootstrap_path(path, **kwargs):
build_environment,
dependable(path),
dependable(allow_failure),
+ dependable(no_unpack),
+ tor_browser_build_out,
+ "--enable-tor-browser-build-only-bootstrap",
+ target,
when=when,
)
@imports("os")
@imports("subprocess")
@imports("sys")
+ @imports("mozbuild.tbbutils")
@imports(_from="mozbuild.dirutils", _import="ensureParentDir")
@imports(_from="importlib", _import="import_module")
@imports(_from="shutil", _import="rmtree")
@@ -148,6 +177,10 @@ def bootstrap_path(path, **kwargs):
build_env,
path,
allow_failure,
+ no_unpack,
+ tor_browser_build_out,
+ tbb_only_bootstrap,
+ target,
):
if not path:
return
@@ -158,6 +191,83 @@ def bootstrap_path(path, **kwargs):
if path_parts[0] == "clang-tools":
path_prefix = path_parts.pop(0)
+ # Small hack because noscript is inside the browser folder.
+ if path_parts[0] == "noscript":
+ path_prefix = "browser"
+
+ def try_tbb_bootstrap(exists):
+ if not tor_browser_build_out:
+ return False
+
+ # Tor browser build doesn't have artifacts for all targets supported
+ # by the Firefox build system. When this is empty it means we are
+ # building for a platform which tbb doesn't support.
+ if not target.tor_browser_build_alias:
+ return False
+
+ artifact = mozbuild.tbbutils.get_artifact_name(path_parts[0], tasks.prefix)
+ if not artifact:
+ log.info("%s is not mapped to a tbb artifact", path_parts[0])
+ return False
+
+ artifact_path = mozbuild.tbbutils.get_artifact_path(
+ tor_browser_build_out,
+ artifact,
+ target,
+ prefix=path_prefix,
+ log=log.warning,
+ )
+ if not artifact_path:
+ log.info("no path found in tbb/out for %s", artifact)
+ return False
+
+ # We will use the name of the artifact as the index.
+ #
+ # It's usually unique to the artifact version, but each artifact follows
+ # a different naming convention, so we can't really get more specific here.
+ artifact_index = artifact_path.rsplit("/", 1)[-1]
+ index_file = os.path.join(toolchains_base_dir, "indices", artifact)
+ try:
+ with open(index_file) as fh:
+ index = fh.read().strip()
+ except Exception:
+ index = None
+ if index == artifact_index and exists:
+ log.debug("%s is up-to-date", artifact)
+ return True
+
+ command = ["artifact", "toolchain", "--from-url", artifact_path]
+
+ if no_unpack:
+ command.append("--no-unpack")
+
+ # Note to rebasers:
+ # From here on, it's a slightly modified copy/paste
+ # from the end of the try_bootstrap function
+ log.info(
+ "%s bootstrapped toolchain from TBB in %s",
+ "Updating" if exists else "Installing",
+ os.path.join(toolchains_base_dir, path_prefix, artifact),
+ )
+ os.makedirs(os.path.join(toolchains_base_dir, path_prefix), exist_ok=True)
+ proc = subprocess.run(
+ [
+ sys.executable,
+ os.path.join(build_env.topsrcdir, "mach"),
+ "--log-no-times",
+ ]
+ + command,
+ cwd=os.path.join(toolchains_base_dir, path_prefix),
+ check=not allow_failure,
+ )
+ if proc.returncode != 0 and allow_failure:
+ return False
+ ensureParentDir(index_file)
+ with open(index_file, "w") as fh:
+ fh.write(artifact_index)
+
+ return True
+
def try_bootstrap(exists):
if not tasks:
return False
@@ -280,9 +390,10 @@ def bootstrap_path(path, **kwargs):
try:
# With --enable-bootstrap=no-update, we don't `try_bootstrap`, except
# when the toolchain can't be found.
- if (
- "no-update" not in enable_bootstrap or not exists
- ) and not try_bootstrap(exists):
+ if ("no-update" not in enable_bootstrap or not exists) and not (
+ try_tbb_bootstrap(exists)
+ or (not tbb_only_bootstrap and try_bootstrap(exists))
+ ):
# If there aren't toolchain artifacts to use for this build,
# don't return a path.
return None
=====================================
build/moz.configure/init.configure
=====================================
@@ -590,6 +590,21 @@ def split_triplet(triplet, allow_wasi=False):
else:
toolchain = "%s-%s" % (cpu, os)
+ # In tor-browser-build we use slightly different terminology for
+ # the supported platforms. Let's prepare that OS string here.
+ #
+ # Not all possible platforms listed here are supported in tbb,
+ # so this value will be empty sometimes.
+ tor_browser_build_alias = None
+ if canonical_os == "Android" and canonical_kernel == "Linux":
+ tor_browser_build_alias = f"android"
+ elif canonical_os == "GNU" and canonical_kernel == "Linux":
+ tor_browser_build_alias = f"linux"
+ elif canonical_os == "OSX" and canonical_kernel == "Darwin":
+ tor_browser_build_alias = f"macos"
+ elif canonical_os == "WINNT" and canonical_kernel == "WINNT":
+ tor_browser_build_alias = f"windows"
+
return namespace(
alias=triplet,
cpu=CPU(canonical_cpu),
@@ -604,6 +619,7 @@ def split_triplet(triplet, allow_wasi=False):
toolchain=toolchain,
vendor=vendor,
sub_configure_alias=sub_configure_alias,
+ tor_browser_build_alias=tor_browser_build_alias,
)
=====================================
moz.configure
=====================================
@@ -229,6 +229,7 @@ check_prog("WGET", ("wget",), allow_missing=True)
include("build/moz.configure/toolchain.configure", when="--enable-compile-environment")
+include("build/moz.configure/basebrowser-resources.configure")
include("build/moz.configure/pkg.configure")
include("build/moz.configure/memory.configure", when="--enable-compile-environment")
=====================================
python/mozboot/mozboot/bootstrap.py
=====================================
@@ -52,21 +52,28 @@ Note on Artifact Mode:
Artifact builds download prebuilt C++ components rather than building
them locally. Artifact builds are faster!
-Artifact builds are recommended for people working on Firefox or
-Firefox for Android frontends, or the GeckoView Java API. They are unsuitable
+Artifact builds are recommended for people working on Tor Browser or
+Base Browser for Android frontends, or the GeckoView Java API. They are unsuitable
for those working on C++ code. For more information see:
https://firefox-source-docs.mozilla.org/contributing/build/artifact_builds.….
-Please choose the version of Firefox you want to build (see note above):
+# Note to Base Browser developers
+
+This is still highly experimental. Expect bugs!
+
+Please choose the version of Base Browser you want to build (see note above):
%s
Your choice: """
APPLICATIONS = OrderedDict(
[
- ("Firefox for Desktop Artifact Mode", "browser_artifact_mode"),
- ("Firefox for Desktop", "browser"),
- ("GeckoView/Firefox for Android Artifact Mode", "mobile_android_artifact_mode"),
- ("GeckoView/Firefox for Android", "mobile_android"),
+ ("Base Browser for Desktop Artifact Mode", "browser_artifact_mode"),
+ ("Base Browser for Desktop", "browser"),
+ (
+ "GeckoView/Base Browser for Android Artifact Mode",
+ "mobile_android_artifact_mode",
+ ),
+ ("GeckoView/Base Browser for Android", "mobile_android"),
("SpiderMonkey JavaScript engine", "js"),
]
)
@@ -360,6 +367,8 @@ class Bootstrapper:
getattr(self.instance, "ensure_%s_packages" % application)()
def check_code_submission(self, checkout_root: Path):
+ return
+
if self.instance.no_interactive or which("moz-phab"):
return
@@ -474,8 +483,7 @@ class Bootstrapper:
configure_mercurial(hg, state_dir)
# Offer to configure Git, if the current checkout or repo type is Git.
- elif git and checkout_type == "git":
- should_configure_git = False
+ elif False and git and checkout_type == "git":
if not self.instance.no_interactive:
should_configure_git = self.instance.prompt_yesno(prompt=CONFIGURE_GIT)
else:
=====================================
python/mozbuild/mozbuild/action/tooltool.py
=====================================
@@ -1029,14 +1029,29 @@ def unpack_file(filename):
"""Untar `filename`, assuming it is uncompressed or compressed with bzip2,
xz, gzip, zst, or unzip a zip file. The file is assumed to contain a single
directory with a name matching the base of the given filename.
- Xz support is handled by shelling out to 'tar'."""
+ Xz support is handled by shelling out to 'tar'.
+
+ tor-browser#41564 - For supporting tor-browser-build artifacts that contain
+ multiple directories, the archive is extracted into a directory with the
+ same name as the base of the filename. This modification is only applied to
+ tar archives, because that is all that was necessary.
+ """
if os.path.isfile(filename) and tarfile.is_tarfile(filename):
tar_file, zip_ext = os.path.splitext(filename)
base_file, tar_ext = os.path.splitext(tar_file)
clean_path(base_file)
log.info('untarring "%s"' % filename)
with TarFile.open(filename) as tar:
- safe_extract(tar)
+ top_level_directories = set()
+ for name in tar.getnames():
+ dir = name.split("/", 1)[0]
+ top_level_directories.add(dir)
+ if len(top_level_directories) == 1:
+ safe_extract(tar)
+ else:
+ safe_extract(
+ tar, path=os.path.join(os.path.dirname(filename), base_file)
+ )
elif os.path.isfile(filename) and filename.endswith(".tar.zst"):
import zstandard
=====================================
python/mozbuild/mozbuild/artifact_commands.py
=====================================
@@ -244,6 +244,12 @@ def artifact_clear_cache(command_context, tree=None, job=None, verbose=False):
nargs="+",
help="Download toolchain artifact from a given task.",
)
+@CommandArgument(
+ "--from-url",
+ metavar="URL",
+ nargs="+",
+ help="Download toolchain artifact from an arbitrary address.",
+)
@CommandArgument(
"--tooltool-manifest",
metavar="MANIFEST",
@@ -273,6 +279,7 @@ def artifact_toolchain(
skip_cache=False,
from_build=(),
from_task=(),
+ from_url=[],
tooltool_manifest=None,
no_unpack=False,
retry=0,
@@ -504,6 +511,13 @@ def artifact_toolchain(
record = ArtifactRecord(task_id, name)
records[record.filename] = record
+ if from_url:
+ for file in from_url:
+ record = DownloadRecord(
+ file, file.rsplit("/", 1)[-1], None, None, None, True
+ )
+ records[record.filename] = record
+
for record in records.values():
command_context.log(
logging.INFO,
=====================================
python/mozbuild/mozbuild/backend/base.py
=====================================
@@ -2,11 +2,14 @@
# 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/.
+import errno
import itertools
+import logging
import os
import time
from abc import ABCMeta, abstractmethod
from contextlib import contextmanager
+from pathlib import Path
import mozpack.path as mozpath
from mach.mixin.logging import LoggingMixin
@@ -239,6 +242,72 @@ class BuildBackend(LoggingMixin):
with open(mozpath.join(dir, ".purgecaches"), "w") as f:
f.write("\n")
+ def _setup_tor_browser_environment(self, config):
+ app = config.substs["MOZ_BUILD_APP"]
+
+ noscript_target_filename = "{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi"
+ noscript_location = config.substs.get("NOSCRIPT")
+
+ def _infallible_symlink(src, dst):
+ try:
+ os.symlink(src, dst)
+ except OSError as e:
+ if e.errno == errno.EEXIST:
+ # If the symlink already exists, remove it and try again.
+ os.remove(dst)
+ os.symlink(src, dst)
+ else:
+ return
+
+ if app == "browser":
+ tbdir = Path(config.topobjdir) / "dist" / "bin"
+
+ if config.substs.get("OS_TARGET") == "Darwin":
+ tbdir = next(tbdir.glob("*.app"))
+ paths = {
+ "docs": tbdir / "Contents/Resources/TorBrowser/Docs",
+ "exts": tbdir / "Contents/Resources/distribution/extensions",
+ "fonts": tbdir / "Resources/fonts",
+ }
+ else:
+ paths = {
+ "docs": tbdir / "TorBrowser/Docs",
+ "exts": tbdir / "distribution/extensions",
+ "fonts": tbdir / "fonts",
+ }
+
+ fonts_location = config.substs.get("TOR_BROWSER_FONTS")
+ if fonts_location:
+ self.log(
+ logging.INFO,
+ "_setup_tor_browser_environment",
+ {
+ "fonts_location": fonts_location,
+ "fonts_target": str(paths["fonts"]),
+ },
+ "Creating symlink for fonts files from {fonts_location} to {fonts_target}",
+ )
+
+ for file in Path(fonts_location).iterdir():
+ target = paths["fonts"] / file.name
+ _infallible_symlink(file, target)
+
+ # Set up NoScript extension
+ if noscript_location:
+ noscript_target = paths["exts"] / noscript_target_filename
+ self.log(
+ logging.INFO,
+ "_setup_tor_browser_environment",
+ {
+ "noscript_location": noscript_location,
+ "noscript_target": str(noscript_target),
+ },
+ "Creating symlink for NoScript from {noscript_location} to {noscript_target}",
+ )
+
+ paths["exts"].mkdir(parents=True, exist_ok=True)
+ _infallible_symlink(noscript_location, noscript_target)
+
def post_build(self, config, output, jobs, verbose, status):
"""Called late during 'mach build' execution, after `build(...)` has finished.
@@ -257,6 +326,9 @@ class BuildBackend(LoggingMixin):
"""
self._write_purgecaches(config)
+ if status == 0:
+ self._setup_tor_browser_environment(config)
+
return status
@contextmanager
=====================================
python/mozbuild/mozbuild/tbbutils.py
=====================================
@@ -0,0 +1,103 @@
+import re
+from urllib.request import Request, urlopen
+
+
+def list_files_http(url):
+ try:
+ req = Request(url, method="GET")
+ with urlopen(req) as response:
+ if response.status != 200:
+ return []
+ html = response.read().decode()
+ except Exception:
+ return []
+
+ links = []
+ for href in re.findall(r'<a href="([^"]+)"', html):
+ if href == "../":
+ continue
+
+ links.append(href)
+
+ return links
+
+
+TOR_BROWSER_BUILD_ARTIFACTS = [
+ # Tor Browser Build-only artifacts, these artifacts are not common with Firefox.
+ "noscript",
+ "fonts",
+]
+
+# Mapping of artifacts from taskcluster to tor-browser-build.
+ARTIFACT_NAME_MAP = {
+ "cbindgen": "cbindgen",
+ # FIXME (tor-browser-build#41471): nasm is more or less ready to go, but it needs to have the
+ # executable in the root of the artifact folder instead of nasm/bin.
+ # "nasm": "nasm",
+ # FIXME (tor-browser-build#41421): the clang project as is, is not ready to use. It needs
+ # to be repackaged with a bunch of things that differ per platform. Fun stuff.
+ # "clang": "clang",
+ "node": "node",
+}
+
+
+def get_artifact_name(original_artifact_name, host):
+ # These are not build artifacts, they are pre-built artifacts to be added to the final build,
+ # therefore this check can come before the host check.
+ if original_artifact_name in TOR_BROWSER_BUILD_ARTIFACTS:
+ return original_artifact_name
+
+ if host != "linux64":
+ # Tor browser build only has development artifacts for linux64 host systems.
+ return None
+
+ return ARTIFACT_NAME_MAP.get(original_artifact_name)
+
+
+def get_artifact_path(url, artifact, target, prefix="", log=lambda *args, **kwargs: {}):
+ if prefix:
+ path = prefix
+ else:
+ path = artifact
+
+ # The `?C=M;O=D` parameters make it so links are ordered by
+ # the last modified date. This here to make us get the latest
+ # version of file in the case there are multiple and we just
+ # grab the first one.
+ files = list_files_http(f"{url}/{path}?C=M;O=D")
+
+ if not files:
+ log(f"No files found in {url} for {artifact}.")
+ return None
+
+ def filter_files(files, keyword):
+ return [file for file in files if keyword in file]
+
+ artifact_files = [file for file in files if file.startswith(artifact)]
+
+ if len(artifact_files) == 0:
+ log(f"No files found in {url} for {artifact}.")
+ return None
+
+ if len(artifact_files) == 1:
+ return f"{url}/{path}/{artifact_files[0]}"
+
+ files_per_os = filter_files(artifact_files, target.tor_browser_build_alias)
+
+ # If there are files in the folder, but they don't have the OS in the name
+ # it probably means we can get any of them because they can be used to build
+ # for any OS. So let's just get the first one.
+ #
+ # Note: It could be the case that the artifact _is_ OS dependant, but there
+ # just are no files for the OS we are looking for. In that case, this will
+ # return an incorrect artifact. This should not happen often though and is
+ # something we cannot address until artifact names are standardized on tbb.
+ if len(files_per_os) == 0:
+ return f"{url}/{artifact}/{artifact_files[0]}"
+
+ elif len(files_per_os) == 1:
+ return f"{url}/{artifact}/{files_per_os[0]}"
+
+ matches = filter_files(files_per_os, target.cpu)
+
+ return f"{url}/{artifact}/{matches[0]}" if matches else None
=====================================
python/mozbuild/mozbuild/test/python.toml
=====================================
@@ -111,6 +111,9 @@ subsuite = "mozbuild"
["test_rewrite_mozbuild.py"]
+["test_tbbutils.py"]
+subsuite = "base-browser"
+
["test_telemetry.py"]
["test_telemetry_settings.py"]
=====================================
python/mozbuild/mozbuild/test/test_tbbutils.py
=====================================
@@ -0,0 +1,139 @@
+import unittest
+from types import SimpleNamespace
+from unittest.mock import MagicMock, patch
+
+import mozunit
+
+from mozbuild.tbbutils import get_artifact_path, list_files_http
+
+
+class TestGetArtifactName(unittest.TestCase):
+ def setUp(self):
+ self.artifact = "artifact"
+ self.host = "linux64"
+
+ @patch("mozbuild.tbbutils.TOR_BROWSER_BUILD_ARTIFACTS", new=["artifact"])
+ def test_artifact_in_tbb_artifacts(self):
+ from mozbuild.tbbutils import get_artifact_name
+
+ result = get_artifact_name(self.artifact, self.host)
+ self.assertEqual(result, self.artifact)
+
+ @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"})
+ def test_host_is_not_linux64(self):
+ from mozbuild.tbbutils import get_artifact_name
+
+ result = get_artifact_name(self.artifact, "linux64-aarch64")
+ self.assertIsNone(result)
+
+ @patch("mozbuild.tbbutils.ARTIFACT_NAME_MAP", new={"artifact": "tcafitra"})
+ def test_mapped_artifact(self):
+ from mozbuild.tbbutils import get_artifact_name
+
+ result = get_artifact_name(self.artifact, self.host)
+ self.assertEqual(result, self.artifact[::-1])
+
+
+class TestGetArtifactPath(unittest.TestCase):
+ def setUp(self):
+ self.url = "http://example.com"
+ self.artifact = "artifact"
+ # This is just an example target which is valid. But it doesn't make
+ # any difference and could be anything for these tests.
+ self.target = SimpleNamespace(tor_browser_build_alias="linux", cpu="x86_64")
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_no_files_returns_none(self, mock_list_files):
+ mock_list_files.return_value = []
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertIsNone(result)
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_no_matching_files_returns_none(self, mock_list_files):
+ mock_list_files.return_value = ["somethingelse.zip", "yetanotherthing.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertIsNone(result)
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_single_artifact_match(self, mock_list_files):
+ mock_list_files.return_value = ["artifact-1.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-1.zip")
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_without_os_returns_first(self, mock_list_files):
+ mock_list_files.return_value = ["artifact-1.zip", "artifact-2.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertTrue(result.startswith(f"{self.url}/{self.artifact}/"))
+ self.assertIn("artifact-", result)
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_with_os_match(self, mock_list_files):
+ mock_list_files.return_value = [
+ "artifact-windows.zip",
+ "artifact-linux.zip",
+ ]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertEqual(result, f"{self.url}/{self.artifact}/artifact-linux.zip")
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_with_cpu_match(self, mock_list_files):
+ mock_list_files.return_value = [
+ "artifact-linux-arm.zip",
+ "artifact-linux-x86_64.zip",
+ ]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertEqual(
+ result, f"{self.url}/{self.artifact}/artifact-linux-x86_64.zip"
+ )
+
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_artifact_with_prefix(self, mock_list_files):
+ mock_list_files.return_value = ["artifact-1.zip"]
+
+ prefix = "prefix"
+ result = get_artifact_path(self.url, self.artifact, self.target, prefix=prefix)
+ self.assertEqual(result, f"{self.url}/{prefix}/artifact-1.zip")
+ mock_list_files.assert_called_with(f"{self.url}/{prefix}?C=M;O=D")
+
+
+class TestListFilesHttp(unittest.TestCase):
+ def setUp(self):
+ self.url = "http://example.com"
+
+ @patch("mozbuild.tbbutils.urlopen")
+ def test_non_200_status_returns_empty(self, mock_urlopen):
+ mock_resp = MagicMock()
+ mock_resp.status = 404
+ mock_resp.read.return_value = b""
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
+
+ result = list_files_http(self.url)
+ self.assertEqual(result, [])
+
+ @patch("mozbuild.tbbutils.urlopen")
+ def test_exception_returns_empty(self, mock_urlopen):
+ mock_urlopen.side_effect = Exception("network error")
+ result = list_files_http(self.url)
+ self.assertEqual(result, [])
+
+ @patch("mozbuild.tbbutils.urlopen")
+ def test_regular_links(self, mock_urlopen):
+ html = b"""
+ <html><body>
+ <a href="../">Parent</a>
+ <a href="file1.zip">file1</a>
+ <a href="file2.zip">file2</a>
+ </body></html>
+ """
+ mock_resp = MagicMock()
+ mock_resp.status = 200
+ mock_resp.read.return_value = html
+ mock_urlopen.return_value.__enter__.return_value = mock_resp
+
+ result = list_files_http(self.url)
+ self.assertEqual(result, ["file1.zip", "file2.zip"])
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/README deleted
=====================================
@@ -1,2 +0,0 @@
-python tests to be run with pytest.
-Requires the compare-locales package.
=====================================
tools/base-browser/git-rebase-fixup-preprocessor → tools/base_browser/git-rebase-fixup-preprocessor
=====================================
=====================================
tools/base-browser/l10n/combine-translation-versions.py → tools/base_browser/l10n/combine-translation-versions.py
=====================================
=====================================
tools/base-browser/l10n/combine/__init__.py → tools/base_browser/l10n/combine/__init__.py
=====================================
=====================================
tools/base-browser/l10n/combine/combine.py → tools/base_browser/l10n/combine/combine.py
=====================================
=====================================
tools/base-browser/l10n/combine/tests/__init__.py → tools/base_browser/l10n/combine/tests/__init__.py
=====================================
=====================================
tools/base_browser/l10n/combine/tests/python.toml
=====================================
@@ -0,0 +1,10 @@
+[DEFAULT]
+subsuite = "base-browser"
+
+["test_android.py"]
+
+["test_dtd.py"]
+
+["test_fluent.py"]
+
+["test_properties.py"]
=====================================
tools/base-browser/l10n/combine/tests/test_android.py → tools/base_browser/l10n/combine/tests/test_android.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def wrap_in_xml(content):
@@ -413,3 +414,7 @@ def test_alternatives():
<string name="string_4_alt">Other string</string>
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/test_dtd.py → tools/base_browser/l10n/combine/tests/test_dtd.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def assert_result(new_content, old_content, expect):
@@ -411,3 +412,7 @@ def test_alternatives():
<!ENTITY string.4.alt "Other string">
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/test_fluent.py → tools/base_browser/l10n/combine/tests/test_fluent.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def assert_result(new_content, old_content, expect):
@@ -475,3 +476,7 @@ def test_alternatives():
-string-4-alt = Other string
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/l10n/combine/tests/test_properties.py → tools/base_browser/l10n/combine/tests/test_properties.py
=====================================
@@ -1,6 +1,7 @@
import textwrap
-from combine import combine_files
+import mozunit
+from base_browser.l10n.combine import combine_files
def assert_result(new_content, old_content, expect):
@@ -408,3 +409,7 @@ def test_alternatives():
string.4.alt = Other string
""",
)
+
+
+if __name__ == "__main__":
+ mozunit.main()
=====================================
tools/base-browser/missing-css-variables.py → tools/base_browser/missing-css-variables.py
=====================================
=====================================
tools/base-browser/tb-dev → tools/base_browser/tb-dev
=====================================
=====================================
tools/moz.build
=====================================
@@ -71,6 +71,7 @@ with Files("tryselect/docs/**"):
SCHEDULES.exclusive = ["docs"]
PYTHON_UNITTEST_MANIFESTS += [
+ "base_browser/l10n/combine/tests/python.toml",
"fuzzing/smoke/python.toml",
"lint/test/python.toml",
"tryselect/test/python.toml",
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/53201c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/53201c…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! BB 43564: Modify ./mach bootstrap for Base Browser
by brizental (@brizental) 08 Sep '25
by brizental (@brizental) 08 Sep '25
08 Sep '25
brizental pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
e3afce24 by Beatriz Rizental at 2025-09-08T18:43:45+02:00
fixup! BB 43564: Modify ./mach bootstrap for Base Browser
- - - - -
3 changed files:
- build/moz.configure/bootstrap.configure
- python/mozbuild/mozbuild/tbbutils.py
- python/mozbuild/mozbuild/test/test_tbbutils.py
Changes:
=====================================
build/moz.configure/bootstrap.configure
=====================================
@@ -211,7 +211,11 @@ def bootstrap_path(path, **kwargs):
return False
artifact_path = mozbuild.tbbutils.get_artifact_path(
- tor_browser_build_out, artifact, target, prefix=path_prefix
+ tor_browser_build_out,
+ artifact,
+ target,
+ prefix=path_prefix,
+ log=log.warning,
)
if not artifact_path:
log.info("no path found in tbb/out for %s", artifact)
=====================================
python/mozbuild/mozbuild/tbbutils.py
=====================================
@@ -58,7 +58,7 @@ def get_artifact_name(original_artifact_name, host):
return ARTIFACT_NAME_MAP.get(original_artifact_name)
-def get_artifact_path(url, artifact, target, prefix=""):
+def get_artifact_path(url, artifact, target, prefix="", log=lambda *args, **kwargs: {}):
if prefix:
path = prefix
else:
@@ -71,6 +71,7 @@ def get_artifact_path(url, artifact, target, prefix=""):
files = list_files_http(f"{url}/{path}?C=M;O=D")
if not files:
+ log(f"No files found in {url} for {artifact}.")
return None
def filter_files(files, keyword):
@@ -78,6 +79,10 @@ def get_artifact_path(url, artifact, target, prefix=""):
artifact_files = [file for file in files if file.startswith(artifact)]
+ if len(artifact_files) == 0:
+ log(f"No files found in {url} for {artifact}.")
+ return None
+
if len(artifact_files) == 1:
return f"{url}/{path}/{artifact_files[0]}"
=====================================
python/mozbuild/mozbuild/test/test_tbbutils.py
=====================================
@@ -48,6 +48,12 @@ class TestGetArtifactPath(unittest.TestCase):
result = get_artifact_path(self.url, self.artifact, self.target)
self.assertIsNone(result)
+ @patch("mozbuild.tbbutils.list_files_http")
+ def test_no_matching_files_returns_none(self, mock_list_files):
+ mock_list_files.return_value = ["somethingelse.zip", "yetanotherthing.zip"]
+ result = get_artifact_path(self.url, self.artifact, self.target)
+ self.assertIsNone(result)
+
@patch("mozbuild.tbbutils.list_files_http")
def test_single_artifact_match(self, mock_list_files):
mock_list_files.return_value = ["artifact-1.zip"]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e3afce2…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/e3afce2…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! MB 419: Mullvad Browser migration procedures.
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
a5709ed4 by Pier Angelo Vendrame at 2025-09-08T16:07:32+02:00
fixup! MB 419: Mullvad Browser migration procedures.
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
2 changed files:
- browser/components/BrowserGlue.sys.mjs
- browser/components/ProfileDataUpgrader.sys.mjs
Changes:
=====================================
browser/components/BrowserGlue.sys.mjs
=====================================
@@ -430,9 +430,7 @@ BrowserGlue.prototype = {
// handle any UI migration
this._migrateUI();
lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile);
-
- // Mullvad Browser-specific version of _migrateUI.
- this._migrateUIMB();
+ lazy.ProfileDataUpgrader.upgradeMB(this._isNewProfile);
if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) {
lazy.PdfJs.checkIsDefault(this._isNewProfile);
@@ -1654,58 +1652,6 @@ BrowserGlue.prototype = {
}
},
- // Use this method for any MB migration that can be run just before showing
- // the UI.
- // Anything that critically needs to be migrated earlier should not use this.
- async _migrateUIMB() {
- // Version 1: Mullvad Browser 14.5a6: Clear home page update url preference
- // (mullvad-browser#411).
- // Version 2: Mullvad Browser 15.0a2: Remove legacy search addons
- // (tor-browser#43111).
- const MB_MIGRATION_VERSION = 2;
- const MIGRATION_PREF = "mullvadbrowser.migration.version";
-
- // If we decide to force updating users to pass through any version
- // following 14.5, we can remove this check, and check only whether
- // MIGRATION_PREF has a user value, like Mozilla does.
- if (this._isNewProfile) {
- // Do not migrate fresh profiles
- Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION);
- return;
- } else if (this._isNewProfile === undefined) {
- // If this happens, check if upstream updated their function and do not
- // set this member anymore!
- console.error("_migrateUIMB: this._isNewProfile is undefined.");
- }
-
- const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
-
- if (currentVersion < 1) {
- Services.prefs.clearUserPref("mullvadbrowser.post_update.url");
- }
- const dropAddons = async list => {
- for (const id of list) {
- try {
- const engine = await lazy.AddonManager.getAddonByID(id);
- await engine?.uninstall();
- } catch {}
- }
- };
- if (currentVersion < 2) {
- await dropAddons([
- "brave(a)search.mozilla.org",
- "ddg(a)search.mozilla.org",
- "ddg-html(a)search.mozilla.org",
- "metager(a)search.mozilla.org",
- "mojeek(a)search.mozilla.org",
- "mullvad-leta(a)search.mozilla.org",
- "startpage(a)search.mozilla.org",
- ]);
- }
-
- Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION);
- },
-
async _showUpgradeDialog() {
const data = await lazy.OnboardingMessageProvider.getUpgradeMessage();
const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow();
=====================================
browser/components/ProfileDataUpgrader.sys.mjs
=====================================
@@ -974,4 +974,50 @@ export let ProfileDataUpgrader = {
}
Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
},
+
+ async upgradeMB(isNewProfile) {
+ // Version 1: Mullvad Browser 14.5a6: Clear home page update url preference
+ // (mullvad-browser#411).
+ // Version 2: Mullvad Browser 15.0a2: Remove legacy search addons
+ // (tor-browser#43111).
+ const MB_MIGRATION_VERSION = 2;
+ const MIGRATION_PREF = "mullvadbrowser.migration.version";
+
+ if (isNewProfile) {
+ // Do not migrate fresh profiles
+ Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION);
+ return;
+ } else if (isNewProfile === undefined) {
+ // If this happens, check if upstream updated their function and do not
+ // set this member anymore!
+ console.error("upgradeTB: isNewProfile is undefined.");
+ }
+
+ const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
+
+ if (currentVersion < 1) {
+ Services.prefs.clearUserPref("mullvadbrowser.post_update.url");
+ }
+ const dropAddons = async list => {
+ for (const id of list) {
+ try {
+ const engine = await lazy.AddonManager.getAddonByID(id);
+ await engine?.uninstall();
+ } catch {}
+ }
+ };
+ if (currentVersion < 2) {
+ await dropAddons([
+ "brave(a)search.mozilla.org",
+ "ddg(a)search.mozilla.org",
+ "ddg-html(a)search.mozilla.org",
+ "metager(a)search.mozilla.org",
+ "mojeek(a)search.mozilla.org",
+ "mullvad-leta(a)search.mozilla.org",
+ "startpage(a)search.mozilla.org",
+ ]);
+ }
+
+ Services.prefs.setIntPref(MIGRATION_PREF, MB_MIGRATION_VERSION);
+ },
};
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/a57…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/a57…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! TB 43006: Disable RFP for Font Visibility on Android
by morgan (@morgan) 08 Sep '25
by morgan (@morgan) 08 Sep '25
08 Sep '25
morgan pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
dc42afa4 by Pier Angelo Vendrame at 2025-09-08T13:51:04+00:00
fixup! TB 43006: Disable RFP for Font Visibility on Android
TB 43943: Refactor fontvis exclusion on Android.
Rather than updating Document, we can use
nsRFPService::HandleExeptionalRFPTargets.
- - - - -
2 changed files:
- dom/base/Document.cpp
- toolkit/components/resistfingerprinting/nsRFPService.cpp
Changes:
=====================================
dom/base/Document.cpp
=====================================
@@ -17308,12 +17308,6 @@ bool Document::RecomputeResistFingerprinting(bool aForceRefreshRTPCallerType) {
}
bool Document::ShouldResistFingerprinting(RFPTarget aTarget) const {
-#ifdef ANDROID
- if (aTarget == RFPTarget::FontVisibilityBaseSystem ||
- aTarget == RFPTarget::FontVisibilityLangPack) {
- return false;
- }
-#endif
return mShouldResistFingerprinting &&
nsRFPService::IsRFPEnabledFor(this->IsInPrivateBrowsing(), aTarget,
mOverriddenFingerprintingSettings);
=====================================
toolkit/components/resistfingerprinting/nsRFPService.cpp
=====================================
@@ -310,6 +310,13 @@ Maybe<bool> nsRFPService::HandleExeptionalRFPTargets(
}
#endif
+#ifdef ANDROID
+ if (aTarget == RFPTarget::FontVisibilityBaseSystem ||
+ aTarget == RFPTarget::FontVisibilityLangPack) {
+ return Some(false);
+ }
+#endif
+
return Nothing();
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/dc42afa…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/dc42afa…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
c735c7ed by Pier Angelo Vendrame at 2025-09-08T15:51:22+02:00
fixup! Firefox preference overrides.
BB 43950: Disable HEVC.
HEVC support can be used for fingerprinting, as it's hardware-dependent
on some systems, or depends on distribution support/installed packages
for Linux.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -483,6 +483,9 @@ pref("gfx.offscreencanvas.enabled", false);
pref("dom.disable_window_move_resize", true);
// Set video VP9 to 0 for everyone (bug 22548)
pref("media.benchmark.vp9.threshold", 0);
+// tor-browser#43950: Disable HEVC, as it will reveal lacking hardware support,
+// or differences in installed codec for Linux systems.
+pref("media.hevc.enabled", false);
pref("privacy.resistFingerprinting.block_mozAddonManager", true); // Bug 26114
pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API
// tor-browser#42043: Stop reporting device IDs (and spoof their number without
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/c73…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/c73…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
53201cbe by Pier Angelo Vendrame at 2025-09-08T15:49:55+02:00
fixup! Firefox preference overrides.
BB 43950: Disable HEVC.
HEVC support can be used for fingerprinting, as it's hardware-dependent
on some systems, or depends on distribution support/installed packages
for Linux.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -483,6 +483,9 @@ pref("gfx.offscreencanvas.enabled", false);
pref("dom.disable_window_move_resize", true);
// Set video VP9 to 0 for everyone (bug 22548)
pref("media.benchmark.vp9.threshold", 0);
+// tor-browser#43950: Disable HEVC, as it will reveal lacking hardware support,
+// or differences in installed codec for Linux systems.
+pref("media.hevc.enabled", false);
pref("privacy.resistFingerprinting.block_mozAddonManager", true); // Bug 26114
pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API
// tor-browser#42043: Stop reporting device IDs (and spoof their number without
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/53201cb…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/53201cb…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] fixup! Firefox preference overrides.
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
c02477fa by Pier Angelo Vendrame at 2025-09-08T13:44:13+00:00
fixup! Firefox preference overrides.
BB 43950: Disable HEVC.
HEVC support can be used for fingerprinting, as it's hardware-dependent
on some systems, or depends on distribution support/installed packages
for Linux.
- - - - -
1 changed file:
- browser/app/profile/001-base-profile.js
Changes:
=====================================
browser/app/profile/001-base-profile.js
=====================================
@@ -483,6 +483,9 @@ pref("gfx.offscreencanvas.enabled", false);
pref("dom.disable_window_move_resize", true);
// Set video VP9 to 0 for everyone (bug 22548)
pref("media.benchmark.vp9.threshold", 0);
+// tor-browser#43950: Disable HEVC, as it will reveal lacking hardware support,
+// or differences in installed codec for Linux systems.
+pref("media.hevc.enabled", false);
pref("privacy.resistFingerprinting.block_mozAddonManager", true); // Bug 26114
pref("dom.webmidi.enabled", false); // Bug 41398: Disable Web MIDI API
// tor-browser#42043: Stop reporting device IDs (and spoof their number without
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c02477f…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/c02477f…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] 3 commits: fixup! BB 42019: Empty browser's clipboard on browser shutdown
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
4c83f2db by Pier Angelo Vendrame at 2025-09-08T12:32:21+02:00
fixup! BB 42019: Empty browser's clipboard on browser shutdown
BB 43770: Follow upstream's BrowserGlue simplifications.
ClipboardPrivacy to its own file and initialize it from the manifest,
rather than from BrowserGlue.
- - - - -
4b7d805c by Pier Angelo Vendrame at 2025-09-08T12:32:32+02:00
fixup! BB 40925: Implemented the Security Level component
BB 43770: Follow upstream's BrowserGlue simplifications.
Initialize the security level notification from the manifest.
Also, since it was the only occurrence of the file path, move it to the
moz-src:// scheme.
- - - - -
7a6cc7a1 by Pier Angelo Vendrame at 2025-09-08T12:35:10+02:00
fixup! BB 42027: Base Browser migration procedures.
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
5 changed files:
- browser/components/BrowserComponents.manifest
- browser/components/BrowserGlue.sys.mjs
- browser/components/ProfileDataUpgrader.sys.mjs
- + browser/modules/ClipboardPrivacy.sys.mjs
- browser/modules/moz.build
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -50,6 +50,8 @@ category browser-first-window-ready resource://gre/modules/CaptchaDetectionPingU
category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs SandboxUtils.maybeWarnAboutMissingUserNamespaces
#endif
#endif
+category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
=====================================
browser/components/BrowserGlue.sys.mjs
=====================================
@@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
AWToolbarButton: "resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs",
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
- AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
BackupService: "resource:///modules/backup/BackupService.sys.mjs",
BrowserSearchTelemetry:
"moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs",
@@ -61,8 +60,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
SearchSERPTelemetry:
"moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs",
- SecurityLevelRestartNotification:
- "resource:///modules/SecurityLevelRestartNotification.sys.mjs",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs",
@@ -106,170 +103,6 @@ if (AppConstants.ENABLE_WEBDRIVER) {
const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
-// Empty clipboard content from private windows on exit
-// (tor-browser#42154)
-const ClipboardPrivacy = {
- _lastClipboardHash: null,
- _globalActivation: false,
- _isPrivateClipboard: false,
- _hasher: null,
- _shuttingDown: false,
- _log: null,
-
- _createTransferable() {
- const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
- Ci.nsITransferable
- );
- trans.init(null);
- return trans;
- },
- _computeClipboardHash() {
- const flavors = ["text/x-moz-url", "text/plain"];
- if (
- !Services.clipboard.hasDataMatchingFlavors(
- flavors,
- Ci.nsIClipboard.kGlobalClipboard
- )
- ) {
- return null;
- }
- const trans = this._createTransferable();
- flavors.forEach(trans.addDataFlavor);
- try {
- Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
- const clipboardContent = {};
- trans.getAnyTransferData({}, clipboardContent);
- const { data } = clipboardContent.value.QueryInterface(
- Ci.nsISupportsString
- );
- const bytes = new TextEncoder().encode(data);
- const hasher = (this._hasher ||= Cc[
- "@mozilla.org/security/hash;1"
- ].createInstance(Ci.nsICryptoHash));
- hasher.init(hasher.SHA256);
- hasher.update(bytes, bytes.length);
- return hasher.finish(true);
- } catch (e) {}
- return null;
- },
-
- startup() {
- this._log = console.createInstance({
- prefix: "ClipboardPrivacy",
- });
- this._lastClipboardHash = this._computeClipboardHash();
-
- // Here we track changes in active window / application,
- // by filtering focus events and window closures.
- const handleActivation = (win, activation) => {
- if (activation) {
- if (!this._globalActivation) {
- // focus changed within this window, bail out.
- return;
- }
- this._globalActivation = false;
- } else if (!Services.focus.activeWindow) {
- // focus is leaving this window:
- // let's track whether it remains within the browser.
- lazy.setTimeout(() => {
- this._globalActivation = !Services.focus.activeWindow;
- }, 100);
- }
-
- const checkClipboardContent = () => {
- const clipboardHash = this._computeClipboardHash();
- if (clipboardHash !== this._lastClipboardHash) {
- this._isPrivateClipboard =
- !activation &&
- (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
- lazy.PrivateBrowsingUtils.isWindowPrivate(win));
- this._lastClipboardHash = clipboardHash;
- this._log.debug(
- `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
- );
- }
- };
-
- if (win.closed) {
- checkClipboardContent();
- } else {
- // defer clipboard access on DOM events to work-around tor-browser#42306
- lazy.setTimeout(checkClipboardContent, 0);
- }
- };
- const focusListener = e =>
- e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
- const initWindow = win => {
- for (const e of ["focusin", "focusout"]) {
- win.addEventListener(e, focusListener);
- }
- };
- for (const w of Services.ww.getWindowEnumerator()) {
- initWindow(w);
- }
- Services.ww.registerNotification((win, event) => {
- switch (event) {
- case "domwindowopened":
- initWindow(win);
- break;
- case "domwindowclosed":
- handleActivation(win, false);
- if (
- this._isPrivateClipboard &&
- lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
- (this._shuttingDown ||
- !Array.from(Services.ww.getWindowEnumerator()).find(
- w =>
- lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
- // We need to filter out the HIDDEN WebExtensions window,
- // which might be private as well but is not UI-relevant.
- !w.location.href.startsWith("chrome://extensions/")
- ))
- ) {
- // no more private windows, empty private content if needed
- this.emptyPrivate();
- }
- }
- });
-
- lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
- "ClipboardPrivacy: removing private data",
- () => {
- this._shuttingDown = true;
- this.emptyPrivate();
- }
- );
- },
- emptyPrivate() {
- if (
- this._isPrivateClipboard &&
- !Services.prefs.getBoolPref(
- "browser.privatebrowsing.preserveClipboard",
- false
- ) &&
- this._lastClipboardHash === this._computeClipboardHash()
- ) {
- // nsIClipboard.emptyClipboard() does nothing in Wayland:
- // we'll set an empty string as a work-around.
- const trans = this._createTransferable();
- const flavor = "text/plain";
- trans.addDataFlavor(flavor);
- const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance(
- Ci.nsISupportsString
- );
- emptyString.data = "";
- trans.setTransferData(flavor, emptyString);
- const { clipboard } = Services,
- { kGlobalClipboard } = clipboard;
- clipboard.setData(trans, null, kGlobalClipboard);
- clipboard.emptyClipboard(kGlobalClipboard);
- this._lastClipboardHash = null;
- this._isPrivateClipboard = false;
- this._log.info("Private clipboard emptied.");
- }
- },
-};
-
ChromeUtils.defineLazyGetter(
lazy,
"WeaveService",
@@ -596,9 +429,7 @@ BrowserGlue.prototype = {
// handle any UI migration
this._migrateUI();
-
- // Base Browser-specific version of _migrateUI.
- this._migrateUIBB();
+ lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile);
// Mullvad Browser-specific version of _migrateUI.
this._migrateUIMB();
@@ -987,10 +818,6 @@ BrowserGlue.prototype = {
lazy.WeaveService.init();
}
- lazy.SecurityLevelRestartNotification.ready();
-
- ClipboardPrivacy.startup();
-
lazy.BrowserUtils.callModulesFromCategory(
{
categoryName: "browser-first-window-ready",
@@ -1827,83 +1654,6 @@ BrowserGlue.prototype = {
}
},
- _migrateUIBB() {
- // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override
- // for tor-browser#41739.
- // Version 2: 14.0a5: Reset the privacy tracking headers preferences since
- // the UI is hidden. tor-browser#42777.
- // Also, do not set
- // dom.security.https_only_mode_send_http_background_request in
- // the security level anymore (tor-browser#42149).
- // Also, reset security.xfocsp.errorReporting.automatic since we
- // hid its neterror checkbox. tor-browser#42653.
- // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070.
- // Version 4: 15.0a2: Drop ML components. tor-browser#44045.
- const MIGRATION_VERSION = 4;
- const MIGRATION_PREF = "basebrowser.migration.version";
- if (this._isNewProfile) {
- // Do not migrate fresh profiles
- Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
- return;
- } else if (this._isNewProfile === undefined) {
- // If this happens, check if upstream updated their function and do not
- // set this member anymore!
- console.error("_migrateUIBB: this._isNewProfile is undefined.");
- }
- // We do not care whether this is a new or old profile, since in version 1
- // we just quickly clear a user preference, which should not do anything to
- // new profiles.
- // Shall we ever raise the version number and have a watershed, we can add
- // a check easily (any version > 0 will be an old profile).
- const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
- if (currentVersion < 1) {
- Services.prefs.clearUserPref(
- "layout.css.prefers-color-scheme.content-override"
- );
- }
- if (currentVersion < 2) {
- for (const prefName of [
- "privacy.globalprivacycontrol.enabled",
- "privacy.donottrackheader.enabled",
- // Telemetry preference for if the user changed the value.
- "privacy.globalprivacycontrol.was_ever_enabled",
- // The next two preferences have no corresponding UI, but are related.
- "privacy.globalprivacycontrol.functionality.enabled",
- "privacy.globalprivacycontrol.pbmode.enabled",
- "dom.security.https_only_mode_send_http_background_request",
- "security.xfocsp.errorReporting.automatic",
- ]) {
- Services.prefs.clearUserPref(prefName);
- }
- }
- if (currentVersion < 3) {
- Services.prefs.clearUserPref("general.smoothScroll");
- }
- if (currentVersion < 4) {
- for (const prefName of [
- "browser.translations.enable",
- "browser.ml.enable",
- "browser.ml.chat.enabled",
- "browser.ml.linkPreview.enabled",
- "browser.tabs.groups.smart.enabled",
- "browser.tabs.groups.smart.userEnabled",
- "extensions.ml.enabled",
- "pdfjs.enableAltText",
- "pdfjs.enableAltTextForEnglish",
- "pdfjs.enableGuessAltText",
- "pdfjs.enableAltTextModelDownload",
- "browser.urlbar.quicksuggest.mlEnabled",
- "places.semanticHistory.featureGate",
- ]) {
- // Preferences are locked. Do not want user values to linger in the
- // user's profile and become active if these preferences become unlocked
- // in the future.
- Services.prefs.clearUserPref(prefName);
- }
- }
- Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
- },
-
// Use this method for any MB migration that can be run just before showing
// the UI.
// Anything that critically needs to be migrated earlier should not use this.
=====================================
browser/components/ProfileDataUpgrader.sys.mjs
=====================================
@@ -900,4 +900,78 @@ export let ProfileDataUpgrader = {
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", newVersion);
},
+
+ upgradeBB(isNewProfile) {
+ // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override
+ // for tor-browser#41739.
+ // Version 2: 14.0a5: Reset the privacy tracking headers preferences since
+ // the UI is hidden. tor-browser#42777.
+ // Also, do not set
+ // dom.security.https_only_mode_send_http_background_request in
+ // the security level anymore (tor-browser#42149).
+ // Also, reset security.xfocsp.errorReporting.automatic since we
+ // hid its neterror checkbox. tor-browser#42653.
+ // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070.
+ // Version 4: 15.0a2: Drop ML components. tor-browser#44045.
+ const MIGRATION_VERSION = 4;
+ const MIGRATION_PREF = "basebrowser.migration.version";
+
+ if (isNewProfile) {
+ // Do not migrate fresh profiles
+ Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
+ return;
+ } else if (isNewProfile === undefined) {
+ // If this happens, check if upstream updated their function and do not
+ // set this member anymore!
+ console.error("upgradeBB: isNewProfile is undefined.");
+ }
+
+ const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
+ if (currentVersion < 1) {
+ Services.prefs.clearUserPref(
+ "layout.css.prefers-color-scheme.content-override"
+ );
+ }
+ if (currentVersion < 2) {
+ for (const prefName of [
+ "privacy.globalprivacycontrol.enabled",
+ "privacy.donottrackheader.enabled",
+ // Telemetry preference for if the user changed the value.
+ "privacy.globalprivacycontrol.was_ever_enabled",
+ // The next two preferences have no corresponding UI, but are related.
+ "privacy.globalprivacycontrol.functionality.enabled",
+ "privacy.globalprivacycontrol.pbmode.enabled",
+ "dom.security.https_only_mode_send_http_background_request",
+ "security.xfocsp.errorReporting.automatic",
+ ]) {
+ Services.prefs.clearUserPref(prefName);
+ }
+ }
+ if (currentVersion < 3) {
+ Services.prefs.clearUserPref("general.smoothScroll");
+ }
+ if (currentVersion < 4) {
+ for (const prefName of [
+ "browser.translations.enable",
+ "browser.ml.enable",
+ "browser.ml.chat.enabled",
+ "browser.ml.linkPreview.enabled",
+ "browser.tabs.groups.smart.enabled",
+ "browser.tabs.groups.smart.userEnabled",
+ "extensions.ml.enabled",
+ "pdfjs.enableAltText",
+ "pdfjs.enableAltTextForEnglish",
+ "pdfjs.enableGuessAltText",
+ "pdfjs.enableAltTextModelDownload",
+ "browser.urlbar.quicksuggest.mlEnabled",
+ "places.semanticHistory.featureGate",
+ ]) {
+ // Preferences are locked. Do not want user values to linger in the
+ // user's profile and become active if these preferences become unlocked
+ // in the future.
+ Services.prefs.clearUserPref(prefName);
+ }
+ }
+ Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
+ },
};
=====================================
browser/modules/ClipboardPrivacy.sys.mjs
=====================================
@@ -0,0 +1,178 @@
+/* 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/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+});
+
+/**
+ * Empty clipboard content from private windows on exit.
+ *
+ * See tor-browser#42154.
+ */
+export const ClipboardPrivacy = {
+ _lastClipboardHash: null,
+ _globalActivation: false,
+ _isPrivateClipboard: false,
+ _hasher: null,
+ _shuttingDown: false,
+ _log: null,
+
+ _createTransferable() {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ return trans;
+ },
+ _computeClipboardHash() {
+ const flavors = ["text/x-moz-url", "text/plain"];
+ if (
+ !Services.clipboard.hasDataMatchingFlavors(
+ flavors,
+ Ci.nsIClipboard.kGlobalClipboard
+ )
+ ) {
+ return null;
+ }
+ const trans = this._createTransferable();
+ flavors.forEach(trans.addDataFlavor);
+ try {
+ Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
+ const clipboardContent = {};
+ trans.getAnyTransferData({}, clipboardContent);
+ const { data } = clipboardContent.value.QueryInterface(
+ Ci.nsISupportsString
+ );
+ const bytes = new TextEncoder().encode(data);
+ const hasher = (this._hasher ||= Cc[
+ "@mozilla.org/security/hash;1"
+ ].createInstance(Ci.nsICryptoHash));
+ hasher.init(hasher.SHA256);
+ hasher.update(bytes, bytes.length);
+ return hasher.finish(true);
+ } catch (e) {}
+ return null;
+ },
+
+ init() {
+ this._log = console.createInstance({
+ prefix: "ClipboardPrivacy",
+ });
+ this._lastClipboardHash = this._computeClipboardHash();
+
+ // Here we track changes in active window / application,
+ // by filtering focus events and window closures.
+ const handleActivation = (win, activation) => {
+ if (activation) {
+ if (!this._globalActivation) {
+ // focus changed within this window, bail out.
+ return;
+ }
+ this._globalActivation = false;
+ } else if (!Services.focus.activeWindow) {
+ // focus is leaving this window:
+ // let's track whether it remains within the browser.
+ lazy.setTimeout(() => {
+ this._globalActivation = !Services.focus.activeWindow;
+ }, 100);
+ }
+
+ const checkClipboardContent = () => {
+ const clipboardHash = this._computeClipboardHash();
+ if (clipboardHash !== this._lastClipboardHash) {
+ this._isPrivateClipboard =
+ !activation &&
+ (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
+ lazy.PrivateBrowsingUtils.isWindowPrivate(win));
+ this._lastClipboardHash = clipboardHash;
+ this._log.debug(
+ `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
+ );
+ }
+ };
+
+ if (win.closed) {
+ checkClipboardContent();
+ } else {
+ // defer clipboard access on DOM events to work-around tor-browser#42306
+ lazy.setTimeout(checkClipboardContent, 0);
+ }
+ };
+ const focusListener = e =>
+ e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
+ const initWindow = win => {
+ for (const e of ["focusin", "focusout"]) {
+ win.addEventListener(e, focusListener);
+ }
+ };
+ for (const w of Services.ww.getWindowEnumerator()) {
+ initWindow(w);
+ }
+ Services.ww.registerNotification((win, event) => {
+ switch (event) {
+ case "domwindowopened":
+ initWindow(win);
+ break;
+ case "domwindowclosed":
+ handleActivation(win, false);
+ if (
+ this._isPrivateClipboard &&
+ lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
+ (this._shuttingDown ||
+ !Array.from(Services.ww.getWindowEnumerator()).find(
+ w =>
+ lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
+ // We need to filter out the HIDDEN WebExtensions window,
+ // which might be private as well but is not UI-relevant.
+ !w.location.href.startsWith("chrome://extensions/")
+ ))
+ ) {
+ // no more private windows, empty private content if needed
+ this.emptyPrivate();
+ }
+ }
+ });
+
+ lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
+ "ClipboardPrivacy: removing private data",
+ () => {
+ this._shuttingDown = true;
+ this.emptyPrivate();
+ }
+ );
+ },
+ emptyPrivate() {
+ if (
+ this._isPrivateClipboard &&
+ !Services.prefs.getBoolPref(
+ "browser.privatebrowsing.preserveClipboard",
+ false
+ ) &&
+ this._lastClipboardHash === this._computeClipboardHash()
+ ) {
+ // nsIClipboard.emptyClipboard() does nothing in Wayland:
+ // we'll set an empty string as a work-around.
+ const trans = this._createTransferable();
+ const flavor = "text/plain";
+ trans.addDataFlavor(flavor);
+ const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ emptyString.data = "";
+ trans.setTransferData(flavor, emptyString);
+ const { clipboard } = Services,
+ { kGlobalClipboard } = clipboard;
+ clipboard.setData(trans, null, kGlobalClipboard);
+ clipboard.emptyClipboard(kGlobalClipboard);
+ this._lastClipboardHash = null;
+ this._isPrivateClipboard = false;
+ this._log.info("Private clipboard emptied.");
+ }
+ },
+};
=====================================
browser/modules/moz.build
=====================================
@@ -136,7 +136,6 @@ EXTRA_JS_MODULES += [
"PopupBlockerObserver.sys.mjs",
"ProcessHangMonitor.sys.mjs",
"Sanitizer.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
"SelectionChangedMenulist.sys.mjs",
"SharingUtils.sys.mjs",
"SiteDataManager.sys.mjs",
@@ -149,6 +148,8 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
+ "ClipboardPrivacy.sys.mjs",
+ "SecurityLevelRestartNotification.sys.mjs",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/b8…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/b8…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] 3 commits: fixup! BB 42019: Empty browser's clipboard on browser shutdown
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
24214f3e by Pier Angelo Vendrame at 2025-09-08T12:12:36+02:00
fixup! BB 42019: Empty browser's clipboard on browser shutdown
BB 43770: Follow upstream's BrowserGlue simplifications.
ClipboardPrivacy to its own file and initialize it from the manifest,
rather than from BrowserGlue.
- - - - -
92770da8 by Pier Angelo Vendrame at 2025-09-08T12:16:00+02:00
fixup! BB 40925: Implemented the Security Level component
BB 43770: Follow upstream's BrowserGlue simplifications.
Initialize the security level notification from the manifest.
Also, since it was the only occurrence of the file path, move it to the
moz-src:// scheme.
- - - - -
88bb3bc2 by Pier Angelo Vendrame at 2025-09-08T12:25:21+02:00
fixup! BB 42027: Base Browser migration procedures.
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
5 changed files:
- browser/components/BrowserComponents.manifest
- browser/components/BrowserGlue.sys.mjs
- browser/components/ProfileDataUpgrader.sys.mjs
- + browser/modules/ClipboardPrivacy.sys.mjs
- browser/modules/moz.build
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -50,6 +50,8 @@ category browser-first-window-ready resource://gre/modules/CaptchaDetectionPingU
category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs SandboxUtils.maybeWarnAboutMissingUserNamespaces
#endif
#endif
+category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
=====================================
browser/components/BrowserGlue.sys.mjs
=====================================
@@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
AWToolbarButton: "resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs",
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
- AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
BackupService: "resource:///modules/backup/BackupService.sys.mjs",
BrowserSearchTelemetry:
"moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs",
@@ -61,8 +60,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
SearchSERPTelemetry:
"moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs",
- SecurityLevelRestartNotification:
- "resource:///modules/SecurityLevelRestartNotification.sys.mjs",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs",
@@ -106,170 +103,6 @@ if (AppConstants.ENABLE_WEBDRIVER) {
const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
-// Empty clipboard content from private windows on exit
-// (tor-browser#42154)
-const ClipboardPrivacy = {
- _lastClipboardHash: null,
- _globalActivation: false,
- _isPrivateClipboard: false,
- _hasher: null,
- _shuttingDown: false,
- _log: null,
-
- _createTransferable() {
- const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
- Ci.nsITransferable
- );
- trans.init(null);
- return trans;
- },
- _computeClipboardHash() {
- const flavors = ["text/x-moz-url", "text/plain"];
- if (
- !Services.clipboard.hasDataMatchingFlavors(
- flavors,
- Ci.nsIClipboard.kGlobalClipboard
- )
- ) {
- return null;
- }
- const trans = this._createTransferable();
- flavors.forEach(trans.addDataFlavor);
- try {
- Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
- const clipboardContent = {};
- trans.getAnyTransferData({}, clipboardContent);
- const { data } = clipboardContent.value.QueryInterface(
- Ci.nsISupportsString
- );
- const bytes = new TextEncoder().encode(data);
- const hasher = (this._hasher ||= Cc[
- "@mozilla.org/security/hash;1"
- ].createInstance(Ci.nsICryptoHash));
- hasher.init(hasher.SHA256);
- hasher.update(bytes, bytes.length);
- return hasher.finish(true);
- } catch (e) {}
- return null;
- },
-
- startup() {
- this._log = console.createInstance({
- prefix: "ClipboardPrivacy",
- });
- this._lastClipboardHash = this._computeClipboardHash();
-
- // Here we track changes in active window / application,
- // by filtering focus events and window closures.
- const handleActivation = (win, activation) => {
- if (activation) {
- if (!this._globalActivation) {
- // focus changed within this window, bail out.
- return;
- }
- this._globalActivation = false;
- } else if (!Services.focus.activeWindow) {
- // focus is leaving this window:
- // let's track whether it remains within the browser.
- lazy.setTimeout(() => {
- this._globalActivation = !Services.focus.activeWindow;
- }, 100);
- }
-
- const checkClipboardContent = () => {
- const clipboardHash = this._computeClipboardHash();
- if (clipboardHash !== this._lastClipboardHash) {
- this._isPrivateClipboard =
- !activation &&
- (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
- lazy.PrivateBrowsingUtils.isWindowPrivate(win));
- this._lastClipboardHash = clipboardHash;
- this._log.debug(
- `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
- );
- }
- };
-
- if (win.closed) {
- checkClipboardContent();
- } else {
- // defer clipboard access on DOM events to work-around tor-browser#42306
- lazy.setTimeout(checkClipboardContent, 0);
- }
- };
- const focusListener = e =>
- e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
- const initWindow = win => {
- for (const e of ["focusin", "focusout"]) {
- win.addEventListener(e, focusListener);
- }
- };
- for (const w of Services.ww.getWindowEnumerator()) {
- initWindow(w);
- }
- Services.ww.registerNotification((win, event) => {
- switch (event) {
- case "domwindowopened":
- initWindow(win);
- break;
- case "domwindowclosed":
- handleActivation(win, false);
- if (
- this._isPrivateClipboard &&
- lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
- (this._shuttingDown ||
- !Array.from(Services.ww.getWindowEnumerator()).find(
- w =>
- lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
- // We need to filter out the HIDDEN WebExtensions window,
- // which might be private as well but is not UI-relevant.
- !w.location.href.startsWith("chrome://extensions/")
- ))
- ) {
- // no more private windows, empty private content if needed
- this.emptyPrivate();
- }
- }
- });
-
- lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
- "ClipboardPrivacy: removing private data",
- () => {
- this._shuttingDown = true;
- this.emptyPrivate();
- }
- );
- },
- emptyPrivate() {
- if (
- this._isPrivateClipboard &&
- !Services.prefs.getBoolPref(
- "browser.privatebrowsing.preserveClipboard",
- false
- ) &&
- this._lastClipboardHash === this._computeClipboardHash()
- ) {
- // nsIClipboard.emptyClipboard() does nothing in Wayland:
- // we'll set an empty string as a work-around.
- const trans = this._createTransferable();
- const flavor = "text/plain";
- trans.addDataFlavor(flavor);
- const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance(
- Ci.nsISupportsString
- );
- emptyString.data = "";
- trans.setTransferData(flavor, emptyString);
- const { clipboard } = Services,
- { kGlobalClipboard } = clipboard;
- clipboard.setData(trans, null, kGlobalClipboard);
- clipboard.emptyClipboard(kGlobalClipboard);
- this._lastClipboardHash = null;
- this._isPrivateClipboard = false;
- this._log.info("Private clipboard emptied.");
- }
- },
-};
-
ChromeUtils.defineLazyGetter(
lazy,
"WeaveService",
@@ -596,9 +429,7 @@ BrowserGlue.prototype = {
// handle any UI migration
this._migrateUI();
-
- // Base Browser-specific version of _migrateUI.
- this._migrateUIBB();
+ lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile);
if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) {
lazy.PdfJs.checkIsDefault(this._isNewProfile);
@@ -984,10 +815,6 @@ BrowserGlue.prototype = {
lazy.WeaveService.init();
}
- lazy.SecurityLevelRestartNotification.ready();
-
- ClipboardPrivacy.startup();
-
lazy.BrowserUtils.callModulesFromCategory(
{
categoryName: "browser-first-window-ready",
@@ -1823,83 +1650,6 @@ BrowserGlue.prototype = {
}
},
- _migrateUIBB() {
- // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override
- // for tor-browser#41739.
- // Version 2: 14.0a5: Reset the privacy tracking headers preferences since
- // the UI is hidden. tor-browser#42777.
- // Also, do not set
- // dom.security.https_only_mode_send_http_background_request in
- // the security level anymore (tor-browser#42149).
- // Also, reset security.xfocsp.errorReporting.automatic since we
- // hid its neterror checkbox. tor-browser#42653.
- // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070.
- // Version 4: 15.0a2: Drop ML components. tor-browser#44045.
- const MIGRATION_VERSION = 4;
- const MIGRATION_PREF = "basebrowser.migration.version";
- if (this._isNewProfile) {
- // Do not migrate fresh profiles
- Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
- return;
- } else if (this._isNewProfile === undefined) {
- // If this happens, check if upstream updated their function and do not
- // set this member anymore!
- console.error("_migrateUIBB: this._isNewProfile is undefined.");
- }
- // We do not care whether this is a new or old profile, since in version 1
- // we just quickly clear a user preference, which should not do anything to
- // new profiles.
- // Shall we ever raise the version number and have a watershed, we can add
- // a check easily (any version > 0 will be an old profile).
- const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
- if (currentVersion < 1) {
- Services.prefs.clearUserPref(
- "layout.css.prefers-color-scheme.content-override"
- );
- }
- if (currentVersion < 2) {
- for (const prefName of [
- "privacy.globalprivacycontrol.enabled",
- "privacy.donottrackheader.enabled",
- // Telemetry preference for if the user changed the value.
- "privacy.globalprivacycontrol.was_ever_enabled",
- // The next two preferences have no corresponding UI, but are related.
- "privacy.globalprivacycontrol.functionality.enabled",
- "privacy.globalprivacycontrol.pbmode.enabled",
- "dom.security.https_only_mode_send_http_background_request",
- "security.xfocsp.errorReporting.automatic",
- ]) {
- Services.prefs.clearUserPref(prefName);
- }
- }
- if (currentVersion < 3) {
- Services.prefs.clearUserPref("general.smoothScroll");
- }
- if (currentVersion < 4) {
- for (const prefName of [
- "browser.translations.enable",
- "browser.ml.enable",
- "browser.ml.chat.enabled",
- "browser.ml.linkPreview.enabled",
- "browser.tabs.groups.smart.enabled",
- "browser.tabs.groups.smart.userEnabled",
- "extensions.ml.enabled",
- "pdfjs.enableAltText",
- "pdfjs.enableAltTextForEnglish",
- "pdfjs.enableGuessAltText",
- "pdfjs.enableAltTextModelDownload",
- "browser.urlbar.quicksuggest.mlEnabled",
- "places.semanticHistory.featureGate",
- ]) {
- // Preferences are locked. Do not want user values to linger in the
- // user's profile and become active if these preferences become unlocked
- // in the future.
- Services.prefs.clearUserPref(prefName);
- }
- }
- Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
- },
-
async _showUpgradeDialog() {
const data = await lazy.OnboardingMessageProvider.getUpgradeMessage();
const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow();
=====================================
browser/components/ProfileDataUpgrader.sys.mjs
=====================================
@@ -900,4 +900,78 @@ export let ProfileDataUpgrader = {
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", newVersion);
},
+
+ upgradeBB(isNewProfile) {
+ // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override
+ // for tor-browser#41739.
+ // Version 2: 14.0a5: Reset the privacy tracking headers preferences since
+ // the UI is hidden. tor-browser#42777.
+ // Also, do not set
+ // dom.security.https_only_mode_send_http_background_request in
+ // the security level anymore (tor-browser#42149).
+ // Also, reset security.xfocsp.errorReporting.automatic since we
+ // hid its neterror checkbox. tor-browser#42653.
+ // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070.
+ // Version 4: 15.0a2: Drop ML components. tor-browser#44045.
+ const MIGRATION_VERSION = 4;
+ const MIGRATION_PREF = "basebrowser.migration.version";
+
+ if (isNewProfile) {
+ // Do not migrate fresh profiles
+ Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
+ return;
+ } else if (isNewProfile === undefined) {
+ // If this happens, check if upstream updated their function and do not
+ // set this member anymore!
+ console.error("upgradeBB: isNewProfile is undefined.");
+ }
+
+ const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
+ if (currentVersion < 1) {
+ Services.prefs.clearUserPref(
+ "layout.css.prefers-color-scheme.content-override"
+ );
+ }
+ if (currentVersion < 2) {
+ for (const prefName of [
+ "privacy.globalprivacycontrol.enabled",
+ "privacy.donottrackheader.enabled",
+ // Telemetry preference for if the user changed the value.
+ "privacy.globalprivacycontrol.was_ever_enabled",
+ // The next two preferences have no corresponding UI, but are related.
+ "privacy.globalprivacycontrol.functionality.enabled",
+ "privacy.globalprivacycontrol.pbmode.enabled",
+ "dom.security.https_only_mode_send_http_background_request",
+ "security.xfocsp.errorReporting.automatic",
+ ]) {
+ Services.prefs.clearUserPref(prefName);
+ }
+ }
+ if (currentVersion < 3) {
+ Services.prefs.clearUserPref("general.smoothScroll");
+ }
+ if (currentVersion < 4) {
+ for (const prefName of [
+ "browser.translations.enable",
+ "browser.ml.enable",
+ "browser.ml.chat.enabled",
+ "browser.ml.linkPreview.enabled",
+ "browser.tabs.groups.smart.enabled",
+ "browser.tabs.groups.smart.userEnabled",
+ "extensions.ml.enabled",
+ "pdfjs.enableAltText",
+ "pdfjs.enableAltTextForEnglish",
+ "pdfjs.enableGuessAltText",
+ "pdfjs.enableAltTextModelDownload",
+ "browser.urlbar.quicksuggest.mlEnabled",
+ "places.semanticHistory.featureGate",
+ ]) {
+ // Preferences are locked. Do not want user values to linger in the
+ // user's profile and become active if these preferences become unlocked
+ // in the future.
+ Services.prefs.clearUserPref(prefName);
+ }
+ }
+ Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
+ },
};
=====================================
browser/modules/ClipboardPrivacy.sys.mjs
=====================================
@@ -0,0 +1,178 @@
+/* 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/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+});
+
+/**
+ * Empty clipboard content from private windows on exit.
+ *
+ * See tor-browser#42154.
+ */
+export const ClipboardPrivacy = {
+ _lastClipboardHash: null,
+ _globalActivation: false,
+ _isPrivateClipboard: false,
+ _hasher: null,
+ _shuttingDown: false,
+ _log: null,
+
+ _createTransferable() {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ return trans;
+ },
+ _computeClipboardHash() {
+ const flavors = ["text/x-moz-url", "text/plain"];
+ if (
+ !Services.clipboard.hasDataMatchingFlavors(
+ flavors,
+ Ci.nsIClipboard.kGlobalClipboard
+ )
+ ) {
+ return null;
+ }
+ const trans = this._createTransferable();
+ flavors.forEach(trans.addDataFlavor);
+ try {
+ Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
+ const clipboardContent = {};
+ trans.getAnyTransferData({}, clipboardContent);
+ const { data } = clipboardContent.value.QueryInterface(
+ Ci.nsISupportsString
+ );
+ const bytes = new TextEncoder().encode(data);
+ const hasher = (this._hasher ||= Cc[
+ "@mozilla.org/security/hash;1"
+ ].createInstance(Ci.nsICryptoHash));
+ hasher.init(hasher.SHA256);
+ hasher.update(bytes, bytes.length);
+ return hasher.finish(true);
+ } catch (e) {}
+ return null;
+ },
+
+ init() {
+ this._log = console.createInstance({
+ prefix: "ClipboardPrivacy",
+ });
+ this._lastClipboardHash = this._computeClipboardHash();
+
+ // Here we track changes in active window / application,
+ // by filtering focus events and window closures.
+ const handleActivation = (win, activation) => {
+ if (activation) {
+ if (!this._globalActivation) {
+ // focus changed within this window, bail out.
+ return;
+ }
+ this._globalActivation = false;
+ } else if (!Services.focus.activeWindow) {
+ // focus is leaving this window:
+ // let's track whether it remains within the browser.
+ lazy.setTimeout(() => {
+ this._globalActivation = !Services.focus.activeWindow;
+ }, 100);
+ }
+
+ const checkClipboardContent = () => {
+ const clipboardHash = this._computeClipboardHash();
+ if (clipboardHash !== this._lastClipboardHash) {
+ this._isPrivateClipboard =
+ !activation &&
+ (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
+ lazy.PrivateBrowsingUtils.isWindowPrivate(win));
+ this._lastClipboardHash = clipboardHash;
+ this._log.debug(
+ `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
+ );
+ }
+ };
+
+ if (win.closed) {
+ checkClipboardContent();
+ } else {
+ // defer clipboard access on DOM events to work-around tor-browser#42306
+ lazy.setTimeout(checkClipboardContent, 0);
+ }
+ };
+ const focusListener = e =>
+ e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
+ const initWindow = win => {
+ for (const e of ["focusin", "focusout"]) {
+ win.addEventListener(e, focusListener);
+ }
+ };
+ for (const w of Services.ww.getWindowEnumerator()) {
+ initWindow(w);
+ }
+ Services.ww.registerNotification((win, event) => {
+ switch (event) {
+ case "domwindowopened":
+ initWindow(win);
+ break;
+ case "domwindowclosed":
+ handleActivation(win, false);
+ if (
+ this._isPrivateClipboard &&
+ lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
+ (this._shuttingDown ||
+ !Array.from(Services.ww.getWindowEnumerator()).find(
+ w =>
+ lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
+ // We need to filter out the HIDDEN WebExtensions window,
+ // which might be private as well but is not UI-relevant.
+ !w.location.href.startsWith("chrome://extensions/")
+ ))
+ ) {
+ // no more private windows, empty private content if needed
+ this.emptyPrivate();
+ }
+ }
+ });
+
+ lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
+ "ClipboardPrivacy: removing private data",
+ () => {
+ this._shuttingDown = true;
+ this.emptyPrivate();
+ }
+ );
+ },
+ emptyPrivate() {
+ if (
+ this._isPrivateClipboard &&
+ !Services.prefs.getBoolPref(
+ "browser.privatebrowsing.preserveClipboard",
+ false
+ ) &&
+ this._lastClipboardHash === this._computeClipboardHash()
+ ) {
+ // nsIClipboard.emptyClipboard() does nothing in Wayland:
+ // we'll set an empty string as a work-around.
+ const trans = this._createTransferable();
+ const flavor = "text/plain";
+ trans.addDataFlavor(flavor);
+ const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ emptyString.data = "";
+ trans.setTransferData(flavor, emptyString);
+ const { clipboard } = Services,
+ { kGlobalClipboard } = clipboard;
+ clipboard.setData(trans, null, kGlobalClipboard);
+ clipboard.emptyClipboard(kGlobalClipboard);
+ this._lastClipboardHash = null;
+ this._isPrivateClipboard = false;
+ this._log.info("Private clipboard emptied.");
+ }
+ },
+};
=====================================
browser/modules/moz.build
=====================================
@@ -136,7 +136,6 @@ EXTRA_JS_MODULES += [
"PopupBlockerObserver.sys.mjs",
"ProcessHangMonitor.sys.mjs",
"Sanitizer.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
"SelectionChangedMenulist.sys.mjs",
"SharingUtils.sys.mjs",
"SiteDataManager.sys.mjs",
@@ -149,6 +148,8 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
+ "ClipboardPrivacy.sys.mjs",
+ "SecurityLevelRestartNotification.sys.mjs",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/a1886d…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/a1886d…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][tor-browser-140.2.0esr-15.0-1] 9 commits: fixup! BB 42019: Empty browser's clipboard on browser shutdown
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch tor-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
d5582e87 by Pier Angelo Vendrame at 2025-09-08T12:03:34+02:00
fixup! BB 42019: Empty browser's clipboard on browser shutdown
BB 43770: Follow upstream's BrowserGlue simplifications.
ClipboardPrivacy to its own file and initialize it from the manifest,
rather than from BrowserGlue.
- - - - -
d4c6e7a4 by Pier Angelo Vendrame at 2025-09-08T12:03:35+02:00
fixup! BB 40925: Implemented the Security Level component
BB 43770: Follow upstream's BrowserGlue simplifications.
Initialize the security level notification from the manifest.
Also, since it was the only occurrence of the file path, move it to the
moz-src:// scheme.
- - - - -
8eccdc93 by Pier Angelo Vendrame at 2025-09-08T12:03:35+02:00
fixup! TB 40933: Add tor-launcher functionality
BB 43770: Follow upstream's BrowserGlue simplifications.
Moved the call to firstWindowLoaded to the manifest.
- - - - -
167c0116 by Pier Angelo Vendrame at 2025-09-08T12:03:36+02:00
fixup! TB 8324: Prevent DNS proxy bypasses caused by Drag&Drop
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
b45bb7f1 by Pier Angelo Vendrame at 2025-09-08T12:03:37+02:00
fixup! TB 43405: Show a prompt whenever we fail to apply Tor settings.
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
ad267a06 by Pier Angelo Vendrame at 2025-09-08T12:03:37+02:00
fixup! TB 40458: Implement .tor.onion aliases
Use proper private method and members.
- - - - -
a05bd724 by Pier Angelo Vendrame at 2025-09-08T12:03:38+02:00
fixup! TB 40458: Implement .tor.onion aliases
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
a857af7e by Pier Angelo Vendrame at 2025-09-08T12:03:38+02:00
fixup! BB 42027: Base Browser migration procedures.
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
51ca3bdf by Pier Angelo Vendrame at 2025-09-08T12:03:39+02:00
fixup! TB 41435: Add a Tor Browser migration function
BB 43770: Follow upstream's BrowserGlue simplifications.
- - - - -
12 changed files:
- browser/components/BrowserComponents.manifest
- browser/components/BrowserGlue.sys.mjs
- browser/components/ProfileDataUpgrader.sys.mjs
- browser/components/onionservices/OnionAliasStore.sys.mjs
- browser/components/onionservices/moz.build
- browser/components/rulesets/RulesetsParent.sys.mjs
- + browser/modules/ClipboardPrivacy.sys.mjs
- browser/modules/moz.build
- dom/base/ContentAreaDropListener.sys.mjs
- toolkit/components/places/PlacesUtils.sys.mjs
- toolkit/components/tor-launcher/tor-launcher.manifest
- toolkit/modules/moz.build
Changes:
=====================================
browser/components/BrowserComponents.manifest
=====================================
@@ -51,6 +51,11 @@ category browser-first-window-ready resource://gre/modules/CaptchaDetectionPingU
category browser-first-window-ready resource://gre/modules/SandboxUtils.sys.mjs SandboxUtils.maybeWarnAboutMissingUserNamespaces
#endif
#endif
+category browser-first-window-ready moz-src:///browser/modules/ClipboardPrivacy.sys.mjs ClipboardPrivacy.init
+category browser-first-window-ready moz-src:///browser/modules/SecurityLevelRestartNotification.sys.mjs SecurityLevelRestartNotification.ready
+category browser-first-window-ready moz-src:///toolkit/modules/DragDropFilter.sys.mjs DragDropFilter.init
+category browser-first-window-ready moz-src:///browser/modules/TorSettingsNotification.sys.mjs TorSettingsNotification.ready
+category browser-first-window-ready moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs OnionAliasStore.init
category browser-idle-startup resource:///modules/PlacesUIUtils.sys.mjs PlacesUIUtils.unblockToolbars
category browser-idle-startup resource:///modules/BuiltInThemes.sys.mjs BuiltInThemes.ensureBuiltInThemes
@@ -94,5 +99,6 @@ category browser-quit-application-granted moz-src:///browser/components/search/S
category browser-quit-application-granted resource://gre/modules/UpdateListener.sys.mjs UpdateListener.reset
#endif
category browser-quit-application-granted resource:///modules/UrlbarSearchTermsPersistence.sys.mjs UrlbarSearchTermsPersistence.uninit
+category browser-quit-application-granted moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs OnionAliasStore.uninit
category search-service-notification moz-src:///browser/components/search/SearchUIUtils.sys.mjs SearchUIUtils.showSearchServiceNotification
=====================================
browser/components/BrowserGlue.sys.mjs
=====================================
@@ -12,7 +12,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
AWToolbarButton: "resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs",
ASRouter: "resource:///modules/asrouter/ASRouter.sys.mjs",
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
- AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
BackupService: "resource:///modules/backup/BackupService.sys.mjs",
BrowserSearchTelemetry:
"moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs",
@@ -31,7 +30,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
DistributionManagement: "resource:///modules/distribution.sys.mjs",
DownloadsViewableInternally:
"resource:///modules/DownloadsViewableInternally.sys.mjs",
- DragDropFilter: "resource://gre/modules/DragDropFilter.sys.mjs",
ExtensionsUI: "resource:///modules/ExtensionsUI.sys.mjs",
// FilePickerCrashed is used by the `listeners` object below.
// eslint-disable-next-line mozilla/valid-lazy
@@ -42,7 +40,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
MigrationUtils: "resource:///modules/MigrationUtils.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
- OnionAliasStore: "resource:///modules/OnionAliasStore.sys.mjs",
OnboardingMessageProvider:
"resource:///modules/asrouter/OnboardingMessageProvider.sys.mjs",
PageDataService: "resource:///modules/pagedata/PageDataService.sys.mjs",
@@ -63,8 +60,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.sys.mjs",
SearchSERPTelemetry:
"moz-src:///browser/components/search/SearchSERPTelemetry.sys.mjs",
- SecurityLevelRestartNotification:
- "resource:///modules/SecurityLevelRestartNotification.sys.mjs",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs",
@@ -75,11 +70,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
TelemetryReportingPolicy:
"resource://gre/modules/TelemetryReportingPolicy.sys.mjs",
TRRRacer: "resource:///modules/TRRPerformance.sys.mjs",
- TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
- TorConnectTopics: "resource://gre/modules/TorConnect.sys.mjs",
- TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
- TorSettingsNotification:
- "resource:///modules/TorSettingsNotification.sys.mjs",
WebChannel: "resource://gre/modules/WebChannel.sys.mjs",
WebProtocolHandlerRegistrar:
"resource:///modules/WebProtocolHandlerRegistrar.sys.mjs",
@@ -113,170 +103,6 @@ if (AppConstants.ENABLE_WEBDRIVER) {
const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
-// Empty clipboard content from private windows on exit
-// (tor-browser#42154)
-const ClipboardPrivacy = {
- _lastClipboardHash: null,
- _globalActivation: false,
- _isPrivateClipboard: false,
- _hasher: null,
- _shuttingDown: false,
- _log: null,
-
- _createTransferable() {
- const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
- Ci.nsITransferable
- );
- trans.init(null);
- return trans;
- },
- _computeClipboardHash() {
- const flavors = ["text/x-moz-url", "text/plain"];
- if (
- !Services.clipboard.hasDataMatchingFlavors(
- flavors,
- Ci.nsIClipboard.kGlobalClipboard
- )
- ) {
- return null;
- }
- const trans = this._createTransferable();
- flavors.forEach(trans.addDataFlavor);
- try {
- Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
- const clipboardContent = {};
- trans.getAnyTransferData({}, clipboardContent);
- const { data } = clipboardContent.value.QueryInterface(
- Ci.nsISupportsString
- );
- const bytes = new TextEncoder().encode(data);
- const hasher = (this._hasher ||= Cc[
- "@mozilla.org/security/hash;1"
- ].createInstance(Ci.nsICryptoHash));
- hasher.init(hasher.SHA256);
- hasher.update(bytes, bytes.length);
- return hasher.finish(true);
- } catch (e) {}
- return null;
- },
-
- startup() {
- this._log = console.createInstance({
- prefix: "ClipboardPrivacy",
- });
- this._lastClipboardHash = this._computeClipboardHash();
-
- // Here we track changes in active window / application,
- // by filtering focus events and window closures.
- const handleActivation = (win, activation) => {
- if (activation) {
- if (!this._globalActivation) {
- // focus changed within this window, bail out.
- return;
- }
- this._globalActivation = false;
- } else if (!Services.focus.activeWindow) {
- // focus is leaving this window:
- // let's track whether it remains within the browser.
- lazy.setTimeout(() => {
- this._globalActivation = !Services.focus.activeWindow;
- }, 100);
- }
-
- const checkClipboardContent = () => {
- const clipboardHash = this._computeClipboardHash();
- if (clipboardHash !== this._lastClipboardHash) {
- this._isPrivateClipboard =
- !activation &&
- (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
- lazy.PrivateBrowsingUtils.isWindowPrivate(win));
- this._lastClipboardHash = clipboardHash;
- this._log.debug(
- `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
- );
- }
- };
-
- if (win.closed) {
- checkClipboardContent();
- } else {
- // defer clipboard access on DOM events to work-around tor-browser#42306
- lazy.setTimeout(checkClipboardContent, 0);
- }
- };
- const focusListener = e =>
- e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
- const initWindow = win => {
- for (const e of ["focusin", "focusout"]) {
- win.addEventListener(e, focusListener);
- }
- };
- for (const w of Services.ww.getWindowEnumerator()) {
- initWindow(w);
- }
- Services.ww.registerNotification((win, event) => {
- switch (event) {
- case "domwindowopened":
- initWindow(win);
- break;
- case "domwindowclosed":
- handleActivation(win, false);
- if (
- this._isPrivateClipboard &&
- lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
- (this._shuttingDown ||
- !Array.from(Services.ww.getWindowEnumerator()).find(
- w =>
- lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
- // We need to filter out the HIDDEN WebExtensions window,
- // which might be private as well but is not UI-relevant.
- !w.location.href.startsWith("chrome://extensions/")
- ))
- ) {
- // no more private windows, empty private content if needed
- this.emptyPrivate();
- }
- }
- });
-
- lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
- "ClipboardPrivacy: removing private data",
- () => {
- this._shuttingDown = true;
- this.emptyPrivate();
- }
- );
- },
- emptyPrivate() {
- if (
- this._isPrivateClipboard &&
- !Services.prefs.getBoolPref(
- "browser.privatebrowsing.preserveClipboard",
- false
- ) &&
- this._lastClipboardHash === this._computeClipboardHash()
- ) {
- // nsIClipboard.emptyClipboard() does nothing in Wayland:
- // we'll set an empty string as a work-around.
- const trans = this._createTransferable();
- const flavor = "text/plain";
- trans.addDataFlavor(flavor);
- const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance(
- Ci.nsISupportsString
- );
- emptyString.data = "";
- trans.setTransferData(flavor, emptyString);
- const { clipboard } = Services,
- { kGlobalClipboard } = clipboard;
- clipboard.setData(trans, null, kGlobalClipboard);
- clipboard.emptyClipboard(kGlobalClipboard);
- this._lastClipboardHash = null;
- this._isPrivateClipboard = false;
- this._log.info("Private clipboard emptied.");
- }
- },
-};
-
ChromeUtils.defineLazyGetter(
lazy,
"WeaveService",
@@ -603,13 +429,8 @@ BrowserGlue.prototype = {
// handle any UI migration
this._migrateUI();
-
- // Base Browser-specific version of _migrateUI.
- this._migrateUIBB();
-
- // Handle any TBB-specific migration before showing the UI. Keep after
- // _migrateUI to make sure this._isNewProfile has been defined.
- this._migrateUITBB();
+ lazy.ProfileDataUpgrader.upgradeBB(this._isNewProfile);
+ lazy.ProfileDataUpgrader.upgradeTB(this._isNewProfile);
if (!Services.prefs.prefHasUserValue(PREF_PDFJS_ISDEFAULT_CACHE_STATE)) {
lazy.PdfJs.checkIsDefault(this._isNewProfile);
@@ -995,16 +816,6 @@ BrowserGlue.prototype = {
lazy.WeaveService.init();
}
- lazy.SecurityLevelRestartNotification.ready();
-
- lazy.DragDropFilter.init();
-
- lazy.TorProviderBuilder.firstWindowLoaded();
-
- lazy.TorSettingsNotification.ready();
-
- ClipboardPrivacy.startup();
-
lazy.BrowserUtils.callModulesFromCategory(
{
categoryName: "browser-first-window-ready",
@@ -1104,7 +915,6 @@ BrowserGlue.prototype = {
// can perform at-shutdown tasks later in shutdown.
Services.fog;
},
- () => lazy.OnionAliasStore.uninit(),
];
for (let task of tasks) {
@@ -1375,30 +1185,6 @@ BrowserGlue.prototype = {
},
},
- {
- task: () => {
- if (!lazy.TorConnect.shouldShowTorConnect) {
- // we will take this path when the user is using the legacy tor launcher or
- // when Tor Browser didn't launch its own tor.
- lazy.OnionAliasStore.init();
- } else {
- // this path is taken when using about:torconnect, we wait to init
- // after we are bootstrapped and connected to tor
- const topic = lazy.TorConnectTopics.BootstrapComplete;
- let bootstrapObserver = {
- observe(aSubject, aTopic) {
- if (aTopic === topic) {
- lazy.OnionAliasStore.init();
- // we only need to init once, so remove ourselves as an obvserver
- Services.obs.removeObserver(this, topic);
- }
- },
- };
- Services.obs.addObserver(bootstrapObserver, topic);
- }
- },
- },
-
// Run TRR performance measurements for DoH.
{
name: "doh-rollout.trrRacer.run",
@@ -1865,208 +1651,6 @@ BrowserGlue.prototype = {
}
},
- _migrateUIBB() {
- // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override
- // for tor-browser#41739.
- // Version 2: 14.0a5: Reset the privacy tracking headers preferences since
- // the UI is hidden. tor-browser#42777.
- // Also, do not set
- // dom.security.https_only_mode_send_http_background_request in
- // the security level anymore (tor-browser#42149).
- // Also, reset security.xfocsp.errorReporting.automatic since we
- // hid its neterror checkbox. tor-browser#42653.
- // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070.
- // Version 4: 15.0a2: Drop ML components. tor-browser#44045.
- const MIGRATION_VERSION = 4;
- const MIGRATION_PREF = "basebrowser.migration.version";
- if (this._isNewProfile) {
- // Do not migrate fresh profiles
- Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
- return;
- } else if (this._isNewProfile === undefined) {
- // If this happens, check if upstream updated their function and do not
- // set this member anymore!
- console.error("_migrateUIBB: this._isNewProfile is undefined.");
- }
- // We do not care whether this is a new or old profile, since in version 1
- // we just quickly clear a user preference, which should not do anything to
- // new profiles.
- // Shall we ever raise the version number and have a watershed, we can add
- // a check easily (any version > 0 will be an old profile).
- const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
- if (currentVersion < 1) {
- Services.prefs.clearUserPref(
- "layout.css.prefers-color-scheme.content-override"
- );
- }
- if (currentVersion < 2) {
- for (const prefName of [
- "privacy.globalprivacycontrol.enabled",
- "privacy.donottrackheader.enabled",
- // Telemetry preference for if the user changed the value.
- "privacy.globalprivacycontrol.was_ever_enabled",
- // The next two preferences have no corresponding UI, but are related.
- "privacy.globalprivacycontrol.functionality.enabled",
- "privacy.globalprivacycontrol.pbmode.enabled",
- "dom.security.https_only_mode_send_http_background_request",
- "security.xfocsp.errorReporting.automatic",
- ]) {
- Services.prefs.clearUserPref(prefName);
- }
- }
- if (currentVersion < 3) {
- Services.prefs.clearUserPref("general.smoothScroll");
- }
- if (currentVersion < 4) {
- for (const prefName of [
- "browser.translations.enable",
- "browser.ml.enable",
- "browser.ml.chat.enabled",
- "browser.ml.linkPreview.enabled",
- "browser.tabs.groups.smart.enabled",
- "browser.tabs.groups.smart.userEnabled",
- "extensions.ml.enabled",
- "pdfjs.enableAltText",
- "pdfjs.enableAltTextForEnglish",
- "pdfjs.enableGuessAltText",
- "pdfjs.enableAltTextModelDownload",
- "browser.urlbar.quicksuggest.mlEnabled",
- "places.semanticHistory.featureGate",
- ]) {
- // Preferences are locked. Do not want user values to linger in the
- // user's profile and become active if these preferences become unlocked
- // in the future.
- Services.prefs.clearUserPref(prefName);
- }
- }
- Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
- },
-
- // Use this method for any TBB migration that can be run just before showing
- // the UI.
- // Anything that critically needs to be migrated earlier should not use this.
- async _migrateUITBB() {
- // Version 1: Tor Browser 12.0. We use it to remove langpacks, after the
- // migration to packaged locales.
- // Version 2: Tor Browser 13.0/13.0a1: tor-browser#41845. Also, removed some
- // torbutton preferences that are not used anymore.
- // Version 3: Tor Browser 13.0.7/13.5a3: Remove blockchair
- // (tor-browser#42283).
- // Version 4: Tor Browser 14.0a4 (2024-09-02): Remove Twitter, Yahoo and
- // YouTube search engines (tor-browser#41835).
- // Version 5: Tor Browser 14.0a5: Clear user preference for CFR settings
- // since we hid the UI (tor-browser#43118).
- // Version 6: Tor Browser 14.5a3: Clear preference for TorSettings that is
- // no longer used (tor-browser#41921).
- // Drop unused TorConnect setting (tor-browser#43462).
- // Version 7: Tor Browser 14.5a6: Clear home page update url preference
- // (tor-browser#43567).
- // Version 8: Tor Browser 15.0a2: Remove legacy search addons
- // (tor-browser#43111).
- const TBB_MIGRATION_VERSION = 8;
- const MIGRATION_PREF = "torbrowser.migration.version";
-
- // If we decide to force updating users to pass through any version
- // following 12.0, we can remove this check, and check only whether
- // MIGRATION_PREF has a user value, like Mozilla does.
- if (this._isNewProfile) {
- // Do not migrate fresh profiles
- Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
- return;
- } else if (this._isNewProfile === undefined) {
- // If this happens, check if upstream updated their function and do not
- // set this member anymore!
- console.error("_migrateUITBB: this._isNewProfile is undefined.");
- }
-
- const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
- const removeLangpacks = async () => {
- for (const addon of await AddonManager.getAddonsByTypes(["locale"])) {
- await addon.uninstall();
- }
- };
- if (currentVersion < 1) {
- try {
- await removeLangpacks();
- } catch (err) {
- console.error("Could not remove langpacks", err);
- }
- }
- if (currentVersion < 2) {
- const prefToClear = [
- // tor-browser#41845: We were forcing these value by check the value of
- // automatic PBM. We decided not to change
- "browser.cache.disk.enable",
- "places.history.enabled",
- "security.nocertdb",
- "permissions.memory_only",
- // Old torbutton preferences not used anymore.
- "extensions.torbutton.loglevel",
- "extensions.torbutton.logmethod",
- "extensions.torbutton.pref_fixup_version",
- "extensions.torbutton.resize_new_windows",
- "extensions.torbutton.startup",
- "extensions.torlauncher.prompt_for_locale",
- "extensions.torlauncher.loglevel",
- "extensions.torlauncher.logmethod",
- "extensions.torlauncher.torrc_fixup_version",
- ];
- for (const pref of prefToClear) {
- if (Services.prefs.prefHasUserValue(pref)) {
- Services.prefs.clearUserPref(pref);
- }
- }
- }
- const dropAddons = async list => {
- for (const id of list) {
- try {
- const engine = await lazy.AddonManager.getAddonByID(id);
- await engine?.uninstall();
- } catch {}
- }
- };
- if (currentVersion < 3) {
- await dropAddons([
- "blockchair(a)search.mozilla.org",
- "blockchair-onion(a)search.mozilla.org",
- ]);
- }
- if (currentVersion < 4) {
- await dropAddons([
- "twitter(a)search.mozilla.org",
- "yahoo(a)search.mozilla.org",
- "youtube(a)search.mozilla.org",
- ]);
- }
- if (currentVersion < 5) {
- for (const pref of [
- "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
- "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
- ]) {
- Services.prefs.clearUserPref(pref);
- }
- }
- if (currentVersion < 6) {
- Services.prefs.clearUserPref("torbrowser.settings.enabled");
- Services.prefs.clearUserPref("torbrowser.bootstrap.allow_internet_test");
- }
- if (currentVersion < 7) {
- Services.prefs.clearUserPref("torbrowser.post_update.url");
- }
- if (currentVersion < 8) {
- await dropAddons([
- "ddg(a)search.mozilla.org",
- "ddg-onion(a)search.mozilla.org",
- "google(a)search.mozilla.org",
- "startpage(a)search.mozilla.org",
- "startpage-onion(a)search.mozilla.org",
- "wikipedia(a)search.mozilla.org",
- ]);
- }
-
- Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
- },
-
async _showUpgradeDialog() {
const data = await lazy.OnboardingMessageProvider.getUpgradeMessage();
const { gBrowser } = lazy.BrowserWindowTracker.getTopWindow();
=====================================
browser/components/ProfileDataUpgrader.sys.mjs
=====================================
@@ -900,4 +900,200 @@ export let ProfileDataUpgrader = {
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", newVersion);
},
+
+ upgradeBB(isNewProfile) {
+ // Version 1: 13.0a3. Reset layout.css.prefers-color-scheme.content-override
+ // for tor-browser#41739.
+ // Version 2: 14.0a5: Reset the privacy tracking headers preferences since
+ // the UI is hidden. tor-browser#42777.
+ // Also, do not set
+ // dom.security.https_only_mode_send_http_background_request in
+ // the security level anymore (tor-browser#42149).
+ // Also, reset security.xfocsp.errorReporting.automatic since we
+ // hid its neterror checkbox. tor-browser#42653.
+ // Version 3: 14.0a7: Reset general.smoothScroll. tor-browser#42070.
+ // Version 4: 15.0a2: Drop ML components. tor-browser#44045.
+ const MIGRATION_VERSION = 4;
+ const MIGRATION_PREF = "basebrowser.migration.version";
+
+ if (isNewProfile) {
+ // Do not migrate fresh profiles
+ Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
+ return;
+ } else if (isNewProfile === undefined) {
+ // If this happens, check if upstream updated their function and do not
+ // set this member anymore!
+ console.error("upgradeBB: isNewProfile is undefined.");
+ }
+
+ const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
+ if (currentVersion < 1) {
+ Services.prefs.clearUserPref(
+ "layout.css.prefers-color-scheme.content-override"
+ );
+ }
+ if (currentVersion < 2) {
+ for (const prefName of [
+ "privacy.globalprivacycontrol.enabled",
+ "privacy.donottrackheader.enabled",
+ // Telemetry preference for if the user changed the value.
+ "privacy.globalprivacycontrol.was_ever_enabled",
+ // The next two preferences have no corresponding UI, but are related.
+ "privacy.globalprivacycontrol.functionality.enabled",
+ "privacy.globalprivacycontrol.pbmode.enabled",
+ "dom.security.https_only_mode_send_http_background_request",
+ "security.xfocsp.errorReporting.automatic",
+ ]) {
+ Services.prefs.clearUserPref(prefName);
+ }
+ }
+ if (currentVersion < 3) {
+ Services.prefs.clearUserPref("general.smoothScroll");
+ }
+ if (currentVersion < 4) {
+ for (const prefName of [
+ "browser.translations.enable",
+ "browser.ml.enable",
+ "browser.ml.chat.enabled",
+ "browser.ml.linkPreview.enabled",
+ "browser.tabs.groups.smart.enabled",
+ "browser.tabs.groups.smart.userEnabled",
+ "extensions.ml.enabled",
+ "pdfjs.enableAltText",
+ "pdfjs.enableAltTextForEnglish",
+ "pdfjs.enableGuessAltText",
+ "pdfjs.enableAltTextModelDownload",
+ "browser.urlbar.quicksuggest.mlEnabled",
+ "places.semanticHistory.featureGate",
+ ]) {
+ // Preferences are locked. Do not want user values to linger in the
+ // user's profile and become active if these preferences become unlocked
+ // in the future.
+ Services.prefs.clearUserPref(prefName);
+ }
+ }
+ Services.prefs.setIntPref(MIGRATION_PREF, MIGRATION_VERSION);
+ },
+
+ async upgradeTB(isNewProfile) {
+ // Version 1: Tor Browser 12.0. We use it to remove langpacks, after the
+ // migration to packaged locales.
+ // Version 2: Tor Browser 13.0/13.0a1: tor-browser#41845. Also, removed some
+ // torbutton preferences that are not used anymore.
+ // Version 3: Tor Browser 13.0.7/13.5a3: Remove blockchair
+ // (tor-browser#42283).
+ // Version 4: Tor Browser 14.0a4 (2024-09-02): Remove Twitter, Yahoo and
+ // YouTube search engines (tor-browser#41835).
+ // Version 5: Tor Browser 14.0a5: Clear user preference for CFR settings
+ // since we hid the UI (tor-browser#43118).
+ // Version 6: Tor Browser 14.5a3: Clear preference for TorSettings that is
+ // no longer used (tor-browser#41921).
+ // Drop unused TorConnect setting (tor-browser#43462).
+ // Version 7: Tor Browser 14.5a6: Clear home page update url preference
+ // (tor-browser#43567).
+ // Version 8: Tor Browser 15.0a2: Remove legacy search addons
+ // (tor-browser#43111).
+ const TBB_MIGRATION_VERSION = 8;
+ const MIGRATION_PREF = "torbrowser.migration.version";
+
+ // If we decide to force updating users to pass through any version
+ // following 12.0, we can remove this check, and check only whether
+ // MIGRATION_PREF has a user value, like Mozilla does.
+ if (isNewProfile) {
+ // Do not migrate fresh profiles
+ Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
+ return;
+ } else if (isNewProfile === undefined) {
+ // If this happens, check if upstream updated their function and do not
+ // set this member anymore!
+ console.error("upgradeTB: isNewProfile is undefined.");
+ }
+
+ const currentVersion = Services.prefs.getIntPref(MIGRATION_PREF, 0);
+ const removeLangpacks = async () => {
+ for (const addon of await AddonManager.getAddonsByTypes(["locale"])) {
+ await addon.uninstall();
+ }
+ };
+ if (currentVersion < 1) {
+ try {
+ await removeLangpacks();
+ } catch (err) {
+ console.error("Could not remove langpacks", err);
+ }
+ }
+ if (currentVersion < 2) {
+ const prefToClear = [
+ // tor-browser#41845: We were forcing these value by check the value of
+ // automatic PBM. We decided not to change
+ "browser.cache.disk.enable",
+ "places.history.enabled",
+ "security.nocertdb",
+ "permissions.memory_only",
+ // Old torbutton preferences not used anymore.
+ "extensions.torbutton.loglevel",
+ "extensions.torbutton.logmethod",
+ "extensions.torbutton.pref_fixup_version",
+ "extensions.torbutton.resize_new_windows",
+ "extensions.torbutton.startup",
+ "extensions.torlauncher.prompt_for_locale",
+ "extensions.torlauncher.loglevel",
+ "extensions.torlauncher.logmethod",
+ "extensions.torlauncher.torrc_fixup_version",
+ ];
+ for (const pref of prefToClear) {
+ if (Services.prefs.prefHasUserValue(pref)) {
+ Services.prefs.clearUserPref(pref);
+ }
+ }
+ }
+ const dropAddons = async list => {
+ for (const id of list) {
+ try {
+ const engine = await lazy.AddonManager.getAddonByID(id);
+ await engine?.uninstall();
+ } catch {}
+ }
+ };
+ if (currentVersion < 3) {
+ await dropAddons([
+ "blockchair(a)search.mozilla.org",
+ "blockchair-onion(a)search.mozilla.org",
+ ]);
+ }
+ if (currentVersion < 4) {
+ await dropAddons([
+ "twitter(a)search.mozilla.org",
+ "yahoo(a)search.mozilla.org",
+ "youtube(a)search.mozilla.org",
+ ]);
+ }
+ if (currentVersion < 5) {
+ for (const pref of [
+ "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
+ "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
+ ]) {
+ Services.prefs.clearUserPref(pref);
+ }
+ }
+ if (currentVersion < 6) {
+ Services.prefs.clearUserPref("torbrowser.settings.enabled");
+ Services.prefs.clearUserPref("torbrowser.bootstrap.allow_internet_test");
+ }
+ if (currentVersion < 7) {
+ Services.prefs.clearUserPref("torbrowser.post_update.url");
+ }
+ if (currentVersion < 8) {
+ await dropAddons([
+ "ddg(a)search.mozilla.org",
+ "ddg-onion(a)search.mozilla.org",
+ "google(a)search.mozilla.org",
+ "startpage(a)search.mozilla.org",
+ "startpage-onion(a)search.mozilla.org",
+ "wikipedia(a)search.mozilla.org",
+ ]);
+ }
+
+ Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
+ },
};
=====================================
browser/components/onionservices/OnionAliasStore.sys.mjs
=====================================
@@ -1,4 +1,6 @@
-// Copyright (c) 2022, The Tor Project, Inc.
+/* 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/. */
import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
@@ -6,7 +8,10 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
JSONFile: "resource://gre/modules/JSONFile.sys.mjs",
- TorRequestWatch: "resource:///modules/TorRequestWatch.sys.mjs",
+ TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
+ TorConnectTopics: "resource://gre/modules/TorConnect.sys.mjs",
+ TorRequestWatch:
+ "moz-src:///browser/components/onionservices/TorRequestWatch.sys.mjs",
});
/* OnionAliasStore observer topics */
@@ -84,12 +89,14 @@ class Channel {
};
}
+ #enabled;
+
constructor(name, pathPrefix, jwk, scope, enabled) {
this.name = name;
this.pathPrefix = pathPrefix;
this.jwk = jwk;
this.scope = scope;
- this._enabled = enabled;
+ this.#enabled = enabled;
this.mappings = [];
this.currentTimestamp = 0;
@@ -158,10 +165,10 @@ class Channel {
log.debug(
`Downloaded and verified rules for ${this.name}, now uncompressing`
);
- this._makeMappings(JSON.parse(await gunzip(rulesGz)));
+ this.#makeMappings(JSON.parse(await gunzip(rulesGz)));
}
- _makeMappings(rules) {
+ #makeMappings(rules) {
const toTest = /^https?:\/\/[a-zA-Z0-9\.]{56}\.onion$/;
const mappings = [];
rules.rulesets.forEach(rule => {
@@ -210,7 +217,7 @@ class Channel {
async updateMappings(force) {
force = force === undefined ? false : !!force;
- if (!this._enabled && !force) {
+ if (!this.#enabled && !force) {
return;
}
await this.updateLatestTimestamp();
@@ -224,10 +231,10 @@ class Channel {
}
get enabled() {
- return this._enabled;
+ return this.#enabled;
}
set enabled(enabled) {
- this._enabled = enabled;
+ this.#enabled = enabled;
if (!enabled) {
this.mappings = [];
this.currentTimestamp = 0;
@@ -243,7 +250,7 @@ class Channel {
pathPrefix: this.pathPrefix,
jwk: this.jwk,
scope,
- enabled: this._enabled,
+ enabled: this.#enabled,
mappings: this.mappings,
currentTimestamp: this.currentTimestamp,
};
@@ -277,37 +284,40 @@ class _OnionAliasStore {
return 86400 * 1000; // 1 day, like HTTPS-Everywhere
}
- constructor() {
- this._channels = new Map();
- this._rulesetTimeout = null;
- this._lastCheck = 0;
- this._storage = null;
- }
+ #channels = new Map();
+ #rulesetTimeout = null;
+ #lastCheck = 0;
+ #storage = null;
async init() {
lazy.TorRequestWatch.start();
- await this._loadSettings();
- if (this.enabled) {
- await this._startUpdates();
+ await this.#loadSettings();
+ if (this.enabled && !lazy.TorConnect.shouldShowTorConnect) {
+ await this.#startUpdates();
+ } else {
+ Services.obs.addObserver(this, lazy.TorConnectTopics.BootstrapComplete);
}
Services.prefs.addObserver(kPrefOnionAliasEnabled, this);
}
uninit() {
- this._clear();
- if (this._rulesetTimeout) {
- clearTimeout(this._rulesetTimeout);
+ this.#clear();
+ if (this.#rulesetTimeout) {
+ clearTimeout(this.#rulesetTimeout);
}
- this._rulesetTimeout = null;
+ this.#rulesetTimeout = null;
+
+ Services.obs.removeObserver(this, lazy.TorConnectTopics.BootstrapComplete);
Services.prefs.removeObserver(kPrefOnionAliasEnabled, this);
+
lazy.TorRequestWatch.stop();
}
async getChannels() {
- if (this._storage === null) {
- await this._loadSettings();
+ if (this.#storage === null) {
+ await this.#loadSettings();
}
- return Array.from(this._channels.values(), ch => ch.toJSON());
+ return Array.from(this.#channels.values(), ch => ch.toJSON());
}
async setChannel(chanData) {
@@ -328,20 +338,20 @@ class _OnionAliasStore {
);
// Call makeKey to make it throw if the key is invalid
await ch.makeKey();
- this._channels.set(name, ch);
- this._applyMappings();
- this._saveSettings();
- setTimeout(this._notifyChanges.bind(this), 1);
+ this.#channels.set(name, ch);
+ this.#applyMappings();
+ this.#saveSettings();
+ setTimeout(this.#notifyChanges.bind(this), 1);
return ch;
}
enableChannel(name, enabled) {
- const channel = this._channels.get(name);
+ const channel = this.#channels.get(name);
if (channel !== null) {
channel.enabled = enabled;
- this._applyMappings();
- this._saveSettings();
- this._notifyChanges();
+ this.#applyMappings();
+ this.#saveSettings();
+ this.#notifyChanges();
if (this.enabled && enabled && !channel.currentTimestamp) {
this.updateChannel(name);
}
@@ -352,46 +362,46 @@ class _OnionAliasStore {
if (!this.enabled) {
throw Error("Onion Aliases are disabled");
}
- const channel = this._channels.get(name);
+ const channel = this.#channels.get(name);
if (channel === null) {
throw Error("Channel not found");
}
await channel.updateMappings(true);
- this._saveSettings();
- this._applyMappings();
- setTimeout(this._notifyChanges.bind(this), 1);
+ this.#saveSettings();
+ this.#applyMappings();
+ setTimeout(this.#notifyChanges.bind(this), 1);
return channel;
}
deleteChannel(name) {
- if (this._channels.delete(name)) {
- this._saveSettings();
- this._applyMappings();
- this._notifyChanges();
+ if (this.#channels.delete(name)) {
+ this.#saveSettings();
+ this.#applyMappings();
+ this.#notifyChanges();
}
}
- async _loadSettings() {
- if (this._storage !== null) {
+ async #loadSettings() {
+ if (this.#storage !== null) {
return;
}
- this._channels = new Map();
- this._storage = new lazy.JSONFile({
+ this.#channels = new Map();
+ this.#storage = new lazy.JSONFile({
path: PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
"onion-aliases.json"
),
- dataPostProcessor: this._settingsProcessor.bind(this),
+ dataPostProcessor: this.#settingsProcessor.bind(this),
});
- await this._storage.load();
- log.debug("Loaded settings", this._storage.data, this._storage.path);
- this._applyMappings();
- this._notifyChanges();
+ await this.#storage.load();
+ log.debug("Loaded settings", this.#storage.data, this.#storage.path);
+ this.#applyMappings();
+ this.#notifyChanges();
}
- _settingsProcessor(data) {
+ #settingsProcessor(data) {
if ("lastCheck" in data) {
- this._lastCheck = data.lastCheck;
+ this.#lastCheck = data.lastCheck;
} else {
data.lastCheck = 0;
}
@@ -410,56 +420,56 @@ class _OnionAliasStore {
}
return true;
});
- this._channels = channels;
+ this.#channels = channels;
return data;
}
- _saveSettings() {
- if (this._storage === null) {
+ #saveSettings() {
+ if (this.#storage === null) {
throw Error("Settings have not been loaded");
}
- this._storage.data.lastCheck = this._lastCheck;
- this._storage.data.channels = Array.from(this._channels.values(), ch =>
+ this.#storage.data.lastCheck = this.#lastCheck;
+ this.#storage.data.channels = Array.from(this.#channels.values(), ch =>
ch.toJSON()
);
- this._storage.saveSoon();
+ this.#storage.saveSoon();
}
- _addMapping(shortOnionHost, longOnionHost) {
+ #addMapping(shortOnionHost, longOnionHost) {
const service = Cc["@torproject.org/onion-alias-service;1"].getService(
Ci.IOnionAliasService
);
service.addOnionAlias(shortOnionHost, longOnionHost);
}
- _clear() {
+ #clear() {
const service = Cc["@torproject.org/onion-alias-service;1"].getService(
Ci.IOnionAliasService
);
service.clearOnionAliases();
}
- _applyMappings() {
- this._clear();
- for (const ch of this._channels.values()) {
+ #applyMappings() {
+ this.#clear();
+ for (const ch of this.#channels.values()) {
if (!ch.enabled) {
continue;
}
for (const [short, long] of ch.mappings) {
- this._addMapping(short, long);
+ this.#addMapping(short, long);
}
}
}
- async _periodicRulesetCheck() {
+ async #periodicRulesetCheck() {
if (!this.enabled) {
log.debug("Onion Aliases are disabled, not updating rulesets.");
return;
}
log.debug("Begin scheduled ruleset update");
- this._lastCheck = Date.now();
+ this.#lastCheck = Date.now();
let anyUpdated = false;
- for (const ch of this._channels.values()) {
+ for (const ch of this.#channels.values()) {
if (!ch.enabled) {
log.debug(`Not updating ${ch.name} because not enabled`);
continue;
@@ -473,22 +483,22 @@ class _OnionAliasStore {
}
}
if (anyUpdated) {
- this._saveSettings();
- this._applyMappings();
- this._notifyChanges();
+ this.#saveSettings();
+ this.#applyMappings();
+ this.#notifyChanges();
} else {
log.debug("No channel has been updated, avoid saving");
}
- this._scheduleCheck(_OnionAliasStore.RULESET_CHECK_INTERVAL);
+ this.#scheduleCheck(_OnionAliasStore.RULESET_CHECK_INTERVAL);
}
- async _startUpdates() {
- // This is a "private" function, so we expect the callers to verify wheter
+ async #startUpdates() {
+ // This is a private function, so we expect the callers to verify whether
// onion aliases are enabled.
// Callees will also do, so we avoid an additional check here.
- const dt = Date.now() - this._lastCheck;
+ const dt = Date.now() - this.#lastCheck;
let force = false;
- for (const ch of this._channels.values()) {
+ for (const ch of this.#channels.values()) {
if (ch.enabled && !ch.currentTimestamp) {
// Edited while being offline or some other error happened
force = true;
@@ -499,34 +509,34 @@ class _OnionAliasStore {
log.debug(
`Mappings are stale (${dt}), or force check requested (${force}), checking them immediately`
);
- await this._periodicRulesetCheck();
+ await this.#periodicRulesetCheck();
} else {
- this._scheduleCheck(_OnionAliasStore.RULESET_CHECK_INTERVAL - dt);
+ this.#scheduleCheck(_OnionAliasStore.RULESET_CHECK_INTERVAL - dt);
}
}
- _scheduleCheck(dt) {
- if (this._rulesetTimeout) {
+ #scheduleCheck(dt) {
+ if (this.#rulesetTimeout) {
log.warn("The previous update timeout was not null");
- clearTimeout(this._rulesetTimeout);
+ clearTimeout(this.#rulesetTimeout);
}
if (!this.enabled) {
log.warn(
"Ignoring the scheduling of a new check because the Onion Alias feature is currently disabled."
);
- this._rulesetTimeout = null;
+ this.#rulesetTimeout = null;
return;
}
log.debug(`Scheduling ruleset update in ${dt}`);
- this._rulesetTimeout = setTimeout(() => {
- this._rulesetTimeout = null;
- this._periodicRulesetCheck();
+ this.#rulesetTimeout = setTimeout(() => {
+ this.#rulesetTimeout = null;
+ this.#periodicRulesetCheck();
}, dt);
}
- _notifyChanges() {
+ #notifyChanges() {
Services.obs.notifyObservers(
- Array.from(this._channels.values(), ch => ch.toJSON()),
+ Array.from(this.#channels.values(), ch => ch.toJSON()),
OnionAliasStoreTopics.ChannelsChanged
);
}
@@ -538,11 +548,16 @@ class _OnionAliasStore {
observe(aSubject, aTopic) {
if (aTopic === "nsPref:changed") {
if (this.enabled) {
- this._startUpdates();
- } else if (this._rulesetTimeout) {
- clearTimeout(this._rulesetTimeout);
- this._rulesetTimeout = null;
+ this.#startUpdates();
+ } else if (this.#rulesetTimeout) {
+ clearTimeout(this.#rulesetTimeout);
+ this.#rulesetTimeout = null;
}
+ } else if (
+ aTopic === lazy.TorConnectTopics.BootstrapComplete &&
+ this.enabled
+ ) {
+ this.#startUpdates();
}
}
}
=====================================
browser/components/onionservices/moz.build
=====================================
@@ -1,8 +1,11 @@
JAR_MANIFESTS += ["jar.mn"]
EXTRA_JS_MODULES += [
- "OnionAliasStore.sys.mjs",
"OnionLocationChild.sys.mjs",
"OnionLocationParent.sys.mjs",
+]
+
+MOZ_SRC_FILES += [
+ "OnionAliasStore.sys.mjs",
"TorRequestWatch.sys.mjs",
]
=====================================
browser/components/rulesets/RulesetsParent.sys.mjs
=====================================
@@ -1,9 +1,11 @@
-// Copyright (c) 2022, The Tor Project, Inc.
+/* 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/. */
import {
OnionAliasStore,
OnionAliasStoreTopics,
-} from "resource:///modules/OnionAliasStore.sys.mjs";
+} from "moz-src:///browser/components/onionservices/OnionAliasStore.sys.mjs";
const kShowWarningPref = "torbrowser.rulesets.show_warning";
@@ -56,9 +58,10 @@ export class RulesetsParent extends JSWindowActorParent {
return {
showWarning: Services.prefs.getBoolPref(kShowWarningPref, true),
};
- case "rulesets:set-channel":
+ case "rulesets:set-channel": {
const ch = await OnionAliasStore.setChannel(message.data);
return ch;
+ }
case "rulesets:update-channel":
// We need to catch any error in this way, because in case of an
// exception, RPMSendQuery does not return on the other side
=====================================
browser/modules/ClipboardPrivacy.sys.mjs
=====================================
@@ -0,0 +1,178 @@
+/* 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/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ setTimeout: "resource://gre/modules/Timer.sys.mjs",
+});
+
+/**
+ * Empty clipboard content from private windows on exit.
+ *
+ * See tor-browser#42154.
+ */
+export const ClipboardPrivacy = {
+ _lastClipboardHash: null,
+ _globalActivation: false,
+ _isPrivateClipboard: false,
+ _hasher: null,
+ _shuttingDown: false,
+ _log: null,
+
+ _createTransferable() {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ return trans;
+ },
+ _computeClipboardHash() {
+ const flavors = ["text/x-moz-url", "text/plain"];
+ if (
+ !Services.clipboard.hasDataMatchingFlavors(
+ flavors,
+ Ci.nsIClipboard.kGlobalClipboard
+ )
+ ) {
+ return null;
+ }
+ const trans = this._createTransferable();
+ flavors.forEach(trans.addDataFlavor);
+ try {
+ Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
+ const clipboardContent = {};
+ trans.getAnyTransferData({}, clipboardContent);
+ const { data } = clipboardContent.value.QueryInterface(
+ Ci.nsISupportsString
+ );
+ const bytes = new TextEncoder().encode(data);
+ const hasher = (this._hasher ||= Cc[
+ "@mozilla.org/security/hash;1"
+ ].createInstance(Ci.nsICryptoHash));
+ hasher.init(hasher.SHA256);
+ hasher.update(bytes, bytes.length);
+ return hasher.finish(true);
+ } catch (e) {}
+ return null;
+ },
+
+ init() {
+ this._log = console.createInstance({
+ prefix: "ClipboardPrivacy",
+ });
+ this._lastClipboardHash = this._computeClipboardHash();
+
+ // Here we track changes in active window / application,
+ // by filtering focus events and window closures.
+ const handleActivation = (win, activation) => {
+ if (activation) {
+ if (!this._globalActivation) {
+ // focus changed within this window, bail out.
+ return;
+ }
+ this._globalActivation = false;
+ } else if (!Services.focus.activeWindow) {
+ // focus is leaving this window:
+ // let's track whether it remains within the browser.
+ lazy.setTimeout(() => {
+ this._globalActivation = !Services.focus.activeWindow;
+ }, 100);
+ }
+
+ const checkClipboardContent = () => {
+ const clipboardHash = this._computeClipboardHash();
+ if (clipboardHash !== this._lastClipboardHash) {
+ this._isPrivateClipboard =
+ !activation &&
+ (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing ||
+ lazy.PrivateBrowsingUtils.isWindowPrivate(win));
+ this._lastClipboardHash = clipboardHash;
+ this._log.debug(
+ `Clipboard changed: private ${this._isPrivateClipboard}, hash ${clipboardHash}.`
+ );
+ }
+ };
+
+ if (win.closed) {
+ checkClipboardContent();
+ } else {
+ // defer clipboard access on DOM events to work-around tor-browser#42306
+ lazy.setTimeout(checkClipboardContent, 0);
+ }
+ };
+ const focusListener = e =>
+ e.isTrusted && handleActivation(e.currentTarget, e.type === "focusin");
+ const initWindow = win => {
+ for (const e of ["focusin", "focusout"]) {
+ win.addEventListener(e, focusListener);
+ }
+ };
+ for (const w of Services.ww.getWindowEnumerator()) {
+ initWindow(w);
+ }
+ Services.ww.registerNotification((win, event) => {
+ switch (event) {
+ case "domwindowopened":
+ initWindow(win);
+ break;
+ case "domwindowclosed":
+ handleActivation(win, false);
+ if (
+ this._isPrivateClipboard &&
+ lazy.PrivateBrowsingUtils.isWindowPrivate(win) &&
+ (this._shuttingDown ||
+ !Array.from(Services.ww.getWindowEnumerator()).find(
+ w =>
+ lazy.PrivateBrowsingUtils.isWindowPrivate(w) &&
+ // We need to filter out the HIDDEN WebExtensions window,
+ // which might be private as well but is not UI-relevant.
+ !w.location.href.startsWith("chrome://extensions/")
+ ))
+ ) {
+ // no more private windows, empty private content if needed
+ this.emptyPrivate();
+ }
+ }
+ });
+
+ lazy.AsyncShutdown.quitApplicationGranted.addBlocker(
+ "ClipboardPrivacy: removing private data",
+ () => {
+ this._shuttingDown = true;
+ this.emptyPrivate();
+ }
+ );
+ },
+ emptyPrivate() {
+ if (
+ this._isPrivateClipboard &&
+ !Services.prefs.getBoolPref(
+ "browser.privatebrowsing.preserveClipboard",
+ false
+ ) &&
+ this._lastClipboardHash === this._computeClipboardHash()
+ ) {
+ // nsIClipboard.emptyClipboard() does nothing in Wayland:
+ // we'll set an empty string as a work-around.
+ const trans = this._createTransferable();
+ const flavor = "text/plain";
+ trans.addDataFlavor(flavor);
+ const emptyString = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ emptyString.data = "";
+ trans.setTransferData(flavor, emptyString);
+ const { clipboard } = Services,
+ { kGlobalClipboard } = clipboard;
+ clipboard.setData(trans, null, kGlobalClipboard);
+ clipboard.emptyClipboard(kGlobalClipboard);
+ this._lastClipboardHash = null;
+ this._isPrivateClipboard = false;
+ this._log.info("Private clipboard emptied.");
+ }
+ },
+};
=====================================
browser/modules/moz.build
=====================================
@@ -136,12 +136,10 @@ EXTRA_JS_MODULES += [
"PopupBlockerObserver.sys.mjs",
"ProcessHangMonitor.sys.mjs",
"Sanitizer.sys.mjs",
- "SecurityLevelRestartNotification.sys.mjs",
"SelectionChangedMenulist.sys.mjs",
"SharingUtils.sys.mjs",
"SiteDataManager.sys.mjs",
"SitePermissions.sys.mjs",
- "TorSettingsNotification.sys.mjs",
"TorUIUtils.sys.mjs",
"TransientPrefs.sys.mjs",
"URILoadingHelper.sys.mjs",
@@ -151,6 +149,9 @@ EXTRA_JS_MODULES += [
MOZ_SRC_FILES += [
"ContextId.sys.mjs",
+ "ClipboardPrivacy.sys.mjs",
+ "SecurityLevelRestartNotification.sys.mjs",
+ "TorSettingsNotification.sys.mjs",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
=====================================
dom/base/ContentAreaDropListener.sys.mjs
=====================================
@@ -5,7 +5,7 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
- OpaqueDrag: "resource://gre/modules/DragDropFilter.sys.mjs",
+ OpaqueDrag: "moz-src:///toolkit/modules/DragDropFilter.sys.mjs",
});
// This component is used for handling dragover and drop of urls.
=====================================
toolkit/components/places/PlacesUtils.sys.mjs
=====================================
@@ -12,7 +12,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
Bookmarks: "resource://gre/modules/Bookmarks.sys.mjs",
History: "resource://gre/modules/History.sys.mjs",
- OpaqueDrag: "resource://gre/modules/DragDropFilter.sys.mjs",
+ OpaqueDrag: "moz-src:///toolkit/modules/DragDropFilter.sys.mjs",
PlacesSyncUtils: "resource://gre/modules/PlacesSyncUtils.sys.mjs",
Sqlite: "resource://gre/modules/Sqlite.sys.mjs",
});
=====================================
toolkit/components/tor-launcher/tor-launcher.manifest
=====================================
@@ -1 +1,2 @@
category profile-after-change TorStartupService @torproject.org/tor-startup-service;1
+category browser-first-window-ready resource://gre/modules/TorProviderBuilder.sys.mjs TorProviderBuilder.firstWindowLoaded
=====================================
toolkit/modules/moz.build
=====================================
@@ -164,7 +164,6 @@ EXTRA_JS_MODULES += [
"DateTimePickerPanel.sys.mjs",
"DeferredTask.sys.mjs",
"DomainFrontedRequests.sys.mjs",
- "DragDropFilter.sys.mjs",
"E10SUtils.sys.mjs",
"EventEmitter.sys.mjs",
"FileUtils.sys.mjs",
@@ -220,6 +219,10 @@ EXTRA_JS_MODULES += [
"WebChannel.sys.mjs",
]
+MOZ_SRC_FILES += [
+ "DragDropFilter.sys.mjs",
+]
+
if CONFIG["MOZ_ASAN_REPORTER"]:
EXTRA_JS_MODULES += [
"AsanReporter.sys.mjs",
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/d28d9f…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/d28d9f…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/tor-browser][base-browser-140.2.0esr-15.0-1] BB 43525: Skip Remote Settings for search engine customization.
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch base-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
a1886d8c by Pier Angelo Vendrame at 2025-09-08T11:57:50+02:00
BB 43525: Skip Remote Settings for search engine customization.
Also, add some bundled search engines.
- - - - -
9 changed files:
- toolkit/components/search/AppProvidedSearchEngine.sys.mjs
- toolkit/components/search/SearchEngineSelector.sys.mjs
- + toolkit/components/search/content/base-browser-search-engine-icons.json
- + toolkit/components/search/content/base-browser-search-engines.json
- + toolkit/components/search/content/duckduckgo.ico
- + toolkit/components/search/content/startpage-16.png
- + toolkit/components/search/content/startpage-32.png
- + toolkit/components/search/jar.mn
- toolkit/components/search/moz.build
Changes:
=====================================
toolkit/components/search/AppProvidedSearchEngine.sys.mjs
=====================================
@@ -116,6 +116,10 @@ class IconHandler {
await this.#buildIconMap();
}
+ if (AppConstants.BASE_BROWSER_VERSION) {
+ return this.#iconMap.get(engineIdentifier);
+ }
+
let iconList = this.#iconMap.get(this.getKey(engineIdentifier)) || [];
return iconList.filter(r =>
this.#identifierMatches(engineIdentifier, r.engineIdentifiers)
@@ -132,29 +136,7 @@ class IconHandler {
* source object or null of there is no icon with the supplied width.
*/
async createIconURL(iconRecord) {
- let iconData;
- try {
- iconData = await this.#iconCollection.attachments.get(iconRecord);
- } catch (ex) {
- console.error(ex);
- }
- if (!iconData) {
- console.warn("Unable to find the attachment for", iconRecord.id);
- // Queue an update in case we haven't downloaded it yet.
- this.#pendingUpdatesMap.set(iconRecord.id, iconRecord);
- this.#maybeQueueIdle();
- return null;
- }
-
- if (iconData.record.last_modified != iconRecord.last_modified) {
- // The icon we have stored is out of date, queue an update so that we'll
- // download the new icon.
- this.#pendingUpdatesMap.set(iconRecord.id, iconRecord);
- this.#maybeQueueIdle();
- }
- return URL.createObjectURL(
- new Blob([iconData.buffer], { type: iconRecord.attachment.mimetype })
- );
+ return iconRecord.url;
}
QueryInterface = ChromeUtils.generateQI(["nsIObserver"]);
@@ -238,27 +220,23 @@ class IconHandler {
* Obtains the icon list from the remote settings collection.
*/
async #buildIconMap() {
- let iconList = [];
try {
- iconList = await this.#iconCollection.get();
+ this.#iconMap = new Map(
+ Object.entries(
+ await (
+ await fetch(
+ "chrome://global/content/search/base-browser-search-engine-icons.json"
+ )
+ ).json()
+ )
+ );
} catch (ex) {
console.error(ex);
+ this.#iconMap = null;
}
- if (!iconList.length) {
+ if (!this.#iconMap) {
console.error("Failed to obtain search engine icon list records");
}
-
- this.#iconMap = new Map();
- for (let record of iconList) {
- let keys = new Set(record.engineIdentifiers.map(this.getKey));
- for (let key of keys) {
- if (this.#iconMap.has(key)) {
- this.#iconMap.get(key).push(record);
- } else {
- this.#iconMap.set(key, [record]);
- }
- }
- }
}
/**
=====================================
toolkit/components/search/SearchEngineSelector.sys.mjs
=====================================
@@ -2,6 +2,8 @@
* 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/. */
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+
/**
* @typedef {import("../uniffi-bindgen-gecko-js/components/generated/RustSearch.sys.mjs").SearchEngineSelector} RustSearchEngineSelector
* We use "Rust" above to avoid conflict with the class name for the JavaScript wrapper.
@@ -92,30 +94,15 @@ export class SearchEngineSelector {
return this._getConfigurationPromise;
}
- this._getConfigurationPromise = Promise.all([
- this._getConfiguration(),
- this._getConfigurationOverrides(),
- ]);
- let remoteSettingsData = await this._getConfigurationPromise;
- this._configuration = remoteSettingsData[0];
- this._configurationOverrides = remoteSettingsData[1];
- delete this._getConfigurationPromise;
-
- if (!this._configuration?.length) {
- throw Components.Exception(
- "Failed to get engine data from Remote Settings",
- Cr.NS_ERROR_UNEXPECTED
- );
- }
-
- if (!this._listenerAdded) {
- this._remoteConfig.on("sync", this._onConfigurationUpdated);
- this._remoteConfigOverrides.on(
- "sync",
- this._onConfigurationOverridesUpdated
- );
- this._listenerAdded = true;
- }
+ let { promise, resolve } = Promise.withResolvers();
+ this._getConfigurationPromise = promise;
+ this._configuration = await (
+ await fetch(
+ "chrome://global/content/search/base-browser-search-engines.json"
+ )
+ ).json();
+ this._configurationOverrides = [];
+ resolve(this._configuration);
if (lazy.SearchUtils.rustSelectorFeatureGate) {
this.#selector.setSearchConfig(
@@ -242,6 +229,12 @@ export class SearchEngineSelector {
* The new configuration object
*/
_onConfigurationUpdated({ data: { current } }) {
+ // tor-browser#43525: Even though RemoteSettings are a no-op for us, we do
+ // not want them to interfere in any way.
+ if (AppConstants.BASE_BROWSER_VERSION) {
+ return;
+ }
+
this._configuration = current;
if (lazy.SearchUtils.rustSelectorFeatureGate) {
@@ -268,6 +261,12 @@ export class SearchEngineSelector {
* The new configuration object
*/
_onConfigurationOverridesUpdated({ data: { current } }) {
+ // tor-browser#43525: Even though RemoteSettings are a no-op for us, we do
+ // not want them to interfere in any way.
+ if (AppConstants.BASE_BROWSER_VERSION) {
+ return;
+ }
+
this._configurationOverrides = current;
if (lazy.SearchUtils.rustSelectorFeatureGate) {
=====================================
toolkit/components/search/content/base-browser-search-engine-icons.json
=====================================
@@ -0,0 +1,15 @@
+{
+ "ddg": [
+ { "url": "chrome://global/content/search/duckduckgo.ico", "imageSize": 32 }
+ ],
+ "startpage": [
+ {
+ "url": "chrome://global/content/search/startpage-16.png",
+ "imageSize": 16
+ },
+ {
+ "url": "chrome://global/content/search/startpage-32.png",
+ "imageSize": 32
+ }
+ ]
+}
=====================================
toolkit/components/search/content/base-browser-search-engines.json
=====================================
@@ -0,0 +1,43 @@
+[
+ {
+ "base": {
+ "aliases": ["duckduckgo", "ddg"],
+ "classification": "general",
+ "name": "DuckDuckGo",
+ "urls": {
+ "search": {
+ "base": "https://duckduckgo.com/",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "04e99a38-13ee-47d8-8aa4-64482b3dea99",
+ "identifier": "ddg",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["startpage", "sp"],
+ "classification": "general",
+ "name": "Startpage",
+ "urls": {
+ "search": {
+ "base": "https://www.startpage.com/sp/search",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "927bbd9f-b2f3-48b4-8974-1c1148028f4d",
+ "identifier": "startpage",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "recordType": "defaultEngines",
+ "globalDefault": "ddg",
+ "globalDefaultPrivate": "ddg"
+ }
+]
=====================================
toolkit/components/search/content/duckduckgo.ico
=====================================
Binary files /dev/null and b/toolkit/components/search/content/duckduckgo.ico differ
=====================================
toolkit/components/search/content/startpage-16.png
=====================================
Binary files /dev/null and b/toolkit/components/search/content/startpage-16.png differ
=====================================
toolkit/components/search/content/startpage-32.png
=====================================
Binary files /dev/null and b/toolkit/components/search/content/startpage-32.png differ
=====================================
toolkit/components/search/jar.mn
=====================================
@@ -0,0 +1,6 @@
+# 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/.
+
+toolkit.jar:
+ content/global/search/ (content/*)
=====================================
toolkit/components/search/moz.build
=====================================
@@ -46,5 +46,7 @@ TESTING_JS_MODULES += [
"tests/SearchTestUtils.sys.mjs",
]
+JAR_MANIFESTS += ["jar.mn"]
+
with Files("**"):
BUG_COMPONENT = ("Firefox", "Search")
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/a1886d8…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/a1886d8…
You're receiving this email because of your account on gitlab.torproject.org.
1
0
[Git][tpo/applications/mullvad-browser][mullvad-browser-140.2.0esr-15.0-1] 4 commits: fixup! MB 213: Customize the search engines list
by Pier Angelo Vendrame (@pierov) 08 Sep '25
by Pier Angelo Vendrame (@pierov) 08 Sep '25
08 Sep '25
Pier Angelo Vendrame pushed to branch mullvad-browser-140.2.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
fd210716 by Pier Angelo Vendrame at 2025-09-08T11:52:36+02:00
fixup! MB 213: Customize the search engines list
BB 43525: Skip Remote Settings for search engine customization.
Drop some changes that we are not going to keep with the shared
approach.
- - - - -
0c345e48 by Pier Angelo Vendrame at 2025-09-08T11:53:16+02:00
dropme! MB 213: Customize the search engines list
BB 43525: Skip Remote Settings for search engine customization.
Prepare the tree for the new approach.
- - - - -
e95419fa by Pier Angelo Vendrame at 2025-09-08T11:53:17+02:00
BB 43525: Skip Remote Settings for search engine customization.
Also, add some bundled search engines.
- - - - -
b80c84c1 by Pier Angelo Vendrame at 2025-09-08T11:53:17+02:00
amend! MB 213: Customize the search engines list
MB 213: Customize the search engines list.
- - - - -
6 changed files:
- toolkit/components/search/AppProvidedSearchEngine.sys.mjs
- toolkit/components/search/SearchEngineSelector.sys.mjs
- toolkit/components/search/SearchService.sys.mjs
- toolkit/components/search/content/mullvadBrowserSearchEngineIcons.json → toolkit/components/search/content/base-browser-search-engine-icons.json
- + toolkit/components/search/content/base-browser-search-engines.json
- − toolkit/components/search/content/mullvadBrowserSearchEngines.json
Changes:
=====================================
toolkit/components/search/AppProvidedSearchEngine.sys.mjs
=====================================
@@ -116,7 +116,14 @@ class IconHandler {
await this.#buildIconMap();
}
- return this.#iconMap.get(engineIdentifier);
+ if (AppConstants.BASE_BROWSER_VERSION) {
+ return this.#iconMap.get(engineIdentifier);
+ }
+
+ let iconList = this.#iconMap.get(this.getKey(engineIdentifier)) || [];
+ return iconList.filter(r =>
+ this.#identifierMatches(engineIdentifier, r.engineIdentifiers)
+ );
}
/**
@@ -218,7 +225,7 @@ class IconHandler {
Object.entries(
await (
await fetch(
- "chrome://global/content/search/mullvadBrowserSearchEngineIcons.json"
+ "chrome://global/content/search/base-browser-search-engine-icons.json"
)
).json()
)
=====================================
toolkit/components/search/SearchEngineSelector.sys.mjs
=====================================
@@ -2,6 +2,8 @@
* 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/. */
+import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
+
/**
* @typedef {import("../uniffi-bindgen-gecko-js/components/generated/RustSearch.sys.mjs").SearchEngineSelector} RustSearchEngineSelector
* We use "Rust" above to avoid conflict with the class name for the JavaScript wrapper.
@@ -92,30 +94,15 @@ export class SearchEngineSelector {
return this._getConfigurationPromise;
}
- this._getConfigurationPromise = Promise.all([
- this._getConfiguration(),
- this._getConfigurationOverrides(),
- ]);
- let remoteSettingsData = await this._getConfigurationPromise;
- this._configuration = remoteSettingsData[0];
- this._configurationOverrides = remoteSettingsData[1];
- delete this._getConfigurationPromise;
-
- if (!this._configuration?.length) {
- throw Components.Exception(
- "Failed to get engine data from Remote Settings",
- Cr.NS_ERROR_UNEXPECTED
- );
- }
-
- if (!this._listenerAdded) {
- this._remoteConfig.on("sync", this._onConfigurationUpdated);
- this._remoteConfigOverrides.on(
- "sync",
- this._onConfigurationOverridesUpdated
- );
- this._listenerAdded = true;
- }
+ let { promise, resolve } = Promise.withResolvers();
+ this._getConfigurationPromise = promise;
+ this._configuration = await (
+ await fetch(
+ "chrome://global/content/search/base-browser-search-engines.json"
+ )
+ ).json();
+ this._configurationOverrides = [];
+ resolve(this._configuration);
if (lazy.SearchUtils.rustSelectorFeatureGate) {
this.#selector.setSearchConfig(
@@ -242,6 +229,12 @@ export class SearchEngineSelector {
* The new configuration object
*/
_onConfigurationUpdated({ data: { current } }) {
+ // tor-browser#43525: Even though RemoteSettings are a no-op for us, we do
+ // not want them to interfere in any way.
+ if (AppConstants.BASE_BROWSER_VERSION) {
+ return;
+ }
+
this._configuration = current;
if (lazy.SearchUtils.rustSelectorFeatureGate) {
@@ -268,6 +261,12 @@ export class SearchEngineSelector {
* The new configuration object
*/
_onConfigurationOverridesUpdated({ data: { current } }) {
+ // tor-browser#43525: Even though RemoteSettings are a no-op for us, we do
+ // not want them to interfere in any way.
+ if (AppConstants.BASE_BROWSER_VERSION) {
+ return;
+ }
+
this._configurationOverrides = current;
if (lazy.SearchUtils.rustSelectorFeatureGate) {
=====================================
toolkit/components/search/SearchService.sys.mjs
=====================================
@@ -25,7 +25,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
Region: "resource://gre/modules/Region.sys.mjs",
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
SearchEngine: "moz-src:///toolkit/components/search/SearchEngine.sys.mjs",
- // eslint-disable-next-line mozilla/valid-lazy
SearchEngineSelector:
"moz-src:///toolkit/components/search/SearchEngineSelector.sys.mjs",
SearchSettings: "moz-src:///toolkit/components/search/SearchSettings.sys.mjs",
@@ -579,7 +578,11 @@ export class SearchService {
// Test-only function to reset just the engine selector so that it can
// load a different configuration.
- resetEngineSelector() {}
+ resetEngineSelector() {
+ this.#engineSelector = new lazy.SearchEngineSelector(
+ this.#handleConfigurationUpdated.bind(this)
+ );
+ }
resetToAppDefaultEngine() {
let appDefaultEngine = this.appDefaultEngine;
@@ -1420,6 +1423,10 @@ export class SearchService {
// We need to catch the region being updated during initialization so we
// start listening straight away.
Services.obs.addObserver(this, lazy.Region.REGION_TOPIC);
+
+ this.#engineSelector = new lazy.SearchEngineSelector(
+ this.#handleConfigurationUpdated.bind(this)
+ );
}
/**
@@ -1652,7 +1659,6 @@ export class SearchService {
* Handles the search configuration being - adds a wait on the user
* being idle, before the search engine update gets handled.
*/
- // eslint-disable-next-line no-unused-private-class-members
#handleConfigurationUpdated() {
if (this.#queuedIdle) {
return;
@@ -2622,12 +2628,21 @@ export class SearchService {
// This is prefixed with _ rather than # because it is
// called in test_remove_engine_notification_box.js
async _fetchEngineSelectorEngines() {
- const engines = await (
- await fetch(
- "chrome://global/content/search/mullvadBrowserSearchEngines.json"
- )
- ).json();
- return { engines, privateDefault: undefined };
+ let searchEngineSelectorProperties = {
+ locale: Services.locale.appLocaleAsBCP47,
+ region: lazy.Region.home || "unknown",
+ channel: lazy.SearchUtils.MODIFIED_APP_CHANNEL,
+ experiment: this._experimentPrefValue,
+ distroID: lazy.SearchUtils.distroID ?? "",
+ };
+
+ for (let [key, value] of Object.entries(searchEngineSelectorProperties)) {
+ this._settings.setMetaDataAttribute(key, value);
+ }
+
+ return this.#engineSelector.fetchEngineConfiguration(
+ searchEngineSelectorProperties
+ );
}
#setDefaultFromSelector(refinedConfig) {
=====================================
toolkit/components/search/content/mullvadBrowserSearchEngineIcons.json → toolkit/components/search/content/base-browser-search-engine-icons.json
=====================================
@@ -1,27 +1,33 @@
{
"ddg": [
- { "url": "chrome://global/content/search/duckduckgo.ico", "iconSize": 32 }
+ { "url": "chrome://global/content/search/duckduckgo.ico", "imageSize": 32 }
],
"ddg-html": [
- { "url": "chrome://global/content/search/duckduckgo.ico", "iconSize": 32 }
+ { "url": "chrome://global/content/search/duckduckgo.ico", "imageSize": 32 }
],
"mullvad-leta": [
- { "url": "chrome://global/content/search/mullvad-leta.svg", "iconSize": 16 }
+ {
+ "url": "chrome://global/content/search/mullvad-leta.svg",
+ "imageSize": 16
+ }
],
"mojeek": [
- { "url": "chrome://global/content/search/mojeek.ico", "iconSize": 32 }
+ { "url": "chrome://global/content/search/mojeek.ico", "imageSize": 32 }
],
"brave": [
- { "url": "chrome://global/content/search/brave.svg", "iconSize": 16 }
+ { "url": "chrome://global/content/search/brave.svg", "imageSize": 16 }
],
"startpage": [
{
"url": "chrome://global/content/search/startpage-16.png",
- "iconSize": 16
+ "imageSize": 16
},
- { "url": "chrome://global/content/search/startpage-32.png", "iconSize": 32 }
+ {
+ "url": "chrome://global/content/search/startpage-32.png",
+ "imageSize": 32
+ }
],
"metager": [
- { "url": "chrome://global/content/search/metager.ico", "iconSize": 196 }
+ { "url": "chrome://global/content/search/metager.ico", "imageSize": 196 }
]
}
=====================================
toolkit/components/search/content/base-browser-search-engines.json
=====================================
@@ -0,0 +1,133 @@
+[
+ {
+ "base": {
+ "aliases": ["mullvad-leta", "leta", "mullvad", "ml"],
+ "classification": "general",
+ "name": "Mullvad Leta",
+ "urls": {
+ "search": {
+ "base": "https://leta.mullvad.net/",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "ee88d691-6d7a-4adb-9fec-5a205565505a",
+ "identifier": "mullvad-leta",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["duckduckgo", "ddg"],
+ "classification": "general",
+ "name": "DuckDuckGo",
+ "urls": {
+ "search": {
+ "base": "https://duckduckgo.com/",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "04e99a38-13ee-47d8-8aa4-64482b3dea99",
+ "identifier": "ddg",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["ddg-html", "duckduckgohtml", "ddgh"],
+ "classification": "general",
+ "name": "DuckDuckGo (HTML)",
+ "urls": {
+ "search": {
+ "base": "https://html.duckduckgo.com/html/",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "98d8c84b-7455-431d-98b9-890e7bcc0041",
+ "identifier": "ddg-html",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["mojeek", "mj"],
+ "classification": "general",
+ "name": "Mojeek",
+ "urls": {
+ "search": {
+ "base": "https://www.mojeek.com/search",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "10df12ac-2b39-4aa9-8845-d5b35d5bb70c",
+ "identifier": "mojeek",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["brave", "bv"],
+ "classification": "general",
+ "name": "Brave Search",
+ "urls": {
+ "search": {
+ "base": "https://search.brave.com/search",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "f479314b-030b-49a8-a2fe-7e1c5d1d9071",
+ "identifier": "brave",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["startpage", "sp"],
+ "classification": "general",
+ "name": "Startpage",
+ "urls": {
+ "search": {
+ "base": "https://www.startpage.com/sp/search",
+ "params": [],
+ "searchTermParamName": "q"
+ }
+ }
+ },
+ "id": "927bbd9f-b2f3-48b4-8974-1c1148028f4d",
+ "identifier": "startpage",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "base": {
+ "aliases": ["metager", "mg"],
+ "classification": "general",
+ "name": "MetaGer",
+ "urls": {
+ "search": {
+ "base": "https://metager.org/meta/meta.ger3",
+ "params": [],
+ "searchTermParamName": "eingabe"
+ }
+ }
+ },
+ "id": "a9d07d93-469c-4bf4-8dd1-fa137f1cc85f",
+ "identifier": "metager",
+ "recordType": "engine",
+ "variants": [{ "environment": { "allRegionsAndLocales": true } }]
+ },
+ {
+ "recordType": "defaultEngines",
+ "globalDefault": "mullvad-leta",
+ "globalDefaultPrivate": "mullvad-leta"
+ }
+]
=====================================
toolkit/components/search/content/mullvadBrowserSearchEngines.json deleted
=====================================
@@ -1,114 +0,0 @@
-[
- {
- "aliases": ["mullvad-leta", "leta", "mullvad", "ml"],
- "name": "Mullvad Leta",
- "urls": {
- "search": {
- "base": "https://leta.mullvad.net/",
- "params": [],
- "searchTermParamName": "q"
- }
- },
- "id": "ee88d691-6d7a-4adb-9fec-5a205565505a",
- "identifier": "mullvad-leta",
- "recordType": "engine",
- "orderHint": 100,
- "variants": []
- },
- {
- "aliases": ["duckduckgo", "ddg"],
- "name": "DuckDuckGo",
- "urls": {
- "search": {
- "base": "https://duckduckgo.com/",
- "params": [],
- "searchTermParamName": "q"
- }
- },
- "id": "04e99a38-13ee-47d8-8aa4-64482b3dea99",
- "identifier": "ddg",
- "recordType": "engine",
- "orderHint": 90,
- "variants": []
- },
- {
- "aliases": ["ddg-html", "duckduckgohtml", "ddgh"],
- "name": "DuckDuckGo (HTML)",
- "urls": {
- "search": {
- "base": "https://html.duckduckgo.com/html/",
- "params": [],
- "searchTermParamName": "q"
- }
- },
- "id": "98d8c84b-7455-431d-98b9-890e7bcc0041",
- "identifier": "ddg-html",
- "recordType": "engine",
- "orderHint": 80,
- "variants": []
- },
- {
- "aliases": ["mojeek", "mj"],
- "name": "Mojeek",
- "urls": {
- "search": {
- "base": "https://www.mojeek.com/search",
- "params": [],
- "searchTermParamName": "q"
- }
- },
- "id": "10df12ac-2b39-4aa9-8845-d5b35d5bb70c",
- "identifier": "mojeek",
- "recordType": "engine",
- "orderHint": 70,
- "variants": []
- },
- {
- "aliases": ["brave", "bv"],
- "name": "Brave Search",
- "urls": {
- "search": {
- "base": "https://search.brave.com/search",
- "params": [],
- "searchTermParamName": "q"
- }
- },
- "id": "f479314b-030b-49a8-a2fe-7e1c5d1d9071",
- "identifier": "brave",
- "recordType": "engine",
- "orderHint": 60,
- "variants": []
- },
- {
- "aliases": ["startpage", "sp"],
- "name": "Startpage",
- "urls": {
- "search": {
- "base": "https://www.startpage.com/sp/search",
- "params": [],
- "searchTermParamName": "q"
- }
- },
- "id": "049f86fd-28fe-4389-910f-aac28f07d745",
- "identifier": "startpage",
- "recordType": "engine",
- "orderHint": 50,
- "variants": []
- },
- {
- "aliases": ["metager", "mg"],
- "name": "MetaGer",
- "urls": {
- "search": {
- "base": "https://metager.org/meta/meta.ger3",
- "params": [],
- "searchTermParamName": "eingabe"
- }
- },
- "id": "a9d07d93-469c-4bf4-8dd1-fa137f1cc85f",
- "identifier": "metager",
- "recordType": "engine",
- "orderHint": 40,
- "variants": []
- }
-]
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/ac…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/ac…
You're receiving this email because of your account on gitlab.torproject.org.
1
0