|
|
@@ -155,6 +155,8 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
this.options = options;
|
|
|
this.options.url = this.options.url || (this.options.doc && this.options.doc.location.href);
|
|
|
this.options.baseURI = this.options.doc && this.options.doc.baseURI;
|
|
|
+ this.options.rootDocument = root;
|
|
|
+ this.options.updatedResources = this.options.updatedResources || {};
|
|
|
this.batchRequest = new BatchRequest();
|
|
|
this.processor = new Processor(options, this.batchRequest);
|
|
|
if (this.options.doc) {
|
|
|
@@ -837,6 +839,15 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
}
|
|
|
|
|
|
async resolveStylesheetURLs() {
|
|
|
+ const options = {
|
|
|
+ maxResourceSize: this.options.maxResourceSize,
|
|
|
+ maxResourceSizeEnabled: this.options.maxResourceSizeEnabled,
|
|
|
+ url: this.options.url,
|
|
|
+ charset: this.charset,
|
|
|
+ compressCSS: this.options.compressCSS,
|
|
|
+ updatedResources: this.options.updatedResources,
|
|
|
+ rootDocument: this.options.rootDocument
|
|
|
+ };
|
|
|
await Promise.all(Array.from(this.doc.querySelectorAll("style, link[rel*=stylesheet]"))
|
|
|
.map(async element => {
|
|
|
let mediaText;
|
|
|
@@ -844,43 +855,51 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
mediaText = element.media.toLowerCase();
|
|
|
}
|
|
|
const stylesheetInfo = { mediaText };
|
|
|
- this.stylesheets.set(element, stylesheetInfo);
|
|
|
if (element.closest("[" + SHADOW_MODE_ATTRIBUTE_NAME + "]")) {
|
|
|
stylesheetInfo.scoped = true;
|
|
|
}
|
|
|
- const options = {
|
|
|
- maxResourceSize: this.options.maxResourceSize,
|
|
|
- maxResourceSizeEnabled: this.options.maxResourceSizeEnabled,
|
|
|
- url: this.options.url,
|
|
|
- charset: this.charset,
|
|
|
- compressCSS: this.options.compressCSS
|
|
|
- };
|
|
|
if (element.tagName.toLowerCase() == "link") {
|
|
|
if (element.charset) {
|
|
|
options.charset = element.charset;
|
|
|
}
|
|
|
}
|
|
|
- let stylesheetContent = await getStylesheetContent(element, this.baseURI, options, this.workStyleElement);
|
|
|
- const match = stylesheetContent.match(/^@charset\s+"([^"]*)";/i);
|
|
|
- if (match && match[1] && match[1] != options.charset) {
|
|
|
- options.charset = match[1];
|
|
|
- stylesheetContent = await getStylesheetContent(element, this.baseURI, options, this.workStyleElement);
|
|
|
- }
|
|
|
- let stylesheet;
|
|
|
- try {
|
|
|
- stylesheet = cssTree.parse(Util.removeCssComments(stylesheetContent));
|
|
|
- } catch (error) {
|
|
|
- // ignored
|
|
|
- }
|
|
|
- if (stylesheet && stylesheet.children) {
|
|
|
- if (this.options.compressCSS) {
|
|
|
- ProcessorHelper.removeSingleLineCssComments(stylesheet);
|
|
|
- }
|
|
|
- stylesheetInfo.stylesheet = stylesheet;
|
|
|
- } else {
|
|
|
- this.stylesheets.delete(element);
|
|
|
- }
|
|
|
+ await processElement(element, stylesheetInfo, this.stylesheets, this.baseURI, options, this.workStyleElement);
|
|
|
}));
|
|
|
+ if (options.rootDocument) {
|
|
|
+ const newResources = Object.keys(options.updatedResources).filter(url => options.updatedResources[url].type == "stylesheet" && !options.updatedResources[url].retrieved).map(url => options.updatedResources[url]);
|
|
|
+ await Promise.all(newResources.map(async resource => {
|
|
|
+ resource.retrieved = true;
|
|
|
+ const stylesheetInfo = {};
|
|
|
+ const element = this.doc.createElement("style");
|
|
|
+ this.doc.body.appendChild(element);
|
|
|
+ element.textContent = resource.content;
|
|
|
+ await processElement(element, stylesheetInfo, this.stylesheets, this.baseURI, options, this.workStyleElement);
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ async function processElement(element, stylesheetInfo, stylesheets, baseURI, options, workStyleElement) {
|
|
|
+ stylesheets.set(element, stylesheetInfo);
|
|
|
+ let stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
|
|
|
+ const match = stylesheetContent.match(/^@charset\s+"([^"]*)";/i);
|
|
|
+ if (match && match[1] && match[1] != options.charset) {
|
|
|
+ options.charset = match[1];
|
|
|
+ stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
|
|
|
+ }
|
|
|
+ let stylesheet;
|
|
|
+ try {
|
|
|
+ stylesheet = cssTree.parse(Util.removeCssComments(stylesheetContent));
|
|
|
+ } catch (error) {
|
|
|
+ // ignored
|
|
|
+ }
|
|
|
+ if (stylesheet && stylesheet.children) {
|
|
|
+ if (options.compressCSS) {
|
|
|
+ ProcessorHelper.removeSingleLineCssComments(stylesheet);
|
|
|
+ }
|
|
|
+ stylesheetInfo.stylesheet = stylesheet;
|
|
|
+ } else {
|
|
|
+ stylesheets.delete(element);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
async function getStylesheetContent(element, baseURI, options, workStyleElement) {
|
|
|
let content;
|
|
|
@@ -1113,6 +1132,7 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
maxResourceSize: this.options.maxResourceSize,
|
|
|
maxResourceSizeEnabled: this.options.maxResourceSizeEnabled
|
|
|
});
|
|
|
+ content.data = Util.getUpdatedResourceContent(resourceURL, content, this.options);
|
|
|
scriptElement.setAttribute("src", content.data);
|
|
|
}
|
|
|
}));
|
|
|
@@ -1437,6 +1457,7 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
validateTextContentType: true
|
|
|
});
|
|
|
resourceURL = content.resourceURL;
|
|
|
+ content.data = Util.getUpdatedResourceContent(resourceURL, content, options);
|
|
|
let importedStylesheetContent = Util.removeCssComments(content.data);
|
|
|
if (options.compressCSS) {
|
|
|
importedStylesheetContent = util.compressCSS(importedStylesheetContent);
|
|
|
@@ -1505,6 +1526,7 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
charset: options.charset
|
|
|
});
|
|
|
resourceURL = content.resourceURL;
|
|
|
+ content.data = Util.getUpdatedResourceContent(content.resourceURL, content, options);
|
|
|
let stylesheetContent = Util.removeCssComments(content.data);
|
|
|
if (options.compressCSS) {
|
|
|
stylesheetContent = util.compressCSS(stylesheetContent);
|
|
|
@@ -1761,6 +1783,15 @@ this.singlefile.lib.core = this.singlefile.lib.core || (() => {
|
|
|
const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
|
|
|
|
|
|
class Util {
|
|
|
+ static getUpdatedResourceContent(resourceURL, content, options) {
|
|
|
+ if (options.rootDocument && options.updatedResources[resourceURL]) {
|
|
|
+ options.updatedResources[resourceURL].retrieved = true;
|
|
|
+ return options.updatedResources[resourceURL].content;
|
|
|
+ } else {
|
|
|
+ return content.data || "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
static normalizeURL(url) {
|
|
|
if (!url || url.startsWith(DATA_URI_PREFIX)) {
|
|
|
return url;
|