Bug 1408152 - Sync the throbber animations again after the tab has finished moving. r?gijs
This feels pretty ugly to dig in to tabbrowser's tabprogresslistener so we can call syncThrobberAnimations. Alternatively, syncThrobberAnimations could be pulled out of the tabprogresslistener now that it would be called from another site. I looked at the CSS changes that the tab moving does and nothing jumped out at me as to why the animation gets out of sync.
MozReview-Commit-ID: ICbyQ8RMAVu
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -311,16 +311,64 @@
} finally {
this.selectedTab = currentTab;
this._previewMode = false;
}
]]>
</body>
</method>
+ <method name="syncThrobberAnimations">
+ <parameter name="aTab"/>
+ <body>
+ <![CDATA[
+ BrowserUtils.promiseLayoutFlushed(aTab.ownerDocument, "style", () => {
+ if (!aTab.parentNode) {
+ return;
+ }
+
+ const animations =
+ Array.from(aTab.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;
+ }
+ }
+ });
+ });
+ ]]>
+ </body>
+ </method>
+
<method name="getBrowserAtIndex">
<parameter name="aIndex"/>
<body>
<![CDATA[
return this.browsers[aIndex];
]]>
</body>
</method>
@@ -608,60 +656,16 @@
!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;
@@ -777,17 +781,17 @@
let continueMonitoring = true;
if (!document.querySelector(".tabbrowser-tab[busy]")) {
SchedulePressure.stopMonitoring(window);
continueMonitoring = false;
}
return {continueMonitoring};
},
});
- this._syncThrobberAnimations();
+ this.mTabBrowser.syncThrobberAnimations(this.mTab);
}
if (this.mTab.selected) {
this.mTabBrowser.mIsBusy = true;
}
}
} else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
@@ -7593,16 +7597,18 @@
}
draggedTab.removeEventListener("transitionend", onTransitionEnd);
draggedTab.removeAttribute("tabdrop-samewindow");
this._finishAnimateTabMove();
if (dropIndex !== false)
this.tabbrowser.moveTabTo(draggedTab, dropIndex);
+
+ this.tabbrowser.syncThrobberAnimations(draggedTab);
}
draggedTab.addEventListener("transitionend", onTransitionEnd);
} else {
this._finishAnimateTabMove();
if (dropIndex !== false)
this.tabbrowser.moveTabTo(draggedTab, dropIndex);
}
} else if (draggedTab) {