html2canvas-pro 2.1.1 → 2.2.0
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/dist/html2canvas-pro.esm.js +10226 -10540
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +10869 -11185
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +8 -8
- package/dist/lib/config.js +0 -22
- package/dist/lib/core/cache-storage.js +1 -38
- package/dist/lib/core/constants.js +25 -0
- package/dist/lib/core/context.js +1 -0
- package/dist/lib/core/features.js +1 -0
- package/dist/lib/core/validator.js +3 -3
- package/dist/lib/css/grouped/background-styles.js +36 -0
- package/dist/lib/css/grouped/border-styles.js +75 -0
- package/dist/lib/css/grouped/font-styles.js +93 -0
- package/dist/lib/css/grouped/layout-styles.js +127 -0
- package/dist/lib/css/index.js +74 -46
- package/dist/lib/css/layout/text.js +7 -6
- package/dist/lib/css/property-descriptors/background-blend-mode.js +41 -0
- package/dist/lib/css/property-descriptors/border-image-repeat.js +42 -0
- package/dist/lib/css/property-descriptors/border-image-slice.js +45 -0
- package/dist/lib/css/property-descriptors/border-image-source.js +21 -0
- package/dist/lib/css/property-descriptors/border-radius.js +1 -1
- package/dist/lib/css/property-descriptors/box-decoration-break.js +18 -0
- package/dist/lib/css/property-descriptors/counter-increment.js +17 -12
- package/dist/lib/css/property-descriptors/counter-reset.js +4 -12
- package/dist/lib/css/property-descriptors/filter.js +76 -0
- package/dist/lib/css/property-descriptors/font-variant-ligatures.js +34 -0
- package/dist/lib/css/property-descriptors/object-fit.js +1 -1
- package/dist/lib/css/property-descriptors/object-position.js +42 -0
- package/dist/lib/css/property-descriptors/visibility.js +1 -1
- package/dist/lib/css/property-descriptors/zoom.js +18 -0
- package/dist/lib/css/syntax/parser.js +0 -1
- package/dist/lib/css/types/color.js +5 -1
- package/dist/lib/css/types/functions/repeating-linear-gradient.js +9 -0
- package/dist/lib/css/types/image.js +12 -2
- package/dist/lib/css/types/length-percentage.js +6 -2
- package/dist/lib/css/types/safe-eval.js +80 -0
- package/dist/lib/dom/document-cloner.js +23 -163
- package/dist/lib/dom/slot-cloner.js +176 -0
- package/dist/lib/index.js +1 -17
- package/dist/lib/render/canvas/background-renderer.js +165 -32
- package/dist/lib/render/canvas/border-image-renderer.js +153 -0
- package/dist/lib/render/canvas/canvas-renderer.js +34 -189
- package/dist/lib/render/canvas/content-renderer.js +202 -0
- package/dist/lib/render/canvas/effects-renderer.js +3 -0
- package/dist/lib/render/canvas/text/text-decoration-renderer.js +99 -0
- package/dist/lib/render/canvas/text-renderer.js +100 -224
- package/dist/lib/render/effects.js +38 -3
- package/dist/lib/render/object-fit.js +19 -15
- package/dist/lib/render/stacking-context.js +11 -0
- package/dist/types/config.d.ts +0 -10
- package/dist/types/core/cache-storage.d.ts +0 -24
- package/dist/types/core/constants.d.ts +22 -0
- package/dist/types/core/context.d.ts +3 -0
- package/dist/types/core/performance-monitor.d.ts +4 -4
- package/dist/types/core/validator.d.ts +6 -8
- package/dist/types/css/grouped/background-styles.d.ts +16 -0
- package/dist/types/css/grouped/border-styles.d.ts +31 -0
- package/dist/types/css/grouped/font-styles.d.ts +35 -0
- package/dist/types/css/grouped/layout-styles.d.ts +46 -0
- package/dist/types/css/index.d.ts +30 -0
- package/dist/types/css/property-descriptors/background-blend-mode.d.ts +23 -0
- package/dist/types/css/property-descriptors/border-image-repeat.d.ts +12 -0
- package/dist/types/css/property-descriptors/border-image-slice.d.ts +10 -0
- package/dist/types/css/property-descriptors/border-image-source.d.ts +4 -0
- package/dist/types/css/property-descriptors/box-decoration-break.d.ts +6 -0
- package/dist/types/css/property-descriptors/counter-increment.d.ts +3 -0
- package/dist/types/css/property-descriptors/filter.d.ts +3 -0
- package/dist/types/css/property-descriptors/font-variant-ligatures.d.ts +14 -0
- package/dist/types/css/property-descriptors/object-position.d.ts +4 -0
- package/dist/types/css/property-descriptors/zoom.d.ts +3 -0
- package/dist/types/css/types/functions/repeating-linear-gradient.d.ts +4 -0
- package/dist/types/css/types/image.d.ts +4 -2
- package/dist/types/css/types/safe-eval.d.ts +8 -0
- package/dist/types/dom/document-cloner.d.ts +3 -44
- package/dist/types/dom/slot-cloner.d.ts +66 -0
- package/dist/types/index.d.ts +3 -7
- package/dist/types/options.d.ts +11 -0
- package/dist/types/render/canvas/background-renderer.d.ts +23 -0
- package/dist/types/render/canvas/border-image-renderer.d.ts +18 -0
- package/dist/types/render/canvas/canvas-renderer.d.ts +1 -0
- package/dist/types/render/canvas/content-renderer.d.ts +44 -0
- package/dist/types/render/canvas/text/text-decoration-renderer.d.ts +18 -0
- package/dist/types/render/canvas/text-renderer.d.ts +12 -1
- package/dist/types/render/effects.d.ts +12 -2
- package/dist/types/render/object-fit.d.ts +2 -1
- package/dist/types/render/renderer-interface.d.ts +11 -9
- package/package.json +5 -10
- package/dist/lib/dom/replaced-elements/pseudo-elements.js +0 -0
- package/dist/types/dom/replaced-elements/pseudo-elements.d.ts +0 -0
|
@@ -9,7 +9,7 @@ const list_style_type_1 = require("../css/property-descriptors/list-style-type")
|
|
|
9
9
|
const index_1 = require("../css/index");
|
|
10
10
|
const quotes_1 = require("../css/property-descriptors/quotes");
|
|
11
11
|
const debugger_1 = require("../core/debugger");
|
|
12
|
-
const
|
|
12
|
+
const slot_cloner_1 = require("./slot-cloner");
|
|
13
13
|
/**
|
|
14
14
|
* Find the parent ShadowRoot of an element, if any
|
|
15
15
|
* @param element - The element to check
|
|
@@ -39,6 +39,7 @@ class DocumentCloner {
|
|
|
39
39
|
this.referenceElement = element;
|
|
40
40
|
this.counters = new counter_1.CounterState();
|
|
41
41
|
this.quoteDepth = 0;
|
|
42
|
+
this.slotCloner = new slot_cloner_1.SlotCloner((node, copyStyles) => this.cloneNode(node, copyStyles), { ignoreElements: options.ignoreElements, copyStyles: options.copyStyles ?? true }, context);
|
|
42
43
|
if (!element.ownerDocument) {
|
|
43
44
|
throw new Error('Cloned element does not have an owner document');
|
|
44
45
|
}
|
|
@@ -98,26 +99,26 @@ class DocumentCloner {
|
|
|
98
99
|
*/
|
|
99
100
|
const baseUri = ownerDocument.baseURI;
|
|
100
101
|
documentClone.open();
|
|
102
|
+
// rawHTML is always a static, internally-generated string:
|
|
103
|
+
// serializeDoctype(document.doctype) + '<html></html>'
|
|
104
|
+
// No user-controlled input — safe for document.write in the sandbox iframe.
|
|
101
105
|
const rawHTML = serializeDoctype(document.doctype) + '<html></html>';
|
|
102
106
|
try {
|
|
103
|
-
// Fixing "This document requires 'TrustedHTML' assignment. The action has been blocked." error.
|
|
104
|
-
// Reuse existing policy when present (e.g. second html2canvas call) to avoid createPolicy duplicate-name throw.
|
|
105
107
|
const ownerWindow = this.referenceElement.ownerDocument?.defaultView;
|
|
106
108
|
const trustedTypesFactory = ownerWindow && ownerWindow.trustedTypes;
|
|
107
109
|
let policy = trustedTypesFactory?.getPolicy?.('html2canvas-pro');
|
|
108
110
|
if (!policy && trustedTypesFactory) {
|
|
109
111
|
policy = trustedTypesFactory.createPolicy('html2canvas-pro', {
|
|
110
|
-
createHTML: (
|
|
112
|
+
createHTML: (s) => s
|
|
111
113
|
});
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
documentClone.write(rawHTML);
|
|
118
|
-
}
|
|
115
|
+
// Prefer Trusted Types when available; fallback is the same static HTML.
|
|
116
|
+
const html = policy ? policy.createHTML(rawHTML) : rawHTML;
|
|
117
|
+
// CodeQL:no - rawHTML is a static internal string, never user-controlled
|
|
118
|
+
documentClone.write(html);
|
|
119
119
|
}
|
|
120
120
|
catch (_e) {
|
|
121
|
+
// CodeQL:no - rawHTML is a static internal string, never user-controlled
|
|
121
122
|
documentClone.write(rawHTML);
|
|
122
123
|
}
|
|
123
124
|
// Chrome scrolls the parent document for some reason after the write to the cloned window???
|
|
@@ -284,162 +285,14 @@ class DocumentCloner {
|
|
|
284
285
|
return blankCanvas;
|
|
285
286
|
}
|
|
286
287
|
appendChildNode(clone, child, copyStyles) {
|
|
287
|
-
this.
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Check if a child node should be cloned based on filtering rules
|
|
291
|
-
* Filters out: scripts, ignored elements, and optionally styles
|
|
292
|
-
*/
|
|
293
|
-
shouldCloneChild(child) {
|
|
294
|
-
return (!(0, node_parser_1.isElementNode)(child) ||
|
|
295
|
-
(!(0, node_parser_1.isScriptElement)(child) &&
|
|
296
|
-
!child.hasAttribute(IGNORE_ATTRIBUTE) &&
|
|
297
|
-
(typeof this.options.ignoreElements !== 'function' || !this.options.ignoreElements(child))));
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Check if a style element should be cloned based on copyStyles option
|
|
301
|
-
*/
|
|
302
|
-
shouldCloneStyleElement(child) {
|
|
303
|
-
return !this.options.copyStyles || !(0, node_parser_1.isElementNode)(child) || !(0, node_parser_1.isStyleElement)(child);
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Safely append a cloned child to a target, applying all filtering rules
|
|
307
|
-
*/
|
|
308
|
-
safeAppendClonedChild(target, child, copyStyles) {
|
|
309
|
-
if (this.shouldCloneChild(child) && this.shouldCloneStyleElement(child)) {
|
|
310
|
-
target.appendChild(this.cloneNode(child, copyStyles));
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Clone assigned nodes from a slot element to the target
|
|
315
|
-
*/
|
|
316
|
-
cloneAssignedNodes(assignedNodes, target, copyStyles) {
|
|
317
|
-
assignedNodes.forEach((node) => {
|
|
318
|
-
this.safeAppendClonedChild(target, node, copyStyles);
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Clone fallback content from a slot element when no nodes are assigned
|
|
323
|
-
*/
|
|
324
|
-
cloneSlotFallbackContent(slot, target, copyStyles) {
|
|
325
|
-
for (let child = slot.firstChild; child; child = child.nextSibling) {
|
|
326
|
-
this.safeAppendClonedChild(target, child, copyStyles);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Handle cloning of a slot element, including assigned nodes or fallback content
|
|
331
|
-
*/
|
|
332
|
-
cloneSlotElement(slot, targetShadowRoot, copyStyles) {
|
|
333
|
-
if (!(0, node_parser_1.isSlotElement)(slot)) {
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
const slotElement = slot;
|
|
337
|
-
// Defensive check: ensure assignedNodes method exists
|
|
338
|
-
if (typeof slotElement.assignedNodes !== 'function') {
|
|
339
|
-
this.context.logger.warn('HTMLSlotElement.assignedNodes is not available', slot);
|
|
340
|
-
this.cloneSlotFallbackContent(slot, targetShadowRoot, copyStyles);
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
const assignedNodes = slotElement.assignedNodes();
|
|
344
|
-
// Defensive check: ensure assignedNodes returns an array
|
|
345
|
-
if (!assignedNodes || !Array.isArray(assignedNodes)) {
|
|
346
|
-
this.context.logger.warn('assignedNodes() did not return a valid array', slot);
|
|
347
|
-
this.cloneSlotFallbackContent(slot, targetShadowRoot, copyStyles);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
if (assignedNodes.length > 0) {
|
|
351
|
-
// Clone assigned nodes
|
|
352
|
-
this.cloneAssignedNodes(assignedNodes, targetShadowRoot, copyStyles);
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
// Clone fallback content
|
|
356
|
-
this.cloneSlotFallbackContent(slot, targetShadowRoot, copyStyles);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Clone shadow DOM children to the target shadow root
|
|
361
|
-
*/
|
|
362
|
-
cloneShadowDOMChildren(shadowRoot, targetShadowRoot, copyStyles) {
|
|
363
|
-
for (let child = shadowRoot.firstChild; child; child = child.nextSibling) {
|
|
364
|
-
if ((0, node_parser_1.isElementNode)(child) && (0, node_parser_1.isSlotElement)(child)) {
|
|
365
|
-
// Handle slot elements specially
|
|
366
|
-
this.cloneSlotElement(child, targetShadowRoot, copyStyles);
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
// Clone regular elements
|
|
370
|
-
this.safeAppendClonedChild(targetShadowRoot, child, copyStyles);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Clone light DOM children to the target element
|
|
376
|
-
*/
|
|
377
|
-
cloneLightDOMChildren(node, clone, copyStyles) {
|
|
378
|
-
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
379
|
-
this.appendChildNode(clone, child, copyStyles);
|
|
380
|
-
}
|
|
288
|
+
this.slotCloner.appendChildNode(clone, child, copyStyles);
|
|
381
289
|
}
|
|
382
290
|
/**
|
|
383
|
-
* Clone
|
|
384
|
-
|
|
385
|
-
cloneSlotElementAsLightDOM(slot, clone, copyStyles) {
|
|
386
|
-
if (!(0, node_parser_1.isSlotElement)(slot)) {
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
const slotElement = slot;
|
|
390
|
-
if (typeof slotElement.assignedNodes !== 'function') {
|
|
391
|
-
// Fallback: clone slot's children
|
|
392
|
-
for (let child = slot.firstChild; child; child = child.nextSibling) {
|
|
393
|
-
this.appendChildNode(clone, child, copyStyles);
|
|
394
|
-
}
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
const assignedNodes = slotElement.assignedNodes();
|
|
398
|
-
if (assignedNodes && Array.isArray(assignedNodes) && assignedNodes.length > 0) {
|
|
399
|
-
// Clone assigned nodes as light DOM
|
|
400
|
-
assignedNodes.forEach((node) => this.appendChildNode(clone, node, copyStyles));
|
|
401
|
-
}
|
|
402
|
-
else {
|
|
403
|
-
// Clone fallback content as light DOM
|
|
404
|
-
for (let child = slot.firstChild; child; child = child.nextSibling) {
|
|
405
|
-
this.appendChildNode(clone, child, copyStyles);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Clone shadow DOM content as light DOM when shadow root creation failed
|
|
411
|
-
* This is a fallback mechanism to ensure content is not lost
|
|
412
|
-
*/
|
|
413
|
-
cloneShadowDOMAsLightDOM(shadowRoot, clone, copyStyles) {
|
|
414
|
-
for (let child = shadowRoot.firstChild; child; child = child.nextSibling) {
|
|
415
|
-
if ((0, node_parser_1.isElementNode)(child) && (0, node_parser_1.isSlotElement)(child)) {
|
|
416
|
-
this.cloneSlotElementAsLightDOM(child, clone, copyStyles);
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
this.appendChildNode(clone, child, copyStyles);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* Clone child nodes from source element to clone element
|
|
425
|
-
* Handles shadow DOM, slots, and light DOM appropriately
|
|
291
|
+
* Clone child nodes from source element to clone element.
|
|
292
|
+
* Delegates to SlotCloner which handles shadow DOM, slots, and light DOM.
|
|
426
293
|
*/
|
|
427
294
|
cloneChildNodes(node, clone, copyStyles) {
|
|
428
|
-
|
|
429
|
-
// Both original and clone have shadow roots - clone shadow DOM content
|
|
430
|
-
this.cloneShadowDOMChildren(node.shadowRoot, clone.shadowRoot, copyStyles);
|
|
431
|
-
// Also clone light DOM (slot content sources)
|
|
432
|
-
this.cloneLightDOMChildren(node, clone, copyStyles);
|
|
433
|
-
}
|
|
434
|
-
else if (node.shadowRoot && !clone.shadowRoot) {
|
|
435
|
-
// Original has shadow root but clone doesn't (creation failed)
|
|
436
|
-
// Fallback: clone shadow DOM content as light DOM to preserve content
|
|
437
|
-
this.cloneShadowDOMAsLightDOM(node.shadowRoot, clone, copyStyles);
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
// No shadow DOM - just clone light DOM children
|
|
441
|
-
this.cloneLightDOMChildren(node, clone, copyStyles);
|
|
442
|
-
}
|
|
295
|
+
this.slotCloner.cloneChildNodes(node, clone, copyStyles);
|
|
443
296
|
}
|
|
444
297
|
cloneNode(node, copyStyles) {
|
|
445
298
|
if ((0, node_parser_1.isTextNode)(node)) {
|
|
@@ -606,7 +459,7 @@ const createIFrameContainer = (ownerDocument, bounds, customContainer) => {
|
|
|
606
459
|
cloneIframeContainer.width = bounds.width.toString();
|
|
607
460
|
cloneIframeContainer.height = bounds.height.toString();
|
|
608
461
|
cloneIframeContainer.scrolling = 'no'; // ios won't scroll without it
|
|
609
|
-
cloneIframeContainer.setAttribute(IGNORE_ATTRIBUTE, 'true');
|
|
462
|
+
cloneIframeContainer.setAttribute(slot_cloner_1.IGNORE_ATTRIBUTE, 'true');
|
|
610
463
|
// Use custom container if provided, otherwise use body
|
|
611
464
|
const container = customContainer || ownerDocument.body;
|
|
612
465
|
container.appendChild(cloneIframeContainer);
|
|
@@ -638,11 +491,18 @@ const iframeLoader = (iframe) => {
|
|
|
638
491
|
const documentClone = cloneWindow.document;
|
|
639
492
|
cloneWindow.onload = iframe.onload = () => {
|
|
640
493
|
cloneWindow.onload = iframe.onload = null;
|
|
494
|
+
const MAX_POLL_ATTEMPTS = 600; // 30 seconds at 50ms intervals
|
|
495
|
+
let attempts = 0;
|
|
641
496
|
const interval = setInterval(() => {
|
|
497
|
+
attempts++;
|
|
642
498
|
if (documentClone.body.childNodes.length > 0 && documentClone.readyState === 'complete') {
|
|
643
499
|
clearInterval(interval);
|
|
644
500
|
resolve(iframe);
|
|
645
501
|
}
|
|
502
|
+
else if (attempts >= MAX_POLL_ATTEMPTS) {
|
|
503
|
+
clearInterval(interval);
|
|
504
|
+
resolve(iframe); // resolve anyway to avoid hanging
|
|
505
|
+
}
|
|
646
506
|
}, 50);
|
|
647
507
|
};
|
|
648
508
|
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SlotCloner = exports.IGNORE_ATTRIBUTE = void 0;
|
|
4
|
+
const node_parser_1 = require("./node-parser");
|
|
5
|
+
/** Exported for reuse in document-cloner.ts */
|
|
6
|
+
exports.IGNORE_ATTRIBUTE = 'data-html2canvas-ignore';
|
|
7
|
+
/**
|
|
8
|
+
* Handles shadow DOM child cloning, slot assignment, and light DOM traversal.
|
|
9
|
+
* Extracted from DocumentCloner to reduce file size and improve separation of concerns.
|
|
10
|
+
*/
|
|
11
|
+
class SlotCloner {
|
|
12
|
+
constructor(cloneNodeFn, options, context) {
|
|
13
|
+
this.cloneNodeFn = cloneNodeFn;
|
|
14
|
+
this.options = options;
|
|
15
|
+
this.context = context;
|
|
16
|
+
}
|
|
17
|
+
appendChildNode(clone, child, copyStyles) {
|
|
18
|
+
this.safeAppendClonedChild(clone, child, copyStyles);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Clone child nodes from source element to clone element.
|
|
22
|
+
* Handles shadow DOM, slots, and light DOM appropriately.
|
|
23
|
+
*/
|
|
24
|
+
cloneChildNodes(node, clone, copyStyles) {
|
|
25
|
+
if (node.shadowRoot && clone.shadowRoot) {
|
|
26
|
+
// Both original and clone have shadow roots - clone shadow DOM content
|
|
27
|
+
this.cloneShadowDOMChildren(node.shadowRoot, clone.shadowRoot, copyStyles);
|
|
28
|
+
// Also clone light DOM (slot content sources)
|
|
29
|
+
this.cloneLightDOMChildren(node, clone, copyStyles);
|
|
30
|
+
}
|
|
31
|
+
else if (node.shadowRoot && !clone.shadowRoot) {
|
|
32
|
+
// Original has shadow root but clone doesn't (creation failed)
|
|
33
|
+
// Fallback: clone shadow DOM content as light DOM to preserve content
|
|
34
|
+
this.cloneShadowDOMAsLightDOM(node.shadowRoot, clone, copyStyles);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// No shadow DOM - just clone light DOM children
|
|
38
|
+
this.cloneLightDOMChildren(node, clone, copyStyles);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if a child node should be cloned based on filtering rules.
|
|
43
|
+
* Filters out: scripts, ignored elements, and optionally styles.
|
|
44
|
+
*/
|
|
45
|
+
shouldCloneChild(child) {
|
|
46
|
+
return (!(0, node_parser_1.isElementNode)(child) ||
|
|
47
|
+
(!(0, node_parser_1.isScriptElement)(child) &&
|
|
48
|
+
!child.hasAttribute(exports.IGNORE_ATTRIBUTE) &&
|
|
49
|
+
(typeof this.options.ignoreElements !== 'function' || !this.options.ignoreElements(child))));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a style element should be cloned based on copyStyles option.
|
|
53
|
+
*/
|
|
54
|
+
shouldCloneStyleElement(child) {
|
|
55
|
+
return !this.options.copyStyles || !(0, node_parser_1.isElementNode)(child) || !(0, node_parser_1.isStyleElement)(child);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Safely append a cloned child to a target, applying all filtering rules.
|
|
59
|
+
*/
|
|
60
|
+
safeAppendClonedChild(target, child, copyStyles) {
|
|
61
|
+
if (this.shouldCloneChild(child) && this.shouldCloneStyleElement(child)) {
|
|
62
|
+
target.appendChild(this.cloneNodeFn(child, copyStyles));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Clone assigned nodes from a slot element to the target.
|
|
67
|
+
*/
|
|
68
|
+
cloneAssignedNodes(assignedNodes, target, copyStyles) {
|
|
69
|
+
assignedNodes.forEach((node) => {
|
|
70
|
+
this.safeAppendClonedChild(target, node, copyStyles);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Clone fallback content from a slot element when no nodes are assigned.
|
|
75
|
+
*/
|
|
76
|
+
cloneSlotFallbackContent(slot, target, copyStyles) {
|
|
77
|
+
for (let child = slot.firstChild; child; child = child.nextSibling) {
|
|
78
|
+
this.safeAppendClonedChild(target, child, copyStyles);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Handle cloning of a slot element, including assigned nodes or fallback content.
|
|
83
|
+
*/
|
|
84
|
+
cloneSlotElement(slot, targetShadowRoot, copyStyles) {
|
|
85
|
+
if (!(0, node_parser_1.isSlotElement)(slot)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const slotElement = slot;
|
|
89
|
+
// Defensive check: ensure assignedNodes method exists
|
|
90
|
+
if (typeof slotElement.assignedNodes !== 'function') {
|
|
91
|
+
this.context.logger.warn('HTMLSlotElement.assignedNodes is not available', slot);
|
|
92
|
+
this.cloneSlotFallbackContent(slot, targetShadowRoot, copyStyles);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const assignedNodes = slotElement.assignedNodes();
|
|
96
|
+
// Defensive check: ensure assignedNodes returns an array
|
|
97
|
+
if (!assignedNodes || !Array.isArray(assignedNodes)) {
|
|
98
|
+
this.context.logger.warn('assignedNodes() did not return a valid array', slot);
|
|
99
|
+
this.cloneSlotFallbackContent(slot, targetShadowRoot, copyStyles);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (assignedNodes.length > 0) {
|
|
103
|
+
// Clone assigned nodes
|
|
104
|
+
this.cloneAssignedNodes(assignedNodes, targetShadowRoot, copyStyles);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// Clone fallback content
|
|
108
|
+
this.cloneSlotFallbackContent(slot, targetShadowRoot, copyStyles);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Clone shadow DOM children to the target shadow root.
|
|
113
|
+
*/
|
|
114
|
+
cloneShadowDOMChildren(shadowRoot, targetShadowRoot, copyStyles) {
|
|
115
|
+
for (let child = shadowRoot.firstChild; child; child = child.nextSibling) {
|
|
116
|
+
if ((0, node_parser_1.isElementNode)(child) && (0, node_parser_1.isSlotElement)(child)) {
|
|
117
|
+
// Handle slot elements specially
|
|
118
|
+
this.cloneSlotElement(child, targetShadowRoot, copyStyles);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Clone regular elements
|
|
122
|
+
this.safeAppendClonedChild(targetShadowRoot, child, copyStyles);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Clone light DOM children to the target element.
|
|
128
|
+
*/
|
|
129
|
+
cloneLightDOMChildren(node, clone, copyStyles) {
|
|
130
|
+
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
131
|
+
this.appendChildNode(clone, child, copyStyles);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Clone slot element as light DOM when shadow root creation failed.
|
|
136
|
+
*/
|
|
137
|
+
cloneSlotElementAsLightDOM(slot, clone, copyStyles) {
|
|
138
|
+
if (!(0, node_parser_1.isSlotElement)(slot)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const slotElement = slot;
|
|
142
|
+
if (typeof slotElement.assignedNodes !== 'function') {
|
|
143
|
+
// Fallback: clone slot's children
|
|
144
|
+
for (let child = slot.firstChild; child; child = child.nextSibling) {
|
|
145
|
+
this.appendChildNode(clone, child, copyStyles);
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const assignedNodes = slotElement.assignedNodes();
|
|
150
|
+
if (assignedNodes && Array.isArray(assignedNodes) && assignedNodes.length > 0) {
|
|
151
|
+
// Clone assigned nodes as light DOM
|
|
152
|
+
assignedNodes.forEach((node) => this.appendChildNode(clone, node, copyStyles));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Clone fallback content as light DOM
|
|
156
|
+
for (let child = slot.firstChild; child; child = child.nextSibling) {
|
|
157
|
+
this.appendChildNode(clone, child, copyStyles);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Clone shadow DOM content as light DOM when shadow root creation failed.
|
|
163
|
+
* This is a fallback mechanism to ensure content is not lost.
|
|
164
|
+
*/
|
|
165
|
+
cloneShadowDOMAsLightDOM(shadowRoot, clone, copyStyles) {
|
|
166
|
+
for (let child = shadowRoot.firstChild; child; child = child.nextSibling) {
|
|
167
|
+
if ((0, node_parser_1.isElementNode)(child) && (0, node_parser_1.isSlotElement)(child)) {
|
|
168
|
+
this.cloneSlotElementAsLightDOM(child, clone, copyStyles);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.appendChildNode(clone, child, copyStyles);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.SlotCloner = SlotCloner;
|
package/dist/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.PerformanceMonitor = exports.createDefaultValidator = exports.Validator = exports.Html2CanvasConfig = exports.html2canvas = void 0;
|
|
4
4
|
const config_1 = require("./config");
|
|
5
5
|
Object.defineProperty(exports, "Html2CanvasConfig", { enumerable: true, get: function () { return config_1.Html2CanvasConfig; } });
|
|
6
6
|
const validator_1 = require("./core/validator");
|
|
@@ -46,20 +46,4 @@ const html2canvas = (element, options = {}, config) => {
|
|
|
46
46
|
return (0, render_element_1.renderElement)(element, options, finalConfig);
|
|
47
47
|
};
|
|
48
48
|
exports.html2canvas = html2canvas;
|
|
49
|
-
/**
|
|
50
|
-
* Set CSP nonce for inline styles.
|
|
51
|
-
*
|
|
52
|
-
* @deprecated Since 2.0.0. Pass `cspNonce` in options instead:
|
|
53
|
-
* `html2canvas(element, { cspNonce: '...' })`
|
|
54
|
-
*/
|
|
55
|
-
const setCspNonce = (nonce) => {
|
|
56
|
-
console.warn('[html2canvas-pro] setCspNonce is deprecated. ' +
|
|
57
|
-
'Pass cspNonce in options instead: html2canvas(element, { cspNonce: "..." })');
|
|
58
|
-
if (typeof window !== 'undefined') {
|
|
59
|
-
(0, config_1.setDefaultConfig)(new config_1.Html2CanvasConfig({ window, cspNonce: nonce }));
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
html2canvas.setCspNonce = setCspNonce;
|
|
63
49
|
exports.default = html2canvas;
|
|
64
|
-
var image_rendering_1 = require("./css/property-descriptors/image-rendering");
|
|
65
|
-
Object.defineProperty(exports, "IMAGE_RENDERING", { enumerable: true, get: function () { return image_rendering_1.IMAGE_RENDERING; } });
|