html2canvas-pro 2.5.4 → 2.6.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * html2canvas-pro 2.5.4 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 2.6.0 <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
  */
@@ -249,6 +249,30 @@
249
249
  };
250
250
  }
251
251
  /**
252
+ * Validate the backgroundColor option
253
+ *
254
+ * @param backgroundColor - CSS color string or null (transparent)
255
+ * @returns Validation result
256
+ */
257
+ validateBackgroundColor(backgroundColor) {
258
+ if (backgroundColor === null) return {
259
+ valid: true,
260
+ sanitized: null
261
+ };
262
+ if (typeof backgroundColor !== "string" || backgroundColor.length === 0) return {
263
+ valid: false,
264
+ error: "backgroundColor must be a CSS color string or null"
265
+ };
266
+ if (/[^a-zA-Z0-9%(),.#\s/-]/.test(backgroundColor)) return {
267
+ valid: false,
268
+ error: `backgroundColor contains characters that cannot appear in a CSS color: ${backgroundColor}`
269
+ };
270
+ return {
271
+ valid: true,
272
+ sanitized: backgroundColor
273
+ };
274
+ }
275
+ /**
252
276
  * Validate image timeout
253
277
  *
254
278
  * @param timeout - Timeout in milliseconds
@@ -393,6 +417,10 @@
393
417
  const nonceResult = this.validateCspNonce(options.cspNonce);
394
418
  if (!nonceResult.valid) errors.push(`CSP nonce: ${nonceResult.error}`);
395
419
  }
420
+ if (options.backgroundColor !== void 0) {
421
+ const colorResult = this.validateBackgroundColor(options.backgroundColor);
422
+ if (!colorResult.valid) errors.push(`backgroundColor: ${colorResult.error}`);
423
+ }
396
424
  if (this.config.customValidator) {
397
425
  const customResult = this.config.customValidator(options, "options");
398
426
  if (!customResult.valid) errors.push(`Custom validation: ${customResult.error}`);
@@ -8069,9 +8097,9 @@
8069
8097
  if (!rules) return true;
8070
8098
  for (let i = 0; i < rules.length; i++) {
8071
8099
  const rule = rules[i];
8072
- if (rule && typeof CSSImportRule !== "undefined" && rule instanceof CSSImportRule) {
8073
- if (!rule.styleSheet || !isSheetFullyApplied(rule.styleSheet, depth + 1)) return false;
8074
- }
8100
+ if (!rule || !("styleSheet" in rule)) continue;
8101
+ const inner = rule.styleSheet;
8102
+ if (!inner || !isSheetFullyApplied(inner, depth + 1)) return false;
8075
8103
  }
8076
8104
  return true;
8077
8105
  };
@@ -8113,7 +8141,7 @@
8113
8141
  * remain as the settle path for plain <link> fetches.
8114
8142
  * @internal – exported for testing only.
8115
8143
  */
8116
- const stylesheetsReady = (documentClone, logger, signal) => {
8144
+ const stylesheetsReady = (documentClone, logger, signal, timeoutMs = STYLESHEET_READY_TIMEOUT_MS) => {
8117
8145
  if (signal?.aborted) return Promise.resolve();
8118
8146
  const pending = [];
8119
8147
  const links = [];
@@ -8143,8 +8171,8 @@
8143
8171
  const timer = setInterval(() => {
8144
8172
  elapsed += 50;
8145
8173
  if (link.sheet) settle();
8146
- else if (elapsed >= 1e4) {
8147
- logger?.warn(`Stylesheet ${href} did not finish loading within ${STYLESHEET_READY_TIMEOUT_MS / 1e3}s; continuing with a possibly unstyled clone`);
8174
+ else if (elapsed >= timeoutMs) {
8175
+ logger?.warn(`Stylesheet ${href} did not finish loading within ${timeoutMs / 1e3}s; continuing with a possibly unstyled clone`);
8148
8176
  settle();
8149
8177
  }
8150
8178
  }, 50);
@@ -8161,8 +8189,8 @@
8161
8189
  const timer = setInterval(() => {
8162
8190
  elapsed += 50;
8163
8191
  const currentSheet = style.sheet;
8164
- if (currentSheet && isSheetFullyApplied(currentSheet) || elapsed >= 1e4) {
8165
- if (elapsed >= 1e4 && !isSheetFullyApplied(currentSheet ?? null)) logger?.warn(`@import chain in an inline <style> did not finish loading within ${STYLESHEET_READY_TIMEOUT_MS / 1e3}s; continuing with a possibly unstyled clone`);
8192
+ if (currentSheet && isSheetFullyApplied(currentSheet) || elapsed >= timeoutMs) {
8193
+ if (elapsed >= timeoutMs && !isSheetFullyApplied(currentSheet ?? null)) logger?.warn(`@import chain in an inline <style> did not finish loading within ${timeoutMs / 1e3}s; continuing with a possibly unstyled clone`);
8166
8194
  clearInterval(timer);
8167
8195
  resolve();
8168
8196
  }
@@ -9850,7 +9878,7 @@
9850
9878
  if (treatAsRealStackingContext || createsStackingContext) {
9851
9879
  const parentStack = treatAsRealStackingContext || child.styles.isPositioned() ? realStackingContext : stackingContext;
9852
9880
  const stack = new StackingContext(paintContainer);
9853
- if (child.styles.isPositioned() || child.styles.opacity < 1 || child.styles.isTransformed()) {
9881
+ if (treatAsRealStackingContext || child.styles.isPositioned() || child.styles.opacity < 1 || child.styles.isTransformed()) {
9854
9882
  const order = child.styles.zIndex.order;
9855
9883
  if (order < 0) {
9856
9884
  let index = 0;
@@ -12148,6 +12176,7 @@
12148
12176
  ctx.fillStyle = rasterized.lastColor;
12149
12177
  ctx.fillRect(area.left, area.top, area.width, area.height);
12150
12178
  ctx.drawImage(rasterized.canvas, area.left + cx - rx, area.top + cy - ry, rx * 2, ry * 2);
12179
+ releaseSurface(rasterized.canvas);
12151
12180
  };
12152
12181
  /** Draw one gradient layer filling the whole mask area. */
12153
12182
  const paintGradientLayer = (ctx, layer, area) => {
@@ -13061,7 +13090,10 @@
13061
13090
  maskCanvas.width = surface.width;
13062
13091
  maskCanvas.height = surface.height;
13063
13092
  const maskCtx = maskCanvas.getContext("2d");
13064
- if (!maskCtx) return;
13093
+ if (!maskCtx) {
13094
+ releaseSurface(maskCanvas);
13095
+ return;
13096
+ }
13065
13097
  maskCtx.scale(this.options.scale, this.options.scale);
13066
13098
  maskCtx.translate(-bounds.left, -bounds.top);
13067
13099
  try {
@@ -13073,6 +13105,7 @@
13073
13105
  }, async (url) => await this.context.cache.match(url));
13074
13106
  } catch (error) {
13075
13107
  this.context.logger.error(`Error rendering mask-image: ${error instanceof Error ? error.message : error}`);
13108
+ releaseSurface(maskCanvas);
13076
13109
  return;
13077
13110
  }
13078
13111
  ctx.save();
@@ -13082,6 +13115,7 @@
13082
13115
  ctx.drawImage(maskCanvas, 0, 0);
13083
13116
  } finally {
13084
13117
  ctx.restore();
13118
+ releaseSurface(maskCanvas);
13085
13119
  }
13086
13120
  }
13087
13121
  /**
@@ -13486,6 +13520,7 @@
13486
13520
  ctx.fillStyle = rasterized.lastColor;
13487
13521
  ctx.fillRect(0, 0, width, height);
13488
13522
  ctx.drawImage(rasterized.canvas, cx - rx, cy - ry, rx * 2, ry * 2);
13523
+ releaseSurface(rasterized.canvas);
13489
13524
  return canvas;
13490
13525
  }
13491
13526
  if (source.type === 5 || source.type === 6) {