Bug 1397393 - Make transitioning between 2 themes with a dynamic theme smooth r?jaws,mconley draft
authorVivek Dhingra <vivek3zero@gmail.com>
Sat, 10 Feb 2018 17:10:05 -0500 (2018-02-10)
changeset 767404 1e31a8735badb8e7c96c45ed564fa5377458d511
parent 766212 d6957f004e9cc3d7408ac3a8f2b49ff97556e27f
push id102589
push userbmo:vivek3zero@gmail.com
push dateWed, 14 Mar 2018 15:14:54 +0000 (2018-03-14)
reviewersjaws, mconley
bugs1397393
milestone61.0a1
Bug 1397393 - Make transitioning between 2 themes with a dynamic theme smooth r?jaws,mconley MozReview-Commit-ID: DlEXkV0MdZ8
browser/themes/shared/browser.inc.css
toolkit/components/extensions/test/browser/browser.ini
toolkit/components/extensions/test/browser/browser_ext_themes_alpha_accentcolor.js
toolkit/components/extensions/test/browser/browser_ext_themes_chromeparity.js
toolkit/components/extensions/test/browser/browser_ext_themes_dynamic_updates.js
toolkit/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
toolkit/components/extensions/test/browser/browser_ext_themes_theme_transition.js
toolkit/components/extensions/test/browser/browser_ext_themes_toolbars.js
toolkit/components/extensions/test/browser/head.js
--- a/browser/themes/shared/browser.inc.css
+++ b/browser/themes/shared/browser.inc.css
@@ -1,44 +1,53 @@
 /* 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/. */
 
 %include downloads/indicator.inc.css
 
 %filter substitution
 %define navbarTabsShadowSize 1px
+%define themeTransition background-color 0.1s cubic-bezier(.17,.67,.83,.67)
 
 :root {
   /* Note: Setting this to 0 (without px) breaks CSS calculations for OSX. */
   --space-above-tabbar: 0px;
 }
 
 :root[extradragspace][tabsintitlebar]:not([inFullscreen]) {
   --space-above-tabbar: 8px;
 }
 
+:root:-moz-lwtheme {
+  transition: @themeTransition@;
+}
+
 /* Toolbar / content area border */
 
 #navigator-toolbox::after {
   content: "";
   display: -moz-box;
   border-bottom: 1px solid var(--toolbox-border-bottom-color);
 }
 
 :root[customizing] #navigator-toolbox::after {
   border-bottom-style: none;
 }
 
+ #nav-bar:-moz-lwtheme {
+  transition: @themeTransition@;
+}
+
 /* Bookmark toolbar */
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar) {
   overflow: -moz-hidden-unscrollable;
   max-height: 4em;
-  transition: min-height 170ms ease-out, max-height 170ms ease-out;
+  transition: min-height 170ms ease-out, max-height 170ms ease-out, @themeTransition@;
   padding: 0 6px 2px;
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar)[collapsed=true] {
   min-height: 0.1px;
   max-height: 0;
   transition: min-height 170ms ease-out, max-height 170ms ease-out, visibility 170ms linear;
 }
--- a/toolkit/components/extensions/test/browser/browser.ini
+++ b/toolkit/components/extensions/test/browser/browser.ini
@@ -18,8 +18,9 @@ support-files =
 [browser_ext_themes_tab_loading.js]
 [browser_ext_themes_tab_text.js]
 [browser_ext_themes_toolbar_fields.js]
 [browser_ext_themes_toolbars.js]
 [browser_ext_themes_toolbarbutton_icons.js]
 [browser_ext_themes_toolbarbutton_colors.js]
 [browser_ext_themes_arrowpanels.js]
 [browser_ext_themes_tab_selected.js]
+[browser_ext_themes_theme_transition.js]
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_alpha_accentcolor.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_alpha_accentcolor.js
@@ -18,13 +18,15 @@ add_task(async function test_alpha_accen
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
   let style = window.getComputedStyle(docEl);
 
+  await new Promise(resolve => setTimeout(resolve, 100));
+
   Assert.equal(style.backgroundColor, "rgb(230, 128, 0)",
                "Window background color should be opaque");
 
   await extension.unload();
 });
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_chromeparity.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_chromeparity.js
@@ -25,25 +25,30 @@ add_task(async function test_support_the
       "face.png": imageBufferFromDataURI(ENCODED_IMAGE_DATA),
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
 
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   Assert.ok(docEl.hasAttribute("lwtheme"), "LWT attribute should be set");
   Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
                "LWT text color attribute should be set");
 
   let style = window.getComputedStyle(docEl);
   Assert.ok(style.backgroundImage.includes("face.png"),
             `The backgroundImage should use face.png. Actual value is: ${style.backgroundImage}`);
