Bug 1551515 - Fix Library button focused/unfocused when clicking on a abuse report panel's radio button. r=mstriemer, a=jcristau
authorLuca Greco <lgreco@mozilla.com>
Mon, 24 Jun 2019 17:18:23 +0000
changeset 537105 2b7fc852c4c72321cebc915201b419ffc388bdd9
parent 537104 1c3f96c23a841c7a6e368d73b9ee5b9573d943a3
child 537106 565911b99d854c1cc735862ea14c99f829d5b907
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstriemer, jcristau
bugs1551515
milestone68.0
Bug 1551515 - Fix Library button focused/unfocused when clicking on a abuse report panel's radio button. r=mstriemer, a=jcristau Differential Revision: https://phabricator.services.mozilla.com/D31600
toolkit/mozapps/extensions/content/abuse-report-frame.html
toolkit/mozapps/extensions/content/abuse-report-frame.js
toolkit/mozapps/extensions/content/abuse-report-panel.js
--- a/toolkit/mozapps/extensions/content/abuse-report-frame.html
+++ b/toolkit/mozapps/extensions/content/abuse-report-frame.html
@@ -7,17 +7,17 @@
 
     <link rel="localization" href="branding/brand.ftl">
     <link rel="localization" href="toolkit/about/aboutAddons.ftl">
     <link rel="localization" href="toolkit/about/abuseReports.ftl">
 
     <script src="chrome://mozapps/content/extensions/abuse-report-panel.js"></script>
   </head>
 
-  <body tabindex="0">
+  <body>
     <!-- WebComponents Templates -->
     <template id="tmpl-abuse-report">
       <div class="modal-overlay-outer"></div>
       <div class="modal-panel-container">
         <form class="card addon-abuse-report" onsubmit="return false;">
           <div class="abuse-report-header">
             <img class="card-heading-icon addon-icon"/>
             <div class="card-contents">
--- a/toolkit/mozapps/extensions/content/abuse-report-frame.js
+++ b/toolkit/mozapps/extensions/content/abuse-report-frame.js
@@ -152,17 +152,17 @@
           abuseReport.addEventListener("abuse-report:updated", this, {once: true});
           abuseReport.addEventListener("abuse-report:submit", this, {once: true});
           abuseReport.addEventListener("abuse-report:cancel", this, {once: true});
           abuseReport.setAbuseReport(report);
           // Hide the content of the underlying about:addons page from
           // screen readers.
           this.aboutAddonsContent.setAttribute("aria-hidden", true);
           // Move the focus to the embedded window.
-          fm.moveFocus(abuseReport.ownerGlobal, null, fm.MOVEFOCUS_ROOT, fm.FLAG_BYKEY);
+          this.focus();
           this.dispatchEvent(new CustomEvent("abuse-report:frame-shown"));
         });
       } else {
         this.hidden = true;
         this.removeAttribute("addon-id");
         this.removeAttribute("report-entry-point");
 
         // Make the content of the underlying about:addons page visible
--- a/toolkit/mozapps/extensions/content/abuse-report-panel.js
+++ b/toolkit/mozapps/extensions/content/abuse-report-panel.js
@@ -399,69 +399,53 @@ class AbuseReport extends HTMLElement {
       _linkAddonAuthor: ".abuse-report-header .addon-author-box a.author",
     });
   }
 
   connectedCallback() {
     this.render();
 
     this.addEventListener("click", this);
-    // Start listening to focus events (to adjust the focused
-    // element during keyboard navigation).
-    document.addEventListener("focus", this, true);
+
     // Start listening to keydown events (to close the modal
-    // when Escape has been pressed).
+    // when Escape has been pressed and to handling the keyboard
+    // navigation).
     document.addEventListener("keydown", this);
   }
 
   disconnectedCallback() {
     this.textContent = "";
     this.removeEventListener("click", this);
-    document.removeEventListener("focus", this, true);
     document.removeEventListener("keydown", this);
   }
 
   handleEvent(evt) {
     if (!this.isConnected || !this.addon) {
       return;
     }
 
     switch (evt.type) {
-      case "focus":
-        if (evt.target === document.body) {
-          // Return the focus to the Firefox UI when the body has been focused
-          // (as it is only reached when navigating back from the last focusable
-          // objects in the abuse report panel).
-          const chromeWin = window.windowRoot.ownerGlobal;
-          // Top level browser's previous sibling.
-          const {
-            previousElementSibling,
-          } = window.parent.docShell.chromeEventHandler;
-          Services.focus.moveFocus(
-            chromeWin, previousElementSibling,
-            Services.focus.MOVE_BACKWARD, Services.focus.FLAG_BYKEY);
-          return;
-        } else if (this.contains(evt.target)) {
-          return;
-        }
-        this.focus();
-        break;
       case "keydown":
         if (evt.key === "Escape") {
           // Prevent Esc to close the panel if the textarea is
           // empty.
           if (this.message && !this._submitPanel.hidden) {
             return;
           }
           this.cancel();
         }
+        this.handleKeyboardNavigation(evt);
         break;
       case "click":
         if (evt.target === this._iconClose ||
             evt.target === this._btnCancel) {
+          // NOTE: clear the focus on the clicked element to ensure that
+          // -moz-focusring pseudo class is not still set on the element
+          // when the panel is going to be shown again (See Bug 1560949).
+          evt.target.blur();
           this.cancel();
         }
         if (evt.target === this._btnNext) {
           this.switchToSubmitMode();
         }
         if (evt.target === this._btnGoBack) {
           this.switchToListMode();
         }
@@ -478,16 +462,51 @@ class AbuseReport extends HTMLElement {
               relatedToCurrent: true,
             });
           }
         }
         break;
     }
   }
 
+  handleKeyboardNavigation(evt) {
+    if (evt.keyCode !== evt.DOM_VK_TAB ||
+      evt.altKey || evt.controlKey || evt.metaKey) {
+      return;
+    }
+
+    const fm = Services.focus;
+    const backward = evt.shiftKey;
+
+    const isFirstFocusableElement = el => {
+      // Also consider the document body as a valid first focusable element.
+      if (el === document.body) {
+        return true;
+      }
+      // XXXrpl unfortunately there is no way to get the first focusable element
+      // without asking the focus manager to move focus to it (similar strategy
+      // is also being used in about:prefereces subdialog.js).
+      const rv = el == fm.moveFocus(window, null, fm.MOVEFOCUS_FIRST, 0);
+      fm.setFocus(el, 0);
+      return rv;
+    };
+
+    // If the focus is exiting the panel while navigating
+    // backward, focus the previous element sibling on the
+    // Firefox UI.
+    if (backward && isFirstFocusableElement(evt.target)) {
+      evt.preventDefault();
+      evt.stopImmediatePropagation();
+      const chromeWin = window.windowRoot.ownerGlobal;
+      Services.focus.moveFocus(
+        chromeWin, null,
+        Services.MOVEFOCUS_BACKWARD, Services.focus.FLAG_BYKEY);
+    }
+  }
+
   render() {
     this.textContent = "";
     this.appendChild(document.importNode(this.template.content, true));
   }
 
   async update() {
     if (!this.addon) {
       return;