[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-115.10.0esr-13.5-1] fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in...

richard (@richard) git at gitlab.torproject.org
Thu Apr 18 21:28:45 UTC 2024



richard pushed to branch tor-browser-115.10.0esr-13.5-1 at The Tor Project / Applications / Tor Browser


Commits:
00e009df by Henry Wilkes at 2024-04-18T13:57:12+01:00
fixup! Bug 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection

Bug 42457: Add a loading icon for Lox invites.

Also reduce the amount of focus-jumping by keeping the focus on the
disabled button.

Also change the focus of the last provideBridgeDialog page to be the
table of bridges. NVDA did not announce the focus when it was set to the
dialog itself.

- - - - -


6 changed files:

- browser/components/torpreferences/content/connectionPane.xhtml
- browser/components/torpreferences/content/loxInviteDialog.js
- browser/components/torpreferences/content/loxInviteDialog.xhtml
- browser/components/torpreferences/content/provideBridgeDialog.js
- browser/components/torpreferences/content/provideBridgeDialog.xhtml
- browser/components/torpreferences/content/torPreferences.css


Changes:

=====================================
browser/components/torpreferences/content/connectionPane.xhtml
=====================================
@@ -48,7 +48,7 @@
           class="network-status-label"
           data-l10n-id="tor-connection-internet-status-label"
         ></html:span>
-        <img alt="" class="network-status-loading-icon" />
+        <img alt="" class="network-status-loading-icon tor-loading-icon" />
         <html:span class="network-status-result"></html:span>
       </html:div>
       <html:button


