Bug 935799 - Add mochitest-e10s-utils.js to help tests when run in an e10s environment. r=felipe,ted
authorMark Hammond <mhammond@skippinet.com.au>
Mon, 03 Mar 2014 09:33:30 +1100
changeset 171800 2ed56735cb6af92b1f7dd80dae1b084fa1e1fa24
parent 171799 435378ccdcbf72defbeaad95d491722d2bedf010
child 171801 6a9f8ca4c9b1f4c26e526dde27fb520181d9606a
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersfelipe, ted
bugs935799
milestone30.0a1
Bug 935799 - Add mochitest-e10s-utils.js to help tests when run in an e10s environment. r=felipe,ted
testing/mochitest/browser-test-overlay.xul
testing/mochitest/browser-test.js
testing/mochitest/jar.mn
testing/mochitest/mochitest-e10s-utils-content.js
testing/mochitest/mochitest-e10s-utils.js
--- a/testing/mochitest/browser-test-overlay.xul
+++ b/testing/mochitest/browser-test-overlay.xul
@@ -4,9 +4,10 @@
    - 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/. -->
 
 <overlay id="browserTestOverlay"
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"/>
   <script type="application/javascript" src="chrome://mochikit/content/browser-test.js"/>
   <script type="application/javascript" src="chrome://mochikit/content/cc-analyzer.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/mochitest-e10s-utils.js"/>
 </overlay>
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -57,16 +57,19 @@ function testOnLoad() {
                          .getInterface(Components.interfaces.nsIWebNavigation);
       webNav.loadURI(url, null, null, null, null);
     };
 
     var listener = 'data:,function doLoad(e) { var data=e.getData("data");removeEventListener("contentEvent", function (e) { doLoad(e); }, false, true);sendAsyncMessage("chromeEvent", {"data":data}); };addEventListener("contentEvent", function (e) { doLoad(e); }, false, true);';
     messageManager.loadFrameScript(listener, true);
     messageManager.addMessageListener("chromeEvent", messageHandler);
   }
+  if (gConfig.e10s) {
+    e10s_init();
+  }
 }
 
 function Tester(aTests, aDumper, aCallback) {
   this.dumper = aDumper;
   this.tests = aTests;
   this.callback = aCallback;
   this.openedWindows = {};
   this.openedURLs = {};
--- a/testing/mochitest/jar.mn
+++ b/testing/mochitest/jar.mn
@@ -1,15 +1,17 @@
 mochikit.jar:
 % content mochikit %content/
   content/browser-harness.xul (browser-harness.xul)
   content/browser-test.js (browser-test.js)
   content/browser-test-overlay.xul (browser-test-overlay.xul)
   content/cc-analyzer.js (cc-analyzer.js)
   content/chrome-harness.js (chrome-harness.js)
+  content/mochitest-e10s-utils.js (mochitest-e10s-utils.js)
+  content/mochitest-e10s-utils-content.js (mochitest-e10s-utils-content.js)
   content/harness.xul (harness.xul)
   content/redirect.html (redirect.html)
   content/server.js (server.js)
   content/chunkifyTests.js (chunkifyTests.js)
   content/manifestLibrary.js (manifestLibrary.js)
   content/dynamic/getMyDirectory.sjs (dynamic/getMyDirectory.sjs)
   content/static/harness.css (static/harness.css)
   content/tests/SimpleTest/ChromePowers.js (tests/SimpleTest/ChromePowers.js)
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/mochitest-e10s-utils-content.js
@@ -0,0 +1,16 @@
+// This is the content script for mochitest-e10s-utils
+
+// We hook up some events and forward them back to the parent for the tests
+// This is only a partial solution to tests using these events - tests which
+// check, eg, event.target is the content window are still likely to be
+// confused.
+// But it's a good start...
+["load", "DOMContentLoaded", "pageshow"].forEach(eventName => {
+  addEventListener(eventName, function eventHandler(event) {
+    // Some tests also rely on load events from, eg, iframes, so we should see
+    // if we can do something sane to support that too.
+    if (event.target == content.document) {
+      sendAsyncMessage("Test:Event", {name: event.type});
+    }
+  }, true);
+});
new file mode 100644
--- /dev/null
+++ b/testing/mochitest/mochitest-e10s-utils.js
@@ -0,0 +1,87 @@
+// Utilities for running tests in an e10s environment.
+
+// There are some tricks/shortcuts that test code takes that we don't see
+// in the real browser code.  These include setting content.location.href
+// (which doesn't work in test code with e10s enabled as the document object
+// is yet to be created), waiting for certain events the main browser doesn't
+// care about and so doesn't normally get special support, eg, the "pageshow"
+// or "load" events).
+// So we make some hacks to pretend these work in the test suite.
+
+// Ideally all these hacks could be removed, but this can only happen when
+// the tests are refactored to not use these tricks.  But this would be a huge
+// amount of work and is unlikely to happen anytime soon...
+
+const CONTENT_URL = "chrome://mochikit/content/mochitest-e10s-utils-content.js";
+
+// This is an object that is used as the "location" on a remote document or
+// window.  It will be overwritten as the real document and window are made
+// available.
+let locationStub = function(browser) {
+  this.browser = browser;
+};
+locationStub.prototype = {
+  get href() {
+    return this.browser.webNavigation.currentURI.spec;
+  },
+  set href(val) {
+    this.browser.loadURI(val);
+  },
+  assign: function(url) {
+    this.href = url;
+  }
+};
+
+// This object is used in place of contentWindow while we wait for it to be
+// overwritten as the real window becomes available.
+let TemporaryWindowStub = function(browser) {
+  this._locationStub = new locationStub(browser);
+};
+
+TemporaryWindowStub.prototype = {
+  // save poor developers from getting confused about why the window isn't
+  // working like a window should..
+  toString: function() {
+    return "[Window Stub for e10s tests]";
+  },
+  get location() {
+    return this._locationStub;
+  },
+  set location(val) {
+    this._locationStub.href = val;
+  },
+  get document() {
+    // so tests can say: document.location....
+    return this;
+  }
+};
+
+// An observer called when a new remote browser element is created.  We replace
+// the _contentWindow in new browsers with our TemporaryWindowStub object.
+function observeNewFrameloader(subject, topic, data) {
+  let browser = subject.QueryInterface(Ci.nsIFrameLoader).ownerElement;
+  browser._contentWindow = new TemporaryWindowStub(browser);
+}
+
+function e10s_init() {
+  // Use the global message manager to inject a content script into all browsers.
+  let globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
+                   .getService(Ci.nsIMessageListenerManager);
+  globalMM.loadFrameScript(CONTENT_URL, true);
+  globalMM.addMessageListener("Test:Event", function(message) {
+    let event = document.createEvent('HTMLEvents');
+    event.initEvent(message.data.name, true, true, {});
+    message.target.dispatchEvent(event);
+  });
+
+  // We add an observer so we can notice new <browser> elements created
+  Services.obs.addObserver(observeNewFrameloader, "remote-browser-shown", false);
+
+  // Listen for an 'oop-browser-crashed' event and log it so people analysing
+  // test logs have a clue about what is going on.
+  window.addEventListener("oop-browser-crashed", (event) => {
+    let uri = event.target.currentURI;
+    Cu.reportError("remote browser crashed while on " +
+                   (uri ? uri.spec : "<unknown>") + "\n");
+  }, true);
+}