+
   Assert.equal(style.backgroundColor, "rgb(" + FRAME_COLOR.join(", ") + ")",
                "Expected correct background color");
+
   Assert.equal(style.color, "rgba(" + TAB_TEXT_COLOR.join(", ") + ")",
                "Expected correct text color");
 
   await extension.unload();
 
   Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
 });
 
@@ -69,22 +74,27 @@ add_task(async function test_support_the
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
   let style = window.getComputedStyle(docEl);
 
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   Assert.equal(style.backgroundColor, "rgb(" + FRAME_COLOR.join(", ") + ")",
                "Window background is set to the colors.frame property");
 
   // Now we'll open a new window to see if the inactive browser accent color changed
   let window2 = await BrowserTestUtils.openNewBrowserWindow();
 
+  await waitForTransition(docEl, "background-color");
+
   Assert.equal(style.backgroundColor, "rgb(" + FRAME_COLOR_INACTIVE.join(", ") + ")",
                `Inactive window background color should be ${FRAME_COLOR_INACTIVE}`);
 
   await BrowserTestUtils.closeWindow(window2);
   await extension.unload();
 
   Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
 });
@@ -109,16 +119,19 @@ add_task(async function test_lack_of_the
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
   let style = window.getComputedStyle(docEl);
 
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   Assert.equal(style.backgroundColor, "rgb(" + FRAME_COLOR.join(", ") + ")",
                "Window background is set to the colors.frame property");
 
   // Now we'll open a new window to make sure the inactive browser accent color stayed the same
   let window2 = await BrowserTestUtils.openNewBrowserWindow();
 
   Assert.equal(style.backgroundColor, "rgb(" + FRAME_COLOR.join(", ") + ")",
                "Inactive window background should not change if colors.frame_inactive isn't set");
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_dynamic_updates.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_dynamic_updates.js
@@ -10,33 +10,37 @@ const BACKGROUND_2 = "data:image/png;bas
 const ACCENT_COLOR_2 = "#03fe03";
 const TEXT_COLOR_2 = "#0ef325";
 
 function hexToRGB(hex) {
   hex = parseInt((hex.indexOf("#") > -1 ? hex.substring(1) : hex), 16);
   return "rgb(" + [hex >> 16, (hex & 0x00FF00) >> 8, (hex & 0x0000FF)].join(", ") + ")";
 }
 
-function validateTheme(backgroundImage, accentColor, textColor, isLWT) {
+// Themes are expected to still be in transition when function is called
+async function validateTheme(backgroundImage, accentColor, textColor, isLWT) {
   let docEl = window.document.documentElement;
   let style = window.getComputedStyle(docEl);
 
   if (isLWT) {
+    // wait for theme transition to end
+    await waitForTransition(docEl, "background-color");
     Assert.ok(docEl.hasAttribute("lwtheme"), "LWT attribute should be set");
     Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
                  "LWT text color attribute should be set");
   }
 
   Assert.ok(style.backgroundImage.includes(backgroundImage), "Expected correct background image");
   if (accentColor.startsWith("#")) {
     accentColor = hexToRGB(accentColor);
   }
   if (textColor.startsWith("#")) {
     textColor = hexToRGB(textColor);
   }
+
   Assert.equal(style.backgroundColor, accentColor, "Expected correct accent color");
   Assert.equal(style.color, textColor, "Expected correct text color");
 }
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.webextensions.themes.enabled", true]],
   });
@@ -76,38 +80,38 @@ add_task(async function test_dynamic_the
     "colors": {
       "accentcolor": ACCENT_COLOR_1,
       "textcolor": TEXT_COLOR_1,
     },
   });
 
   await extension.awaitMessage("theme-updated");
 
-  validateTheme("image1.png", ACCENT_COLOR_1, TEXT_COLOR_1, true);
+  await validateTheme("image1.png", ACCENT_COLOR_1, TEXT_COLOR_1, true);
 
   extension.sendMessage("update-theme", {
     "images": {
       "headerURL": "image2.png",
     },
     "colors": {
       "accentcolor": ACCENT_COLOR_2,
       "textcolor": TEXT_COLOR_2,
     },
   });
 
   await extension.awaitMessage("theme-updated");
 
