Bug 1393565 - De-duplicate the JS code and CSS that sets the bookmark and pocket library animation. r?gijs draft
authorJared Wein <jwein@mozilla.com>
Wed, 30 Aug 2017 15:58:24 -0400
changeset 659231 0e73aca9498d177068cfd547710bbd7353373f69
parent 656158 29f6ea3a68853d02fc8c2666d8a633e8831bbf6d
child 659249 b800c495318be6473033f96af7786576dbb30d34
push id78058
push userbmo:jaws@mozilla.com
push dateTue, 05 Sep 2017 18:00:21 +0000
reviewersgijs
bugs1393565
milestone57.0a1
Bug 1393565 - De-duplicate the JS code and CSS that sets the bookmark and pocket library animation. r?gijs MozReview-Commit-ID: Z1kIdrY2Um
browser/base/content/browser-places.js
browser/extensions/pocket/bootstrap.js
browser/extensions/pocket/skin/shared/pocket.css
browser/themes/shared/toolbarbutton-icons.inc.css
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -73,31 +73,16 @@ var StarUI = {
         elt.removeAttribute("disabled");
       elt.removeAttribute("wasDisabled");
     });
   },
 
   // nsIDOMEventListener
   handleEvent(aEvent) {
     switch (aEvent.type) {
-      case "animationend": {
-        let animatableBox = document.getElementById("library-animatable-box");
-        if (aEvent.animationName.startsWith("library-bookmark-animation")) {
-          animatableBox.setAttribute("fade", "true");
-        } else if (aEvent.animationName == "library-bookmark-fade") {
-          animatableBox.removeEventListener("animationend", this);
-          animatableBox.removeAttribute("animate");
-          animatableBox.removeAttribute("fade");
-          let libraryButton = document.getElementById("library-button");
-          // Put the 'fill' back in the normal icon.
-          libraryButton.removeAttribute("animate");
-          gNavToolbox.removeAttribute("animate");
-        }
-        break;
-      }
       case "mousemove":
         clearTimeout(this._autoCloseTimer);
         // The autoclose timer is not disabled on generic mouseout
         // because the user may not have actually interacted with the popup.
         break;
       case "popuphidden": {
         clearTimeout(this._autoCloseTimer);
         if (aEvent.originalTarget == this.panel) {
@@ -115,17 +100,16 @@ var StarUI = {
           let guidsForRemoval = this._itemGuids;
           this._itemGuids = null;
           this._itemIdsMap = null;
 
           if (this._batching) {
             this.endBatch();
           }
 
-          let libraryButton;
           if (removeBookmarksOnPopupHidden && guidsForRemoval) {
             if (this._isNewBookmark) {
               if (!PlacesUIUtils.useAsyncTransactions) {
                 PlacesUtils.transactionManager.undoTransaction();
                 break;
               }
               PlacesTransactions.undo().catch(Cu.reportError);
               break;
@@ -139,42 +123,18 @@ var StarUI = {
                   PlacesUtils.transactionManager.doTransaction(txn);
                 }
               }
               break;
             }
 
             PlacesTransactions.Remove(guidsForRemoval)
                               .transact().catch(Cu.reportError);
-          } else if (this._isNewBookmark &&
-                     Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") &&
-                     (libraryButton = document.getElementById("library-button")) &&
-                     libraryButton.getAttribute("cui-areatype") != "menu-panel" &&
-                     libraryButton.getAttribute("overflowedItem") != "true" &&
-                     libraryButton.closest("#nav-bar")) {
-            let animatableBox = document.getElementById("library-animatable-box");
-            let navBar = document.getElementById("nav-bar");
-            let libraryIcon = document.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
-            let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
-            let iconBounds = dwu.getBoundsWithoutFlushing(libraryIcon);
-            let libraryBounds = dwu.getBoundsWithoutFlushing(libraryButton);
-
-            animatableBox.style.setProperty("--library-button-y", libraryBounds.y + "px");
-            animatableBox.style.setProperty("--library-button-height", libraryBounds.height + "px");
-            animatableBox.style.setProperty("--library-icon-x", iconBounds.x + "px");
-            if (navBar.hasAttribute("brighttext")) {
-              animatableBox.setAttribute("brighttext", "true");
-            } else {
-              animatableBox.removeAttribute("brighttext");
-            }
-            animatableBox.removeAttribute("fade");
-            gNavToolbox.setAttribute("animate", "bookmark");
-            libraryButton.setAttribute("animate", "bookmark");
-            animatableBox.setAttribute("animate", "bookmark");
-            animatableBox.addEventListener("animationend", this);
+          } else if (this._isNewBookmark) {
+            LibraryUI.triggerLibraryAnimation("bookmark");
           }
         }
         break;
       }
       case "keypress":
         clearTimeout(this._autoCloseTimer);
         this._autoCloseTimerEnabled = false;
 
@@ -1540,16 +1500,78 @@ var RecentBookmarksMenuUI = {
   onEndUpdateBatch() {},
   onItemAdded() {},
   onItemChanged() {},
   onItemVisited() {},
   onItemMoved() {},
 }
 
 /**
+ * Handles the Library button in the toolbar.
+ */
+var LibraryUI = {
+  triggerLibraryAnimation(animation) {
+    if (!this.hasOwnProperty("COSMETIC_ANIMATIONS_ENABLED")) {
+      XPCOMUtils.defineLazyPreferenceGetter(this, "COSMETIC_ANIMATIONS_ENABLED",
+        "toolkit.cosmeticAnimations.enabled", true);
+    }
+
+    let libraryButton = document.getElementById("library-button");
+    if (!libraryButton ||
+        libraryButton.getAttribute("cui-areatype") == "menu-panel" ||
+        libraryButton.getAttribute("overflowedItem") == "true" ||
+        !libraryButton.closest("#nav-bar") ||
+        !this.COSMETIC_ANIMATIONS_ENABLED) {
+      return;
+    }
+    let animatableBox = document.getElementById("library-animatable-box");
+    let navBar = document.getElementById("nav-bar");
+    let libraryIcon = document.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
+    let dwu = window.getInterface(Ci.nsIDOMWindowUtils);
+    let iconBounds = dwu.getBoundsWithoutFlushing(libraryIcon);
+    let libraryBounds = dwu.getBoundsWithoutFlushing(libraryButton);
+
+    animatableBox.style.setProperty("--library-button-y", libraryBounds.y + "px");
+    animatableBox.style.setProperty("--library-button-height", libraryBounds.height + "px");
+    animatableBox.style.setProperty("--library-icon-x", iconBounds.x + "px");
+    if (navBar.hasAttribute("brighttext")) {
+      animatableBox.setAttribute("brighttext", "true");
+    } else {
+      animatableBox.removeAttribute("brighttext");
+    }
+    animatableBox.removeAttribute("fade");
+    gNavToolbox.setAttribute("animate", animation);
+    libraryButton.setAttribute("animate", animation);
+    animatableBox.setAttribute("animate", animation);
+    if (!this._libraryButtonAnimationEndListeners[animation]) {
+      this._libraryButtonAnimationEndListeners[animation] = event => {
+        this._libraryButtonAnimationEndListener(event, animation);
+      }
+    }
+    animatableBox.addEventListener("animationend", this._libraryButtonAnimationEndListeners[animation]);
+  },
+
+  _libraryButtonAnimationEndListeners: {},
+  _libraryButtonAnimationEndListener(aEvent, animation) {
+    let animatableBox = document.getElementById("library-animatable-box");
+    if (aEvent.animationName.startsWith(`library-${animation}-animation`)) {
+      animatableBox.setAttribute("fade", "true");
+    } else if (aEvent.animationName == `library-${animation}-fade`) {
+      animatableBox.removeEventListener("animationend", LibraryUI._libraryButtonAnimationEndListeners[animation]);
+      animatableBox.removeAttribute("animate");
+      animatableBox.removeAttribute("fade");
+      let libraryButton = document.getElementById("library-button");
+      // Put the 'fill' back in the normal icon.
+      libraryButton.removeAttribute("animate");
+      gNavToolbox.removeAttribute("animate");
+    }
+  },
+};
+
+/**
  * Handles the bookmarks menu-button in the toolbar.
  */
 
 var BookmarkingUI = {
   STAR_ID: "star-button",
   BOOKMARK_BUTTON_ID: "bookmarks-menu-button",
   BOOKMARK_BUTTON_SHORTCUT: "addBookmarkAsKb",
   get button() {
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -153,17 +153,17 @@ var PocketPageAction = {
           PocketPageAction.urlbarNode = urlbarNode;
           PocketPageAction.urlbarNode.setAttribute("open", "true");
           if (Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
             PocketPageAction.urlbarNode.setAttribute("animate", "true");
           }
         },
         onIframeHiding(iframe, panel) {
           if (iframe.getAttribute("itemAdded") == "true") {
-            PocketPageAction.startLibraryAnimation(iframe.ownerDocument);
+            iframe.ownerGlobal.LibraryUI.triggerLibraryAnimation("pocket");
           }
         },
         onIframeHidden(iframe, panel) {
           if (!PocketPageAction.urlbarNode) {
             return;
           }
           PocketPageAction.urlbarNode.removeAttribute("animate");
           PocketPageAction.urlbarNode.removeAttribute("open");
@@ -185,64 +185,16 @@ var PocketPageAction = {
       if (pocketButtonBox) {
         pocketButtonBox.remove();
       }
     }
 
     this.pageAction.remove();
     this.pageAction = null;
   },
-
-  startLibraryAnimation(doc) {
-    let libraryButton = doc.getElementById("library-button");
-    if (!Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled") ||
-        !libraryButton ||
-        libraryButton.getAttribute("cui-areatype") == "menu-panel" ||
-        libraryButton.getAttribute("overflowedItem") == "true" ||
-        !libraryButton.closest("#nav-bar")) {
-      return;
-    }
-
-    let animatableBox = doc.getElementById("library-animatable-box");
-    let navBar = doc.getElementById("nav-bar");
-    let libraryIcon = doc.getAnonymousElementByAttribute(libraryButton, "class", "toolbarbutton-icon");
-    let dwu = doc.defaultView.getInterface(Ci.nsIDOMWindowUtils);
-    let iconBounds = dwu.getBoundsWithoutFlushing(libraryIcon);
-    let libraryBounds = dwu.getBoundsWithoutFlushing(libraryButton);
-
-    animatableBox.style.setProperty("--library-button-y", libraryBounds.y + "px");
-    animatableBox.style.setProperty("--library-button-height", libraryBounds.height + "px");
-    animatableBox.style.setProperty("--library-icon-x", iconBounds.x + "px");
-    if (navBar.hasAttribute("brighttext")) {
-      animatableBox.setAttribute("brighttext", "true");
-    } else {
-      animatableBox.removeAttribute("brighttext");
-    }
-    animatableBox.removeAttribute("fade");
-    doc.defaultView.gNavToolbox.setAttribute("animate", "pocket");
-    libraryButton.setAttribute("animate", "pocket");
-    animatableBox.setAttribute("animate", "pocket");
-    animatableBox.addEventListener("animationend", PocketPageAction.onLibraryButtonAnimationEnd);
-  },
-
-  onLibraryButtonAnimationEnd(event) {
-    let doc = event.target.ownerDocument;
-    let libraryButton = doc.getElementById("library-button");
-    let animatableBox = doc.getElementById("library-animatable-box");
-    if (event.animationName.startsWith("library-pocket-animation")) {
-      animatableBox.setAttribute("fade", "true");
-    } else if (event.animationName == "library-pocket-fade") {
-      animatableBox.removeEventListener("animationend", PocketPageAction.onLibraryButtonAnimationEnd);
-      // Put the 'fill' back in the normal icon.
-      libraryButton.removeAttribute("animate");
-      animatableBox.removeAttribute("animate");
-      animatableBox.removeAttribute("fade");
-      event.target.ownerGlobal.gNavToolbox.removeAttribute("animate");
-    }
-  },
 };
 
 // PocketContextMenu
 // When the context menu is opened check if we need to build and enable pocket UI.
 var PocketContextMenu = {
   init() {
     Services.obs.addObserver(this, "on-build-contextmenu");
   },
--- a/browser/extensions/pocket/skin/shared/pocket.css
+++ b/browser/extensions/pocket/skin/shared/pocket.css
@@ -95,20 +95,16 @@
   width: 260px;
 }
 
 #pocket-button-box[animate="true"]:-moz-locale-dir(rtl) > #pocket-animatable-box > #pocket-animatable-image,
 #pocket-button[open="true"][animationsenabled][cui-areatype="toolbar"]:not([overflowedItem="true"]):-moz-locale-dir(rtl) > .toolbarbutton-animatable-box > .toolbarbutton-animatable-image {
   animation-name: pocket-animation-rtl;
 }
 
-#library-button[animate="pocket"] > .toolbarbutton-icon {
-  fill: transparent;
-}
-
 @keyframes library-pocket-animation {
   from {
     transform: translateX(0);
     fill: inherit;
   }
   25% {
     fill: inherit;
   }
@@ -146,46 +142,16 @@
   from {
     fill: #ef4056;
   }
   to {
     fill: inherit;
   }
 }
 
-.toolbarbutton-animatable-box[animate="pocket"] {
-  position: absolute;
-  overflow: hidden;
-  /* Position the sprite at the y-position of the library-button, then adjust
-     based on the size difference between half of the button height and half
-     of the sprite height. */
-  top: calc(var(--library-button-y) + var(--library-button-height) / 2 - 27px);
-  /* Since .toolbarbutton-icon uses a different width than the animatable box,
-     we need to set a margin relative to the difference in widths.
-     margin-left is used here even in RTL because the item is positioned using `left` */
-  left: calc(var(--library-icon-x) + (16px + 2 * var(--toolbarbutton-inner-padding) - 22px) / 2);
-  /* Set the min- and max- width and height of the box equal to that
-     of each frame of the SVG sprite. Setting the width and height via
-     the `width` and `height` CSS properties causes an assertion for
-     `inline-size less than zero: 'aContainingBlockISize >= 0'` (bug 1379332). */
-  min-width: 22px;
-  max-width: 22px;
-  /* Height of each frame within the SVG sprite. The sprite must have equal amount
-     of space above and below the icon to allow it to vertically center with the
-     sprite's icon on top of the toolbar icon when using position:absolute;. */
-  min-height: 54px;
-  max-height: 54px;
-  z-index: 2;
-}
-
-.toolbarbutton-animatable-box[animate="pocket"] > .toolbarbutton-animatable-image {
-  height: var(--toolbarbutton-height); /* Height must be equal to height of toolbarbutton padding-box */
-  min-height: 54px; /* Minimum height must be equal to the height of the SVG sprite */
-}
-
 .toolbarbutton-animatable-box[animate="pocket"] > .toolbarbutton-animatable-image {
   background-image: url("chrome://pocket-shared/skin/library-pocket-animation.svg");
   width: 1078px;
   animation-name: library-pocket-animation;
   animation-duration: 800ms;
   animation-timing-function: steps(48);
 }
 
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -453,21 +453,21 @@ toolbar[brighttext] .toolbarbutton-1 {
   from {
     fill: var(--toolbarbutton-icon-fill-attention);
   }
   to {
     fill: inherit;
   }
 }
 
-#library-button[animate="bookmark"] > .toolbarbutton-icon {
+#library-button[animate] > .toolbarbutton-icon {
   fill: transparent;
 }
 
