Bug 1518054: Move a11y focus event firing from richlistitem.current to richlistbox.currentItem to fix the All Downloads view. r=paolo
authorJames Teh <jteh@mozilla.com>
Tue, 22 Jan 2019 02:08:26 +0000
changeset 514765 c46ba0dfc91c8f734dc36f8c6faa5c9be0638a21
parent 514764 ada22b635f8d13adb7725c2fcac51d0e12e3ed59
child 514766 989238f82c8949cbdb9a664d93f9313c0b5ab8e2
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspaolo
bugs1518054, 1492326
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1518054: Move a11y focus event firing from richlistitem.current to richlistbox.currentItem to fix the All Downloads view. r=paolo The All Downloads view removes and re-adds its richlistbox for performance reasons. However, after bug 1492326, this causes the richlistitem's .current property to be assigned before its binding is applied. Since the .current property fires a11y focus events, this means this property is overridden and thus the events never get fired for that item. To fix this, move a11y focus event firing into richlistbox.currentItem. Differential Revision: https://phabricator.services.mozilla.com/D16932
browser/base/content/urlbarBindings.xml
toolkit/content/widgets/richlistbox.js
toolkit/content/widgets/richlistbox.xml
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1491,17 +1491,18 @@ file, You can obtain one at http://mozil
               this.setAttribute("usertyping", "true");
             } else {
               this.removeAttribute("usertyping");
             }
             // If the popup already had accessibility focus, bring it back to
             // the input, since the user is editing.
             if (!this.popup.richlistbox.suppressMenuItemEvent &&
                 this.popup.richlistbox.currentItem) {
-              this.popup.richlistbox.currentItem._fireEvent("DOMMenuItemInactive");
+              this.popup.richlistbox._fireEvent(
+                this.popup.richlistbox.currentItem, "DOMMenuItemInactive");
             }
             // The user is typing, so don't give accessibility focus to the
             // popup, even if an item gets automatically selected.
             this.popup.richlistbox.suppressMenuItemEvent = true;
             // Only wait for a result when we are sure to get one.  In some
             // cases, like when pasting the same exact text, we may not fire
             // a new search and we won't get a result.
             this._onInputHandledText = this.mController.handleText();
@@ -2221,17 +2222,18 @@ file, You can obtain one at http://mozil
             window.windowUtils.getBoundsWithoutFlushing(document.getElementById("nav-bar")).bottom -
             window.windowUtils.getBoundsWithoutFlushing(aInput).bottom);
 
           if (!this.richlistbox.suppressMenuItemEvent && this.richlistbox.currentItem) {
             // The richlistbox fired a DOMMenuItemActive for accessibility,
             // but because the popup isn't open yet, accessibility will ignore
             // it. Therefore, fire it again once the popup opens.
             this.addEventListener("popupshown", () => {
-              this.richlistbox.currentItem._fireEvent("DOMMenuItemActive");
+              this.richlistbox._fireEvent(this.richlistbox.currentItem,
+                "DOMMenuItemActive");
             }, {once: true});
           }
 
           this.openPopup(aElement, "after_start", 0, yOffset, false, false);
 
           // Do this immediately after we've requested the popup to open. This
           // will cause sync reflows but prevents flickering.
           if (needsHandleOverUnderflow) {
--- a/toolkit/content/widgets/richlistbox.js
+++ b/toolkit/content/widgets/richlistbox.js
@@ -107,17 +107,17 @@ MozElements.RichListBox = class RichList
       if (this.getRowCount() > 0) {
         if (this.currentIndex == -1) {
           this.currentIndex = this.getIndexOfFirstVisibleRow();
           let currentItem = this.getItemAtIndex(this.currentIndex);
           if (currentItem) {
             this.selectItem(currentItem);
           }
         } else {
-          this.currentItem._fireEvent("DOMMenuItemActive");
+          this._fireEvent(this.currentItem, "DOMMenuItemActive");
         }
       }
       this._lastKeyTime = 0;
     });
 
     this.addEventListener("click", event => {
       // clicking into nothing should unselect
       if (event.originalTarget == this) {
@@ -212,21 +212,30 @@ MozElements.RichListBox = class RichList
   // nsIDOMXULSelectControlElement
   set currentItem(val) {
     if (this._currentItem == val) {
       return val;
     }
 
     if (this._currentItem) {
       this._currentItem.current = false;
+      if (!val && !this.suppressMenuItemEvent) {
+        // An item is losing focus and there is no new item to focus.
+        // Notify a11y that there is no focused item.
+        this._fireEvent(this._currentItem, "DOMMenuItemInactive");
+      }
     }
     this._currentItem = val;
 
     if (val) {
       val.current = true;
+      if (!this.suppressMenuItemEvent) {
+        // Notify a11y that this item got focus.
+        this._fireEvent(val, "DOMMenuItemActive");
+      }
     }
 
     return val;
   }
   get currentItem() {
     return this._currentItem;
   }
 
@@ -810,16 +819,22 @@ MozElements.RichListBox = class RichList
 
   /**
    * For backwards-compatibility and for convenience.
    * Use ensureElementIsVisible instead
    */
   ensureSelectedElementIsVisible() {
     return this.ensureElementIsVisible(this.selectedItem);
   }
+
+  _fireEvent(aTarget, aName) {
+    let event = document.createEvent("Events");
+    event.initEvent(aName, true, true);
+    aTarget.dispatchEvent(event);
+  }
 };
 
 MozXULElement.implementCustomInterface(MozElements.RichListBox, [
   Ci.nsIDOMXULSelectControlElement,
   Ci.nsIDOMXULMultiSelectControlElement,
 ]);
 
 customElements.define("richlistbox", MozElements.RichListBox);
--- a/toolkit/content/widgets/richlistbox.xml
+++ b/toolkit/content/widgets/richlistbox.xml
@@ -100,36 +100,19 @@
       </property>
 
       <property name="current" onget="return this.getAttribute('current') == 'true';">
         <setter><![CDATA[
           if (val)
             this.setAttribute("current", "true");
           else
             this.removeAttribute("current");
-
-          let control = this.control;
-          if (!control || !control.suppressMenuItemEvent) {
-            this._fireEvent(val ? "DOMMenuItemActive" : "DOMMenuItemInactive");
-          }
-
           return val;
         ]]></setter>
       </property>
-
-      <method name="_fireEvent">
-        <parameter name="name"/>
-        <body>
-        <![CDATA[
-          var event = document.createEvent("Events");
-          event.initEvent(name, true, true);
-          this.dispatchEvent(event);
-        ]]>
-        </body>
-      </method>
     </implementation>
 
     <handlers>
       <!-- If there is no modifier key, we select on mousedown, not
            click, so that drags work correctly. -->
       <handler event="mousedown">
         <![CDATA[
           var control = this.control;