tbb-commits
Threads by month
- ----- 2025 -----
- 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
- 19369 discussions

[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
by morgan (@morgan) 15 Oct '25
by morgan (@morgan) 15 Oct '25
15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
23c8a66b by Henry Wilkes at 2025-10-15T17:43:21+00:00
fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
TB 44101: Show tor connection status in the nav bar when the tab bar is
hidden.
- - - - -
4 changed files:
- browser/base/content/navigator-toolbox.inc.xhtml
- toolkit/components/torconnect/content/torConnectTitlebarStatus.css
- + toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml
- toolkit/components/torconnect/content/torConnectTitlebarStatus.js
Changes:
=====================================
browser/base/content/navigator-toolbox.inc.xhtml
=====================================
@@ -100,12 +100,7 @@
#include private-browsing-indicator.inc.xhtml
<toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/>
- <html:div id="tor-connect-titlebar-status" role="status">
- <html:img alt=""
- src="chrome://global/content/torconnect/tor-not-connected-to-connected-animated.svg" />
- <html:span id="tor-connect-titlebar-status-label"></html:span>
- </html:div>
-
+#include ../../../toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml
#include titlebar-items.inc.xhtml
</toolbar>
@@ -526,6 +521,7 @@
<hbox class="titlebar-spacer" type="post-tabs"/>
#include private-browsing-indicator.inc.xhtml
<toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/>
+#include ../../../toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml
#include titlebar-items.inc.xhtml
</toolbar>
=====================================
toolkit/components/torconnect/content/torConnectTitlebarStatus.css
=====================================
@@ -1,16 +1,28 @@
-#tor-connect-titlebar-status:not([hidden]) {
+.tor-connect-titlebar-status:not([hidden]) {
display: flex;
align-items: center;
- /* Want same as #private-browsing-indicator-with-label */
+ /* Want same as .private-browsing-indicator-with-label */
margin-inline: 7px;
+
+ #navigator-toolbox[tabs-hidden] #TabsToolbar > & {
+ /* Hide in the tabs bar when the tabs bar is hidden. E.g. when using
+ * vertical tabs. Should be shown in the #nav-bar instead.
+ * See tor-browser#44101. */
+ display: none;
+ }
+
+ #navigator-toolbox:not([tabs-hidden]) #nav-bar > & {
+ /* Hide in the nav bar when the (horizontal) tabs bar is visible. */
+ display: none;
+ }
}
-#tor-connect-titlebar-status-label {
+.tor-connect-titlebar-status-label {
margin-inline: 6px;
white-space: nowrap;
}
-#tor-connect-titlebar-status img {
+.tor-connect-titlebar-status img {
-moz-context-properties: fill, stroke;
fill: var(--icon-color);
stroke: var(--icon-color);
@@ -25,7 +37,7 @@
object-position: var(--tor-not-connected-offset);
}
-#tor-connect-titlebar-status.tor-connect-status-potentially-blocked img {
+.tor-connect-titlebar-status.tor-connect-status-potentially-blocked img {
/* NOTE: context-stroke is only used for the first "frame" for the slash. When
* we assign the potentially-blocked class, we do *not* expect to be connected
* at the same time, so we only expect this first frame to be visible in this
@@ -33,17 +45,17 @@
stroke: var(--icon-color-critical);
}
-#tor-connect-titlebar-status.tor-connect-status-connected img {
+.tor-connect-titlebar-status.tor-connect-status-connected img {
object-position: var(--tor-connected-offset);
}
@media not ((prefers-contrast) or (forced-colors)) {
/* Make the connected text and icon purple. */
- #tor-connect-titlebar-status.tor-connect-status-connected {
+ .tor-connect-titlebar-status.tor-connect-status-connected {
color: var(--tor-text-color);
}
- #tor-connect-titlebar-status.tor-connect-status-connected img {
+ .tor-connect-titlebar-status.tor-connect-status-connected img {
fill: var(--tor-text-color);
stroke: var(--tor-text-color);
}
@@ -60,11 +72,11 @@
}
@media (prefers-reduced-motion: no-preference) {
- #tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition {
+ .tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition {
transition: color 1000ms;
}
- #tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition img {
+ .tor-connect-titlebar-status.tor-connect-status-connected.tor-connect-status-animate-transition img {
transition: fill 1000ms, stroke 1000ms;
animation-name: onion-not-connected-to-connected;
animation-delay: 200ms;
=====================================
toolkit/components/torconnect/content/torConnectTitlebarStatus.inc.xhtml
=====================================
@@ -0,0 +1,7 @@
+<html:div class="tor-connect-titlebar-status" role="status">
+ <html:img
+ alt=""
+ src="chrome://global/content/torconnect/tor-not-connected-to-connected-animated.svg"
+ />
+ <html:span class="tor-connect-titlebar-status-label"></html:span>
+</html:div>
=====================================
toolkit/components/torconnect/content/torConnectTitlebarStatus.js
=====================================
@@ -3,21 +3,15 @@
*/
var gTorConnectTitlebarStatus = {
/**
- * The status element in the title bar.
+ * The status elements and their labels.
*
- * @type {Element}
+ * @type {{status: Element, label: Element}[]}
*/
- node: null,
- /**
- * The status label.
- *
- * @type {Element}
- */
- label: null,
+ _elements: [],
/**
* Whether we are connected, or null if the connection state is not yet known.
*
- * @type {bool?}
+ * @type {boolean?}
*/
connected: null,
@@ -31,21 +25,21 @@ var gTorConnectTitlebarStatus = {
this._strings = TorStrings.torConnect;
- this.node = document.getElementById("tor-connect-titlebar-status");
- this.label = document.getElementById("tor-connect-titlebar-status-label");
+ this._elements = Array.from(
+ document.querySelectorAll(".tor-connect-titlebar-status"),
+ element => {
+ return {
+ status: element,
+ label: element.querySelector(".tor-connect-titlebar-status-label"),
+ };
+ }
+ );
// The title also acts as an accessible name for the role="status".
- this.node.setAttribute("title", this._strings.titlebarStatusName);
+ for (const { status } of this._elements) {
+ status.setAttribute("title", this._strings.titlebarStatusName);
+ }
- this._observeTopic = TorConnectTopics.StageChange;
- this._stateListener = {
- observe: (subject, topic) => {
- if (topic !== this._observeTopic) {
- return;
- }
- this._torConnectStateChanged();
- },
- };
- Services.obs.addObserver(this._stateListener, this._observeTopic);
+ Services.obs.addObserver(this, TorConnectTopics.StageChange);
this._torConnectStateChanged();
},
@@ -54,7 +48,15 @@ var gTorConnectTitlebarStatus = {
* De-initialize the component.
*/
uninit() {
- Services.obs.removeObserver(this._stateListener, this._observeTopic);
+ Services.obs.removeObserver(this, TorConnectTopics.StageChange);
+ },
+
+ observe(subject, topic) {
+ switch (topic) {
+ case TorConnectTopics.StageChange:
+ this._torConnectStateChanged();
+ break;
+ }
},
/**
@@ -67,7 +69,7 @@ var gTorConnectTitlebarStatus = {
switch (TorConnect.stageName) {
case TorConnectStage.Disabled:
// Hide immediately.
- this.node.hidden = true;
+ this._setHidden(true);
return;
case TorConnectStage.Bootstrapped:
textId = "titlebarStatusConnected";
@@ -85,7 +87,9 @@ var gTorConnectTitlebarStatus = {
}
break;
}
- this.label.textContent = this._strings[textId];
+ for (const { label } of this._elements) {
+ label.textContent = this._strings[textId];
+ }
if (this.connected !== connected) {
// When we are transitioning from
// this.connected = false
@@ -104,11 +108,13 @@ var gTorConnectTitlebarStatus = {
//
// We only expect this latter case when opening a new window after
// bootstrapping has already completed. See tor-browser#41850.
- this.node.classList.toggle(
- "tor-connect-status-animate-transition",
- connected && this.connected !== null
- );
- this.node.classList.toggle("tor-connect-status-connected", connected);
+ for (const { status } of this._elements) {
+ status.classList.toggle(
+ "tor-connect-status-animate-transition",
+ connected && this.connected !== null
+ );
+ status.classList.toggle("tor-connect-status-connected", connected);
+ }
this.connected = connected;
if (connected) {
this._startHiding();
@@ -119,10 +125,23 @@ var gTorConnectTitlebarStatus = {
this._stopHiding();
}
}
- this.node.classList.toggle(
- "tor-connect-status-potentially-blocked",
- potentiallyBlocked
- );
+ for (const { status } of this._elements) {
+ status.classList.toggle(
+ "tor-connect-status-potentially-blocked",
+ potentiallyBlocked
+ );
+ }
+ },
+
+ /**
+ * Hide or show the status.
+ *
+ * @param {boolean} hide - Whether to hide the status.
+ */
+ _setHidden(hide) {
+ for (const { status } of this._elements) {
+ status.hidden = hide;
+ }
},
/**
@@ -134,7 +153,7 @@ var gTorConnectTitlebarStatus = {
return;
}
this._hidingTimeout = setTimeout(() => {
- this.node.hidden = true;
+ this._setHidden(true);
}, 5000);
},
@@ -146,6 +165,6 @@ var gTorConnectTitlebarStatus = {
clearTimeout(this._hidingTimeout);
this._hidingTimeout = 0;
}
- this.node.hidden = false;
+ this._setHidden(false);
},
};
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/23c8a66…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/23c8a66…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
by morgan (@morgan) 15 Oct '25
by morgan (@morgan) 15 Oct '25
15 Oct '25
morgan pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
9795642d by Pier Angelo Vendrame at 2025-10-15T17:37:56+00:00
Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D268183
- - - - -
5 changed files:
- browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
- toolkit/components/search/OpenSearchEngine.sys.mjs
- toolkit/components/search/SearchEngine.sys.mjs
- toolkit/components/search/SearchService.sys.mjs
- toolkit/components/search/SearchUtils.sys.mjs
Changes:
=====================================
browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
=====================================
@@ -286,10 +286,22 @@ class ProviderContextualSearch extends ActionsProvider {
let { type, engine } = this.#resultEngine;
if (type == OPEN_SEARCH_ENGINE) {
+ let originAttributes;
+ try {
+ let currentURI = Services.io.newURI(queryContext.currentPage);
+ originAttributes = {
+ firstPartyDomain: Services.eTLD.getSchemelessSite(currentURI),
+ };
+ } catch {}
let openSearchEngineData = await lazy.loadAndParseOpenSearchEngine(
- Services.io.newURI(engine.uri)
+ Services.io.newURI(engine.uri),
+ null,
+ originAttributes
);
- engine = new lazy.OpenSearchEngine({ engineData: openSearchEngineData });
+ engine = new lazy.OpenSearchEngine({
+ engineData: openSearchEngineData,
+ originAttributes,
+ });
}
this.#performSearch(
=====================================
toolkit/components/search/OpenSearchEngine.sys.mjs
=====================================
@@ -56,6 +56,8 @@ export class OpenSearchEngine extends SearchEngine {
* @param {string} [options.faviconURL]
* The website favicon, to be used if the engine data hasn't specified an
* icon.
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to download additional resources.
*/
constructor(options = {}) {
super({
@@ -68,7 +70,10 @@ export class OpenSearchEngine extends SearchEngine {
});
if (options.faviconURL) {
- this._setIcon(options.faviconURL, undefined, false).catch(e =>
+ this._setIcon(options.faviconURL, {
+ override: false,
+ originAttributes: options.originAttributes,
+ }).catch(e =>
lazy.logConsole.error(
`Error while setting icon for search engine ${options.engineData.name}:`,
e.message
@@ -77,7 +82,7 @@ export class OpenSearchEngine extends SearchEngine {
}
if (options.engineData) {
- this.#setEngineData(options.engineData);
+ this.#setEngineData(options.engineData, options.originAttributes);
// As this is a new engine, we must set the verification hash for the load
// path set in the constructor.
@@ -189,8 +194,10 @@ export class OpenSearchEngine extends SearchEngine {
*
* @param {OpenSearchProperties} data
* The OpenSearch data.
+ * @param {object} originAttributes
+ * The origin attributes for any additional downloads
*/
- #setEngineData(data) {
+ #setEngineData(data, originAttributes) {
let name = data.name.trim();
if (Services.search.getEngineByName(name)) {
throw Components.Exception(
@@ -258,11 +265,12 @@ export class OpenSearchEngine extends SearchEngine {
}
for (let image of data.images) {
- this._setIcon(image.url, image.size).catch(e =>
- lazy.logConsole.error(
- `Error while setting icon for search engine ${data.name}:`,
- e.message
- )
+ this._setIcon(image.url, { size: image.size, originAttributes }).catch(
+ e =>
+ lazy.logConsole.error(
+ `Error while setting icon for search engine ${data.name}:`,
+ e.message
+ )
);
}
}
=====================================
toolkit/components/search/SearchEngine.sys.mjs
=====================================
@@ -585,15 +585,19 @@ export class SearchEngine {
* @param {string} iconURL
* A URI string pointing to the engine's icon.
* Must have http[s], data, or moz-extension protocol.
- * @param {number} [size]
+ * @param {object} options
+ * The options object
+ * @param {number} [options.size]
* Width and height of the icon (determined automatically if not provided).
- * @param {boolean} [override]
+ * @param {boolean} [options.override]
* Whether the new URI should override an existing one.
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to load the icon.
* @returns {Promise<void>}
* Resolves when the icon was set.
* Rejects with an Error if there was an error.
*/
- async _setIcon(iconURL, size, override = true) {
+ async _setIcon(iconURL, options = { override: true }) {
lazy.logConsole.debug(
"_setIcon: Setting icon url for",
this.name,
@@ -601,8 +605,12 @@ export class SearchEngine {
limitURILength(iconURL)
);
- [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, size);
- this._addIconToMap(iconURL, size, override);
+ let size;
+ [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, {
+ size: options.size,
+ originAttributes: options.originAttributes,
+ });
+ this._addIconToMap(iconURL, size, options.override);
if (this._engineAddedToStore) {
lazy.SearchUtils.notifyAction(
@@ -620,18 +628,24 @@ export class SearchEngine {
* @param {string} iconURL
* A URI string pointing to the engine's icon.
* Must have http[s], data, or moz-extension protocol.
- * @param {number} [size]
+ * @param {object} options
+ * The options object
+ * @param {number} [options.size]
* Width and height of the icon (determined automatically if not provided).
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to load the icon.
* @returns {Promise<[string, number]>}
* Resolves to [dataURL, size] if successful and rejects if there was an error.
*/
- async _downloadAndRescaleIcon(iconURL, size) {
+ async _downloadAndRescaleIcon(iconURL, options = {}) {
let uri = lazy.SearchUtils.makeURI(iconURL);
if (!uri) {
throw new Error(`Invalid URI`);
}
+ let size = options.size;
+
switch (uri.scheme) {
case "moz-extension": {
if (!size) {
@@ -644,7 +658,10 @@ export class SearchEngine {
case "data":
case "http":
case "https": {
- let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(uri);
+ let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(
+ uri,
+ options.originAttributes
+ );
if (byteArray.length > lazy.SearchUtils.MAX_ICON_SIZE) {
lazy.logConsole.debug(
`Rescaling icon for search engine ${this.name}.`
=====================================
toolkit/components/search/SearchService.sys.mjs
=====================================
@@ -772,7 +772,11 @@ export class SearchService {
null,
originAttributes
);
- engine = new lazy.OpenSearchEngine({ engineData, faviconURL: iconURL });
+ engine = new lazy.OpenSearchEngine({
+ engineData,
+ faviconURL: iconURL,
+ originAttributes,
+ });
} catch (ex) {
throw Components.Exception(
"addEngine: Error adding engine:\n" + ex,
=====================================
toolkit/components/search/SearchUtils.sys.mjs
=====================================
@@ -511,13 +511,19 @@ export var SearchUtils = {
*
* @param {string|nsIURI} uri
* The URI to the icon.
+ * @param {object} [originAttributes]
+ * The origin attributes to download the icon.
* @returns {Promise<[Uint8Array, string]>}
* Resolves to an array containing the data and the mime type.
* Rejects if the icon cannot be fetched.
*/
- async fetchIcon(uri) {
+ async fetchIcon(uri, originAttributes = null) {
return new Promise((resolve, reject) => {
- let chan = SearchUtils.makeChannel(uri, Ci.nsIContentPolicy.TYPE_IMAGE);
+ let chan = SearchUtils.makeChannel(
+ uri,
+ Ci.nsIContentPolicy.TYPE_IMAGE,
+ originAttributes
+ );
let listener = new SearchUtils.LoadListener(
chan,
/^image\//,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/979…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/979…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
by morgan (@morgan) 15 Oct '25
by morgan (@morgan) 15 Oct '25
15 Oct '25
morgan pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
803d875f by Pier Angelo Vendrame at 2025-10-15T17:37:02+00:00
Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D268183
- - - - -
5 changed files:
- browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
- toolkit/components/search/OpenSearchEngine.sys.mjs
- toolkit/components/search/SearchEngine.sys.mjs
- toolkit/components/search/SearchService.sys.mjs
- toolkit/components/search/SearchUtils.sys.mjs
Changes:
=====================================
browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
=====================================
@@ -286,10 +286,22 @@ class ProviderContextualSearch extends ActionsProvider {
let { type, engine } = this.#resultEngine;
if (type == OPEN_SEARCH_ENGINE) {
+ let originAttributes;
+ try {
+ let currentURI = Services.io.newURI(queryContext.currentPage);
+ originAttributes = {
+ firstPartyDomain: Services.eTLD.getSchemelessSite(currentURI),
+ };
+ } catch {}
let openSearchEngineData = await lazy.loadAndParseOpenSearchEngine(
- Services.io.newURI(engine.uri)
+ Services.io.newURI(engine.uri),
+ null,
+ originAttributes
);
- engine = new lazy.OpenSearchEngine({ engineData: openSearchEngineData });
+ engine = new lazy.OpenSearchEngine({
+ engineData: openSearchEngineData,
+ originAttributes,
+ });
}
this.#performSearch(
=====================================
toolkit/components/search/OpenSearchEngine.sys.mjs
=====================================
@@ -56,6 +56,8 @@ export class OpenSearchEngine extends SearchEngine {
* @param {string} [options.faviconURL]
* The website favicon, to be used if the engine data hasn't specified an
* icon.
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to download additional resources.
*/
constructor(options = {}) {
super({
@@ -68,7 +70,10 @@ export class OpenSearchEngine extends SearchEngine {
});
if (options.faviconURL) {
- this._setIcon(options.faviconURL, undefined, false).catch(e =>
+ this._setIcon(options.faviconURL, {
+ override: false,
+ originAttributes: options.originAttributes,
+ }).catch(e =>
lazy.logConsole.error(
`Error while setting icon for search engine ${options.engineData.name}:`,
e.message
@@ -77,7 +82,7 @@ export class OpenSearchEngine extends SearchEngine {
}
if (options.engineData) {
- this.#setEngineData(options.engineData);
+ this.#setEngineData(options.engineData, options.originAttributes);
// As this is a new engine, we must set the verification hash for the load
// path set in the constructor.
@@ -189,8 +194,10 @@ export class OpenSearchEngine extends SearchEngine {
*
* @param {OpenSearchProperties} data
* The OpenSearch data.
+ * @param {object} originAttributes
+ * The origin attributes for any additional downloads
*/
- #setEngineData(data) {
+ #setEngineData(data, originAttributes) {
let name = data.name.trim();
if (Services.search.getEngineByName(name)) {
throw Components.Exception(
@@ -258,11 +265,12 @@ export class OpenSearchEngine extends SearchEngine {
}
for (let image of data.images) {
- this._setIcon(image.url, image.size).catch(e =>
- lazy.logConsole.error(
- `Error while setting icon for search engine ${data.name}:`,
- e.message
- )
+ this._setIcon(image.url, { size: image.size, originAttributes }).catch(
+ e =>
+ lazy.logConsole.error(
+ `Error while setting icon for search engine ${data.name}:`,
+ e.message
+ )
);
}
}
=====================================
toolkit/components/search/SearchEngine.sys.mjs
=====================================
@@ -585,15 +585,19 @@ export class SearchEngine {
* @param {string} iconURL
* A URI string pointing to the engine's icon.
* Must have http[s], data, or moz-extension protocol.
- * @param {number} [size]
+ * @param {object} options
+ * The options object
+ * @param {number} [options.size]
* Width and height of the icon (determined automatically if not provided).
- * @param {boolean} [override]
+ * @param {boolean} [options.override]
* Whether the new URI should override an existing one.
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to load the icon.
* @returns {Promise<void>}
* Resolves when the icon was set.
* Rejects with an Error if there was an error.
*/
- async _setIcon(iconURL, size, override = true) {
+ async _setIcon(iconURL, options = { override: true }) {
lazy.logConsole.debug(
"_setIcon: Setting icon url for",
this.name,
@@ -601,8 +605,12 @@ export class SearchEngine {
limitURILength(iconURL)
);
- [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, size);
- this._addIconToMap(iconURL, size, override);
+ let size;
+ [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, {
+ size: options.size,
+ originAttributes: options.originAttributes,
+ });
+ this._addIconToMap(iconURL, size, options.override);
if (this._engineAddedToStore) {
lazy.SearchUtils.notifyAction(
@@ -620,18 +628,24 @@ export class SearchEngine {
* @param {string} iconURL
* A URI string pointing to the engine's icon.
* Must have http[s], data, or moz-extension protocol.
- * @param {number} [size]
+ * @param {object} options
+ * The options object
+ * @param {number} [options.size]
* Width and height of the icon (determined automatically if not provided).
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to load the icon.
* @returns {Promise<[string, number]>}
* Resolves to [dataURL, size] if successful and rejects if there was an error.
*/
- async _downloadAndRescaleIcon(iconURL, size) {
+ async _downloadAndRescaleIcon(iconURL, options = {}) {
let uri = lazy.SearchUtils.makeURI(iconURL);
if (!uri) {
throw new Error(`Invalid URI`);
}
+ let size = options.size;
+
switch (uri.scheme) {
case "moz-extension": {
if (!size) {
@@ -644,7 +658,10 @@ export class SearchEngine {
case "data":
case "http":
case "https": {
- let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(uri);
+ let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(
+ uri,
+ options.originAttributes
+ );
if (byteArray.length > lazy.SearchUtils.MAX_ICON_SIZE) {
lazy.logConsole.debug(
`Rescaling icon for search engine ${this.name}.`
=====================================
toolkit/components/search/SearchService.sys.mjs
=====================================
@@ -772,7 +772,11 @@ export class SearchService {
null,
originAttributes
);
- engine = new lazy.OpenSearchEngine({ engineData, faviconURL: iconURL });
+ engine = new lazy.OpenSearchEngine({
+ engineData,
+ faviconURL: iconURL,
+ originAttributes,
+ });
} catch (ex) {
throw Components.Exception(
"addEngine: Error adding engine:\n" + ex,
=====================================
toolkit/components/search/SearchUtils.sys.mjs
=====================================
@@ -511,13 +511,19 @@ export var SearchUtils = {
*
* @param {string|nsIURI} uri
* The URI to the icon.
+ * @param {object} [originAttributes]
+ * The origin attributes to download the icon.
* @returns {Promise<[Uint8Array, string]>}
* Resolves to an array containing the data and the mime type.
* Rejects if the icon cannot be fetched.
*/
- async fetchIcon(uri) {
+ async fetchIcon(uri, originAttributes = null) {
return new Promise((resolve, reject) => {
- let chan = SearchUtils.makeChannel(uri, Ci.nsIContentPolicy.TYPE_IMAGE);
+ let chan = SearchUtils.makeChannel(
+ uri,
+ Ci.nsIContentPolicy.TYPE_IMAGE,
+ originAttributes
+ );
let listener = new SearchUtils.LoadListener(
chan,
/^image\//,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/803d875…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/803d875…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
by morgan (@morgan) 15 Oct '25
by morgan (@morgan) 15 Oct '25
15 Oct '25
morgan pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
39767109 by Pier Angelo Vendrame at 2025-10-15T17:28:31+00:00
Bug 1993166 - Improve origin attributes on opensearch. r=Standard8,urlbar-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D268183
- - - - -
5 changed files:
- browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
- toolkit/components/search/OpenSearchEngine.sys.mjs
- toolkit/components/search/SearchEngine.sys.mjs
- toolkit/components/search/SearchService.sys.mjs
- toolkit/components/search/SearchUtils.sys.mjs
Changes:
=====================================
browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs
=====================================
@@ -286,10 +286,22 @@ class ProviderContextualSearch extends ActionsProvider {
let { type, engine } = this.#resultEngine;
if (type == OPEN_SEARCH_ENGINE) {
+ let originAttributes;
+ try {
+ let currentURI = Services.io.newURI(queryContext.currentPage);
+ originAttributes = {
+ firstPartyDomain: Services.eTLD.getSchemelessSite(currentURI),
+ };
+ } catch {}
let openSearchEngineData = await lazy.loadAndParseOpenSearchEngine(
- Services.io.newURI(engine.uri)
+ Services.io.newURI(engine.uri),
+ null,
+ originAttributes
);
- engine = new lazy.OpenSearchEngine({ engineData: openSearchEngineData });
+ engine = new lazy.OpenSearchEngine({
+ engineData: openSearchEngineData,
+ originAttributes,
+ });
}
this.#performSearch(
=====================================
toolkit/components/search/OpenSearchEngine.sys.mjs
=====================================
@@ -56,6 +56,8 @@ export class OpenSearchEngine extends SearchEngine {
* @param {string} [options.faviconURL]
* The website favicon, to be used if the engine data hasn't specified an
* icon.
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to download additional resources.
*/
constructor(options = {}) {
super({
@@ -68,7 +70,10 @@ export class OpenSearchEngine extends SearchEngine {
});
if (options.faviconURL) {
- this._setIcon(options.faviconURL, undefined, false).catch(e =>
+ this._setIcon(options.faviconURL, {
+ override: false,
+ originAttributes: options.originAttributes,
+ }).catch(e =>
lazy.logConsole.error(
`Error while setting icon for search engine ${options.engineData.name}:`,
e.message
@@ -77,7 +82,7 @@ export class OpenSearchEngine extends SearchEngine {
}
if (options.engineData) {
- this.#setEngineData(options.engineData);
+ this.#setEngineData(options.engineData, options.originAttributes);
// As this is a new engine, we must set the verification hash for the load
// path set in the constructor.
@@ -189,8 +194,10 @@ export class OpenSearchEngine extends SearchEngine {
*
* @param {OpenSearchProperties} data
* The OpenSearch data.
+ * @param {object} originAttributes
+ * The origin attributes for any additional downloads
*/
- #setEngineData(data) {
+ #setEngineData(data, originAttributes) {
let name = data.name.trim();
if (Services.search.getEngineByName(name)) {
throw Components.Exception(
@@ -258,11 +265,12 @@ export class OpenSearchEngine extends SearchEngine {
}
for (let image of data.images) {
- this._setIcon(image.url, image.size).catch(e =>
- lazy.logConsole.error(
- `Error while setting icon for search engine ${data.name}:`,
- e.message
- )
+ this._setIcon(image.url, { size: image.size, originAttributes }).catch(
+ e =>
+ lazy.logConsole.error(
+ `Error while setting icon for search engine ${data.name}:`,
+ e.message
+ )
);
}
}
=====================================
toolkit/components/search/SearchEngine.sys.mjs
=====================================
@@ -585,15 +585,19 @@ export class SearchEngine {
* @param {string} iconURL
* A URI string pointing to the engine's icon.
* Must have http[s], data, or moz-extension protocol.
- * @param {number} [size]
+ * @param {object} options
+ * The options object
+ * @param {number} [options.size]
* Width and height of the icon (determined automatically if not provided).
- * @param {boolean} [override]
+ * @param {boolean} [options.override]
* Whether the new URI should override an existing one.
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to load the icon.
* @returns {Promise<void>}
* Resolves when the icon was set.
* Rejects with an Error if there was an error.
*/
- async _setIcon(iconURL, size, override = true) {
+ async _setIcon(iconURL, options = { override: true }) {
lazy.logConsole.debug(
"_setIcon: Setting icon url for",
this.name,
@@ -601,8 +605,12 @@ export class SearchEngine {
limitURILength(iconURL)
);
- [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, size);
- this._addIconToMap(iconURL, size, override);
+ let size;
+ [iconURL, size] = await this._downloadAndRescaleIcon(iconURL, {
+ size: options.size,
+ originAttributes: options.originAttributes,
+ });
+ this._addIconToMap(iconURL, size, options.override);
if (this._engineAddedToStore) {
lazy.SearchUtils.notifyAction(
@@ -620,18 +628,24 @@ export class SearchEngine {
* @param {string} iconURL
* A URI string pointing to the engine's icon.
* Must have http[s], data, or moz-extension protocol.
- * @param {number} [size]
+ * @param {object} options
+ * The options object
+ * @param {number} [options.size]
* Width and height of the icon (determined automatically if not provided).
+ * @param {object} [options.originAttributes]
+ * The origin attributes to use to load the icon.
* @returns {Promise<[string, number]>}
* Resolves to [dataURL, size] if successful and rejects if there was an error.
*/
- async _downloadAndRescaleIcon(iconURL, size) {
+ async _downloadAndRescaleIcon(iconURL, options = {}) {
let uri = lazy.SearchUtils.makeURI(iconURL);
if (!uri) {
throw new Error(`Invalid URI`);
}
+ let size = options.size;
+
switch (uri.scheme) {
case "moz-extension": {
if (!size) {
@@ -644,7 +658,10 @@ export class SearchEngine {
case "data":
case "http":
case "https": {
- let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(uri);
+ let [byteArray, contentType] = await lazy.SearchUtils.fetchIcon(
+ uri,
+ options.originAttributes
+ );
if (byteArray.length > lazy.SearchUtils.MAX_ICON_SIZE) {
lazy.logConsole.debug(
`Rescaling icon for search engine ${this.name}.`
=====================================
toolkit/components/search/SearchService.sys.mjs
=====================================
@@ -772,7 +772,11 @@ export class SearchService {
null,
originAttributes
);
- engine = new lazy.OpenSearchEngine({ engineData, faviconURL: iconURL });
+ engine = new lazy.OpenSearchEngine({
+ engineData,
+ faviconURL: iconURL,
+ originAttributes,
+ });
} catch (ex) {
throw Components.Exception(
"addEngine: Error adding engine:\n" + ex,
=====================================
toolkit/components/search/SearchUtils.sys.mjs
=====================================
@@ -511,13 +511,19 @@ export var SearchUtils = {
*
* @param {string|nsIURI} uri
* The URI to the icon.
+ * @param {object} [originAttributes]
+ * The origin attributes to download the icon.
* @returns {Promise<[Uint8Array, string]>}
* Resolves to an array containing the data and the mime type.
* Rejects if the icon cannot be fetched.
*/
- async fetchIcon(uri) {
+ async fetchIcon(uri, originAttributes = null) {
return new Promise((resolve, reject) => {
- let chan = SearchUtils.makeChannel(uri, Ci.nsIContentPolicy.TYPE_IMAGE);
+ let chan = SearchUtils.makeChannel(
+ uri,
+ Ci.nsIContentPolicy.TYPE_IMAGE,
+ originAttributes
+ );
let listener = new SearchUtils.LoadListener(
chan,
/^image\//,
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/3976710…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/3976710…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/mullvad-browser][mullvad-browser-140.4.0esr-15.0-1] 12 commits: fixup! BB 41919: Letterboxing, add temporarily visible web content-size...
by henry (@henry) 15 Oct '25
by henry (@henry) 15 Oct '25
15 Oct '25
henry pushed to branch mullvad-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Mullvad Browser
Commits:
81be023b by Henry Wilkes at 2025-10-15T18:07:04+01:00
fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing.
TB 44214: Fix letterboxing status indicator for RTL.
- - - - -
deed64ab by Henry Wilkes at 2025-10-15T18:07:05+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop unnecessary CSS rules.
- - - - -
f18fc8f5 by Henry Wilkes at 2025-10-15T18:07:06+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS logical positions for the status panel, rather than
"left" and "right".
- - - - -
77ea2cff by Henry Wilkes at 2025-10-15T18:07:06+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop upstream's rules for placing content.
- - - - -
725f9b44 by Henry Wilkes at 2025-10-15T18:07:07+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop letterboxing gradient.
- - - - -
c5acc4f7 by Henry Wilkes at 2025-10-15T18:07:08+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Separate out the essential --letterboxing-width and
--letterboxing-height rules into their own .letterboxing block, to have
the property values set on.
- - - - -
d3f14e86 by Henry Wilkes at 2025-10-15T18:07:09+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS nesting. Part 1.
- - - - -
f622ced5 by Henry Wilkes at 2025-10-15T18:07:09+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS nesting. Part 2.
- - - - -
9bd8b0b8 by Henry Wilkes at 2025-10-15T18:07:10+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Move the letterboxing classes one element up from tabpanels to
tabbox.
This is because we need to restyle the tabbox.
- - - - -
f6b94a03 by Henry Wilkes at 2025-10-15T18:07:11+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use a CSS class to show/hide the letterboxing border, rather
than setting the border-radius in javascript.
- - - - -
9f0d03ed by Henry Wilkes at 2025-10-15T18:07:12+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Rename CSS variable from top-radius to radius-top. This is
closer to what upstream has done recently for tokens, where higher
specificity is appended.
- - - - -
a11336fe by Henry Wilkes at 2025-10-15T18:07:12+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Update letterboxing styling for ESR 140.
- - - - -
4 changed files:
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/themes/shared/tabbrowser/content-area.css
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
- toolkit/components/resistfingerprinting/content/letterboxing.css
Changes:
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -879,7 +879,13 @@ var FullScreen = {
}
this._isChromeCollapsed = false;
- Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
+ // Need a subject to know which window this applies to.
+ // Base browser patch can be dropped after bugzilla bug 1992036.
+ Services.obs.notifyObservers(
+ gNavToolbox,
+ "fullscreen-nav-toolbox",
+ "shown"
+ );
},
hideNavToolbox(aAnimate = false) {
@@ -943,7 +949,13 @@ var FullScreen = {
gNavToolbox.style.marginTop =
-gNavToolbox.getBoundingClientRect().height + "px";
this._isChromeCollapsed = true;
- Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");
+ // Need a subject to know which window this applies to.
+ // Base browser patch can be dropped after bugzilla bug 1880918.
+ Services.obs.notifyObservers(
+ gNavToolbox,
+ "fullscreen-nav-toolbox",
+ "hidden"
+ );
MousePosTracker.removeListener(this);
},
=====================================
browser/themes/shared/tabbrowser/content-area.css
=====================================
@@ -242,13 +242,17 @@
}
}
-#statuspanel[type=letterboxingStatus] > #statuspanel-label,
-#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label {
+#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label {
background-image: url("chrome://browser/skin/window.svg");
background-size: 1em;
background-repeat: no-repeat;
- background-position-x: .5em;
background-position-y: center;
+ background-position-x: left .5em;
+
+ &:-moz-locale-dir(rtl) {
+ background-position-x: right .5em;
+ }
+
padding-inline-start: 2em;
-moz-context-properties: fill;
fill: var(--color-accent-primary);
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -18,8 +18,6 @@ const kPrefLetterboxingTesting =
"privacy.resistFingerprinting.letterboxing.testing";
const kPrefLetterboxingVcenter =
"privacy.resistFingerprinting.letterboxing.vcenter";
-const kPrefLetterboxingGradient =
- "privacy.resistFingerprinting.letterboxing.gradient";
const kPrefLetterboxingDidForceSize =
"privacy.resistFingerprinting.letterboxing.didForceSize";
const kPrefLetterboxingRememberSize =
@@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize =
const kTopicDOMWindowOpened = "domwindowopened";
const kTopicDOMWindowClosed = "domwindowclosed";
+const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox";
+
const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings";
+const kPrefVerticalTabs = "sidebar.verticalTabs";
+
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ Color: "resource://gre/modules/Color.sys.mjs",
+});
ChromeUtils.defineLazyGetter(lazy, "logConsole", () =>
console.createInstance({
prefix: "RFPHelper",
@@ -155,7 +160,8 @@ class _RFPHelper {
Services.prefs.addObserver(kPrefResistFingerprinting, this);
Services.prefs.addObserver(kPrefLetterboxing, this);
Services.prefs.addObserver(kPrefLetterboxingVcenter, this);
- Services.prefs.addObserver(kPrefLetterboxingGradient, this);
+ Services.prefs.addObserver(kPrefVerticalTabs, this);
+ Services.obs.addObserver(this, kTopicFullscreenNavToolbox);
XPCOMUtils.defineLazyPreferenceGetter(
this,
@@ -188,9 +194,10 @@ class _RFPHelper {
// Remove unconditional observers
Services.prefs.removeObserver(kPrefResistFingerprinting, this);
- Services.prefs.removeObserver(kPrefLetterboxingGradient, this);
Services.prefs.removeObserver(kPrefLetterboxingVcenter, this);
Services.prefs.removeObserver(kPrefLetterboxing, this);
+ Services.prefs.removeObserver(kPrefVerticalTabs, this);
+ Services.obs.removeObserver(this, kTopicFullscreenNavToolbox);
// Remove the RFP observers, swallowing exceptions if they weren't present
this._removeLanguagePrefObservers();
}
@@ -212,6 +219,15 @@ class _RFPHelper {
case kTopicDOMWindowClosed:
this._handleDOMWindowClosed(subject);
break;
+ case kTopicFullscreenNavToolbox:
+ // The `subject` is the gNavToolbox.
+ // Record whether the toobox has been hidden when the browser (not
+ // content) is in fullscreen.
+ subject.ownerGlobal.gBrowser.tabbox.classList.toggle(
+ "letterboxing-nav-toolbox-hidden",
+ data === "hidden"
+ );
+ break;
default:
break;
}
@@ -226,6 +242,13 @@ class _RFPHelper {
resizeObserver.observe(browser.parentElement);
break;
}
+ case "nativethemechange":
+ // NOTE: "nativethemechange" seems to always be sent after
+ // "windowlwthemeupdate". So all the lwtheme CSS properties should be
+ // set to the new theme's values already, so we don't need to wait for
+ // windowlwthemeupdate.
+ this._updateLetterboxingColors(aMessage.currentTarget, true);
+ break;
default:
break;
}
@@ -245,9 +268,13 @@ class _RFPHelper {
Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize);
// fall-through
case kPrefLetterboxingVcenter:
- case kPrefLetterboxingGradient:
this._handleLetterboxingPrefChanged();
break;
+ case kPrefVerticalTabs:
+ if (this.letterboxingEnabled) {
+ forEachWindow(win => this._updateLetterboxingColors(win));
+ }
+ break;
default:
break;
}
@@ -452,7 +479,7 @@ class _RFPHelper {
// If not already cached on the document object, traverse the CSSOM and
// find the rule applying the default letterboxing styles to browsers
// preemptively in order to beat race conditions on tab/window creation
- return (document._letterboxingMarginsRule ||= (() => {
+ return (document._letterboxingDefaultRule ||= (() => {
const LETTERBOX_CSS_SELECTOR = ".letterboxing";
const LETTERBOX_CSS_URL =
"chrome://global/content/resistfingerprinting/letterboxing.css";
@@ -688,26 +715,22 @@ class _RFPHelper {
if (lastRoundedSize) {
// Check whether the letterboxing margin is less than the border radius,
- // and if so flatten the borders.
- let borderRadius = parseInt(
- win
- .getComputedStyle(browserContainer)
- .getPropertyValue("--letterboxing-border-radius")
+ // and if so do not show an outline.
+ const gapVertical = parentHeight - lastRoundedSize.height;
+ const gapHorizontal = parentWidth - lastRoundedSize.width;
+ browserParent.classList.toggle(
+ "letterboxing-show-outline",
+ gapVertical >= this._letterboxingBorderRadius ||
+ gapHorizontal >= this._letterboxingBorderRadius
+ );
+ // When the Letterboxing area is top-aligned, only show the sidebar corner
+ // if there is enough horizontal space.
+ // The factor of 4 is from the horizontal centre-alignment and wanting
+ // enough space for twice the corner radius.
+ browserParent.classList.toggle(
+ "letterboxing-show-sidebar-corner",
+ gapHorizontal >= 4 * this._letterboxingBorderRadius
);
- if (
- borderRadius &&
- parentWidth - lastRoundedSize.width < borderRadius &&
- parentHeight - lastRoundedSize.height < borderRadius
- ) {
- borderRadius = 0;
- } else {
- borderRadius = "";
- }
- styleChanges.queueIfNeeded(browserParent, {
- "--letterboxing-decorator-visibility":
- borderRadius === 0 ? "hidden" : "",
- "--letterboxing-border-radius": borderRadius,
- });
if (win.gBrowser.selectedBrowser == aBrowser) {
const updateStatus = async args => {
win.XULBrowserWindow.letterboxingStatus = args
@@ -769,20 +792,31 @@ class _RFPHelper {
_resetContentSize(aBrowser) {
aBrowser.parentElement.classList.add("exclude-letterboxing");
+ aBrowser.parentElement.classList.remove(
+ "letterboxing-show-outline",
+ "letterboxing-show-sidebar-corner"
+ );
}
_updateSizeForTabsInWindow(aWindow) {
let tabBrowser = aWindow.gBrowser;
- tabBrowser.tabpanels?.classList.add("letterboxing");
- tabBrowser.tabpanels?.classList.toggle(
+ tabBrowser.tabbox.classList.add("letterboxing");
+ tabBrowser.tabbox.classList.toggle(
"letterboxing-vcenter",
Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false)
);
- tabBrowser.tabpanels?.classList.toggle(
- "letterboxing-gradient",
- Services.prefs.getBoolPref(kPrefLetterboxingGradient, false)
- );
+ if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) {
+ // Cache the value since it is not expected to change in a session for any
+ // window.
+ this._letterboxingBorderRadius = Math.ceil(
+ parseFloat(
+ aWindow
+ .getComputedStyle(tabBrowser.tabbox)
+ .getPropertyValue("--letterboxing-border-radius")
+ )
+ );
+ }
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
@@ -791,7 +825,7 @@ class _RFPHelper {
// We need to add this class late because otherwise new windows get
// maximized.
aWindow.setTimeout(() => {
- tabBrowser.tabpanels?.classList.add("letterboxing-ready");
+ tabBrowser.tabbox.classList.add("letterboxing-ready");
if (!aWindow._rfpOriginalSize) {
this._recordWindowSize(aWindow);
}
@@ -869,6 +903,247 @@ class _RFPHelper {
this._resizeObservers.set(aWindow, resizeObserver);
// Rounding the content viewport.
this._updateSizeForTabsInWindow(aWindow);
+
+ this._updateLetterboxingColors(aWindow, true);
+ aWindow.addEventListener("nativethemechange", this);
+ }
+
+ /**
+ * Convert a CSS property to its RGBA value.
+ *
+ * @param {Window} win - The window for the element.
+ * @param {CSSStyleDeclaration} style - The computed style for the element we
+ * want to grab the color from.
+ * @param {string} property - The name of the property we want.
+ *
+ * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields
+ * are relative to the 0-255 color range. The "a" field is in the 0-1 range.
+ */
+ _convertToRGBA(win, style, property) {
+ let cssColor = style.getPropertyValue(property);
+ if (!cssColor) {
+ lazy.logConsole.error(`Missing color "${property}"`);
+ return { r: 0, g: 0, b: 0, a: 0 };
+ }
+ const currentColorRegex =
+ /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g;
+ if (currentColorRegex.test(cssColor)) {
+ const currentColor = style.color;
+ cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => {
+ return pre + currentColor + post;
+ });
+ lazy.logConsole.debug(
+ "Replaced currentColor.",
+ property,
+ currentColor,
+ cssColor
+ );
+ }
+ /* Can drop the document argument after bugzilla bug 1973684 (142). */
+ const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document);
+ if (!colorRGBA) {
+ lazy.logConsole.error(
+ `Failed to convert "${property}" color (${cssColor}) to RGBA`
+ );
+ return { r: 0, g: 0, b: 0, a: 0 };
+ }
+ return colorRGBA;
+ }
+
+ /**
+ * Compose two colors with alpha values on top of each other.
+ *
+ * @param {InspectorRGBATuple} topRGBA - The color to place on the top.
+ * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom.
+ *
+ * @returns {InspectorRGBATuple} - The composed color.
+ */
+ _composeRGBA(topRGBA, bottomRGBA) {
+ const topA = Math.max(0, Math.min(1, topRGBA.a));
+ const bottomA = Math.max(0, Math.min(1, bottomRGBA.a));
+ const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1.
+ if (a === 0) {
+ return { r: 0, g: 0, b: 0, a };
+ }
+ const ret = { a };
+ for (const field of ["r", "g", "b"]) {
+ ret[field] =
+ (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a;
+ }
+ return ret;
+ }
+
+ /**
+ * Calculate the urlbar's container opaque background color, removing any
+ * transparency.
+ *
+ * @param {Window} win - The window to calculate the color for.
+ * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar
+ * element.
+ *
+ * @returns {InspectorRGBATuple} - The calculated color, which will be opaque.
+ */
+ _calculateUrlbarContainerColor(win, style) {
+ let colorRGBA;
+ if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) {
+ lazy.logConsole.debug("Toolbar background used.");
+ colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor");
+ if (colorRGBA.a === 1) {
+ return colorRGBA;
+ }
+ } else {
+ // The urlbar only has the toolbox colour.
+ colorRGBA = { r: 0, g: 0, b: 0, a: 0 };
+ }
+ let toolboxHasBackgroundImage = false;
+ const isLwTheme = win.document.documentElement.hasAttribute("lwtheme");
+ if (isLwTheme) {
+ for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) {
+ const headerImage = style.getPropertyValue(prop);
+ if (headerImage && headerImage !== "none") {
+ // The theme sets a background image behind the urlbar. No easy way to
+ // derive a single colour from this.
+ toolboxHasBackgroundImage = true;
+ lazy.logConsole.debug(
+ "Toolbox has background image.",
+ prop,
+ headerImage
+ );
+ break;
+ }
+ }
+ }
+ if (!toolboxHasBackgroundImage) {
+ lazy.logConsole.debug("Toolbox background used.");
+ colorRGBA = this._composeRGBA(
+ colorRGBA,
+ this._convertToRGBA(win, style, "--toolbox-bgcolor")
+ );
+ if (colorRGBA.a === 1) {
+ return colorRGBA;
+ }
+ }
+
+ // Determine whether the urlbar is dark.
+ // At this point, the urlbar background has some transparency, likely on top
+ // of an image.
+ // We use the theme's text colour to figure out whether the urlbar
+ // background is overall meant to be light or dark. Unlike the urlbar, we
+ // expect this colour to be (almost) opaque.
+ const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color");
+ const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b);
+ if (textColor.relativeLuminance >= 0.5) {
+ // Light text, so assume it has a dark background.
+ // Combine with a generic opaque dark colour. Copied from "frame" for the
+ // built-in dark theme.
+ lazy.logConsole.debug("Generic dark background used.");
+ const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 };
+ return this._composeRGBA(colorRGBA, darkFrameRGBA);
+ }
+ // Combine with an opaque light colour. Copied from "frame" for the built-in
+ // light theme.
+ lazy.logConsole.debug("Generic light background used.");
+ const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 };
+ return this._composeRGBA(colorRGBA, lightFrameRGBA);
+ }
+
+ /**
+ * Update the Letterboxing colors and related classes, or clear them if
+ * Letterboxing is not enabled.
+ *
+ * @param {Window} win - The window to update the colors for.
+ * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled.
+ */
+ _updateLetterboxingColors(win, letterboxingEnabled) {
+ let urlbarBackgroundRGBA;
+ let urlbarTextRGBA;
+ let contentSeparatorRGBA;
+ let urlbarBackgroundDark = false;
+ let lowBackgroundOutlineContrast = false;
+
+ if (letterboxingEnabled) {
+ // Want the effective colour of various elements without any alpha values
+ // so they can be used consistently.
+ const navbarStyle = win.getComputedStyle(
+ win.document.getElementById("nav-bar")
+ );
+ const containerRGBA = this._calculateUrlbarContainerColor(
+ win,
+ navbarStyle
+ );
+ urlbarBackgroundRGBA = this._composeRGBA(
+ this._convertToRGBA(
+ win,
+ navbarStyle,
+ "--toolbar-field-background-color"
+ ),
+ containerRGBA
+ );
+ urlbarTextRGBA = this._composeRGBA(
+ this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"),
+ urlbarBackgroundRGBA
+ );
+ /* Separator between the urlbar container #nav-bar and the tabbox. */
+ const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox);
+ contentSeparatorRGBA = this._composeRGBA(
+ this._convertToRGBA(
+ win,
+ tabboxStyle,
+ "--chrome-content-separator-color"
+ ),
+ containerRGBA
+ );
+ const bgColor = new lazy.Color(
+ urlbarBackgroundRGBA.r,
+ urlbarBackgroundRGBA.g,
+ urlbarBackgroundRGBA.b
+ );
+ const outlineColor = new lazy.Color(
+ contentSeparatorRGBA.r,
+ contentSeparatorRGBA.g,
+ contentSeparatorRGBA.b
+ );
+ const contrastRatio = bgColor.contrastRatio(outlineColor);
+ lazy.logConsole.debug(
+ "Outline-background contrast ratio.",
+ contrastRatio
+ );
+ urlbarBackgroundDark = bgColor.relativeLuminance < 0.5;
+ /* Very low contrast ratio. For reference the default light theme has
+ * a contrast ratio of ~1.1. */
+ lowBackgroundOutlineContrast = contrastRatio < 1.05;
+ }
+ for (const { name, colorRGBA } of [
+ {
+ name: "--letterboxing-urlbar-text-color",
+ colorRGBA: urlbarTextRGBA,
+ },
+ {
+ name: "--letterboxing-urlbar-background-color",
+ colorRGBA: urlbarBackgroundRGBA,
+ },
+ {
+ name: "--letterboxing-content-separator-color",
+ colorRGBA: contentSeparatorRGBA,
+ },
+ ]) {
+ if (letterboxingEnabled) {
+ win.gBrowser.tabbox.style.setProperty(
+ name,
+ `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})`
+ );
+ } else {
+ win.gBrowser.tabbox.style.removeProperty(name);
+ }
+ }
+ win.gBrowser.tabbox.classList.toggle(
+ "letterboxing-urlbar-background-dark",
+ urlbarBackgroundDark
+ );
+ win.gBrowser.tabbox.classList.toggle(
+ "letterboxing-low-background-outline-contrast",
+ lowBackgroundOutlineContrast
+ );
}
_detachWindow(aWindow) {
@@ -886,7 +1161,7 @@ class _RFPHelper {
aWindow.removeEventListener("TabOpen", this);
// revert tabpanel's style to default
- tabBrowser.tabpanels?.classList.remove("letterboxing");
+ tabBrowser.tabbox.classList.remove("letterboxing");
// and restore default size on each browser element
for (let tab of tabBrowser.tabs) {
@@ -896,6 +1171,9 @@ class _RFPHelper {
aWindow.removeEventListener("dblclick", this._onWindowDoubleClick);
delete aWindow.shrinkToLetterbox;
aWindow.removeEventListener("sizemodechange", windowResizeHandler);
+
+ aWindow.removeEventListener("nativethemechange", this);
+ this._updateLetterboxingColors(aWindow, false);
}
_handleDOMWindowOpened(win) {
=====================================
toolkit/components/resistfingerprinting/content/letterboxing.css
=====================================
@@ -7,17 +7,106 @@
* RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL,
* respectively), where --letterboxing-width & --letterboxing-height are
* actually set.
+ * Keep this block first and separate to the rules that do not necessarily
+ * require --letterboxing-width or --letterboxing-height.
*/
.letterboxing {
- --letterboxing-bgcolor: var(--tabpanel-background-color);
- --letterboxing-border-radius: 8px;
- --letterboxing-border-top-radius: 0;
+ .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
+ width: var(--letterboxing-width) !important;
+ height: var(--letterboxing-height) !important;
+ }
+}
+
+#tabbrowser-tabbox.letterboxing {
+ --letterboxing-bgcolor: var(--background-color-canvas);
+ /* Match the border radius used for the sidebar. */
+ --letterboxing-border-radius: var(--border-radius-medium);
+ --letterboxing-border-radius-top: 0;
--letterboxing-vertical-alignment: start;
- --letterboxing-shadow-color: rgba(12, 12, 13, 0.10);
- --letterboxing-gradient-color1: var(--letterboxing-bgcolor);
- --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor));
- --letterboxing-border-color: var(--letterboxing-bgcolor);
- --letterboxing-decorator-visibility: visible;
+ --letterboxing-shadow: none;
+ --letterboxing-outline-color: var(--border-color);
+ --letterboxing-outline-width: 1px;
+
+ @media not ((prefers-contrast) or (forced-colors)) {
+ /* Match the #sidebar outline width. */
+ --letterboxing-outline-width: 0.5px;
+ --letterboxing-shadow-color: rgba(58, 57, 68, 0.20);
+ --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color);
+
+ /* Match the effective urlbar background colour. */
+ --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
+ /* Match the effective colour of the separator between the urlbar container
+ * and the content. */
+ --letterboxing-outline-color: var(--letterboxing-content-separator-color);
+
+ &.letterboxing-urlbar-background-dark {
+ --letterboxing-shadow-color: #15141a;
+ }
+
+ &.letterboxing-low-background-outline-contrast {
+ /* The default content separator colour has insufficient contrast. */
+ --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black);
+
+ &.letterboxing-urlbar-background-dark {
+ /* Lighten the colour. */
+ --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white);
+ }
+ }
+ }
+
+ @media (prefers-contrast) and (not (forced-colors)) {
+ :root[lwtheme] & {
+ /* User with prefers-contrast coming from the system settings, but also an
+ * installed theme. */
+ --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
+ /* Presumably a user with prefers-contrast and a custom theme has chosen
+ * a theme where the contrast between the urlbar and the text is
+ * sufficiently high or low. */
+ --letterboxing-outline-color: var(--letterboxing-urlbar-text-color);
+ }
+ }
+
+ background: var(--letterboxing-bgcolor);
+
+ &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) {
+ /* Letterboxing outline is visible for the current tab. Replace the usual
+ * outline to match the Letterboxing outline. For most scenarios, this
+ * should be mostly the same colour as when Letterboxing is not visible. But
+ * it may make a difference for some theme combinations. */
+ outline-color: var(--letterboxing-outline-color);
+ outline-width: var(--letterboxing-outline-width);
+ }
+
+ #tabbrowser-tabpanels {
+ /* Override the --tabpanel-background-color.
+ * Also, make sure this remains transparent, otherwise it will overlap the
+ * parent's corner's border-radius due to it's "position: relative" rule. */
+ /* TODO: FIX this for newtab pages. tor-browser#44085 */
+ background: transparent;
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.revamp") {
+ :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is(
+ /* When the Letterboxing area is aligned to the top, show the rounded
+ * corner if there is enough vertical space between the sidebar and the
+ * browser element, which is not rounded at the top. */
+ :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner),
+ /* When the Letterboxing area is aligned to the centre, show the rounded
+ * corner if the Letterboxing border is shown. */
+ .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline)
+ ) {
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.position_start") {
+ border-start-start-radius: var(--letterboxing-border-radius);
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media not -moz-pref("sidebar.position_start") {
+ border-start-end-radius: var(--letterboxing-border-radius);
+ }
+ }
+ }
.browserContainer {
/*
@@ -26,101 +115,71 @@
* doesn't get notified on horizontal shrinking.
*/
overflow: hidden;
- background: var(--letterboxing-bgcolor);
}
- .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
- box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color);
- border-radius: var(--letterboxing-border-radius);
- border-top-left-radius: var(--letterboxing-border-top-radius);
- border-top-right-radius: var(--letterboxing-border-top-radius);
- width: var(--letterboxing-width) !important;
- height: var(--letterboxing-height) !important;
- background: var(--letterboxing-gradient-color2);
+ &.letterboxing-vcenter {
+ --letterboxing-border-radius-top: var(--letterboxing-border-radius);
+ --letterboxing-vertical-alignment: center;
}
}
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing) {
- place-content: start center;
-}
-
-.browserDecorator {
- display: none;
- pointer-events: none;
- background: transparent;
- position: relative;
- z-index: 1;
-}
+.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
+ :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & {
+ place-content: var(--letterboxing-vertical-alignment) center;
+ }
-.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
- --letterboxing-border-top-radius: var(--letterboxing-border-radius);
- --letterboxing-vertical-alignment: center;
-}
+ :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline {
+ browser {
+ /* We use clip-path rather than border-radius because border-radius on its
+ * own leads to rendering artefacts in the corners (tested with GNOME).
+ * See tor-browser#44214 (comment 3262962). */
+ /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */
+ clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius));
+ }
-.letterboxing.letterboxing-gradient .browserContainer {
- background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor);
-}
+ .browserDecorator {
+ /* Need a separate browserDecorator element because the clip-path on the
+ * browser would exclude the outline and box-shadow. */
+ /* TODO: Move these rules to the browser element once bugzilla bug 1991874
+ * is resolved, and drop browserDecorator. */
+ display: block;
+ border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius);
+ /* NOTE: The top outline will not be visible when this is aligned to the
+ * top. */
+ outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
+ box-shadow: var(--letterboxing-shadow);
+ }
-:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing)
- > .browserDecorator {
- display: initial;
- visibility: var(--letterboxing-decorator-visibility);
- border-radius: var(--letterboxing-border-radius);
- border-top-left-radius: var(--letterboxing-border-top-radius);
- border-top-right-radius: var(--letterboxing-border-top-radius);
- box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px;
- border: .1px solid var(--letterboxing-border-color);
- outline: .1px solid var(--letterboxing-bgcolor);
- height: calc(var(--letterboxing-height) + 1px);
- top: -1px;
-}
+ #statuspanel:not([mirror]) #statuspanel-label {
+ border-end-start-radius: var(--letterboxing-border-radius);
+ }
-.letterboxing-vcenter .browserDecorator {
- height: auto !important;
- top: 0 !important;
-}
+ #statuspanel[mirror] #statuspanel-label {
+ border-end-end-radius: var(--letterboxing-border-radius);
+ }
+ }
-/*
- Align status bar with content.
- TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117)
-*/
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel:not([hidden]) {
- position: relative;
- place-self: end left;
- left: 0;
- right: 0;
- z-index: 2;
- --letterboxing-status-left-radius: var(--letterboxing-border-radius);
- --letterboxing-status-right-radius: 0;
-}
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel:not([mirror]):-moz-locale-dir(rtl),
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel[mirror]:-moz-locale-dir(ltr) {
- left: 0;
- right: 0;
- --letterboxing-status-right-radius: var(--letterboxing-border-radius);
- --letterboxing-status-left-radius: 0;
- justify-self: right;
-}
+ #statuspanel {
+ position: relative;
+ place-self: end start;
+ z-index: 2;
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
-#statuspanel-label {
- border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius);
- margin: 0;
- border: 1px solid var(--letterboxing-border-color);
- max-width: calc(var(--letterboxing-width) * .5);
-}
+ &[mirror] {
+ justify-self: end;
+ }
+ }
-browser:fullscreen {
- --letterboxing-border-top-radius: 0;
- --letterboxing-border-radius: 0;
+ #statuspanel-label {
+ margin: 0;
+ outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
+ max-width: calc(var(--letterboxing-width) * .5);
+ }
}
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing) {
- place-content: var(--letterboxing-vertical-alignment) center;
+.browserDecorator {
+ display: none;
+ pointer-events: none;
+ background: transparent;
+ position: relative;
+ z-index: 1;
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/42…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/42…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][base-browser-140.4.0esr-15.0-1] 12 commits: fixup! BB 41919: Letterboxing, add temporarily visible web content-size...
by henry (@henry) 15 Oct '25
by henry (@henry) 15 Oct '25
15 Oct '25
henry pushed to branch base-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
a8d9aeef by Henry Wilkes at 2025-10-15T18:01:07+01:00
fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing.
TB 44214: Fix letterboxing status indicator for RTL.
- - - - -
73862bbd by Henry Wilkes at 2025-10-15T18:01:08+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop unnecessary CSS rules.
- - - - -
09abf933 by Henry Wilkes at 2025-10-15T18:01:09+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS logical positions for the status panel, rather than
"left" and "right".
- - - - -
943bae35 by Henry Wilkes at 2025-10-15T18:01:09+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop upstream's rules for placing content.
- - - - -
9b4b135b by Henry Wilkes at 2025-10-15T18:01:10+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop letterboxing gradient.
- - - - -
4d9307d1 by Henry Wilkes at 2025-10-15T18:01:11+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Separate out the essential --letterboxing-width and
--letterboxing-height rules into their own .letterboxing block, to have
the property values set on.
- - - - -
e4d77eb2 by Henry Wilkes at 2025-10-15T18:01:12+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS nesting. Part 1.
- - - - -
261e8522 by Henry Wilkes at 2025-10-15T18:01:12+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS nesting. Part 2.
- - - - -
2282a161 by Henry Wilkes at 2025-10-15T18:01:13+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Move the letterboxing classes one element up from tabpanels to
tabbox.
This is because we need to restyle the tabbox.
- - - - -
f48a8536 by Henry Wilkes at 2025-10-15T18:01:14+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use a CSS class to show/hide the letterboxing border, rather
than setting the border-radius in javascript.
- - - - -
26d1b64a by Henry Wilkes at 2025-10-15T18:01:15+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Rename CSS variable from top-radius to radius-top. This is
closer to what upstream has done recently for tokens, where higher
specificity is appended.
- - - - -
940d5e2c by Henry Wilkes at 2025-10-15T18:01:15+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Update letterboxing styling for ESR 140.
- - - - -
4 changed files:
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/themes/shared/tabbrowser/content-area.css
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
- toolkit/components/resistfingerprinting/content/letterboxing.css
Changes:
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -879,7 +879,13 @@ var FullScreen = {
}
this._isChromeCollapsed = false;
- Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
+ // Need a subject to know which window this applies to.
+ // Base browser patch can be dropped after bugzilla bug 1992036.
+ Services.obs.notifyObservers(
+ gNavToolbox,
+ "fullscreen-nav-toolbox",
+ "shown"
+ );
},
hideNavToolbox(aAnimate = false) {
@@ -943,7 +949,13 @@ var FullScreen = {
gNavToolbox.style.marginTop =
-gNavToolbox.getBoundingClientRect().height + "px";
this._isChromeCollapsed = true;
- Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");
+ // Need a subject to know which window this applies to.
+ // Base browser patch can be dropped after bugzilla bug 1880918.
+ Services.obs.notifyObservers(
+ gNavToolbox,
+ "fullscreen-nav-toolbox",
+ "hidden"
+ );
MousePosTracker.removeListener(this);
},
=====================================
browser/themes/shared/tabbrowser/content-area.css
=====================================
@@ -242,13 +242,17 @@
}
}
-#statuspanel[type=letterboxingStatus] > #statuspanel-label,
-#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label {
+#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label {
background-image: url("chrome://browser/skin/window.svg");
background-size: 1em;
background-repeat: no-repeat;
- background-position-x: .5em;
background-position-y: center;
+ background-position-x: left .5em;
+
+ &:-moz-locale-dir(rtl) {
+ background-position-x: right .5em;
+ }
+
padding-inline-start: 2em;
-moz-context-properties: fill;
fill: var(--color-accent-primary);
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -18,8 +18,6 @@ const kPrefLetterboxingTesting =
"privacy.resistFingerprinting.letterboxing.testing";
const kPrefLetterboxingVcenter =
"privacy.resistFingerprinting.letterboxing.vcenter";
-const kPrefLetterboxingGradient =
- "privacy.resistFingerprinting.letterboxing.gradient";
const kPrefLetterboxingDidForceSize =
"privacy.resistFingerprinting.letterboxing.didForceSize";
const kPrefLetterboxingRememberSize =
@@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize =
const kTopicDOMWindowOpened = "domwindowopened";
const kTopicDOMWindowClosed = "domwindowclosed";
+const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox";
+
const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings";
+const kPrefVerticalTabs = "sidebar.verticalTabs";
+
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ Color: "resource://gre/modules/Color.sys.mjs",
+});
ChromeUtils.defineLazyGetter(lazy, "logConsole", () =>
console.createInstance({
prefix: "RFPHelper",
@@ -155,7 +160,8 @@ class _RFPHelper {
Services.prefs.addObserver(kPrefResistFingerprinting, this);
Services.prefs.addObserver(kPrefLetterboxing, this);
Services.prefs.addObserver(kPrefLetterboxingVcenter, this);
- Services.prefs.addObserver(kPrefLetterboxingGradient, this);
+ Services.prefs.addObserver(kPrefVerticalTabs, this);
+ Services.obs.addObserver(this, kTopicFullscreenNavToolbox);
XPCOMUtils.defineLazyPreferenceGetter(
this,
@@ -188,9 +194,10 @@ class _RFPHelper {
// Remove unconditional observers
Services.prefs.removeObserver(kPrefResistFingerprinting, this);
- Services.prefs.removeObserver(kPrefLetterboxingGradient, this);
Services.prefs.removeObserver(kPrefLetterboxingVcenter, this);
Services.prefs.removeObserver(kPrefLetterboxing, this);
+ Services.prefs.removeObserver(kPrefVerticalTabs, this);
+ Services.obs.removeObserver(this, kTopicFullscreenNavToolbox);
// Remove the RFP observers, swallowing exceptions if they weren't present
this._removeLanguagePrefObservers();
}
@@ -212,6 +219,15 @@ class _RFPHelper {
case kTopicDOMWindowClosed:
this._handleDOMWindowClosed(subject);
break;
+ case kTopicFullscreenNavToolbox:
+ // The `subject` is the gNavToolbox.
+ // Record whether the toobox has been hidden when the browser (not
+ // content) is in fullscreen.
+ subject.ownerGlobal.gBrowser.tabbox.classList.toggle(
+ "letterboxing-nav-toolbox-hidden",
+ data === "hidden"
+ );
+ break;
default:
break;
}
@@ -226,6 +242,13 @@ class _RFPHelper {
resizeObserver.observe(browser.parentElement);
break;
}
+ case "nativethemechange":
+ // NOTE: "nativethemechange" seems to always be sent after
+ // "windowlwthemeupdate". So all the lwtheme CSS properties should be
+ // set to the new theme's values already, so we don't need to wait for
+ // windowlwthemeupdate.
+ this._updateLetterboxingColors(aMessage.currentTarget, true);
+ break;
default:
break;
}
@@ -245,9 +268,13 @@ class _RFPHelper {
Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize);
// fall-through
case kPrefLetterboxingVcenter:
- case kPrefLetterboxingGradient:
this._handleLetterboxingPrefChanged();
break;
+ case kPrefVerticalTabs:
+ if (this.letterboxingEnabled) {
+ forEachWindow(win => this._updateLetterboxingColors(win));
+ }
+ break;
default:
break;
}
@@ -452,7 +479,7 @@ class _RFPHelper {
// If not already cached on the document object, traverse the CSSOM and
// find the rule applying the default letterboxing styles to browsers
// preemptively in order to beat race conditions on tab/window creation
- return (document._letterboxingMarginsRule ||= (() => {
+ return (document._letterboxingDefaultRule ||= (() => {
const LETTERBOX_CSS_SELECTOR = ".letterboxing";
const LETTERBOX_CSS_URL =
"chrome://global/content/resistfingerprinting/letterboxing.css";
@@ -688,26 +715,22 @@ class _RFPHelper {
if (lastRoundedSize) {
// Check whether the letterboxing margin is less than the border radius,
- // and if so flatten the borders.
- let borderRadius = parseInt(
- win
- .getComputedStyle(browserContainer)
- .getPropertyValue("--letterboxing-border-radius")
+ // and if so do not show an outline.
+ const gapVertical = parentHeight - lastRoundedSize.height;
+ const gapHorizontal = parentWidth - lastRoundedSize.width;
+ browserParent.classList.toggle(
+ "letterboxing-show-outline",
+ gapVertical >= this._letterboxingBorderRadius ||
+ gapHorizontal >= this._letterboxingBorderRadius
+ );
+ // When the Letterboxing area is top-aligned, only show the sidebar corner
+ // if there is enough horizontal space.
+ // The factor of 4 is from the horizontal centre-alignment and wanting
+ // enough space for twice the corner radius.
+ browserParent.classList.toggle(
+ "letterboxing-show-sidebar-corner",
+ gapHorizontal >= 4 * this._letterboxingBorderRadius
);
- if (
- borderRadius &&
- parentWidth - lastRoundedSize.width < borderRadius &&
- parentHeight - lastRoundedSize.height < borderRadius
- ) {
- borderRadius = 0;
- } else {
- borderRadius = "";
- }
- styleChanges.queueIfNeeded(browserParent, {
- "--letterboxing-decorator-visibility":
- borderRadius === 0 ? "hidden" : "",
- "--letterboxing-border-radius": borderRadius,
- });
if (win.gBrowser.selectedBrowser == aBrowser) {
const updateStatus = async args => {
win.XULBrowserWindow.letterboxingStatus = args
@@ -769,20 +792,31 @@ class _RFPHelper {
_resetContentSize(aBrowser) {
aBrowser.parentElement.classList.add("exclude-letterboxing");
+ aBrowser.parentElement.classList.remove(
+ "letterboxing-show-outline",
+ "letterboxing-show-sidebar-corner"
+ );
}
_updateSizeForTabsInWindow(aWindow) {
let tabBrowser = aWindow.gBrowser;
- tabBrowser.tabpanels?.classList.add("letterboxing");
- tabBrowser.tabpanels?.classList.toggle(
+ tabBrowser.tabbox.classList.add("letterboxing");
+ tabBrowser.tabbox.classList.toggle(
"letterboxing-vcenter",
Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false)
);
- tabBrowser.tabpanels?.classList.toggle(
- "letterboxing-gradient",
- Services.prefs.getBoolPref(kPrefLetterboxingGradient, false)
- );
+ if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) {
+ // Cache the value since it is not expected to change in a session for any
+ // window.
+ this._letterboxingBorderRadius = Math.ceil(
+ parseFloat(
+ aWindow
+ .getComputedStyle(tabBrowser.tabbox)
+ .getPropertyValue("--letterboxing-border-radius")
+ )
+ );
+ }
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
@@ -791,7 +825,7 @@ class _RFPHelper {
// We need to add this class late because otherwise new windows get
// maximized.
aWindow.setTimeout(() => {
- tabBrowser.tabpanels?.classList.add("letterboxing-ready");
+ tabBrowser.tabbox.classList.add("letterboxing-ready");
if (!aWindow._rfpOriginalSize) {
this._recordWindowSize(aWindow);
}
@@ -869,6 +903,247 @@ class _RFPHelper {
this._resizeObservers.set(aWindow, resizeObserver);
// Rounding the content viewport.
this._updateSizeForTabsInWindow(aWindow);
+
+ this._updateLetterboxingColors(aWindow, true);
+ aWindow.addEventListener("nativethemechange", this);
+ }
+
+ /**
+ * Convert a CSS property to its RGBA value.
+ *
+ * @param {Window} win - The window for the element.
+ * @param {CSSStyleDeclaration} style - The computed style for the element we
+ * want to grab the color from.
+ * @param {string} property - The name of the property we want.
+ *
+ * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields
+ * are relative to the 0-255 color range. The "a" field is in the 0-1 range.
+ */
+ _convertToRGBA(win, style, property) {
+ let cssColor = style.getPropertyValue(property);
+ if (!cssColor) {
+ lazy.logConsole.error(`Missing color "${property}"`);
+ return { r: 0, g: 0, b: 0, a: 0 };
+ }
+ const currentColorRegex =
+ /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g;
+ if (currentColorRegex.test(cssColor)) {
+ const currentColor = style.color;
+ cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => {
+ return pre + currentColor + post;
+ });
+ lazy.logConsole.debug(
+ "Replaced currentColor.",
+ property,
+ currentColor,
+ cssColor
+ );
+ }
+ /* Can drop the document argument after bugzilla bug 1973684 (142). */
+ const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document);
+ if (!colorRGBA) {
+ lazy.logConsole.error(
+ `Failed to convert "${property}" color (${cssColor}) to RGBA`
+ );
+ return { r: 0, g: 0, b: 0, a: 0 };
+ }
+ return colorRGBA;
+ }
+
+ /**
+ * Compose two colors with alpha values on top of each other.
+ *
+ * @param {InspectorRGBATuple} topRGBA - The color to place on the top.
+ * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom.
+ *
+ * @returns {InspectorRGBATuple} - The composed color.
+ */
+ _composeRGBA(topRGBA, bottomRGBA) {
+ const topA = Math.max(0, Math.min(1, topRGBA.a));
+ const bottomA = Math.max(0, Math.min(1, bottomRGBA.a));
+ const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1.
+ if (a === 0) {
+ return { r: 0, g: 0, b: 0, a };
+ }
+ const ret = { a };
+ for (const field of ["r", "g", "b"]) {
+ ret[field] =
+ (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a;
+ }
+ return ret;
+ }
+
+ /**
+ * Calculate the urlbar's container opaque background color, removing any
+ * transparency.
+ *
+ * @param {Window} win - The window to calculate the color for.
+ * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar
+ * element.
+ *
+ * @returns {InspectorRGBATuple} - The calculated color, which will be opaque.
+ */
+ _calculateUrlbarContainerColor(win, style) {
+ let colorRGBA;
+ if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) {
+ lazy.logConsole.debug("Toolbar background used.");
+ colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor");
+ if (colorRGBA.a === 1) {
+ return colorRGBA;
+ }
+ } else {
+ // The urlbar only has the toolbox colour.
+ colorRGBA = { r: 0, g: 0, b: 0, a: 0 };
+ }
+ let toolboxHasBackgroundImage = false;
+ const isLwTheme = win.document.documentElement.hasAttribute("lwtheme");
+ if (isLwTheme) {
+ for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) {
+ const headerImage = style.getPropertyValue(prop);
+ if (headerImage && headerImage !== "none") {
+ // The theme sets a background image behind the urlbar. No easy way to
+ // derive a single colour from this.
+ toolboxHasBackgroundImage = true;
+ lazy.logConsole.debug(
+ "Toolbox has background image.",
+ prop,
+ headerImage
+ );
+ break;
+ }
+ }
+ }
+ if (!toolboxHasBackgroundImage) {
+ lazy.logConsole.debug("Toolbox background used.");
+ colorRGBA = this._composeRGBA(
+ colorRGBA,
+ this._convertToRGBA(win, style, "--toolbox-bgcolor")
+ );
+ if (colorRGBA.a === 1) {
+ return colorRGBA;
+ }
+ }
+
+ // Determine whether the urlbar is dark.
+ // At this point, the urlbar background has some transparency, likely on top
+ // of an image.
+ // We use the theme's text colour to figure out whether the urlbar
+ // background is overall meant to be light or dark. Unlike the urlbar, we
+ // expect this colour to be (almost) opaque.
+ const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color");
+ const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b);
+ if (textColor.relativeLuminance >= 0.5) {
+ // Light text, so assume it has a dark background.
+ // Combine with a generic opaque dark colour. Copied from "frame" for the
+ // built-in dark theme.
+ lazy.logConsole.debug("Generic dark background used.");
+ const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 };
+ return this._composeRGBA(colorRGBA, darkFrameRGBA);
+ }
+ // Combine with an opaque light colour. Copied from "frame" for the built-in
+ // light theme.
+ lazy.logConsole.debug("Generic light background used.");
+ const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 };
+ return this._composeRGBA(colorRGBA, lightFrameRGBA);
+ }
+
+ /**
+ * Update the Letterboxing colors and related classes, or clear them if
+ * Letterboxing is not enabled.
+ *
+ * @param {Window} win - The window to update the colors for.
+ * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled.
+ */
+ _updateLetterboxingColors(win, letterboxingEnabled) {
+ let urlbarBackgroundRGBA;
+ let urlbarTextRGBA;
+ let contentSeparatorRGBA;
+ let urlbarBackgroundDark = false;
+ let lowBackgroundOutlineContrast = false;
+
+ if (letterboxingEnabled) {
+ // Want the effective colour of various elements without any alpha values
+ // so they can be used consistently.
+ const navbarStyle = win.getComputedStyle(
+ win.document.getElementById("nav-bar")
+ );
+ const containerRGBA = this._calculateUrlbarContainerColor(
+ win,
+ navbarStyle
+ );
+ urlbarBackgroundRGBA = this._composeRGBA(
+ this._convertToRGBA(
+ win,
+ navbarStyle,
+ "--toolbar-field-background-color"
+ ),
+ containerRGBA
+ );
+ urlbarTextRGBA = this._composeRGBA(
+ this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"),
+ urlbarBackgroundRGBA
+ );
+ /* Separator between the urlbar container #nav-bar and the tabbox. */
+ const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox);
+ contentSeparatorRGBA = this._composeRGBA(
+ this._convertToRGBA(
+ win,
+ tabboxStyle,
+ "--chrome-content-separator-color"
+ ),
+ containerRGBA
+ );
+ const bgColor = new lazy.Color(
+ urlbarBackgroundRGBA.r,
+ urlbarBackgroundRGBA.g,
+ urlbarBackgroundRGBA.b
+ );
+ const outlineColor = new lazy.Color(
+ contentSeparatorRGBA.r,
+ contentSeparatorRGBA.g,
+ contentSeparatorRGBA.b
+ );
+ const contrastRatio = bgColor.contrastRatio(outlineColor);
+ lazy.logConsole.debug(
+ "Outline-background contrast ratio.",
+ contrastRatio
+ );
+ urlbarBackgroundDark = bgColor.relativeLuminance < 0.5;
+ /* Very low contrast ratio. For reference the default light theme has
+ * a contrast ratio of ~1.1. */
+ lowBackgroundOutlineContrast = contrastRatio < 1.05;
+ }
+ for (const { name, colorRGBA } of [
+ {
+ name: "--letterboxing-urlbar-text-color",
+ colorRGBA: urlbarTextRGBA,
+ },
+ {
+ name: "--letterboxing-urlbar-background-color",
+ colorRGBA: urlbarBackgroundRGBA,
+ },
+ {
+ name: "--letterboxing-content-separator-color",
+ colorRGBA: contentSeparatorRGBA,
+ },
+ ]) {
+ if (letterboxingEnabled) {
+ win.gBrowser.tabbox.style.setProperty(
+ name,
+ `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})`
+ );
+ } else {
+ win.gBrowser.tabbox.style.removeProperty(name);
+ }
+ }
+ win.gBrowser.tabbox.classList.toggle(
+ "letterboxing-urlbar-background-dark",
+ urlbarBackgroundDark
+ );
+ win.gBrowser.tabbox.classList.toggle(
+ "letterboxing-low-background-outline-contrast",
+ lowBackgroundOutlineContrast
+ );
}
_detachWindow(aWindow) {
@@ -886,7 +1161,7 @@ class _RFPHelper {
aWindow.removeEventListener("TabOpen", this);
// revert tabpanel's style to default
- tabBrowser.tabpanels?.classList.remove("letterboxing");
+ tabBrowser.tabbox.classList.remove("letterboxing");
// and restore default size on each browser element
for (let tab of tabBrowser.tabs) {
@@ -896,6 +1171,9 @@ class _RFPHelper {
aWindow.removeEventListener("dblclick", this._onWindowDoubleClick);
delete aWindow.shrinkToLetterbox;
aWindow.removeEventListener("sizemodechange", windowResizeHandler);
+
+ aWindow.removeEventListener("nativethemechange", this);
+ this._updateLetterboxingColors(aWindow, false);
}
_handleDOMWindowOpened(win) {
=====================================
toolkit/components/resistfingerprinting/content/letterboxing.css
=====================================
@@ -7,17 +7,106 @@
* RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL,
* respectively), where --letterboxing-width & --letterboxing-height are
* actually set.
+ * Keep this block first and separate to the rules that do not necessarily
+ * require --letterboxing-width or --letterboxing-height.
*/
.letterboxing {
- --letterboxing-bgcolor: var(--tabpanel-background-color);
- --letterboxing-border-radius: 8px;
- --letterboxing-border-top-radius: 0;
+ .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
+ width: var(--letterboxing-width) !important;
+ height: var(--letterboxing-height) !important;
+ }
+}
+
+#tabbrowser-tabbox.letterboxing {
+ --letterboxing-bgcolor: var(--background-color-canvas);
+ /* Match the border radius used for the sidebar. */
+ --letterboxing-border-radius: var(--border-radius-medium);
+ --letterboxing-border-radius-top: 0;
--letterboxing-vertical-alignment: start;
- --letterboxing-shadow-color: rgba(12, 12, 13, 0.10);
- --letterboxing-gradient-color1: var(--letterboxing-bgcolor);
- --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor));
- --letterboxing-border-color: var(--letterboxing-bgcolor);
- --letterboxing-decorator-visibility: visible;
+ --letterboxing-shadow: none;
+ --letterboxing-outline-color: var(--border-color);
+ --letterboxing-outline-width: 1px;
+
+ @media not ((prefers-contrast) or (forced-colors)) {
+ /* Match the #sidebar outline width. */
+ --letterboxing-outline-width: 0.5px;
+ --letterboxing-shadow-color: rgba(58, 57, 68, 0.20);
+ --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color);
+
+ /* Match the effective urlbar background colour. */
+ --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
+ /* Match the effective colour of the separator between the urlbar container
+ * and the content. */
+ --letterboxing-outline-color: var(--letterboxing-content-separator-color);
+
+ &.letterboxing-urlbar-background-dark {
+ --letterboxing-shadow-color: #15141a;
+ }
+
+ &.letterboxing-low-background-outline-contrast {
+ /* The default content separator colour has insufficient contrast. */
+ --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black);
+
+ &.letterboxing-urlbar-background-dark {
+ /* Lighten the colour. */
+ --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white);
+ }
+ }
+ }
+
+ @media (prefers-contrast) and (not (forced-colors)) {
+ :root[lwtheme] & {
+ /* User with prefers-contrast coming from the system settings, but also an
+ * installed theme. */
+ --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
+ /* Presumably a user with prefers-contrast and a custom theme has chosen
+ * a theme where the contrast between the urlbar and the text is
+ * sufficiently high or low. */
+ --letterboxing-outline-color: var(--letterboxing-urlbar-text-color);
+ }
+ }
+
+ background: var(--letterboxing-bgcolor);
+
+ &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) {
+ /* Letterboxing outline is visible for the current tab. Replace the usual
+ * outline to match the Letterboxing outline. For most scenarios, this
+ * should be mostly the same colour as when Letterboxing is not visible. But
+ * it may make a difference for some theme combinations. */
+ outline-color: var(--letterboxing-outline-color);
+ outline-width: var(--letterboxing-outline-width);
+ }
+
+ #tabbrowser-tabpanels {
+ /* Override the --tabpanel-background-color.
+ * Also, make sure this remains transparent, otherwise it will overlap the
+ * parent's corner's border-radius due to it's "position: relative" rule. */
+ /* TODO: FIX this for newtab pages. tor-browser#44085 */
+ background: transparent;
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.revamp") {
+ :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is(
+ /* When the Letterboxing area is aligned to the top, show the rounded
+ * corner if there is enough vertical space between the sidebar and the
+ * browser element, which is not rounded at the top. */
+ :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner),
+ /* When the Letterboxing area is aligned to the centre, show the rounded
+ * corner if the Letterboxing border is shown. */
+ .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline)
+ ) {
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.position_start") {
+ border-start-start-radius: var(--letterboxing-border-radius);
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media not -moz-pref("sidebar.position_start") {
+ border-start-end-radius: var(--letterboxing-border-radius);
+ }
+ }
+ }
.browserContainer {
/*
@@ -26,101 +115,71 @@
* doesn't get notified on horizontal shrinking.
*/
overflow: hidden;
- background: var(--letterboxing-bgcolor);
}
- .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
- box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color);
- border-radius: var(--letterboxing-border-radius);
- border-top-left-radius: var(--letterboxing-border-top-radius);
- border-top-right-radius: var(--letterboxing-border-top-radius);
- width: var(--letterboxing-width) !important;
- height: var(--letterboxing-height) !important;
- background: var(--letterboxing-gradient-color2);
+ &.letterboxing-vcenter {
+ --letterboxing-border-radius-top: var(--letterboxing-border-radius);
+ --letterboxing-vertical-alignment: center;
}
}
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing) {
- place-content: start center;
-}
-
-.browserDecorator {
- display: none;
- pointer-events: none;
- background: transparent;
- position: relative;
- z-index: 1;
-}
+.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
+ :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & {
+ place-content: var(--letterboxing-vertical-alignment) center;
+ }
-.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
- --letterboxing-border-top-radius: var(--letterboxing-border-radius);
- --letterboxing-vertical-alignment: center;
-}
+ :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline {
+ browser {
+ /* We use clip-path rather than border-radius because border-radius on its
+ * own leads to rendering artefacts in the corners (tested with GNOME).
+ * See tor-browser#44214 (comment 3262962). */
+ /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */
+ clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius));
+ }
-.letterboxing.letterboxing-gradient .browserContainer {
- background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor);
-}
+ .browserDecorator {
+ /* Need a separate browserDecorator element because the clip-path on the
+ * browser would exclude the outline and box-shadow. */
+ /* TODO: Move these rules to the browser element once bugzilla bug 1991874
+ * is resolved, and drop browserDecorator. */
+ display: block;
+ border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius);
+ /* NOTE: The top outline will not be visible when this is aligned to the
+ * top. */
+ outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
+ box-shadow: var(--letterboxing-shadow);
+ }
-:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing)
- > .browserDecorator {
- display: initial;
- visibility: var(--letterboxing-decorator-visibility);
- border-radius: var(--letterboxing-border-radius);
- border-top-left-radius: var(--letterboxing-border-top-radius);
- border-top-right-radius: var(--letterboxing-border-top-radius);
- box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px;
- border: .1px solid var(--letterboxing-border-color);
- outline: .1px solid var(--letterboxing-bgcolor);
- height: calc(var(--letterboxing-height) + 1px);
- top: -1px;
-}
+ #statuspanel:not([mirror]) #statuspanel-label {
+ border-end-start-radius: var(--letterboxing-border-radius);
+ }
-.letterboxing-vcenter .browserDecorator {
- height: auto !important;
- top: 0 !important;
-}
+ #statuspanel[mirror] #statuspanel-label {
+ border-end-end-radius: var(--letterboxing-border-radius);
+ }
+ }
-/*
- Align status bar with content.
- TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117)
-*/
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel:not([hidden]) {
- position: relative;
- place-self: end left;
- left: 0;
- right: 0;
- z-index: 2;
- --letterboxing-status-left-radius: var(--letterboxing-border-radius);
- --letterboxing-status-right-radius: 0;
-}
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel:not([mirror]):-moz-locale-dir(rtl),
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel[mirror]:-moz-locale-dir(ltr) {
- left: 0;
- right: 0;
- --letterboxing-status-right-radius: var(--letterboxing-border-radius);
- --letterboxing-status-left-radius: 0;
- justify-self: right;
-}
+ #statuspanel {
+ position: relative;
+ place-self: end start;
+ z-index: 2;
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
-#statuspanel-label {
- border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius);
- margin: 0;
- border: 1px solid var(--letterboxing-border-color);
- max-width: calc(var(--letterboxing-width) * .5);
-}
+ &[mirror] {
+ justify-self: end;
+ }
+ }
-browser:fullscreen {
- --letterboxing-border-top-radius: 0;
- --letterboxing-border-radius: 0;
+ #statuspanel-label {
+ margin: 0;
+ outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
+ max-width: calc(var(--letterboxing-width) * .5);
+ }
}
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing) {
- place-content: var(--letterboxing-vertical-alignment) center;
+.browserDecorator {
+ display: none;
+ pointer-events: none;
+ background: transparent;
+ position: relative;
+ z-index: 1;
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/29c65a…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/29c65a…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][tor-browser-140.4.0esr-15.0-1] 13 commits: dropme! fixup! TB 41917: Change letterboxing styling for Tor Browser.
by henry (@henry) 15 Oct '25
by henry (@henry) 15 Oct '25
15 Oct '25
henry pushed to branch tor-browser-140.4.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
0dddcb33 by Henry Wilkes at 2025-10-15T15:59:48+01:00
dropme! fixup! TB 41917: Change letterboxing styling for Tor Browser.
TB 44214: Drop letterboxing changes for Tor Browser only.
The commit "TB 41917: Change letterboxing styling for Tor Browser."
should be entirely dropped.
- - - - -
fdc52857 by Henry Wilkes at 2025-10-15T15:59:49+01:00
fixup! BB 41919: Letterboxing, add temporarily visible web content-size indicator on window resizing.
TB 44214: Fix letterboxing status indicator for RTL.
- - - - -
f00744fe by Henry Wilkes at 2025-10-15T15:59:50+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop unnecessary CSS rules.
- - - - -
805650c4 by Henry Wilkes at 2025-10-15T15:59:51+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS logical positions for the status panel, rather than
"left" and "right".
- - - - -
bfe8d56b by Henry Wilkes at 2025-10-15T15:59:52+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop upstream's rules for placing content.
- - - - -
b8a35ecc by Henry Wilkes at 2025-10-15T15:59:53+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Drop letterboxing gradient.
- - - - -
999bb9f2 by Henry Wilkes at 2025-10-15T15:59:53+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Separate out the essential --letterboxing-width and
--letterboxing-height rules into their own .letterboxing block, to have
the property values set on.
- - - - -
31b90c2e by Henry Wilkes at 2025-10-15T15:59:54+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS nesting. Part 1.
- - - - -
58d4acdc by Henry Wilkes at 2025-10-15T15:59:55+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use CSS nesting. Part 2.
- - - - -
317b197e by Henry Wilkes at 2025-10-15T15:59:56+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Move the letterboxing classes one element up from tabpanels to
tabbox.
This is because we need to restyle the tabbox.
- - - - -
cb287829 by Henry Wilkes at 2025-10-15T15:59:57+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Use a CSS class to show/hide the letterboxing border, rather
than setting the border-radius in javascript.
- - - - -
ff6fdbef by Henry Wilkes at 2025-10-15T15:59:58+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Rename CSS variable from top-radius to radius-top. This is
closer to what upstream has done recently for tokens, where higher
specificity is appended.
- - - - -
196110ba by Henry Wilkes at 2025-10-15T17:19:07+01:00
fixup! BB 32308: Use direct browser sizing for letterboxing.
TB 44214: Update letterboxing styling for ESR 140.
- - - - -
4 changed files:
- browser/base/content/browser-fullScreenAndPointerLock.js
- browser/themes/shared/tabbrowser/content-area.css
- toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
- toolkit/components/resistfingerprinting/content/letterboxing.css
Changes:
=====================================
browser/base/content/browser-fullScreenAndPointerLock.js
=====================================
@@ -879,7 +879,13 @@ var FullScreen = {
}
this._isChromeCollapsed = false;
- Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "shown");
+ // Need a subject to know which window this applies to.
+ // Base browser patch can be dropped after bugzilla bug 1992036.
+ Services.obs.notifyObservers(
+ gNavToolbox,
+ "fullscreen-nav-toolbox",
+ "shown"
+ );
},
hideNavToolbox(aAnimate = false) {
@@ -943,7 +949,13 @@ var FullScreen = {
gNavToolbox.style.marginTop =
-gNavToolbox.getBoundingClientRect().height + "px";
this._isChromeCollapsed = true;
- Services.obs.notifyObservers(null, "fullscreen-nav-toolbox", "hidden");
+ // Need a subject to know which window this applies to.
+ // Base browser patch can be dropped after bugzilla bug 1880918.
+ Services.obs.notifyObservers(
+ gNavToolbox,
+ "fullscreen-nav-toolbox",
+ "hidden"
+ );
MousePosTracker.removeListener(this);
},
=====================================
browser/themes/shared/tabbrowser/content-area.css
=====================================
@@ -242,13 +242,17 @@
}
}
-#statuspanel[type=letterboxingStatus] > #statuspanel-label,
-#statuspanel[previoustype=letterboxingStatus][inactive] > #statuspanel-label {
+#statuspanel:is([type=letterboxingStatus], [previoustype=letterboxingStatus][inactive]) > #statuspanel-label {
background-image: url("chrome://browser/skin/window.svg");
background-size: 1em;
background-repeat: no-repeat;
- background-position-x: .5em;
background-position-y: center;
+ background-position-x: left .5em;
+
+ &:-moz-locale-dir(rtl) {
+ background-position-x: right .5em;
+ }
+
padding-inline-start: 2em;
-moz-context-properties: fill;
fill: var(--color-accent-primary);
=====================================
toolkit/components/resistfingerprinting/RFPHelper.sys.mjs
=====================================
@@ -18,8 +18,6 @@ const kPrefLetterboxingTesting =
"privacy.resistFingerprinting.letterboxing.testing";
const kPrefLetterboxingVcenter =
"privacy.resistFingerprinting.letterboxing.vcenter";
-const kPrefLetterboxingGradient =
- "privacy.resistFingerprinting.letterboxing.gradient";
const kPrefLetterboxingDidForceSize =
"privacy.resistFingerprinting.letterboxing.didForceSize";
const kPrefLetterboxingRememberSize =
@@ -28,10 +26,17 @@ const kPrefLetterboxingRememberSize =
const kTopicDOMWindowOpened = "domwindowopened";
const kTopicDOMWindowClosed = "domwindowclosed";
+const kTopicFullscreenNavToolbox = "fullscreen-nav-toolbox";
+
const kPrefResizeWarnings = "privacy.resistFingerprinting.resizeWarnings";
+const kPrefVerticalTabs = "sidebar.verticalTabs";
+
const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ Color: "resource://gre/modules/Color.sys.mjs",
+});
ChromeUtils.defineLazyGetter(lazy, "logConsole", () =>
console.createInstance({
prefix: "RFPHelper",
@@ -155,7 +160,8 @@ class _RFPHelper {
Services.prefs.addObserver(kPrefResistFingerprinting, this);
Services.prefs.addObserver(kPrefLetterboxing, this);
Services.prefs.addObserver(kPrefLetterboxingVcenter, this);
- Services.prefs.addObserver(kPrefLetterboxingGradient, this);
+ Services.prefs.addObserver(kPrefVerticalTabs, this);
+ Services.obs.addObserver(this, kTopicFullscreenNavToolbox);
XPCOMUtils.defineLazyPreferenceGetter(
this,
@@ -188,9 +194,10 @@ class _RFPHelper {
// Remove unconditional observers
Services.prefs.removeObserver(kPrefResistFingerprinting, this);
- Services.prefs.removeObserver(kPrefLetterboxingGradient, this);
Services.prefs.removeObserver(kPrefLetterboxingVcenter, this);
Services.prefs.removeObserver(kPrefLetterboxing, this);
+ Services.prefs.removeObserver(kPrefVerticalTabs, this);
+ Services.obs.removeObserver(this, kTopicFullscreenNavToolbox);
// Remove the RFP observers, swallowing exceptions if they weren't present
this._removeLanguagePrefObservers();
}
@@ -212,6 +219,15 @@ class _RFPHelper {
case kTopicDOMWindowClosed:
this._handleDOMWindowClosed(subject);
break;
+ case kTopicFullscreenNavToolbox:
+ // The `subject` is the gNavToolbox.
+ // Record whether the toobox has been hidden when the browser (not
+ // content) is in fullscreen.
+ subject.ownerGlobal.gBrowser.tabbox.classList.toggle(
+ "letterboxing-nav-toolbox-hidden",
+ data === "hidden"
+ );
+ break;
default:
break;
}
@@ -226,6 +242,13 @@ class _RFPHelper {
resizeObserver.observe(browser.parentElement);
break;
}
+ case "nativethemechange":
+ // NOTE: "nativethemechange" seems to always be sent after
+ // "windowlwthemeupdate". So all the lwtheme CSS properties should be
+ // set to the new theme's values already, so we don't need to wait for
+ // windowlwthemeupdate.
+ this._updateLetterboxingColors(aMessage.currentTarget, true);
+ break;
default:
break;
}
@@ -245,9 +268,13 @@ class _RFPHelper {
Services.prefs.clearUserPref(kPrefLetterboxingDidForceSize);
// fall-through
case kPrefLetterboxingVcenter:
- case kPrefLetterboxingGradient:
this._handleLetterboxingPrefChanged();
break;
+ case kPrefVerticalTabs:
+ if (this.letterboxingEnabled) {
+ forEachWindow(win => this._updateLetterboxingColors(win));
+ }
+ break;
default:
break;
}
@@ -452,7 +479,7 @@ class _RFPHelper {
// If not already cached on the document object, traverse the CSSOM and
// find the rule applying the default letterboxing styles to browsers
// preemptively in order to beat race conditions on tab/window creation
- return (document._letterboxingMarginsRule ||= (() => {
+ return (document._letterboxingDefaultRule ||= (() => {
const LETTERBOX_CSS_SELECTOR = ".letterboxing";
const LETTERBOX_CSS_URL =
"chrome://global/content/resistfingerprinting/letterboxing.css";
@@ -688,26 +715,22 @@ class _RFPHelper {
if (lastRoundedSize) {
// Check whether the letterboxing margin is less than the border radius,
- // and if so flatten the borders.
- let borderRadius = parseInt(
- win
- .getComputedStyle(browserContainer)
- .getPropertyValue("--letterboxing-border-radius")
+ // and if so do not show an outline.
+ const gapVertical = parentHeight - lastRoundedSize.height;
+ const gapHorizontal = parentWidth - lastRoundedSize.width;
+ browserParent.classList.toggle(
+ "letterboxing-show-outline",
+ gapVertical >= this._letterboxingBorderRadius ||
+ gapHorizontal >= this._letterboxingBorderRadius
+ );
+ // When the Letterboxing area is top-aligned, only show the sidebar corner
+ // if there is enough horizontal space.
+ // The factor of 4 is from the horizontal centre-alignment and wanting
+ // enough space for twice the corner radius.
+ browserParent.classList.toggle(
+ "letterboxing-show-sidebar-corner",
+ gapHorizontal >= 4 * this._letterboxingBorderRadius
);
- if (
- borderRadius &&
- parentWidth - lastRoundedSize.width < borderRadius &&
- parentHeight - lastRoundedSize.height < borderRadius
- ) {
- borderRadius = 0;
- } else {
- borderRadius = "";
- }
- styleChanges.queueIfNeeded(browserParent, {
- "--letterboxing-decorator-visibility":
- borderRadius === 0 ? "hidden" : "",
- "--letterboxing-border-radius": borderRadius,
- });
if (win.gBrowser.selectedBrowser == aBrowser) {
const updateStatus = async args => {
win.XULBrowserWindow.letterboxingStatus = args
@@ -769,20 +792,31 @@ class _RFPHelper {
_resetContentSize(aBrowser) {
aBrowser.parentElement.classList.add("exclude-letterboxing");
+ aBrowser.parentElement.classList.remove(
+ "letterboxing-show-outline",
+ "letterboxing-show-sidebar-corner"
+ );
}
_updateSizeForTabsInWindow(aWindow) {
let tabBrowser = aWindow.gBrowser;
- tabBrowser.tabpanels?.classList.add("letterboxing");
- tabBrowser.tabpanels?.classList.toggle(
+ tabBrowser.tabbox.classList.add("letterboxing");
+ tabBrowser.tabbox.classList.toggle(
"letterboxing-vcenter",
Services.prefs.getBoolPref(kPrefLetterboxingVcenter, false)
);
- tabBrowser.tabpanels?.classList.toggle(
- "letterboxing-gradient",
- Services.prefs.getBoolPref(kPrefLetterboxingGradient, false)
- );
+ if (this._letterboxingBorderRadius === undefined && tabBrowser.tabbox) {
+ // Cache the value since it is not expected to change in a session for any
+ // window.
+ this._letterboxingBorderRadius = Math.ceil(
+ parseFloat(
+ aWindow
+ .getComputedStyle(tabBrowser.tabbox)
+ .getPropertyValue("--letterboxing-border-radius")
+ )
+ );
+ }
for (let tab of tabBrowser.tabs) {
let browser = tab.linkedBrowser;
@@ -791,7 +825,7 @@ class _RFPHelper {
// We need to add this class late because otherwise new windows get
// maximized.
aWindow.setTimeout(() => {
- tabBrowser.tabpanels?.classList.add("letterboxing-ready");
+ tabBrowser.tabbox.classList.add("letterboxing-ready");
if (!aWindow._rfpOriginalSize) {
this._recordWindowSize(aWindow);
}
@@ -869,6 +903,247 @@ class _RFPHelper {
this._resizeObservers.set(aWindow, resizeObserver);
// Rounding the content viewport.
this._updateSizeForTabsInWindow(aWindow);
+
+ this._updateLetterboxingColors(aWindow, true);
+ aWindow.addEventListener("nativethemechange", this);
+ }
+
+ /**
+ * Convert a CSS property to its RGBA value.
+ *
+ * @param {Window} win - The window for the element.
+ * @param {CSSStyleDeclaration} style - The computed style for the element we
+ * want to grab the color from.
+ * @param {string} property - The name of the property we want.
+ *
+ * @returns {InspectorRGBATuple} - The RGBA color. The "r", "g", "b" fields
+ * are relative to the 0-255 color range. The "a" field is in the 0-1 range.
+ */
+ _convertToRGBA(win, style, property) {
+ let cssColor = style.getPropertyValue(property);
+ if (!cssColor) {
+ lazy.logConsole.error(`Missing color "${property}"`);
+ return { r: 0, g: 0, b: 0, a: 0 };
+ }
+ const currentColorRegex =
+ /(^|[^a-zA-Z0-9_-])currentColor($|[^a-zA-Z0-9_-])/g;
+ if (currentColorRegex.test(cssColor)) {
+ const currentColor = style.color;
+ cssColor = cssColor.replace(currentColorRegex, (_, pre, post) => {
+ return pre + currentColor + post;
+ });
+ lazy.logConsole.debug(
+ "Replaced currentColor.",
+ property,
+ currentColor,
+ cssColor
+ );
+ }
+ /* Can drop the document argument after bugzilla bug 1973684 (142). */
+ const colorRGBA = win.InspectorUtils.colorToRGBA(cssColor, win.document);
+ if (!colorRGBA) {
+ lazy.logConsole.error(
+ `Failed to convert "${property}" color (${cssColor}) to RGBA`
+ );
+ return { r: 0, g: 0, b: 0, a: 0 };
+ }
+ return colorRGBA;
+ }
+
+ /**
+ * Compose two colors with alpha values on top of each other.
+ *
+ * @param {InspectorRGBATuple} topRGBA - The color to place on the top.
+ * @param {InspectorRGBATuple} bottomRGBA - The color to place on the bottom.
+ *
+ * @returns {InspectorRGBATuple} - The composed color.
+ */
+ _composeRGBA(topRGBA, bottomRGBA) {
+ const topA = Math.max(0, Math.min(1, topRGBA.a));
+ const bottomA = Math.max(0, Math.min(1, bottomRGBA.a));
+ const a = topA + bottomA - topA * bottomA; // Should be 1 if either is 1.
+ if (a === 0) {
+ return { r: 0, g: 0, b: 0, a };
+ }
+ const ret = { a };
+ for (const field of ["r", "g", "b"]) {
+ ret[field] =
+ (topRGBA[field] * topA + bottomRGBA[field] * bottomA * (1 - topA)) / a;
+ }
+ return ret;
+ }
+
+ /**
+ * Calculate the urlbar's container opaque background color, removing any
+ * transparency.
+ *
+ * @param {Window} win - The window to calculate the color for.
+ * @param {CSSStyleDeclaration} style - The computed style for the #nav-bar
+ * element.
+ *
+ * @returns {InspectorRGBATuple} - The calculated color, which will be opaque.
+ */
+ _calculateUrlbarContainerColor(win, style) {
+ let colorRGBA;
+ if (!Services.prefs.getBoolPref(kPrefVerticalTabs)) {
+ lazy.logConsole.debug("Toolbar background used.");
+ colorRGBA = this._convertToRGBA(win, style, "--toolbar-bgcolor");
+ if (colorRGBA.a === 1) {
+ return colorRGBA;
+ }
+ } else {
+ // The urlbar only has the toolbox colour.
+ colorRGBA = { r: 0, g: 0, b: 0, a: 0 };
+ }
+ let toolboxHasBackgroundImage = false;
+ const isLwTheme = win.document.documentElement.hasAttribute("lwtheme");
+ if (isLwTheme) {
+ for (const prop of ["--lwt-header-image", "--lwt-additional-images"]) {
+ const headerImage = style.getPropertyValue(prop);
+ if (headerImage && headerImage !== "none") {
+ // The theme sets a background image behind the urlbar. No easy way to
+ // derive a single colour from this.
+ toolboxHasBackgroundImage = true;
+ lazy.logConsole.debug(
+ "Toolbox has background image.",
+ prop,
+ headerImage
+ );
+ break;
+ }
+ }
+ }
+ if (!toolboxHasBackgroundImage) {
+ lazy.logConsole.debug("Toolbox background used.");
+ colorRGBA = this._composeRGBA(
+ colorRGBA,
+ this._convertToRGBA(win, style, "--toolbox-bgcolor")
+ );
+ if (colorRGBA.a === 1) {
+ return colorRGBA;
+ }
+ }
+
+ // Determine whether the urlbar is dark.
+ // At this point, the urlbar background has some transparency, likely on top
+ // of an image.
+ // We use the theme's text colour to figure out whether the urlbar
+ // background is overall meant to be light or dark. Unlike the urlbar, we
+ // expect this colour to be (almost) opaque.
+ const textRGBA = this._convertToRGBA(win, style, "--toolbar-field-color");
+ const textColor = new lazy.Color(textRGBA.r, textRGBA.g, textRGBA.b);
+ if (textColor.relativeLuminance >= 0.5) {
+ // Light text, so assume it has a dark background.
+ // Combine with a generic opaque dark colour. Copied from "frame" for the
+ // built-in dark theme.
+ lazy.logConsole.debug("Generic dark background used.");
+ const darkFrameRGBA = { r: 28, g: 27, b: 34, a: 1 };
+ return this._composeRGBA(colorRGBA, darkFrameRGBA);
+ }
+ // Combine with an opaque light colour. Copied from "frame" for the built-in
+ // light theme.
+ lazy.logConsole.debug("Generic light background used.");
+ const lightFrameRGBA = { r: 234, g: 234, b: 237, a: 1 };
+ return this._composeRGBA(colorRGBA, lightFrameRGBA);
+ }
+
+ /**
+ * Update the Letterboxing colors and related classes, or clear them if
+ * Letterboxing is not enabled.
+ *
+ * @param {Window} win - The window to update the colors for.
+ * @param {boolean} letterboxingEnabled - Whether Letterboxing is enabled.
+ */
+ _updateLetterboxingColors(win, letterboxingEnabled) {
+ let urlbarBackgroundRGBA;
+ let urlbarTextRGBA;
+ let contentSeparatorRGBA;
+ let urlbarBackgroundDark = false;
+ let lowBackgroundOutlineContrast = false;
+
+ if (letterboxingEnabled) {
+ // Want the effective colour of various elements without any alpha values
+ // so they can be used consistently.
+ const navbarStyle = win.getComputedStyle(
+ win.document.getElementById("nav-bar")
+ );
+ const containerRGBA = this._calculateUrlbarContainerColor(
+ win,
+ navbarStyle
+ );
+ urlbarBackgroundRGBA = this._composeRGBA(
+ this._convertToRGBA(
+ win,
+ navbarStyle,
+ "--toolbar-field-background-color"
+ ),
+ containerRGBA
+ );
+ urlbarTextRGBA = this._composeRGBA(
+ this._convertToRGBA(win, navbarStyle, "--toolbar-field-color"),
+ urlbarBackgroundRGBA
+ );
+ /* Separator between the urlbar container #nav-bar and the tabbox. */
+ const tabboxStyle = win.getComputedStyle(win.gBrowser.tabbox);
+ contentSeparatorRGBA = this._composeRGBA(
+ this._convertToRGBA(
+ win,
+ tabboxStyle,
+ "--chrome-content-separator-color"
+ ),
+ containerRGBA
+ );
+ const bgColor = new lazy.Color(
+ urlbarBackgroundRGBA.r,
+ urlbarBackgroundRGBA.g,
+ urlbarBackgroundRGBA.b
+ );
+ const outlineColor = new lazy.Color(
+ contentSeparatorRGBA.r,
+ contentSeparatorRGBA.g,
+ contentSeparatorRGBA.b
+ );
+ const contrastRatio = bgColor.contrastRatio(outlineColor);
+ lazy.logConsole.debug(
+ "Outline-background contrast ratio.",
+ contrastRatio
+ );
+ urlbarBackgroundDark = bgColor.relativeLuminance < 0.5;
+ /* Very low contrast ratio. For reference the default light theme has
+ * a contrast ratio of ~1.1. */
+ lowBackgroundOutlineContrast = contrastRatio < 1.05;
+ }
+ for (const { name, colorRGBA } of [
+ {
+ name: "--letterboxing-urlbar-text-color",
+ colorRGBA: urlbarTextRGBA,
+ },
+ {
+ name: "--letterboxing-urlbar-background-color",
+ colorRGBA: urlbarBackgroundRGBA,
+ },
+ {
+ name: "--letterboxing-content-separator-color",
+ colorRGBA: contentSeparatorRGBA,
+ },
+ ]) {
+ if (letterboxingEnabled) {
+ win.gBrowser.tabbox.style.setProperty(
+ name,
+ `rgb(${colorRGBA.r}, ${colorRGBA.g}, ${colorRGBA.b})`
+ );
+ } else {
+ win.gBrowser.tabbox.style.removeProperty(name);
+ }
+ }
+ win.gBrowser.tabbox.classList.toggle(
+ "letterboxing-urlbar-background-dark",
+ urlbarBackgroundDark
+ );
+ win.gBrowser.tabbox.classList.toggle(
+ "letterboxing-low-background-outline-contrast",
+ lowBackgroundOutlineContrast
+ );
}
_detachWindow(aWindow) {
@@ -886,7 +1161,7 @@ class _RFPHelper {
aWindow.removeEventListener("TabOpen", this);
// revert tabpanel's style to default
- tabBrowser.tabpanels?.classList.remove("letterboxing");
+ tabBrowser.tabbox.classList.remove("letterboxing");
// and restore default size on each browser element
for (let tab of tabBrowser.tabs) {
@@ -896,6 +1171,9 @@ class _RFPHelper {
aWindow.removeEventListener("dblclick", this._onWindowDoubleClick);
delete aWindow.shrinkToLetterbox;
aWindow.removeEventListener("sizemodechange", windowResizeHandler);
+
+ aWindow.removeEventListener("nativethemechange", this);
+ this._updateLetterboxingColors(aWindow, false);
}
_handleDOMWindowOpened(win) {
=====================================
toolkit/components/resistfingerprinting/content/letterboxing.css
=====================================
@@ -7,30 +7,106 @@
* RFPHelper.sys.mjs (LETTERBOX_CSS_SELECTOR and LETTERBOX_CSS_URL,
* respectively), where --letterboxing-width & --letterboxing-height are
* actually set.
+ * Keep this block first and separate to the rules that do not necessarily
+ * require --letterboxing-width or --letterboxing-height.
*/
.letterboxing {
- --letterboxing-bgcolor: var(--tabpanel-background-color);
- --letterboxing-border-radius: 8px;
- --letterboxing-border-top-radius: 0;
+ .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
+ width: var(--letterboxing-width) !important;
+ height: var(--letterboxing-height) !important;
+ }
+}
+
+#tabbrowser-tabbox.letterboxing {
+ --letterboxing-bgcolor: var(--background-color-canvas);
+ /* Match the border radius used for the sidebar. */
+ --letterboxing-border-radius: var(--border-radius-medium);
+ --letterboxing-border-radius-top: 0;
--letterboxing-vertical-alignment: start;
- --letterboxing-shadow-color: rgba(12, 12, 13, 0.10);
- --letterboxing-gradient-color1: var(--letterboxing-bgcolor);
- --letterboxing-gradient-color2: color-mix(in srgb, var(--chrome-content-separator-color) 50%, var(--letterboxing-bgcolor));
- --letterboxing-border-color: var(--letterboxing-bgcolor);
- --letterboxing-decorator-visibility: visible;
-
- /* Re-styling for Tor Browser. */
- /* stylelint-disable declaration-block-no-duplicate-custom-properties */
- --letterboxing-bgcolor: light-dark(#F0F0F4, #52525E);
- --letterboxing-gradient-color1: light-dark(
- rgba(0, 219, 222, 0.02),
- rgba(0, 219, 222, 0.06)
- );
- --letterboxing-gradient-color2: light-dark(
- rgba(252, 0, 255, 0.02),
- rgba(252, 0, 255, 0.06)
- );
- /* stylelint-enable declaration-block-no-duplicate-custom-properties */
+ --letterboxing-shadow: none;
+ --letterboxing-outline-color: var(--border-color);
+ --letterboxing-outline-width: 1px;
+
+ @media not ((prefers-contrast) or (forced-colors)) {
+ /* Match the #sidebar outline width. */
+ --letterboxing-outline-width: 0.5px;
+ --letterboxing-shadow-color: rgba(58, 57, 68, 0.20);
+ --letterboxing-shadow: 0 2px 14px 0 var(--letterboxing-shadow-color);
+
+ /* Match the effective urlbar background colour. */
+ --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
+ /* Match the effective colour of the separator between the urlbar container
+ * and the content. */
+ --letterboxing-outline-color: var(--letterboxing-content-separator-color);
+
+ &.letterboxing-urlbar-background-dark {
+ --letterboxing-shadow-color: #15141a;
+ }
+
+ &.letterboxing-low-background-outline-contrast {
+ /* The default content separator colour has insufficient contrast. */
+ --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, black);
+
+ &.letterboxing-urlbar-background-dark {
+ /* Lighten the colour. */
+ --letterboxing-outline-color: color-mix(in srgb, var(--letterboxing-content-separator-color) 90%, white);
+ }
+ }
+ }
+
+ @media (prefers-contrast) and (not (forced-colors)) {
+ :root[lwtheme] & {
+ /* User with prefers-contrast coming from the system settings, but also an
+ * installed theme. */
+ --letterboxing-bgcolor: var(--letterboxing-urlbar-background-color);
+ /* Presumably a user with prefers-contrast and a custom theme has chosen
+ * a theme where the contrast between the urlbar and the text is
+ * sufficiently high or low. */
+ --letterboxing-outline-color: var(--letterboxing-urlbar-text-color);
+ }
+ }
+
+ background: var(--letterboxing-bgcolor);
+
+ &:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline) {
+ /* Letterboxing outline is visible for the current tab. Replace the usual
+ * outline to match the Letterboxing outline. For most scenarios, this
+ * should be mostly the same colour as when Letterboxing is not visible. But
+ * it may make a difference for some theme combinations. */
+ outline-color: var(--letterboxing-outline-color);
+ outline-width: var(--letterboxing-outline-width);
+ }
+
+ #tabbrowser-tabpanels {
+ /* Override the --tabpanel-background-color.
+ * Also, make sure this remains transparent, otherwise it will overlap the
+ * parent's corner's border-radius due to it's "position: relative" rule. */
+ /* TODO: FIX this for newtab pages. tor-browser#44085 */
+ background: transparent;
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.revamp") {
+ :root:not([inDOMFullscreen]) &[sidebar-shown]:not(.letterboxing-nav-toolbox-hidden):is(
+ /* When the Letterboxing area is aligned to the top, show the rounded
+ * corner if there is enough vertical space between the sidebar and the
+ * browser element, which is not rounded at the top. */
+ :not(.letterboxing-vcenter):has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-sidebar-corner),
+ /* When the Letterboxing area is aligned to the centre, show the rounded
+ * corner if the Letterboxing border is shown. */
+ .letterboxing-vcenter:has(.deck-selected .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing).letterboxing-show-outline)
+ ) {
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media -moz-pref("sidebar.position_start") {
+ border-start-start-radius: var(--letterboxing-border-radius);
+ }
+
+ /* stylelint-disable-next-line media-query-no-invalid */
+ @media not -moz-pref("sidebar.position_start") {
+ border-start-end-radius: var(--letterboxing-border-radius);
+ }
+ }
+ }
.browserContainer {
/*
@@ -39,101 +115,71 @@
* doesn't get notified on horizontal shrinking.
*/
overflow: hidden;
- background: var(--letterboxing-bgcolor);
}
- .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) > browser {
- box-shadow: 0 4px 8px 0 var(--letterboxing-shadow-color);
- border-radius: var(--letterboxing-border-radius);
- border-top-left-radius: var(--letterboxing-border-top-radius);
- border-top-right-radius: var(--letterboxing-border-top-radius);
- width: var(--letterboxing-width) !important;
- height: var(--letterboxing-height) !important;
- background: var(--letterboxing-gradient-color2);
+ &.letterboxing-vcenter {
+ --letterboxing-border-radius-top: var(--letterboxing-border-radius);
+ --letterboxing-vertical-alignment: center;
}
}
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing) {
- place-content: start center;
-}
-
-.browserDecorator {
- display: none;
- pointer-events: none;
- background: transparent;
- position: relative;
- z-index: 1;
-}
+.browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
+ :root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready & {
+ place-content: var(--letterboxing-vertical-alignment) center;
+ }
-.letterboxing.letterboxing-vcenter .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing) {
- --letterboxing-border-top-radius: var(--letterboxing-border-radius);
- --letterboxing-vertical-alignment: center;
-}
+ :root:not([inDOMFullscreen]) .letterboxing &.letterboxing-show-outline {
+ browser {
+ /* We use clip-path rather than border-radius because border-radius on its
+ * own leads to rendering artefacts in the corners (tested with GNOME).
+ * See tor-browser#44214 (comment 3262962). */
+ /* TODO: Use border-radius once bugzilla bug 1991874 is resolved. */
+ clip-path: rect(auto auto auto auto round var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius));
+ }
-.letterboxing.letterboxing-gradient .browserContainer {
- background: linear-gradient(283deg, var(--letterboxing-gradient-color1) 0%, var(--letterboxing-gradient-color2) 100%), var(--letterboxing-bgcolor);
-}
+ .browserDecorator {
+ /* Need a separate browserDecorator element because the clip-path on the
+ * browser would exclude the outline and box-shadow. */
+ /* TODO: Move these rules to the browser element once bugzilla bug 1991874
+ * is resolved, and drop browserDecorator. */
+ display: block;
+ border-radius: var(--letterboxing-border-radius-top) var(--letterboxing-border-radius-top) var(--letterboxing-border-radius) var(--letterboxing-border-radius);
+ /* NOTE: The top outline will not be visible when this is aligned to the
+ * top. */
+ outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
+ box-shadow: var(--letterboxing-shadow);
+ }
-:root:not([inDOMFullscreen]) .letterboxing .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing)
- > .browserDecorator {
- display: initial;
- visibility: var(--letterboxing-decorator-visibility);
- border-radius: var(--letterboxing-border-radius);
- border-top-left-radius: var(--letterboxing-border-top-radius);
- border-top-right-radius: var(--letterboxing-border-top-radius);
- box-shadow: var(--letterboxing-border-color) 0 0 .1px inset, var(--letterboxing-border-color) 0 0 .1px;
- border: .1px solid var(--letterboxing-border-color);
- outline: .1px solid var(--letterboxing-bgcolor);
- height: calc(var(--letterboxing-height) + 1px);
- top: -1px;
-}
+ #statuspanel:not([mirror]) #statuspanel-label {
+ border-end-start-radius: var(--letterboxing-border-radius);
+ }
-.letterboxing-vcenter .browserDecorator {
- height: auto !important;
- top: 0 !important;
-}
+ #statuspanel[mirror] #statuspanel-label {
+ border-end-end-radius: var(--letterboxing-border-radius);
+ }
+ }
-/*
- Align status bar with content.
- TODO: switch to nested CSS selectors for conciseness when available (Firefox >= 117)
-*/
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel:not([hidden]) {
- position: relative;
- place-self: end left;
- left: 0;
- right: 0;
- z-index: 2;
- --letterboxing-status-left-radius: var(--letterboxing-border-radius);
- --letterboxing-status-right-radius: 0;
-}
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel:not([mirror]):-moz-locale-dir(rtl),
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
- > #statuspanel[mirror]:-moz-locale-dir(ltr) {
- left: 0;
- right: 0;
- --letterboxing-status-right-radius: var(--letterboxing-border-radius);
- --letterboxing-status-left-radius: 0;
- justify-self: right;
-}
+ #statuspanel {
+ position: relative;
+ place-self: end start;
+ z-index: 2;
-.letterboxing .browserContainer:not(.responsive-mode) > .browserStack:not(.exclude-letterboxing)
-#statuspanel-label {
- border-radius: 0 0 var(--letterboxing-status-right-radius) var(--letterboxing-status-left-radius);
- margin: 0;
- border: 1px solid var(--letterboxing-border-color);
- max-width: calc(var(--letterboxing-width) * .5);
-}
+ &[mirror] {
+ justify-self: end;
+ }
+ }
-browser:fullscreen {
- --letterboxing-border-top-radius: 0;
- --letterboxing-border-radius: 0;
+ #statuspanel-label {
+ margin: 0;
+ outline: var(--letterboxing-outline-width) solid var(--letterboxing-outline-color);
+ max-width: calc(var(--letterboxing-width) * .5);
+ }
}
-:root:not([inDOMFullscreen]) .letterboxing.letterboxing-ready .browserContainer:not(.responsive-mode)
- > .browserStack:not(.exclude-letterboxing) {
- place-content: var(--letterboxing-vertical-alignment) center;
+.browserDecorator {
+ display: none;
+ pointer-events: none;
+ background: transparent;
+ position: relative;
+ z-index: 1;
}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/be15e4…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/be15e4…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/mullvad-browser][mullvad-browser-128.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25
by ma1 (@ma1) 15 Oct '25
15 Oct '25
ma1 pushed to branch mullvad-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser
Commits:
accaaaf6 by hackademix at 2025-10-15T15:57:20+02:00
fixup! BB 40925: Implemented the Security Level component
BB 44242: Hand over Security Level's WebAssembly controls to NoScript
- - - - -
1 changed file:
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -79,6 +79,7 @@ const max_caps = [
"object",
"other",
"script",
+ "wasm",
"webgl",
"noscript",
];
@@ -259,7 +260,6 @@ var initializeNoScriptControl = () => {
// for each security setting. Note that 2-m and 3-m are identical,
// corresponding to the old 2-medium-high setting. We also separately
// bind NoScript settings to the browser.security_level.security_slider
-// (see noscript-control.js).
/* eslint-disable */
// prettier-ignore
const kSecuritySettings = {
@@ -272,7 +272,9 @@ const kSecuritySettings = {
"gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ],
"svg.disabled": [, true, false, false, false],
"javascript.options.asmjs": [, false, false, false, true ],
- "javascript.options.wasm": [, false, false, false, true ],
+ // tor-browser#44234, tor-browser#44242: this interferes with the correct
+ // functioning of the browser. So, WASM is also handled by NoScript now.
+ "javascript.options.wasm": [, true, true, true, true ],
};
/* eslint-enable */
@@ -339,16 +341,19 @@ var write_setting_to_prefs = function (settingIndex) {
// security settings matches. Otherwise return null.
var read_setting_from_prefs = function (prefNames) {
prefNames = prefNames || Object.keys(kSecuritySettings);
- for (let settingIndex of [1, 2, 3, 4]) {
+ for (const settingIndex of [1, 2, 3, 4]) {
let possibleSetting = true;
// For the given settingIndex, check if all current pref values
// match the setting.
- for (let prefName of prefNames) {
- if (
- kSecuritySettings[prefName][settingIndex] !==
- Services.prefs.getBoolPref(prefName)
- ) {
+ for (const prefName of prefNames) {
+ const wanted = kSecuritySettings[prefName][settingIndex];
+ const actual = Services.prefs.getBoolPref(prefName);
+ if (wanted !== actual) {
possibleSetting = false;
+ logger.info(
+ `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!`
+ );
+ break;
}
}
if (possibleSetting) {
@@ -373,7 +378,7 @@ var initializeSecurityPrefs = function () {
if (initializedSecPrefs) {
return;
}
- logger.info("Initializing security-prefs.js");
+ logger.info("Initializing security level");
initializedSecPrefs = true;
const wasCustom = Services.prefs.getBoolPref(kCustomPref, false);
@@ -381,6 +386,21 @@ var initializeSecurityPrefs = function () {
// and it should not be custom.
let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4);
desiredIndex = fixupIndex(desiredIndex);
+
+ if (!(wasCustom && desiredIndex == 4)) {
+ // The current level is non-customized Standard, or
+ // Safer / Safest (either customized or not): the global
+ // javascript.options.wasm pref interferes with the correct
+ // functioning of the browser, so instead we rely on NoScript
+ // to disable WebAssembly now (tor-browser#44234, tor-browser#44242).
+ // We skip flipping in customized Standard, because if its value was
+ // found false under such as circumstance, that would suggest
+ // an intentional user choice we don't want to interfere with.
+ // Unlike other javascript.options.* preferences, this one is safe
+ // to flip without a browser restart because it's checked whenever a
+ // context is created.
+ Services.prefs.setBoolPref("javascript.options.wasm", true);
+ }
// Make sure the user has a set preference user value.
Services.prefs.setIntPref(kSliderPref, desiredIndex);
Services.prefs.setBoolPref(kCustomPref, wasCustom);
@@ -453,7 +473,7 @@ var initializeSecurityPrefs = function () {
});
}
- logger.info("security-prefs.js initialization complete");
+ logger.info("Security level initialization complete");
};
// tor-browser#41460: we changed preference names in 12.0.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/acc…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/acc…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][base-browser-128.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25
by ma1 (@ma1) 15 Oct '25
15 Oct '25
ma1 pushed to branch base-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
14084797 by hackademix at 2025-10-15T15:57:13+02:00
fixup! BB 40925: Implemented the Security Level component
BB 44242: Hand over Security Level's WebAssembly controls to NoScript
- - - - -
1 changed file:
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -79,6 +79,7 @@ const max_caps = [
"object",
"other",
"script",
+ "wasm",
"webgl",
"noscript",
];
@@ -247,7 +248,6 @@ var initializeNoScriptControl = () => {
// for each security setting. Note that 2-m and 3-m are identical,
// corresponding to the old 2-medium-high setting. We also separately
// bind NoScript settings to the browser.security_level.security_slider
-// (see noscript-control.js).
/* eslint-disable */
// prettier-ignore
const kSecuritySettings = {
@@ -260,7 +260,9 @@ const kSecuritySettings = {
"gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ],
"svg.disabled": [, true, false, false, false],
"javascript.options.asmjs": [, false, false, false, true ],
- "javascript.options.wasm": [, false, false, false, true ],
+ // tor-browser#44234, tor-browser#44242: this interferes with the correct
+ // functioning of the browser. So, WASM is also handled by NoScript now.
+ "javascript.options.wasm": [, true, true, true, true ],
};
/* eslint-enable */
@@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) {
// security settings matches. Otherwise return null.
var read_setting_from_prefs = function (prefNames) {
prefNames = prefNames || Object.keys(kSecuritySettings);
- for (let settingIndex of [1, 2, 3, 4]) {
+ for (const settingIndex of [1, 2, 3, 4]) {
let possibleSetting = true;
// For the given settingIndex, check if all current pref values
// match the setting.
- for (let prefName of prefNames) {
- if (
- kSecuritySettings[prefName][settingIndex] !==
- Services.prefs.getBoolPref(prefName)
- ) {
+ for (const prefName of prefNames) {
+ const wanted = kSecuritySettings[prefName][settingIndex];
+ const actual = Services.prefs.getBoolPref(prefName);
+ if (wanted !== actual) {
possibleSetting = false;
+ logger.info(
+ `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!`
+ );
+ break;
}
}
if (possibleSetting) {
@@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () {
if (initializedSecPrefs) {
return;
}
- logger.info("Initializing security-prefs.js");
+ logger.info("Initializing security level");
initializedSecPrefs = true;
const wasCustom = Services.prefs.getBoolPref(kCustomPref, false);
@@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () {
// and it should not be custom.
let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4);
desiredIndex = fixupIndex(desiredIndex);
+
+ if (!(wasCustom && desiredIndex == 4)) {
+ // The current level is non-customized Standard, or
+ // Safer / Safest (either customized or not): the global
+ // javascript.options.wasm pref interferes with the correct
+ // functioning of the browser, so instead we rely on NoScript
+ // to disable WebAssembly now (tor-browser#44234, tor-browser#44242).
+ // We skip flipping in customized Standard, because if its value was
+ // found false under such as circumstance, that would suggest
+ // an intentional user choice we don't want to interfere with.
+ // Unlike other javascript.options.* preferences, this one is safe
+ // to flip without a browser restart because it's checked whenever a
+ // context is created.
+ Services.prefs.setBoolPref("javascript.options.wasm", true);
+ }
// Make sure the user has a set preference user value.
Services.prefs.setIntPref(kSliderPref, desiredIndex);
Services.prefs.setBoolPref(kCustomPref, wasCustom);
@@ -441,7 +461,7 @@ var initializeSecurityPrefs = function () {
});
}
- logger.info("security-prefs.js initialization complete");
+ logger.info("Security level initialization complete");
};
// tor-browser#41460: we changed preference names in 12.0.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1408479…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/1408479…
You're receiving this email because of your account on gitlab.torproject.org.
1
0

[Git][tpo/applications/tor-browser][tor-browser-128.14.0esr-14.5-1] fixup! BB 40925: Implemented the Security Level component
by ma1 (@ma1) 15 Oct '25
by ma1 (@ma1) 15 Oct '25
15 Oct '25
ma1 pushed to branch tor-browser-128.14.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
19fc83ce by hackademix at 2025-10-15T15:57:05+02:00
fixup! BB 40925: Implemented the Security Level component
BB 44242: Hand over Security Level's WebAssembly controls to NoScript
- - - - -
1 changed file:
- toolkit/components/securitylevel/SecurityLevel.sys.mjs
Changes:
=====================================
toolkit/components/securitylevel/SecurityLevel.sys.mjs
=====================================
@@ -79,6 +79,7 @@ const max_caps = [
"object",
"other",
"script",
+ "wasm",
"webgl",
"noscript",
];
@@ -247,7 +248,6 @@ var initializeNoScriptControl = () => {
// for each security setting. Note that 2-m and 3-m are identical,
// corresponding to the old 2-medium-high setting. We also separately
// bind NoScript settings to the browser.security_level.security_slider
-// (see noscript-control.js).
/* eslint-disable */
// prettier-ignore
const kSecuritySettings = {
@@ -260,7 +260,9 @@ const kSecuritySettings = {
"gfx.font_rendering.opentype_svg.enabled": [, false, false, false, true ],
"svg.disabled": [, true, false, false, false],
"javascript.options.asmjs": [, false, false, false, true ],
- "javascript.options.wasm": [, false, false, false, true ],
+ // tor-browser#44234, tor-browser#44242: this interferes with the correct
+ // functioning of the browser. So, WASM is also handled by NoScript now.
+ "javascript.options.wasm": [, true, true, true, true ],
};
/* eslint-enable */
@@ -327,16 +329,19 @@ var write_setting_to_prefs = function (settingIndex) {
// security settings matches. Otherwise return null.
var read_setting_from_prefs = function (prefNames) {
prefNames = prefNames || Object.keys(kSecuritySettings);
- for (let settingIndex of [1, 2, 3, 4]) {
+ for (const settingIndex of [1, 2, 3, 4]) {
let possibleSetting = true;
// For the given settingIndex, check if all current pref values
// match the setting.
- for (let prefName of prefNames) {
- if (
- kSecuritySettings[prefName][settingIndex] !==
- Services.prefs.getBoolPref(prefName)
- ) {
+ for (const prefName of prefNames) {
+ const wanted = kSecuritySettings[prefName][settingIndex];
+ const actual = Services.prefs.getBoolPref(prefName);
+ if (wanted !== actual) {
possibleSetting = false;
+ logger.info(
+ `${prefName} does not match level ${settingIndex}: ${actual}, should be ${wanted}!`
+ );
+ break;
}
}
if (possibleSetting) {
@@ -361,7 +366,7 @@ var initializeSecurityPrefs = function () {
if (initializedSecPrefs) {
return;
}
- logger.info("Initializing security-prefs.js");
+ logger.info("Initializing security level");
initializedSecPrefs = true;
const wasCustom = Services.prefs.getBoolPref(kCustomPref, false);
@@ -369,6 +374,21 @@ var initializeSecurityPrefs = function () {
// and it should not be custom.
let desiredIndex = Services.prefs.getIntPref(kSliderPref, 4);
desiredIndex = fixupIndex(desiredIndex);
+
+ if (!(wasCustom && desiredIndex == 4)) {
+ // The current level is non-customized Standard, or
+ // Safer / Safest (either customized or not): the global
+ // javascript.options.wasm pref interferes with the correct
+ // functioning of the browser, so instead we rely on NoScript
+ // to disable WebAssembly now (tor-browser#44234, tor-browser#44242).
+ // We skip flipping in customized Standard, because if its value was
+ // found false under such as circumstance, that would suggest
+ // an intentional user choice we don't want to interfere with.
+ // Unlike other javascript.options.* preferences, this one is safe
+ // to flip without a browser restart because it's checked whenever a
+ // context is created.
+ Services.prefs.setBoolPref("javascript.options.wasm", true);
+ }
// Make sure the user has a set preference user value.
Services.prefs.setIntPref(kSliderPref, desiredIndex);
Services.prefs.setBoolPref(kCustomPref, wasCustom);
@@ -441,7 +461,7 @@ var initializeSecurityPrefs = function () {
});
}
- logger.info("security-prefs.js initialization complete");
+ logger.info("Security level initialization complete");
};
// tor-browser#41460: we changed preference names in 12.0.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/19fc83c…
--
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/19fc83c…
You're receiving this email because of your account on gitlab.torproject.org.
1
0