From 5aa259a41e2b4e380f4fbdc4d765126a9019e85d Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Wed, 1 Mar 2017 14:26:43 -0500 Subject: [PATCH] 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 : e4012c59d69fb5a7f371fadfd9548aa381c6058d --- browser/base/content/tabbrowser.css | 5 ++ browser/base/content/tabbrowser.xml | 79 +++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/browser/base/content/tabbrowser.css b/browser/base/content/tabbrowser.css index 2d9b7dd9035b..b766e7e11f9e 100644 --- a/browser/base/content/tabbrowser.css +++ b/browser/base/content/tabbrowser.css @@ -97,10 +97,15 @@ browser[pending] { display: none; } +browser[pendingtabchild], browser[pendingpaint] { opacity: 0; } +tabbrowser[pendingtabchild] { + background-color: #ffffff !important; +} + tabbrowser[pendingpaint] { background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png); background-repeat: no-repeat; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 7e96d4d409c7..15b064d1cfc4 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3724,6 +3724,7 @@ visibleTab: this.selectedTab, // Tab that's on screen. spinnerTab: null, // Tab showing a spinner. + blankTab: null, // Tab showing blank. originalTab: this.selectedTab, // Tab that we started on. tabbrowser: this, // Reference to gBrowser. @@ -3733,6 +3734,12 @@ // Map from tabs to STATE_* (below). tabState: new Map(), + // Holds a collection of '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. switchInProgress: false, @@ -3828,6 +3835,7 @@ window.addEventListener("sizemodechange", this); window.addEventListener("SwapDocShells", this, true); window.addEventListener("EndSwapDocShells", this, true); + window.addEventListener("MozTabChildNotReady", this, true); if (!this.minimized) { this.setTabState(this.requestedTab, this.STATE_LOADED); } @@ -3850,6 +3858,7 @@ window.removeEventListener("sizemodechange", this); window.removeEventListener("SwapDocShells", this, true); window.removeEventListener("EndSwapDocShells", this, true); + window.removeEventListener("MozTabChildNotReady", this, true); this.tabbrowser._switcher = null; @@ -3865,6 +3874,7 @@ this.assert(this.tabbrowser._switcher); this.assert(this.tabbrowser._switcher === this); this.assert(!this.spinnerTab); + this.assert(!this.blankTab); this.assert(!this.loadTimer); this.assert(!this.loadingTab); this.assert(this.lastVisibleTab === this.requestedTab); @@ -3895,20 +3905,42 @@ // This function is called after all the main state changes to // make sure we display the right tab. updateDisplay() { + let shouldBeBlank = this.pendingTabChild.has( + this.requestedTab.linkedBrowser); + // Figure out which tab we actually want visible right now. let showTab = null; 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 // available, show it. showTab = this.lastVisibleTab; } 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; } + // 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. - 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) { this.spinnerHidden(); this.tabbrowser.removeAttribute("pendingpaint"); @@ -3993,6 +4025,9 @@ if (this.lastVisibleTab && !this.lastVisibleTab.linkedBrowser) { this.lastVisibleTab = null; } + if (this.blankTab && !this.blankTab.linkedBrowser) { + this.blankTab = null; + } if (this.spinnerTab && !this.spinnerTab.linkedBrowser) { this.spinnerHidden(); this.spinnerTab = null; @@ -4105,8 +4140,9 @@ // Fires when the layers become available for a tab. onLayersReady(browser) { + this.pendingTabChild.delete(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.getTabState(tab) == this.STATE_LOADED); @@ -4131,6 +4167,7 @@ // Called when we're done clearing the layers for a tab. onLayersCleared(browser) { + this.pendingTabChild.delete(browser); let tab = this.tabbrowser.getTabForBrowser(browser); if (tab) { this.logState(`onLayersCleared(${tab._tPos})`); @@ -4151,6 +4188,15 @@ } else if (this.getTabState(tab) == this.STATE_UNLOADING) { 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,25 @@ 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); + this.logState(`onTabChildNotReady(${tab._tPos})`); + + this.assert(this.getTabState(tab) == this.STATE_LOADING); + 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. requestTab(tab) { if (tab === this.requestedTab) { @@ -4303,6 +4368,8 @@ this.onSwapDocShells(event.originalTarget, event.detail); } else if (event.type == "EndSwapDocShells") { this.onEndSwapDocShells(event.originalTarget, event.detail); + } else if (event.type == "MozTabChildNotReady") { + this.onTabChildNotReady(event.originalTarget); } this.postActions(); @@ -4329,7 +4396,8 @@ */ maybeFinishTabSwitch() { 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. // The changes will be visible after the next refresh driver tick + composite. let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window); @@ -4420,6 +4488,7 @@ if (tab === this.lastVisibleTab) accum += "V"; if (tab === this.loadingTab) accum += "L"; if (tab === this.requestedTab) accum += "R"; + if (tab === this.blankTab) accum += "B"; if (state == this.STATE_LOADED) accum += "(+)"; if (state == this.STATE_LOADING) accum += "(+?)"; if (state == this.STATE_UNLOADED) accum += "(-)";