morgan pushed to branch tor-browser-153.1.0esr-16.0-1 at The Tor Project / Applications / Tor Browser Commits: c6627585 by Henry Wilkes at 2026-08-24T12:19:25+00:00 fixup! BB 40925: Implemented the Security Level component BB 45201: Move security level settings to the settings config. - - - - - 9af10306 by Henry Wilkes at 2026-08-24T12:19:25+00:00 fixup! Base Browser strings BB 45201: Update security level setting strings. - - - - - af669639 by Henry Wilkes at 2026-08-24T12:19:25+00:00 BB 45201: Customize moz- input widgets for Base Browser. - - - - - 21 changed files: - browser/components/preferences/config/privacy.mjs - browser/components/preferences/preferences.js - browser/components/preferences/preferences.xhtml - browser/components/preferences/privacy.inc.xhtml - browser/components/preferences/privacy.js - − browser/components/securitylevel/SecurityLevelUIUtils.sys.mjs - + browser/components/securitylevel/config/security-level.mjs - browser/components/securitylevel/content/securityLevel.js - + browser/components/securitylevel/content/securityLevelDialog.css - browser/components/securitylevel/content/securityLevelDialog.js - browser/components/securitylevel/content/securityLevelDialog.xhtml - − browser/components/securitylevel/content/securityLevelPreferences.css - − browser/components/securitylevel/content/securityLevelPreferences.inc.xhtml - browser/components/securitylevel/jar.mn - browser/components/securitylevel/moz.build - + browser/components/securitylevel/widgets/security-level-description.css - + browser/components/securitylevel/widgets/security-level-description.mjs - + browser/components/securitylevel/widgets/security-level-display.css - + browser/components/securitylevel/widgets/security-level-display.mjs - toolkit/content/widgets/lit-utils.mjs - toolkit/locales/en-US/toolkit/global/base-browser.ftl Changes: ===================================== browser/components/preferences/config/privacy.mjs ===================================== @@ -7,6 +7,10 @@ import { SettingGroupManager } from "chrome://browser/content/preferences/config/SettingGroupManager.mjs"; import { Preferences } from "chrome://global/content/preferences/Preferences.mjs"; +ChromeUtils.importESModule( + "chrome://browser/content/securitylevel/config/security-level.mjs", + { global: "current" } +); ChromeUtils.importESModule( "chrome://browser/content/preferences/config/protections-from-apps.mjs", { global: "current" } ===================================== browser/components/preferences/preferences.js ===================================== @@ -409,6 +409,7 @@ const CONFIG_PANES = Object.freeze({ "ipprotection", "cookiesAndSiteData2", "history2", + "securityLevelGroup", "nonTechnicalPrivacy2", "dnsOverHttps", "connectionLink", ===================================== browser/components/preferences/preferences.xhtml ===================================== @@ -43,7 +43,6 @@ href="chrome://browser/content/preferences/letterboxing.css" /> - <link rel="stylesheet" href="chrome://browser/content/securitylevel/securityLevelPreferences.css" /> <link rel="stylesheet" href="chrome://browser/content/torpreferences/torPreferences.css" /> <link rel="stylesheet" href="chrome://browser/content/onionservices/authPreferences.css" /> @@ -108,6 +107,8 @@ <script type="module" src="chrome://browser/content/preferences/widgets/update-information.mjs"></script> <script type="module" src="chrome://browser/content/preferences/widgets/update-state.mjs"></script> <script type="module" src="chrome://browser/content/ipprotection/bandwidth-usage.mjs"></script> + <script type="module" src="chrome://browser/content/securitylevel/widgets/security-level-description.mjs"></script> + <script type="module" src="chrome://browser/content/securitylevel/widgets/security-level-display.mjs"></script> <script type="module" src="chrome://browser/content/torpreferences/widgets/tor-bridges-display.mjs"></script> <script type="module" src="chrome://browser/content/torpreferences/widgets/tor-connection-assist-banner.mjs"></script> <script type="module" src="chrome://browser/content/torpreferences/widgets/tor-connection-status.mjs"></script> ===================================== browser/components/preferences/privacy.inc.xhtml ===================================== @@ -579,7 +579,7 @@ <html:h1 data-l10n-id="security-header"/> </hbox> -#include ../securitylevel/content/securityLevelPreferences.inc.xhtml +<html:setting-group groupid="securityLevelGroup" data-category="panePrivacy" hidden="true" data-srd-migrated=""></html:setting-group> <!-- addons, forgery (phishing) UI Security --> <html:setting-group groupid="browsingProtection" data-category="panePrivacy" hidden="true" data-srd-migrated=""></html:setting-group> ===================================== browser/components/preferences/privacy.js ===================================== @@ -60,13 +60,6 @@ ChromeUtils.defineLazyGetter(lazy, "gParentalControlsService", () => : null ); -// TODO: module import via ChromeUtils.defineModuleGetter -XPCOMUtils.defineLazyScriptGetter( - this, - ["SecurityLevelPreferences"], - "chrome://browser/content/securitylevel/securityLevel.js" -); - XPCOMUtils.defineLazyPreferenceGetter( this, "gIsFirstPartyIsolated", @@ -150,16 +143,6 @@ function initTCPStandardSection() { var gPrivacyPane = { _pane: null, - /** - * Show the Security Level UI - */ - _initSecurityLevel() { - SecurityLevelPreferences.init(); - window.addEventListener("unload", () => SecurityLevelPreferences.uninit(), { - once: true, - }); - }, - /** * Whether the prompt to restart Firefox should appear when changing the autostart pref. */ @@ -654,6 +637,7 @@ var gPrivacyPane = { initSettingGroup("etpReset"); initSettingGroup("etpCustomize"); initSettingGroup("networkProxy"); + initSettingGroup("securityLevelGroup"); /* Initialize Content Blocking */ this.initContentBlocking(); @@ -663,7 +647,6 @@ var gPrivacyPane = { this.networkCookieBehaviorReadPrefs(); this._initTrackingProtectionExtensionControl(); this._ensureTrackingProtectionExceptionListMigration(); - this._initSecurityLevel(); Preferences.get("privacy.trackingprotection.enabled").on( "change", ===================================== browser/components/securitylevel/SecurityLevelUIUtils.sys.mjs deleted ===================================== @@ -1,73 +0,0 @@ -/** - * Common methods for the desktop security level components. - */ -export const SecurityLevelUIUtils = { - /** - * Create an element that gives a description of the security level. To be - * used in the settings. - * - * @param {string} level - The security level to describe. - * @param {Document} doc - The document where the element will be inserted. - * - * @returns {Element} - The newly created element. - */ - createDescriptionElement(level, doc) { - const el = doc.createElement("div"); - el.classList.add("security-level-description"); - - let l10nIdSummary; - let bullets; - switch (level) { - case "standard": - l10nIdSummary = "security-level-summary-standard"; - break; - case "safer": - l10nIdSummary = "security-level-summary-safer"; - bullets = [ - "security-level-preferences-bullet-https-only-javascript", - "security-level-preferences-bullet-limit-font-and-symbols", - "security-level-preferences-bullet-limit-media", - ]; - break; - case "safest": - l10nIdSummary = "security-level-summary-safest"; - bullets = [ - "security-level-preferences-bullet-disabled-javascript", - "security-level-preferences-bullet-limit-font-and-symbols-and-images", - "security-level-preferences-bullet-limit-media", - ]; - break; - case "custom": - l10nIdSummary = "security-level-summary-custom"; - break; - default: - throw Error(`Unhandled level: ${level}`); - } - - const summaryEl = doc.createElement("div"); - summaryEl.classList.add("security-level-summary"); - doc.l10n.setAttributes(summaryEl, l10nIdSummary); - - el.append(summaryEl); - - if (!bullets) { - return el; - } - - const listEl = doc.createElement("ul"); - listEl.classList.add("security-level-description-extra"); - // Add a mozilla styling class as well: - listEl.classList.add("privacy-extra-information"); - for (const l10nId of bullets) { - const bulletEl = doc.createElement("li"); - bulletEl.classList.add("security-level-description-bullet"); - - doc.l10n.setAttributes(bulletEl, l10nId); - - listEl.append(bulletEl); - } - - el.append(listEl); - return el; - }, -}; ===================================== browser/components/securitylevel/config/security-level.mjs ===================================== @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import { SettingGroupManager } from "chrome://browser/content/preferences/config/SettingGroupManager.mjs"; +import { Preferences } from "chrome://global/content/preferences/Preferences.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + SecurityLevelPrefs: + "moz-src:///toolkit/components/securitylevel/SecurityLevel.sys.mjs", +}); + +SettingGroupManager.registerGroups({ + securityLevelGroup: { + subcategory: "securitylevel", + l10nId: "security-level-settings-group", + headingLevel: 2, + supportPage: "tor-manual:features__security-levels", + items: [ + { + id: "securityLevelBoxGroup", + control: "moz-box-group", + items: [ + { + id: "securityLevelCurrent", + control: "security-level-display", + }, + { + id: "securityLevelChangeButton", + l10nId: "security-level-settings-change-button", + control: "moz-box-button", + }, + ], + }, + ], + }, +}); + +Preferences.addSetting({ + id: "securityLevelCurrent", + setup(emitChange) { + Services.prefs.addObserver( + "browser.security_level.security_slider", + emitChange + ); + Services.prefs.addObserver( + "browser.security_level.security_custom", + emitChange + ); + + return () => { + Services.prefs.removeObserver( + "browser.security_level.security_slider", + emitChange + ); + Services.prefs.removeObserver( + "browser.security_level.security_custom", + emitChange + ); + }; + }, + get: () => { + return lazy.SecurityLevelPrefs.securityLevelSummary; + }, +}); + +Preferences.addSetting({ + id: "securityLevelBoxGroup", +}); + +Preferences.addSetting({ + id: "securityLevelChangeButton", + onUserClick() { + window.gSubDialog.open( + "chrome://browser/content/securitylevel/securityLevelDialog.xhtml", + { features: "resizable=yes" } + ); + }, +}); ===================================== browser/components/securitylevel/content/securityLevel.js ===================================== @@ -5,8 +5,6 @@ ChromeUtils.defineESModuleGetters(this, { SecurityLevelPrefs: "moz-src:///toolkit/components/securitylevel/SecurityLevel.sys.mjs", - SecurityLevelUIUtils: - "moz-src:///browser/components/securitylevel/SecurityLevelUIUtils.sys.mjs", }); /* @@ -263,103 +261,3 @@ var SecurityLevelPanel = { } }, }; /* SecurityLevelPanel */ - -/* - Security Level Preferences Code - - Code to handle init and update of security level section in about:preferences#privacy -*/ - -var SecurityLevelPreferences = { - _securityPrefsBranch: null, - - /** - * The element that shows the current security level. - * - * @type {?Element} - */ - _currentEl: null, - - _populateXUL() { - this._currentEl = document.getElementById("security-level-current"); - const changeButton = document.getElementById("security-level-change"); - const badgeEl = this._currentEl.querySelector( - ".security-level-current-badge" - ); - - for (const { level, nameId } of [ - { level: "standard", nameId: "security-level-panel-level-standard" }, - { level: "safer", nameId: "security-level-panel-level-safer" }, - { level: "safest", nameId: "security-level-panel-level-safest" }, - { level: "custom", nameId: "security-level-panel-level-custom" }, - ]) { - // Classes that control visibility: - // security-level-current-standard - // security-level-current-safer - // security-level-current-safest - // security-level-current-custom - const visibilityClass = `security-level-current-${level}`; - const nameEl = document.createElement("div"); - nameEl.classList.add("security-level-name", visibilityClass); - document.l10n.setAttributes(nameEl, nameId); - - const descriptionEl = SecurityLevelUIUtils.createDescriptionElement( - level, - document - ); - descriptionEl.classList.add(visibilityClass); - - this._currentEl.insertBefore(nameEl, badgeEl); - this._currentEl.insertBefore(descriptionEl, changeButton); - } - - changeButton.addEventListener("click", () => { - this._openDialog(); - }); - }, - - _openDialog() { - gSubDialog.open( - "chrome://browser/content/securitylevel/securityLevelDialog.xhtml", - { features: "resizable=yes" } - ); - }, - - _configUIFromPrefs() { - // Set a data-current-level attribute for showing the current security - // level, and hiding the rest. - this._currentEl.dataset.currentLevel = - SecurityLevelPrefs.securityLevelSummary; - }, - - init() { - // populate XUL with localized strings - this._populateXUL(); - - // read prefs and populate UI - this._configUIFromPrefs(); - - // register for pref chagnes - this._securityPrefsBranch = Services.prefs.getBranch( - "browser.security_level." - ); - this._securityPrefsBranch.addObserver("", this); - }, - - uninit() { - // unregister for pref change events - this._securityPrefsBranch.removeObserver("", this); - this._securityPrefsBranch = null; - }, - - // callback for when prefs change - observe(subject, topic, data) { - switch (topic) { - case "nsPref:changed": - if (data == "security_slider" || data == "security_custom") { - this._configUIFromPrefs(); - } - break; - } - }, -}; /* SecurityLevelPreferences */ ===================================== browser/components/securitylevel/content/securityLevelDialog.css ===================================== @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +/* Hack to gain the internal styling of <moz-badge>. + * TODO: Drop this once we can use moz-badge. tor-browser#45229. */ +@import url("chrome://global/content/elements/moz-badge.css"); + +#security-level-radiogroup { + margin-block: var(--space-large) var(--space-xlarge); +} + +.security-level-radio-label { + display: inline flex; + gap: var(--space-small); +} + +.security-level-radio-label .moz-badge-label > span { + /* Still accessible to screen reader, but not visual. + * Keep inline, but with no layout width. */ + display: inline-block; + width: 1px; + margin-inline-end: -1px; + clip-path: inset(50%); +} + +.moz-badge[hidden] { + display: none; +} ===================================== browser/components/securitylevel/content/securityLevelDialog.js ===================================== @@ -3,9 +3,6 @@ const { SecurityLevelPrefs } = ChromeUtils.importESModule( "moz-src:///toolkit/components/securitylevel/SecurityLevel.sys.mjs" ); -const { SecurityLevelUIUtils } = ChromeUtils.importESModule( - "moz-src:///browser/components/securitylevel/SecurityLevelUIUtils.sys.mjs" -); const gSecurityLevelDialog = { /** @@ -27,9 +24,9 @@ const gSecurityLevelDialog = { */ _radiogroup: null, /** - * A list of radio options and their containers. + * A list of radio options and their descriptions. * - * @type {?Array<{ container: Element, radio: Element }>} + * @type {?Array<{ description: Element, radio: Element }>} */ _radioOptions: null, @@ -56,85 +53,42 @@ const gSecurityLevelDialog = { this._radiogroup = document.getElementById("security-level-radiogroup"); this._radioOptions = Array.from( - this._radiogroup.querySelectorAll(".security-level-radio-container"), - container => { - return { - container, - radio: container.querySelector(".security-level-radio"), - }; + this._radiogroup.querySelectorAll("moz-radio"), + radio => { + const description = radio.querySelector("security-level-description"); + // Hide bullets by default. + description.hideBullets = true; + return { radio, description }; } ); - for (const { container, radio } of this._radioOptions) { - const level = radio.value; - radio.id = `security-level-radio-${level}`; - const currentEl = container.querySelector( - ".security-level-current-badge" - ); - currentEl.id = `security-level-current-badge-${level}`; - const descriptionEl = SecurityLevelUIUtils.createDescriptionElement( - level, - document - ); - descriptionEl.classList.add("indent"); - descriptionEl.id = `security-level-description-${level}`; - - // Wait for the full translation of the element before adding it to the - // DOM. In particular, we want to make sure the elements have text before - // we measure the maxHeight below. - await document.l10n.translateFragment(descriptionEl); - document.l10n.pauseObserving(); - container.append(descriptionEl); - document.l10n.resumeObserving(); - - if (level === this._prevLevel) { - currentEl.hidden = false; - // When the currentEl is visible, include it in the accessible name for - // the radio option. - // NOTE: The currentEl has an accessible name which includes punctuation - // to help separate it's content from the security level name. - // E.g. "Standard (Current level)". - radio.setAttribute("aria-labelledby", `${radio.id} ${currentEl.id}`); - } else { - currentEl.hidden = true; - } - // We point the accessible description to the wrapping - // .security-level-description element, rather than its children - // that define the actual text content. This means that when the - // privacy-extra-information is shown or hidden, its text content is - // included or excluded from the accessible description, respectively. - radio.setAttribute("aria-describedby", descriptionEl.id); + for (const { radio } of this._radioOptions) { + radio.querySelector(".moz-badge").hidden = + radio.value !== this._prevLevel; } - // We want to reserve the maximum height of the radiogroup so that the + // We want to reserve the maximum height of the moz-radio-group so that the // dialog has enough height when the user switches options. So we cycle // through the options and measure the height when they are selected to set // a minimum height that fits all of them. - // NOTE: At the time of implementation, at this point the dialog may not - // yet have the "subdialog" attribute, which means it is missing the - // common.css stylesheet from its shadow root, which effects the size of the - // .radio-check element and the font. Therefore, we have duplicated the - // import of common.css in SecurityLevelDialog.xhtml to ensure it is applied - // at this earlier stage. let maxHeight = 0; - for (const { container } of this._radioOptions) { - container.classList.add("selected"); + for (const { description } of this._radioOptions) { + description.hideBullets = false; + await this._settled(); maxHeight = Math.max( maxHeight, this._radiogroup.getBoundingClientRect().height ); - container.classList.remove("selected"); + description.hideBullets = true; } this._radiogroup.style.minHeight = `${maxHeight}px`; if (this._prevLevel !== "custom") { this._selectedLevel = this._prevLevel; this._radiogroup.value = this._prevLevel; - } else { - this._radiogroup.selectedItem = null; } - this._radiogroup.addEventListener("select", () => { + this._radiogroup.addEventListener("change", () => { this._selectedLevel = this._radiogroup.value; this._updateSelected(); }); @@ -142,16 +96,31 @@ const gSecurityLevelDialog = { this._updateSelected(); }, + /** + * Wait for the DOM to be settled after some change. + */ + async _settled() { + // Wait for the widgets to react to some change. + await Promise.all([ + this._radiogroup.updateComplete, + ...this._radioOptions.map(({ radio }) => radio.updateComplete), + ]); + // Also wait for any string population. + if (document.hasPendingL10nMutations) { + await new Promise(r => + document.addEventListener("L10nMutationsFinished", r, { once: true }) + ); + } + }, + /** * Update the UI in response to a change in selection. */ _updateSelected() { this._acceptButton.disabled = !this._selectedLevel || this._selectedLevel === this._prevLevel; - // Have the container's `selected` CSS class match the selection state of - // the radio elements. - for (const { container, radio } of this._radioOptions) { - container.classList.toggle("selected", radio.selected); + for (const { description, radio } of this._radioOptions) { + description.hideBullets = !radio.checked; } }, ===================================== browser/components/securitylevel/content/securityLevelDialog.xhtml ===================================== @@ -10,18 +10,6 @@ <dialog id="security-level-dialog" buttons="accept,cancel"> <linkset> <html:link rel="stylesheet" href="chrome://global/skin/global.css" /> - <!-- NOTE: We include common.css explicitly, rather than relying on - - the dialog's shadowroot importing it, which is late loaded in - - response to the dialog's "subdialog" attribute, which is set - - in response to DOMFrameContentLoaded. - - In particular, we need the .radio-check rule and font rules from - - common-shared.css to be in place when gSecurityLevelDialog.init is - - called, which will help ensure that the radio element has the correct - - size when we measure its bounding box. --> - <html:link - rel="stylesheet" - href="chrome://global/skin/in-content/common.css" - /> <html:link rel="stylesheet" href="chrome://browser/skin/preferences/preferences.css" @@ -32,57 +20,77 @@ /> <html:link rel="stylesheet" - href="chrome://browser/content/securitylevel/securityLevelPreferences.css" + href="chrome://browser/content/securitylevel/securityLevelDialog.css" /> <html:link rel="localization" href="branding/brand.ftl" /> <html:link rel="localization" href="toolkit/global/base-browser.ftl" /> </linkset> + <html:script + type="module" + src="chrome://global/content/elements/moz-radio-group.mjs" + ></html:script> + <html:script + type="module" + src="chrome://browser/content/securitylevel/widgets/security-level-description.mjs" + ></html:script> <script src="chrome://browser/content/securitylevel/securityLevelDialog.js" /> <description data-l10n-id="security-level-dialog-restart-description" /> - <radiogroup id="security-level-radiogroup" class="highlighting-group"> - <html:div - class="security-level-radio-container security-level-grid privacy-detailedoption info-box-container" - > - <radio - class="security-level-radio security-level-name" + <moz-radio-group + xmlns="http://www.w3.org/1999/xhtml" + id="security-level-radiogroup" + data-l10n-id="security-level-dialog-radio-group" + > + <moz-radio value="standard" use-label-slot=""> + <span class="security-level-radio-label" slot="label"> + <span data-l10n-id="security-level-panel-level-standard"></span> + <!-- TODO: Use <moz-badge> widget once bugzilla bug 2065204 is + - resolved. See tor-browser#45229. --> + <span class="moz-badge" hidden="hidden"> + <span + class="moz-badge-label" + data-l10n-id="security-level-preferences-current-badge" + ></span> + </span> + </span> + <security-level-description + slot="description" value="standard" - data-l10n-id="security-level-preferences-level-standard" - /> - <html:div - class="security-level-current-badge" - data-l10n-id="security-level-preferences-current-badge" - ></html:div> - </html:div> - <html:div - class="security-level-radio-container security-level-grid privacy-detailedoption info-box-container" - > - <radio - class="security-level-radio security-level-name" + ></security-level-description> + </moz-radio> + <moz-radio value="safer" use-label-slot=""> + <span class="security-level-radio-label" slot="label"> + <span data-l10n-id="security-level-panel-level-safer"></span> + <span class="moz-badge" hidden="hidden"> + <span + class="moz-badge-label" + data-l10n-id="security-level-preferences-current-badge" + ></span> + </span> + </span> + <security-level-description + slot="description" value="safer" - data-l10n-id="security-level-preferences-level-safer" - /> - <html:div - class="security-level-current-badge" - data-l10n-id="security-level-preferences-current-badge" - ></html:div> - </html:div> - <html:div - class="security-level-radio-container security-level-grid privacy-detailedoption info-box-container" - > - <radio - class="security-level-radio security-level-name" + ></security-level-description> + </moz-radio> + <moz-radio value="safest" use-label-slot=""> + <span class="security-level-radio-label" slot="label"> + <span data-l10n-id="security-level-panel-level-safest"></span> + <span class="moz-badge" hidden="hidden"> + <span + class="moz-badge-label" + data-l10n-id="security-level-preferences-current-badge" + ></span> + </span> + </span> + <security-level-description + slot="description" value="safest" - data-l10n-id="security-level-preferences-level-safest" - /> - <html:div - class="security-level-current-badge" - data-l10n-id="security-level-preferences-current-badge" - ></html:div> - </html:div> - </radiogroup> + ></security-level-description> + </moz-radio> + </moz-radio-group> </dialog> </window> ===================================== browser/components/securitylevel/content/securityLevelPreferences.css deleted ===================================== @@ -1,171 +0,0 @@ -.security-level-grid { - display: grid; - grid-template: - "icon name badge button" min-content - "icon summary summary button" auto - "icon extra extra ." auto - / max-content max-content 1fr max-content; -} - -.security-level-icon { - grid-area: icon; - align-self: start; - width: var(--icon-size-large); - height: var(--icon-size-large); - -moz-context-properties: fill, stroke; - fill: var(--icon-color); - stroke: var(--icon-color-warning); - margin-block-start: var(--space-xsmall); - margin-inline-end: var(--space-large); -} - -:-moz-locale-dir(rtl) .security-level-icon { - transform: scaleX(-1); -} - -.security-level-current-badge { - grid-area: badge; - align-self: center; - justify-self: start; - white-space: nowrap; - background: var(--background-color-information); - color: inherit; - font-size: var(--font-size-small); - border-radius: var(--border-radius-circle); - margin-inline-start: var(--space-small); - padding-block: var(--space-xsmall); - padding-inline: var(--space-small); -} - -.security-level-current-badge span { - /* Still accessible to screen reader, but not visual. - * Keep inline, but with no layout width. */ - display: inline-block; - width: 1px; - margin-inline-end: -1px; - clip-path: inset(50%); -} - -@media (prefers-contrast) and (not (forced-colors)) { - .security-level-current-badge { - /* Match the checkbox/radio colors. */ - background: var(--color-accent-primary); - color: var(--button-text-color-primary); - } -} - -@media (forced-colors) { - .security-level-current-badge { - /* Match the checkbox/radio/selected colors. */ - background: SelectedItem; - color: SelectedItemText; - } -} - -.security-level-name { - grid-area: name; - font-weight: var(--font-weight-bold); - align-self: center; - white-space: nowrap; -} - -.security-level-description { - display: grid; - grid-column: summary-start / extra-end; - grid-row: summary-start / extra-end; - grid-template-rows: subgrid; - grid-template-columns: subgrid; - margin-block-start: var(--space-small); -} - -.security-level-summary { - grid-area: summary; -} - -.security-level-description-extra { - grid-area: extra; - margin-block: var(--space-medium) 0; - margin-inline: var(--space-large) 0; - padding: 0; -} - -.security-level-description-bullet:not(:last-child) { - margin-block-end: var(--space-medium); -} - -/* Tweak current security level display. */ - -#security-level-current { - margin-block-start: var(--space-large); - background: var(--background-color-box); - border: var(--border-width) solid var(--border-color); - border-radius: var(--border-radius-small); - padding: var(--space-medium); -} - -#security-level-change { - grid-area: button; - align-self: center; - margin: 0; - margin-inline-start: var(--space-large); -} - -/* Adjust which content is visible depending on the current security level. */ - -#security-level-current:not([data-current-level="standard"]) .security-level-current-standard { - display: none; -} - -#security-level-current:not([data-current-level="safer"]) .security-level-current-safer { - display: none; -} - -#security-level-current:not([data-current-level="safest"]) .security-level-current-safest { - display: none; -} - -#security-level-current:not([data-current-level="custom"]) .security-level-current-custom { - display: none; -} - -#security-level-current[data-current-level="standard"] .security-level-icon { - content: url("chrome://browser/content/securitylevel/security-level-standard.svg"); -} - -#security-level-current[data-current-level="safer"] .security-level-icon { - content: url("chrome://browser/content/securitylevel/security-level-safer.svg"); -} - -#security-level-current[data-current-level="safest"] .security-level-icon { - content: url("chrome://browser/content/securitylevel/security-level-safest.svg"); -} - -#security-level-current[data-current-level="custom"] .security-level-icon { - content: url("chrome://browser/content/securitylevel/security-level-custom.svg"); -} - -/* Tweak security level dialog. */ - -#security-level-radiogroup { - margin-block: var(--space-large) var(--space-xlarge); -} - -.security-level-radio-container { - padding-block: var(--space-large); -} - -#security-level-radiogroup .security-level-radio { - margin: 0; -} - -#security-level-radiogroup .radio-label-box { - /* .security-level-current-badge already has a margin. */ - margin: 0; -} - -#security-level-radiogroup .privacy-detailedoption.security-level-radio-container:not(.selected) .security-level-description-extra { - /* .privacy-detailedoption uses visibility: hidden, which does not work with - * our grid display (the margin is still reserved) so we use display: none - * instead. */ - display: none; -} ===================================== browser/components/securitylevel/content/securityLevelPreferences.inc.xhtml deleted ===================================== @@ -1,35 +0,0 @@ -<groupbox id="securityLevel-groupbox" - data-category="panePrivacy" - data-subcategory="securitylevel" - hidden="true"> - <label> - <html:h2 data-l10n-id="security-level-preferences-heading"></html:h2> - </label> - <vbox flex="1"> - <description class="description-deemphasized" flex="1"> - <html:span - id="securityLevel-overview" - data-l10n-id="security-level-preferences-overview" - ></html:span> - <html:a - is="moz-support-link" - support-page="tor-manual:features__security-levels" - data-l10n-id="security-level-preferences-learn-more-link" - ></html:a> - </description> - <html:div id="security-level-current" class="security-level-grid"> - <html:img - class="security-level-icon" - alt="" - /> - <html:div - class="security-level-current-badge" - data-l10n-id="security-level-preferences-current-badge" - ></html:div> - <html:button - id="security-level-change" - data-l10n-id="security-level-preferences-change-button" - ></html:button> - </html:div> - </vbox> -</groupbox> ===================================== browser/components/securitylevel/jar.mn ===================================== @@ -1,11 +1,16 @@ browser.jar: + content/browser/securitylevel/config/security-level.mjs (config/security-level.mjs) + content/browser/securitylevel/widgets/security-level-description.mjs (widgets/security-level-description.mjs) + content/browser/securitylevel/widgets/security-level-description.css (widgets/security-level-description.css) + content/browser/securitylevel/widgets/security-level-display.mjs (widgets/security-level-display.mjs) + content/browser/securitylevel/widgets/security-level-display.css (widgets/security-level-display.css) content/browser/securitylevel/securityLevel.js (content/securityLevel.js) content/browser/securitylevel/securityLevelPanel.css (content/securityLevelPanel.css) content/browser/securitylevel/securityLevelButton.css (content/securityLevelButton.css) - content/browser/securitylevel/securityLevelPreferences.css (content/securityLevelPreferences.css) content/browser/securitylevel/security-level-custom.svg (content/security-level-custom.svg) content/browser/securitylevel/security-level-safer.svg (content/security-level-safer.svg) content/browser/securitylevel/security-level-safest.svg (content/security-level-safest.svg) content/browser/securitylevel/security-level-standard.svg (content/security-level-standard.svg) content/browser/securitylevel/securityLevelDialog.xhtml (content/securityLevelDialog.xhtml) content/browser/securitylevel/securityLevelDialog.js (content/securityLevelDialog.js) + content/browser/securitylevel/securityLevelDialog.css (content/securityLevelDialog.css) ===================================== browser/components/securitylevel/moz.build ===================================== @@ -1,5 +1 @@ JAR_MANIFESTS += ["jar.mn"] - -MOZ_SRC_FILES += [ - "SecurityLevelUIUtils.sys.mjs", -] ===================================== browser/components/securitylevel/widgets/security-level-description.css ===================================== @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +p { + margin: 0; +} + +:host([hide-bullets]) ul { + display: none; +} + +ul { + margin: 0; + padding-inline: var(--space-large) 0; +} + +li { + margin-block-start: var(--space-xsmall); +} ===================================== browser/components/securitylevel/widgets/security-level-description.mjs ===================================== @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; +import { html, repeat } from "chrome://global/content/vendor/lit.all.mjs"; + +/** + * Widget that displays the description for a given security level. + * + * @tagname security-level-description + * @property {string} value - The security level to show the description for. + * @property {boolean} hideBullets - Whether to hide the bullet points for the + * description. + */ +class SecurityLevelDescription extends MozLitElement { + static properties = { + value: { type: String }, + hideBullets: { type: Boolean, reflect: true, attribute: "hide-bullets" }, + }; + + static #config = { + standard: { + summaryL10nId: "security-level-summary-standard", + }, + safer: { + summaryL10nId: "security-level-summary-safer", + bullets: [ + "security-level-preferences-bullet-https-only-javascript", + "security-level-preferences-bullet-limit-font-and-symbols", + "security-level-preferences-bullet-limit-media", + ], + }, + safest: { + summaryL10nId: "security-level-summary-safest", + bullets: [ + "security-level-preferences-bullet-disabled-javascript", + "security-level-preferences-bullet-limit-font-and-symbols-and-images", + "security-level-preferences-bullet-limit-media", + ], + }, + custom: { + summaryL10nId: "security-level-summary-custom", + }, + }; + + listTemplate() { + const bullets = SecurityLevelDescription.#config[this.value]?.bullets; + if (!bullets) { + return ""; + } + return html` + <ul> + ${repeat( + bullets, + l10nId => l10nId, + l10nId => html`<li data-l10n-id=${l10nId}></li>` + )} + </ul> + `; + } + + render() { + let l10nId = SecurityLevelDescription.#config[this.value]?.summaryL10nId; + if (!l10nId) { + return ""; + } + + return html` + <link + rel="stylesheet" + href="chrome://browser/content/securitylevel/widgets/security-level-description.css" + /> + <p data-l10n-id=${l10nId}></p> + ${this.listTemplate()} + `; + } +} +customElements.define("security-level-description", SecurityLevelDescription); ===================================== browser/components/securitylevel/widgets/security-level-display.css ===================================== @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +:host { + --box-icon-stroke: var(--icon-color-warning); +} + +p { + margin: 0; +} + +.text-content { + padding: var(--box-padding); +} + +.icon:dir(rtl) { + transform: scaleX(-1); +} + +security-level-description { + grid-area: description; +} ===================================== browser/components/securitylevel/widgets/security-level-display.mjs ===================================== @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +import { MozBoxBase } from "chrome://global/content/lit-utils.mjs"; +import { html } from "chrome://global/content/vendor/lit.all.mjs"; + +/** + * Widget for displaying the current security level. + * + * @tagname security-level-display + * @property {string} value - The current security level. + */ +class SecurityLevelDisplay extends MozBoxBase { + static properties = { + value: { type: String }, + _nameL10nId: { type: String }, + }; + + static #config = { + standard: { + nameL10nId: "security-level-panel-level-standard", + iconSrc: + "chrome://browser/content/securitylevel/security-level-standard.svg", + }, + safer: { + nameL10nId: "security-level-panel-level-safer", + iconSrc: + "chrome://browser/content/securitylevel/security-level-safer.svg", + }, + safest: { + nameL10nId: "security-level-panel-level-safest", + iconSrc: + "chrome://browser/content/securitylevel/security-level-safest.svg", + }, + custom: { + nameL10nId: "security-level-panel-level-custom", + iconSrc: + "chrome://browser/content/securitylevel/security-level-custom.svg", + }, + }; + + willUpdate() { + const levelConfig = SecurityLevelDisplay.#config[this.value]; + this._nameL10nId = levelConfig?.nameL10nId ?? null; + this.iconSrc = levelConfig?.iconSrc ?? null; + } + + render() { + if (!this._nameL10nId) { + return ""; + } + // NOTE: styleTemplate and iconTemplate come from MozBoxBase. + return html` + ${this.stylesTemplate()} + <link + rel="stylesheet" + href="chrome://browser/content/securitylevel/widgets/security-level-display.css" + /> + <div class="text-content has-icon has-description"> + ${this.iconTemplate()} + <p class="label" data-l10n-id=${this._nameL10nId}></p> + <security-level-description + .value=${this.value} + class="text-deemphasized" + ></security-level-description> + </div> + `; + } +} +customElements.define("security-level-display", SecurityLevelDisplay); ===================================== toolkit/content/widgets/lit-utils.mjs ===================================== @@ -266,6 +266,9 @@ export class MozBaseInputElement extends MozLitElement { // label-align-before is a customisation for the moz-toggle in about:tor. // See tor-browser#43727. labelAlignBefore: { type: Boolean, attribute: "label-align-before" }, + // Allow us to set more complex content in a label (e.g. a moz-badge). + // See tor-browser#45201. + useLabelSlot: { type: Boolean, attribute: "use-label-slot" }, }; /** @type {"inline" | "block" | "inline-end"} */ static inputLayout = "inline"; @@ -465,7 +468,7 @@ export class MozBaseInputElement extends MozLitElement { } labelTemplate() { - if (!this.label) { + if (!this.label && !this.useLabelSlot) { return ""; } let labelEl; @@ -478,6 +481,12 @@ export class MozBaseInputElement extends MozLitElement { class="text text-box-trim-start" .textContent=${this.label} ></h3>`; + } else if (this.useLabelSlot) { + labelEl = html`<slot + class="text" + name="label" + @slotchange=${this.onSlotchange} + ></slot>`; } else { labelEl = html`<span class="text" .textContent=${this.label}></span>`; } ===================================== toolkit/locales/en-US/toolkit/global/base-browser.ftl ===================================== @@ -185,13 +185,11 @@ security-level-panel-open-settings-button = Settings… ## Security level settings. -security-level-preferences-heading = Security Level -security-level-preferences-overview = Disable certain web features that can be used to attack your security and anonymity. -security-level-preferences-learn-more-link = Learn more -# Text for a badge that labels the currently active security level. -# The text in between '<span>' and '</span>' should contain some kind of bracket, like '(' and ')', or other punctuation used in your language to separate out text from its surrounding context. This will not be visible, but will be use for screen readers to make it clear that the text is not part of the same sentence. For example, in US English this would be read as "(Current level)", and the full line of text would be read as "Safest (Current level)". -security-level-preferences-current-badge = <span>(</span>Current level<span>)</span> -security-level-preferences-change-button = Change… +security-level-settings-group = + .label = Security level + .description = Disable certain web features that can be used to attack your security and anonymity. +security-level-settings-change-button = + .label = Change security level ## Security level settings dialog. @@ -201,13 +199,12 @@ security-level-dialog-window = # '-brand-short-name' is the localized browser name, like "Tor Browser". security-level-dialog-restart-description = You will need to restart { -brand-short-name } to apply any changes. This will close all windows and tabs. -security-level-preferences-level-standard = - .label = Standard -security-level-preferences-level-safer = - .label = Safer -security-level-preferences-level-safest = - .label = Safest - +# Text for a badge that labels the currently active security level. +# The text in between '<span>' and '</span>' should contain some kind of bracket, like '(' and ')', or other punctuation used in your language to separate out text from its surrounding context. This will not be visible, but will be use for screen readers to make it clear that the text is not part of the same sentence. For example, in US English this would be read as "(Current level)", and the full line of text would be read as "Safest (Current level)". +security-level-preferences-current-badge = <span>(</span>Current level<span>)</span> +# The "aria-label" provides a name for the group of radio options. This is not visibly shown, but it is useful for screen reader users. +security-level-dialog-radio-group = + .aria-label = Security level security-level-dialog-save-restart = .label = Save and restart View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/8915010... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/8915010... You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
participants (1)
-
morgan (@morgan)