Bug 527003 - adding tests for accessibility service shutdown. r=surkov
authorYura Zenevich <yzenevich@mozilla.com>
Tue, 23 Aug 2016 14:27:23 -0400
changeset 352284 40dc3b9f167746940d1e00418ca8500537916061
parent 352283 dfc92970fcef54eac33015cd93a4f49366325680
child 352285 502e48c4fb6a5b83f0df39e9a9e0971d3dc6d1f9
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov
bugs527003
milestone51.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 527003 - adding tests for accessibility service shutdown. r=surkov MozReview-Commit-ID: 4c4S9CH7JoB
accessible/moz.build
accessible/tests/browser/.eslintrc
accessible/tests/browser/browser.ini
accessible/tests/browser/browser_caching_attributes.js
accessible/tests/browser/browser_caching_description.js
accessible/tests/browser/browser_caching_name.js
accessible/tests/browser/browser_caching_relations.js
accessible/tests/browser/browser_caching_states.js
accessible/tests/browser/browser_caching_value.js
accessible/tests/browser/browser_events_caretmove.js
accessible/tests/browser/browser_events_hide.js
accessible/tests/browser/browser_events_show.js
accessible/tests/browser/browser_events_statechange.js
accessible/tests/browser/browser_events_textchange.js
accessible/tests/browser/browser_shutdown_multi_reference.js
accessible/tests/browser/browser_shutdown_scope_lifecycle.js
accessible/tests/browser/browser_shutdown_start_restart.js
accessible/tests/browser/browser_treeupdate_ariadialog.js
accessible/tests/browser/browser_treeupdate_ariaowns.js
accessible/tests/browser/browser_treeupdate_canvas.js
accessible/tests/browser/browser_treeupdate_cssoverflow.js
accessible/tests/browser/browser_treeupdate_doc.js
accessible/tests/browser/browser_treeupdate_gencontent.js
accessible/tests/browser/browser_treeupdate_hidden.js
accessible/tests/browser/browser_treeupdate_imagemap.js
accessible/tests/browser/browser_treeupdate_list.js
accessible/tests/browser/browser_treeupdate_list_editabledoc.js
accessible/tests/browser/browser_treeupdate_listener.js
accessible/tests/browser/browser_treeupdate_optgroup.js
accessible/tests/browser/browser_treeupdate_removal.js
accessible/tests/browser/browser_treeupdate_table.js
accessible/tests/browser/browser_treeupdate_textleaf.js
accessible/tests/browser/browser_treeupdate_visibility.js
accessible/tests/browser/browser_treeupdate_whitespace.js
accessible/tests/browser/doc_treeupdate_ariadialog.html
accessible/tests/browser/doc_treeupdate_ariaowns.html
accessible/tests/browser/doc_treeupdate_imagemap.html
accessible/tests/browser/doc_treeupdate_removal.xhtml
accessible/tests/browser/doc_treeupdate_visibility.html
accessible/tests/browser/doc_treeupdate_whitespace.html
accessible/tests/browser/e10s/browser.ini
accessible/tests/browser/e10s/browser_caching_attributes.js
accessible/tests/browser/e10s/browser_caching_description.js
accessible/tests/browser/e10s/browser_caching_name.js
accessible/tests/browser/e10s/browser_caching_relations.js
accessible/tests/browser/e10s/browser_caching_states.js
accessible/tests/browser/e10s/browser_caching_value.js
accessible/tests/browser/e10s/browser_events_caretmove.js
accessible/tests/browser/e10s/browser_events_hide.js
accessible/tests/browser/e10s/browser_events_show.js
accessible/tests/browser/e10s/browser_events_statechange.js
accessible/tests/browser/e10s/browser_events_textchange.js
accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
accessible/tests/browser/e10s/browser_treeupdate_canvas.js
accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
accessible/tests/browser/e10s/browser_treeupdate_doc.js
accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
accessible/tests/browser/e10s/browser_treeupdate_hidden.js
accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
accessible/tests/browser/e10s/browser_treeupdate_list.js
accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
accessible/tests/browser/e10s/browser_treeupdate_listener.js
accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
accessible/tests/browser/e10s/browser_treeupdate_removal.js
accessible/tests/browser/e10s/browser_treeupdate_table.js
accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
accessible/tests/browser/e10s/browser_treeupdate_visibility.js
accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
accessible/tests/browser/e10s/doc_treeupdate_visibility.html
accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
accessible/tests/browser/e10s/events.js
accessible/tests/browser/e10s/head.js
accessible/tests/browser/events.js
accessible/tests/browser/head.js
accessible/tests/browser/shared-head.js
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -25,9 +25,12 @@ DIRS += [ 'aom',
           'xpcom'
 ]
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['xul']
 
 TEST_DIRS += ['tests/mochitest']
 
-BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
+BROWSER_CHROME_MANIFESTS += [
+  'tests/browser/browser.ini',
+  'tests/browser/e10s/browser.ini'
+]
--- a/accessible/tests/browser/.eslintrc
+++ b/accessible/tests/browser/.eslintrc
@@ -2,46 +2,56 @@
   "extends": [
     "../../../testing/mochitest/browser.eslintrc"
   ],
   // All globals made available in the test environment.
   "globals": {
     // Content scripts have global 'content' object
     "content": true,
 
+    "add_task": true,
+
     // Defined in accessible/tests/mochitest/ common.js, name.js, states.js
     "prettyName": true,
     "statesToString": true,
     "eventTypeToString": true,
     "testAttrs": true,
     "testAbsentAttrs": true,
     "testName": true,
     "testDescr": true,
     "testStates": true,
     "testRelation": true,
     "testValue": true,
     "testAccessibleTree": true,
     "isAccessible": true,
     "getAccessibleDOMNodeID": true,
 
-    // Defined for all accessibility browser tests.
+    // Defined for all top level accessibility browser tests.
+    "setE10sPrefs": true,
+    "unsetE10sPrefs": true,
+    "initPromise": true,
+    "shutdownPromise": true,
+    "forceGC": true,
+
+    // Defined for all e10s accessibility browser tests.
     "addAccessibleTask": true,
     "BrowserTestUtils": true,
     "ContentTask": true,
     "gBrowser": true,
     "isDefunct": true,
     "loadScripts": true,
+    "loadFrameScripts": true,
     "Logger": true,
     "MOCHITESTS_DIR": true,
     "waitForEvent": true,
     "waitForMultipleEvents": true,
     "invokeSetAttribute": true,
     "invokeSetStyle": true,
     "invokeFocus": true,
-    "findAccessibleChildByID": true
+    "findAccessibleChildByID": true,
   },
   "rules": {
     "mozilla/mark-test-function-used": 1,
     "mozilla/no-aArgs": 1,
     "mozilla/no-cpows-in-tests": 1,
     "mozilla/reject-importGlobalProperties": 1,
     "mozilla/var-only-at-top-level": 1,
 
@@ -63,17 +73,17 @@
     "func-style": 0,
     "generator-star": 0,
     "global-strict": 0,
     "handle-callback-err": [2, "er"],
     "indent": [2, 2, {"SwitchCase": 1}],
     "key-spacing": [2, {"beforeColon": false, "afterColon": true}],
     "linebreak-style": 0,
     "max-depth": 0,
-    "max-nested-callbacks": [2, 3],
+    "max-nested-callbacks": [2, 4],
     "max-params": 0,
     "max-statements": 0,
     "new-cap": [2, {"capIsNew": false}],
     "new-parens": 2,
     "no-array-constructor": 2,
     "no-bitwise": 0,
     "no-caller": 2,
     "no-catch-shadow": 2,
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -1,52 +1,9 @@
 [DEFAULT]
-skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
-support-files =
-  events.js
-  head.js
-  doc_treeupdate_ariadialog.html
-  doc_treeupdate_ariaowns.html
-  doc_treeupdate_imagemap.html
-  doc_treeupdate_removal.xhtml
-  doc_treeupdate_visibility.html
-  doc_treeupdate_whitespace.html
-  !/accessible/tests/mochitest/*.js
-  !/accessible/tests/mochitest/letters.gif
-  !/accessible/tests/mochitest/moz.png
-
-# Caching tests
-[browser_caching_attributes.js]
-[browser_caching_description.js]
-[browser_caching_name.js]
-skip-if = e10s
-[browser_caching_relations.js]
-[browser_caching_states.js]
-[browser_caching_value.js]
 
-# Events tests
-[browser_events_caretmove.js]
-[browser_events_hide.js]
-[browser_events_show.js]
-[browser_events_statechange.js]
-[browser_events_textchange.js]
+support-files =
+  head.js
+  shared-head.js
 
-# Tree update tests
-[browser_treeupdate_ariadialog.js]
-[browser_treeupdate_ariaowns.js]
-skip-if = e10s
-[browser_treeupdate_canvas.js]
-[browser_treeupdate_cssoverflow.js]
-[browser_treeupdate_doc.js]
-[browser_treeupdate_gencontent.js]
-[browser_treeupdate_hidden.js]
-[browser_treeupdate_imagemap.js]
-skip-if = e10s
-[browser_treeupdate_list.js]
-[browser_treeupdate_list_editabledoc.js]
-[browser_treeupdate_listener.js]
-[browser_treeupdate_optgroup.js]
-[browser_treeupdate_removal.js]
-[browser_treeupdate_table.js]
-[browser_treeupdate_textleaf.js]
-[browser_treeupdate_visibility.js]
-[browser_treeupdate_whitespace.js]
-skip-if = true # Failing due to incorrect index of test container children on document load.
+[browser_shutdown_multi_reference.js]
+[browser_shutdown_scope_lifecycle.js]
+[browser_shutdown_start_restart.js]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_multi_reference.js
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+  info('Creating a service');
+  // Create a11y service.
+  let a11yInit = initPromise();
+  let accService1 = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  yield a11yInit;
+  ok(accService1, 'Service initialized');
+
+  // Add another reference to a11y service. This will not trigger
+  // 'a11y-init-or-shutdown' event
+  let accService2 = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  ok(accService2, 'Service initialized');
+
+  info('Removing all service references');
+  let canShutdown = false;
+  // This promise will resolve only if canShutdonw flag is set to true. If
+  // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut
+  // down, the promise will reject.
+  let a11yShutdown = new Promise((resolve, reject) =>
+    shutdownPromise().then(flag => canShutdown ?
+      resolve() : reject('Accessible service was shut down incorrectly')));
+  // Remove first a11y service reference.
+  accService1 = null;
+  ok(!accService1, 'Service is removed');
+  // Force garbage collection that should not trigger shutdown because there is
+  // another reference.
+  forceGC();
+
+  // Have some breathing room when removing a11y service references.
+  yield new Promise(resolve => executeSoon(resolve));
+
+  // Now allow a11y service to shutdown.
+  canShutdown = true;
+  // Remove last a11y service reference.
+  accService2 = null;
+  ok(!accService2, 'Service is removed');
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yShutdown;
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_scope_lifecycle.js
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* testScopeLifecycle() {
+  // Create a11y service inside of the function scope. Its reference should be
+  // released once the anonimous function is called.
+  let a11yInitThenShutdown = initPromise().then(shutdownPromise);
+
+  (function() {
+    let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+      Ci.nsIAccessibilityService);
+    ok(accService, 'Service initialized');
+  })();
+
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yInitThenShutdown;
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_start_restart.js
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* testStartAndRestart() {
+  info('Creating a service');
+  // Create a11y service.
+  let a11yInit = initPromise();
+  let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  yield a11yInit;
+  ok(accService, 'Service initialized');
+
+  info('Removing a service');
+  // Remove the only reference to an a11y service.
+  let a11yShutdown = shutdownPromise();
+  accService = null;
+  ok(!accService, 'Service is removed');
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yShutdown;
+
+  info('Recreating a service');
+  // Re-create a11y service.
+  a11yInit = initPromise();
+  accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  yield a11yInit;
+  ok(accService, 'Service initialized again');
+
+  info('Removing a service again');
+  // Remove the only reference to an a11y service again.
+  a11yShutdown = shutdownPromise();
+  accService = null;
+  ok(!accService, 'Service is removed again');
+  // Force garbage collection that should trigger shutdown.
+  forceGC();
+  yield a11yShutdown;
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -0,0 +1,53 @@
+[DEFAULT]
+skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
+support-files =
+  events.js
+  head.js
+  doc_treeupdate_ariadialog.html
+  doc_treeupdate_ariaowns.html
+  doc_treeupdate_imagemap.html
+  doc_treeupdate_removal.xhtml
+  doc_treeupdate_visibility.html
+  doc_treeupdate_whitespace.html
+  !/accessible/tests/browser/shared-head.js
+  !/accessible/tests/mochitest/*.js
+  !/accessible/tests/mochitest/letters.gif
+  !/accessible/tests/mochitest/moz.png
+
+# Caching tests
+[browser_caching_attributes.js]
+[browser_caching_description.js]
+[browser_caching_name.js]
+skip-if = e10s
+[browser_caching_relations.js]
+[browser_caching_states.js]
+[browser_caching_value.js]
+
+# Events tests
+[browser_events_caretmove.js]
+[browser_events_hide.js]
+[browser_events_show.js]
+[browser_events_statechange.js]
+[browser_events_textchange.js]
+
+# Tree update tests
+[browser_treeupdate_ariadialog.js]
+[browser_treeupdate_ariaowns.js]
+skip-if = e10s
+[browser_treeupdate_canvas.js]
+[browser_treeupdate_cssoverflow.js]
+[browser_treeupdate_doc.js]
+[browser_treeupdate_gencontent.js]
+[browser_treeupdate_hidden.js]
+[browser_treeupdate_imagemap.js]
+skip-if = e10s
+[browser_treeupdate_list.js]
+[browser_treeupdate_list_editabledoc.js]
+[browser_treeupdate_listener.js]
+[browser_treeupdate_optgroup.js]
+[browser_treeupdate_removal.js]
+[browser_treeupdate_table.js]
+[browser_treeupdate_textleaf.js]
+[browser_treeupdate_visibility.js]
+[browser_treeupdate_whitespace.js]
+skip-if = true # Failing due to incorrect index of test container children on document load.
rename from accessible/tests/browser/browser_caching_attributes.js
rename to accessible/tests/browser/e10s/browser_caching_attributes.js
rename from accessible/tests/browser/browser_caching_description.js
rename to accessible/tests/browser/e10s/browser_caching_description.js
rename from accessible/tests/browser/browser_caching_name.js
rename to accessible/tests/browser/e10s/browser_caching_name.js
rename from accessible/tests/browser/browser_caching_relations.js
rename to accessible/tests/browser/e10s/browser_caching_relations.js
rename from accessible/tests/browser/browser_caching_states.js
rename to accessible/tests/browser/e10s/browser_caching_states.js
rename from accessible/tests/browser/browser_caching_value.js
rename to accessible/tests/browser/e10s/browser_caching_value.js
rename from accessible/tests/browser/browser_events_caretmove.js
rename to accessible/tests/browser/e10s/browser_events_caretmove.js
rename from accessible/tests/browser/browser_events_hide.js
rename to accessible/tests/browser/e10s/browser_events_hide.js
rename from accessible/tests/browser/browser_events_show.js
rename to accessible/tests/browser/e10s/browser_events_show.js
rename from accessible/tests/browser/browser_events_statechange.js
rename to accessible/tests/browser/e10s/browser_events_statechange.js
rename from accessible/tests/browser/browser_events_textchange.js
rename to accessible/tests/browser/e10s/browser_events_textchange.js
rename from accessible/tests/browser/browser_treeupdate_ariadialog.js
rename to accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
rename from accessible/tests/browser/browser_treeupdate_ariaowns.js
rename to accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
rename from accessible/tests/browser/browser_treeupdate_canvas.js
rename to accessible/tests/browser/e10s/browser_treeupdate_canvas.js
rename from accessible/tests/browser/browser_treeupdate_cssoverflow.js
rename to accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
rename from accessible/tests/browser/browser_treeupdate_doc.js
rename to accessible/tests/browser/e10s/browser_treeupdate_doc.js
rename from accessible/tests/browser/browser_treeupdate_gencontent.js
rename to accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
rename from accessible/tests/browser/browser_treeupdate_hidden.js
rename to accessible/tests/browser/e10s/browser_treeupdate_hidden.js
rename from accessible/tests/browser/browser_treeupdate_imagemap.js
rename to accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
rename from accessible/tests/browser/browser_treeupdate_list.js
rename to accessible/tests/browser/e10s/browser_treeupdate_list.js
rename from accessible/tests/browser/browser_treeupdate_list_editabledoc.js
rename to accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
rename from accessible/tests/browser/browser_treeupdate_listener.js
rename to accessible/tests/browser/e10s/browser_treeupdate_listener.js
rename from accessible/tests/browser/browser_treeupdate_optgroup.js
rename to accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
rename from accessible/tests/browser/browser_treeupdate_removal.js
rename to accessible/tests/browser/e10s/browser_treeupdate_removal.js
rename from accessible/tests/browser/browser_treeupdate_table.js
rename to accessible/tests/browser/e10s/browser_treeupdate_table.js
rename from accessible/tests/browser/browser_treeupdate_textleaf.js
rename to accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
rename from accessible/tests/browser/browser_treeupdate_visibility.js
rename to accessible/tests/browser/e10s/browser_treeupdate_visibility.js
rename from accessible/tests/browser/browser_treeupdate_whitespace.js
rename to accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
rename from accessible/tests/browser/doc_treeupdate_ariadialog.html
rename to accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
rename from accessible/tests/browser/doc_treeupdate_ariaowns.html
rename to accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
rename from accessible/tests/browser/doc_treeupdate_imagemap.html
rename to accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
rename from accessible/tests/browser/doc_treeupdate_removal.xhtml
rename to accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
rename from accessible/tests/browser/doc_treeupdate_visibility.html
rename to accessible/tests/browser/e10s/doc_treeupdate_visibility.html
rename from accessible/tests/browser/doc_treeupdate_whitespace.html
rename to accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
rename from accessible/tests/browser/events.js
rename to accessible/tests/browser/e10s/events.js
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/e10s/head.js
@@ -0,0 +1,84 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_DOCUMENT_LOAD_COMPLETE, CURRENT_CONTENT_DIR, loadFrameScripts */
+
+/* exported addAccessibleTask */
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
+/**
+ * A wrapper around browser test add_task that triggers an accessible test task
+ * as a new browser test task with given document, data URL or markup snippet.
+ * @param  {String}             doc    URL (relative to current directory) or
+ *                                     data URL or markup snippet that is used
+ *                                     to test content with
+ * @param  {Function|Function*} task   a generator or a function with tests to
+ *                                     run
+ */
+function addAccessibleTask(doc, task) {
+  add_task(function*() {
+    let url;
+    if (doc.includes('doc_')) {
+      url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
+    } else {
+      // Assume it's a markup snippet.
+      url = `data:text/html,
+        <html>
+          <head>
+            <meta charset="utf-8"/>
+            <title>Accessibility Test</title>
+          </head>
+          <body id="body">${doc}</body>
+        </html>`;
+    }
+
+    registerCleanupFunction(() => {
+      let observers = Services.obs.enumerateObservers('accessible-event');
+      while (observers.hasMoreElements()) {
+        Services.obs.removeObserver(
+          observers.getNext().QueryInterface(Ci.nsIObserver),
+          'accessible-event');
+      }
+    });
+
+    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
+
+    yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: url
+    }, function*(browser) {
+      registerCleanupFunction(() => {
+        if (browser) {
+          let tab = gBrowser.getTabForBrowser(browser);
+          if (tab && !tab.closing && tab.linkedBrowser) {
+            gBrowser.removeTab(tab);
+          }
+        }
+      });
+
+      yield SimpleTest.promiseFocus(browser);
+
+      loadFrameScripts(browser,
+        'let { document, window, navigator } = content;',
+        { name: 'common.js', dir: MOCHITESTS_DIR });
+
+      Logger.log(
+        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
+      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
+
+      let event = yield onDocLoad;
+      yield task(browser, event.accessible);
+    });
+  });
+}
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'e10s/events.js');
--- a/accessible/tests/browser/head.js
+++ b/accessible/tests/browser/head.js
@@ -1,303 +1,116 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
-/* global EVENT_DOCUMENT_LOAD_COMPLETE */
-
-/* exported Logger, MOCHITESTS_DIR, isDefunct, addAccessibleTask,
-            invokeSetAttribute, invokeFocus, invokeSetStyle,
-            findAccessibleChildByID, getAccessibleDOMNodeID */
-
-const { interfaces: Ci, utils: Cu } = Components;
-
-Cu.import('resource://gre/modules/Services.jsm');
-
-/**
- * Current browser test directory path used to load subscripts.
- */
-const CURRENT_DIR =
-  'chrome://mochitests/content/browser/accessible/tests/browser/';
-/**
- * A11y mochitest directory where we find common files used in both browser and
- * plain tests.
- */
-const MOCHITESTS_DIR =
-  'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
-/**
- * A base URL for test files used in content.
- */
-const CURRENT_CONTENT_DIR =
-  'http://example.com/browser/accessible/tests/browser/';
+/* exported initPromise, shutdownPromise,
+            setE10sPrefs, unsetE10sPrefs, forceGC */
 
 /**
- * Used to dump debug information.
+ * Set e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are set.
  */
-let Logger = {
-  /**
-   * Set up this variable to dump log messages into console.
-   */
-  dumpToConsole: false,
-
-  /**
-   * Set up this variable to dump log messages into error console.
-   */
-  dumpToAppConsole: false,
-
-  /**
-   * Return true if dump is enabled.
-   */
-  get enabled() {
-    return this.dumpToConsole || this.dumpToAppConsole;
-  },
-
-  /**
-   * Dump information into console if applicable.
-   */
-  log(msg) {
-    if (this.enabled) {
-      this.logToConsole(msg);
-      this.logToAppConsole(msg);
-    }
-  },
-
-  /**
-   * Log message to console.
-   */
-  logToConsole(msg) {
-    if (this.dumpToConsole) {
-      dump(`\n${msg}\n`);
-    }
-  },
-
-  /**
-   * Log message to error console.
-   */
-  logToAppConsole(msg) {
-    if (this.dumpToAppConsole) {
-      Services.console.logStringMessage(`${msg}`);
-    }
-  }
-};
-
-/**
- * Check if an accessible object has a defunct test.
- * @param  {nsIAccessible}  accessible object to test defunct state for
- * @return {Boolean}        flag indicating defunct state
- */
-function isDefunct(accessible) {
-  let defunct = false;
-  try {
-    let extState = {};
-    accessible.getState({}, extState);
-    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
-  } catch (x) {
-    defunct = true;
-  } finally {
-    if (defunct) {
-      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
-    }
-  }
-  return defunct;
+function setE10sPrefs() {
+  return new Promise(resolve =>
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ['browser.tabs.remote.autostart', true],
+        ['browser.tabs.remote.force-enable', true],
+        ['extensions.e10sBlocksEnabling', false]
+      ]
+    }, resolve));
 }
 
 /**
- * Asynchronously set or remove content element's attribute (in content process
- * if e10s is enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @param  {String}  attr     attribute name
- * @param  {String?} value    optional attribute value, if not present, remove
- *                            attribute
- * @return {Promise}          promise indicating that attribute is set/removed
+ * Unset e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are unset.
  */
-function invokeSetAttribute(browser, id, attr, value) {
-  if (value) {
-    Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
-  } else {
-    Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
-  }
-  return ContentTask.spawn(browser, { id, attr, value },
-    ({ id, attr, value }) => {
-      let elm = content.document.getElementById(id);
-      if (value) {
-        elm.setAttribute(attr, value);
-      } else {
-        elm.removeAttribute(attr);
-      }
-    });
+function unsetE10sPrefs() {
+  return new Promise(resolve => {
+    SpecialPowers.popPrefEnv(resolve);
+  });
 }
 
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
 /**
- * Asynchronously set or remove content element's style (in content process if
- * e10s is enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @param  {String}  aStyle   style property name
- * @param  {String?} aValue   optional style property value, if not present,
- *                            remove style
- * @return {Promise}          promise indicating that style is set/removed
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired.
+ * @return {Promise} event promise evaluating to event's data
  */
-function invokeSetStyle(browser, id, style, value) {
-  if (value) {
-    Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
-  } else {
-    Logger.log(`Removing ${style} style from node with id: ${id}`);
-  }
-  return ContentTask.spawn(browser, { id, style, value },
-    ({ id, style, value }) => {
-      let elm = content.document.getElementById(id);
-      if (value) {
-        elm.style[style] = value;
-      } else {
-        delete elm.style[style];
-      }
-    });
-}
-
-/**
- * Asynchronously set focus on a content element (in content process if e10s is
- * enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @return {Promise} promise  indicating that focus is set
- */
-function invokeFocus(browser, id) {
-  Logger.log(`Setting focus on a node with id: ${id}`);
-  return ContentTask.spawn(browser, id, id => {
-    let elm = content.document.getElementById(id);
-    if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
-        elm instanceof Ci.nsIDOMXULTextBoxElement) {
-      elm.selectionStart = elm.selectionEnd = elm.value.length;
-    }
-    elm.focus();
+function a11yInitOrShutdownPromise() {
+  return new Promise(resolve => {
+    let observe = (subject, topic, data) => {
+      Services.obs.removeObserver(observe, 'a11y-init-or-shutdown');
+      resolve(data);
+    };
+    Services.obs.addObserver(observe, 'a11y-init-or-shutdown', false);
   });
 }
 
 /**
- * Traverses the accessible tree starting from a given accessible as a root and
- * looks for an accessible that matches based on its DOMNode id.
- * @param  {nsIAccessible}  accessible root accessible
- * @param  {String}         id         id to look up accessible for
- * @return {nsIAccessible?}            found accessible if any
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired
+ * in content.
+ * @param  {Object}   browser  current "tabbrowser" element
+ * @return {Promise}  event    promise evaluating to event's data
  */
-function findAccessibleChildByID(accessible, id) {
-  if (getAccessibleDOMNodeID(accessible) === id) {
-    return accessible;
-  }
-  for (let i = 0; i < accessible.children.length; ++i) {
-    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
-    if (found) {
-      return found;
-    }
-  }
+function contentA11yInitOrShutdownPromise(browser) {
+  return ContentTask.spawn(browser, {}, a11yInitOrShutdownPromise);
 }
 
 /**
- * Load a list of scripts into the test
- * @param {Array} scripts  a list of scripts to load
- */
-function loadScripts(...scripts) {
-  for (let script of scripts) {
-    let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
-      `${script.dir}${script.name}`;
-    Services.scriptloader.loadSubScript(path, this);
-  }
-}
-
-/**
- * Load a list of frame scripts into test's content.
- * @param {Object} browser   browser element that content belongs to
- * @param {Array}  scripts   a list of scripts to load into content
+ * A helper function that maps 'a11y-init-or-shutdown' event to a promise that
+ * resovles or rejects depending on whether accessibility service is expected to
+ * be initialized or shut down.
  */
-function loadFrameScripts(browser, ...scripts) {
-  let mm = browser.messageManager;
-  for (let script of scripts) {
-    let frameScript;
-    if (typeof script === 'string') {
-      if (script.includes('.js')) {
-        // If script string includes a .js extention, assume it is a script
-        // path.
-        frameScript = `${CURRENT_DIR}${script}`;
-      } else {
-        // Otherwise it is a serealized script.
-        frameScript = `data:,${script}`;
-      }
-    } else {
-      // Script is a object that has { dir, name } format.
-      frameScript = `${script.dir}${script.name}`;
-    }
-    mm.loadFrameScript(frameScript, false, true);
-  }
+function promiseOK(promise, expected) {
+  return promise.then(flag =>
+    flag === expected ? Promise.resolve() : Promise.reject());
 }
 
 /**
- * A wrapper around browser test add_task that triggers an accessible test task
- * as a new browser test task with given document, data URL or markup snippet.
- * @param  {String}             doc    URL (relative to current directory) or
- *                                     data URL or markup snippet that is used
- *                                     to test content with
- * @param  {Function|Function*} task   a generator or a function with tests to
- *                                     run
+ * Checks and returns a promise that resolves when accessibility service is
+ * initialized with the correct flag.
+ * @param  {?Object} contentBrowser optinal remove browser object that indicates
+ *                                  that accessibility service is expected to be
+ *                                  initialized in content process.
+ * @return {Promise}                promise that resolves when the accessibility
+ *                                  service initialized correctly.
  */
-function addAccessibleTask(doc, task) {
-  add_task(function*() {
-    let url;
-    if (doc.includes('doc_')) {
-      url = `${CURRENT_CONTENT_DIR}${doc}`;
-    } else {
-      // Assume it's a markup snippet.
-      url = `data:text/html,
-        <html>
-          <head>
-            <meta charset="utf-8"/>
-            <title>Accessibility Test</title>
-          </head>
-          <body id="body">${doc}</body>
-        </html>`;
-    }
-
-    registerCleanupFunction(() => {
-      let observers = Services.obs.enumerateObservers('accessible-event');
-      while (observers.hasMoreElements()) {
-        Services.obs.removeObserver(
-          observers.getNext().QueryInterface(Ci.nsIObserver),
-          'accessible-event');
-      }
-    });
-
-    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
-
-    yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: url
-    }, function*(browser) {
-      registerCleanupFunction(() => {
-        if (browser) {
-          let tab = gBrowser.getTabForBrowser(browser);
-          if (tab && !tab.closing && tab.linkedBrowser) {
-            gBrowser.removeTab(tab);
-          }
-        }
-      });
-
-      yield SimpleTest.promiseFocus(browser);
-
-      loadFrameScripts(browser,
-        'let { document, window, navigator } = content;',
-        { name: 'common.js', dir: MOCHITESTS_DIR });
-
-      Logger.log(
-        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
-      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
-
-      let event = yield onDocLoad;
-      yield task(browser, event.accessible);
-    });
-  });
+function initPromise(contentBrowser) {
+  let a11yInitPromise = contentBrowser ?
+    contentA11yInitOrShutdownPromise(contentBrowser) :
+    a11yInitOrShutdownPromise();
+  return promiseOK(a11yInitPromise, '1').then(
+    () => ok(true, 'Service initialized correctly'),
+    () => ok(false, 'Service shutdown incorrectly'));
 }
 
-// Loading and common.js from accessible/tests/mochitest/ for all tests, as well
-// as events.js.
-loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js');
+/**
+ * Checks and returns a promise that resolves when accessibility service is
+ * shut down with the correct flag.
+ * @param  {?Object} contentBrowser optinal remove browser object that indicates
+ *                                  that accessibility service is expected to be
+ *                                  shut down in content process.
+ * @return {Promise}                promise that resolves when the accessibility
+ *                                  service shuts down correctly.
+ */
+function shutdownPromise(contentBrowser) {
+  let a11yShutdownPromise = contentBrowser ?
+    contentA11yInitOrShutdownPromise(contentBrowser) :
+    a11yInitOrShutdownPromise();
+  return promiseOK(a11yShutdownPromise, '0').then(
+    () => ok(true, 'Service shutdown correctly'),
+    () => ok(false, 'Service initialized incorrectly'));
+}
+
+/**
+ * Force garbage collection.
+ */
+function forceGC() {
+  Cu.forceCC();
+  Cu.forceGC();
+}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/shared-head.js
@@ -0,0 +1,229 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* exported Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus,
+            invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID,
+            CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
+
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+/**
+ * Current browser test directory path used to load subscripts.
+ */
+const CURRENT_DIR =
+  'chrome://mochitests/content/browser/accessible/tests/browser/';
+/**
+ * A11y mochitest directory where we find common files used in both browser and
+ * plain tests.
+ */
+const MOCHITESTS_DIR =
+  'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
+/**
+ * A base URL for test files used in content.
+ */
+const CURRENT_CONTENT_DIR =
+  'http://example.com/browser/accessible/tests/browser/';
+
+/**
+ * Used to dump debug information.
+ */
+let Logger = {
+  /**
+   * Set up this variable to dump log messages into console.
+   */
+  dumpToConsole: false,
+
+  /**
+   * Set up this variable to dump log messages into error console.
+   */
+  dumpToAppConsole: false,
+
+  /**
+   * Return true if dump is enabled.
+   */
+  get enabled() {
+    return this.dumpToConsole || this.dumpToAppConsole;
+  },
+
+  /**
+   * Dump information into console if applicable.
+   */
+  log(msg) {
+    if (this.enabled) {
+      this.logToConsole(msg);
+      this.logToAppConsole(msg);
+    }
+  },
+
+  /**
+   * Log message to console.
+   */
+  logToConsole(msg) {
+    if (this.dumpToConsole) {
+      dump(`\n${msg}\n`);
+    }
+  },
+
+  /**
+   * Log message to error console.
+   */
+  logToAppConsole(msg) {
+    if (this.dumpToAppConsole) {
+      Services.console.logStringMessage(`${msg}`);
+    }
+  }
+};
+
+/**
+ * Check if an accessible object has a defunct test.
+ * @param  {nsIAccessible}  accessible object to test defunct state for
+ * @return {Boolean}        flag indicating defunct state
+ */
+function isDefunct(accessible) {
+  let defunct = false;
+  try {
+    let extState = {};
+    accessible.getState({}, extState);
+    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
+  } catch (x) {
+    defunct = true;
+  } finally {
+    if (defunct) {
+      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
+    }
+  }
+  return defunct;
+}
+
+/**
+ * Asynchronously set or remove content element's attribute (in content process
+ * if e10s is enabled).
+ * @param  {Object}  browser  current "tabbrowser" element
+ * @param  {String}  id       content element id
+ * @param  {String}  attr     attribute name
+ * @param  {String?} value    optional attribute value, if not present, remove
+ *                            attribute
+ * @return {Promise}          promise indicating that attribute is set/removed
+ */
+function invokeSetAttribute(browser, id, attr, value) {
+  if (value) {
+    Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
+  } else {
+    Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
+  }
+  return ContentTask.spawn(browser, { id, attr, value },
+    ({ id, attr, value }) => {
+      let elm = content.document.getElementById(id);
+      if (value) {
+        elm.setAttribute(attr, value);
+      } else {
+        elm.removeAttribute(attr);
+      }
+    });
+}
+
+/**
+ * Asynchronously set or remove content element's style (in content process if
+ * e10s is enabled).
+ * @param  {Object}  browser  current "tabbrowser" element
+ * @param  {String}  id       content element id
+ * @param  {String}  aStyle   style property name
+ * @param  {String?} aValue   optional style property value, if not present,
+ *                            remove style
+ * @return {Promise}          promise indicating that style is set/removed
+ */
+function invokeSetStyle(browser, id, style, value) {
+  if (value) {
+    Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
+  } else {
+    Logger.log(`Removing ${style} style from node with id: ${id}`);
+  }
+  return ContentTask.spawn(browser, { id, style, value },
+    ({ id, style, value }) => {
+      let elm = content.document.getElementById(id);
+      if (value) {
+        elm.style[style] = value;
+      } else {
+        delete elm.style[style];
+      }
+    });
+}
+
+/**
+ * Asynchronously set focus on a content element (in content process if e10s is
+ * enabled).
+ * @param  {Object}  browser  current "tabbrowser" element
+ * @param  {String}  id       content element id
+ * @return {Promise} promise  indicating that focus is set
+ */
+function invokeFocus(browser, id) {
+  Logger.log(`Setting focus on a node with id: ${id}`);
+  return ContentTask.spawn(browser, id, id => {
+    let elm = content.document.getElementById(id);
+    if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
+        elm instanceof Ci.nsIDOMXULTextBoxElement) {
+      elm.selectionStart = elm.selectionEnd = elm.value.length;
+    }
+    elm.focus();
+  });
+}
+
+/**
+ * Traverses the accessible tree starting from a given accessible as a root and
+ * looks for an accessible that matches based on its DOMNode id.
+ * @param  {nsIAccessible}  accessible root accessible
+ * @param  {String}         id         id to look up accessible for
+ * @return {nsIAccessible?}            found accessible if any
+ */
+function findAccessibleChildByID(accessible, id) {
+  if (getAccessibleDOMNodeID(accessible) === id) {
+    return accessible;
+  }
+  for (let i = 0; i < accessible.children.length; ++i) {
+    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
+    if (found) {
+      return found;
+    }
+  }
+}
+
+/**
+ * Load a list of scripts into the test
+ * @param {Array} scripts  a list of scripts to load
+ */
+function loadScripts(...scripts) {
+  for (let script of scripts) {
+    let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
+      `${script.dir}${script.name}`;
+    Services.scriptloader.loadSubScript(path, this);
+  }
+}
+
+/**
+ * Load a list of frame scripts into test's content.
+ * @param {Object} browser   browser element that content belongs to
+ * @param {Array}  scripts   a list of scripts to load into content
+ */
+function loadFrameScripts(browser, ...scripts) {
+  let mm = browser.messageManager;
+  for (let script of scripts) {
+    let frameScript;
+    if (typeof script === 'string') {
+      if (script.includes('.js')) {
+        // If script string includes a .js extention, assume it is a script
+        // path.
+        frameScript = `${CURRENT_DIR}${script}`;
+      } else {
+        // Otherwise it is a serealized script.
+        frameScript = `data:,${script}`;
+      }
+    } else {
+      // Script is a object that has { dir, name } format.
+      frameScript = `${script.dir}${script.name}`;
+    }
+    mm.loadFrameScript(frameScript, false, true);
+  }
+}