Bug 1540984: Pre-load theme header image and share across all windows. r=aswan

For large header images, built-in memory caching does not work correctly, and
we wind up seeing a flicker any time a new window is opened or becomes active.
This patch caches the header image in an Image element at startup, and uses
that element as the backing for the header image in all browser windows.

Differential Revision: https://phabricator.services.mozilla.com/D29554

--HG--
extra : source : 6159d01c9341088edcabb318259c8ce6201c61ac
extra : amend_source : c43c543fd142f11b8439a6011a6ffe0f610e1015
This commit is contained in:
Kris Maglione 2019-05-01 12:43:25 -07:00
Родитель dce072b19c
Коммит 154872a672
8 изменённых файлов: 66 добавлений и 24 удалений

Просмотреть файл

@ -28,8 +28,7 @@ add_task(async function test_support_theme_frame() {
"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}`);
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/face.png`);
Assert.equal(style.backgroundColor, "rgb(" + FRAME_COLOR.join(", ") + ")",
"Expected correct background color");
Assert.equal(style.color, "rgb(" + TAB_TEXT_COLOR.join(", ") + ")",

Просмотреть файл

@ -25,7 +25,7 @@ function validateTheme(backgroundImage, accentColor, textColor, isLWT) {
"LWT text color attribute should be set");
}
Assert.ok(style.backgroundImage.includes(backgroundImage), "Expected correct background image");
checkThemeHeaderImage(window, backgroundImage);
if (accentColor.startsWith("#")) {
accentColor = hexToRGB(accentColor);
}
@ -75,7 +75,7 @@ add_task(async function test_dynamic_theme_updates() {
await extension.awaitMessage("theme-updated");
validateTheme("image1.png", ACCENT_COLOR_1, TEXT_COLOR_1, true);
validateTheme(`moz-extension://${extension.uuid}/image1.png`, ACCENT_COLOR_1, TEXT_COLOR_1, true);
// Check with the LWT aliases (to update on Firefox 69, because the
// LWT aliases are going to be removed).
@ -91,7 +91,7 @@ add_task(async function test_dynamic_theme_updates() {
await extension.awaitMessage("theme-updated");
validateTheme("image2.png", ACCENT_COLOR_2, TEXT_COLOR_2, true);
validateTheme(`moz-extension://${extension.uuid}/image2.png`, ACCENT_COLOR_2, TEXT_COLOR_2, true);
extension.sendMessage("reset-theme");

Просмотреть файл

@ -28,7 +28,7 @@ add_task(async function test_support_LWT_properties() {
Assert.equal(docEl.getAttribute("lwthemetextcolor"), "bright",
"LWT text color attribute should be set");
Assert.ok(style.backgroundImage.includes("image1.png"), "Expected background image");
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/image1.png`);
Assert.equal(style.backgroundColor, "rgb(" + hexToRGB(ACCENT_COLOR).join(", ") + ")",
"Expected correct background color");
Assert.equal(style.color, "rgb(" + hexToRGB(TEXT_COLOR).join(", ") + ")",

Просмотреть файл

@ -34,10 +34,9 @@ add_task(async function test_support_backgrounds_position() {
let toolboxCS = window.getComputedStyle(toolbox);
let rootCS = window.getComputedStyle(docEl);
let rootBgImage = rootCS.backgroundImage.split(",")[0].trim();
let bgImage = toolboxCS.backgroundImage.split(",")[0].trim();
Assert.ok(rootBgImage.includes("face1.png"),
`The backgroundImage should use face1.png. Actual value is: ${rootBgImage}`);
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/face1.png`);
Assert.equal(toolboxCS.backgroundImage, Array(3).fill(bgImage).join(", "),
"The backgroundImage should use face2.png three times.");
Assert.equal(toolboxCS.backgroundPosition, "0% 0%, 50% 0%, 100% 100%",
@ -95,10 +94,8 @@ add_task(async function test_support_backgrounds_repeat() {
let rootCS = window.getComputedStyle(docEl);
let toolboxCS = window.getComputedStyle(toolbox);
let bgImage = rootCS.backgroundImage.split(",")[0].trim();
Assert.ok(bgImage.includes("face0.png"),
`The backgroundImage should use face.png. Actual value is: ${bgImage}`);
Assert.equal([1, 2, 3].map(num => bgImage.replace(/face[\d]*/, `face${num}`)).join(", "),
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/face0.png`);
Assert.equal([1, 2, 3].map(num => `url("moz-extension://${extension.uuid}/face${num}.png")`).join(", "),
toolboxCS.backgroundImage, "The backgroundImage should use face.png three times.");
Assert.equal(rootCS.backgroundPosition, "100% 0%",
"The backgroundPosition should use the default value for root.");
@ -146,9 +143,7 @@ add_task(async function test_additional_images_check() {
let rootCS = window.getComputedStyle(docEl);
let toolboxCS = window.getComputedStyle(toolbox);
let bgImage = rootCS.backgroundImage.split(",")[0];
Assert.ok(bgImage.includes("face.png"),
`The backgroundImage should use face.png. Actual value is: ${bgImage}`);
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/face.png`);
Assert.equal("none", toolboxCS.backgroundImage,
"The backgroundImage should not use face.png.");
Assert.equal(rootCS.backgroundPosition, "100% 0%",

Просмотреть файл

@ -24,22 +24,20 @@ add_task(async function test_multiple_windows() {
await extension.startup();
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");
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/image1.png`);
// Now we'll open a new window to see if the theme is also applied there.
let window2 = await BrowserTestUtils.openNewBrowserWindow();
docEl = window2.document.documentElement;
style = window2.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");
checkThemeHeaderImage(window, `moz-extension://${extension.uuid}/image1.png`);
await BrowserTestUtils.closeWindow(window2);
await extension.unload();

Просмотреть файл

@ -1,6 +1,6 @@
/* exported ACCENT_COLOR, BACKGROUND, ENCODED_IMAGE_DATA, FRAME_COLOR, TAB_TEXT_COLOR,
TEXT_COLOR, TAB_BACKGROUND_TEXT_COLOR, imageBufferFromDataURI, hexToCSS, hexToRGB, testBorderColor,
waitForTransition */
waitForTransition, checkThemeHeaderImage */
"use strict";
@ -82,3 +82,20 @@ function testBorderColor(element, expected) {
hexToCSS(expected),
"Element bottom border color should be set.");
}
function checkThemeHeaderImage(window, expectedURL) {
const {LightweightThemeManager} = ChromeUtils.import("resource://gre/modules/LightweightThemeManager.jsm");
let root = window.document.documentElement;
if (expectedURL === "none") {
Assert.equal(window.getComputedStyle(root).backgroundImage,
"none", "Should have no background image");
} else {
Assert.equal(window.getComputedStyle(root).backgroundImage,
"-moz-element(#lwt-header-image)",
"Should have -moz-element background image");
Assert.equal(LightweightThemeManager.themeData.theme.headerImage.src,
expectedURL, "Theme image has expected source");
}
}

Просмотреть файл

@ -220,7 +220,15 @@ LightweightThemeConsumer.prototype = {
}
this._setExperiment(active, themeData.experiment, theme.experimental);
_setImage(root, active, "--lwt-header-image", theme.headerURL);
if (theme.headerImage) {
this._doc.mozSetImageElement("lwt-header-image", theme.headerImage);
root.style.setProperty("--lwt-header-image", "-moz-element(#lwt-header-image)");
} else {
this._doc.mozSetImageElement("lwt-header-image", null);
root.style.removeProperty("--lwt-header-image");
}
_setImage(root, active, "--lwt-additional-images", theme.additionalBackgrounds);
_setProperties(root, active, theme);

Просмотреть файл

@ -6,14 +6,39 @@
var EXPORTED_SYMBOLS = ["LightweightThemeManager"];
// Holds optional fallback theme data that will be returned when no data for an
// active theme can be found. This the case for WebExtension Themes, for example.
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "hiddenWindow", () => {
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
let browser = Services.appShell.createWindowlessBrowser(true);
let principal = Services.scriptSecurityManager.getSystemPrincipal();
browser.docShell.createAboutBlankContentViewer(principal);
Services.obs.addObserver(() => { browser.close(); }, "xpcom-will-shutdown");
return browser.document.ownerGlobal;
});
if (AppConstants.DEBUG) {
void hiddenWindow;
}
var _fallbackThemeData = null;
var LightweightThemeManager = {
set fallbackThemeData(data) {
if (data && Object.getOwnPropertyNames(data).length) {
_fallbackThemeData = Object.assign({}, data);
for (let key of ["theme", "darkTheme"]) {
let data = _fallbackThemeData[key] || null;
if (data && typeof data.headerURL === "string") {
data.headerImage = new hiddenWindow.Image();
data.headerImage.src = data.headerURL;
}
}
} else {
_fallbackThemeData = null;
}