Bug 1352119 - Implement new page loading indicator animation, part 2: add a "burst" when loading finishes draft
authorJim Porter <jporter@mozilla.com>
Thu, 13 Jul 2017 13:26:04 -0500
changeset 616070 3fa4f50374b8dfb835ac04ac6ccbad1dc34149c2
parent 616069 68ce602a01eb052b14fa0faa1284703f74c759e3
child 639354 e0b188594445a32f00ed27df73c6e8fbd29219c8
push id70560
push userbmo:squibblyflabbetydoo@gmail.com
push dateWed, 26 Jul 2017 17:25:34 +0000
bugs1352119
milestone56.0a1
Bug 1352119 - Implement new page loading indicator animation, part 2: add a "burst" when loading finishes 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
@@ -724,16 +724,23 @@
                     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) {
+                    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);
@@ -7418,16 +7425,19 @@
                   class="tab-background">
           <xul:hbox xbl:inherits="pinned,selected=visuallyselected"
                     class="tab-background-start"/>
           <xul:hbox xbl:inherits="pinned,selected=visuallyselected"
                     class="tab-background-middle"/>
           <xul:hbox xbl:inherits="pinned,selected=visuallyselected"
                     class="tab-background-end"/>
         </xul:hbox>
+        <xul:hbox xbl:inherits="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"
                     role="presentation"
                     layer="true"/>
           <xul:image xbl:inherits="src=image,loadingprincipal=iconLoadingPrincipal,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
                      anonid="tab-icon-image"
@@ -7707,16 +7717,30 @@
       </method>
 
        <method name="finishMediaBlockTimer">
         <body><![CDATA[
           TelemetryStopwatch.finish("TAB_MEDIA_BLOCKING_TIME_MS", this);
         ]]></body>
       </method>
 
+      <method name="animateLoadingBurst">
+        <body><![CDATA[
+          if (Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
+            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;
           }
@@ -7814,16 +7838,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
@@ -210,16 +210,17 @@
   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/sidebar/close.svg                       (../shared/sidebar/close.svg)
 #ifndef MOZ_PHOTON_ANIMATIONS
   skin/classic/browser/tabbrowser/connecting.png               (../shared/tabbrowser/connecting.png)
   skin/classic/browser/tabbrowser/connecting@2x.png            (../shared/tabbrowser/connecting@2x.png)
 #else
   skin/classic/browser/tabbrowser/loading.svg                  (../shared/tabbrowser/loading.svg)
+  skin/classic/browser/tabbrowser/loading-burst.svg            (../shared/tabbrowser/loading-burst.svg)
 #endif
   skin/classic/browser/tabbrowser/crashed.svg                  (../shared/tabbrowser/crashed.svg)
 #ifdef MOZ_PHOTON_THEME
   skin/classic/browser/tabbrowser/indicator-tab-attention.svg  (../shared/tabbrowser/indicator-tab-attention.svg)
 #endif
   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)
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
@@ -62,16 +62,57 @@
 }
 
 .tab-content {
   position: relative;
   padding-inline-end: 9px;
   padding-inline-start: 9px;
 }
 
+%ifdef MOZ_PHOTON_ANIMATIONS
+.tab-loading-burst {
+  position: relative;
+  overflow: hidden;
+  margin-top: 2px;
+}
+
+.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%;
+  margin-inline-start: calc(17px - 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);
+  }
+}
+%endif
+
 .tab-throbber,
 .tab-icon-image,
 .tab-sharing-icon-overlay,
 .tab-icon-sound,
 .tab-close-button {
   margin-top: 1px;
 }