From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001
From: "Matt A. Tobin" <mattatobin@localhost.localdomain>
Date: Fri, 2 Feb 2018 04:16:08 -0500
Subject: Add m-esr52 at 52.6.0

---
 .../originattributes/test/browser/.eslintrc.js     |   7 +
 .../originattributes/test/browser/browser.ini      |  64 ++++
 .../test/browser/browser_blobURLIsolation.js       |  97 ++++++
 .../test/browser/browser_broadcastChannel.js       |  47 +++
 .../originattributes/test/browser/browser_cache.js | 259 +++++++++++++++
 .../test/browser/browser_clientAuth.js             |  44 +++
 .../test/browser/browser_cookieIsolation.js        |  31 ++
 .../test/browser/browser_favicon_firstParty.js     | 343 +++++++++++++++++++
 .../test/browser/browser_favicon_userContextId.js  | 257 +++++++++++++++
 .../test/browser/browser_firstPartyIsolation.js    | 174 ++++++++++
 .../test/browser/browser_httpauth.js               |  54 +++
 .../test/browser/browser_imageCacheIsolation.js    |  80 +++++
 .../test/browser/browser_localStorageIsolation.js  |  24 ++
 .../test/browser/browser_sharedworker.js           |  26 ++
 .../originattributes/test/browser/dummy.html       |   9 +
 .../test/browser/file_broadcastChannel.html        |  16 +
 .../test/browser/file_broadcastChanneliFrame.html  |  15 +
 .../originattributes/test/browser/file_cache.html  |  25 ++
 .../test/browser/file_favicon.html                 |  11 +
 .../originattributes/test/browser/file_favicon.png | Bin 0 -> 344 bytes
 .../test/browser/file_favicon.png^headers^         |   1 +
 .../test/browser/file_favicon_cache.html           |  11 +
 .../test/browser/file_favicon_cache.png            | Bin 0 -> 344 bytes
 .../test/browser/file_favicon_thirdParty.html      |  11 +
 .../test/browser/file_firstPartyBasic.html         |   8 +
 .../test/browser/file_sharedworker.html            |  10 +
 .../test/browser/file_sharedworker.js              |   9 +
 .../test/browser/file_thirdPartyChild.audio.ogg    | Bin 0 -> 2603 bytes
 .../test/browser/file_thirdPartyChild.embed.png    | Bin 0 -> 95 bytes
 .../test/browser/file_thirdPartyChild.fetch.html   |   8 +
 .../test/browser/file_thirdPartyChild.iframe.html  |  18 +
 .../test/browser/file_thirdPartyChild.img.png      | Bin 0 -> 95 bytes
 .../test/browser/file_thirdPartyChild.import.js    |   1 +
 .../test/browser/file_thirdPartyChild.link.css     |   1 +
 .../test/browser/file_thirdPartyChild.object.png   | Bin 0 -> 95 bytes
 .../test/browser/file_thirdPartyChild.request.html |   8 +
 .../test/browser/file_thirdPartyChild.script.js    |   1 +
 .../browser/file_thirdPartyChild.sharedworker.js   |   1 +
 .../test/browser/file_thirdPartyChild.track.vtt    |  13 +
 .../test/browser/file_thirdPartyChild.video.ogv    | Bin 0 -> 16049 bytes
 .../browser/file_thirdPartyChild.worker.fetch.html |   8 +
 .../test/browser/file_thirdPartyChild.worker.js    |   9 +
 .../file_thirdPartyChild.worker.request.html       |   8 +
 .../browser/file_thirdPartyChild.worker.xhr.html   |   8 +
 .../test/browser/file_thirdPartyChild.xhr.html     |   8 +
 .../originattributes/test/browser/head.js          | 365 +++++++++++++++++++++
 .../originattributes/test/browser/test.html        |  20 ++
 .../originattributes/test/browser/test.js          |   1 +
 .../originattributes/test/browser/test.js^headers^ |   1 +
 .../originattributes/test/browser/test2.html       |  12 +
 .../originattributes/test/browser/test2.js         |   1 +
 .../test/browser/test2.js^headers^                 |   1 +
 .../test/browser/test_firstParty.html              |  15 +
 .../test/browser/test_firstParty_cookie.html       |  13 +
 .../browser/test_firstParty_html_redirect.html     |   9 +
 .../browser/test_firstParty_http_redirect.html     |   9 +
 .../test_firstParty_http_redirect.html^headers^    |   2 +
 .../test_firstParty_iframe_http_redirect.html      |  13 +
 .../test/browser/test_firstParty_postMessage.html  |  28 ++
 .../originattributes/test/browser/window.html      |  13 +
 .../test/browser/worker_blobify.js                 |  11 +
 .../test/browser/worker_deblobify.js               |  31 ++
 .../test/mochitest/file_empty.html                 |   2 +
 .../originattributes/test/mochitest/mochitest.ini  |   5 +
 .../test/mochitest/test_permissions_api.html       | 207 ++++++++++++
 65 files changed, 2484 insertions(+)
 create mode 100644 browser/components/originattributes/test/browser/.eslintrc.js
 create mode 100644 browser/components/originattributes/test/browser/browser.ini
 create mode 100644 browser/components/originattributes/test/browser/browser_blobURLIsolation.js
 create mode 100644 browser/components/originattributes/test/browser/browser_broadcastChannel.js
 create mode 100644 browser/components/originattributes/test/browser/browser_cache.js
 create mode 100644 browser/components/originattributes/test/browser/browser_clientAuth.js
 create mode 100644 browser/components/originattributes/test/browser/browser_cookieIsolation.js
 create mode 100644 browser/components/originattributes/test/browser/browser_favicon_firstParty.js
 create mode 100644 browser/components/originattributes/test/browser/browser_favicon_userContextId.js
 create mode 100644 browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
 create mode 100644 browser/components/originattributes/test/browser/browser_httpauth.js
 create mode 100644 browser/components/originattributes/test/browser/browser_imageCacheIsolation.js
 create mode 100644 browser/components/originattributes/test/browser/browser_localStorageIsolation.js
 create mode 100644 browser/components/originattributes/test/browser/browser_sharedworker.js
 create mode 100644 browser/components/originattributes/test/browser/dummy.html
 create mode 100644 browser/components/originattributes/test/browser/file_broadcastChannel.html
 create mode 100644 browser/components/originattributes/test/browser/file_broadcastChanneliFrame.html
 create mode 100644 browser/components/originattributes/test/browser/file_cache.html
 create mode 100644 browser/components/originattributes/test/browser/file_favicon.html
 create mode 100644 browser/components/originattributes/test/browser/file_favicon.png
 create mode 100644 browser/components/originattributes/test/browser/file_favicon.png^headers^
 create mode 100644 browser/components/originattributes/test/browser/file_favicon_cache.html
 create mode 100644 browser/components/originattributes/test/browser/file_favicon_cache.png
 create mode 100644 browser/components/originattributes/test/browser/file_favicon_thirdParty.html
 create mode 100644 browser/components/originattributes/test/browser/file_firstPartyBasic.html
 create mode 100644 browser/components/originattributes/test/browser/file_sharedworker.html
 create mode 100644 browser/components/originattributes/test/browser/file_sharedworker.js
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.audio.ogg
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.embed.png
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.fetch.html
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.img.png
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.import.js
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.link.css
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.object.png
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.request.html
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.script.js
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.track.vtt
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.video.ogv
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html
 create mode 100644 browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html
 create mode 100644 browser/components/originattributes/test/browser/head.js
 create mode 100644 browser/components/originattributes/test/browser/test.html
 create mode 100644 browser/components/originattributes/test/browser/test.js
 create mode 100644 browser/components/originattributes/test/browser/test.js^headers^
 create mode 100644 browser/components/originattributes/test/browser/test2.html
 create mode 100644 browser/components/originattributes/test/browser/test2.js
 create mode 100644 browser/components/originattributes/test/browser/test2.js^headers^
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty.html
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty_cookie.html
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty_html_redirect.html
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty_http_redirect.html
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty_http_redirect.html^headers^
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty_iframe_http_redirect.html
 create mode 100644 browser/components/originattributes/test/browser/test_firstParty_postMessage.html
 create mode 100644 browser/components/originattributes/test/browser/window.html
 create mode 100644 browser/components/originattributes/test/browser/worker_blobify.js
 create mode 100644 browser/components/originattributes/test/browser/worker_deblobify.js
 create mode 100644 browser/components/originattributes/test/mochitest/file_empty.html
 create mode 100644 browser/components/originattributes/test/mochitest/mochitest.ini
 create mode 100644 browser/components/originattributes/test/mochitest/test_permissions_api.html

(limited to 'browser/components/originattributes/test')

