Bug 595601 - Option to not load tabs from inactive groups on initial browser startup (and until such time as the tab(s) become part of an active group); r=zpao

This commit is contained in:
Tim Taubert 2011-05-20 00:07:55 +02:00
Родитель 66ce76121f
Коммит f868df08cb
12 изменённых файлов: 526 добавлений и 188 удалений

Просмотреть файл

@ -785,6 +785,8 @@ pref("browser.sessionstore.max_resumed_crashes", 1);
// Other tabs won't be restored until they are selected // Other tabs won't be restored until they are selected
// N = The number of tabs to restore at the same time // N = The number of tabs to restore at the same time
pref("browser.sessionstore.max_concurrent_tabs", 3); pref("browser.sessionstore.max_concurrent_tabs", 3);
// Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
pref("browser.sessionstore.restore_hidden_tabs", false);
// allow META refresh by default // allow META refresh by default
pref("accessibility.blockautorefresh", false); pref("accessibility.blockautorefresh", false);

Просмотреть файл

@ -64,6 +64,7 @@ _BROWSER_FILES = \
browser_tabview_bug595518.js \ browser_tabview_bug595518.js \
browser_tabview_bug595521.js \ browser_tabview_bug595521.js \
browser_tabview_bug595560.js \ browser_tabview_bug595560.js \
browser_tabview_bug595601.js \
browser_tabview_bug595804.js \ browser_tabview_bug595804.js \
browser_tabview_bug595930.js \ browser_tabview_bug595930.js \
browser_tabview_bug595943.js \ browser_tabview_bug595943.js \

Просмотреть файл

