2016-02-26 22:40:38 +03:00
|
|
|
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const { Services } = require("resource://gre/modules/Services.jsm");
|
|
|
|
const { getTheme } = require("devtools/client/shared/theme");
|
|
|
|
|
|
|
|
function iterStyleNodes(window, func) {
|
|
|
|
for (let node of window.document.childNodes) {
|
|
|
|
// Look for ProcessingInstruction nodes.
|
|
|
|
if (node.nodeType === 7) {
|
|
|
|
func(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const links = window.document.getElementsByTagNameNS(
|
|
|
|
"http://www.w3.org/1999/xhtml", "link"
|
|
|
|
);
|
|
|
|
for (let node of links) {
|
|
|
|
func(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function replaceCSS(window, fileURI) {
|
|
|
|
const document = window.document;
|
|
|
|
const randomKey = Math.random();
|
|
|
|
Services.obs.notifyObservers(null, "startupcache-invalidate", null);
|
|
|
|
|
|
|
|
// Scan every CSS tag and reload ones that match the file we are
|
|
|
|
// looking for.
|
|
|
|
iterStyleNodes(window, node => {
|
|
|
|
if (node.nodeType === 7) {
|
|
|
|
// xml-stylesheet declaration
|
|
|
|
if (node.data.includes(fileURI)) {
|
|
|
|
const newNode = window.document.createProcessingInstruction(
|
|
|
|
"xml-stylesheet",
|
|
|
|
`href="${fileURI}?s=${randomKey}" type="text/css"`
|
|
|
|
);
|
|
|
|
document.insertBefore(newNode, node);
|
|
|
|
document.removeChild(node);
|
|
|
|
}
|
|
|
|
} else if (node.href.includes(fileURI)) {
|
|
|
|
const parentNode = node.parentNode;
|
|
|
|
const newNode = window.document.createElementNS(
|
|
|
|
"http://www.w3.org/1999/xhtml",
|
|
|
|
"link"
|
|
|
|
);
|
|
|
|
newNode.rel = "stylesheet";
|
|
|
|
newNode.type = "text/css";
|
|
|
|
newNode.href = fileURI + "?s=" + randomKey;
|
|
|
|
|
|
|
|
parentNode.insertBefore(newNode, node);
|
|
|
|
parentNode.removeChild(node);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function _replaceResourceInSheet(sheet, filename, randomKey) {
|
|
|
|
for (let i = 0; i < sheet.cssRules.length; i++) {
|
|
|
|
const rule = sheet.cssRules[i];
|
|
|
|
if (rule.type === rule.IMPORT_RULE) {
|
|
|
|
_replaceResourceInSheet(rule.styleSheet, filename);
|
|
|
|
} else if (rule.cssText.includes(filename)) {
|
|
|
|
// Strip off any existing query strings. This might lose
|
|
|
|
// updates for files if there are multiple resources
|
|
|
|
// referenced in the same rule, but the chances of someone hot
|
|
|
|
// reloading multiple resources in the same rule is very low.
|
|
|
|
const text = rule.cssText.replace(/\?s=0.\d+/g, "");
|
|
|
|
const newRule = (
|
|
|
|
text.replace(filename, filename + "?s=" + randomKey)
|
|
|
|
);
|
|
|
|
|
|
|
|
sheet.deleteRule(i);
|
|
|
|
sheet.insertRule(newRule, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function replaceCSSResource(window, fileURI) {
|
|
|
|
const document = window.document;
|
|
|
|
const randomKey = Math.random();
|
|
|
|
|
|
|
|
// Only match the filename. False positives are much better than
|
|
|
|
// missing updates, as all that would happen is we reload more
|
|
|
|
// resources than we need. We do this because many resources only
|
|
|
|
// use relative paths.
|
|
|
|
const parts = fileURI.split("/");
|
|
|
|
const file = parts[parts.length - 1];
|
|
|
|
|
|
|
|
// Scan every single rule in the entire page for any reference to
|
|
|
|
// this resource, and re-insert the rule to force it to update.
|
|
|
|
for (let sheet of document.styleSheets) {
|
|
|
|
_replaceResourceInSheet(sheet, file, randomKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let node of document.querySelectorAll("img,image")) {
|
|
|
|
if (node.src.startsWith(fileURI)) {
|
|
|
|
node.src = fileURI + "?s=" + randomKey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function watchCSS(window) {
|
|
|
|
if (Services.prefs.getBoolPref("devtools.loader.hotreload")) {
|
2016-04-04 21:51:38 +03:00
|
|
|
const watcher = require("devtools/client/shared/devtools-file-watcher");
|
2016-02-26 22:40:38 +03:00
|
|
|
|
|
|
|
function onFileChanged(_, relativePath) {
|
|
|
|
if (relativePath.match(/\.css$/)) {
|
|
|
|
if (relativePath.startsWith("client/themes")) {
|
|
|
|
let path = relativePath.replace(/^client\/themes\//, "");
|
|
|
|
|
|
|
|
// Special-case a few files that get imported from other CSS
|
|
|
|
// files. We just manually hot reload the parent CSS file.
|
|
|
|
if (path === "variables.css" || path === "toolbars.css" ||
|
|
|
|
path === "common.css" || path === "splitters.css") {
|
|
|
|
replaceCSS(window, "chrome://devtools/skin/" + getTheme() + "-theme.css");
|
|
|
|
} else {
|
|
|
|
replaceCSS(window, "chrome://devtools/skin/" + path);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
replaceCSS(
|
|
|
|
window,
|
2016-05-17 21:25:54 +03:00
|
|
|
"chrome://devtools/content/" + relativePath.replace(/^client\//, "")
|
2016-02-26 22:40:38 +03:00
|
|
|
);
|
|
|
|
replaceCSS(window, "resource://devtools/" + relativePath);
|
|
|
|
} else if (relativePath.match(/\.(svg|png)$/)) {
|
|
|
|
relativePath = relativePath.replace(/^client\/themes\//, "");
|
|
|
|
replaceCSSResource(window, "chrome://devtools/skin/" + relativePath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
watcher.on("file-changed", onFileChanged);
|
|
|
|
|
|
|
|
window.addEventListener("unload", () => {
|
|
|
|
watcher.off("file-changed", onFileChanged);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = { watchCSS };
|