Bug 1233127 - Use createProcessingIntruction instead of addon-sdk to load stylesheets in tools;r=pbrosset

This commit is contained in:
Brian Grinstead 2015-12-17 17:00:28 -08:00
parent 2fe7e9c7a5
commit 29fa8cae1d
3 changed files with 113 additions and 80 deletions

View File

@ -2,60 +2,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test for dynamically registering and unregistering themes
const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/framework/test/";
var toolbox;
function test()
{
gBrowser.selectedTab = gBrowser.addTab();
let target = TargetFactory.forTab(gBrowser.selectedTab);
add_task(function* themeRegistration() {
let tab = yield addTab("data:text/html,test");
let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target);
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
gDevTools.showToolbox(target).then(testRegister);
}, true);
let themeId = yield new Promise(resolve => {
gDevTools.once("theme-registered", (e, themeId) => {
resolve(themeId);
});
content.location = "data:text/html,test for dynamically registering and unregistering themes";
}
function testRegister(aToolbox)
{
toolbox = aToolbox
gDevTools.once("theme-registered", themeRegistered);
gDevTools.registerTheme({
id: "test-theme",
label: "Test theme",
stylesheets: [CHROME_URL + "doc_theme.css"],
classList: ["theme-test"],
gDevTools.registerTheme({
id: "test-theme",
label: "Test theme",
stylesheets: [CHROME_URL + "doc_theme.css"],
classList: ["theme-test"],
});
});
}
function themeRegistered(event, themeId)
{
is(themeId, "test-theme", "theme-registered event handler sent theme id");
ok(gDevTools.getThemeDefinitionMap().has(themeId), "theme added to map");
});
// Test that new theme appears in the Options panel
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "options").then(() => {
let panel = toolbox.getCurrentPanel();
let doc = panel.panelWin.frameElement.contentDocument;
let themeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
add_task(function* themeInOptionsPanel() {
ok(themeOption, "new theme exists in the Options panel");
yield toolbox.selectTool("options");
// Apply the new theme.
applyTheme();
});
}
function applyTheme()
{
let panel = toolbox.getCurrentPanel();
let panelWin = toolbox.getCurrentPanel().panelWin;
let doc = panelWin.frameElement.contentDocument;
let themeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
ok(themeOption, "new theme exists in the Options panel");
let testThemeOption = doc.querySelector("#devtools-theme-box > radio[value=test-theme]");
let lightThemeOption = doc.querySelector("#devtools-theme-box > radio[value=light]");
@ -65,24 +50,26 @@ function applyTheme()
// Select test theme.
testThemeOption.click();
info("Waiting for theme to finish loading");
yield once(panelWin, "theme-switch-complete");
color = panelWin.getComputedStyle(testThemeOption).color;
is(color, "rgb(255, 0, 0)", "style applied");
// Select light theme
lightThemeOption.click();
info("Waiting for theme to finish loading");
yield once(panelWin, "theme-switch-complete");
color = panelWin.getComputedStyle(testThemeOption).color;
isnot(color, "rgb(255, 0, 0)", "style unapplied");
// Select test theme again.
testThemeOption.click();
});
// Then unregister the test theme.
testUnregister();
}
function testUnregister()
{
add_task(function* themeUnregistration() {
gDevTools.unregisterTheme("test-theme");
ok(!gDevTools.getThemeDefinitionMap().has("test-theme"), "theme removed from map");
@ -94,20 +81,9 @@ function testUnregister()
// The default light theme must be selected now.
is(themeBox.selectedItem, themeBox.querySelector("[value=light]"),
"theme light must be selected");
});
// Make sure the tab-attaching process is done before we destroy the toolbox.
let target = TargetFactory.forTab(gBrowser.selectedTab);
let actor = target.activeTab.actor;
target.client.attachTab(actor, (response) => {
cleanup();
});
}
function cleanup()
{
toolbox.destroy().then(function() {
toolbox = null;
gBrowser.removeCurrentTab();
finish();
});
}
add_task(function* cleanup() {
yield toolbox.destroy();
toolbox = null;
});

View File

