summaryrefslogtreecommitdiffstats
path: root/browser/components/contextualidentity/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/components/contextualidentity/test
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/contextualidentity/test')
-rw-r--r--browser/components/contextualidentity/test/browser/blank.html2
-rw-r--r--browser/components/contextualidentity/test/browser/browser.ini62
-rw-r--r--browser/components/contextualidentity/test/browser/browser_aboutURLs.js70
-rw-r--r--browser/components/contextualidentity/test/browser/browser_blobUrl.js92
-rw-r--r--browser/components/contextualidentity/test/browser/browser_broadcastchannel.js85
-rw-r--r--browser/components/contextualidentity/test/browser/browser_count_and_remove.js107
-rw-r--r--browser/components/contextualidentity/test/browser/browser_eme.js223
-rw-r--r--browser/components/contextualidentity/test/browser/browser_favicon.js147
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js259
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js95
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js170
-rw-r--r--browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js425
-rw-r--r--browser/components/contextualidentity/test/browser/browser_imageCache.js70
-rw-r--r--browser/components/contextualidentity/test/browser/browser_middleClick.js50
-rw-r--r--browser/components/contextualidentity/test/browser/browser_newtabButton.js214
-rw-r--r--browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js184
-rw-r--r--browser/components/contextualidentity/test/browser/browser_relatedTab.js94
-rw-r--r--browser/components/contextualidentity/test/browser/browser_reopenIn.js164
-rw-r--r--browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js122
-rw-r--r--browser/components/contextualidentity/test/browser/browser_saveLink.js134
-rw-r--r--browser/components/contextualidentity/test/browser/browser_serviceworkers.js121
-rw-r--r--browser/components/contextualidentity/test/browser/browser_tab_color_update.js42
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontext.js88
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js93
-rw-r--r--browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js181
-rw-r--r--browser/components/contextualidentity/test/browser/browser_windowName.js80
-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.html22
-rw-r--r--browser/components/contextualidentity/test/browser/file_set_storages.html41
-rw-r--r--browser/components/contextualidentity/test/browser/head.js48
-rw-r--r--browser/components/contextualidentity/test/browser/saveLink.sjs55
-rw-r--r--browser/components/contextualidentity/test/browser/serviceworker.html12
-rw-r--r--browser/components/contextualidentity/test/browser/worker.js1
35 files changed, 3599 insertions, 0 deletions
diff --git a/browser/components/contextualidentity/test/browser/blank.html b/browser/components/contextualidentity/test/browser/blank.html
new file mode 100644
index 0000000000..bcc2e389b8
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/blank.html
@@ -0,0 +1,2 @@
+<!doctype html>
+This page intentionally left blank.
diff --git a/browser/components/contextualidentity/test/browser/browser.ini b/browser/components/contextualidentity/test/browser/browser.ini
new file mode 100644
index 0000000000..8bee56df91
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -0,0 +1,62 @@
+[DEFAULT]
+support-files =
+ head.js
+ empty_file.html
+ file_reflect_cookie_into_title.html
+ favicon-normal32.png
+ file_set_storages.html
+ serviceworker.html
+ worker.js
+ blank.html
+
+[browser_aboutURLs.js]
+skip-if =
+ os == 'linux' && bits == 64 && !debug # Bug 1731442
+[browser_blobUrl.js]
+[browser_broadcastchannel.js]
+[browser_count_and_remove.js]
+[browser_eme.js]
+skip-if = (os == "win" && processor == "aarch64") # bug 1531927
+[browser_favicon.js]
+[browser_forgetAPI_EME_forgetThisSite.js]
+skip-if = (os == "win" && processor == "aarch64") # bug 1531927
+[browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js]
+[browser_forgetAPI_quota_clearStoragesForPrincipal.js]
+https_first_disabled = true
+skip-if = verify
+[browser_forgetaboutsite.js]
+skip-if = true # Bug 1541885
+[browser_imageCache.js]
+skip-if = (verify && debug && (os == 'win'))
+[browser_middleClick.js]
+skip-if =
+ (verify && debug && (os == 'linux'))
+ apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
+ os == 'linux' && socketprocess_networking
+ win10_2004 && !debug
+
+[browser_newtabButton.js]
+[browser_originattrs_reopenin.js]
+https_first_disabled = true
+[browser_relatedTab.js]
+[browser_reopenIn.js]
+https_first_disabled = true
+[browser_restore_getCookiesWithOriginAttributes.js]
+[browser_saveLink.js]
+skip-if = (verify && !debug && (os == 'win'))
+support-files =
+ saveLink.sjs
+ !/toolkit/content/tests/browser/common/mockTransfer.js
+[browser_serviceworkers.js]
+[browser_tab_color_update.js]
+[browser_usercontext.js]
+[browser_usercontextid_new_window.js]
+[browser_usercontextid_tabdrop.js]
+https_first_disabled = true
+skip-if =
+ os == "mac" # Intermittent failure - bug 1268276
+ os == "win" # Intermittent failure - bug 1268276
+[browser_windowName.js]
+tags = openwindow
+[browser_windowOpen.js]
+tags = openwindow
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 0000000000..b2e43bfe65
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
@@ -0,0 +1,70 @@
+"use strict";
+
+// For some about: URLs, they will take more time to load and cause timeout.
+// See Bug 1270998.
+requestLongerTimeout(2);
+
+add_task(async function () {
+ let aboutURLs = [];
+
+ // List of about: URLs that may cause problem, so we skip them in this test.
+ let skipURLs = [
+ // about:addons triggers an assertion in NS_CompareLoadInfoAndLoadContext:
+ // "The value of mUserContextId in the loadContext and in the loadInfo are not the same"
+ // This is due to a fetch request that has the default user context. Since
+ // the fetch request omits credentials, the user context doesn't matter.
+ "addons",
+ // about:credits and about:logins will initiate network request.
+ "credits",
+ "logins",
+ // about:telemetry will fetch Telemetry asynchronously and takes longer,
+ // so we skip this for now.
+ "telemetry",
+ // about:downloads causes a shutdown leak with stylo-chrome. bug 1419943.
+ "downloads",
+ // about:debugging requires specific wait code for internal pending RDP requests.
+ "debugging",
+ "debugging-new",
+ // about:protections uses RPM to send a message as soon as the page loads,
+ // the page is destoryed before getting a response.
+ "protections",
+ ];
+
+ 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 = Services.io.newURI("about:" + aboutType);
+ let flags = am.getURIFlags(uri);
+ if (
+ !(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT) &&
+ !skipURLs.includes(aboutType)
+ ) {
+ 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 = BrowserTestUtils.addTab(gBrowser, "about:" + url, {
+ userContextId: 1,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ ok(true, "Done loading about:" + url);
+
+ 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 0000000000..636ffcc301
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
@@ -0,0 +1,92 @@
+"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_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test() {
+ info("Creating a tab with UCI = 1...");
+ let tab1 = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 1 });
+ is(tab1.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ let browser1 = gBrowser.getBrowserForTab(tab1);
+ await BrowserTestUtils.browserLoaded(browser1);
+
+ let blobURL;
+
+ info("Creating a blob URL...");
+ await SpecialPowers.spawn(browser1, [], 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 = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 2 });
+ is(tab2.getAttribute("usercontextid"), "2", "New tab has UCI equal 2");
+
+ let browser2 = gBrowser.getBrowserForTab(tab2);
+ await BrowserTestUtils.browserLoaded(browser2);
+
+ await SpecialPowers.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 = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 1 });
+ is(tab3.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ let browser3 = gBrowser.getBrowserForTab(tab3);
+ await BrowserTestUtils.browserLoaded(browser3);
+
+ await SpecialPowers.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"
+ );
+ });
+
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab2);
+ 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 0000000000..0bb27f1146
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_broadcastchannel.js
@@ -0,0 +1,85 @@
+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 and browser.
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+async function runTestForReceiver(receiver) {
+ let channelName = "contextualidentity-broadcastchannel";
+
+ // reflect the received message on title
+ await SpecialPowers.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 = await openTabInUserContext(URI, 1);
+ let sender2 = await 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]) {
+ await SpecialPowers.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
+ await SpecialPowers.spawn(
+ receiver.browser,
+ [sender2.message],
+ async function (message) {
+ await 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);
+}
+
+add_setup(async function () {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test() {
+ info("Checking broadcast channel with browser tab receiver");
+ let receiver = await openTabInUserContext(URI, 2);
+ await runTestForReceiver(receiver);
+ 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 0000000000..8f044db94e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
@@ -0,0 +1,107 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function openTabInUserContext(userContextId) {
+ let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", { userContextId });
+ gBrowser.selectedTab = tab;
+}
+
+add_setup(async function () {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test() {
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 0,
+ "0 container tabs by default."
+ );
+
+ openTabInUserContext(1);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 1,
+ "1 container tab created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 1,
+ "1 container tab created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 0,
+ "0 container tabs created with id 2"
+ );
+
+ openTabInUserContext(1);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 2,
+ "2 container tabs created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 2,
+ "2 container tabs created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 0,
+ "0 container tabs created with id 2"
+ );
+
+ openTabInUserContext(2);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 3,
+ "3 container tab created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 2,
+ "2 container tabs created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 1,
+ "1 container tab created with id 2"
+ );
+
+ await ContextualIdentityService.closeContainerTabs(1);
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 1,
+ "1 container tab created"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 0,
+ "0 container tabs created with id 1"
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 1,
+ "1 container tab created with id 2"
+ );
+
+ await ContextualIdentityService.closeContainerTabs();
+ is(
+ ContextualIdentityService.countContainerTabs(),
+ 0,
+ "0 container tabs at the end."
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(1),
+ 0,
+ "0 container tabs at the end with id 1."
+ );
+ is(
+ ContextualIdentityService.countContainerTabs(2),
+ 0,
+ "0 container tabs at the end with id 2."
+ );
+});
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 0000000000..8c5f4fab76
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_eme.js
@@ -0,0 +1,223 @@
+/*
+ * Bug 1283325 - A test case to test the EME is originAttributes aware or not.
+ */
+const CC = Components.Constructor;
+
+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;
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await 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_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["media.mediasource.enabled", true],
+ ["media.mediasource.webm.enabled", true],
+ ["media.clearkey.persistent-license.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test() {
+ // Open a tab with the default container.
+ let defaultContainer = await 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 = await SpecialPowers.spawn(
+ defaultContainer.browser,
+ [keyInfo],
+ async function (aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the media key.
+ await 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();
+ await 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 = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ USER_ID_PERSONAL
+ );
+
+ await SpecialPowers.spawn(
+ personalContainer.browser,
+ [keyInfo],
+ async function (aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session to check that mediakeys do not share with
+ // default container.
+ await 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.
+ BrowserTestUtils.removeTab(defaultContainer.tab);
+
+ // Close personal container tab.
+ 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 0000000000..d6374ecd3c
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_favicon.js
@@ -0,0 +1,147 @@
+/*
+ * Bug 1270678 - A test case to test does the favicon obey originAttributes.
+ */
+
+let { HttpServer } = ChromeUtils.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();
+ }
+ );
+ });
+}
+
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await 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_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ // 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(async function test() {
+ waitForExplicitFinish();
+
+ // First, get the icon data.
+ await 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 = await openTabInUserContext(testURL, userContextId);
+
+ // Write a cookie according to the userContext.
+ await SpecialPowers.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);
+
+ await new Promise(resolve => {
+ PlacesUtils.favicons.setAndFetchFaviconForPage(
+ pageURI,
+ favIconURI,
+ true,
+ PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+ {
+ onComplete() {
+ resolve();
+ },
+ },
+ tabInfo.browser.contentPrincipal
+ );
+ });
+
+ 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 0000000000..d36e47d3cb
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -0,0 +1,259 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the media key.
+ */
+
+const CC = Components.Constructor;
+
+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.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await 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.
+async function setupEMEKey(browser) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+
+ // Setup the EME key.
+ let result = await SpecialPowers.spawn(
+ browser,
+ [keyInfo],
+ async function (aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the EME key.
+ await 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();
+ await 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.
+async function checkEMEKey(browser, emeSessionId) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+ keyInfo.sessionId = emeSessionId;
+
+ await SpecialPowers.spawn(browser, [keyInfo], async function (aKeyInfo) {
+ let access = await content.navigator.requestMediaKeySystemAccess(
+ "org.w3.clearkey",
+ [
+ {
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{ contentType: "video/webm" }],
+ sessionTypes: ["persistent-license"],
+ persistentState: "required",
+ },
+ ]
+ );
+ let mediaKeys = await access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session with the sessionId.
+ await 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_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["media.mediasource.enabled", true],
+ ["media.mediasource.webm.enabled", true],
+ ["media.clearkey.persistent-license.enabled", true],
+ ],
+ });
+});
+
+add_task(async 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] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Setup EME Key.
+ emeSessionIds[userContextId] = await setupEMEKey(
+ tabs[userContextId].browser
+ );
+
+ // Close this tab.
+ 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] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Check whether EME Key has been cleared.
+ await checkEMEKey(
+ tabs[userContextId].browser,
+ emeSessionIds[userContextId]
+ );
+
+ // Close this tab.
+ 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 0000000000..673df942d8
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
@@ -0,0 +1,95 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for cookies.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal"];
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, { userContextId });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async 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] = await openTabInUserContext(
+ TEST_URL + "file_reflect_cookie_into_title.html?" + value,
+ userContextId
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ 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.
+ for (let cookie of Services.cookies.getCookiesWithOriginAttributes(
+ JSON.stringify({}),
+ TEST_HOST
+ )) {
+ Services.cookies.remove(
+ cookie.host,
+ cookie.name,
+ cookie.path,
+ cookie.originAttributes
+ );
+ }
+
+ // Check that whether cookies has been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!cookies.length, "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 0000000000..d4b57d08f6
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
@@ -0,0 +1,170 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the quota manager.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal"];
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+// Setup an entry for the indexedDB.
+async function setupIndexedDB(browser) {
+ await SpecialPowers.spawn(
+ browser,
+ [{ input: "TestForgetAPIs" }],
+ async 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 = await 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 });
+
+ await 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);
+ await 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.
+async function checkIndexedDB(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = await 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");
+ }
+
+ db.close();
+
+ content.indexedDB.deleteDatabase("idb");
+ });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test_quota_clearStoragesForPrincipal() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Setup an entry for the indexedDB.
+ await setupIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Using quota manager to clear all indexed DB for a given domain.
+ let caUtils = {};
+ Services.scriptloader.loadSubScript(
+ "chrome://global/content/contentAreaUtils.js",
+ caUtils
+ );
+ let httpURI = caUtils.makeURI("http://" + TEST_HOST);
+ let httpPrincipal = Services.scriptSecurityManager.createContentPrincipal(
+ httpURI,
+ {}
+ );
+ let clearRequest = Services.qms.clearStoragesForPrincipal(
+ httpPrincipal,
+ null,
+ null,
+ true
+ );
+ await new Promise(resolve => {
+ clearRequest.callback = () => {
+ resolve();
+ };
+ });
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = await openTabInUserContext(
+ TEST_URL + "empty_file.html",
+ userContextId
+ );
+
+ // Check whether indexed DB has been cleared.
+ await checkIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ 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 0000000000..928675eb3f
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
@@ -0,0 +1,425 @@
+/*
+ * Bug 1238183 - Test cases for forgetAboutSite with userContextId.
+ */
+
+const CC = Components.Constructor;
+
+let { ForgetAboutSite } = ChromeUtils.importESModule(
+ "resource://gre/modules/ForgetAboutSite.sys.mjs"
+);
+let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+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);
+}
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, { userContextId });
+}
+
+function createURI(uri) {
+ return Services.io.newURI(uri);
+}
+
+function getCacheStorage(where, lci) {
+ if (!lci) {
+ lci = Services.loadContextInfo.default;
+ }
+ switch (where) {
+ case "disk":
+ return Services.cache2.diskCacheStorage(lci);
+ case "memory":
+ return Services.cache2.memoryCacheStorage(lci);
+ case "pin":
+ return Services.cache2.pinningCacheStorage(lci);
+ }
+ return null;
+}
+
+function OpenCacheEntry(key, where, flags, lci) {
+ return new Promise(resolve => {
+ key = createURI(key);
+ function CacheListener() {}
+ CacheListener.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsICacheEntryOpenCallback"]),
+
+ onCacheEntryCheck(entry) {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ },
+
+ onCacheEntryAvailable(entry, isnew, status) {
+ resolve();
+ },
+
+ run() {
+ let storage = getCacheStorage(where, lci);
+ storage.asyncOpenURI(key, "", flags, this);
+ },
+ };
+
+ new CacheListener().run();
+ });
+}
+
+//
+// Test functions.
+//
+
+// Cookies
+async 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] = await openTabInUserContext(
+ TEST_URL + "file_reflect_cookie_into_title.html?" + value,
+ userContextId
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ Assert.equal(foundCookie.name, COOKIE_NAME, "Check cookie name");
+ Assert.equal(
+ foundCookie.value,
+ USER_CONTEXTS[userContextId],
+ "Check cookie value"
+ );
+ }
+
+ // Forget the site.
+ await ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Check that whether cookies has been cleared or not.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!cookies.length, "No Cookie should be here");
+ }
+}
+
+// Cache
+async function test_cache_cleared() {
+ // First, add some caches.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ await OpenCacheEntry(
+ "http://" + TEST_HOST + "/",
+ "disk",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+
+ await OpenCacheEntry(
+ "http://" + TEST_HOST + "/",
+ "memory",
+ Ci.nsICacheStorage.OPEN_NORMALLY,
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+ }
+
+ // Check that caches have been set correctly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let mem = getCacheStorage(
+ "memory",
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+ let disk = getCacheStorage(
+ "disk",
+ Services.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.
+ await ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+
+ // Check that do caches be removed or not?
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let mem = getCacheStorage(
+ "memory",
+ Services.loadContextInfo.custom(false, { userContextId })
+ );
+ let disk = getCacheStorage(
+ "disk",
+ Services.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
+async 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] = await openTabInUserContext(
+ "http://localhost:" +
+ gHttpServer.identity.primaryPort +
+ "/loadImage.html",
+ userContextId
+ );
+ 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.
+ await ForgetAboutSite.removeDataFromDomain("localhost");
+
+ // Load again.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context to cache image.
+ tabs[userContextId] = await openTabInUserContext(
+ "http://localhost:" +
+ gHttpServer.identity.primaryPort +
+ "/loadImage.html",
+ userContextId
+ );
+ 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
+async 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 = await openTabInUserContext(
+ TEST_URL + "file_set_storages.html?" + value,
+ userContextId
+ );
+
+ // Check that the storages has been set correctly.
+ await SpecialPowers.spawn(
+ tabInfo.browser,
+ [{ userContext: USER_CONTEXTS[userContextId] }],
+ async 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 = await 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);
+
+ await new Promise(done => {
+ storeRequest.onsuccess = event => {
+ let res = storeRequest.result;
+ Assert.equal(
+ res.userContext,
+ arg.userContext,
+ "Check the indexedDB value"
+ );
+ done();
+ };
+ });
+ }
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+
+ // Forget the site.
+ await 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 = await openTabInUserContext(
+ TEST_URL + "file_set_storages.html",
+ userContextId
+ );
+
+ // Check that do storages be cleared or not.
+ await SpecialPowers.spawn(tabInfo.browser, [], async 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 = await 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.
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+}
+
+add_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ // 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(async 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 0000000000..ca9c956c82
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_imageCache.js
@@ -0,0 +1,70 @@
+let { HttpServer } = ChromeUtils.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 = atob(
+ "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_setup(async function () {
+ // make sure userContext is enabled.
+ await 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
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return tab;
+}
+
+add_task(async function test() {
+ for (
+ let userContextId = 0;
+ userContextId < NUM_USER_CONTEXTS;
+ userContextId++
+ ) {
+ let tab = await 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 0000000000..9b9fbcb737
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_middleClick.js
@@ -0,0 +1,50 @@
+"use strict";
+
+const BASE_ORIGIN = "http://example.com";
+const URI =
+ BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/empty_file.html";
+
+add_task(async function () {
+ info("Opening a new container tab...");
+
+ let tab = BrowserTestUtils.addTab(gBrowser, URI, { userContextId: 1 });
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ info("Create a HTMLAnchorElement...");
+ await SpecialPowers.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 = await new Promise((resolve, reject) => {
+ gBrowser.tabContainer.addEventListener(
+ "TabOpen",
+ function (openEvent) {
+ resolve(openEvent.target);
+ },
+ { once: true }
+ );
+
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#clickMe",
+ { button: 1 },
+ browser
+ );
+ });
+
+ is(newTab.getAttribute("usercontextid"), "1", "Correct UserContextId?");
+
+ // newTab shouldn't be closed in the same event tick as TabOpen.
+ await TestUtils.waitForTick();
+
+ BrowserTestUtils.removeTab(tab);
+ 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 0000000000..02f50f27d6
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
@@ -0,0 +1,214 @@
+"use strict";
+
+// Testing that when the user opens the add tab menu and clicks menu items
+// the correct context id is opened
+
+function findPopup(browser = gBrowser) {
+ return browser.tabContainer.querySelector(".new-tab-popup");
+}
+
+function findContextPopup() {
+ return document.querySelector("#new-tab-button-popup");
+}
+
+add_task(async function test_containers_no_left_click() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", false],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+ let popup = findPopup();
+ ok(popup, "new tab should have a popup");
+
+ // Test context menu
+ let contextMenu = findContextPopup();
+ is(contextMenu.state, "closed", "Context menu is initally closed.");
+
+ let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+
+ let target = gBrowser.tabContainer.newTabButton;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "contextmenu" });
+ await popupshown;
+
+ is(contextMenu.state, "open", "Context menu is open.");
+ let contextIdItems = contextMenu.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ contextMenu.hidePopup();
+});
+
+add_task(async function test_containers_with_left_click() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", true],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!findPopup(),
+ "Wait for popup to exist"
+ );
+ let popup = findPopup();
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "mousedown" });
+ await popupShownPromise;
+ let contextIdItems = popup.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ popup.hidePopup();
+ await popupHiddenPromise;
+
+ for (let i = 0; i <= 4; i++) {
+ popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "mousedown" });
+
+ await popupShownPromise;
+ let contextIdItem = popup.querySelector(
+ `menuitem[data-usercontextid="${i}"]`
+ );
+
+ ok(contextIdItem, `User context id ${i} exists`);
+
+ // waitForNewTab doesn't work for default tabs due to a different code path that doesn't cause a load event
+ let waitForTabPromise = BrowserTestUtils.waitForEvent(
+ gBrowser.tabContainer,
+ "TabOpen"
+ );
+ EventUtils.synthesizeMouseAtCenter(contextIdItem, {});
+
+ let tabEvent = await waitForTabPromise;
+ let tab = tabEvent.target;
+ if (i > 0) {
+ is(
+ tab.getAttribute("usercontextid"),
+ "" + i,
+ `New tab has UCI equal ${i}`
+ );
+ } else {
+ ok(!tab.hasAttribute("usercontextid"), `New tab has no UCI`);
+ }
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ // Test context menu
+ let contextMenu = findContextPopup();
+ is(contextMenu.state, "closed", "Context menu is initally closed.");
+
+ let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+
+ let target = gBrowser.tabContainer.newTabButton;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "contextmenu" });
+ await popupshown;
+
+ is(contextMenu.state, "open", "Context menu is open.");
+ contextIdItems = contextMenu.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ contextMenu.hidePopup();
+});
+
+add_task(async function test_no_containers_no_left_click() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", false],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", false],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+ let popup = findPopup();
+ ok(!popup, "new tab should not have a popup");
+
+ // Test context menu
+ let contextMenu = findContextPopup();
+ let target = gBrowser.tabContainer.newTabButton;
+ EventUtils.synthesizeMouseAtCenter(target, { type: "contextmenu" });
+ is(contextMenu.state, "closed", "Context menu does not open.");
+});
+
+add_task(async function test_private_mode() {
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let privateDocument = privateWindow.document;
+ let { tabContainer } = privateWindow.gBrowser;
+ let newTab = tabContainer.newTabButton;
+ let newTab2 = privateDocument.getElementById("new-tab-button");
+ // Check to ensure we are talking about the right button
+ ok(!!newTab.clientWidth, "new tab button should not be hidden");
+ ok(!newTab2.clientWidth, "overflow new tab button should be hidden");
+ let popup = findPopup(privateWindow.gBrowser);
+ ok(!popup, "new tab should not have a popup");
+ await BrowserTestUtils.closeWindow(privateWindow);
+});
+
+add_task(async function test_opening_container_tab_context() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["privacy.userContext.newTabContainerOnLeftClick.enabled", true],
+ ],
+ });
+
+ let newTabButton = gBrowser.tabContainer.newTabButton;
+ ok(newTabButton, "New tab button exists");
+ ok(!newTabButton.hidden, "New tab button is visible");
+
+ let popup = findContextPopup();
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "contextmenu" });
+ await popupShownPromise;
+ let contextIdItems = popup.querySelectorAll("menuitem");
+ // 4 + default + manage containers
+ is(contextIdItems.length, 6, "Has 6 menu items");
+ popup.hidePopup();
+ await popupHiddenPromise;
+
+ for (let i = 0; i <= 4; i++) {
+ popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(newTabButton, { type: "contextmenu" });
+
+ await popupShownPromise;
+ let contextIdItem = popup.querySelector(
+ `menuitem[data-usercontextid="${i}"]`
+ );
+
+ ok(contextIdItem, `User context id ${i} exists`);
+
+ // waitForNewTab doesn't work for default tabs due to a different code path that doesn't cause a load event
+ let waitForTabPromise = BrowserTestUtils.waitForEvent(
+ gBrowser.tabContainer,
+ "TabOpen"
+ );
+ popup.activateItem(contextIdItem);
+
+ let tabEvent = await waitForTabPromise;
+ let tab = tabEvent.target;
+ if (i > 0) {
+ is(
+ tab.getAttribute("usercontextid"),
+ "" + i,
+ `New tab has UCI equal ${i}`
+ );
+ } else {
+ ok(!tab.hasAttribute("usercontextid"), `New tab has no UCI`);
+ }
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js b/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js
new file mode 100644
index 0000000000..306f2da1ab
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_originattrs_reopenin.js
@@ -0,0 +1,184 @@
+/* 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/. */
+
+/* import-globals-from ../../../../base/content/test/tabs/helper_origin_attrs_testing.js */
+loadTestSubscript(
+ "../../../../base/content/test/tabs/helper_origin_attrs_testing.js"
+);
+
+const PATH =
+ "/browser/browser/components/contextualidentity/test/browser/blank.html";
+
+const URI_EXAMPLECOM = "https://example.com/" + PATH;
+const URI_EXAMPLEORG = "https://example.org/" + PATH;
+
+var TEST_CASES = [
+ URI_EXAMPLECOM,
+ URI_EXAMPLEORG,
+ "about:preferences",
+ "about:config",
+];
+
+var remoteTypes;
+
+var xulFrameLoaderCreatedCounter = {};
+
+function handleEventLocal(aEvent) {
+ if (aEvent.type != "XULFrameLoaderCreated") {
+ return;
+ }
+ // Ignore <browser> element in about:preferences and any other special pages
+ if ("gBrowser" in aEvent.target.ownerGlobal) {
+ xulFrameLoaderCreatedCounter.numCalledSoFar++;
+ }
+}
+const NUM_PAGES_OPEN_FOR_EACH_TEST_CASE = 5;
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ // don't preload tabs so we don't have extra XULFrameLoaderCreated events
+ // firing
+ ["browser.newtab.preload", false],
+ ],
+ });
+
+ requestLongerTimeout(5);
+});
+
+function setupRemoteTypes() {
+ remoteTypes = {
+ regular: { "about:preferences": null, "about:config": null },
+ 1: { "about:preferences": null, "about:config": null },
+ 2: { "about:preferences": null, "about:config": null },
+ 3: { "about:preferences": null, "about:config": null },
+ };
+ if (gFissionBrowser) {
+ remoteTypes.regular[URI_EXAMPLECOM] = "webIsolated=https://example.com";
+ remoteTypes.regular[URI_EXAMPLEORG] = "webIsolated=https://example.org";
+ remoteTypes["1"][URI_EXAMPLECOM] =
+ "webIsolated=https://example.com^userContextId=1";
+ remoteTypes["1"][URI_EXAMPLEORG] =
+ "webIsolated=https://example.org^userContextId=1";
+ remoteTypes["2"][URI_EXAMPLECOM] =
+ "webIsolated=https://example.com^userContextId=2";
+ remoteTypes["2"][URI_EXAMPLEORG] =
+ "webIsolated=https://example.org^userContextId=2";
+ remoteTypes["3"][URI_EXAMPLECOM] =
+ "webIsolated=https://example.com^userContextId=3";
+ remoteTypes["3"][URI_EXAMPLEORG] =
+ "webIsolated=https://example.org^userContextId=3";
+ } else {
+ remoteTypes.regular[URI_EXAMPLECOM] = "web";
+ remoteTypes.regular[URI_EXAMPLEORG] = "web";
+ remoteTypes["1"][URI_EXAMPLECOM] = "web";
+ remoteTypes["1"][URI_EXAMPLEORG] = "web";
+ remoteTypes["2"][URI_EXAMPLECOM] = "web";
+ remoteTypes["2"][URI_EXAMPLEORG] = "web";
+ remoteTypes["3"][URI_EXAMPLECOM] = "web";
+ remoteTypes["3"][URI_EXAMPLEORG] = "web";
+ }
+}
+
+add_task(async function testReopen() {
+ setupRemoteTypes();
+ /**
+ * Open a regular tab
+ * For each url
+ * navigate regular tab to that url
+ * pid_seen = [regular tab pid]
+ * For each container
+ * reopen regular tab in that container
+ * verify remote type
+ * reopen container tab in regular tab
+ * Close all the tabs
+ * This tests that behaviour of reopening tabs is correct
+ */
+
+ let regularPage = await openURIInRegularTab("about:blank");
+ var currRemoteType;
+
+ for (const uri of TEST_CASES) {
+ // Navigate regular tab to a test-uri
+ let loaded = BrowserTestUtils.browserLoaded(
+ regularPage.tab.linkedBrowser,
+ false,
+ uri
+ );
+ await BrowserTestUtils.loadURIString(regularPage.tab.linkedBrowser, uri);
+ await loaded;
+ info(`Start Opened ${uri} in a regular tab`);
+ currRemoteType = regularPage.tab.linkedBrowser.remoteType;
+ is(currRemoteType, remoteTypes.regular[uri], "correct remote type");
+
+ let containerTabs = [];
+
+ // Reopen this test-uri in different containers and ensure pids are different
+ for (var userCtxId = 1; userCtxId <= NUM_USER_CONTEXTS; userCtxId++) {
+ // Prepare the reopen menu
+ let reopenMenu = await openReopenMenuForTab(regularPage.tab);
+
+ // Add a listener for XULFrameLoaderCreated
+ initXulFrameLoaderCreatedCounter(xulFrameLoaderCreatedCounter);
+ regularPage.tab.ownerGlobal.gBrowser.addEventListener(
+ "XULFrameLoaderCreated",
+ handleEventLocal
+ );
+
+ // Reopen the page in a container
+ let containerTab = await openTabInContainer(
+ gBrowser,
+ uri,
+ reopenMenu,
+ userCtxId.toString()
+ );
+ info(`Re-opened ${uri} in a container tab ${userCtxId}`);
+
+ // Check correct remote type
+ currRemoteType = containerTab.linkedBrowser.remoteType;
+ is(
+ currRemoteType,
+ remoteTypes[userCtxId.toString()][uri],
+ "correct remote type"
+ );
+
+ // Check that XULFrameLoaderCreated has fired off correct number of times
+ is(
+ xulFrameLoaderCreatedCounter.numCalledSoFar,
+ 1,
+ `XULFrameLoaderCreated was fired once when reopening ${uri} in container ${userCtxId}`
+ );
+ regularPage.tab.ownerGlobal.gBrowser.removeEventListener(
+ "XULFrameLoaderCreated",
+ handleEventLocal
+ );
+
+ // Save the tab so we can close it later
+ containerTabs.push(containerTab);
+ }
+ let containedTabsOpenedInNoContainer = [];
+
+ for (const containerTab of containerTabs) {
+ info(`About to re-open ${uri} in a regular, no-container tab`);
+ let reopenMenu = await openReopenMenuForTab(containerTab);
+ let reopenedInNoContainerTab = await openTabInContainer(
+ gBrowser,
+ uri,
+ reopenMenu,
+ "0"
+ );
+ info(`Re-opened ${uri} in a regular, no-container tab`);
+ // Check remote type
+ currRemoteType = reopenedInNoContainerTab.linkedBrowser.remoteType;
+ is(currRemoteType, remoteTypes.regular[uri], "correct remote type");
+ containedTabsOpenedInNoContainer.push(reopenedInNoContainerTab);
+ }
+
+ // Close tabs
+ for (const tab of containerTabs.concat(containedTabsOpenedInNoContainer)) {
+ BrowserTestUtils.removeTab(tab);
+ }
+ }
+ BrowserTestUtils.removeTab(regularPage.tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_relatedTab.js b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
new file mode 100644
index 0000000000..3451d3d16e
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
@@ -0,0 +1,94 @@
+"use strict";
+
+/*
+ * Bug 1325014 - Adding tab related to current tab inherits current tab's container usercontextid unless otherwise specified
+ */
+
+add_task(async function () {
+ let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ userContextId: 1,
+ });
+ let ReferrerInfo = Components.Constructor(
+ "@mozilla.org/referrer-info;1",
+ "nsIReferrerInfo",
+ "init"
+ );
+
+ gBrowser.selectedTab = tab;
+ let relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ relatedToCurrent: true,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "1",
+ "Related tab (relatedToCurrent) inherits current tab's usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 1,
+ "Related tab's browser actually inherits the current tab's usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ gBrowser.selectedTab = tab;
+ relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ relatedToCurrent: true,
+ userContextId: 2,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "2",
+ "Related tab (relatedToCurrent) with overridden usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 2,
+ "Related tab's browser actually gets overridden usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ gBrowser.selectedTab = tab;
+ let referrerInfo = new ReferrerInfo(
+ Ci.nsIReferrerInfo.EMPTY,
+ true,
+ gBrowser.currentURI
+ );
+ relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ referrerInfo,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "1",
+ "Related tab (referrer) inherits current tab's usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 1,
+ "Related tab's browser (referrer) actually inherits the current tab's usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ gBrowser.selectedTab = tab;
+ referrerInfo = new ReferrerInfo(
+ Ci.nsIReferrerInfo.EMPTY,
+ true,
+ gBrowser.currentURI
+ );
+ relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ referrerInfo,
+ userContextId: 2,
+ });
+ is(
+ relatedTab.getAttribute("usercontextid"),
+ "2",
+ "Related tab (referrer) with overridden usercontextid"
+ );
+ is(
+ relatedTab.linkedBrowser.contentPrincipal.userContextId,
+ 2,
+ "Related tab's browser (referrer) actually gets overridden usercontextid"
+ );
+ BrowserTestUtils.removeTab(relatedTab);
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_reopenIn.js b/browser/components/contextualidentity/test/browser/browser_reopenIn.js
new file mode 100644
index 0000000000..9bda63b201
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_reopenIn.js
@@ -0,0 +1,164 @@
+"use strict";
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+async function openTabMenuFor(tab) {
+ let tabMenu = tab.ownerDocument.getElementById("tabContextMenu");
+
+ let tabMenuShown = BrowserTestUtils.waitForEvent(tabMenu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(
+ tab,
+ { type: "contextmenu" },
+ tab.ownerGlobal
+ );
+ await tabMenuShown;
+
+ return tabMenu;
+}
+
+async function openReopenMenuForTab(tab) {
+ await openTabMenuFor(tab);
+
+ let reopenItem = tab.ownerDocument.getElementById(
+ "context_reopenInContainer"
+ );
+ ok(!reopenItem.hidden, "Reopen in Container item should be shown");
+
+ let reopenMenu = reopenItem.getElementsByTagName("menupopup")[0];
+ let reopenMenuShown = BrowserTestUtils.waitForEvent(reopenMenu, "popupshown");
+ reopenItem.openMenu(true);
+ await reopenMenuShown;
+
+ return reopenMenu;
+}
+
+function checkMenuItem(reopenMenu, shown, hidden) {
+ for (let id of shown) {
+ ok(
+ reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`),
+ `User context id ${id} should exist`
+ );
+ }
+ for (let id of hidden) {
+ ok(
+ !reopenMenu.querySelector(`menuitem[data-usercontextid="${id}"]`),
+ `User context id ${id} shouldn't exist`
+ );
+ }
+}
+
+function openTabInContainer(gBrowser, reopenMenu, id) {
+ let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL, true);
+ let menuitem = reopenMenu.querySelector(
+ `menuitem[data-usercontextid="${id}"]`
+ );
+ reopenMenu.activateItem(menuitem);
+ return tabPromise;
+}
+
+add_task(async function testReopen() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: TEST_URL,
+ });
+ ok(
+ !tab.hasAttribute("usercontextid"),
+ "Tab with No Container should be opened"
+ );
+
+ let reopenMenu = await openReopenMenuForTab(tab);
+ checkMenuItem(reopenMenu, [1, 2, 3, 4], [0]);
+
+ let containerTab = await openTabInContainer(gBrowser, reopenMenu, "1");
+
+ is(
+ containerTab.getAttribute("usercontextid"),
+ "1",
+ "Tab with UCI=1 should be opened"
+ );
+ is(
+ containerTab.linkedBrowser.currentURI.spec,
+ TEST_URL,
+ "Same page should be opened"
+ );
+
+ reopenMenu = await openReopenMenuForTab(containerTab);
+ checkMenuItem(reopenMenu, [0, 2, 3, 4], [1]);
+
+ let noContainerTab = await openTabInContainer(gBrowser, reopenMenu, "0");
+
+ ok(
+ !noContainerTab.hasAttribute("usercontextid"),
+ "Tab with no UCI should be opened"
+ );
+ is(
+ noContainerTab.linkedBrowser.currentURI.spec,
+ TEST_URL,
+ "Same page should be opened"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(containerTab);
+ BrowserTestUtils.removeTab(noContainerTab);
+});
+
+add_task(async function testDisabled() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", false]],
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: TEST_URL,
+ });
+ ok(
+ !tab.hasAttribute("usercontextid"),
+ "Tab with No Container should be opened"
+ );
+
+ let tabMenu = await openTabMenuFor(tab);
+ let reopenItem = document.getElementById("context_reopenInContainer");
+ ok(reopenItem.hidden, "Reopen in Container item should be hidden");
+
+ // Close the tab menu.
+ tabMenu.hidePopup();
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function testPrivateMode() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser: privateWindow.gBrowser,
+ url: TEST_URL,
+ });
+ ok(
+ !tab.hasAttribute("usercontextid"),
+ "Tab with No Container should be opened"
+ );
+
+ let tabMenu = await openTabMenuFor(tab);
+ let reopenItem = privateWindow.document.getElementById(
+ "context_reopenInContainer"
+ );
+ ok(reopenItem.hidden, "Reopen in Container item should be hidden");
+
+ // Close the tab menu.
+ tabMenu.hidePopup();
+
+ await BrowserTestUtils.closeWindow(privateWindow);
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
new file mode 100644
index 0000000000..df79de9ca2
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
@@ -0,0 +1,122 @@
+/*
+ * Bug 1334587 - A Test case for checking whether forgetting APIs are working for cookies.
+ */
+
+const CC = Components.Constructor;
+
+const TEST_HOST = "example.com";
+const TEST_URL =
+ "http://" +
+ TEST_HOST +
+ "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = ["default", "personal", "work"];
+
+const DELETE_CONTEXT = 1;
+const COOKIE_NAME = "userContextId";
+
+//
+// Support functions.
+//
+
+async function openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, { userContextId });
+}
+
+//
+// Test functions.
+//
+
+add_setup(async function () {
+ // Make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+function checkCookies(ignoreContext = null) {
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ if (ignoreContext && userContextId === String(ignoreContext)) {
+ continue;
+ }
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ is(foundCookie.name, COOKIE_NAME, "Check cookie name");
+ is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+}
+
+function deleteCookies(onlyContext = null) {
+ // Using getCookiesWithOriginAttributes() to get all cookies for a certain
+ // domain by using the originAttributes pattern, and clear all these cookies.
+ for (let cookie of Services.cookies.getCookiesWithOriginAttributes(
+ JSON.stringify({}),
+ TEST_HOST
+ )) {
+ if (!onlyContext || cookie.originAttributes.userContextId == onlyContext) {
+ Services.cookies.remove(
+ cookie.host,
+ cookie.name,
+ cookie.path,
+ cookie.originAttributes
+ );
+ }
+ }
+}
+
+add_task(async function test_cookie_getCookiesWithOriginAttributes() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 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] = await openTabInUserContext(
+ TEST_URL + "file_reflect_cookie_into_title.html?" + value,
+ userContextId
+ );
+
+ // Close this tab.
+ BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(cookies.length, "Cookies available");
+
+ let foundCookie = cookies[0];
+ is(foundCookie.name, COOKIE_NAME, "Check cookie name");
+ is(foundCookie.value, USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+ checkCookies();
+
+ deleteCookies(DELETE_CONTEXT);
+
+ checkCookies(DELETE_CONTEXT);
+
+ deleteCookies();
+
+ // Check that whether cookies has been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let cookies = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!cookies.length, "No Cookie should be here");
+ }
+});
diff --git a/browser/components/contextualidentity/test/browser/browser_saveLink.js b/browser/components/contextualidentity/test/browser/browser_saveLink.js
new file mode 100644
index 0000000000..65f60f7eef
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_saveLink.js
@@ -0,0 +1,134 @@
+"use strict";
+
+const BASE_ORIGIN = "https://example.com";
+const URI =
+ BASE_ORIGIN +
+ "/browser/browser/components/contextualidentity/test/browser/saveLink.sjs";
+
+let MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+add_setup(async function () {
+ info("Setting the prefs.");
+
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ // This test does a redirect from https to http and it checks the
+ // cookies. This is incompatible with the cookie SameSite schemeful
+ // feature and we need to disable it.
+ ["network.cookie.sameSite.schemeful", false],
+ // This Test trys to download mixed content
+ // so we need to make sure that this is not blocked
+ ["dom.block_download_insecure", false],
+ ],
+ });
+});
+
+add_task(async function test() {
+ info("Let's open a new window");
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+
+ info("Opening tab with UCI: 1");
+ let tab = BrowserTestUtils.addTab(win.gBrowser, URI + "?UCI=1", {
+ userContextId: 1,
+ });
+
+ // select tab and make sure its browser is focused
+ win.gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ info("Waiting to load content");
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ win.document,
+ "popupshown"
+ );
+
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "#fff",
+ { type: "contextmenu", button: 2 },
+ win.gBrowser.selectedBrowser
+ );
+ info("Right clicked!");
+
+ await popupShownPromise;
+ info("Context menu opened");
+
+ info("Let's create a temporary dir");
+ let tempDir = createTemporarySaveDirectory();
+ let destFile;
+
+ MockFilePicker.displayDirectory = tempDir;
+ MockFilePicker.showCallback = fp => {
+ info("MockFilePicker showCallback");
+
+ let fileName = fp.defaultString;
+ destFile = tempDir.clone();
+ destFile.append(fileName);
+
+ MockFilePicker.setFiles([destFile]);
+ MockFilePicker.filterIndex = 1; // kSaveAsType_URL
+
+ info("MockFilePicker showCallback done");
+ };
+
+ let transferCompletePromise = new Promise(resolve => {
+ function onTransferComplete(downloadSuccess) {
+ ok(downloadSuccess, "File should have been downloaded successfully");
+ resolve();
+ }
+
+ mockTransferCallback = onTransferComplete;
+ mockTransferRegisterer.register();
+ });
+
+ registerCleanupFunction(function () {
+ mockTransferRegisterer.unregister();
+ MockFilePicker.cleanup();
+ tempDir.remove(true);
+ });
+
+ // Select "Save Link As" option from context menu
+ let saveLinkCommand = win.document.getElementById("context-savelink");
+ info("saveLinkCommand: " + saveLinkCommand);
+ saveLinkCommand.doCommand();
+
+ let contextMenu = win.document.getElementById("contentAreaContextMenu");
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await popupHiddenPromise;
+ info("popup hidden");
+
+ await transferCompletePromise;
+
+ // Let's query the SJS to know if the download happened with the correct cookie.
+ let response = await fetch(URI + "?result=1");
+ let text = await response.text();
+ is(text, "Result:UCI=1", "Correct cookie used: -" + text + "-");
+
+ info("Closing the window");
+ await BrowserTestUtils.closeWindow(win);
+});
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
+ this
+);
+
+function createTemporarySaveDirectory() {
+ let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ saveDir.append("testsavedir");
+ if (!saveDir.exists()) {
+ info("create testsavedir!");
+ saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ }
+ info("return from createTempSaveDir: " + saveDir.path);
+ return saveDir;
+}
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 0000000000..4f42c55d73
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_serviceworkers.js
@@ -0,0 +1,121 @@
+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 = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ return tab;
+}
+
+add_setup(async function () {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["dom.ipc.processCount", 1],
+ ],
+ });
+});
+
+let infos = [];
+
+add_task(async 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
+ await BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab));
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+ }
+
+ if (!allRegistered()) {
+ await promiseAllRegistered();
+ }
+ ok(true, "all service workers are registered");
+
+ // Unregistered all service workers added in this test
+ for (let info of infos) {
+ await 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() {
+ if (allRegistered()) {
+ swm.removeListener(listener);
+ resolve();
+ }
+ },
+ };
+ swm.addListener(listener);
+ });
+}
+
+function promiseUnregister(info) {
+ return new Promise(function (resolve) {
+ swm.unregister(
+ info.principal,
+ {
+ unregisterSucceeded(aState) {
+ ok(aState, "ServiceWorkerRegistration exists");
+ resolve();
+ },
+ unregisterFailed(aState) {
+ ok(false, "unregister should succeed");
+ },
+ },
+ info.scope
+ );
+ });
+}
diff --git a/browser/components/contextualidentity/test/browser/browser_tab_color_update.js b/browser/components/contextualidentity/test/browser/browser_tab_color_update.js
new file mode 100644
index 0000000000..e4ae649d0a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_tab_color_update.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser, false, uri);
+ return { tab };
+}
+
+/**
+ * When a container's colour changes, the tab colour should update.
+ */
+add_task(async function test_tab_color_updates() {
+ const kId = 2;
+ let { tab } = await openTabInUserContext("https://example.com/", kId);
+ let contextIdInfo = ContextualIdentityService.getPublicIdentityFromId(kId);
+ ok(
+ tab.classList.contains("identity-color-" + contextIdInfo.color),
+ `Should use color ${contextIdInfo.color} for tab`
+ );
+
+ // The container has a localized name which isn't in the contextIdInfo object.
+ // We need to pass a valid name for the update to go through, so make one up.
+ let name = "Foo";
+ // Don't hardcode a colour so changes in defaults won't cause the following
+ // test to fail or be a no-op.
+ let otherColor = contextIdInfo.color == "green" ? "orange" : "green";
+ registerCleanupFunction(() => ContextualIdentityService.resetDefault());
+ ContextualIdentityService.update(kId, name, contextIdInfo.icon, otherColor);
+
+ ok(
+ tab.classList.contains("identity-color-" + otherColor),
+ `Should use color ${otherColor} for tab after update`
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
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 0000000000..dafdf95394
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontext.js
@@ -0,0 +1,88 @@
+/* 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 = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ return tab;
+}
+
+add_setup(async function () {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["dom.ipc.processCount", 1],
+ ],
+ });
+});
+
+add_task(async 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
+ await 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);
+ await 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);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // get the title
+ let title = browser.contentTitle.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_new_window.js b/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js
new file mode 100644
index 0000000000..5fd4c6f3c2
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_new_window.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the content of new browser windows have the expected
+// userContextId when it passed as the window arguments.
+
+const TEST_URI =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://mochi.test:8888"
+ ) + "empty_file.html";
+
+function openWindowWithUserContextId(userContextId, isPrivate) {
+ let flags = "chrome,dialog=no,all";
+ if (isPrivate) {
+ flags += ",private";
+ }
+
+ let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+
+ let urlSupports = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ urlSupports.data = TEST_URI;
+ args.appendElement(urlSupports);
+
+ args.appendElement(null);
+ args.appendElement(null);
+ args.appendElement(null);
+ args.appendElement(null);
+
+ let userContextIdSupports = Cc[
+ "@mozilla.org/supports-PRUint32;1"
+ ].createInstance(Ci.nsISupportsPRUint32);
+ userContextIdSupports.data = userContextId;
+ args.appendElement(userContextIdSupports);
+
+ args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
+ args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
+ args.appendElement(Services.scriptSecurityManager.getSystemPrincipal());
+
+ let windowPromise = BrowserTestUtils.waitForNewWindow({ url: TEST_URI });
+ Services.ww.openWindow(
+ null,
+ AppConstants.BROWSER_CHROME_URL,
+ "_blank",
+ flags,
+ args
+ );
+ return windowPromise;
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.userContext.enabled", true]],
+ });
+});
+
+add_task(async function test_new_window() {
+ let win = await openWindowWithUserContextId(1, false);
+
+ await SpecialPowers.spawn(win.gBrowser.selectedBrowser, [TEST_URI], url => {
+ Assert.equal(content.document.URL, url, "expected document URL");
+ let { originAttributes } = content.document.nodePrincipal;
+ Assert.equal(originAttributes.userContextId, 1, "expected userContextId");
+ Assert.equal(
+ originAttributes.privateBrowsingId,
+ 0,
+ "expected non-private context"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function test_new_private_window() {
+ let win = await openWindowWithUserContextId(1, true);
+
+ await SpecialPowers.spawn(win.gBrowser.selectedBrowser, [TEST_URI], url => {
+ Assert.equal(content.document.URL, url, "expected document URL");
+ let { originAttributes } = content.document.nodePrincipal;
+ Assert.equal(originAttributes.userContextId, 1, "expected userContextId");
+ Assert.equal(
+ originAttributes.privateBrowsingId,
+ 1,
+ "expected private context"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
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 0000000000..bddf39eb87
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
@@ -0,0 +1,181 @@
+"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(async function () {
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/");
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ "http://test1.example.com/",
+ true
+ );
+
+ // 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
+ );
+
+ await awaitDrop;
+
+ let tab2 = await newTabPromise;
+ Assert.ok(
+ !tab2.hasAttribute("usercontextid"),
+ "Tab shouldn't have usercontextid attribute"
+ );
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], async 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");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ 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(async function () {
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/", {
+ userContextId: 1,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
+ let newTabPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ "http://test1.example.com/",
+ true
+ );
+
+ // 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
+ );
+
+ await awaitDrop;
+
+ let tab2 = await newTabPromise;
+ Assert.equal(tab2.getAttribute("usercontextid"), 1);
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], async 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");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ 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(async function () {
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/", {
+ userContextId: 1,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let tab2 = BrowserTestUtils.addTab(gBrowser, "http://example.org/", {
+ userContextId: 2,
+ });
+ await 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
+ );
+
+ await awaitDrop;
+ Assert.equal(tab2.getAttribute("usercontextid"), 2);
+
+ await BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
+
+ await SpecialPowers.spawn(tab2.linkedBrowser, [], async 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");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+ 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 0000000000..5ba2cc0e0a
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_windowName.js
@@ -0,0 +1,80 @@
+/* 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_setup(async function () {
+ // make sure userContext is enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["browser.link.open_newwindow", 3],
+ ],
+ });
+});
+
+add_task(async function test() {
+ info("Creating first tab...");
+ let tab1 = BrowserTestUtils.addTab(gBrowser, BASE_URI + "?old", {
+ userContextId: 1,
+ });
+ let browser1 = gBrowser.getBrowserForTab(tab1);
+ await BrowserTestUtils.browserLoaded(browser1);
+ await SpecialPowers.spawn(browser1, [], function (opts) {
+ content.window.name = "tab-1";
+ });
+
+ info("Creating second tab...");
+ let tab2 = BrowserTestUtils.addTab(gBrowser, BASE_URI + "?old", {
+ userContextId: 2,
+ });
+ let browser2 = gBrowser.getBrowserForTab(tab2);
+ await BrowserTestUtils.browserLoaded(browser2);
+ await SpecialPowers.spawn(browser2, [], 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...");
+ await SpecialPowers.spawn(
+ browser1,
+ [{ url: BASE_URI + "?new" }],
+ async function (opts) {
+ await 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.tabs.length; ++i) {
+ let tab = gBrowser.tabs[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 0000000000..d550b03ed8
--- /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_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.userContext.enabled", true],
+ ["browser.link.open_newwindow", 2],
+ ],
+ });
+});
+
+add_task(async function test() {
+ info("Creating a tab with UCI = 1...");
+ let tab = BrowserTestUtils.addTab(gBrowser, BASE_URI, { userContextId: 1 });
+ is(tab.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ info("Opening a new window from this tab...");
+ let newWinPromise = BrowserTestUtils.waitForNewWindow({ url: BASE_URI });
+ SpecialPowers.spawn(browser, [BASE_URI], function (url) {
+ content.window.newWindow = content.window.open(url, "_blank");
+ });
+
+ let newWin = await newWinPromise;
+ let newTab = newWin.gBrowser.selectedTab;
+
+ is(newTab.getAttribute("usercontextid"), "1", "New tab has UCI equal 1");
+
+ info("Closing the new window and tab...");
+ await BrowserTestUtils.closeWindow(newWin);
+ 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 0000000000..c6d11dcd57
--- /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 0000000000..5535363c94
--- /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 0000000000..cc05453507
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/file_reflect_cookie_into_title.html
@@ -0,0 +1,22 @@
+<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) {
+ 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 0000000000..96c46f9062
--- /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">
+ "use strict";
+
+ // if we have a query string, use it to set storages
+ if (window.location.search.length) {
+ 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/head.js b/browser/components/contextualidentity/test/browser/head.js
new file mode 100644
index 0000000000..b5bf451d25
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/head.js
@@ -0,0 +1,48 @@
+async function openTabMenuFor(tab) {
+ let tabMenu = tab.ownerDocument.getElementById("tabContextMenu");
+
+ let tabMenuShown = BrowserTestUtils.waitForEvent(tabMenu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(
+ tab,
+ { type: "contextmenu" },
+ tab.ownerGlobal
+ );
+ await tabMenuShown;
+
+ return tabMenu;
+}
+
+async function openReopenMenuForTab(tab) {
+ await openTabMenuFor(tab);
+
+ let reopenItem = tab.ownerDocument.getElementById(
+ "context_reopenInContainer"
+ );
+ ok(!reopenItem.hidden, "Reopen in Container item should be shown");
+
+ const menuPopup = reopenItem.menupopup;
+ const menuPopupPromise = BrowserTestUtils.waitForEvent(
+ menuPopup,
+ "popupshown"
+ );
+ info(`About to open a popup`);
+ reopenItem.openMenu(true);
+ info(`Waiting for the menu popup promise`);
+ await menuPopupPromise;
+ info(`Awaited menu popup promise`);
+ return menuPopup;
+}
+
+function openTabInContainer(gBrowser, url, reopenMenu, id) {
+ let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url, true);
+ let menuitem = reopenMenu.querySelector(
+ `menuitem[data-usercontextid="${id}"]`
+ );
+ info(`about to activate item`);
+ reopenMenu.activateItem(menuitem);
+ return tabPromise;
+}
+
+function loadTestSubscript(filePath) {
+ Services.scriptloader.loadSubScript(new URL(filePath, gTestPath).href, this);
+}
diff --git a/browser/components/contextualidentity/test/browser/saveLink.sjs b/browser/components/contextualidentity/test/browser/saveLink.sjs
new file mode 100644
index 0000000000..bbdb756364
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/saveLink.sjs
@@ -0,0 +1,55 @@
+const HTTP_ORIGIN = "http://example.com";
+const HTTPS_ORIGIN = "https://example.com";
+const URI_PATH =
+ "/browser/browser/components/contextualidentity/test/browser/saveLink.sjs";
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(aRequest, aResponse) {
+ var params = new URLSearchParams(aRequest.queryString);
+
+ // This is the first request, where we set the cookie.
+ if (params.has("UCI")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.setHeader("Set-Cookie", "UCI=" + params.get("UCI"));
+ aResponse.write(
+ "<html><body><a href='" +
+ HTTPS_ORIGIN +
+ URI_PATH +
+ "?redirect=1' id='fff'>this is a link</a></body></html>"
+ );
+ return;
+ }
+
+ // Second request. This is the save-as content, but we make a redirect to see
+ // if we are able to follow it.
+ if (params.has("redirect")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 302, "Found");
+ aResponse.setHeader(
+ "Location",
+ HTTP_ORIGIN + URI_PATH + "?download=1",
+ false
+ );
+ aResponse.write("Redirect!");
+ return;
+ }
+
+ // This is the 3rd request where we offer the content to be saved.
+ if (params.has("download")) {
+ setState("downloadUCI", aRequest.getHeader("Cookie"));
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.write("All Good!");
+ return;
+ }
+
+ // This is the last request to check that the download happened with the correct cookie
+ if (params.has("result")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.write("Result:" + getState("downloadUCI"));
+ return;
+ }
+
+ // We should not be here!
+ aResponse.setStatusLine(aRequest.httpVersion, 500);
+ aResponse.write("ERROR!!!");
+}
diff --git a/browser/components/contextualidentity/test/browser/serviceworker.html b/browser/components/contextualidentity/test/browser/serviceworker.html
new file mode 100644
index 0000000000..11edd001a2
--- /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 0000000000..2aba167d18
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/worker.js
@@ -0,0 +1 @@
+// empty worker, always succeed!