Bug 1500257 part 9 - Add basic tests for out-of-process iframes. r=qdot
authorRyan Hunt <rhunt@eqrion.net>
Wed, 23 Jan 2019 11:08:40 -0600
changeset 458568 409364e06a94691655aaa3676cb473fd9ba38001
parent 458567 473bed49a2fd6d7a128ef11e778366b5beec897f
child 458627 c0d540e06ce3f60d32e25672456e828f162d4940
push id111857
push userrhunt@eqrion.net
push dateTue, 12 Feb 2019 02:34:21 +0000
treeherdermozilla-inbound@409364e06a94 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs1500257
milestone67.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 1500257 part 9 - Add basic tests for out-of-process iframes. r=qdot Differential Revision: https://phabricator.services.mozilla.com/D17449
dom/ipc/tests/file_dummy.html
dom/ipc/tests/mochitest.ini
dom/ipc/tests/test_force_oop_iframe.html
modules/libpref/init/all.js
testing/mochitest/tests/SimpleTest/ChromeTask.js
testing/mochitest/tests/SimpleTest/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/file_dummy.html
@@ -0,0 +1,1 @@
+<h1>This is a dummy file!</h1>
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -18,8 +18,12 @@ skip-if = toolkit == 'cocoa' # cocoa: di
 skip-if = !(crashreporter && !e10s && (toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows'))
 [test_temporaryfile_stream.html]
 skip-if = !e10s
 support-files =
   blob_verify.sjs
   !/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
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/test_force_oop_iframe.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/ChromeTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+"use strict";
+/* eslint-env mozilla/frame-script */
+
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({"set": [["browser.fission.oopif.attribute", true]]});
+
+  // This iframe should be loaded out of process. Unfortunately as of the time
+  // of this test's creation, many different events which we could use to detect
+  // this load have not been implemented yet.
+  let contentCreated = ChromeTask.spawn(null, async function() {
+    let wgp = await new Promise(resolve => {
+      function observer(parent) {
+        info("WGP with origin: " + parent.documentPrincipal.origin);
+        if (parent.documentPrincipal.origin !== "http://mochi.test:8888") {
+          return;
+        }
+
+        Services.obs.removeObserver(observer, "window-global-created");
+        resolve(parent);
+      }
+      Services.obs.addObserver(observer, "window-global-created");
+    });
+
+    is(wgp.isInProcess, false, "not in-process");
+    ok(wgp.rootFrameLoader, "Has frameloader");
+    ok(wgp.documentPrincipal, "Has document principal");
+  });
+
+  var iframe = document.createElement("iframe");
+  iframe.setAttribute("fission", "true");
+  iframe.setAttribute("src", "file_dummy.html");
+  document.body.appendChild(iframe);
+
+  // Check that this isn't loaded in-process, or using a nested tabParent object.
+  let frameLoader = SpecialPowers.wrap(iframe).frameLoader;
+  is(frameLoader.docShell, null);
+  is(frameLoader.tabParent, null);
+
+  await contentCreated;
+});
+
+</script>
+</body>
+</html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2677,17 +2677,17 @@ pref("security.notification_enable_delay
 
 #if defined(DEBUG) && !defined(ANDROID)
 pref("csp.about_uris_without_csp", "blank,printpreview,srcdoc,about,addons,cache-entry,config,crashes,debugging,devtools,downloads,home,memory,networking,newtab,performance,plugins,policies,profiles,restartrequired,searchreset,serviceworkers,sessionrestore,support,sync-log,telemetry,url-classifier,webrtc,welcomeback");
 // the following prefs are for testing purposes only.
 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.
 pref("security.signed_content.CSP.default", "script-src 'self'; style-src 'self'");
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
new file mode 100644
--- /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);
+  },
+};
--- a/testing/mochitest/tests/SimpleTest/moz.build
+++ b/testing/mochitest/tests/SimpleTest/moz.build
@@ -3,16 +3,17 @@
 # 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/.
 
 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',
     'LogController.js',
     'MemoryStats.js',
     'MockObjects.js',
     'NativeKeyCodes.js',
     'paint_listener.js',