single-file-core 1.6.11 → 1.6.13
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 +232 -26
- package/core/index.js +65 -37
- package/core/util.js +13 -4
- package/modules/html-serializer.js +9 -9
- package/package.json +1 -1
- package/single-file-bootstrap.js +2 -0
package/core/helper.js
CHANGED
|
@@ -94,6 +94,13 @@ const EMPTY_RESOURCE = "data:,";
|
|
|
94
94
|
const POSTER_CONTENT_TYPES = ["image/webp", "image/jpeg"];
|
|
95
95
|
const POSTER_QUALITY = 0.8;
|
|
96
96
|
const NESTING_TRACK_ID_ATTRIBUTE_NAME = "data-sf-nesting-track-id";
|
|
97
|
+
const NESTING_START_MARKER = NESTING_TRACK_ID_ATTRIBUTE_NAME + "-start ";
|
|
98
|
+
const NESTING_END_MARKER = NESTING_TRACK_ID_ATTRIBUTE_NAME + "-end ";
|
|
99
|
+
const NESTING_RECREATED_ATTRIBUTE_NAME = NESTING_TRACK_ID_ATTRIBUTE_NAME + "-recreated";
|
|
100
|
+
const NESTING_SHADOW_ROOT_TRACK_ID_PREFIX = "s";
|
|
101
|
+
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
102
|
+
const RAW_TEXT_TAG_NAMES = ["SCRIPT", "STYLE", "TEXTAREA", "TITLE", "XMP", "IFRAME", "NOEMBED", "NOFRAMES", "PLAINTEXT", "NOSCRIPT"];
|
|
103
|
+
const COMMENT_NODE_FILTER = 128;
|
|
97
104
|
const DEPRECATED_OPTION_NAMES = {
|
|
98
105
|
loadDeferredImages: "loadDeferredContent",
|
|
99
106
|
loadDeferredImagesMaxIdleTime: "loadDeferredContentMaxIdleTime",
|
|
@@ -141,6 +148,8 @@ export {
|
|
|
141
148
|
parseDocContent,
|
|
142
149
|
markInvalidNesting,
|
|
143
150
|
fixInvalidNesting,
|
|
151
|
+
removeNestingMarkers,
|
|
152
|
+
getNestingMarkerData,
|
|
144
153
|
normalizeOptions,
|
|
145
154
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
146
155
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
@@ -171,6 +180,9 @@ export {
|
|
|
171
180
|
MESSAGE_PREFIX,
|
|
172
181
|
NO_SCRIPT_PROPERTY_NAME,
|
|
173
182
|
NESTING_TRACK_ID_ATTRIBUTE_NAME,
|
|
183
|
+
NESTING_START_MARKER,
|
|
184
|
+
NESTING_END_MARKER,
|
|
185
|
+
NESTING_RECREATED_ATTRIBUTE_NAME,
|
|
174
186
|
getPosterDataURI
|
|
175
187
|
};
|
|
176
188
|
|
|
@@ -311,6 +323,7 @@ function preProcessDoc(doc, win, options) {
|
|
|
311
323
|
markedElements: []
|
|
312
324
|
};
|
|
313
325
|
}
|
|
326
|
+
setNestingMarkersData(doc);
|
|
314
327
|
let referrer = "";
|
|
315
328
|
if (doc.referrer) {
|
|
316
329
|
try {
|
|
@@ -343,18 +356,30 @@ function markInvalidNesting(doc) {
|
|
|
343
356
|
if (!doc.body) {
|
|
344
357
|
return;
|
|
345
358
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
359
|
+
removeNestingMarkers(doc);
|
|
360
|
+
markInvalidNestingInRoot(doc, doc.body, "", () => serialize(doc));
|
|
361
|
+
getShadowRoots(doc.body).forEach((shadowRoot, indexShadowRoot) =>
|
|
362
|
+
markInvalidNestingInRoot(doc, shadowRoot, NESTING_SHADOW_ROOT_TRACK_ID_PREFIX + indexShadowRoot, () => shadowRoot.innerHTML));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function markInvalidNestingInRoot(doc, root, rootTrackId, getContent) {
|
|
366
|
+
if (rootTrackId) {
|
|
367
|
+
Array.from(root.children).forEach((child, indexChild) => addTrackIds(child, indexChild, rootTrackId));
|
|
368
|
+
} else {
|
|
369
|
+
addTrackIds(root);
|
|
370
|
+
}
|
|
371
|
+
const verificationDoc = parseDocContent(getContent());
|
|
372
|
+
const markedMap = buildTrackIdMap(root);
|
|
373
|
+
const normalizedMap = buildTrackIdMap(verificationDoc.documentElement);
|
|
350
374
|
const trackIds = new Set();
|
|
375
|
+
const droppedElements = [];
|
|
351
376
|
Object.keys(markedMap).forEach(id => {
|
|
352
377
|
if (id in normalizedMap) {
|
|
353
378
|
const markedParent = markedMap[id].parentElement?.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME) || null;
|
|
354
379
|
const normalizedParent = normalizedMap[id]?.parentElement?.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME) || null;
|
|
355
380
|
if (markedParent !== normalizedParent) {
|
|
356
381
|
let current = markedMap[id];
|
|
357
|
-
while (current && current !==
|
|
382
|
+
while (current && current !== root) {
|
|
358
383
|
const currentId = current.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
359
384
|
if (currentId) {
|
|
360
385
|
trackIds.add(currentId);
|
|
@@ -362,9 +387,22 @@ function markInvalidNesting(doc) {
|
|
|
362
387
|
current = current.parentElement;
|
|
363
388
|
}
|
|
364
389
|
}
|
|
390
|
+
} else if (testDroppedElement(markedMap[id])) {
|
|
391
|
+
trackIds.add(id);
|
|
392
|
+
droppedElements.push(markedMap[id]);
|
|
365
393
|
}
|
|
366
394
|
});
|
|
367
|
-
|
|
395
|
+
droppedElements.forEach(element => {
|
|
396
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
397
|
+
element.prepend(doc.createComment(NESTING_START_MARKER + id));
|
|
398
|
+
element.append(doc.createComment(NESTING_END_MARKER + id));
|
|
399
|
+
});
|
|
400
|
+
setNestingMarkersData(root);
|
|
401
|
+
if (rootTrackId) {
|
|
402
|
+
Array.from(root.children).forEach(child => cleanupTrackIds(child, trackIds));
|
|
403
|
+
} else {
|
|
404
|
+
cleanupTrackIds(root, trackIds);
|
|
405
|
+
}
|
|
368
406
|
|
|
369
407
|
function addTrackIds(element, index = 0, parentTrackId = "") {
|
|
370
408
|
const trackId = parentTrackId ? `${parentTrackId}.${index + 1}` : `${index + 1}`;
|
|
@@ -375,9 +413,13 @@ function markInvalidNesting(doc) {
|
|
|
375
413
|
Array.from(element.children).forEach((child, indexChild) => addTrackIds(child, indexChild, trackId));
|
|
376
414
|
}
|
|
377
415
|
|
|
378
|
-
function buildTrackIdMap(
|
|
416
|
+
function buildTrackIdMap(root) {
|
|
379
417
|
const trackIds = {};
|
|
380
|
-
|
|
418
|
+
if (root.getAttribute) {
|
|
419
|
+
traverse(root);
|
|
420
|
+
} else {
|
|
421
|
+
Array.from(root.children).forEach(traverse);
|
|
422
|
+
}
|
|
381
423
|
return trackIds;
|
|
382
424
|
|
|
383
425
|
function traverse(element) {
|
|
@@ -391,6 +433,21 @@ function markInvalidNesting(doc) {
|
|
|
391
433
|
}
|
|
392
434
|
}
|
|
393
435
|
|
|
436
|
+
function testDroppedElement(element) {
|
|
437
|
+
if (element.namespaceURI != HTML_NAMESPACE) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
let ancestor = element.parentElement;
|
|
441
|
+
while (ancestor) {
|
|
442
|
+
const tagName = ancestor.tagName.toUpperCase();
|
|
443
|
+
if (tagName == "TEMPLATE" || RAW_TEXT_TAG_NAMES.includes(tagName)) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
ancestor = ancestor.parentElement;
|
|
447
|
+
}
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
|
|
394
451
|
function cleanupTrackIds(element, toKeep) {
|
|
395
452
|
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
396
453
|
if (id && !toKeep.has(id)) {
|
|
@@ -400,33 +457,180 @@ function markInvalidNesting(doc) {
|
|
|
400
457
|
}
|
|
401
458
|
}
|
|
402
459
|
|
|
403
|
-
function
|
|
404
|
-
|
|
460
|
+
function getShadowRoots(element, shadowRoots = []) {
|
|
461
|
+
Array.from(element.children).forEach(child => {
|
|
462
|
+
if (child.namespaceURI == HTML_NAMESPACE && !child.classList.contains(SINGLE_FILE_UI_ELEMENT_CLASS) && child.tagName.toLowerCase() != INFOBAR_TAGNAME) {
|
|
463
|
+
const shadowRoot = getShadowRoot(child);
|
|
464
|
+
if (shadowRoot) {
|
|
465
|
+
shadowRoots.push(shadowRoot);
|
|
466
|
+
getShadowRoots(shadowRoot, shadowRoots);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
getShadowRoots(child, shadowRoots);
|
|
470
|
+
});
|
|
471
|
+
return shadowRoots;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function getNestingMarkerData(element) {
|
|
475
|
+
return encodeURIComponent(JSON.stringify({
|
|
476
|
+
tag: element.localName,
|
|
477
|
+
attributes: Array.from(element.attributes)
|
|
478
|
+
.filter(attribute => attribute.name != NESTING_RECREATED_ATTRIBUTE_NAME)
|
|
479
|
+
.map(attribute => [attribute.name, attribute.value])
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function setNestingMarkersData(root) {
|
|
484
|
+
const walker = (root.ownerDocument || root).createTreeWalker(root, COMMENT_NODE_FILTER);
|
|
485
|
+
while (walker.nextNode()) {
|
|
486
|
+
const comment = walker.currentNode;
|
|
487
|
+
if (comment.data.startsWith(NESTING_START_MARKER)) {
|
|
488
|
+
const id = comment.data.substring(NESTING_START_MARKER.length).split(" ")[0];
|
|
489
|
+
comment.data = NESTING_START_MARKER + id + " " + getNestingMarkerData(comment.parentNode);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function removeNestingMarkers(doc) {
|
|
495
|
+
if (doc.body) {
|
|
496
|
+
[doc, ...getShadowRoots(doc.body)].forEach(root => {
|
|
497
|
+
const comments = [];
|
|
498
|
+
const walker = doc.createTreeWalker(root, COMMENT_NODE_FILTER);
|
|
499
|
+
while (walker.nextNode()) {
|
|
500
|
+
if (walker.currentNode.data.startsWith(NESTING_START_MARKER) || walker.currentNode.data.startsWith(NESTING_END_MARKER)) {
|
|
501
|
+
comments.push(walker.currentNode);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
comments.forEach(comment => comment.remove());
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function fixInvalidNesting(document, NESTING_TRACK_ID_ATTRIBUTE_NAME, preventCleanup = false, options = {}) {
|
|
510
|
+
const START_MARKER = NESTING_TRACK_ID_ATTRIBUTE_NAME + "-start ";
|
|
511
|
+
const END_MARKER = NESTING_TRACK_ID_ATTRIBUTE_NAME + "-end ";
|
|
512
|
+
const RECREATED_ATTRIBUTE_NAME = NESTING_TRACK_ID_ATTRIBUTE_NAME + "-recreated";
|
|
405
513
|
if (document.currentScript) {
|
|
406
514
|
document.currentScript.remove();
|
|
407
515
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
516
|
+
const roots = [];
|
|
517
|
+
if (options.rootElement) {
|
|
518
|
+
roots.push(options.rootElement);
|
|
519
|
+
} else if (document.body) {
|
|
520
|
+
addRoots(document.body);
|
|
521
|
+
}
|
|
522
|
+
roots.forEach(root => {
|
|
523
|
+
recreateElements(root);
|
|
524
|
+
if (!options.recreateOnly) {
|
|
525
|
+
moveElements(root);
|
|
418
526
|
}
|
|
419
527
|
});
|
|
420
528
|
if (!preventCleanup) {
|
|
421
|
-
|
|
529
|
+
roots.forEach(root => {
|
|
530
|
+
const elements = Array.from(root.querySelectorAll("[" + NESTING_TRACK_ID_ATTRIBUTE_NAME + "]"));
|
|
531
|
+
if (root.getAttribute && root.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME)) {
|
|
532
|
+
elements.push(root);
|
|
533
|
+
}
|
|
534
|
+
elements.forEach(element => element.removeAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME));
|
|
535
|
+
});
|
|
422
536
|
}
|
|
423
537
|
|
|
424
|
-
function
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
538
|
+
function addRoots(root) {
|
|
539
|
+
roots.push(root);
|
|
540
|
+
root.querySelectorAll("*").forEach(element => {
|
|
541
|
+
if (element.shadowRoot) {
|
|
542
|
+
addRoots(element.shadowRoot);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function recreateElements(root) {
|
|
548
|
+
const startComments = [];
|
|
549
|
+
const walker = document.createTreeWalker(root, 128);
|
|
550
|
+
while (walker.nextNode()) {
|
|
551
|
+
if (walker.currentNode.data.startsWith(START_MARKER)) {
|
|
552
|
+
startComments.push(walker.currentNode);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
startComments.forEach(startComment => {
|
|
556
|
+
const separatorIndex = startComment.data.indexOf(" ", START_MARKER.length);
|
|
557
|
+
let endComment, data;
|
|
558
|
+
if (separatorIndex != -1) {
|
|
559
|
+
const id = startComment.data.substring(START_MARKER.length, separatorIndex);
|
|
560
|
+
endComment = startComment.nextSibling;
|
|
561
|
+
while (endComment && !(endComment.nodeType == 8 && endComment.data == END_MARKER + id)) {
|
|
562
|
+
endComment = endComment.nextSibling;
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
data = globalThis.JSON.parse(decodeURIComponent(startComment.data.substring(separatorIndex + 1)));
|
|
566
|
+
} catch {
|
|
567
|
+
/* ignored */
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (endComment && data) {
|
|
571
|
+
const element = document.createElement(data.tag);
|
|
572
|
+
data.attributes.forEach(([name, value]) => {
|
|
573
|
+
try {
|
|
574
|
+
element.setAttribute(name, value);
|
|
575
|
+
} catch {
|
|
576
|
+
/* ignored */
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
if (preventCleanup) {
|
|
580
|
+
element.setAttribute(RECREATED_ATTRIBUTE_NAME, "");
|
|
581
|
+
}
|
|
582
|
+
startComment.before(element);
|
|
583
|
+
while (startComment.nextSibling != endComment) {
|
|
584
|
+
element.appendChild(startComment.nextSibling);
|
|
585
|
+
}
|
|
586
|
+
startComment.remove();
|
|
587
|
+
endComment.remove();
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function moveElements(root) {
|
|
593
|
+
const trackIds = {};
|
|
594
|
+
const elements = [];
|
|
595
|
+
if (root.getAttribute) {
|
|
596
|
+
buildTrackIdMap(root);
|
|
597
|
+
} else {
|
|
598
|
+
Array.from(root.children).forEach(buildTrackIdMap);
|
|
599
|
+
}
|
|
600
|
+
elements.forEach(element => {
|
|
601
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
602
|
+
const originalElement = trackIds[id];
|
|
603
|
+
if (originalElement != element) {
|
|
604
|
+
if (!preventCleanup || options.mergeCopies) {
|
|
605
|
+
if (originalElement.contains(element)) {
|
|
606
|
+
element.replaceWith(...element.childNodes);
|
|
607
|
+
} else {
|
|
608
|
+
originalElement.append(...element.childNodes);
|
|
609
|
+
element.remove();
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
} else {
|
|
613
|
+
const idParts = id.split(".");
|
|
614
|
+
if (idParts.length > 1) {
|
|
615
|
+
const parentId = idParts.slice(0, -1).join(".");
|
|
616
|
+
const expectedParent = trackIds[parentId];
|
|
617
|
+
if (expectedParent && element.parentElement !== expectedParent && !element.contains(expectedParent)) {
|
|
618
|
+
expectedParent.appendChild(element);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
function buildTrackIdMap(element) {
|
|
625
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
626
|
+
if (id) {
|
|
627
|
+
if (!(id in trackIds)) {
|
|
628
|
+
trackIds[id] = element;
|
|
629
|
+
}
|
|
630
|
+
elements.push(element);
|
|
631
|
+
}
|
|
632
|
+
Array.from(element.children).forEach(buildTrackIdMap);
|
|
428
633
|
}
|
|
429
|
-
Array.from(element.children).forEach(buildTrackIdMap);
|
|
430
634
|
}
|
|
431
635
|
}
|
|
432
636
|
|
|
@@ -509,6 +713,7 @@ function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map
|
|
|
509
713
|
// ignored
|
|
510
714
|
}
|
|
511
715
|
getElementsInfo(win, doc, shadowRoot, options, data, adoptedStyleSheetsCache, elementHidden);
|
|
716
|
+
setNestingMarkersData(shadowRoot);
|
|
512
717
|
shadowRootInfo.content = shadowRoot.innerHTML;
|
|
513
718
|
shadowRootInfo.mode = shadowRoot.mode;
|
|
514
719
|
shadowRootInfo.delegateFocus = shadowRoot.delegatesFocus;
|
|
@@ -852,6 +1057,7 @@ function testHiddenElement(element, computedStyle) {
|
|
|
852
1057
|
}
|
|
853
1058
|
|
|
854
1059
|
function postProcessDoc(doc, markedElements, invalidElements) {
|
|
1060
|
+
removeNestingMarkers(doc);
|
|
855
1061
|
doc.querySelectorAll("[" + DISABLED_NOSCRIPT_ATTRIBUTE_NAME + "]").forEach(element => {
|
|
856
1062
|
element.textContent = element.getAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME);
|
|
857
1063
|
element.removeAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME);
|
package/core/index.js
CHANGED
|
@@ -212,6 +212,9 @@ class Runner {
|
|
|
212
212
|
this.options.frames = [];
|
|
213
213
|
}
|
|
214
214
|
this.options.content = this.options.content || (rootDocDefined ? util.serialize(this.options.doc) : null);
|
|
215
|
+
if (rootDocDefined) {
|
|
216
|
+
util.removeNestingMarkers(this.options.doc);
|
|
217
|
+
}
|
|
215
218
|
this.onprogress = options.onprogress || (() => { });
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -498,9 +501,9 @@ class Processor {
|
|
|
498
501
|
pageContent = content.data || "";
|
|
499
502
|
}
|
|
500
503
|
this.doc = util.parseDocContent(pageContent, this.baseURI);
|
|
504
|
+
util.fixInvalidNesting(this.doc, true, { recreateOnly: true });
|
|
501
505
|
removeInsertedParagraphs(this.doc);
|
|
502
|
-
|
|
503
|
-
util.fixInvalidNesting(this.doc);
|
|
506
|
+
util.fixInvalidNesting(this.doc, true, { mergeCopies: true });
|
|
504
507
|
if (this.options.saveRawPage) {
|
|
505
508
|
let charset;
|
|
506
509
|
this.doc.querySelectorAll("meta[charset]").forEach(element => {
|
|
@@ -608,13 +611,20 @@ class Processor {
|
|
|
608
611
|
if (this.options.displayStats) {
|
|
609
612
|
size = util.getContentSize(this.doc.documentElement.outerHTML);
|
|
610
613
|
}
|
|
611
|
-
|
|
612
|
-
|
|
614
|
+
const collapsedElementCount = collapseRecreatedElements(this.doc);
|
|
615
|
+
const invalidNesting = collapsedElementCount || this.doc.querySelector(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`);
|
|
616
|
+
if (invalidNesting) {
|
|
613
617
|
const scriptElement = this.doc.createElement("script");
|
|
614
618
|
scriptElement.textContent = `(${util.getFixInvalidNestingSource()})(document, "${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}");`;
|
|
615
619
|
this.doc.body.appendChild(scriptElement);
|
|
616
620
|
}
|
|
617
|
-
|
|
621
|
+
let content = util.serialize(this.doc, this.options.compressHTML);
|
|
622
|
+
if (invalidNesting) {
|
|
623
|
+
const closedParagraphs = getClosedParagraphs(this.doc, content);
|
|
624
|
+
if (closedParagraphs.size) {
|
|
625
|
+
content = util.serialize(this.doc, this.options.compressHTML, closedParagraphs);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
618
628
|
if (this.options.displayStats) {
|
|
619
629
|
const contentSize = util.getContentSize(content);
|
|
620
630
|
this.stats.set("processed", "HTML bytes", contentSize);
|
|
@@ -1005,11 +1015,17 @@ class Processor {
|
|
|
1005
1015
|
}
|
|
1006
1016
|
|
|
1007
1017
|
resetReferrerMeta() {
|
|
1008
|
-
this.doc.querySelectorAll("meta[name=referrer]").forEach(element => element.remove());
|
|
1009
1018
|
const metaElement = this.doc.createElement("meta");
|
|
1010
1019
|
metaElement.setAttribute("name", "referrer");
|
|
1011
1020
|
metaElement.setAttribute("content", "no-referrer");
|
|
1012
|
-
this.doc.
|
|
1021
|
+
const referrerElements = Array.from(this.doc.querySelectorAll("meta[name=referrer]"));
|
|
1022
|
+
const headReferrerElement = referrerElements.find(element => element.parentElement == this.doc.head);
|
|
1023
|
+
if (headReferrerElement) {
|
|
1024
|
+
headReferrerElement.replaceWith(metaElement);
|
|
1025
|
+
} else {
|
|
1026
|
+
this.doc.head.appendChild(metaElement);
|
|
1027
|
+
}
|
|
1028
|
+
referrerElements.forEach(element => element.remove());
|
|
1013
1029
|
}
|
|
1014
1030
|
|
|
1015
1031
|
setInputValues() {
|
|
@@ -1482,6 +1498,12 @@ class Processor {
|
|
|
1482
1498
|
if (shadowDoc.body) {
|
|
1483
1499
|
shadowDoc.body.childNodes.forEach(node => templateElement.appendChild(shadowDoc.importNode(node, true)));
|
|
1484
1500
|
}
|
|
1501
|
+
util.fixInvalidNesting(doc, true, { rootElement: templateElement, recreateOnly: true });
|
|
1502
|
+
removeInsertedParagraphs(templateElement);
|
|
1503
|
+
util.fixInvalidNesting(doc, true, { rootElement: templateElement, mergeCopies: true });
|
|
1504
|
+
if (templateElement.querySelector(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`)) {
|
|
1505
|
+
templateElement.setAttribute(SHADOWROOT_ATTRIBUTE_NAME, "open");
|
|
1506
|
+
}
|
|
1485
1507
|
processElement(templateElement);
|
|
1486
1508
|
if (element.firstChild) {
|
|
1487
1509
|
element.insertBefore(templateElement, element.firstChild);
|
|
@@ -1795,34 +1817,40 @@ function testIgnoredPath(resourceURL) {
|
|
|
1795
1817
|
return resourceURL && (resourceURL.startsWith(DATA_URI_PREFIX) || resourceURL == ABOUT_BLANK_URI);
|
|
1796
1818
|
}
|
|
1797
1819
|
|
|
1798
|
-
function
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1820
|
+
function collapseRecreatedElements(doc) {
|
|
1821
|
+
const elements = Array.from(doc.querySelectorAll(`[${util.NESTING_RECREATED_ATTRIBUTE_NAME}]`)).reverse();
|
|
1822
|
+
elements.forEach(element => {
|
|
1823
|
+
const id = element.getAttribute(util.NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
1824
|
+
element.before(doc.createComment(util.NESTING_START_MARKER + id + " " + util.getNestingMarkerData(element)));
|
|
1825
|
+
while (element.firstChild) {
|
|
1826
|
+
element.before(element.firstChild);
|
|
1827
|
+
}
|
|
1828
|
+
element.before(doc.createComment(util.NESTING_END_MARKER + id));
|
|
1829
|
+
element.remove();
|
|
1830
|
+
});
|
|
1831
|
+
return elements.length;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
function removeInsertedParagraphs(doc) {
|
|
1835
|
+
getInsertedParagraphs(doc).forEach((_, paragraph) => paragraph.remove());
|
|
1805
1836
|
}
|
|
1806
1837
|
|
|
1807
|
-
function
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
});
|
|
1838
|
+
function getClosedParagraphs(doc, content) {
|
|
1839
|
+
const parsedDoc = util.parseDocContent(content);
|
|
1840
|
+
const roots = [];
|
|
1841
|
+
addRoots(parsedDoc);
|
|
1842
|
+
const trackIds = new Set();
|
|
1843
|
+
roots.forEach(root => getInsertedParagraphs(root).forEach(trackId => trackIds.add(trackId)));
|
|
1844
|
+
return new Set(Array.from(doc.querySelectorAll(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`)).filter(element =>
|
|
1845
|
+
element.tagName == PARAGRAPH_TAG_NAME && trackIds.has(element.getAttribute(util.NESTING_TRACK_ID_ATTRIBUTE_NAME))));
|
|
1846
|
+
|
|
1847
|
+
function addRoots(root) {
|
|
1848
|
+
roots.push(root);
|
|
1849
|
+
root.querySelectorAll("template").forEach(templateElement => addRoots(templateElement.content));
|
|
1822
1850
|
}
|
|
1823
1851
|
}
|
|
1824
1852
|
|
|
1825
|
-
function
|
|
1853
|
+
function getInsertedParagraphs(doc) {
|
|
1826
1854
|
const trackedElements = new Map();
|
|
1827
1855
|
doc.querySelectorAll(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`).forEach(element =>
|
|
1828
1856
|
trackedElements.set(element.getAttribute(util.NESTING_TRACK_ID_ATTRIBUTE_NAME), element));
|
|
@@ -1831,33 +1859,33 @@ function removeInsertedParagraphs(doc) {
|
|
|
1831
1859
|
const parentTrackId = getParentTrackId(trackId);
|
|
1832
1860
|
const expectedParent = trackedElements.get(parentTrackId);
|
|
1833
1861
|
if (expectedParent && element.parentElement != expectedParent && !element.contains(expectedParent) &&
|
|
1834
|
-
|
|
1862
|
+
getExpectedParagraphTrackId(trackedElements, parentTrackId)) {
|
|
1835
1863
|
displacedElements.add(element);
|
|
1836
1864
|
}
|
|
1837
1865
|
});
|
|
1838
|
-
const insertedParagraphs = new
|
|
1866
|
+
const insertedParagraphs = new Map();
|
|
1839
1867
|
displacedElements.forEach(element => {
|
|
1840
1868
|
let sibling = element.nextSibling;
|
|
1841
1869
|
while (sibling && (sibling.nodeType != 1 || displacedElements.has(sibling))) {
|
|
1842
1870
|
sibling = sibling.nextSibling;
|
|
1843
1871
|
}
|
|
1844
1872
|
if (sibling && sibling.tagName == PARAGRAPH_TAG_NAME && !sibling.attributes.length && !sibling.childNodes.length) {
|
|
1845
|
-
|
|
1873
|
+
const trackId = element.getAttribute(util.NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
1874
|
+
insertedParagraphs.set(sibling, getExpectedParagraphTrackId(trackedElements, getParentTrackId(trackId)));
|
|
1846
1875
|
}
|
|
1847
1876
|
});
|
|
1848
|
-
insertedParagraphs
|
|
1877
|
+
return insertedParagraphs;
|
|
1849
1878
|
}
|
|
1850
1879
|
|
|
1851
|
-
function
|
|
1880
|
+
function getExpectedParagraphTrackId(trackedElements, trackId) {
|
|
1852
1881
|
let element = trackedElements.get(trackId);
|
|
1853
1882
|
while (element) {
|
|
1854
1883
|
if (element.tagName == PARAGRAPH_TAG_NAME) {
|
|
1855
|
-
return
|
|
1884
|
+
return trackId;
|
|
1856
1885
|
}
|
|
1857
1886
|
trackId = getParentTrackId(trackId);
|
|
1858
1887
|
element = trackId ? trackedElements.get(trackId) : null;
|
|
1859
1888
|
}
|
|
1860
|
-
return false;
|
|
1861
1889
|
}
|
|
1862
1890
|
|
|
1863
1891
|
function getParentTrackId(trackId) {
|
package/core/util.js
CHANGED
|
@@ -128,8 +128,14 @@ function getInstance(utilOptions) {
|
|
|
128
128
|
return doc;
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
|
-
fixInvalidNesting(doc, preventCleanup = true) {
|
|
132
|
-
helper.fixInvalidNesting(doc, helper.NESTING_TRACK_ID_ATTRIBUTE_NAME, preventCleanup);
|
|
131
|
+
fixInvalidNesting(doc, preventCleanup = true, options) {
|
|
132
|
+
helper.fixInvalidNesting(doc, helper.NESTING_TRACK_ID_ATTRIBUTE_NAME, preventCleanup, options);
|
|
133
|
+
},
|
|
134
|
+
removeNestingMarkers(doc) {
|
|
135
|
+
helper.removeNestingMarkers(doc);
|
|
136
|
+
},
|
|
137
|
+
getNestingMarkerData(element) {
|
|
138
|
+
return helper.getNestingMarkerData(element);
|
|
133
139
|
},
|
|
134
140
|
markInvalidNesting(doc) {
|
|
135
141
|
helper.markInvalidNesting(doc, helper.NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
@@ -179,8 +185,8 @@ function getInstance(utilOptions) {
|
|
|
179
185
|
postProcessDoc(doc, markedElements, invalidElements) {
|
|
180
186
|
helper.postProcessDoc(doc, markedElements, invalidElements);
|
|
181
187
|
},
|
|
182
|
-
serialize(doc, compressHTML) {
|
|
183
|
-
return modules.serializer.process(doc, compressHTML);
|
|
188
|
+
serialize(doc, compressHTML, omittedEndTagElements) {
|
|
189
|
+
return modules.serializer.process(doc, compressHTML, omittedEndTagElements);
|
|
184
190
|
},
|
|
185
191
|
removeQuotes(string) {
|
|
186
192
|
return helper.removeQuotes(string);
|
|
@@ -226,6 +232,9 @@ function getInstance(utilOptions) {
|
|
|
226
232
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
227
233
|
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME,
|
|
228
234
|
NESTING_TRACK_ID_ATTRIBUTE_NAME: helper.NESTING_TRACK_ID_ATTRIBUTE_NAME,
|
|
235
|
+
NESTING_START_MARKER: helper.NESTING_START_MARKER,
|
|
236
|
+
NESTING_END_MARKER: helper.NESTING_END_MARKER,
|
|
237
|
+
NESTING_RECREATED_ATTRIBUTE_NAME: helper.NESTING_RECREATED_ATTRIBUTE_NAME,
|
|
229
238
|
getPosterDataURI: helper.getPosterDataURI
|
|
230
239
|
};
|
|
231
240
|
|
|
@@ -59,7 +59,7 @@ export {
|
|
|
59
59
|
process
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
function process(doc, compressHTML) {
|
|
62
|
+
function process(doc, compressHTML, omittedEndTagElements) {
|
|
63
63
|
const docType = doc.doctype;
|
|
64
64
|
let docTypeString = "";
|
|
65
65
|
if (docType) {
|
|
@@ -74,16 +74,16 @@ function process(doc, compressHTML) {
|
|
|
74
74
|
docTypeString += " [" + docType.internalSubset + "]";
|
|
75
75
|
docTypeString += "> ";
|
|
76
76
|
}
|
|
77
|
-
return docTypeString + serialize(doc.documentElement, compressHTML);
|
|
77
|
+
return docTypeString + serialize(doc.documentElement, compressHTML, omittedEndTagElements);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function serialize(node, compressHTML,
|
|
80
|
+
function serialize(node, compressHTML, omittedEndTagElements) {
|
|
81
81
|
if (node.nodeType == Node_TEXT_NODE) {
|
|
82
82
|
return serializeTextNode(node);
|
|
83
83
|
} else if (node.nodeType == Node_COMMENT_NODE) {
|
|
84
84
|
return serializeCommentNode(node);
|
|
85
85
|
} else if (node.nodeType == Node_ELEMENT_NODE) {
|
|
86
|
-
return serializeElement(node, compressHTML,
|
|
86
|
+
return serializeElement(node, compressHTML, omittedEndTagElements);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -95,7 +95,7 @@ function serializeTextNode(textNode) {
|
|
|
95
95
|
}
|
|
96
96
|
if (!parentTagName || TEXT_NODE_TAGS.includes(parentTagName)) {
|
|
97
97
|
if ((parentTagName == "SCRIPT" && (!parentNode.type || parentNode.type == "text/javascript")) || parentTagName == "STYLE") {
|
|
98
|
-
return textNode.textContent.replace(/<\//gi, "<\\/")
|
|
98
|
+
return textNode.textContent.replace(/<\//gi, "<\\/");
|
|
99
99
|
}
|
|
100
100
|
return textNode.textContent;
|
|
101
101
|
} else {
|
|
@@ -107,7 +107,7 @@ function serializeCommentNode(commentNode) {
|
|
|
107
107
|
return "<!--" + commentNode.textContent + "-->";
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
function serializeElement(element, compressHTML,
|
|
110
|
+
function serializeElement(element, compressHTML, omittedEndTagElements) {
|
|
111
111
|
const tagName = getTagName(element);
|
|
112
112
|
const omittedStartTag = compressHTML && OMITTED_START_TAGS.find(omittedStartTag => tagName == getTagName(omittedStartTag) && omittedStartTag.accept(element));
|
|
113
113
|
let content = "";
|
|
@@ -119,10 +119,10 @@ function serializeElement(element, compressHTML, isSVG) {
|
|
|
119
119
|
if (tagName == "TEMPLATE" && !element.childNodes.length) {
|
|
120
120
|
content += element.innerHTML;
|
|
121
121
|
} else {
|
|
122
|
-
Array.from(element.childNodes).forEach(childNode => content += serialize(childNode, compressHTML,
|
|
122
|
+
Array.from(element.childNodes).forEach(childNode => content += serialize(childNode, compressHTML, omittedEndTagElements));
|
|
123
123
|
}
|
|
124
|
-
const omittedEndTag = compressHTML && OMITTED_END_TAGS.find(omittedEndTag => tagName == getTagName(omittedEndTag) && omittedEndTag.accept(element.nextSibling, element));
|
|
125
|
-
if (
|
|
124
|
+
const omittedEndTag = (omittedEndTagElements && omittedEndTagElements.has(element)) || compressHTML && OMITTED_END_TAGS.find(omittedEndTag => tagName == getTagName(omittedEndTag) && omittedEndTag.accept(element.nextSibling, element));
|
|
125
|
+
if (!omittedEndTag && !VOID_TAG_NAMES.includes(tagName)) {
|
|
126
126
|
content += "</" + tagName.toLowerCase() + ">";
|
|
127
127
|
}
|
|
128
128
|
return content;
|
package/package.json
CHANGED
package/single-file-bootstrap.js
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
32
32
|
preProcessDoc,
|
|
33
33
|
postProcessDoc,
|
|
34
|
+
markInvalidNesting,
|
|
34
35
|
getShadowRoot
|
|
35
36
|
} from "./core/helper.js";
|
|
36
37
|
|
|
@@ -46,6 +47,7 @@ const helper = {
|
|
|
46
47
|
serialize(doc, compressHTML) {
|
|
47
48
|
return serializer.process(doc, compressHTML);
|
|
48
49
|
},
|
|
50
|
+
markInvalidNesting,
|
|
49
51
|
getShadowRoot
|
|
50
52
|
};
|
|
51
53
|
|