diff --git a/browser/components/originattributes/test/browser/.eslintrc.js b/browser/components/originattributes/test/browser/.eslintrc.js
new file mode 100644
index 000000000..7c8021192
--- /dev/null
+++ b/browser/components/originattributes/test/browser/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "../../../../../testing/mochitest/browser.eslintrc.js"
+  ]
+};
diff --git a/browser/components/originattributes/test/browser/browser.ini b/browser/components/originattributes/test/browser/browser.ini
new file mode 100644
index 000000000..61f674377
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -0,0 +1,64 @@
+[DEFAULT]
+tags = usercontextid firstpartyisolation originattributes
+support-files =
+  dummy.html
+  file_broadcastChannel.html
+  file_broadcastChanneliFrame.html
+  file_cache.html
+  file_favicon.html
+  file_favicon.png
+  file_favicon.png^headers^
+  file_favicon_cache.html
+  file_favicon_cache.png
+  file_favicon_thirdParty.html
+  file_firstPartyBasic.html
+  file_sharedworker.html
+  file_sharedworker.js
+  file_thirdPartyChild.audio.ogg
+  file_thirdPartyChild.embed.png
+  file_thirdPartyChild.fetch.html
+  file_thirdPartyChild.iframe.html
+  file_thirdPartyChild.img.png
+  file_thirdPartyChild.import.js
+  file_thirdPartyChild.link.css
+  file_thirdPartyChild.object.png
+  file_thirdPartyChild.request.html
+  file_thirdPartyChild.script.js
+  file_thirdPartyChild.sharedworker.js
+  file_thirdPartyChild.track.vtt
+  file_thirdPartyChild.video.ogv
+  file_thirdPartyChild.worker.fetch.html
+  file_thirdPartyChild.worker.js
+  file_thirdPartyChild.worker.request.html
+  file_thirdPartyChild.worker.xhr.html
+  file_thirdPartyChild.xhr.html
+  head.js
+  test.js
+  test.js^headers^
+  test.html
+  test2.html
+  test2.js
+  test2.js^headers^
+  test_firstParty.html
+  test_firstParty_cookie.html
+  test_firstParty_html_redirect.html
+  test_firstParty_http_redirect.html
+  test_firstParty_http_redirect.html^headers^
+  test_firstParty_iframe_http_redirect.html
+  test_firstParty_postMessage.html
+  window.html
+  worker_blobify.js
+  worker_deblobify.js
+
+[browser_broadcastChannel.js]
+[browser_cache.js]
+[browser_cookieIsolation.js]
+[browser_favicon_firstParty.js]
+[browser_favicon_userContextId.js]
+[browser_firstPartyIsolation.js]
+[browser_localStorageIsolation.js]
+[browser_blobURLIsolation.js]
+[browser_imageCacheIsolation.js]
+[browser_sharedworker.js]
+[browser_httpauth.js]
+[browser_clientAuth.js]
diff --git a/browser/components/originattributes/test/browser/browser_blobURLIsolation.js b/browser/components/originattributes/test/browser/browser_blobURLIsolation.js
new file mode 100644
index 000000000..1d1b7c8e1
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_blobURLIsolation.js
@@ -0,0 +1,97 @@
+/**
+ * Bug 1264573 - A test case for blob url isolation.
+ */
+
+const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/" +
+                  "originattributes/test/browser/file_firstPartyBasic.html";
+const SCRIPT_WORKER_BLOBIFY = "worker_blobify.js";
+const SCRIPT_WORKER_DEBLOBIFY = "worker_deblobify.js";
+
+function page_blobify(browser, input) {
+  return ContentTask.spawn(browser, input, function(input) {
+    return { blobURL: content.URL.createObjectURL(new content.Blob([input])) };
+  });
+}
+
+function page_deblobify(browser, blobURL) {
+  return ContentTask.spawn(browser, blobURL, function* (blobURL) {
+    if ("error" in blobURL) {
+      return blobURL;
+    }
+    blobURL = blobURL.blobURL;
+
+    function blobURLtoBlob(blobURL) {
+      return new content.Promise(function (resolve) {
+        let xhr = new content.XMLHttpRequest();
+        xhr.open("GET", blobURL, true);
+        xhr.onload = function () {
+          resolve(xhr.response);
+        };
+        xhr.onerror = function () {
+          resolve("xhr error");
+        };
+        xhr.responseType = "blob";
+        xhr.send();
+      });
+    }
+
+    function blobToString(blob) {
+      return new content.Promise(function (resolve) {
+        let fileReader = new content.FileReader();
+        fileReader.onload = function () {
+          resolve(fileReader.result);
+        };
+        fileReader.readAsText(blob);
+      });
+    }
+
+    let blob = yield blobURLtoBlob(blobURL);
+    if (blob == "xhr error") {
+      return "xhr error";
+    }
+
+    return yield blobToString(blob);
+  });
+}
+
+function workerIO(browser, scriptFile, message) {
+  return ContentTask.spawn(browser, {scriptFile, message}, function* (args) {
+    let worker = new content.Worker(args.scriptFile);
+    let promise = new content.Promise(function(resolve) {
+      let listenFunction = function(event) {
+        worker.removeEventListener("message", listenFunction, false);
+        worker.terminate();
+        resolve(event.data);
+      };
+      worker.addEventListener("message", listenFunction, false);
+    });
+    worker.postMessage(args.message);
+    return yield promise;
+  });
+}
+
+let worker_blobify = (browser, input) => workerIO(browser, SCRIPT_WORKER_BLOBIFY, input);
+let worker_deblobify = (browser, blobURL) => workerIO(browser, SCRIPT_WORKER_DEBLOBIFY, blobURL);
+
+function doTest(blobify, deblobify) {
+  let blobURL = null;
+  return function* (browser) {
+    if (blobURL === null) {
+      let input = Math.random().toString();
+      blobURL = yield blobify(browser, input);
+      return input;
+    }
+    let result = yield deblobify(browser, blobURL);
+    blobURL = null;
+    return result;
+  }
+}
+
+let tests = [];
+for (let blobify of [page_blobify, worker_blobify]) {
+  for (let deblobify of [page_deblobify, worker_deblobify]) {
+    tests.push(doTest(blobify, deblobify));
+  }
+}
+
+IsolationTestTools.runTests(TEST_PAGE, tests);
diff --git a/browser/components/originattributes/test/browser/browser_broadcastChannel.js b/browser/components/originattributes/test/browser/browser_broadcastChannel.js
new file mode 100644
index 000000000..3a2bd7405
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_broadcastChannel.js
@@ -0,0 +1,47 @@
+/*
+ * Bug 1264571 - A test case of broadcast channels for first party isolation.
+ */
+
+const TEST_DOMAIN = "http://example.net/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/originattributes/test/browser/";
+const TEST_PAGE = TEST_PATH + "file_broadcastChannel.html";
+
+function* doTest(aBrowser) {
+  let response = yield ContentTask.spawn(aBrowser, null, function* () {
+
+    let displayItem = content.document.getElementById("display");
+
+    // If there is nothing in the 'display', we will try to send a message to
+    // the broadcast channel and wait until this message has been delivered.
+    // The way that how we make sure the message is delivered is based on an
+    // iframe which will reply everything it receives from the broadcast channel
+    // to the current window through the postMessage. So, we can know that the
+    // boradcast message is sent successfully when the window receives a message
+    // from the iframe.
+    if (displayItem.innerHTML === "") {
+      let data = Math.random().toString();
+
+      let receivedData = yield new Promise(resolve => {
+        let listenFunc = event => {
+          content.removeEventListener("message", listenFunc);
+          resolve(event.data);
+        };
+
+        let bc = new content.BroadcastChannel("testBroadcastChannel");
+
+        content.addEventListener("message", listenFunc, false);
+        bc.postMessage(data);
+      });
+
+      is(receivedData, data, "The value should be the same.");
+
+      return receivedData;
+    }
+
+    return displayItem.innerHTML;
+  });
+
+  return response;
+}
+
+IsolationTestTools.runTests(TEST_PAGE, doTest);
diff --git a/browser/components/originattributes/test/browser/browser_cache.js b/browser/components/originattributes/test/browser/browser_cache.js
new file mode 100644
index 000000000..d5f3a8f58
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_cache.js
@@ -0,0 +1,259 @@
+/*
+ * Bug 1264577 - A test case for testing caches of various submodules.
+ *   This test case will load two pages that each page loads various resources
+ *   within the same third party domain for the same originAttributes or different
+ *   originAttributes. And then, it verifies the number of cache entries and
+ *   the originAttributes of loading channels. If these two pages belong to
+ *   the same originAttributes, the number of cache entries for a certain
+ *   resource would be one. Otherwise, it would be two.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+let {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", {});
+let protocolProxyService = Cc["@mozilla.org/network/protocol-proxy-service;1"]
+                             .getService(Ci.nsIProtocolProxyService);
+
+const TEST_DOMAIN = "http://example.net";
+const TEST_PATH = "/browser/browser/components/originattributes/test/browser/";
+const TEST_PAGE = TEST_DOMAIN + TEST_PATH + "file_cache.html";
+
+let suffixes = ["iframe.html", "link.css", "script.js", "img.png", "object.png",
+                "embed.png", "xhr.html", "worker.xhr.html", "audio.ogg",
+                "video.ogv", "track.vtt",
+                "fetch.html", "worker.fetch.html",
+                "request.html", "worker.request.html",
+                "import.js", "worker.js", "sharedworker.js"];
+
+// A random value for isolating video/audio elements across different tests.
+let randomSuffix;
+
+function clearAllImageCaches() {
+  let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  let imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function cacheDataForContext(loadContextInfo) {
+  return new Promise(resolve => {
+    let cacheEntries = [];
+    let cacheVisitor = {
+      onCacheStorageInfo(num, consumption) {},
+      onCacheEntryInfo(uri, idEnhance) {
+        cacheEntries.push({ uri: uri,
+                            idEnhance: idEnhance });
+      },
+      onCacheEntryVisitCompleted() {
+        resolve(cacheEntries);
+      },
+      QueryInterface(iid) {
+        if (iid.equals(Ci.nsICacheStorageVisitor))
+          return this;
+
+        throw Components.results.NS_ERROR_NO_INTERFACE;
+      }
+    };
+    // Visiting the disk cache also visits memory storage so we do not
+    // need to use Services.cache2.memoryCacheStorage() here.
+    let storage = Services.cache2.diskCacheStorage(loadContextInfo, false);
+    storage.asyncVisitStorage(cacheVisitor, true);
+  });
+}
+
+let countMatchingCacheEntries = function (cacheEntries, domain, fileSuffix) {
+  return cacheEntries.map(entry => entry.uri.asciiSpec)
+                     .filter(spec => spec.includes(domain))
+                     .filter(spec => spec.includes("file_thirdPartyChild." + fileSuffix))
+                     .length;
+};
+
+function observeChannels(onChannel) {
+  // We use a dummy proxy filter to catch all channels, even those that do not
+  // generate an "http-on-modify-request" notification, such as link preconnects.
+  let proxyFilter = {
+    applyFilter : function (aProxyService, aChannel, aProxy) {
+      // We have the channel; provide it to the callback.
+      onChannel(aChannel);
+      // Pass on aProxy unmodified.
+      return aProxy;
+    }
+  };
+  protocolProxyService.registerChannelFilter(proxyFilter, 0);
+  // Return the stop() function:
+  return () => protocolProxyService.unregisterChannelFilter(proxyFilter);
+}
+
+function startObservingChannels(aMode) {
+  let stopObservingChannels = observeChannels(function (channel) {
+    let originalURISpec = channel.originalURI.spec;
+    if (originalURISpec.includes("example.net")) {
+      let loadInfo = channel.loadInfo;
+
+      switch (aMode) {
+        case TEST_MODE_FIRSTPARTY:
+          ok(loadInfo.originAttributes.firstPartyDomain === "example.com" ||
+             loadInfo.originAttributes.firstPartyDomain === "example.org",
+             "first party for " + originalURISpec + " is " + loadInfo.originAttributes.firstPartyDomain);
+          break;
+
+        case TEST_MODE_NO_ISOLATION:
+          ok(ChromeUtils.isOriginAttributesEqual(loadInfo.originAttributes, ChromeUtils.fillNonDefaultOriginAttributes()),
+             "OriginAttributes for " + originalURISpec + " is default.");
+          break;
+
+        case TEST_MODE_CONTAINERS:
+          ok(loadInfo.originAttributes.userContextId === 1 ||
+             loadInfo.originAttributes.userContextId === 2,
+             "userContextId for " + originalURISpec + " is " + loadInfo.originAttributes.userContextId);
+          break;
+
+        default:
+          ok(false, "Unknown test mode.");
+      }
+    }
+  });
+  return stopObservingChannels;
+}
+
+let stopObservingChannels;
+
+// The init function, which clears image and network caches, and generates
+// the random value for isolating video and audio elements across different
+// test runs.
+function* doInit(aMode) {
+  yield SpecialPowers.pushPrefEnv({"set": [["network.predictor.enabled",         false],
+                                           ["network.predictor.enable-prefetch", false]]});
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  randomSuffix = Math.random();
+  stopObservingChannels = startObservingChannels(aMode);
+}
+
+// In the test function, we dynamically generate the video and audio element,
+// and assign a random suffix to their URL to isolate them across different
+// test runs.
+function* doTest(aBrowser) {
+
+  let argObj = {
+    randomSuffix: randomSuffix,
+    urlPrefix: TEST_DOMAIN + TEST_PATH,
+  };
+
+  yield ContentTask.spawn(aBrowser, argObj, function* (arg) {
+    let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.ogv";
+    let audioURL = arg.urlPrefix + "file_thirdPartyChild.audio.ogg";
+    let trackURL = arg.urlPrefix + "file_thirdPartyChild.track.vtt";
+    let URLSuffix = "?r=" + arg.randomSuffix;
+
+    // Create the audio and video elements.
+    let audio = content.document.createElement('audio');
+    let video = content.document.createElement('video');
+    let audioSource = content.document.createElement('source');
+    let audioTrack = content.document.createElement('track');
+
+    // Append the audio and track element into the body, and wait until they're finished.
+    yield new Promise(resolve => {
+      let audioLoaded = false;
+      let trackLoaded = false;
+
+      let audioListener = () => {
+        audio.removeEventListener("canplaythrough", audioListener);
+
+        audioLoaded = true;
+        if (audioLoaded && trackLoaded) {
+          resolve();
+        }
+      };
+
+      let trackListener = () => {
+        audioTrack.removeEventListener("load", trackListener);
+
+        trackLoaded = true;
+        if (audioLoaded && trackLoaded) {
+          resolve();
+        }
+      };
+
+      // Add the event listeners before everything in case we lose events.
+      audioTrack.addEventListener("load", trackListener, false);
+      audio.addEventListener("canplaythrough", audioListener, false);
+
+      // Assign attributes for the audio element.
+      audioSource.setAttribute("src", audioURL + URLSuffix);
+      audioSource.setAttribute("type", "audio/ogg");
+      audioTrack.setAttribute("src", trackURL);
+      audioTrack.setAttribute("kind", "subtitles");
+
+      audio.appendChild(audioSource);
+      audio.appendChild(audioTrack);
+      audio.autoplay = true;
+
+      content.document.body.appendChild(audio);
+    });
+
+    // Append the video element into the body, and wait until it's finished.
+    yield new Promise(resolve => {
+      let listener = () => {
+        video.removeEventListener("canplaythrough", listener);
+        resolve();
+      };
+
+      // Add the event listener before everything in case we lose the event.
+      video.addEventListener("canplaythrough", listener, false);
+
+      // Assign attributes for the video element.
+      video.setAttribute("src", videoURL + URLSuffix);
+      video.setAttribute("type", "video/ogg");
+
+      content.document.body.appendChild(video);
+    });
+  });
+
+  return 0;
+}
+
+// The check function, which checks the number of cache entries.
+function* doCheck(aShouldIsolate, aInputA, aInputB) {
+  let expectedEntryCount = 1;
+  let data = [];
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.default));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.private));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, {})));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { userContextId: 1 })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { userContextId: 1 })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { userContextId: 2 })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { userContextId: 2 })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { firstPartyDomain: "example.com" })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { firstPartyDomain: "example.com" })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(false, { firstPartyDomain: "example.org" })));
+  data = data.concat(yield cacheDataForContext(LoadContextInfo.custom(true, { firstPartyDomain: "example.org" })));
+
+  if (aShouldIsolate) {
+    expectedEntryCount = 2;
+  }
+
+  for (let suffix of suffixes) {
+    let foundEntryCount = countMatchingCacheEntries(data, "example.net", suffix);
+    let result = (expectedEntryCount === foundEntryCount);
+    ok(result, "Cache entries expected for " + suffix + ": " + expectedEntryCount +
+               ", and found " + foundEntryCount);
+  }
+
+  stopObservingChannels();
+  stopObservingChannels = undefined;
+  return true;
+}
+
+let testArgs = {
+  url: TEST_PAGE,
+  firstFrameSetting: DEFAULT_FRAME_SETTING,
+  secondFrameSetting: [TEST_TYPE_FRAME],
+};
+
+IsolationTestTools.runTests(testArgs, doTest, doCheck, doInit);
diff --git a/browser/components/originattributes/test/browser/browser_clientAuth.js b/browser/components/originattributes/test/browser/browser_clientAuth.js
new file mode 100644
index 000000000..48961dce0
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_clientAuth.js
@@ -0,0 +1,44 @@
+let certCached = true;
+let secondTabStarted = false;
+
+function onCertDialogLoaded(subject) {
+  certCached = false;
+  // Click OK.
+  subject.acceptDialog();
+}
+
+Services.obs.addObserver(onCertDialogLoaded, "cert-dialog-loaded", false);
+
+registerCleanupFunction(() => {
+  Services.obs.removeObserver(onCertDialogLoaded, "cert-dialog-loaded");
+});
+
+function* setup() {
+  yield SpecialPowers.pushPrefEnv({
+    set: [["security.default_personal_cert", "Ask Every Time"]]
+  });
+}
+
+function getResult() {
+  // The first tab always returns true.
+  if (!secondTabStarted) {
+    certCached = true;
+    secondTabStarted = true;
+    return true;
+  }
+
+  // The second tab returns true if the cert is cached, so it will be different
+  // from the result of the first tab, and considered isolated.
+  let ret = certCached;
+  certCached = true;
+  secondTabStarted = false;
+  return ret;
+}
+
+// aGetResultImmediately must be true because we need to get the result before
+// the next tab is opened.
+IsolationTestTools.runTests("https://requireclientcert.example.com",
+                            getResult,
+                            null, // aCompareResultFunc
+                            setup, // aBeginFunc
+                            true); // aGetResultImmediately
diff --git a/browser/components/originattributes/test/browser/browser_cookieIsolation.js b/browser/components/originattributes/test/browser/browser_cookieIsolation.js
new file mode 100644
index 000000000..6259723ba
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_cookieIsolation.js
@@ -0,0 +1,31 @@
+/**
+ * Bug 1312541 - A test case for document.cookie isolation.
+ */
+
+const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/" +
+                  "originattributes/test/browser/file_firstPartyBasic.html";
+
+// Use a random key so we don't access it in later tests.
+const key = "key" + Math.random().toString();
+const re = new RegExp(key + "=([0-9\.]+)");
+
+// Define the testing function
+function* doTest(aBrowser) {
+  return yield ContentTask.spawn(aBrowser, {key, re},
+                                 function ({key, re}) {
+    let result = re.exec(content.document.cookie);
+    if (result) {
+      return result[1];
+    }
+    // No value is found, so we create one.
+    let value = Math.random().toString();
+    content.document.cookie = key + "=" + value;
+    return value;
+  });
+}
+
+registerCleanupFunction(() => {
+    Services.cookies.removeAll();
+});
+
+IsolationTestTools.runTests(TEST_PAGE, doTest);
diff --git a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
new file mode 100644
index 000000000..b3a18947b
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
@@ -0,0 +1,343 @@
+/**
+ * Bug 1277803 - A test case for testing favicon loading across different first party domains.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+
+const FIRST_PARTY_ONE = "example.com";
+const FIRST_PARTY_TWO = "example.org";
+const THIRD_PARTY = "mochi.test:8888";
+
+const TEST_SITE_ONE = "http://" + FIRST_PARTY_ONE;
+const TEST_SITE_TWO = "http://" + FIRST_PARTY_TWO;
+const THIRD_PARTY_SITE = "http://" + THIRD_PARTY;
+const TEST_DIRECTORY = "/browser/browser/components/originattributes/test/browser/";
+
+const TEST_PAGE = TEST_DIRECTORY + "file_favicon.html";
+const TEST_THIRD_PARTY_PAGE = TEST_DIRECTORY + "file_favicon_thirdParty.html";
+const TEST_CACHE_PAGE = TEST_DIRECTORY + "file_favicon_cache.html";
+
+const FAVICON_URI = TEST_DIRECTORY + "file_favicon.png";
+const TEST_FAVICON_CACHE_URI = TEST_DIRECTORY + "file_favicon_cache.png";
+
+let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+
+function clearAllImageCaches() {
+  let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  let imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function clearAllPlacesFavicons() {
+  let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
+                          .getService(Ci.nsIFaviconService);
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "places-favicons-expired") {
+          resolve();
+          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "places-favicons-expired", false);
+    faviconService.expireAllFavicons();
+  });
+}
+
+function observeFavicon(aFirstPartyDomain, aExpectedCookie, aPageURI) {
+  let faviconReqXUL = false;
+  let faviconReqPlaces = false;
+  let expectedPrincipal = Services.scriptSecurityManager
+                                  .createCodebasePrincipal(aPageURI, { firstPartyDomain: aFirstPartyDomain });
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        // Make sure that the topic is 'http-on-modify-request'.
+        if (aTopic === "http-on-modify-request") {
+          // We check the firstPartyDomain for the originAttributes of the loading
+          // channel. All requests for the favicon should contain the correct
+          // firstPartyDomain. There are two requests for a favicon loading, one
+          // from the Places library and one from the XUL image. The difference
+          // of them is the loading principal. The Places will use the content
+          // principal and the XUL image will use the system principal.
+
+          let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+          let reqLoadInfo = httpChannel.loadInfo;
+          let loadingPrincipal = reqLoadInfo.loadingPrincipal;
+          let triggeringPrincipal = reqLoadInfo.triggeringPrincipal;
+
+          // Make sure this is a favicon request.
+          if (!httpChannel.URI.spec.endsWith(FAVICON_URI)) {
+            return;
+          }
+
+          // Check the first party domain.
+          is(reqLoadInfo.originAttributes.firstPartyDomain, aFirstPartyDomain,
+            "The loadInfo has correct first party domain");
+
+          if (loadingPrincipal.equals(systemPrincipal)) {
+            faviconReqXUL = true;
+            ok(triggeringPrincipal.equals(expectedPrincipal),
+              "The triggeringPrincipal of favicon loading from XUL should be the content principal.");
+          } else {
+            faviconReqPlaces = true;
+            ok(loadingPrincipal.equals(expectedPrincipal),
+              "The loadingPrincipal of favicon loading from Places should be the content prinicpal");
+          }
+
+          let faviconCookie = httpChannel.getRequestHeader("cookie");
+
+          is(faviconCookie, aExpectedCookie, "The cookie of the favicon loading is correct.");
+        } else {
+          ok(false, "Received unexpected topic: ", aTopic);
+        }
+
+        if (faviconReqXUL && faviconReqPlaces) {
+          Services.obs.removeObserver(observer, "http-on-modify-request", false);
+          resolve();
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-modify-request", false);
+  });
+}
+
+function waitOnFaviconResponse(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "http-on-examine-response" ||
+            aTopic === "http-on-examine-cached-response") {
+
+          let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+          let loadInfo = httpChannel.loadInfo;
+
+          if (httpChannel.URI.spec !== aFaviconURL) {
+            return;
+          }
+
+          let result = {
+            topic: aTopic,
+            firstPartyDomain: loadInfo.originAttributes.firstPartyDomain
+          };
+
+          resolve(result);
+          Services.obs.removeObserver(observer, "http-on-examine-response", false);
+          Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-examine-response", false);
+    Services.obs.addObserver(observer, "http-on-examine-cached-response", false);
+  });
+}
+
+function waitOnFaviconLoaded(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      onPageChanged(uri, attr, value, id) {
+
+        if (attr === Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON &&
+            value === aFaviconURL) {
+          resolve();
+          PlacesUtils.history.removeObserver(observer, false);
+        }
+      },
+    };
+
+    PlacesUtils.history.addObserver(observer, false);
+  });
+}
+
+function* openTab(aURL) {
+  let tab = gBrowser.addTab(aURL);
+
+  // Select tab and make sure its browser is focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerGlobal.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return {tab, browser};
+}
+
+function* assignCookiesUnderFirstParty(aURL, aFirstParty, aCookieValue) {
+  // Open a tab under the given aFirstParty, and this tab will have an
+  // iframe which loads the aURL.
+  let tabInfo = yield openTabInFirstParty(aURL, aFirstParty);
+
+  // Add cookies into the iframe.
+  yield ContentTask.spawn(tabInfo.browser, aCookieValue, function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+function* generateCookies(aThirdParty) {
+  // we generate two different cookies for two first party domains.
+  let cookies = [];
+  cookies.push(Math.random().toString());
+  cookies.push(Math.random().toString());
+
+  let firstSiteURL;
+  let secondSiteURL;
+
+  if (aThirdParty) {
+    // Add cookies into the third party site with different first party domain.
+    firstSiteURL = THIRD_PARTY_SITE;
+    secondSiteURL = THIRD_PARTY_SITE;
+  } else {
+    // Add cookies into sites.
+    firstSiteURL = TEST_SITE_ONE;
+    secondSiteURL = TEST_SITE_TWO;
+  }
+
+  yield assignCookiesUnderFirstParty(firstSiteURL, TEST_SITE_ONE, cookies[0]);
+  yield assignCookiesUnderFirstParty(secondSiteURL, TEST_SITE_TWO, cookies[1]);
+
+  return cookies;
+}
+
+function* doTest(aTestPage, aExpectedCookies, aFaviconURL) {
+  let firstPageURI = makeURI(TEST_SITE_ONE + aTestPage);
+  let secondPageURI = makeURI(TEST_SITE_TWO + aTestPage);
+
+  // Start to observe the event of that favicon has been fully loaded.
+  let promiseFaviconLoaded = waitOnFaviconLoaded(aFaviconURL);
+
+  // Start to observe the favicon requests earlier in case we miss it.
+  let promiseObserveFavicon = observeFavicon(FIRST_PARTY_ONE, aExpectedCookies[0], firstPageURI);
+
+  // Open the tab for the first site.
+  let tabInfo = yield openTab(TEST_SITE_ONE + aTestPage);
+
+  // Waiting until favicon requests are all made.
+  yield promiseObserveFavicon;
+
+  // Waiting until favicon loaded.
+  yield promiseFaviconLoaded;
+
+  // Close the tab.
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+
+  // Start to observe the favicon requests earlier in case we miss it.
+  promiseObserveFavicon = observeFavicon(FIRST_PARTY_TWO, aExpectedCookies[1], secondPageURI);
+
+  // Open the tab for the second site.
+  tabInfo = yield openTab(TEST_SITE_TWO + aTestPage);
+
+  // Waiting until favicon requests are all made.
+  yield promiseObserveFavicon;
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+add_task(function* setup() {
+  // Make sure first party isolation is enabled.
+  yield SpecialPowers.pushPrefEnv({"set": [
+      ["privacy.firstparty.isolate", true]
+  ]});
+});
+
+// A clean up function to prevent affecting other tests.
+registerCleanupFunction(() => {
+  // Clear all cookies.
+  let cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
+                     .getService(Ci.nsICookieManager);
+  cookieMgr.removeAll();
+
+  // Clear all image caches and network caches.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+});
+
+add_task(function* test_favicon_firstParty() {
+  for (let testThirdParty of [false, true]) {
+    // Clear all image caches and network caches before running the test.
+    clearAllImageCaches();
+
+    let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+    networkCache.clear();
+
+    // Clear Places favicon caches.
+    yield clearAllPlacesFavicons();
+
+    let cookies = yield generateCookies(testThirdParty);
+
+    if (testThirdParty) {
+      yield doTest(TEST_THIRD_PARTY_PAGE, cookies, THIRD_PARTY_SITE + FAVICON_URI);
+    } else {
+      yield doTest(TEST_PAGE, cookies, TEST_SITE_ONE + FAVICON_URI);
+    }
+  }
+});
+
+add_task(function* test_favicon_cache_firstParty() {
+  // Clear all image caches and network caches before running the test.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Start to observer the event of that favicon has been fully loaded and cached.
+  let promiseForFaviconLoaded = waitOnFaviconLoaded(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Start to observer for the favicon response of the first tab.
+  let responsePromise = waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Open the tab for the first site.
+  let tabInfoA = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
+
+  // Waiting for the favicon response.
+  let response = yield responsePromise;
+
+  // Make sure the favicon is loaded through the network and its first party domain is correct.
+  is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network.");
+  is(response.firstPartyDomain, FIRST_PARTY_ONE, "We should only observe the network response for the first first party.");
+
+  // Waiting until the favicon has been loaded and cached.
+  yield promiseForFaviconLoaded;
+
+  // Open the tab again for checking the image cache is working correctly.
+  let tabInfoB = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
+
+  // Start to observe the favicon response, the second tab actually will not
+  // make any network request since the favicon will be loaded by the cache for
+  // both Places and XUL image. So here, we are going to observe the favicon
+  // response for the third tab which opens with the second first party.
+  let promiseForFaviconResponse = waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Open the tab for the second site.
+  let tabInfoC = yield openTab(TEST_SITE_TWO + TEST_CACHE_PAGE);
+
+  // Wait for the favicon response. In this case, we suppose to catch the
+  // response for the third tab but not the second tab since it will not
+  // go through the network.
+  response = yield promiseForFaviconResponse;
+
+  // Check that the favicon response has came from the network and it has the
+  // correct first party domain.
+  is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network again.");
+  is(response.firstPartyDomain, FIRST_PARTY_TWO, "We should only observe the network response for the second first party.");
+
+  yield BrowserTestUtils.removeTab(tabInfoA.tab);
+  yield BrowserTestUtils.removeTab(tabInfoB.tab);
+  yield BrowserTestUtils.removeTab(tabInfoC.tab);
+});
diff --git a/browser/components/originattributes/test/browser/browser_favicon_userContextId.js b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
new file mode 100644
index 000000000..507e0a6d4
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
@@ -0,0 +1,257 @@
+/**
+ * Bug 1277803 - A test caes for testing favicon loading across different userContextId.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+  "resource://gre/modules/Promise.jsm");
+
+const TEST_SITE = "http://example.net";
+const TEST_THIRD_PARTY_SITE = "http://mochi.test:8888";
+
+const TEST_PAGE = TEST_SITE + "/browser/browser/components/originattributes/" +
+                  "test/browser/file_favicon.html";
+const FAVICON_URI = TEST_SITE + "/browser/browser/components/originattributes/" +
+                    "test/browser/file_favicon.png";
+const TEST_THIRD_PARTY_PAGE = "http://example.com/browser/browser/components/" +
+                              "originattributes/test/browser/file_favicon_thirdParty.html";
+const THIRD_PARTY_FAVICON_URI = TEST_THIRD_PARTY_SITE + "/browser/browser/components/" +
+                                "originattributes/test/browser/file_favicon.png";
+
+const USER_CONTEXT_ID_PERSONAL = 1;
+const USER_CONTEXT_ID_WORK     = 2;
+
+let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+
+function clearAllImageCaches() {
+  var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  var imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function clearAllPlacesFavicons() {
+  let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
+                          .getService(Ci.nsIFaviconService);
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "places-favicons-expired") {
+          resolve();
+          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "places-favicons-expired", false);
+    faviconService.expireAllFavicons();
+  });
+}
+
+function FaviconObserver(aUserContextId, aExpectedCookie, aPageURI, aFaviconURL) {
+  this.reset(aUserContextId, aExpectedCookie, aPageURI, aFaviconURL);
+}
+
+FaviconObserver.prototype = {
+  observe(aSubject, aTopic, aData) {
+    // Make sure that the topic is 'http-on-modify-request'.
+    if (aTopic === "http-on-modify-request") {
+      // We check the userContextId for the originAttributes of the loading
+      // channel. All requests for the favicon should contain the correct
+      // userContextId. There are two requests for a favicon loading, one
+      // from the Places library and one from the XUL image. The difference
+      // of them is the loading principal. The Places will use the content
+      // principal and the XUL image will use the system principal.
+
+      let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+      let reqLoadInfo = httpChannel.loadInfo;
+      let loadingPrincipal;
+      let triggeringPrincipal;
+
+      // Make sure this is a favicon request.
+      if (httpChannel.URI.spec !== this._faviconURL) {
+        return;
+      }
+
+      if (reqLoadInfo) {
+        loadingPrincipal = reqLoadInfo.loadingPrincipal;
+        triggeringPrincipal = reqLoadInfo.triggeringPrincipal;
+      }
+
+      // Check the userContextId.
+      is(reqLoadInfo.originAttributes.userContextId, this._curUserContextId,
+        "The loadInfo has correct userContextId");
+
+      if (loadingPrincipal.equals(systemPrincipal)) {
+        this._faviconReqXUL = true;
+        ok(triggeringPrincipal.equals(this._expectedPrincipal),
+          "The triggeringPrincipal of favicon loading from XUL should be the content principal.");
+      } else {
+        this._faviconReqPlaces = true;
+        ok(loadingPrincipal.equals(this._expectedPrincipal),
+          "The loadingPrincipal of favicon loading from Places should be the content prinicpal");
+      }
+
+      let faviconCookie = httpChannel.getRequestHeader("cookie");
+
+      is(faviconCookie, this._expectedCookie, "The cookie of the favicon loading is correct.");
+    } else {
+      ok(false, "Received unexpected topic: ", aTopic);
+    }
+
+    if (this._faviconReqXUL && this._faviconReqPlaces) {
+      this._faviconLoaded.resolve();
+    }
+  },
+
+  reset(aUserContextId, aExpectedCookie, aPageURI, aFaviconURL) {
+    this._curUserContextId = aUserContextId;
+    this._expectedCookie = aExpectedCookie;
+    this._expectedPrincipal = Services.scriptSecurityManager
+                                      .createCodebasePrincipal(aPageURI, { userContextId: aUserContextId });
+    this._faviconReqXUL = false;
+    this._faviconReqPlaces = false;
+    this._faviconURL = aFaviconURL;
+    this._faviconLoaded = new Promise.defer();
+  },
+
+  get promise() {
+    return this._faviconLoaded.promise;
+  }
+};
+
+function waitOnFaviconLoaded(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      onPageChanged(uri, attr, value, id) {
+
+        if (attr === Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON &&
+            value === aFaviconURL) {
+          resolve();
+          PlacesUtils.history.removeObserver(observer, false);
+        }
+      },
+    };
+
+    PlacesUtils.history.addObserver(observer, false);
+  });
+}
+
+function* generateCookies(aHost) {
+  // we generate two different cookies for two userContextIds.
+  let cookies = [];
+  cookies.push(Math.random().toString());
+  cookies.push(Math.random().toString());
+
+  // Then, we add cookies into the site for 'personal' and 'work'.
+  let tabInfoA = yield openTabInUserContext(aHost, USER_CONTEXT_ID_PERSONAL);
+  let tabInfoB = yield openTabInUserContext(aHost, USER_CONTEXT_ID_WORK);
+
+  yield ContentTask.spawn(tabInfoA.browser, cookies[0], function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield ContentTask.spawn(tabInfoB.browser, cookies[1], function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield BrowserTestUtils.removeTab(tabInfoA.tab);
+  yield BrowserTestUtils.removeTab(tabInfoB.tab);
+
+  return cookies;
+}
+
+function* doTest(aTestPage, aFaviconHost, aFaviconURL) {
+  let cookies = yield generateCookies(aFaviconHost);
+  let pageURI = makeURI(aTestPage);
+
+  // Create the observer object for observing request channels of the personal
+  // container.
+  let observer = new FaviconObserver(USER_CONTEXT_ID_PERSONAL, cookies[0], pageURI, aFaviconURL);
+
+  // Add the observer earlier in case we miss it.
+  let promiseWaitOnFaviconLoaded = waitOnFaviconLoaded(aFaviconURL);
+
+  Services.obs.addObserver(observer, "http-on-modify-request", false);
+
+  // Open the tab with the personal container.
+  let tabInfo = yield openTabInUserContext(aTestPage, USER_CONTEXT_ID_PERSONAL);
+
+  // Waiting for favicon requests are all made.
+  yield observer.promise;
+  // Waiting for favicon loaded.
+  yield promiseWaitOnFaviconLoaded;
+
+  // Close the tab.
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+
+  // Reset the observer for observing requests for the work container.
+  observer.reset(USER_CONTEXT_ID_WORK, cookies[1], pageURI, aFaviconURL);
+  tabInfo = yield openTabInUserContext(aTestPage, USER_CONTEXT_ID_WORK);
+
+  // Waiting for favicon requests are all made.
+  yield observer.promise;
+
+  Services.obs.removeObserver(observer, "http-on-modify-request", false);
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+add_task(function* setup() {
+  // Make sure userContext is enabled.
+  yield SpecialPowers.pushPrefEnv({"set": [
+      ["privacy.userContext.enabled", true]
+  ]});
+});
+
+// A clean up function to prevent affecting other tests.
+registerCleanupFunction(() => {
+  // Clear all cookies.
+  let cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
+                     .getService(Ci.nsICookieManager);
+  cookieMgr.removeAll();
+
+  // Clear all image caches and network caches.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear Places favicon caches.
+  clearAllPlacesFavicons();
+});
+
+add_task(function* test_favicon_userContextId() {
+  // Clear all image caches before running the test.
+  clearAllImageCaches();
+
+  // Clear all network caches.
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear Places favicon caches.
+  yield clearAllPlacesFavicons();
+
+  yield doTest(TEST_PAGE, TEST_SITE, FAVICON_URI);
+});
+
+add_task(function* test_thirdPartyFavicon_userContextId() {
+  // Clear all image caches before running the test.
+  clearAllImageCaches();
+
+  // Clear all network caches.
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear Places favicon caches.
+  yield clearAllPlacesFavicons();
+
+  yield doTest(TEST_THIRD_PARTY_PAGE, TEST_THIRD_PARTY_SITE, THIRD_PARTY_FAVICON_URI);
+});
diff --git a/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js b/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
new file mode 100644
index 000000000..ddda6afae
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_firstPartyIsolation.js
@@ -0,0 +1,174 @@
+const BASE_URL = "http://mochi.test:8888/browser/browser/components/originattributes/test/browser/";
+const BASE_DOMAIN = "mochi.test";
+
+add_task(function* setup() {
+  Services.prefs.setBoolPref("privacy.firstparty.isolate", true);
+  registerCleanupFunction(function () {
+    Services.prefs.clearUserPref("privacy.firstparty.isolate");
+  });
+});
+
+/**
+ * Test for the top-level document and child iframes should have the
+ * firstPartyDomain attribute.
+ */
+add_task(function* principal_test() {
+  let tab = gBrowser.addTab(BASE_URL + "test_firstParty.html");
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser, true, function (url) {
+    return url == BASE_URL + "test_firstParty.html";
+  });
+
+  yield ContentTask.spawn(tab.linkedBrowser, { firstPartyDomain: BASE_DOMAIN }, function* (attrs) {
+    info("document principal: " + content.document.nodePrincipal.origin);
+    Assert.equal(docShell.getOriginAttributes().firstPartyDomain, "",
+                 "top-level docShell shouldn't have firstPartyDomain attribute.");
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The document should have firstPartyDomain");
+
+    for (let i = 1; i < 4; i++) {
+      let iframe = content.document.getElementById("iframe" + i);
+      info("iframe principal: " + iframe.contentDocument.nodePrincipal.origin);
+      Assert.equal(iframe.frameLoader.docShell.getOriginAttributes().firstPartyDomain,
+                   attrs.firstPartyDomain, "iframe's docshell should have firstPartyDomain");
+      Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
+                   attrs.firstPartyDomain, "iframe should have firstPartyDomain");
+    }
+  });
+
+  gBrowser.removeTab(tab);
+});
+
+/**
+ * Test for the cookie jars of the top-level document and child iframe should be
+ * isolated by firstPartyDomain.
+ */
+add_task(function* cookie_test() {
+  let tab = gBrowser.addTab(BASE_URL + "test_firstParty_cookie.html");
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser, true);
+
+  let iter = Services.cookies.enumerator;
+  let count = 0;
+  while (iter.hasMoreElements()) {
+    count++;
+    let cookie = iter.getNext().QueryInterface(Ci.nsICookie2);
+    Assert.equal(cookie.value, "foo", "Cookie value should be foo");
+    Assert.equal(cookie.originAttributes.firstPartyDomain, BASE_DOMAIN, "Cookie's origin attributes should be " + BASE_DOMAIN);
+  }
+
+  // one cookie is from requesting test.js from top-level doc, and the other from
+  // requesting test2.js from iframe test2.html.
+  Assert.equal(count, 2, "Should have two cookies");
+
+  gBrowser.removeTab(tab);
+});
+
+/**
+ * Test for after redirect, the top-level document should update the firstPartyDomain
+ * attribute. However if the redirect is happening on the iframe, the attribute
+ * should remain the same.
+ */
+add_task(function* redirect_test() {
+  let tab = gBrowser.addTab(BASE_URL + "test_firstParty_http_redirect.html");
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  yield ContentTask.spawn(tab.linkedBrowser, { firstPartyDomain: "example.com" }, function* (attrs) {
+    info("document principal: " + content.document.nodePrincipal.origin);
+    info("document uri: " + content.document.documentURI);
+
+    Assert.equal(content.document.documentURI, "http://example.com/browser/browser/components/originattributes/test/browser/dummy.html",
+                 "The page should have been redirected to http://example.com/browser/browser/components/originattributes/test/browser/dummy.html");
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The document should have firstPartyDomain");
+  });
+
+  // Since this is a HTML redirect, we wait until the final page is loaded.
+  let tab2 = gBrowser.addTab(BASE_URL + "test_firstParty_html_redirect.html");
+  yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser, false, function(url) {
+    return url == "http://example.com/";
+  });
+
+  yield ContentTask.spawn(tab2.linkedBrowser, { firstPartyDomain: "example.com" }, function* (attrs) {
+    info("2nd tab document principal: " + content.document.nodePrincipal.origin);
+    info("2nd tab document uri: " + content.document.documentURI);
+    Assert.equal(content.document.documentURI, "http://example.com/",
+                 "The page should have been redirected to http://example.com");
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The document should have firstPartyDomain");
+  });
+
+  let tab3 = gBrowser.addTab(BASE_URL + "test_firstParty_iframe_http_redirect.html");
+  yield BrowserTestUtils.browserLoaded(tab3.linkedBrowser, true, function(url) {
+    return url == (BASE_URL + "test_firstParty_iframe_http_redirect.html");
+  });
+
+  // This redirect happens on the iframe, so unlike the two redirect tests above,
+  // the firstPartyDomain should still stick to the current top-level document,
+  // which is mochi.test.
+  yield ContentTask.spawn(tab3.linkedBrowser, { firstPartyDomain: "mochi.test" }, function* (attrs) {
+    let iframe = content.document.getElementById("iframe1");
+    info("iframe document principal: " + iframe.contentDocument.nodePrincipal.origin);
+    info("iframe document uri: " + iframe.contentDocument.documentURI);
+
+    Assert.equal(iframe.contentDocument.documentURI, "http://example.com/browser/browser/components/originattributes/test/browser/dummy.html",
+                 "The page should have been redirected to http://example.com/browser/browser/components/originattributes/test/browser/dummy.html");
+    Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The iframe should have firstPartyDomain: " + attrs.firstPartyDomain);
+  });
+
+  gBrowser.removeTab(tab);
+  gBrowser.removeTab(tab2);
+  gBrowser.removeTab(tab3);
+});
+
+/**
+ * Test for postMessage between document and iframe.
+ */
+add_task(function* postMessage_test() {
+  let tab = gBrowser.addTab(BASE_URL + "test_firstParty_postMessage.html");
+
+  // The top-level page will post a message to its child iframe, and wait for
+  // another message from the iframe, once it receives the message, it will
+  // create another iframe, dummy.html.
+  // So we wait until dummy.html is loaded
+  yield BrowserTestUtils.browserLoaded(tab.linkedBrowser, true, function (url) {
+    return url == BASE_URL + "dummy.html";
+  });
+
+  yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
+    info("document principal: " + content.document.nodePrincipal.origin);
+    let value = content.document.getElementById("message").textContent;
+    Assert.equal(value, "OK");
+  });
+
+  gBrowser.removeTab(tab);
+});
+
+/**
+ * When the web page calls window.open, the new window should have the same
+ * firstPartyDomain attribute.
+ */
+add_task(function* openWindow_test() {
+  Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+  registerCleanupFunction(function () {
+    Services.prefs.clearUserPref("browser.link.open_newwindow");
+  });
+
+  let tab = gBrowser.addTab(BASE_URL + "window.html");
+  let win = yield BrowserTestUtils.waitForNewWindow();
+
+  yield ContentTask.spawn(win.gBrowser.selectedBrowser, { firstPartyDomain: "mochi.test" }, function* (attrs) {
+    Assert.equal(docShell.getOriginAttributes().firstPartyDomain, attrs.firstPartyDomain,
+                 "window.open() should have firstPartyDomain attribute");
+    Assert.equal(content.document.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "The document should have firstPartyDomain");
+
+    let iframe = content.document.getElementById("iframe1");
+    Assert.equal(iframe.frameLoader.docShell.getOriginAttributes().firstPartyDomain,
+                 attrs.firstPartyDomain, "iframe's docshell should have firstPartyDomain");
+    Assert.equal(iframe.contentDocument.nodePrincipal.originAttributes.firstPartyDomain,
+                 attrs.firstPartyDomain, "iframe should have firstPartyDomain");
+  });
+
+  gBrowser.removeTab(tab);
+  yield BrowserTestUtils.closeWindow(win);
+});
+
diff --git a/browser/components/originattributes/test/browser/browser_httpauth.js b/browser/components/originattributes/test/browser/browser_httpauth.js
new file mode 100644
index 000000000..0b7b1540e
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_httpauth.js
@@ -0,0 +1,54 @@
+let Cu = Components.utils;
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+
+let server = new HttpServer();
+server.registerPathHandler('/file.html', fileHandler);
+server.start(-1);
+
+let BASE_URI = 'http://localhost:' + server.identity.primaryPort;
+let FILE_URI = BASE_URI + '/file.html';
+
+let credentialQueue = [];
+
+// Ask the user agent for authorization.
+function fileHandler(metadata, response) {
+  if (!metadata.hasHeader("Authorization")) {
+    response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+    response.setHeader("WWW-Authenticate", "Basic realm=\"User Visible Realm\"");
+    return;
+  }
+
+  // This will be "account:password" encoded in base64.
+  credentialQueue.push(metadata.getHeader("Authorization"));
+
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/html", false);
+  let body = "<html><body></body></html>";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function onCommonDialogLoaded(subject) {
+  // Submit random account and password
+  let dialog = subject.Dialog;
+  dialog.ui.loginTextbox.setAttribute("value", Math.random());
+  dialog.ui.password1Textbox.setAttribute("value", Math.random());
+  dialog.ui.button0.click();
+}
+
+Services.obs.addObserver(onCommonDialogLoaded, "common-dialog-loaded", false);
+
+registerCleanupFunction(() => {
+  Services.obs.removeObserver(onCommonDialogLoaded, "common-dialog-loaded");
+  server.stop(() => {
+    server = null;
+  });
+});
+
+function getResult() {
+  // If two targets are isolated, they should get different credentials.
+  // Otherwise, the credentials will be cached and therefore the same.
+  return credentialQueue.shift();
+}
+
+IsolationTestTools.runTests(FILE_URI, getResult);
+
diff --git a/browser/components/originattributes/test/browser/browser_imageCacheIsolation.js b/browser/components/originattributes/test/browser/browser_imageCacheIsolation.js
new file mode 100644
index 000000000..a24cec9ac
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_imageCacheIsolation.js
@@ -0,0 +1,80 @@
+/*
+ * Bug 1264572 - A test case for image cache isolation.
+ */
+
+requestLongerTimeout(2);
+
+let Cu = Components.utils;
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+
+const NUM_ISOLATION_LOADS = 2;
+const NUM_CACHED_LOADS = 1;
+
+let gHits = 0;
+
+let server = new HttpServer();
+server.registerPathHandler('/image.png', imageHandler);
+server.registerPathHandler('/file.html', fileHandler);
+server.start(-1);
+
+registerCleanupFunction(() => {
+  server.stop(() => {
+    server = null;
+  });
+});
+
+let BASE_URI = 'http://localhost:' + server.identity.primaryPort;
+let IMAGE_URI = BASE_URI + '/image.png';
+let FILE_URI = BASE_URI + '/file.html';
+
+function imageHandler(metadata, response) {
+  info('XXX: loading image from server');
+  gHits++;
+  response.setHeader("Cache-Control", "max-age=10000", false);
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "image/png", false);
+  var body = "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function fileHandler(metadata, response) {
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  response.setHeader("Content-Type", "text/html", false);
+  let body = `<html><body><image src=${IMAGE_URI}></body></html>`;
+  response.bodyOutputStream.write(body, body.length);
+}
+
+function doBefore() {
+  // reset hit counter
+  info('XXX resetting gHits');
+  gHits = 0;
+  info('XXX clearing image cache');
+  let imageCache = Cc["@mozilla.org/image/tools;1"]
+                      .getService(Ci.imgITools)
+                      .getImgCacheForDocument(null);
+  imageCache.clearCache(true);
+  imageCache.clearCache(false);
+  info('XXX clearning network cache');
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+}
+
+// the test function does nothing on purpose.
+function doTest(aBrowser) {
+  return 0;
+}
+
+// the check function
+function doCheck(shouldIsolate, a, b) {
+  // if we're doing first party isolation and the image cache isolation is
+  // working, then gHits should be 2 because the image would have been loaded
+  // one per first party domain.  if first party isolation is disabled, then
+  // gHits should be 1 since there would be one image load from the server and
+  // one load from the image cache.
+  info(`XXX check: gHits == ${gHits}, shouldIsolate == ${shouldIsolate}`);
+  return shouldIsolate ? gHits == NUM_ISOLATION_LOADS
+                       : gHits == NUM_CACHED_LOADS;
+}
+
+IsolationTestTools.runTests(FILE_URI, doTest, doCheck, doBefore);
diff --git a/browser/components/originattributes/test/browser/browser_localStorageIsolation.js b/browser/components/originattributes/test/browser/browser_localStorageIsolation.js
new file mode 100644
index 000000000..41bde80e4
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_localStorageIsolation.js
@@ -0,0 +1,24 @@
+/**
+ * Bug 1264567 - A test case for localStorage isolation.
+ */
+
+const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/" +
+                  "originattributes/test/browser/file_firstPartyBasic.html";
+
+// Use a random key so we don't access it in later tests.
+const key = Math.random().toString();
+
+// Define the testing function
+function* doTest(aBrowser) {
+  return yield ContentTask.spawn(aBrowser, key, function (key) {
+    let value = content.localStorage.getItem(key);
+    if (value === null) {
+      // No value is found, so we create one.
+      value = Math.random().toString();
+      content.localStorage.setItem(key, value);
+    }
+    return value;
+  });
+}
+
+IsolationTestTools.runTests(TEST_PAGE, doTest);
diff --git a/browser/components/originattributes/test/browser/browser_sharedworker.js b/browser/components/originattributes/test/browser/browser_sharedworker.js
new file mode 100644
index 000000000..7049407f6
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_sharedworker.js
@@ -0,0 +1,26 @@
+/**
+ * Bug 1264593 - A test case for the shared worker by first party isolation.
+ */
+
+const TEST_DOMAIN = "http://example.net/";
+const TEST_PATH = TEST_DOMAIN + "browser/browser/components/originattributes/test/browser/";
+const TEST_PAGE = TEST_PATH + "file_sharedworker.html";
+
+function* getResultFromSharedworker(aBrowser) {
+  let response = yield ContentTask.spawn(aBrowser, null, function* () {
+    let worker = new content.SharedWorker("file_sharedworker.js", "isolationSharedWorkerTest");
+
+    let result = yield new Promise(resolve => {
+      worker.port.onmessage = function (e) {
+        content.document.getElementById("display").innerHTML = e.data;
+        resolve(e.data);
+      };
+    });
+
+    return result;
+  });
+
+  return response;
+}
+
+IsolationTestTools.runTests(TEST_PAGE, getResultFromSharedworker);
diff --git a/browser/components/originattributes/test/browser/dummy.html b/browser/components/originattributes/test/browser/dummy.html
new file mode 100644
index 000000000..1a87e2840
--- /dev/null
+++ b/browser/components/originattributes/test/browser/dummy.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+<title>Dummy test page</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+</head>
+<body>
+<p>Dummy test page</p>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_broadcastChannel.html b/browser/components/originattributes/test/browser/file_broadcastChannel.html
new file mode 100644
index 000000000..14bd7a022
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_broadcastChannel.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Page broadcast channel creator for first party isolation</title>
+</head>
+<body>
+  <div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+  <iframe id="iframe" src="file_broadcastChanneliFrame.html"></iframe>>
+<script type="text/javascript;version=1.7">
+let bc = new BroadcastChannel("testBroadcastChannel");
+bc.onmessage = function (e) {
+  document.getElementById("display").innerHTML = e.data;
+};
+</script>
+</body>
diff --git a/browser/components/originattributes/test/browser/file_broadcastChanneliFrame.html b/browser/components/originattributes/test/browser/file_broadcastChanneliFrame.html
new file mode 100644
index 000000000..a2140e617
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_broadcastChanneliFrame.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Page broadcast channel responder for first party isolation</title>
+</head>
+<body>
+  <div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+<script type="text/javascript;version=1.7">
+let bc = new BroadcastChannel("testBroadcastChannel");
+bc.onmessage = function (e) {
+  window.parent.postMessage(e.data, "*");
+};
+</script>
+</body>
diff --git a/browser/components/originattributes/test/browser/file_cache.html b/browser/components/originattributes/test/browser/file_cache.html
new file mode 100644
index 000000000..788ec899d
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_cache.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<head>
+  <link rel="stylesheet" type="text/css"
+        href="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.link.css">
+  <link rel="preconnect" href="http://example.net">
+</head>
+<body>
+<div>file_cache.html</div>
+
+<iframe src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html">
+</iframe>
+
+<script src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js">
+</script>
+
+<img src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.img.png">
+
+<embed src="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.embed.png">
+
+<object data="http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.object.png"
+        type="image/png"></object>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_favicon.html b/browser/components/originattributes/test/browser/file_favicon.html
new file mode 100644
index 000000000..b571134e1
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="file_favicon.png" />
+  </head>
+  <body>
+    Favicon!!
+  </body>
+</html>
\ No newline at end of file
diff --git a/browser/components/originattributes/test/browser/file_favicon.png b/browser/components/originattributes/test/browser/file_favicon.png
new file mode 100644
index 000000000..5535363c9
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_favicon.png differ
diff --git a/browser/components/originattributes/test/browser/file_favicon.png^headers^ b/browser/components/originattributes/test/browser/file_favicon.png^headers^
new file mode 100644
index 000000000..9e23c73b7
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon.png^headers^
@@ -0,0 +1 @@
+Cache-Control: no-cache
diff --git a/browser/components/originattributes/test/browser/file_favicon_cache.html b/browser/components/originattributes/test/browser/file_favicon_cache.html
new file mode 100644
index 000000000..2a7343b8e
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon_cache.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="http://mochi.test:8888/browser/browser/components/originattributes/test/browser/file_favicon_cache.png" />
+  </head>
+  <body>
+    Third Party Favicon!!
+  </body>
+</html>
\ No newline at end of file
diff --git a/browser/components/originattributes/test/browser/file_favicon_cache.png b/browser/components/originattributes/test/browser/file_favicon_cache.png
new file mode 100644
index 000000000..5535363c9
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_favicon_cache.png differ
diff --git a/browser/components/originattributes/test/browser/file_favicon_thirdParty.html b/browser/components/originattributes/test/browser/file_favicon_thirdParty.html
new file mode 100644
index 000000000..4a2dd680a
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon_thirdParty.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="http://mochi.test:8888/browser/browser/components/originattributes/test/browser/file_favicon.png" />
+  </head>
+  <body>
+    Third Party Favicon!!
+  </body>
+</html>
\ No newline at end of file
diff --git a/browser/components/originattributes/test/browser/file_firstPartyBasic.html b/browser/components/originattributes/test/browser/file_firstPartyBasic.html
new file mode 100644
index 000000000..713187fb2
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_firstPartyBasic.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>First Party Isolation Tests</title>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_sharedworker.html b/browser/components/originattributes/test/browser/file_sharedworker.html
new file mode 100644
index 000000000..b9ff793bd
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_sharedworker.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Page SharedWorker creator for first party isolation</title>
+</head>
+<body>
+<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_sharedworker.js b/browser/components/originattributes/test/browser/file_sharedworker.js
new file mode 100644
index 000000000..82f075a37
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_sharedworker.js
@@ -0,0 +1,9 @@
+self.randomValue = Math.random();
+
+/* global onconnect:true */
+
+onconnect = function (e) {
+  let port = e.ports[0];
+  port.postMessage(self.randomValue);
+  port.start();
+};
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.audio.ogg b/browser/components/originattributes/test/browser/file_thirdPartyChild.audio.ogg
new file mode 100644
index 000000000..edda4e912
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_thirdPartyChild.audio.ogg differ
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.embed.png b/browser/components/originattributes/test/browser/file_thirdPartyChild.embed.png
new file mode 100644
index 000000000..c5916f289
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_thirdPartyChild.embed.png differ
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.fetch.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.fetch.html
new file mode 100644
index 000000000..037901ad0
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.fetch.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.fetch.html</div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html
new file mode 100644
index 000000000..b047d5b41
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.html</div>
+<script>
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", "http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html", true);
+  xhr.send();
+  var worker = new Worker("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js");
+  var sharedWorker = new SharedWorker("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js");
+
+  fetch("file_thirdPartyChild.fetch.html", {cache: "force-cache"} );
+  fetch(new Request("file_thirdPartyChild.request.html"), {cache: "force-cache"} );
+</script>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.img.png b/browser/components/originattributes/test/browser/file_thirdPartyChild.img.png
new file mode 100644
index 000000000..c5916f289
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_thirdPartyChild.img.png differ
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.import.js b/browser/components/originattributes/test/browser/file_thirdPartyChild.import.js
new file mode 100644
index 000000000..dbf8f8376
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.import.js
@@ -0,0 +1 @@
+// dummy script, to be called by self.importScripts(...)
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.link.css b/browser/components/originattributes/test/browser/file_thirdPartyChild.link.css
new file mode 100644
index 000000000..06d6e2672
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.link.css
@@ -0,0 +1 @@
+/* Dummy CSS file, used by browser_cache.js. */
\ No newline at end of file
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.object.png b/browser/components/originattributes/test/browser/file_thirdPartyChild.object.png
new file mode 100644
index 000000000..c5916f289
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_thirdPartyChild.object.png differ
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.request.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.request.html
new file mode 100644
index 000000000..108ed2ffa
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.request.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.request.html</div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js b/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js
new file mode 100644
index 000000000..6ddf436c0
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js
@@ -0,0 +1 @@
+// Dummy child script, used by browser_cache.js
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js b/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js
new file mode 100644
index 000000000..b262fa10a
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js
@@ -0,0 +1 @@
+// dummy file
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.track.vtt b/browser/components/originattributes/test/browser/file_thirdPartyChild.track.vtt
new file mode 100644
index 000000000..b37cb40e4
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.track.vtt
@@ -0,0 +1,13 @@
+WEBVTT FILE
+
+1
+00:00:00.500 --> 00:00:02.000 D:vertical A:start
+blah blah blah
+
+2
+00:00:02.500 --> 00:00:04.300
+this is a test
+
+3
+00:00:05.000 --> 00:00:07.000
+one more line
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.video.ogv b/browser/components/originattributes/test/browser/file_thirdPartyChild.video.ogv
new file mode 100644
index 000000000..68dee3cf2
Binary files /dev/null and b/browser/components/originattributes/test/browser/file_thirdPartyChild.video.ogv differ
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html
new file mode 100644
index 000000000..47e42d1e5
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.worker.fetch.html</div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js
new file mode 100644
index 000000000..b04e2c7de
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js
@@ -0,0 +1,9 @@
+var xhr = new XMLHttpRequest();
+xhr.open("GET", "http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html", true);
+xhr.send();
+
+fetch("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html", {cache: "force-cache"} );
+var myRequest = new Request("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html");
+fetch(myRequest, {cache: "force-cache"} );
+
+self.importScripts("http://example.net/browser/browser/components/originattributes/test/browser/file_thirdPartyChild.import.js");
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html
new file mode 100644
index 000000000..5b5c55bfe
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.worker.request.html</div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html
new file mode 100644
index 000000000..9fc107f37
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.xhr.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.worker.xhr.html</div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html b/browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html
new file mode 100644
index 000000000..f56e7b3c1
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_thirdPartyChild.xhr.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+<!-- The child page, used by browser_cache.js -->
+<body>
+<div>thirdPartyChild.html</div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/head.js b/browser/components/originattributes/test/browser/head.js
new file mode 100644
index 000000000..96559a10d
--- /dev/null
+++ b/browser/components/originattributes/test/browser/head.js
@@ -0,0 +1,365 @@
+/* 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';
+
+const TEST_URL_PATH = "/browser/browser/components/originattributes/test/browser/";
+
+// The flags of test modes.
+const TEST_MODE_FIRSTPARTY   = 0;
+const TEST_MODE_NO_ISOLATION = 1;
+const TEST_MODE_CONTAINERS   = 2;
+
+// The name of each mode.
+const TEST_MODE_NAMES = [ "first party isolation",
+                          "no isolation",
+                          "containers" ];
+
+// The frame types.
+const TEST_TYPE_FRAME  = 0;
+const TEST_TYPE_IFRAME = 1;
+
+// The default frame setting.
+const DEFAULT_FRAME_SETTING = [ TEST_TYPE_IFRAME ];
+
+let gFirstPartyBasicPage = TEST_URL_PATH + "file_firstPartyBasic.html";
+
+/**
+ * Add a tab for the given url with the specific user context id.
+ *
+ * @param aURL
+ *    The url of the page.
+ * @param aUserContextId
+ *    The user context id for this tab.
+ *
+ * @return tab     - The tab object of this tab.
+ *         browser - The browser object of this tab.
+ */
+function* openTabInUserContext(aURL, aUserContextId) {
+  // Open the tab in the correct userContextId.
+  let tab = gBrowser.addTab(aURL, {userContextId: aUserContextId});
+
+  // Select tab and make sure its browser is focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerGlobal.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return {tab, browser};
+}
+
+/**
+ * Add a tab for a page with the given first party domain. This page will have
+ * an iframe which is loaded with the given url by default or you could specify
+ * a frame setting to create nested frames. And this function will also modify
+ * the 'content' in the ContentTask to the target frame's window object.
+ *
+ * @param aURL
+ *    The url of the iframe.
+ * @param aFirstPartyDomain
+ *    The first party domain.
+ * @param aFrameSetting
+ *    This setting controls how frames are organized within the page. The
+ *    setting is an array of frame types, the first item indicates the
+ *    frame type (iframe or frame) of the first layer of the frame structure,
+ *    and the second item indicates the second layer, and so on. The aURL will
+ *    be loaded at the deepest layer. This is optional.
+ *
+ * @return tab     - The tab object of this tab.
+ *         browser - The browser object of this tab.
+ */
+function* openTabInFirstParty(aURL, aFirstPartyDomain,
+                              aFrameSetting = DEFAULT_FRAME_SETTING) {
+
+  // If the first party domain ends with '/', we remove it.
+  if (aFirstPartyDomain.endsWith('/')) {
+    aFirstPartyDomain = aFirstPartyDomain.slice(0, -1);
+  }
+
+  let basicPageURL = aFirstPartyDomain + gFirstPartyBasicPage;
+
+  // Open the tab for the basic first party page.
+  let tab = gBrowser.addTab(basicPageURL);
+
+  // Select tab and make sure its browser is focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerGlobal.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+
+  let pageArgs = { url: aURL,
+                   frames: aFrameSetting,
+                   typeFrame: TEST_TYPE_FRAME,
+                   typeIFrame: TEST_TYPE_IFRAME,
+                   basicFrameSrc: basicPageURL};
+
+  // Create the frame structure.
+  yield ContentTask.spawn(browser, pageArgs, function* (arg) {
+    let typeFrame = arg.typeFrame;
+    let typeIFrame = arg.typeIFrame;
+
+    // Redefine the 'content' for allowing us to change its target, and making
+    // ContentTask.spawn can directly work on the frame element.
+    this.frameWindow = content;
+
+    Object.defineProperty(this, "content", {
+      get: () => this.frameWindow
+    });
+
+    let frameElement;
+    let numOfLayers = 0;
+
+    for (let type of arg.frames) {
+      let document = content.document;
+      numOfLayers++;
+
+      if (type === typeFrame) {
+        // Add a frameset which carries the frame element.
+        let frameSet = document.createElement('frameset');
+        frameSet.cols = "50%,50%";
+
+        let frame = document.createElement('frame');
+        let dummyFrame = document.createElement('frame');
+
+        frameSet.appendChild(frame);
+        frameSet.appendChild(dummyFrame);
+
+        document.body.appendChild(frameSet);
+
+        frameElement = frame;
+      } else if (type === typeIFrame) {
+        // Add an iframe.
+        let iframe = document.createElement('iframe');
+        document.body.appendChild(iframe);
+
+        frameElement = iframe;
+      } else {
+        ok(false, "Invalid frame type.");
+        break;
+      }
+
+      // Wait for the frame to be loaded.
+      yield new Promise(done => {
+        frameElement.addEventListener("load", function loadEnd() {
+          frameElement.removeEventListener("load", loadEnd, true);
+          done();
+        }, true);
+
+        // If it is the deepest layer, we load the target URL. Otherwise, we
+        // load a basic page.
+        if (numOfLayers === arg.frames.length) {
+          frameElement.setAttribute("src", arg.url);
+        } else {
+          frameElement.setAttribute("src", arg.basicFrameSrc);
+        }
+      });
+
+      // Redirect the 'content' to the frame's window.
+      this.frameWindow = frameElement.contentWindow;
+    }
+  });
+
+  return {tab, browser};
+}
+
+this.IsolationTestTools = {
+  /**
+   * Adds isolation tests for first party isolation, no isolation
+   * and containers respectively.
+   *
+   * @param aTask
+   *    The testing task which will be run in different settings.
+   */
+  _add_task(aTask) {
+    add_task(function* addTaskForIsolationTests() {
+      let testSettings = [
+        { mode: TEST_MODE_FIRSTPARTY,
+          skip: false,
+          prefs: [["privacy.firstparty.isolate", true]]
+        },
+        { mode: TEST_MODE_NO_ISOLATION,
+          skip: false,
+          prefs: [["privacy.firstparty.isolate", false]]
+        },
+        { mode: TEST_MODE_CONTAINERS,
+          skip: false,
+          prefs: [["privacy.userContext.enabled", true]]
+        },
+      ];
+
+      // Add test tasks.
+      for (let testSetting of testSettings) {
+        IsolationTestTools._addTaskForMode(testSetting.mode,
+                                           testSetting.prefs,
+                                           testSetting.skip,
+                                           aTask);
+      }
+    });
+  },
+
+  _addTaskForMode(aMode, aPref, aSkip, aTask) {
+    if (aSkip) {
+      return;
+    }
+
+    add_task(function* () {
+      info("Starting the test for " + TEST_MODE_NAMES[aMode]);
+
+      // Before run this task, reset the preferences first.
+      yield SpecialPowers.flushPrefEnv();
+
+      // Make sure preferences are set properly.
+      yield SpecialPowers.pushPrefEnv({"set": aPref});
+
+      yield SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]});
+
+      yield aTask(aMode);
+    });
+  },
+
+  /**
+   * Add a tab with the given tab setting, this will open different types of
+   * tabs according to the given test mode. A tab setting means a isolation
+   * target in different test mode; a tab setting indicates a first party
+   * domain when testing the first party isolation, it is a user context
+   * id when testing containers.
+   *
+   * @param aMode
+   *    The test mode which decides what type of tabs will be opened.
+   * @param aURL
+   *    The url which is going to open.
+   * @param aTabSettingObj
+   *    The tab setting object includes 'firstPartyDomain' for the first party
+   *    domain and 'userContextId' for Containers.
+   * @param aFrameSetting
+   *    This setting controls how frames are organized within the page. The
+   *    setting is an array of frame types, the first item indicates the
+   *    frame type (iframe or frame) of the first layer of the frame structure,
+   *    and the second item indicates the second layer, and so on. The aURL
+   *    will be loaded at the deepest layer. This is optional.
+   *
+   * @return tab     - The tab object of this tab.
+   *         browser - The browser object of this tab.
+   */
+  _addTab(aMode, aURL, aTabSettingObj, aFrameSetting) {
+    if (aMode === TEST_MODE_CONTAINERS) {
+      return openTabInUserContext(aURL, aTabSettingObj.userContextId);
+    }
+
+    return openTabInFirstParty(aURL, aTabSettingObj.firstPartyDomain,
+                                aFrameSetting);
+
+  },
+
+  /**
+   * Run isolation tests. The framework will run tests with standard combinations
+   * of prefs and tab settings, and checks whether the isolation is working.
+   *
+   * @param aURL
+   *    The URL of the page that will be tested or an object contains 'url',
+   *    the tested page, 'firstFrameSetting' for the frame setting of the first
+   *    tab, and 'secondFrameSetting' for the second tab.
+   * @param aGetResultFuncs
+   *    An array of functions or a single function which are responsible for
+   *    returning the isolation result back to the framework for further checking.
+   *    Each of these functions will be provided the browser object of the tab,
+   *    that allows modifying or fetchings results from the page content.
+   * @param aCompareResultFunc
+   *    An optional function which allows modifying the way how does framework
+   *    check results. This function will be provided a boolean to indicate
+   *    the isolation is no or off and two results. This function should return
+   *    a boolean to tell that whether isolation is working. If this function
+   *    is not given, the framework will take case checking by itself.
+   * @param aBeforeFunc
+   *    An optional function which is called before any tabs are created so
+   *    that the test case can set up/reset local state.
+   * @param aGetResultImmediately
+   *    An optional boolean to ensure we get results before the next tab is opened.
+   */
+  runTests(aURL, aGetResultFuncs, aCompareResultFunc, aBeforeFunc,
+           aGetResultImmediately) {
+    let pageURL;
+    let firstFrameSetting;
+    let secondFrameSetting;
+
+    // Request a longer timeout since the test will run a test for three times
+    // with different settings. Thus, one test here represents three tests.
+    // For this reason, we triple the timeout.
+    requestLongerTimeout(3);
+
+    if (typeof aURL === "string") {
+      pageURL = aURL;
+    } else if (typeof aURL === "object") {
+      pageURL = aURL.url;
+      firstFrameSetting = aURL.firstFrameSetting;
+      secondFrameSetting = aURL.secondFrameSetting;
+    }
+
+    if (!Array.isArray(aGetResultFuncs)) {
+      aGetResultFuncs = [aGetResultFuncs];
+    }
+
+    let tabSettings = [
+                        { firstPartyDomain: "http://example.com", userContextId: 1},
+                        { firstPartyDomain: "http://example.org", userContextId: 2}
+                      ];
+
+    this._add_task(function* (aMode) {
+      let tabSettingA = 0;
+
+      for (let tabSettingB of [0, 1]) {
+        // Give the test a chance to set up before each case is run.
+        if (aBeforeFunc) {
+          yield aBeforeFunc(aMode);
+        }
+
+        // Create Tabs.
+        let tabInfoA = yield IsolationTestTools._addTab(aMode,
+                                                        pageURL,
+                                                        tabSettings[tabSettingA],
+                                                        firstFrameSetting);
+        let resultsA = [];
+        if (aGetResultImmediately) {
+          for (let getResultFunc of aGetResultFuncs) {
+            resultsA.push(yield getResultFunc(tabInfoA.browser));
+          }
+        }
+        let tabInfoB = yield IsolationTestTools._addTab(aMode,
+                                                        pageURL,
+                                                        tabSettings[tabSettingB],
+                                                        secondFrameSetting);
+        let i = 0;
+        for (let getResultFunc of aGetResultFuncs) {
+          // Fetch results from tabs.
+          let resultA = aGetResultImmediately ? resultsA[i++] :
+                        yield getResultFunc(tabInfoA.browser);
+          let resultB = yield getResultFunc(tabInfoB.browser);
+
+          // Compare results.
+          let result = false;
+          let shouldIsolate = (aMode !== TEST_MODE_NO_ISOLATION) &&
+                              tabSettingA !== tabSettingB;
+          if (aCompareResultFunc) {
+            result = yield aCompareResultFunc(shouldIsolate, resultA, resultB);
+          } else {
+            result = shouldIsolate ? resultA !== resultB :
+                                     resultA === resultB;
+          }
+
+          let msg = `Testing ${TEST_MODE_NAMES[aMode]} for ` +
+            `isolation ${shouldIsolate ? "on" : "off"} with TabSettingA ` +
+            `${tabSettingA} and tabSettingB ${tabSettingB}` +
+            `, resultA = ${resultA}, resultB = ${resultB}`;
+
+          ok(result, msg);
+        }
+
+        // Close Tabs.
+        yield BrowserTestUtils.removeTab(tabInfoA.tab);
+        yield BrowserTestUtils.removeTab(tabInfoB.tab);
+      }
+    });
+  }
+};
diff --git a/browser/components/originattributes/test/browser/test.html b/browser/components/originattributes/test/browser/test.html
new file mode 100644
index 000000000..214daa4b7
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1260931</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script>
+  window.onmessage = function (evt) {
+    if (evt.data != "HI") {
+      return;
+    }
+
+    window.parent.postMessage("OK", "http://mochi.test:8888");
+  };
+  </script>
+</head>
+<body>
+  Hello World.
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test.js b/browser/components/originattributes/test/browser/test.js
new file mode 100644
index 000000000..d290af9b0
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test.js
@@ -0,0 +1 @@
+var i = 1;
diff --git a/browser/components/originattributes/test/browser/test.js^headers^ b/browser/components/originattributes/test/browser/test.js^headers^
new file mode 100644
index 000000000..881f5bff0
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test.js^headers^
@@ -0,0 +1 @@
+Set-Cookie: test=foo
diff --git a/browser/components/originattributes/test/browser/test2.html b/browser/components/originattributes/test/browser/test2.html
new file mode 100644
index 000000000..370be1560
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1260931</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script src="test2.js"></script>
+</head>
+<body>
+  Hello World.
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test2.js b/browser/components/originattributes/test/browser/test2.js
new file mode 100644
index 000000000..d290af9b0
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test2.js
@@ -0,0 +1 @@
+var i = 1;
diff --git a/browser/components/originattributes/test/browser/test2.js^headers^ b/browser/components/originattributes/test/browser/test2.js^headers^
new file mode 100644
index 000000000..43604be7f
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test2.js^headers^
@@ -0,0 +1 @@
+Set-Cookie: test2=foo
diff --git a/browser/components/originattributes/test/browser/test_firstParty.html b/browser/components/originattributes/test/browser/test_firstParty.html
new file mode 100644
index 000000000..a90e01c4c
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>Test for Bug 1260931</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <div>
+    <iframe id="iframe1" src="http://example.com"></iframe>
+    <iframe id="iframe2" sandbox="" src="http://example.com"></iframe>
+    <iframe id="iframe3" sandbox="allow-same-origin" src="http://example.com"></iframe>
+  </div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test_firstParty_cookie.html b/browser/components/originattributes/test/browser/test_firstParty_cookie.html
new file mode 100644
index 000000000..44547c0d7
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_cookie.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1260931</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script src="test.js"></script>
+</head>
+<body>
+  Hello World.
+  <iframe id="iframe1" src="test2.html"></iframe>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test_firstParty_html_redirect.html b/browser/components/originattributes/test/browser/test_firstParty_html_redirect.html
new file mode 100644
index 000000000..3c52d4f8c
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_html_redirect.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf8" http-equiv="refresh" content="0; url=http://example.com/"/>
+  <title>Test for Bug 1260931</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test_firstParty_http_redirect.html b/browser/components/originattributes/test/browser/test_firstParty_http_redirect.html
new file mode 100644
index 000000000..7b794a011
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_http_redirect.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>Test for Bug 1260931</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test_firstParty_http_redirect.html^headers^ b/browser/components/originattributes/test/browser/test_firstParty_http_redirect.html^headers^
new file mode 100644
index 000000000..c6d2757aa
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_http_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Found
+Location: http://example.com/browser/browser/components/originattributes/test/browser/dummy.html
diff --git a/browser/components/originattributes/test/browser/test_firstParty_iframe_http_redirect.html b/browser/components/originattributes/test/browser/test_firstParty_iframe_http_redirect.html
new file mode 100644
index 000000000..fd7df46c1
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_iframe_http_redirect.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>Test for Bug 1260931</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <div>
+    <iframe id="iframe1" src="test_firstParty_http_redirect.html"></iframe>
+  </div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/test_firstParty_postMessage.html b/browser/components/originattributes/test/browser/test_firstParty_postMessage.html
new file mode 100644
index 000000000..5df8a5950
--- /dev/null
+++ b/browser/components/originattributes/test/browser/test_firstParty_postMessage.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8"/>
+  <title>Test for Bug 1260931</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+function onload() {
+  let iframe1 = document.getElementById("iframe1");
+  iframe1.contentWindow.postMessage("HI", "http://mochi.test:8888");
+}
+
+window.onmessage = function (evt) {
+  document.getElementById("message").textContent = evt.data;
+
+  let iframe2 = document.createElement("iframe");
+  iframe2.src = "dummy.html";
+  document.body.appendChild(iframe2);
+};
+</script>
+<body onload="onload()">
+  <div>
+    <iframe id="iframe1" src="test.html"></iframe>
+    <span id="message"></span>
+  </div>
+</body>
+</html>
diff --git a/browser/components/originattributes/test/browser/window.html b/browser/components/originattributes/test/browser/window.html
new file mode 100644
index 000000000..34216030c
--- /dev/null
+++ b/browser/components/originattributes/test/browser/window.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+  <head>
+    <meta charset="utf8">
+    <title>Page creating a popup</title>
+  </head>
+  <body>
+    <script type="text/javascript">
+       var w = window.open();
+       w.document.body.innerHTML = "<iframe id='iframe1' src='data:text/plain,test2'></iframe>";
+    </script>
+  </body>
+</html>
diff --git a/browser/components/originattributes/test/browser/worker_blobify.js b/browser/components/originattributes/test/browser/worker_blobify.js
new file mode 100644
index 000000000..56c0996a3
--- /dev/null
+++ b/browser/components/originattributes/test/browser/worker_blobify.js
@@ -0,0 +1,11 @@
+// Wait for a string to be posted to this worker.
+// Create a blob containing this string, and then
+// post back a blob URL pointing to the blob.
+self.addEventListener("message", function (e) {
+  try {
+    var blobURL = URL.createObjectURL(new Blob([e.data]));
+    postMessage({ blobURL });
+  } catch (e) {
+    postMessage({ error: e.message });
+  }
+}, false);
diff --git a/browser/components/originattributes/test/browser/worker_deblobify.js b/browser/components/originattributes/test/browser/worker_deblobify.js
new file mode 100644
index 000000000..1d6511a20
--- /dev/null
+++ b/browser/components/originattributes/test/browser/worker_deblobify.js
@@ -0,0 +1,31 @@
+// Wait for a blob URL to be posted to this worker.
+// Obtain the blob, and read the string contained in it.
+// Post back the string.
+
+var postStringInBlob = function (blobObject) {
+  var fileReader = new FileReaderSync();
+  var result = fileReader.readAsText(blobObject);
+  postMessage(result);
+};
+
+self.addEventListener("message", function (e) {
+  if ("error" in e.data) {
+    postMessage(e.data);
+    return;
+  }
+  var blobURL = e.data.blobURL,
+      xhr = new XMLHttpRequest();
+  try {
+    xhr.open("GET", blobURL, true);
+    xhr.onload = function () {
+      postStringInBlob(xhr.response);
+    };
+    xhr.onerror = function () {
+      postMessage({ error: "xhr error" });
+    };
+    xhr.responseType = "blob";
+    xhr.send();
+  } catch (e) {
+    postMessage({ error: e.message });
+  }
+}, false);
diff --git a/browser/components/originattributes/test/mochitest/file_empty.html b/browser/components/originattributes/test/mochitest/file_empty.html
new file mode 100644
index 000000000..bc98b4d2e
--- /dev/null
+++ b/browser/components/originattributes/test/mochitest/file_empty.html
@@ -0,0 +1,2 @@
+<h1>I'm just a support file</h1>
+<p>I get loaded to do permission testing.</p>
\ No newline at end of file
diff --git a/browser/components/originattributes/test/mochitest/mochitest.ini b/browser/components/originattributes/test/mochitest/mochitest.ini
new file mode 100644
index 000000000..5df9998ca
--- /dev/null
+++ b/browser/components/originattributes/test/mochitest/mochitest.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+support-files =
+  file_empty.html
+
+[test_permissions_api.html]
diff --git a/browser/components/originattributes/test/mochitest/test_permissions_api.html b/browser/components/originattributes/test/mochitest/test_permissions_api.html
new file mode 100644
index 000000000..63c74d1fe
--- /dev/null
+++ b/browser/components/originattributes/test/mochitest/test_permissions_api.html
@@ -0,0 +1,207 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <title>Test for Permissions API</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+
+<body>
+  <pre id="test"></pre>
+  <script type="application/javascript;version=1.8">
+  /*globals SpecialPowers, SimpleTest, is, ok, */
+  'use strict';
+
+  const {
+    UNKNOWN_ACTION,
+    PROMPT_ACTION,
+    ALLOW_ACTION,
+    DENY_ACTION
+  } = SpecialPowers.Ci.nsIPermissionManager;
+
+  SimpleTest.waitForExplicitFinish();
+
+  const PERMISSIONS = [{
+    name: 'geolocation',
+    type: 'geo'
+  }, {
+    name: 'notifications',
+    type: 'desktop-notification'
+  }, {
+    name: 'push',
+    type: 'desktop-notification'
+  }, ];
+
+  const UNSUPPORTED_PERMISSIONS = [
+    'foobarbaz', // Not in spec, for testing only.
+    'midi',
+  ];
+
+  // Create a closure, so that tests are run on the correct window object.
+  function createPermissionTester(aWindow) {
+    return {
+      setPermissions(allow) {
+        const permissions = PERMISSIONS.map(({ type }) => {
+          return {
+            type,
+            allow,
+            'context': aWindow.document
+          };
+        });
+        return new Promise((resolve) => {
+          SpecialPowers.popPermissions(() => {
+            SpecialPowers.pushPermissions(permissions, resolve);
+          });
+        });
+      },
+      revokePermissions() {
+        const promisesToRevoke = PERMISSIONS.map(({ name })  => {
+          return aWindow.navigator.permissions
+            .revoke({ name })
+            .then(
+              ({ state }) => is(state, 'prompt', `correct state for '${name}'`),
+              () => ok(false, `revoke should not have rejected for '${name}'`)
+            );
+        });
+        return Promise.all(promisesToRevoke);
+      },
+      revokeUnsupportedPermissions() {
+        const promisesToRevoke = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
+          return aWindow.navigator.permissions
+            .revoke({ name })
+            .then(
+              () => ok(false, `revoke should not have resolved for '${name}'`),
+              error => is(error.name, 'TypeError', `revoke should have thrown TypeError for '${name}'`)
+            );
+        });
+        return Promise.all(promisesToRevoke);
+      },
+      checkPermissions(state) {
+        const promisesToQuery = PERMISSIONS.map(({ name }) => {
+          return aWindow.navigator.permissions
+            .query({ name })
+            .then(
+              () => is(state, state, `correct state for '${name}'`),
+              () => ok(false, `query should not have rejected for '${name}'`)
+            );
+          });
+        return Promise.all(promisesToQuery);
+      },
+      checkUnsupportedPermissions() {
+        const promisesToQuery = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
+          return aWindow.navigator.permissions
+            .query({ name })
+            .then(
+              () => ok(false, `query should not have resolved for '${name}'`),
+              error => {
+                is(error.name, 'TypeError',
+                  `query should have thrown TypeError for '${name}'`);
+              }
+            );
+          });
+        return Promise.all(promisesToQuery);
+      },
+      promiseStateChanged(name, state) {
+        return aWindow.navigator.permissions
+          .query({ name })
+          .then(status => {
+            return new Promise( resolve => {
+              status.onchange = () => {
+                status.onchange = null;
+                is(status.state, state, `state changed for '${name}'`);
+                resolve();
+              };
+            });
+          },
+          () => ok(false, `query should not have rejected for '${name}'`));
+      },
+      testStatusOnChange() {
+        return new Promise((resolve) => {
+          SpecialPowers.popPermissions(() => {
+            const permission = 'geolocation';
+            const promiseGranted = this.promiseStateChanged(permission, 'granted');
+            this.setPermissions(ALLOW_ACTION);
+            promiseGranted.then(() => {
+              const promisePrompt = this.promiseStateChanged(permission, 'prompt');
+              SpecialPowers.popPermissions();
+              return promisePrompt;
+            }).then(resolve);
+          });
+        });
+      },
+      testInvalidQuery() {
+        return aWindow.navigator.permissions
+          .query({ name: 'invalid' })
+          .then(
+            () => ok(false, 'invalid query should not have resolved'),
+            () => ok(true, 'invalid query should have rejected')
+          );
+      },
+      testInvalidRevoke() {
+        return aWindow.navigator.permissions
+          .revoke({ name: 'invalid' })
+          .then(
+            () => ok(false, 'invalid revoke should not have resolved'),
+            () => ok(true, 'invalid revoke should have rejected')
+          );
+      },
+    };
+  }
+
+  function enablePrefs() {
+    const ops = {
+      'set': [
+        ['dom.permissions.revoke.enable', true],
+        ['privacy.firstparty.isolate', true],
+      ],
+    };
+    return SpecialPowers.pushPrefEnv(ops);
+  }
+
+  function createIframe() {
+    return new Promise((resolve) => {
+      const iframe = document.createElement('iframe');
+      iframe.src = 'file_empty.html';
+      iframe.onload = () => resolve(iframe.contentWindow);
+      document.body.appendChild(iframe);
+    });
+  }
+  debugger;
+  window.onload = () => {
+    enablePrefs()
+      .then(createIframe)
+      .then(createPermissionTester)
+      .then((tester) => {
+        return tester
+          .checkUnsupportedPermissions()
+          .then(() => tester.setPermissions(UNKNOWN_ACTION))
+          .then(() => tester.checkPermissions('prompt'))
+          .then(() => tester.setPermissions(PROMPT_ACTION))
+          .then(() => tester.checkPermissions('prompt'))
+          .then(() => tester.setPermissions(ALLOW_ACTION))
+          .then(() => tester.checkPermissions('granted'))
+          .then(() => tester.setPermissions(DENY_ACTION))
+          .then(() => tester.checkPermissions('denied'))
+          .then(() => tester.testStatusOnChange())
+          .then(() => tester.testInvalidQuery())
+          .then(() => tester.revokeUnsupportedPermissions())
+          .then(() => tester.revokePermissions())
+          .then(() => tester.checkPermissions('prompt'))
+          .then(() => tester.testInvalidRevoke());
+      })
+      .then(SimpleTest.finish)
+      .catch((e) => {
+        ok(false, `Unexpected error ${e}`);
+        SimpleTest.finish();
+      });
+  };
+  </script>
+</body>
+
+</html>
-- 
cgit v1.2.3