Bug 1392157 - Implement new page loading indicator animation, part 2: add a "burst" when loading finishes. r?jaws draft
authorJim Porter <jporter@mozilla.com>
Fri, 11 Aug 2017 14:00:22 -0500
changeset 652163 5c904ba7d65d3801a2adfade63c5b0994280922e
parent 652136 892c8916ba32b7733e06bfbfdd4083ffae3ca028
child 652164 2e8d9da485510dc34d8f1f1d7efce3df3cb7d70b
child 652186 f0126c5d0ca92d66604eca1ecbed9a94ee19092b
child 652296 d4f69b97708605aadeb3f951159bd21690eb955a
push id75957
push userbmo:jaws@mozilla.com
push dateThu, 24 Aug 2017 14:15:28 +0000
reviewersjaws
bugs1392157
milestone57.0a1
Bug 1392157 - Implement new page loading indicator animation, part 2: add a "burst" when loading finishes. r?jaws MozReview-Commit-ID: 6TUmwfcZXv0
browser/base/content/tabbrowser.xml
browser/themes/shared/jar.inc.mn
browser/themes/shared/tabbrowser/loading-burst.svg
browser/themes/shared/tabs.inc.css
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -720,16 +720,24 @@
                     this.mTabBrowser.mIsBusy = true;
                   }
                 }
               } else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
                          aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
 
                 if (this.mTab.hasAttribute("busy")) {
                   this.mTab.removeAttribute("busy");
+
+                  // Only animate the "burst" indicating the page has loaded if
+                  // the top-level page is the one that finished loading.
+                  if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
+                      Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
+                    this.mTab.animateLoadingBurst();
+                  }
+
                   this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
                   if (!this.mTab.selected)
                     this.mTab.setAttribute("unread", "true");
                 }
                 this.mTab.removeAttribute("progress");
 
                 if (aWebProgress.isTopLevel) {
                   let isSuccessful = Components.isSuccessCode(aStatus);
@@ -7350,16 +7358,19 @@
       <xul:stack class="tab-stack" flex="1">
         <xul:vbox xbl:inherits="selected=visuallyselected,fadein"
                   class="tab-background">
           <xul:hbox xbl:inherits="selected=visuallyselected"
                     class="tab-line"/>
           <xul:spacer flex="1"/>
           <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"
                     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"
@@ -7636,16 +7647,28 @@
       </method>
 
        <method name="finishMediaBlockTimer">
         <body><![CDATA[
           TelemetryStopwatch.finish("TAB_MEDIA_BLOCKING_TIME_MS", this);
         ]]></body>
       </method>
 
+      <method name="animateLoadingBurst">
+        <body><![CDATA[
+          if (this.hasAttribute("bursting")) {
+            // Stop the animation if it's already playing so we can restart it.
+            this.removeAttribute("bursting");
+            let burst = document.getAnonymousElementByAttribute(this, "anonid", "tab-loading-burst");
+            window.getComputedStyle(burst).animationName;
+          }
+          this.setAttribute("bursting", "true");
+        ]]></body>
+      </method>
+
       <method name="toggleMuteAudio">
         <parameter name="aMuteReason"/>
         <body>
         <![CDATA[
           // Do not attempt to toggle mute state if browser is lazy.
           if (!this.linkedPanel) {
             return;
           }
@@ -7743,16 +7766,24 @@
           return;
         }
 
         if (this._overPlayingIcon) {
           this.toggleMuteAudio();
         }
       ]]>
       </handler>
