Bug 1445251 - do not instantiate a11y service if it was already turned off when resetting accessible walker actor. r=pbro
authorYura Zenevich <yura.zenevich@gmail.com>
Thu, 15 Mar 2018 12:54:12 -0400
changeset 408631 f16989641e4954ccf91587ba31780e323092636d
parent 408630 55b8fe1015cb42340e3270bd9ad1f06efa5f7006
child 408632 1085c5fbc6e87055747095ffc3a08456afa9bcd6
push id33649
push userbtara@mozilla.com
push dateSat, 17 Mar 2018 10:29:43 +0000
treeherdermozilla-central@97160a734959 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1445251
milestone61.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 1445251 - do not instantiate a11y service if it was already turned off when resetting accessible walker actor. r=pbro MozReview-Commit-ID: 8H9xeRs4nR6
devtools/server/actors/accessibility.js
--- a/devtools/server/actors/accessibility.js
+++ b/devtools/server/actors/accessibility.js
@@ -106,16 +106,22 @@ register("XULWindowAccessibleHighlighter
  * Helper function that determines if nsIAccessible object is in defunct state.
  *
  * @param  {nsIAccessible}  accessible
  *         object to be tested.
  * @return {Boolean}
  *         True if accessible object is defunct, false otherwise.
  */
 function isDefunct(accessible) {
+  // If accessibility is disabled, safely assume that the accessible object is
+  // now dead.
+  if (!Services.appinfo.accessibilityEnabled) {
+    return true;
+  }
+
   let defunct = false;
 
   try {
     let extState = {};
     accessible.getState({}, extState);
     // extState.value is a bitmask. We are applying bitwise AND to mask out
     // irrelevant states.
     defunct = !!(extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
@@ -397,21 +403,23 @@ const AccessibleWalkerActor = ActorClass
     }
 
     this.cancelPick();
 
     // Clean up accessible actors cache.
     if (this.refMap.size > 0) {
       try {
         if (this.rootDoc) {
-          this.purgeSubtree(this.a11yService.getAccessibleFor(this.rootDoc),
+          this.purgeSubtree(this.getRawAccessibleFor(this.rootDoc),
                             this.rootDoc);
         }
       } catch (e) {
         // Accessibility service might be already destroyed.
+      } finally {
+        this.refMap.clear();
       }
     }
 
     delete this.a11yService;
     this.setA11yServiceGetter();
   },
 
   destroy() {
@@ -441,20 +449,24 @@ const AccessibleWalkerActor = ActorClass
     this.refMap.set(rawAccessible, actor);
 
     return actor;
   },
 
   /**
    * Clean up accessible actors cache for a given accessible's subtree.
    *
-   * @param  {nsIAccessible} rawAccessible
+   * @param  {null|nsIAccessible} rawAccessible
    * @param  {null|Object}   rawNode
    */
   purgeSubtree(rawAccessible, rawNode) {
+    if (!rawAccessible) {
+      return;
+    }
+
     let actor = this.getRef(rawAccessible);
     if (actor && rawAccessible && !actor.isDefunct) {
       for (let child = rawAccessible.firstChild; child; child = child.nextSibling) {
         this.purgeSubtree(child);
       }
     }
 
     this.refMap.delete(rawAccessible);
@@ -485,34 +497,58 @@ const AccessibleWalkerActor = ActorClass
    * @return {Promise}
    */
   getDocument() {
     if (!this.rootDoc || !this.rootDoc.documentElement) {
       return this.once("document-ready").then(docAcc => this.addRef(docAcc));
     }
 
     if (isXUL(this.rootWin)) {
-      let doc = this.addRef(this.a11yService.getAccessibleFor(this.rootDoc));
+      let doc = this.addRef(this.getRawAccessibleFor(this.rootDoc));
       return Promise.resolve(doc);
     }
 
-    let doc = this.a11yService.getAccessibleFor(this.rootDoc);
+    let doc = this.getRawAccessibleFor(this.rootDoc);
     let state = {};
     doc.getState(state, {});
     if (state.value & Ci.nsIAccessibleStates.STATE_BUSY) {
       return this.once("document-ready").then(docAcc => this.addRef(docAcc));
     }
 
     return Promise.resolve(this.addRef(doc));
   },
 
+  /**
+   * Get an accessible actor for a domnode actor.
+   * @param  {Object} domNode
+   *         domnode actor for which accessible actor is being created.
+   * @return {Promse}
+   *         A promise that resolves when accessible actor is created for a
+   *         domnode actor.
+   */
   getAccessibleFor(domNode) {
     // We need to make sure that the document is loaded processed by a11y first.
     return this.getDocument().then(() =>
-      this.addRef(this.a11yService.getAccessibleFor(domNode.rawNode)));
+      this.addRef(this.getRawAccessibleFor(domNode.rawNode)));
+  },
+
+  /**
+   * Get a raw accessible object for a raw node.
+   * @param  {DOMNode} rawNode
+   *         Raw node for which accessible object is being retrieved.
+   * @return {nsIAccessible}
+   *         Accessible object for a given DOMNode.
+   */
+  getRawAccessibleFor(rawNode) {
+    // Accessible can only be retrieved iff accessibility service is enabled.
+    if (!Services.appinfo.accessibilityEnabled) {
+      return null;
+    }
+
+    return this.a11yService.getAccessibleFor(rawNode);
   },
 
   async getAncestry(accessible) {
     if (accessible.indexInParent === -1) {
       return [];
     }
     const doc = await this.getDocument();
     let ancestry = [];
@@ -826,17 +862,17 @@ const AccessibleWalkerActor = ActorClass
   async _findAndAttachAccessible(event) {
     let target = event.originalTarget || event.target;
     let rawAccessible;
     // Find a first accessible object in the target's ancestry, including
     // target. Note: not all DOM nodes have corresponding accessible objects
     // (for example, a <DIV> element that is used as a container for other
     // things) thus we need to find one that does.
     while (!rawAccessible && target) {
-      rawAccessible = this.a11yService.getAccessibleFor(target);
+      rawAccessible = this.getRawAccessibleFor(target);
       target = target.parentNode;
     }
     // If raw accessible object is defunct or detached, no need to cache it and
     // its ancestry.
     if (!rawAccessible || isDefunct(rawAccessible) || rawAccessible.indexInParent < 0) {
       return null;
     }