morgan pushed to branch tor-browser-128.1.0esr-14.0-1 at The Tor Project / Applications / Tor Browser
Commits: 8a68fba1 by Henry Wilkes at 2024-08-21T19:22:49+00:00 fixup! Bug 40701: Add security warning when downloading a file
Bug 41820: Convert downloads warning to moz-message-bar.
Move the logic to DownloadsTorWarning.sys.mjs so it remains in one place.
- - - - - 49c2f08e by Henry Wilkes at 2024-08-21T19:22:49+00:00 fixup! Tor Browser strings
Bug 41820: Use moz-message-bar for the downloads warning.
- - - - - 04d6d341 by Henry Wilkes at 2024-08-21T19:22:49+00:00 fixup! Tor Browser localization migration scripts.
Bug 41820: Use moz-message-bar for the downloads warning.
We add a migration to convert the old string values into the new value.
- - - - -
13 changed files:
- + browser/components/downloads/DownloadsTorWarning.sys.mjs - browser/components/downloads/content/allDownloadsView.js - browser/components/downloads/content/contentAreaDownloadsView.js - browser/components/downloads/content/contentAreaDownloadsView.xhtml - browser/components/downloads/content/downloads.css - browser/components/downloads/content/downloads.js - browser/components/downloads/content/downloadsPanel.inc.xhtml - browser/components/downloads/moz.build - browser/components/places/content/places.js - browser/components/places/content/places.xhtml - browser/themes/shared/downloads/downloads.inc.css - toolkit/locales/en-US/toolkit/global/tor-browser.ftl - + tools/torbrowser/l10n/migrations/bug-41820-moz-message-bar-download-warning.py
Changes:
===================================== browser/components/downloads/DownloadsTorWarning.sys.mjs ===================================== @@ -0,0 +1,152 @@ +/* import-globals-from /browser/base/content/utilityOverlay.js */ + +const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning"; + +/** + * Manages an instance of a tor warning. + */ +export class DownloadsTorWarning { + /** + * Observer for showing or hiding the warning. + * + * @type {function} + */ + #torWarningPrefObserver; + + /** + * Whether the warning is active. + * + * @type {boolean} + */ + #active = false; + + /** + * The moz-message-bar element that should show the warning. + * + * @type {MozMessageBar} + */ + #warningElement; + + /** + * The dismiss button for the warning. + * + * @type {HTMLButton} + */ + #dismissButton; + + /** + * Attach to an instance of the tor warning. + * + * @param {MozMessageBar} warningElement - The warning element to initialize + * and attach to. + * @param {boolean} isChrome - Whether the element belongs to the chrome. + * Otherwise it belongs to content. + * @param {function} moveFocus - Callback to move the focus out of the warning + * when it is hidden. + * @param {function} [onLinkClick] - Callback that is called when a link is + * about to open. + */ + constructor(warningElement, isChrome, moveFocus, onLinkClick) { + const doc = warningElement.ownerDocument; + this.#warningElement = warningElement; + warningElement.setAttribute( + "data-l10n-id", + "downloads-tor-warning-message-bar" + ); + warningElement.setAttribute("data-l10n-attrs", "heading, message"); + + // Observe changes to the tor warning pref. + this.#torWarningPrefObserver = () => { + if (Services.prefs.getBoolPref(PREF_SHOW_DOWNLOAD_WARNING)) { + warningElement.hidden = false; + } else { + const hadFocus = warningElement.contains(doc.activeElement); + warningElement.hidden = true; + if (hadFocus) { + moveFocus(); + } + } + }; + + const tailsLink = doc.createElement("a"); + tailsLink.setAttribute("slot", "support-link"); + tailsLink.href = "https://tails.net/"; + tailsLink.target = "_blank"; + tailsLink.setAttribute("data-l10n-id", "downloads-tor-warning-tails-link"); + if (isChrome) { + // Intercept clicks on the tails link. + tailsLink.addEventListener("click", event => { + event.preventDefault(); + onLinkClick?.(); + doc.defaultView.openWebLinkIn(tailsLink.href, "tab"); + }); + } + + const dismissButton = doc.createElement("button"); + dismissButton.setAttribute("slot", "actions"); + dismissButton.setAttribute( + "data-l10n-id", + "downloads-tor-warning-dismiss-button" + ); + if (isChrome) { + dismissButton.classList.add("footer-button"); + } + + dismissButton.addEventListener("click", () => { + Services.prefs.setBoolPref(PREF_SHOW_DOWNLOAD_WARNING, false); + }); + + warningElement.append(tailsLink); + warningElement.append(dismissButton); + + this.#dismissButton = dismissButton; + } + + /** + * Whether the warning is hidden by the preference. + * + * @type {boolean} + */ + get hidden() { + return this.#warningElement.hidden; + } + + /** + * The dismiss button for the warning. + * + * @type {HTMLButton} + */ + get dismissButton() { + return this.#dismissButton; + } + + /** + * Activate the instance. + */ + activate() { + if (this.#active) { + return; + } + this.#active = true; + Services.prefs.addObserver( + PREF_SHOW_DOWNLOAD_WARNING, + this.#torWarningPrefObserver + ); + // Initialize. + this.#torWarningPrefObserver(); + } + + /** + * Deactivate the instance. + */ + deactivate() { + if (!this.#active) { + return; + } + this.#active = false; + Services.prefs.removeObserver( + PREF_SHOW_DOWNLOAD_WARNING, + this.#torWarningPrefObserver + ); + } +}
===================================== browser/components/downloads/content/allDownloadsView.js ===================================== @@ -213,7 +213,6 @@ var DownloadsView = { */ function DownloadsPlacesView( aRichListBox, - torWarningMessageBar, aActive = true, aSuppressionFlag = DownloadsCommon.SUPPRESS_ALL_DOWNLOADS_OPEN ) { @@ -242,46 +241,6 @@ function DownloadsPlacesView( aSuppressionFlag; }
- // Tor browser warning message/alert shown above the list. - - const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning"; - // Observe changes to the tor warning pref. - const torWarningPrefObserver = () => { - if (Services.prefs.getBoolPref(PREF_SHOW_DOWNLOAD_WARNING)) { - torWarningMessageBar.hidden = false; - } else { - // Re-assign focus if it is about to be lost. - if (torWarningMessageBar.contains(document.activeElement)) { - // Try and focus the downloads list. - // NOTE: If #downloadsListBox is still hidden, this will do nothing. - // But in this case there are no other focusable targets within the - // view, so we just leave it up to the focus handler. - this._richlistbox.focus({ preventFocusRing: true }); - } - torWarningMessageBar.hidden = true; - } - }; - - Services.prefs.addObserver( - PREF_SHOW_DOWNLOAD_WARNING, - torWarningPrefObserver - ); - // Initialize. - torWarningPrefObserver(); - - window.addEventListener("unload", () => { - Services.prefs.removeObserver( - PREF_SHOW_DOWNLOAD_WARNING, - torWarningPrefObserver - ); - }); - - torWarningMessageBar - .querySelector(".downloads-tor-warning-dismiss-button") - .addEventListener("click", () => { - Services.prefs.setBoolPref(PREF_SHOW_DOWNLOAD_WARNING, false); - }); - // Make sure to unregister the view if the window is closed. window.addEventListener( "unload",
===================================== browser/components/downloads/content/contentAreaDownloadsView.js ===================================== @@ -8,12 +8,30 @@ const { PrivateBrowsingUtils } = ChromeUtils.importESModule( "resource://gre/modules/PrivateBrowsingUtils.sys.mjs" );
+const { DownloadsTorWarning } = ChromeUtils.importESModule( + "resource:///modules/DownloadsTorWarning.sys.mjs" +); + var ContentAreaDownloadsView = { init() { - const torWarningMessage = document.getElementById( - "aboutDownloadsTorWarning" - ); let box = document.getElementById("downloadsListBox"); + + const torWarning = new DownloadsTorWarning( + document.getElementById("aboutDownloadsTorWarning"), + false, + () => { + // Try and focus the downloads list. + // NOTE: If #downloadsListBox is still hidden, this will do nothing. + // But in this case there are no other focusable targets within the + // view, so we just leave it up to the focus handler. + box.focus({ preventFocusRing: true }); + } + ); + torWarning.activate(); + window.addEventListener("unload", () => { + torWarning.deactivate(); + }); + let suppressionFlag = DownloadsCommon.SUPPRESS_CONTENT_AREA_DOWNLOADS_OPEN; box.addEventListener( "InitialDownloadsLoaded", @@ -30,7 +48,7 @@ var ContentAreaDownloadsView = { // experience was bad. // Without auto-focusing the downloads list, a screen reader should not // skip beyond the alert's content. - if (torWarningMessage.hidden) { + if (torWarning.hidden) { document .getElementById("downloadsListBox") .focus({ focusVisible: false }); @@ -44,12 +62,7 @@ var ContentAreaDownloadsView = { }, { once: true } ); - let view = new DownloadsPlacesView( - box, - torWarningMessage, - true, - suppressionFlag - ); + let view = new DownloadsPlacesView(box, true, suppressionFlag); document.addEventListener("visibilitychange", () => { let indicator = DownloadsCommon.getIndicatorData(window); if (document.visibilityState === "visible") {
===================================== browser/components/downloads/content/contentAreaDownloadsView.xhtml ===================================== @@ -49,36 +49,7 @@ </keyset> #endif
- <html:message-bar - id="aboutDownloadsTorWarning" - class="downloads-tor-warning-message-bar" - role="alert" - aria-labelledby="aboutDownloadsTorWarningTitle" - aria-describedby="aboutDownloadsTorWarningDescription" - > - <html:div class="downloads-tor-warning-grid"> - <html:p - id="aboutDownloadsTorWarningTitle" - class="downloads-tor-warning-title" - data-l10n-id="downloads-tor-warning-title" - ></html:p> - <html:p - id="aboutDownloadsTorWarningDescription" - class="downloads-tor-warning-description" - data-l10n-id="downloads-tor-warning-description" - > - <html:a - href="https://tails.net/" - target="_blank" - data-l10n-name="tails-link" - ></html:a> - </html:p> - <html:button - class="downloads-tor-warning-dismiss-button" - data-l10n-id="downloads-tor-warning-dismiss-button" - ></html:button> - </html:div> - </html:message-bar> + <html:moz-message-bar id="aboutDownloadsTorWarning"></html:moz-message-bar>
<richlistbox flex="1" seltype="multiple"
===================================== browser/components/downloads/content/downloads.css ===================================== @@ -92,63 +92,13 @@ #downloadsPanel-mainView { min-width: 37em; padding: 0.62em; - /* If we don't set a width, #downloadsPanelTorWarningDescription will request + /* If we don't set a width, #downloadsPanelTorWarning will request * its max-content width. */ width: 37em; }
-#downloadsPanel-mainView { - /* Fix the layout to ensure the #downloadsWarningDescription is given enough - * vertical space. For tor-browser#40701. - * TODO: May no longer be necessary after esr 115 due to bugzilla bug 1816455. - */ - display: flex; - flex-direction: column; -} - -.downloads-tor-warning-grid { - display: grid; - grid-template: - "title button" min-content - "description button" auto - / 1fr max-content; - gap: 8px; - margin-block: 8px; - /* Some extra space between the text and the icon. */ - margin-inline-start: 8px; -} - -.downloads-tor-warning-grid .downloads-tor-warning-title { - grid-area: title; - margin: 0; -} - -.downloads-tor-warning-grid .downloads-tor-warning-description { - grid-area: description; - margin: 0; -} - -.downloads-tor-warning-grid .downloads-tor-warning-dismiss-button { - grid-area: button; - align-self: center; -} - -.downloads-tor-warning-description, -.downloads-tor-warning-title { - line-height: 1.4; -} - -.downloads-tor-warning-title { - font-weight: bold; -} - -#downloadsPanelTorWarning :is( - .downloads-tor-warning-description, - .downloads-tor-warning-title -) { - padding-inline: 8px; - margin-block-start: 8px; - margin-block-end: 0; +#downloadsPanelTorWarning { + margin-block-end: var(--arrowpanel-menuitem-padding-block); }
#downloadsHistory,
===================================== browser/components/downloads/content/downloads.js ===================================== @@ -31,8 +31,6 @@
"use strict";
-const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning"; - var { XPCOMUtils } = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ); @@ -42,6 +40,7 @@ ChromeUtils.defineESModuleGetters(this, { FileUtils: "resource://gre/modules/FileUtils.sys.mjs", NetUtil: "resource://gre/modules/NetUtil.sys.mjs", PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", + DownloadsTorWarning: "resource:///modules/DownloadsTorWarning.sys.mjs", });
const { Integration } = ChromeUtils.importESModule( @@ -76,11 +75,11 @@ var DownloadsPanel = { _waitingDataForOpen: false,
/** - * State of tor's download warning. It should only be initialized once (text assigned, - * button commands assigned). This tracks if that has been performed and prevents - * repeats. + * Tracks whether to show the tor warning or not. + * + * @type {?DownloadsTorWarning} */ - _torWarningInitialized: false, + _torWarning: null,
/** * Starts loading the download data in background, without opening the panel. @@ -98,30 +97,20 @@ var DownloadsPanel = { ); }
- const torWarningMessage = document.getElementById( - "downloadsPanelTorWarning" - ); - if (!this._torWarningPrefObserver) { - // Observe changes to the tor warning pref. - this._torWarningPrefObserver = () => { - if (Services.prefs.getBoolPref(PREF_SHOW_DOWNLOAD_WARNING)) { - torWarningMessage.hidden = false; - } else { - const hadFocus = torWarningMessage.contains(document.activeElement); - torWarningMessage.hidden = true; + if (!this._torWarning) { + this._torWarning = new DownloadsTorWarning( + document.getElementById("downloadsPanelTorWarning"), + true, + () => { // Re-assign focus that was lost. - if (hadFocus) { - this._focusPanel(true); - } + this._focusPanel(true); + }, + () => { + this.hidePanel(); } - }; - Services.prefs.addObserver( - PREF_SHOW_DOWNLOAD_WARNING, - this._torWarningPrefObserver ); - // Initialize - this._torWarningPrefObserver(); } + this._torWarning.activate();
if (this._initialized) { DownloadsCommon.log("DownloadsPanel is already initialized."); @@ -149,33 +138,6 @@ var DownloadsPanel = { DownloadsSummary );
- if (!this._torWarningInitialized) { - // Intercept clicks on the tails link. - // NOTE: We listen for clicks on the parent instead of the - // <a data-l10n-name="tails-link"> element because the latter may be - // swapped for a new instance by Fluent when refreshing the parent. - torWarningMessage - .querySelector(".downloads-tor-warning-description") - .addEventListener("click", event => { - const tailsLink = event.target.closest( - ".downloads-tor-warning-tails-link" - ); - if (!tailsLink) { - return; - } - event.preventDefault(); - this.hidePanel(); - openWebLinkIn(tailsLink.href, "tab"); - }); - - torWarningMessage - .querySelector(".downloads-tor-warning-dismiss-button") - .addEventListener("click", () => { - Services.prefs.setBoolPref(PREF_SHOW_DOWNLOAD_WARNING, false); - }); - this._torWarningInitialized = true; - } - DownloadsCommon.log( "DownloadsView attached - the panel for this window", "should now see download items come in." @@ -218,13 +180,7 @@ var DownloadsPanel = { DownloadIntegration.downloadSpamProtection.unregister(window); }
- if (this._torWarningPrefObserver) { - Services.prefs.removeObserver( - PREF_SHOW_DOWNLOAD_WARNING, - this._torWarningPrefObserver - ); - delete this._torWarningPrefObserver; - } + this._torWarning?.deactivate();
this._initialized = false;
@@ -582,13 +538,8 @@ var DownloadsPanel = { // Focus the "Got it" button if it is visible. // This should ensure that the alert is read aloud by Orca when the // downloads panel is opened. See tor-browser#42642. - const torWarningMessage = document.getElementById( - "downloadsPanelTorWarning" - ); - if (!torWarningMessage.hidden) { - torWarningMessage - .querySelector(".downloads-tor-warning-dismiss-button") - .focus(focusOptions); + if (!this._torWarning?.hidden) { + this._torWarning.dismissButton.focus(focusOptions); return; }
===================================== browser/components/downloads/content/downloadsPanel.inc.xhtml ===================================== @@ -104,37 +104,8 @@ disablekeynav="true">
<panelview id="downloadsPanel-mainView"> - <vbox id="downloadsPanelTorWarning"> - <vbox - role="alert" - aria-labelledby="downloadsPanelTorWarningTitle" - aria-describedby="downloadsPanelTorWarningDescription" - > - <html:p - id="downloadsPanelTorWarningTitle" - class="downloads-tor-warning-title" - data-l10n-id="downloads-tor-warning-title" - ></html:p> - <html:p - id="downloadsPanelTorWarningDescription" - class="downloads-tor-warning-description" - data-l10n-id="downloads-tor-warning-description" - > - <html:a - href="https://tails.net/" - data-l10n-name="tails-link" - class="downloads-tor-warning-tails-link" - ></html:a> - </html:p> - <html:moz-button-group class="panel-footer"> - <html:button - class="footer-button downloads-tor-warning-dismiss-button" - data-l10n-id="downloads-tor-warning-dismiss-button" - ></html:button> - </html:moz-button-group> - </vbox> - <toolbarseparator /> - </vbox> + <html:moz-message-bar id="downloadsPanelTorWarning"> + </html:moz-message-bar> <vbox class="panel-view-body-unscrollable"> <richlistbox id="downloadsListBox" data-l10n-id="downloads-panel-items"
===================================== browser/components/downloads/moz.build ===================================== @@ -15,6 +15,7 @@ EXTRA_JS_MODULES += [ "DownloadsCommon.sys.mjs", "DownloadSpamProtection.sys.mjs", "DownloadsTaskbar.sys.mjs", + "DownloadsTorWarning.sys.mjs", "DownloadsViewableInternally.sys.mjs", "DownloadsViewUI.sys.mjs", ]
===================================== browser/components/places/content/places.js ===================================== @@ -6,7 +6,6 @@ /* import-globals-from editBookmark.js */ /* import-globals-from /toolkit/content/contentAreaUtils.js */ /* import-globals-from /browser/components/downloads/content/allDownloadsView.js */ -/* import-globals-from /browser/base/content/utilityOverlay.js */
/* Shared Places Import - change other consumers if you change this: */ var { XPCOMUtils } = ChromeUtils.importESModule( @@ -18,6 +17,7 @@ ChromeUtils.defineESModuleGetters(this, { PlacesBackups: "resource://gre/modules/PlacesBackups.sys.mjs", PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", + DownloadsTorWarning: "resource:///modules/DownloadsTorWarning.sys.mjs", }); XPCOMUtils.defineLazyScriptGetter( this, @@ -158,15 +158,25 @@ var PlacesOrganizer = { "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
- const torWarningMessage = document.getElementById( - "placesDownloadsTorWarning" + const torWarning = new DownloadsTorWarning( + document.getElementById("placesDownloadsTorWarning"), + true, + () => { + document + .getElementById("downloadsListBox") + .focus({ preventFocusRing: true }); + } ); + torWarning.activate(); + window.addEventListener("unload", () => { + torWarning.deactivate(); + }); + ContentArea.setContentViewForQueryString( DOWNLOADS_QUERY, () => new DownloadsPlacesView( document.getElementById("downloadsListBox"), - torWarningMessage, false ), { @@ -176,23 +186,6 @@ var PlacesOrganizer = { } );
- // Intercept clicks on the tor warning tails link. - // NOTE: We listen for clicks on the parent instead of the - // <a data-l10n-name="tails-link"> element because the latter may be - // swapped for a new instance by Fluent when refreshing the parent. - document - .querySelector(".downloads-tor-warning-description") - .addEventListener("click", event => { - const tailsLink = event.target.closest( - ".downloads-tor-warning-tails-link" - ); - if (!tailsLink) { - return; - } - event.preventDefault(); - openWebLinkIn(tailsLink.href, "tab"); - }); - ContentArea.init();
this._places = document.getElementById("placesList"); @@ -1432,6 +1425,7 @@ var ContentArea = { oldView.associatedElement.hidden = true; aNewView.associatedElement.hidden = false;
+ // Hide the Tor warning when not in the downloads view. const isDownloads = aNewView.associatedElement.id === "downloadsListBox"; const torWarningMessage = document.getElementById( "placesDownloadsTorWarning"
===================================== browser/components/places/content/places.xhtml ===================================== @@ -363,35 +363,8 @@ </tree> <splitter collapse="none" persist="state"></splitter> <vbox id="contentView"> - <html:message-bar - id="placesDownloadsTorWarning" - role="alert" - aria-labelledby="placesDownloadsTorWarningTitle" - aria-describedby="placesDownloadsTorWarningDescription" - > - <html:div class="downloads-tor-warning-grid"> - <html:p - id="placesDownloadsTorWarningTitle" - class="downloads-tor-warning-title" - data-l10n-id="downloads-tor-warning-title" - ></html:p> - <html:p - id="placesDownloadsTorWarningDescription" - class="downloads-tor-warning-description" - data-l10n-id="downloads-tor-warning-description" - > - <html:a - href="https://tails.net/" - class="downloads-tor-warning-tails-link" - data-l10n-name="tails-link" - ></html:a> - </html:p> - <html:button - class="downloads-tor-warning-dismiss-button" - data-l10n-id="downloads-tor-warning-dismiss-button" - ></html:button> - </html:div> - </html:message-bar> + <html:moz-message-bar id="placesDownloadsTorWarning"> + </html:moz-message-bar> <vbox id="placesViewsBox" flex="1"> <tree id="placeContent" class="placesTree"
===================================== browser/themes/shared/downloads/downloads.inc.css ===================================== @@ -252,7 +252,6 @@ }
/*** Toolbarseparator ***/ -#downloadsPanelTorWarning toolbarseparator, #downloadsFooterButtons > toolbarseparator { margin-inline: 0; }
===================================== toolkit/locales/en-US/toolkit/global/tor-browser.ftl ===================================== @@ -559,9 +559,13 @@ crypto-safety-prompt-dismiss-button = Dismiss Warning ## Downloads warning. ## Shown in downloads panel, about:downloads and Library window.
-downloads-tor-warning-title = Be careful opening downloads -# "Tails" is the brand name for the Tails operating system and should be localized appropriately, and will be a link to its website. The name should be wrapped in '<a data-l10n-name="tails-link">' and '</a>'. -downloads-tor-warning-description = Some files may connect to the internet when opened without using Tor. To be safe, open the files while offline or use a portable operating system like <a data-l10n-name="tails-link">Tails</a>. +# "Tails" is the brand name for the Tails operating system and should be localized appropriately. +downloads-tor-warning-message-bar = + .heading = Be careful opening downloads + .message = Some files may connect to the internet when opened without using Tor. To be safe, open the files while offline or use a portable operating system like Tails. +# This will be a link to the Tails operating system website. +# "Tails" is the brand name for the Tails operating system and should be localized appropriately. +downloads-tor-warning-tails-link= Learn more about Tails # Button to dismiss the warning forever. downloads-tor-warning-dismiss-button = Got it
===================================== tools/torbrowser/l10n/migrations/bug-41820-moz-message-bar-download-warning.py ===================================== @@ -0,0 +1,67 @@ +import re + +import fluent.syntax.ast as FTL +from fluent.migrate.transforms import COPY_PATTERN, FluentSource +from fluent.syntax.visitor import Visitor + + +class RemoveAnchorVisitor(Visitor): + """Class to remove <a> and </a> wrappers from a Fluent TextElement.""" + + def __init__(self): + # Good enough regex for our needs that will match starting and ending + # tags. + self._anchor_regex = re.compile(r"</?[aA](| [^>]*)>") + super().__init__() + + def visit_TextElement(self, node): + node.value = self._anchor_regex.sub("", node.value) + + +class RemoveAnchorTransform(FluentSource): + """Class to remove <a> and </a> wrappers from a Fluent source.""" + + def __call__(self, ctx): + pattern = ctx.get_fluent_source_pattern(self.path, self.key).clone() + # Visit every node in the pattern, replacing each TextElement's content. + RemoveAnchorVisitor().visit(pattern) + return pattern + + +def migrate(ctx): + # Convert + # + # downloads-tor-warning-title = A + # downloads-tor-warning-description = B<a data-l10n-name="tails-link">C</a>D + # + # to + # + # downloads-tor-warning-message-bar = + # .heading = A + # .message = BCD + ctx.add_transforms( + "tor-browser.ftl", + "tor-browser.ftl", + [ + FTL.Message( + id=FTL.Identifier("downloads-tor-warning-message-bar"), + value=None, + attributes=[ + FTL.Attribute( + id=FTL.Identifier("heading"), + value=COPY_PATTERN( + "tor-browser.ftl", + "downloads-tor-warning-title", + ), + ), + FTL.Attribute( + id=FTL.Identifier("message"), + value=RemoveAnchorTransform( + "tor-browser.ftl", + "downloads-tor-warning-description", + ), + ), + ], + ), + ], + )
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/f650dac...
tbb-commits@lists.torproject.org