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
changeset 765463 06d779bec058c87b6d87a4dcea1e9499c5b7cefa
parent 753584 80ca8e45becbf8f38a06d94d14899947ed834c4d
push id102081
push userbmo:vivek3zero@gmail.com
push dateFri, 09 Mar 2018 20:13:53 +0000
reviewersjaws, mconley
bugs1397393
milestone60.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
@@ -15,8 +15,9 @@ support-files =
 [browser_ext_themes_separators.js]
 [browser_ext_themes_static_onUpdated.js]
 [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_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
@@ -16,15 +16,19 @@ add_task(async function test_alpha_accen
     files: {
       "image1.png": BACKGROUND,
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
+
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   let style = window.getComputedStyle(docEl);
 
   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");
 });
 
@@ -67,16 +72,20 @@ add_task(async function test_support_the
     files: {
       "image1.png": BACKGROUND,
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
+
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   let style = window.getComputedStyle(docEl);
 
   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();
 
@@ -107,16 +116,20 @@ add_task(async function test_lack_of_the
     files: {
       "image1.png": BACKGROUND,
     },
   });
 
   await extension.startup();
 
   let docEl = window.document.documentElement;
+
+  // wait for theme transition to end
+  await waitForTransition(docEl, "background-color");
+
   let style = window.getComputedStyle(docEl);
 
   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();
 
--- 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,40 @@ 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) {
     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);
   }
+
+  if (isLWT) {
+    // wait for theme transition to end
+    await waitForTransition(docEl, "background-color");
+  }
+
   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 +83,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 +147,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
@@ -29,18 +29,23 @@ add_task(async function test_support_LWT
   let docEl = window.document.documentElement;
   let style = window.getComputedStyle(docEl);
 
   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("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");
 });
 
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
@@ -37,19 +37,24 @@ add_task(async function test_support_too
   let toolbars = [...toolbox.querySelectorAll("toolbar:not(#TabsToolbar)")].filter(toolbar => {
     let bounds = toolbar.getBoundingClientRect();
     return bounds.width > 0 && bounds.height > 0;
   });
 
   info(`Checking toolbar colors for ${toolbars.length} toolbars.`);
   for (let toolbar of toolbars) {
     info(`Testing ${toolbar.id}`);
+
+    // wait for theme transition to end
+    await waitForTransition(toolbar, "background-color");
+
     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
@@ -48,8 +48,19 @@ function rgbToCSS(rgb) {
 function hexToCSS(hex) {
   return rgbToCSS(hexToRGB(hex));
 }
 
 function imageBufferFromDataURI(encodedImageData) {
   let decodedImageData = atob(encodedImageData);
   return Uint8Array.from(decodedImageData, byte => byte.charCodeAt(0)).buffer;
 }
+
+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();
+      }
+    });
+  });
+}