+      <handler event="animationend">
+      <![CDATA[
+        let anonid = event.originalTarget.getAttribute("anonid");
+        if (anonid == "tab-loading-burst") {
+          this.removeAttribute("bursting");
+        }
+      ]]>
+      </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
     <implementation implements="nsIDOMEventListener">
       <method name="_tabOnAttrModified">
         <parameter name="aEvent"/>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -186,16 +186,17 @@
   skin/classic/browser/badge-add-engine.png                    (../shared/search/badge-add-engine.png)
   skin/classic/browser/badge-add-engine@2x.png                 (../shared/search/badge-add-engine@2x.png)
   skin/classic/browser/search-indicator-badge-add.png          (../shared/search/search-indicator-badge-add.png)
   skin/classic/browser/search-indicator-badge-add@2x.png       (../shared/search/search-indicator-badge-add@2x.png)
   skin/classic/browser/search-indicator-magnifying-glass.svg   (../shared/search/search-indicator-magnifying-glass.svg)
   skin/classic/browser/search-arrow-go.svg                     (../shared/search/search-arrow-go.svg)
   skin/classic/browser/gear.svg                                (../shared/search/gear.svg)
   skin/classic/browser/tabbrowser/loading.svg                  (../shared/tabbrowser/loading.svg)
+  skin/classic/browser/tabbrowser/loading-burst.svg            (../shared/tabbrowser/loading-burst.svg)
   skin/classic/browser/tabbrowser/crashed.svg                  (../shared/tabbrowser/crashed.svg)
   skin/classic/browser/tabbrowser/newtab.svg                   (../shared/tabbrowser/newtab.svg)
   skin/classic/browser/tabbrowser/indicator-tab-attention.svg  (../shared/tabbrowser/indicator-tab-attention.svg)
   skin/classic/browser/tabbrowser/pendingpaint.png             (../shared/tabbrowser/pendingpaint.png)
   skin/classic/browser/tabbrowser/tab-audio-playing.svg        (../shared/tabbrowser/tab-audio-playing.svg)
   skin/classic/browser/tabbrowser/tab-audio-muted.svg          (../shared/tabbrowser/tab-audio-muted.svg)
   skin/classic/browser/tabbrowser/tab-audio-blocked.svg        (../shared/tabbrowser/tab-audio-blocked.svg)
   skin/classic/browser/tabbrowser/tab-audio-small.svg          (../shared/tabbrowser/tab-audio-small.svg)
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/tabbrowser/loading-burst.svg
@@ -0,0 +1,6 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="5" cy="5" r="5" fill="context-fill"/>
+</svg>
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -57,16 +57,62 @@
 .tab-content {
   padding: 0 @horizontalTabPadding@;
 }
 
 .tab-content[pinned] {
   padding: 0 12px;
 }
 
+.tab-loading-burst {
+  position: relative;
+  overflow: hidden;
+}
+
+.tab-loading-burst::before {
+  position: absolute;
+  content: "";
+  /* We set the width to be a percentage of the tab's width so that we can use
+     the `scale` transform to scale it up to the full size of the tab when the
+     burst occurs. We also need to set the margin-inline-start so that the
+     center of the burst matches the center of the favicon. */
+  width: 5%;
+  height: 100%;
+  /* Center the burst over the .tab-loading-burst; it's 9px from the edge thanks
+     to .tab-content, plus 8px more since .tab-loading-burst is 16px wide. */
+  margin-inline-start: calc(17px - 2.5%);
+}
+
+.tab-loading-burst[pinned]::before {
+  /* This is like the margin-inline-start rule above, except that icons for
+     pinned tabs are 12px from the edge. */
+  margin-inline-start: calc(20px - 2.5%);
+}
+
+.tab-loading-burst[bursting]::before {
+  background-image: url("chrome://browser/skin/tabbrowser/loading-burst.svg");
+  background-position: center center;
+  background-size: 100% auto;
+  background-repeat: no-repeat;
+  animation: tab-burst-animation 1.8s var(--animation-easing-function);
+  -moz-context-properties: fill;
+  fill: var(--tab-loading-fill);
+}
+
+@keyframes tab-burst-animation {
+  0% {
+    opacity: 0.4;
+    transform: scale(1);
+  }
+  100% {
+    opacity: 0;
+    transform: scale(40);
+  }
+}
+
 .tab-throbber,
 .tab-icon-image,
 .tab-sharing-icon-overlay,
 .tab-icon-sound,
 .tab-close-button {
   margin-top: 1px;
 }