html2canvas-pro 2.5.2 → 2.5.3
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/README.md +2 -2
- package/dist/html2canvas-pro.cjs +66 -6
- package/dist/html2canvas-pro.cjs.map +1 -1
- package/dist/html2canvas-pro.esm.js +66 -6
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +66 -6
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +6 -6
- package/dist/types/dom/iframe-mount.d.ts +19 -0
- package/package.json +18 -19
package/dist/html2canvas-pro.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* html2canvas-pro 2.5.
|
|
2
|
+
* html2canvas-pro 2.5.3 <https://yorickshan.github.io/html2canvas-pro/>
|
|
3
3
|
* Copyright (c) 2024-present yorickshan and html2canvas-pro contributors
|
|
4
4
|
* Released under MIT License
|
|
5
5
|
*/
|
|
@@ -8040,6 +8040,54 @@
|
|
|
8040
8040
|
const imagesReady = (document) => {
|
|
8041
8041
|
return Promise.all([].slice.call(document.images, 0).map(imageReady));
|
|
8042
8042
|
};
|
|
8043
|
+
/**
|
|
8044
|
+
* Wait until every external stylesheet link of the clone has been applied or
|
|
8045
|
+
* definitively failed, or the readiness budget expires.
|
|
8046
|
+
*
|
|
8047
|
+
* The clone is mounted by adoptNode() into an already-closed document
|
|
8048
|
+
* (close() must precede adoptNode for Chrome uppercase class-name matching,
|
|
8049
|
+
* see the mount sequence below), so the cloned <link rel="stylesheet">
|
|
8050
|
+
* elements are inserted after the document finished loading: they neither
|
|
8051
|
+
* delay the iframe load event nor change readyState, and the iframeLoader
|
|
8052
|
+
* readiness poll cannot observe them. On a cold cache the first capture
|
|
8053
|
+
* could then measure the clone before its external CSS arrived and render
|
|
8054
|
+
* an unstyled layout (issue #245). Inline <style> clones apply
|
|
8055
|
+
* synchronously and document-level adopted stylesheets are serialised at
|
|
8056
|
+
* clone time, so neither needs this wait.
|
|
8057
|
+
* @internal – exported for testing only.
|
|
8058
|
+
*/
|
|
8059
|
+
const stylesheetsReady = (documentClone, logger, signal) => {
|
|
8060
|
+
if (signal?.aborted) return Promise.resolve();
|
|
8061
|
+
const pending = [];
|
|
8062
|
+
const links = documentClone.getElementsByTagName("link");
|
|
8063
|
+
for (let i = 0; i < links.length; i++) {
|
|
8064
|
+
const link = links[i];
|
|
8065
|
+
if (!link || link.sheet) continue;
|
|
8066
|
+
const rel = (link.getAttribute("rel") ?? "").trim().toLowerCase().split(/\s+/);
|
|
8067
|
+
if (!rel.includes("stylesheet") || rel.includes("alternate") || link.disabled || !link.getAttribute("href")) continue;
|
|
8068
|
+
pending.push(new Promise((resolve) => {
|
|
8069
|
+
let timer;
|
|
8070
|
+
const settle = () => {
|
|
8071
|
+
link.removeEventListener("load", settle);
|
|
8072
|
+
link.removeEventListener("error", settle);
|
|
8073
|
+
signal?.removeEventListener("abort", onAbort);
|
|
8074
|
+
clearTimeout(timer);
|
|
8075
|
+
resolve();
|
|
8076
|
+
};
|
|
8077
|
+
const onAbort = () => settle();
|
|
8078
|
+
timer = setTimeout(() => {
|
|
8079
|
+
logger?.warn(`Stylesheet ${link.href} did not finish loading within ${CLONE_READY_TIMEOUT_MS / 1e3}s; continuing with a possibly unstyled clone`);
|
|
8080
|
+
settle();
|
|
8081
|
+
}, CLONE_READY_TIMEOUT_MS);
|
|
8082
|
+
link.addEventListener("load", settle);
|
|
8083
|
+
link.addEventListener("error", settle);
|
|
8084
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
8085
|
+
if (link.sheet) settle();
|
|
8086
|
+
}));
|
|
8087
|
+
}
|
|
8088
|
+
if (pending.length === 0) return Promise.resolve();
|
|
8089
|
+
return Promise.all(pending).then(() => void 0);
|
|
8090
|
+
};
|
|
8043
8091
|
const iframeLoader = (iframe, logger, signal) => {
|
|
8044
8092
|
return new Promise((resolve, reject) => {
|
|
8045
8093
|
const cloneWindow = iframe.contentWindow;
|
|
@@ -8082,6 +8130,15 @@
|
|
|
8082
8130
|
});
|
|
8083
8131
|
};
|
|
8084
8132
|
/**
|
|
8133
|
+
* Reduce a doctype component to characters that are inert inside a
|
|
8134
|
+
* <!DOCTYPE ...> declaration. Doctype content is parsed as a declaration
|
|
8135
|
+
* token, never as HTML markup, but the fields originate from the captured
|
|
8136
|
+
* document — stripping markup delimiters and control characters here makes
|
|
8137
|
+
* that property locally verifiable (and keeps the sanitizer guarantee even
|
|
8138
|
+
* if a future serializer ever embeds the result elsewhere).
|
|
8139
|
+
*/
|
|
8140
|
+
const sanitizeDoctypePart = (value) => value.replace(/[<>&"'`\\]|[\u0000-\u001f\u007f]/g, "");
|
|
8141
|
+
/**
|
|
8085
8142
|
* Serialise a document type declaration to an HTML string.
|
|
8086
8143
|
* @internal – exported for testing only, not part of the public API.
|
|
8087
8144
|
*/
|
|
@@ -8089,12 +8146,12 @@
|
|
|
8089
8146
|
let str = "";
|
|
8090
8147
|
if (doctype) {
|
|
8091
8148
|
str += "<!DOCTYPE ";
|
|
8092
|
-
if (doctype.name) str += doctype.name;
|
|
8093
|
-
if (doctype.internalSubset) str += " " + doctype.internalSubset
|
|
8149
|
+
if (doctype.name) str += sanitizeDoctypePart(doctype.name);
|
|
8150
|
+
if (doctype.internalSubset) str += " " + sanitizeDoctypePart(doctype.internalSubset);
|
|
8094
8151
|
if (doctype.publicId) {
|
|
8095
|
-
str += " PUBLIC \"" + doctype.publicId
|
|
8096
|
-
if (doctype.systemId) str += " \"" + doctype.systemId
|
|
8097
|
-
} else if (doctype.systemId) str += " SYSTEM \"" + doctype.systemId
|
|
8152
|
+
str += " PUBLIC \"" + sanitizeDoctypePart(doctype.publicId) + "\"";
|
|
8153
|
+
if (doctype.systemId) str += " \"" + sanitizeDoctypePart(doctype.systemId) + "\"";
|
|
8154
|
+
} else if (doctype.systemId) str += " SYSTEM \"" + sanitizeDoctypePart(doctype.systemId) + "\"";
|
|
8098
8155
|
str += ">";
|
|
8099
8156
|
}
|
|
8100
8157
|
return str;
|
|
@@ -8130,6 +8187,8 @@
|
|
|
8130
8187
|
const referenceElement = mount.clonedReferenceElement;
|
|
8131
8188
|
if (typeof referenceElement === "undefined") throw new Error(`Error finding the ${mount.referenceElementName} in the cloned document`);
|
|
8132
8189
|
throwIfAborted(mount.signal);
|
|
8190
|
+
await stylesheetWait;
|
|
8191
|
+
throwIfAborted(mount.signal);
|
|
8133
8192
|
if (documentClone.fonts && documentClone.fonts.ready) await documentClone.fonts.ready;
|
|
8134
8193
|
throwIfAborted(mount.signal);
|
|
8135
8194
|
if (/(AppleWebKit)/g.test(navigator.userAgent)) {
|
|
@@ -8180,6 +8239,7 @@
|
|
|
8180
8239
|
const adoptedNode = documentClone.adoptNode(mount.documentElement);
|
|
8181
8240
|
addBase(adoptedNode, baseUri);
|
|
8182
8241
|
documentClone.replaceChild(adoptedNode, documentClone.documentElement);
|
|
8242
|
+
const stylesheetWait = stylesheetsReady(documentClone, context.logger, mount.signal);
|
|
8183
8243
|
return iframeLoad;
|
|
8184
8244
|
};
|
|
8185
8245
|
//#endregion
|