Bug 1023862 - Electrolysis (e10s) support for the requestAutocomplete testing framework. r=MattN
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Sun, 27 Jul 2014 18:18:44 +0100
changeset 196334 15c19c592c12451289c384810a037b648a0bcca5
parent 196333 bc751d1d8c1dc23dcdfc76c923f0fde62fc5dded
child 196335 d77f6a96ff960d0755cafa0e1dd976d9d285d311
child 196529 5afb5ea1d0d2f05d5f81c42d6409b86a53e02145
push id46844
push usercbook@mozilla.com
push dateMon, 28 Jul 2014 14:30:47 +0000
treeherdermozilla-inbound@7dd701896de8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1023862
milestone34.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 1023862 - Electrolysis (e10s) support for the requestAutocomplete testing framework. r=MattN
toolkit/components/formautofill/moz.build
toolkit/components/formautofill/test/browser/browser.ini
toolkit/components/formautofill/test/browser/head.js
toolkit/components/formautofill/test/browser/loader.js
toolkit/components/formautofill/test/chrome/chrome.ini
toolkit/components/formautofill/test/chrome/head.js
toolkit/components/formautofill/test/chrome/loader.js
toolkit/components/formautofill/test/chrome/loader_parent.js
toolkit/components/formautofill/test/chrome/test_infrastructure.html
toolkit/components/formautofill/test/chrome/test_infrastructure.js
toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.js
toolkit/components/formautofill/test/head_common.js
toolkit/components/formautofill/test/loader_common.js
toolkit/components/formautofill/test/xpcshell.ini
toolkit/components/formautofill/test/xpcshell/head.js
toolkit/components/formautofill/test/xpcshell/loader.js
toolkit/components/formautofill/test/xpcshell/test_integration.js
toolkit/components/formautofill/test/xpcshell/xpcshell.ini
--- a/toolkit/components/formautofill/moz.build
+++ b/toolkit/components/formautofill/moz.build
@@ -8,17 +8,16 @@ BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'test/chrome/chrome.ini',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += [
-    'test/xpcshell.ini',
     'test/xpcshell/xpcshell.ini',
 ]
 
 XPIDL_SOURCES += [
     'nsIFormAutofillContentService.idl',
 ]
 
 XPIDL_MODULE = 'toolkit_formautofill'
--- a/toolkit/components/formautofill/test/browser/browser.ini
+++ b/toolkit/components/formautofill/test/browser/browser.ini
@@ -1,7 +1,10 @@
 [DEFAULT]
+# The following files starting with ".." are installed in the current folder.
 support-files =
   ../head_common.js
+  ../loader_common.js
   head.js
+  loader.js
 
 [browser_infrastructure.js]
 [browser_ui_requestAutocomplete.js]
--- a/toolkit/components/formautofill/test/browser/head.js
+++ b/toolkit/components/formautofill/test/browser/head.js
@@ -1,39 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * This file makes the common testing infrastructure available to the browser
- * tests located in this folder.  This is only used as an infrastructure file,
- * and new common functions should be added to the "head_common.js" file.
+ * Initialization specific to Form Autofill mochitest-browser tests.
  */
 
 "use strict";
 
-let ChromeUtils = {};
-Services.scriptloader.loadSubScript(
-  "chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
-
-/* --- Adapters for the mochitest-browser-chrome infrastructure --- */
-
-let Output = {
-  print: info,
-};
+// We cannot start initialization from "loader.js" like we do in the xpcshell
+// and mochitest-chrome frameworks, thus we load the script here.
+Services.scriptloader.loadSubScript(getRootDirectory(gTestPath) + "loader.js",
+                                    this);
 
-let Assert = {
-  ok: function (actual) {
-    let stack = Components.stack.caller;
-    ok(actual, "[" + stack.name + " : " + stack.lineNumber + "] " + actual +
-               " == true");
-  },
-  equal: function (actual, expected) {
-    let stack = Components.stack.caller;
-    is(actual, expected, "[" + stack.name + " : " + stack.lineNumber + "] " +
-               actual + " == " + expected);
-  },
-};
-
-/* --- Shared infrastructure --- */
-
-Services.scriptloader.loadSubScript(
-  "chrome://mochitests/content/browser/" +
-  "toolkit/components/formautofill/test/browser/head_common.js", this);
+// The testing framework is fully initialized at this point, you can add
+// mochitest-browser specific test initialization here.  If you need shared
+// functions or initialization that are not specific to mochitest-browser,
+// consider adding them to "head_common.js" in the parent folder instead.
copy from toolkit/components/formautofill/test/browser/head.js
copy to toolkit/components/formautofill/test/browser/loader.js
--- a/toolkit/components/formautofill/test/browser/head.js
+++ b/toolkit/components/formautofill/test/browser/loader.js
@@ -1,39 +1,42 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * This file makes the common testing infrastructure available to the browser
- * tests located in this folder.  This is only used as an infrastructure file,
- * and new common functions should be added to the "head_common.js" file.
+ * Infrastructure for the mochitest-browser tests located in this folder.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file.  Use "head_common.js" or "head.js" for shared code.
  */
 
 "use strict";
 
+Services.scriptloader.loadSubScript(getRootDirectory(gTestPath) +
+                                    "loader_common.js", this);
+
 let ChromeUtils = {};
 Services.scriptloader.loadSubScript(
   "chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
 
-/* --- Adapters for the mochitest-browser-chrome infrastructure --- */
-
+// Define output functions so they look the same across all frameworks.
 let Output = {
   print: info,
 };
 
+// Define assertion functions so they look the same across all frameworks.
 let Assert = {
-  ok: function (actual) {
-    let stack = Components.stack.caller;
-    ok(actual, "[" + stack.name + " : " + stack.lineNumber + "] " + actual +
-               " == true");
-  },
-  equal: function (actual, expected) {
-    let stack = Components.stack.caller;
-    is(actual, expected, "[" + stack.name + " : " + stack.lineNumber + "] " +
-               actual + " == " + expected);
-  },
+  ok: _mochitestAssert.ok,
+  equal: _mochitestAssert.equal,
 };
 
-/* --- Shared infrastructure --- */
+// Define task registration functions, see description in "loader_common.js".
+let add_task_in_parent_process = add_task;
+let add_task_in_child_process = function () {};
+let add_task_in_both_processes = add_task;
 
-Services.scriptloader.loadSubScript(
-  "chrome://mochitests/content/browser/" +
-  "toolkit/components/formautofill/test/browser/head_common.js", this);
+Services.scriptloader.loadSubScript(getRootDirectory(gTestPath) +
+                                    "head_common.js", this);
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file.  Use "head_common.js" or "head.js" for shared code.
--- a/toolkit/components/formautofill/test/chrome/chrome.ini
+++ b/toolkit/components/formautofill/test/chrome/chrome.ini
@@ -1,7 +1,17 @@
 [DEFAULT]
+# The following files starting with ".." are installed in the current folder.
 support-files =
   ../head_common.js
+  ../loader_common.js
   head.js
+  test_infrastructure.js
+  test_requestAutocomplete_cancel.js
+  loader_parent.js
+  loader.js
+
+# For each test defined below, the associated JavaScript file must be declared
+# in the list above.  This is required because a "support-files" declaration on
+# the individual test would override the global list instead of adding entries.
 
 [test_infrastructure.html]
 [test_requestAutocomplete_cancel.html]
--- a/toolkit/components/formautofill/test/chrome/head.js
+++ b/toolkit/components/formautofill/test/chrome/head.js
@@ -1,71 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * This file makes the common testing infrastructure available to the chrome
- * tests located in this folder.  This is only used as an infrastructure file,
- * and new common functions should be added to the "head_common.js" file.
+ * Initialization specific to Form Autofill mochitest-chrome tests.
+ *
+ * This file is loaded by "loader.js".
  */
 
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
-Cu.import("resource://gre/modules/Services.jsm", this);
-
-Services.scriptloader.loadSubScript(
-  "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js", this);
-
-let ChromeUtils = {};
-Services.scriptloader.loadSubScript(
-  "chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
-
-/* --- Adapters for the mochitest-chrome infrastructure --- */
-
-let Output = {
-  print: info,
-};
-
-let Assert = {
-  ok: function (actual) {
-    let stack = Components.stack.caller;
-    ok(actual, "[" + stack.name + " : " + stack.lineNumber + "] " + actual +
-               " == true");
-  },
-  equal: function (actual, expected) {
-    let stack = Components.stack.caller;
-    is(actual, expected, "[" + stack.name + " : " + stack.lineNumber + "] " +
-               actual + " == " + expected);
-  },
-};
-
-let executeSoon = SimpleTest.executeSoon;
-
-let gTestTasks = [];
-let add_task = taskFn => gTestTasks.push(taskFn);
-
-SimpleTest.waitForExplicitFinish();
-
-window.addEventListener("load", function onLoad() {
-  window.removeEventListener("load", onLoad);
-
-  Task.spawn(function* () {
-    try {
-      for (let taskFn of gTestTasks) {
-        info("Running " + taskFn.name);
-        yield Task.spawn(taskFn);
-      }
-    } catch (ex) {
-      ok(false, ex);
-    }
-
-    SimpleTest.finish();
-  });
-});
-
-/* --- Shared infrastructure --- */
-
-let headUrl = "chrome://mochitests/content/chrome/" +
-              "toolkit/components/formautofill/test/chrome/head_common.js";
-Services.scriptloader.loadSubScript(headUrl, this);
+// The testing framework is fully initialized at this point, you can add
+// mochitest-chrome specific test initialization here.  If you need shared
+// functions or initialization that are not specific to mochitest-chrome,
+// consider adding them to "head_common.js" in the parent folder instead.
copy from toolkit/components/formautofill/test/chrome/head.js
copy to toolkit/components/formautofill/test/chrome/loader.js
--- a/toolkit/components/formautofill/test/chrome/head.js
+++ b/toolkit/components/formautofill/test/chrome/loader.js
@@ -1,71 +1,120 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * This file makes the common testing infrastructure available to the chrome
- * tests located in this folder.  This is only used as an infrastructure file,
- * and new common functions should be added to the "head_common.js" file.
+ * Infrastructure for the mochitest-chrome tests located in this folder.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file.  Use "head_common.js" or "head.js" for shared code.
  */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 
 Services.scriptloader.loadSubScript(
   "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js", this);
 
+let sharedUrl = SimpleTest.getTestFileURL("loader_common.js");
+Services.scriptloader.loadSubScript(sharedUrl, this);
+
 let ChromeUtils = {};
 Services.scriptloader.loadSubScript(
   "chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
 
-/* --- Adapters for the mochitest-chrome infrastructure --- */
+let parentScript = SpecialPowers.loadChromeScript(
+                                 SimpleTest.getTestFileURL("loader_parent.js"));
+
+// Replace the extension of the loaded HTML file with ".js"
+let testUrl = location.href.replace(/\.\w+$/, ".js");
 
+// Start loading the test script in the parent process.
+let promiseParentInitFinished = new Promise(function (resolve) {
+  parentScript.addMessageListener("finish_load_in_parent", resolve);
+});
+parentScript.sendAsyncMessage("start_load_in_parent", { testUrl: testUrl });
+
+// Define output functions so they look the same across all frameworks.
 let Output = {
   print: info,
 };
 
+// Define assertion functions so they look the same across all frameworks.
 let Assert = {
-  ok: function (actual) {
-    let stack = Components.stack.caller;
-    ok(actual, "[" + stack.name + " : " + stack.lineNumber + "] " + actual +
-               " == true");
-  },
-  equal: function (actual, expected) {
-    let stack = Components.stack.caller;
-    is(actual, expected, "[" + stack.name + " : " + stack.lineNumber + "] " +
-               actual + " == " + expected);
-  },
+  ok: _mochitestAssert.ok,
+  equal: _mochitestAssert.equal,
 };
 
 let executeSoon = SimpleTest.executeSoon;
 
 let gTestTasks = [];
-let add_task = taskFn => gTestTasks.push(taskFn);
 
-SimpleTest.waitForExplicitFinish();
+// Define task registration functions, see description in "loader_common.js".
+function add_task(taskFn) {
+  gTestTasks.push([taskFn, "content", taskFn.name]);
+}
+function add_task_in_parent_process(taskFn, taskIdOverride) {
+  let taskId = taskIdOverride || getTaskId(Components.stack.caller);
+  gTestTasks.push([taskFn, "parent", taskId]);
+};
+function add_task_in_both_processes(taskFn) {
+  // We need to define a task ID based on our direct caller.
+  add_task_in_parent_process(taskFn, getTaskId(Components.stack.caller));
+  add_task(taskFn);
+};
+let add_task_in_child_process = add_task;
 
 window.addEventListener("load", function onLoad() {
   window.removeEventListener("load", onLoad);
 
   Task.spawn(function* () {
     try {
-      for (let taskFn of gTestTasks) {
-        info("Running " + taskFn.name);
-        yield Task.spawn(taskFn);
+      for (let [taskFn, taskType, taskId] of gTestTasks) {
+        if (taskType == "content") {
+          // This is a normal task executed in the current process.
+          info("Running " + taskFn.name);
+          yield Task.spawn(taskFn);
+        } else {
+          // This is a task executed in the parent process.
+          info("Running task in parent process: " + taskFn.name);
+          let promiseFinished = new Promise(function (resolve) {
+            parentScript.addMessageListener("finish_task_" + taskId, resolve);
+          });
+          parentScript.sendAsyncMessage("start_task_" + taskId);
+          yield promiseFinished;
+          info("Finished task in parent process: " + taskFn.name);
+        }
       }
     } catch (ex) {
       ok(false, ex);
     }
 
     SimpleTest.finish();
   });
 });
 
-/* --- Shared infrastructure --- */
+// Wait for the test script to be loaded in the parent process.  This means that
+// test tasks are registered and ready, but have not been executed yet.
+add_task(function* wait_loading_in_parent_process() {
+  yield promiseParentInitFinished;
+});
+
+let headUrl = SimpleTest.getTestFileURL("head_common.js");
+Services.scriptloader.loadSubScript(headUrl, this);
 
-let headUrl = "chrome://mochitests/content/chrome/" +
-              "toolkit/components/formautofill/test/chrome/head_common.js";
-Services.scriptloader.loadSubScript(headUrl, this);
+Output.print("Loading test file: " + testUrl);
+Services.scriptloader.loadSubScript(testUrl, this);
+
+// Register the execution of termination tasks after all other tasks.
+add_task(terminationTaskFn);
+add_task_in_parent_process(terminationTaskFn, terminationTaskFn.name);
+
+SimpleTest.waitForExplicitFinish();
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file.  Use "head_common.js" or "head.js" for shared code.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/loader_parent.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure for the mochitest-chrome tests located in this folder, always
+ * executed in the parent process.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file.  Use "head_common.js" or "head.js" for shared code.
+ */
+
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+
+let sharedUrl = "chrome://mochitests/content/chrome/" +
+                "toolkit/components/formautofill/test/chrome/loader_common.js";
+Services.scriptloader.loadSubScript(sharedUrl, this);
+
+// Define output functions so they look the same across all frameworks.  Since
+// we don't have an output function available here, we report as TEST-PASS.
+let Output = {
+  print: message => assert.ok(true, message),
+};
+
+// Define assertion functions so they look the same across all frameworks.
+let Assert = {
+  ok: assert.ok,
+  equal: assert.equal,
+};
+
+// Define task registration functions, see description in "loader_common.js".
+function add_task_in_parent_process(taskFn, taskIdOverride) {
+  let taskId = taskIdOverride || getTaskId(Components.stack.caller);
+  Output.print("Registering in the parent process: " + taskId);
+  addMessageListener("start_task_" + taskId, function () {
+    Task.spawn(function* () {
+      try {
+        Output.print("Running in the parent process " + taskId);
+        yield Task.spawn(taskFn);
+      } catch (ex) {
+        assert.ok(false, ex);
+      }
+
+      sendAsyncMessage("finish_task_" + taskId, {});
+    });
+  });
+}
+let add_task = function () {};
+let add_task_in_child_process = function () {};
+let add_task_in_both_processes = add_task_in_parent_process;
+
+// We need to wait for the child process to send us the path of the test file
+// to load before we can actually start loading it.
+let context = this;
+addMessageListener("start_load_in_parent", function (message) {
+  Output.print("Starting loading infrastructure in parent process.");
+  let headUrl = "chrome://mochitests/content/chrome/" +
+                "toolkit/components/formautofill/test/chrome/head_common.js";
+  Services.scriptloader.loadSubScript(headUrl, context);
+
+  Services.scriptloader.loadSubScript(message.testUrl, context);
+
+  // Register the execution of termination tasks after all other tasks.
+  add_task_in_parent_process(terminationTaskFn, terminationTaskFn.name);
+
+  Output.print("Finished loading infrastructure in parent process.");
+  sendAsyncMessage("finish_load_in_parent", {});
+});
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file.  Use "head_common.js" or "head.js" for shared code.
--- a/toolkit/components/formautofill/test/chrome/test_infrastructure.html
+++ b/toolkit/components/formautofill/test/chrome/test_infrastructure.html
@@ -1,72 +1,8 @@
 <!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
+<script type="application/javascript;version=1.7" src="loader.js"></script>
 <!-- Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/ -->
 
 <p id="paragraph">Paragraph contents.</p>
 
-<script type="application/javascript;version=1.7" src="head.js"></script>
-<script type="application/javascript;version=1.7">
-
-/*
- * Tests the local testing infrastructure.
- */
-
-"use strict";
-
-/**
- * Tests the truth assertion function.
- */
-add_task(function* test_assert_truth() {
-  Assert.ok(1 != 2);
-});
-
-/**
- * Tests the equality assertion function.
- */
-add_task(function* test_assert_equality() {
-  Assert.equal(1 + 1, 2);
-});
-
-/**
- * Uses some of the utility functions provided by the framework.
- */
-add_task(function* test_utility_functions() {
-  // The "print" function is useful to log information that is not known before.
-  let randomString = "R" + Math.floor(Math.random() * 10);
-  Output.print("The random contents will be '" + randomString + "'.");
-
-  // Create the text file with the random contents.
-  let path = yield TestUtils.getTempFile("test-infrastructure.txt");
-  yield OS.File.writeAtomic(path, new TextEncoder().encode(randomString));
-
-  // Test a few utility functions.
-  yield TestUtils.waitForTick();
-  yield TestUtils.waitMs(50);
-
-  let promiseMyNotification = TestUtils.waitForNotification("my-topic");
-  Services.obs.notifyObservers(null, "my-topic", "");
-  yield promiseMyNotification;
-
-  // Check the file size.  The file will be deleted automatically later.
-  Assert.equal((yield OS.File.stat(path)).size, randomString.length);
-});
-
-/**
- * This type of test has access to the content declared above.
- */
-add_task(function* test_content() {
-  Assert.equal($("paragraph").innerHTML, "Paragraph contents.");
-
-  let promiseMyEvent = TestUtils.waitForEvent($("paragraph"), "MyEvent");
-
-  let event = document.createEvent("CustomEvent");
-  event.initCustomEvent("MyEvent", true, false, {});
-  $("paragraph").dispatchEvent(event);
-
-  yield promiseMyEvent;
-});
-
-add_task(terminationTaskFn);
-
-</script>
 </body></html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/test_infrastructure.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the local testing infrastructure.
+ */
+
+"use strict";
+
+/**
+ * Tests the truth assertion function.
+ */
+add_task(function* test_assert_truth() {
+  Assert.ok(1 != 2);
+});
+
+/**
+ * Tests the equality assertion function.
+ */
+add_task(function* test_assert_equality() {
+  Assert.equal(1 + 1, 2);
+});
+
+/**
+ * Uses some of the utility functions provided by the framework.
+ */
+add_task(function* test_utility_functions() {
+  // The "print" function is useful to log information that is not known before.
+  let randomString = "R" + Math.floor(Math.random() * 10);
+  Output.print("The random contents will be '" + randomString + "'.");
+
+  // Create the text file with the random contents.
+  let path = yield TestUtils.getTempFile("test-infrastructure.txt");
+  yield OS.File.writeAtomic(path, new TextEncoder().encode(randomString));
+
+  // Test a few utility functions.
+  yield TestUtils.waitForTick();
+  yield TestUtils.waitMs(50);
+
+  let promiseMyNotification = TestUtils.waitForNotification("my-topic");
+  Services.obs.notifyObservers(null, "my-topic", "");
+  yield promiseMyNotification;
+
+  // Check the file size.  The file will be deleted automatically later.
+  Assert.equal((yield OS.File.stat(path)).size, randomString.length);
+});
+
+/**
+ * This type of test has access to the content declared above.
+ */
+add_task(function* test_content() {
+  Assert.equal($("paragraph").innerHTML, "Paragraph contents.");
+
+  let promiseMyEvent = TestUtils.waitForEvent($("paragraph"), "MyEvent");
+
+  let event = document.createEvent("CustomEvent");
+  event.initCustomEvent("MyEvent", true, false, {});
+  $("paragraph").dispatchEvent(event);
+
+  yield promiseMyEvent;
+});
--- a/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
+++ b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.html
@@ -1,38 +1,9 @@
-<!DOCTYPE HTML><html><head><meta charset="utf-8"></head><body>
+<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
+<script type="application/javascript;version=1.7" src="loader.js"></script>
 <!-- Any copyright is dedicated to the Public Domain.
    - http://creativecommons.org/publicdomain/zero/1.0/ -->
 
 <form id="form">
 </form>
 
-<script type="application/javascript;version=1.7" src="head.js"></script>
-<script type="application/javascript;version=1.7">
-
-/*
- * Tests the response sent when requestAutocomplete is canceled by the user.
- */
-
-"use strict";
-
-/**
- * The requestAutocomplete UI will not be displayed during these tests.
- */
-add_task(function* test_initialize() {
-  FormAutofillTest.requestAutocompleteResponse = { canceled: true };
-});
-
-/**
- * Tests the case where the feature is canceled.
- */
-add_task(function* test_cancel() {
-  let promise = TestUtils.waitForEvent($("form"), "autocompleteerror");
-  $("form").requestAutocomplete();
-  let errorEvent = yield promise;
-
-  Assert.equal(errorEvent.reason, "cancel");
-});
-
-add_task(terminationTaskFn);
-
-</script>
 </body></html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/test/chrome/test_requestAutocomplete_cancel.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the response sent when requestAutocomplete is canceled by the user.
+ */
+
+"use strict";
+
+/**
+ * The requestAutocomplete UI will not be displayed during these tests.
+ */
+add_task_in_parent_process(function* test_cancel_init() {
+  FormAutofillTest.requestAutocompleteResponse = { canceled: true };
+});
+
+/**
+ * Tests the case where the feature is canceled.
+ */
+add_task(function* test_cancel() {
+  let promise = TestUtils.waitForEvent($("form"), "autocompleteerror");
+  $("form").requestAutocomplete();
+  let errorEvent = yield promise;
+
+  Assert.equal(errorEvent.reason, "cancel");
+});
--- a/toolkit/components/formautofill/test/head_common.js
+++ b/toolkit/components/formautofill/test/head_common.js
@@ -1,54 +1,37 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * Provides shared infrastructure for the automated tests.
+ * Initialization of Form Autofill tests shared between all frameworks.
+ *
+ * A copy of this file is installed in each of the framework subfolders, this
+ * means it becomes a sibling of the test files in the final layout.  This is
+ * determined by how manifest "support-files" installation works.
  */
 
 "use strict";
 
+// The requestAutocomplete framework is available at this point, you can add
+// mochitest-chrome specific test initialization here.  If you need shared
+// functions or initialization that are not specific to mochitest-chrome,
+// consider adding them to "head_common.js" in the parent folder instead.
+
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
                                   "resource://gre/modules/DownloadPaths.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormAutofill",
                                   "resource://gre/modules/FormAutofill.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Promise",
-                                  "resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
-                                  "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 
-let gTerminationTasks = [];
-let add_termination_task = taskFn => gTerminationTasks.push(taskFn);
-
-/**
- * None of the testing frameworks support asynchronous termination functions, so
- * this task must be registered later, after the other "add_task" calls.
- *
- * Even xpcshell doesn't support calling "add_task" in the "tail.js" file,
- * because it registers the task but does not wait for its termination,
- * potentially leading to intermittent failures in subsequent tests.
- */
-let terminationTaskFn = function* test_common_terminate() {
-  for (let taskFn of gTerminationTasks) {
-    try {
-      yield Task.spawn(taskFn);
-    } catch (ex) {
-      Output.print(ex);
-      Assert.ok(false);
-    }
-  }
-};
-
 /* --- Global helpers --- */
 
 // Some of these functions are already implemented in other parts of the source
 // tree, see bug 946708 about sharing more code.
 
 let TestUtils = {
   /**
    * Waits for at least one tick of the event loop.  This means that all pending
@@ -222,23 +205,17 @@ let TestData = {
         }],
       }],
     };
   },
 };
 
 /* --- Initialization and termination functions common to all tests --- */
 
-add_task(function* test_common_initialize() {
-  // We must manually enable the feature while testing.
-  Services.prefs.setBoolPref("dom.forms.requestAutocomplete", true);
-  add_termination_task(function* () {
-    Services.prefs.clearUserPref("dom.forms.requestAutocomplete");
-  });
-
+add_task_in_parent_process(function* () {
   // If required, we return a mock response instead of displaying the UI.
   let mockIntegrationFn = base => ({
     createRequestAutocompleteUI: Task.async(function* () {
       // Call the base method to display the UI if override is not requested.
       if (FormAutofillTest.requestAutocompleteResponse === null) {
         return yield base.createRequestAutocompleteUI.apply(this, arguments);
       }
 
@@ -253,8 +230,16 @@ add_task(function* test_common_initializ
     }),
   });
 
   FormAutofill.registerIntegration(mockIntegrationFn);
   add_termination_task(function* () {
     FormAutofill.unregisterIntegration(mockIntegrationFn);
   });
 });
+
+add_task_in_both_processes(function* () {
+  // We must manually enable the feature while testing.
+  Services.prefs.setBoolPref("dom.forms.requestAutocomplete", true);
+  add_termination_task(function* () {
+    Services.prefs.clearUserPref("dom.forms.requestAutocomplete");
+  });
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/formautofill/test/loader_common.js
@@ -0,0 +1,120 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Infrastructure common to the test frameworks located in subfolders.
+ *
+ * A copy of this file is installed in each of the framework subfolders, this
+ * means it becomes a sibling of the test files in the final layout.  This is
+ * determined by how manifest "support-files" installation works.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file.  Use "head_common.js" or the "head.js" file of each
+ * framework for shared code.
+ */
+
+"use strict";
+
+/*
+ * --------------------
+ *  FRAMEWORK OVERVIEW
+ * --------------------
+ *
+ * This framework is designed in such a way that test can be written in similar
+ * ways in the xpcshell, mochitest-chrome, and mochitest-browser frameworks,
+ * both when tests are running in the parent process or in a content process.
+ *
+ * There are some basic self-documenting assertion and output functions:
+ *
+ * Assert.ok(actualValue);
+ * Assert.is(actualValue, expectedValue);
+ * Output.print(string);
+ *
+ * Test cases and initialization functions are declared in shared head files
+ * ("head_common.js" and "head.js") as well as individual test files.  When
+ * tests run in an Elecrolysis (e10s) environment, they are executed in both
+ * processes at first.  Normally, at this point only the registration of test
+ * cases happen.  When everything has finished loading, tests are started and
+ * appropriately synchronized between processes.
+ *
+ * Tests can be declared using the add_task syntax:
+ *
+ * add_task(function* test_something () { ... });
+ *   This adds a test either in the parent process or child process:
+ *     - Parent: xpcshell, mochitest-chrome without --e10s, mochitest-browser
+ *     - Child: mochitest-chrome with --e10s
+ *   In the future, these might run in the child process for "xpcshell --e10s".
+ *
+ * add_task_in_parent_process(function* test_something () { ... });
+ *   This test runs in the parent process, but the child process will wait for
+ *   its completion before continuing with the next task.  This wait currently
+ *   happens only in mochitest-chrome with --e10s, in other frameworks that run
+ *   only in the parent process this is the same as a normal add_task.
+ *
+ * add_task_in_child_process(function* test_something () { ... });
+ *   This test runs only in the child process.  This means that the test is not
+ *   run unless this is an e10s test, currently mochitest-chrome with --e10s.
+ *
+ * add_task_in_both_processes(function* test_something () { ... });
+ *   Useful for initialization that must be done both in the parent and the
+ *   child, like setting preferences.
+ *
+ * add_termination_task(function* () { ... });
+ *   Registers a new asynchronous termination task.  This is executed after all
+ *   test cases in the file finished, and always in the same process where the
+ *   termination task is registered.
+ */
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
+
+let gTerminationTasks = [];
+
+/**
+ * None of the testing frameworks support asynchronous termination functions, so
+ * this task must be registered later, after the other "add_task" calls.
+ *
+ * Even xpcshell doesn't support calling "add_task" in the "tail.js" file,
+ * because it registers the task but does not wait for its termination,
+ * potentially leading to intermittent failures in subsequent tests.
+ */
+function* terminationTaskFn() {
+  for (let taskFn of gTerminationTasks) {
+    try {
+      yield Task.spawn(taskFn);
+    } catch (ex) {
+      Output.print(ex);
+      Assert.ok(false);
+    }
+  }
+};
+
+function add_termination_task(taskFn) {
+  gTerminationTasks.push(taskFn);
+}
+
+/**
+ * Returns a unique identifier used for synchronizing the given test task
+ * between the parent and child processes.
+ */
+function getTaskId(stackFrame) {
+  return stackFrame.filename + ":" + stackFrame.lineNumber;
+}
+
+// This is a shared helper for mochitest-chrome and mochitest-browser.
+let _mochitestAssert = {
+  ok: function (actual) {
+    let stack = Components.stack.caller;
+    ok(actual, "[" + stack.name + " : " + stack.lineNumber + "] " + actual +
+               " == true");
+  },
+  equal: function (actual, expected) {
+    let stack = Components.stack.caller;
+    is(actual, expected, "[" + stack.name + " : " + stack.lineNumber + "] " +
+               actual + " == " + expected);
+  },
+};
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file.  Use "head_common.js" or "head.js" for shared code.
deleted file mode 100644
--- a/toolkit/components/formautofill/test/xpcshell.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[DEFAULT]
-support-files = head_common.js
--- a/toolkit/components/formautofill/test/xpcshell/head.js
+++ b/toolkit/components/formautofill/test/xpcshell/head.js
@@ -1,29 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * This file makes the common testing infrastructure available to the xpcshell
- * tests located in this folder.  This is only used as an infrastructure file,
- * and new common functions should be added to the "head_common.js" file.
+ * Initialization specific to Form Autofill xpcshell tests.
+ *
+ * This file is loaded by "loader.js".
  */
 
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
-Cu.import("resource://gre/modules/Services.jsm", this);
-
-/* --- Adapters for the xpcshell infrastructure --- */
-
-let Output = {
-  print: do_print,
-};
-
-let executeSoon = do_execute_soon;
-let setTimeout = (fn, delay) => do_timeout(delay, fn);
-
-function run_test() {
-  do_get_profile();
-  run_next_test();
-}
+// The testing framework is fully initialized at this point, you can add
+// xpcshell specific test initialization here.  If you need shared functions or
+// initialization that are not specific to xpcshell, consider adding them to
+// "head_common.js" in the parent folder instead.
copy from toolkit/components/formautofill/test/xpcshell/head.js
copy to toolkit/components/formautofill/test/xpcshell/loader.js
--- a/toolkit/components/formautofill/test/xpcshell/head.js
+++ b/toolkit/components/formautofill/test/xpcshell/loader.js
@@ -1,29 +1,46 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
- * This file makes the common testing infrastructure available to the xpcshell
- * tests located in this folder.  This is only used as an infrastructure file,
- * and new common functions should be added to the "head_common.js" file.
+ * Infrastructure for the xpcshell tests located in this folder.
+ *
+ * See "loader_common.js" in the parent folder for a general overview.
+ *
+ * Unless you are adding new features to the framework, you shouldn't have to
+ * modify this file.  Use "head_common.js" or "head.js" for shared code.
  */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 
-/* --- Adapters for the xpcshell infrastructure --- */
+Services.scriptloader.loadSubScript(
+  Services.io.newFileURI(do_get_file("loader_common.js")).spec, this);
 
+// Define output functions so they look the same across all frameworks.
 let Output = {
   print: do_print,
 };
 
 let executeSoon = do_execute_soon;
 let setTimeout = (fn, delay) => do_timeout(delay, fn);
 
+// Define task registration functions, see description in "loader_common.js".
+let add_task_in_parent_process = add_task;
+let add_task_in_child_process = function () {};
+let add_task_in_both_processes = add_task;
+
+Services.scriptloader.loadSubScript(
+  Services.io.newFileURI(do_get_file("head_common.js")).spec, this);
+
+// Tests are always run asynchronously and with the profile loaded.
 function run_test() {
   do_get_profile();
   run_next_test();
 }
+
+// Reminder: unless you are adding new features to the framework, you shouldn't
+// have to modify this file.  Use "head_common.js" or "head.js" for shared code.
--- a/toolkit/components/formautofill/test/xpcshell/test_integration.js
+++ b/toolkit/components/formautofill/test/xpcshell/test_integration.js
@@ -5,17 +5,17 @@
  * Tests overriding the FormAutofillIntegration module functions.
  */
 
 "use strict";
 
 /**
  * The requestAutocomplete UI will not be displayed during these tests.
  */
-add_task(function* test_initialize() {
+add_task_in_parent_process(function* test_initialize() {
   FormAutofillTest.requestAutocompleteResponse = { canceled: true };
 });
 
 /**
  * Registers and unregisters an integration override function.
  */
 add_task(function* test_integration_override() {
   let overrideCalled = false;
--- a/toolkit/components/formautofill/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/formautofill/test/xpcshell/xpcshell.ini
@@ -1,6 +1,11 @@
 [DEFAULT]
-head = head.js ../head_common.js
+head = loader.js head.js
 tail =
+# The following files starting with ".." are installed in the current folder.
+# However, they cannot be referenced directly in the "head" directive above.
+support-files =
+  ../head_common.js
+  ../loader_common.js
 
 [test_infrastructure.js]
 [test_integration.js]