Commit 56281733 authored by Dan Ballard's avatar Dan Ballard Committed by Pier Angelo Vendrame
Browse files

Bug 40701: Add security warning when downloading a file

Shown in the downloads panel, about:downloads and places.xhtml.
parent 5062cdb4
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@ var DownloadsView = {
 */
function DownloadsPlacesView(
  aRichListBox,
  torWarningMessageBar,
  aActive = true,
  aSuppressionFlag = DownloadsCommon.SUPPRESS_ALL_DOWNLOADS_OPEN
) {
@@ -241,6 +242,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",
+25 −4
Original line number Diff line number Diff line
@@ -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(
@@ -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 |=
@@ -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", () => {
      let indicator = DownloadsCommon.getIndicatorData(window);
      if (document.visibilityState === "visible") {
+32 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@

    <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"/>
@@ -48,6 +49,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"
+57 −0
Original line number Diff line number Diff line
@@ -92,6 +92,63 @@
#downloadsPanel-mainView {
  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,
+84 −11
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@

"use strict";

const PREF_SHOW_DOWNLOAD_WARNING = "browser.download.showTorWarning";

var { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);
@@ -73,6 +75,13 @@ var DownloadsPanel = {
  /** The panel will be shown as soon as data is available. */
  _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.
   */
  _torWarningInitialized: false,

  /**
   * Starts loading the download data in background, without opening the panel.
   * Use showPanel instead to load the data and open the panel at the same time.
@@ -89,6 +98,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._initialized) {
      DownloadsCommon.log("DownloadsPanel is already initialized.");
      return;
@@ -115,6 +148,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."
@@ -157,6 +217,14 @@ var DownloadsPanel = {
      DownloadIntegration.downloadSpamProtection.unregister(window);
    }

    if (this._torWarningPrefObserver) {
      Services.prefs.removeObserver(
        PREF_SHOW_DOWNLOAD_WARNING,
        this._torWarningPrefObserver
      );
      delete this._torWarningPrefObserver;
    }

    this._initialized = false;

    DownloadsSummary.active = false;
@@ -486,8 +554,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.panel.state != "open") {
        return;
@@ -500,6 +571,8 @@ var DownloadsPanel = {
      ) {
        return;
      }
    }

    let focusOptions = {};
    if (this._preventFocusRing) {
      focusOptions.focusVisible = false;
Loading