ma1 pushed to branch tor-browser-102.12.0esr-12.5-1 at The Tor Project / Applications / Tor Browser
Commits: 787ac906 by Dan Ballard at 2023-06-06T20:52:51+00:00 amend! Bug 40701: Add in pane security warning when downloading a file
Bug 40701: Add security warning when downloading a file
Shown in the downloads panel, about:downloads and places.xhtml.
- - - - -
11 changed files:
- 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/places/content/places.css - browser/components/places/content/places.js - browser/components/places/content/places.xhtml - browser/themes/shared/customizableui/panelUI-shared.css - browser/themes/shared/downloads/contentAreaDownloadsView.css
Changes:
===================================== browser/components/downloads/content/allDownloadsView.js ===================================== @@ -212,6 +212,7 @@ var DownloadsView = { */ function DownloadsPlacesView( aRichListBox, + torWarningMessageBar, aActive = true, aSuppressionFlag = DownloadsCommon.SUPPRESS_ALL_DOWNLOADS_OPEN ) { @@ -241,6 +242,46 @@ function DownloadsPlacesView( ).attentionSuppressed |= 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", event => { + 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 ===================================== @@ -10,6 +10,9 @@ const { PrivateBrowsingUtils } = ChromeUtils.import(
var ContentAreaDownloadsView = { init() { + const torWarningMessage = document.getElementById( + "aboutDownloadsTorWarning" + ); let box = document.getElementById("downloadsListBox"); let suppressionFlag = DownloadsCommon.SUPPRESS_CONTENT_AREA_DOWNLOADS_OPEN; box.addEventListener( @@ -17,9 +20,22 @@ var ContentAreaDownloadsView = { () => { // Set focus to Downloads list once it is created // And prevent it from showing the focus ring around the richlistbox (Bug 1702694) - document - .getElementById("downloadsListBox") - .focus({ preventFocusRing: true }); + // Prevent focusing the list whilst the tor browser warning is shown. + // Some screen readers (tested with Orca and NVDA) will not read out + // alerts if they are already present on page load. In that case, a + // screen reader user may not be aware of the warning before they + // interact with the downloads list, which we do not want. + // Some hacky workarounds were tested with Orca to get it to read back + // the alert before the focus is read, but this was inconsistent and the + // experience was bad. + // Without auto-focusing the downloads list, a screen reader should not + // skip beyond the alert's content. + if (torWarningMessage.hidden) { + document + .getElementById("downloadsListBox") + .focus({ preventFocusRing: true }); + } + // Pause the indicator if the browser is active. if (document.visibilityState === "visible") { DownloadsCommon.getIndicatorData( @@ -29,7 +45,12 @@ var ContentAreaDownloadsView = { }, { once: true } ); - let view = new DownloadsPlacesView(box, true, suppressionFlag); + let view = new DownloadsPlacesView( + box, + torWarningMessage, + true, + suppressionFlag + ); document.addEventListener("visibilitychange", aEvent => { let indicator = DownloadsCommon.getIndicatorData(window); if (document.visibilityState === "visible") { @@ -42,6 +63,53 @@ var ContentAreaDownloadsView = { if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) { view.place = "place:transition=7&sort=4"; } + + torWarningMessage.querySelector( + ".downloads-tor-warning-title" + ).textContent = this._getTorString("torbutton.download.warning.title"); + + const tailsLink = document.createElement("a"); + tailsLink.href = "https://tails.boum.org/"; + tailsLink.target = "_blank"; + tailsLink.textContent = this._getTorString( + "torbutton.download.warning.tails_brand_name" + ); + + const [beforeLink, afterLink] = this._getTorString( + "torbutton.download.warning.description" + ).split("%S"); + + torWarningMessage + .querySelector(".downloads-tor-warning-description") + .append(beforeLink, tailsLink, afterLink); + + torWarningMessage.querySelector( + ".downloads-tor-warning-dismiss-button" + ).textContent = this._getTorString("torbutton.download.warning.dismiss"); + }, + + /** + * Get a string from the properties bundle. + * + * @param {string} name - The string name. + * + * @return {string} The string. + */ + _getTorString(name) { + if (!this._stringBundle) { + this._stringBundle = Services.strings.createBundle( + "chrome://torbutton/locale/torbutton.properties" + ); + } + try { + return this._stringBundle.GetStringFromName(name); + } catch {} + if (!this._fallbackStringBundle) { + this._fallbackStringBundle = Services.strings.createBundle( + "resource://torbutton/locale/en-US/torbutton.properties" + ); + } + return this._fallbackStringBundle.GetStringFromName(name); }, };
===================================== browser/components/downloads/content/contentAreaDownloadsView.xhtml ===================================== @@ -39,6 +39,23 @@ </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"> + </html:p> + <html:p id="aboutDownloadsTorWarningDescription" + class="downloads-tor-warning-description"> + </html:p> + <html:button class="downloads-tor-warning-dismiss-button"> + </html:button> + </html:div> + </html:message-bar> + <richlistbox flex="1" seltype="multiple" id="downloadsListBox"
===================================== browser/components/downloads/content/downloads.css ===================================== @@ -104,18 +104,52 @@ flex-direction: column; }
-#downloadsWarning p { - padding-inline: 8px; - margin-block-start: 8px; - margin-block-end: 0; +.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; }
-#downloadsWarningHeaderTitle { +.downloads-tor-warning-title { font-weight: bold; }
-#downloadsWarningDescription { +#downloadsPanelTorWarning :is( + .downloads-tor-warning-description, + .downloads-tor-warning-title +) { + padding-inline: 8px; + margin-block-start: 8px; + margin-block-end: 0; +} + +#downloadsPanelTorWarningDescription { /* Make sure we wrap the text rather than request the default max-content * width from the parent XUL -moz-box. */ width: 0;
===================================== browser/components/downloads/content/downloads.js ===================================== @@ -33,8 +33,6 @@
const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning";
-const TAILS_URI = "https://tails.boum.org/"; - var { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); @@ -143,13 +141,28 @@ var DownloadsPanel = { ); }
- let showDownloadWarning = Services.prefs.getBoolPref( - PREF_SHOW_DOWNLOAD_WARNING + const torWarningMessage = document.getElementById( + "downloadsPanelTorWarning" ); - if (!showDownloadWarning) { - document.getElementById("downloadsWarning").hidden = true; - } else { - document.getElementById("downloadsWarning").hidden = false; + if (!this._torWarningPrefObserver) { + // Observe changes to the tor warning pref. + this._torWarningPrefObserver = () => { + if (Services.prefs.getBoolPref(PREF_SHOW_DOWNLOAD_WARNING)) { + torWarningMessage.hidden = false; + } else { + // Re-assign focus if it is about to be lost. + if (torWarningMessage.contains(document.activeElement)) { + this._focusPanel(true); + } + torWarningMessage.hidden = true; + } + }; + Services.prefs.addObserver( + PREF_SHOW_DOWNLOAD_WARNING, + this._torWarningPrefObserver + ); + // Initialize + this._torWarningPrefObserver(); }
if (this._state != this.kStateUninitialized) { @@ -175,42 +188,40 @@ var DownloadsPanel = { DownloadsSummary );
- if (this._torWarningInitialized == 0) { - document.getElementById( - "downloadsWarningHeaderTitle" - ).textContent = this._getString("torbutton.download.warning.title"); - let tailsBrandName = this._getString( - "torbutton.download.warning.tails_brand_name" - ); + if (!this._torWarningInitialized) { + torWarningMessage.querySelector( + ".downloads-tor-warning-title" + ).textContent = this._getTorString("torbutton.download.warning.title");
- let warningDescriptionText = this._getString( - "torbutton.download.warning.description" - ); - let [head, rest] = warningDescriptionText.split("%S"); const tailsLink = document.createElement("a"); - tailsLink.setAttribute("href", TAILS_URI); - tailsLink.textContent = tailsBrandName.trim(); + tailsLink.href = "https://tails.boum.org/"; + tailsLink.textContent = this._getTorString( + "torbutton.download.warning.tails_brand_name" + ); tailsLink.addEventListener("click", event => { event.preventDefault(); this.hidePanel(); - openWebLinkIn(TAILS_URI, "tab"); + openWebLinkIn(tailsLink.href, "tab"); });
- let downloadsWarningDescription = document.getElementById( - "downloadsWarningDescription" - ); - downloadsWarningDescription.append(head, tailsLink, rest); + const [beforeLink, afterLink] = this._getTorString( + "torbutton.download.warning.description" + ).split("%S"); + + torWarningMessage + .querySelector(".downloads-tor-warning-description") + .append(beforeLink, tailsLink, afterLink);
- let dismissBtn = document.getElementById( - "downloadWarningDismiss" + let dismissButton = torWarningMessage.querySelector( + ".downloads-tor-warning-dismiss-button" ); - dismissBtn.textContent = this._getString("torbutton.download.warning.dismiss"); - dismissBtn.addEventListener("click", event => { + dismissButton.textContent = this._getTorString( + "torbutton.download.warning.dismiss" + ); + dismissButton.addEventListener("click", event => { Services.prefs.setBoolPref(PREF_SHOW_DOWNLOAD_WARNING, false); - document.getElementById("downloadsWarning").hidden = true; - this._focusPanel(true); }); - this._torWarningInitialized = 1; + this._torWarningInitialized = true; }
DownloadsCommon.log( @@ -254,6 +265,14 @@ var DownloadsPanel = { ); }
+ if (this._torWarningPrefObserver) { + Services.prefs.removeObserver( + PREF_SHOW_DOWNLOAD_WARNING, + this._torWarningPrefObserver + ); + delete this._torWarningPrefObserver; + } + this._state = this.kStateUninitialized;
DownloadsSummary.active = false; @@ -591,14 +610,17 @@ var DownloadsPanel = { * * @param {bool} [forceFocus=false] - Whether to force move the focus. */ - _focusPanel(forceFocus=false) { + _focusPanel(forceFocus = false) { if (!forceFocus) { // We may be invoked while the panel is still waiting to be shown. if (this._state != this.kStateShown) { return; }
- if (document.activeElement && this.panel.contains(document.activeElement)) { + if ( + document.activeElement && + this.panel.contains(document.activeElement) + ) { return; } } @@ -729,7 +751,7 @@ var DownloadsPanel = { * * @return {string} The string. */ - _getString(name) { + _getTorString(name) { if (!this._stringBundle) { this._stringBundle = Services.strings.createBundle( "chrome://torbutton/locale/torbutton.properties"
===================================== browser/components/downloads/content/downloadsPanel.inc.xhtml ===================================== @@ -124,16 +124,19 @@ disablekeynav="true">
<panelview id="downloadsPanel-mainView"> - <vbox id="downloadsWarning"> + <vbox id="downloadsPanelTorWarning"> <vbox role="alert" - aria-labelledby="downloadsWarningHeaderTitle" - aria-describedby="downloadsWarningDescription"> - <html:p id="downloadsWarningHeaderTitle"></html:p> - <html:p id="downloadsWarningDescription"> + aria-labelledby="downloadsPanelTorWarningTitle" + aria-describedby="downloadsPanelTorWarningDescription"> + <html:p id="downloadsPanelTorWarningTitle" + class="downloads-tor-warning-title"> + </html:p> + <html:p id="downloadsPanelTorWarningDescription" + class="downloads-tor-warning-description"> </html:p>
<html:div class="panel-footer"> - <html:button id="downloadWarningDismiss"> + <html:button class="downloads-tor-warning-dismiss-button"> </html:button> </html:div> </vbox>
===================================== browser/components/places/content/places.css ===================================== @@ -126,3 +126,7 @@ tree[is="places-tree"] > treechildren::-moz-tree-cell { margin: 2px 4px; color: currentColor; } + +#placesDownloadsTorWarning:not(.downloads-visible) { + display: none; +}
===================================== browser/components/places/content/places.js ===================================== @@ -6,6 +6,7 @@ /* import-globals-from instantEditBookmark.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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); @@ -164,11 +165,15 @@ var PlacesOrganizer = { "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
+ const torWarningMessage = document.getElementById( + "placesDownloadsTorWarning" + ); ContentArea.setContentViewForQueryString( DOWNLOADS_QUERY, () => new DownloadsPlacesView( document.getElementById("downloadsListBox"), + torWarningMessage, false ), { @@ -178,6 +183,33 @@ var PlacesOrganizer = { } );
+ // Initialize tor warning text content. + torWarningMessage.querySelector( + ".downloads-tor-warning-title" + ).textContent = this._getTorString("torbutton.download.warning.title"); + + const tailsLink = document.createElement("a"); + tailsLink.href = "https://tails.boum.org/"; + tailsLink.textContent = this._getTorString( + "torbutton.download.warning.tails_brand_name" + ); + tailsLink.addEventListener("click", event => { + event.preventDefault(); + openWebLinkIn(tailsLink.href, "tab"); + }); + + const [beforeLink, afterLink] = this._getTorString( + "torbutton.download.warning.description" + ).split("%S"); + + torWarningMessage + .querySelector(".downloads-tor-warning-description") + .append(beforeLink, tailsLink, afterLink); + + torWarningMessage.querySelector( + ".downloads-tor-warning-dismiss-button" + ).textContent = this._getTorString("torbutton.download.warning.dismiss"); + ContentArea.init();
this._places = document.getElementById("placesList"); @@ -246,6 +278,30 @@ var PlacesOrganizer = { ContentArea.focus(); },
+ /** + * Get a string from the properties bundle. + * + * @param {string} name - The string name. + * + * @returns {string} The string. + */ + _getTorString(name) { + if (!this._stringBundle) { + this._stringBundle = Services.strings.createBundle( + "chrome://torbutton/locale/torbutton.properties" + ); + } + try { + return this._stringBundle.GetStringFromName(name); + } catch {} + if (!this._fallbackStringBundle) { + this._fallbackStringBundle = Services.strings.createBundle( + "resource://torbutton/locale/en-US/torbutton.properties" + ); + } + return this._fallbackStringBundle.GetStringFromName(name); + }, + QueryInterface: ChromeUtils.generateQI([]),
handleEvent: function PO_handleEvent(aEvent) { @@ -1386,9 +1442,20 @@ var ContentArea = { oldView.associatedElement.hidden = true; aNewView.associatedElement.hidden = false;
+ const isDownloads = aNewView.associatedElement.id === "downloadsListBox"; + const torWarningMessage = document.getElementById( + "placesDownloadsTorWarning" + ); + const torWarningLoosingFocus = + torWarningMessage.contains(document.activeElement) && !isDownloads; + torWarningMessage.classList.toggle("downloads-visible", isDownloads); + // If the content area inactivated view was focused, move focus // to the new view. - if (document.activeElement == oldView.associatedElement) { + if ( + document.activeElement == oldView.associatedElement || + torWarningLoosingFocus + ) { aNewView.associatedElement.focus(); } }
===================================== browser/components/places/content/places.xhtml ===================================== @@ -345,6 +345,21 @@ </tree> <splitter collapse="none" persist="state"></splitter> <vbox id="contentView" flex="4"> + <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"> + </html:p> + <html:p id="placesDownloadsTorWarningDescription" + class="downloads-tor-warning-description"> + </html:p> + <html:button class="downloads-tor-warning-dismiss-button"> + </html:button> + </html:div> + </html:message-bar> <vbox id="placesViewsBox" flex="1"> <tree id="placeContent" class="plain placesTree"
===================================== browser/themes/shared/customizableui/panelUI-shared.css ===================================== @@ -1324,7 +1324,7 @@ panelview .toolbarbutton-1 { #downloadsFooterButtons > toolbarseparator, .cui-widget-panelview menuseparator, .cui-widget-panel toolbarseparator, -#downloadsWarning toolbarseparator, +#downloadsPanelTorWarning toolbarseparator, #securityLevel-panel toolbarseparator { appearance: none; min-height: 0;
===================================== browser/themes/shared/downloads/contentAreaDownloadsView.css ===================================== @@ -25,3 +25,7 @@ text-align: center; color: var(--in-content-deemphasized-text); } + +#aboutDownloadsTorWarning { + margin-block-end: 8px; +}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/787ac906...
tor-commits@lists.torproject.org