зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset d1cd5199bf45 (bug 867142)
This commit is contained in:
Родитель
9381ad4dcd
Коммит
aca2d68a01
|
@ -16,6 +16,9 @@ const STATE_QUITTING = -1;
|
|||
const STATE_STOPPED_STR = "stopped";
|
||||
const STATE_RUNNING_STR = "running";
|
||||
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
const TAB_STATE_RESTORING = 2;
|
||||
|
||||
const PRIVACY_NONE = 0;
|
||||
const PRIVACY_ENCRYPTED = 1;
|
||||
const PRIVACY_FULL = 2;
|
||||
|
@ -232,29 +235,6 @@ this.SessionStore = {
|
|||
|
||||
checkPrivacyLevel: function ss_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) {
|
||||
return SessionStoreInternal.checkPrivacyLevel(aIsHTTPS, aUseDefaultPref);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a given browser is waiting to be restored. That means its
|
||||
* history and state is ready but we wait until it's higher up in the priority
|
||||
* queue or until it's made visible (if restore_on_demand=true).
|
||||
*
|
||||
* @param aBrowser Browser reference
|
||||
* @returns bool
|
||||
*/
|
||||
isTabStateNeedsRestore: function ss_isTabStateNeedsRestore(aBrowser) {
|
||||
return TabRestoreStates.isNeedsRestore(aBrowser);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns whether a given browser is currently restoring, i.e. we wait for
|
||||
* the actual page to load and will restore form data when it's finished.
|
||||
*
|
||||
* @param aBrowser Browser reference
|
||||
* @returns bool
|
||||
*/
|
||||
isTabStateRestoring: function ss_isTabStateRestoring(aBrowser) {
|
||||
return TabRestoreStates.isRestoring(aBrowser);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1077,7 +1057,7 @@ let SessionStoreInternal = {
|
|||
RestoringTabsData.remove(aTab.linkedBrowser);
|
||||
delete aTab.linkedBrowser.__SS_formDataSaved;
|
||||
delete aTab.linkedBrowser.__SS_hostSchemeData;
|
||||
if (TabRestoreStates.has(aTab.linkedBrowser))
|
||||
if (aTab.linkedBrowser.__SS_restoreState)
|
||||
this._resetTabRestoringState(aTab);
|
||||
});
|
||||
openWindows[aWindow.__SSi] = true;
|
||||
|
@ -1272,10 +1252,10 @@ let SessionStoreInternal = {
|
|||
// If this tab was in the middle of restoring or still needs to be restored,
|
||||
// we need to reset that state. If the tab was restoring, we will attempt to
|
||||
// restore the next tab.
|
||||
if (TabRestoreStates.has(browser)) {
|
||||
let wasRestoring = TabRestoreStates.isRestoring(browser);
|
||||
let previousState = browser.__SS_restoreState;
|
||||
if (previousState) {
|
||||
this._resetTabRestoringState(aTab);
|
||||
if (wasRestoring)
|
||||
if (previousState == TAB_STATE_RESTORING)
|
||||
this.restoreNextTab();
|
||||
}
|
||||
|
||||
|
@ -1337,7 +1317,8 @@ let SessionStoreInternal = {
|
|||
// following "load" is too late for deleting the data caches)
|
||||
// It's possible to get a load event after calling stop on a browser (when
|
||||
// overwriting tabs). We want to return early if the tab hasn't been restored yet.
|
||||
if (TabRestoreStates.isNeedsRestore(aBrowser)) {
|
||||
if (aBrowser.__SS_restoreState &&
|
||||
aBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1373,8 +1354,11 @@ let SessionStoreInternal = {
|
|||
this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
|
||||
|
||||
let tab = aWindow.gBrowser.selectedTab;
|
||||
// Explicitly call restoreTab() to to restore the tab if we need to.
|
||||
if (TabRestoreStates.isNeedsRestore(tab.linkedBrowser))
|
||||
// If __SS_restoreState is still on the browser and it is
|
||||
// TAB_STATE_NEEDS_RESTORE, then then we haven't restored
|
||||
// this tab yet. Explicitly call restoreTab to kick off the restore.
|
||||
if (tab.linkedBrowser.__SS_restoreState &&
|
||||
tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
||||
this.restoreTab(tab);
|
||||
|
||||
// attempt to update the current URL we send in a crash report
|
||||
|
@ -1384,7 +1368,8 @@ let SessionStoreInternal = {
|
|||
|
||||
onTabShow: function ssi_onTabShow(aWindow, aTab) {
|
||||
// If the tab hasn't been restored yet, move it into the right bucket
|
||||
if (TabRestoreStates.isNeedsRestore(aTab.linkedBrowser)) {
|
||||
if (aTab.linkedBrowser.__SS_restoreState &&
|
||||
aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
|
||||
TabRestoreQueue.hiddenToVisible(aTab);
|
||||
|
||||
// let's kick off tab restoration again to ensure this tab gets restored
|
||||
|
@ -1399,7 +1384,8 @@ let SessionStoreInternal = {
|
|||
|
||||
onTabHide: function ssi_onTabHide(aWindow, aTab) {
|
||||
// If the tab hasn't been restored yet, move it into the right bucket
|
||||
if (TabRestoreStates.isNeedsRestore(aTab.linkedBrowser)) {
|
||||
if (aTab.linkedBrowser.__SS_restoreState &&
|
||||
aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
|
||||
TabRestoreQueue.visibleToHidden(aTab);
|
||||
}
|
||||
|
||||
|
@ -2770,7 +2756,7 @@ let SessionStoreInternal = {
|
|||
// state (in restoreHistoryPrecursor).
|
||||
if (aOverwriteTabs) {
|
||||
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
||||
if (TabRestoreStates.has(tabbrowser.browsers[i]))
|
||||
if (tabbrowser.browsers[i].__SS_restoreState)
|
||||
this._resetTabRestoringState(tabbrowser.tabs[i]);
|
||||
}
|
||||
}
|
||||
|
@ -3000,7 +2986,7 @@ let SessionStoreInternal = {
|
|||
// keep the data around to prevent dataloss in case
|
||||
// a tab gets closed before it's been properly restored
|
||||
RestoringTabsData.set(browser, tabData);
|
||||
TabRestoreStates.setNeedsRestore(browser);
|
||||
browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
|
||||
browser.setAttribute("pending", "true");
|
||||
tab.setAttribute("pending", "true");
|
||||
|
||||
|
@ -3190,7 +3176,7 @@ let SessionStoreInternal = {
|
|||
this._tabsRestoringCount++;
|
||||
|
||||
// Set this tab's state to restoring
|
||||
TabRestoreStates.setIsRestoring(browser);
|
||||
browser.__SS_restoreState = TAB_STATE_RESTORING;
|
||||
browser.removeAttribute("pending");
|
||||
aTab.removeAttribute("pending");
|
||||
|
||||
|
@ -4414,11 +4400,10 @@ let SessionStoreInternal = {
|
|||
let browser = aTab.linkedBrowser;
|
||||
|
||||
// Keep the tab's previous state for later in this method
|
||||
let wasRestoring = TabRestoreStates.isRestoring(browser);
|
||||
let wasNeedsRestore = TabRestoreStates.isNeedsRestore(browser);
|
||||
let previousState = browser.__SS_restoreState;
|
||||
|
||||
// The browser is no longer in any sort of restoring state.
|
||||
TabRestoreStates.remove(browser);
|
||||
delete browser.__SS_restoreState;
|
||||
|
||||
aTab.removeAttribute("pending");
|
||||
browser.removeAttribute("pending");
|
||||
|
@ -4430,11 +4415,11 @@ let SessionStoreInternal = {
|
|||
// Remove the progress listener if we should.
|
||||
this._removeTabsProgressListener(window);
|
||||
|
||||
if (wasRestoring) {
|
||||
if (previousState == TAB_STATE_RESTORING) {
|
||||
if (this._tabsRestoringCount)
|
||||
this._tabsRestoringCount--;
|
||||
}
|
||||
else if (wasNeedsRestore) {
|
||||
else if (previousState == TAB_STATE_NEEDS_RESTORE) {
|
||||
// Make sure the session history listener is removed. This is normally
|
||||
// done in restoreTab, but this tab is being removed before that gets called.
|
||||
this._removeSHistoryListener(aTab);
|
||||
|
@ -4687,41 +4672,6 @@ let TabAttributes = {
|
|||
}
|
||||
};
|
||||
|
||||
// A map keeping track of all tab restore states. A tab might be 'needs-restore'
|
||||
// if it waits until the restoration process is kicked off. This might start
|
||||
// when the tab reaches a higher position in the priority queue or when it's
|
||||
// made visible (when restore_on_demand=true). If a tab is 'restoring' we wait
|
||||
// for its actual page to load and will then restore form data etc. If has()
|
||||
// returns false the tab has not been restored from previous data or it has
|
||||
// already finished restoring and is thus now seen as a valid and complete tab.
|
||||
let TabRestoreStates = {
|
||||
_states: new WeakMap(),
|
||||
|
||||
has: function (browser) {
|
||||
return this._states.has(browser);
|
||||
},
|
||||
|
||||
isNeedsRestore: function ss_isNeedsRestore(browser) {
|
||||
return this._states.get(browser) === "needs-restore";
|
||||
},
|
||||
|
||||
setNeedsRestore: function (browser) {
|
||||
this._states.set(browser, "needs-restore");
|
||||
},
|
||||
|
||||
isRestoring: function ss_isRestoring(browser) {
|
||||
return this._states.get(browser) === "restoring";
|
||||
},
|
||||
|
||||
setIsRestoring: function (browser) {
|
||||
this._states.set(browser, "restoring");
|
||||
},
|
||||
|
||||
remove: function (browser) {
|
||||
this._states.delete(browser);
|
||||
}
|
||||
};
|
||||
|
||||
// This is used to help meter the number of restoring tabs. This is the control
|
||||
// point for telling the next tab to restore. It gets attached to each gBrowser
|
||||
// via gBrowser.addTabsProgressListener
|
||||
|
@ -4729,7 +4679,8 @@ let gRestoreTabsProgressListener = {
|
|||
onStateChange: function(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
// Ignore state changes on browsers that we've already restored and state
|
||||
// changes that aren't applicable.
|
||||
if (TabRestoreStates.isRestoring(aBrowser) &&
|
||||
if (aBrowser.__SS_restoreState &&
|
||||
aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
|
||||
|
|
|
@ -73,7 +73,7 @@ let TabsProgressListener = {
|
|||
},
|
||||
|
||||
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if (this.callback && SessionStore.isTabStateRestoring(aBrowser) &&
|
||||
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)
|
||||
|
@ -85,9 +85,9 @@ let TabsProgressListener = {
|
|||
|
||||
for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
|
||||
let browser = this.window.gBrowser.tabs[i].linkedBrowser;
|
||||
if (SessionStore.isTabStateRestoring(browser))
|
||||
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
|
||||
isRestoring++;
|
||||
else if (SessionStore.isTabStateNeedsRestore(browser))
|
||||
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
||||
needsRestore++;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,17 @@ function test() {
|
|||
// any given time. This guarantees that a finishing load won't start another.
|
||||
Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
|
||||
|
||||
// We have our own progress listener for this test, which we'll attach before our state is set
|
||||
let progressListener = {
|
||||
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
|
||||
progressCallback(aBrowser);
|
||||
}
|
||||
}
|
||||
|
||||
let state = { windows: [{ tabs: [
|
||||
{ entries: [{ url: "http://example.org#1" }], extData: { "uniq": r() } },
|
||||
{ entries: [{ url: "http://example.org#2" }], extData: { "uniq": r() } }, // overwriting
|
||||
|
@ -31,10 +42,10 @@ function test() {
|
|||
{ entries: [{ url: "http://example.org#6" }] } // creating
|
||||
], selected: 1 }] };
|
||||
|
||||
gProgressListener.setCallback(function progressCallback(aBrowser) {
|
||||
function progressCallback(aBrowser) {
|
||||
// We'll remove the progress listener after the first one because we aren't
|
||||
// loading any other tabs
|
||||
gProgressListener.unsetCallback();
|
||||
window.gBrowser.removeTabsProgressListener(progressListener);
|
||||
|
||||
let curState = JSON.parse(ss.getBrowserState());
|
||||
for (let i = 0; i < curState.windows[0].tabs.length; i++) {
|
||||
|
@ -98,8 +109,9 @@ function test() {
|
|||
"(creating) new data is stored in extData where there was none");
|
||||
|
||||
cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
window.gBrowser.addTabsProgressListener(progressListener);
|
||||
ss.setBrowserState(JSON.stringify(state));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,15 +18,19 @@ function test() {
|
|||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
TabsProgressListener.uninit();
|
||||
ss.setBrowserState(stateBackup);
|
||||
});
|
||||
|
||||
|
||||
TabsProgressListener.init();
|
||||
|
||||
window.addEventListener("SSWindowStateReady", function onReady() {
|
||||
window.removeEventListener("SSWindowStateReady", onReady, false);
|
||||
|
||||
let firstProgress = true;
|
||||
|
||||
gProgressListener.setCallback(function (browser, needsRestore, isRestoring) {
|
||||
TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
|
||||
if (firstProgress) {
|
||||
firstProgress = false;
|
||||
is(isRestoring, 3, "restoring 3 tabs concurrently");
|
||||
|
@ -35,7 +39,7 @@ function test() {
|
|||
}
|
||||
|
||||
if (0 == needsRestore) {
|
||||
gProgressListener.unsetCallback();
|
||||
TabsProgressListener.unsetCallback();
|
||||
waitForFocus(finish);
|
||||
}
|
||||
});
|
||||
|
@ -45,3 +49,51 @@ function test() {
|
|||
|
||||
ss.setBrowserState(JSON.stringify(statePinned));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ function test() {
|
|||
isnot(gBrowser.selectedTab, tab, "newly created tab is not selected");
|
||||
|
||||
ss.setTabState(tab, JSON.stringify(tabState));
|
||||
ok(SessionStore.isTabStateNeedsRestore(browser), "tab needs restoring");
|
||||
is(browser.__SS_restoreState, TAB_STATE_NEEDS_RESTORE, "tab needs restoring");
|
||||
|
||||
let state = JSON.parse(ss.getTabState(tab));
|
||||
let formdata = state.entries[0].formdata;
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
const TAB_STATE_RESTORING = 2;
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
|
||||
let SessionStore = tmp.SessionStore;
|
||||
|
@ -238,7 +241,7 @@ let gProgressListener = {
|
|||
function gProgressListener_onStateChange(aBrowser, aWebProgress, aRequest,
|
||||
aStateFlags, aStatus) {
|
||||
if ((!this._checkRestoreState ||
|
||||
SessionStore.isTabStateRestoring(aBrowser)) &&
|
||||
aBrowser.__SS_restoreState == TAB_STATE_RESTORING) &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
|
||||
|
@ -253,9 +256,9 @@ let gProgressListener = {
|
|||
for (let win in BrowserWindowIterator()) {
|
||||
for (let i = 0; i < win.gBrowser.tabs.length; i++) {
|
||||
let browser = win.gBrowser.tabs[i].linkedBrowser;
|
||||
if (SessionStore.isTabStateRestoring(browser))
|
||||
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
|
||||
isRestoring++;
|
||||
else if (SessionStore.isTabStateNeedsRestore(browser))
|
||||
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
||||
needsRestore++;
|
||||
else
|
||||
wasRestored++;
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
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:[
|
||||
|
@ -122,9 +125,9 @@ function countTabs() {
|
|||
|
||||
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
|
||||
let browser = window.gBrowser.tabs[i].linkedBrowser;
|
||||
if (SessionStore.isTabStateRestoring(browser))
|
||||
if (browser.__SS_restoreState == TAB_STATE_RESTORING)
|
||||
isRestoring++;
|
||||
else if (SessionStore.isTabStateNeedsRestore(browser))
|
||||
else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
|
||||
needsRestore++;
|
||||
}
|
||||
}
|
||||
|
@ -165,9 +168,8 @@ let TabsProgressListener = {
|
|||
self.callback.apply(null, countTabs());
|
||||
};
|
||||
|
||||
let isRestoring = SessionStore.isTabStateRestoring(aBrowser);
|
||||
let needsRestore = SessionStore.isTabStateNeedsRestore(aBrowser);
|
||||
let wasRestoring = !isRestoring && !needsRestore && aBrowser.__wasRestoring;
|
||||
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)
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
|
||||
let SessionStore = tmp.SessionStore;
|
||||
|
||||
// Some tests here assume that all restored tabs are loaded without waiting for
|
||||
// the user to bring them to the foreground. We ensure this by resetting the
|
||||
// related preference (see the "firefox.js" defaults file for details).
|
||||
|
@ -121,6 +117,8 @@ function newWindowWithTabView(shownCallback, loadCallback, width, height) {
|
|||
|
||||
// ----------
|
||||
function afterAllTabsLoaded(callback, win) {
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
|
||||
win = win || window;
|
||||
|
||||
let stillToLoad = 0;
|
||||
|
@ -139,7 +137,8 @@ function afterAllTabsLoaded(callback, win) {
|
|||
let browser = tab.linkedBrowser;
|
||||
|
||||
let isRestorable = !(tab.hidden && !restoreHiddenTabs &&
|
||||
SessionStore.isTabStateNeedsRestore(browser));
|
||||
browser.__SS_restoreState &&
|
||||
browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE);
|
||||
|
||||
if (isRestorable && browser.contentDocument.readyState != "complete" ||
|
||||
browser.webProgress.isLoadingDocument) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче