diff --git a/dom/ipc/tests/file_dummy.html b/dom/ipc/tests/file_dummy.html
new file mode 100644
index 000000000000..41de16a74957
--- /dev/null
+++ b/dom/ipc/tests/file_dummy.html
@@ -0,0 +1 @@
+
This is a dummy file!
diff --git a/dom/ipc/tests/mochitest.ini b/dom/ipc/tests/mochitest.ini
index 79142013f790..ec86dbbb6ab5 100644
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -23,3 +23,7 @@ support-files =
!/dom/canvas/test/captureStream_common.js
[test_Preallocated.html]
skip-if = !e10s
+[test_force_oop_iframe.html]
+skip-if = !e10s || webrender # oop-iframes trigger a debug assertion in webrender picture caching
+support-files =
+ file_dummy.html
diff --git a/dom/ipc/tests/test_force_oop_iframe.html b/dom/ipc/tests/test_force_oop_iframe.html
new file mode 100644
index 000000000000..df86a0faebb3
--- /dev/null
+++ b/dom/ipc/tests/test_force_oop_iframe.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 37f85d9f8067..77d679d57983 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2682,7 +2682,7 @@ pref("csp.overrule_about_uris_without_csp_whitelist", false);
pref("csp.skip_about_page_has_csp_assert", false);
// assertion flag will be set to false after fixing Bug 1473549
pref("security.allow_eval_with_system_principal", false);
-pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,content-task.js,tree.xml,dialog.xml,preferencesbindings.js,wizard.xml,lodash.js,jszip.js,ajv-4.1.1.js,updates.js,setup,jsol.js,parent_utils.js");
+pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,content-task.js,tree.xml,dialog.xml,preferencesbindings.js,wizard.xml,lodash.js,jszip.js,ajv-4.1.1.js,updates.js,setup,jsol.js,parent_utils.js,chrometask_chromescript");
#endif
// Default Content Security Policy to apply to signed contents.
diff --git a/testing/mochitest/tests/SimpleTest/ChromeTask.js b/testing/mochitest/tests/SimpleTest/ChromeTask.js
new file mode 100644
index 000000000000..5fbd6fe369cc
--- /dev/null
+++ b/testing/mochitest/tests/SimpleTest/ChromeTask.js
@@ -0,0 +1,179 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* 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";
+
+function ChromeTask_ChromeScript() {
+ "use strict";
+
+ const {Task} = ChromeUtils.import("resource://testing-common/Task.jsm");
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ const AssertCls = ChromeUtils.import("resource://testing-common/Assert.jsm", null).Assert;
+
+ addMessageListener("chrome-task:spawn", function(aData) {
+ let id = aData.id;
+ let source = aData.runnable || "()=>{}";
+
+ function getStack(aStack) {
+ let frames = [];
+ for (let frame = aStack; frame; frame = frame.caller) {
+ frames.push(frame.filename + ":" + frame.name + ":" + frame.lineNumber);
+ }
+ return frames.join("\n");
+ }
+
+ /* eslint-disable no-unused-vars */
+ var Assert = new AssertCls((err, message, stack) => {
+ sendAsyncMessage("chrome-task:test-result", {
+ id,
+ condition: !err,
+ name: err ? err.message : message,
+ stack: getStack(err ? err.stack : stack),
+ });
+ });
+
+ var ok = Assert.ok.bind(Assert);
+ var is = Assert.equal.bind(Assert);
+ var isnot = Assert.notEqual.bind(Assert);
+
+ function todo(expr, name) {
+ sendAsyncMessage("chrome-task:test-todo", {id, expr, name});
+ }
+
+ function todo_is(a, b, name) {
+ sendAsyncMessage("chrome-task:test-todo_is", {id, a, b, name});
+ }
+
+ function info(name) {
+ sendAsyncMessage("chrome-task:test-info", {id, name});
+ }
+ /* eslint-enable no-unused-vars */
+
+ try {
+ let runnablestr = `
+ (() => {
+ return (${source});
+ })();`;
+
+ // eslint-disable-next-line no-eval
+ let runnable = eval(runnablestr);
+ let iterator = runnable.call(this, aData.arg);
+ Task.spawn(iterator).then((val) => {
+ sendAsyncMessage("chrome-task:complete", {
+ id,
+ result: val,
+ });
+ }, (e) => {
+ sendAsyncMessage("chrome-task:complete", {
+ id,
+ error: e.toString(),
+ });
+ });
+ } catch (e) {
+ sendAsyncMessage("chrome-task:complete", {
+ id,
+ error: e.toString(),
+ });
+ }
+ });
+}
+
+
+/**
+ * This object provides the public module functions.
+ */
+var ChromeTask = {
+ /**
+ * the ChromeScript if it has already been loaded.
+ */
+ _chromeScript: null,
+
+ /**
+ * Mapping from message id to associated promise.
+ */
+ _promises: new Map(),
+
+ /**
+ * Incrementing integer to generate unique message id.
+ */
+ _messageID: 1,
+
+ /**
+ * Creates and starts a new task in the chrome process.
+ *
+ * @param arg A single serializable argument that will be passed to the
+ * task when executed on the content process.
+ * @param task
+ * - A generator or function which will be serialized and sent to
+ * the remote browser to be executed. Unlike Task.spawn, this
+ * argument may not be an iterator as it will be serialized and
+ * sent to the remote browser.
+ * @return A promise object where you can register completion callbacks to be
+ * called when the task terminates.
+ * @resolves With the final returned value of the task if it executes
+ * successfully.
+ * @rejects An error message if execution fails.
+ */
+ spawn: function ChromeTask_spawn(arg, task) {
+ // Load the frame script if needed.
+ let handle = ChromeTask._chromeScript;
+ if (!handle) {
+ handle = SpecialPowers.loadChromeScript(ChromeTask_ChromeScript);
+ handle.addMessageListener("chrome-task:complete", ChromeTask.onComplete);
+ handle.addMessageListener("chrome-task:test-result", ChromeTask.onResult);
+ handle.addMessageListener("chrome-task:test-info", ChromeTask.onInfo);
+ handle.addMessageListener("chrome-task:test-todo", ChromeTask.onTodo);
+ handle.addMessageListener("chrome-task:test-todo_is", ChromeTask.onTodoIs);
+ ChromeTask._chromeScript = handle;
+ }
+
+ let deferred = {};
+ deferred.promise = new Promise((resolve, reject) => {
+ deferred.resolve = resolve;
+ deferred.reject = reject;
+ });
+
+ let id = ChromeTask._messageID++;
+ ChromeTask._promises.set(id, deferred);
+
+ handle.sendAsyncMessage(
+ "chrome-task:spawn",
+ {
+ id,
+ runnable: task.toString(),
+ arg,
+ });
+
+ return deferred.promise;
+ },
+
+ onComplete(aData) {
+ let deferred = ChromeTask._promises.get(aData.id);
+ ChromeTask._promises.delete(aData.id);
+
+ if (aData.error) {
+ deferred.reject(aData.error);
+ } else {
+ deferred.resolve(aData.result);
+ }
+ },
+
+ onResult(aData) {
+ SimpleTest.record(aData.condition, aData.name);
+ },
+
+ onInfo(aData) {
+ SimpleTest.info(aData.name);
+ },
+
+ onTodo(aData) {
+ SimpleTest.todo(aData.expr, aData.name);
+ },
+
+ onTodoIs(aData) {
+ SimpleTest.todo_is(aData.a, aData.b, aData.name);
+ },
+};
diff --git a/testing/mochitest/tests/SimpleTest/moz.build b/testing/mochitest/tests/SimpleTest/moz.build
index 2ea92577e607..4bb6b989fbc7 100644
--- a/testing/mochitest/tests/SimpleTest/moz.build
+++ b/testing/mochitest/tests/SimpleTest/moz.build
@@ -8,6 +8,7 @@ TEST_HARNESS_FILES.testing.mochitest.tests.SimpleTest += [
'/docshell/test/chrome/docshell_helpers.js',
'/testing/specialpowers/content/MozillaLogger.js',
'AddTask.js',
+ 'ChromeTask.js',
'EventUtils.js',
'ExtensionTestUtils.js',
'iframe-between-tests.html',