html2canvas-pro 2.1.0 → 2.1.1
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 +21 -7
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +21 -7
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +3 -3
- package/dist/lib/core/cache-storage.js +2 -2
- package/dist/lib/core/features.js +2 -2
- package/dist/lib/render/canvas/background-renderer.js +6 -0
- package/dist/lib/render/canvas/canvas-renderer.js +5 -1
- package/dist/lib/render/canvas/foreignobject-renderer.js +5 -1
- package/package.json +3 -11
- package/dist/lib/invariant.js +0 -9
- package/dist/types/invariant.d.ts +0 -1
- package/src/__tests__/index.ts +0 -99
- package/src/config.ts +0 -107
- package/src/core/__mocks__/cache-storage.ts +0 -1
- package/src/core/__mocks__/context.ts +0 -19
- package/src/core/__mocks__/features.ts +0 -8
- package/src/core/__mocks__/logger.ts +0 -17
- package/src/core/__tests__/cache-storage.test.ts +0 -205
- package/src/core/__tests__/cache-storage.ts +0 -278
- package/src/core/__tests__/logger.ts +0 -29
- package/src/core/__tests__/validator.ts +0 -359
- package/src/core/bitwise.ts +0 -1
- package/src/core/cache-storage.ts +0 -315
- package/src/core/context.ts +0 -31
- package/src/core/debugger.ts +0 -32
- package/src/core/features.ts +0 -222
- package/src/core/logger.ts +0 -64
- package/src/core/origin-checker.ts +0 -57
- package/src/core/performance-monitor.ts +0 -241
- package/src/core/render-element.ts +0 -272
- package/src/core/util.ts +0 -1
- package/src/core/validator.ts +0 -593
- package/src/css/index.ts +0 -427
- package/src/css/layout/__mocks__/bounds.ts +0 -6
- package/src/css/layout/bounds.ts +0 -79
- package/src/css/layout/text.ts +0 -161
- package/src/css/property-descriptor.ts +0 -49
- package/src/css/property-descriptors/__tests__/background-tests.ts +0 -65
- package/src/css/property-descriptors/__tests__/clip-path.test.ts +0 -280
- package/src/css/property-descriptors/__tests__/font-family.ts +0 -25
- package/src/css/property-descriptors/__tests__/image-rendering-integration.test.ts +0 -153
- package/src/css/property-descriptors/__tests__/image-rendering-performance.test.ts +0 -175
- package/src/css/property-descriptors/__tests__/image-rendering.test.ts +0 -72
- package/src/css/property-descriptors/__tests__/paint-order.ts +0 -87
- package/src/css/property-descriptors/__tests__/text-shadow.ts +0 -94
- package/src/css/property-descriptors/__tests__/transform-tests.ts +0 -18
- package/src/css/property-descriptors/background-clip.ts +0 -30
- package/src/css/property-descriptors/background-color.ts +0 -9
- package/src/css/property-descriptors/background-image.ts +0 -27
- package/src/css/property-descriptors/background-origin.ts +0 -31
- package/src/css/property-descriptors/background-position.ts +0 -38
- package/src/css/property-descriptors/background-repeat.ts +0 -44
- package/src/css/property-descriptors/background-size.ts +0 -27
- package/src/css/property-descriptors/border-color.ts +0 -13
- package/src/css/property-descriptors/border-radius.ts +0 -19
- package/src/css/property-descriptors/border-style.ts +0 -34
- package/src/css/property-descriptors/border-width.ts +0 -20
- package/src/css/property-descriptors/box-shadow.ts +0 -60
- package/src/css/property-descriptors/clip-path.ts +0 -271
- package/src/css/property-descriptors/color.ts +0 -9
- package/src/css/property-descriptors/content.ts +0 -26
- package/src/css/property-descriptors/counter-increment.ts +0 -43
- package/src/css/property-descriptors/counter-reset.ts +0 -36
- package/src/css/property-descriptors/direction.ts +0 -23
- package/src/css/property-descriptors/display.ts +0 -117
- package/src/css/property-descriptors/duration.ts +0 -14
- package/src/css/property-descriptors/float.ts +0 -29
- package/src/css/property-descriptors/font-family.ts +0 -38
- package/src/css/property-descriptors/font-size.ts +0 -9
- package/src/css/property-descriptors/font-style.ts +0 -25
- package/src/css/property-descriptors/font-variant.ts +0 -12
- package/src/css/property-descriptors/font-weight.ts +0 -26
- package/src/css/property-descriptors/image-rendering.ts +0 -33
- package/src/css/property-descriptors/letter-spacing.ts +0 -25
- package/src/css/property-descriptors/line-break.ts +0 -22
- package/src/css/property-descriptors/line-height.ts +0 -22
- package/src/css/property-descriptors/list-style-image.ts +0 -19
- package/src/css/property-descriptors/list-style-position.ts +0 -22
- package/src/css/property-descriptors/list-style-type.ts +0 -179
- package/src/css/property-descriptors/margin.ts +0 -13
- package/src/css/property-descriptors/mix-blend-mode.ts +0 -35
- package/src/css/property-descriptors/object-fit.ts +0 -39
- package/src/css/property-descriptors/opacity.ts +0 -15
- package/src/css/property-descriptors/overflow-wrap.ts +0 -22
- package/src/css/property-descriptors/overflow.ts +0 -34
- package/src/css/property-descriptors/padding.ts +0 -14
- package/src/css/property-descriptors/paint-order.ts +0 -42
- package/src/css/property-descriptors/position.ts +0 -30
- package/src/css/property-descriptors/quotes.ts +0 -57
- package/src/css/property-descriptors/rotate.ts +0 -34
- package/src/css/property-descriptors/text-align.ts +0 -26
- package/src/css/property-descriptors/text-decoration-color.ts +0 -9
- package/src/css/property-descriptors/text-decoration-line.ts +0 -38
- package/src/css/property-descriptors/text-decoration-style.ts +0 -32
- package/src/css/property-descriptors/text-decoration-thickness.ts +0 -30
- package/src/css/property-descriptors/text-overflow.ts +0 -23
- package/src/css/property-descriptors/text-shadow.ts +0 -52
- package/src/css/property-descriptors/text-transform.ts +0 -27
- package/src/css/property-descriptors/text-underline-offset.ts +0 -27
- package/src/css/property-descriptors/transform-origin.ts +0 -29
- package/src/css/property-descriptors/transform.ts +0 -74
- package/src/css/property-descriptors/visibility.ts +0 -25
- package/src/css/property-descriptors/webkit-line-clamp.ts +0 -30
- package/src/css/property-descriptors/webkit-text-stroke-color.ts +0 -8
- package/src/css/property-descriptors/webkit-text-stroke-width.ts +0 -15
- package/src/css/property-descriptors/word-break.ts +0 -25
- package/src/css/property-descriptors/writing-mode.ts +0 -37
- package/src/css/property-descriptors/z-index.ts +0 -27
- package/src/css/syntax/__tests__/tokernizer-tests.ts +0 -29
- package/src/css/syntax/parser.ts +0 -188
- package/src/css/syntax/tokenizer.ts +0 -822
- package/src/css/type-descriptor.ts +0 -7
- package/src/css/types/__tests__/color-tests.ts +0 -147
- package/src/css/types/__tests__/image-tests.ts +0 -239
- package/src/css/types/angle.ts +0 -86
- package/src/css/types/color-math.ts +0 -22
- package/src/css/types/color-spaces/a98.ts +0 -86
- package/src/css/types/color-spaces/p3.ts +0 -92
- package/src/css/types/color-spaces/pro-photo.ts +0 -87
- package/src/css/types/color-spaces/rec2020.ts +0 -90
- package/src/css/types/color-spaces/srgb.ts +0 -87
- package/src/css/types/color-utilities.ts +0 -452
- package/src/css/types/color.ts +0 -485
- package/src/css/types/functions/-prefix-linear-gradient.ts +0 -35
- package/src/css/types/functions/-prefix-radial-gradient.ts +0 -106
- package/src/css/types/functions/-webkit-gradient.ts +0 -69
- package/src/css/types/functions/__tests__/radial-gradient.ts +0 -69
- package/src/css/types/functions/counter.ts +0 -511
- package/src/css/types/functions/gradient.ts +0 -206
- package/src/css/types/functions/linear-gradient.ts +0 -28
- package/src/css/types/functions/radial-gradient.ts +0 -101
- package/src/css/types/image.ts +0 -120
- package/src/css/types/index.ts +0 -1
- package/src/css/types/length-percentage.ts +0 -137
- package/src/css/types/length.ts +0 -7
- package/src/css/types/time.ts +0 -20
- package/src/dom/__mocks__/document-cloner.ts +0 -22
- package/src/dom/__tests__/dom-normalizer.test.ts +0 -133
- package/src/dom/__tests__/element-container.test.ts +0 -129
- package/src/dom/document-cloner.ts +0 -929
- package/src/dom/dom-normalizer.ts +0 -133
- package/src/dom/element-container.ts +0 -75
- package/src/dom/elements/li-element-container.ts +0 -10
- package/src/dom/elements/ol-element-container.ts +0 -12
- package/src/dom/elements/select-element-container.ts +0 -10
- package/src/dom/elements/textarea-element-container.ts +0 -9
- package/src/dom/node-parser.ts +0 -177
- package/src/dom/node-type-guards.ts +0 -70
- package/src/dom/replaced-elements/canvas-element-container.ts +0 -15
- package/src/dom/replaced-elements/iframe-element-container.ts +0 -55
- package/src/dom/replaced-elements/image-element-container.ts +0 -16
- package/src/dom/replaced-elements/index.ts +0 -5
- package/src/dom/replaced-elements/input-element-container.ts +0 -105
- package/src/dom/replaced-elements/pseudo-elements.ts +0 -0
- package/src/dom/replaced-elements/svg-element-container.ts +0 -23
- package/src/dom/text-container.ts +0 -42
- package/src/global.d.ts +0 -19
- package/src/index.ts +0 -82
- package/src/invariant.ts +0 -5
- package/src/options.ts +0 -55
- package/src/render/__tests__/object-fit.test.ts +0 -85
- package/src/render/background.ts +0 -298
- package/src/render/bezier-curve.ts +0 -47
- package/src/render/border.ts +0 -165
- package/src/render/bound-curves.ts +0 -388
- package/src/render/box-sizing.ts +0 -31
- package/src/render/canvas/__tests__/background-renderer.test.ts +0 -72
- package/src/render/canvas/__tests__/border-renderer.test.ts +0 -24
- package/src/render/canvas/__tests__/effects-renderer.test.ts +0 -32
- package/src/render/canvas/__tests__/text-renderer.test.ts +0 -471
- package/src/render/canvas/background-renderer.ts +0 -271
- package/src/render/canvas/border-renderer.ts +0 -224
- package/src/render/canvas/canvas-path.ts +0 -31
- package/src/render/canvas/canvas-renderer.ts +0 -641
- package/src/render/canvas/effects-renderer.ts +0 -130
- package/src/render/canvas/foreignobject-renderer.ts +0 -53
- package/src/render/canvas/text-renderer.ts +0 -700
- package/src/render/effects.ts +0 -75
- package/src/render/font-metrics.ts +0 -72
- package/src/render/object-fit.ts +0 -100
- package/src/render/path.ts +0 -37
- package/src/render/renderer-interface.ts +0 -28
- package/src/render/stacking-context.ts +0 -386
- package/src/render/vector.ts +0 -19
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Origin Checker
|
|
3
|
-
*
|
|
4
|
-
* Provides origin checking functionality without global static state.
|
|
5
|
-
* Each instance maintains its own anchor element and origin reference.
|
|
6
|
-
*
|
|
7
|
-
* Replaces the static methods in CacheStorage with instance-based approach.
|
|
8
|
-
*/
|
|
9
|
-
export class OriginChecker {
|
|
10
|
-
private readonly link: HTMLAnchorElement;
|
|
11
|
-
private readonly origin: string;
|
|
12
|
-
|
|
13
|
-
constructor(window: Window) {
|
|
14
|
-
if (!window || !window.document) {
|
|
15
|
-
throw new Error('Valid window object required for OriginChecker');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!window.location || !window.location.href) {
|
|
19
|
-
throw new Error('Window object must have valid location');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
this.link = window.document.createElement('a');
|
|
23
|
-
this.origin = this.getOrigin(window.location.href);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get the origin (protocol + hostname + port) of a URL
|
|
28
|
-
*
|
|
29
|
-
* @param url - URL to parse
|
|
30
|
-
* @returns Origin string (e.g., "https://example.com:8080")
|
|
31
|
-
*/
|
|
32
|
-
getOrigin(url: string): string {
|
|
33
|
-
this.link.href = url;
|
|
34
|
-
// IE9 hack: accessing href twice to ensure it's properly parsed
|
|
35
|
-
this.link.href = this.link.href;
|
|
36
|
-
return this.link.protocol + this.link.hostname + this.link.port;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Check if a URL is from the same origin as the context
|
|
41
|
-
*
|
|
42
|
-
* @param src - URL to check
|
|
43
|
-
* @returns true if same origin, false otherwise
|
|
44
|
-
*/
|
|
45
|
-
isSameOrigin(src: string): boolean {
|
|
46
|
-
return this.getOrigin(src) === this.origin;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Get the current context origin
|
|
51
|
-
*
|
|
52
|
-
* @returns The origin of the context window
|
|
53
|
-
*/
|
|
54
|
-
getContextOrigin(): string {
|
|
55
|
-
return this.origin;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { Context } from './context';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Performance Metric
|
|
5
|
-
*
|
|
6
|
-
* Represents a single performance measurement
|
|
7
|
-
*/
|
|
8
|
-
export interface PerformanceMetric {
|
|
9
|
-
name: string;
|
|
10
|
-
startTime: number;
|
|
11
|
-
endTime?: number;
|
|
12
|
-
duration?: number;
|
|
13
|
-
metadata?: Record<string, any>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Performance Summary
|
|
18
|
-
*
|
|
19
|
-
* Aggregated performance data
|
|
20
|
-
*/
|
|
21
|
-
export interface PerformanceSummary {
|
|
22
|
-
totalDuration: number;
|
|
23
|
-
metrics: PerformanceMetric[];
|
|
24
|
-
breakdown: Array<{
|
|
25
|
-
name: string;
|
|
26
|
-
duration: number;
|
|
27
|
-
percentage: string;
|
|
28
|
-
}>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Performance Monitor
|
|
33
|
-
*
|
|
34
|
-
* Tracks performance metrics throughout the rendering pipeline.
|
|
35
|
-
* Provides insights into where time is spent during rendering.
|
|
36
|
-
*
|
|
37
|
-
* Usage:
|
|
38
|
-
* ```typescript
|
|
39
|
-
* const monitor = new PerformanceMonitor(context);
|
|
40
|
-
*
|
|
41
|
-
* monitor.start('clone');
|
|
42
|
-
* await cloneDocument();
|
|
43
|
-
* monitor.end('clone');
|
|
44
|
-
*
|
|
45
|
-
* const summary = monitor.getSummary();
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
export class PerformanceMonitor {
|
|
49
|
-
private readonly activeMetrics: Map<string, PerformanceMetric> = new Map();
|
|
50
|
-
private readonly completedMetrics: PerformanceMetric[] = [];
|
|
51
|
-
private readonly enabled: boolean;
|
|
52
|
-
private readonly getTime: () => number;
|
|
53
|
-
|
|
54
|
-
constructor(
|
|
55
|
-
private readonly context: Context | null,
|
|
56
|
-
enabled: boolean = true
|
|
57
|
-
) {
|
|
58
|
-
this.enabled = enabled;
|
|
59
|
-
|
|
60
|
-
// Fallback for environments without performance.now()
|
|
61
|
-
this.getTime =
|
|
62
|
-
typeof performance !== 'undefined' && typeof performance.now === 'function'
|
|
63
|
-
? () => performance.now()
|
|
64
|
-
: () => Date.now();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Start measuring a performance metric
|
|
69
|
-
*
|
|
70
|
-
* @param name - Unique name for this metric
|
|
71
|
-
* @param metadata - Optional metadata to attach
|
|
72
|
-
*/
|
|
73
|
-
start(name: string, metadata?: Record<string, any>): void {
|
|
74
|
-
if (!this.enabled) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (this.activeMetrics.has(name)) {
|
|
79
|
-
this.context?.logger.warn(`Performance metric '${name}' already started. Overwriting.`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
this.activeMetrics.set(name, {
|
|
83
|
-
name,
|
|
84
|
-
startTime: this.getTime(),
|
|
85
|
-
metadata
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* End measuring a performance metric
|
|
91
|
-
*
|
|
92
|
-
* @param name - Name of the metric to end
|
|
93
|
-
* @returns The completed metric, or undefined if not found
|
|
94
|
-
*/
|
|
95
|
-
end(name: string): PerformanceMetric | undefined {
|
|
96
|
-
if (!this.enabled) {
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const metric = this.activeMetrics.get(name);
|
|
101
|
-
|
|
102
|
-
if (!metric) {
|
|
103
|
-
this.context?.logger.warn(`Performance metric '${name}' not found. Was start() called?`);
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
metric.endTime = this.getTime();
|
|
108
|
-
metric.duration = metric.endTime - metric.startTime;
|
|
109
|
-
|
|
110
|
-
this.completedMetrics.push(metric);
|
|
111
|
-
this.activeMetrics.delete(name);
|
|
112
|
-
|
|
113
|
-
this.context?.logger.debug(`⏱️ ${name}: ${metric.duration.toFixed(2)}ms`, metric.metadata);
|
|
114
|
-
|
|
115
|
-
return metric;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Measure a synchronous function
|
|
120
|
-
*
|
|
121
|
-
* @param name - Name for this measurement
|
|
122
|
-
* @param fn - Function to measure
|
|
123
|
-
* @param metadata - Optional metadata
|
|
124
|
-
* @returns The function's return value
|
|
125
|
-
*/
|
|
126
|
-
measure<T>(name: string, fn: () => T, metadata?: Record<string, any>): T {
|
|
127
|
-
this.start(name, metadata);
|
|
128
|
-
try {
|
|
129
|
-
const result = fn();
|
|
130
|
-
this.end(name);
|
|
131
|
-
return result;
|
|
132
|
-
} catch (error) {
|
|
133
|
-
this.end(name);
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Measure an asynchronous function
|
|
140
|
-
*
|
|
141
|
-
* @param name - Name for this measurement
|
|
142
|
-
* @param fn - Async function to measure
|
|
143
|
-
* @param metadata - Optional metadata
|
|
144
|
-
* @returns Promise resolving to the function's return value
|
|
145
|
-
*/
|
|
146
|
-
async measureAsync<T>(name: string, fn: () => Promise<T>, metadata?: Record<string, any>): Promise<T> {
|
|
147
|
-
this.start(name, metadata);
|
|
148
|
-
try {
|
|
149
|
-
const result = await fn();
|
|
150
|
-
this.end(name);
|
|
151
|
-
return result;
|
|
152
|
-
} catch (error) {
|
|
153
|
-
this.end(name);
|
|
154
|
-
throw error;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get all completed metrics
|
|
160
|
-
*
|
|
161
|
-
* @returns Array of completed performance metrics
|
|
162
|
-
*/
|
|
163
|
-
getMetrics(): PerformanceMetric[] {
|
|
164
|
-
return [...this.completedMetrics];
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Get a specific metric by name
|
|
169
|
-
*
|
|
170
|
-
* @param name - Metric name
|
|
171
|
-
* @returns The metric, or undefined if not found
|
|
172
|
-
*/
|
|
173
|
-
getMetric(name: string): PerformanceMetric | undefined {
|
|
174
|
-
return this.completedMetrics.find((m) => m.name === name);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Get performance summary
|
|
179
|
-
*
|
|
180
|
-
* @returns Aggregated performance data
|
|
181
|
-
*/
|
|
182
|
-
getSummary(): PerformanceSummary {
|
|
183
|
-
const totalDuration = this.completedMetrics.reduce((sum, metric) => sum + (metric.duration || 0), 0);
|
|
184
|
-
|
|
185
|
-
const breakdown = this.completedMetrics.map((metric) => ({
|
|
186
|
-
name: metric.name,
|
|
187
|
-
duration: metric.duration || 0,
|
|
188
|
-
percentage: totalDuration > 0 ? (((metric.duration || 0) / totalDuration) * 100).toFixed(1) + '%' : '0%'
|
|
189
|
-
}));
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
totalDuration,
|
|
193
|
-
metrics: this.getMetrics(),
|
|
194
|
-
breakdown
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Log performance summary to console
|
|
200
|
-
*/
|
|
201
|
-
logSummary(): void {
|
|
202
|
-
if (!this.enabled || this.completedMetrics.length === 0 || !this.context) {
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const summary = this.getSummary();
|
|
207
|
-
|
|
208
|
-
this.context.logger.info(`\n📊 Performance Summary (Total: ${summary.totalDuration.toFixed(2)}ms):`);
|
|
209
|
-
|
|
210
|
-
summary.breakdown
|
|
211
|
-
.sort((a, b) => b.duration - a.duration)
|
|
212
|
-
.forEach((item) => {
|
|
213
|
-
this.context!.logger.info(
|
|
214
|
-
` ${item.name.padEnd(20)} ${item.duration.toFixed(2).padStart(8)}ms ${item.percentage.padStart(6)}`
|
|
215
|
-
);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Clear all metrics
|
|
221
|
-
*/
|
|
222
|
-
clear(): void {
|
|
223
|
-
this.activeMetrics.clear();
|
|
224
|
-
this.completedMetrics.splice(0);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Check if monitoring is enabled
|
|
229
|
-
*/
|
|
230
|
-
isEnabled(): boolean {
|
|
231
|
-
return this.enabled;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Get active (uncompleted) metrics
|
|
236
|
-
* Useful for debugging leaked measurements
|
|
237
|
-
*/
|
|
238
|
-
getActiveMetrics(): string[] {
|
|
239
|
-
return Array.from(this.activeMetrics.keys());
|
|
240
|
-
}
|
|
241
|
-
}
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import { Bounds, parseBounds, parseDocumentSize } from '../css/layout/bounds';
|
|
2
|
-
import { COLORS, parseColor } from '../css/types/color';
|
|
3
|
-
import { isTransparent } from '../css/types/color-utilities';
|
|
4
|
-
import { CloneConfigurations, DocumentCloner } from '../dom/document-cloner';
|
|
5
|
-
import { isBodyElement, isHTMLElement, parseTree } from '../dom/node-parser';
|
|
6
|
-
import { ElementContainer } from '../dom/element-container';
|
|
7
|
-
import { CanvasRenderer, RenderConfigurations } from '../render/canvas/canvas-renderer';
|
|
8
|
-
import { ForeignObjectRenderer } from '../render/canvas/foreignobject-renderer';
|
|
9
|
-
import { Context } from './context';
|
|
10
|
-
import { Html2CanvasConfig } from '../config';
|
|
11
|
-
import { createDefaultValidator } from './validator';
|
|
12
|
-
import { PerformanceMonitor } from './performance-monitor';
|
|
13
|
-
import type { Options } from '../options';
|
|
14
|
-
|
|
15
|
-
const coerceNumberOptions = (opts: Partial<Options>): void => {
|
|
16
|
-
const numKeys: (keyof Options)[] = [
|
|
17
|
-
'scale',
|
|
18
|
-
'width',
|
|
19
|
-
'height',
|
|
20
|
-
'imageTimeout',
|
|
21
|
-
'x',
|
|
22
|
-
'y',
|
|
23
|
-
'windowWidth',
|
|
24
|
-
'windowHeight',
|
|
25
|
-
'scrollX',
|
|
26
|
-
'scrollY'
|
|
27
|
-
];
|
|
28
|
-
numKeys.forEach((key) => {
|
|
29
|
-
const v = opts[key];
|
|
30
|
-
if (v !== undefined && v !== null && typeof v !== 'number') {
|
|
31
|
-
const n = Number(v);
|
|
32
|
-
if (!Number.isNaN(n)) {
|
|
33
|
-
(opts as Record<string, unknown>)[key] = n;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const renderElement = async (
|
|
40
|
-
element: HTMLElement,
|
|
41
|
-
opts: Partial<Options>,
|
|
42
|
-
config: Html2CanvasConfig
|
|
43
|
-
): Promise<HTMLCanvasElement> => {
|
|
44
|
-
coerceNumberOptions(opts);
|
|
45
|
-
|
|
46
|
-
// Input validation (unless explicitly skipped)
|
|
47
|
-
if (!opts.skipValidation) {
|
|
48
|
-
const validator = opts.validator || createDefaultValidator();
|
|
49
|
-
|
|
50
|
-
const elementValidation = validator.validateElement(element);
|
|
51
|
-
if (!elementValidation.valid) {
|
|
52
|
-
throw new Error(elementValidation.error);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const optionsValidation = validator.validateOptions(opts);
|
|
56
|
-
if (!optionsValidation.valid) {
|
|
57
|
-
throw new Error(`Invalid options: ${optionsValidation.error}`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (!element || typeof element !== 'object') {
|
|
62
|
-
throw new Error('Invalid element provided as first argument');
|
|
63
|
-
}
|
|
64
|
-
const ownerDocument = element.ownerDocument;
|
|
65
|
-
|
|
66
|
-
if (!ownerDocument) {
|
|
67
|
-
throw new Error(`Element is not attached to a Document`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const defaultView = ownerDocument.defaultView;
|
|
71
|
-
|
|
72
|
-
if (!defaultView) {
|
|
73
|
-
throw new Error(`Document is not attached to a Window`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const resourceOptions = {
|
|
77
|
-
allowTaint: opts.allowTaint ?? false,
|
|
78
|
-
imageTimeout: opts.imageTimeout ?? 15000,
|
|
79
|
-
proxy: opts.proxy,
|
|
80
|
-
useCORS: opts.useCORS ?? false,
|
|
81
|
-
customIsSameOrigin: opts.customIsSameOrigin
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const contextOptions = {
|
|
85
|
-
logging: opts.logging ?? true,
|
|
86
|
-
cache: opts.cache ?? config.cache,
|
|
87
|
-
...resourceOptions
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const DEFAULT_WINDOW_WIDTH = 800;
|
|
91
|
-
const DEFAULT_WINDOW_HEIGHT = 600;
|
|
92
|
-
const DEFAULT_SCROLL = 0;
|
|
93
|
-
const win = defaultView as Window & {
|
|
94
|
-
innerWidth?: number;
|
|
95
|
-
innerHeight?: number;
|
|
96
|
-
pageXOffset?: number;
|
|
97
|
-
pageYOffset?: number;
|
|
98
|
-
};
|
|
99
|
-
const windowOptions = {
|
|
100
|
-
windowWidth: opts.windowWidth ?? win.innerWidth ?? DEFAULT_WINDOW_WIDTH,
|
|
101
|
-
windowHeight: opts.windowHeight ?? win.innerHeight ?? DEFAULT_WINDOW_HEIGHT,
|
|
102
|
-
scrollX: opts.scrollX ?? win.pageXOffset ?? DEFAULT_SCROLL,
|
|
103
|
-
scrollY: opts.scrollY ?? win.pageYOffset ?? DEFAULT_SCROLL
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const windowBounds = new Bounds(
|
|
107
|
-
windowOptions.scrollX,
|
|
108
|
-
windowOptions.scrollY,
|
|
109
|
-
windowOptions.windowWidth,
|
|
110
|
-
windowOptions.windowHeight
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const context = new Context(contextOptions, windowBounds, config);
|
|
114
|
-
|
|
115
|
-
const performanceMonitoring = opts.enablePerformanceMonitoring ?? opts.logging ?? false;
|
|
116
|
-
const perfMonitor = new PerformanceMonitor(context, performanceMonitoring);
|
|
117
|
-
|
|
118
|
-
perfMonitor.start('total', {
|
|
119
|
-
width: windowOptions.windowWidth,
|
|
120
|
-
height: windowOptions.windowHeight
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const signal = opts.signal;
|
|
124
|
-
|
|
125
|
-
if (signal?.aborted) {
|
|
126
|
-
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const foreignObjectRendering = opts.foreignObjectRendering ?? false;
|
|
130
|
-
|
|
131
|
-
const cloneOptions: CloneConfigurations = {
|
|
132
|
-
allowTaint: opts.allowTaint ?? false,
|
|
133
|
-
onclone: opts.onclone,
|
|
134
|
-
ignoreElements: opts.ignoreElements,
|
|
135
|
-
iframeContainer: opts.iframeContainer,
|
|
136
|
-
inlineImages: foreignObjectRendering,
|
|
137
|
-
copyStyles: foreignObjectRendering,
|
|
138
|
-
cspNonce: opts.cspNonce ?? config.cspNonce
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
context.logger.debug(
|
|
142
|
-
`Starting document clone with size ${windowBounds.width}x${
|
|
143
|
-
windowBounds.height
|
|
144
|
-
} scrolled to ${-windowBounds.left},${-windowBounds.top}`
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
perfMonitor.start('clone');
|
|
148
|
-
const documentCloner = new DocumentCloner(context, element, cloneOptions);
|
|
149
|
-
const clonedElement = documentCloner.clonedReferenceElement;
|
|
150
|
-
if (!clonedElement) {
|
|
151
|
-
throw new Error('Unable to find element in cloned iframe');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const container = await documentCloner.toIFrame(ownerDocument, windowBounds);
|
|
155
|
-
perfMonitor.end('clone');
|
|
156
|
-
|
|
157
|
-
if (signal?.aborted) {
|
|
158
|
-
if (opts.removeContainer ?? true) {
|
|
159
|
-
DocumentCloner.destroy(container);
|
|
160
|
-
}
|
|
161
|
-
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const { width, height, left, top } =
|
|
165
|
-
isBodyElement(clonedElement) || isHTMLElement(clonedElement)
|
|
166
|
-
? parseDocumentSize(clonedElement.ownerDocument)
|
|
167
|
-
: parseBounds(context, clonedElement);
|
|
168
|
-
|
|
169
|
-
const backgroundColor = parseBackgroundColor(context, clonedElement, opts.backgroundColor);
|
|
170
|
-
|
|
171
|
-
const renderOptions: RenderConfigurations = {
|
|
172
|
-
canvas: opts.canvas,
|
|
173
|
-
backgroundColor,
|
|
174
|
-
signal,
|
|
175
|
-
scale: opts.scale ?? defaultView.devicePixelRatio ?? 1,
|
|
176
|
-
x: (opts.x ?? 0) + left,
|
|
177
|
-
y: (opts.y ?? 0) + top,
|
|
178
|
-
width: opts.width ?? Math.ceil(width),
|
|
179
|
-
height: opts.height ?? Math.ceil(height),
|
|
180
|
-
imageSmoothing: opts.imageSmoothing,
|
|
181
|
-
imageSmoothingQuality: opts.imageSmoothingQuality
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
let canvas;
|
|
185
|
-
|
|
186
|
-
let root: ElementContainer | undefined;
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
if (foreignObjectRendering) {
|
|
190
|
-
context.logger.debug(`Document cloned, using foreign object rendering`);
|
|
191
|
-
perfMonitor.start('render-foreignobject');
|
|
192
|
-
const renderer = new ForeignObjectRenderer(context, renderOptions);
|
|
193
|
-
canvas = await renderer.render(clonedElement);
|
|
194
|
-
perfMonitor.end('render-foreignobject');
|
|
195
|
-
} else {
|
|
196
|
-
context.logger.debug(
|
|
197
|
-
`Document cloned, element located at ${left},${top} with size ${width}x${height} using computed rendering`
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
context.logger.debug(`Starting DOM parsing`);
|
|
201
|
-
perfMonitor.start('parse');
|
|
202
|
-
if (signal?.aborted) {
|
|
203
|
-
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
204
|
-
}
|
|
205
|
-
root = parseTree(context, clonedElement);
|
|
206
|
-
perfMonitor.end('parse');
|
|
207
|
-
|
|
208
|
-
if (backgroundColor === root.styles.backgroundColor) {
|
|
209
|
-
root.styles.backgroundColor = COLORS.TRANSPARENT;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
context.logger.debug(
|
|
213
|
-
`Starting renderer for element at ${renderOptions.x},${renderOptions.y} with size ${renderOptions.width}x${renderOptions.height}`
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
perfMonitor.start('render');
|
|
217
|
-
if (signal?.aborted) {
|
|
218
|
-
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
219
|
-
}
|
|
220
|
-
const renderer = new CanvasRenderer(context, renderOptions);
|
|
221
|
-
canvas = await renderer.render(root);
|
|
222
|
-
perfMonitor.end('render');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
perfMonitor.start('cleanup');
|
|
226
|
-
if (opts.removeContainer ?? true) {
|
|
227
|
-
if (!DocumentCloner.destroy(container)) {
|
|
228
|
-
context.logger.error(`Cannot detach cloned iframe as it is not in the DOM anymore`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
perfMonitor.end('cleanup');
|
|
232
|
-
|
|
233
|
-
perfMonitor.end('total');
|
|
234
|
-
context.logger.debug(`Finished rendering`);
|
|
235
|
-
|
|
236
|
-
if (performanceMonitoring) {
|
|
237
|
-
perfMonitor.logSummary();
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return canvas;
|
|
241
|
-
} finally {
|
|
242
|
-
if (root) {
|
|
243
|
-
root.restoreTree();
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
const parseBackgroundColor = (context: Context, element: HTMLElement, backgroundColorOverride?: string | null) => {
|
|
249
|
-
const ownerDocument = element.ownerDocument;
|
|
250
|
-
// http://www.w3.org/TR/css3-background/#special-backgrounds
|
|
251
|
-
const documentBackgroundColor = ownerDocument.documentElement
|
|
252
|
-
? parseColor(context, getComputedStyle(ownerDocument.documentElement).backgroundColor as string)
|
|
253
|
-
: COLORS.TRANSPARENT;
|
|
254
|
-
const bodyBackgroundColor = ownerDocument.body
|
|
255
|
-
? parseColor(context, getComputedStyle(ownerDocument.body).backgroundColor as string)
|
|
256
|
-
: COLORS.TRANSPARENT;
|
|
257
|
-
|
|
258
|
-
const defaultBackgroundColor =
|
|
259
|
-
typeof backgroundColorOverride === 'string'
|
|
260
|
-
? parseColor(context, backgroundColorOverride)
|
|
261
|
-
: backgroundColorOverride === null
|
|
262
|
-
? COLORS.TRANSPARENT
|
|
263
|
-
: 0xffffffff;
|
|
264
|
-
|
|
265
|
-
return element === ownerDocument.documentElement
|
|
266
|
-
? isTransparent(documentBackgroundColor)
|
|
267
|
-
? isTransparent(bodyBackgroundColor)
|
|
268
|
-
? defaultBackgroundColor
|
|
269
|
-
: bodyBackgroundColor
|
|
270
|
-
: documentBackgroundColor
|
|
271
|
-
: defaultBackgroundColor;
|
|
272
|
-
};
|
package/src/core/util.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const SMALL_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
|