Bug 1072467 - Add tests for e10s add-on shims (r=mconley,ted)
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 07 Oct 2014 11:46:24 -0700
changeset 209230 839f8ca256d48b595691bb54ddcf6ced0a253600
parent 209229 0c77607466894b4c22465c360d853e208e979898
child 209231 e5c5d33293aa89bcae840849adba3438d57810c5
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmconley, ted
bugs1072467
milestone35.0a1
Bug 1072467 - Add tests for e10s add-on shims (r=mconley,ted)
toolkit/components/addoncompat/moz.build
toolkit/components/addoncompat/tests/Makefile.in
toolkit/components/addoncompat/tests/addon/bootstrap.js
toolkit/components/addoncompat/tests/addon/chrome.manifest
toolkit/components/addoncompat/tests/addon/install.rdf
toolkit/components/addoncompat/tests/browser/browser.ini
toolkit/components/addoncompat/tests/browser/browser_addonShims.js
toolkit/components/addoncompat/tests/browser/browser_addonShims_testpage.html
toolkit/components/addoncompat/tests/browser/browser_addonShims_testpage2.html
toolkit/components/addoncompat/tests/moz.build
--- a/toolkit/components/addoncompat/moz.build
+++ b/toolkit/components/addoncompat/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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_DIRS += ['tests']
+
 EXTRA_COMPONENTS += [
     'addoncompat.manifest',
     'multiprocessShims.js',
 ]
 
 EXTRA_JS_MODULES += [
     'RemoteAddonsChild.jsm',
     'RemoteAddonsParent.jsm',
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/Makefile.in
@@ -0,0 +1,15 @@
+# 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/.
+
+include $(topsrcdir)/config/rules.mk
+
+# This is so hacky. Waiting on bug 988938.
+addondir = $(srcdir)/addon
+TESTROOT = $(CURDIR)/$(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+testdir = $(TESTROOT)/browser
+
+libs::
+	$(EXIT_ON_ERROR) \
+	$(NSINSTALL) -D $(testdir); \
+	(cd $(addondir) && zip -qr $(testdir)/addon.xpi *)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/addon/bootstrap.js
@@ -0,0 +1,266 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+const baseURL = "http://mochi.test:8888/browser/" +
+  "toolkit/components/addoncompat/tests/browser/";
+
+function forEachWindow(f)
+{
+  let wins = Services.ww.getWindowEnumerator("navigator:browser");
+  while (wins.hasMoreElements()) {
+    let win = wins.getNext();
+    if (win.gBrowser) {
+      f(win);
+    }
+  }
+}
+
+function addLoadListener(target, listener)
+{
+  target.addEventListener("load", function handler(event) {
+    target.removeEventListener("load", handler, true);
+    return listener(event);
+  }, true);
+}
+
+let gWin;
+let gBrowser;
+let ok, is, info;
+
+// Make sure that the shims for window.content, browser.contentWindow,
+// and browser.contentDocument are working.
+function testContentWindow()
+{
+  return new Promise(function(resolve, reject) {
+    const url = baseURL + "browser_addonShims_testpage.html";
+    let tab = gBrowser.addTab(url);
+    gBrowser.selectedTab = tab;
+    let browser = tab.linkedBrowser;
+    addLoadListener(browser, function handler() {
+      ok(gWin.content, "content is defined on chrome window");
+      ok(browser.contentWindow, "contentWindow is defined");
+      ok(browser.contentDocument, "contentWindow is defined");
+      is(gWin.content, browser.contentWindow, "content === contentWindow");
+
+      ok(browser.contentDocument.getElementById("link"), "link present in document");
+
+      // FIXME: Waiting on bug 1073631.
+      //is(browser.contentWindow.wrappedJSObject.global, 3, "global available on document");
+
+      gBrowser.removeTab(tab);
+      resolve();
+    });
+  });
+}
+
+// Test for bug 1060046 and bug 1072607. We want to make sure that
+// adding and removing listeners works as expected.
+function testListeners()
+{
+  return new Promise(function(resolve, reject) {
+    const url1 = baseURL + "browser_addonShims_testpage.html";
+    const url2 = baseURL + "browser_addonShims_testpage2.html";
+
+    let tab = gBrowser.addTab(url2);
+    let browser = tab.linkedBrowser;
+    addLoadListener(browser, function handler() {
+      function dummyHandler() {}
+
+      // Test that a removed listener stays removed (bug
+      // 1072607). We're looking to make sure that adding and removing
+      // a listener here doesn't cause later listeners to fire more
+      // than once.
+      for (let i = 0; i < 5; i++) {
+        gBrowser.addEventListener("load", dummyHandler, true);
+        gBrowser.removeEventListener("load", dummyHandler, true);
+      }
+
+      // We also want to make sure that this listener doesn't fire
+      // after it's removed.
+      let loadWithRemoveCount = 0;
+      addLoadListener(browser, function handler1() {
+        loadWithRemoveCount++;
+        is(event.target.documentURI, url1, "only fire for first url");
+      });
+
+      // Load url1 and then url2. We want to check that:
+      // 1. handler1 only fires for url1.
+      // 2. handler2 only fires once for url1 (so the second time it
+      //    fires should be for url2).
+      let loadCount = 0;
+      browser.addEventListener("load", function handler2(event) {
+        loadCount++;
+        if (loadCount == 1) {
+          is(event.target.documentURI, url1, "first load is for first page loaded");
+          browser.loadURI(url2);
+        } else {
+          gBrowser.removeEventListener("load", handler2, true);
+
+          is(event.target.documentURI, url2, "second load is for second page loaded");
+          is(loadWithRemoveCount, 1, "load handler is only called once");
+
+          gBrowser.removeTab(tab);
+          resolve();
+        }
+      }, true);
+
+      browser.loadURI(url1);
+    });
+  });
+}
+
+// Test for bug 1059207. We want to make sure that adding a capturing
+// listener and a non-capturing listener to the same element works as
+// expected.
+function testCapturing()
+{
+  return new Promise(function(resolve, reject) {
+    let capturingCount = 0;
+    let nonCapturingCount = 0;
+
+    function capturingHandler(event) {
+      is(capturingCount, 0, "capturing handler called once");
+      is(nonCapturingCount, 0, "capturing handler called before bubbling handler");
+      capturingCount++;
+    }
+
+    function nonCapturingHandler(event) {
+      is(capturingCount, 1, "bubbling handler called after capturing handler");
+      is(nonCapturingCount, 0, "bubbling handler called once");
+      nonCapturingCount++;
+    }
+
+    gBrowser.addEventListener("mousedown", capturingHandler, true);
+    gBrowser.addEventListener("mousedown", nonCapturingHandler, false);
+
+    const url = baseURL + "browser_addonShims_testpage.html";
+    let tab = gBrowser.addTab(url);
+    let browser = tab.linkedBrowser;
+    addLoadListener(browser, function handler() {
+      let win = browser.contentWindow;
+      let event = win.document.createEvent("MouseEvents");
+      event.initMouseEvent("mousedown", true, false, win, 1,
+                           1, 0, 0, 0, // screenX, screenY, clientX, clientY
+                           false, false, false, false, // ctrlKey, altKey, shiftKey, metaKey
+                           0, null); // buttonCode, relatedTarget
+
+      let element = win.document.getElementById("output");
+      element.dispatchEvent(event);
+
+      is(capturingCount, 1, "capturing handler fired");
+      is(nonCapturingCount, 1, "bubbling handler fired");
+
+      gBrowser.removeEventListener("mousedown", capturingHandler, true);
+      gBrowser.removeEventListener("mousedown", nonCapturingHandler, false);
+
+      gBrowser.removeTab(tab);
+      resolve();
+    });
+  });
+}
+
+// Make sure we get observer notifications that normally fire in the
+// child.
+function testObserver()
+{
+  return new Promise(function(resolve, reject) {
+    let observerFired = 0;
+
+    function observer(subject, topic, data) {
+      Services.obs.removeObserver(observer, "document-element-inserted");
+      observerFired++;
+    }
+    Services.obs.addObserver(observer, "document-element-inserted", false);
+
+    let count = 0;
+    const url = baseURL + "browser_addonShims_testpage.html";
+    let tab = gBrowser.addTab(url);
+    let browser = tab.linkedBrowser;
+    browser.addEventListener("load", function handler() {
+      count++;
+      if (count == 1) {
+        browser.reload();
+      } else {
+        browser.removeEventListener("load", handler);
+
+        is(observerFired, 1, "got observer notification");
+
+        gBrowser.removeTab(tab);
+        resolve();
+      }
+    }, true);
+  });
+}
+
+// Test for bug 1072472. Make sure that creating a sandbox to run code
+// in the content window works. This is essentially a test for
+// Greasemonkey.
+function testSandbox()
+{
+  return new Promise(function(resolve, reject) {
+    const url = baseURL + "browser_addonShims_testpage.html";
+    let tab = gBrowser.addTab(url);
+    let browser = tab.linkedBrowser;
+    browser.addEventListener("load", function handler() {
+      browser.removeEventListener("load", handler);
+
+      let sandbox = Cu.Sandbox(browser.contentWindow,
+                               {sandboxPrototype: browser.contentWindow,
+                                wantXrays: false});
+      Cu.evalInSandbox("const unsafeWindow = window;", sandbox);
+      Cu.evalInSandbox("document.getElementById('output').innerHTML = 'hello';", sandbox);
+
+      is(browser.contentDocument.getElementById("output").innerHTML, "hello",
+         "sandbox code ran successfully");
+
+      gBrowser.removeTab(tab);
+      resolve();
+    }, true);
+  });
+}
+
+function runTests(win, funcs)
+{
+  ok = funcs.ok;
+  is = funcs.is;
+  info = funcs.info;
+
+  gWin = win;
+  gBrowser = win.gBrowser;
+
+  return testContentWindow().
+    then(testListeners).
+    then(testCapturing).
+    then(testObserver).
+    then(testSandbox);
+}
+
+/*
+ bootstrap.js API
+*/
+
+function startup(aData, aReason)
+{
+  forEachWindow(win => {
+    win.runAddonShimTests = (funcs) => runTests(win, funcs);
+  });
+}
+
+function shutdown(aData, aReason)
+{
+  forEachWindow(win => {
+    delete win.runAddonShimTests;
+  });
+}
+
+function install(aData, aReason)
+{
+}
+
+function uninstall(aData, aReason)
+{
+}
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/addon/chrome.manifest
@@ -0,0 +1,1 @@
+content addonshim1 content/
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/addon/install.rdf
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>test-addon-shim-1@tests.mozilla.org</em:id>
+    <em:version>1</em:version>
+    <em:type>2</em:type>
+    <em:bootstrap>true</em:bootstrap>
+
+    <!-- Front End MetaData -->
+    <em:name>Test addon shim 1</em:name>
+    <em:description>Test an add-on that needs multiprocess shims.</em:description>
+    <em:multiprocessCompatible>false</em:multiprocessCompatible>
+
+    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>toolkit@mozilla.org</em:id>
+        <em:minVersion>10.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/browser/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+  addon.xpi
+  browser_addonShims_testpage.html
+  browser_addonShims_testpage2.html
+generated-files =
+  addon.xpi
+
+[browser_addonShims.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/browser/browser_addonShims.js
@@ -0,0 +1,54 @@
+let {AddonManager} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
+
+const ADDON_URL = "http://example.com/browser/toolkit/components/addoncompat/tests/browser/addon.xpi";
+
+// Install a test add-on that will exercise e10s shims.
+//   url: Location of the add-on.
+function addAddon(url)
+{
+  info("Installing add-on: " + url);
+
+  return new Promise(function(resolve, reject) {
+    AddonManager.getInstallForURL(url, installer => {
+      installer.install();
+      let listener = {
+        onInstallEnded: function(addon, addonInstall) {
+          installer.removeListener(listener);
+
+          // Wait for add-on's startup scripts to execute. See bug 997408
+          executeSoon(function() {
+            resolve(addonInstall);
+          });
+        }
+      };
+      installer.addListener(listener);
+    }, "application/x-xpinstall");
+  });
+}
+
+// Uninstall a test add-on.
+//   addon: The addon reference returned from addAddon.
+function removeAddon(addon)
+{
+  info("Removing addon.");
+
+  return new Promise(function(resolve, reject) {
+    let listener = {
+      onUninstalled: function(uninstalledAddon) {
+        if (uninstalledAddon != addon) {
+          return;
+        }
+        AddonManager.removeAddonListener(listener);
+        resolve();
+      }
+    };
+    AddonManager.addAddonListener(listener);
+    addon.uninstall();
+  });
+}
+
+add_task(function* test_addon_shims() {
+  let addon = yield addAddon(ADDON_URL);
+  yield window.runAddonShimTests({ok: ok, is: is, info: info});
+  yield removeAddon(addon);
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/browser/browser_addonShims_testpage.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>shim test</title>
+</head>
+
+<body>
+Hello!
+
+<a href="browser_addonShims_testpage2.html" id="link">Link</a>
+<div id="output"></div>
+
+<script type="text/javascript">
+var global = 3;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/browser/browser_addonShims_testpage2.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>shim test</title>
+</head>
+
+<body>
+Hello!
+
+<a href="browser_addonShims_testpage.html" id="link">Link</a>
+
+<script type="text/javascript">
+var global = 5;
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/addoncompat/tests/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += ['browser/browser.ini']