single-file-core 1.5.68 → 1.5.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core/helper.js CHANGED
@@ -389,9 +389,7 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
389
389
  data.shadowRoots.push(shadowRootInfo);
390
390
  try {
391
391
  if (shadowRoot.adoptedStyleSheets) {
392
- if (shadowRoot.adoptedStyleSheets.length) {
393
- shadowRootInfo.adoptedStyleSheets = getStylesheetsContent(shadowRoot.adoptedStyleSheets, adoptedStyleSheetsCache);
394
- } else if (shadowRoot.adoptedStyleSheets.length === undefined) {
392
+
395
393
  const listener = event => shadowRootInfo.adoptedStyleSheets = event.detail.adoptedStyleSheets;
396
394
  shadowRoot.addEventListener(GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT, listener);
397
395
  shadowRoot.dispatchEvent(new CustomEvent(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, { bubbles: true }));
@@ -399,7 +397,7 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
399
397
  element.dispatchEvent(new CustomEvent(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, { bubbles: true }));
400
398
  }
401
399
  shadowRoot.removeEventListener(GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT, listener);
402
- }
400
+
403
401
  }
404
402
  // eslint-disable-next-line no-unused-vars
405
403
  } catch (error) {
package/core/index.js CHANGED
@@ -415,7 +415,6 @@ const SHADOWROOT_ATTRIBUTE_NAME = "shadowrootmode";
415
415
  const SHADOWROOT_DELEGATES_FOCUS = "shadowrootdelegatesfocus";
416
416
  const SHADOWROOT_CLONABLE = "shadowrootclonable";
417
417
  const SHADOWROOT_SERIALIZABLE = "shadowrootserializable";
418
- const SCRIPT_TEMPLATE_SHADOW_ROOT = "data-template-shadow-root";
419
418
  const SCRIPT_OPTIONS = "data-single-file-options";
420
419
  const UTF8_CHARSET = "utf-8";
421
420
 
@@ -539,15 +538,6 @@ class Processor {
539
538
  if (this.options.includeInfobar) {
540
539
  util.appendInfobar(this.doc, this.options);
541
540
  }
542
- if (this.doc.querySelector("template[" + SHADOWROOT_ATTRIBUTE_NAME + "]") || (this.options.shadowRoots && this.options.shadowRoots.length)) {
543
- if (this.options.blockScripts) {
544
- this.doc.querySelectorAll("script[" + SCRIPT_TEMPLATE_SHADOW_ROOT + "]").forEach(element => element.remove());
545
- }
546
- const scriptElement = this.doc.createElement("script");
547
- scriptElement.setAttribute(SCRIPT_TEMPLATE_SHADOW_ROOT, "");
548
- scriptElement.textContent = `(()=>{document.currentScript.remove();processNode(document);function processNode(node){node.querySelectorAll("template[${SHADOWROOT_ATTRIBUTE_NAME}]").forEach(element=>{let shadowRoot = element.parentElement.shadowRoot;if (!shadowRoot) {try {shadowRoot=element.parentElement.attachShadow({mode:element.getAttribute("${SHADOWROOT_ATTRIBUTE_NAME}"),delegatesFocus:element.getAttribute("${SHADOWROOT_DELEGATES_FOCUS}")!=null,clonable:element.getAttribute("${SHADOWROOT_CLONABLE}")!=null,serializable:element.getAttribute("${SHADOWROOT_SERIALIZABLE}")!=null});shadowRoot.innerHTML=element.innerHTML;element.remove()} catch (error) {} if (shadowRoot) {processNode(shadowRoot)}}})}})()`;
549
- this.doc.body.appendChild(scriptElement);
550
- }
551
541
  if (this.options.insertCanonicalLink && this.options.saveUrl.match(HTTP_URI_PREFIX)) {
552
542
  let canonicalLink = this.doc.querySelector("link[rel=canonical]");
553
543
  if (!canonicalLink) {
@@ -850,7 +840,7 @@ class Processor {
850
840
  element.setAttribute("src", DISABLED_SCRIPT);
851
841
  }
852
842
  });
853
- const scriptElements = this.doc.querySelectorAll("script:not([type=\"application/ld+json\"]):not([" + SCRIPT_TEMPLATE_SHADOW_ROOT + "]):not([" + SCRIPT_OPTIONS + "])");
843
+ const scriptElements = this.doc.querySelectorAll("script:not([type=\"application/ld+json\"]):not([" + SCRIPT_OPTIONS + "])");
854
844
  this.stats.set("discarded", "scripts", scriptElements.length);
855
845
  this.stats.set("processed", "scripts", scriptElements.length);
856
846
  scriptElements.forEach(element => element.remove());
@@ -17025,10 +17025,35 @@ async function evalTemplate(template = "", options, content, doc, context = {})
17025
17025
  },
17026
17026
  // eslint-disable-next-line no-unused-vars
17027
17027
  "stringify": value => { try { return JSON.stringify(value); } catch (error) { return value; } },
17028
- // eslint-disable-next-line no-unused-vars
17029
- "encode-base64": value => { try { return btoa(value); } catch (error) { return value; } },
17030
- // eslint-disable-next-line no-unused-vars
17031
- "decode-base64": value => { try { return atob(value); } catch (error) { return value; } },
17028
+ "encode-base64": value => {
17029
+ // can be replaced with Uint8Array.toBase64() which should already be supported by most browsers
17030
+ // function taken from https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa
17031
+ function bytesToBase64(bytes) {
17032
+ const binString = Array.from(bytes, (byte) =>
17033
+ String.fromCodePoint(byte),
17034
+ ).join("");
17035
+ return btoa(binString);
17036
+ }
17037
+
17038
+ const utf8Array = new TextEncoder().encode(value);
17039
+ return bytesToBase64(utf8Array);
17040
+ },
17041
+ "decode-base64": value => {
17042
+ // can be replaced with Uint8Array.fromBase64() which should already be supported by most browsers
17043
+ // function taken from https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa
17044
+ function base64ToBytes(base64) {
17045
+ const binString = atob(base64);
17046
+ return Uint8Array.from(binString, (m) => m.codePointAt(0));
17047
+ }
17048
+
17049
+ try {
17050
+ const utf8Array = base64ToBytes(value);
17051
+ return new TextDecoder("utf-8").decode(utf8Array);
17052
+ // eslint-disable-next-line no-unused-vars
17053
+ } catch(error) {
17054
+ return value;
17055
+ }
17056
+ },
17032
17057
  // eslint-disable-next-line no-unused-vars
17033
17058
  "encode-uri": value => { try { return encodeURI(value); } catch (error) { return value; } },
17034
17059
  // eslint-disable-next-line no-unused-vars
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.5.68",
3
+ "version": "1.5.70",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -27,7 +27,7 @@ export {
27
27
  extract
28
28
  };
29
29
 
30
- async function extract(content, { password, prompt = () => { }, shadowRootScriptURL, zipOptions = { useWebWorkers: false }, noBlobURL } = {}) {
30
+ async function extract(content, { password, prompt = () => { }, zipOptions = { useWebWorkers: false }, noBlobURL } = {}) {
31
31
  const KNOWN_MIMETYPES = {
32
32
  "gif": "image/gif",
33
33
  "jpg": "image/jpeg",
@@ -191,11 +191,6 @@ async function extract(content, { password, prompt = () => { }, shadowRootScript
191
191
  resource.textContent = textContent;
192
192
  resource.content = await getContent(resource);
193
193
  }
194
- if (filename.match(REGEXP_MATCH_INDEX)) {
195
- if (shadowRootScriptURL) {
196
- resource.textContent = textContent.replace(/<script data-template-shadow-root.*<\/script>/g, "<script data-template-shadow-root src=" + shadowRootScriptURL + "></" + "script>");
197
- }
198
- }
199
194
  if (filename.match(REGEXP_MATCH_ROOT_INDEX)) {
200
195
  docContent = textContent;
201
196
  url = resource.url;
@@ -76,11 +76,13 @@
76
76
  const JSON = globalThis.JSON;
77
77
  const MutationObserver = globalThis.MutationObserver;
78
78
  const URL = globalThis.URL;
79
+ const CSSStyleSheet = globalThis.CSSStyleSheet;
79
80
 
80
81
  const observers = new Map();
81
82
  const observedElements = new Map();
82
83
 
83
84
  let dispatchScrollEvent;
85
+ let adoptedStylesheetsData = new Map();
84
86
 
85
87
  init();
86
88
  new MutationObserver(init).observe(document, { childList: true });
@@ -438,13 +440,71 @@
438
440
  globalThis.IntersectionObserver.toString = function () { return "function IntersectionObserver() { [native code] }"; };
439
441
  }
440
442
 
443
+ const originalReplaceSync = CSSStyleSheet.prototype.replaceSync;
444
+ CSSStyleSheet.prototype.replaceSync = function (text) {
445
+ try {
446
+ const result = originalReplaceSync.apply(this, [text]);
447
+ adoptedStylesheetsData.set(this, text);
448
+ return result;
449
+ } catch (error) {
450
+ error.stack = error.message + "\n" + " \n" + error.stack.trim().split("\n").slice(-1).join("\n");
451
+ throw error;
452
+ }
453
+ };
454
+ CSSStyleSheet.prototype.replaceSync.toString = function () { return "function replaceSync() { [native code] }"; };
455
+ const orginalReplace = CSSStyleSheet.prototype.replace;
456
+ CSSStyleSheet.prototype.replace = async function (text) {
457
+ try {
458
+ const result = await orginalReplace.apply(this, [text]);
459
+ adoptedStylesheetsData.set(this, text);
460
+ return result;
461
+ } catch (error) {
462
+ error.stack = error.message + "\n" + " \n" + error.stack.trim().split("\n").slice(-1).join("\n");
463
+ throw error;
464
+ }
465
+ };
466
+ CSSStyleSheet.prototype.replace.toString = function () { return "function replace() { [native code] }"; };
467
+ const originalInsertRule = CSSStyleSheet.prototype.insertRule;
468
+ CSSStyleSheet.prototype.insertRule = function (rule, index) {
469
+ try {
470
+ const result = originalInsertRule.apply(this, [rule, index]);
471
+ adoptedStylesheetsData.delete(this);
472
+ return result;
473
+ } catch (error) {
474
+ error.stack = error.message + "\n" + " \n" + error.stack.trim().split("\n").slice(-1).join("\n");
475
+ throw error;
476
+ }
477
+ };
478
+ CSSStyleSheet.prototype.insertRule.toString = function () { return "function insertRule() { [native code] }"; };
479
+ const originalDeleteRule = CSSStyleSheet.prototype.deleteRule;
480
+ CSSStyleSheet.prototype.deleteRule = function (index) {
481
+ try {
482
+ const result = originalDeleteRule.apply(this, [index]);
483
+ adoptedStylesheetsData.delete(this);
484
+ return result;
485
+ } catch (error) {
486
+ error.stack = error.message + "\n" + " \n" + error.stack.trim().split("\n").slice(-1).join("\n");
487
+ throw error;
488
+ }
489
+ };
490
+ CSSStyleSheet.prototype.deleteRule.toString = function () { return "function deleteRule() { [native code] }"; };
491
+
441
492
  function getAdoptedStylesheetsListener(event) {
442
493
  const shadowRoot = event.target.shadowRoot;
443
494
  event.stopPropagation();
444
495
  if (shadowRoot) {
445
496
  shadowRoot.addEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener, { capture: true });
446
- shadowRoot.addEventListener(UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, () => shadowRoot.removeEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener), { once: true });
447
- const adoptedStyleSheets = Array.from(shadowRoot.adoptedStyleSheets).map(stylesheet => Array.from(stylesheet.cssRules).map(cssRule => cssRule.cssText).join("\n"));
497
+ shadowRoot.addEventListener(UNREGISTER_GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, () => {
498
+ shadowRoot.removeEventListener(GET_ADOPTED_STYLESHEETS_REQUEST_EVENT, getAdoptedStylesheetsListener);
499
+ adoptedStylesheetsData.clear();
500
+ }, { once: true });
501
+ const adoptedStyleSheets = Array.from(shadowRoot.adoptedStyleSheets).map(stylesheet => {
502
+ if (adoptedStylesheetsData.has(stylesheet)) {
503
+ return adoptedStylesheetsData.get(stylesheet);
504
+ } else {
505
+ return Array.from(stylesheet.cssRules).map(cssRule => cssRule.cssText).join("\n");
506
+ }
507
+ });
448
508
  if (adoptedStyleSheets.length) {
449
509
  shadowRoot.dispatchEvent(new CustomEvent(GET_ADOPTED_STYLESHEETS_RESPONSE_EVENT, { detail: { adoptedStyleSheets } }));
450
510
  }