summaryrefslogtreecommitdiffstats
path: root/browser/components/contextualidentity
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/contextualidentity')
-rw-r--r--browser/components/contextualidentity/content/usercontext.css91
-rw-r--r--browser/components/contextualidentity/jar.mn6
-rw-r--r--browser/components/contextualidentity/moz.build14
-rw-r--r--browser/components/contextualidentity/test/browser/.eslintrc.js11
-rw-r--r--browser/components/contextualidentity/test/browser/browser.ini30
-rw-r--r--browser/components/contextualidentity/test/browser/browser_aboutURLs.js49
-rw-r--r--browser/components/contextualidentity/test/browser/browser_blobUrl.js78
-rw-r--r--browser/components/contextualidentity/test/browser/browser_broadcastchannel.js80
-rw-r--r--browser/components/contextualidentity/test/browser/browser_count_and_remove.js34
-rw-r--r--browser/components/contextualidentity/test/browser/browser_eme.js186
-rw-r--r--browser/components/contextualidentity/test/browser/browser_favicon.js140
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js219
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js86
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js147
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js352
-rw-r--r--browser/components/contextualidentity/test/browser/browser_imageCache.js59
-rw-r--r--browser/components/contextualidentity/test/browser/browser_middleClick.js41
-rw-r--r--browser/components/contextualidentity/test/browser/browser_newtabButton.js35
-rw-r--r--browser/components/contextualidentity/test/browser/browser_serviceworkers.js108
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontext.js86
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js134
-rw-r--r--browser/components/contextualidentity/test/browser/browser_windowName.js74
-rw-r--r--browser/components/contextualidentity/test/browser/browser_windowOpen.js41
-rw-r--r--browser/components/contextualidentity/test/browser/empty_file.html5
-rw-r--r--browser/components/contextualidentity/test/browser/favicon-normal32.pngbin0 -> 344 bytes
-rw-r--r--browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html23
-rw-r--r--browser/components/contextualidentity/test/browser/file_set_storages.html41
-rw-r--r--browser/components/contextualidentity/test/browser/serviceworker.html12
-rw-r--r--browser/components/contextualidentity/test/browser/worker.js1
29 files changed, 2183 insertions, 0 deletions
diff --git a/browser/components/contextualidentity/content/usercontext.css b/browser/components/contextualidentity/content/usercontext.css
new file mode 100644
index 000000000..728275d9f
--- /dev/null
+++ b/browser/components/contextualidentity/content/usercontext.css
@@ -0,0 +1,91 @@
+[data-identity-color="blue"] {
+ --identity-tab-color: #0996f8;
+ --identity-icon-color: #00a7e0;
+}
+
+[data-identity-color="turquoise"] {
+ --identity-tab-color: #01bdad;
+ --identity-icon-color: #01bdad;
+}
+
+[data-identity-color="green"] {
+ --identity-tab-color: #57bd35;
+ --identity-icon-color: #7dc14c;
+}
+
+[data-identity-color="yellow"] {
+ --identity-tab-color: #ffcb00;
+ --identity-icon-color: #ffcb00;
+}
+
+[data-identity-color="orange"] {
+ --identity-tab-color: #ff9216;
+ --identity-icon-color: #ff9216;
+}
+
+[data-identity-color="red"] {
+ --identity-tab-color: #d92215;
+ --identity-icon-color: #d92215;
+}
+
+[data-identity-color="pink"] {
+ --identity-tab-color: #ea385e;
+ --identity-icon-color: #ee5195;
+}
+
+[data-identity-color="purple"] {
+ --identity-tab-color: #7a2f7a;
+ --identity-icon-color: #7a2f7a;
+}
+
+[data-identity-icon="fingerprint"] {
+ --identity-icon: url("chrome://browser/content/usercontext.svg#fingerprint");
+}
+
+[data-identity-icon="briefcase"] {
+ --identity-icon: url("chrome://browser/content/usercontext.svg#briefcase");
+}
+
+[data-identity-icon="dollar"] {
+ --identity-icon: url("chrome://browser/content/usercontext.svg#dollar");
+}
+
+[data-identity-icon="cart"] {
+ --identity-icon: url("chrome://browser/content/usercontext.svg#cart");
+}
+
+[data-identity-icon="circle"] {
+ --identity-icon: url("chrome://browser/content/usercontext.svg#circle");
+}
+
+#userContext-indicator {
+ height: 16px;
+ width: 16px;
+}
+
+#userContext-label {
+ margin-inline-end: 3px;
+ color: var(--identity-tab-color);
+}
+
+#userContext-icons {
+ -moz-box-align: center;
+}
+
+.tabbrowser-tab[usercontextid] {
+ background-image: linear-gradient(to right, transparent 20%, var(--identity-tab-color) 30%, var(--identity-tab-color) 70%, transparent 80%);
+ background-size: auto 2px;
+ background-repeat: no-repeat;
+}
+
+.userContext-icon,
+.menuitem-iconic[data-usercontextid] > .menu-iconic-left > .menu-iconic-icon,
+.subviewbutton[usercontextid] > .toolbarbutton-icon,
+#userContext-indicator {
+ background-image: var(--identity-icon);
+ filter: url(chrome://browser/skin/filters.svg#fill);
+ fill: var(--identity-icon-color);
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
diff --git a/browser/components/contextualidentity/jar.mn b/browser/components/contextualidentity/jar.mn
new file mode 100644
index 000000000..848245949
--- /dev/null
+++ b/browser/components/contextualidentity/jar.mn
@@ -0,0 +1,6 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+browser.jar:
+ content/browser/usercontext/usercontext.css (content/usercontext.css)
diff --git a/browser/components/contextualidentity/moz.build b/browser/components/contextualidentity/moz.build
new file mode 100644
index 000000000..62d333db8
--- /dev/null
+++ b/browser/components/contextualidentity/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+BROWSER_CHROME_MANIFESTS += [
+ 'test/browser/browser.ini',
+]
+
+JAR_MANIFESTS += ['jar.mn']
+
+with Files('**'):
+ BUG_COMPONENT = ('Firefox', 'Contextual Identity')
diff --git a/browser/components/contextualidentity/test/browser/.eslintrc.js b/browser/components/contextualidentity/test/browser/.eslintrc.js
new file mode 100644
index 000000000..e25a6863e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/.eslintrc.js
@@ -0,0 +1,11 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/mochitest/browser.eslintrc.js"
+ ],
+
+ "rules": {
+ "no-undef": "error"
+ }
+};
diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini
new file mode 100644
index 000000000..55083f8d2
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -0,0 +1,30 @@
+[DEFAULT]
+support-files =
+ empty_file.html
+ file_reflect_cookie_into_title.html
+ favicon-normal32.png
+ file_set_storages.html
+ serviceworker.html
+ worker.js
+
+[browser_aboutURLs.js]
+[browser_eme.js]
+[browser_favicon.js]
+[browser_forgetaboutsite.js]
+[browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js]
+[browser_forgetAPI_EME_forgetThisSite.js]
+[browser_forgetAPI_quota_clearStoragesForPrincipal.js]
+[browser_newtabButton.js]
+[browser_usercontext.js]
+[browser_usercontextid_tabdrop.js]
+skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
+[browser_windowName.js]
+tags = openwindow
+[browser_windowOpen.js]
+tags = openwindow
+[browser_serviceworkers.js]
+[browser_broadcastchannel.js]
+[browser_blobUrl.js]
+[browser_middleClick.js]
+[browser_imageCache.js]
+[browser_count_and_remove.js]
diff --git a/browser/components/contextualidentity/test/browser/browser_aboutURLs.js b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
new file mode 100644
index 000000000..586bca37f
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
@@ -0,0 +1,49 @@
+"use strict";
+
+// For some about: URLs, they will take more time to load and cause timeout.
+// See Bug 1270998.
+requestLongerTimeout(2);
+
+add_task(function* () {
+ let aboutURLs = [];
+
+ // List of about: URLs that will initiate network requests.
+ let networkURLs = [
+ "credits",
+ "telemetry" // about:telemetry will fetch Telemetry asynchrounously and takes
+ // longer, we skip this for now.
+ ];
+
+ let ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ for (let cid in Cc) {
+ let result = cid.match(/@mozilla.org\/network\/protocol\/about;1\?what\=(.*)$/);
+ if (!result) {
+ continue;
+ }
+
+ let aboutType = result[1];
+ let contract = "@mozilla.org/network/protocol/about;1?what=" + aboutType;
+ try {
+ let am = Cc[contract].getService(Ci.nsIAboutModule);
+ let uri = ios.newURI("about:"+aboutType, null, null);
+ let flags = am.getURIFlags(uri);
+ if (!(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT) &&
+ networkURLs.indexOf(aboutType) == -1) {
+ aboutURLs.push(aboutType);
+ }
+ } catch (e) {
+ // getService might have thrown if the component doesn't actually
+ // implement nsIAboutModule
+ }
+ }
+
+ for (let url of aboutURLs) {
+ info("Loading about:" + url);
+ let tab = gBrowser.addTab("about:"+url, {userContextId: 1});
+ yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ ok(true, "Done loading about:" + url);
+
+ yield BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_blobUrl.js b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
new file mode 100644
index 000000000..8a441311e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
@@ -0,0 +1,78 @@
+"use strict";
+
+// Here we want to test that blob URLs are not available cross containers.
+
+const BASE_URI = "http://mochi.test:8888/browser/browser/components/"
+ + "contextualidentity/test/browser/empty_file.html";
+
+add_task(function* setup() {
+ yield new Promise((resolve) => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true]
+ ]}, resolve);
+ });
+});
+
+
+add_task(function* test() {
+ info("Creating a tab with UCI = 1...");
+ let tab1 = gBrowser.addTab(BASE_URI, {userContextId: 1});
+ is(tab1.getAttribute('usercontextid'), 1, "New tab has UCI equal 1");
+
+ let browser1 = gBrowser.getBrowserForTab(tab1);
+ yield BrowserTestUtils.browserLoaded(browser1);
+
+ let blobURL;
+
+ info("Creating a blob URL...");
+ yield ContentTask.spawn(browser1, null, function() {
+ return Promise.resolve(content.window.URL.createObjectURL(new content.window.Blob([123])));
+ }).then(newURL => { blobURL = newURL });
+
+ info("Blob URL: " + blobURL);
+
+ info("Creating a tab with UCI = 2...");
+ let tab2 = gBrowser.addTab(BASE_URI, {userContextId: 2});
+ is(tab2.getAttribute('usercontextid'), 2, "New tab has UCI equal 2");
+
+ let browser2 = gBrowser.getBrowserForTab(tab2);
+ yield BrowserTestUtils.browserLoaded(browser2);
+
+ yield ContentTask.spawn(browser2, blobURL, function(url) {
+ return new Promise(resolve => {
+ var xhr = new content.window.XMLHttpRequest();
+ xhr.onerror = function() { resolve("SendErrored"); }
+ xhr.onload = function() { resolve("SendLoaded"); }
+ xhr.open("GET", url);
+ xhr.send();
+ });
+ }).then(status => {
+ is(status, "SendErrored", "Using a blob URI from one user context id in another should not work");
+ });
+
+ info("Creating a tab with UCI = 1...");
+ let tab3 = gBrowser.addTab(BASE_URI, {userContextId: 1});
+ is(tab3.getAttribute('usercontextid'), 1, "New tab has UCI equal 1");
+
+ let browser3 = gBrowser.getBrowserForTab(tab3);
+ yield BrowserTestUtils.browserLoaded(browser3);
+
+ yield ContentTask.spawn(browser3, blobURL, function(url) {
+ return new Promise(resolve => {
+ var xhr = new content.window.XMLHttpRequest();
+ xhr.open("GET", url);
+ try {
+ xhr.send();
+ resolve("SendSucceeded");
+ } catch (e) {
+ resolve("SendThrew");
+ }
+ });
+ }).then(status => {
+ is(status, "SendSucceeded", "Using a blob URI within a single user context id should work");
+ });
+
+ yield BrowserTestUtils.removeTab(tab1);
+ yield BrowserTestUtils.removeTab(tab2);
+ yield BrowserTestUtils.removeTab(tab3);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js b/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js
new file mode 100644
index 000000000..a821ce96b
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js
@@ -0,0 +1,80 @@
+let { classes: Cc, interfaces: Ci } = Components;
+
+const BASE_ORIGIN = "http://example.com";
+const URI = BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function* openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // 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_task(function* setup() {
+ // make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true]
+ ]}, resolve);
+ });
+});
+
+add_task(function* test() {
+ let receiver = yield* openTabInUserContext(URI, 2);
+
+ let channelName = "contextualidentity-broadcastchannel";
+
+ // reflect the received message on title
+ yield ContentTask.spawn(receiver.browser, channelName,
+ function (name) {
+ content.window.testPromise = new content.window.Promise(resolve => {
+ content.window.bc = new content.window.BroadcastChannel(name);
+ content.window.bc.onmessage = function (e) {
+ content.document.title += e.data;
+ resolve();
+ }
+ });
+ }
+ );
+
+ let sender1 = yield* openTabInUserContext(URI, 1);
+ let sender2 = yield* openTabInUserContext(URI, 2);
+ sender1.message = "Message from user context #1";
+ sender2.message = "Message from user context #2";
+
+ // send a message from a tab in different user context first
+ // then send a message from a tab in the same user context
+ for (let sender of [sender1, sender2]) {
+ yield ContentTask.spawn(
+ sender.browser,
+ { name: channelName, message: sender.message },
+ function (opts) {
+ let bc = new content.window.BroadcastChannel(opts.name);
+ bc.postMessage(opts.message);
+ });
+ }
+
+ // Since sender1 sends before sender2, if the title is exactly
+ // sender2's message, sender1's message must've been blocked
+ yield ContentTask.spawn(receiver.browser, sender2.message,
+ function* (message) {
+ yield content.window.testPromise.then(function() {
+ is(content.document.title, message,
+ "should only receive messages from the same user context");
+ });
+ }
+ );
+
+ gBrowser.removeTab(sender1.tab);
+ gBrowser.removeTab(sender2.tab);
+ gBrowser.removeTab(receiver.tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_count_and_remove.js b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
new file mode 100644
index 000000000..23b7e948a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/ContextualIdentityService.jsm");
+
+function openTabInUserContext(userContextId) {
+ let tab = gBrowser.addTab("about:blank", {userContextId});
+ gBrowser.selectedTab = tab;
+}
+
+add_task(function* setup() {
+ // make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true]
+ ]});
+});
+
+add_task(function* test() {
+ is(ContextualIdentityService.countContainerTabs(), 0, "0 container tabs by default.");
+
+ openTabInUserContext(1);
+ is(ContextualIdentityService.countContainerTabs(), 1, "1 container tab created");
+
+ openTabInUserContext(1);
+ is(ContextualIdentityService.countContainerTabs(), 2, "2 container tab created");
+
+ openTabInUserContext(2);
+ is(ContextualIdentityService.countContainerTabs(), 3, "3 container tab created");
+
+ ContextualIdentityService.closeAllContainerTabs();
+ is(ContextualIdentityService.countContainerTabs(), 0, "0 container tab at the end.");
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_eme.js b/browser/components/contextualidentity/test/browser/browser_eme.js
new file mode 100644
index 000000000..557648d60
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_eme.js
@@ -0,0 +1,186 @@
+/*
+ * Bug 1283325 - A test case to test the EME is originAttributes aware or not.
+ */
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const TESTKEY = {
+ initDataType: 'keyids',
+ initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"], "type":"persistent-license"}',
+ kid: "LwVHf8JLtPrv2GUXFW2v_A",
+ key: "97b9ddc459c8d5ff23c1f2754c95abe8",
+ sessionType: 'persistent-license',
+};
+
+const USER_ID_DEFAULT = 0;
+const USER_ID_PERSONAL = 1;
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerDocument.defaultView.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+ return {tab, browser};
+}
+
+function HexToBase64(hex)
+{
+ var bin = "";
+ for (var i = 0; i < hex.length; i += 2) {
+ bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+ return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function Base64ToHex(str)
+{
+ var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+ var res = "";
+ for (var i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+ return res;
+}
+
+function ByteArrayToHex(array) {
+ let bin = String.fromCharCode.apply(null, new Uint8Array(array));
+ let res = "";
+
+ for (let i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+
+ return res;
+}
+
+function generateKeyObject(aKid, aKey) {
+ let keyObj = {
+ kty: 'oct',
+ kid: aKid,
+ k: HexToBase64(aKey),
+ };
+
+ return new TextEncoder().encode(JSON.stringify({
+ keys: [keyObj]
+ }));
+}
+
+function generateKeyInfo(aData) {
+ let keyInfo = {
+ initDataType: aData.initDataType,
+ initData: new TextEncoder().encode(aData.initData),
+ sessionType: aData.sessionType,
+ keyObj: generateKeyObject(aData.kid, aData.key),
+ };
+
+ return keyInfo;
+}
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ [ "media.mediasource.enabled", true ],
+ [ "media.eme.apiVisible", true ],
+ [ "media.mediasource.webm.enabled", true ],
+ [ "media.clearkey.persistent-license.enabled", true ],
+ ]}, resolve);
+ });
+});
+
+add_task(function* test() {
+ // Open a tab with the default container.
+ let defaultContainer = yield openTabInUserContext(TEST_URL + "empty_file.html", USER_ID_DEFAULT);
+
+ // Generate the key info for the default container.
+ let keyInfo = generateKeyInfo(TESTKEY);
+
+ // Update the media key for the default container.
+ let result = yield ContentTask.spawn(defaultContainer.browser, keyInfo, function* (aKeyInfo) {
+ let access = yield content.navigator.requestMediaKeySystemAccess('org.w3.clearkey',
+ [{
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{contentType: 'video/webm'}],
+ sessionTypes: ['persistent-license'],
+ persistentState: 'required',
+ }]);
+ let mediaKeys = yield access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the media key.
+ yield new Promise(resolve => {
+ session.addEventListener("message", function(event) {
+ session.update(aKeyInfo.keyObj).then(
+ () => { resolve(); }
+ ).catch(
+ () => {
+ ok(false, "Update the media key fail.");
+ resolve();
+ }
+ );
+ });
+
+ session.generateRequest(aKeyInfo.initDataType, aKeyInfo.initData);
+ });
+
+ let map = session.keyStatuses;
+
+ is(map.size, 1, "One media key has been added.");
+
+ if (map.size === 1) {
+ res.keyId = map.keys().next().value;
+ res.sessionId = session.sessionId;
+ }
+
+ // Close the session.
+ session.close();
+ yield session.closed;
+
+ return res;
+ });
+
+ // Check the media key ID.
+ is(ByteArrayToHex(result.keyId), Base64ToHex(TESTKEY.kid), "The key Id of the default container is correct.");
+
+ // Store the sessionId for the further checking.
+ keyInfo.sessionId = result.sessionId;
+
+ // Open a tab with personal container.
+ let personalContainer = yield openTabInUserContext(TEST_URL + "empty_file.html", USER_ID_PERSONAL);
+
+ yield ContentTask.spawn(personalContainer.browser, keyInfo, function* (aKeyInfo) {
+ let access = yield content.navigator.requestMediaKeySystemAccess('org.w3.clearkey',
+ [{
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{contentType: 'video/webm'}],
+ sessionTypes: ['persistent-license'],
+ persistentState: 'required',
+ }]);
+ let mediaKeys = yield access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session to check that mediakeys do not share with
+ // default container.
+ yield session.load(aKeyInfo.sessionId);
+
+ let map = session.keyStatuses;
+
+ // Check that there is no media key here.
+ is(map.size, 0, "No media key should be here for the personal container.");
+ });
+
+ // Close default container tab.
+ yield BrowserTestUtils.removeTab(defaultContainer.tab);
+
+ // Close personal container tab.
+ yield BrowserTestUtils.removeTab(personalContainer.tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_favicon.js b/browser/components/contextualidentity/test/browser/browser_favicon.js
new file mode 100644
index 000000000..a0a7eb208
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_favicon.js
@@ -0,0 +1,140 @@
+/*
+ * Bug 1270678 - A test case to test does the favicon obey originAttributes.
+ */
+let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+ "work",
+];
+
+let gHttpServer = null;
+let gUserContextId;
+let gFaviconData;
+
+function getIconFile() {
+ new Promise(resolve => {
+ NetUtil.asyncFetch({
+ uri: "http://www.example.com/browser/browser/components/contextualidentity/test/browser/favicon-normal32.png",
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
+ }, function(inputStream, status) {
+ let size = inputStream.available();
+ gFaviconData = NetUtil.readInputStreamToString(inputStream, size);
+ resolve();
+ });
+ });
+}
+
+function* openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // 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 loadIndexHandler(metadata, response) {
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html", false);
+ let body = `
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+ <meta charset='utf-8'>
+ <title>Favicon Test</title>
+ </head>
+ <body>
+ Favicon!!
+ </body>
+ </html>`;
+ response.bodyOutputStream.write(body, body.length);
+}
+
+function loadFaviconHandler(metadata, response) {
+ let expectedCookie = "userContext=" + USER_CONTEXTS[gUserContextId];
+
+ if (metadata.hasHeader("Cookie")) {
+ is(metadata.getHeader("Cookie"), expectedCookie, "The cookie has matched with the expected cookie.");
+ } else {
+ ok(false, "The request should have a cookie.");
+ }
+
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "image/png", false);
+ response.bodyOutputStream.write(gFaviconData, gFaviconData.length);
+}
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true]
+ ]}, resolve);
+ });
+
+ // Create a http server for the image cache test.
+ if (!gHttpServer) {
+ gHttpServer = new HttpServer();
+ gHttpServer.registerPathHandler('/', loadIndexHandler);
+ gHttpServer.registerPathHandler('/favicon.png', loadFaviconHandler);
+ gHttpServer.start(-1);
+ }
+});
+
+registerCleanupFunction(() => {
+ gHttpServer.stop(() => {
+ gHttpServer = null;
+ });
+});
+
+add_task(function* test() {
+ waitForExplicitFinish();
+
+ // First, get the icon data.
+ yield getIconFile();
+
+ let serverPort = gHttpServer.identity.primaryPort;
+ let testURL = "http://localhost:" + serverPort + "/";
+ let testFaviconURL = "http://localhost:" + serverPort + "/favicon.png";
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ gUserContextId = userContextId;
+
+ // Load the page in 3 different contexts and set a cookie
+ // which should only be visible in that context.
+
+ // Open our tab in the given user context.
+ let tabInfo = yield* openTabInUserContext(testURL, userContextId);
+
+ // Write a cookie according to the userContext.
+ yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function (arg) {
+ content.document.cookie = "userContext=" + arg.userContext;
+ });
+
+ let pageURI = NetUtil.newURI(testURL);
+ let favIconURI = NetUtil.newURI(testFaviconURL);
+
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI,
+ true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
+ onComplete() {
+ resolve();
+ },
+ },
+ tabInfo.browser.contentPrincipal);
+ });
+
+ yield BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
new file mode 100644
index 000000000..1a97448c0
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -0,0 +1,219 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the media key.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+
+const TEST_EME_KEY = {
+ initDataType: 'keyids',
+ initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"], "type":"persistent-license"}',
+ kid: "LwVHf8JLtPrv2GUXFW2v_A",
+ key: "97b9ddc459c8d5ff23c1f2754c95abe8",
+ sessionType: 'persistent-license',
+};
+
+//
+// Support functions.
+//
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // 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 HexToBase64(hex) {
+ var bin = "";
+ for (var i = 0; i < hex.length; i += 2) {
+ bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+ return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function Base64ToHex(str) {
+ var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+ var res = "";
+ for (var i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+ return res;
+}
+
+function ByteArrayToHex(array) {
+ let bin = String.fromCharCode.apply(null, new Uint8Array(array));
+ let res = "";
+
+ for (let i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+
+ return res;
+}
+
+function generateKeyObject(aKid, aKey) {
+ let keyObj = {
+ kty: 'oct',
+ kid: aKid,
+ k: HexToBase64(aKey),
+ };
+
+ return new TextEncoder().encode(JSON.stringify({
+ keys: [keyObj]
+ }));
+}
+
+function generateKeyInfo(aData) {
+ let keyInfo = {
+ initDataType: aData.initDataType,
+ initData: new TextEncoder().encode(aData.initData),
+ sessionType: aData.sessionType,
+ keyObj: generateKeyObject(aData.kid, aData.key),
+ };
+
+ return keyInfo;
+}
+
+// Setup a EME key for the given browser, and return the sessionId.
+function* setupEMEKey(browser) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+
+ // Setup the EME key.
+ let result = yield ContentTask.spawn(browser, keyInfo, function* (aKeyInfo) {
+ let access = yield content.navigator.requestMediaKeySystemAccess('org.w3.clearkey',
+ [{
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{contentType: 'video/webm'}],
+ sessionTypes: ['persistent-license'],
+ persistentState: 'required',
+ }]);
+ let mediaKeys = yield access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the EME key.
+ yield new Promise(resolve => {
+ session.addEventListener("message", function(event) {
+ session.update(aKeyInfo.keyObj).then(
+ () => { resolve(); }
+ ).catch(
+ () => {
+ ok(false, "Update the EME key fail.");
+ resolve();
+ }
+ );
+ });
+
+ session.generateRequest(aKeyInfo.initDataType, aKeyInfo.initData);
+ });
+
+ let map = session.keyStatuses;
+
+ is(map.size, 1, "One EME key has been added.");
+
+ if (map.size === 1) {
+ res.keyId = map.keys().next().value;
+ res.sessionId = session.sessionId;
+ }
+
+ // Close the session.
+ session.close();
+ yield session.closed;
+
+ return res;
+ });
+
+ // Check the EME key ID.
+ is(ByteArrayToHex(result.keyId), Base64ToHex(TEST_EME_KEY.kid), "The key Id is correct.");
+ return result.sessionId;
+}
+
+// Check whether the EME key has been cleared.
+function* checkEMEKey(browser, emeSessionId) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+ keyInfo.sessionId = emeSessionId;
+
+ yield ContentTask.spawn(browser, keyInfo, function* (aKeyInfo) {
+ let access = yield content.navigator.requestMediaKeySystemAccess('org.w3.clearkey',
+ [{
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{contentType: 'video/webm'}],
+ sessionTypes: ['persistent-license'],
+ persistentState: 'required',
+ }]);
+ let mediaKeys = yield access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session with the sessionId.
+ yield session.load(aKeyInfo.sessionId);
+
+ let map = session.keyStatuses;
+
+ // Check that there is no media key here.
+ is(map.size, 0, "No media key should be here after forgetThisSite() was called.");
+ });
+}
+
+//
+// Test functions.
+//
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ [ "media.mediasource.enabled", true ],
+ [ "media.eme.apiVisible", true ],
+ [ "media.mediasource.webm.enabled", true ],
+ [ "media.clearkey.persistent-license.enabled", true ],
+ ]});
+});
+
+add_task(function* test_EME_forgetThisSite() {
+ let tabs = [];
+ let emeSessionIds = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Setup EME Key.
+ emeSessionIds[userContextId] = yield setupEMEKey(tabs[userContextId].browser);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Clear all EME data for a given domain with originAttributes pattern.
+ let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].
+ getService(Ci.mozIGeckoMediaPluginChromeService);
+ mps.forgetThisSite(TEST_HOST, JSON.stringify({}));
+
+ // Open tabs again to check EME keys have been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Check whether EME Key has been cleared.
+ yield checkEMEKey(tabs[userContextId].browser, emeSessionIds[userContextId]);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
new file mode 100644
index 000000000..1d9024d25
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
@@ -0,0 +1,86 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for cookies.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+
+//
+// Support functions.
+//
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // 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 getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, {userContextId});
+}
+
+//
+// Test functions.
+//
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ ]});
+});
+
+add_task(function* test_cookie_getCookiesWithOriginAttributes() {
+ let tabs = [];
+ let cookieName = "userContextId";
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set a cookie
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "file_reflect_cookie_into_title.html?" + value, userContextId);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+ ok(enumerator.hasMoreElements(), "Cookies available");
+
+ let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+ is(foundCookie["name"], cookieName, "Check cookie name");
+ is(foundCookie["value"], USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+
+ // Using getCookiesWithOriginAttributes() to get all cookies for a certain
+ // domain by using the originAttributes pattern, and clear all these cookies.
+ let enumerator = Services.cookies.getCookiesWithOriginAttributes(JSON.stringify({}), TEST_HOST);
+ while (enumerator.hasMoreElements()) {
+ let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+ Services.cookies.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
+ }
+
+ // Check that whether cookies has been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!enumerator.hasMoreElements(), "No Cookie should be here");
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
new file mode 100644
index 000000000..6a4b37c55
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
@@ -0,0 +1,147 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the quota manager.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+
+//
+// Support functions.
+//
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // 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};
+}
+
+// Setup an entry for the indexedDB.
+function* setupIndexedDB(browser) {
+ yield ContentTask.spawn(browser, { input: "TestForgetAPIs" }, function* (arg) {
+ let request = content.indexedDB.open("idb", 1);
+
+ request.onerror = function() {
+ throw new Error("error opening db connection");
+ };
+
+ request.onupgradeneeded = event => {
+ let db = event.target.result;
+ let store = db.createObjectStore("obj", { keyPath: "id" });
+ store.createIndex("userContext", "userContext", { unique: false });
+ };
+
+ let db = yield new Promise(resolve => {
+ request.onsuccess = event => {
+ resolve(event.target.result);
+ };
+ });
+
+ // Add an entry into the indexedDB.
+ let transaction = db.transaction(["obj"], "readwrite");
+ let store = transaction.objectStore("obj");
+ store.add({id: 1, userContext: arg.input});
+
+ yield new Promise(resolve => {
+ transaction.oncomplete = () => {
+ resolve();
+ };
+ });
+
+ // Check the indexedDB has been set properly.
+ transaction = db.transaction(["obj"], "readonly");
+ store = transaction.objectStore("obj");
+ let getRequest = store.get(1);
+ yield new Promise(resolve => {
+ getRequest.onsuccess = () => {
+ let res = getRequest.result;
+ is(res.userContext, arg.input, "Check the indexedDB value");
+ resolve();
+ };
+ });
+ });
+}
+
+// Check whether the indexedDB has been cleared.
+function* checkIndexedDB(browser) {
+ yield ContentTask.spawn(browser, null, function* () {
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = yield new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+
+ try {
+ db.transaction(["obj"], "readonly");
+ ok(false, "The indexedDB should not exist");
+ } catch (e) {
+ is(e.name, "NotFoundError", "The indexedDB does not exist as expected");
+ }
+ });
+}
+
+//
+// Test functions.
+//
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ ]});
+});
+
+add_task(function* test_quota_clearStoragesForPrincipal() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Setup an entry for the indexedDB.
+ yield setupIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Using quota manager to clear all indexed DB for a given domain.
+ let qms = Cc["@mozilla.org/dom/quota-manager-service;1"].
+ getService(Ci.nsIQuotaManagerService);
+
+ let caUtils = {};
+ let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ getService(Ci.mozIJSSubScriptLoader);
+ scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
+ caUtils);
+ let httpURI = caUtils.makeURI("http://" + TEST_HOST);
+ let httpPrincipal = Services.scriptSecurityManager
+ .createCodebasePrincipal(httpURI, {});
+ qms.clearStoragesForPrincipal(httpPrincipal, null, true);
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Check whether indexed DB has been cleared.
+ yield checkIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
new file mode 100644
index 000000000..9efc86e0c
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
@@ -0,0 +1,352 @@
+/*
+ * Bug 1238183 - Test cases for forgetAboutSite with userContextId.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/ForgetAboutSite.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+let LoadContextInfo = Cc["@mozilla.org/load-context-info-factory;1"]
+ .getService(Ci.nsILoadContextInfoFactory);
+let css = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+ .getService(Ci.nsICacheStorageService);
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+const COOKIE_NAME = "userContextId";
+
+// Counter for image load hits.
+let gHits = 0;
+
+let gHttpServer = null;
+
+function imageHandler(metadata, response) {
+ // A 1x1 PNG image.
+ // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+ const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+ "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
+ gHits++;
+ response.setHeader("Cache-Control", "max-age=10000", false);
+ response.setStatusLine(metadata.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+ response.write(IMAGE);
+}
+
+function loadImagePageHandler(metadata, response) {
+ response.setHeader("Cache-Control", "max-age=10000", false);
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html", false);
+ let body = "<!DOCTYPE HTML>\
+ <html>\
+ <head>\
+ <meta charset='utf-8'>\
+ <title>Load Image</title>\
+ </head>\
+ <body>\
+ <img src='image.png'>\
+ </body>\
+ </html>";
+ response.bodyOutputStream.write(body, body.length);
+}
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // 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 getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, {userContextId});
+}
+
+function createURI(uri)
+{
+ let ioServ = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+ return ioServ.newURI(uri, null, null);
+}
+
+function getCacheStorage(where, lci, appcache)
+{
+ if (!lci) lci = LoadContextInfo.default;
+ switch (where) {
+ case "disk": return css.diskCacheStorage(lci, false);
+ case "memory": return css.memoryCacheStorage(lci);
+ case "appcache": return css.appCacheStorage(lci, appcache);
+ case "pin": return css.pinningCacheStorage(lci);
+ }
+ return null;
+}
+
+function OpenCacheEntry(key, where, flags, lci)
+{
+ return new Promise(resolve => {
+ key = createURI(key);
+ function CacheListener() { }
+ CacheListener.prototype = {
+ _appCache: null,
+
+ QueryInterface: function (iid) {
+ if (iid.equals(Components.interfaces.nsICacheEntryOpenCallback) ||
+ iid.equals(Components.interfaces.nsISupports))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ onCacheEntryCheck: function(entry, appCache) {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ },
+
+ onCacheEntryAvailable: function (entry, isnew, appCache, status) {
+ resolve();
+ },
+
+ run: function () {
+ let storage = getCacheStorage(where, lci, this._appCache);
+ storage.asyncOpenURI(key, "", flags, this);
+ }
+ };
+
+ (new CacheListener()).run();
+ });
+}
+
+//
+// Test functions.
+//
+
+// Cookies
+function* test_cookie_cleared() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set a cookie
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "file_reflect_cookie_into_title.html?" + value, userContextId);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+ ok(enumerator.hasMoreElements(), "Cookies available");
+
+ let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+ Assert.equal(foundCookie["name"], COOKIE_NAME, "Check cookie name");
+ Assert.equal(foundCookie["value"], USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+
+ // Forget the site.
+ ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Check that whether cookies has been cleared or not.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!enumerator.hasMoreElements(), "No Cookie should be here");
+ }
+}
+
+// Cache
+function* test_cache_cleared() {
+ // First, add some caches.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ yield OpenCacheEntry("http://" + TEST_HOST + "/",
+ "disk",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ LoadContextInfo.custom(false, {userContextId}));
+
+ yield OpenCacheEntry("http://" + TEST_HOST + "/",
+ "memory",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ LoadContextInfo.custom(false, {userContextId}));
+ }
+
+
+ // Check that caches have been set correctly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let mem = getCacheStorage("memory", LoadContextInfo.custom(false, {userContextId}));
+ let disk = getCacheStorage("disk", LoadContextInfo.custom(false, {userContextId}));
+
+ Assert.ok(mem.exists(createURI("http://" + TEST_HOST + "/"), ""), "The memory cache has been set correctly");
+ Assert.ok(disk.exists(createURI("http://" + TEST_HOST + "/"), ""), "The disk cache has been set correctly");
+ }
+
+ // Forget the site.
+ ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Check that do caches be removed or not?
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let mem = getCacheStorage("memory", LoadContextInfo.custom(false, {userContextId}));
+ let disk = getCacheStorage("disk", LoadContextInfo.custom(false, {userContextId}));
+
+ Assert.ok(!mem.exists(createURI("http://" + TEST_HOST + "/"), ""), "The memory cache is cleared");
+ Assert.ok(!disk.exists(createURI("http://" + TEST_HOST + "/"), ""), "The disk cache is cleared");
+ }
+}
+
+// Image Cache
+function* test_image_cache_cleared() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context to cache image.
+ tabs[userContextId] = yield* openTabInUserContext('http://localhost:' + gHttpServer.identity.primaryPort + '/loadImage.html',
+ userContextId);
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ let expectedHits = USER_CONTEXTS.length;
+
+ // Check that image cache works with the userContextId.
+ is(gHits, expectedHits, "The image should be loaded" + expectedHits + "times.");
+
+ // Reset the cache count.
+ gHits = 0;
+
+ // Forget the site.
+ ForgetAboutSite.removeDataFromDomain("localhost:" + gHttpServer.identity.primaryPort + "/");
+
+ // Load again.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context to cache image.
+ tabs[userContextId] = yield* openTabInUserContext('http://localhost:' + gHttpServer.identity.primaryPort + '/loadImage.html',
+ userContextId);
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that image cache was cleared and the server gets another two hits.
+ is(gHits, expectedHits, "The image should be loaded" + expectedHits + "times.");
+}
+
+// Offline Storage
+function* test_storage_cleared() {
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set the local storage
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html?" + value, userContextId);
+
+ // Check that the storages has been set correctly.
+ yield ContentTask.spawn(tabInfo.browser, { userContext: USER_CONTEXTS[userContextId] }, function* (arg) {
+ // Check that the local storage has been set correctly.
+ Assert.equal(content.localStorage.getItem("userContext"), arg.userContext, "Check the local storage value");
+
+ // Check that the session storage has been set correctly.
+ Assert.equal(content.sessionStorage.getItem("userContext"), arg.userContext, "Check the session storage value");
+
+ // Check that the indexedDB has been set correctly.
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = yield new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+
+ let transaction = db.transaction(["obj"], "readonly");
+ let store = transaction.objectStore("obj");
+ let storeRequest = store.get(1);
+
+ yield new Promise(done => {
+ storeRequest.onsuccess = event => {
+ let res = storeRequest.result;
+ Assert.equal(res.userContext, arg.userContext, "Check the indexedDB value");
+ done();
+ };
+ });
+ });
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+
+ // Forget the site.
+ ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Open the tab again without setting the localStorage and check that the
+ // local storage has been cleared or not.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context without setting local storage.
+ let tabInfo = yield* openTabInUserContext(TEST_URL+ "file_set_storages.html", userContextId);
+
+ // Check that do storages be cleared or not.
+ yield ContentTask.spawn(tabInfo.browser, null, function* () {
+ // Check that does the local storage be cleared or not.
+ Assert.ok(!content.localStorage.getItem("userContext"), "The local storage has been cleared");
+
+ // Check that does the session storage be cleared or not.
+ Assert.ok(!content.sessionStorage.getItem("userContext"), "The session storage has been cleared");
+
+ // Check that does the indexedDB be cleared or not.
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = yield new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+ try {
+ db.transaction(["obj"], "readonly");
+ Assert.ok(false, "The indexedDB should not exist");
+ } catch (e) {
+ Assert.equal(e.name, "NotFoundError", "The indexedDB does not exist as expected");
+ }
+ });
+
+ // Close the tab.
+ yield BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+}
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true]
+ ]}, resolve);
+ });
+
+ // Create a http server for the image cache test.
+ if (!gHttpServer) {
+ gHttpServer = new HttpServer();
+ gHttpServer.registerPathHandler('/image.png', imageHandler);
+ gHttpServer.registerPathHandler('/loadImage.html', loadImagePageHandler);
+ gHttpServer.start(-1);
+ }
+});
+
+let tests = [
+ test_cookie_cleared,
+ test_cache_cleared,
+ test_image_cache_cleared,
+ test_storage_cleared,
+];
+
+add_task(function* test() {
+ for (let i = 0; i < tests.length; i++)
+ add_task(tests[i]);
+});
+
+registerCleanupFunction(() => {
+ gHttpServer.stop(() => {
+ gHttpServer = null;
+ });
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_imageCache.js b/browser/components/contextualidentity/test/browser/browser_imageCache.js
new file mode 100644
index 000000000..df36d44c1
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_imageCache.js
@@ -0,0 +1,59 @@
+let Cu = Components.utils;
+let {HttpServer} = Cu.import("resource://testing-common/httpd.js", {});
+
+const NUM_USER_CONTEXTS = 3;
+
+let gHits = 0;
+
+let server = new HttpServer();
+server.registerPathHandler('/image.png', imageHandler);
+server.registerPathHandler('/file.html', fileHandler);
+server.start(-1);
+
+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) {
+ 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);
+}
+
+add_task(function* setup() {
+ // make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [["privacy.userContext.enabled", true]]});
+});
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function* openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerDocument.defaultView.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+ return tab;
+}
+
+add_task(function* test() {
+ for (let userContextId = 0; userContextId < NUM_USER_CONTEXTS; userContextId++) {
+ let tab = yield* openTabInUserContext(FILE_URI, userContextId);
+ gBrowser.removeTab(tab);
+ }
+ is(gHits, NUM_USER_CONTEXTS, "should get an image request for each user contexts");
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_middleClick.js b/browser/components/contextualidentity/test/browser/browser_middleClick.js
new file mode 100644
index 000000000..f3bed2b53
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_middleClick.js
@@ -0,0 +1,41 @@
+"use strict";
+
+const BASE_ORIGIN = "http://example.com";
+const URI = BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+add_task(function* () {
+ info("Opening a new container tab...");
+
+ let tab = gBrowser.addTab(URI, { userContextId: 1 });
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+
+ info("Create a HTMLAnchorElement...");
+ yield ContentTask.spawn(browser, URI,
+ function(URI) {
+ let anchor = content.document.createElement("a");
+ anchor.setAttribute('id', 'clickMe');
+ anchor.setAttribute("href", URI);
+ anchor.appendChild(content.document.createTextNode("click me!"));
+ content.document.body.appendChild(anchor);
+ }
+ );
+
+ info("Synthesize a mouse click and wait for a new tab...");
+ let newTab = yield new Promise((resolve, reject) => {
+ gBrowser.tabContainer.addEventListener("TabOpen", function onTabOpen(openEvent) {
+ gBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen);
+ resolve(openEvent.target);
+ })
+
+ BrowserTestUtils.synthesizeMouseAtCenter("#clickMe", { button: 1 }, browser);
+ });
+
+ is(newTab.getAttribute("usercontextid"), 1, "Correct UserContextId?");
+
+ yield BrowserTestUtils.removeTab(tab);
+ yield BrowserTestUtils.removeTab(newTab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_newtabButton.js b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
new file mode 100644
index 000000000..228e6f971
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
@@ -0,0 +1,35 @@
+"use strict";
+
+// Testing that when the user opens the add tab menu and clicks menu items
+// the correct context id is opened
+
+add_task(function* test() {
+ yield SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true]
+ ]});
+
+ let newTab = document.getElementById('tabbrowser-tabs');
+ let newTabButton = document.getAnonymousElementByAttribute(newTab, "anonid", "tabs-newtab-button");
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+ yield BrowserTestUtils.waitForCondition(() => !!document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup"), "Wait for popup to exist");
+ let popup = document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup");
+
+ for (let i = 1; i <= 4; i++) {
+ let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, {type: "mousedown"});
+
+ yield popupShownPromise;
+ let contextIdItem = popup.querySelector(`menuitem[data-usercontextid="${i}"]`);
+
+ ok(contextIdItem, `User context id ${i} exists`);
+
+ let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+ EventUtils.synthesizeMouseAtCenter(contextIdItem, {});
+
+ let tab = yield waitForTabPromise;
+
+ is(tab.getAttribute('usercontextid'), i, `New tab has UCI equal ${i}`);
+ yield BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_serviceworkers.js b/browser/components/contextualidentity/test/browser/browser_serviceworkers.js
new file mode 100644
index 000000000..b074b91ac
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_serviceworkers.js
@@ -0,0 +1,108 @@
+let { classes: Cc, interfaces: Ci } = Components;
+
+let swm = Cc["@mozilla.org/serviceworkers/manager;1"].
+ getService(Ci.nsIServiceWorkerManager);
+
+const BASE_ORIGIN = "https://example.com";
+const URI = BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/serviceworker.html";
+const NUM_USER_CONTEXTS = 3;
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ return tab;
+}
+
+add_task(function* setup() {
+ // make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.ipc.processCount", 1]
+ ]}, resolve);
+ });
+});
+
+let infos = [];
+
+add_task(function* test() {
+ // Open the same URI in multiple user contexts, and make sure we have a
+ // separate service worker in each of the contexts
+ for (let userContextId = 0; userContextId < NUM_USER_CONTEXTS; userContextId++) {
+ // Open a tab in given user contexts
+ let tab = openTabInUserContext(URI, userContextId);
+
+ // wait for tab load
+ yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+ }
+
+ if (!allRegistered()) {
+ yield promiseAllRegistered();
+ }
+ ok(true, "all service workers are registered");
+
+ // Unregistered all service workers added in this test
+ for (let info of infos) {
+ yield promiseUnregister(info);
+ }
+});
+
+function allRegistered() {
+ let results = [];
+ let registrations = swm.getAllRegistrations();
+ for (let i = 0; i < registrations.length; i++) {
+ let info = registrations.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
+ let principal = info.principal;
+ if (principal.originNoSuffix === BASE_ORIGIN) {
+ results[principal.userContextId] = true;
+ infos[principal.userContextId] = info;
+ }
+ }
+ for (let userContextId = 0; userContextId < NUM_USER_CONTEXTS; userContextId++) {
+ if (!results[userContextId]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function promiseAllRegistered() {
+ return new Promise(function(resolve) {
+ let listener = {
+ onRegister: function() {
+ if (allRegistered()) {
+ swm.removeListener(listener);
+ resolve();
+ }
+ }
+ }
+ swm.addListener(listener);
+ });
+}
+
+function promiseUnregister(info) {
+ return new Promise(function(resolve) {
+ swm.unregister(info.principal, {
+ unregisterSucceeded: function(aState) {
+ ok(aState, "ServiceWorkerRegistration exists");
+ resolve();
+ },
+ unregisterFailed: function(aState) {
+ ok(false, "unregister should succeed");
+ }
+ }, info.scope);
+ });
+}
diff --git a/browser/components/contextualidentity/test/browser/browser_usercontext.js b/browser/components/contextualidentity/test/browser/browser_usercontext.js
new file mode 100644
index 000000000..e0e785d3f
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+ "work",
+];
+
+const BASE_URI = "http://mochi.test:8888/browser/browser/components/"
+ + "contextualidentity/test/browser/file_reflect_cookie_into_title.html";
+
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ return tab;
+}
+
+add_task(function* setup() {
+ // make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true],
+ ["dom.ipc.processCount", 1]
+ ]}, resolve);
+ });
+});
+
+add_task(function* test() {
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // load the page in 3 different contexts and set a cookie
+ // which should only be visible in that context
+ let cookie = USER_CONTEXTS[userContextId];
+
+ // open our tab in the given user context
+ let tab = openTabInUserContext(BASE_URI+"?"+cookie, userContextId);
+
+ // wait for tab load
+ yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+ }
+
+ {
+ // Set a cookie in a different context so we can detect if that affects
+ // cross-context properly. If we don't do that, we get an UNEXPECTED-PASS
+ // for the localStorage case for the last tab we set.
+ let tab = openTabInUserContext(BASE_URI+"?foo", 9999);
+ yield BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+ gBrowser.removeTab(tab);
+ }
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page without setting the cookie this time
+ let expectedContext = USER_CONTEXTS[userContextId];
+
+ let tab = openTabInUserContext(BASE_URI, userContextId);
+
+ // wait for load
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+
+ // get the title
+ let title = browser.contentDocument.title.trim().split("|");
+
+ // check each item in the title and validate it meets expectatations
+ for (let part of title) {
+ let [storageMethodName, value] = part.split("=");
+ is(value, expectedContext,
+ "the title reflects the expected contextual identity of " +
+ expectedContext + " for method " + storageMethodName + ": " + value);
+ }
+
+ gBrowser.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
new file mode 100644
index 000000000..6a8fbc591
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
@@ -0,0 +1,134 @@
+"use strict";
+
+let EventUtils = {};
+Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+
+/**
+ * Dragging an URL to a tab without userContextId set.
+ */
+add_task(function* () {
+ let tab = gBrowser.addTab("http://example.com/");
+ yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://test1.example.com/");
+
+ // A drop type of "link" onto an existing tab would normally trigger a
+ // load in that same tab, but tabbrowser code in _getDragTargetTab treats
+ // drops on the outer edges of a tab differently (loading a new tab
+ // instead). Make events created by synthesizeDrop have all of their
+ // coordinates set to 0 (screenX/screenY), so they're treated as drops
+ // on the outer edge of the tab, thus they open new tabs.
+ let event = {
+ clientX: 0,
+ clientY: 0,
+ screenX: 0,
+ screenY: 0,
+ };
+ EventUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
+
+ yield awaitDrop;
+
+ let tab2 = yield newTabPromise;
+ Assert.ok(!tab2.hasAttribute("usercontextid"), "Tab shouldn't have usercontextid attribute");
+
+ yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ yield ContentTask.spawn(tab2.linkedBrowser, {}, function* () {
+ Assert.equal(content.document.documentURI, "http://test1.example.com/");
+ Assert.equal(content.document.nodePrincipal.originAttributes.userContextId, 0);
+
+ // referrer is empty when urls are dragged to new or existing tabs.
+ // If this changes in the future, it would be okay to send the referrer
+ // in this case because we are creating a new tab with the default
+ // usercontextid as the original tab.
+ Assert.equal(content.document.referrer, "", "referrer should be empty");
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+ yield BrowserTestUtils.removeTab(tab2);
+});
+
+/**
+ * When dragging an URL to a new tab, the new tab should have the same
+ * userContextId as the original tab.
+ */
+add_task(function* () {
+ let tab = gBrowser.addTab("http://example.com/", {userContextId: 1});
+ yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://test1.example.com/");
+
+ // A drop type of "link" onto an existing tab would normally trigger a
+ // load in that same tab, but tabbrowser code in _getDragTargetTab treats
+ // drops on the outer edges of a tab differently (loading a new tab
+ // instead). Make events created by synthesizeDrop have all of their
+ // coordinates set to 0 (screenX/screenY), so they're treated as drops
+ // on the outer edge of the tab, thus they open new tabs.
+ let event = {
+ clientX: 0,
+ clientY: 0,
+ screenX: 0,
+ screenY: 0,
+ };
+ EventUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
+
+ yield awaitDrop;
+
+ let tab2 = yield newTabPromise;
+ Assert.equal(tab2.getAttribute("usercontextid"), 1);
+
+ yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ yield ContentTask.spawn(tab2.linkedBrowser, {}, function* () {
+ Assert.equal(content.document.documentURI, "http://test1.example.com/");
+ Assert.equal(content.document.nodePrincipal.originAttributes.userContextId, 1);
+
+ // referrer is empty when urls are dragged to new or existing tabs.
+ // If this changes in the future, it would be okay to send the referrer
+ // in this case because we are creating a new tab with the same
+ // usercontextid as the original tab.
+ Assert.equal(content.document.referrer, "", "referrer should be empty");
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+ yield BrowserTestUtils.removeTab(tab2);
+});
+
+/**
+ * When dragging a URL from one tab or link on a tab to an existing tab, the
+ * existing tab should not change its userContextId.
+ * Ex: if you drag a link from tab 1 with userContext 1 to tab 2 with
+ * userContext 2, the link will open in tab 2 with userContext 2.
+ */
+add_task(function* () {
+ let tab = gBrowser.addTab("http://example.com/", {userContextId: 1});
+ yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let tab2 = gBrowser.addTab("http://example.org/", {userContextId: 2});
+ yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+
+ EventUtils.synthesizeDrop(tab, tab2, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window);
+
+ yield awaitDrop;
+ Assert.equal(tab2.getAttribute("usercontextid"), 2);
+
+ yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ yield ContentTask.spawn(tab2.linkedBrowser, {}, function* () {
+ Assert.equal(content.document.documentURI, "http://test1.example.com/");
+ Assert.equal(content.document.nodePrincipal.originAttributes.userContextId, 2);
+
+ // referrer is empty when urls are dragged to new or existing tabs.
+ // If this changes in the future, we should ensure that we are not sending
+ // a referrer for this case! When opening links across user contexts, we
+ // don't want the referrer to follow the user from one context to another.
+ Assert.equal(content.document.referrer, "", "referrer should be empty");
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+ yield BrowserTestUtils.removeTab(tab2);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_windowName.js b/browser/components/contextualidentity/test/browser/browser_windowName.js
new file mode 100644
index 000000000..555c421ce
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_windowName.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+ "work",
+];
+
+const BASE_URI = "http://mochi.test:8888/browser/browser/components/"
+ + "contextualidentity/test/browser/empty_file.html";
+
+add_task(function* setup() {
+ // make sure userContext is enabled.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true],
+ ["browser.link.open_newwindow", 3],
+ ]}, resolve);
+ });
+});
+
+add_task(function* test() {
+ info("Creating first tab...");
+ let tab1 = gBrowser.addTab(BASE_URI + '?old', {userContextId: 1});
+ let browser1 = gBrowser.getBrowserForTab(tab1);
+ yield BrowserTestUtils.browserLoaded(browser1);
+ yield ContentTask.spawn(browser1, null, function(opts) {
+ content.window.name = 'tab-1';
+ });
+
+ info("Creating second tab...");
+ let tab2 = gBrowser.addTab(BASE_URI + '?old', {userContextId: 2});
+ let browser2 = gBrowser.getBrowserForTab(tab2);
+ yield BrowserTestUtils.browserLoaded(browser2);
+ yield ContentTask.spawn(browser2, null, function(opts) {
+ content.window.name = 'tab-2';
+ });
+
+ // Let's try to open a window from tab1 with a name 'tab-2'.
+ info("Opening a window from the first tab...");
+ yield ContentTask.spawn(browser1, { url: BASE_URI + '?new' }, function* (opts) {
+ yield (new content.window.wrappedJSObject.Promise(resolve => {
+ let w = content.window.wrappedJSObject.open(opts.url, 'tab-2');
+ w.onload = function() { resolve(); }
+ }));
+ });
+
+ is(browser1.contentTitle, '?old', "Tab1 title must be 'old'");
+ is(browser1.contentPrincipal.userContextId, 1, "Tab1 UCI must be 1");
+
+ is(browser2.contentTitle, '?old', "Tab2 title must be 'old'");
+ is(browser2.contentPrincipal.userContextId, 2, "Tab2 UCI must be 2");
+
+ let found = false;
+ for (let i = 0; i < gBrowser.tabContainer.childNodes.length; ++i) {
+ let tab = gBrowser.tabContainer.childNodes[i];
+ let browser = gBrowser.getBrowserForTab(tab);
+ if (browser.contentTitle == '?new') {
+ is(browser.contentPrincipal.userContextId, 1, "Tab3 UCI must be 1");
+ isnot(browser, browser1, "Tab3 is not browser 1");
+ isnot(browser, browser2, "Tab3 is not browser 2");
+ gBrowser.removeTab(tab);
+ found = true;
+ break;
+ }
+ }
+
+ ok(found, "We have tab3");
+
+ gBrowser.removeTab(tab1);
+ gBrowser.removeTab(tab2);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_windowOpen.js b/browser/components/contextualidentity/test/browser/browser_windowOpen.js
new file mode 100644
index 000000000..00c6e0aa0
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_windowOpen.js
@@ -0,0 +1,41 @@
+"use strict";
+
+// Here we want to test that a new opened window shows the same UI of the
+// parent one if this has been loaded from a particular container.
+
+const BASE_URI = "http://mochi.test:8888/browser/browser/components/"
+ + "contextualidentity/test/browser/empty_file.html";
+
+add_task(function* setup() {
+ yield new Promise((resolve) => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["privacy.userContext.enabled", true],
+ ["browser.link.open_newwindow", 2],
+ ]}, resolve);
+ });
+});
+
+
+add_task(function* test() {
+ info("Creating a tab with UCI = 1...");
+ let tab = gBrowser.addTab(BASE_URI, {userContextId: 1});
+ is(tab.getAttribute('usercontextid'), 1, "New tab has UCI equal 1");
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+
+ info("Opening a new window from this tab...");
+ ContentTask.spawn(browser, BASE_URI, function(url) {
+ content.window.newWindow = content.window.open(url, "_blank");
+ });
+
+ let newWin = yield BrowserTestUtils.waitForNewWindow();
+ let newTab = newWin.gBrowser.selectedTab;
+
+ yield BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
+ is(newTab.getAttribute('usercontextid'), 1, "New tab has UCI equal 1");
+
+ info("Closing the new window and tab...");
+ yield BrowserTestUtils.closeWindow(newWin);
+ yield BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/empty_file.html b/browser/components/contextualidentity/test/browser/empty_file.html
new file mode 100644
index 000000000..c6d11dcd5
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/empty_file.html
@@ -0,0 +1,5 @@
+<html><body>
+<script>
+document.title = window.location.search;
+</script>
+</body></html>
diff --git a/browser/components/contextualidentity/test/browser/favicon-normal32.png b/browser/components/contextualidentity/test/browser/favicon-normal32.png
new file mode 100644
index 000000000..5535363c9
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/favicon-normal32.png
Binary files differ
diff --git a/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html
new file mode 100644
index 000000000..b04f3fd5c
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html
@@ -0,0 +1,23 @@
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>title not set</title>
+ <script>
+ // if we have a query string, use it to set the cookie and localStorage
+ if (window.location.search.length > 0) {
+ let context_name = window.location.search.substr(1);
+ document.cookie = "userContextId=" + context_name;
+ localStorage.setItem("userContext", context_name);
+ }
+
+ // get the cookie
+ let [name, val] = document.cookie.split("=");
+
+ // set the title to reflect the cookie and local storage values we find
+ document.title = "cookie=" + val + "|"
+ + "local=" + localStorage.getItem("userContext");
+ </script>
+ </head>
+ <body></body>
+</html>
+
diff --git a/browser/components/contextualidentity/test/browser/file_set_storages.html b/browser/components/contextualidentity/test/browser/file_set_storages.html
new file mode 100644
index 000000000..c6adcbdde
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/file_set_storages.html
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>Bug 1238183</title>
+ </head>
+ <body>
+ <script type="application/javascript;version=1.7">
+ "use strict";
+
+ // if we have a query string, use it to set storages
+ if (window.location.search.length > 0) {
+ let context_name = window.location.search.substr(1);
+ localStorage.setItem("userContext", context_name);
+ sessionStorage.setItem("userContext", context_name);
+
+ let request = indexedDB.open("idb", 1);
+
+ request.onerror = function() {
+ throw new Error("error opening db connection");
+ };
+
+ request.onupgradeneeded = event => {
+ let db = event.target.result;
+ let store = db.createObjectStore("obj", { keyPath: "id" });
+ store.createIndex("userContext", "userContext", { unique: false });
+ };
+
+ request.onsuccess = event => {
+ let db = request.result;
+ let transaction = db.transaction(["obj"], "readwrite");
+ let store = transaction.objectStore("obj");
+ store.add({id: 1, userContext: context_name});
+
+ transaction.oncomplete = () => {
+ db.close();
+ };
+ };
+ }
+ </script>
+ </body>
+</html>
diff --git a/browser/components/contextualidentity/test/browser/serviceworker.html b/browser/components/contextualidentity/test/browser/serviceworker.html
new file mode 100644
index 000000000..11edd001a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/serviceworker.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ navigator.serviceWorker.register("worker.js");
+ </script>
+ </head>
+ <body>
+ This is a test page.
+ </body>
+<html>
diff --git a/browser/components/contextualidentity/test/browser/worker.js b/browser/components/contextualidentity/test/browser/worker.js
new file mode 100644
index 000000000..2aba167d1
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/worker.js
@@ -0,0 +1 @@
+// empty worker, always succeed!