diff --git a/browser/components/preferences/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js index 6d557b80eae6..7a9d5b6f6dd5 100644 --- a/browser/components/preferences/in-content/privacy.js +++ b/browser/components/preferences/in-content/privacy.js @@ -85,6 +85,7 @@ var gPrivacyPane = { let count = ContextualIdentityService.countContainerTabs(); if (count == 0) { + ContextualIdentityService.disableContainers(); Services.prefs.setBoolPref("privacy.userContext.enabled", false); return; } @@ -105,6 +106,7 @@ var gPrivacyPane = { okButton, cancelButton, null, null, {}); if (rv == 0) { ContextualIdentityService.closeContainerTabs(); + ContextualIdentityService.disableContainers(); Services.prefs.setBoolPref("privacy.userContext.enabled", false); return; } diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 4581eec3d2e0..75a50ab72683 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -45,7 +45,7 @@ const OBSERVING = [ "quit-application-granted", "browser-lastwindow-close-granted", "quit-application", "browser:purge-session-history", "browser:purge-domain-data", - "idle-daily", + "idle-daily", "clear-origin-attributes-data" ]; // XUL Window properties to (re)store @@ -748,6 +748,13 @@ var SessionStoreInternal = { this.onIdleDaily(); this._notifyOfClosedObjectsChange(); break; + case "clear-origin-attributes-data": + let userContextId = 0; + try { + userContextId = JSON.parse(aData).userContextId; + } catch(e) {} + if (userContextId) + this._forgetTabsWithUserContextId(userContextId); } }, @@ -2582,6 +2589,33 @@ var SessionStoreInternal = { return undefined; }, + // This method deletes all the closedTabs matching userContextId. + _forgetTabsWithUserContextId(userContextId) { + let windowsEnum = Services.wm.getEnumerator("navigator:browser"); + while (windowsEnum.hasMoreElements()) { + let window = windowsEnum.getNext(); + let windowState = this._windows[window.__SSi]; + if (windowState) { + // In order to remove the tabs in the correct order, we store the + // indexes, into an array, then we revert the array and remove closed + // data from the last one going backward. + let indexes = []; + windowState._closedTabs.forEach((closedTab, index) => { + if (closedTab.state.userContextId == userContextId) { + indexes.push(index); + } + }); + + for (let index of indexes.reverse()) { + this.removeClosedTabData(windowState._closedTabs, index); + } + } + } + + // Notify of changes to closed objects. + this._notifyOfClosedObjectsChange(); + }, + /** * Restores the session state stored in LastSession. This will attempt * to merge data into the current session. If a window was opened at startup diff --git a/browser/components/sessionstore/test/browser.ini b/browser/components/sessionstore/test/browser.ini index 4566f3a0c605..8ac1ffec7c1a 100644 --- a/browser/components/sessionstore/test/browser.ini +++ b/browser/components/sessionstore/test/browser.ini @@ -243,3 +243,5 @@ skip-if = !e10s # GroupedSHistory is e10s-only [browser_closed_objects_changed_notifications_tabs.js] [browser_closed_objects_changed_notifications_windows.js] + +[browser_disable_containers.js] diff --git a/browser/components/sessionstore/test/browser_disable_containers.js b/browser/components/sessionstore/test/browser_disable_containers.js new file mode 100644 index 000000000000..17729a80915f --- /dev/null +++ b/browser/components/sessionstore/test/browser_disable_containers.js @@ -0,0 +1,77 @@ +"use strict"; + +/** + * This test is to see if tabs can be reopened when containers are disabled. + */ + +Cu.import("resource:///modules/sessionstore/SessionStore.jsm"); + +async function openAndCloseTab(window, userContextId, url) { + let tab = window.gBrowser.addTab(url, { userContextId }); + await promiseBrowserLoaded(tab.linkedBrowser, true, url); + await TabStateFlusher.flush(tab.linkedBrowser); + await promiseRemoveTab(tab); +} + +async function openTab(window, userContextId, url) { + let tab = window.gBrowser.addTab(url, { userContextId }); + await promiseBrowserLoaded(tab.linkedBrowser, true, url); + await TabStateFlusher.flush(tab.linkedBrowser); +} + +async function openWindow(url) { + let win = await promiseNewWindowLoaded(); + let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY; + win.gBrowser.selectedBrowser.loadURIWithFlags(url, flags); + await promiseBrowserLoaded(win.gBrowser.selectedBrowser, true, url); + return win; +} + +add_task(function* test_undoCloseById() { + // Clear the lists of closed windows and tabs. + forgetClosedWindows(); + while (SessionStore.getClosedTabCount(window)) { + SessionStore.forgetClosedTab(window, 0); + } + + // Open a new window. + let win = yield openWindow("about:robots"); + + // Open and close a tab. + yield openAndCloseTab(win, 0, "about:mozilla"); + is(SessionStore.lastClosedObjectType, "tab", "The last closed object is a tab"); + + // Open and close a container tab. + yield openAndCloseTab(win, 3, "about:about"); + is(SessionStore.lastClosedObjectType, "tab", "The last closed object is a tab"); + + // Restore the last closed tab. + let tab = SessionStore.undoCloseTab(win, 0); + yield promiseBrowserLoaded(tab.linkedBrowser); + is(tab.linkedBrowser.currentURI.spec, "about:about", "The expected tab was re-opened"); + is(tab.getAttribute("usercontextid"), 3, "No userContextId for this tab"); + + // Open a few container tabs. + yield openTab(win, 1, "about:robots"); + yield openTab(win, 1, "about:about"); + yield openTab(win, 2, "about:profiles"); + + // Let's simulate the disabling of containers. + ContextualIdentityService.closeContainerTabs(); + ContextualIdentityService.disableContainers(); + + // Let's check we don't have container tab opened. + for (let i = 0; i < win.gBrowser.tabContainer.childNodes.length; ++i) { + let tab = win.gBrowser.tabContainer.childNodes[i]; + ok(!tab.hasAttribute("usercontextid"), "No userContextId for this tab"); + } + + // Restore the last closed tab (we don't want the container tabs). + tab = SessionStore.undoCloseTab(win, 0); + yield promiseBrowserLoaded(tab.linkedBrowser); + is(tab.linkedBrowser.currentURI.spec, "about:mozilla", "The expected tab was re-opened"); + ok(!tab.hasAttribute("usercontextid"), "No userContextId for this tab"); + + // Close the window again. + yield BrowserTestUtils.closeWindow(win); +}); diff --git a/toolkit/components/contextualidentity/ContextualIdentityService.jsm b/toolkit/components/contextualidentity/ContextualIdentityService.jsm index 6bd4081326d8..dd7a6d8d98d9 100644 --- a/toolkit/components/contextualidentity/ContextualIdentityService.jsm +++ b/toolkit/components/contextualidentity/ContextualIdentityService.jsm @@ -298,6 +298,13 @@ _ContextualIdentityService.prototype = { }, userContextId); }, + disableContainers() { + for (let identity of this._identities) { + Services.obs.notifyObservers(null, "clear-origin-attributes-data", + JSON.stringify({ userContextId: identity.userContextId })); + } + }, + _forEachContainerTab(callback, userContextId = 0) { let windowList = Services.wm.getEnumerator("navigator:browser"); while (windowList.hasMoreElements()) {