=====================================
browser/components/torpreferences/content/loxInviteDialog.js
=====================================
@@ -62,12 +62,12 @@ const gLoxInvites = {
     this._remainingInvitesEl = document.getElementById(
       "lox-invite-dialog-remaining"
     );
+    this._generateArea = document.getElementById(
+      "lox-invite-dialog-generate-area"
+    );
     this._generateButton = document.getElementById(
       "lox-invite-dialog-generate-button"
     );
-    this._connectingEl = document.getElementById(
-      "lox-invite-dialog-connecting"
-    );
     this._errorEl = document.getElementById("lox-invite-dialog-error-message");
     this._inviteListEl = document.getElementById("lox-invite-dialog-list");
 
@@ -237,20 +237,46 @@ const gLoxInvites = {
   _setGenerating(isGenerating) {
     this._generating = isGenerating;
     this._updateGenerateButtonState();
-    this._connectingEl.classList.toggle("show-connecting", isGenerating);
+    this._generateArea.classList.toggle("show-connecting", isGenerating);
   },
 
+  /**
+   * Whether the generate button is disabled.
+   *
+   * @type {boolean}
+   */
+  _generateDisabled: false,
   /**
    * Update the state of the generate button.
    */
   _updateGenerateButtonState() {
-    this._generateButton.disabled = this._generating || !this._remainingInvites;
+    const disabled = this._generating || !this._remainingInvites;
+    this._generateDisabled = disabled;
+    // When generating we use "aria-disabled" rather than the "disabled"
+    // attribute so that the button can remain focusable whilst we generate
+    // invites.
+    // NOTE: When we generate the invite the focus will move to the invite list,
+    // so it should be safe to make the button non-focusable in this case.
+    const spoofDisabled = this._generating;
+    this._generateButton.disabled = disabled && !spoofDisabled;
+    this._generateButton.classList.toggle(
+      "spoof-button-disabled",
+      spoofDisabled
+    );
+    if (spoofDisabled) {
+      this._generateButton.setAttribute("aria-disabled", "true");
+    } else {
+      this._generateButton.removeAttribute("aria-disabled");
+    }
   },
 
   /**
    * Start generating a new invite.
    */
   _generateNewInvite() {
+    if (this._generateDisabled) {
+      return;
+    }
     if (this._generating) {
       console.error("Already generating an invite");
       return;
@@ -258,15 +284,13 @@ const gLoxInvites = {
     this._setGenerating(true);
     // Clear the previous error.
     this._updateGenerateError(null);
-    // Move focus from the button to the connecting element, since button is
-    // now disabled.
-    this._connectingEl.focus();
 
-    let lostFocus = false;
+    let moveFocus = false;
     Lox.generateInvite(this._loxId)
       .finally(() => {
-        // Fetch whether the connecting label still has focus before we hide it.
-        lostFocus = this._connectingEl.contains(document.activeElement);
+        // Fetch whether the generate button has focus before we potentially
+        // disable it.
+        moveFocus = this._generateButton.contains(document.activeElement);
         this._setGenerating(false);
       })
       .then(
@@ -279,7 +303,7 @@ const gLoxInvites = {
             this._inviteListEl.selectedIndex = 0;
           }
 
-          if (lostFocus) {
+          if (moveFocus) {
             // Move focus to the new invite before we hide the "Connecting"
             // message.
             this._inviteListEl.focus();
@@ -295,12 +319,6 @@ const gLoxInvites = {
               this._updateGenerateError("generic");
               break;
           }
-
-          if (lostFocus) {
-            // Move focus back to the button before we hide the "Connecting"
-            // message.
-            this._generateButton.focus();
-          }
         }
       );
   },
@@ -315,7 +333,7 @@ const gLoxInvites = {
     // First clear the existing error.
     this._errorEl.removeAttribute("data-l10n-id");
     this._errorEl.textContent = "";
-    this._errorEl.classList.toggle("show-error", !!type);
+    this._generateArea.classList.toggle("show-error", !!type);
 
     if (!type) {
       return;


=====================================
browser/components/torpreferences/content/loxInviteDialog.xhtml
=====================================
@@ -40,10 +40,14 @@
           id="lox-invite-dialog-error-message"
           role="alert"
         ></html:span>
+        <img
+          id="lox-invite-dialog-loading-icon"
+          class="tor-loading-icon"
+          alt=""
+        />
         <html:span
           id="lox-invite-dialog-connecting"
           role="alert"
-          tabindex="0"
           data-l10n-id="lox-invite-dialog-connecting"
         ></html:span>
       </html:div>


=====================================
browser/components/torpreferences/content/provideBridgeDialog.js
=====================================
@@ -84,13 +84,19 @@ const gProvideBridgeDialog = {
 
     this._dialog = document.getElementById("user-provide-bridge-dialog");
     this._acceptButton = this._dialog.getButton("accept");
+
+    // Inject our stylesheet into the shadow root so that the accept button can
+    // take the spoof-button-disabled styling.
+    const styleLink = document.createElement("link");
+    styleLink.rel = "stylesheet";
+    styleLink.href =
+      "chrome://browser/content/torpreferences/torPreferences.css";
+    this._dialog.shadowRoot.append(styleLink);
+
     this._textarea = document.getElementById("user-provide-bridge-textarea");
     this._errorEl = document.getElementById(
       "user-provide-bridge-error-message"
     );
-    this._connectingEl = document.getElementById(
-      "user-provide-bridge-connecting"
-    );
     this._resultDescription = document.getElementById(
       "user-provide-result-description"
     );
@@ -152,13 +158,16 @@ const gProvideBridgeDialog = {
    * Reset focus position in the dialog.
    */
   takeFocus() {
-    if (this._page === "entry") {
-      this._textarea.focus();
-    } else {
-      // Move focus to the <xul:window> element.
-      // In particular, we do not want to keep the focus on the (same) accept
-      // button (with now different text).
-      document.documentElement.focus();
+    switch (this._page) {
+      case "entry":
+        this._textarea.focus();
+        break;
+      case "result":
+        // Move focus to the table.
+        // In particular, we do not want to keep the focus on the (same) accept
+        // button (with now different text).
+        this._bridgeGrid.focus();
+        break;
     }
   },
 
@@ -193,12 +202,27 @@ const gProvideBridgeDialog = {
     }
   },
 
+  /**
+   * Whether the dialog accept button is disabled.
+   *
+   * @type {boolean}
+   */
+  _acceptDisabled: false,
   /**
    * Callback for whenever the accept button's might need to be disabled.
    */
   updateAcceptDisabled() {
-    this._acceptButton.disabled =
+    const disabled =
       this._page === "entry" && (this.isEmpty() || this._loxLoading);
+    this._acceptDisabled = disabled;
+    // Spoof the button to look and act as if it is disabled, but still allow
+    // keyboard focus so the user can sit on this button whilst we are loading.
+    this._acceptButton.classList.toggle("spoof-button-disabled", disabled);
+    if (disabled) {
+      this._acceptButton.setAttribute("aria-disabled", "true");
+    } else {
+      this._acceptButton.removeAttribute("aria-disabled");
+    }
   },
 
   /**
@@ -217,16 +241,7 @@ const gProvideBridgeDialog = {
   setLoxLoading(isLoading) {
     this._loxLoading = isLoading;
     this._textarea.readOnly = isLoading;
-    this._connectingEl.classList.toggle("show-connecting", isLoading);
-    if (
-      isLoading &&
-      this._acceptButton.contains(
-        this._acceptButton.getRootNode().activeElement
-      )
-    ) {
-      // Move focus to the alert before we disable the button.
-      this._connectingEl.focus();
-    }
+    this._dialog.classList.toggle("show-connecting", isLoading);
     this.updateAcceptDisabled();
   },
 
@@ -236,6 +251,12 @@ const gProvideBridgeDialog = {
    * @param {Event} event - The dialogaccept event.
    */
   onDialogAccept(event) {
+    if (this._acceptDisabled) {
+      // Prevent closing.
+      event.preventDefault();
+      return;
+    }
+
     if (this._page === "result") {
       this._result.accepted = true;
       // Continue to close the dialog.
@@ -313,14 +334,11 @@ const gProvideBridgeDialog = {
     this._errorEl.textContent = "";
     if (error) {
       this._textarea.setAttribute("aria-invalid", "true");
-      // Move focus back to the text area, likely away from the Next button or
-      // the "Connecting..." alert.
-      this._textarea.focus();
     } else {
       this._textarea.removeAttribute("aria-invalid");
     }
     this._textarea.classList.toggle("invalid-input", !!error);
-    this._errorEl.classList.toggle("show-error", !!error);
+    this._dialog.classList.toggle("show-error", !!error);
 
     if (!error) {
       return;


=====================================
browser/components/torpreferences/content/provideBridgeDialog.xhtml
=====================================
@@ -51,10 +51,14 @@
           role="alert"
           aria-live="assertive"
         ></html:span>
+        <img
+          id="user-provide-bridge-loading-icon"
+          class="tor-loading-icon"
+          alt=""
+        />
         <html:span
           id="user-provide-bridge-connecting"
           role="alert"
-          tabindex="0"
           data-l10n-id="user-provide-bridge-dialog-connecting"
         ></html:span>
       </html:div>
@@ -70,6 +74,8 @@
         id="user-provide-bridge-grid-display"
         class="tor-bridges-grid"
         role="table"
+        tabindex="0"
+        aria-labelledby="user-provide-result-description"
       ></html:div>
       <html:template id="user-provide-bridge-row-template">
         <html:div class="tor-bridges-grid-row" role="row">


=====================================
browser/components/torpreferences/content/torPreferences.css
=====================================
@@ -14,6 +14,24 @@ button.spoof-button-disabled {
   pointer-events: none;
 }
 
+.tor-loading-icon {
+  width: 16px;
+  height: 16px;
+  content: image-set(
+    url("chrome://global/skin/icons/tor-light-loading.png"),
+    url("chrome://global/skin/icons/tor-light-loading@2x.png") 2x
+  );
+}
+
+ at media (prefers-color-scheme: dark) {
+  .tor-loading-icon {
+    content: image-set(
+      url("chrome://global/skin/icons/tor-dark-loading.png"),
+      url("chrome://global/skin/icons/tor-dark-loading@2x.png") 2x
+    );
+  }
+}
+
 /* Status */
 
 #network-status-internet-area {
@@ -81,21 +99,6 @@ button.spoof-button-disabled {
 
 .network-status-loading-icon {
   margin-inline-end: 0.5em;
-  width: 16px;
-  height: 16px;
-  content: image-set(
-    url("chrome://global/skin/icons/tor-light-loading.png"),
-    url("chrome://global/skin/icons/tor-light-loading@2x.png") 2x
-  );
-}
-
- at media (prefers-color-scheme: dark) {
-  .network-status-loading-icon {
-    content: image-set(
-      url("chrome://global/skin/icons/tor-dark-loading.png"),
-      url("chrome://global/skin/icons/tor-dark-loading@2x.png") 2x
-    );
-  }
 }
 
 #network-status-internet-area:not(.status-loading) .network-status-loading-icon {
@@ -900,6 +903,8 @@ dialog#torPreferences-requestBridge-dialog > hbox {
 #lox-invite-dialog-message-area {
   grid-area: message;
   justify-self: end;
+  display: flex;
+  align-items: center;
 }
 
 #lox-invite-dialog-message-area::after {
@@ -911,19 +916,29 @@ dialog#torPreferences-requestBridge-dialog > hbox {
   color: var(--in-content-error-text-color);
 }
 
-#lox-invite-dialog-error-message:not(.show-error) {
+#lox-invite-dialog-generate-area:not(.show-error) #lox-invite-dialog-error-message {
   display: none;
 }
 
 #lox-invite-dialog-connecting {
   color: var(--text-color-deemphasized);
-  /* TODO: Add spinner ::before */
+  /* Gap with #user-provide-bridge-loading-icon. */
+  margin-inline-start: 0.5em;
 }
 
-#lox-invite-dialog-connecting:not(.show-connecting) {
+#lox-invite-dialog-generate-area:not(.show-connecting) #lox-invite-dialog-connecting {
   display: none;
 }
 
+#lox-invite-dialog-loading-icon {
+  flex: 0 0 auto;
+}
+
+#lox-invite-dialog-generate-area:not(.show-connecting) #lox-invite-dialog-loading-icon {
+  /* Use width:0 to effectively hide, but still occupy vertical space. */
+  width: 0;
+}
+
 #lox-invite-dialog-list-label {
   font-weight: 700;
 }
@@ -1019,6 +1034,8 @@ groupbox#torPreferences-bridges-group textarea {
   flex: 0 0 auto;
   margin-block: 8px 12px;
   align-self: end;
+  display: flex;
+  align-items: center;
 }
 
 #user-provide-bridge-message-area::after {
@@ -1035,19 +1052,29 @@ groupbox#torPreferences-bridges-group textarea {
   color: var(--in-content-error-text-color);
 }
 
-#user-provide-bridge-error-message.not(.show-error) {
+#user-provide-bridge-dialog:not(.show-error) #user-provide-bridge-error-message {
   display: none;
 }
 
 #user-provide-bridge-connecting {
   color: var(--text-color-deemphasized);
-  /* TODO: Add spinner ::before */
+  /* Gap with #user-provide-bridge-loading-icon. */
+  margin-inline-start: 0.5em;
 }
 
-#user-provide-bridge-connecting:not(.show-connecting) {
+#user-provide-bridge-dialog:not(.show-connecting) #user-provide-bridge-connecting {
   display: none;
 }
 
+#user-provide-bridge-loading-icon {
+  flex: 0 0 auto;
+}
+
+#user-provide-bridge-dialog:not(.show-connecting) #user-provide-bridge-loading-icon {
+  /* Use width:0 to effectively hide, but still occupy vertical space. */
+  width: 0;
+}
+
 #user-provide-bridge-result-page {
   flex: 1 1 0;
   min-height: 0;
@@ -1065,6 +1092,11 @@ groupbox#torPreferences-bridges-group textarea {
   margin-block: 8px;
 }
 
+#user-provide-bridge-grid-display:focus-visible {
+  outline: var(--in-content-focus-outline);
+  outline-offset: var(--in-content-focus-outline-offset);
+}
+
 /* Connection settings dialog */
 #torPreferences-connection-dialog label {
   /* Do not wrap the labels. */



View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/00e009df2d38975ffa60f64a6fd8e9d4e60787a7

-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/00e009df2d38975ffa60f64a6fd8e9d4e60787a7
You're receiving this email because of your account on gitlab.torproject.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tor-commits/attachments/20240418/d95b3488/attachment-0001.htm>


More information about the tor-commits mailing list