forked from mirrors/gecko-dev
		
	 afed0705da
			
		
	
	
		afed0705da
		
	
	
	
	
		
			
			Current state: -------------- Session cookies - those that have no Expires or Max-Age directive, sent as a header or set via document.cookie - are meant to live for the duration of a session. SessionStore is a feature that aims to enable users to resume where they left off last time they closed the browser. So SessionStore will persist and restore those cookies that the cookie service only keeps in memory. SessionCookies.jsm registers observers with the cookie service and is thus notified of cookie additions, deletions, and modifications as-it-happens. It has its own internal storage that we could easily serialize and write to disk together with the rest of the session data. The hangs shown in various profiles stem from the fact that since the inception of SessionStore as an add-on around Firefox 2, cookies have been tacked to windows. This means that whenever we collect session data for a specific window (i.e. tabs, their shistory entries, etc.) we have to iterate *all* its tabs and *all* their shistory entries to enumerate the hosts contained in that window. We will then ask the internal cookie store in SessionCookies.jsm to give us all cookies for these hosts and then store them together with the window. This way we filter out cookies from tabs/hosts that have no active documents (BFCache counts as "active"). Changes in this patch: ---------------------- Instead of trying to only retain cookies from “active” documents, i.e. those contained somewhere in the shistory of a tab, we now simply save all session cookies of the session. This will surely reduce user complaints about us "logging them out" too fast because we discard cookies from tabs they open only once in a while, although those definitely belong to the browsing session. Instead of storing the cookies per each window we now have a top-level "cookies" attribute that is a list of cookies. These get restored whenever we restore a session. Legacy window.cookies attributes will still be restored to support older session formats for a while. The DEFER_SESSION startup mode is active by default when a user choses not to restore their whole session automatically but they still have one or more pinned tabs. These pinned tabs are restored automatically and split off of the rest of the session. The rest can be restored manually if the user chooses to do so. In the past, we here extracted and restored only the pinned tabs' cookies from the last session. This filtering also works against how some sites (e.g. Google) use session cookies. It also means we have to iterate all windows, tabs, shistory entries, and cookies to find the data we want. This patch changes our past behavior so that we now restore only pinned tabs but all session cookies. So we don't have to filter, and pages will break less likely. We hereby assume that a user having pinned tabs wants to continue their browsing session partially, although without Firefox remembering the exact list of tabs. Or they simply like starting off of a clean slate.
		
			
				
	
	
		
			142 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			142 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 file spawns content tasks.
 | |
| /* eslint-env mozilla/frame-script */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| add_task(function* () {
 | |
|   for (let i = 0; i < 3; ++i) {
 | |
|     let tab = gBrowser.addTab("http://example.com/", { userContextId: i });
 | |
|     let browser = tab.linkedBrowser;
 | |
| 
 | |
|     yield promiseBrowserLoaded(browser);
 | |
| 
 | |
|     let tab2 = gBrowser.duplicateTab(tab);
 | |
|     Assert.equal(tab2.getAttribute("usercontextid"), i);
 | |
|     let browser2 = tab2.linkedBrowser;
 | |
|     yield promiseTabRestored(tab2)
 | |
| 
 | |
|     yield ContentTask.spawn(browser2, { expectedId: i }, function* (args) {
 | |
|       let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
 | |
|       Assert.equal(loadContext.originAttributes.userContextId,
 | |
|         args.expectedId, "The docShell has the correct userContextId");
 | |
|     });
 | |
| 
 | |
|     yield promiseRemoveTab(tab);
 | |
|     yield promiseRemoveTab(tab2);
 | |
|   }
 | |
| });
 | |
| 
 | |
| add_task(function* () {
 | |
|   let tab = gBrowser.addTab("http://example.com/", { userContextId: 1 });
 | |
|   let browser = tab.linkedBrowser;
 | |
| 
 | |
|   yield promiseBrowserLoaded(browser);
 | |
| 
 | |
|   gBrowser.selectedTab = tab;
 | |
| 
 | |
|   let tab2 = gBrowser.duplicateTab(tab);
 | |
|   let browser2 = tab2.linkedBrowser;
 | |
|   yield promiseTabRestored(tab2)
 | |
| 
 | |
|   yield ContentTask.spawn(browser2, { expectedId: 1 }, function* (args) {
 | |
|     Assert.equal(docShell.getOriginAttributes().userContextId,
 | |
|                  args.expectedId,
 | |
|                  "The docShell has the correct userContextId");
 | |
|   });
 | |
| 
 | |
|   yield promiseRemoveTab(tab);
 | |
|   yield promiseRemoveTab(tab2);
 | |
| });
 | |
| 
 | |
| add_task(function* () {
 | |
|   let tab = gBrowser.addTab("http://example.com/", { userContextId: 1 });
 | |
|   let browser = tab.linkedBrowser;
 | |
| 
 | |
|   yield promiseBrowserLoaded(browser);
 | |
| 
 | |
|   gBrowser.removeTab(tab);
 | |
| 
 | |
|   let tab2 = ss.undoCloseTab(window, 0);
 | |
|   Assert.equal(tab2.getAttribute("usercontextid"), 1);
 | |
|   yield promiseTabRestored(tab2);
 | |
|   yield ContentTask.spawn(tab2.linkedBrowser, { expectedId: 1 }, function* (args) {
 | |
|     Assert.equal(docShell.getOriginAttributes().userContextId,
 | |
|                  args.expectedId,
 | |
|                  "The docShell has the correct userContextId");
 | |
|   });
 | |
| 
 | |
|   yield promiseRemoveTab(tab2);
 | |
| });
 | |
| 
 | |
| // Opens "uri" in a new tab with the provided userContextId and focuses it.
 | |
| // Returns the newly opened tab.
 | |
| function* openTabInUserContext(userContextId) {
 | |
|   // Open the tab in the correct userContextId.
 | |
|   let tab = gBrowser.addTab("http://example.com", { userContextId });
 | |
| 
 | |
|   // Select tab and make sure its browser is focused.
 | |
|   gBrowser.selectedTab = tab;
 | |
|   tab.ownerGlobal.focus();
 | |
| 
 | |
|   let browser = gBrowser.getBrowserForTab(tab);
 | |
|   yield BrowserTestUtils.browserLoaded(browser);
 | |
|   return { tab, browser };
 | |
| }
 | |
| 
 | |
| function waitForNewCookie() {
 | |
|   return new Promise(resolve => {
 | |
|     Services.obs.addObserver(function observer(subj, topic, data) {
 | |
|       if (data == "added") {
 | |
|         Services.obs.removeObserver(observer, topic);
 | |
|         resolve();
 | |
|       }
 | |
|     }, "cookie-changed", false);
 | |
|   });
 | |
| }
 | |
| 
 | |
| add_task(function* test() {
 | |
|   const USER_CONTEXTS = [
 | |
|     "default",
 | |
|     "personal",
 | |
|     "work",
 | |
|   ];
 | |
| 
 | |
|   const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 | |
|   const { TabStateFlusher } = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 | |
| 
 | |
|   // Make sure userContext is enabled.
 | |
|   yield SpecialPowers.pushPrefEnv({
 | |
|     "set": [ [ "privacy.userContext.enabled", true ] ]
 | |
|   });
 | |
| 
 | |
|   Services.cookies.removeAll();
 | |
| 
 | |
|   for (let userContextId of Object.keys(USER_CONTEXTS)) {
 | |
|     // Load the page in 3 different contexts and set a cookie
 | |
|     // which should only be visible in that context.
 | |
|     let cookie = USER_CONTEXTS[userContextId];
 | |
| 
 | |
|     // Open our tab in the given user context.
 | |
|     let { tab, browser } = yield* openTabInUserContext(userContextId);
 | |
| 
 | |
|     yield Promise.all([
 | |
|       waitForNewCookie(),
 | |
|       ContentTask.spawn(browser, cookie,
 | |
|         passedCookie => content.document.cookie = passedCookie)
 | |
|     ]);
 | |
| 
 | |
|     // Ensure the tab's session history is up-to-date.
 | |
|     yield TabStateFlusher.flush(browser);
 | |
| 
 | |
|     // Remove the tab.
 | |
|     gBrowser.removeTab(tab);
 | |
|   }
 | |
| 
 | |
|   let state = JSON.parse(ss.getBrowserState());
 | |
|   is(state.cookies.length, USER_CONTEXTS.length,
 | |
|     "session restore should have each container's cookie");
 | |
| });
 |