summaryrefslogtreecommitdiffstats
path: root/browser/components/privatebrowsing
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/privatebrowsing')
-rw-r--r--browser/components/privatebrowsing/content/aboutPrivateBrowsing.css8
-rw-r--r--browser/components/privatebrowsing/content/aboutPrivateBrowsing.html64
-rw-r--r--browser/components/privatebrowsing/content/aboutPrivateBrowsing.js141
-rw-r--r--browser/components/privatebrowsing/jar.mn8
-rw-r--r--browser/components/privatebrowsing/moz.build14
-rw-r--r--browser/components/privatebrowsing/test/browser/browser.ini64
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js64
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js445
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js206
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js25
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_search_banner.js317
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_blobUrl.js69
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js97
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js65
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cleanup.js46
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js101
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html33
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js69
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_crh.js48
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir.js133
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js146
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_toggle.js125
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js324
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html13
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_history_shift_click.js69
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_lastpbcontextexited.js63
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js28
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js46
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page.html11
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page2.html10
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page1.html10
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page2.html10
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js71
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js29
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_nonbrowser.js21
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js175
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html8
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js61
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js85
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js83
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js62
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html13
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_rememberprompt.js90
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_sidebar.js85
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_theming.js46
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_ui.js99
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js48
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js112
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle_page.html9
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_xrprompt_page.html11
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js46
-rw-r--r--browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js80
-rw-r--r--browser/components/privatebrowsing/test/browser/empty_file.html1
-rw-r--r--browser/components/privatebrowsing/test/browser/file_favicon.html11
-rw-r--r--browser/components/privatebrowsing/test/browser/file_favicon.pngbin0 -> 344 bytes
-rw-r--r--browser/components/privatebrowsing/test/browser/file_favicon.png^headers^1
-rw-r--r--browser/components/privatebrowsing/test/browser/file_triggeringprincipal_oa.html10
-rw-r--r--browser/components/privatebrowsing/test/browser/head.js90
-rw-r--r--browser/components/privatebrowsing/test/browser/popup.html12
-rw-r--r--browser/components/privatebrowsing/test/browser/title.sjs22
60 files changed, 4253 insertions, 0 deletions
diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css
new file mode 100644
index 0000000000..26d143a7b0
--- /dev/null
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.css
@@ -0,0 +1,8 @@
+/* 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/. */
+
+html.private .showNormal,
+html.normal .showPrivate {
+ display: none !important;
+}
diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.html b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.html
new file mode 100644
index 0000000000..4d22812723
--- /dev/null
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.html
@@ -0,0 +1,64 @@
+<!--
+# 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/.
+-->
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml" class="private">
+ <head>
+ <meta http-equiv="Content-Security-Policy" content="default-src chrome: blob:; object-src 'none'"/>
+ <link rel="icon" href="chrome://browser/skin/privatebrowsing/favicon.svg"/>
+ <link rel="stylesheet" href="chrome://browser/content/aboutPrivateBrowsing.css" media="all"/>
+ <link rel="stylesheet" href="chrome://browser/skin/privatebrowsing/aboutPrivateBrowsing.css" media="all"/>
+ <link rel="localization" href="branding/brand.ftl"/>
+ <link rel="localization" href="browser/branding/brandings.ftl">
+ <link rel="localization" href="browser/aboutPrivateBrowsing.ftl"/>
+ <script src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
+ <script src="chrome://browser/content/contentSearchHandoffUI.js"></script>
+ </head>
+
+ <body>
+ <p class="showNormal" data-l10n-id="about-private-browsing-not-private"></p>
+ <button id="startPrivateBrowsing"
+ class="showNormal" data-l10n-id="privatebrowsingpage-open-private-window-label"></button>
+ <div id="search-banner" class="search-banner"
+ hidden="true">
+ <button id="search-banner-close-button"
+ class="search-banner-close-button"
+ data-l10n-id="about-private-browsing-search-banner-close-button">
+ <img class="search-banner-close-image" src="chrome://global/skin/icons/close.svg"/>
+ </button>
+ <div class="banner-body">
+ <h1 id="about-private-browsing-search-banner-title"
+ data-l10n-id="about-private-browsing-search-banner-title"
+ data-l10n-args='{"engineName": ""}'></h1>
+ <p id="about-private-browsing-search-banner-description"
+ data-l10n-id="about-private-browsing-search-banner-description">
+ <a href="" id="open-search-options-link" data-l10n-name="link-options"></a>
+ </p>
+ </div>
+ </div>
+ <div class="showPrivate showSearch container">
+ <div class="logo-and-wordmark">
+ <div class="logo"></div>
+ <div class="wordmark"></div>
+ </div>
+ <div class="search-inner-wrapper">
+ <button id="search-handoff-button" class="search-handoff-button" tabindex="-1" data-l10n-id="about-private-browsing">
+ <div class="fake-textbox" data-l10n-id="about-private-browsing-search-placeholder"></div>
+ <input id="fake-editable" class="fake-editable" tabindex="-1" aria-hidden="true" />
+ <div class="fake-caret"></div>
+ </button>
+ </div>
+ <div class="info">
+ <h1 data-l10n-id="about-private-browsing-info-title"></h1>
+ <p data-l10n-id="about-private-browsing-info-description"></p>
+ <a id="private-browsing-myths" data-l10n-id="about-private-browsing-info-myths"></a>
+ </div>
+ </div>
+
+ <p id="private-browsing-vpn-text" class="vpn-promo" data-l10n-id="about-private-browsing-need-more-privacy"></p>
+ <a id="private-browsing-vpn-link" class="vpn-promo" data-l10n-id="about-private-browsing-turn-on-vpn"></a>
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
new file mode 100644
index 0000000000..5d9b78b902
--- /dev/null
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -0,0 +1,141 @@
+/* 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/. */
+
+/* eslint-env mozilla/frame-script */
+
+document.addEventListener("DOMContentLoaded", function() {
+ if (!RPMIsWindowPrivate()) {
+ document.documentElement.classList.remove("private");
+ document.documentElement.classList.add("normal");
+ document
+ .getElementById("startPrivateBrowsing")
+ .addEventListener("click", function() {
+ RPMSendAsyncMessage("OpenPrivateWindow");
+ });
+ return;
+ }
+
+ // Setup the private browsing myths link.
+ document
+ .getElementById("private-browsing-myths")
+ .setAttribute(
+ "href",
+ RPMGetFormatURLPref("app.support.baseURL") + "private-browsing-myths"
+ );
+
+ // Setup the private browsing VPN link.
+ const vpnPromoUrl = RPMGetFormatURLPref(
+ "browser.privatebrowsing.vpnpromourl"
+ );
+ if (vpnPromoUrl) {
+ document
+ .getElementById("private-browsing-vpn-link")
+ .setAttribute("href", vpnPromoUrl);
+ } else {
+ // If the link is undefined, remove the promo completely
+ document.querySelectorAll(".vpn-promo").forEach(vpnEl => vpnEl.remove());
+ }
+
+ // Check ShouldShowVPNPromo
+ RPMSendQuery("ShouldShowVPNPromo", {}).then(shouldShow => {
+ if (!shouldShow) {
+ document.querySelectorAll(".vpn-promo").forEach(vpnEl => vpnEl.remove());
+ }
+ });
+
+ // Set up the private search banner.
+ const privateSearchBanner = document.getElementById("search-banner");
+
+ RPMSendQuery("ShouldShowSearchBanner", {}).then(engineName => {
+ if (engineName) {
+ document.l10n.setAttributes(
+ document.getElementById("about-private-browsing-search-banner-title"),
+ "about-private-browsing-search-banner-title",
+ { engineName }
+ );
+ privateSearchBanner.removeAttribute("hidden");
+ document.body.classList.add("showBanner");
+ }
+
+ // We set this attribute so that tests know when we are done.
+ document.documentElement.setAttribute("SearchBannerInitialized", true);
+ });
+
+ function hideSearchBanner() {
+ privateSearchBanner.setAttribute("hidden", "true");
+ document.body.classList.remove("showBanner");
+ RPMSendAsyncMessage("SearchBannerDismissed");
+ }
+
+ document
+ .getElementById("search-banner-close-button")
+ .addEventListener("click", () => {
+ hideSearchBanner();
+ });
+
+ let openSearchOptions = document.getElementById(
+ "about-private-browsing-search-banner-description"
+ );
+ let openSearchOptionsEvtHandler = evt => {
+ if (
+ evt.target.id == "open-search-options-link" &&
+ (evt.keyCode == evt.DOM_VK_RETURN || evt.type == "click")
+ ) {
+ RPMSendAsyncMessage("OpenSearchPreferences");
+ hideSearchBanner();
+ }
+ };
+ openSearchOptions.addEventListener("click", openSearchOptionsEvtHandler);
+ openSearchOptions.addEventListener("keypress", openSearchOptionsEvtHandler);
+
+ // Setup the search hand-off box.
+ let btn = document.getElementById("search-handoff-button");
+ let editable = document.getElementById("fake-editable");
+ let HIDE_SEARCH_TOPIC = "HideSearch";
+ let SHOW_SEARCH_TOPIC = "ShowSearch";
+ let SEARCH_HANDOFF_TOPIC = "SearchHandoff";
+
+ function showSearch() {
+ btn.classList.remove("focused");
+ btn.classList.remove("hidden");
+ RPMRemoveMessageListener(SHOW_SEARCH_TOPIC, showSearch);
+ }
+
+ function hideSearch() {
+ btn.classList.add("hidden");
+ }
+
+ function handoffSearch(text) {
+ RPMSendAsyncMessage(SEARCH_HANDOFF_TOPIC, { text });
+ RPMAddMessageListener(SHOW_SEARCH_TOPIC, showSearch);
+ if (text) {
+ hideSearch();
+ } else {
+ btn.classList.add("focused");
+ RPMAddMessageListener(HIDE_SEARCH_TOPIC, hideSearch);
+ }
+ }
+ btn.addEventListener("focus", function() {
+ handoffSearch();
+ });
+ btn.addEventListener("click", function() {
+ handoffSearch();
+ });
+
+ // Hand-off any text that gets dropped or pasted
+ editable.addEventListener("drop", function(ev) {
+ ev.preventDefault();
+ let text = ev.dataTransfer.getData("text");
+ if (text) {
+ handoffSearch(text);
+ }
+ });
+ editable.addEventListener("paste", function(ev) {
+ ev.preventDefault();
+ handoffSearch(ev.clipboardData.getData("Text"));
+ });
+
+ // Load contentSearchUI so it sets the search engine icon for us.
+ new window.ContentSearchHandoffUIController();
+});
diff --git a/browser/components/privatebrowsing/jar.mn b/browser/components/privatebrowsing/jar.mn
new file mode 100644
index 0000000000..0b54263264
--- /dev/null
+++ b/browser/components/privatebrowsing/jar.mn
@@ -0,0 +1,8 @@
+# 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/aboutPrivateBrowsing.css (content/aboutPrivateBrowsing.css)
+ content/browser/aboutPrivateBrowsing.html (content/aboutPrivateBrowsing.html)
+ content/browser/aboutPrivateBrowsing.js (content/aboutPrivateBrowsing.js)
diff --git a/browser/components/privatebrowsing/moz.build b/browser/components/privatebrowsing/moz.build
new file mode 100644
index 0000000000..7078f7e4db
--- /dev/null
+++ b/browser/components/privatebrowsing/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", "Private Browsing")
diff --git a/browser/components/privatebrowsing/test/browser/browser.ini b/browser/components/privatebrowsing/test/browser/browser.ini
new file mode 100644
index 0000000000..a2938f3c9e
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -0,0 +1,64 @@
+[DEFAULT]
+tags = openwindow
+support-files =
+ browser_privatebrowsing_concurrent_page.html
+ browser_privatebrowsing_geoprompt_page.html
+ browser_privatebrowsing_xrprompt_page.html
+ browser_privatebrowsing_localStorage_before_after_page.html
+ browser_privatebrowsing_localStorage_before_after_page2.html
+ browser_privatebrowsing_localStorage_page1.html
+ browser_privatebrowsing_localStorage_page2.html
+ browser_privatebrowsing_placesTitleNoUpdate.html
+ browser_privatebrowsing_protocolhandler_page.html
+ browser_privatebrowsing_windowtitle_page.html
+ head.js
+ popup.html
+ title.sjs
+ empty_file.html
+ file_favicon.html
+ file_favicon.png
+ file_favicon.png^headers^
+ file_triggeringprincipal_oa.html
+
+[browser_privatebrowsing_DownloadLastDirWithCPS.js]
+[browser_privatebrowsing_about.js]
+skip-if = verify
+tags = trackingprotection
+[browser_privatebrowsing_about_search_banner.js]
+[browser_privatebrowsing_aboutSessionRestore.js]
+[browser_privatebrowsing_cache.js]
+[browser_privatebrowsing_certexceptionsui.js]
+[browser_privatebrowsing_cleanup.js]
+[browser_privatebrowsing_concurrent.js]
+skip-if = release_or_beta
+[browser_privatebrowsing_context_and_chromeFlags.js]
+[browser_privatebrowsing_crh.js]
+[browser_privatebrowsing_downloadLastDir.js]
+skip-if = verify
+[browser_privatebrowsing_downloadLastDir_c.js]
+[browser_privatebrowsing_downloadLastDir_toggle.js]
+[browser_privatebrowsing_favicon.js]
+[browser_privatebrowsing_history_shift_click.js]
+[browser_privatebrowsing_lastpbcontextexited.js]
+[browser_privatebrowsing_localStorage.js]
+[browser_privatebrowsing_localStorage_before_after.js]
+[browser_privatebrowsing_noSessionRestoreMenuOption.js]
+[browser_privatebrowsing_nonbrowser.js]
+[browser_privatebrowsing_opendir.js]
+[browser_privatebrowsing_placesTitleNoUpdate.js]
+[browser_privatebrowsing_placestitle.js]
+[browser_privatebrowsing_popupblocker.js]
+[browser_privatebrowsing_protocolhandler.js]
+[browser_privatebrowsing_rememberprompt.js]
+tags = geolocation xr
+[browser_privatebrowsing_sidebar.js]
+[browser_privatebrowsing_theming.js]
+[browser_privatebrowsing_ui.js]
+[browser_privatebrowsing_urlbarfocus.js]
+[browser_privatebrowsing_windowtitle.js]
+[browser_privatebrowsing_zoom.js]
+[browser_privatebrowsing_zoomrestore.js]
+[browser_privatebrowsing_newtab_from_popup.js]
+skip-if = (!e10s && os == "win" && (bits == 64)) # Bug 1354865
+[browser_privatebrowsing_blobUrl.js]
+[browser_oa_private_browsing_window.js]
diff --git a/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js b/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
new file mode 100644
index 0000000000..82ae693ce2
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
@@ -0,0 +1,64 @@
+"use strict";
+
+const PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://example.com"
+);
+const TEST_PAGE = PATH + "file_triggeringprincipal_oa.html";
+const DUMMY_PAGE = PATH + "empty_file.html";
+
+add_task(
+ async function test_principal_right_click_open_link_in_new_private_win() {
+ await BrowserTestUtils.withNewTab(TEST_PAGE, async function(browser) {
+ let promiseNewWindow = BrowserTestUtils.waitForNewWindow({
+ url: DUMMY_PAGE,
+ });
+
+ // simulate right-click open link in new private window
+ BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
+ document.getElementById("context-openlinkprivate").doCommand();
+ event.target.hidePopup();
+ return true;
+ });
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#checkPrincipalOA",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+ let privateWin = await promiseNewWindow;
+
+ await SpecialPowers.spawn(
+ privateWin.gBrowser.selectedBrowser,
+ [{ DUMMY_PAGE, TEST_PAGE }],
+ // eslint-disable-next-line no-shadow
+ async function({ DUMMY_PAGE, TEST_PAGE }) {
+ // eslint-disable-line
+
+ let channel = content.docShell.currentDocumentChannel;
+ is(
+ channel.URI.spec,
+ DUMMY_PAGE,
+ "sanity check to ensure we check principal for right URI"
+ );
+
+ let triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
+ ok(
+ triggeringPrincipal.isContentPrincipal,
+ "sanity check to ensure principal is a contentPrincipal"
+ );
+ is(
+ triggeringPrincipal.spec,
+ TEST_PAGE,
+ "test page must be the triggering page"
+ );
+ is(
+ triggeringPrincipal.originAttributes.privateBrowsingId,
+ 1,
+ "must have correct privateBrowsingId"
+ );
+ }
+ );
+ await BrowserTestUtils.closeWindow(privateWin);
+ });
+ }
+);
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js
new file mode 100644
index 0000000000..1a406f27f9
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_DownloadLastDirWithCPS.js
@@ -0,0 +1,445 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+var gTests;
+function test() {
+ waitForExplicitFinish();
+ requestLongerTimeout(2);
+ runTest().catch(ex => ok(false, ex));
+}
+
+/*
+ * ================
+ * Helper functions
+ * ================
+ */
+
+function createWindow(aOptions) {
+ return new Promise(resolve => whenNewWindowLoaded(aOptions, resolve));
+}
+
+function getFile(downloadLastDir, aURI) {
+ return new Promise(resolve => downloadLastDir.getFileAsync(aURI, resolve));
+}
+
+function setFile(downloadLastDir, aURI, aValue) {
+ downloadLastDir.setFile(aURI, aValue);
+ return new Promise(resolve => executeSoon(resolve));
+}
+
+function clearHistoryAndWait() {
+ clearHistory();
+ return new Promise(resolve => executeSoon(_ => executeSoon(resolve)));
+}
+
+/*
+ * ===================
+ * Function with tests
+ * ===================
+ */
+
+async function runTest() {
+ let FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})
+ .FileUtils;
+ let DownloadLastDir = ChromeUtils.import(
+ "resource://gre/modules/DownloadLastDir.jsm",
+ {}
+ ).DownloadLastDir;
+
+ let tmpDir = FileUtils.getDir("TmpD", [], true);
+ let dir1 = newDirectory();
+ let dir2 = newDirectory();
+ let dir3 = newDirectory();
+
+ let uri1 = Services.io.newURI("http://test1.com/");
+ let uri2 = Services.io.newURI("http://test2.com/");
+ let uri3 = Services.io.newURI("http://test3.com/");
+ let uri4 = Services.io.newURI("http://test4.com/");
+
+ // cleanup functions registration
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.download.lastDir.savePerSite");
+ Services.prefs.clearUserPref("browser.download.lastDir");
+ [dir1, dir2, dir3].forEach(dir => dir.remove(true));
+ win.close();
+ pbWin.close();
+ });
+
+ function checkDownloadLastDir(gDownloadLastDir, aLastDir) {
+ is(
+ gDownloadLastDir.file.path,
+ aLastDir.path,
+ "gDownloadLastDir should point to the expected last directory"
+ );
+ return getFile(gDownloadLastDir, uri1);
+ }
+
+ function checkDownloadLastDirNull(gDownloadLastDir) {
+ is(gDownloadLastDir.file, null, "gDownloadLastDir should be null");
+ return getFile(gDownloadLastDir, uri1);
+ }
+
+ /*
+ * ================================
+ * Create a regular and a PB window
+ * ================================
+ */
+
+ let win = await createWindow({ private: false });
+ let pbWin = await createWindow({ private: true });
+
+ let downloadLastDir = new DownloadLastDir(win);
+ let pbDownloadLastDir = new DownloadLastDir(pbWin);
+
+ /*
+ * ==================
+ * Beginning of tests
+ * ==================
+ */
+
+ is(
+ typeof downloadLastDir,
+ "object",
+ "downloadLastDir should be a valid object"
+ );
+ is(downloadLastDir.file, null, "LastDir pref should be null to start with");
+
+ // set up last dir
+ await setFile(downloadLastDir, null, tmpDir);
+ is(
+ downloadLastDir.file.path,
+ tmpDir.path,
+ "LastDir should point to the tmpDir"
+ );
+ isnot(
+ downloadLastDir.file,
+ tmpDir,
+ "downloadLastDir.file should not be pointing to tmpDir"
+ );
+
+ // set uri1 to dir1, all should now return dir1
+ // also check that a new object is returned
+ await setFile(downloadLastDir, uri1, dir1);
+ is(
+ downloadLastDir.file.path,
+ dir1.path,
+ "downloadLastDir should return dir1"
+ );
+ isnot(
+ downloadLastDir.file,
+ dir1,
+ "downloadLastDir.file should not return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri1)).path,
+ dir1.path,
+ "uri1 should return dir1"
+ ); // set in CPS
+ isnot(
+ await getFile(downloadLastDir, uri1),
+ dir1,
+ "getFile on uri1 should not return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri2)).path,
+ dir1.path,
+ "uri2 should return dir1"
+ ); // fallback
+ isnot(
+ await getFile(downloadLastDir, uri2),
+ dir1,
+ "getFile on uri2 should not return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir1.path,
+ "uri3 should return dir1"
+ ); // fallback
+ isnot(
+ await getFile(downloadLastDir, uri3),
+ dir1,
+ "getFile on uri3 should not return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri4)).path,
+ dir1.path,
+ "uri4 should return dir1"
+ ); // fallback
+ isnot(
+ await getFile(downloadLastDir, uri4),
+ dir1,
+ "getFile on uri4 should not return dir1"
+ );
+
+ // set uri2 to dir2, all except uri1 should now return dir2
+ await setFile(downloadLastDir, uri2, dir2);
+ is(
+ downloadLastDir.file.path,
+ dir2.path,
+ "downloadLastDir should point to dir2"
+ );
+ is(
+ (await getFile(downloadLastDir, uri1)).path,
+ dir1.path,
+ "uri1 should return dir1"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri2)).path,
+ dir2.path,
+ "uri2 should return dir2"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir2.path,
+ "uri3 should return dir2"
+ ); // fallback
+ is(
+ (await getFile(downloadLastDir, uri4)).path,
+ dir2.path,
+ "uri4 should return dir2"
+ ); // fallback
+
+ // set uri3 to dir3, all except uri1 and uri2 should now return dir3
+ await setFile(downloadLastDir, uri3, dir3);
+ is(
+ downloadLastDir.file.path,
+ dir3.path,
+ "downloadLastDir should point to dir3"
+ );
+ is(
+ (await getFile(downloadLastDir, uri1)).path,
+ dir1.path,
+ "uri1 should return dir1"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri2)).path,
+ dir2.path,
+ "uri2 should return dir2"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir3.path,
+ "uri3 should return dir3"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri4)).path,
+ dir3.path,
+ "uri4 should return dir4"
+ ); // fallback
+
+ // set uri1 to dir2, all except uri3 should now return dir2
+ await setFile(downloadLastDir, uri1, dir2);
+ is(
+ downloadLastDir.file.path,
+ dir2.path,
+ "downloadLastDir should point to dir2"
+ );
+ is(
+ (await getFile(downloadLastDir, uri1)).path,
+ dir2.path,
+ "uri1 should return dir2"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri2)).path,
+ dir2.path,
+ "uri2 should return dir2"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir3.path,
+ "uri3 should return dir3"
+ ); // set in CPS
+ is(
+ (await getFile(downloadLastDir, uri4)).path,
+ dir2.path,
+ "uri4 should return dir2"
+ ); // fallback
+
+ await clearHistoryAndWait();
+
+ // check clearHistory removes all data
+ is(downloadLastDir.file, null, "clearHistory removes all data");
+ is(await getFile(downloadLastDir, uri1), null, "uri1 should point to null");
+ is(await getFile(downloadLastDir, uri2), null, "uri2 should point to null");
+ is(await getFile(downloadLastDir, uri3), null, "uri3 should point to null");
+ is(await getFile(downloadLastDir, uri4), null, "uri4 should point to null");
+
+ await setFile(downloadLastDir, null, tmpDir);
+
+ // check data set outside PB mode is remembered
+ is(
+ (await checkDownloadLastDir(pbDownloadLastDir, tmpDir)).path,
+ tmpDir.path,
+ "uri1 should return the expected last directory"
+ );
+ is(
+ (await checkDownloadLastDir(downloadLastDir, tmpDir)).path,
+ tmpDir.path,
+ "uri1 should return the expected last directory"
+ );
+ await clearHistoryAndWait();
+
+ await setFile(downloadLastDir, uri1, dir1);
+
+ // check data set using CPS outside PB mode is remembered
+ is(
+ (await checkDownloadLastDir(pbDownloadLastDir, dir1)).path,
+ dir1.path,
+ "uri1 should return the expected last directory"
+ );
+ is(
+ (await checkDownloadLastDir(downloadLastDir, dir1)).path,
+ dir1.path,
+ "uri1 should return the expected last directory"
+ );
+ await clearHistoryAndWait();
+
+ // check data set inside PB mode is forgotten
+ await setFile(pbDownloadLastDir, null, tmpDir);
+
+ is(
+ (await checkDownloadLastDir(pbDownloadLastDir, tmpDir)).path,
+ tmpDir.path,
+ "uri1 should return the expected last directory"
+ );
+ is(
+ await checkDownloadLastDirNull(downloadLastDir),
+ null,
+ "uri1 should return the expected last directory"
+ );
+
+ await clearHistoryAndWait();
+
+ // check data set using CPS inside PB mode is forgotten
+ await setFile(pbDownloadLastDir, uri1, dir1);
+
+ is(
+ (await checkDownloadLastDir(pbDownloadLastDir, dir1)).path,
+ dir1.path,
+ "uri1 should return the expected last directory"
+ );
+ is(
+ await checkDownloadLastDirNull(downloadLastDir),
+ null,
+ "uri1 should return the expected last directory"
+ );
+
+ // check data set outside PB mode but changed inside is remembered correctly
+ await setFile(downloadLastDir, uri1, dir1);
+ await setFile(pbDownloadLastDir, uri1, dir2);
+ is(
+ (await checkDownloadLastDir(pbDownloadLastDir, dir2)).path,
+ dir2.path,
+ "uri1 should return the expected last directory"
+ );
+ is(
+ (await checkDownloadLastDir(downloadLastDir, dir1)).path,
+ dir1.path,
+ "uri1 should return the expected last directory"
+ );
+
+ /*
+ * ====================
+ * Create new PB window
+ * ====================
+ */
+
+ // check that the last dir store got cleared in a new PB window
+ pbWin.close();
+ // And give it time to close
+ await new Promise(resolve => executeSoon(resolve));
+
+ pbWin = await createWindow({ private: true });
+ pbDownloadLastDir = new DownloadLastDir(pbWin);
+
+ is(
+ (await checkDownloadLastDir(pbDownloadLastDir, dir1)).path,
+ dir1.path,
+ "uri1 should return the expected last directory"
+ );
+
+ await clearHistoryAndWait();
+
+ // check clearHistory inside PB mode clears data outside PB mode
+ await setFile(pbDownloadLastDir, uri1, dir2);
+
+ await clearHistoryAndWait();
+
+ is(
+ await checkDownloadLastDirNull(downloadLastDir),
+ null,
+ "uri1 should return the expected last directory"
+ );
+ is(
+ await checkDownloadLastDirNull(pbDownloadLastDir),
+ null,
+ "uri1 should return the expected last directory"
+ );
+
+ // check that disabling CPS works
+ Services.prefs.setBoolPref("browser.download.lastDir.savePerSite", false);
+
+ await setFile(downloadLastDir, uri1, dir1);
+ is(downloadLastDir.file.path, dir1.path, "LastDir should be set to dir1");
+ is(
+ (await getFile(downloadLastDir, uri1)).path,
+ dir1.path,
+ "uri1 should return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri2)).path,
+ dir1.path,
+ "uri2 should return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir1.path,
+ "uri3 should return dir1"
+ );
+ is(
+ (await getFile(downloadLastDir, uri4)).path,
+ dir1.path,
+ "uri4 should return dir1"
+ );
+
+ downloadLastDir.setFile(uri2, dir2);
+ is(downloadLastDir.file.path, dir2.path, "LastDir should be set to dir2");
+ is(
+ (await getFile(downloadLastDir, uri1)).path,
+ dir2.path,
+ "uri1 should return dir2"
+ );
+ is(
+ (await getFile(downloadLastDir, uri2)).path,
+ dir2.path,
+ "uri2 should return dir2"
+ );
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir2.path,
+ "uri3 should return dir2"
+ );
+ is(
+ (await getFile(downloadLastDir, uri4)).path,
+ dir2.path,
+ "uri4 should return dir2"
+ );
+
+ Services.prefs.clearUserPref("browser.download.lastDir.savePerSite");
+
+ // check that passing null to setFile clears the stored value
+ await setFile(downloadLastDir, uri3, dir3);
+ is(
+ (await getFile(downloadLastDir, uri3)).path,
+ dir3.path,
+ "LastDir should be set to dir3"
+ );
+ await setFile(downloadLastDir, uri3, null);
+ is(await getFile(downloadLastDir, uri3), null, "uri3 should return null");
+
+ await clearHistoryAndWait();
+
+ finish();
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
new file mode 100644
index 0000000000..5cb3de6b51
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
@@ -0,0 +1,206 @@
+/* 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/. */
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
+});
+
+XPCOMUtils.defineLazyGetter(this, "UrlbarTestUtils", () => {
+ const { UrlbarTestUtils: module } = ChromeUtils.import(
+ "resource://testing-common/UrlbarTestUtils.jsm"
+ );
+ module.init(this);
+ return module;
+});
+
+/**
+ * Clicks the given link and checks this opens the given URI in the same tab.
+ *
+ * This function does not return to the previous page.
+ */
+async function testLinkOpensUrl({ win, tab, elementId, expectedUrl }) {
+ let loadedPromise = BrowserTestUtils.browserLoaded(tab);
+ await SpecialPowers.spawn(tab, [elementId], async function(elemId) {
+ content.document.getElementById(elemId).click();
+ });
+ await loadedPromise;
+ is(
+ tab.currentURI.spec,
+ expectedUrl,
+ `Clicking ${elementId} opened ${expectedUrl} in the same tab.`
+ );
+}
+
+let expectedEngineAlias;
+let expectedIconURL;
+
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.search.separatePrivateDefault", true]],
+ });
+
+ const originalPrivateDefault = await Services.search.getDefaultPrivate();
+ // We have to use a built-in engine as we are currently hard-coding the aliases.
+ const privateEngine = await Services.search.getEngineByName("DuckDuckGo");
+ await Services.search.setDefaultPrivate(privateEngine);
+ expectedEngineAlias = privateEngine.aliases[0];
+ expectedIconURL = privateEngine.iconURI.spec;
+
+ registerCleanupFunction(async () => {
+ await Services.search.setDefaultPrivate(originalPrivateDefault);
+ });
+});
+
+/**
+ * Tests the private-browsing-myths link in "about:privatebrowsing".
+ */
+add_task(async function test_myths_link() {
+ Services.prefs.setCharPref("app.support.baseURL", "https://example.com/");
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("app.support.baseURL");
+ });
+
+ let { win, tab } = await openAboutPrivateBrowsing();
+
+ await testLinkOpensUrl({
+ win,
+ tab,
+ elementId: "private-browsing-myths",
+ expectedUrl: "https://example.com/private-browsing-myths",
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+function urlBarHasHiddenFocus(win) {
+ return win.gURLBar.focused && !win.gURLBar.hasAttribute("focused");
+}
+
+function urlBarHasNormalFocus(win) {
+ return win.gURLBar.hasAttribute("focused");
+}
+
+/**
+ * Tests that we have the correct icon displayed.
+ */
+add_task(async function test_search_icon() {
+ let { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [expectedIconURL], async function(iconURL) {
+ is(
+ content.document.body.getAttribute("style"),
+ `--newtab-search-icon:url(${iconURL});`,
+ "Should have the correct icon URL for the logo"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+/**
+ * Tests the search hand-off on character keydown in "about:privatebrowsing".
+ */
+add_task(async function test_search_handoff_on_keydown() {
+ let { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ let btn = content.document.getElementById("search-handoff-button");
+ btn.click();
+ ok(btn.classList.contains("focused"), "in-content search has focus styles");
+ });
+ ok(urlBarHasHiddenFocus(win), "url bar has hidden focused");
+
+ // Expect two searches, one to enter search mode and then another in search
+ // mode.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(win);
+
+ await new Promise(r => EventUtils.synthesizeKey("f", {}, win, r));
+ await SpecialPowers.spawn(tab, [], async function() {
+ ok(
+ content.document
+ .getElementById("search-handoff-button")
+ .classList.contains("hidden"),
+ "in-content search is hidden"
+ );
+ });
+ await searchPromise;
+ ok(urlBarHasNormalFocus(win), "url bar has normal focused");
+ await UrlbarTestUtils.assertSearchMode(win, {
+ engineName: "DuckDuckGo",
+ source: UrlbarUtils.RESULT_SOURCE.SEARCH,
+ entry: "handoff",
+ });
+ is(win.gURLBar.value, "f", "url bar has search text");
+
+ // Close the popup.
+ await UrlbarTestUtils.exitSearchMode(win);
+ await UrlbarTestUtils.promisePopupClose(win);
+
+ // Hitting ESC should reshow the in-content search
+ await new Promise(r => EventUtils.synthesizeKey("KEY_Escape", {}, win, r));
+ await SpecialPowers.spawn(tab, [], async function() {
+ ok(
+ !content.document
+ .getElementById("search-handoff-button")
+ .classList.contains("hidden"),
+ "in-content search is not hidden"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+/**
+ * Tests the search hand-off on composition start in "about:privatebrowsing".
+ */
+add_task(async function test_search_handoff_on_composition_start() {
+ let { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ content.document.getElementById("search-handoff-button").click();
+ });
+ ok(urlBarHasHiddenFocus(win), "url bar has hidden focused");
+ await new Promise(r =>
+ EventUtils.synthesizeComposition({ type: "compositionstart" }, win, r)
+ );
+ ok(urlBarHasNormalFocus(win), "url bar has normal focused");
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+/**
+ * Tests the search hand-off on paste in "about:privatebrowsing".
+ */
+add_task(async function test_search_handoff_on_paste() {
+ let { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ content.document.getElementById("search-handoff-button").click();
+ });
+ ok(urlBarHasHiddenFocus(win), "url bar has hidden focused");
+ var helper = SpecialPowers.Cc[
+ "@mozilla.org/widget/clipboardhelper;1"
+ ].getService(SpecialPowers.Ci.nsIClipboardHelper);
+ helper.copyString("words");
+
+ // Expect two searches, one to enter search mode and then another in search
+ // mode.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(win);
+
+ await new Promise(r =>
+ EventUtils.synthesizeKey("v", { accelKey: true }, win, r)
+ );
+
+ await searchPromise;
+
+ ok(urlBarHasNormalFocus(win), "url bar has normal focused");
+ await UrlbarTestUtils.assertSearchMode(win, {
+ engineName: "DuckDuckGo",
+ source: UrlbarUtils.RESULT_SOURCE.SEARCH,
+ entry: "handoff",
+ });
+ is(win.gURLBar.value, "words", "url bar has search text");
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
new file mode 100644
index 0000000000..eb19d1b1fd
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
@@ -0,0 +1,25 @@
+/* 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/. */
+
+// This test checks that the session restore button from about:sessionrestore
+// is disabled in private mode
+add_task(async function testNoSessionRestoreButton() {
+ // Opening, then closing, a private window shouldn't create session data.
+ (await BrowserTestUtils.openNewBrowserWindow({ private: true })).close();
+
+ let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ let tab = BrowserTestUtils.addTab(win.gBrowser, "about:sessionrestore");
+ let browser = tab.linkedBrowser;
+
+ await BrowserTestUtils.browserLoaded(browser);
+
+ await SpecialPowers.spawn(browser, [], async function() {
+ Assert.ok(
+ content.document.getElementById("errorTryAgain").disabled,
+ "The Restore about:sessionrestore button should be disabled"
+ );
+ });
+
+ win.close();
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_search_banner.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_search_banner.js
new file mode 100644
index 0000000000..d36f52a730
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about_search_banner.js
@@ -0,0 +1,317 @@
+/* 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/. */
+
+// This test makes sure that about:privatebrowsing correctly shows the search
+// banner.
+
+const { AboutPrivateBrowsingParent } = ChromeUtils.import(
+ "resource:///actors/AboutPrivateBrowsingParent.jsm"
+);
+
+const PREF_UI_ENABLED = "browser.search.separatePrivateDefault.ui.enabled";
+const PREF_BANNER_SHOWN =
+ "browser.search.separatePrivateDefault.ui.banner.shown";
+const PREF_MAX_SEARCH_BANNER_SHOW_COUNT =
+ "browser.search.separatePrivateDefault.ui.banner.max";
+const MAX_SHOW_COUNT = 5;
+
+add_task(async function setup() {
+ SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_UI_ENABLED, false],
+ [PREF_BANNER_SHOWN, 0],
+ [PREF_MAX_SEARCH_BANNER_SHOW_COUNT, MAX_SHOW_COUNT],
+ ],
+ });
+
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+});
+
+add_task(async function test_not_shown_if_pref_off() {
+ SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_UI_ENABLED, false],
+ [PREF_MAX_SEARCH_BANNER_SHOW_COUNT, 5],
+ ],
+ });
+
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+ ok(
+ content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should be hiding the in-content search banner"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function test_not_shown_if_max_count_0() {
+ // To avoid having to restart Firefox and slow down tests, we manually reset
+ // the session pref.
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+
+ SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_UI_ENABLED, true],
+ [PREF_MAX_SEARCH_BANNER_SHOW_COUNT, 0],
+ ],
+ });
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+ ok(
+ content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should be hiding the in-content search banner"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function test_show_banner_first() {
+ // To avoid having to restart Firefox and slow down tests, we manually reset
+ // the session pref.
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+
+ SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_UI_ENABLED, true],
+ [PREF_MAX_SEARCH_BANNER_SHOW_COUNT, MAX_SHOW_COUNT],
+ ],
+ });
+
+ let prefChanged = TestUtils.waitForPrefChange(PREF_BANNER_SHOWN);
+
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ Assert.equal(
+ await prefChanged,
+ 1,
+ "Should have incremented the amount of times shown."
+ );
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+
+ ok(
+ !content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should be showing the in-content search banner"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+
+ const { win: win1, tab: tab1 } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab1, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+
+ ok(
+ content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should not be showing the banner in a second window."
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win1);
+
+ Assert.equal(
+ Services.prefs.getIntPref(PREF_BANNER_SHOWN, -1),
+ 1,
+ "Should not have changed the preference further"
+ );
+});
+
+add_task(async function test_show_banner_max_times() {
+ // We've already shown the UI once, so show it a few more times.
+ for (let i = 1; i < MAX_SHOW_COUNT; i++) {
+ // To avoid having to restart Firefox and slow down tests, we manually reset
+ // the session pref.
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+
+ let prefChanged = TestUtils.waitForPrefChange(PREF_BANNER_SHOWN);
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ Assert.equal(
+ await prefChanged,
+ i + 1,
+ "Should have incremented the amount of times shown."
+ );
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+
+ ok(
+ !content.document
+ .getElementById("search-banner")
+ .hasAttribute("hidden"),
+ "Should be showing the banner again"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+ }
+
+ // Final time!
+
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+
+ ok(
+ content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should not be showing the banner again"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function test_show_banner_close_no_more() {
+ SpecialPowers.pushPrefEnv({
+ set: [[PREF_BANNER_SHOWN, 0]],
+ });
+
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+
+ ok(
+ !content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should be showing the banner again before closing"
+ );
+
+ content.document.getElementById("search-banner-close-button").click();
+
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ ContentTaskUtils.is_hidden(
+ content.document.getElementById("search-banner")
+ ),
+ "should have closed the in-content search banner after clicking close"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+
+ Assert.equal(
+ Services.prefs.getIntPref(PREF_BANNER_SHOWN, -1),
+ MAX_SHOW_COUNT,
+ "Should have set the shown preference to the maximum"
+ );
+});
+
+add_task(async function test_show_banner_open_preferences_and_no_more() {
+ SpecialPowers.pushPrefEnv({
+ set: [[PREF_BANNER_SHOWN, 0]],
+ });
+
+ AboutPrivateBrowsingParent.setShownThisSession(false);
+
+ const { win, tab } = await openAboutPrivateBrowsing();
+
+ // This is "borrowed" from the preferences test code, as waiting for the
+ // full preferences to load helps avoid leaking a window.
+ const finalPaneEvent = Services.prefs.getBoolPref(
+ "identity.fxaccounts.enabled"
+ )
+ ? "sync-pane-loaded"
+ : "privacy-pane-loaded";
+ let finalPrefPaneLoaded = TestUtils.topicObserved(finalPaneEvent, () => true);
+ const waitForInitialized = new Promise(resolve => {
+ tab.addEventListener(
+ "Initialized",
+ () => {
+ tab.contentWindow.addEventListener(
+ "load",
+ async function() {
+ await finalPrefPaneLoaded;
+ resolve();
+ },
+ { once: true }
+ );
+ },
+ { capture: true, once: true }
+ );
+ });
+
+ await SpecialPowers.spawn(tab, [], async function() {
+ await ContentTaskUtils.waitForCondition(
+ () =>
+ content.document.documentElement.hasAttribute(
+ "SearchBannerInitialized"
+ ),
+ "Should have initialized"
+ );
+
+ ok(
+ !content.document.getElementById("search-banner").hasAttribute("hidden"),
+ "should be showing the banner again before opening prefs"
+ );
+
+ content.document.getElementById("open-search-options-link").click();
+ });
+
+ info("Waiting for preference window load");
+ await waitForInitialized;
+
+ await BrowserTestUtils.closeWindow(win);
+
+ Assert.equal(
+ Services.prefs.getIntPref(PREF_BANNER_SHOWN, -1),
+ MAX_SHOW_COUNT,
+ "Should have set the shown preference to the maximum"
+ );
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_blobUrl.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_blobUrl.js
new file mode 100644
index 0000000000..5988037dec
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_blobUrl.js
@@ -0,0 +1,69 @@
+"use strict";
+
+// Here we want to test that blob URLs are not available between private and
+// non-private browsing.
+
+const BASE_URI =
+ "http://mochi.test:8888/browser/browser/components/" +
+ "privatebrowsing/test/browser/empty_file.html";
+
+add_task(async function test() {
+ const loaded = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ BASE_URI
+ );
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, BASE_URI);
+ await loaded;
+
+ let blobURL;
+ info("Creating a blob URL...");
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
+ return Promise.resolve(
+ content.window.URL.createObjectURL(
+ new Blob([123], { type: "text/plain" })
+ )
+ );
+ }).then(newURL => {
+ blobURL = newURL;
+ });
+
+ info("Blob URL: " + blobURL);
+
+ info("Creating a private window...");
+
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let privateTab = privateWin.gBrowser.selectedBrowser;
+
+ const privateTabLoaded = BrowserTestUtils.browserLoaded(
+ privateTab,
+ false,
+ BASE_URI
+ );
+ BrowserTestUtils.loadURI(privateTab, BASE_URI);
+ await privateTabLoaded;
+
+ await SpecialPowers.spawn(privateTab, [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"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(privateWin);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
new file mode 100644
index 0000000000..7f6dd9645b
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
@@ -0,0 +1,97 @@
+/* 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/. */
+
+// Check about:cache after private browsing
+// This test covers MozTrap test 6047
+// bug 880621
+
+var tmp = {};
+
+const { Sanitizer } = ChromeUtils.import("resource:///modules/Sanitizer.jsm");
+
+function test() {
+ waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["privacy.partition.network_state", false]],
+ },
+ function() {
+ Sanitizer.sanitize(["cache"], { ignoreTimespan: false });
+
+ getStorageEntryCount("regular", function(nrEntriesR1) {
+ is(nrEntriesR1, 0, "Disk cache reports 0KB and has no entries");
+
+ get_cache_for_private_window();
+ });
+ }
+ );
+}
+
+function getStorageEntryCount(device, goon) {
+ var storage;
+ switch (device) {
+ case "private":
+ storage = Services.cache2.diskCacheStorage(
+ Services.loadContextInfo.private,
+ false
+ );
+ break;
+ case "regular":
+ storage = Services.cache2.diskCacheStorage(
+ Services.loadContextInfo.default,
+ false
+ );
+ break;
+ default:
+ throw new Error(`Unknown device ${device} at getStorageEntryCount`);
+ }
+
+ var visitor = {
+ entryCount: 0,
+ onCacheStorageInfo(aEntryCount, aConsumption) {},
+ onCacheEntryInfo(uri) {
+ var urispec = uri.asciiSpec;
+ info(device + ":" + urispec + "\n");
+ if (urispec.match(/^http:\/\/example.org\//)) {
+ ++this.entryCount;
+ }
+ },
+ onCacheEntryVisitCompleted() {
+ goon(this.entryCount);
+ },
+ };
+
+ storage.asyncVisitStorage(visitor, true);
+}
+
+function get_cache_for_private_window() {
+ let win = whenNewWindowLoaded({ private: true }, function() {
+ executeSoon(function() {
+ ok(true, "The private window got loaded");
+
+ let tab = BrowserTestUtils.addTab(win.gBrowser, "http://example.org");
+ win.gBrowser.selectedTab = tab;
+ let newTabBrowser = win.gBrowser.getBrowserForTab(tab);
+
+ BrowserTestUtils.browserLoaded(newTabBrowser).then(function() {
+ executeSoon(function() {
+ getStorageEntryCount("private", function(nrEntriesP) {
+ ok(
+ nrEntriesP >= 1,
+ "Memory cache reports some entries from example.org domain"
+ );
+
+ getStorageEntryCount("regular", function(nrEntriesR2) {
+ is(nrEntriesR2, 0, "Disk cache reports 0KB and has no entries");
+
+ win.close();
+ finish();
+ });
+ });
+ });
+ });
+ });
+ });
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js
new file mode 100644
index 0000000000..8868e66340
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js
@@ -0,0 +1,65 @@
+/* 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/. */
+
+// This test makes sure that certificate exceptions UI behaves correctly
+// in private browsing windows, based on whether it's opened from the prefs
+// window or from the SSL error page (see bug 461627).
+
+function test() {
+ const EXCEPTIONS_DLG_URL = "chrome://pippki/content/exceptionDialog.xhtml";
+ const EXCEPTIONS_DLG_FEATURES = "chrome,centerscreen";
+ const INVALID_CERT_LOCATION = "https://nocert.example.com/";
+ waitForExplicitFinish();
+
+ // open a private browsing window
+ var pbWin = OpenBrowserWindow({ private: true });
+ pbWin.addEventListener(
+ "load",
+ function() {
+ doTest();
+ },
+ { once: true }
+ );
+
+ // Test the certificate exceptions dialog
+ function doTest() {
+ let params = {
+ exceptionAdded: false,
+ location: INVALID_CERT_LOCATION,
+ prefetchCert: true,
+ };
+ function testCheckbox() {
+ win.removeEventListener("load", testCheckbox);
+ Services.obs.addObserver(function onCertUI(aSubject, aTopic, aData) {
+ Services.obs.removeObserver(onCertUI, "cert-exception-ui-ready");
+ ok(win.gCert, "The certificate information should be available now");
+
+ let checkbox = win.document.getElementById("permanent");
+ ok(
+ checkbox.hasAttribute("disabled"),
+ "the permanent checkbox should be disabled when handling the private browsing mode"
+ );
+ ok(
+ !checkbox.hasAttribute("checked"),
+ "the permanent checkbox should not be checked when handling the private browsing mode"
+ );
+ win.close();
+ cleanup();
+ }, "cert-exception-ui-ready");
+ }
+ var win = pbWin.openDialog(
+ EXCEPTIONS_DLG_URL,
+ "",
+ EXCEPTIONS_DLG_FEATURES,
+ params
+ );
+ win.addEventListener("load", testCheckbox);
+ }
+
+ function cleanup() {
+ // close the private browsing window
+ pbWin.close();
+ finish();
+ }
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cleanup.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cleanup.js
new file mode 100644
index 0000000000..2f1a71d110
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cleanup.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const DOMAIN = "http://example.com/";
+const PATH = "browser/browser/components/privatebrowsing/test/browser/";
+const TOP_PAGE = DOMAIN + PATH + "empty_file.html";
+
+add_task(async () => {
+ // Create a private browsing window.
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+
+ let privateTab = privateWindow.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(privateTab, TOP_PAGE);
+ await BrowserTestUtils.browserLoaded(privateTab);
+
+ let observerExited = {
+ observe(aSubject, aTopic, aData) {
+ ok(false, "Notification received!");
+ },
+ };
+ Services.obs.addObserver(observerExited, "last-pb-context-exited");
+
+ let popup = BrowserTestUtils.waitForNewWindow();
+
+ await SpecialPowers.spawn(privateTab, [], () => {
+ content.window.open("empty_file.html", "_blank", "width=300,height=300");
+ });
+
+ popup = await popup;
+ ok(!!popup, "Popup shown");
+
+ await BrowserTestUtils.closeWindow(privateWindow);
+ Services.obs.removeObserver(observerExited, "last-pb-context-exited");
+
+ let notificationPromise = TestUtils.topicObserved("last-pb-context-exited");
+
+ popup.close();
+
+ await notificationPromise;
+ ok(true, "Notification received!");
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
new file mode 100644
index 0000000000..976f02dc68
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent.js
@@ -0,0 +1,101 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Test opening two tabs that share a localStorage, but keep one in private mode.
+// Ensure that values from one don't leak into the other, and that values from
+// earlier private storage sessions aren't visible later.
+
+// Step 1: create new tab, load a page that sets test=value in non-private storage
+// Step 2: create a new tab, load a page that sets test2=value2 in private storage
+// Step 3: load a page in the tab from step 1 that checks the value of test2 is value2 and the total count in non-private storage is 1
+// Step 4: load a page in the tab from step 2 that checks the value of test is value and the total count in private storage is 1
+
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.ipc.processCount", 1]],
+ });
+});
+
+add_task(async function test() {
+ let prefix =
+ "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html";
+
+ function getElts(browser) {
+ return browser.contentTitle.split("|");
+ }
+
+ // Step 1
+ let non_private_browser = gBrowser.selectedBrowser;
+ let url = prefix + "?action=set&name=test&value=value&initial=true";
+ BrowserTestUtils.loadURI(non_private_browser, url);
+ await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
+
+ // Step 2
+ let private_window = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let private_browser = private_window.gBrowser.selectedBrowser;
+ url = prefix + "?action=set&name=test2&value=value2";
+ BrowserTestUtils.loadURI(private_browser, url);
+ await BrowserTestUtils.browserLoaded(private_browser, false, url);
+
+ // Step 3
+ url = prefix + "?action=get&name=test2";
+ BrowserTestUtils.loadURI(non_private_browser, url);
+ await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
+ let elts = await getElts(non_private_browser);
+ isnot(elts[0], "value2", "public window shouldn't see private storage");
+ is(elts[1], "1", "public window should only see public items");
+
+ // Step 4
+ url = prefix + "?action=get&name=test";
+ BrowserTestUtils.loadURI(private_browser, url);
+ await BrowserTestUtils.browserLoaded(private_browser, false, url);
+ elts = await getElts(private_browser);
+ isnot(elts[0], "value", "private window shouldn't see public storage");
+ is(elts[1], "1", "private window should only see private items");
+
+ // Reopen the private window again, without privateBrowsing, which should clear the
+ // the private storage.
+ private_window.close();
+ private_window = await BrowserTestUtils.openNewBrowserWindow({
+ private: false,
+ });
+ private_browser = null;
+ await new Promise(resolve => Cu.schedulePreciseGC(resolve));
+ private_browser = private_window.gBrowser.selectedBrowser;
+
+ url = prefix + "?action=get&name=test2";
+ BrowserTestUtils.loadURI(private_browser, url);
+ await BrowserTestUtils.browserLoaded(private_browser, false, url);
+ elts = await getElts(private_browser);
+ isnot(
+ elts[0],
+ "value2",
+ "public window shouldn't see cleared private storage"
+ );
+ is(elts[1], "1", "public window should only see public items");
+
+ // Making it private again should clear the storage and it shouldn't
+ // be able to see the old private storage as well.
+ private_window.close();
+ private_window = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ private_browser = null;
+ await new Promise(resolve => Cu.schedulePreciseGC(resolve));
+ private_browser = private_window.gBrowser.selectedBrowser;
+
+ url = prefix + "?action=set&name=test3&value=value3";
+ BrowserTestUtils.loadURI(private_browser, url);
+ await BrowserTestUtils.browserLoaded(private_browser, false, url);
+ elts = await getElts(private_browser);
+ is(elts[1], "1", "private window should only see new private items");
+
+ // Cleanup.
+ url = prefix + "?final=true";
+ BrowserTestUtils.loadURI(non_private_browser, url);
+ await BrowserTestUtils.browserLoaded(non_private_browser, false, url);
+ private_window.close();
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html
new file mode 100644
index 0000000000..96d3b74c7c
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_concurrent_page.html
@@ -0,0 +1,33 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+ var oGetVars = {};
+
+ if (window.location.search.length > 1) {
+ for (var aItKey, nKeyId = 0, aCouples = window.location.search.substr(1).split("&");
+ nKeyId < aCouples.length;
+ nKeyId++) {
+ aItKey = aCouples[nKeyId].split("=");
+ oGetVars[unescape(aItKey[0])] = aItKey.length > 1 ? unescape(aItKey[1]) : "";
+ }
+ }
+
+ if (oGetVars.initial == "true") {
+ localStorage.clear();
+ }
+
+ if (oGetVars.action == "set") {
+ localStorage.setItem(oGetVars.name, oGetVars.value);
+ document.title = localStorage.getItem(oGetVars.name) + "|" + localStorage.length;
+ } else if (oGetVars.action == "get") {
+ document.title = localStorage.getItem(oGetVars.name) + "|" + localStorage.length;
+ }
+
+ if (oGetVars.final == "true") {
+ localStorage.clear();
+ }
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
new file mode 100644
index 0000000000..838c199acd
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_context_and_chromeFlags.js
@@ -0,0 +1,69 @@
+"use strict";
+
+/**
+ * Given some window in the parent process, ensure that
+ * the nsIAppWindow has the CHROME_PRIVATE_WINDOW chromeFlag,
+ * and that the usePrivateBrowsing property is set to true on
+ * both the window's nsILoadContext, as well as on the initial
+ * browser's content docShell nsILoadContext.
+ *
+ * @param win (nsIDOMWindow)
+ * An nsIDOMWindow in the parent process.
+ * @return Promise
+ */
+function assertWindowIsPrivate(win) {
+ let winDocShell = win.docShell;
+ let chromeFlags = winDocShell.treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIAppWindow).chromeFlags;
+
+ if (!win.gBrowser.selectedBrowser.hasContentOpener) {
+ Assert.ok(
+ chromeFlags & Ci.nsIWebBrowserChrome.CHROME_PRIVATE_WINDOW,
+ "Should have the private window chrome flag"
+ );
+ }
+
+ let loadContext = winDocShell.QueryInterface(Ci.nsILoadContext);
+ Assert.ok(
+ loadContext.usePrivateBrowsing,
+ "The parent window should be using private browsing"
+ );
+
+ return SpecialPowers.spawn(
+ win.gBrowser.selectedBrowser,
+ [],
+ async function() {
+ let contentLoadContext = docShell.QueryInterface(Ci.nsILoadContext);
+ Assert.ok(
+ contentLoadContext.usePrivateBrowsing,
+ "Content docShell should be using private browsing"
+ );
+ }
+ );
+}
+
+/**
+ * Tests that chromeFlags bits and the nsILoadContext.usePrivateBrowsing
+ * attribute are properly set when opening a new private browsing
+ * window.
+ */
+add_task(async function test_context_and_chromeFlags() {
+ let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ await assertWindowIsPrivate(win);
+
+ let browser = win.gBrowser.selectedBrowser;
+
+ let newWinPromise = BrowserTestUtils.waitForNewWindow({
+ url: "http://example.com/",
+ });
+ await SpecialPowers.spawn(browser, [], async function() {
+ content.open("http://example.com", "_blank", "width=100,height=100");
+ });
+
+ let win2 = await newWinPromise;
+ await assertWindowIsPrivate(win2);
+
+ await BrowserTestUtils.closeWindow(win2);
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_crh.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_crh.js
new file mode 100644
index 0000000000..9b532ab8eb
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_crh.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+// This test makes sure that the Clear Recent History menu item and command
+// is disabled inside the private browsing mode.
+
+add_task(async function test() {
+ function checkDisableOption(aPrivateMode, aWindow) {
+ let crhCommand = aWindow.document.getElementById("Tools:Sanitize");
+ ok(crhCommand, "The clear recent history command should exist");
+
+ is(
+ PrivateBrowsingUtils.isWindowPrivate(aWindow),
+ aPrivateMode,
+ "PrivateBrowsingUtils should report the correct per-window private browsing status"
+ );
+ is(
+ crhCommand.hasAttribute("disabled"),
+ aPrivateMode,
+ "Clear Recent History command should be disabled according to the private browsing mode"
+ );
+ }
+
+ let testURI = "http://mochi.test:8888/";
+
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let privateBrowser = privateWin.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(privateBrowser, testURI);
+ await BrowserTestUtils.browserLoaded(privateBrowser);
+
+ info("Test on private window");
+ checkDisableOption(true, privateWin);
+
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ let browser = win.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(browser, testURI);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ info("Test on public window");
+ checkDisableOption(false, win);
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(privateWin);
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir.js
new file mode 100644
index 0000000000..908c2a1d28
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir.js
@@ -0,0 +1,133 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+function test() {
+ waitForExplicitFinish();
+
+ let FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})
+ .FileUtils;
+ let DownloadLastDir = ChromeUtils.import(
+ "resource://gre/modules/DownloadLastDir.jsm",
+ {}
+ ).DownloadLastDir;
+ let MockFilePicker = SpecialPowers.MockFilePicker;
+ let launcher = {
+ source: Services.io.newURI("http://test1.com/file"),
+ };
+
+ MockFilePicker.init(window);
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
+
+ let prefs = Services.prefs.getBranch("browser.download.");
+ let launcherDialog = Cc["@mozilla.org/helperapplauncherdialog;1"].getService(
+ Ci.nsIHelperAppLauncherDialog
+ );
+ let tmpDir = FileUtils.getDir("TmpD", [], true);
+ let dir1 = newDirectory();
+ let dir2 = newDirectory();
+ let dir3 = newDirectory();
+ let file1 = newFileInDirectory(dir1);
+ let file2 = newFileInDirectory(dir2);
+ let file3 = newFileInDirectory(dir3);
+
+ // cleanup functions registration
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.download.lastDir");
+ [dir1, dir2, dir3].forEach(dir => dir.remove(true));
+ MockFilePicker.cleanup();
+ });
+ prefs.setComplexValue("lastDir", Ci.nsIFile, tmpDir);
+
+ function testOnWindow(aPrivate, aCallback) {
+ whenNewWindowLoaded({ private: aPrivate }, function(win) {
+ let gDownloadLastDir = new DownloadLastDir(win);
+ aCallback(win, gDownloadLastDir);
+ gDownloadLastDir.cleanupPrivateFile();
+ });
+ }
+
+ function testDownloadDir(
+ aWin,
+ gDownloadLastDir,
+ aFile,
+ aDisplayDir,
+ aLastDir,
+ aGlobalLastDir,
+ aCallback
+ ) {
+ // Check lastDir preference.
+ is(
+ prefs.getComplexValue("lastDir", Ci.nsIFile).path,
+ aDisplayDir.path,
+ "LastDir should be the expected display dir"
+ );
+ // Check gDownloadLastDir value.
+ is(
+ gDownloadLastDir.file.path,
+ aDisplayDir.path,
+ "gDownloadLastDir should be the expected display dir"
+ );
+
+ MockFilePicker.setFiles([aFile]);
+ MockFilePicker.displayDirectory = null;
+
+ launcher.saveDestinationAvailable = function(file) {
+ ok(!!file, "promptForSaveToFile correctly returned a file");
+
+ // File picker should start with expected display dir.
+ is(
+ MockFilePicker.displayDirectory.path,
+ aDisplayDir.path,
+ "File picker should start with browser.download.lastDir"
+ );
+ // browser.download.lastDir should be modified on not private windows
+ is(
+ prefs.getComplexValue("lastDir", Ci.nsIFile).path,
+ aLastDir.path,
+ "LastDir should be the expected last dir"
+ );
+ // gDownloadLastDir should be usable outside of private windows
+ is(
+ gDownloadLastDir.file.path,
+ aGlobalLastDir.path,
+ "gDownloadLastDir should be the expected global last dir"
+ );
+
+ launcher.saveDestinationAvailable = null;
+ aWin.close();
+ aCallback();
+ };
+
+ launcherDialog.promptForSaveToFileAsync(launcher, aWin, null, null, null);
+ }
+
+ testOnWindow(false, function(win, downloadDir) {
+ testDownloadDir(win, downloadDir, file1, tmpDir, dir1, dir1, function() {
+ testOnWindow(true, function(win1, downloadDir1) {
+ testDownloadDir(
+ win1,
+ downloadDir1,
+ file2,
+ dir1,
+ dir1,
+ dir2,
+ function() {
+ testOnWindow(false, function(win2, downloadDir2) {
+ testDownloadDir(
+ win2,
+ downloadDir2,
+ file3,
+ dir1,
+ dir3,
+ dir3,
+ finish
+ );
+ });
+ }
+ );
+ });
+ });
+ });
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
new file mode 100644
index 0000000000..6e17ed9b55
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_c.js
@@ -0,0 +1,146 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+function test() {
+ waitForExplicitFinish();
+
+ let FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})
+ .FileUtils;
+ let DownloadLastDir = ChromeUtils.import(
+ "resource://gre/modules/DownloadLastDir.jsm",
+ {}
+ ).DownloadLastDir;
+ let MockFilePicker = SpecialPowers.MockFilePicker;
+
+ MockFilePicker.init(window);
+ MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
+
+ let validateFileNameToRestore = validateFileName;
+ let prefs = Services.prefs.getBranch("browser.download.");
+ let tmpDir = FileUtils.getDir("TmpD", [], true);
+ let dir1 = newDirectory();
+ let dir2 = newDirectory();
+ let dir3 = newDirectory();
+ let file1 = newFileInDirectory(dir1);
+ let file2 = newFileInDirectory(dir2);
+ let file3 = newFileInDirectory(dir3);
+
+ // cleanup function registration
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.download.lastDir");
+ [dir1, dir2, dir3].forEach(dir => dir.remove(true));
+ MockFilePicker.cleanup();
+ validateFileName = validateFileNameToRestore;
+ });
+
+ // Overwrite validateFileName to validate everything
+ validateFileName = foo => foo;
+
+ let params = {
+ fileInfo: new FileInfo(
+ "test.txt",
+ "test.txt",
+ "test",
+ "txt",
+ "http://mozilla.org/test.txt"
+ ),
+ contentType: "text/plain",
+ saveMode: SAVEMODE_FILEONLY,
+ saveAsType: kSaveAsType_Complete,
+ file: null,
+ };
+
+ prefs.setComplexValue("lastDir", Ci.nsIFile, tmpDir);
+
+ function testOnWindow(aPrivate, aCallback) {
+ whenNewWindowLoaded({ private: aPrivate }, function(win) {
+ let gDownloadLastDir = new DownloadLastDir(win);
+ aCallback(win, gDownloadLastDir);
+ });
+ }
+
+ function testDownloadDir(
+ aWin,
+ gDownloadLastDir,
+ aFile,
+ aDisplayDir,
+ aLastDir,
+ aGlobalLastDir,
+ aCallback
+ ) {
+ // Check lastDir preference.
+ is(
+ prefs.getComplexValue("lastDir", Ci.nsIFile).path,
+ aDisplayDir.path,
+ "LastDir should be the expected display dir"
+ );
+ // Check gDownloadLastDir value.
+ is(
+ gDownloadLastDir.file.path,
+ aDisplayDir.path,
+ "gDownloadLastDir should be the expected display dir"
+ );
+
+ MockFilePicker.setFiles([aFile]);
+ MockFilePicker.displayDirectory = null;
+ aWin
+ .promiseTargetFile(params)
+ .then(function() {
+ // File picker should start with expected display dir.
+ is(
+ MockFilePicker.displayDirectory.path,
+ aDisplayDir.path,
+ "File picker should start with browser.download.lastDir"
+ );
+ // browser.download.lastDir should be modified on not private windows
+ is(
+ prefs.getComplexValue("lastDir", Ci.nsIFile).path,
+ aLastDir.path,
+ "LastDir should be the expected last dir"
+ );
+ // gDownloadLastDir should be usable outside of private windows
+ is(
+ gDownloadLastDir.file.path,
+ aGlobalLastDir.path,
+ "gDownloadLastDir should be the expected global last dir"
+ );
+
+ gDownloadLastDir.cleanupPrivateFile();
+ aWin.close();
+ aCallback();
+ })
+ .catch(function() {
+ ok(false);
+ });
+ }
+
+ testOnWindow(false, function(win, downloadDir) {
+ testDownloadDir(win, downloadDir, file1, tmpDir, dir1, dir1, function() {
+ testOnWindow(true, function(win1, downloadDir1) {
+ testDownloadDir(
+ win1,
+ downloadDir1,
+ file2,
+ dir1,
+ dir1,
+ dir2,
+ function() {
+ testOnWindow(false, function(win2, downloadDir2) {
+ testDownloadDir(
+ win2,
+ downloadDir2,
+ file3,
+ dir1,
+ dir3,
+ dir3,
+ finish
+ );
+ });
+ }
+ );
+ });
+ });
+ });
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_toggle.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_toggle.js
new file mode 100644
index 0000000000..678781df9b
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadLastDir_toggle.js
@@ -0,0 +1,125 @@
+const { FileUtils } = ChromeUtils.import(
+ "resource://gre/modules/FileUtils.jsm"
+);
+const { DownloadLastDir } = ChromeUtils.import(
+ "resource://gre/modules/DownloadLastDir.jsm"
+);
+
+/**
+ * Tests how the browser remembers the last download folder
+ * from download to download, with a particular emphasis
+ * on how it behaves when private browsing windows open.
+ */
+add_task(async function test_downloads_last_dir_toggle() {
+ let tmpDir = FileUtils.getDir("TmpD", [], true);
+ let dir1 = newDirectory();
+
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.download.lastDir");
+ dir1.remove(true);
+ });
+
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ let gDownloadLastDir = new DownloadLastDir(win);
+ is(
+ typeof gDownloadLastDir,
+ "object",
+ "gDownloadLastDir should be a valid object"
+ );
+ is(
+ gDownloadLastDir.file,
+ null,
+ "gDownloadLastDir.file should be null to start with"
+ );
+
+ gDownloadLastDir.file = tmpDir;
+ is(
+ gDownloadLastDir.file.path,
+ tmpDir.path,
+ "LastDir should point to the temporary directory"
+ );
+ isnot(
+ gDownloadLastDir.file,
+ tmpDir,
+ "gDownloadLastDir.file should not be pointing to the tmpDir"
+ );
+
+ gDownloadLastDir.file = 1; // not an nsIFile
+ is(gDownloadLastDir.file, null, "gDownloadLastDir.file should be null");
+
+ gDownloadLastDir.file = tmpDir;
+ clearHistory();
+ is(gDownloadLastDir.file, null, "gDownloadLastDir.file should be null");
+
+ gDownloadLastDir.file = tmpDir;
+ await BrowserTestUtils.closeWindow(win);
+
+ info("Opening the first private window");
+ await testHelper({ private: true, expectedDir: tmpDir });
+ info("Opening a non-private window");
+ await testHelper({ private: false, expectedDir: tmpDir });
+ info("Opening a private window and setting download directory");
+ await testHelper({ private: true, setDir: dir1, expectedDir: dir1 });
+ info("Opening a non-private window and checking download directory");
+ await testHelper({ private: false, expectedDir: tmpDir });
+ info("Opening private window and clearing history");
+ await testHelper({ private: true, clearHistory: true, expectedDir: null });
+ info("Opening a non-private window and checking download directory");
+ await testHelper({ private: true, expectedDir: null });
+});
+
+/**
+ * Opens a new window and performs some test actions on it based
+ * on the options object that have been passed in.
+ *
+ * @param options (Object)
+ * An object with the following properties:
+ *
+ * clearHistory (bool, optional):
+ * Whether or not to simulate clearing session history.
+ * Defaults to false.
+ *
+ * setDir (nsIFile, optional):
+ * An nsIFile for setting the last download directory.
+ * If not set, the load download directory is not changed.
+ *
+ * expectedDir (nsIFile, expectedDir):
+ * An nsIFile for what we expect the last download directory
+ * should be. The nsIFile is not compared directly - only
+ * paths are compared. If expectedDir is not set, then the
+ * last download directory is expected to be null.
+ *
+ * @returns Promise
+ */
+async function testHelper(options) {
+ let win = await BrowserTestUtils.openNewBrowserWindow(options);
+ let gDownloadLastDir = new DownloadLastDir(win);
+
+ if (options.clearHistory) {
+ clearHistory();
+ }
+
+ if (options.setDir) {
+ gDownloadLastDir.file = options.setDir;
+ }
+
+ let expectedDir = options.expectedDir;
+
+ if (expectedDir) {
+ is(
+ gDownloadLastDir.file.path,
+ expectedDir.path,
+ "gDownloadLastDir should point to the expected last directory"
+ );
+ isnot(
+ gDownloadLastDir.file,
+ expectedDir,
+ "gDownloadLastDir.file should not be pointing to the last directory"
+ );
+ } else {
+ is(gDownloadLastDir.file, null, "gDownloadLastDir should be null");
+ }
+
+ gDownloadLastDir.cleanupPrivateFile();
+ await BrowserTestUtils.closeWindow(win);
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
new file mode 100644
index 0000000000..c0e671ce34
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
@@ -0,0 +1,324 @@
+/* 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/. */
+
+// This test make sure that the favicon of the private browsing is isolated.
+
+const TEST_SITE = "http://mochi.test:8888";
+const TEST_CACHE_SITE = "http://www.example.com";
+const TEST_DIRECTORY =
+ "/browser/browser/components/privatebrowsing/test/browser/";
+
+const TEST_PAGE = TEST_SITE + TEST_DIRECTORY + "file_favicon.html";
+const TEST_CACHE_PAGE = TEST_CACHE_SITE + TEST_DIRECTORY + "file_favicon.html";
+const FAVICON_URI = TEST_SITE + TEST_DIRECTORY + "file_favicon.png";
+const FAVICON_CACHE_URI = TEST_CACHE_SITE + TEST_DIRECTORY + "file_favicon.png";
+
+let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+let makeURI = ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm", {})
+ .BrowserUtils.makeURI;
+
+function clearAllImageCaches() {
+ let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"].getService(
+ SpecialPowers.Ci.imgITools
+ );
+ let imageCache = tools.getImgCacheForDocument(window.document);
+ imageCache.clearCache(true); // true=chrome
+ imageCache.clearCache(false); // false=content
+}
+
+function clearAllPlacesFavicons() {
+ let faviconService = Cc["@mozilla.org/browser/favicon-service;1"].getService(
+ Ci.nsIFaviconService
+ );
+
+ return new Promise(resolve => {
+ let observer = {
+ observe(aSubject, aTopic, aData) {
+ if (aTopic === "places-favicons-expired") {
+ resolve();
+ Services.obs.removeObserver(observer, "places-favicons-expired");
+ }
+ },
+ };
+
+ Services.obs.addObserver(observer, "places-favicons-expired");
+ faviconService.expireAllFavicons();
+ });
+}
+
+function observeFavicon(aIsPrivate, aExpectedCookie, aPageURI) {
+ let attr = {};
+
+ if (aIsPrivate) {
+ attr.privateBrowsingId = 1;
+ }
+
+ let expectedPrincipal = Services.scriptSecurityManager.createContentPrincipal(
+ aPageURI,
+ attr
+ );
+
+ return new Promise(resolve => {
+ let observer = {
+ observe(aSubject, aTopic, aData) {
+ // Make sure that the topic is 'http-on-modify-request'.
+ if (aTopic === "http-on-modify-request") {
+ // We check the privateBrowsingId for the originAttributes of the loading
+ // channel. All requests for the favicon should contain the correct
+ // privateBrowsingId. There are two requests for a favicon loading, one
+ // from the Places library and one from the XUL image. The difference
+ // of them is the loading principal. The Places will use the content
+ // principal and the XUL image will use the system principal.
+
+ let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+ let reqLoadInfo = httpChannel.loadInfo;
+ let loadingPrincipal = reqLoadInfo.loadingPrincipal;
+
+ // Make sure this is a favicon request.
+ if (httpChannel.URI.spec !== FAVICON_URI) {
+ return;
+ }
+
+ // Check the privateBrowsingId.
+ if (aIsPrivate) {
+ is(
+ reqLoadInfo.originAttributes.privateBrowsingId,
+ 1,
+ "The loadInfo has correct privateBrowsingId"
+ );
+ } else {
+ is(
+ reqLoadInfo.originAttributes.privateBrowsingId,
+ 0,
+ "The loadInfo has correct privateBrowsingId"
+ );
+ }
+
+ ok(
+ loadingPrincipal.equals(expectedPrincipal),
+ "The loadingPrincipal of favicon loading from Places should be the content prinicpal"
+ );
+
+ let faviconCookie = httpChannel.getRequestHeader("cookie");
+
+ is(
+ faviconCookie,
+ aExpectedCookie,
+ "The cookie of the favicon loading is correct."
+ );
+ } else {
+ ok(false, "Received unexpected topic: ", aTopic);
+ }
+
+ resolve();
+ Services.obs.removeObserver(observer, "http-on-modify-request");
+ },
+ };
+
+ Services.obs.addObserver(observer, "http-on-modify-request");
+ });
+}
+
+function waitOnFaviconResponse(aFaviconURL) {
+ return new Promise(resolve => {
+ let observer = {
+ observe(aSubject, aTopic, aData) {
+ if (
+ aTopic === "http-on-examine-response" ||
+ aTopic === "http-on-examine-cached-response"
+ ) {
+ let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+ let loadInfo = httpChannel.loadInfo;
+
+ if (httpChannel.URI.spec !== aFaviconURL) {
+ return;
+ }
+
+ let result = {
+ topic: aTopic,
+ privateBrowsingId: loadInfo.originAttributes.privateBrowsingId,
+ };
+
+ resolve(result);
+ Services.obs.removeObserver(observer, "http-on-examine-response");
+ Services.obs.removeObserver(
+ observer,
+ "http-on-examine-cached-response"
+ );
+ }
+ },
+ };
+
+ Services.obs.addObserver(observer, "http-on-examine-response");
+ Services.obs.addObserver(observer, "http-on-examine-cached-response");
+ });
+}
+
+function waitOnFaviconLoaded(aFaviconURL) {
+ return PlacesTestUtils.waitForNotification(
+ "favicon-changed",
+ events => events.some(e => e.faviconUrl == aFaviconURL),
+ "places"
+ );
+}
+
+async function assignCookies(aBrowser, aURL, aCookieValue) {
+ let tabInfo = await openTab(aBrowser, aURL);
+
+ await SpecialPowers.spawn(tabInfo.browser, [aCookieValue], async function(
+ value
+ ) {
+ content.document.cookie = value;
+ });
+
+ BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+async function openTab(aBrowser, aURL) {
+ let tab = BrowserTestUtils.addTab(aBrowser, aURL);
+
+ // Select tab and make sure its browser is focused.
+ aBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = aBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+ return { tab, browser };
+}
+
+registerCleanupFunction(async () => {
+ Services.cookies.removeAll();
+ clearAllImageCaches();
+ Services.cache2.clear();
+ await PlacesUtils.history.clear();
+ await PlacesUtils.bookmarks.eraseEverything();
+});
+
+add_task(async function test_favicon_privateBrowsing() {
+ // Clear all image caches before running the test.
+ clearAllImageCaches();
+ // Clear all favicons in Places.
+ await clearAllPlacesFavicons();
+
+ // Create a private browsing window.
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let pageURI = makeURI(TEST_PAGE);
+
+ // Generate two random cookies for non-private window and private window
+ // respectively.
+ let cookies = [];
+ cookies.push(Math.random().toString());
+ cookies.push(Math.random().toString());
+
+ // Open a tab in private window and add a cookie into it.
+ await assignCookies(privateWindow.gBrowser, TEST_SITE, cookies[0]);
+
+ // Open a tab in non-private window and add a cookie into it.
+ await assignCookies(gBrowser, TEST_SITE, cookies[1]);
+
+ // Add the observer earlier in case we don't capture events in time.
+ let promiseObserveFavicon = observeFavicon(true, cookies[0], pageURI);
+
+ // The page must be bookmarked for favicon requests to go through in PB mode.
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ url: TEST_PAGE,
+ });
+
+ // Open a tab for the private window.
+ let tabInfo = await openTab(privateWindow.gBrowser, TEST_PAGE);
+
+ info("Waiting until favicon requests are all made in private window.");
+ await promiseObserveFavicon;
+
+ // Close the tab.
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ // FIXME: We need to wait for the next event tick here to avoid observing
+ // the previous tab info in the next step (bug 1446725).
+ await new Promise(executeSoon);
+
+ // Add the observer earlier in case we don't capture events in time.
+ promiseObserveFavicon = observeFavicon(false, cookies[1], pageURI);
+
+ // Open a tab for the non-private window.
+ tabInfo = await openTab(gBrowser, TEST_PAGE);
+
+ info("Waiting until favicon requests are all made in non-private window.");
+ await promiseObserveFavicon;
+
+ // Close the tab.
+ BrowserTestUtils.removeTab(tabInfo.tab);
+ await BrowserTestUtils.closeWindow(privateWindow);
+});
+
+add_task(async function test_favicon_cache_privateBrowsing() {
+ // Clear all image caches and network cache before running the test.
+ clearAllImageCaches();
+
+ Services.cache2.clear();
+
+ // Clear all favicons in Places.
+ await clearAllPlacesFavicons();
+
+ // Add an observer for making sure the favicon has been loaded and cached.
+ let promiseFaviconLoaded = waitOnFaviconLoaded(FAVICON_CACHE_URI);
+ let promiseFaviconResponse = waitOnFaviconResponse(FAVICON_CACHE_URI);
+
+ // Open a tab for the non-private window.
+ let tabInfoNonPrivate = await openTab(gBrowser, TEST_CACHE_PAGE);
+
+ let response = await promiseFaviconResponse;
+
+ await promiseFaviconLoaded;
+
+ // Check that the favicon response has come from the network and it has the
+ // correct privateBrowsingId.
+ is(
+ response.topic,
+ "http-on-examine-response",
+ "The favicon image should be loaded through network."
+ );
+ is(
+ response.privateBrowsingId,
+ 0,
+ "We should observe the network response for the non-private tab."
+ );
+
+ // Create a private browsing window.
+ let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+
+ // The page must be bookmarked for favicon requests to go through in PB mode.
+ await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+ url: TEST_CACHE_PAGE,
+ });
+
+ promiseFaviconResponse = waitOnFaviconResponse(FAVICON_CACHE_URI);
+
+ // Open a tab for the private window.
+ let tabInfoPrivate = await openTab(privateWindow.gBrowser, TEST_CACHE_PAGE);
+
+ // Wait for the favicon response of the private tab.
+ response = await promiseFaviconResponse;
+
+ // Make sure the favicon is loaded through the network and its privateBrowsingId is correct.
+ is(
+ response.topic,
+ "http-on-examine-response",
+ "The favicon image should be loaded through the network again."
+ );
+ is(
+ response.privateBrowsingId,
+ 1,
+ "We should observe the network response for the private tab."
+ );
+
+ BrowserTestUtils.removeTab(tabInfoPrivate.tab);
+ BrowserTestUtils.removeTab(tabInfoNonPrivate.tab);
+ await BrowserTestUtils.closeWindow(privateWindow);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html
new file mode 100644
index 0000000000..01ed3f3d2c
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>Geolocation invoker</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ navigator.geolocation.getCurrentPosition(function(pos) {
+ // ignore
+ });
+ </script>
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_history_shift_click.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_history_shift_click.js
new file mode 100644
index 0000000000..87d9e5a7c9
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_history_shift_click.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+add_task(async function() {
+ await testShiftClickOpensNewWindow("back-button");
+});
+
+add_task(async function() {
+ await testShiftClickOpensNewWindow("forward-button");
+});
+
+// Create new private browser, open new tab and set history state, then return the window
+async function createPrivateWindow() {
+ const privateWindow = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ await BrowserTestUtils.openNewForegroundTab(
+ privateWindow.gBrowser,
+ "http://example.com"
+ );
+ await SpecialPowers.spawn(
+ privateWindow.gBrowser.selectedBrowser,
+ [],
+ async function() {
+ content.history.pushState({}, "first item", "first-item.html");
+ content.history.pushState({}, "second item", "second-item.html");
+ content.history.pushState({}, "third item", "third-item.html");
+ content.history.back();
+ }
+ );
+ await TestUtils.topicObserved("sessionstore-state-write-complete");
+
+ // Wait for the session data to be flushed before continuing the test
+ await new Promise(resolve =>
+ SessionStore.getSessionHistory(privateWindow.gBrowser.selectedTab, resolve)
+ );
+
+ info("Private window created");
+
+ return privateWindow;
+}
+
+async function testShiftClickOpensNewWindow(buttonId) {
+ const privateWindow = await createPrivateWindow();
+
+ const button = privateWindow.document.getElementById(buttonId);
+ // Wait for the new private window to be created after click
+ const newPrivateWindowPromise = BrowserTestUtils.waitForNewWindow();
+
+ EventUtils.synthesizeMouseAtCenter(button, { shiftKey: true }, privateWindow);
+
+ info("Waiting for new private browser to open");
+
+ const newPrivateWindow = await newPrivateWindowPromise;
+
+ ok(
+ PrivateBrowsingUtils.isBrowserPrivate(newPrivateWindow.gBrowser),
+ "New window is private"
+ );
+
+ // Cleanup
+ await Promise.all([
+ BrowserTestUtils.closeWindow(privateWindow),
+ BrowserTestUtils.closeWindow(newPrivateWindow),
+ ]);
+
+ info("Closed all windows");
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_lastpbcontextexited.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_lastpbcontextexited.js
new file mode 100644
index 0000000000..c19c53ca28
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_lastpbcontextexited.js
@@ -0,0 +1,63 @@
+/* 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/. */
+
+function test() {
+ // We need to open a new window for this so that its docshell would get destroyed
+ // when clearing the PB mode flag.
+ function runTest(aCloseWindow, aCallback) {
+ let newWin = OpenBrowserWindow({ private: true });
+ SimpleTest.waitForFocus(function() {
+ let expectedExiting = true;
+ let expectedExited = false;
+ let observerExiting = {
+ observe(aSubject, aTopic, aData) {
+ is(
+ aTopic,
+ "last-pb-context-exiting",
+ "Correct topic should be dispatched (exiting)"
+ );
+ is(expectedExiting, true, "notification not expected yet (exiting)");
+ expectedExited = true;
+ Services.obs.removeObserver(
+ observerExiting,
+ "last-pb-context-exiting"
+ );
+ },
+ };
+ let observerExited = {
+ observe(aSubject, aTopic, aData) {
+ is(
+ aTopic,
+ "last-pb-context-exited",
+ "Correct topic should be dispatched (exited)"
+ );
+ is(expectedExited, true, "notification not expected yet (exited)");
+ Services.obs.removeObserver(observerExited, "last-pb-context-exited");
+ aCallback();
+ },
+ };
+ Services.obs.addObserver(observerExiting, "last-pb-context-exiting");
+ Services.obs.addObserver(observerExited, "last-pb-context-exited");
+ expectedExiting = true;
+ aCloseWindow(newWin);
+ newWin = null;
+ SpecialPowers.forceGC();
+ }, newWin);
+ }
+
+ waitForExplicitFinish();
+
+ runTest(
+ function(newWin) {
+ // Simulate pressing the window close button
+ newWin.document.getElementById("cmd_closeWindow").doCommand();
+ },
+ function() {
+ runTest(function(newWin) {
+ // Simulate closing the last tab
+ newWin.document.getElementById("cmd_close").doCommand();
+ }, finish);
+ }
+ );
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
new file mode 100644
index 0000000000..c8e212b397
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
@@ -0,0 +1,28 @@
+/* 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/. */
+
+add_task(async function test() {
+ requestLongerTimeout(2);
+ const page1 =
+ "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/" +
+ "browser_privatebrowsing_localStorage_page1.html";
+
+ let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+
+ win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser, page1);
+ let browser = win.gBrowser.selectedBrowser;
+ await BrowserTestUtils.browserLoaded(browser);
+
+ BrowserTestUtils.loadURI(
+ browser,
+ "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/" +
+ "browser_privatebrowsing_localStorage_page2.html"
+ );
+ await BrowserTestUtils.browserLoaded(browser);
+
+ is(browser.contentTitle, "2", "localStorage should contain 2 items");
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
new file mode 100644
index 0000000000..3537236068
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+// Ensure that a storage instance used by both private and public sessions at different times does not
+// allow any data to leak due to cached values.
+
+// Step 1: Load browser_privatebrowsing_localStorage_before_after_page.html in a private tab, causing a storage
+// item to exist. Close the tab.
+// Step 2: Load the same page in a non-private tab, ensuring that the storage instance reports only one item
+// existing.
+
+add_task(async function test() {
+ let prefix =
+ "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/";
+
+ // Step 1.
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let testURL =
+ prefix + "browser_privatebrowsing_localStorage_before_after_page.html";
+ await BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser, testURL);
+
+ is(
+ privateWin.gBrowser.selectedBrowser.contentTitle,
+ "1",
+ "localStorage should contain 1 item"
+ );
+
+ // Step 2.
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ testURL =
+ prefix + "browser_privatebrowsing_localStorage_before_after_page2.html";
+ await BrowserTestUtils.openNewForegroundTab(win.gBrowser, testURL);
+
+ is(
+ win.gBrowser.selectedBrowser.contentTitle,
+ "null|0",
+ "localStorage should contain 0 items"
+ );
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(privateWin);
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page.html
new file mode 100644
index 0000000000..0fcb3f89be
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page.html
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+ localStorage.clear();
+ localStorage.setItem("zzztest", "zzzvalue");
+ document.title = localStorage.length;
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page2.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page2.html
new file mode 100644
index 0000000000..4eccebdf48
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after_page2.html
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+ document.title = localStorage.getItem("zzztest", "zzzvalue") + "|" + localStorage.length;
+ localStorage.clear();
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page1.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page1.html
new file mode 100644
index 0000000000..ecf5507e0a
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page1.html
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+ localStorage.clear();
+ localStorage.setItem("test1", "value1");
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page2.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page2.html
new file mode 100644
index 0000000000..d49c7fea29
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_page2.html
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script type="text/javascript">
+ localStorage.setItem("test2", "value2");
+ document.title = localStorage.length;
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
new file mode 100644
index 0000000000..d7a7b547de
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
@@ -0,0 +1,71 @@
+/**
+ * Tests that a popup window in private browsing window opens
+ * new tab links in the original private browsing window as
+ * new tabs.
+ *
+ * This is a regression test for bug 1202634.
+ */
+
+// We're able to sidestep some quote-escaping issues when
+// nesting data URI's by encoding the second data URI in
+// base64.
+const POPUP_BODY_BASE64 = btoa(`<a href="http://example.com/" target="_blank"
+ id="second">
+ Now click this
+ </a>`);
+const POPUP_LINK = `data:text/html;charset=utf-8;base64,${POPUP_BODY_BASE64}`;
+const WINDOW_BODY = `data:text/html,
+ <a href="%23" id="first"
+ onclick="window.open('${POPUP_LINK}', '_blank',
+ 'width=630,height=500')">
+ First click this.
+ </a>`;
+
+add_task(async function test_private_popup_window_opens_private_tabs() {
+ // allow top level data: URI navigations, otherwise clicking a data: link fails
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.data_uri.block_toplevel_data_uri_navigations", false]],
+ });
+ let privWin = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+
+ // Sanity check - this browser better be private.
+ ok(
+ PrivateBrowsingUtils.isWindowPrivate(privWin),
+ "Opened a private browsing window."
+ );
+
+ // First, open a private browsing window, and load our
+ // testing page.
+ let privBrowser = privWin.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(privBrowser, WINDOW_BODY);
+ await BrowserTestUtils.browserLoaded(privBrowser);
+
+ // Next, click on the link in the testing page, and ensure
+ // that a private popup window is opened.
+ let openedPromise = BrowserTestUtils.waitForNewWindow({ url: POPUP_LINK });
+
+ await BrowserTestUtils.synthesizeMouseAtCenter("#first", {}, privBrowser);
+ let popupWin = await openedPromise;
+ ok(
+ PrivateBrowsingUtils.isWindowPrivate(popupWin),
+ "Popup window was private."
+ );
+
+ // Now click on the link in the popup, and ensure that a new
+ // tab is opened in the original private browsing window.
+ let newTabPromise = BrowserTestUtils.waitForNewTab(privWin.gBrowser);
+ let popupBrowser = popupWin.gBrowser.selectedBrowser;
+ await BrowserTestUtils.synthesizeMouseAtCenter("#second", {}, popupBrowser);
+ let newPrivTab = await newTabPromise;
+
+ // Ensure that the newly created tab's browser is private.
+ ok(
+ PrivateBrowsingUtils.isBrowserPrivate(newPrivTab.linkedBrowser),
+ "Newly opened tab should be private."
+ );
+
+ // Clean up
+ BrowserTestUtils.removeTab(newPrivTab);
+ await BrowserTestUtils.closeWindow(popupWin);
+ await BrowserTestUtils.closeWindow(privWin);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js
new file mode 100644
index 0000000000..17ea34d1aa
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js
@@ -0,0 +1,29 @@
+"use strict";
+
+/**
+ * Tests that if we open a tab within a private browsing window, and then
+ * close that private browsing window, that subsequent private browsing
+ * windows do not allow the command for restoring the last session.
+ */
+add_task(async function test_no_session_restore_menu_option() {
+ let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ ok(true, "The first private window got loaded");
+ BrowserTestUtils.addTab(win.gBrowser, "about:mozilla");
+ await BrowserTestUtils.closeWindow(win);
+
+ win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ let srCommand = win.document.getElementById("Browser:RestoreLastSession");
+ ok(srCommand, "The Session Restore command should exist");
+ is(
+ PrivateBrowsingUtils.isWindowPrivate(win),
+ true,
+ "PrivateBrowsingUtils should report the correct per-window private browsing status"
+ );
+ is(
+ srCommand.hasAttribute("disabled"),
+ true,
+ "The Session Restore command should be disabled in private browsing mode"
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_nonbrowser.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_nonbrowser.js
new file mode 100644
index 0000000000..40811292bb
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_nonbrowser.js
@@ -0,0 +1,21 @@
+"use strict";
+
+/**
+ * Tests that we fire the last-pb-context-exited observer notification
+ * when the last private browsing window closes, even if a chrome window
+ * was opened from that private browsing window.
+ */
+add_task(async function() {
+ let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ let chromeWin = win.open(
+ "chrome://browser/content/places/places.xhtml",
+ "_blank",
+ "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar"
+ );
+ await BrowserTestUtils.waitForEvent(chromeWin, "load");
+ let obsPromise = TestUtils.topicObserved("last-pb-context-exited");
+ await BrowserTestUtils.closeWindow(win);
+ await obsPromise;
+ Assert.ok(true, "Got the last-pb-context-exited notification");
+ chromeWin.close();
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js
new file mode 100644
index 0000000000..c2dbdbc596
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js
@@ -0,0 +1,175 @@
+/* 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/. */
+
+// This test makes sure that the last open directory used inside the private
+// browsing mode is not remembered after leaving that mode.
+
+var windowsToClose = [];
+function testOnWindow(options, callback) {
+ var win = OpenBrowserWindow(options);
+ win.addEventListener(
+ "load",
+ function() {
+ windowsToClose.push(win);
+ callback(win);
+ },
+ { once: true }
+ );
+}
+
+registerCleanupFunction(function() {
+ windowsToClose.forEach(function(win) {
+ win.close();
+ });
+});
+
+function test() {
+ // initialization
+ waitForExplicitFinish();
+ let dir1 = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ let dir2 = Services.dirsvc.get("TmpD", Ci.nsIFile);
+ let file = dir2.clone();
+ file.append("pbtest.file");
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+ const kPrefName = "browser.open.lastDir";
+
+ function setupCleanSlate(win) {
+ win.gLastOpenDirectory.reset();
+ Services.prefs.clearUserPref(kPrefName);
+ }
+
+ setupCleanSlate(window);
+
+ // open one regular and one private window
+ testOnWindow(undefined, function(nonPrivateWindow) {
+ setupCleanSlate(nonPrivateWindow);
+ testOnWindow({ private: true }, function(privateWindow) {
+ setupCleanSlate(privateWindow);
+
+ // Test 1: general workflow test
+
+ // initial checks
+ ok(
+ !nonPrivateWindow.gLastOpenDirectory.path,
+ "Last open directory path should be initially empty"
+ );
+ nonPrivateWindow.gLastOpenDirectory.path = dir2;
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir2.path,
+ "The path should be successfully set"
+ );
+ nonPrivateWindow.gLastOpenDirectory.path = null;
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir2.path,
+ "The path should be not change when assigning it to null"
+ );
+ nonPrivateWindow.gLastOpenDirectory.path = dir1;
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The path should be successfully outside of the private browsing mode"
+ );
+
+ // test the private window
+ is(
+ privateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The path should not change when entering the private browsing mode"
+ );
+ privateWindow.gLastOpenDirectory.path = dir2;
+ is(
+ privateWindow.gLastOpenDirectory.path.path,
+ dir2.path,
+ "The path should successfully change inside the private browsing mode"
+ );
+
+ // test the non-private window
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The path should be reset to the same path as before entering the private browsing mode"
+ );
+
+ setupCleanSlate(nonPrivateWindow);
+ setupCleanSlate(privateWindow);
+
+ // Test 2: the user first tries to open a file inside the private browsing mode
+
+ // test the private window
+ ok(
+ !privateWindow.gLastOpenDirectory.path,
+ "No original path should exist inside the private browsing mode"
+ );
+ privateWindow.gLastOpenDirectory.path = dir1;
+ is(
+ privateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The path should be successfully set inside the private browsing mode"
+ );
+ // test the non-private window
+ ok(
+ !nonPrivateWindow.gLastOpenDirectory.path,
+ "The path set inside the private browsing mode should not leak when leaving that mode"
+ );
+
+ setupCleanSlate(nonPrivateWindow);
+ setupCleanSlate(privateWindow);
+
+ // Test 3: the last open directory is set from a previous session, it should be used
+ // in normal mode
+
+ Services.prefs.setComplexValue(kPrefName, Ci.nsIFile, dir1);
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The pref set from last session should take effect outside the private browsing mode"
+ );
+
+ setupCleanSlate(nonPrivateWindow);
+ setupCleanSlate(privateWindow);
+
+ // Test 4: the last open directory is set from a previous session, it should be used
+ // in private browsing mode mode
+
+ Services.prefs.setComplexValue(kPrefName, Ci.nsIFile, dir1);
+ // test the private window
+ is(
+ privateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The pref set from last session should take effect inside the private browsing mode"
+ );
+ // test the non-private window
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "The pref set from last session should remain in effect after leaving the private browsing mode"
+ );
+
+ setupCleanSlate(nonPrivateWindow);
+ setupCleanSlate(privateWindow);
+
+ // Test 5: setting the path to a file shouldn't work
+
+ nonPrivateWindow.gLastOpenDirectory.path = file;
+ ok(
+ !nonPrivateWindow.gLastOpenDirectory.path,
+ "Setting the path to a file shouldn't work when it's originally null"
+ );
+ nonPrivateWindow.gLastOpenDirectory.path = dir1;
+ nonPrivateWindow.gLastOpenDirectory.path = file;
+ is(
+ nonPrivateWindow.gLastOpenDirectory.path.path,
+ dir1.path,
+ "Setting the path to a file shouldn't work when it's not originally null"
+ );
+
+ // cleanup
+ file.remove(false);
+ finish();
+ });
+ });
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html
new file mode 100644
index 0000000000..f5bb3212f8
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>Title 1</title>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
new file mode 100644
index 0000000000..94cac06158
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
@@ -0,0 +1,61 @@
+/* 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/. */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+// Test to make sure that the visited page titles do not get updated inside the
+// private browsing mode.
+"use strict";
+
+add_task(async function test() {
+ const TEST_URL =
+ "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html";
+ const TITLE_1 = "Title 1";
+ const TITLE_2 = "Title 2";
+
+ await PlacesUtils.history.clear();
+
+ let promiseTitleChanged = PlacesTestUtils.waitForNotification(
+ "page-title-changed",
+ events => events[0].url == TEST_URL,
+ "places"
+ );
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+ registerCleanupFunction(async () => {
+ BrowserTestUtils.removeTab(tab);
+ });
+ info("Wait for a title change notification.");
+ await promiseTitleChanged;
+ await BrowserTestUtils.waitForCondition(async function() {
+ let entry = await PlacesUtils.history.fetch(TEST_URL);
+ return entry && entry.title == TITLE_1;
+ }, "The title matches the original title after first visit");
+
+ promiseTitleChanged = PlacesTestUtils.waitForNotification(
+ "page-title-changed",
+ events => events[0].url == TEST_URL,
+ "places"
+ );
+ await PlacesTestUtils.addVisits({ uri: TEST_URL, title: TITLE_2 });
+ info("Wait for a title change notification.");
+ await promiseTitleChanged;
+ await BrowserTestUtils.waitForCondition(async function() {
+ let entry = await PlacesUtils.history.fetch(TEST_URL);
+ return entry && entry.title == TITLE_2;
+ }, "The title matches the original title after updating visit");
+
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ registerCleanupFunction(async () => {
+ await BrowserTestUtils.closeWindow(privateWin);
+ });
+ await BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser, TEST_URL);
+ // Wait long enough to be sure history didn't set a title.
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ is(
+ (await PlacesUtils.history.fetch(TEST_URL)).title,
+ TITLE_2,
+ "The title remains the same after visiting in private window"
+ );
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
new file mode 100644
index 0000000000..8cf26b6721
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
@@ -0,0 +1,85 @@
+/* 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/. */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+// This test makes sure that the title of existing history entries does not
+// change inside a private window.
+
+add_task(async function test() {
+ const TEST_URL =
+ "http://mochi.test:8888/browser/browser/components/" +
+ "privatebrowsing/test/browser/title.sjs";
+ let cm = Services.cookies;
+
+ function cleanup() {
+ // delete all cookies
+ cm.removeAll();
+ // delete all history items
+ return PlacesUtils.history.clear();
+ }
+
+ await cleanup();
+ registerCleanupFunction(cleanup);
+
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ registerCleanupFunction(async () => {
+ await BrowserTestUtils.closeWindow(win);
+ });
+
+ let promiseTitleChanged = PlacesTestUtils.waitForNotification(
+ "page-title-changed",
+ events => events[0].url == TEST_URL,
+ "places"
+ );
+ await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+ await promiseTitleChanged;
+ await BrowserTestUtils.waitForCondition(async function() {
+ let entry = await PlacesUtils.history.fetch(TEST_URL);
+ return entry && entry.title == "No Cookie";
+ }, "The page should be loaded without any cookie for the first time");
+
+ promiseTitleChanged = PlacesTestUtils.waitForNotification(
+ "page-title-changed",
+ events => events[0].url == TEST_URL,
+ "places"
+ );
+ await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+ await promiseTitleChanged;
+ await BrowserTestUtils.waitForCondition(async function() {
+ let entry = await PlacesUtils.history.fetch(TEST_URL);
+ return entry && entry.title == "Cookie";
+ }, "The page should be loaded with a cookie for the second time");
+
+ await cleanup();
+
+ promiseTitleChanged = PlacesTestUtils.waitForNotification(
+ "page-title-changed",
+ events => events[0].url == TEST_URL,
+ "places"
+ );
+ await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+ await promiseTitleChanged;
+ await BrowserTestUtils.waitForCondition(async function() {
+ let entry = await PlacesUtils.history.fetch(TEST_URL);
+ return entry && entry.title == "No Cookie";
+ }, "The page should be loaded without any cookie again");
+
+ // Reopen the page in a private browser window, it should not notify a title
+ // change.
+ let win2 = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ registerCleanupFunction(async () => {
+ let promisePBExit = TestUtils.topicObserved("last-pb-context-exited");
+ await BrowserTestUtils.closeWindow(win2);
+ await promisePBExit;
+ });
+
+ await BrowserTestUtils.openNewForegroundTab(win2.gBrowser, TEST_URL);
+ // Wait long enough to be sure history didn't set a title.
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ is(
+ (await PlacesUtils.history.fetch(TEST_URL)).title,
+ "No Cookie",
+ "The title remains the same after visiting in private window"
+ );
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
new file mode 100644
index 0000000000..aecf7ba999
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
@@ -0,0 +1,83 @@
+/* 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/. */
+
+// This test makes sure that private browsing mode disables the remember option
+// for the popup blocker menu.
+add_task(async function test() {
+ let testURI =
+ "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/popup.html";
+ let oldPopupPolicy = Services.prefs.getBoolPref(
+ "dom.disable_open_during_load"
+ );
+ Services.prefs.setBoolPref("dom.disable_open_during_load", true);
+
+ registerCleanupFunction(() => {
+ Services.prefs.setBoolPref("dom.disable_open_during_load", oldPopupPolicy);
+ });
+
+ function testPopupBlockerMenuItem(aExpectedDisabled, aWindow, aCallback) {
+ aWindow.gBrowser.addEventListener(
+ "DOMUpdateBlockedPopups",
+ function() {
+ executeSoon(function() {
+ let notification = aWindow.gBrowser
+ .getNotificationBox()
+ .getNotificationWithValue("popup-blocked");
+ ok(notification, "The notification box should be displayed");
+
+ function checkMenuItem(callback) {
+ dump("CMI: in\n");
+ aWindow.document.addEventListener("popupshown", function listener(
+ event
+ ) {
+ dump("CMI: popupshown\n");
+ aWindow.document.removeEventListener("popupshown", listener);
+
+ if (aExpectedDisabled) {
+ is(
+ aWindow.document
+ .getElementById("blockedPopupAllowSite")
+ .getAttribute("disabled"),
+ "true",
+ "The allow popups menu item should be disabled"
+ );
+ }
+
+ event.originalTarget.hidePopup();
+ dump("CMI: calling back\n");
+ callback();
+ dump("CMI: called back\n");
+ });
+ dump("CMI: out\n");
+ }
+
+ checkMenuItem(function() {
+ aCallback();
+ });
+ notification.querySelector("button").doCommand();
+ });
+ },
+ { once: true }
+ );
+
+ BrowserTestUtils.loadURI(aWindow.gBrowser.selectedBrowser, testURI);
+ }
+
+ let win1 = await BrowserTestUtils.openNewBrowserWindow();
+ await new Promise(resolve => waitForFocus(resolve, win1));
+ await new Promise(resolve => testPopupBlockerMenuItem(false, win1, resolve));
+
+ let win2 = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ await new Promise(resolve => waitForFocus(resolve, win2));
+ await new Promise(resolve => testPopupBlockerMenuItem(true, win2, resolve));
+
+ let win3 = await BrowserTestUtils.openNewBrowserWindow();
+ await new Promise(resolve => waitForFocus(resolve, win3));
+ await new Promise(resolve => testPopupBlockerMenuItem(false, win3, resolve));
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(win1);
+ await BrowserTestUtils.closeWindow(win2);
+ await BrowserTestUtils.closeWindow(win3);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
new file mode 100644
index 0000000000..d1ace79998
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
@@ -0,0 +1,62 @@
+/* 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/. */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+// This test makes sure that the web pages can't register protocol handlers
+// inside the private browsing mode.
+
+add_task(async function test() {
+ let notificationValue = "Protocol Registration: web+testprotocol";
+ let testURI =
+ "https://example.com/browser/" +
+ "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html";
+
+ let doTest = async function(aIsPrivateMode, aWindow) {
+ let tab = (aWindow.gBrowser.selectedTab = BrowserTestUtils.addTab(
+ aWindow.gBrowser,
+ testURI
+ ));
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ let promiseFinished = PromiseUtils.defer();
+ setTimeout(function() {
+ let notificationBox = aWindow.gBrowser.getNotificationBox();
+ let notification = notificationBox.getNotificationWithValue(
+ notificationValue
+ );
+
+ if (aIsPrivateMode) {
+ // Make sure the notification is correctly displayed without a remember control
+ ok(
+ !notification,
+ "Notification box should not be displayed inside of private browsing mode"
+ );
+ } else {
+ // Make sure the notification is correctly displayed with a remember control
+ ok(
+ notification,
+ "Notification box should be displaying outside of private browsing mode"
+ );
+ }
+
+ promiseFinished.resolve();
+ }, 100); // remember control is added in a setTimeout(0) call
+
+ await promiseFinished.promise;
+ };
+
+ // test first when not on private mode
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ await doTest(false, win);
+
+ // then test when on private mode
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ await doTest(true, privateWin);
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(win);
+ await BrowserTestUtils.closeWindow(privateWin);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html
new file mode 100644
index 0000000000..200fda0d42
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>Protocol registrar page</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ navigator.registerProtocolHandler("web+testprotocol",
+ "https://example.com/foobar?uri=%s",
+ "Test Protocol");
+ </script>
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_rememberprompt.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_rememberprompt.js
new file mode 100644
index 0000000000..57c067e5a6
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_rememberprompt.js
@@ -0,0 +1,90 @@
+/* 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/. */
+
+// This test makes sure that the geolocation prompt does not show a remember
+// control inside the private browsing mode.
+
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.vr.always_support_vr", true]],
+ });
+});
+
+add_task(async function test() {
+ function checkPrompt(aURL, aName, aPrivateMode, aWindow) {
+ return (async function() {
+ aWindow.gBrowser.selectedTab = BrowserTestUtils.addTab(
+ aWindow.gBrowser,
+ aURL
+ );
+ await BrowserTestUtils.browserLoaded(aWindow.gBrowser.selectedBrowser);
+
+ let notification = aWindow.PopupNotifications.getNotification(aName);
+
+ // Wait until the notification is available.
+ while (!notification) {
+ await new Promise(resolve => {
+ executeSoon(resolve);
+ });
+ notification = aWindow.PopupNotifications.getNotification(aName);
+ }
+
+ if (aPrivateMode) {
+ // Make sure the notification is correctly displayed without a remember control
+ ok(
+ !notification.options.checkbox.show,
+ "Secondary actions should not exist (always/never remember)"
+ );
+ } else {
+ ok(
+ notification.options.checkbox.show,
+ "Secondary actions should exist (always/never remember)"
+ );
+ }
+ notification.remove();
+
+ aWindow.gBrowser.removeCurrentTab();
+ })();
+ }
+
+ function checkPrivateBrowsingRememberPrompt(aURL, aName) {
+ return (async function() {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ let browser = win.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(browser, aURL);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ await checkPrompt(aURL, aName, false, win);
+
+ let privateWin = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+ let privateBrowser = privateWin.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(privateBrowser, aURL);
+ await BrowserTestUtils.browserLoaded(privateBrowser);
+
+ await checkPrompt(aURL, aName, true, privateWin);
+
+ // Cleanup
+ await BrowserTestUtils.closeWindow(win);
+ await BrowserTestUtils.closeWindow(privateWin);
+ })();
+ }
+
+ const geoTestPageURL =
+ "https://example.com/browser/" +
+ "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html";
+
+ await checkPrivateBrowsingRememberPrompt(geoTestPageURL, "geolocation");
+
+ const vrEnabled = Services.prefs.getBoolPref("dom.vr.enabled");
+
+ if (vrEnabled) {
+ const xrTestPageURL =
+ "https://example.com/browser/" +
+ "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_xrprompt_page.html";
+
+ await checkPrivateBrowsingRememberPrompt(xrTestPageURL, "xr");
+ }
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_sidebar.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_sidebar.js
new file mode 100644
index 0000000000..749eed9766
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_sidebar.js
@@ -0,0 +1,85 @@
+/* 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/. */
+
+// This test makes sure that Sidebars do not migrate across windows with
+// different privacy states
+
+// See Bug 885054: https://bugzilla.mozilla.org/show_bug.cgi?id=885054
+
+function test() {
+ waitForExplicitFinish();
+
+ // opens a sidebar
+ function openSidebar(win) {
+ return win.SidebarUI.show("viewBookmarksSidebar").then(() => win);
+ }
+
+ let windowCache = [];
+ function cacheWindow(w) {
+ windowCache.push(w);
+ return w;
+ }
+ function closeCachedWindows() {
+ windowCache.forEach(w => w.close());
+ }
+
+ // Part 1: NON PRIVATE WINDOW -> PRIVATE WINDOW
+ openWindow(window, {}, 1)
+ .then(cacheWindow)
+ .then(openSidebar)
+ .then(win => openWindow(win, { private: true }))
+ .then(cacheWindow)
+ .then(function({ document }) {
+ let sidebarBox = document.getElementById("sidebar-box");
+ is(
+ sidebarBox.hidden,
+ true,
+ "Opening a private window from reg window does not open the sidebar"
+ );
+ })
+ // Part 2: NON PRIVATE WINDOW -> NON PRIVATE WINDOW
+ .then(() => openWindow(window))
+ .then(cacheWindow)
+ .then(openSidebar)
+ .then(win => openWindow(win))
+ .then(cacheWindow)
+ .then(function({ document }) {
+ let sidebarBox = document.getElementById("sidebar-box");
+ is(
+ sidebarBox.hidden,
+ false,
+ "Opening a reg window from reg window does open the sidebar"
+ );
+ })
+ // Part 3: PRIVATE WINDOW -> NON PRIVATE WINDOW
+ .then(() => openWindow(window, { private: true }))
+ .then(cacheWindow)
+ .then(openSidebar)
+ .then(win => openWindow(win))
+ .then(cacheWindow)
+ .then(function({ document }) {
+ let sidebarBox = document.getElementById("sidebar-box");
+ is(
+ sidebarBox.hidden,
+ true,
+ "Opening a reg window from a private window does not open the sidebar"
+ );
+ })
+ // Part 4: PRIVATE WINDOW -> PRIVATE WINDOW
+ .then(() => openWindow(window, { private: true }))
+ .then(cacheWindow)
+ .then(openSidebar)
+ .then(win => openWindow(win, { private: true }))
+ .then(cacheWindow)
+ .then(function({ document }) {
+ let sidebarBox = document.getElementById("sidebar-box");
+ is(
+ sidebarBox.hidden,
+ false,
+ "Opening a private window from private window does open the sidebar"
+ );
+ })
+ .then(closeCachedWindows)
+ .then(finish);
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_theming.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_theming.js
new file mode 100644
index 0000000000..ade1357e57
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_theming.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+// This test makes sure that privatebrowsingmode attribute of the window is correctly
+// adjusted based on whether the window is a private window.
+
+var windowsToClose = [];
+function testOnWindow(options, callback) {
+ var win = OpenBrowserWindow(options);
+ win.addEventListener(
+ "load",
+ function() {
+ windowsToClose.push(win);
+ executeSoon(() => callback(win));
+ },
+ { once: true }
+ );
+}
+
+registerCleanupFunction(function() {
+ windowsToClose.forEach(function(win) {
+ win.close();
+ });
+});
+
+function test() {
+ // initialization
+ waitForExplicitFinish();
+
+ ok(
+ !document.documentElement.hasAttribute("privatebrowsingmode"),
+ "privatebrowsingmode should not be present in normal mode"
+ );
+
+ // open a private window
+ testOnWindow({ private: true }, function(win) {
+ is(
+ win.document.documentElement.getAttribute("privatebrowsingmode"),
+ "temporary",
+ 'privatebrowsingmode should be "temporary" inside the private browsing mode'
+ );
+
+ finish();
+ });
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_ui.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_ui.js
new file mode 100644
index 0000000000..17c5833af6
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_ui.js
@@ -0,0 +1,99 @@
+/* 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/. */
+
+// This test makes sure that the gPrivateBrowsingUI object, the Private Browsing
+// menu item and its XUL <command> element work correctly.
+
+function test() {
+ // initialization
+ waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({
+ set: [["security.allow_eval_with_system_principal", true]],
+ });
+ let windowsToClose = [];
+ let testURI = "about:blank";
+ let pbMenuItem;
+ let cmd;
+
+ function doTest(aIsPrivateMode, aWindow, aCallback) {
+ BrowserTestUtils.browserLoaded(aWindow.gBrowser.selectedBrowser).then(
+ function() {
+ ok(aWindow.gPrivateBrowsingUI, "The gPrivateBrowsingUI object exists");
+
+ pbMenuItem = aWindow.document.getElementById("menu_newPrivateWindow");
+ ok(pbMenuItem, "The Private Browsing menu item exists");
+
+ cmd = aWindow.document.getElementById("Tools:PrivateBrowsing");
+ isnot(
+ cmd,
+ null,
+ "XUL command object for the private browsing service exists"
+ );
+
+ is(
+ pbMenuItem.getAttribute("label"),
+ "New Private Window",
+ 'The Private Browsing menu item should read "New Private Window"'
+ );
+ is(
+ PrivateBrowsingUtils.isWindowPrivate(aWindow),
+ aIsPrivateMode,
+ "PrivateBrowsingUtils should report the correct per-window private browsing status (privateBrowsing should be " +
+ aIsPrivateMode +
+ ")"
+ );
+
+ aCallback();
+ }
+ );
+
+ BrowserTestUtils.loadURI(aWindow.gBrowser.selectedBrowser, testURI);
+ }
+
+ function openPrivateBrowsingModeByUI(aWindow, aCallback) {
+ Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
+ aSubject.addEventListener(
+ "load",
+ function() {
+ Services.obs.removeObserver(observer, "domwindowopened");
+ windowsToClose.push(aSubject);
+ aCallback(aSubject);
+ },
+ { once: true }
+ );
+ }, "domwindowopened");
+
+ cmd = aWindow.document.getElementById("Tools:PrivateBrowsing");
+ var func = new Function("", cmd.getAttribute("oncommand"));
+ func.call(cmd);
+ }
+
+ function testOnWindow(aOptions, aCallback) {
+ whenNewWindowLoaded(aOptions, function(aWin) {
+ windowsToClose.push(aWin);
+ // execute should only be called when need, like when you are opening
+ // web pages on the test. If calling executeSoon() is not necesary, then
+ // call whenNewWindowLoaded() instead of testOnWindow() on your test.
+ executeSoon(() => aCallback(aWin));
+ });
+ }
+
+ // this function is called after calling finish() on the test.
+ registerCleanupFunction(function() {
+ windowsToClose.forEach(function(aWin) {
+ aWin.close();
+ });
+ });
+
+ // test first when not on private mode
+ testOnWindow({}, function(aWin) {
+ doTest(false, aWin, function() {
+ // then test when on private mode, opening a new private window from the
+ // user interface.
+ openPrivateBrowsingModeByUI(aWin, function(aPrivateWin) {
+ doTest(true, aPrivateWin, finish);
+ });
+ });
+ });
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
new file mode 100644
index 0000000000..9bad3fc1e2
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarfocus.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+// This test makes sure that the URL bar is focused when entering the private window.
+
+"use strict";
+
+const { AboutNewTab } = ChromeUtils.import(
+ "resource:///modules/AboutNewTab.jsm"
+);
+
+function checkUrlbarFocus(win) {
+ let urlbar = win.gURLBar;
+ is(
+ win.document.activeElement,
+ urlbar.inputField,
+ "URL Bar should be focused"
+ );
+ is(urlbar.value, "", "URL Bar should be empty");
+}
+
+function openNewPrivateWindow() {
+ return new Promise(resolve => {
+ whenNewWindowLoaded({ private: true }, win => {
+ executeSoon(() => resolve(win));
+ });
+ });
+}
+
+add_task(async function() {
+ let win = await openNewPrivateWindow();
+ checkUrlbarFocus(win);
+ win.close();
+});
+
+add_task(async function() {
+ AboutNewTab.newTabURL = "about:blank";
+ registerCleanupFunction(() => {
+ AboutNewTab.resetNewTabURL();
+ });
+
+ let win = await openNewPrivateWindow();
+ checkUrlbarFocus(win);
+ win.close();
+
+ AboutNewTab.resetNewTabURL();
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
new file mode 100644
index 0000000000..e1f72c0b39
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
@@ -0,0 +1,112 @@
+/* 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/. */
+
+// This test makes sure that the window title changes correctly while switching
+// from and to private browsing mode.
+
+const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+
+add_task(async function test() {
+ const testPageURL =
+ "http://mochi.test:8888/browser/" +
+ "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle_page.html";
+ requestLongerTimeout(2);
+
+ // initialization of expected titles
+ let test_title = "Test title";
+ let app_name = document.title;
+
+ // XXX: Bug 1597849 - Dehardcode titles by fetching them from Fluent
+ // to compare with the actual values.
+ const isMacOS = AppConstants.platform == "macosx";
+
+ let pb_postfix = isMacOS ? ` — (Private Browsing)` : ` (Private Browsing)`;
+ let page_with_title = isMacOS ? test_title : `${test_title} — ${app_name}`;
+ let page_without_title = app_name;
+ let about_pb_title = app_name;
+ let pb_page_with_title = isMacOS
+ ? `${test_title}${pb_postfix}`
+ : `${test_title} — ${app_name}${pb_postfix}`;
+ let pb_page_without_title = `${app_name}${pb_postfix}`;
+ let pb_about_pb_title = `${app_name}${pb_postfix}`;
+
+ async function testTabTitle(aWindow, url, insidePB, expected_title) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(aWindow.gBrowser);
+ BrowserTestUtils.loadURI(tab.linkedBrowser, url);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return aWindow.document.title === expected_title;
+ }, `Window title should be ${expected_title}, got ${aWindow.document.title}`);
+
+ is(
+ aWindow.document.title,
+ expected_title,
+ "The window title for " +
+ url +
+ " is correct (" +
+ (insidePB ? "inside" : "outside") +
+ " private browsing mode)"
+ );
+
+ let win = aWindow.gBrowser.replaceTabWithWindow(tab);
+ await BrowserTestUtils.waitForEvent(win, "load", false);
+
+ await BrowserTestUtils.waitForCondition(() => {
+ return win.document.title === expected_title;
+ }, `Window title should be ${expected_title}, got ${win.document.title}`);
+
+ is(
+ win.document.title,
+ expected_title,
+ "The window title for " +
+ url +
+ " detached tab is correct (" +
+ (insidePB ? "inside" : "outside") +
+ " private browsing mode)"
+ );
+
+ await Promise.all([
+ BrowserTestUtils.closeWindow(win),
+ BrowserTestUtils.closeWindow(aWindow),
+ ]);
+ }
+
+ function openWin(private) {
+ return BrowserTestUtils.openNewBrowserWindow({ private });
+ }
+ await testTabTitle(
+ await openWin(false),
+ "about:blank",
+ false,
+ page_without_title
+ );
+ await testTabTitle(await openWin(false), testPageURL, false, page_with_title);
+ await testTabTitle(
+ await openWin(false),
+ "about:privatebrowsing",
+ false,
+ about_pb_title
+ );
+ await testTabTitle(
+ await openWin(true),
+ "about:blank",
+ true,
+ pb_page_without_title
+ );
+ await testTabTitle(
+ await openWin(true),
+ testPageURL,
+ true,
+ pb_page_with_title
+ );
+ await testTabTitle(
+ await openWin(true),
+ "about:privatebrowsing",
+ true,
+ pb_about_pb_title
+ );
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle_page.html
new file mode 100644
index 0000000000..760bde7d14
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle_page.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>Test title</title>
+ </head>
+ <body>
+ Test page for the window title test
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_xrprompt_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_xrprompt_page.html
new file mode 100644
index 0000000000..4330785df2
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_xrprompt_page.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <title>XR invoker</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ navigator.getVRDisplays();
+ </script>
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
new file mode 100644
index 0000000000..048796e7d2
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+// This test makes sure that private browsing turns off doesn't cause zoom
+// settings to be reset on tab switch (bug 464962)
+
+add_task(async function test() {
+ let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+ let tabAbout = await BrowserTestUtils.openNewForegroundTab(
+ win.gBrowser,
+ "about:mozilla"
+ );
+ let tabMozilla = await BrowserTestUtils.openNewForegroundTab(
+ win.gBrowser,
+ "about:mozilla"
+ );
+
+ let mozillaZoom = win.ZoomManager.zoom;
+
+ // change the zoom on the mozilla page
+ win.FullZoom.enlarge();
+ // make sure the zoom level has been changed
+ isnot(win.ZoomManager.zoom, mozillaZoom, "Zoom level can be changed");
+ mozillaZoom = win.ZoomManager.zoom;
+
+ // switch to about: tab
+ await BrowserTestUtils.switchTab(win.gBrowser, tabAbout);
+
+ // switch back to mozilla tab
+ await BrowserTestUtils.switchTab(win.gBrowser, tabMozilla);
+
+ // make sure the zoom level has not changed
+ is(
+ win.ZoomManager.zoom,
+ mozillaZoom,
+ "Entering private browsing should not reset the zoom on a tab"
+ );
+
+ // cleanup
+ win.FullZoom.reset();
+ BrowserTestUtils.removeTab(tabMozilla);
+ BrowserTestUtils.removeTab(tabAbout);
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
new file mode 100644
index 0000000000..36181041bb
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
@@ -0,0 +1,80 @@
+/* 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/. */
+
+// This test makes sure that about:privatebrowsing does not appear zoomed in
+// if there is already a zoom site pref for about:blank (bug 487656).
+
+add_task(async function test() {
+ // initialization
+ let windowsToClose = [];
+ let windowsToReset = [];
+
+ function promiseLocationChange() {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function onLocationChange(subj, topic, data) {
+ Services.obs.removeObserver(onLocationChange, topic);
+ resolve();
+ }, "browser-fullZoom:location-change");
+ });
+ }
+
+ async function promiseTestReady(aIsZoomedWindow, aWindow) {
+ // Need to wait on two things, the ordering of which is not guaranteed:
+ // (1) the page load, and (2) FullZoom's update to the new page's zoom
+ // level. FullZoom broadcasts "browser-fullZoom:location-change" when its
+ // update is done. (See bug 856366 for details.)
+
+ let browser = aWindow.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(browser, "about:blank");
+ await Promise.all([
+ BrowserTestUtils.browserLoaded(browser),
+ promiseLocationChange(),
+ ]);
+ doTest(aIsZoomedWindow, aWindow);
+ }
+
+ function doTest(aIsZoomedWindow, aWindow) {
+ if (aIsZoomedWindow) {
+ is(
+ aWindow.ZoomManager.zoom,
+ 1,
+ "Zoom level for freshly loaded about:blank should be 1"
+ );
+ // change the zoom on the blank page
+ aWindow.FullZoom.enlarge();
+ isnot(
+ aWindow.ZoomManager.zoom,
+ 1,
+ "Zoom level for about:blank should be changed"
+ );
+ return;
+ }
+
+ // make sure the zoom level is set to 1
+ is(
+ aWindow.ZoomManager.zoom,
+ 1,
+ "Zoom level for about:privatebrowsing should be reset"
+ );
+ }
+
+ function testOnWindow(options, callback) {
+ return BrowserTestUtils.openNewBrowserWindow(options).then(win => {
+ windowsToClose.push(win);
+ windowsToReset.push(win);
+ return win;
+ });
+ }
+
+ await testOnWindow({}).then(win => promiseTestReady(true, win));
+ await testOnWindow({ private: true }).then(win =>
+ promiseTestReady(false, win)
+ );
+
+ // cleanup
+ windowsToReset.forEach(win => win.FullZoom.reset());
+ await Promise.all(
+ windowsToClose.map(win => BrowserTestUtils.closeWindow(win))
+ );
+});
diff --git a/browser/components/privatebrowsing/test/browser/empty_file.html b/browser/components/privatebrowsing/test/browser/empty_file.html
new file mode 100644
index 0000000000..0dc101b533
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/empty_file.html
@@ -0,0 +1 @@
+<html><body></body></html>
diff --git a/browser/components/privatebrowsing/test/browser/file_favicon.html b/browser/components/privatebrowsing/test/browser/file_favicon.html
new file mode 100644
index 0000000000..f294b47758
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/file_favicon.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>Favicon Test for originAttributes</title>
+ <link rel="icon" type="image/png" href="file_favicon.png" />
+ </head>
+ <body>
+ Favicon!!
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/file_favicon.png b/browser/components/privatebrowsing/test/browser/file_favicon.png
new file mode 100644
index 0000000000..5535363c94
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/file_favicon.png
Binary files differ
diff --git a/browser/components/privatebrowsing/test/browser/file_favicon.png^headers^ b/browser/components/privatebrowsing/test/browser/file_favicon.png^headers^
new file mode 100644
index 0000000000..9e23c73b7f
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/file_favicon.png^headers^
@@ -0,0 +1 @@
+Cache-Control: no-cache
diff --git a/browser/components/privatebrowsing/test/browser/file_triggeringprincipal_oa.html b/browser/components/privatebrowsing/test/browser/file_triggeringprincipal_oa.html
new file mode 100644
index 0000000000..cd05e833f3
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/file_triggeringprincipal_oa.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1348801</title>
+</head>
+<body>
+ <a href="empty_file.html" id="checkPrincipalOA">checkPrincipalOA</a>
+</body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/head.js b/browser/components/privatebrowsing/test/browser/head.js
new file mode 100644
index 0000000000..f437597512
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/head.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { PromiseUtils } = ChromeUtils.import(
+ "resource://gre/modules/PromiseUtils.jsm"
+);
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(
+ this,
+ "PlacesUtils",
+ "resource://gre/modules/PlacesUtils.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "PlacesTestUtils",
+ "resource://testing-common/PlacesTestUtils.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "TestUtils",
+ "resource://testing-common/TestUtils.jsm"
+);
+
+function whenNewWindowLoaded(aOptions, aCallback) {
+ let win = OpenBrowserWindow(aOptions);
+ let focused = SimpleTest.promiseFocus(win);
+ let startupFinished = TestUtils.topicObserved(
+ "browser-delayed-startup-finished",
+ subject => subject == win
+ ).then(() => win);
+ Promise.all([focused, startupFinished]).then(results =>
+ executeSoon(() => aCallback(results[1]))
+ );
+
+ return win;
+}
+
+function openWindow(aParent, aOptions) {
+ let win = aParent.OpenBrowserWindow(aOptions);
+ return TestUtils.topicObserved(
+ "browser-delayed-startup-finished",
+ subject => subject == win
+ ).then(() => win);
+}
+
+/**
+ * Opens a new private window and loads "about:privatebrowsing" there.
+ */
+async function openAboutPrivateBrowsing() {
+ let win = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ waitForTabURL: "about:privatebrowsing",
+ });
+ let tab = win.gBrowser.selectedBrowser;
+ return { win, tab };
+}
+
+function newDirectory() {
+ let FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})
+ .FileUtils;
+ let tmpDir = FileUtils.getDir("TmpD", [], true);
+ let dir = tmpDir.clone();
+ dir.append("testdir");
+ dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ return dir;
+}
+
+function newFileInDirectory(aDir) {
+ let FileUtils = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {})
+ .FileUtils;
+ let file = aDir.clone();
+ file.append("testfile");
+ file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_FILE);
+ return file;
+}
+
+function clearHistory() {
+ // simulate clearing the private data
+ Services.obs.notifyObservers(null, "browser:purge-session-history");
+}
+
+function _initTest() {
+ // Don't use about:home as the homepage for new windows
+ Services.prefs.setIntPref("browser.startup.page", 0);
+ registerCleanupFunction(() =>
+ Services.prefs.clearUserPref("browser.startup.page")
+ );
+}
+
+_initTest();
diff --git a/browser/components/privatebrowsing/test/browser/popup.html b/browser/components/privatebrowsing/test/browser/popup.html
new file mode 100644
index 0000000000..68bbbfa260
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/popup.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Page creating a popup</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ window.open("data:text/plain,test", "testwin");
+ </script>
+ </body>
+</html>
diff --git a/browser/components/privatebrowsing/test/browser/title.sjs b/browser/components/privatebrowsing/test/browser/title.sjs
new file mode 100644
index 0000000000..568e235be1
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/title.sjs
@@ -0,0 +1,22 @@
+/* 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/. */
+
+// This provides the tests with a page with different titles based on whether
+// a cookie is present or not.
+
+function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html", false);
+
+ var cookie = "name=value";
+ var title = "No Cookie";
+ if (request.hasHeader("Cookie") && request.getHeader("Cookie") == cookie)
+ title = "Cookie";
+ else
+ response.setHeader("Set-Cookie", cookie, false);
+
+ response.write("<html><head><title>");
+ response.write(title);
+ response.write("</title><body>test page</body></html>");
+}