-  validateTheme("image2.png", ACCENT_COLOR_2, TEXT_COLOR_2, true);
+  await validateTheme("image2.png", ACCENT_COLOR_2, TEXT_COLOR_2, true);
 
   extension.sendMessage("reset-theme");
 
   await extension.awaitMessage("theme-reset");
 
   let {backgroundImage, backgroundColor, color} = defaultStyle;
-  validateTheme(backgroundImage, backgroundColor, color, false);
+  await validateTheme(backgroundImage, backgroundColor, color, false);
 
   await extension.unload();
 
   let docEl = window.document.documentElement;
   Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
 });
 
 add_task(async function test_dynamic_theme_updates_with_data_url() {
@@ -140,36 +144,36 @@ add_task(async function test_dynamic_the
     "colors": {
       "accentcolor": ACCENT_COLOR_1,
       "textcolor": TEXT_COLOR_1,
     },
   });
 
   await extension.awaitMessage("theme-updated");
 
-  validateTheme(BACKGROUND_1, ACCENT_COLOR_1, TEXT_COLOR_1, true);
+  await validateTheme(BACKGROUND_1, ACCENT_COLOR_1, TEXT_COLOR_1, true);
 
   extension.sendMessage("update-theme", {
     "images": {
       "headerURL": BACKGROUND_2,
     },
     "colors": {
       "accentcolor": ACCENT_COLOR_2,
       "textcolor": TEXT_COLOR_2,
     },
   });
 
   await extension.awaitMessage("theme-updated");
 
-  validateTheme(BACKGROUND_2, ACCENT_COLOR_2, TEXT_COLOR_2, true);
+  await validateTheme(BACKGROUND_2, ACCENT_COLOR_2, TEXT_COLOR_2, true);
 
   extension.sendMessage("reset-theme");
 
   await extension.awaitMessage("theme-reset");
 
   let {backgroundImage, backgroundColor, color} = defaultStyle;
-  validateTheme(backgroundImage, backgroundColor, color, false);
+  await validateTheme(backgroundImage, backgroundColor, color, false);
 
   await extension.unload();
 
   let docEl = window.document.documentElement;
   Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
 });
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_lwtsupport.js
@@ -30,18 +30,23 @@ add_task(async function test_support_LWT
   let style = window.getComputedStyle(docEl);
 
   Assert.ok(docEl.hasAttribute("lwtheme"), "LWT attribute should be set");
   Assert.ok(docEl.hasAttribute("lwtheme-image"), "LWT image attribute should be set");
   Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
                "LWT text color attribute should be set");
 
   Assert.ok(style.backgroundImage.includes("image1.png"), "Expected background image");
+
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   Assert.equal(style.backgroundColor, "rgb(" + hexToRGB(ACCENT_COLOR).join(", ") + ")",
                "Expected correct background color");
