Bug 1385453 - Add API to tabbrowser to speculatively warm-up tabs for tab switching. r=billm

MozReview-Commit-ID: FIVx5d6ZOqq

--HG--
extra : rebase_source : 81749fb1c5ae398b7ff28ce5bcd4761b7d458a93
This commit is contained in:
Mike Conley 2017-08-21 10:11:33 -04:00
Родитель d9fd1ad2a1
Коммит 415debd9d4
2 изменённых файлов: 92 добавлений и 9 удалений

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

@ -1547,6 +1547,11 @@ pref("browser.tabs.remote.autostart.1", false);
pref("browser.tabs.remote.autostart.2", true); pref("browser.tabs.remote.autostart.2", true);
#endif #endif
// For speculatively warming up tabs to improve perceived
// performance while using the async tab switcher.
pref("browser.tabs.remote.maxWarmingTabs", 3);
pref("browser.tabs.remote.warmingUnloadDelayMs", 2000);
// For the about:tabcrashed page // For the about:tabcrashed page
pref("browser.tabs.crashReporting.sendReport", true); pref("browser.tabs.crashReporting.sendReport", true);
pref("browser.tabs.crashReporting.includeURL", false); pref("browser.tabs.crashReporting.includeURL", false);

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

@ -4086,6 +4086,12 @@
// removed from the set upon MozAfterPaint. // removed from the set upon MozAfterPaint.
maybeVisibleTabs: new Set([this.selectedTab]), maybeVisibleTabs: new Set([this.selectedTab]),
// This holds onto the set of tabs that we've been asked to warm up.
// This is used only for Telemetry and logging, and (in order to not
// over-complicate the async tab switcher any further) has nothing to do
// with how warmed tabs are loaded and unloaded.
warmingTabs: new WeakSet(),
STATE_UNLOADED: 0, STATE_UNLOADED: 0,
STATE_LOADING: 1, STATE_LOADING: 1,
STATE_LOADED: 2, STATE_LOADED: 2,
@ -4140,6 +4146,7 @@
this.onLayersReady(browser); this.onLayersReady(browser);
} }
} else if (state == this.STATE_UNLOADING) { } else if (state == this.STATE_UNLOADING) {
this.unwarmTab(tab);
browser.docShellIsActive = false; browser.docShellIsActive = false;
if (!tabParent) { if (!tabParent) {
this.onLayersCleared(browser); this.onLayersCleared(browser);
@ -4168,6 +4175,11 @@
init() { init() {
this.log("START"); this.log("START");
XPCOMUtils.defineLazyPreferenceGetter(this, "MAX_WARMING_TABS",
"browser.tabs.remote.maxWarmingTabs", 3);
XPCOMUtils.defineLazyPreferenceGetter(this, "WARMING_UNLOAD_DELAY" /* ms */,
"browser.tabs.remote.warmingUnloadDelayMs", 2000);
// If we minimized the window before the switcher was activated, // If we minimized the window before the switcher was activated,
// we might have set the preserveLayers flag for the current // we might have set the preserveLayers flag for the current
// browser. Let's clear it. // browser. Let's clear it.
@ -4417,6 +4429,7 @@
for (let [tab, ] of this.tabState) { for (let [tab, ] of this.tabState) {
if (!tab.linkedBrowser) { if (!tab.linkedBrowser) {
this.tabState.delete(tab); this.tabState.delete(tab);
this.unwarmTab(tab);
} }
} }
@ -4474,6 +4487,7 @@
// See how many tabs still have work to do. // See how many tabs still have work to do.
let numPending = 0; let numPending = 0;
let numWarming = 0;
for (let [tab, state] of this.tabState) { for (let [tab, state] of this.tabState) {
// Skip print preview browsers since they shouldn't affect tab switching. // Skip print preview browsers since they shouldn't affect tab switching.
if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) { if (this.tabbrowser._printPreviewBrowsers.has(tab.linkedBrowser)) {
@ -4482,6 +4496,10 @@
if (state == this.STATE_LOADED && tab !== this.requestedTab) { if (state == this.STATE_LOADED && tab !== this.requestedTab) {
numPending++; numPending++;
if (tab !== this.visibleTab) {
numWarming++;
}
} }
if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) { if (state == this.STATE_LOADING || state == this.STATE_UNLOADING) {
numPending++; numPending++;
@ -4497,8 +4515,14 @@
return; return;
} }
if (this.blankTab) { this.maybeFinishTabSwitch();
this.maybeFinishTabSwitch();
if (numWarming > this.MAX_WARMING_TABS) {
this.logState("Hit MAX_WARMING_TABS");
if (this.unloadTimer) {
this.clearTimer(this.unloadTimer);
}
this.onUnloadTimeout();
} }
if (numPending == 0) { if (numPending == 0) {
@ -4512,6 +4536,7 @@
onUnloadTimeout() { onUnloadTimeout() {
this.logState("onUnloadTimeout"); this.logState("onUnloadTimeout");
this.unloadTimer = null; this.unloadTimer = null;
this.warmingTabs = new WeakSet();
this.preActions(); this.preActions();
let numPending = 0; let numPending = 0;
@ -4561,8 +4586,6 @@
this.getTabState(tab) == this.STATE_LOADED); this.getTabState(tab) == this.STATE_LOADED);
this.setTabState(tab, this.STATE_LOADED); this.setTabState(tab, this.STATE_LOADED);
this.maybeFinishTabSwitch();
if (this.loadingTab === tab) { if (this.loadingTab === tab) {
this.clearTimer(this.loadTimer); this.clearTimer(this.loadTimer);
this.loadTimer = null; this.loadTimer = null;
@ -4575,7 +4598,6 @@
// around. // around.
onPaint() { onPaint() {
this.maybeVisibleTabs.clear(); this.maybeVisibleTabs.clear();
this.maybeFinishTabSwitch();
}, },
// Called when we're done clearing the layers for a tab. // Called when we're done clearing the layers for a tab.
@ -4706,19 +4728,66 @@
this.setTabState(tab, this.STATE_LOADING); this.setTabState(tab, this.STATE_LOADING);
}, },
canWarmTab(tab) {
// If the tab is not yet inserted, closing, not remote,
// crashed, already visible, or already requested, warming
// up the tab makes no sense.
if (this.minimizedOrFullyOccluded ||
!tab.linkedPanel ||
tab.closing ||
!tab.linkedBrowser.isRemoteBrowser ||
!tab.linkedBrowser.frameLoader.tabParent) {
return false;
}
// Similarly, if the tab is already in STATE_LOADING or
// STATE_LOADED somehow, there's no point in trying to
// warm it up.
let state = this.getTabState(tab);
if (state === this.STATE_LOADING ||
state === this.STATE_LOADED) {
return false;
}
return true;
},
unwarmTab(tab) {
this.warmingTabs.delete(tab);
},
warmupTab(tab) {
if (!this.canWarmTab(tab)) {
return;
}
this.logState("warmupTab " + this.tinfo(tab));
this.warmingTabs.add(tab);
this.setTabState(tab, this.STATE_LOADING);
this.suppressDisplayPortAndQueueUnload(tab, this.WARMING_UNLOAD_DELAY);
},
// Called when the user asks to switch to a given tab. // Called when the user asks to switch to a given tab.
requestTab(tab) { requestTab(tab) {
if (tab === this.requestedTab) { if (tab === this.requestedTab) {
return; return;
} }
this.unwarmTab(tab);
this._requestingTab = true; this._requestingTab = true;
this.logState("requestTab " + this.tinfo(tab)); this.logState("requestTab " + this.tinfo(tab));
this.startTabSwitch(); this.startTabSwitch();
this.requestedTab = tab; this.requestedTab = tab;
let browser = this.requestedTab.linkedBrowser; this.suppressDisplayPortAndQueueUnload(this.requestedTab, this.UNLOAD_DELAY);
this._requestingTab = false;
},
suppressDisplayPortAndQueueUnload(tab, unloadTimeout) {
let browser = tab.linkedBrowser;
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader; let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) { if (fl && fl.tabParent && !this.activeSuppressDisplayport.has(fl.tabParent)) {
@ -4731,10 +4800,9 @@
if (this.unloadTimer) { if (this.unloadTimer) {
this.clearTimer(this.unloadTimer); this.clearTimer(this.unloadTimer);
} }
this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), this.UNLOAD_DELAY); this.unloadTimer = this.setTimer(() => this.onUnloadTimeout(), unloadTimeout);
this.postActions(); this.postActions();
this._requestingTab = false;
}, },
handleEvent(event, delayed = false) { handleEvent(event, delayed = false) {
@ -4824,7 +4892,6 @@
TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", window); TelemetryStopwatch.finish("FX_TAB_SWITCH_SPINNER_VISIBLE_LONG_MS", window);
this.addMarker("AsyncTabSwitch:SpinnerHidden"); this.addMarker("AsyncTabSwitch:SpinnerHidden");
// we do not get a onPaint after displaying the spinner // we do not get a onPaint after displaying the spinner
this.maybeFinishTabSwitch();
}, },
addMarker(marker) { addMarker(marker) {
@ -4876,12 +4943,14 @@
for (let i = 0; i < this.tabbrowser.tabs.length; i++) { for (let i = 0; i < this.tabbrowser.tabs.length; i++) {
let tab = this.tabbrowser.tabs[i]; let tab = this.tabbrowser.tabs[i];
let state = this.getTabState(tab); let state = this.getTabState(tab);
let isWarming = this.warmingTabs.has(tab);
accum += i + ":"; accum += i + ":";
if (tab === this.lastVisibleTab) accum += "V"; if (tab === this.lastVisibleTab) accum += "V";
if (tab === this.loadingTab) accum += "L"; if (tab === this.loadingTab) accum += "L";
if (tab === this.requestedTab) accum += "R"; if (tab === this.requestedTab) accum += "R";
if (tab === this.blankTab) accum += "B"; if (tab === this.blankTab) accum += "B";
if (isWarming) accum += "(W)";
if (state == this.STATE_LOADED) accum += "(+)"; if (state == this.STATE_LOADED) accum += "(+)";
if (state == this.STATE_LOADING) accum += "(+?)"; if (state == this.STATE_LOADING) accum += "(+?)";
if (state == this.STATE_UNLOADED) accum += "(-)"; if (state == this.STATE_UNLOADED) accum += "(-)";
@ -4901,6 +4970,15 @@
]]></body> ]]></body>
</method> </method>
<method name="warmupTab">
<parameter name="aTab"/>
<body>
<![CDATA[
this._getSwitcher().warmupTab(aTab);
]]>
</body>
</method>
<!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT <!-- BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
MAKE SURE TO ADD IT HERE AS WELL. --> MAKE SURE TO ADD IT HERE AS WELL. -->
<property name="canGoBack" <property name="canGoBack"