Loading browser/components/downloads/content/allDownloadsView.js +41 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ var DownloadsView = { */ function DownloadsPlacesView( aRichListBox, torWarningMessageBar, aActive = true, aSuppressionFlag = DownloadsCommon.SUPPRESS_ALL_DOWNLOADS_OPEN ) { Loading Loading @@ -244,6 +245,46 @@ 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", event => { Services.prefs.setBoolPref(PREF_SHOW_DOWNLOAD_WARNING, false); }); // Make sure to unregister the view if the window is closed. window.addEventListener( "unload", Loading browser/components/downloads/content/contentAreaDownloadsView.js +72 −4 Original line number Diff line number Diff line Loading @@ -10,6 +10,9 @@ const { PrivateBrowsingUtils } = ChromeUtils.importESModule( var ContentAreaDownloadsView = { init() { const torWarningMessage = document.getElementById( "aboutDownloadsTorWarning" ); let box = document.getElementById("downloadsListBox"); let suppressionFlag = DownloadsCommon.SUPPRESS_CONTENT_AREA_DOWNLOADS_OPEN; box.addEventListener( Loading @@ -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) // 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({ focusVisible: false }); } // Pause the indicator if the browser is active. if (document.visibilityState === "visible") { DownloadsCommon.getIndicatorData(window).attentionSuppressed |= Loading @@ -28,7 +44,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") { Loading @@ -41,6 +62,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.net/"; 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); }, }; Loading browser/components/downloads/content/contentAreaDownloadsView.xhtml +17 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,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" Loading browser/components/downloads/content/downloads.css +57 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,63 @@ font: caption; min-width: 37em; padding: 0.62em; /* If we don't set a width, #downloadsPanelTorWarningDescription 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; } #downloadsHistory, Loading browser/components/downloads/content/downloads.js +117 −11 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ "use strict"; const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning"; var { XPCOMUtils } = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ); Loading Loading @@ -77,6 +79,13 @@ var DownloadsPanel = { */ _state: 0, /** * 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. */ _torWarningInitialized: false, /** The panel is not linked to downloads data yet. */ get kStateUninitialized() { return 0; Loading Loading @@ -110,6 +119,30 @@ 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 { // 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) { DownloadsCommon.log("DownloadsPanel is already initialized."); return; Loading @@ -133,6 +166,42 @@ var DownloadsPanel = { DownloadsSummary ); if (!this._torWarningInitialized) { torWarningMessage.querySelector( ".downloads-tor-warning-title" ).textContent = this._getTorString("torbutton.download.warning.title"); const tailsLink = document.createElement("a"); tailsLink.href = "https://tails.net"; tailsLink.textContent = this._getTorString( "torbutton.download.warning.tails_brand_name" ); tailsLink.addEventListener("click", event => { event.preventDefault(); this.hidePanel(); 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); let dismissButton = torWarningMessage.querySelector( ".downloads-tor-warning-dismiss-button" ); dismissButton.textContent = this._getTorString( "torbutton.download.warning.dismiss" ); dismissButton.addEventListener("click", event => { 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." Loading Loading @@ -172,6 +241,14 @@ var DownloadsPanel = { DownloadIntegration.downloadSpamProtection.unregister(window); } if (this._torWarningPrefObserver) { Services.prefs.removeObserver( PREF_SHOW_DOWNLOAD_WARNING, this._torWarningPrefObserver ); delete this._torWarningPrefObserver; } this._state = this.kStateUninitialized; DownloadsSummary.active = false; Loading Loading @@ -506,8 +583,11 @@ var DownloadsPanel = { /** * Move focus to the main element in the downloads panel, unless another * element in the panel is already focused. * * @param {bool} [forceFocus=false] - Whether to force move the focus. */ _focusPanel() { _focusPanel(forceFocus = false) { if (!forceFocus) { // We may be invoked while the panel is still waiting to be shown. if (this._state != this.kStateShown) { return; Loading @@ -520,6 +600,8 @@ var DownloadsPanel = { ) { return; } } let focusOptions = {}; if (this._preventFocusRing) { focusOptions.focusVisible = false; Loading Loading @@ -643,6 +725,30 @@ var DownloadsPanel = { } }, 0); }, /** * 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); }, }; XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel); Loading Loading
browser/components/downloads/content/allDownloadsView.js +41 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ var DownloadsView = { */ function DownloadsPlacesView( aRichListBox, torWarningMessageBar, aActive = true, aSuppressionFlag = DownloadsCommon.SUPPRESS_ALL_DOWNLOADS_OPEN ) { Loading Loading @@ -244,6 +245,46 @@ 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", event => { Services.prefs.setBoolPref(PREF_SHOW_DOWNLOAD_WARNING, false); }); // Make sure to unregister the view if the window is closed. window.addEventListener( "unload", Loading
browser/components/downloads/content/contentAreaDownloadsView.js +72 −4 Original line number Diff line number Diff line Loading @@ -10,6 +10,9 @@ const { PrivateBrowsingUtils } = ChromeUtils.importESModule( var ContentAreaDownloadsView = { init() { const torWarningMessage = document.getElementById( "aboutDownloadsTorWarning" ); let box = document.getElementById("downloadsListBox"); let suppressionFlag = DownloadsCommon.SUPPRESS_CONTENT_AREA_DOWNLOADS_OPEN; box.addEventListener( Loading @@ -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) // 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({ focusVisible: false }); } // Pause the indicator if the browser is active. if (document.visibilityState === "visible") { DownloadsCommon.getIndicatorData(window).attentionSuppressed |= Loading @@ -28,7 +44,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") { Loading @@ -41,6 +62,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.net/"; 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); }, }; Loading
browser/components/downloads/content/contentAreaDownloadsView.xhtml +17 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,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" Loading
browser/components/downloads/content/downloads.css +57 −0 Original line number Diff line number Diff line Loading @@ -93,6 +93,63 @@ font: caption; min-width: 37em; padding: 0.62em; /* If we don't set a width, #downloadsPanelTorWarningDescription 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; } #downloadsHistory, Loading
browser/components/downloads/content/downloads.js +117 −11 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ "use strict"; const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning"; var { XPCOMUtils } = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ); Loading Loading @@ -77,6 +79,13 @@ var DownloadsPanel = { */ _state: 0, /** * 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. */ _torWarningInitialized: false, /** The panel is not linked to downloads data yet. */ get kStateUninitialized() { return 0; Loading Loading @@ -110,6 +119,30 @@ 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 { // 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) { DownloadsCommon.log("DownloadsPanel is already initialized."); return; Loading @@ -133,6 +166,42 @@ var DownloadsPanel = { DownloadsSummary ); if (!this._torWarningInitialized) { torWarningMessage.querySelector( ".downloads-tor-warning-title" ).textContent = this._getTorString("torbutton.download.warning.title"); const tailsLink = document.createElement("a"); tailsLink.href = "https://tails.net"; tailsLink.textContent = this._getTorString( "torbutton.download.warning.tails_brand_name" ); tailsLink.addEventListener("click", event => { event.preventDefault(); this.hidePanel(); 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); let dismissButton = torWarningMessage.querySelector( ".downloads-tor-warning-dismiss-button" ); dismissButton.textContent = this._getTorString( "torbutton.download.warning.dismiss" ); dismissButton.addEventListener("click", event => { 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." Loading Loading @@ -172,6 +241,14 @@ var DownloadsPanel = { DownloadIntegration.downloadSpamProtection.unregister(window); } if (this._torWarningPrefObserver) { Services.prefs.removeObserver( PREF_SHOW_DOWNLOAD_WARNING, this._torWarningPrefObserver ); delete this._torWarningPrefObserver; } this._state = this.kStateUninitialized; DownloadsSummary.active = false; Loading Loading @@ -506,8 +583,11 @@ var DownloadsPanel = { /** * Move focus to the main element in the downloads panel, unless another * element in the panel is already focused. * * @param {bool} [forceFocus=false] - Whether to force move the focus. */ _focusPanel() { _focusPanel(forceFocus = false) { if (!forceFocus) { // We may be invoked while the panel is still waiting to be shown. if (this._state != this.kStateShown) { return; Loading @@ -520,6 +600,8 @@ var DownloadsPanel = { ) { return; } } let focusOptions = {}; if (this._preventFocusRing) { focusOptions.focusVisible = false; Loading Loading @@ -643,6 +725,30 @@ var DownloadsPanel = { } }, 0); }, /** * 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); }, }; XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel); Loading