+
   Assert.equal(style.color, "rgb(" + hexToRGB(TEXT_COLOR).join(", ") + ")",
                "Expected correct text color");
 
   await extension.unload();
 
   Assert.ok(!docEl.hasAttribute("lwtheme"), "LWT attribute should not be set");
   Assert.ok(!docEl.hasAttribute("lwtheme-image"), "LWT image attribute should not be set");
 });
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_theme_transition.js
@@ -0,0 +1,66 @@
+"use strict";
+
+// This test checks whether the applied theme transition effects are applied
+// correctly.
+
+add_task(async function test_theme_transition_effects() {
+  const ACCENT_COLOR = "#aaf442";
+  const TOOLBAR = "#f27489";
+  const TEXT_COLOR = "#000000";
+  const TRANSITION_PROPERTY = "background-color";
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "theme": {
+        "images": {
+          "headerURL": "image1.png",
+        },
+        "colors": {
+          "accentcolor": ACCENT_COLOR,
+          "textcolor": TEXT_COLOR,
+          "toolbar": TOOLBAR,
+          "toolbar_text": TEXT_COLOR,
+        },
+      },
+    },
+    files: {
+      "image1.png": BACKGROUND,
+    },
+
+  });
+
+  await extension.startup();
+
+  // check if transition effect is set for root, which affects transition for
+  // accent color
+  let docEl = window.document.documentElement;
+  let rootCS = window.getComputedStyle(docEl);
+
+  Assert.equal(
+    rootCS.getPropertyValue("transition-property"),
+    TRANSITION_PROPERTY,
+    "Transition property set for root"
+  );
+
+  // now check transition effect for toolbars
+  let navbar = document.querySelector("#nav-bar");
+  let navbarCS = window.getComputedStyle(navbar);
+
+  Assert.equal(
+    navbarCS.getPropertyValue("transition-property").includes(TRANSITION_PROPERTY),
+    true,
+    "Transition property set for #nav-bar"
+  );
+
+  let bookmarksBar = document.querySelector("#PersonalToolbar");
+  bookmarksBar.setAttribute("collapsed", "false");
+  let bookmarksBarCS = window.getComputedStyle(bookmarksBar);
+
+  Assert.equal(
+    bookmarksBarCS.getPropertyValue("transition-property").includes(TRANSITION_PROPERTY),
+    true,
+    "Transition property set for #PersonalToolbar"
+  );
+
+  await extension.unload();
+});
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_toolbars.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_toolbars.js
@@ -28,22 +28,27 @@ add_task(async function test_support_too
   await extension.startup();
 
   let toolbox = document.querySelector("#navigator-toolbox");
   let toolbars = [...toolbox.querySelectorAll("toolbar:not(#TabsToolbar)")].filter(toolbar => {
     let bounds = toolbar.getBoundingClientRect();
     return bounds.width > 0 && bounds.height > 0;
   });
 
+  // Wait for theme transition to end for one of the toolbars
+  await waitForTransition(toolbars[toolbars.length - 1], "background-color");
+
   info(`Checking toolbar colors for ${toolbars.length} toolbars.`);
   for (let toolbar of toolbars) {
     info(`Testing ${toolbar.id}`);
+
     Assert.equal(window.getComputedStyle(toolbar).backgroundColor,
                  hexToCSS(TOOLBAR_COLOR),
                  "Toolbar background color should be set.");
+
     Assert.equal(window.getComputedStyle(toolbar).color,
                  hexToCSS(TOOLBAR_TEXT_COLOR),
                  "Toolbar text color should be set.");
   }
 
   info("Checking selected tab colors");
   let selectedTab = document.querySelector(".tabbrowser-tab[selected]");
   Assert.equal(window.getComputedStyle(selectedTab).color,
--- a/toolkit/components/extensions/test/browser/head.js
+++ b/toolkit/components/extensions/test/browser/head.js
@@ -1,10 +1,11 @@
 /* exported ACCENT_COLOR, BACKGROUND, ENCODED_IMAGE_DATA, FRAME_COLOR, TAB_TEXT_COLOR,
-   TEXT_COLOR, TAB_BACKGROUND_TEXT_COLOR, imageBufferFromDataURI, hexToCSS, hexToRGB, testBorderColor */
+   TEXT_COLOR, TAB_BACKGROUND_TEXT_COLOR, imageBufferFromDataURI, hexToCSS, hexToRGB, testBorderColor
+   TEXT_COLOR, BACKGROUND_TAB_TEXT_COLOR, imageBufferFromDataURI, hexToCSS, hexToRGB, waitForTransition */
 
 "use strict";
 
 const BACKGROUND = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0" +
   "DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
 const ENCODED_IMAGE_DATA = "iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0h" +
   "STQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAdhwAAHYcBj+XxZQAAB5dJREFUSMd" +
   "91vmTlEcZB/Bvd7/vO+/ce83O3gfLDUsC4VgIghBUEo2GM9GCFTaQBEISA1qIEVNQ4aggJDGIgAGTlFUKKcqKQpVHaQyny7FrCMiywp4ze+/Mzs67M/P" +
@@ -64,8 +65,19 @@ function testBorderColor(element, expect
                "Element right border color should be set.");
   Assert.equal(computedStyle.borderTopColor,
                hexToCSS(expected),
                "Element top border color should be set.");
   Assert.equal(computedStyle.borderBottomColor,
                hexToCSS(expected),
                "Element bottom border color should be set.");
 }
+
+function waitForTransition(doc, property) {
+  return new Promise(resolve => {
+    let listener = doc.addEventListener("transitionend", e => {
+      if (e.target == doc && e.propertyName == property) {
+        doc.removeEventListener("transitionend", listener);
+        resolve();
+      }
+    });
+  });
+}