@checkly/playwright-core 1.41.2-beta.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.
Files changed (305) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +3 -0
  4. package/ThirdPartyNotices.txt +1513 -0
  5. package/bin/PrintDeps.exe +0 -0
  6. package/bin/README.md +2 -0
  7. package/bin/install_media_pack.ps1 +5 -0
  8. package/bin/reinstall_chrome_beta_linux.sh +40 -0
  9. package/bin/reinstall_chrome_beta_mac.sh +13 -0
  10. package/bin/reinstall_chrome_beta_win.ps1 +23 -0
  11. package/bin/reinstall_chrome_stable_linux.sh +40 -0
  12. package/bin/reinstall_chrome_stable_mac.sh +12 -0
  13. package/bin/reinstall_chrome_stable_win.ps1 +23 -0
  14. package/bin/reinstall_msedge_beta_linux.sh +40 -0
  15. package/bin/reinstall_msedge_beta_mac.sh +11 -0
  16. package/bin/reinstall_msedge_beta_win.ps1 +22 -0
  17. package/bin/reinstall_msedge_dev_linux.sh +40 -0
  18. package/bin/reinstall_msedge_dev_mac.sh +11 -0
  19. package/bin/reinstall_msedge_dev_win.ps1 +22 -0
  20. package/bin/reinstall_msedge_stable_linux.sh +40 -0
  21. package/bin/reinstall_msedge_stable_mac.sh +11 -0
  22. package/bin/reinstall_msedge_stable_win.ps1 +23 -0
  23. package/browsers.json +64 -0
  24. package/cli.js +17 -0
  25. package/index.d.ts +17 -0
  26. package/index.js +33 -0
  27. package/index.mjs +28 -0
  28. package/lib/androidServerImpl.js +69 -0
  29. package/lib/browserServerImpl.js +92 -0
  30. package/lib/cli/driver.js +97 -0
  31. package/lib/cli/program.js +582 -0
  32. package/lib/cli/programWithTestStub.js +67 -0
  33. package/lib/client/accessibility.js +50 -0
  34. package/lib/client/android.js +473 -0
  35. package/lib/client/api.js +272 -0
  36. package/lib/client/artifact.js +79 -0
  37. package/lib/client/browser.js +145 -0
  38. package/lib/client/browserContext.js +509 -0
  39. package/lib/client/browserType.js +233 -0
  40. package/lib/client/cdpSession.js +53 -0
  41. package/lib/client/channelOwner.js +229 -0
  42. package/lib/client/clientHelper.js +57 -0
  43. package/lib/client/clientInstrumentation.js +40 -0
  44. package/lib/client/connection.js +327 -0
  45. package/lib/client/consoleMessage.js +55 -0
  46. package/lib/client/coverage.js +41 -0
  47. package/lib/client/dialog.js +57 -0
  48. package/lib/client/download.js +62 -0
  49. package/lib/client/electron.js +130 -0
  50. package/lib/client/elementHandle.js +291 -0
  51. package/lib/client/errors.js +77 -0
  52. package/lib/client/events.js +93 -0
  53. package/lib/client/fetch.js +343 -0
  54. package/lib/client/fileChooser.js +45 -0
  55. package/lib/client/frame.js +506 -0
  56. package/lib/client/harRouter.js +93 -0
  57. package/lib/client/input.js +111 -0
  58. package/lib/client/jsHandle.js +123 -0
  59. package/lib/client/jsonPipe.js +35 -0
  60. package/lib/client/localUtils.js +35 -0
  61. package/lib/client/locator.js +432 -0
  62. package/lib/client/network.js +601 -0
  63. package/lib/client/page.js +707 -0
  64. package/lib/client/playwright.js +74 -0
  65. package/lib/client/selectors.js +67 -0
  66. package/lib/client/stream.js +54 -0
  67. package/lib/client/tracing.js +135 -0
  68. package/lib/client/types.js +24 -0
  69. package/lib/client/video.js +51 -0
  70. package/lib/client/waiter.js +158 -0
  71. package/lib/client/webError.js +37 -0
  72. package/lib/client/worker.js +71 -0
  73. package/lib/client/writableStream.js +54 -0
  74. package/lib/common/socksProxy.js +569 -0
  75. package/lib/common/timeoutSettings.js +73 -0
  76. package/lib/common/types.js +5 -0
  77. package/lib/generated/consoleApiSource.js +7 -0
  78. package/lib/generated/injectedScriptSource.js +7 -0
  79. package/lib/generated/recorderSource.js +7 -0
  80. package/lib/generated/utilityScriptSource.js +7 -0
  81. package/lib/image_tools/colorUtils.js +98 -0
  82. package/lib/image_tools/compare.js +108 -0
  83. package/lib/image_tools/imageChannel.js +70 -0
  84. package/lib/image_tools/stats.js +102 -0
  85. package/lib/inProcessFactory.js +54 -0
  86. package/lib/inprocess.js +20 -0
  87. package/lib/outofprocess.js +67 -0
  88. package/lib/protocol/debug.js +27 -0
  89. package/lib/protocol/serializers.js +172 -0
  90. package/lib/protocol/transport.js +82 -0
  91. package/lib/protocol/validator.js +2599 -0
  92. package/lib/protocol/validatorPrimitives.js +139 -0
  93. package/lib/remote/playwrightConnection.js +274 -0
  94. package/lib/remote/playwrightServer.js +110 -0
  95. package/lib/server/accessibility.js +62 -0
  96. package/lib/server/android/android.js +441 -0
  97. package/lib/server/android/backendAdb.js +172 -0
  98. package/lib/server/artifact.js +104 -0
  99. package/lib/server/browser.js +129 -0
  100. package/lib/server/browserContext.js +609 -0
  101. package/lib/server/browserType.js +300 -0
  102. package/lib/server/chromium/appIcon.png +0 -0
  103. package/lib/server/chromium/chromium.js +346 -0
  104. package/lib/server/chromium/chromiumSwitches.js +41 -0
  105. package/lib/server/chromium/crAccessibility.js +237 -0
  106. package/lib/server/chromium/crBrowser.js +521 -0
  107. package/lib/server/chromium/crConnection.js +228 -0
  108. package/lib/server/chromium/crCoverage.js +246 -0
  109. package/lib/server/chromium/crDevTools.js +104 -0
  110. package/lib/server/chromium/crDragDrop.js +144 -0
  111. package/lib/server/chromium/crExecutionContext.js +156 -0
  112. package/lib/server/chromium/crInput.js +171 -0
  113. package/lib/server/chromium/crNetworkManager.js +723 -0
  114. package/lib/server/chromium/crPage.js +1173 -0
  115. package/lib/server/chromium/crPdf.js +147 -0
  116. package/lib/server/chromium/crProtocolHelper.js +131 -0
  117. package/lib/server/chromium/crServiceWorker.js +115 -0
  118. package/lib/server/chromium/defaultFontFamilies.js +145 -0
  119. package/lib/server/chromium/videoRecorder.js +155 -0
  120. package/lib/server/console.js +59 -0
  121. package/lib/server/cookieStore.js +112 -0
  122. package/lib/server/debugController.js +236 -0
  123. package/lib/server/debugger.js +132 -0
  124. package/lib/server/deviceDescriptors.js +21 -0
  125. package/lib/server/deviceDescriptorsSource.json +1549 -0
  126. package/lib/server/dialog.js +70 -0
  127. package/lib/server/dispatchers/androidDispatcher.js +193 -0
  128. package/lib/server/dispatchers/artifactDispatcher.js +118 -0
  129. package/lib/server/dispatchers/browserContextDispatcher.js +306 -0
  130. package/lib/server/dispatchers/browserDispatcher.js +170 -0
  131. package/lib/server/dispatchers/browserTypeDispatcher.js +55 -0
  132. package/lib/server/dispatchers/cdpSessionDispatcher.js +48 -0
  133. package/lib/server/dispatchers/debugControllerDispatcher.js +103 -0
  134. package/lib/server/dispatchers/dialogDispatcher.js +44 -0
  135. package/lib/server/dispatchers/dispatcher.js +400 -0
  136. package/lib/server/dispatchers/electronDispatcher.js +80 -0
  137. package/lib/server/dispatchers/elementHandlerDispatcher.js +228 -0
  138. package/lib/server/dispatchers/frameDispatcher.js +287 -0
  139. package/lib/server/dispatchers/jsHandleDispatcher.js +102 -0
  140. package/lib/server/dispatchers/jsonPipeDispatcher.js +61 -0
  141. package/lib/server/dispatchers/localUtilsDispatcher.js +399 -0
  142. package/lib/server/dispatchers/networkDispatchers.js +221 -0
  143. package/lib/server/dispatchers/pageDispatcher.js +363 -0
  144. package/lib/server/dispatchers/playwrightDispatcher.js +105 -0
  145. package/lib/server/dispatchers/selectorsDispatcher.js +36 -0
  146. package/lib/server/dispatchers/streamDispatcher.js +62 -0
  147. package/lib/server/dispatchers/tracingDispatcher.js +54 -0
  148. package/lib/server/dispatchers/writableStreamDispatcher.js +55 -0
  149. package/lib/server/dom.js +808 -0
  150. package/lib/server/download.js +53 -0
  151. package/lib/server/electron/electron.js +254 -0
  152. package/lib/server/electron/loader.js +57 -0
  153. package/lib/server/errors.js +68 -0
  154. package/lib/server/fetch.js +611 -0
  155. package/lib/server/fileChooser.js +42 -0
  156. package/lib/server/fileUploadUtils.js +71 -0
  157. package/lib/server/firefox/ffAccessibility.js +215 -0
  158. package/lib/server/firefox/ffBrowser.js +447 -0
  159. package/lib/server/firefox/ffConnection.js +168 -0
  160. package/lib/server/firefox/ffExecutionContext.js +138 -0
  161. package/lib/server/firefox/ffInput.js +150 -0
  162. package/lib/server/firefox/ffNetworkManager.js +231 -0
  163. package/lib/server/firefox/ffPage.js +558 -0
  164. package/lib/server/firefox/firefox.js +91 -0
  165. package/lib/server/formData.js +75 -0
  166. package/lib/server/frameSelectors.js +171 -0
  167. package/lib/server/frames.js +1597 -0
  168. package/lib/server/har/harRecorder.js +139 -0
  169. package/lib/server/har/harTracer.js +539 -0
  170. package/lib/server/helper.js +103 -0
  171. package/lib/server/index.js +96 -0
  172. package/lib/server/input.js +301 -0
  173. package/lib/server/instrumentation.js +74 -0
  174. package/lib/server/isomorphic/utilityScriptSerializers.js +212 -0
  175. package/lib/server/javascript.js +305 -0
  176. package/lib/server/launchApp.js +90 -0
  177. package/lib/server/macEditingCommands.js +139 -0
  178. package/lib/server/network.js +607 -0
  179. package/lib/server/page.js +793 -0
  180. package/lib/server/pipeTransport.js +85 -0
  181. package/lib/server/playwright.js +82 -0
  182. package/lib/server/progress.js +102 -0
  183. package/lib/server/protocolError.js +49 -0
  184. package/lib/server/recorder/codeGenerator.js +153 -0
  185. package/lib/server/recorder/csharp.js +310 -0
  186. package/lib/server/recorder/java.js +216 -0
  187. package/lib/server/recorder/javascript.js +229 -0
  188. package/lib/server/recorder/jsonl.js +47 -0
  189. package/lib/server/recorder/language.js +44 -0
  190. package/lib/server/recorder/python.js +275 -0
  191. package/lib/server/recorder/recorderActions.js +5 -0
  192. package/lib/server/recorder/recorderApp.js +181 -0
  193. package/lib/server/recorder/recorderUtils.js +48 -0
  194. package/lib/server/recorder/utils.js +45 -0
  195. package/lib/server/recorder.js +700 -0
  196. package/lib/server/registry/browserFetcher.js +168 -0
  197. package/lib/server/registry/dependencies.js +322 -0
  198. package/lib/server/registry/index.js +925 -0
  199. package/lib/server/registry/nativeDeps.js +383 -0
  200. package/lib/server/registry/oopDownloadBrowserMain.js +138 -0
  201. package/lib/server/screenshotter.js +354 -0
  202. package/lib/server/selectors.js +73 -0
  203. package/lib/server/socksInterceptor.js +100 -0
  204. package/lib/server/trace/recorder/snapshotter.js +168 -0
  205. package/lib/server/trace/recorder/snapshotterInjected.js +493 -0
  206. package/lib/server/trace/recorder/tracing.js +552 -0
  207. package/lib/server/trace/test/inMemorySnapshotter.js +93 -0
  208. package/lib/server/trace/viewer/traceViewer.js +229 -0
  209. package/lib/server/transport.js +191 -0
  210. package/lib/server/types.js +24 -0
  211. package/lib/server/usKeyboardLayout.js +555 -0
  212. package/lib/server/webkit/webkit.js +87 -0
  213. package/lib/server/webkit/wkAccessibility.js +194 -0
  214. package/lib/server/webkit/wkBrowser.js +328 -0
  215. package/lib/server/webkit/wkConnection.js +173 -0
  216. package/lib/server/webkit/wkExecutionContext.js +146 -0
  217. package/lib/server/webkit/wkInput.js +169 -0
  218. package/lib/server/webkit/wkInterceptableRequest.js +158 -0
  219. package/lib/server/webkit/wkPage.js +1198 -0
  220. package/lib/server/webkit/wkProvisionalPage.js +59 -0
  221. package/lib/server/webkit/wkWorkers.js +104 -0
  222. package/lib/third_party/diff_match_patch.js +2222 -0
  223. package/lib/third_party/pixelmatch.js +255 -0
  224. package/lib/utils/ascii.js +31 -0
  225. package/lib/utils/comparators.js +171 -0
  226. package/lib/utils/crypto.js +33 -0
  227. package/lib/utils/debug.js +46 -0
  228. package/lib/utils/debugLogger.js +89 -0
  229. package/lib/utils/env.js +47 -0
  230. package/lib/utils/eventsHelper.js +38 -0
  231. package/lib/utils/fileUtils.js +66 -0
  232. package/lib/utils/glob.js +83 -0
  233. package/lib/utils/happy-eyeballs.js +154 -0
  234. package/lib/utils/headers.js +52 -0
  235. package/lib/utils/hostPlatform.js +124 -0
  236. package/lib/utils/httpServer.js +195 -0
  237. package/lib/utils/index.js +324 -0
  238. package/lib/utils/isomorphic/cssParser.js +250 -0
  239. package/lib/utils/isomorphic/cssTokenizer.js +979 -0
  240. package/lib/utils/isomorphic/locatorGenerators.js +651 -0
  241. package/lib/utils/isomorphic/locatorParser.js +179 -0
  242. package/lib/utils/isomorphic/locatorUtils.js +62 -0
  243. package/lib/utils/isomorphic/selectorParser.js +397 -0
  244. package/lib/utils/isomorphic/stringUtils.js +107 -0
  245. package/lib/utils/isomorphic/traceUtils.js +39 -0
  246. package/lib/utils/linuxUtils.js +78 -0
  247. package/lib/utils/manualPromise.js +109 -0
  248. package/lib/utils/mimeType.js +29 -0
  249. package/lib/utils/multimap.js +75 -0
  250. package/lib/utils/network.js +189 -0
  251. package/lib/utils/processLauncher.js +248 -0
  252. package/lib/utils/profiler.js +53 -0
  253. package/lib/utils/rtti.js +41 -0
  254. package/lib/utils/semaphore.js +51 -0
  255. package/lib/utils/spawnAsync.js +45 -0
  256. package/lib/utils/stackTrace.js +123 -0
  257. package/lib/utils/task.js +58 -0
  258. package/lib/utils/time.js +37 -0
  259. package/lib/utils/timeoutRunner.js +131 -0
  260. package/lib/utils/traceUtils.js +44 -0
  261. package/lib/utils/userAgent.js +105 -0
  262. package/lib/utils/wsServer.js +125 -0
  263. package/lib/utils/zipFile.js +75 -0
  264. package/lib/utils/zones.js +99 -0
  265. package/lib/utilsBundle.js +81 -0
  266. package/lib/utilsBundleImpl/index.js +51 -0
  267. package/lib/utilsBundleImpl/xdg-open +1066 -0
  268. package/lib/vite/htmlReport/index.html +66 -0
  269. package/lib/vite/recorder/assets/codeMirrorModule-Hs9-1ZG4.css +1 -0
  270. package/lib/vite/recorder/assets/codeMirrorModule-I9ks4y7D.js +24 -0
  271. package/lib/vite/recorder/assets/codicon-zGuYmc9o.ttf +0 -0
  272. package/lib/vite/recorder/assets/index-ljsTwXtJ.css +1 -0
  273. package/lib/vite/recorder/assets/index-yg8ypzl6.js +47 -0
  274. package/lib/vite/recorder/index.html +29 -0
  275. package/lib/vite/recorder/playwright-logo.svg +9 -0
  276. package/lib/vite/traceViewer/assets/codeMirrorModule-GluP1cQ1.js +24 -0
  277. package/lib/vite/traceViewer/assets/codeMirrorModule-fqJB1XDu.js +24 -0
  278. package/lib/vite/traceViewer/assets/codeMirrorModule-y3M3aAqy.js +24 -0
  279. package/lib/vite/traceViewer/assets/wsPort-Rvwd4WC-.js +69 -0
  280. package/lib/vite/traceViewer/assets/wsPort-dlD7vDkY.js +69 -0
  281. package/lib/vite/traceViewer/assets/wsPort-qOE2NWrO.js +69 -0
  282. package/lib/vite/traceViewer/assets/xtermModule-Yt6xwiJ_.js +9 -0
  283. package/lib/vite/traceViewer/codeMirrorModule.Hs9-1ZG4.css +1 -0
  284. package/lib/vite/traceViewer/codicon.zGuYmc9o.ttf +0 -0
  285. package/lib/vite/traceViewer/index.-g_5lMbJ.css +1 -0
  286. package/lib/vite/traceViewer/index.4X7zDysg.js +2 -0
  287. package/lib/vite/traceViewer/index.HkJgzlGy.js +2 -0
  288. package/lib/vite/traceViewer/index.html +26 -0
  289. package/lib/vite/traceViewer/index.kRjx5sAJ.js +2 -0
  290. package/lib/vite/traceViewer/playwright-logo.svg +9 -0
  291. package/lib/vite/traceViewer/snapshot.html +21 -0
  292. package/lib/vite/traceViewer/sw.bundle.js +4 -0
  293. package/lib/vite/traceViewer/uiMode.1Wcp_Kto.js +10 -0
  294. package/lib/vite/traceViewer/uiMode.GTNzARcV.js +10 -0
  295. package/lib/vite/traceViewer/uiMode.html +17 -0
  296. package/lib/vite/traceViewer/uiMode.pWy0Re7G.css +1 -0
  297. package/lib/vite/traceViewer/uiMode.zV-7Lf9v.js +10 -0
  298. package/lib/vite/traceViewer/wsPort.kSgQKQ0y.css +1 -0
  299. package/lib/vite/traceViewer/xtermModule.0lwXJFHT.css +32 -0
  300. package/lib/zipBundle.js +25 -0
  301. package/lib/zipBundleImpl.js +5 -0
  302. package/package.json +43 -0
  303. package/types/protocol.d.ts +20304 -0
  304. package/types/structs.d.ts +45 -0
  305. package/types/types.d.ts +20626 -0
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Snapshotter = void 0;
7
+ var _browserContext = require("../../browserContext");
8
+ var _page = require("../../page");
9
+ var _eventsHelper = require("../../../utils/eventsHelper");
10
+ var _debugLogger = require("../../../utils/debugLogger");
11
+ var _snapshotterInjected = require("./snapshotterInjected");
12
+ var _utils = require("../../../utils");
13
+ var _utilsBundle = require("../../../utilsBundle");
14
+ /**
15
+ * Copyright (c) Microsoft Corporation.
16
+ *
17
+ * Licensed under the Apache License, Version 2.0 (the "License");
18
+ * you may not use this file except in compliance with the License.
19
+ * You may obtain a copy of the License at
20
+ *
21
+ * http://www.apache.org/licenses/LICENSE-2.0
22
+ *
23
+ * Unless required by applicable law or agreed to in writing, software
24
+ * distributed under the License is distributed on an "AS IS" BASIS,
25
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26
+ * See the License for the specific language governing permissions and
27
+ * limitations under the License.
28
+ */
29
+
30
+ class Snapshotter {
31
+ constructor(context, delegate) {
32
+ this._context = void 0;
33
+ this._delegate = void 0;
34
+ this._eventListeners = [];
35
+ this._snapshotStreamer = void 0;
36
+ this._initialized = false;
37
+ this._started = false;
38
+ this._context = context;
39
+ this._delegate = delegate;
40
+ const guid = (0, _utils.createGuid)();
41
+ this._snapshotStreamer = '__playwright_snapshot_streamer_' + guid;
42
+ }
43
+ started() {
44
+ return this._started;
45
+ }
46
+ async start() {
47
+ this._started = true;
48
+ if (!this._initialized) {
49
+ this._initialized = true;
50
+ await this._initialize();
51
+ }
52
+ await this.reset();
53
+ }
54
+ async reset() {
55
+ if (this._started) await this._runInAllFrames(`window["${this._snapshotStreamer}"].reset()`);
56
+ }
57
+ async stop() {
58
+ this._started = false;
59
+ }
60
+ resetForReuse() {
61
+ // Next time we start recording, we will call addInitScript again.
62
+ this._initialized = false;
63
+ }
64
+ async _initialize() {
65
+ for (const page of this._context.pages()) this._onPage(page);
66
+ this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._context, _browserContext.BrowserContext.Events.Page, this._onPage.bind(this))];
67
+ const initScript = `(${_snapshotterInjected.frameSnapshotStreamer})("${this._snapshotStreamer}", ${!!this._context._options.javaScriptEnabled})`;
68
+ await this._context.addInitScript(initScript);
69
+ await this._runInAllFrames(initScript);
70
+ }
71
+ async _runInAllFrames(expression) {
72
+ const frames = [];
73
+ for (const page of this._context.pages()) frames.push(...page.frames());
74
+ await Promise.all(frames.map(frame => {
75
+ return frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => _debugLogger.debugLogger.log('error', e));
76
+ }));
77
+ }
78
+ dispose() {
79
+ _eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
80
+ }
81
+ async captureSnapshot(page, callId, snapshotName, element) {
82
+ // Prepare expression synchronously.
83
+ const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(snapshotName)})`;
84
+
85
+ // In a best-effort manner, without waiting for it, mark target element.
86
+ element === null || element === void 0 || element.callFunctionNoReply((element, callId) => {
87
+ const customEvent = new CustomEvent('__playwright_target__', {
88
+ bubbles: true,
89
+ cancelable: true,
90
+ detail: callId,
91
+ composed: true
92
+ });
93
+ element.dispatchEvent(customEvent);
94
+ }, callId);
95
+
96
+ // In each frame, in a non-stalling manner, capture the snapshots.
97
+ const snapshots = page.frames().map(async frame => {
98
+ const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => _debugLogger.debugLogger.log('error', e));
99
+ // Something went wrong -> bail out, our snapshots are best-efforty.
100
+ if (!data || !this._started) return;
101
+ const snapshot = {
102
+ callId,
103
+ snapshotName,
104
+ pageId: page.guid,
105
+ frameId: frame.guid,
106
+ frameUrl: data.url,
107
+ doctype: data.doctype,
108
+ html: data.html,
109
+ viewport: data.viewport,
110
+ timestamp: (0, _utils.monotonicTime)(),
111
+ collectionTime: data.collectionTime,
112
+ resourceOverrides: [],
113
+ isMainFrame: page.mainFrame() === frame
114
+ };
115
+ for (const {
116
+ url,
117
+ content,
118
+ contentType
119
+ } of data.resourceOverrides) {
120
+ if (typeof content === 'string') {
121
+ const buffer = Buffer.from(content);
122
+ const sha1 = (0, _utils.calculateSha1)(buffer) + '.' + (_utilsBundle.mime.getExtension(contentType) || 'dat');
123
+ this._delegate.onSnapshotterBlob({
124
+ sha1,
125
+ buffer
126
+ });
127
+ snapshot.resourceOverrides.push({
128
+ url,
129
+ sha1
130
+ });
131
+ } else {
132
+ snapshot.resourceOverrides.push({
133
+ url,
134
+ ref: content
135
+ });
136
+ }
137
+ }
138
+ this._delegate.onFrameSnapshot(snapshot);
139
+ });
140
+ await Promise.all(snapshots);
141
+ }
142
+ _onPage(page) {
143
+ // Annotate frame hierarchy so that snapshots could include frame ids.
144
+ for (const frame of page.frames()) this._annotateFrameHierarchy(frame);
145
+ this._eventListeners.push(_eventsHelper.eventsHelper.addEventListener(page, _page.Page.Events.FrameAttached, frame => this._annotateFrameHierarchy(frame)));
146
+ }
147
+ async _annotateFrameHierarchy(frame) {
148
+ try {
149
+ const frameElement = await frame.frameElement();
150
+ const parent = frame.parentFrame();
151
+ if (!parent) return;
152
+ const context = await parent._mainContext();
153
+ await (context === null || context === void 0 ? void 0 : context.evaluate(({
154
+ snapshotStreamer,
155
+ frameElement,
156
+ frameId
157
+ }) => {
158
+ window[snapshotStreamer].markIframe(frameElement, frameId);
159
+ }, {
160
+ snapshotStreamer: this._snapshotStreamer,
161
+ frameElement,
162
+ frameId: frame.guid
163
+ }));
164
+ frameElement.dispose();
165
+ } catch (e) {}
166
+ }
167
+ }
168
+ exports.Snapshotter = Snapshotter;
@@ -0,0 +1,493 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.frameSnapshotStreamer = frameSnapshotStreamer;
7
+ /**
8
+ * Copyright (c) Microsoft Corporation.
9
+ *
10
+ * Licensed under the Apache License, Version 2.0 (the "License");
11
+ * you may not use this file except in compliance with the License.
12
+ * You may obtain a copy of the License at
13
+ *
14
+ * http://www.apache.org/licenses/LICENSE-2.0
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+
23
+ function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
24
+ // Communication with Playwright.
25
+ if (window[snapshotStreamer]) return;
26
+
27
+ // Attributes present in the snapshot.
28
+ const kShadowAttribute = '__playwright_shadow_root_';
29
+ const kValueAttribute = '__playwright_value_';
30
+ const kCheckedAttribute = '__playwright_checked_';
31
+ const kSelectedAttribute = '__playwright_selected_';
32
+ const kScrollTopAttribute = '__playwright_scroll_top_';
33
+ const kScrollLeftAttribute = '__playwright_scroll_left_';
34
+ const kStyleSheetAttribute = '__playwright_style_sheet_';
35
+ const kTargetAttribute = '__playwright_target__';
36
+ const kCustomElementsAttribute = '__playwright_custom_elements__';
37
+ const kCurrentSrcAttribute = '__playwright_current_src__';
38
+
39
+ // Symbols for our own info on Nodes/StyleSheets.
40
+ const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
41
+ const kCachedData = Symbol('__playwright_snapshot_cache_');
42
+ const kEndOfList = Symbol('__playwright_end_of_list_');
43
+ function resetCachedData(obj) {
44
+ delete obj[kCachedData];
45
+ }
46
+ function ensureCachedData(obj) {
47
+ if (!obj[kCachedData]) obj[kCachedData] = {};
48
+ return obj[kCachedData];
49
+ }
50
+ function removeHash(url) {
51
+ try {
52
+ const u = new URL(url);
53
+ u.hash = '';
54
+ return u.toString();
55
+ } catch (e) {
56
+ return url;
57
+ }
58
+ }
59
+ class Streamer {
60
+ constructor() {
61
+ this._lastSnapshotNumber = 0;
62
+ this._staleStyleSheets = new Set();
63
+ this._readingStyleSheet = false;
64
+ // To avoid invalidating due to our own reads.
65
+ this._fakeBase = void 0;
66
+ this._observer = void 0;
67
+ const invalidateCSSGroupingRule = rule => {
68
+ if (rule.parentStyleSheet) this._invalidateStyleSheet(rule.parentStyleSheet);
69
+ };
70
+ this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'insertRule', sheet => this._invalidateStyleSheet(sheet));
71
+ this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'deleteRule', sheet => this._invalidateStyleSheet(sheet));
72
+ this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'addRule', sheet => this._invalidateStyleSheet(sheet));
73
+ this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'removeRule', sheet => this._invalidateStyleSheet(sheet));
74
+ this._interceptNativeGetter(window.CSSStyleSheet.prototype, 'rules', sheet => this._invalidateStyleSheet(sheet));
75
+ this._interceptNativeGetter(window.CSSStyleSheet.prototype, 'cssRules', sheet => this._invalidateStyleSheet(sheet));
76
+ this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'replaceSync', sheet => this._invalidateStyleSheet(sheet));
77
+ this._interceptNativeMethod(window.CSSGroupingRule.prototype, 'insertRule', invalidateCSSGroupingRule);
78
+ this._interceptNativeMethod(window.CSSGroupingRule.prototype, 'deleteRule', invalidateCSSGroupingRule);
79
+ this._interceptNativeGetter(window.CSSGroupingRule.prototype, 'cssRules', invalidateCSSGroupingRule);
80
+ this._interceptNativeAsyncMethod(window.CSSStyleSheet.prototype, 'replace', sheet => this._invalidateStyleSheet(sheet));
81
+ this._fakeBase = document.createElement('base');
82
+ this._observer = new MutationObserver(list => this._handleMutations(list));
83
+ const observerConfig = {
84
+ attributes: true,
85
+ subtree: true
86
+ };
87
+ this._observer.observe(document, observerConfig);
88
+ this._refreshListenersWhenNeeded();
89
+ }
90
+ _refreshListenersWhenNeeded() {
91
+ this._refreshListeners();
92
+ const customEventName = '__playwright_snapshotter_global_listeners_check__';
93
+ let seenEvent = false;
94
+ const handleCustomEvent = () => seenEvent = true;
95
+ window.addEventListener(customEventName, handleCustomEvent);
96
+ const observer = new MutationObserver(entries => {
97
+ // Check for new documentElement in case we need to reinstall document listeners.
98
+ const newDocumentElement = entries.some(entry => Array.from(entry.addedNodes).includes(document.documentElement));
99
+ if (newDocumentElement) {
100
+ // New documentElement - let's check whether listeners are still here.
101
+ seenEvent = false;
102
+ window.dispatchEvent(new CustomEvent(customEventName));
103
+ if (!seenEvent) {
104
+ // Listener did not fire. Reattach the listener and notify.
105
+ window.addEventListener(customEventName, handleCustomEvent);
106
+ this._refreshListeners();
107
+ }
108
+ }
109
+ });
110
+ observer.observe(document, {
111
+ childList: true
112
+ });
113
+ }
114
+ _refreshListeners() {
115
+ document.addEventListener('__playwright_target__', event => {
116
+ if (!event.detail) return;
117
+ const callId = event.detail;
118
+ event.composedPath()[0].__playwright_target__ = callId;
119
+ });
120
+ }
121
+ _interceptNativeMethod(obj, method, cb) {
122
+ const native = obj[method];
123
+ if (!native) return;
124
+ obj[method] = function (...args) {
125
+ const result = native.call(this, ...args);
126
+ cb(this, result);
127
+ return result;
128
+ };
129
+ }
130
+ _interceptNativeAsyncMethod(obj, method, cb) {
131
+ const native = obj[method];
132
+ if (!native) return;
133
+ obj[method] = async function (...args) {
134
+ const result = await native.call(this, ...args);
135
+ cb(this, result);
136
+ return result;
137
+ };
138
+ }
139
+ _interceptNativeGetter(obj, prop, cb) {
140
+ const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
141
+ Object.defineProperty(obj, prop, {
142
+ ...descriptor,
143
+ get: function () {
144
+ const result = descriptor.get.call(this);
145
+ cb(this, result);
146
+ return result;
147
+ }
148
+ });
149
+ }
150
+ _handleMutations(list) {
151
+ for (const mutation of list) ensureCachedData(mutation.target).attributesCached = undefined;
152
+ }
153
+ _invalidateStyleSheet(sheet) {
154
+ if (this._readingStyleSheet) return;
155
+ this._staleStyleSheets.add(sheet);
156
+ }
157
+ _updateStyleElementStyleSheetTextIfNeeded(sheet, forceText) {
158
+ const data = ensureCachedData(sheet);
159
+ if (this._staleStyleSheets.has(sheet) || forceText && data.cssText === undefined) {
160
+ this._staleStyleSheets.delete(sheet);
161
+ try {
162
+ data.cssText = this._getSheetText(sheet);
163
+ } catch (e) {
164
+ // Sometimes we cannot access cross-origin stylesheets.
165
+ data.cssText = '';
166
+ }
167
+ }
168
+ return data.cssText;
169
+ }
170
+
171
+ // Returns either content, ref, or no override.
172
+ _updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber) {
173
+ const data = ensureCachedData(sheet);
174
+ if (this._staleStyleSheets.has(sheet)) {
175
+ this._staleStyleSheets.delete(sheet);
176
+ try {
177
+ data.cssText = this._getSheetText(sheet);
178
+ data.cssRef = snapshotNumber;
179
+ return data.cssText;
180
+ } catch (e) {
181
+ // Sometimes we cannot access cross-origin stylesheets.
182
+ }
183
+ }
184
+ return data.cssRef === undefined ? undefined : snapshotNumber - data.cssRef;
185
+ }
186
+ markIframe(iframeElement, frameId) {
187
+ iframeElement[kSnapshotFrameId] = frameId;
188
+ }
189
+ reset() {
190
+ this._staleStyleSheets.clear();
191
+ const visitNode = node => {
192
+ resetCachedData(node);
193
+ if (node.nodeType === Node.ELEMENT_NODE) {
194
+ const element = node;
195
+ if (element.shadowRoot) visitNode(element.shadowRoot);
196
+ }
197
+ for (let child = node.firstChild; child; child = child.nextSibling) visitNode(child);
198
+ };
199
+ visitNode(document.documentElement);
200
+ visitNode(this._fakeBase);
201
+ }
202
+ __sanitizeMetaAttribute(name, value, httpEquiv) {
203
+ if (name === 'charset') return 'utf-8';
204
+ if (httpEquiv.toLowerCase() !== 'content-type' || name !== 'content') return value;
205
+ const [type, ...params] = value.split(';');
206
+ if (type !== 'text/html' || params.length <= 0) return value;
207
+ const charsetParamIdx = params.findIndex(param => param.trim().startsWith('charset='));
208
+ if (charsetParamIdx > -1) params[charsetParamIdx] = 'charset=utf-8';
209
+ return `${type}; ${params.join('; ')}`;
210
+ }
211
+ _sanitizeUrl(url) {
212
+ if (url.startsWith('javascript:') || url.startsWith('vbscript:')) return '';
213
+ return url;
214
+ }
215
+ _sanitizeSrcSet(srcset) {
216
+ return srcset.split(',').map(src => {
217
+ src = src.trim();
218
+ const spaceIndex = src.lastIndexOf(' ');
219
+ if (spaceIndex === -1) return this._sanitizeUrl(src);
220
+ return this._sanitizeUrl(src.substring(0, spaceIndex).trim()) + src.substring(spaceIndex);
221
+ }).join(', ');
222
+ }
223
+ _resolveUrl(base, url) {
224
+ if (url === '') return '';
225
+ try {
226
+ return new URL(url, base).href;
227
+ } catch (e) {
228
+ return url;
229
+ }
230
+ }
231
+ _getSheetBase(sheet) {
232
+ let rootSheet = sheet;
233
+ while (rootSheet.parentStyleSheet) rootSheet = rootSheet.parentStyleSheet;
234
+ if (rootSheet.ownerNode) return rootSheet.ownerNode.baseURI;
235
+ return document.baseURI;
236
+ }
237
+ _getSheetText(sheet) {
238
+ this._readingStyleSheet = true;
239
+ try {
240
+ const rules = [];
241
+ for (const rule of sheet.cssRules) rules.push(rule.cssText);
242
+ return rules.join('\n');
243
+ } finally {
244
+ this._readingStyleSheet = false;
245
+ }
246
+ }
247
+ captureSnapshot() {
248
+ const timestamp = performance.now();
249
+ const snapshotNumber = ++this._lastSnapshotNumber;
250
+ let nodeCounter = 0;
251
+ let shadowDomNesting = 0;
252
+ let headNesting = 0;
253
+
254
+ // Ensure we are up to date.
255
+ this._handleMutations(this._observer.takeRecords());
256
+ const definedCustomElements = new Set();
257
+ const visitNode = node => {
258
+ const nodeType = node.nodeType;
259
+ const nodeName = nodeType === Node.DOCUMENT_FRAGMENT_NODE ? 'template' : node.nodeName;
260
+ if (nodeType !== Node.ELEMENT_NODE && nodeType !== Node.DOCUMENT_FRAGMENT_NODE && nodeType !== Node.TEXT_NODE) return;
261
+ if (nodeName === 'SCRIPT') return;
262
+ // Don't preload resources.
263
+ if (nodeName === 'LINK' && nodeType === Node.ELEMENT_NODE) {
264
+ var _getAttribute;
265
+ const rel = (_getAttribute = node.getAttribute('rel')) === null || _getAttribute === void 0 ? void 0 : _getAttribute.toLowerCase();
266
+ if (rel === 'preload' || rel === 'prefetch') return;
267
+ }
268
+ if (removeNoScript && nodeName === 'NOSCRIPT') return;
269
+ if (nodeName === 'META' && node.httpEquiv.toLowerCase() === 'content-security-policy') return;
270
+ // Skip iframes which are inside document's head as they are not visible.
271
+ // See https://github.com/microsoft/playwright/issues/12005.
272
+ if ((nodeName === 'IFRAME' || nodeName === 'FRAME') && headNesting) return;
273
+ const data = ensureCachedData(node);
274
+ const values = [];
275
+ let equals = !!data.cached;
276
+ let extraNodes = 0;
277
+ const expectValue = value => {
278
+ equals = equals && data.cached[values.length] === value;
279
+ values.push(value);
280
+ };
281
+ const checkAndReturn = n => {
282
+ data.attributesCached = true;
283
+ if (equals) return {
284
+ equals: true,
285
+ n: [[snapshotNumber - data.ref[0], data.ref[1]]]
286
+ };
287
+ nodeCounter += extraNodes;
288
+ data.ref = [snapshotNumber, nodeCounter++];
289
+ data.cached = values;
290
+ return {
291
+ equals: false,
292
+ n
293
+ };
294
+ };
295
+ if (nodeType === Node.TEXT_NODE) {
296
+ const value = node.nodeValue || '';
297
+ expectValue(value);
298
+ return checkAndReturn(value);
299
+ }
300
+ if (nodeName === 'STYLE') {
301
+ const sheet = node.sheet;
302
+ let cssText;
303
+ if (sheet) cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet);
304
+ cssText = cssText || node.textContent || '';
305
+ expectValue(cssText);
306
+ // Compensate for the extra 'cssText' text node.
307
+ extraNodes++;
308
+ return checkAndReturn([nodeName, {}, cssText]);
309
+ }
310
+ const attrs = {};
311
+ const result = [nodeName, attrs];
312
+ const visitChild = child => {
313
+ const snapshot = visitNode(child);
314
+ if (snapshot) {
315
+ result.push(snapshot.n);
316
+ expectValue(child);
317
+ equals = equals && snapshot.equals;
318
+ }
319
+ };
320
+ const visitChildStyleSheet = child => {
321
+ const snapshot = visitStyleSheet(child);
322
+ if (snapshot) {
323
+ result.push(snapshot.n);
324
+ expectValue(child);
325
+ equals = equals && snapshot.equals;
326
+ }
327
+ };
328
+ if (nodeType === Node.DOCUMENT_FRAGMENT_NODE) attrs[kShadowAttribute] = 'open';
329
+ if (nodeType === Node.ELEMENT_NODE) {
330
+ var _window$customElement;
331
+ const element = node;
332
+ if (element.localName.includes('-') && (_window$customElement = window.customElements) !== null && _window$customElement !== void 0 && _window$customElement.get(element.localName)) definedCustomElements.add(element.localName);
333
+ if (nodeName === 'INPUT' || nodeName === 'TEXTAREA') {
334
+ const value = element.value;
335
+ expectValue(kValueAttribute);
336
+ expectValue(value);
337
+ attrs[kValueAttribute] = value;
338
+ }
339
+ if (nodeName === 'INPUT' && ['checkbox', 'radio'].includes(element.type)) {
340
+ const value = element.checked ? 'true' : 'false';
341
+ expectValue(kCheckedAttribute);
342
+ expectValue(value);
343
+ attrs[kCheckedAttribute] = value;
344
+ }
345
+ if (nodeName === 'OPTION') {
346
+ const value = element.selected ? 'true' : 'false';
347
+ expectValue(kSelectedAttribute);
348
+ expectValue(value);
349
+ attrs[kSelectedAttribute] = value;
350
+ }
351
+ if (element.scrollTop) {
352
+ expectValue(kScrollTopAttribute);
353
+ expectValue(element.scrollTop);
354
+ attrs[kScrollTopAttribute] = '' + element.scrollTop;
355
+ }
356
+ if (element.scrollLeft) {
357
+ expectValue(kScrollLeftAttribute);
358
+ expectValue(element.scrollLeft);
359
+ attrs[kScrollLeftAttribute] = '' + element.scrollLeft;
360
+ }
361
+ if (element.shadowRoot) {
362
+ ++shadowDomNesting;
363
+ visitChild(element.shadowRoot);
364
+ --shadowDomNesting;
365
+ }
366
+ if ('__playwright_target__' in element) {
367
+ expectValue(kTargetAttribute);
368
+ expectValue(element['__playwright_target__']);
369
+ attrs[kTargetAttribute] = element['__playwright_target__'];
370
+ }
371
+ }
372
+ if (nodeName === 'HEAD') {
373
+ ++headNesting;
374
+ // Insert fake <base> first, to ensure all <link> elements use the proper base uri.
375
+ this._fakeBase.setAttribute('href', document.baseURI);
376
+ visitChild(this._fakeBase);
377
+ }
378
+ for (let child = node.firstChild; child; child = child.nextSibling) visitChild(child);
379
+ if (nodeName === 'HEAD') --headNesting;
380
+ expectValue(kEndOfList);
381
+ let documentOrShadowRoot = null;
382
+ if (node.ownerDocument.documentElement === node) documentOrShadowRoot = node.ownerDocument;else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) documentOrShadowRoot = node;
383
+ if (documentOrShadowRoot) {
384
+ for (const sheet of documentOrShadowRoot.adoptedStyleSheets || []) visitChildStyleSheet(sheet);
385
+ expectValue(kEndOfList);
386
+ }
387
+
388
+ // Process iframe src attribute before bailing out since it depends on a symbol, not the DOM.
389
+ if (nodeName === 'IFRAME' || nodeName === 'FRAME') {
390
+ const element = node;
391
+ const frameId = element[kSnapshotFrameId];
392
+ const name = 'src';
393
+ const value = frameId ? `/snapshot/${frameId}` : '';
394
+ expectValue(name);
395
+ expectValue(value);
396
+ attrs[name] = value;
397
+ }
398
+
399
+ // Process custom elements before bailing out since they depend on JS, not the DOM.
400
+ if (nodeName === 'BODY' && definedCustomElements.size) {
401
+ const value = [...definedCustomElements].join(',');
402
+ expectValue(kCustomElementsAttribute);
403
+ expectValue(value);
404
+ attrs[kCustomElementsAttribute] = value;
405
+ }
406
+
407
+ // Process currentSrc before bailing out since it depends on JS, not the DOM.
408
+ if (nodeName === 'IMG' || nodeName === 'PICTURE') {
409
+ const value = nodeName === 'PICTURE' ? '' : this._sanitizeUrl(node.currentSrc);
410
+ expectValue(kCurrentSrcAttribute);
411
+ expectValue(value);
412
+ attrs[kCurrentSrcAttribute] = value;
413
+ }
414
+
415
+ // We can skip attributes comparison because nothing else has changed,
416
+ // and mutation observer didn't tell us about the attributes.
417
+ if (equals && data.attributesCached && !shadowDomNesting) return checkAndReturn(result);
418
+ if (nodeType === Node.ELEMENT_NODE) {
419
+ const element = node;
420
+ for (let i = 0; i < element.attributes.length; i++) {
421
+ const name = element.attributes[i].name;
422
+ if (nodeName === 'LINK' && name === 'integrity') continue;
423
+ if (nodeName === 'IFRAME' && (name === 'src' || name === 'srcdoc' || name === 'sandbox')) continue;
424
+ if (nodeName === 'FRAME' && name === 'src') continue;
425
+ let value = element.attributes[i].value;
426
+ if (nodeName === 'META') value = this.__sanitizeMetaAttribute(name, value, node.httpEquiv);else if (name === 'src' && nodeName === 'IMG') value = this._sanitizeUrl(value);else if (name === 'srcset' && nodeName === 'IMG') value = this._sanitizeSrcSet(value);else if (name === 'srcset' && nodeName === 'SOURCE') value = this._sanitizeSrcSet(value);else if (name === 'href' && nodeName === 'LINK') value = this._sanitizeUrl(value);else if (name.startsWith('on')) value = '';
427
+ expectValue(name);
428
+ expectValue(value);
429
+ attrs[name] = value;
430
+ }
431
+ expectValue(kEndOfList);
432
+ }
433
+ if (result.length === 2 && !Object.keys(attrs).length) result.pop(); // Remove empty attrs when there are no children.
434
+ return checkAndReturn(result);
435
+ };
436
+ const visitStyleSheet = sheet => {
437
+ const data = ensureCachedData(sheet);
438
+ const oldCSSText = data.cssText;
439
+ const cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet, true /* forceText */);
440
+ if (cssText === oldCSSText) return {
441
+ equals: true,
442
+ n: [[snapshotNumber - data.ref[0], data.ref[1]]]
443
+ };
444
+ data.ref = [snapshotNumber, nodeCounter++];
445
+ return {
446
+ equals: false,
447
+ n: ['template', {
448
+ [kStyleSheetAttribute]: cssText
449
+ }]
450
+ };
451
+ };
452
+ let html;
453
+ if (document.documentElement) {
454
+ const {
455
+ n
456
+ } = visitNode(document.documentElement);
457
+ html = n;
458
+ } else {
459
+ html = ['html'];
460
+ }
461
+ const result = {
462
+ html,
463
+ doctype: document.doctype ? document.doctype.name : undefined,
464
+ resourceOverrides: [],
465
+ viewport: {
466
+ width: window.innerWidth,
467
+ height: window.innerHeight
468
+ },
469
+ url: location.href,
470
+ timestamp,
471
+ collectionTime: 0
472
+ };
473
+ for (const sheet of this._staleStyleSheets) {
474
+ if (sheet.href === null) continue;
475
+ const content = this._updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber);
476
+ if (content === undefined) {
477
+ // Unable to capture stylesheet contents.
478
+ continue;
479
+ }
480
+ const base = this._getSheetBase(sheet);
481
+ const url = removeHash(this._resolveUrl(base, sheet.href));
482
+ result.resourceOverrides.push({
483
+ url,
484
+ content,
485
+ contentType: 'text/css'
486
+ });
487
+ }
488
+ result.collectionTime = performance.now() - result.timestamp;
489
+ return result;
490
+ }
491
+ }
492
+ window[snapshotStreamer] = new Streamer();
493
+ }