mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 989393 - Clean up old tabs and windows. r=ttaubert
This commit is contained in:
parent
379aea1acd
commit
616d7a75ab
@ -1018,6 +1018,8 @@ pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
|
||||
pref("browser.sessionstore.upgradeBackup.latestBuildID", "");
|
||||
// End-users should not run sessionstore in debug mode
|
||||
pref("browser.sessionstore.debug", false);
|
||||
// Forget closed windows/tabs after two weeks
|
||||
pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
|
||||
|
||||
// allow META refresh by default
|
||||
pref("accessibility.blockautorefresh", false);
|
||||
|
@ -36,6 +36,7 @@ const OBSERVING = [
|
||||
"quit-application", "browser:purge-session-history",
|
||||
"browser:purge-domain-data",
|
||||
"gather-telemetry",
|
||||
"idle-daily",
|
||||
];
|
||||
|
||||
// XUL Window properties to (re)store
|
||||
@ -570,6 +571,9 @@ let SessionStoreInternal = {
|
||||
case "gather-telemetry":
|
||||
this.onGatherTelemetry();
|
||||
break;
|
||||
case "idle-daily":
|
||||
this.onIdleDaily();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1428,6 +1432,39 @@ let SessionStoreInternal = {
|
||||
return SessionFile.gatherTelemetry(stateString);
|
||||
},
|
||||
|
||||
// Clean up data that has been closed a long time ago.
|
||||
// Do not reschedule a save. This will wait for the next regular
|
||||
// save.
|
||||
onIdleDaily: function() {
|
||||
// Remove old closed windows
|
||||
this._cleanupOldData([this._closedWindows]);
|
||||
|
||||
// Remove closed tabs of closed windows
|
||||
this._cleanupOldData([winData._closedTabs for (winData of this._closedWindows)]);
|
||||
|
||||
// Remove closed tabs of open windows
|
||||
this._cleanupOldData([this._windows[key]._closedTabs for (key of Object.keys(this._windows))]);
|
||||
},
|
||||
|
||||
// Remove "old" data from an array
|
||||
_cleanupOldData: function(targets) {
|
||||
const TIME_TO_LIVE = this._prefBranch.getIntPref("sessionstore.cleanup.forget_closed_after");
|
||||
const now = Date.now();
|
||||
|
||||
for (let array of targets) {
|
||||
for (let i = array.length - 1; i >= 0; --i) {
|
||||
let data = array[i];
|
||||
// Make sure that we have a timestamp to tell us when the target
|
||||
// has been closed. If we don't have a timestamp, default to a
|
||||
// safe timestamp: just now.
|
||||
data.closedAt = data.closedAt || now;
|
||||
if (now - data.closedAt > TIME_TO_LIVE) {
|
||||
array.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* ........ nsISessionStore API .............. */
|
||||
|
||||
getBrowserState: function ssi_getBrowserState() {
|
||||
@ -1671,6 +1708,8 @@ let SessionStoreInternal = {
|
||||
|
||||
// reopen the window
|
||||
let state = { windows: this._closedWindows.splice(aIndex, 1) };
|
||||
delete state.windows[0].closedAt; // Window is now open.
|
||||
|
||||
let window = this._openWindowWithState(state);
|
||||
this.windowToFocus = window;
|
||||
return window;
|
||||
@ -2472,6 +2511,7 @@ let SessionStoreInternal = {
|
||||
} else {
|
||||
delete tab.__SS_extdata;
|
||||
}
|
||||
delete tabData.closedAt; // Tab is now open.
|
||||
|
||||
// Flush all data from the content script synchronously. This is done so
|
||||
// that all async messages that are still on their way to chrome will
|
||||
|
@ -61,6 +61,7 @@ support-files =
|
||||
[browser_attributes.js]
|
||||
[browser_broadcast.js]
|
||||
[browser_capabilities.js]
|
||||
[browser_cleaner.js]
|
||||
[browser_dying_cache.js]
|
||||
[browser_dynamic_frames.js]
|
||||
[browser_form_restore_events.js]
|
||||
|
160
browser/components/sessionstore/test/browser_cleaner.js
Normal file
160
browser/components/sessionstore/test/browser_cleaner.js
Normal file
@ -0,0 +1,160 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
/*
|
||||
* This test ensures that Session Restore eventually forgets about
|
||||
* tabs and windows that have been closed a long time ago.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
|
||||
const LONG_TIME_AGO = 1;
|
||||
|
||||
const URL_TAB1 = "http://example.com/browser_cleaner.js?newtab1=" + Math.random();
|
||||
const URL_TAB2 = "http://example.com/browser_cleaner.js?newtab2=" + Math.random();
|
||||
const URL_NEWWIN = "http://example.com/browser_cleaner.js?newwin=" + Math.random();
|
||||
|
||||
function isRecent(stamp) {
|
||||
is(typeof stamp, "number", "This is a timestamp");
|
||||
return Date.now() - stamp <= 60000;
|
||||
}
|
||||
|
||||
function promiseCleanup () {
|
||||
info("Cleaning up browser");
|
||||
|
||||
return promiseBrowserState(getClosedState());
|
||||
};
|
||||
|
||||
function getClosedState() {
|
||||
return Cu.cloneInto(CLOSED_STATE, {});
|
||||
}
|
||||
|
||||
let CLOSED_STATE;
|
||||
|
||||
add_task(function* init() {
|
||||
while (ss.getClosedWindowCount() > 0) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
while (ss.getClosedTabCount(window) > 0) {
|
||||
ss.forgetClosedTab(window, 0);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_open_and_close() {
|
||||
let newTab1 = gBrowser.addTab(URL_TAB1);
|
||||
yield promiseBrowserLoaded(newTab1.linkedBrowser);
|
||||
|
||||
let newTab2 = gBrowser.addTab(URL_TAB2);
|
||||
yield promiseBrowserLoaded(newTab2.linkedBrowser);
|
||||
|
||||
let newWin = yield promiseNewWindowLoaded();
|
||||
let tab = newWin.gBrowser.addTab(URL_NEWWIN);
|
||||
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
|
||||
|
||||
info("1. Making sure that before closing, we don't have closedAt");
|
||||
// For the moment, no "closedAt"
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows[0].closedAt || false, false, "1. Main window doesn't have closedAt");
|
||||
is(state.windows[1].closedAt || false, false, "1. Second window doesn't have closedAt");
|
||||
is(state.windows[0].tabs[0].closedAt || false, false, "1. First tab doesn't have closedAt");
|
||||
is(state.windows[0].tabs[1].closedAt || false, false, "1. Second tab doesn't have closedAt");
|
||||
|
||||
|
||||
|
||||
info("2. Making sure that after closing, we have closedAt");
|
||||
|
||||
// Now close stuff, this should add closeAt
|
||||
yield promiseWindowClosed(newWin);
|
||||
gBrowser.removeTab(newTab1);
|
||||
gBrowser.removeTab(newTab2);
|
||||
|
||||
state = CLOSED_STATE = JSON.parse(ss.getBrowserState());
|
||||
|
||||
is(state.windows[0].closedAt || false, false, "2. Main window doesn't have closedAt");
|
||||
ok(isRecent(state._closedWindows[0].closedAt), "2. Second window was closed recently");
|
||||
ok(isRecent(state.windows[0]._closedTabs[0].closedAt), "2. First tab was closed recently");
|
||||
ok(isRecent(state.windows[0]._closedTabs[1].closedAt), "2. Second tab was closed recently");
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_restore() {
|
||||
info("3. Making sure that after restoring, we don't have closedAt");
|
||||
yield promiseBrowserState(CLOSED_STATE);
|
||||
|
||||
let newWin = ss.undoCloseWindow(0);
|
||||
yield promiseDelayedStartupFinished(newWin);
|
||||
|
||||
let newTab2 = ss.undoCloseTab(window, 0);
|
||||
yield promiseTabRestored(newTab2);
|
||||
|
||||
let newTab1 = ss.undoCloseTab(window, 0);
|
||||
yield promiseTabRestored(newTab1);
|
||||
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
|
||||
is(state.windows[0].closedAt || false, false, "3. Main window doesn't have closedAt");
|
||||
is(state.windows[1].closedAt || false, false, "3. Second window doesn't have closedAt");
|
||||
is(state.windows[0].tabs[0].closedAt || false, false, "3. First tab doesn't have closedAt");
|
||||
is(state.windows[0].tabs[1].closedAt || false, false, "3. Second tab doesn't have closedAt");
|
||||
|
||||
yield promiseWindowClosed(newWin);
|
||||
gBrowser.removeTab(newTab1);
|
||||
gBrowser.removeTab(newTab2);
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_old_data() {
|
||||
info("4. Removing closedAt from the sessionstore, making sure that it is added upon idle-daily");
|
||||
|
||||
let state = getClosedState();
|
||||
delete state._closedWindows[0].closedAt;
|
||||
delete state.windows[0]._closedTabs[0].closedAt;
|
||||
delete state.windows[0]._closedTabs[1].closedAt;
|
||||
yield promiseBrowserState(state);
|
||||
|
||||
info("Sending idle-daily");
|
||||
Services.obs.notifyObservers(null, "idle-daily", "");
|
||||
info("Sent idle-daily");
|
||||
|
||||
state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows[0].closedAt || false, false, "4. Main window doesn't have closedAt");
|
||||
ok(isRecent(state._closedWindows[0].closedAt), "4. Second window was closed recently");
|
||||
ok(isRecent(state.windows[0]._closedTabs[0].closedAt), "4. First tab was closed recently");
|
||||
ok(isRecent(state.windows[0]._closedTabs[1].closedAt), "4. Second tab was closed recently");
|
||||
yield promiseCleanup();
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_cleanup() {
|
||||
|
||||
info("5. Altering closedAt to an old date, making sure that stuff gets collected, eventually");
|
||||
yield promiseCleanup();
|
||||
|
||||
let state = getClosedState();
|
||||
state._closedWindows[0].closedAt = LONG_TIME_AGO;
|
||||
state.windows[0]._closedTabs[0].closedAt = LONG_TIME_AGO;
|
||||
state.windows[0]._closedTabs[1].closedAt = Date.now();
|
||||
let url = state.windows[0]._closedTabs[1].state.entries[0].url;
|
||||
|
||||
yield promiseBrowserState(state);
|
||||
|
||||
info("Sending idle-daily");
|
||||
Services.obs.notifyObservers(null, "idle-daily", "");
|
||||
info("Sent idle-daily");
|
||||
|
||||
state = JSON.parse(ss.getBrowserState());
|
||||
is(state._closedWindows[0], undefined, "5. Second window was forgotten");
|
||||
|
||||
is(state.windows[0]._closedTabs.length, 1, "5. Only one closed tab left");
|
||||
is(state.windows[0]._closedTabs[0].state.entries[0].url, url, "5. The second tab is still here");
|
||||
yield promiseCleanup();
|
||||
});
|
||||
|
@ -88,6 +88,12 @@ function provideWindow(aCallback, aURL, aFeatures) {
|
||||
|
||||
// This assumes that tests will at least have some state/entries
|
||||
function waitForBrowserState(aState, aSetStateCallback) {
|
||||
if (typeof aState == "string") {
|
||||
aState = JSON.parse(aState);
|
||||
}
|
||||
if (typeof aState != "object") {
|
||||
throw new TypeError("Argument must be an object or a JSON representation of an object");
|
||||
}
|
||||
let windows = [window];
|
||||
let tabsRestored = 0;
|
||||
let expectedTabsRestored = 0;
|
||||
@ -172,6 +178,10 @@ function waitForBrowserState(aState, aSetStateCallback) {
|
||||
ss.setBrowserState(JSON.stringify(aState));
|
||||
}
|
||||
|
||||
function promiseBrowserState(aState) {
|
||||
return new Promise(resolve => waitForBrowserState(aState, resolve));
|
||||
}
|
||||
|
||||
// Doesn't assume that the tab needs to be closed in a cleanup function.
|
||||
// If that's the case, the test author should handle that in the test.
|
||||
function waitForTabState(aTab, aState, aCallback) {
|
||||
@ -481,6 +491,9 @@ function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
function promiseDelayedStartupFinished(aWindow) {
|
||||
return new Promise((resolve) => whenDelayedStartupFinished(aWindow, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* The test runner that controls the execution flow of our tests.
|
||||
|
Loading…
Reference in New Issue
Block a user