single-file-core 1.6.4 → 1.6.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 +174 -37
- package/core/index.js +56 -32
- package/core/infobar.js +55 -47
- package/core/lib/processor-helper-common.js +26 -5
- package/core/lib/processor-helper-inline.js +115 -0
- package/core/lib/processor-helper.js +86 -60
- package/modules/css-fonts-minifier.js +139 -7
- package/modules/css-rules-minifier.js +32 -6
- package/modules/css-selector-sanitizer.js +12 -0
- package/package.json +1 -1
- package/processors/compression/compression-packager.js +8 -0
- package/processors/compression/compression.js +33 -11
- package/processors/frame-tree/content/content-frame-tree.js +2 -0
- package/processors/hooks/content/content-hooks-frames-web.js +3 -1
- package/single-file-infobar.js +2 -1
package/core/infobar.js
CHANGED
|
@@ -68,26 +68,6 @@ const INFOBAR_STYLES = `
|
|
|
68
68
|
border-color: #eee;
|
|
69
69
|
border-radius: 16px;
|
|
70
70
|
z-index: 2147483647;
|
|
71
|
-
animation-name: flash;
|
|
72
|
-
animation-duration: .5s;
|
|
73
|
-
animation-timing-function: cubic-bezier(0.39, 0.58, 0.57, 1);
|
|
74
|
-
animation-delay: 1s;
|
|
75
|
-
animation-iteration-count: 2;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.infobar:not(:focus-within):not(.infobar-focus)::after {
|
|
79
|
-
content: "";
|
|
80
|
-
position: absolute;
|
|
81
|
-
inset: -2px;
|
|
82
|
-
border: 2px solid #dd6a00;
|
|
83
|
-
border-radius: inherit;
|
|
84
|
-
opacity: 0;
|
|
85
|
-
pointer-events: none;
|
|
86
|
-
animation-name: ripple;
|
|
87
|
-
animation-duration: 3s;
|
|
88
|
-
animation-timing-function: ease-out;
|
|
89
|
-
animation-delay: 2s;
|
|
90
|
-
animation-iteration-count: 3;
|
|
91
71
|
}
|
|
92
72
|
|
|
93
73
|
.infobar:valid, .infobar:not(:focus-within):not(.infobar-focus) .infobar-content {
|
|
@@ -139,33 +119,6 @@ const INFOBAR_STYLES = `
|
|
|
139
119
|
min-block-size: 24px;
|
|
140
120
|
}
|
|
141
121
|
|
|
142
|
-
@keyframes flash {
|
|
143
|
-
0%, 100% {
|
|
144
|
-
background-color: #737373;
|
|
145
|
-
}
|
|
146
|
-
50% {
|
|
147
|
-
background-color: #dd6a00;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
@keyframes ripple {
|
|
152
|
-
0% {
|
|
153
|
-
transform: scale(1);
|
|
154
|
-
opacity: 1;
|
|
155
|
-
}
|
|
156
|
-
45%, 100% {
|
|
157
|
-
transform: scale(2);
|
|
158
|
-
opacity: 0;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
@media (prefers-reduced-motion: reduce) {
|
|
163
|
-
.infobar,
|
|
164
|
-
.infobar:not(:focus-within):not(.infobar-focus)::after {
|
|
165
|
-
animation-name: none;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
122
|
.infobar:focus-within .infobar-icon, .infobar.infobar-focus .infobar-icon {
|
|
170
123
|
z-index: -1;
|
|
171
124
|
background-image: none;
|
|
@@ -195,6 +148,57 @@ const INFOBAR_STYLES = `
|
|
|
195
148
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAgMAAADXB5lNAAABhmlDQ1BJQ0MgcHJvZmlsZQAAKJF9kj1Iw0AYht+mSkUrDnYQcchQnSyIijqWKhbBQmkrtOpgcukfNGlIUlwcBdeCgz+LVQcXZ10dXAVB8AfEydFJ0UVK/C4ptIjx4LiH9+59+e67A4RGhalm1wSgapaRisfEbG5VDLyiDwEAvZiVmKkn0osZeI6ve/j4ehfhWd7n/hz9St5kgE8kjjLdsIg3iGc2LZ3zPnGIlSSF+Jx43KACiR+5Lrv8xrnosMAzQ0YmNU8cIhaLHSx3MCsZKvE0cVhRNcoXsi4rnLc4q5Uaa9XJbxjMaytprtMcQRxLSCAJETJqKKMCCxFaNVJMpGg/5uEfdvxJcsnkKoORYwFVqJAcP/gb/O6tWZiadJOCMaD7xbY/RoHALtCs2/b3sW03TwD/M3Cltf3VBjD3SXq9rYWPgIFt4OK6rcl7wOUOMPSkS4bkSH6aQqEAvJ/RM+WAwVv6EGtu31r7OH0AMtSr5Rvg4BAYK1L2use9ezr79u+ZVv9+AFlNcp0UUpiqAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5AsHAB8VC4EQ6QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAJUExURQAAAICHi4qKioTuJAkAAAABdFJOUwBA5thmAAAAAWJLR0QCZgt8ZAAAAJtJREFUOI3NkrsBgCAMRLFwBPdxBArcfxXFkO8rbKWAAJfHJ9faf9vuYX/749T5NmShm3bEwbe2SxeuM4+2oxDL1cDoKtVUjRy+tH78Cv2CS+wIiQNC1AEhk4AQeUTMWUJMfUJMSEJMSEY8kIx4IONroaYAimNxsXp1PA7PxwfVL8QnowwoVC0lig07wDDVUjAdbAnjwtow/z/bDW7eI4M2KruJAAAAAElFTkSuQmCC);
|
|
196
149
|
}
|
|
197
150
|
`;
|
|
151
|
+
const INFOBAR_ANIMATIONS_STYLES = `
|
|
152
|
+
.infobar {
|
|
153
|
+
animation-name: flash;
|
|
154
|
+
animation-duration: .5s;
|
|
155
|
+
animation-timing-function: cubic-bezier(0.39, 0.58, 0.57, 1);
|
|
156
|
+
animation-delay: 1s;
|
|
157
|
+
animation-iteration-count: 2;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.infobar:not(:focus-within):not(.infobar-focus)::after {
|
|
161
|
+
content: "";
|
|
162
|
+
position: absolute;
|
|
163
|
+
inset: -2px;
|
|
164
|
+
border: 2px solid #dd6a00;
|
|
165
|
+
border-radius: inherit;
|
|
166
|
+
opacity: 0;
|
|
167
|
+
pointer-events: none;
|
|
168
|
+
animation-name: ripple;
|
|
169
|
+
animation-duration: 3s;
|
|
170
|
+
animation-timing-function: ease-out;
|
|
171
|
+
animation-delay: 2s;
|
|
172
|
+
animation-iteration-count: 3;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@keyframes flash {
|
|
176
|
+
0%, 100% {
|
|
177
|
+
background-color: #737373;
|
|
178
|
+
}
|
|
179
|
+
50% {
|
|
180
|
+
background-color: #dd6a00;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@keyframes ripple {
|
|
185
|
+
0% {
|
|
186
|
+
transform: scale(1);
|
|
187
|
+
opacity: 1;
|
|
188
|
+
}
|
|
189
|
+
45%, 100% {
|
|
190
|
+
transform: scale(2);
|
|
191
|
+
opacity: 0;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@media (prefers-reduced-motion: reduce) {
|
|
196
|
+
.infobar,
|
|
197
|
+
.infobar:not(:focus-within):not(.infobar-focus)::after {
|
|
198
|
+
animation-name: none;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
`;
|
|
198
202
|
|
|
199
203
|
export { displayIcon, appendInfobar, refreshInfobarInfo, extractInfobarData, INFOBAR_TAGNAME };
|
|
200
204
|
|
|
@@ -221,6 +225,9 @@ function appendInfobar(doc, options, useShadowRoot) {
|
|
|
221
225
|
const shadowRootContent = doc.createElement("div");
|
|
222
226
|
const styleElement = doc.createElement("style");
|
|
223
227
|
styleElement.textContent = INFOBAR_STYLES;
|
|
228
|
+
if (options.animateInfobar) {
|
|
229
|
+
styleElement.textContent += INFOBAR_ANIMATIONS_STYLES;
|
|
230
|
+
}
|
|
224
231
|
if (options.infobarPositionAbsolute) {
|
|
225
232
|
styleElement.textContent += ".infobar { position: absolute; }";
|
|
226
233
|
const parentElementStyle = getComputedStyle(parentElement);
|
|
@@ -332,6 +339,7 @@ function displayIcon(doc, useShadowRoot, options = {}) {
|
|
|
332
339
|
const infoData = extractInfobarData(doc);
|
|
333
340
|
if (infoData.saveUrl) {
|
|
334
341
|
infoData.openInfobar = options.openInfobar;
|
|
342
|
+
infoData.animateInfobar = options.animateInfobar;
|
|
335
343
|
infoData.infobarPositionAbsolute = options.infobarPositionAbsolute;
|
|
336
344
|
infoData.infobarPositionTop = options.infobarPositionTop;
|
|
337
345
|
infoData.infobarPositionRight = options.infobarPositionRight;
|
|
@@ -414,6 +414,7 @@ class ProcessorHelperCommon {
|
|
|
414
414
|
sheetIndex++;
|
|
415
415
|
});
|
|
416
416
|
processFontDetails(fontsDetails, fonts);
|
|
417
|
+
this.markRepeatedFontFaceRules(fontsDetails);
|
|
417
418
|
await Promise.all([...stylesheets].map(async ([, stylesheetInfo], sheetIndex) => {
|
|
418
419
|
if (stylesheetInfo.stylesheet) {
|
|
419
420
|
const cssRules = stylesheetInfo.stylesheet.children;
|
|
@@ -431,6 +432,21 @@ class ProcessorHelperCommon {
|
|
|
431
432
|
return stats;
|
|
432
433
|
}
|
|
433
434
|
|
|
435
|
+
markRepeatedFontFaceRules(fontsDetails) {
|
|
436
|
+
const lastRules = new Map();
|
|
437
|
+
fontsDetails.fonts.forEach((fontInfo, ruleData) => {
|
|
438
|
+
const ruleKey = this.getFontKey(ruleData) + " " + this.getPropertyValue(ruleData, "src");
|
|
439
|
+
const previousRuleData = lastRules.get(ruleKey);
|
|
440
|
+
if (previousRuleData) {
|
|
441
|
+
fontsDetails.repeatedFonts.add(previousRuleData);
|
|
442
|
+
}
|
|
443
|
+
lastRules.set(ruleKey, ruleData);
|
|
444
|
+
});
|
|
445
|
+
fontsDetails.medias.forEach(mediaFontsDetails => this.markRepeatedFontFaceRules(mediaFontsDetails));
|
|
446
|
+
fontsDetails.supports.forEach(supportsFontsDetails => this.markRepeatedFontFaceRules(supportsFontsDetails));
|
|
447
|
+
fontsDetails.layers.forEach(layerFontsDetails => this.markRepeatedFontFaceRules(layerFontsDetails));
|
|
448
|
+
}
|
|
449
|
+
|
|
434
450
|
async processFontFaceRules(cssRules, sheetIndex, fontsDetails, fonts, fontTests, stats) {
|
|
435
451
|
const removedRules = [];
|
|
436
452
|
let mediaIndex = 0, supportsIndex = 0, layerIndex = 0;
|
|
@@ -451,11 +467,9 @@ class ProcessorHelperCommon {
|
|
|
451
467
|
} else if (ruleData.type == "Atrule" && ruleData.name == "font-face") {
|
|
452
468
|
const fontInfo = fontsDetails.fonts.get(ruleData);
|
|
453
469
|
if (fontInfo) {
|
|
454
|
-
|
|
455
|
-
if (fontsDetails.emittedFonts.has(ruleKey)) {
|
|
470
|
+
if (fontsDetails.repeatedFonts.has(ruleData)) {
|
|
456
471
|
removedRules.push(cssRule);
|
|
457
472
|
} else {
|
|
458
|
-
fontsDetails.emittedFonts.add(ruleKey);
|
|
459
473
|
const keptRule = await this.processFontFaceRule(ruleData, fontInfo, fonts, fontTests, stats);
|
|
460
474
|
if (!keptRule) {
|
|
461
475
|
removedRules.push(cssRule);
|
|
@@ -469,6 +483,9 @@ class ProcessorHelperCommon {
|
|
|
469
483
|
removedRules.forEach(cssRule => cssRules.remove(cssRule));
|
|
470
484
|
}
|
|
471
485
|
|
|
486
|
+
groupDuplicateFonts() {
|
|
487
|
+
}
|
|
488
|
+
|
|
472
489
|
getFontsDetails(doc, cssRules, sheetIndex, mediaFontsDetails) {
|
|
473
490
|
let mediaIndex = 0, supportsIndex = 0, layerIndex = 0;
|
|
474
491
|
cssRules.forEach(ruleData => {
|
|
@@ -516,7 +533,7 @@ class ProcessorHelperCommon {
|
|
|
516
533
|
medias: new Map(),
|
|
517
534
|
supports: new Map(),
|
|
518
535
|
layers: new Map(),
|
|
519
|
-
|
|
536
|
+
repeatedFonts: new Set()
|
|
520
537
|
};
|
|
521
538
|
}
|
|
522
539
|
|
|
@@ -529,7 +546,11 @@ class ProcessorHelperCommon {
|
|
|
529
546
|
getFontStretch(this.getPropertyValue(ruleData, "font-stretch")),
|
|
530
547
|
this.getPropertyValue(ruleData, "font-variant") || "normal",
|
|
531
548
|
this.getPropertyValue(ruleData, "font-feature-settings"),
|
|
532
|
-
this.getPropertyValue(ruleData, "font-variation-settings")
|
|
549
|
+
this.getPropertyValue(ruleData, "font-variation-settings"),
|
|
550
|
+
this.getPropertyValue(ruleData, "ascent-override"),
|
|
551
|
+
this.getPropertyValue(ruleData, "descent-override"),
|
|
552
|
+
this.getPropertyValue(ruleData, "line-gap-override"),
|
|
553
|
+
this.getPropertyValue(ruleData, "size-adjust")
|
|
533
554
|
]);
|
|
534
555
|
}
|
|
535
556
|
|
|
@@ -33,6 +33,13 @@ const Image = globalThis.Image;
|
|
|
33
33
|
|
|
34
34
|
const ABOUT_BLANK_URI = "about:blank";
|
|
35
35
|
const UTF8_CHARSET = "utf-8";
|
|
36
|
+
const DATA_URI_PREFIX = "data:";
|
|
37
|
+
const DEFAULT_FONT_WEIGHT = 400;
|
|
38
|
+
const FONT_KEY_FAMILY_INDEX = 0;
|
|
39
|
+
const FONT_KEY_WEIGHT_INDEX = 1;
|
|
40
|
+
const FONT_KEY_STYLE_INDEX = 2;
|
|
41
|
+
const FONT_KEY_STRETCH_INDEX = 4;
|
|
42
|
+
const NESTED_AT_RULE_NAMES = ["media", "supports", "layer", "container"];
|
|
36
43
|
const PREFIX_DATA_URI_IMAGE_SVG = "data:image/svg+xml";
|
|
37
44
|
const PREFIXES_FORBIDDEN_DATA_URI = ["data:text/"];
|
|
38
45
|
const SCRIPT_TAG_FOUND = /<script/gi;
|
|
@@ -65,6 +72,7 @@ import {
|
|
|
65
72
|
resizeImage,
|
|
66
73
|
toDataURI
|
|
67
74
|
} from "./processor-helper-common.js";
|
|
75
|
+
import { getFontWeight } from "./../helper.js";
|
|
68
76
|
|
|
69
77
|
export {
|
|
70
78
|
getProcessorHelperClass,
|
|
@@ -569,6 +577,97 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
569
577
|
doc.querySelectorAll("link[rel*=stylesheet][rel*=alternate][title]").forEach(element => element.remove());
|
|
570
578
|
}
|
|
571
579
|
|
|
580
|
+
groupDuplicateFonts(stylesheets, fonts, options) {
|
|
581
|
+
if (options.usedFonts && options.usedFonts.length) {
|
|
582
|
+
const fontFaces = [];
|
|
583
|
+
stylesheets.forEach(stylesheetInfo => {
|
|
584
|
+
if (stylesheetInfo.stylesheet && stylesheetInfo.stylesheet.children) {
|
|
585
|
+
getFontFaces(stylesheetInfo.stylesheet.children, "", fontFaces);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
const families = new Map();
|
|
589
|
+
fontFaces.forEach(fontFace => {
|
|
590
|
+
const fontKey = JSON.parse(this.getFontKey(fontFace.ruleData));
|
|
591
|
+
const familyKey = fontFace.scope + JSON.stringify([fontKey[FONT_KEY_FAMILY_INDEX],
|
|
592
|
+
fontKey[FONT_KEY_STYLE_INDEX], fontKey[FONT_KEY_STRETCH_INDEX]]);
|
|
593
|
+
let family = families.get(familyKey);
|
|
594
|
+
if (!family) {
|
|
595
|
+
family = { name: fontKey[FONT_KEY_FAMILY_INDEX], groups: new Map(), mergeable: true };
|
|
596
|
+
families.set(familyKey, family);
|
|
597
|
+
}
|
|
598
|
+
const source = this.getPropertyValue(fontFace.ruleData, "src");
|
|
599
|
+
const range = this.getFontWeightRange(fontFace.ruleData);
|
|
600
|
+
if (source && source.includes(DATA_URI_PREFIX) && range) {
|
|
601
|
+
fontKey[FONT_KEY_WEIGHT_INDEX] = null;
|
|
602
|
+
const groupKey = JSON.stringify(fontKey) + " " + source;
|
|
603
|
+
const group = family.groups.get(groupKey);
|
|
604
|
+
if (group) {
|
|
605
|
+
group.push({ fontFace, range });
|
|
606
|
+
} else {
|
|
607
|
+
family.groups.set(groupKey, [{ fontFace, range }]);
|
|
608
|
+
}
|
|
609
|
+
} else {
|
|
610
|
+
family.mergeable = false;
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
families.forEach(family => this.mergeFontFaceWeights(family, options.usedFonts));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Every group of the family has to be merged or none of it: two groups that differ only in
|
|
618
|
+
// unicode-range are one composite face, and merging one of them alone would leave a weight
|
|
619
|
+
// matched by two faces that are no longer a composite, which CSS Fonts 4 §5.2 resolves in a way
|
|
620
|
+
// that "can differ between multiple user agents". Requiring every group to span the same set of
|
|
621
|
+
// weights is what keeps the merged faces a composite, and it also blocks the case where some
|
|
622
|
+
// other face of the family sits inside the interval, since that face is a group of its own.
|
|
623
|
+
mergeFontFaceWeights(family, usedFonts) {
|
|
624
|
+
const groups = Array.from(family.groups.values());
|
|
625
|
+
const weightKey = group => group.map(({ range }) => range.join("-")).sort().join(",");
|
|
626
|
+
if (family.mergeable && groups.some(group => group.length > 1) &&
|
|
627
|
+
groups.every(group => weightKey(group) == weightKey(groups[0]))) {
|
|
628
|
+
const ranges = groups[0].map(({ range }) => range);
|
|
629
|
+
const minWeight = Math.min(...ranges.map(([min]) => min));
|
|
630
|
+
const maxWeight = Math.max(...ranges.map(([, max]) => max));
|
|
631
|
+
const declared = weight => ranges.some(([min, max]) => weight >= min && weight <= max);
|
|
632
|
+
const usedWeightInRange = usedFonts.some(([usedFamily, usedWeight]) => {
|
|
633
|
+
const weight = Number(usedWeight);
|
|
634
|
+
return usedFamily == family.name && weight > minWeight && weight < maxWeight && !declared(weight);
|
|
635
|
+
});
|
|
636
|
+
if (!usedWeightInRange) {
|
|
637
|
+
groups.forEach(group => {
|
|
638
|
+
const kept = group[group.length - 1];
|
|
639
|
+
this.setFontWeightRange(kept.fontFace.ruleData, minWeight, maxWeight);
|
|
640
|
+
group.forEach(({ fontFace }) => {
|
|
641
|
+
if (fontFace != kept.fontFace) {
|
|
642
|
+
fontFace.cssRules.remove(fontFace.cssRule);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
getFontWeightRange(ruleData) {
|
|
651
|
+
const value = this.getPropertyValue(ruleData, "font-weight");
|
|
652
|
+
if (value === undefined) {
|
|
653
|
+
return [DEFAULT_FONT_WEIGHT, DEFAULT_FONT_WEIGHT];
|
|
654
|
+
}
|
|
655
|
+
const weights = value.trim().split(/\s+/).map(weight => Number(getFontWeight(util.removeQuotes(weight))));
|
|
656
|
+
if (weights.length <= 2 && weights.every(weight => Number.isFinite(weight))) {
|
|
657
|
+
return [Math.min(...weights), Math.max(...weights)];
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
setFontWeightRange(ruleData, minWeight, maxWeight) {
|
|
662
|
+
const value = cssTree.parse(minWeight == maxWeight ? String(minWeight) : minWeight + " " + maxWeight, { context: "value" });
|
|
663
|
+
const declaration = ruleData.block.children.filter(node => node.property == "font-weight").tail;
|
|
664
|
+
if (declaration) {
|
|
665
|
+
declaration.data.value = value;
|
|
666
|
+
} else {
|
|
667
|
+
ruleData.block.children.appendData({ type: "Declaration", property: "font-weight", important: false, value });
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
572
671
|
async processFontFaceRule(ruleData, fontInfo, fontDeclarations, fontTests, stats) {
|
|
573
672
|
const removedNodes = [];
|
|
574
673
|
for (let node = ruleData.block.children.head; node; node = node.next) {
|
|
@@ -662,4 +761,20 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
662
761
|
return true;
|
|
663
762
|
}
|
|
664
763
|
};
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
function getFontFaces(cssRules, scope, fontFaces) {
|
|
767
|
+
for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
|
|
768
|
+
const ruleData = cssRule.data;
|
|
769
|
+
if (ruleData.type == "Atrule" && ruleData.name == "import" && ruleData.prelude && ruleData.prelude.children &&
|
|
770
|
+
ruleData.prelude.children.head.data.importedChildren) {
|
|
771
|
+
getFontFaces(ruleData.prelude.children.head.data.importedChildren,
|
|
772
|
+
scope + "|import " + cssTree.generate(ruleData.prelude), fontFaces);
|
|
773
|
+
} else if (ruleData.type == "Atrule" && NESTED_AT_RULE_NAMES.includes(ruleData.name) && ruleData.block && ruleData.block.children) {
|
|
774
|
+
getFontFaces(ruleData.block.children,
|
|
775
|
+
scope + "|" + ruleData.name + " " + (ruleData.prelude ? cssTree.generate(ruleData.prelude) : ""), fontFaces);
|
|
776
|
+
} else if (ruleData.type == "Atrule" && ruleData.name == "font-face" && ruleData.block && ruleData.block.children) {
|
|
777
|
+
fontFaces.push({ ruleData, cssRule, cssRules, scope });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
665
780
|
}
|
|
@@ -114,7 +114,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
114
114
|
const stylesheet = sharedEntry
|
|
115
115
|
? sharedEntry[1].stylesheet
|
|
116
116
|
: cssTree.parse(content, { context: "stylesheet", parseCustomProperty: true });
|
|
117
|
-
resources.stylesheets.set(resources.stylesheets.size, { name,
|
|
117
|
+
resources.stylesheets.set(resources.stylesheets.size, { name, stylesheet });
|
|
118
118
|
linkElements.set(stylesheetRefIndex, linkElement);
|
|
119
119
|
sharedStyleElements.set(styleElement, stylesheetRefIndex);
|
|
120
120
|
});
|
|
@@ -194,35 +194,23 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
194
194
|
if (supportsNode) {
|
|
195
195
|
supportsCondition = cssTree.generate(supportsNode);
|
|
196
196
|
}
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const content = await this.getStylesheetContent(resourceURL, options);
|
|
215
|
-
stylesheetInfo.url = resourceURL = content.resourceURL;
|
|
216
|
-
content.data = getUpdatedResourceContent(resourceURL, options) || content.data;
|
|
217
|
-
stylesheetInfo.stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
|
|
218
|
-
stylesheet = stylesheetInfo.stylesheet;
|
|
219
|
-
const ancestorStyleSheets = new Set(importedStyleSheets);
|
|
220
|
-
ancestorStyleSheets.add(requestedURL);
|
|
221
|
-
ancestorStyleSheets.add(resourceURL);
|
|
222
|
-
await this.resolveImportURLs(stylesheetInfo, resourceURL, options, workStylesheet, resources, stylesheets, ancestorStyleSheets);
|
|
223
|
-
stylesheets.set({ urlNode }, stylesheetInfo);
|
|
224
|
-
}
|
|
225
|
-
urlNode.importedChildren = stylesheet.children;
|
|
197
|
+
const stylesheetInfo = {
|
|
198
|
+
scoped,
|
|
199
|
+
mediaText,
|
|
200
|
+
layerName,
|
|
201
|
+
supportsCondition
|
|
202
|
+
};
|
|
203
|
+
stylesheets.set({ urlNode }, stylesheetInfo);
|
|
204
|
+
const requestedURL = resourceURL;
|
|
205
|
+
const content = await this.getStylesheetContent(resourceURL, options);
|
|
206
|
+
stylesheetInfo.url = resourceURL = content.resourceURL;
|
|
207
|
+
content.data = getUpdatedResourceContent(resourceURL, options) || content.data;
|
|
208
|
+
stylesheetInfo.stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
|
|
209
|
+
const ancestorStyleSheets = new Set(importedStyleSheets);
|
|
210
|
+
ancestorStyleSheets.add(requestedURL);
|
|
211
|
+
ancestorStyleSheets.add(resourceURL);
|
|
212
|
+
await this.resolveImportURLs(stylesheetInfo, resourceURL, options, workStylesheet, resources, stylesheets, ancestorStyleSheets);
|
|
213
|
+
urlNode.importedChildren = stylesheetInfo.stylesheet.children;
|
|
226
214
|
urlNode.importedMediaText = mediaText;
|
|
227
215
|
urlNode.importedLayerName = layerName;
|
|
228
216
|
urlNode.importedSupportsCondition = supportsCondition;
|
|
@@ -235,36 +223,27 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
235
223
|
async resolveLinkStylesheetURLs(stylesheetInfo, element, resourceURL, baseURI, options, workStylesheet, resources, stylesheets) {
|
|
236
224
|
resourceURL = normalizeURL(resourceURL);
|
|
237
225
|
if (resourceURL && resourceURL != baseURI && resourceURL != ABOUT_BLANK_URI) {
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
226
|
+
const content = await util.getContent(resourceURL, {
|
|
227
|
+
maxResourceSize: options.maxResourceSize,
|
|
228
|
+
maxResourceSizeEnabled: options.maxResourceSizeEnabled,
|
|
229
|
+
charset: options.charset,
|
|
230
|
+
frameId: options.frameId,
|
|
231
|
+
resourceReferrer: options.resourceReferrer,
|
|
232
|
+
validateTextContentType: true,
|
|
233
|
+
baseURI: baseURI,
|
|
234
|
+
blockMixedContent: options.blockMixedContent,
|
|
235
|
+
expectedType: "stylesheet",
|
|
236
|
+
acceptHeaders: options.acceptHeaders,
|
|
237
|
+
networkTimeout: options.networkTimeout
|
|
238
|
+
});
|
|
239
|
+
if (!(matchCharsetEquals(content.data, content.charset) || matchCharsetEquals(content.data, options.charset))) {
|
|
240
|
+
options = Object.assign({}, options, { charset: getCharset(content.data) });
|
|
241
|
+
await this.resolveLinkStylesheetURLs(stylesheetInfo, element, resourceURL, baseURI, options, workStylesheet, resources, stylesheets);
|
|
245
242
|
} else {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
frameId: options.frameId,
|
|
251
|
-
resourceReferrer: options.resourceReferrer,
|
|
252
|
-
validateTextContentType: true,
|
|
253
|
-
baseURI: baseURI,
|
|
254
|
-
blockMixedContent: options.blockMixedContent,
|
|
255
|
-
expectedType: "stylesheet",
|
|
256
|
-
acceptHeaders: options.acceptHeaders,
|
|
257
|
-
networkTimeout: options.networkTimeout
|
|
258
|
-
});
|
|
259
|
-
if (!(matchCharsetEquals(content.data, content.charset) || matchCharsetEquals(content.data, options.charset))) {
|
|
260
|
-
options = Object.assign({}, options, { charset: getCharset(content.data) });
|
|
261
|
-
await this.resolveLinkStylesheetURLs(stylesheetInfo, element, resourceURL, baseURI, options, workStylesheet, resources, stylesheets);
|
|
262
|
-
} else {
|
|
263
|
-
resourceURL = content.resourceURL;
|
|
264
|
-
content.data = getUpdatedResourceContent(content.resourceURL, options) || content.data;
|
|
265
|
-
stylesheetInfo.stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
|
|
266
|
-
await this.resolveImportURLs(stylesheetInfo, resourceURL, options, workStylesheet, resources, stylesheets);
|
|
267
|
-
}
|
|
243
|
+
resourceURL = content.resourceURL;
|
|
244
|
+
content.data = getUpdatedResourceContent(content.resourceURL, options) || content.data;
|
|
245
|
+
stylesheetInfo.stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
|
|
246
|
+
await this.resolveImportURLs(stylesheetInfo, resourceURL, options, workStylesheet, resources, stylesheets);
|
|
268
247
|
}
|
|
269
248
|
}
|
|
270
249
|
}
|
|
@@ -300,6 +279,27 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
300
279
|
resources.fonts.set(indexResource, { name, content, extension, contentType, url: resourceURL });
|
|
301
280
|
}
|
|
302
281
|
|
|
282
|
+
groupDuplicateFonts(stylesheets, fonts) {
|
|
283
|
+
const canonicalNames = new Map();
|
|
284
|
+
const fontResources = [...fonts]
|
|
285
|
+
.filter(([, resource]) => resource.content)
|
|
286
|
+
.sort(([indexResource], [otherIndexResource]) => indexResource - otherIndexResource);
|
|
287
|
+
fontResources.forEach(([indexResource, resource], index) => {
|
|
288
|
+
const original = fontResources.slice(0, index).find(([, previousResource]) => testSameContent(previousResource.content, resource.content));
|
|
289
|
+
if (original) {
|
|
290
|
+
canonicalNames.set(resource.name, original[1].name);
|
|
291
|
+
fonts.delete(indexResource);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
if (canonicalNames.size) {
|
|
295
|
+
stylesheets.forEach(stylesheetInfo => {
|
|
296
|
+
if (stylesheetInfo.stylesheet) {
|
|
297
|
+
getUrlFunctions(stylesheetInfo.stylesheet).forEach(urlNode => replaceFontName(urlNode, canonicalNames));
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
303
|
async processStyle(ruleData, options, resources, batchRequest) {
|
|
304
304
|
const urls = getUrlFunctions(ruleData);
|
|
305
305
|
await Promise.all(urls.map(async urlNode => {
|
|
@@ -428,7 +428,11 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
428
428
|
Object.keys(pageResources).forEach(resourceType => {
|
|
429
429
|
const unusedResources = Array.from(pageResources[resourceType]).filter(([, value]) => !textContent.includes(value.name));
|
|
430
430
|
unusedResources.forEach(([indexResource]) => pageResources[resourceType].delete(indexResource));
|
|
431
|
-
|
|
431
|
+
const orderedResources = Array.from(pageResources[resourceType]);
|
|
432
|
+
if (orderedResources.every(([indexResource]) => typeof indexResource == "number")) {
|
|
433
|
+
orderedResources.sort(([indexResource], [otherIndexResource]) => indexResource - otherIndexResource);
|
|
434
|
+
}
|
|
435
|
+
resources[resourceType] = orderedResources.map(([, resource]) => resource);
|
|
432
436
|
});
|
|
433
437
|
const viewportElement = doc.head.querySelector("meta[name=viewport]");
|
|
434
438
|
const viewport = viewportElement ? viewportElement.content : null;
|
|
@@ -588,4 +592,26 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
588
592
|
return true;
|
|
589
593
|
}
|
|
590
594
|
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function testSameContent(content, otherContent) {
|
|
598
|
+
if (content.length != otherContent.length) {
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
for (let index = 0; index < content.length; index++) {
|
|
602
|
+
if (content[index] != otherContent[index]) {
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function replaceFontName(urlNode, canonicalNames) {
|
|
610
|
+
canonicalNames.forEach((canonicalName, name) => {
|
|
611
|
+
if (urlNode.value == name) {
|
|
612
|
+
urlNode.value = canonicalName;
|
|
613
|
+
} else if (urlNode.value.endsWith(" " + name)) {
|
|
614
|
+
urlNode.value = urlNode.value.substring(0, urlNode.value.length - name.length) + canonicalName;
|
|
615
|
+
}
|
|
616
|
+
});
|
|
591
617
|
}
|