@ -1,93 +1,121 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
function test() { const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const DUMMY_PAGE_URL = "http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html";
const DUMMY_PAGE_URL_2 = "http://mochi.test:8888/";
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); const DUMMY_PAGE_URL = "http://mochi.test:8888/browser/browser/base/content/test/tabview/dummy_page.html";
const DUMMY_PAGE_URL_2 = "http://mochi.test:8888/";
let state = {
windows: [{
tabs: [{
entries: [{ url: DUMMY_PAGE_URL }],
hidden: true,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":21,"top":29,"width":204,"height":153},' +
'"userSize":null,"url":"' + DUMMY_PAGE_URL + '","groupID":1,' +
'"imageData":null,"title":null}'
}
},{
entries: [{ url: DUMMY_PAGE_URL_2 }],
hidden: false,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":315,"top":29,"width":111,"height":84},' +
'"userSize":null,"url":"' + DUMMY_PAGE_URL_2 + '","groupID":2,' +
'"imageData":null,"title":null}'
},
}],
selected:2,
_closedTabs: [],
extData: {
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
"tabview-group":
'{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},' +
'"userSize":null,"title":"","id":1},' +
'"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},' +
'"userSize":null,"title":"","id":2}}',
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":788,"height":548}}'
}, sizemode:"normal"
}]
};
function test() {
waitForExplicitFinish(); waitForExplicitFinish();
// open a new window and setup the window state. registerCleanupFunction(function () {
let newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no"); Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
newWin.addEventListener("load", function(event) { });
this.removeEventListener("load", arguments.callee, false);
let newState = { Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);
windows: [{
tabs: [{
entries: [{ url: DUMMY_PAGE_URL }],
hidden: true,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":21,"top":29,"width":204,"height":153},' +
'"userSize":null,"url":"' + DUMMY_PAGE_URL + '","groupID":1,' +
'"imageData":null,"title":null}'
}
},{
entries: [{ url: DUMMY_PAGE_URL_2 }],
hidden: false,
attributes: {},
extData: {
"tabview-tab":
'{"bounds":{"left":315,"top":29,"width":111,"height":84},' +
'"userSize":null,"url":"' + DUMMY_PAGE_URL_2 + '","groupID":2,' +
'"imageData":null,"title":null}'
},
}],
selected:2,
_closedTabs: [],
extData: {
"tabview-groups": '{"nextID":3,"activeGroupId":2}',
"tabview-group":
'{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},' +
'"userSize":null,"title":"","id":1},' +
'"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},' +
'"userSize":null,"title":"","id":2}}',
"tabview-ui": '{"pageBounds":{"left":0,"top":0,"width":788,"height":548}}'
}, sizemode:"normal"
}]
};
ss.setWindowState(newWin, JSON.stringify(newState), true);
let firstTab = newWin.gBrowser.tabs[0]; testTabSwitchAfterRestore(function () {
let secondTab = newWin.gBrowser.tabs[1]; Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", true);
// wait until the first tab is fully loaded testTabSwitchAfterRestore(function () {
let browser = newWin.gBrowser.getBrowserForTab(firstTab); waitForFocus(finish);
let onLoad = function() { });
browser.removeEventListener("load", onLoad, true); });
}
is(browser.currentURI.spec, DUMMY_PAGE_URL, function testTabSwitchAfterRestore(callback) {
"The url of first tab url is dummy_page.html"); newWindowWithState(state, function (win) {
registerCleanupFunction(function () win.close());
// check the hidden state of both tabs. let [firstTab, secondTab] = win.gBrowser.tabs;
ok(firstTab.hidden, "The first tab is hidden"); is(firstTab.linkedBrowser.currentURI.spec, DUMMY_PAGE_URL,
ok(!secondTab.hidden, "The second tab is not hidden"); "The url of first tab url is dummy_page.html");
is(secondTab, newWin.gBrowser.selectedTab, "The second tab is selected");
// when the second tab is hidden, the iframe should be initialized and // check the hidden state of both tabs.
// the first tab should be visible. ok(firstTab.hidden, "The first tab is hidden");
let onTabHide = function() { ok(!secondTab.hidden, "The second tab is not hidden");
newWin.gBrowser.tabContainer.removeEventListener("TabHide", onTabHide, true); is(secondTab, win.gBrowser.selectedTab, "The second tab is selected");
ok(newWin.TabView.getContentWindow(), ""); // when the second tab is hidden, Panorama should be initialized and
// the first tab should be visible.
let container = win.gBrowser.tabContainer;
container.addEventListener("TabHide", function onTabHide() {
container.removeEventListener("TabHide", onTabHide, false);
ok(!firstTab.hidden, "The first tab is not hidden"); ok(win.TabView.getContentWindow(), "Panorama is loaded");
is(firstTab, newWin.gBrowser.selectedTab, "The first tab is selected"); ok(!firstTab.hidden, "The first tab is not hidden");
ok(secondTab.hidden, "The second tab is hidden"); is(firstTab, win.gBrowser.selectedTab, "The first tab is selected");
ok(secondTab.hidden, "The second tab is hidden");
// clean up and finish callback();
newWin.close(); }, false);
finish(); // switch to another tab
}; win.switchToTabHavingURI(DUMMY_PAGE_URL);
newWin.gBrowser.tabContainer.addEventListener("TabHide", onTabHide, true); });
}
// switch to another tab function newWindowWithState(state, callback) {
newWin.switchToTabHavingURI(DUMMY_PAGE_URL); let opts = "chrome,all,dialog=no,height=800,width=800";
} let win = window.openDialog(getBrowserURL(), "_blank", opts);
browser.addEventListener("load", onLoad, true);
whenWindowLoaded(win, function () {
ss.setWindowState(win, JSON.stringify(state), true);
});
whenWindowStateReady(win, function () {
afterAllTabsLoaded(function () callback(win), win);
});
}
function whenWindowLoaded(win, callback) {
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
executeSoon(callback);
}, false);
}
function whenWindowStateReady(win, callback) {
win.addEventListener("SSWindowStateReady", function onReady() {
win.removeEventListener("SSWindowStateReady", onReady, false);
executeSoon(callback);
}, false); }, false);
} }

Просмотреть файл

