Bug 662812 - Panorama isn't aware of the current SSWindowState when being initialized; r=zpao

This commit is contained in:
Tim Taubert 2011-08-16 11:06:14 +02:00
Родитель 332ca401eb
Коммит f3bb27e32e
9 изменённых файлов: 189 добавлений и 87 удалений

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

@ -184,6 +184,23 @@ let Storage = {
return existingData;
},
// ----------
// Function: readWindowBusyState
// Returns the current busyState for the given window.
readWindowBusyState: function Storage_readWindowBusyState(win) {
let state;
try {
let data = this._sessionStore.getWindowState(win);
if (data)
state = JSON.parse(data);
} catch (e) {
Utils.log("Error while parsing window state");
}
return (state && state.windows[0].busy);
},
// ----------
// Function: saveGroupItemsData
// Saves the global data for the <GroupItems> singleton for the given window.

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

@ -123,9 +123,9 @@ let UI = {
wasInTabView: false
},
// Variable: _storageBusyCount
// Used to keep track of how many calls to storageBusy vs storageReady.
_storageBusyCount: 0,
// Variable: _storageBusy
// Tells whether the storage is currently busy or not.
_storageBusy: false,
// Variable: isDOMWindowClosing
// Tells wether the parent window is about to close
@ -169,6 +169,10 @@ let UI = {
// ___ storage
Storage.init();
if (Storage.readWindowBusyState(gWindow))
this.storageBusy();
let data = Storage.readUIData(gWindow);
this._storageSanity(data);
this._pageBounds = data.pageBounds;
@ -612,12 +616,13 @@ let UI = {
// Pauses the storage activity that conflicts with sessionstore updates and
// private browsing mode switches. Calls can be nested.
storageBusy: function UI_storageBusy() {
if (!this._storageBusyCount) {
TabItems.pauseReconnecting();
GroupItems.pauseAutoclose();
}
this._storageBusyCount++;
if (this._storageBusy)
return;
this._storageBusy = true;
TabItems.pauseReconnecting();
GroupItems.pauseAutoclose();
},
// ----------
@ -625,16 +630,18 @@ let UI = {
// Resumes the activity paused by storageBusy, and updates for any new group
// information in sessionstore. Calls can be nested.
storageReady: function UI_storageReady() {
this._storageBusyCount--;
if (!this._storageBusyCount) {
let hasGroupItemsData = GroupItems.load();
if (!hasGroupItemsData)
this.reset();
TabItems.resumeReconnecting();
GroupItems._updateTabBar();
GroupItems.resumeAutoclose();
}
if (!this._storageBusy)
return;
this._storageBusy = false;
let hasGroupItemsData = GroupItems.load();
if (!hasGroupItemsData)
this.reset();
TabItems.resumeReconnecting();
GroupItems._updateTabBar();
GroupItems.resumeAutoclose();
},
// ----------
@ -730,7 +737,7 @@ let UI = {
} else {
// If we're currently in the process of entering private browsing,
// we don't want to go to the Tab View UI.
if (self._storageBusyCount)
if (self._storageBusy)
return;
// if not closing the last tab

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

@ -37,26 +37,20 @@ function setupTwo(win) {
contentWindow.TabItems.update(tabItem.tab);
tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
item.removeSubscriber("savedCachedImageData", onSaved);
--numTabsToSave;
if (!--numTabsToSave)
restoreWindow();
});
});
// after the window is closed, restore it.
let xulWindowDestory = function() {
Services.obs.removeObserver(
xulWindowDestory, "xul-window-destroyed", false);
// "xul-window-destroyed" is just fired just before a XUL window is
// destroyed so restore window and test it after a delay
let restoreWindow = function() {
executeSoon(function() {
restoredWin = undoCloseWindow();
restoredWin.addEventListener("load", function onLoad(event) {
restoredWin.removeEventListener("load", onLoad, false);
registerCleanupFunction(function() restoredWin.close());
// ensure that closed tabs have been saved
is(numTabsToSave, 0, "All tabs were saved when window was closed.");
is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
// setup tab variables and listen to the tabs load progress
@ -103,7 +97,6 @@ function setupTwo(win) {
}, false);
});
};
Services.obs.addObserver(xulWindowDestory, "xul-window-destroyed", false);
win.close();
}

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

@ -49,11 +49,15 @@ function onTabViewLoadedAndShown() {
groupTitles[a] = gi.getTitle();
}
contentWindow.gPrefBranch.setBoolPref("animate_zoom", false);
// Create a second tab
gBrowser.addTab("about:robots");
is(gBrowser.tabs.length, 2, "we now have 2 tabs");
registerCleanupFunction(function() {
gBrowser.removeTab(gBrowser.tabs[1]);
contentWindow.gPrefBranch.clearUserPref("animate_zoom");
});
afterAllTabsLoaded(function() {

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

@ -361,7 +361,10 @@ function togglePrivateBrowsing(callback) {
Services.obs.addObserver(function observe() {
Services.obs.removeObserver(observe, topic);
afterAllTabsLoaded(callback);
// use executeSoon() to let Panorama load its group data from the session
// before we call afterAllTabsLoaded()
executeSoon(function () afterAllTabsLoaded(callback));
}, topic, false);
let pb = Cc["@mozilla.org/privatebrowsing;1"].

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

@ -762,7 +762,7 @@ SessionStoreService.prototype = {
aWindow.__SSi = "window" + Date.now();
// and create its data object
this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
if (!this._isWindowLoaded(aWindow))
this._windows[aWindow.__SSi]._restoring = true;
if (!aWindow.toolbar.visible)
@ -946,6 +946,9 @@ SessionStoreService.prototype = {
// save the window if it has multiple tabs or a single saveable tab
if (winData.tabs.length > 1 ||
(winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]))) {
// we don't want to save the busy state
delete winData.busy;
this._closedWindows.unshift(winData);
this._capClosedWindows();
}
@ -1242,7 +1245,7 @@ SessionStoreService.prototype = {
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
var window = aTab.ownerDocument.defaultView;
this._sendWindowStateEvent(window, "Busy");
this._setWindowStateBusy(window);
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
},
@ -1258,7 +1261,7 @@ SessionStoreService.prototype = {
tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
tabState.pinned = false;
this._sendWindowStateEvent(aWindow, "Busy");
this._setWindowStateBusy(aWindow);
let newTab = aTab == aWindow.gBrowser.selectedTab ?
aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) :
aWindow.gBrowser.addTab();
@ -1301,7 +1304,7 @@ SessionStoreService.prototype = {
let closedTab = closedTabs.splice(aIndex, 1).shift();
let closedTabState = closedTab.state;
this._sendWindowStateEvent(aWindow, "Busy");
this._setWindowStateBusy(aWindow);
// create a new tab
let browser = aWindow.gBrowser;
let tab = browser.addTab();
@ -2511,7 +2514,7 @@ SessionStoreService.prototype = {
// We're not returning from this before we end up calling restoreHistoryPrecursor
// for this window, so make sure we send the SSWindowStateBusy event.
this._sendWindowStateEvent(aWindow, "Busy");
this._setWindowStateBusy(aWindow);
if (root._closedWindows)
this._closedWindows = root._closedWindows;
@ -2698,7 +2701,7 @@ SessionStoreService.prototype = {
if (aTabs.length == 0) {
// this is normally done in restoreHistory() but as we're returning early
// here we need to take care of it.
this._sendWindowStateEvent(aWindow, "Ready");
this._setWindowStateReady(aWindow);
return;
}
@ -2843,7 +2846,7 @@ SessionStoreService.prototype = {
if (aTabs.length == 0) {
// At this point we're essentially ready for consumers to read/write data
// via the sessionstore API so we'll send the SSWindowStateReady event.
this._sendWindowStateEvent(aWindow, "Ready");
this._setWindowStateReady(aWindow);
return; // no more tabs to restore
}
@ -3981,6 +3984,42 @@ SessionStoreService.prototype = {
}
},
/**
* Set the given window's busy state
* @param aWindow the window
* @param aValue the window's busy state
*/
_setWindowStateBusyValue:
function sss__changeWindowStateBusyValue(aWindow, aValue) {
this._windows[aWindow.__SSi].busy = aValue;
// Keep the to-be-restored state in sync because that is returned by
// getWindowState() as long as the window isn't loaded, yet.
if (!this._isWindowLoaded(aWindow)) {
let stateToRestore = this._statesToRestore[aWindow.__SS_restoreID].windows[0];
stateToRestore.busy = aValue;
}
},
/**
* Set the given window's state to 'not busy'.
* @param aWindow the window
*/
_setWindowStateReady: function sss__setWindowStateReady(aWindow) {
this._setWindowStateBusyValue(aWindow, false);
this._sendWindowStateEvent(aWindow, "Ready");
},
/**
* Set the given window's state to 'busy'.
* @param aWindow the window
*/
_setWindowStateBusy: function sss__setWindowStateBusy(aWindow) {
this._setWindowStateBusyValue(aWindow, true);
this._sendWindowStateEvent(aWindow, "Busy");
},
/**
* Dispatch an SSWindowState_____ event for the given window.
* @param aWindow the window

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

@ -150,6 +150,7 @@ _BROWSER_TEST_FILES = \
browser_635418.js \
browser_636279.js \
browser_659591.js \
browser_662812.js \
$(NULL)
ifneq ($(OS_ARCH),Darwin)

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

@ -4,8 +4,6 @@
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"}]},
@ -23,22 +21,14 @@ function test() {
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);
});
test_loadTabs(false, finish);
});
}
@ -46,10 +36,9 @@ 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) {
newWindowWithState(state, function (win, needsRestore, isRestoring) {
if (firstProgress) {
firstProgress = false;
is(isRestoring, 3, "restoring 3 tabs concurrently");
@ -57,60 +46,75 @@ function test_loadTabs(restoreHiddenTabs, callback) {
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");
if (win.gBrowser.tabs.length - needsRestore == expectedTabs) {
is(win.gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
TabsProgressListener.uninit();
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);
init: function (win) {
this.window = win;
this.window.gBrowser.addTabsProgressListener(this);
},
uninit: function () {
this.unsetCallback();
gBrowser.removeTabsProgressListener(this);
this.window.gBrowser.removeTabsProgressListener(this);
delete this.window;
delete this.callback;
},
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());
this.callback.apply(null, [this.window].concat(this.countTabs()));
},
countTabs: function () {
let needsRestore = 0, isRestoring = 0;
for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
let browser = this.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];
}
}
// ----------
function whenWindowLoaded(win, callback) {
win.addEventListener("load", function onLoad() {
win.removeEventListener("load", onLoad, false);
executeSoon(callback);
}, false);
}
// ----------
function newWindowWithState(state, callback) {
let opts = "chrome,all,dialog=no,height=800,width=800";
let win = window.openDialog(getBrowserURL(), "_blank", opts);
registerCleanupFunction(function () win.close());
whenWindowLoaded(win, function () {
TabsProgressListener.init(win);
TabsProgressListener.setCallback(callback);
ss.setWindowState(win, JSON.stringify(state), true);
});
}

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

@ -0,0 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
waitForExplicitFinish();
window.addEventListener("SSWindowStateBusy", function onBusy() {
window.removeEventListener("SSWindowStateBusy", onBusy, false);
let state = JSON.parse(ss.getWindowState(window));
ok(state.windows[0].busy, "window is busy");
window.addEventListener("SSWindowStateReady", function onReady() {
window.removeEventListener("SSWindowStateReady", onReady, false);
let state = JSON.parse(ss.getWindowState(window));
ok(!state.windows[0].busy, "window is not busy");
gBrowser.removeTab(gBrowser.tabs[1]);
executeSoon(finish);
}, false);
}, false);
// create a new tab
let tab = gBrowser.addTab("about:mozilla");
let browser = tab.linkedBrowser;
// close and restore it
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
gBrowser.removeTab(tab);
ss.undoCloseTab(window, 0);
}, true);
}