-.toolbarbutton-animatable-box[animate="bookmark"] {
+.toolbarbutton-animatable-box[animate] {
   position: absolute;
   overflow: hidden;
   /* Position the sprite at the y-position of the library-button, then adjust
      based on the size difference between half of the button height and half
      of the sprite height. */
   top: calc(var(--library-button-y) + var(--library-button-height) / 2 - 27px);
   /* Set a margin relative to the difference in widths of the .toolbarbutton-icon
      and the .toolbar-animatable-box. This is correct even in RTL because the item
@@ -477,19 +477,22 @@ toolbar[brighttext] .toolbarbutton-1 {
      of each frame of the SVG sprite (must use min- and max- due to bug 1379332). */
   min-width: 22px;
   max-width: 22px;
   min-height: 54px;
   max-height: 54px;
   z-index: 2;
 }
 
-.toolbarbutton-animatable-box[animate="bookmark"] > .toolbarbutton-animatable-image {
+.toolbarbutton-animatable-box[animate] > .toolbarbutton-animatable-image {
   height: var(--toolbarbutton-height);
   min-height: 54px; /* Minimum height must be equal to the height of the SVG sprite */
+}
+
+.toolbarbutton-animatable-box[animate="bookmark"] > .toolbarbutton-animatable-image {
   background-image: url("chrome://browser/skin/library-bookmark-animation.svg");
   width: 1078px;
   animation-name: library-bookmark-animation;
   animation-duration: 800ms;
   animation-timing-function: steps(48);
   -moz-context-properties: fill, stroke;
   stroke: var(--toolbarbutton-icon-fill-attention);
 }