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