@ -7,7 +7,8 @@ var toolbox;
add_task(function*() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = yield gDevTools.showToolbox(target);
let root = toolbox.frame.contentDocument.documentElement;
let doc = toolbox.frame.contentDocument;
let root = doc.documentElement;
let platform = root.getAttribute("platform");
let expectedPlatform = getPlatform();
@ -15,7 +16,24 @@ add_task(function*() {
let theme = Services.prefs.getCharPref("devtools.theme");
let className = "theme-" + theme;
ok(root.classList.contains(className), ":root has " + className + " class (current theme)");
ok(root.classList.contains(className),
":root has " + className + " class (current theme)");
// Convert the xpath result into an array of strings
// like `href="{URL}" type="text/css"`
let sheetsIterator = doc.evaluate("processing-instruction('xml-stylesheet')",
doc, null, XPathResult.ANY_TYPE, null);
let sheetsInDOM = [];
let sheet;
while (sheet = sheetsIterator.iterateNext()) {
sheetsInDOM.push(sheet.data);
}
let sheetsFromTheme = gDevTools.getThemeDefinition(theme).stylesheets;
info ("Checking for existence of " + sheetsInDOM.length + " sheets");
for (let sheet of sheetsFromTheme) {
ok(sheetsInDOM.some(s=>s.includes(sheet)), "There is a stylesheet for " + sheet);
}
yield toolbox.destroy();
});

View File

@ -3,8 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function() {
const DEVTOOLS_SKIN_URL = "chrome://devtools/skin/";
const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-light.css";
let documentElement = document.documentElement;
let devtoolsStyleSheets = new WeakMap();
function forceStyle() {
let computedStyle = window.getComputedStyle(documentElement);
@ -19,6 +20,45 @@
documentElement.style.display = display; // Restore
}
/*
* Append a new processing instruction and return an object with
* - styleSheet: DOMNode
* - loadPromise: Promise that resolves once the sheets loads or errors
*/
function appendStyleSheet(url) {
let styleSheetAttr = `href="${url}" type="text/css"`;
let styleSheet = document.createProcessingInstruction(
"xml-stylesheet", styleSheetAttr);
let loadPromise = new Promise((resolve, reject) => {
function onload() {
styleSheet.removeEventListener("load", onload);
styleSheet.removeEventListener("error", onerror);
resolve();
}
function onerror() {
styleSheet.removeEventListener("load", onload);
styleSheet.removeEventListener("error", onerror);
reject("Failed to load theme file " + url);
}
styleSheet.addEventListener("load", onload);
styleSheet.addEventListener("error", onerror);
});
document.insertBefore(styleSheet, documentElement);
return {styleSheet, loadPromise};
}
/*
* Notify the window that a theme switch finished so tests can check the DOM
*/
function notifyWindow() {
window.dispatchEvent(new CustomEvent("theme-switch-complete", {}));
}
/*
* Apply all the sheets from `newTheme` and remove all of the sheets
* from `oldTheme`
*/
function switchTheme(newTheme, oldTheme) {
if (newTheme === oldTheme) {
return;
@ -28,8 +68,8 @@
// Unload all theme stylesheets related to the old theme.
if (oldThemeDef) {
for (let url of oldThemeDef.stylesheets) {
StylesheetUtils.removeSheet(window, url, "author");
for (let sheet of devtoolsStyleSheets.get(oldThemeDef) || []) {
sheet.remove();
}
}
@ -42,8 +82,17 @@
newThemeDef = gDevTools.getThemeDefinition("light");
}
// Store the sheets in a WeakMap for access later when the theme gets
// unapplied. It's hard to query for processing instructions so this
// is an easy way to access them later without storing a property on
// the window
devtoolsStyleSheets.set(newThemeDef, []);
let loadEvents = [];
for (let url of newThemeDef.stylesheets) {
StylesheetUtils.loadSheet(window, url, "author");
let {styleSheet,loadPromise} = appendStyleSheet(url);
devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
loadEvents.push(loadPromise);
}
// Floating scroll-bars like in OSX
@ -53,21 +102,10 @@
// TODO: extensions might want to customize scrollbar styles too.
if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
let scrollbarsUrl = Services.io.newURI(
DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
if (newTheme == "dark") {
StylesheetUtils.loadSheet(
window,
scrollbarsUrl,
"agent"
);
StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
} else if (oldTheme == "dark") {
StylesheetUtils.removeSheet(
window,
scrollbarsUrl,
"agent"
);
StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
}
forceStyle();
}
@ -92,6 +130,8 @@
// Final notification for further theme-switching related logic.
gDevTools.emit("theme-switched", window, newTheme, oldTheme);
Promise.all(loadEvents).then(notifyWindow, console.error.bind(console));
}
function handlePrefChange(event, data) {
@ -101,7 +141,6 @@
}
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://devtools/client/framework/gDevTools.jsm");
const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});