codeplay-common 4.3.8 → 4.4.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,265 +1,265 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const rootDir = path.resolve(__dirname, '..');
5
- const androidDir = path.join(rootDir, 'android');
6
- const capacitorConfigPath = path.join(rootDir, 'capacitor.config.json');
7
- const backupProfile = path.join(rootDir, 'baselineProfiles', 'baseline-prof.txt');
8
- const generatedProfile = path.join(
9
- androidDir,
10
- 'app',
11
- 'src',
12
- 'main',
13
- 'generated',
14
- 'baselineProfiles',
15
- 'baseline-prof.txt'
16
- );
17
-
18
- function readFile(filePath) {
19
- return fs.readFileSync(filePath, 'utf8');
20
- }
21
-
22
- function writeFile(filePath, content) {
23
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
24
- fs.writeFileSync(filePath, content);
25
- }
26
-
27
- function patchFile(filePath, patcher) {
28
- const before = readFile(filePath);
29
- const after = patcher(before);
30
- if (after !== before) {
31
- writeFile(filePath, after);
32
- console.log(`Updated ${path.relative(rootDir, filePath)}`);
33
- }
34
- }
35
-
36
- function filesAreSame(firstPath, secondPath) {
37
- const firstStat = fs.statSync(firstPath);
38
- const secondStat = fs.statSync(secondPath);
39
-
40
- if (firstStat.size !== secondStat.size) return false;
41
- if (firstStat.mtimeMs === secondStat.mtimeMs) return true;
42
-
43
- return Buffer.compare(fs.readFileSync(firstPath), fs.readFileSync(secondPath)) === 0;
44
- }
45
-
46
- function copyProfileFile(sourcePath, destinationPath, copiedMessage) {
47
- fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
48
-
49
- if (fs.existsSync(destinationPath) && filesAreSame(sourcePath, destinationPath)) {
50
- console.log(`Baseline profile already in sync at ${path.relative(rootDir, destinationPath)}`);
51
- return;
52
- }
53
-
54
- fs.copyFileSync(sourcePath, destinationPath);
55
- console.log(copiedMessage);
56
- }
57
-
58
- function insertAfter(content, marker, insertion) {
59
- if (content.includes(insertion.trim())) return content;
60
- const index = content.indexOf(marker);
61
- if (index === -1) return `${content.trimEnd()}\n${insertion}\n`;
62
- const insertAt = index + marker.length;
63
- return `${content.slice(0, insertAt)}${insertion}${content.slice(insertAt)}`;
64
- }
65
-
66
- function insertBeforeClosingBrace(content, blockStart, insertion) {
67
- if (content.includes(insertion.trim())) return content;
68
- const startIndex = content.indexOf(blockStart);
69
- if (startIndex === -1) return content;
70
-
71
- let depth = 0;
72
- for (let index = startIndex; index < content.length; index += 1) {
73
- const char = content[index];
74
- if (char === '{') depth += 1;
75
- if (char === '}') {
76
- depth -= 1;
77
- if (depth === 0) {
78
- return `${content.slice(0, index).trimEnd()}\n${insertion}\n${content.slice(index)}`;
79
- }
80
- }
81
- }
82
- return content;
83
- }
84
-
85
- function getAppIdFromCapacitorConfig() {
86
- if (!fs.existsSync(capacitorConfigPath)) {
87
- throw new Error(`capacitor.config.json not found at ${capacitorConfigPath}`);
88
- }
89
-
90
- let config;
91
- try {
92
- config = JSON.parse(readFile(capacitorConfigPath));
93
- } catch (error) {
94
- throw new Error(`Failed to parse capacitor.config.json: ${error.message}`);
95
- }
96
-
97
- const appId = config?.appId;
98
- if (typeof appId !== 'string' || !appId.trim()) {
99
- throw new Error('capacitor.config.json does not contain a valid appId.');
100
- }
101
-
102
- return appId.trim();
103
- }
104
-
105
- function sanitizeJavaPackageSegment(segment) {
106
- const safeSegment = segment.replace(/[^a-zA-Z0-9_]/g, '_');
107
- return safeSegment.replace(/^[0-9]/, (match) => `_${match}`) || 'app';
108
- }
109
-
110
- function toSafeJavaPackageName(value) {
111
- return value
112
- .split('.')
113
- .map(sanitizeJavaPackageSegment)
114
- .join('.');
115
- }
116
-
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const rootDir = path.resolve(__dirname, '..');
5
+ const androidDir = path.join(rootDir, 'android');
6
+ const capacitorConfigPath = path.join(rootDir, 'capacitor.config.json');
7
+ const backupProfile = path.join(rootDir, 'baselineProfiles', 'baseline-prof.txt');
8
+ const generatedProfile = path.join(
9
+ androidDir,
10
+ 'app',
11
+ 'src',
12
+ 'main',
13
+ 'generated',
14
+ 'baselineProfiles',
15
+ 'baseline-prof.txt'
16
+ );
17
+
18
+ function readFile(filePath) {
19
+ return fs.readFileSync(filePath, 'utf8');
20
+ }
21
+
22
+ function writeFile(filePath, content) {
23
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
24
+ fs.writeFileSync(filePath, content);
25
+ }
26
+
27
+ function patchFile(filePath, patcher) {
28
+ const before = readFile(filePath);
29
+ const after = patcher(before);
30
+ if (after !== before) {
31
+ writeFile(filePath, after);
32
+ console.log(`Updated ${path.relative(rootDir, filePath)}`);
33
+ }
34
+ }
35
+
36
+ function filesAreSame(firstPath, secondPath) {
37
+ const firstStat = fs.statSync(firstPath);
38
+ const secondStat = fs.statSync(secondPath);
39
+
40
+ if (firstStat.size !== secondStat.size) return false;
41
+ if (firstStat.mtimeMs === secondStat.mtimeMs) return true;
42
+
43
+ return Buffer.compare(fs.readFileSync(firstPath), fs.readFileSync(secondPath)) === 0;
44
+ }
45
+
46
+ function copyProfileFile(sourcePath, destinationPath, copiedMessage) {
47
+ fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
48
+
49
+ if (fs.existsSync(destinationPath) && filesAreSame(sourcePath, destinationPath)) {
50
+ console.log(`Baseline profile already in sync at ${path.relative(rootDir, destinationPath)}`);
51
+ return;
52
+ }
53
+
54
+ fs.copyFileSync(sourcePath, destinationPath);
55
+ console.log(copiedMessage);
56
+ }
57
+
58
+ function insertAfter(content, marker, insertion) {
59
+ if (content.includes(insertion.trim())) return content;
60
+ const index = content.indexOf(marker);
61
+ if (index === -1) return `${content.trimEnd()}\n${insertion}\n`;
62
+ const insertAt = index + marker.length;
63
+ return `${content.slice(0, insertAt)}${insertion}${content.slice(insertAt)}`;
64
+ }
65
+
66
+ function insertBeforeClosingBrace(content, blockStart, insertion) {
67
+ if (content.includes(insertion.trim())) return content;
68
+ const startIndex = content.indexOf(blockStart);
69
+ if (startIndex === -1) return content;
70
+
71
+ let depth = 0;
72
+ for (let index = startIndex; index < content.length; index += 1) {
73
+ const char = content[index];
74
+ if (char === '{') depth += 1;
75
+ if (char === '}') {
76
+ depth -= 1;
77
+ if (depth === 0) {
78
+ return `${content.slice(0, index).trimEnd()}\n${insertion}\n${content.slice(index)}`;
79
+ }
80
+ }
81
+ }
82
+ return content;
83
+ }
84
+
85
+ function getAppIdFromCapacitorConfig() {
86
+ if (!fs.existsSync(capacitorConfigPath)) {
87
+ throw new Error(`capacitor.config.json not found at ${capacitorConfigPath}`);
88
+ }
89
+
90
+ let config;
91
+ try {
92
+ config = JSON.parse(readFile(capacitorConfigPath));
93
+ } catch (error) {
94
+ throw new Error(`Failed to parse capacitor.config.json: ${error.message}`);
95
+ }
96
+
97
+ const appId = config?.appId;
98
+ if (typeof appId !== 'string' || !appId.trim()) {
99
+ throw new Error('capacitor.config.json does not contain a valid appId.');
100
+ }
101
+
102
+ return appId.trim();
103
+ }
104
+
105
+ function sanitizeJavaPackageSegment(segment) {
106
+ const safeSegment = segment.replace(/[^a-zA-Z0-9_]/g, '_');
107
+ return safeSegment.replace(/^[0-9]/, (match) => `_${match}`) || 'app';
108
+ }
109
+
110
+ function toSafeJavaPackageName(value) {
111
+ return value
112
+ .split('.')
113
+ .map(sanitizeJavaPackageSegment)
114
+ .join('.');
115
+ }
116
+
117
117
  function getBaselineProfileBuildGradle(modulePackage) {
118
118
  return `apply plugin: 'com.android.test'\napply plugin: 'androidx.baselineprofile'\n\nandroid {\n namespace = \"${modulePackage}\"\n compileSdk = rootProject.ext.compileSdkVersion\n\n defaultConfig {\n minSdkVersion rootProject.ext.minSdkVersion\n targetSdkVersion rootProject.ext.targetSdkVersion\n testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n }\n\n targetProjectPath = \":app\"\n}\n\nbaselineProfile {\n useConnectedDevices true\n}\n\ndef generatedBaselineProfileDir = file(\"$projectDir/../app/src/main/generated/baselineProfiles\")\ndef stableBaselineProfileDir = file(\"$projectDir/../baselineProfiles\")\n\ntasks.register(\"copyGeneratedBaselineProfiles\") {\n group = \"baselineprofile\"\n description = \"Copies generated baseline profile files from app module to a stable project-level folder.\"\n\n doLast {\n if (generatedBaselineProfileDir.exists()) {\n delete(stableBaselineProfileDir)\n copy {\n from(generatedBaselineProfileDir)\n include(\"**/*.txt\")\n into(stableBaselineProfileDir)\n }\n println \"Copied baseline profile files to: \" + stableBaselineProfileDir\n } else {\n println \"No generated baseline profiles found at \" + generatedBaselineProfileDir\n }\n }\n}\n\ntasks.matching { task ->\n task.name in ["connectedReleaseAndroidTest", "connectedNonMinifiedReleaseAndroidTest", "connectedAndroidTest"]\n}.configureEach {\n finalizedBy("copyGeneratedBaselineProfiles")\n}\n\ndependencies {\n implementation \"androidx.benchmark:benchmark-macro-junit4:1.4.0\"\n implementation \"androidx.test.ext:junit:$androidxJunitVersion\"\n implementation \"androidx.test.uiautomator:uiautomator:2.3.0\"\n}\n`;
119
119
  }