@ -1,134 +1,97 @@
/* Any copyright is dedicated to the Public Domain. /* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */ http://creativecommons.org/publicdomain/zero/1.0/ */
let newTabOne; let win;
let originalTab; let cw;
function test() { function test() {
waitForExplicitFinish(); waitForExplicitFinish();
originalTab = gBrowser.visibleTabs[0]; let onLoad = function (tvwin) {
newTabOne = gBrowser.addTab("http://mochi.test:8888/"); win = tvwin;
registerCleanupFunction(function () win.close());
let browser = gBrowser.getBrowserForTab(newTabOne); win.gBrowser.loadOneTab("http://mochi.test:8888/", {inBackground: true});
let onLoad = function() {
browser.removeEventListener("load", onLoad, true);
// show the tab view
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
}
browser.addEventListener("load", onLoad, true);
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
afterAllTabItemsUpdated(function() {
let contentWindow = document.getElementById("tab-view").contentWindow;
testOne(contentWindow);
});
}
function testOne(contentWindow) {
onSearchEnabledAndDisabled(contentWindow, function() {
testTwo(contentWindow);
});
// press cmd/ctrl F
EventUtils.synthesizeKey("f", { accelKey: true });
}
function testTwo(contentWindow) {
onSearchEnabledAndDisabled(contentWindow, function() {
testThree(contentWindow);
});
// press /
EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow);
}
function testThree(contentWindow) {
let groupItem = createEmptyGroupItem(contentWindow, 200);
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
TabView.toggle();
}; };
let onTabViewShown = function() {
window.removeEventListener("tabviewshown", onTabViewShown, false);
is(contentWindow.UI.getActiveTab(), groupItem.getChild(0), let onShow = function () {
cw = win.TabView.getContentWindow();
ok(win.TabView.isVisible(), "Tab View is visible");
afterAllTabItemsUpdated(testOne, win);
};
newWindowWithTabView(onShow, onLoad);
}
function testOne() {
whenSearchEnabledAndDisabled(testTwo);
// press cmd/ctrl F
EventUtils.synthesizeKey("f", {accelKey: true}, cw);
}
function testTwo() {
whenSearchEnabledAndDisabled(testThree);
// press /
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
}
function testThree() {
let onTabViewShown = function () {
is(cw.UI.getActiveTab(), groupItem.getChild(0),
"The active tab is newly created tab item"); "The active tab is newly created tab item");
let onSearchEnabled = function() { let onSearchEnabled = function () {
contentWindow.removeEventListener( let doc = cw.document;
"tabviewsearchenabled", onSearchEnabled, false); let searchBox = cw.iQ("#searchbox");
let hasFocus = doc.hasFocus() && doc.activeElement == searchBox[0];
ok(hasFocus, "The search box has focus");
let searchBox = contentWindow.iQ("#searchbox"); let tab = win.gBrowser.tabs[1];
searchBox.val(tab._tabViewTabItem.$tabTitle[0].innerHTML);
ok(contentWindow.document.hasFocus() && cw.performSearch();
contentWindow.document.activeElement == searchBox[0],
"The search box has focus");
searchBox.val(newTabOne._tabViewTabItem.$tabTitle[0].innerHTML);
contentWindow.performSearch(); whenTabViewIsHidden(function () {
is(tab, win.gBrowser.selectedTab, "The search result tab is shown");
let checkSelectedTab = function() { waitForFocus(finish);
window.removeEventListener("tabviewhidden", checkSelectedTab, false); }, win);
is(newTabOne, gBrowser.selectedTab, "The search result tab is shown");
cleanUpAndFinish(groupItem.getChild(0), contentWindow);
};
window.addEventListener("tabviewhidden", checkSelectedTab, false);
// use the tabview menu (the same as pressing cmd/ctrl + e) // use the tabview menu (the same as pressing cmd/ctrl + e)
document.getElementById("menu_tabview").doCommand(); win.document.getElementById("menu_tabview").doCommand();
}; };
contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow); whenSearchEnabled(onSearchEnabled);
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
}; };
window.addEventListener("tabviewhidden", onTabViewHidden, false);
window.addEventListener("tabviewshown", onTabViewShown, false); whenTabViewIsHidden(function () {
showTabView(onTabViewShown, win);
}, win);
// click on the + button // click on the + button
let groupItem = createEmptyGroupItem(cw, 300, 300, 200);
let newTabButton = groupItem.container.getElementsByClassName("newTabButton"); let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
ok(newTabButton[0], "New tab button exists"); ok(newTabButton[0], "New tab button exists");
EventUtils.sendMouseEvent({ type: "click" }, newTabButton[0], contentWindow); EventUtils.sendMouseEvent({type: "click"}, newTabButton[0], cw);
} }
function onSearchEnabledAndDisabled(contentWindow, callback) { function whenSearchEnabledAndDisabled(callback) {
let onSearchEnabled = function() { whenSearchEnabled(function () {
contentWindow.removeEventListener( whenSearchDisabled(callback);
"tabviewsearchenabled", onSearchEnabled, false); cw.hideSearch();
contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled, false); });
contentWindow.hideSearch(); }
}
let onSearchDisabled = function() { function whenSearchEnabled(callback) {
contentWindow.removeEventListener( cw.addEventListener("tabviewsearchenabled", function onSearchEnabled() {
"tabviewsearchdisabled", onSearchDisabled, false); cw.removeEventListener("tabviewsearchenabled", onSearchEnabled, false);
callback(); callback();
} }, false);
contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
} }
function cleanUpAndFinish(tabItem, contentWindow) { function whenSearchDisabled(callback) {
gBrowser.selectedTab = originalTab; cw.addEventListener("tabviewsearchdisabled", function onSearchDisabled() {
gBrowser.removeTab(newTabOne); cw.removeEventListener("tabviewsearchdisabled", onSearchDisabled, false);
gBrowser.removeTab(tabItem.tab); callback();
}, false);
finish();
} }
function createEmptyGroupItem(contentWindow, padding) {
let pageBounds = contentWindow.Items.getPageBounds();
pageBounds.inset(padding, padding);
let box = new contentWindow.Rect(pageBounds);
box.width = 300;
box.height = 300;
let emptyGroupItem = new contentWindow.GroupItem([], { bounds: box });
return emptyGroupItem;
}

