single-file-core 1.0.13 → 1.0.16

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.
@@ -244,7 +244,7 @@ function getFontFamilyNames(declarations) {
244
244
  const font = declarations.children.filter(node => node.property == "font").tail;
245
245
  if (font && font.data && font.data.value) {
246
246
  try {
247
- const parsedFont = fontPropertyParser.parse(cssTree.generate(font.data.value));
247
+ const parsedFont = fontPropertyParser.parse(font.data.value);
248
248
  parsedFont.family.forEach(familyName => fontFamilyNames.push(helper.normalizeFontFamily(familyName)));
249
249
  } catch (error) {
250
250
  // ignored
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.0.13",
3
+ "version": "1.0.16",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -233,7 +233,6 @@ function initResponse(message) {
233
233
  frameData.videos = messageFrameData.videos;
234
234
  frameData.usedFonts = messageFrameData.usedFonts;
235
235
  frameData.shadowRoots = messageFrameData.shadowRoots;
236
- frameData.imports = messageFrameData.imports;
237
236
  frameData.processed = messageFrameData.processed;
238
237
  }
239
238
  });
@@ -401,7 +400,6 @@ function getFrameData(document, globalThis, windowId, options) {
401
400
  videos: docData.videos,
402
401
  usedFonts: docData.usedFonts,
403
402
  shadowRoots: docData.shadowRoots,
404
- imports: docData.imports,
405
403
  processed: true
406
404
  };
407
405
  }
@@ -41,7 +41,6 @@ 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
43
 
44
- const browser = globalThis.browser;
45
44
  const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
46
45
  const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
47
46
  const CustomEvent = globalThis.CustomEvent;
@@ -69,17 +68,6 @@ if (document instanceof Document) {
69
68
  fontFaces.delete(JSON.stringify(key));
70
69
  });
71
70
  addEventListener(CLEAR_FONTS_EVENT, () => fontFaces = new Map());
72
- let scriptElement = document.createElement("script");
73
- scriptElement.src = "data:," + "(" + injectedScript.toString() + ")()";
74
- (document.documentElement || document).appendChild(scriptElement);
75
- scriptElement.remove();
76
- if (browser && browser.runtime && browser.runtime.getURL) {
77
- scriptElement = document.createElement("script");
78
- scriptElement.src = browser.runtime.getURL("/lib/single-file-hooks-frames.js");
79
- scriptElement.async = false;
80
- (document.documentElement || document).appendChild(scriptElement);
81
- scriptElement.remove();
82
- }
83
71
  }
84
72
 
85
73
  export {
@@ -135,80 +123,4 @@ function loadDeferredImagesResetZoomLevel(options) {
135
123
  } else {
136
124
  dispatchEvent(new CustomEvent(LOAD_DEFERRED_IMAGES_RESET_EVENT));
137
125
  }
138
- }
139
-
140
- function injectedScript() {
141
- if (typeof globalThis == "undefined") {
142
- window.globalThis = window;
143
- }
144
- const document = globalThis.document;
145
- const console = globalThis.console;
146
- const dispatchEvent = event => globalThis.dispatchEvent(event);
147
- const CustomEvent = globalThis.CustomEvent;
148
- const FileReader = globalThis.FileReader;
149
- const Blob = globalThis.Blob;
150
- const warn = (console && console.warn && ((...args) => console.warn(...args))) || (() => { });
151
- const NEW_FONT_FACE_EVENT = "single-file-new-font-face";
152
- const DELETE_FONT_EVENT = "single-file-delete-font";
153
- const CLEAR_FONTS_EVENT = "single-file-clear-fonts";
154
- const FONT_STYLE_PROPERTIES = {
155
- family: "font-family",
156
- style: "font-style",
157
- weight: "font-weight",
158
- stretch: "font-stretch",
159
- unicodeRange: "unicode-range",
160
- variant: "font-variant",
161
- featureSettings: "font-feature-settings"
162
- };
163
-
164
- if (globalThis.FontFace) {
165
- const FontFace = globalThis.FontFace;
166
- let warningFontFaceDisplayed;
167
- globalThis.FontFace = function () {
168
- if (!warningFontFaceDisplayed) {
169
- warn("SingleFile is hooking the FontFace constructor, document.fonts.delete and document.fonts.clear to handle dynamically loaded fonts.");
170
- warningFontFaceDisplayed = true;
171
- }
172
- getDetailObject(...arguments).then(detail => dispatchEvent(new CustomEvent(NEW_FONT_FACE_EVENT, { detail })));
173
- return new FontFace(...arguments);
174
- };
175
- globalThis.FontFace.toString = function () { return "function FontFace() { [native code] }"; };
176
- const deleteFont = document.fonts.delete;
177
- document.fonts.delete = function (fontFace) {
178
- getDetailObject(fontFace.family).then(detail => dispatchEvent(new CustomEvent(DELETE_FONT_EVENT, { detail })));
179
- return deleteFont.call(document.fonts, fontFace);
180
- };
181
- document.fonts.delete.toString = function () { return "function delete() { [native code] }"; };
182
- const clearFonts = document.fonts.clear;
183
- document.fonts.clear = function () {
184
- dispatchEvent(new CustomEvent(CLEAR_FONTS_EVENT));
185
- return clearFonts.call(document.fonts);
186
- };
187
- document.fonts.clear.toString = function () { return "function clear() { [native code] }"; };
188
- }
189
-
190
- async function getDetailObject(fontFamily, src, descriptors) {
191
- const detail = {};
192
- detail["font-family"] = fontFamily;
193
- detail.src = src;
194
- if (descriptors) {
195
- Object.keys(descriptors).forEach(descriptor => {
196
- if (FONT_STYLE_PROPERTIES[descriptor]) {
197
- detail[FONT_STYLE_PROPERTIES[descriptor]] = descriptors[descriptor];
198
- }
199
- });
200
- }
201
- return new Promise(resolve => {
202
- if (detail.src instanceof ArrayBuffer) {
203
- const reader = new FileReader();
204
- reader.readAsDataURL(new Blob([detail.src]));
205
- reader.addEventListener("load", () => {
206
- detail.src = "url(" + reader.result + ")";
207
- resolve(detail);
208
- });
209
- } else {
210
- resolve(detail);
211
- }
212
- });
213
- }
214
126
  }
@@ -22,13 +22,11 @@
22
22
  */
23
23
 
24
24
  import * as frameTree from "./frame-tree/content/content-frame-tree.js";
25
- import * as hooks from "./hooks/content/content-hooks.js";
26
25
  import * as hooksFrames from "./hooks/content/content-hooks-frames.js";
27
26
  import * as lazy from "./lazy/content/content-lazy-loader.js";
28
27
 
29
28
  export {
30
29
  frameTree,
31
- hooks,
32
30
  hooksFrames,
33
31
  lazy
34
32
  };
@@ -116,8 +116,7 @@ const STAGES = [{
116
116
  parallel: [
117
117
  { option: "blockVideos", action: "insertMissingVideoPosters" },
118
118
  { action: "resolveStylesheetURLs" },
119
- { option: "!removeFrames", action: "resolveFrameURLs" },
120
- { action: "resolveHtmlImportURLs" }
119
+ { option: "!removeFrames", action: "resolveFrameURLs" }
121
120
  ]
122
121
  }, {
123
122
  sequential: [
@@ -137,8 +136,7 @@ const STAGES = [{
137
136
  ],
138
137
  parallel: [
139
138
  { option: "removeAlternativeFonts", action: "removeAlternativeFonts" },
140
- { option: "!removeFrames", action: "processFrames" },
141
- { option: "!removeImports", action: "processHtmlImports" },
139
+ { option: "!removeFrames", action: "processFrames" }
142
140
  ]
143
141
  }, {
144
142
  sequential: [
@@ -178,7 +176,6 @@ class Runner {
178
176
  this.options.videos = docData.videos;
179
177
  this.options.usedFonts = docData.usedFonts;
180
178
  this.options.shadowRoots = docData.shadowRoots;
181
- this.options.imports = docData.imports;
182
179
  this.options.referrer = docData.referrer;
183
180
  this.markedElements = docData.markedElements;
184
181
  this.invalidElements = docData.invalidElements;
@@ -212,9 +209,6 @@ class Runner {
212
209
  if (this.options.frames) {
213
210
  this.options.frames.forEach(cancelRunner);
214
211
  }
215
- if (this.options.imports) {
216
- this.options.imports.forEach(cancelRunner);
217
- }
218
212
  }
219
213
 
220
214
  function cancelRunner(resourceData) {
@@ -428,9 +422,6 @@ class Processor {
428
422
  if (!this.options.saveRawPage && !this.options.removeFrames && this.options.frames) {
429
423
  this.options.frames.forEach(frameData => this.maxResources += frameData.maxResources || 0);
430
424
  }
431
- if (!this.options.removeImports && this.options.imports) {
432
- this.options.imports.forEach(importData => this.maxResources += importData.maxResources || 0);
433
- }
434
425
  this.stats.set("processed", "resources", this.maxResources);
435
426
  }
436
427
 
@@ -794,6 +785,7 @@ class Processor {
794
785
  this.doc.querySelectorAll("link[rel*=\"icon\"]").forEach(element => element.remove());
795
786
  }
796
787
  this.doc.querySelectorAll("a[ping]").forEach(element => element.removeAttribute("ping"));
788
+ this.doc.querySelectorAll("link[rel=import][href]").forEach(element => element.remove());
797
789
  }
798
790
 
799
791
  replaceInvalidElements() {
@@ -1134,7 +1126,6 @@ class Processor {
1134
1126
  options.videos = frameData.videos;
1135
1127
  options.usedFonts = frameData.usedFonts;
1136
1128
  options.shadowRoots = frameData.shadowRoots;
1137
- options.imports = frameData.imports;
1138
1129
  frameData.runner = new Runner(options);
1139
1130
  frameData.frameElement = frameElement;
1140
1131
  await frameData.runner.loadPage();
@@ -1197,49 +1188,6 @@ class Processor {
1197
1188
  }
1198
1189
  }
1199
1190
 
1200
- async resolveHtmlImportURLs() {
1201
- const linkElements = Array.from(this.doc.querySelectorAll("link[rel=import][href]"));
1202
- await Promise.all(linkElements.map(async linkElement => {
1203
- const resourceURL = linkElement.href;
1204
- if (this.options.saveOriginalURLs && !isDataURL(resourceURL)) {
1205
- linkElement.setAttribute("data-sf-original-href", resourceURL);
1206
- }
1207
- linkElement.removeAttribute("href");
1208
- const options = Object.create(this.options);
1209
- options.insertSingleFileComment = false;
1210
- options.insertCanonicalLink = false;
1211
- options.insertMetaNoIndex = false;
1212
- options.saveFavicon = false;
1213
- options.removeUnusedStyles = false;
1214
- options.removeAlternativeMedias = false;
1215
- options.removeUnusedFonts = false;
1216
- options.removeHiddenElements = false;
1217
- options.url = resourceURL;
1218
- const attributeValue = linkElement.getAttribute(util.HTML_IMPORT_ATTRIBUTE_NAME);
1219
- if (attributeValue) {
1220
- const importData = options.imports[Number(attributeValue)];
1221
- if (importData) {
1222
- options.content = importData.content;
1223
- importData.runner = new Runner(options);
1224
- await importData.runner.loadPage();
1225
- await importData.runner.initialize();
1226
- if (!options.removeImports) {
1227
- importData.maxResources = importData.runner.batchRequest.getMaxResources();
1228
- }
1229
- importData.runner.getStyleSheets().forEach(stylesheet => {
1230
- const importedStyleElement = this.doc.createElement("style");
1231
- linkElement.insertAdjacentElement("afterEnd", importedStyleElement);
1232
- this.stylesheets.set(importedStyleElement, stylesheet);
1233
- });
1234
- }
1235
- }
1236
- if (options.removeImports) {
1237
- linkElement.remove();
1238
- this.stats.add("discarded", "HTML imports", 1);
1239
- }
1240
- }));
1241
- }
1242
-
1243
1191
  removeUnusedStyles() {
1244
1192
  if (!this.mediaAllInfo) {
1245
1193
  this.mediaAllInfo = util.getMediaAllInfo(this.doc, this.stylesheets, this.styles);
@@ -1393,26 +1341,6 @@ class Processor {
1393
1341
  }
1394
1342
  }
1395
1343
 
1396
- async processHtmlImports() {
1397
- const linkElements = Array.from(this.doc.querySelectorAll("link[rel=import]"));
1398
- await Promise.all(linkElements.map(async linkElement => {
1399
- const attributeValue = linkElement.getAttribute(util.HTML_IMPORT_ATTRIBUTE_NAME);
1400
- if (attributeValue) {
1401
- const importData = this.options.imports[Number(attributeValue)];
1402
- if (importData.runner) {
1403
- this.stats.add("processed", "HTML imports", 1);
1404
- await importData.runner.run();
1405
- const pageData = await importData.runner.getPageData();
1406
- linkElement.removeAttribute(util.HTML_IMPORT_ATTRIBUTE_NAME);
1407
- linkElement.setAttribute("href", "data:text/html," + pageData.content);
1408
- this.stats.addAll(pageData);
1409
- } else {
1410
- this.stats.add("discarded", "HTML imports", 1);
1411
- }
1412
- }
1413
- }));
1414
- }
1415
-
1416
1344
  replaceStylesheets() {
1417
1345
  this.doc.querySelectorAll("style").forEach(styleElement => {
1418
1346
  const stylesheetInfo = this.stylesheets.get(styleElement);
@@ -2280,7 +2208,6 @@ const STATS_DEFAULT_VALUES = {
2280
2208
  discarded: {
2281
2209
  "HTML bytes": 0,
2282
2210
  "hidden elements": 0,
2283
- "HTML imports": 0,
2284
2211
  scripts: 0,
2285
2212
  objects: 0,
2286
2213
  "audio sources": 0,
@@ -2295,7 +2222,6 @@ const STATS_DEFAULT_VALUES = {
2295
2222
  processed: {
2296
2223
  "HTML bytes": 0,
2297
2224
  "hidden elements": 0,
2298
- "HTML imports": 0,
2299
2225
  scripts: 0,
2300
2226
  objects: 0,
2301
2227
  "audio sources": 0,
@@ -21,19 +21,4 @@
21
21
  * Source.
22
22
  */
23
23
 
24
- /* global globalThis */
25
-
26
- const browser = globalThis.browser;
27
- const document = globalThis.document;
28
- const Document = globalThis.Document;
29
-
30
- if (document instanceof Document) {
31
- const scriptElement = document.createElement("script");
32
- scriptElement.async = false;
33
- if (browser && browser.runtime && browser.runtime.getURL) {
34
- scriptElement.src = browser.runtime.getURL("/lib/single-file-hooks.js");
35
- scriptElement.async = false;
36
- }
37
- (document.documentElement || document).appendChild(scriptElement);
38
- scriptElement.remove();
39
- }
24
+ export * from "./processors/frame-tree/content/content-frame-tree.js";
@@ -39,7 +39,6 @@ const IMAGE_ATTRIBUTE_NAME = "data-single-file-image";
39
39
  const POSTER_ATTRIBUTE_NAME = "data-single-file-poster";
40
40
  const VIDEO_ATTRIBUTE_NAME = "data-single-file-video";
41
41
  const CANVAS_ATTRIBUTE_NAME = "data-single-file-canvas";
42
- const HTML_IMPORT_ATTRIBUTE_NAME = "data-single-file-import";
43
42
  const STYLE_ATTRIBUTE_NAME = "data-single-file-movable-style";
44
43
  const INPUT_VALUE_ATTRIBUTE_NAME = "data-single-file-input-value";
45
44
  const LAZY_SRC_ATTRIBUTE_NAME = "data-single-file-lazy-loaded-src";
@@ -90,7 +89,6 @@ export {
90
89
  CANVAS_ATTRIBUTE_NAME,
91
90
  INPUT_VALUE_ATTRIBUTE_NAME,
92
91
  SHADOW_ROOT_ATTRIBUTE_NAME,
93
- HTML_IMPORT_ATTRIBUTE_NAME,
94
92
  STYLE_ATTRIBUTE_NAME,
95
93
  LAZY_SRC_ATTRIBUTE_NAME,
96
94
  STYLESHEET_ATTRIBUTE_NAME,
@@ -165,7 +163,6 @@ function preProcessDoc(doc, win, options) {
165
163
  videos: [],
166
164
  usedFonts: [],
167
165
  shadowRoots: [],
168
- imports: [],
169
166
  markedElements: []
170
167
  };
171
168
  }
@@ -178,14 +175,13 @@ function preProcessDoc(doc, win, options) {
178
175
  videos: elementsInfo.videos,
179
176
  usedFonts: Array.from(elementsInfo.usedFonts.values()),
180
177
  shadowRoots: elementsInfo.shadowRoots,
181
- imports: elementsInfo.imports,
182
178
  referrer: doc.referrer,
183
179
  markedElements: elementsInfo.markedElements,
184
180
  invalidElements
185
181
  };
186
182
  }
187
183
 
188
- function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [], imports: [], markedElements: [] }, ascendantHidden) {
184
+ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [], markedElements: [] }, ascendantHidden) {
189
185
  const elements = Array.from(element.childNodes).filter(node => (node instanceof win.HTMLElement) || (node instanceof win.SVGElement));
190
186
  elements.forEach(element => {
191
187
  let elementHidden, elementKept, computedStyle;
@@ -329,13 +325,6 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
329
325
  data.markedElements.push(element);
330
326
  }
331
327
  }
332
- if (element.tagName == "LINK") {
333
- if (element.import && element.import.documentElement) {
334
- data.imports.push({ content: serialize(element.import) });
335
- element.setAttribute(HTML_IMPORT_ATTRIBUTE_NAME, data.imports.length - 1);
336
- data.markedElements.push(element);
337
- }
338
- }
339
328
  if (element.tagName == "INPUT") {
340
329
  if (element.type != "password") {
341
330
  element.setAttribute(INPUT_VALUE_ATTRIBUTE_NAME, element.value);
@@ -429,7 +418,7 @@ function postProcessDoc(doc, markedElements, invalidElements) {
429
418
  doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.removeAttribute("hidden"));
430
419
  }
431
420
  if (!markedElements) {
432
- const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME, HTML_IMPORT_ATTRIBUTE_NAME, STYLESHEET_ATTRIBUTE_NAME, ASYNC_SCRIPT_ATTRIBUTE_NAME];
421
+ const singleFileAttributes = [REMOVED_CONTENT_ATTRIBUTE_NAME, HIDDEN_FRAME_ATTRIBUTE_NAME, HIDDEN_CONTENT_ATTRIBUTE_NAME, PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, IMAGE_ATTRIBUTE_NAME, POSTER_ATTRIBUTE_NAME, VIDEO_ATTRIBUTE_NAME, CANVAS_ATTRIBUTE_NAME, INPUT_VALUE_ATTRIBUTE_NAME, SHADOW_ROOT_ATTRIBUTE_NAME, STYLESHEET_ATTRIBUTE_NAME, ASYNC_SCRIPT_ATTRIBUTE_NAME];
433
422
  markedElements = doc.querySelectorAll(singleFileAttributes.map(name => "[" + name + "]").join(","));
434
423
  }
435
424
  markedElements.forEach(element => {
@@ -444,7 +433,6 @@ function postProcessDoc(doc, markedElements, invalidElements) {
444
433
  element.removeAttribute(CANVAS_ATTRIBUTE_NAME);
445
434
  element.removeAttribute(INPUT_VALUE_ATTRIBUTE_NAME);
446
435
  element.removeAttribute(SHADOW_ROOT_ATTRIBUTE_NAME);
447
- element.removeAttribute(HTML_IMPORT_ATTRIBUTE_NAME);
448
436
  element.removeAttribute(STYLESHEET_ATTRIBUTE_NAME);
449
437
  element.removeAttribute(ASYNC_SCRIPT_ATTRIBUTE_NAME);
450
438
  element.removeAttribute(STYLE_ATTRIBUTE_NAME);
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2010-2022 Gildas Lormeau
3
+ * contact : gildas.lormeau <at> gmail.com
4
+ *
5
+ * This file is part of SingleFile.
6
+ *
7
+ * The code in this file is free software: you can redistribute it and/or
8
+ * modify it under the terms of the GNU Affero General Public License
9
+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
10
+ * of the License, or (at your option) any later version.
11
+ *
12
+ * The code in this file is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
15
+ * General Public License for more details.
16
+ *
17
+ * As additional permission under GNU AGPL version 3 section 7, you may
18
+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
19
+ * AGPL normally required by section 4, provided you include this license
20
+ * notice and a URL through which recipients can access the Corresponding
21
+ * Source.
22
+ */
23
+
24
+ import "./processors/hooks/content/content-hooks-frames-web.js";
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2010-2022 Gildas Lormeau
3
+ * contact : gildas.lormeau <at> gmail.com
4
+ *
5
+ * This file is part of SingleFile.
6
+ *
7
+ * The code in this file is free software: you can redistribute it and/or
8
+ * modify it under the terms of the GNU Affero General Public License
9
+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
10
+ * of the License, or (at your option) any later version.
11
+ *
12
+ * The code in this file is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
15
+ * General Public License for more details.
16
+ *
17
+ * As additional permission under GNU AGPL version 3 section 7, you may
18
+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
19
+ * AGPL normally required by section 4, provided you include this license
20
+ * notice and a URL through which recipients can access the Corresponding
21
+ * Source.
22
+ */
23
+
24
+ import "./processors/hooks/content/content-hooks-web.js";
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Copyright 2010-2022 Gildas Lormeau
3
+ * contact : gildas.lormeau <at> gmail.com
4
+ *
5
+ * This file is part of SingleFile.
6
+ *
7
+ * The code in this file is free software: you can redistribute it and/or
8
+ * modify it under the terms of the GNU Affero General Public License
9
+ * (GNU AGPL) as published by the Free Software Foundation, either version 3
10
+ * of the License, or (at your option) any later version.
11
+ *
12
+ * The code in this file is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
15
+ * General Public License for more details.
16
+ *
17
+ * As additional permission under GNU AGPL version 3 section 7, you may
18
+ * distribute UNMODIFIED VERSIONS OF THIS file without the copy of the GNU
19
+ * AGPL normally required by section 4, provided you include this license
20
+ * notice and a URL through which recipients can access the Corresponding
21
+ * Source.
22
+ */
23
+
24
+ import "./common/content-infobar-web.js";
@@ -179,7 +179,6 @@ function getInstance(utilOptions) {
179
179
  POSTER_ATTRIBUTE_NAME: helper.POSTER_ATTRIBUTE_NAME,
180
180
  VIDEO_ATTRIBUTE_NAME: helper.VIDEO_ATTRIBUTE_NAME,
181
181
  CANVAS_ATTRIBUTE_NAME: helper.CANVAS_ATTRIBUTE_NAME,
182
- HTML_IMPORT_ATTRIBUTE_NAME: helper.HTML_IMPORT_ATTRIBUTE_NAME,
183
182
  STYLE_ATTRIBUTE_NAME: helper.STYLE_ATTRIBUTE_NAME,
184
183
  INPUT_VALUE_ATTRIBUTE_NAME: helper.INPUT_VALUE_ATTRIBUTE_NAME,
185
184
  SHADOW_ROOT_ATTRIBUTE_NAME: helper.SHADOW_ROOT_ATTRIBUTE_NAME,
File without changes
@@ -48,6 +48,7 @@
48
48
  * SOFTWARE.
49
49
  */
50
50
 
51
+ import * as cssTree from "./css-tree.js";
51
52
  const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
52
53
  const REGEXP_DOUBLE_QUOTES_STRING = /^"(.*?)"$/;
53
54
 
@@ -100,28 +101,6 @@ const fontStretchKeywords = [
100
101
  "ultra-expanded"
101
102
  ];
102
103
 
103
- const cssFontSizeKeywords = [
104
- "xx-small",
105
- "x-small",
106
- "small",
107
- "medium",
108
- "large",
109
- "x-large",
110
- "xx-large",
111
- "larger",
112
- "smaller"
113
- ];
114
-
115
- const cssListHelpers = {
116
- splitBySpaces,
117
- split,
118
- splitByCommas
119
- };
120
-
121
- const helpers = {
122
- isSize
123
- };
124
-
125
104
  const errorPrefix = "[parse-css-font] ";
126
105
 
127
106
  export {
@@ -129,15 +108,11 @@ export {
129
108
  };
130
109
 
131
110
  function parse(value) {
132
- if (typeof value !== "string") {
133
- throw new TypeError(errorPrefix + "Expected a string.");
134
- }
135
- if (value === "") {
136
- throw error("Cannot parse an empty string.");
137
- }
138
- if (systemFontKeywords.indexOf(value) !== -1) {
139
- return { system: value };
111
+ const stringValue = cssTree.generate(value);
112
+ if (systemFontKeywords.indexOf(stringValue) !== -1) {
113
+ return { system: stringValue };
140
114
  }
115
+ const tokens = value.children;
141
116
 
142
117
  const font = {
143
118
  lineHeight: "normal",
@@ -148,10 +123,8 @@ function parse(value) {
148
123
  };
149
124
 
150
125
  let isLocked = false;
151
- const tokens = cssListHelpers.splitBySpaces(value);
152
- let token = tokens.shift();
153
- for (; token; token = tokens.shift()) {
154
-
126
+ for (let tokenNode = tokens.head; tokenNode; tokenNode = tokenNode.next) {
127
+ const token = cssTree.generate(tokenNode.data);
155
128
  if (token === "normal" || globalKeywords.indexOf(token) !== -1) {
156
129
  ["style", "variant", "weight", "stretch"].forEach((prop) => {
157
130
  font[prop] = token;
@@ -184,19 +157,29 @@ function parse(value) {
184
157
  continue;
185
158
  }
186
159
 
187
- if (helpers.isSize(token)) {
188
- const parts = cssListHelpers.split(token, ["/"]);
189
- font.size = parts[0];
190
- if (parts[1]) {
191
- font.lineHeight = parseLineHeight(parts[1]);
192
- } else if (tokens[0] === "/") {
193
- tokens.shift();
194
- font.lineHeight = parseLineHeight(tokens.shift());
160
+ if (tokenNode.data.type == "Dimension") {
161
+ font.size = cssTree.generate(tokenNode.data);
162
+ tokenNode = tokenNode.next;
163
+ if (tokenNode && tokenNode.data.type == "Operator" && tokenNode.data.value == "/" && tokenNode.next) {
164
+ tokenNode = tokenNode.next;
165
+ font.lineHeight = cssTree.generate(tokenNode.data);
166
+ tokenNode = tokenNode.next;
167
+ } else if (tokens.head.data.type == "Operator" && tokens.head.data.value == "/" && tokens.head.next) {
168
+ font.lineHeight = cssTree.generate(tokens.head.next.data);
169
+ tokenNode = tokens.head.next.next;
195
170
  }
196
- if (!tokens.length) {
171
+ if (!tokenNode) {
197
172
  throw error("Missing required font-family.");
198
173
  }
199
- font.family = cssListHelpers.splitByCommas(tokens.join(" ")).map(removeQuotes);
174
+ font.family = [];
175
+ for (; tokenNode; tokenNode = tokenNode.next) {
176
+ while (tokenNode && tokenNode.data.type == "Operator" && tokenNode.data.value == ",") {
177
+ tokenNode = tokenNode.next;
178
+ }
179
+ if (tokenNode) {
180
+ font.family.push(removeQuotes(cssTree.generate(tokenNode.data)));
181
+ }
182
+ }
200
183
  return font;
201
184
  }
202
185
 
@@ -217,122 +200,6 @@ function error(message) {
217
200
  return new Error(errorPrefix + message);
218
201
  }
219
202
 
220
- function parseLineHeight(value) {
221
- const parsed = parseFloat(value);
222
- if (parsed.toString() === value) {
223
- return parsed;
224
- }
225
- return value;
226
- }
227
-
228
- /**
229
- * Splits a CSS declaration value (shorthand) using provided separators
230
- * as the delimiters.
231
- */
232
- function split(
233
- /**
234
- * A CSS declaration value (shorthand).
235
- */
236
- value,
237
- /**
238
- * Any number of separator characters used for splitting.
239
- */
240
- separators,
241
- {
242
- last = false,
243
- } = {},
244
- ) {
245
- if (typeof value !== "string") {
246
- throw new TypeError("expected a string");
247
- }
248
- if (!Array.isArray(separators)) {
249
- throw new TypeError("expected a string array of separators");
250
- }
251
- if (typeof last !== "boolean") {
252
- throw new TypeError("expected a Boolean value for options.last");
253
- }
254
- const array = [];
255
- let current = "";
256
- let splitMe = false;
257
-
258
- let func = 0;
259
- let quote = false;
260
- let escape = false;
261
-
262
- for (const char of value) {
263
-
264
- if (quote) {
265
- if (escape) {
266
- escape = false;
267
- } else if (char === "\\") {
268
- escape = true;
269
- } else if (char === quote) {
270
- quote = false;
271
- }
272
- } else if (char === "\"" || char === "'") {
273
- quote = char;
274
- } else if (char === "(") {
275
- func += 1;
276
- } else if (char === ")") {
277
- if (func > 0) {
278
- func -= 1;
279
- }
280
- } else if (func === 0) {
281
- if (separators.indexOf(char) !== -1) {
282
- splitMe = true;
283
- }
284
- }
285
-
286
- if (splitMe) {
287
- if (current !== "") {
288
- array.push(current.trim());
289
- }
290
- current = "";
291
- splitMe = false;
292
- } else {
293
- current += char;
294
- }
295
- }
296
-
297
- if (last || current !== "") {
298
- array.push(current.trim());
299
- }
300
- return array;
301
- }
302
-
303
- /**
304
- * Splits a CSS declaration value (shorthand) using whitespace characters
305
- * as the delimiters.
306
- */
307
- function splitBySpaces(
308
- /**
309
- * A CSS declaration value (shorthand).
310
- */
311
- value,
312
- ) {
313
- const spaces = [" ", "\n", "\t"];
314
- return split(value, spaces);
315
- }
316
-
317
- /**
318
- * Splits a CSS declaration value (shorthand) using commas as the delimiters.
319
- */
320
- function splitByCommas(
321
- /**
322
- * A CSS declaration value (shorthand).
323
- */
324
- value,
325
- ) {
326
- const comma = ",";
327
- return split(value, [comma], { last: true });
328
- }
329
-
330
- function isSize(value) {
331
- return !isNaN(parseFloat(value))
332
- || value.indexOf("/") !== -1
333
- || cssFontSizeKeywords.indexOf(value) !== -1;
334
- }
335
-
336
203
  function removeQuotes(string) {
337
204
  if (string.match(REGEXP_SIMPLE_QUOTES_STRING)) {
338
205
  string = string.replace(REGEXP_SIMPLE_QUOTES_STRING, "$1");