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 +25 −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 browser/components/downloads/content/contentAreaDownloadsView.xhtml +32 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ <linkset> <html:link rel="localization" href="toolkit/global/textActions.ftl"/> <html:link rel="localization" href="browser/downloads.ftl" /> <html:link rel="localization" href="toolkit/global/tor-browser.ftl" /> </linkset> <script src="chrome://global/content/globalOverlay.js"/> Loading @@ -36,6 +37,37 @@ </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> <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 +99 −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,31 @@ 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; // Re-assign focus that was lost. if (hadFocus) { this._focusPanel(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 +167,33 @@ 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", 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 +233,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 +575,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,10 +592,26 @@ var DownloadsPanel = { ) { return; } } let focusOptions = {}; if (this._preventFocusRing) { focusOptions.focusVisible = false; } // 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); return; } if (DownloadsView.richListBox.itemCount > 0) { if (DownloadsView.canChangeSelectedItem) { DownloadsView.richListBox.selectedIndex = 0; 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 +25 −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
browser/components/downloads/content/contentAreaDownloadsView.xhtml +32 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ <linkset> <html:link rel="localization" href="toolkit/global/textActions.ftl"/> <html:link rel="localization" href="browser/downloads.ftl" /> <html:link rel="localization" href="toolkit/global/tor-browser.ftl" /> </linkset> <script src="chrome://global/content/globalOverlay.js"/> Loading @@ -36,6 +37,37 @@ </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> <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 +99 −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,31 @@ 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; // Re-assign focus that was lost. if (hadFocus) { this._focusPanel(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 +167,33 @@ 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", 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 +233,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 +575,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,10 +592,26 @@ var DownloadsPanel = { ) { return; } } let focusOptions = {}; if (this._preventFocusRing) { focusOptions.focusVisible = false; } // 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); return; } if (DownloadsView.richListBox.itemCount > 0) { if (DownloadsView.canChangeSelectedItem) { DownloadsView.richListBox.selectedIndex = 0; Loading