single-file-core 1.5.4 → 1.5.6

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
@@ -195,7 +195,8 @@ function preProcessDoc(doc, win, options) {
195
195
  }
196
196
  return {
197
197
  canvases: elementsInfo.canvases,
198
- fonts: getFontsData(doc),
198
+ fonts: getFontsData(),
199
+ worklets: getWorkletsData(),
199
200
  stylesheets: getStylesheetsData(doc),
200
201
  images: elementsInfo.images,
201
202
  posters: elementsInfo.posters,
@@ -577,6 +578,10 @@ function getFontsData() {
577
578
  return hooksFrames.getFontsData();
578
579
  }
579
580
 
581
+ function getWorkletsData() {
582
+ return hooksFrames.getWorkletsData();
583
+ }
584
+
580
585
  function serialize(doc) {
581
586
  const docType = doc.doctype;
582
587
  let docTypeString = "";
package/core/index.js CHANGED
@@ -141,7 +141,8 @@ const STAGES = [{
141
141
  { action: "processStylesheets" },
142
142
  { action: "processStyleAttributes" },
143
143
  { action: "processPageResources" },
144
- { action: "processScripts" }
144
+ { action: "processScripts" },
145
+ { action: "processWorklets" }
145
146
  ]
146
147
  }, {
147
148
  sequential: [
@@ -188,6 +189,7 @@ class Runner {
188
189
  const docData = util.preProcessDoc(this.options.doc, this.options.win, this.options);
189
190
  this.options.canvases = docData.canvases;
190
191
  this.options.fonts = docData.fonts;
192
+ this.options.worklets = docData.worklets;
191
193
  this.options.stylesheets = docData.stylesheets;
192
194
  this.options.images = docData.images;
193
195
  this.options.posters = docData.posters;
@@ -428,6 +430,7 @@ class Processor {
428
430
  this.resources = {
429
431
  cssVariables: new Map(),
430
432
  fonts: new Map(),
433
+ worklets: new Map(),
431
434
  stylesheets: new Map(),
432
435
  scripts: new Map(),
433
436
  images: new Map(),
@@ -1239,6 +1242,7 @@ class Processor {
1239
1242
  options.content = frameData.content;
1240
1243
  options.canvases = frameData.canvases;
1241
1244
  options.fonts = frameData.fonts;
1245
+ options.worklets = frameData.worklets;
1242
1246
  options.stylesheets = frameData.stylesheets;
1243
1247
  options.images = frameData.images;
1244
1248
  options.posters = frameData.posters;
@@ -1368,6 +1372,18 @@ class Processor {
1368
1372
  }));
1369
1373
  }
1370
1374
 
1375
+ async processWorklets() {
1376
+ if (this.options.worklets.length) {
1377
+ const scriptElement = this.doc.createElement("script");
1378
+ scriptElement.textContent = "if (CSS && CSS.paintWorklet && CSS.paintWorklet.addModule) {\n";
1379
+ await Promise.all(this.options.worklets.map(async ({ moduleURL, options }) => {
1380
+ await this.processorHelper.processWorklet(scriptElement, moduleURL, options, this.options, this.charset, this.batchRequest, this.resources);
1381
+ }));
1382
+ scriptElement.textContent += "}";
1383
+ this.doc.head.appendChild(scriptElement);
1384
+ }
1385
+ }
1386
+
1371
1387
  removeAlternativeImages() {
1372
1388
  util.removeAlternativeImages(this.doc);
1373
1389
  }
@@ -475,6 +475,23 @@ function getProcessorHelperClass(utilInstance) {
475
475
  element.setAttribute("src", content);
476
476
  }
477
477
 
478
+ async processWorklet(scriptElement, resourceURL, workletOptions, options, charset, batchRequest) {
479
+ let { content } = await batchRequest.addURL(resourceURL, {
480
+ asBinary: true,
481
+ charset: charset != UTF8_CHARSET && charset,
482
+ maxResourceSize: options.maxResourceSize,
483
+ maxResourceSizeEnabled: options.maxResourceSizeEnabled,
484
+ frameId: options.windowId,
485
+ resourceReferrer: options.resourceReferrer,
486
+ baseURI: options.baseURI,
487
+ blockMixedContent: options.blockMixedContent,
488
+ expectedType: "script",
489
+ acceptHeaders: options.acceptHeaders,
490
+ networkTimeout: options.networkTimeout
491
+ });
492
+ scriptElement.textContent += `CSS.paintWorklet.addModule("${content}", ${JSON.stringify(workletOptions)});\n`;
493
+ }
494
+
478
495
  setMetaCSP(metaElement) {
479
496
  metaElement.content = "default-src 'none'; font-src 'self' data:; img-src 'self' data:; style-src 'unsafe-inline'; media-src 'self' data:; script-src 'unsafe-inline' data:; object-src 'self' data:; frame-src 'self' data:;";
480
497
  }
@@ -394,6 +394,25 @@ function getProcessorHelperClass(utilInstance) {
394
394
  resources.scripts.set(indexResource, { name, content, extension, contentType, url: resourceURL });
395
395
  }
396
396
 
397
+ async processWorklet(scriptElement, resourceURL, workletOptions, options, charset, batchRequest, resources) {
398
+ let { content, indexResource, extension, contentType } = await batchRequest.addURL(resourceURL, {
399
+ asBinary: true,
400
+ charset: charset != UTF8_CHARSET && charset,
401
+ maxResourceSize: options.maxResourceSize,
402
+ maxResourceSizeEnabled: options.maxResourceSizeEnabled,
403
+ frameId: options.windowId,
404
+ resourceReferrer: options.resourceReferrer,
405
+ baseURI: options.baseURI,
406
+ blockMixedContent: options.blockMixedContent,
407
+ expectedType: "script",
408
+ acceptHeaders: options.acceptHeaders,
409
+ networkTimeout: options.networkTimeout
410
+ });
411
+ const name = "scripts/" + indexResource + extension;
412
+ scriptElement.textContent += `CSS.paintWorklet.addModule("${name}", ${JSON.stringify(workletOptions)});\n`;
413
+ resources.worklets.set(indexResource, { name, workletOptions, content, extension, contentType, url: resourceURL });
414
+ }
415
+
397
416
  setMetaCSP(metaElement) {
398
417
  metaElement.content = "default-src 'none'; connect-src 'self' data: blob:; font-src 'self' data: blob:; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline' data: blob:; frame-src 'self' data: blob:; media-src 'self' data: blob:; script-src 'self' 'unsafe-inline' data: blob:; object-src 'self' data: blob:;";
399
418
  }
@@ -308,6 +308,11 @@ function processDeclarations(declarationsInfo, declarations, selectorInfo, proce
308
308
  processedProperties.add(declarationData.property);
309
309
  }
310
310
  }
311
+ } else if (declarationData.type == "Rule") {
312
+ const declarationInfo = declarationsInfo.get(declarationData);
313
+ if (!declarationInfo) {
314
+ declarationsInfo.set(declarationData, { selectorInfo });
315
+ }
311
316
  }
312
317
  }
313
318
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -237,6 +237,7 @@ function initResponse(message) {
237
237
  frameData.url = messageFrameData.url;
238
238
  frameData.canvases = messageFrameData.canvases;
239
239
  frameData.fonts = messageFrameData.fonts;
240
+ frameData.worklets = messageFrameData.worklets;
240
241
  frameData.stylesheets = messageFrameData.stylesheets;
241
242
  frameData.images = messageFrameData.images;
242
243
  frameData.posters = messageFrameData.posters;
@@ -414,6 +415,7 @@ function getFrameData(document, globalThis, windowId, options, scrolling) {
414
415
  title: document.title,
415
416
  canvases: docData.canvases,
416
417
  fonts: docData.fonts,
418
+ worklets: docData.worklets,
417
419
  stylesheets: docData.stylesheets,
418
420
  images: docData.images,
419
421
  posters: docData.posters,
@@ -50,6 +50,7 @@
50
50
  const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
51
51
  const DELETE_FONT_EVENT = "single-file-delete-font";
52
52
  const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
53
+ const NEW_WORKLET_EVENT = "single-file-new-worklet";
53
54
  const FONT_STYLE_PROPERTIES = {
54
55
  family: "font-family",
55
56
  style: "font-style",
@@ -71,6 +72,7 @@
71
72
  const Blob = globalThis.Blob;
72
73
  const JSON = globalThis.JSON;
73
74
  const MutationObserver = globalThis.MutationObserver;
75
+ const URL = globalThis.URL;
74
76
 
75
77
  const observers = new Map();
76
78
  const observedElements = new Map();
@@ -307,7 +309,15 @@
307
309
  delete screen.width;
308
310
  }
309
311
 
310
-
312
+ if (globalThis.CSS && globalThis.CSS.paintWorklet && globalThis.CSS.paintWorklet.addModule) {
313
+ const addModule = globalThis.CSS.paintWorklet.addModule;
314
+ globalThis.CSS.paintWorklet.addModule = function (moduleURL, options) {
315
+ const result = addModule.apply(globalThis.CSS.paintWorklet, arguments);
316
+ moduleURL = new URL(moduleURL, document.baseURI).href;
317
+ document.dispatchEvent(new CustomEvent(NEW_WORKLET_EVENT, { detail: { moduleURL, options } }));
318
+ return result;
319
+ };
320
+ }
311
321
 
312
322
  if (globalThis.FontFace) {
313
323
  const FontFace = globalThis.FontFace;
@@ -40,7 +40,9 @@ const IMAGE_LOADED_EVENT = "single-file-image-loaded";
40
40
  const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
41
41
  const DELETE_FONT_EVENT = "single-file-delete-font";
42
42
  const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
43
+ const NEW_WORKLET_EVENT = "single-file-new-worklet";
43
44
  const FONT_FACE_PROPERTY_NAME = "_singleFile_fontFaces";
45
+ const WORKLET_PROPERTY_NAME = "_singleFile_worklets";
44
46
 
45
47
  const CustomEvent = globalThis.CustomEvent;
46
48
  const document = globalThis.document;
@@ -48,12 +50,17 @@ const Document = globalThis.Document;
48
50
  const JSON = globalThis.JSON;
49
51
  const MutationObserver = globalThis.MutationObserver;
50
52
 
51
- let fontFaces;
53
+ let fontFaces, worklets;
52
54
  if (window[FONT_FACE_PROPERTY_NAME]) {
53
55
  fontFaces = window[FONT_FACE_PROPERTY_NAME];
54
56
  } else {
55
57
  fontFaces = window[FONT_FACE_PROPERTY_NAME] = new Map();
56
58
  }
59
+ if (window[WORKLET_PROPERTY_NAME]) {
60
+ worklets = window[WORKLET_PROPERTY_NAME];
61
+ } else {
62
+ worklets = window[WORKLET_PROPERTY_NAME] = new Map();
63
+ }
57
64
 
58
65
  init();
59
66
  new MutationObserver(init).observe(document, { childList: true });
@@ -73,11 +80,16 @@ function init() {
73
80
  fontFaces.delete(JSON.stringify(key));
74
81
  });
75
82
  document.addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
83
+ document.addEventListener(NEW_WORKLET_EVENT, event => {
84
+ const detail = event.detail;
85
+ worklets.set(detail.moduleURL, detail);
86
+ });
76
87
  }
77
88
  }
78
89
 
79
90
  export {
80
91
  getFontsData,
92
+ getWorkletsData,
81
93
  loadDeferredImagesStart,
82
94
  loadDeferredImagesEnd,
83
95
  loadDeferredImagesResetZoomLevel,
@@ -89,6 +101,10 @@ function getFontsData() {
89
101
  return Array.from(fontFaces.values());
90
102
  }
91
103
 
104
+ function getWorkletsData() {
105
+ return Array.from(worklets.values());
106
+ }
107
+
92
108
  function loadDeferredImagesStart(options) {
93
109
  if (options.loadDeferredImagesBlockCookies) {
94
110
  document.dispatchEvent(new CustomEvent(BLOCK_COOKIES_START_EVENT));