Просмотреть файл

@ -0,0 +1,184 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
let stateBackup = ss.getBrowserState();
let state = {windows:[{tabs:[
// first group
{entries:[{url:"http://example.com#1"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#1\",\"groupID\":2}"}},
{entries:[{url:"http://example.com#2"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#2\",\"groupID\":2}"}},
{entries:[{url:"http://example.com#3"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#3\",\"groupID\":2}"}},
{entries:[{url:"http://example.com#4"}],extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#4\",\"groupID\":2}"}},
// second group
{entries:[{url:"http://example.com#5"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#5\",\"groupID\":1}"}},
{entries:[{url:"http://example.com#6"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#6\",\"groupID\":1}"}},
{entries:[{url:"http://example.com#7"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#7\",\"groupID\":1}"}},
{entries:[{url:"http://example.com#8"}],hidden:true,extData:{"tabview-tab":"{\"bounds\":{\"left\":20,\"top\":20,\"width\":20,\"height\":20},\"url\":\"http://example.com#8\",\"groupID\":1}"}}
],selected:5,extData:{
"tabview-groups":"{\"nextID\":8,\"activeGroupId\":1}","tabview-group":"{\"1\":{\"bounds\":{\"left\":15,\"top\":10,\"width\":415,\"height\":367},\"userSize\":{\"x\":415,\"y\":367},\"title\":\"\",\"id\":1},\"2\":{\"bounds\":{\"left\":286,\"top\":488,\"width\":418,\"height\":313},\"title\":\"\",\"id\":2}}",
"tabview-ui":"{\"pageBounds\":{\"left\":0,\"top\":0,\"width\":940,\"height\":1075}}"
}}]};
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);
TabsProgressListener.init();
registerCleanupFunction(function () {
TabsProgressListener.uninit();
Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
ss.setBrowserState(stateBackup);
});
Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3);
TabView._initFrame(function () {
executeSoon(testRestoreWithHiddenTabs);
});
}
function testRestoreWithHiddenTabs() {
let checked = false;
let ssReady = false;
let tabsRestored = false;
let check = function () {
if (checked || !ssReady || !tabsRestored)
return;
checked = true;
is(gBrowser.tabs.length, 8, "there are now eight tabs");
is(gBrowser.visibleTabs.length, 4, "four visible tabs");
let cw = TabView.getContentWindow();
is(cw.GroupItems.groupItems.length, 2, "there are now two groupItems");
testSwitchToInactiveGroup();
}
whenSessionStoreReady(function () {
ssReady = true;
check();
});
TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
if (4 < needsRestore)
return;
TabsProgressListener.unsetCallback();
is(needsRestore, 4, "4/8 tabs restored");
tabsRestored = true;
check();
});
ss.setBrowserState(JSON.stringify(state));
}
function testSwitchToInactiveGroup() {
let firstProgress = true;
TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
if (firstProgress) {
firstProgress = false;
is(isRestoring, 3, "restoring 3 tabs concurrently");
} else {
ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
}
if (needsRestore)
return;
TabsProgressListener.unsetCallback();
is(gBrowser.visibleTabs.length, 4, "four visible tabs");
waitForFocus(finish);
});
gBrowser.selectedTab = gBrowser.tabs[4];
}
function whenSessionStoreReady(callback) {
window.addEventListener("SSWindowStateReady", function onReady() {
window.removeEventListener("SSWindowStateReady", onReady, false);
executeSoon(callback);
}, false);
}
function countTabs() {
let needsRestore = 0, isRestoring = 0;
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
while (windowsEnum.hasMoreElements()) {
let window = windowsEnum.getNext();
if (window.closed)
continue;
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
let browser = window.gBrowser.tabs[i].linkedBrowser;
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
isRestoring++;
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
needsRestore++;
}
}
return [needsRestore, isRestoring];
}
let TabsProgressListener = {
init: function () {
gBrowser.addTabsProgressListener(this);
},
uninit: function () {
this.unsetCallback();
gBrowser.removeTabsProgressListener(this);
},
setCallback: function (callback) {
this.callback = callback;
},
unsetCallback: function () {
delete this.callback;
},
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
let isNetwork = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
let isWindow = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
if (!(this.callback && isNetwork && isWindow))
return;
let self = this;
let finalize = function () {
if (wasRestoring)
delete aBrowser.__wasRestoring;
self.callback.apply(null, countTabs());
};
let isRestoring = aBrowser.__SS_restoreState == TAB_STATE_RESTORING;
let wasRestoring = !aBrowser.__SS_restoreState && aBrowser.__wasRestoring;
let hasStopped = aStateFlags & Ci.nsIWebProgressListener.STATE_STOP;
if (isRestoring && !hasStopped)
aBrowser.__wasRestoring = true;
if (hasStopped && (isRestoring || wasRestoring))
finalize();
}
}

Просмотреть файл

@ -38,6 +38,7 @@ function test() {
if (!callback) if (!callback)
callback = finish; callback = finish;
assertOneSingleGroupItem();
callback(); callback();
}); });
} }
@ -111,7 +112,7 @@ function test() {
// some callback waiting to be fired after gBrowser.loadOneTab(). After // some callback waiting to be fired after gBrowser.loadOneTab(). After
// that the browser is in a state where loadURI() will create a new entry // that the browser is in a state where loadURI() will create a new entry
// in the session history (that is vital for back/forward functionality). // in the session history (that is vital for back/forward functionality).
afterAllTabsLoaded(function () SimpleTest.executeSoon(continueTest)); afterAllTabsLoaded(function () executeSoon(continueTest));
} }
// ---------- // ----------
@ -190,7 +191,7 @@ function enterAndLeavePrivateBrowsing(callback) {
pb.privateBrowsingEnabled = false; pb.privateBrowsingEnabled = false;
else { else {
Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete"); Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete");
afterAllTabsLoaded(callback); afterAllTabsLoaded(function () executeSoon(callback));
} }
} }

Просмотреть файл

@ -113,6 +113,8 @@ function test() {
showTabView(function () { showTabView(function () {
cw = TabView.getContentWindow(); cw = TabView.getContentWindow();
assertNumberOfGroups('start', 1);
createGroupItem(); createGroupItem();
afterAllTabsLoaded(function () { afterAllTabsLoaded(function () {

Просмотреть файл

@ -93,9 +93,13 @@ function newWindowWithTabView(shownCallback, loadCallback, width, height) {
// ---------- // ----------
function afterAllTabsLoaded(callback, win) { function afterAllTabsLoaded(callback, win) {
const TAB_STATE_NEEDS_RESTORE = 1;
win = win || window; win = win || window;
let stillToLoad = 0; let stillToLoad = 0;
let restoreHiddenTabs = Services.prefs.getBoolPref(
"browser.sessionstore.restore_hidden_tabs");
function onLoad() { function onLoad() {
this.removeEventListener("load", onLoad, true); this.removeEventListener("load", onLoad, true);
@ -105,8 +109,14 @@ function afterAllTabsLoaded(callback, win) {
} }
for (let a = 0; a < win.gBrowser.tabs.length; a++) { for (let a = 0; a < win.gBrowser.tabs.length; a++) {
let browser = win.gBrowser.tabs[a].linkedBrowser; let tab = win.gBrowser.tabs[a];
if (browser.contentDocument.readyState != "complete" || let browser = tab.linkedBrowser;
let isRestorable = !(tab.hidden && !restoreHiddenTabs &&
browser.__SS_restoreState &&
browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE);
if (isRestorable && browser.contentDocument.readyState != "complete" ||
browser.webProgress.isLoadingDocument) { browser.webProgress.isLoadingDocument) {
stillToLoad++; stillToLoad++;
browser.addEventListener("load", onLoad, true); browser.addEventListener("load", onLoad, true);
@ -114,7 +124,7 @@ function afterAllTabsLoaded(callback, win) {
} }
if (!stillToLoad) if (!stillToLoad)
callback(); executeSoon(callback);
} }
// ---------- // ----------

Просмотреть файл

@ -224,6 +224,9 @@ SessionStoreService.prototype = {
// number of tabs to restore concurrently, pref controlled. // number of tabs to restore concurrently, pref controlled.
_maxConcurrentTabRestores: null, _maxConcurrentTabRestores: null,
// whether to restore hidden tabs or not, pref controlled.
_restoreHiddenTabs: null,
// The state from the previous session (after restoring pinned tabs) // The state from the previous session (after restoring pinned tabs)
_lastSessionState: null, _lastSessionState: null,
@ -281,6 +284,10 @@ SessionStoreService.prototype = {
this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs"); this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
this._prefBranch.addObserver("sessionstore.max_concurrent_tabs", this, true); this._prefBranch.addObserver("sessionstore.max_concurrent_tabs", this, true);
this._restoreHiddenTabs =
this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs");
this._prefBranch.addObserver("sessionstore.restore_hidden_tabs", this, true);
// Make sure gRestoreTabsProgressListener has a reference to sessionstore // Make sure gRestoreTabsProgressListener has a reference to sessionstore
// so that it can make calls back in // so that it can make calls back in
gRestoreTabsProgressListener.ss = this; gRestoreTabsProgressListener.ss = this;
@ -598,6 +605,10 @@ SessionStoreService.prototype = {
this._maxConcurrentTabRestores = this._maxConcurrentTabRestores =
this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs"); this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
break; break;
case "sessionstore.restore_hidden_tabs":
this._restoreHiddenTabs =
this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs");
break;
} }
break; break;
case "timer-callback": // timer call back for delayed saving case "timer-callback": // timer call back for delayed saving
@ -1078,6 +1089,10 @@ SessionStoreService.prototype = {
this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab)); this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
// Just put it at the end of the list of visible tabs; // Just put it at the end of the list of visible tabs;
this._tabsToRestore.visible.push(aTab); this._tabsToRestore.visible.push(aTab);
// let's kick off tab restoration again to ensure this tab gets restored
// with "restore_hidden_tabs" == false (now that it has become visible)
this.restoreNextTab();
} }
// Default delay of 2 seconds gives enough time to catch multiple TabShow // Default delay of 2 seconds gives enough time to catch multiple TabShow
@ -2944,7 +2959,7 @@ SessionStoreService.prototype = {
if (this._tabsToRestore.visible.length) { if (this._tabsToRestore.visible.length) {
nextTabArray = this._tabsToRestore.visible; nextTabArray = this._tabsToRestore.visible;
} }
else if (this._tabsToRestore.hidden.length) { else if (this._restoreHiddenTabs && this._tabsToRestore.hidden.length) {
nextTabArray = this._tabsToRestore.hidden; nextTabArray = this._tabsToRestore.hidden;
} }

Просмотреть файл

@ -127,6 +127,7 @@ _BROWSER_TEST_FILES = \
browser_589246.js \ browser_589246.js \
browser_590268.js \ browser_590268.js \
browser_590563.js \ browser_590563.js \
browser_595601-restore_hidden.js \
browser_597315.js \ browser_597315.js \
browser_597315_index.html \ browser_597315_index.html \
browser_597315_a.html \ browser_597315_a.html \

Просмотреть файл

@ -0,0 +1,119 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
let stateBackup = ss.getBrowserState();
let state = {windows:[{tabs:[
{entries:[{url:"http://example.com#1"}]},
{entries:[{url:"http://example.com#2"}]},
{entries:[{url:"http://example.com#3"}]},
{entries:[{url:"http://example.com#4"}]},
{entries:[{url:"http://example.com#5"}], hidden: true},
{entries:[{url:"http://example.com#6"}], hidden: true},
{entries:[{url:"http://example.com#7"}], hidden: true},
{entries:[{url:"http://example.com#8"}], hidden: true}
]}]};
function test() {
waitForExplicitFinish();
registerCleanupFunction(function () {
Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
TabsProgressListener.uninit();
ss.setBrowserState(stateBackup);
});
TabsProgressListener.init();
Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3);
// First stage: restoreHiddenTabs = true
// Second stage: restoreHiddenTabs = false
test_loadTabs(true, function () {
test_loadTabs(false, function () {
waitForFocus(finish);
});
});
}
function test_loadTabs(restoreHiddenTabs, callback) {
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", restoreHiddenTabs);
let expectedTabs = restoreHiddenTabs ? 8 : 4;
let firstProgress = true;
TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
if (firstProgress) {
firstProgress = false;
is(isRestoring, 3, "restoring 3 tabs concurrently");
} else {
ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
}
if (gBrowser.tabs.length - needsRestore == expectedTabs) {
TabsProgressListener.unsetCallback();
is(gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
callback();
}
});
ss.setBrowserState(JSON.stringify(state));
}
function countTabs() {
let needsRestore = 0, isRestoring = 0;
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
while (windowsEnum.hasMoreElements()) {
let window = windowsEnum.getNext();
if (window.closed)
continue;
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
let browser = window.gBrowser.tabs[i].linkedBrowser;
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
isRestoring++;
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
needsRestore++;
}
}
return [needsRestore, isRestoring];
}
let TabsProgressListener = {
init: function () {
gBrowser.addTabsProgressListener(this);
},
uninit: function () {
this.unsetCallback();
gBrowser.removeTabsProgressListener(this);
},
setCallback: function (callback) {
this.callback = callback;
},
unsetCallback: function () {
delete this.callback;
},
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
this.callback.apply(null, countTabs());
}
}

Просмотреть файл

@ -47,8 +47,20 @@ function waitForBrowserState(aState, aSetStateCallback) {
let windowsOpen = 1; let windowsOpen = 1;
let listening = false; let listening = false;
let windowObserving = false; let windowObserving = false;
let restoreHiddenTabs = Services.prefs.getBoolPref(
"browser.sessionstore.restore_hidden_tabs");
aState.windows.forEach(function(winState) expectedTabsRestored += winState.tabs.length); aState.windows.forEach(function (winState) {
winState.tabs.forEach(function (tabState) {
if (restoreHiddenTabs || !tabState.hidden)
expectedTabsRestored++;
});
});
// There must be only hidden tabs and restoreHiddenTabs = false. We still
// expect one of them to be restored because it gets shown automatically.
if (!expectedTabsRestored)
expectedTabsRestored = 1;
function onSSTabRestored(aEvent) { function onSSTabRestored(aEvent) {
if (++tabsRestored == expectedTabsRestored) { if (++tabsRestored == expectedTabsRestored) {