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