зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
66ce76121f
Коммит
f868df08cb
|
@ -785,6 +785,8 @@ pref("browser.sessionstore.max_resumed_crashes", 1);
|
|||
// Other tabs won't be restored until they are selected
|
||||
// N = The number of tabs to restore at the same time
|
||||
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
|
||||
pref("accessibility.blockautorefresh", false);
|
||||
|
|
|
@ -64,6 +64,7 @@ _BROWSER_FILES = \
|
|||
browser_tabview_bug595518.js \
|
||||
browser_tabview_bug595521.js \
|
||||
browser_tabview_bug595560.js \
|
||||
browser_tabview_bug595601.js \
|
||||
browser_tabview_bug595804.js \
|
||||
browser_tabview_bug595930.js \
|
||||
browser_tabview_bug595943.js \
|
||||
|
|
|
@ -1,93 +1,121 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
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/";
|
||||
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
|
||||
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();
|
||||
|
||||
// open a new window and setup the window state.
|
||||
let newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
|
||||
newWin.addEventListener("load", function(event) {
|
||||
this.removeEventListener("load", arguments.callee, false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
|
||||
});
|
||||
|
||||
let newState = {
|
||||
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);
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false);
|
||||
|
||||
let firstTab = newWin.gBrowser.tabs[0];
|
||||
let secondTab = newWin.gBrowser.tabs[1];
|
||||
testTabSwitchAfterRestore(function () {
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", true);
|
||||
|
||||
// wait until the first tab is fully loaded
|
||||
let browser = newWin.gBrowser.getBrowserForTab(firstTab);
|
||||
let onLoad = function() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
testTabSwitchAfterRestore(function () {
|
||||
waitForFocus(finish);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
is(browser.currentURI.spec, DUMMY_PAGE_URL,
|
||||
"The url of first tab url is dummy_page.html");
|
||||
function testTabSwitchAfterRestore(callback) {
|
||||
newWindowWithState(state, function (win) {
|
||||
registerCleanupFunction(function () win.close());
|
||||
|
||||
// check the hidden state of both tabs.
|
||||
ok(firstTab.hidden, "The first tab is hidden");
|
||||
ok(!secondTab.hidden, "The second tab is not hidden");
|
||||
is(secondTab, newWin.gBrowser.selectedTab, "The second tab is selected");
|
||||
let [firstTab, secondTab] = win.gBrowser.tabs;
|
||||
is(firstTab.linkedBrowser.currentURI.spec, DUMMY_PAGE_URL,
|
||||
"The url of first tab url is dummy_page.html");
|
||||
|
||||
// when the second tab is hidden, the iframe should be initialized and
|
||||
// the first tab should be visible.
|
||||
let onTabHide = function() {
|
||||
newWin.gBrowser.tabContainer.removeEventListener("TabHide", onTabHide, true);
|
||||
// check the hidden state of both tabs.
|
||||
ok(firstTab.hidden, "The first tab is hidden");
|
||||
ok(!secondTab.hidden, "The second tab is not hidden");
|
||||
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");
|
||||
is(firstTab, newWin.gBrowser.selectedTab, "The first tab is selected");
|
||||
ok(secondTab.hidden, "The second tab is hidden");
|
||||
ok(win.TabView.getContentWindow(), "Panorama is loaded");
|
||||
ok(!firstTab.hidden, "The first tab is not hidden");
|
||||
is(firstTab, win.gBrowser.selectedTab, "The first tab is selected");
|
||||
ok(secondTab.hidden, "The second tab is hidden");
|
||||
|
||||
// clean up and finish
|
||||
newWin.close();
|
||||
callback();
|
||||
}, false);
|
||||
|
||||
finish();
|
||||
};
|
||||
newWin.gBrowser.tabContainer.addEventListener("TabHide", onTabHide, true);
|
||||
// switch to another tab
|
||||
win.switchToTabHavingURI(DUMMY_PAGE_URL);
|
||||
});
|
||||
}
|
||||
|
||||
// switch to another tab
|
||||
newWin.switchToTabHavingURI(DUMMY_PAGE_URL);
|
||||
}
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
function newWindowWithState(state, callback) {
|
||||
let opts = "chrome,all,dialog=no,height=800,width=800";
|
||||
let win = window.openDialog(getBrowserURL(), "_blank", opts);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,134 +1,97 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let newTabOne;
|
||||
let originalTab;
|
||||
let win;
|
||||
let cw;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
originalTab = gBrowser.visibleTabs[0];
|
||||
newTabOne = gBrowser.addTab("http://mochi.test:8888/");
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(newTabOne);
|
||||
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 onLoad = function (tvwin) {
|
||||
win = tvwin;
|
||||
registerCleanupFunction(function () win.close());
|
||||
win.gBrowser.loadOneTab("http://mochi.test:8888/", {inBackground: true});
|
||||
};
|
||||
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");
|
||||
|
||||
let onSearchEnabled = function() {
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchenabled", onSearchEnabled, false);
|
||||
let onSearchEnabled = function () {
|
||||
let doc = cw.document;
|
||||
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() &&
|
||||
contentWindow.document.activeElement == searchBox[0],
|
||||
"The search box has focus");
|
||||
searchBox.val(newTabOne._tabViewTabItem.$tabTitle[0].innerHTML);
|
||||
cw.performSearch();
|
||||
|
||||
contentWindow.performSearch();
|
||||
|
||||
let checkSelectedTab = function() {
|
||||
window.removeEventListener("tabviewhidden", checkSelectedTab, false);
|
||||
is(newTabOne, gBrowser.selectedTab, "The search result tab is shown");
|
||||
cleanUpAndFinish(groupItem.getChild(0), contentWindow);
|
||||
};
|
||||
window.addEventListener("tabviewhidden", checkSelectedTab, false);
|
||||
whenTabViewIsHidden(function () {
|
||||
is(tab, win.gBrowser.selectedTab, "The search result tab is shown");
|
||||
waitForFocus(finish);
|
||||
}, win);
|
||||
|
||||
// use the tabview menu (the same as pressing cmd/ctrl + e)
|
||||
document.getElementById("menu_tabview").doCommand();
|
||||
};
|
||||
contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
|
||||
EventUtils.synthesizeKey("VK_SLASH", { type: "keydown" }, contentWindow);
|
||||
win.document.getElementById("menu_tabview").doCommand();
|
||||
};
|
||||
|
||||
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
|
||||
let groupItem = createEmptyGroupItem(cw, 300, 300, 200);
|
||||
let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
|
||||
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) {
|
||||
let onSearchEnabled = function() {
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchenabled", onSearchEnabled, false);
|
||||
contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled, false);
|
||||
contentWindow.hideSearch();
|
||||
}
|
||||
let onSearchDisabled = function() {
|
||||
contentWindow.removeEventListener(
|
||||
"tabviewsearchdisabled", onSearchDisabled, false);
|
||||
function whenSearchEnabledAndDisabled(callback) {
|
||||
whenSearchEnabled(function () {
|
||||
whenSearchDisabled(callback);
|
||||
cw.hideSearch();
|
||||
});
|
||||
}
|
||||
|
||||
function whenSearchEnabled(callback) {
|
||||
cw.addEventListener("tabviewsearchenabled", function onSearchEnabled() {
|
||||
cw.removeEventListener("tabviewsearchenabled", onSearchEnabled, false);
|
||||
callback();
|
||||
}
|
||||
contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function cleanUpAndFinish(tabItem, contentWindow) {
|
||||
gBrowser.selectedTab = originalTab;
|
||||
gBrowser.removeTab(newTabOne);
|
||||
gBrowser.removeTab(tabItem.tab);
|
||||
|
||||
finish();
|
||||
function whenSearchDisabled(callback) {
|
||||
cw.addEventListener("tabviewsearchdisabled", function onSearchDisabled() {
|
||||
cw.removeEventListener("tabviewsearchdisabled", onSearchDisabled, false);
|
||||
callback();
|
||||
}, false);
|
||||
}
|
||||
|
||||
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)
|
||||
callback = finish;
|
||||
|
||||
assertOneSingleGroupItem();
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
@ -111,7 +112,7 @@ function test() {
|
|||
// some callback waiting to be fired after gBrowser.loadOneTab(). After
|
||||
// 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).
|
||||
afterAllTabsLoaded(function () SimpleTest.executeSoon(continueTest));
|
||||
afterAllTabsLoaded(function () executeSoon(continueTest));
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
@ -190,7 +191,7 @@ function enterAndLeavePrivateBrowsing(callback) {
|
|||
pb.privateBrowsingEnabled = false;
|
||||
else {
|
||||
Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete");
|
||||
afterAllTabsLoaded(callback);
|
||||
afterAllTabsLoaded(function () executeSoon(callback));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,8 @@ function test() {
|
|||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
assertNumberOfGroups('start', 1);
|
||||
|
||||
createGroupItem();
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
|
|
|
@ -93,9 +93,13 @@ function newWindowWithTabView(shownCallback, loadCallback, width, height) {
|
|||
|
||||
// ----------
|
||||
function afterAllTabsLoaded(callback, win) {
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
|
||||
win = win || window;
|
||||
|
||||
let stillToLoad = 0;
|
||||
let restoreHiddenTabs = Services.prefs.getBoolPref(
|
||||
"browser.sessionstore.restore_hidden_tabs");
|
||||
|
||||
function onLoad() {
|
||||
this.removeEventListener("load", onLoad, true);
|
||||
|
@ -105,8 +109,14 @@ function afterAllTabsLoaded(callback, win) {
|
|||
}
|
||||
|
||||
for (let a = 0; a < win.gBrowser.tabs.length; a++) {
|
||||
let browser = win.gBrowser.tabs[a].linkedBrowser;
|
||||
if (browser.contentDocument.readyState != "complete" ||
|
||||
let tab = win.gBrowser.tabs[a];
|
||||
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) {
|
||||
stillToLoad++;
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
|
@ -114,7 +124,7 @@ function afterAllTabsLoaded(callback, win) {
|
|||
}
|
||||
|
||||
if (!stillToLoad)
|
||||
callback();
|
||||
executeSoon(callback);
|
||||
}
|
||||
|
||||
// ----------
|
||||
|
|
|
@ -224,6 +224,9 @@ SessionStoreService.prototype = {
|
|||
|
||||
// number of tabs to restore concurrently, pref controlled.
|
||||
_maxConcurrentTabRestores: null,
|
||||
|
||||
// whether to restore hidden tabs or not, pref controlled.
|
||||
_restoreHiddenTabs: null,
|
||||
|
||||
// The state from the previous session (after restoring pinned tabs)
|
||||
_lastSessionState: null,
|
||||
|
@ -281,6 +284,10 @@ SessionStoreService.prototype = {
|
|||
this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
|
||||
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
|
||||
// so that it can make calls back in
|
||||
gRestoreTabsProgressListener.ss = this;
|
||||
|
@ -598,6 +605,10 @@ SessionStoreService.prototype = {
|
|||
this._maxConcurrentTabRestores =
|
||||
this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs");
|
||||
break;
|
||||
case "sessionstore.restore_hidden_tabs":
|
||||
this._restoreHiddenTabs =
|
||||
this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "timer-callback": // timer call back for delayed saving
|
||||
|
@ -1078,6 +1089,10 @@ SessionStoreService.prototype = {
|
|||
this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
|
||||
// Just put it at the end of the list of visible tabs;
|
||||
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
|
||||
|
@ -2944,7 +2959,7 @@ SessionStoreService.prototype = {
|
|||
if (this._tabsToRestore.visible.length) {
|
||||
nextTabArray = this._tabsToRestore.visible;
|
||||
}
|
||||
else if (this._tabsToRestore.hidden.length) {
|
||||
else if (this._restoreHiddenTabs && this._tabsToRestore.hidden.length) {
|
||||
nextTabArray = this._tabsToRestore.hidden;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ _BROWSER_TEST_FILES = \
|
|||
browser_589246.js \
|
||||
browser_590268.js \
|
||||
browser_590563.js \
|
||||
browser_595601-restore_hidden.js \
|
||||
browser_597315.js \
|
||||
browser_597315_index.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 listening = 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) {
|
||||
if (++tabsRestored == expectedTabsRestored) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче