Bug 1394985 - Synchronize throbber animations; r?jaws
MozReview-Commit-ID: Gxgl8IJX6vZ
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -612,16 +612,60 @@
!aLocation) {
return true;
}
let location = aLocation ? aLocation.spec : "";
return location == "about:blank";
},
+ _syncThrobberAnimations() {
+ const originalTab = this.mTab;
+ BrowserUtils.promiseLayoutFlushed(this.mTab.ownerDocument, "style", () => {
+ if (!originalTab.parentNode) {
+ return;
+ }
+
+ const animations =
+ Array.from(originalTab.parentNode.getElementsByTagName("tab"))
+ .map(tab => {
+ const throbber =
+ document.getAnonymousElementByAttribute(tab, "anonid", "tab-throbber");
+ return throbber ? throbber.getAnimations({ subtree: true }) : [];
+ })
+ .reduce((a, b) => a.concat(b))
+ .filter(anim =>
+ anim instanceof CSSAnimation &&
+ (anim.animationName === "tab-throbber-animation" ||
+ anim.animationName === "tab-throbber-animation-rtl") &&
+ (anim.playState === "running" || anim.playState === "pending"));
+
+ // Synchronize with the oldest running animation, if any.
+ const firstStartTime = Math.min(
+ ...animations.map(anim => anim.startTime === null ? Infinity : anim.startTime)
+ );
+ if (firstStartTime === Infinity) {
+ return;
+ }
+ requestAnimationFrame(() => {
+ for (let animation of animations) {
+ // If |animation| has been cancelled since this rAF callback
+ // was scheduled we don't want to set its startTime since
+ // that would restart it. We check for a cancelled animation
+ // by looking for a null currentTime rather than checking
+ // the playState, since reading the playState of
+ // a CSSAnimation object will flush style.
+ if (animation.currentTime !== null) {
+ animation.startTime = firstStartTime;
+ }
+ }
+ });
+ });
+ },
+
onProgressChange(aWebProgress, aRequest,
aCurSelfProgress, aMaxSelfProgress,
aCurTotalProgress, aMaxTotalProgress) {
this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
if (!this._shouldShowProgress(aRequest))
return;
@@ -709,16 +753,17 @@
delete this.mBrowser.initialPageLoadedFromURLBar;
// If the browser is loading it must not be crashed anymore
this.mTab.removeAttribute("crashed");
}
if (this._shouldShowProgress(aRequest)) {
if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
this.mTab.setAttribute("busy", "true");
+ this._syncThrobberAnimations();
}
if (this.mTab.selected) {
this.mTabBrowser.mIsBusy = true;
}
}
} else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
@@ -7513,16 +7558,17 @@
<xul:hbox class="tab-bottom-line"/>
</xul:vbox>
<xul:hbox xbl:inherits="pinned,bursting"
anonid="tab-loading-burst"
class="tab-loading-burst"/>
<xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
class="tab-content" align="center">
<xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
+ anonid="tab-throbber"
class="tab-throbber"
layer="true"/>
<xul:image xbl:inherits="src=image,loadingprincipal=iconLoadingPrincipal,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
anonid="tab-icon-image"
class="tab-icon-image"
validate="never"
role="presentation"/>
<xul:image xbl:inherits="sharing,selected=visuallyselected,pinned"
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -106,16 +106,24 @@ public:
// for more efficient internal usage.
const nsString& AnimationName() const { return mAnimationName; }
// Animation interface overrides
virtual Promise* GetReady(ErrorResult& aRv) override;
virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior) override;
virtual void Pause(ErrorResult& aRv) override;
+ // NOTE: tabbrowser.xml currently relies on the fact that reading the
+ // currentTime of a CSSAnimation does *not* flush style (whereas reading the
+ // playState does). If CSS Animations 2 specifies that reading currentTime
+ // also flushes style we will need to find another way to detect canceled
+ // animations in tabbrowser.xml. On the other hand, if CSS Animations 2
+ // specifies that reading playState does *not* flush style (and we drop the
+ // following override), then we should update tabbrowser.xml to check
+ // the playState instead.
virtual AnimationPlayState PlayStateFromJS() const override;
virtual void PlayFromJS(ErrorResult& aRv) override;
void PlayFromStyle();
void PauseFromStyle();
void CancelFromStyle() override
{
// When an animation is disassociated with style it enters an odd state