michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); michael@0: michael@0: const TAB_STATE_NEEDS_RESTORE = 1; michael@0: const TAB_STATE_RESTORING = 2; michael@0: michael@0: let stateBackup = ss.getBrowserState(); michael@0: michael@0: let state = {windows:[{tabs:[ michael@0: // first group michael@0: {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}"}}, michael@0: {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}"}}, michael@0: {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}"}}, michael@0: {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}"}}, michael@0: michael@0: // second group michael@0: {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}"}}, michael@0: {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}"}}, michael@0: {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}"}}, michael@0: {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}"}} michael@0: ],selected:4,extData:{ michael@0: "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}}", michael@0: "tabview-ui":"{\"pageBounds\":{\"left\":0,\"top\":0,\"width\":940,\"height\":1075}}" michael@0: }}]}; michael@0: michael@0: function test() { michael@0: waitForExplicitFinish(); michael@0: michael@0: Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false); michael@0: michael@0: TabsProgressListener.init(); michael@0: michael@0: registerCleanupFunction(function () { michael@0: TabsProgressListener.uninit(); michael@0: michael@0: Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs"); michael@0: michael@0: gBrowser.selectedTab = gBrowser.tabs[0]; michael@0: ss.setBrowserState(stateBackup); michael@0: }); michael@0: michael@0: TabView._initFrame(function () { michael@0: executeSoon(testRestoreWithHiddenTabs); michael@0: }); michael@0: } michael@0: michael@0: function testRestoreWithHiddenTabs() { michael@0: TabsProgressListener.setCallback(function (needsRestore, isRestoring) { michael@0: if (needsRestore <= 4) { michael@0: TabsProgressListener.unsetCallback(); michael@0: is(needsRestore, 4, "4/8 tabs restored"); michael@0: } michael@0: }); michael@0: michael@0: waitForBrowserState(state, 4, function () { michael@0: is(gBrowser.tabs.length, 8, "there are now eight tabs"); michael@0: is(gBrowser.visibleTabs.length, 4, "four visible tabs"); michael@0: michael@0: let cw = TabView.getContentWindow(); michael@0: is(cw.GroupItems.groupItems.length, 2, "there are now two groupItems"); michael@0: michael@0: testSwitchToInactiveGroup(); michael@0: }); michael@0: } michael@0: michael@0: function testSwitchToInactiveGroup() { michael@0: let firstProgress = true; michael@0: michael@0: TabsProgressListener.setCallback(function (needsRestore, isRestoring) { michael@0: if (firstProgress) { michael@0: firstProgress = false; michael@0: is(isRestoring, 3, "restoring 3 tabs concurrently"); michael@0: } else { michael@0: ok(isRestoring < 4, "restoring max. 3 tabs concurrently"); michael@0: } michael@0: michael@0: if (needsRestore) michael@0: return; michael@0: michael@0: TabsProgressListener.unsetCallback(); michael@0: michael@0: is(gBrowser.visibleTabs.length, 4, "four visible tabs"); michael@0: waitForFocus(finish); michael@0: }); michael@0: michael@0: gBrowser.selectedTab = gBrowser.tabs[4]; michael@0: } michael@0: michael@0: function waitForBrowserState(state, numTabs, callback) { michael@0: let tabContainer = gBrowser.tabContainer; michael@0: tabContainer.addEventListener("SSTabRestored", function onRestored() { michael@0: if (--numTabs <= 0) { michael@0: tabContainer.removeEventListener("SSTabRestored", onRestored, true); michael@0: executeSoon(callback); michael@0: } michael@0: }, true); michael@0: michael@0: ss.setBrowserState(JSON.stringify(state)); michael@0: } michael@0: michael@0: function countTabs() { michael@0: let needsRestore = 0, isRestoring = 0; michael@0: let windowsEnum = Services.wm.getEnumerator("navigator:browser"); michael@0: michael@0: while (windowsEnum.hasMoreElements()) { michael@0: let window = windowsEnum.getNext(); michael@0: if (window.closed) michael@0: continue; michael@0: michael@0: for (let i = 0; i < window.gBrowser.tabs.length; i++) { michael@0: let browser = window.gBrowser.tabs[i].linkedBrowser; michael@0: if (browser.__SS_restoreState == TAB_STATE_RESTORING) michael@0: isRestoring++; michael@0: else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) michael@0: needsRestore++; michael@0: } michael@0: } michael@0: michael@0: return [needsRestore, isRestoring]; michael@0: } michael@0: michael@0: let TabsProgressListener = { michael@0: init: function () { michael@0: gBrowser.addTabsProgressListener(this); michael@0: }, michael@0: michael@0: uninit: function () { michael@0: this.unsetCallback(); michael@0: gBrowser.removeTabsProgressListener(this); michael@0: }, michael@0: michael@0: setCallback: function (callback) { michael@0: this.callback = callback; michael@0: }, michael@0: michael@0: unsetCallback: function () { michael@0: delete this.callback; michael@0: }, michael@0: michael@0: onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { michael@0: let isNetwork = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK; michael@0: let isWindow = aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW; michael@0: michael@0: if (!(this.callback && isNetwork && isWindow)) michael@0: return; michael@0: michael@0: let self = this; michael@0: let finalize = function () { michael@0: if (wasRestoring) michael@0: delete aBrowser.__wasRestoring; michael@0: michael@0: self.callback.apply(null, countTabs()); michael@0: }; michael@0: michael@0: let isRestoring = aBrowser.__SS_restoreState == TAB_STATE_RESTORING; michael@0: let wasRestoring = !aBrowser.__SS_restoreState && aBrowser.__wasRestoring; michael@0: let hasStopped = aStateFlags & Ci.nsIWebProgressListener.STATE_STOP; michael@0: michael@0: if (isRestoring && !hasStopped) michael@0: aBrowser.__wasRestoring = true; michael@0: michael@0: if (hasStopped && (isRestoring || wasRestoring)) michael@0: finalize(); michael@0: } michael@0: }