[tbb-commits] [tor-browser] 03/03: fixup! Bug 40925: Implemented the Security Level component

gitolite role git at cupani.torproject.org
Tue Dec 6 11:43:38 UTC 2022


This is an automated email from the git hooks/post-receive script.

pierov pushed a commit to branch tor-browser-102.5.0esr-12.5-1
in repository tor-browser.

commit 1add8d34c1bb91f299d0419aab2cac4edbe142aa
Author: Henry Wilkes <henry at torproject.org>
AuthorDate: Mon Nov 21 16:35:34 2022 +0000

    fixup! Bug 40925: Implemented the Security Level component
    
    Make the security level panel and button more accessible:
    
    + We use semantic elements if possible and otherwise set aria attributes. In
      particular, we give the panel a dialog role.
    + We set the "Restore Defaults" button as default if it is shown since
      it is the recommended action.
    + We focus the default button when showing the popup.
    + We handle the case where the security level button is in the overflow
      menu by closing the overflow menu first. Before this key navigation in
      the sub-panel could disrupt the position in the parent panel.
    
    We also simplify the code in general and use HTML for the main content
    of the panel.
---
 .../securitylevel/content/securityLevel.js         | 293 +++++++++------------
 .../content/securityLevelButton.inc.xhtml          |   6 +-
 .../securitylevel/content/securityLevelPanel.css   |  70 +++--
 .../content/securityLevelPanel.inc.xhtml           |  58 ++--
 4 files changed, 182 insertions(+), 245 deletions(-)

diff --git a/browser/components/securitylevel/content/securityLevel.js b/browser/components/securitylevel/content/securityLevel.js
index 4df65fe5452b..e823ac993812 100644
--- a/browser/components/securitylevel/content/securityLevel.js
+++ b/browser/components/securitylevel/content/securityLevel.js
@@ -2,10 +2,6 @@
 
 /* global AppConstants, Services, openPreferences, XPCOMUtils */
 
-XPCOMUtils.defineLazyModuleGetters(this, {
-  PanelMultiView: "resource:///modules/PanelMultiView.jsm",
-});
-
 XPCOMUtils.defineLazyGetter(this, "SecurityLevelStrings", () => {
   let strings = {
     // Generic terms
@@ -111,58 +107,92 @@ var SecurityLevelPrefs = {
 
 var SecurityLevelButton = {
   _securityPrefsBranch: null,
+  /**
+   * Whether we have added popup listeners to the panel.
+   *
+   * @type {boolean}
+   */
+  _panelPopupListenersSetup: false,
+  /**
+   * The toolbar button element.
+   *
+   * @type {Element}
+   */
+  _button: null,
+  /**
+   * The button that the panel should point to. Either the toolbar button or the
+   * overflow button.
+   *
+   * @type {Element}
+   */
+  _anchorButton: null,
 
   _configUIFromPrefs() {
-    const securityLevelButton = this.button;
-    if (securityLevelButton != null) {
-      const level = SecurityLevelPrefs.securityLevel;
-      if (!level) {
-        return;
-      }
-      const customStr = SecurityLevelPrefs.securityCustom ? "_custom" : "";
-      securityLevelButton.setAttribute("level", `${level}${customStr}`);
-      securityLevelButton.setAttribute(
-        "tooltiptext",
-        SecurityLevelStrings[`security_level_tooltip_${level}`]
-      );
+    const level = SecurityLevelPrefs.securityLevel;
+    if (!level) {
+      return;
     }
+    const customStr = SecurityLevelPrefs.securityCustom ? "_custom" : "";
+    this._button.setAttribute("level", `${level}${customStr}`);
+    this._button.setAttribute(
+      "tooltiptext",
+      SecurityLevelStrings[`security_level_tooltip_${level}`]
+    );
   },
 
   /**
-   * The node for this button.
-   *
-   * Note, the returned element may be part of the DOM or may live in the
-   * toolbox palette, where it may be added later to the DOM through
-   * customization.
-   *
-   * @type {MozToolbarbutton}
+   * Open the panel popup for the button.
    */
-  get button() {
-    // We first search in the DOM for the security level button. If it does not
-    // exist it may be in the toolbox palette. We still want to return the
-    // button in the latter case to allow it to be initialized or adjusted in
-    // case it is added back through customization.
-    return (
-      document.getElementById("security-level-button") ||
-      window.gNavToolbox.palette.querySelector("#security-level-button")
-    );
-  },
+  openPopup() {
+    let anchorNode;
+    const overflowPanel = document.getElementById("widget-overflow");
+    if (overflowPanel.contains(this._button)) {
+      // We are in the overflow panel.
+      // We first close the overflow panel, otherwise focus will not return to
+      // the nav-bar-overflow-button if the security level panel is closed with
+      // "Escape" (the navigation toolbar does not track focus when a panel is
+      // opened whilst another is already open).
+      // NOTE: In principle, using PanelMultiView would allow us to open panels
+      // from within another panel. However, when using panelmultiview for the
+      // security level panel, tab navigation was broken within the security
+      // level panel. PanelMultiView may be set up to work with a menu-like
+      // panel rather than our dialog-like panel.
+      overflowPanel.hidePopup();
+      this._anchorButton = document.getElementById("nav-bar-overflow-button");
+      anchorNode = this._anchorButton.icon;
+    } else {
+      this._anchorButton = this._button;
+      anchorNode = this._button.badgeStack;
+    }
 
-  get anchor() {
-    let button = this.button;
-    let anchor = button?.icon;
-    if (!anchor) {
-      return null;
+    const panel = SecurityLevelPanel.panel;
+    if (!this._panelPopupListenersSetup) {
+      this._panelPopupListenersSetup = true;
+      // NOTE: We expect the _anchorButton to not change whilst the popup is
+      // open.
+      panel.addEventListener("popupshown", () => {
+        this._anchorButton.setAttribute("open", "true");
+      });
+      panel.addEventListener("popuphidden", () => {
+        this._anchorButton.removeAttribute("open");
+      });
     }
 
-    anchor.setAttribute("consumeanchor", button.id);
-    return anchor;
+    panel.openPopup(anchorNode, "bottomcenter topright", 0, 0, false);
   },
 
   init() {
+    // We first search in the DOM for the security level button. If it does not
+    // exist it may be in the toolbox palette. We still want to return the
+    // button in the latter case to allow it to be initialized or adjusted in
+    // case it is added back through customization.
+    this._button =
+      document.getElementById("security-level-button") ||
+      window.gNavToolbox.palette.querySelector("#security-level-button");
     // Set a label to be be used as the accessible name, and to be shown in the
     // overflow menu and during customization.
-    this.button?.setAttribute("label", SecurityLevelStrings.security_level);
+    this._button.setAttribute("label", SecurityLevelStrings.security_level);
+    this._button.addEventListener("command", () => this.openPopup());
     // set the initial class based off of the current pref
     this._configUIFromPrefs();
 
@@ -190,32 +220,6 @@ var SecurityLevelButton = {
         break;
     }
   },
-
-  // for when the toolbar button needs to be activated and displays the Security Level panel
-  //
-  // In the toolbarbutton xul you'll notice we register this callback for both onkeypress and
-  // onmousedown. We do this to match the behavior of other panel spawning buttons such as Downloads,
-  // Library, and the Hamburger menus. Using oncommand alone would result in only getting fired
-  // after onclick, which is mousedown followed by mouseup.
-  onCommand(aEvent) {
-    // snippet borrowed from /browser/components/downloads/content/indicator.js DownloadsIndicatorView.onCommand(evt)
-    if (
-      // On Mac, ctrl-click will send a context menu event from the widget, so
-      // we don't want to bring up the panel when ctrl key is pressed.
-      (aEvent.type == "mousedown" &&
-        (aEvent.button != 0 ||
-          (AppConstants.platform == "macosx" && aEvent.ctrlKey))) ||
-      (aEvent.type == "keypress" && aEvent.key != " " && aEvent.key != "Enter")
-    ) {
-      return;
-    }
-
-    // we need to set this attribute for the button to be shaded correctly to look like it is pressed
-    // while the security level panel is open
-    this.button.setAttribute("open", "true");
-    SecurityLevelPanel.show();
-    aEvent.stopPropagation();
-  },
 }; /* SecurityLevelButton */
 
 /*
@@ -226,66 +230,49 @@ var SecurityLevelButton = {
 
 var SecurityLevelPanel = {
   _securityPrefsBranch: null,
-  _panel: null,
-  _anchor: null,
   _populated: false,
 
-  _selectors: Object.freeze({
-    panel: "panel#securityLevel-panel",
-    icon: "vbox#securityLevel-vbox>vbox",
-    labelLevel: "label#securityLevel-level",
-    labelCustom: "label#securityLevel-custom",
-    summary: "description#securityLevel-summary",
-    restoreDefaults: "button#securityLevel-restoreDefaults",
-    advancedSecuritySettings: "button#securityLevel-advancedSecuritySettings",
-    // Selectors used only for l10n - remove them when switching to Fluent
-    header: "#securityLevel-header",
-    learnMore: "#securityLevel-panel .learnMore",
-  }),
-
   _populateXUL() {
-    let selectors = this._selectors;
-
     this._elements = {
-      panel: document.querySelector(selectors.panel),
-      icon: document.querySelector(selectors.icon),
-      labelLevel: document.querySelector(selectors.labelLevel),
-      labelCustom: document.querySelector(selectors.labelCustom),
-      summaryDescription: document.querySelector(selectors.summary),
-      restoreDefaultsButton: document.querySelector(selectors.restoreDefaults),
-      advancedSecuritySettings: document.querySelector(
-        selectors.advancedSecuritySettings
+      panel: document.getElementById("securityLevel-panel"),
+      background: document.getElementById("securityLevel-background"),
+      levelName: document.getElementById("securityLevel-level"),
+      customName: document.getElementById("securityLevel-custom"),
+      summary: document.getElementById("securityLevel-summary"),
+      restoreDefaultsButton: document.getElementById(
+        "securityLevel-restoreDefaults"
       ),
-      header: document.querySelector(selectors.header),
-      learnMore: document.querySelector(selectors.learnMore),
+      settingsButton: document.getElementById("securityLevel-settings"),
     };
 
-    this._elements.header.textContent = SecurityLevelStrings.security_level;
-    this._elements.labelCustom.setAttribute(
-      "value",
-      SecurityLevelStrings.security_level_custom
-    );
-    this._elements.learnMore.setAttribute(
-      "value",
-      SecurityLevelStrings.security_level_learn_more
-    );
+    document.getElementById("securityLevel-header").textContent =
+      SecurityLevelStrings.security_level;
+    this._elements.customName.textContent =
+      SecurityLevelStrings.security_level_custom;
+    const learnMoreEl = document.getElementById("securityLevel-learnMore");
+    learnMoreEl.textContent = SecurityLevelStrings.security_level_learn_more;
+    learnMoreEl.addEventListener("click", event => {
+      window.openTrustedLinkIn(learnMoreEl.href, "tab");
+      this.hide();
+      event.preventDefault();
+    });
     this._elements.restoreDefaultsButton.textContent =
       SecurityLevelStrings.security_level_restore;
-    this._elements.advancedSecuritySettings.textContent =
+    this._elements.settingsButton.textContent =
       SecurityLevelStrings.security_level_change;
 
-    this._elements.panel.addEventListener("onpopupshown", e => {
-      this.onPopupShown(e);
-    });
-    this._elements.panel.addEventListener("onpopuphidden", e => {
-      this.onPopupHidden(e);
-    });
     this._elements.restoreDefaultsButton.addEventListener("command", () => {
       this.restoreDefaults();
     });
-    this._elements.advancedSecuritySettings.addEventListener("command", () => {
-      this.openAdvancedSecuritySettings();
+    this._elements.settingsButton.addEventListener("command", () => {
+      this.openSecuritySettings();
+    });
+
+    this._elements.panel.addEventListener("popupshown", () => {
+      // Bring focus into the panel by focusing the default button.
+      this._elements.panel.querySelector('button[default="true"]').focus();
     });
+
     this._populated = true;
     this._configUIFromPrefs();
   },
@@ -301,26 +288,35 @@ var SecurityLevelPanel = {
     const custom = SecurityLevelPrefs.securityCustom;
 
     // only visible when user is using custom settings
-    let labelCustomWarning = this._elements.labelCustom;
-    labelCustomWarning.hidden = !custom;
-    let buttonRestoreDefaults = this._elements.restoreDefaultsButton;
-    buttonRestoreDefaults.hidden = !custom;
+    this._elements.customName.hidden = !custom;
+    this._elements.restoreDefaultsButton.hidden = !custom;
+    if (custom) {
+      this._elements.settingsButton.removeAttribute("default");
+      this._elements.restoreDefaultsButton.setAttribute("default", "true");
+    } else {
+      this._elements.settingsButton.setAttribute("default", "true");
+      this._elements.restoreDefaultsButton.removeAttribute("default");
+    }
 
-    const summary = this._elements.summaryDescription;
     // Descriptions change based on security level
-    if (level) {
-      this._elements.icon.setAttribute("level", level);
-      this._elements.labelLevel.setAttribute(
-        "value",
-        SecurityLevelStrings[`security_level_${level}`]
-      );
-      summary.textContent =
-        SecurityLevelStrings[`security_level_${level}_summary`];
-    }
-    // override the summary text with custom warning
-    if (custom) {
-      summary.textContent = SecurityLevelStrings.security_level_custom_summary;
+    this._elements.background.setAttribute("level", level);
+    this._elements.levelName.textContent =
+      SecurityLevelStrings[`security_level_${level}`];
+    this._elements.summary.textContent = custom
+      ? SecurityLevelStrings.security_level_custom_summary
+      : SecurityLevelStrings[`security_level_${level}_summary`];
+  },
+
+  /**
+   * The popup element.
+   *
+   * @type {MozPanel}
+   */
+  get panel() {
+    if (!this._populated) {
+      this._populateXUL();
     }
+    return this._elements.panel;
   },
 
   init() {
@@ -335,37 +331,18 @@ var SecurityLevelPanel = {
     this._securityPrefsBranch = null;
   },
 
-  show() {
-    // we have to defer this until after the browser has finished init'ing
-    // before we can populate the panel
-    if (!this._populated) {
-      this._populateXUL();
-    }
-
-    this._elements.panel.hidden = false;
-    PanelMultiView.openPopup(
-      this._elements.panel,
-      SecurityLevelButton.anchor,
-      "bottomcenter topright",
-      0,
-      0,
-      false,
-      null
-    ).catch(Cu.reportError);
-  },
-
   hide() {
-    PanelMultiView.hidePopup(this._elements.panel);
+    this._elements.panel.hidePopup();
   },
 
   restoreDefaults() {
     SecurityLevelPrefs.securityCustom = false;
-    // hide and reshow so that layout re-renders properly
-    this.hide();
-    this.show(this._anchor);
+    // Move focus to the settings button since restore defaults button will
+    // become hidden.
+    this._elements.settingsButton.focus();
   },
 
-  openAdvancedSecuritySettings() {
+  openSecuritySettings() {
     openPreferences("privacy-securitylevel");
     this.hide();
   },
@@ -380,16 +357,6 @@ var SecurityLevelPanel = {
         break;
     }
   },
-
-  // callback when the panel is displayed
-  onPopupShown(event) {
-    SecurityLevelButton.button.setAttribute("open", "true");
-  },
-
-  // callback when the panel is hidden
-  onPopupHidden(event) {
-    SecurityLevelButton.button.removeAttribute("open");
-  },
 }; /* SecurityLevelPanel */
 
 /*
diff --git a/browser/components/securitylevel/content/securityLevelButton.inc.xhtml b/browser/components/securitylevel/content/securityLevelButton.inc.xhtml
index 96ee1ec0ca49..1737d27914c3 100644
--- a/browser/components/securitylevel/content/securityLevelButton.inc.xhtml
+++ b/browser/components/securitylevel/content/securityLevelButton.inc.xhtml
@@ -1,7 +1,5 @@
-<toolbarbutton id="security-level-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+<toolbarbutton id="security-level-button"
+               class="toolbarbutton-1 chromeclass-toolbar-additional"
                badged="true"
                removable="true"
-               onmousedown="SecurityLevelButton.onCommand(event);"
-               onkeypress="SecurityLevelButton.onCommand(event);"
-               closemenu="none"
                cui-areatype="toolbar"/>
diff --git a/browser/components/securitylevel/content/securityLevelPanel.css b/browser/components/securitylevel/content/securityLevelPanel.css
index c50acf0ae76c..5a13852be015 100644
--- a/browser/components/securitylevel/content/securityLevelPanel.css
+++ b/browser/components/securitylevel/content/securityLevelPanel.css
@@ -1,71 +1,61 @@
 /* Security Level CSS */
 
-panelview#securityLevel-panelview {
-  width: 25em;
-}
-
-vbox#securityLevel-vbox > vbox {
+#securityLevel-background {
+  /* xul:vbox with display: block will be correctly measured in size by the
+   * parent xul:panel, with line-wrapping taken into account, and allocated the
+   * required space. */
+  display: block;
+  min-height: 10em;
+  padding-inline: 16px;
   background-repeat: no-repeat;
-  /* icon center-line should be in-line with right margin */
-  /* -margin + panelWidth - imageWidth/2 */
-  background-position: calc(-16px + 25em - 4.5em) 0.4em;
+  background-position-y: top 0.4em;
+  /* Icon center should be in-line with end padding.
+   * We set the right-to-left position here, and the left-to-right position
+   * below. */
+  --background-inline-offset: calc(16px - 4.5em);
+  background-position-x: left var(--background-inline-offset);
   background-size: 9em 9em;
   -moz-context-properties: fill, fill-opacity;
   fill-opacity: 1;
   fill: var(--button-bgcolor);
-  min-height: 10em;
 }
 
-vbox#securityLevel-vbox > vbox[level="standard"] {
+/* NOTE: Use ":dir" instead of ":-moz-locale-dir" when panel switches to HTML. */
+#securityLevel-background:-moz-locale-dir(ltr) {
+  background-position-x: right var(--background-inline-offset);
+}
+
+#securityLevel-background[level="standard"] {
   background-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#standard");
 }
-vbox#securityLevel-vbox > vbox[level="safer"] {
+
+#securityLevel-background[level="safer"] {
   background-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safer");
 }
-vbox#securityLevel-vbox > vbox[level="safest"] {
+
+#securityLevel-background[level="safest"] {
   background-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safest");
 }
 
-vbox#securityLevel-vbox > toolbarseparator {
+/* Override margin in panelUI-shared.css */
+#securityLevel-panel toolbarseparator#securityLevel-separator {
   margin-inline: 16px;
 }
 
-vbox#securityLevel-vbox > vbox {
-  margin-inline: 0;
-  padding-inline: 16px;
-}
-
-vbox#securityLevel-vbox > vbox * {
-  margin-inline: 0;
-}
-
-label#securityLevel-level {
+#securityLevel-level {
   font-size: 1.25em;
   font-weight: 600;
-  padding-top: 0.15em;
 }
 
-label#securityLevel-custom {
+#securityLevel-custom {
   border-radius: 4px;
   background-color: var(--yellow-50);
   color: black;
-  font-size: 1em;
-  height: 1.6em;
-  line-height: 1.0em;
   padding: 0.4em 0.5em;
-  margin-inline-start: 1em !important;
+  margin-inline-start: 1em;
 }
 
-description#securityLevel-summary {
-  margin-top: 1em;
+#securityLevel-summary {
   padding-inline-end: 5em;
-}
-
-vbox#securityLevel-vbox > hbox.panel-footer {
-  display: flex;
-}
-
-
-button#securityLevel-advancedSecuritySettings {
-  margin-block: 0;
+  max-width: 20em;
 }
diff --git a/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml b/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml
index c485f819aba9..d330042f7311 100644
--- a/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml
+++ b/browser/components/securitylevel/content/securityLevelPanel.inc.xhtml
@@ -1,44 +1,26 @@
 <panel id="securityLevel-panel"
-       role="group"
+       role="dialog"
+       aria-labelledby="securityLevel-header"
+       aria-describedby="securityLevel-subheading securityLevel-summary"
        type="arrow"
        orient="vertical"
        level="top"
-       hidden="true"
        class="panel-no-padding">
-  <panelmultiview mainViewId="securityLevel-panelview">
-    <panelview id="securityLevel-panelview" descriptionheightworkaround="true">
-      <vbox id="securityLevel-vbox">
-        <box class="panel-header">
-          <html:h1 id="securityLevel-header"/>
-        </box>
-        <toolbarseparator></toolbarseparator>
-        <vbox>
-          <hbox>
-            <label id="securityLevel-level"/>
-            <vbox>
-              <spacer flex="1"/>
-              <label id="securityLevel-custom"/>
-              <spacer flex="1"/>
-            </vbox>
-            <spacer flex="1"/>
-          </hbox>
-          <description id="securityLevel-summary"/>
-          <hbox>
-            <label
-              class="learnMore text-link"
-              href="about:manual#security-settings"
-              useoriginprincipal="true"
-              onclick="SecurityLevelPanel.hide();"
-              is="text-link"/>
-            <spacer/>
-          </hbox>
-        </vbox>
-        <hbox class="panel-footer">
-            <button id="securityLevel-restoreDefaults"/>
-            <button id="securityLevel-advancedSecuritySettings"
-                    default="true"/>
-        </hbox>
-      </vbox>
-    </panelview>
-  </panelmultiview>
+  <box class="panel-header">
+    <html:h1 id="securityLevel-header"></html:h1>
+  </box>
+  <toolbarseparator id="securityLevel-separator"></toolbarseparator>
+  <vbox id="securityLevel-background" class="panel-subview-body">
+    <html:p id="securityLevel-subheading">
+      <html:span id="securityLevel-level"></html:span>
+      <html:span id="securityLevel-custom"></html:span>
+    </html:p>
+    <html:p id="securityLevel-summary"></html:p>
+    <html:a id="securityLevel-learnMore" href="about:manual#security-settings">
+    </html:a>
+  </vbox>
+  <hbox class="panel-footer">
+    <button id="securityLevel-settings"/>
+    <button id="securityLevel-restoreDefaults"/>
+  </hbox>
 </panel>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the tbb-commits mailing list