mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-28 12:45:27 +00:00
Bug 1342927 - Make async tab switcher show a blank tab when switching to tabs that have no TabChild yet. r=billm
MozReview-Commit-ID: J09CZHFGM2B --HG-- extra : rebase_source : b34eea5bf3b4c02c80f8ef6cecf3abf683e197cb
This commit is contained in:
parent
bae06d41c9
commit
aa4919b4b7
@ -97,10 +97,15 @@ browser[pending] {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
browser[pendingtabchild],
|
||||||
browser[pendingpaint] {
|
browser[pendingpaint] {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tabbrowser[pendingtabchild] {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
tabbrowser[pendingpaint] {
|
tabbrowser[pendingpaint] {
|
||||||
background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
|
background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
@ -3724,6 +3724,7 @@
|
|||||||
|
|
||||||
visibleTab: this.selectedTab, // Tab that's on screen.
|
visibleTab: this.selectedTab, // Tab that's on screen.
|
||||||
spinnerTab: null, // Tab showing a spinner.
|
spinnerTab: null, // Tab showing a spinner.
|
||||||
|
blankTab: null, // Tab showing blank.
|
||||||
originalTab: this.selectedTab, // Tab that we started on.
|
originalTab: this.selectedTab, // Tab that we started on.
|
||||||
|
|
||||||
tabbrowser: this, // Reference to gBrowser.
|
tabbrowser: this, // Reference to gBrowser.
|
||||||
@ -3733,6 +3734,12 @@
|
|||||||
// Map from tabs to STATE_* (below).
|
// Map from tabs to STATE_* (below).
|
||||||
tabState: new Map(),
|
tabState: new Map(),
|
||||||
|
|
||||||
|
// Holds a collection of <xul:browser>'s for this tabbrowser
|
||||||
|
// that we cannot force paint since their TabChild's haven't
|
||||||
|
// been constructed yet. Instead, we show blank tabs for them
|
||||||
|
// when attempting to switch to them.
|
||||||
|
pendingTabChild: new WeakSet(),
|
||||||
|
|
||||||
// True if we're in the midst of switching tabs.
|
// True if we're in the midst of switching tabs.
|
||||||
switchInProgress: false,
|
switchInProgress: false,
|
||||||
|
|
||||||
@ -3828,6 +3835,7 @@
|
|||||||
window.addEventListener("sizemodechange", this);
|
window.addEventListener("sizemodechange", this);
|
||||||
window.addEventListener("SwapDocShells", this, true);
|
window.addEventListener("SwapDocShells", this, true);
|
||||||
window.addEventListener("EndSwapDocShells", this, true);
|
window.addEventListener("EndSwapDocShells", this, true);
|
||||||
|
window.addEventListener("MozTabChildNotReady", this, true);
|
||||||
if (!this.minimized) {
|
if (!this.minimized) {
|
||||||
this.setTabState(this.requestedTab, this.STATE_LOADED);
|
this.setTabState(this.requestedTab, this.STATE_LOADED);
|
||||||
}
|
}
|
||||||
@ -3850,6 +3858,7 @@
|
|||||||
window.removeEventListener("sizemodechange", this);
|
window.removeEventListener("sizemodechange", this);
|
||||||
window.removeEventListener("SwapDocShells", this, true);
|
window.removeEventListener("SwapDocShells", this, true);
|
||||||
window.removeEventListener("EndSwapDocShells", this, true);
|
window.removeEventListener("EndSwapDocShells", this, true);
|
||||||
|
window.removeEventListener("MozTabChildNotReady", this, true);
|
||||||
|
|
||||||
this.tabbrowser._switcher = null;
|
this.tabbrowser._switcher = null;
|
||||||
|
|
||||||
@ -3865,6 +3874,7 @@
|
|||||||
this.assert(this.tabbrowser._switcher);
|
this.assert(this.tabbrowser._switcher);
|
||||||
this.assert(this.tabbrowser._switcher === this);
|
this.assert(this.tabbrowser._switcher === this);
|
||||||
this.assert(!this.spinnerTab);
|
this.assert(!this.spinnerTab);
|
||||||
|
this.assert(!this.blankTab);
|
||||||
this.assert(!this.loadTimer);
|
this.assert(!this.loadTimer);
|
||||||
this.assert(!this.loadingTab);
|
this.assert(!this.loadingTab);
|
||||||
this.assert(this.lastVisibleTab === this.requestedTab);
|
this.assert(this.lastVisibleTab === this.requestedTab);
|
||||||
@ -3895,20 +3905,42 @@
|
|||||||
// This function is called after all the main state changes to
|
// This function is called after all the main state changes to
|
||||||
// make sure we display the right tab.
|
// make sure we display the right tab.
|
||||||
updateDisplay() {
|
updateDisplay() {
|
||||||
|
let shouldBeBlank = this.pendingTabChild.has(
|
||||||
|
this.requestedTab.linkedBrowser);
|
||||||
|
|
||||||
// Figure out which tab we actually want visible right now.
|
// Figure out which tab we actually want visible right now.
|
||||||
let showTab = null;
|
let showTab = null;
|
||||||
if (this.getTabState(this.requestedTab) != this.STATE_LOADED &&
|
if (this.getTabState(this.requestedTab) != this.STATE_LOADED &&
|
||||||
this.lastVisibleTab && this.loadTimer) {
|
this.lastVisibleTab && this.loadTimer && !shouldBeBlank) {
|
||||||
// If we can't show the requestedTab, and lastVisibleTab is
|
// If we can't show the requestedTab, and lastVisibleTab is
|
||||||
// available, show it.
|
// available, show it.
|
||||||
showTab = this.lastVisibleTab;
|
showTab = this.lastVisibleTab;
|
||||||
} else {
|
} else {
|
||||||
// Show the requested tab. If it's not available, we'll show the spinner.
|
// Show the requested tab. If it's not available, we'll show the spinner or a blank tab.
|
||||||
showTab = this.requestedTab;
|
showTab = this.requestedTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First, let's deal with blank tabs, which we show instead
|
||||||
|
// of the spinner when the tab is not currently set up
|
||||||
|
// properly in the content process.
|
||||||
|
if (!shouldBeBlank && this.blankTab) {
|
||||||
|
this.tabbrowser.removeAttribute("pendingtabchild");
|
||||||
|
this.blankTab.linkedBrowser.removeAttribute("pendingtabchild");
|
||||||
|
this.blankTab = null;
|
||||||
|
} else if (shouldBeBlank && this.blankTab !== showTab) {
|
||||||
|
if (this.blankTab) {
|
||||||
|
this.blankTab.linkedBrowser.removeAttribute("pendingtabchild");
|
||||||
|
}
|
||||||
|
this.blankTab = showTab;
|
||||||
|
this.tabbrowser.setAttribute("pendingtabchild", "true");
|
||||||
|
this.blankTab.linkedBrowser.setAttribute("pendingtabchild", "true");
|
||||||
|
}
|
||||||
|
|
||||||
// Show or hide the spinner as needed.
|
// Show or hide the spinner as needed.
|
||||||
let needSpinner = this.getTabState(showTab) != this.STATE_LOADED && !this.minimized;
|
let needSpinner = this.getTabState(showTab) != this.STATE_LOADED &&
|
||||||
|
!this.minimized &&
|
||||||
|
!shouldBeBlank;
|
||||||
|
|
||||||
if (!needSpinner && this.spinnerTab) {
|
if (!needSpinner && this.spinnerTab) {
|
||||||
this.spinnerHidden();
|
this.spinnerHidden();
|
||||||
this.tabbrowser.removeAttribute("pendingpaint");
|
this.tabbrowser.removeAttribute("pendingpaint");
|
||||||
@ -3993,6 +4025,9 @@
|
|||||||
if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
|
if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) {
|
||||||
this.lastVisibleTab = null;
|
this.lastVisibleTab = null;
|
||||||
}
|
}
|
||||||
|
if (this.blankTab && !this.blankTab.linkedBrowser) {
|
||||||
|
this.blankTab = null;
|
||||||
|
}
|
||||||
if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
|
if (this.spinnerTab && !this.spinnerTab.linkedBrowser) {
|
||||||
this.spinnerHidden();
|
this.spinnerHidden();
|
||||||
this.spinnerTab = null;
|
this.spinnerTab = null;
|
||||||
@ -4105,8 +4140,9 @@
|
|||||||
|
|
||||||
// Fires when the layers become available for a tab.
|
// Fires when the layers become available for a tab.
|
||||||
onLayersReady(browser) {
|
onLayersReady(browser) {
|
||||||
|
this.pendingTabChild.delete(browser);
|
||||||
let tab = this.tabbrowser.getTabForBrowser(browser);
|
let tab = this.tabbrowser.getTabForBrowser(browser);
|
||||||
this.logState(`onLayersReady(${tab._tPos})`);
|
this.logState(`onLayersReady(${tab._tPos}, ${browser.isRemoteBrowser})`);
|
||||||
|
|
||||||
this.assert(this.getTabState(tab) == this.STATE_LOADING ||
|
this.assert(this.getTabState(tab) == this.STATE_LOADING ||
|
||||||
this.getTabState(tab) == this.STATE_LOADED);
|
this.getTabState(tab) == this.STATE_LOADED);
|
||||||
@ -4131,6 +4167,7 @@
|
|||||||
|
|
||||||
// Called when we're done clearing the layers for a tab.
|
// Called when we're done clearing the layers for a tab.
|
||||||
onLayersCleared(browser) {
|
onLayersCleared(browser) {
|
||||||
|
this.pendingTabChild.delete(browser);
|
||||||
let tab = this.tabbrowser.getTabForBrowser(browser);
|
let tab = this.tabbrowser.getTabForBrowser(browser);
|
||||||
if (tab) {
|
if (tab) {
|
||||||
this.logState(`onLayersCleared(${tab._tPos})`);
|
this.logState(`onLayersCleared(${tab._tPos})`);
|
||||||
@ -4151,6 +4188,15 @@
|
|||||||
} else if (this.getTabState(tab) == this.STATE_UNLOADING) {
|
} else if (this.getTabState(tab) == this.STATE_UNLOADING) {
|
||||||
this.onLayersCleared(tab.linkedBrowser);
|
this.onLayersCleared(tab.linkedBrowser);
|
||||||
}
|
}
|
||||||
|
} else if (this.getTabState(tab) == this.STATE_LOADED) {
|
||||||
|
// A tab just changed from non-remote to remote, which means
|
||||||
|
// that it's gone back into the STATE_LOADING state until
|
||||||
|
// it sends up a layer tree. We also add the browser to
|
||||||
|
// the pendingTabChild set since this browser is unlikely
|
||||||
|
// to have its TabChild set up right away, and we want to
|
||||||
|
// make it appear "blank" instead of showing a spinner for it.
|
||||||
|
this.pendingTabChild.add(tab.linkedBrowser);
|
||||||
|
this.setTabState(tab, this.STATE_LOADING);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -4248,6 +4294,35 @@
|
|||||||
this.setTabState(tab, this.STATE_LOADING);
|
this.setTabState(tab, this.STATE_LOADING);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// The tab for this browser isn't currently set
|
||||||
|
// up in the content process, so we have no chance
|
||||||
|
// of painting it right away. We'll paint a blank
|
||||||
|
// tab instead.
|
||||||
|
onTabChildNotReady(browser) {
|
||||||
|
let tab = this.tabbrowser.getTabForBrowser(browser);
|
||||||
|
|
||||||
|
let state = this.getTabState(tab);
|
||||||
|
this.assert(state == this.STATE_LOADING ||
|
||||||
|
state == this.STATE_LOADED);
|
||||||
|
|
||||||
|
// Because the TabChildNotReady event is queued from
|
||||||
|
// off of the main thread, it's possible that while
|
||||||
|
// it was being queued, the layer tree became ready
|
||||||
|
// and the state changed to STATE_LOADED. In that
|
||||||
|
// case, there's nothing to do here.
|
||||||
|
if (state == this.STATE_LOADING) {
|
||||||
|
this.logState(`onTabChildNotReady(${tab._tPos})`);
|
||||||
|
this.pendingTabChild.add(browser);
|
||||||
|
this.maybeFinishTabSwitch();
|
||||||
|
|
||||||
|
if (this.loadingTab === tab) {
|
||||||
|
this.clearTimer(this.loadTimer);
|
||||||
|
this.loadTimer = null;
|
||||||
|
this.loadingTab = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 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) {
|
||||||
@ -4303,6 +4378,8 @@
|
|||||||
this.onSwapDocShells(event.originalTarget, event.detail);
|
this.onSwapDocShells(event.originalTarget, event.detail);
|
||||||
} else if (event.type == "EndSwapDocShells") {
|
} else if (event.type == "EndSwapDocShells") {
|
||||||
this.onEndSwapDocShells(event.originalTarget, event.detail);
|
this.onEndSwapDocShells(event.originalTarget, event.detail);
|
||||||
|
} else if (event.type == "MozTabChildNotReady") {
|
||||||
|
this.onTabChildNotReady(event.originalTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.postActions();
|
this.postActions();
|
||||||
@ -4329,7 +4406,8 @@
|
|||||||
*/
|
*/
|
||||||
maybeFinishTabSwitch() {
|
maybeFinishTabSwitch() {
|
||||||
if (this.switchInProgress && this.requestedTab &&
|
if (this.switchInProgress && this.requestedTab &&
|
||||||
this.getTabState(this.requestedTab) == this.STATE_LOADED) {
|
(this.getTabState(this.requestedTab) == this.STATE_LOADED ||
|
||||||
|
this.requestedTab === this.blankTab)) {
|
||||||
// After this point the tab has switched from the content thread's point of view.
|
// After this point the tab has switched from the content thread's point of view.
|
||||||
// The changes will be visible after the next refresh driver tick + composite.
|
// The changes will be visible after the next refresh driver tick + composite.
|
||||||
let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
|
let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
|
||||||
@ -4420,6 +4498,7 @@
|
|||||||
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 (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 += "(-)";
|
||||||
|
Loading…
Reference in New Issue
Block a user