html2canvas-pro 2.0.1 → 2.0.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * html2canvas-pro 2.0.1 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 2.0.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
  */
@@ -6586,41 +6586,56 @@ class DocumentCloner {
6586
6586
  return iframe;
6587
6587
  });
6588
6588
  /**
6589
- * The baseURI of the document will be lost after documentClone.open().
6590
- * We save it before open() to preserve the original base URI for resource resolution.
6591
- * */
6592
- const baseUri = documentClone.baseURI;
6589
+ * The base URI used for resolving relative URLs (e.g. background-image) in the clone.
6590
+ * Must come from the source document: the iframe document is about:blank, so
6591
+ * documentClone.baseURI would break getComputedStyle() for relative background URLs.
6592
+ */
6593
+ const baseUri = ownerDocument.baseURI;
6593
6594
  documentClone.open();
6595
+ const rawHTML = serializeDoctype(document.doctype) + '<html></html>';
6594
6596
  try {
6595
- // fixing "This document requires 'TrustedHTML' assignment. The action has been blocked." error
6596
- // @ts-ignore
6597
- const policy = trustedTypes.createPolicy('my-policy', {
6598
- createHTML: (string) => string
6599
- });
6600
- const rawHTML = serializeDoctype(document.doctype) + '<html></html>';
6601
- const trustedHTML = policy.createHTML(rawHTML);
6602
- documentClone.write(trustedHTML);
6597
+ // Fixing "This document requires 'TrustedHTML' assignment. The action has been blocked." error.
6598
+ // Reuse existing policy when present (e.g. second html2canvas call) to avoid createPolicy duplicate-name throw.
6599
+ const ownerWindow = this.referenceElement.ownerDocument?.defaultView;
6600
+ const trustedTypesFactory = ownerWindow && ownerWindow.trustedTypes;
6601
+ let policy = trustedTypesFactory?.getPolicy?.('html2canvas-pro');
6602
+ if (!policy && trustedTypesFactory) {
6603
+ policy = trustedTypesFactory.createPolicy('html2canvas-pro', {
6604
+ createHTML: (string) => string
6605
+ });
6606
+ }
6607
+ if (policy) {
6608
+ documentClone.write(policy.createHTML(rawHTML));
6609
+ }
6610
+ else {
6611
+ documentClone.write(rawHTML);
6612
+ }
6603
6613
  }
6604
- catch (e) {
6605
- // if browser does not support trustedTypes
6606
- documentClone.write(serializeDoctype(document.doctype) + '<html></html>');
6614
+ catch (_e) {
6615
+ documentClone.write(rawHTML);
6607
6616
  }
6608
6617
  // Chrome scrolls the parent document for some reason after the write to the cloned window???
6609
6618
  restoreOwnerScroll(this.referenceElement.ownerDocument, scrollX, scrollY);
6610
6619
  /**
6611
- * Note: adoptNode() should be called AFTER documentClone.open() and close()
6620
+ * IMPORTANT: documentClone.close() MUST be called BEFORE adoptNode().
6621
+ *
6622
+ * In Chrome, calling adoptNode() while the document is still "open"
6623
+ * (between document.open() and document.close()) causes CSS rules with
6624
+ * uppercase characters in class names (e.g. ".MyClass") to not match
6625
+ * correctly. Chrome's CSS engine only enters a fully-resolved matching
6626
+ * mode once the document is closed.
6612
6627
  *
6613
- * In Chrome, calling adoptNode() before or during open/write may cause
6614
- * styles with uppercase characters in class names (e.g. ".MyClass") to not apply correctly.
6628
+ * Correct order: open() → write() close() adoptNode() replaceChild()
6615
6629
  *
6616
- * Fix:
6617
- * - Make sure adoptNode() is called after documentClone.open() and close()
6618
- * - This allows Chrome to properly match and apply all CSS rules including mixed-case class selectors.
6619
- * */
6630
+ * Timing: close() queues the iframe 'load' event; because JS is single-threaded,
6631
+ * the synchronous adoptNode() and replaceChild() below complete before that
6632
+ * event is dispatched. iframeLoader's setInterval will therefore see the body
6633
+ * already populated on its first tick.
6634
+ */
6635
+ documentClone.close();
6620
6636
  const adoptedNode = documentClone.adoptNode(this.documentElement);
6621
6637
  addBase(adoptedNode, baseUri);
6622
6638
  documentClone.replaceChild(adoptedNode, documentClone.documentElement);
6623
- documentClone.close();
6624
6639
  return iframeLoad;
6625
6640
  }
6626
6641
  createElementClone(node) {
@@ -7149,13 +7164,16 @@ const serializeDoctype = (doctype) => {
7149
7164
  str += doctype.name;
7150
7165
  }
7151
7166
  if (doctype.internalSubset) {
7152
- str += doctype.internalSubset;
7167
+ str += ' ' + doctype.internalSubset.replace(/"/g, '&quot;').replace(/>/g, '&gt;');
7153
7168
  }
7154
7169
  if (doctype.publicId) {
7155
- str += `"${doctype.publicId}"`;
7170
+ str += ' PUBLIC "' + doctype.publicId.replace(/"/g, '&quot;') + '"';
7171
+ if (doctype.systemId) {
7172
+ str += ' "' + doctype.systemId.replace(/"/g, '&quot;') + '"';
7173
+ }
7156
7174
  }
7157
- if (doctype.systemId) {
7158
- str += `"${doctype.systemId}"`;
7175
+ else if (doctype.systemId) {
7176
+ str += ' SYSTEM "' + doctype.systemId.replace(/"/g, '&quot;') + '"';
7159
7177
  }
7160
7178
  str += '>';
7161
7179
  }