Bug 1312816 - Preemptively start a11y in chrome when raisesAccessibilityExceptions marionette capability is set. r=ato
authorYura Zenevich <yzenevich@mozilla.com>
Mon, 07 Nov 2016 12:15:08 -0500
changeset 321422 4e6109ba565cc525681a1d3a4bfd343de1830b37
parent 321421 cd40a468ed9c1da2403dd54d2c472bcf14d21118
child 321423 f88d94ae4967e2bd71ce2a7de16af7d8b9129291
child 321458 e388e453977c7bf257d87e97ff003c43f44b7c0f
push id30927
push userphilringnalda@gmail.com
push dateTue, 08 Nov 2016 04:37:38 +0000
treeherdermozilla-central@f88d94ae4967 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato
bugs1312816
milestone52.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 1312816 - Preemptively start a11y in chrome when raisesAccessibilityExceptions marionette capability is set. r=ato MozReview-Commit-ID: KshEWHvz8SD
testing/marionette/accessibility.js
testing/marionette/driver.js
--- a/testing/marionette/accessibility.js
+++ b/testing/marionette/accessibility.js
@@ -4,44 +4,57 @@
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 
+const logger = Log.repository.getLogger("Marionette");
+
 Cu.import("chrome://marionette/content/error.js");
 
 XPCOMUtils.defineLazyModuleGetter(
     this, "setInterval", "resource://gre/modules/Timer.jsm");
 XPCOMUtils.defineLazyModuleGetter(
     this, "clearInterval", "resource://gre/modules/Timer.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "service",
-    () => Cc["@mozilla.org/accessibilityService;1"].getService(Ci.nsIAccessibilityService));
+XPCOMUtils.defineLazyGetter(this, "service", () => {
+  let service;
+  try {
+    service = Cc["@mozilla.org/accessibilityService;1"].getService(
+      Ci.nsIAccessibilityService);
+  } catch (e) {
+    logger.warn("Accessibility module is not present");
+  } finally {
+    return service;
+  }
+});
 
 this.EXPORTED_SYMBOLS = ["accessibility"];
 
-const logger = Log.repository.getLogger("Marionette");
-
 /**
  * Number of attempts to get an accessible object for an element.
  * We attempt more than once because accessible tree can be out of sync
  * with the DOM tree for a short period of time.
  */
 const GET_ACCESSIBLE_ATTEMPTS = 100;
 
 /**
  * An interval between attempts to retrieve an accessible object for an
  * element.
  */
 const GET_ACCESSIBLE_ATTEMPT_INTERVAL = 10;
 
-this.accessibility = {};
+this.accessibility = {
+  get service() {
+    return service;
+  }
+};
 
 /**
  * Accessible states used to check element"s state from the accessiblity API
  * perspective.
  * Note: if gecko is built with --disable-accessibility, the interfaces are not
  * defined. This is why we use getters instead to be able to use these
  * statically.
  */
@@ -118,40 +131,42 @@ accessibility.Checks = class {
    * Get an accessible object for an element.
    *
    * @param {DOMElement|XULElement} element
    *     Element to get the accessible object for.
    * @param {boolean=} mustHaveAccessible
    *     Flag indicating that the element must have an accessible object.
    *     Defaults to not require this.
    *
-   * @return {nsIAccessible}
-   *     Accessibility object for the given element.
+   * @return {Promise: nsIAccessible}
+   *     Promise with an accessibility object for the given element.
    */
   getAccessible(element, mustHaveAccessible = false) {
-    return new Promise((resolve, reject) => {
-      let acc = service.getAccessibleFor(element);
+    if (!this.strict) {
+      return Promise.resolve();
+    }
 
-      // if accessible object is found, return it;
-      // if it is not required, also resolve
-      if (acc || !mustHaveAccessible) {
-        resolve(acc);
+    return new Promise((resolve, reject) => {
+      if (!accessibility.service) {
+        reject();
+        return;
+      }
 
-      // if we must have an accessible but are strict,
-      // reject now and avoid polling for an accessible object
-      } else if (mustHaveAccessible && !this.strict) {
-        reject();
-
-      // if we require an accessible object, we need to poll for it
-      // because accessible tree might be
-      // out of sync with DOM tree for a short time
+      let acc = accessibility.service.getAccessibleFor(element);
+      if (acc || !mustHaveAccessible) {
+        // if accessible object is found, return it;
+        // if it is not required, also resolve
+        resolve(acc);
       } else {
+        // if we require an accessible object, we need to poll for it
+        // because accessible tree might be
+        // out of sync with DOM tree for a short time
         let attempts = GET_ACCESSIBLE_ATTEMPTS;
         let intervalId = setInterval(() => {
-          let acc = service.getAccessibleFor(element);
+          let acc = accessibility.service.getAccessibleFor(element);
           if (acc || --attempts <= 0) {
             clearInterval(intervalId);
             if (acc) {
               resolve(acc);
             } else {
               reject();
             }
           }
@@ -169,17 +184,17 @@ accessibility.Checks = class {
    *     Accessible object.
    *
    * @return {boolean}
    *     True if an actionable role is found on the accessible, false
    *     otherwise.
    */
   isActionableRole(accessible) {
     return accessibility.ActionableRoles.has(
-        service.getStringRole(accessible.role));
+        accessibility.service.getStringRole(accessible.role));
   }
 
   /**
    * Test if an accessible has at least one action that it supports.
    *
    * @param {nsIAccessible} accessible
    *     Accessible object.
    *
@@ -407,22 +422,20 @@ accessibility.Checks = class {
    * @param {string} message
    * @param {DOMElement|XULElement} element
    *     Element that caused an error.
    *
    * @throws ElementNotAccessibleError
    *     If |strict| is true.
    */
   error(message, element) {
-    if (!message) {
+    if (!message || !this.strict) {
       return;
     }
     if (element) {
       let {id, tagName, className} = element;
       message += `: id: ${id}, tagName: ${tagName}, className: ${className}`;
     }
-    if (this.strict) {
-      throw new ElementNotAccessibleError(message);
-    }
-    logger.debug(message);
+
+    throw new ElementNotAccessibleError(message);
   }
 
 };
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -12,16 +12,17 @@ var loader = Cc["@mozilla.org/moz/jssubs
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
     this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
 
+Cu.import("chrome://marionette/content/accessibility.js");
 Cu.import("chrome://marionette/content/atom.js");
 Cu.import("chrome://marionette/content/browser.js");
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/evaluate.js");
 Cu.import("chrome://marionette/content/event.js");
 Cu.import("chrome://marionette/content/interaction.js");
 Cu.import("chrome://marionette/content/legacyaction.js");
@@ -479,16 +480,24 @@ GeckoDriver.prototype.newSession = funct
     throw new SessionNotCreatedError("Maximum number of active sessions.")
   }
   this.sessionId = cmd.parameters.sessionId ||
       cmd.parameters.session_id ||
       element.generateUUID();
 
   this.newSessionCommandId = cmd.id;
   this.setSessionCapabilities(cmd.parameters.capabilities);
+  // If we are testing accessibility with marionette, start a11y service in
+  // chrome first. This will ensure that we do not have any content-only
+  // services hanging around.
+  if (this.sessionCapabilities.raisesAccessibilityExceptions &&
+      accessibility.service) {
+    logger.info("Preemptively starting accessibility service in Chrome");
+  }
+
   this.scriptTimeout = 10000;
 
   let registerBrowsers = this.registerPromise();
   let browserListening = this.listeningPromise();
 
   let waitForWindow = function() {
     let win = this.getCurrentWindow();
     if (!win) {