Bug 1312816 - Preemptively start a11y in chrome when raisesAccessibilityExceptions marionette capability is set. r=ato
MozReview-Commit-ID: KshEWHvz8SD
--- 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) {