120
-
121
- const baselineProfileManifest = `<?xml version="1.0" encoding="utf-8"?>
122
- <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
123
- `;
124
-
125
- function getBaselineProfileGenerator(modulePackage, appId) {
126
- return `package ${modulePackage};\n\nimport androidx.benchmark.macro.junit4.BaselineProfileRule;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport kotlin.Unit;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n@RunWith(AndroidJUnit4.class)\npublic class BaselineProfileGenerator {\n private static final String PACKAGE_NAME = \"${appId}\";\n\n @Rule\n public final BaselineProfileRule baselineProfileRule = new BaselineProfileRule();\n\n @Test\n public void generateStartupProfile() {\n baselineProfileRule.collect(\n PACKAGE_NAME,\n 15,\n 3,\n null,\n true,\n true,\n scope -> {\n scope.getDevice().pressHome();\n scope.startActivityAndWait();\n scope.getDevice().waitForIdle();\n waitForWebViewStartup();\n scope.killProcess();\n return Unit.INSTANCE;\n }\n );\n }\n\n private void waitForWebViewStartup() {\n try {\n Thread.sleep(3000);\n } catch (InterruptedException exception) {\n Thread.currentThread().interrupt();\n }\n }\n}\n`;
127
- }
128
-
129
- function ensureBaselineProfilePlugin() {
130
- const rootBuildGradle = path.join(androidDir, 'build.gradle');
131
- patchFile(rootBuildGradle, (content) =>
132
- insertAfter(
133
- content,
134
- "classpath 'com.google.gms:google-services:4.4.4'",
135
- "\n classpath 'androidx.baselineprofile:androidx.baselineprofile.gradle.plugin:1.4.1'"
136
- )
137
- );
138
- }
139
-
140
- function ensureSettingsModule() {
141
- const settingsGradle = path.join(androidDir, 'settings.gradle');
142
- patchFile(settingsGradle, (content) =>
143
- insertAfter(
144
- content,
145
- "include ':app'",
146
- "\ninclude ':baselineprofile'\nproject(':baselineprofile').projectDir = new File('./baselineprofile/')\n"
147
- )
148
- );
149
- }
150
-
151
- function ensureAppGradle() {
152
- const appBuildGradle = path.join(androidDir, 'app', 'build.gradle');
153
- patchFile(appBuildGradle, (content) => {
154
- let next = insertAfter(
155
- content,
156
- "apply plugin: 'com.android.application'",
157
- "\napply plugin: 'androidx.baselineprofile'"
158
- );
159
-
160
- if (!next.includes('androidx.profileinstaller:profileinstaller')) {
161
- next = insertBeforeClosingBrace(
162
- next,
163
- 'dependencies {',
164
- ' implementation "androidx.profileinstaller:profileinstaller:1.4.1"'
165
- );
166
- }
167
-
168
- if (!next.includes("baselineProfile project(':baselineprofile')")) {
169
- next = insertBeforeClosingBrace(
170
- next,
171
- 'dependencies {',
172
- " baselineProfile project(':baselineprofile')"
173
- );
174
- }
175
-
176
- if (!next.includes('automaticGenerationDuringBuild false')) {
177
- next = insertAfter(
178
- next,
179
- "apply from: 'capacitor.build.gradle'",
180
- '\n\nbaselineProfile {\n automaticGenerationDuringBuild false\n saveInSrc true\n mergeIntoMain true\n}\n'
181
- );
182
- }
183
-
184
- return next;
185
- });
186
- }
187
-
188
- function ensureBaselineProfileModule(appId) {
189
- const safeAppId = toSafeJavaPackageName(appId);
190
- const modulePackage = `${safeAppId}.baselineprofile`;
191
-
192
- writeFile(path.join(androidDir, 'baselineprofile', 'build.gradle'), getBaselineProfileBuildGradle(modulePackage));
193
- writeFile(
194
- path.join(androidDir, 'baselineprofile', 'src', 'main', 'AndroidManifest.xml'),
195
- baselineProfileManifest
196
- );
197
- writeFile(
198
- path.join(
199
- androidDir,
200
- 'baselineprofile',
201
- 'src',
202
- 'main',
203
- 'java',
204
- ...safeAppId.split('.'),
205
- 'baselineprofile',
206
- 'BaselineProfileGenerator.java'
207
- ),
208
- getBaselineProfileGenerator(modulePackage, appId)
209
- );
210
- console.log('Ensured android/baselineprofile module');
211
- }
212
-
213
- function syncGeneratedProfileBackup() {
214
- const generatedExists = fs.existsSync(generatedProfile);
215
- const backupExists = fs.existsSync(backupProfile);
216
-
217
- if (!generatedExists && !backupExists) {
218
- console.log('No baseline-prof.txt found yet. Run: cd android && .\\gradlew.bat :app:generateBaselineProfile --console=plain');
219
- return;
220
- }
221
-
222
- if (generatedExists && !backupExists) {
223
- copyProfileFile(generatedProfile, backupProfile, 'Backed up generated baseline profile to baselineProfiles/baseline-prof.txt');
224
- return;
225
- }
226
-
227
- if (!generatedExists && backupExists) {
228
- copyProfileFile(backupProfile, generatedProfile, 'Restored baseline profile into android/app/src/main/generated/baselineProfiles');
229
- return;
230
- }
231
-
232
- const generatedTime = fs.statSync(generatedProfile).mtimeMs;
233
- const backupTime = fs.statSync(backupProfile).mtimeMs;
234
- if (generatedTime >= backupTime) {
235
- copyProfileFile(generatedProfile, backupProfile, 'Updated baseline profile backup from generated profile');
236
- } else {
237
- copyProfileFile(backupProfile, generatedProfile, 'Restored generated baseline profile from backup');
238
- }
239
- }
240
-
241
- function main() {
242
- const appId = getAppIdFromCapacitorConfig();
243
-
244
- if (!fs.existsSync(androidDir)) {
245
- console.log('Android platform folder is missing. Run `npx cap add android` first, then rerun this script.');
246
- return;
247
- }
248
-
249
- ensureBaselineProfilePlugin();
250
- ensureSettingsModule();
251
- ensureAppGradle();
252
- ensureBaselineProfileModule(appId);
253
- syncGeneratedProfileBackup();
254
- console.log('Baseline Profile setup is ready.');
255
- }
256
-
257
- main();
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
120
+
121
+ const baselineProfileManifest = `<?xml version="1.0" encoding="utf-8"?>
122
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" />
123
+ `;
124
+
125
+ function getBaselineProfileGenerator(modulePackage, appId) {
126
+ return `package ${modulePackage};\n\nimport androidx.benchmark.macro.junit4.BaselineProfileRule;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport kotlin.Unit;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n@RunWith(AndroidJUnit4.class)\npublic class BaselineProfileGenerator {\n private static final String PACKAGE_NAME = \"${appId}\";\n\n @Rule\n public final BaselineProfileRule baselineProfileRule = new BaselineProfileRule();\n\n @Test\n public void generateStartupProfile() {\n baselineProfileRule.collect(\n PACKAGE_NAME,\n 15,\n 3,\n null,\n true,\n true,\n scope -> {\n scope.getDevice().pressHome();\n scope.startActivityAndWait();\n scope.getDevice().waitForIdle();\n waitForWebViewStartup();\n scope.killProcess();\n return Unit.INSTANCE;\n }\n );\n }\n\n private void waitForWebViewStartup() {\n try {\n Thread.sleep(3000);\n } catch (InterruptedException exception) {\n Thread.currentThread().interrupt();\n }\n }\n}\n`;
127
+ }
128
+
129
+ function ensureBaselineProfilePlugin() {
130
+ const rootBuildGradle = path.join(androidDir, 'build.gradle');
131
+ patchFile(rootBuildGradle, (content) =>
132
+ insertAfter(
133
+ content,
134
+ "classpath 'com.google.gms:google-services:4.4.4'",
135
+ "\n classpath 'androidx.baselineprofile:androidx.baselineprofile.gradle.plugin:1.4.1'"
136
+ )
137
+ );
138
+ }
139
+
140
+ function ensureSettingsModule() {
141
+ const settingsGradle = path.join(androidDir, 'settings.gradle');
142
+ patchFile(settingsGradle, (content) =>
143
+ insertAfter(
144
+ content,
145
+ "include ':app'",
146
+ "\ninclude ':baselineprofile'\nproject(':baselineprofile').projectDir = new File('./baselineprofile/')\n"
147
+ )
148
+ );
149
+ }
150
+
151
+ function ensureAppGradle() {
152
+ const appBuildGradle = path.join(androidDir, 'app', 'build.gradle');
153
+ patchFile(appBuildGradle, (content) => {
154
+ let next = insertAfter(
155
+ content,
156
+ "apply plugin: 'com.android.application'",
157
+ "\napply plugin: 'androidx.baselineprofile'"
158
+ );
159
+
160
+ if (!next.includes('androidx.profileinstaller:profileinstaller')) {
161
+ next = insertBeforeClosingBrace(
162
+ next,
163
+ 'dependencies {',
164
+ ' implementation "androidx.profileinstaller:profileinstaller:1.4.1"'
165
+ );
166
+ }
167
+
168
+ if (!next.includes("baselineProfile project(':baselineprofile')")) {
169
+ next = insertBeforeClosingBrace(
170
+ next,
171
+ 'dependencies {',
172
+ " baselineProfile project(':baselineprofile')"
173
+ );
174
+ }
175
+
176
+ if (!next.includes('automaticGenerationDuringBuild false')) {
177
+ next = insertAfter(
178
+ next,
179
+ "apply from: 'capacitor.build.gradle'",
180
+ '\n\nbaselineProfile {\n automaticGenerationDuringBuild false\n saveInSrc true\n mergeIntoMain true\n}\n'
181
+ );
182
+ }
183
+
184
+ return next;
185
+ });
186
+ }
187
+
188
+ function ensureBaselineProfileModule(appId) {
189
+ const safeAppId = toSafeJavaPackageName(appId);
190
+ const modulePackage = `${safeAppId}.baselineprofile`;
191
+
192
+ writeFile(path.join(androidDir, 'baselineprofile', 'build.gradle'), getBaselineProfileBuildGradle(modulePackage));
193
+ writeFile(
194
+ path.join(androidDir, 'baselineprofile', 'src', 'main', 'AndroidManifest.xml'),
195
+ baselineProfileManifest
196
+ );
197
+ writeFile(
198
+ path.join(
199
+ androidDir,
200
+ 'baselineprofile',
201
+ 'src',
202
+ 'main',
203
+ 'java',
204
+ ...safeAppId.split('.'),
205
+ 'baselineprofile',
206
+ 'BaselineProfileGenerator.java'
207
+ ),
208
+ getBaselineProfileGenerator(modulePackage, appId)
209
+ );
210
+ console.log('Ensured android/baselineprofile module');
211
+ }
212
+
213
+ function syncGeneratedProfileBackup() {
214
+ const generatedExists = fs.existsSync(generatedProfile);
215
+ const backupExists = fs.existsSync(backupProfile);
216
+
217
+ if (!generatedExists && !backupExists) {
218
+ console.log('No baseline-prof.txt found yet. Run: cd android && .\\gradlew.bat :app:generateBaselineProfile --console=plain');
219
+ return;
220
+ }
221
+
222
+ if (generatedExists && !backupExists) {
223
+ copyProfileFile(generatedProfile, backupProfile, 'Backed up generated baseline profile to baselineProfiles/baseline-prof.txt');
224
+ return;
225
+ }
226
+
227
+ if (!generatedExists && backupExists) {
228
+ copyProfileFile(backupProfile, generatedProfile, 'Restored baseline profile into android/app/src/main/generated/baselineProfiles');
229
+ return;
230
+ }
231
+
232
+ const generatedTime = fs.statSync(generatedProfile).mtimeMs;
233
+ const backupTime = fs.statSync(backupProfile).mtimeMs;
234
+ if (generatedTime >= backupTime) {
235
+ copyProfileFile(generatedProfile, backupProfile, 'Updated baseline profile backup from generated profile');
236
+ } else {
237
+ copyProfileFile(backupProfile, generatedProfile, 'Restored generated baseline profile from backup');
238
+ }
239
+ }
240
+
241
+ function main() {
242
+ const appId = getAppIdFromCapacitorConfig();
243
+
244
+ if (!fs.existsSync(androidDir)) {
245
+ console.log('Android platform folder is missing. Run `npx cap add android` first, then rerun this script.');
246
+ return;
247
+ }
248
+
249
+ ensureBaselineProfilePlugin();
250
+ ensureSettingsModule();
251
+ ensureAppGradle();
252
+ ensureBaselineProfileModule(appId);
253
+ syncGeneratedProfileBackup();
254
+ console.log('Baseline Profile setup is ready.');
255
+ }
256
+
257
+ main();
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
@@ -574,7 +574,10 @@ async function capturePageSnapshot(client) {
574
574
  const evaluation = await client.send('Runtime.evaluate', {
575
575
  expression: `(async () => {
576
576
  const clonedRoot = document.documentElement.cloneNode(true);
577
- const originalElements = [document.documentElement, ...document.querySelectorAll('*')];
577
+ const originalElements = [
578
+ document.documentElement,
579
+ ...document.documentElement.querySelectorAll('*'),
580
+ ];
578
581
  const clonedElements = [clonedRoot, ...clonedRoot.querySelectorAll('*')];
579
582
 
580
583
  const blobToDataUrl = (blob) => new Promise((resolve, reject) => {
@@ -619,11 +622,14 @@ async function capturePageSnapshot(client) {
619
622
  }
620
623
  } else if (original instanceof HTMLCanvasElement) {
621
624
  try {
622
- const replacement = document.createElement('img');
623
- replacement.src = original.toDataURL('image/png');
624
- replacement.alt = original.getAttribute('aria-label') || '';
625
- replacement.setAttribute('style', original.getAttribute('style') || '');
626
- clone.replaceWith(replacement);
625
+ clone.style.setProperty(
626
+ 'background-image',
627
+ 'url("' + original.toDataURL('image/png') + '")',
628
+ 'important',
629
+ );
630
+ clone.style.setProperty('background-position', 'center', 'important');
631
+ clone.style.setProperty('background-repeat', 'no-repeat', 'important');
632
+ clone.style.setProperty('background-size', '100% 100%', 'important');
627
633
  } catch {
628
634
  // Keep the canvas element when its pixels cannot be exported.
629
635
  }