patchright-core 1.55.2 → 1.56.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.
- package/bin/install_webkit_wsl.ps1 +35 -0
- package/bin/reinstall_chrome_beta_mac.sh +1 -1
- package/bin/reinstall_chrome_stable_mac.sh +1 -1
- package/bin/reinstall_msedge_beta_mac.sh +1 -1
- package/bin/reinstall_msedge_dev_mac.sh +1 -1
- package/bin/reinstall_msedge_stable_mac.sh +1 -1
- package/browsers.json +13 -13
- package/lib/browserServerImpl.js +3 -6
- package/lib/client/browser.js +0 -10
- package/lib/client/browserContext.js +2 -8
- package/lib/client/channelOwner.js +0 -7
- package/lib/client/consoleMessage.js +2 -3
- package/lib/client/electron.js +1 -1
- package/lib/client/events.js +1 -0
- package/lib/client/locator.js +5 -5
- package/lib/client/network.js +3 -0
- package/lib/client/page.js +13 -1
- package/lib/generated/bindingsControllerSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +1 -1
- package/lib/generated/utilityScriptSource.js +1 -1
- package/lib/protocol/validator.js +26 -15
- package/lib/remote/playwrightServer.js +12 -14
- package/lib/server/bidi/bidiBrowser.js +54 -10
- package/lib/server/bidi/bidiChromium.js +1 -1
- package/lib/server/bidi/bidiConnection.js +31 -6
- package/lib/server/bidi/bidiExecutionContext.js +4 -4
- package/lib/server/bidi/bidiFirefox.js +16 -1
- package/lib/server/bidi/bidiNetworkManager.js +82 -16
- package/lib/server/bidi/bidiPage.js +84 -18
- package/lib/server/browserType.js +3 -3
- package/lib/server/chromium/chromium.js +1 -1
- package/lib/server/chromium/chromiumSwitches.js +3 -2
- package/lib/server/chromium/crBrowser.js +2 -33
- package/lib/server/chromium/crNetworkManager.js +146 -39
- package/lib/server/chromium/crPage.js +22 -31
- package/lib/server/codegen/csharp.js +19 -26
- package/lib/server/codegen/java.js +4 -0
- package/lib/server/codegen/javascript.js +3 -1
- package/lib/server/codegen/python.js +2 -0
- package/lib/server/debugController.js +8 -36
- package/lib/server/deviceDescriptorsSource.json +62 -62
- package/lib/server/dispatchers/androidDispatcher.js +17 -0
- package/lib/server/dispatchers/browserContextDispatcher.js +1 -15
- package/lib/server/dispatchers/networkDispatchers.js +6 -3
- package/lib/server/dispatchers/pageDispatcher.js +24 -0
- package/lib/server/dom.js +8 -3
- package/lib/server/firefox/ffPage.js +1 -2
- package/lib/server/firefox/firefox.js +1 -1
- package/lib/server/frames.js +8 -3
- package/lib/server/har/harTracer.js +7 -8
- package/lib/server/network.js +12 -0
- package/lib/server/page.js +39 -17
- package/lib/server/recorder/chat.js +2 -2
- package/lib/server/recorder/recorderRunner.js +4 -0
- package/lib/server/registry/browserFetcher.js +3 -3
- package/lib/server/registry/index.js +27 -0
- package/lib/server/trace/recorder/snapshotter.js +13 -2
- package/lib/server/trace/recorder/snapshotterInjected.js +3 -1
- package/lib/server/utils/comparators.js +2 -2
- package/lib/server/utils/env.js +7 -2
- package/lib/server/utils/wsServer.js +2 -7
- package/lib/server/webkit/webkit.js +24 -8
- package/lib/server/webkit/wkBrowser.js +7 -3
- package/lib/server/webkit/wkPage.js +7 -8
- package/lib/server/webkit/wsl/webkit-wsl-transport-client.js +74 -0
- package/lib/server/webkit/wsl/webkit-wsl-transport-server.js +113 -0
- package/lib/utils/isomorphic/ariaSnapshot.js +12 -10
- package/lib/utils/isomorphic/protocolMetainfo.js +3 -0
- package/lib/utils/isomorphic/urlMatch.js +3 -8
- package/lib/utilsBundle.js +3 -0
- package/lib/utilsBundleImpl/index.js +80 -80
- package/lib/vite/htmlReport/index.html +37 -28
- package/lib/vite/recorder/assets/codeMirrorModule-RJCXzfmE.js +24 -0
- package/lib/vite/recorder/assets/index-Ri0uHF7I.css +1 -0
- package/lib/vite/recorder/assets/index-Y-X2TGJv.js +193 -0
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/traceViewer/assets/codeMirrorModule-rbQPefq7.js +24 -0
- package/lib/vite/traceViewer/assets/defaultSettingsView-CLbol9XR.js +265 -0
- package/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +9 -0
- package/lib/vite/traceViewer/defaultSettingsView.TQ8_7ybu.css +1 -0
- package/lib/vite/traceViewer/index.I8N9v4jT.css +1 -0
- package/lib/vite/traceViewer/index.html +4 -4
- package/lib/vite/traceViewer/index.zIVi6mN9.js +2 -0
- package/lib/vite/traceViewer/sw.bundle.js +3 -3
- package/lib/vite/traceViewer/uiMode.B_CpmIpF.js +5 -0
- package/lib/vite/traceViewer/uiMode.Btcz36p_.css +1 -0
- package/lib/vite/traceViewer/uiMode.html +4 -4
- package/lib/vite/traceViewer/{xtermModule.Beg8tuEN.css → xtermModule.DYP7pi_n.css} +1 -1
- package/package.json +2 -4
- package/types/protocol.d.ts +7084 -6870
- package/types/types.d.ts +57 -52
- package/lib/vite/recorder/assets/codeMirrorModule-DzQ0k89p.js +0 -24
- package/lib/vite/recorder/assets/index-CI4HQ-Zb.css +0 -1
- package/lib/vite/recorder/assets/index-D7C7daHH.js +0 -184
- package/lib/vite/traceViewer/assets/codeMirrorModule-Di48jgWx.js +0 -24
- package/lib/vite/traceViewer/assets/defaultSettingsView-szBn8781.js +0 -256
- package/lib/vite/traceViewer/assets/xtermModule-BoAIEibi.js +0 -9
- package/lib/vite/traceViewer/defaultSettingsView.DVJHpiGt.css +0 -1
- package/lib/vite/traceViewer/index.BFsek2M6.css +0 -1
- package/lib/vite/traceViewer/index.DQvXoPLL.js +0 -2
- package/lib/vite/traceViewer/uiMode.BatfzHMG.css +0 -1
- package/lib/vite/traceViewer/uiMode.dBV3oN9h.js +0 -5
|
@@ -51,7 +51,6 @@ class CRBrowser extends import_browser.Browser {
|
|
|
51
51
|
this._clientRootSessionPromise = null;
|
|
52
52
|
this._contexts = /* @__PURE__ */ new Map();
|
|
53
53
|
this._crPages = /* @__PURE__ */ new Map();
|
|
54
|
-
this._backgroundPages = /* @__PURE__ */ new Map();
|
|
55
54
|
this._serviceWorkers = /* @__PURE__ */ new Map();
|
|
56
55
|
this._version = "";
|
|
57
56
|
this._tracingRecording = false;
|
|
@@ -133,7 +132,7 @@ class CRBrowser extends import_browser.Browser {
|
|
|
133
132
|
async _waitForAllPagesToBeInitialized() {
|
|
134
133
|
await Promise.all([...this._crPages.values()].map((crPage) => crPage._page.waitForInitializedOrError()));
|
|
135
134
|
}
|
|
136
|
-
_onAttachedToTarget({ targetInfo, sessionId }) {
|
|
135
|
+
_onAttachedToTarget({ targetInfo, sessionId, waitingForDebugger }) {
|
|
137
136
|
if (targetInfo.type === "browser")
|
|
138
137
|
return;
|
|
139
138
|
const session = this._session.createChildSession(sessionId);
|
|
@@ -153,16 +152,10 @@ class CRBrowser extends import_browser.Browser {
|
|
|
153
152
|
return;
|
|
154
153
|
}
|
|
155
154
|
(0, import_assert.assert)(!this._crPages.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId);
|
|
156
|
-
(0, import_assert.assert)(!this._backgroundPages.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId);
|
|
157
155
|
(0, import_assert.assert)(!this._serviceWorkers.has(targetInfo.targetId), "Duplicate target " + targetInfo.targetId);
|
|
158
|
-
if (targetInfo.type === "background_page") {
|
|
159
|
-
const backgroundPage = new import_crPage.CRPage(session, targetInfo.targetId, context, null, { hasUIWindow: false, isBackgroundPage: true });
|
|
160
|
-
this._backgroundPages.set(targetInfo.targetId, backgroundPage);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
156
|
if (targetInfo.type === "page" || treatOtherAsPage) {
|
|
164
157
|
const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null;
|
|
165
|
-
const crPage = new import_crPage.CRPage(session, targetInfo.targetId, context, opener, { hasUIWindow: targetInfo.type === "page"
|
|
158
|
+
const crPage = new import_crPage.CRPage(session, targetInfo.targetId, context, opener, { hasUIWindow: targetInfo.type === "page" });
|
|
166
159
|
this._crPages.set(targetInfo.targetId, crPage);
|
|
167
160
|
return;
|
|
168
161
|
}
|
|
@@ -183,12 +176,6 @@ class CRBrowser extends import_browser.Browser {
|
|
|
183
176
|
crPage.didClose();
|
|
184
177
|
return;
|
|
185
178
|
}
|
|
186
|
-
const backgroundPage = this._backgroundPages.get(targetId);
|
|
187
|
-
if (backgroundPage) {
|
|
188
|
-
this._backgroundPages.delete(targetId);
|
|
189
|
-
backgroundPage.didClose();
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
179
|
const serviceWorker = this._serviceWorkers.get(targetId);
|
|
193
180
|
if (serviceWorker) {
|
|
194
181
|
this._serviceWorkers.delete(targetId);
|
|
@@ -200,9 +187,6 @@ class CRBrowser extends import_browser.Browser {
|
|
|
200
187
|
for (const crPage of this._crPages.values())
|
|
201
188
|
crPage.didClose();
|
|
202
189
|
this._crPages.clear();
|
|
203
|
-
for (const backgroundPage of this._backgroundPages.values())
|
|
204
|
-
backgroundPage.didClose();
|
|
205
|
-
this._backgroundPages.clear();
|
|
206
190
|
for (const serviceWorker of this._serviceWorkers.values())
|
|
207
191
|
serviceWorker.didClose();
|
|
208
192
|
this._serviceWorkers.clear();
|
|
@@ -295,7 +279,6 @@ class CRBrowser extends import_browser.Browser {
|
|
|
295
279
|
class CRBrowserContext extends import_browserContext.BrowserContext {
|
|
296
280
|
static {
|
|
297
281
|
this.CREvents = {
|
|
298
|
-
BackgroundPage: "backgroundpage",
|
|
299
282
|
ServiceWorker: "serviceworker"
|
|
300
283
|
};
|
|
301
284
|
}
|
|
@@ -480,12 +463,6 @@ class CRBrowserContext extends import_browserContext.BrowserContext {
|
|
|
480
463
|
await Promise.all(this._crPages().map((crPage) => crPage._mainFrameSession._stopVideoRecording()));
|
|
481
464
|
}
|
|
482
465
|
onClosePersistent() {
|
|
483
|
-
for (const [targetId, backgroundPage] of this._browser._backgroundPages.entries()) {
|
|
484
|
-
if (backgroundPage._browserContext === this && backgroundPage._page.initializedOrUndefined()) {
|
|
485
|
-
backgroundPage.didClose();
|
|
486
|
-
this._browser._backgroundPages.delete(targetId);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
466
|
}
|
|
490
467
|
async clearCache() {
|
|
491
468
|
for (const page of this._crPages())
|
|
@@ -497,14 +474,6 @@ class CRBrowserContext extends import_browserContext.BrowserContext {
|
|
|
497
474
|
browserContextId: this._browserContextId
|
|
498
475
|
});
|
|
499
476
|
}
|
|
500
|
-
backgroundPages() {
|
|
501
|
-
const result = [];
|
|
502
|
-
for (const backgroundPage of this._browser._backgroundPages.values()) {
|
|
503
|
-
if (backgroundPage._browserContext === this && backgroundPage._page.initializedOrUndefined())
|
|
504
|
-
result.push(backgroundPage._page);
|
|
505
|
-
}
|
|
506
|
-
return result;
|
|
507
|
-
}
|
|
508
477
|
serviceWorkers() {
|
|
509
478
|
return Array.from(this._browser._serviceWorkers.values()).filter((serviceWorker) => serviceWorker.browserContext === this);
|
|
510
479
|
}
|
|
@@ -533,57 +533,91 @@ class RouteImpl {
|
|
|
533
533
|
if (!allInjections.includes(binding)) allInjections.push(binding);
|
|
534
534
|
}
|
|
535
535
|
if (isTextHtml && allInjections.length) {
|
|
536
|
-
let
|
|
537
|
-
let
|
|
536
|
+
let useNonce = false;
|
|
537
|
+
let scriptNonce = null;
|
|
538
|
+
if (response.isBase64) {
|
|
539
|
+
response.isBase64 = false;
|
|
540
|
+
response.body = Buffer.from(response.body, "base64").toString("utf-8");
|
|
541
|
+
}
|
|
542
|
+
const cspHeaderNames = ["content-security-policy", "content-security-policy-report-only"];
|
|
538
543
|
for (let i = 0; i < response.headers.length; i++) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
useNonce = false;
|
|
548
|
-
} else {
|
|
549
|
-
const scriptSrcRegex = /(script-src[^;]*)(;|$)/;
|
|
550
|
-
const newCspValue = cspValue.replace(scriptSrcRegex, `$1 'nonce-${scriptNonce}'$2`);
|
|
551
|
-
response.headers[i].value = newCspValue;
|
|
544
|
+
const headerName = response.headers[i].name.toLowerCase();
|
|
545
|
+
if (cspHeaderNames.includes(headerName)) {
|
|
546
|
+
const originalCsp = response.headers[i].value || "";
|
|
547
|
+
if (!useNonce) {
|
|
548
|
+
const nonceMatch = originalCsp.match(/script-src[^;]*'nonce-([^'"\s;]+)'/i);
|
|
549
|
+
if (nonceMatch && nonceMatch[1]) {
|
|
550
|
+
scriptNonce = nonceMatch[1];
|
|
551
|
+
useNonce = true;
|
|
552
552
|
}
|
|
553
553
|
}
|
|
554
|
-
|
|
554
|
+
const fixedCsp = this._fixCSP(originalCsp, scriptNonce);
|
|
555
|
+
response.headers[i].value = fixedCsp;
|
|
555
556
|
}
|
|
556
557
|
}
|
|
558
|
+
if (typeof response.body === "string" && response.body.length) {
|
|
559
|
+
response.body = response.body.replace(
|
|
560
|
+
/<meta[^>]*http-equiv=(?:"|')?Content-Security-Policy(?:"|')?[^>]*>/gi,
|
|
561
|
+
(match) => {
|
|
562
|
+
const contentMatch = match.match(/content=(?:"|')([^"']*)(?:"|')/i);
|
|
563
|
+
if (contentMatch && contentMatch[1]) {
|
|
564
|
+
let originalCsp = contentMatch[1];
|
|
565
|
+
originalCsp = originalCsp.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/"/g, '"').replace(/ /g, " ").replace(/&#(d+);/g, (match2, dec) => String.fromCharCode(dec)).replace(/&#x([0-9a-fA-F]+);/g, (match2, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
566
|
+
if (!useNonce) {
|
|
567
|
+
const nonceMatch = originalCsp.match(/script-src[^;]*'nonce-([^'"\s;]+)'/i);
|
|
568
|
+
if (nonceMatch && nonceMatch[1]) {
|
|
569
|
+
scriptNonce = nonceMatch[1];
|
|
570
|
+
useNonce = true;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const fixedCsp = this._fixCSP(originalCsp, scriptNonce);
|
|
574
|
+
const encodedCsp = fixedCsp.replace(/'/g, "'").replace(/"/g, """);
|
|
575
|
+
return match.replace(contentMatch[1], encodedCsp);
|
|
576
|
+
}
|
|
577
|
+
return match;
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
}
|
|
557
581
|
let injectionHTML = "";
|
|
558
582
|
allInjections.forEach((script) => {
|
|
559
583
|
let scriptId = import_crypto.default.randomBytes(22).toString("hex");
|
|
560
584
|
let scriptSource = script.source || script;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
} else {
|
|
564
|
-
injectionHTML += `<script class="${this._page.delegate.initScriptTag}" id="${scriptId}" type="text/javascript">document.getElementById("${scriptId}")?.remove();${scriptSource}</script>`;
|
|
565
|
-
}
|
|
585
|
+
const nonceAttr = useNonce ? `nonce="${scriptNonce}"` : "";
|
|
586
|
+
injectionHTML += `<script class="${this._page.delegate.initScriptTag}" ${nonceAttr} id="${scriptId}" type="text/javascript">document.getElementById("${scriptId}")?.remove();${scriptSource}</script>`;
|
|
566
587
|
});
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
588
|
+
const lower = response.body.toLowerCase();
|
|
589
|
+
const headStartIndex = lower.indexOf("<head");
|
|
590
|
+
if (headStartIndex !== -1) {
|
|
591
|
+
const headEndTagIndex = lower.indexOf("</head>", headStartIndex);
|
|
592
|
+
if (headEndTagIndex !== -1) {
|
|
593
|
+
const headOpenEnd = response.body.indexOf(">", headStartIndex) + 1;
|
|
594
|
+
const headContent = response.body.slice(headOpenEnd, headEndTagIndex);
|
|
595
|
+
const headContentLower = headContent.toLowerCase();
|
|
596
|
+
const firstScriptIndex = headContentLower.indexOf("<script");
|
|
597
|
+
if (firstScriptIndex !== -1) {
|
|
598
|
+
const insertPosition = headOpenEnd + firstScriptIndex;
|
|
599
|
+
response.body = response.body.slice(0, insertPosition) + injectionHTML + response.body.slice(insertPosition);
|
|
600
|
+
} else {
|
|
601
|
+
response.body = response.body.slice(0, headEndTagIndex) + injectionHTML + response.body.slice(headEndTagIndex);
|
|
578
602
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
} else if (/<html[^>]*>/i.test(response.body)) {
|
|
584
|
-
response.body = response.body.replace(/<html[^>]*>/i, `$&<head>${injectionHTML}</head>`);
|
|
603
|
+
} else {
|
|
604
|
+
const headStartTagEnd = response.body.indexOf(">", headStartIndex) + 1;
|
|
605
|
+
response.body = response.body.slice(0, headStartTagEnd) + injectionHTML + response.body.slice(headStartTagEnd);
|
|
606
|
+
}
|
|
585
607
|
} else {
|
|
586
|
-
|
|
608
|
+
const doctypeIndex = lower.indexOf("<!doctype");
|
|
609
|
+
if (doctypeIndex === 0) {
|
|
610
|
+
const doctypeEnd = response.body.indexOf(">", doctypeIndex) + 1;
|
|
611
|
+
response.body = response.body.slice(0, doctypeEnd) + injectionHTML + response.body.slice(doctypeEnd);
|
|
612
|
+
} else {
|
|
613
|
+
const htmlIndex = lower.indexOf("<html");
|
|
614
|
+
if (htmlIndex !== -1) {
|
|
615
|
+
const htmlTagEnd = response.body.indexOf(">", htmlIndex) + 1;
|
|
616
|
+
response.body = response.body.slice(0, htmlTagEnd) + `<head>${injectionHTML}</head>` + response.body.slice(htmlTagEnd);
|
|
617
|
+
} else {
|
|
618
|
+
response.body = injectionHTML + response.body;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
587
621
|
}
|
|
588
622
|
}
|
|
589
623
|
this._fulfilled = true;
|
|
@@ -609,6 +643,79 @@ class RouteImpl {
|
|
|
609
643
|
});
|
|
610
644
|
});
|
|
611
645
|
}
|
|
646
|
+
_fixCSP(csp, scriptNonce) {
|
|
647
|
+
if (!csp || typeof csp !== "string") return csp;
|
|
648
|
+
const directives = csp.split(";").map((d) => d.trim()).filter((d) => d && d.length > 0);
|
|
649
|
+
const fixedDirectives = [];
|
|
650
|
+
let hasScriptSrc = false;
|
|
651
|
+
for (let directive of directives) {
|
|
652
|
+
if (!directive.trim()) continue;
|
|
653
|
+
const parts = directive.trim().split(/s+/);
|
|
654
|
+
if (parts.length === 0) continue;
|
|
655
|
+
const directiveName = parts[0].toLowerCase();
|
|
656
|
+
const directiveValues = parts.slice(1);
|
|
657
|
+
switch (directiveName) {
|
|
658
|
+
case "script-src":
|
|
659
|
+
hasScriptSrc = true;
|
|
660
|
+
let values = [...directiveValues];
|
|
661
|
+
if (scriptNonce && !values.some((v) => v.includes(`nonce-${scriptNonce}`))) {
|
|
662
|
+
values.push(`'nonce-${scriptNonce}'`);
|
|
663
|
+
}
|
|
664
|
+
if (!values.includes("'unsafe-eval'")) {
|
|
665
|
+
values.push("'unsafe-eval'");
|
|
666
|
+
}
|
|
667
|
+
fixedDirectives.push(`script-src ${values.join(" ")}`);
|
|
668
|
+
break;
|
|
669
|
+
case "style-src":
|
|
670
|
+
let styleValues = [...directiveValues];
|
|
671
|
+
if (!styleValues.includes("'unsafe-inline'")) {
|
|
672
|
+
styleValues.push("'unsafe-inline'");
|
|
673
|
+
}
|
|
674
|
+
fixedDirectives.push(`style-src ${styleValues.join(" ")}`);
|
|
675
|
+
break;
|
|
676
|
+
case "img-src":
|
|
677
|
+
let imgValues = [...directiveValues];
|
|
678
|
+
if (!imgValues.includes("data:") && !imgValues.includes("*")) {
|
|
679
|
+
imgValues.push("data:");
|
|
680
|
+
}
|
|
681
|
+
fixedDirectives.push(`img-src ${imgValues.join(" ")}`);
|
|
682
|
+
break;
|
|
683
|
+
case "font-src":
|
|
684
|
+
let fontValues = [...directiveValues];
|
|
685
|
+
if (!fontValues.includes("data:") && !fontValues.includes("*")) {
|
|
686
|
+
fontValues.push("data:");
|
|
687
|
+
}
|
|
688
|
+
fixedDirectives.push(`font-src ${fontValues.join(" ")}`);
|
|
689
|
+
break;
|
|
690
|
+
case "connect-src":
|
|
691
|
+
let connectValues = [...directiveValues];
|
|
692
|
+
const hasWs = connectValues.some((v) => v.includes("ws:") || v.includes("wss:") || v === "*");
|
|
693
|
+
if (!hasWs) {
|
|
694
|
+
connectValues.push("ws:", "wss:");
|
|
695
|
+
}
|
|
696
|
+
fixedDirectives.push(`connect-src ${connectValues.join(" ")}`);
|
|
697
|
+
break;
|
|
698
|
+
case "frame-ancestors":
|
|
699
|
+
let frameAncestorValues = [...directiveValues];
|
|
700
|
+
if (frameAncestorValues.includes("'none'")) {
|
|
701
|
+
frameAncestorValues = ["'self'"];
|
|
702
|
+
}
|
|
703
|
+
fixedDirectives.push(`frame-ancestors ${frameAncestorValues.join(" ")}`);
|
|
704
|
+
break;
|
|
705
|
+
default:
|
|
706
|
+
fixedDirectives.push(directive);
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
if (!hasScriptSrc) {
|
|
711
|
+
if (scriptNonce) {
|
|
712
|
+
fixedDirectives.push(`script-src 'self' 'unsafe-eval' 'nonce-${scriptNonce}'`);
|
|
713
|
+
} else {
|
|
714
|
+
fixedDirectives.push(`script-src 'self' 'unsafe-eval'`);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return fixedDirectives.join("; ");
|
|
718
|
+
}
|
|
612
719
|
async _networkRequestIntercepted(event) {
|
|
613
720
|
if (event.resourceType !== "Document") {
|
|
614
721
|
return;
|
|
@@ -45,7 +45,6 @@ var network = __toESM(require("../network"));
|
|
|
45
45
|
var import_page = require("../page");
|
|
46
46
|
var import_registry = require("../registry");
|
|
47
47
|
var import_crAccessibility = require("./crAccessibility");
|
|
48
|
-
var import_crBrowser = require("./crBrowser");
|
|
49
48
|
var import_crCoverage = require("./crCoverage");
|
|
50
49
|
var import_crDragDrop = require("./crDragDrop");
|
|
51
50
|
var import_crExecutionContext = require("./crExecutionContext");
|
|
@@ -55,7 +54,6 @@ var import_crPdf = require("./crPdf");
|
|
|
55
54
|
var import_crProtocolHelper = require("./crProtocolHelper");
|
|
56
55
|
var import_defaultFontFamilies = require("./defaultFontFamilies");
|
|
57
56
|
var import_videoRecorder = require("./videoRecorder");
|
|
58
|
-
var import_browserContext = require("../browserContext");
|
|
59
57
|
var import_errors = require("../errors");
|
|
60
58
|
var import_protocolError = require("../protocolError");
|
|
61
59
|
class CRPage {
|
|
@@ -69,7 +67,6 @@ class CRPage {
|
|
|
69
67
|
this._nextWindowOpenPopupFeatures = [];
|
|
70
68
|
this._targetId = targetId;
|
|
71
69
|
this._opener = opener;
|
|
72
|
-
this._isBackgroundPage = bits.isBackgroundPage;
|
|
73
70
|
const dragManager = new import_crDragDrop.DragManager(this);
|
|
74
71
|
this.rawKeyboard = new import_crInput.RawKeyboardImpl(client, browserContext._browser._platform() === "mac", dragManager);
|
|
75
72
|
this.rawMouse = new import_crInput.RawMouseImpl(this, client, dragManager);
|
|
@@ -93,10 +90,9 @@ class CRPage {
|
|
|
93
90
|
if (viewportSize)
|
|
94
91
|
this._page.setEmulatedSizeFromWindowOpen({ viewport: viewportSize, screen: viewportSize });
|
|
95
92
|
}
|
|
96
|
-
const createdEvent = this._isBackgroundPage ? import_crBrowser.CRBrowserContext.CREvents.BackgroundPage : import_browserContext.BrowserContext.Events.Page;
|
|
97
93
|
this._mainFrameSession._initialize(bits.hasUIWindow).then(
|
|
98
|
-
() => this._page.reportAsNew(this._opener?._page, void 0
|
|
99
|
-
(error) => this._page.reportAsNew(this._opener?._page, error
|
|
94
|
+
() => this._page.reportAsNew(this._opener?._page, void 0),
|
|
95
|
+
(error) => this._page.reportAsNew(this._opener?._page, error)
|
|
100
96
|
);
|
|
101
97
|
}
|
|
102
98
|
static mainFrameSession(page) {
|
|
@@ -371,13 +367,13 @@ class FrameSession {
|
|
|
371
367
|
import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.exceptionThrown", (exception) => this._handleException(exception.exceptionDetails)),
|
|
372
368
|
import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextCreated", (event) => this._onExecutionContextCreated(event.context)),
|
|
373
369
|
import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextDestroyed", (event) => this._onExecutionContextDestroyed(event.executionContextId)),
|
|
374
|
-
import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", (event) => this._onExecutionContextsCleared())
|
|
375
|
-
import_eventsHelper.eventsHelper.addEventListener(this._client, "Target.attachedToTarget", (event) => this._onAttachedToTarget(event)),
|
|
376
|
-
import_eventsHelper.eventsHelper.addEventListener(this._client, "Target.detachedFromTarget", (event) => this._onDetachedFromTarget(event))
|
|
370
|
+
import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", (event) => this._onExecutionContextsCleared())
|
|
377
371
|
]);
|
|
378
372
|
}
|
|
379
373
|
_addBrowserListeners() {
|
|
380
374
|
this._eventListeners.push(...[
|
|
375
|
+
import_eventsHelper.eventsHelper.addEventListener(this._client, "Target.attachedToTarget", (event) => this._onAttachedToTarget(event)),
|
|
376
|
+
import_eventsHelper.eventsHelper.addEventListener(this._client, "Target.detachedFromTarget", (event) => this._onDetachedFromTarget(event)),
|
|
381
377
|
import_eventsHelper.eventsHelper.addEventListener(this._client, "Inspector.targetCrashed", (event) => this._onTargetCrashed()),
|
|
382
378
|
import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.screencastFrame", (event) => this._onScreencastFrame(event)),
|
|
383
379
|
import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.windowOpen", (event) => this._onWindowOpen(event))
|
|
@@ -409,6 +405,7 @@ class FrameSession {
|
|
|
409
405
|
if (!this._isMainFrame())
|
|
410
406
|
this._addRendererListeners();
|
|
411
407
|
this._addBrowserListeners();
|
|
408
|
+
this._bufferedAttachedToTargetEvents = [];
|
|
412
409
|
const promises = [
|
|
413
410
|
this._client.send("Page.enable"),
|
|
414
411
|
this._client.send("Page.getFrameTree").then(({ frameTree }) => {
|
|
@@ -416,6 +413,10 @@ class FrameSession {
|
|
|
416
413
|
this._handleFrameTree(frameTree);
|
|
417
414
|
this._addRendererListeners();
|
|
418
415
|
}
|
|
416
|
+
const attachedToTargetEvents = this._bufferedAttachedToTargetEvents || [];
|
|
417
|
+
this._bufferedAttachedToTargetEvents = void 0;
|
|
418
|
+
for (const event of attachedToTargetEvents)
|
|
419
|
+
this._onAttachedToTarget(event);
|
|
419
420
|
const isInitialEmptyPage = this._isMainFrame() && this._page.mainFrame().url() === ":";
|
|
420
421
|
if (isInitialEmptyPage) {
|
|
421
422
|
lifecycleEventsEnabled.catch((e) => {
|
|
@@ -444,16 +445,7 @@ class FrameSession {
|
|
|
444
445
|
worldName: this._crPage.utilityWorldName
|
|
445
446
|
}),
|
|
446
447
|
this._crPage._networkManager.addSession(this._client, void 0, this._isMainFrame()),
|
|
447
|
-
this._client.send("Target.setAutoAttach", {
|
|
448
|
-
autoAttach: true,
|
|
449
|
-
waitForDebuggerOnStart: true,
|
|
450
|
-
flatten: true,
|
|
451
|
-
filter: [
|
|
452
|
-
{ type: "iframe" },
|
|
453
|
-
{ type: "worker" },
|
|
454
|
-
{ type: "service_worker", exclude: !process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS }
|
|
455
|
-
]
|
|
456
|
-
})
|
|
448
|
+
this._client.send("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true })
|
|
457
449
|
];
|
|
458
450
|
if (!this._page.isStorageStatePage) {
|
|
459
451
|
if (this._crPage._browserContext.needsPlaywrightBinding())
|
|
@@ -644,10 +636,17 @@ class FrameSession {
|
|
|
644
636
|
this._onExecutionContextDestroyed(contextId);
|
|
645
637
|
}
|
|
646
638
|
async _onAttachedToTarget(event) {
|
|
639
|
+
if (this._bufferedAttachedToTargetEvents) {
|
|
640
|
+
this._bufferedAttachedToTargetEvents.push(event);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
647
643
|
const session = this._client.createChildSession(event.sessionId);
|
|
648
644
|
if (event.targetInfo.type === "iframe") {
|
|
649
645
|
const targetId = event.targetInfo.targetId;
|
|
650
|
-
|
|
646
|
+
let frame = this._page.frameManager.frame(targetId);
|
|
647
|
+
if (!frame && event.targetInfo.parentFrameId) {
|
|
648
|
+
frame = this._page.frameManager.frameAttached(targetId, event.targetInfo.parentFrameId);
|
|
649
|
+
}
|
|
651
650
|
if (!frame)
|
|
652
651
|
return;
|
|
653
652
|
this._page.frameManager.removeChildFramesRecursively(frame);
|
|
@@ -684,22 +683,14 @@ class FrameSession {
|
|
|
684
683
|
this._crPage._networkManager.addSession(session, this._page.frameManager.frame(this._targetId) ?? void 0).catch(() => {
|
|
685
684
|
});
|
|
686
685
|
session._sendMayFail("Runtime.runIfWaitingForDebugger");
|
|
687
|
-
session._sendMayFail("Target.setAutoAttach", {
|
|
688
|
-
autoAttach: true,
|
|
689
|
-
waitForDebuggerOnStart: true,
|
|
690
|
-
flatten: true,
|
|
691
|
-
filter: [
|
|
692
|
-
{ type: "worker" },
|
|
693
|
-
{ type: "service_worker", exclude: !process.env.PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS }
|
|
694
|
-
]
|
|
695
|
-
});
|
|
686
|
+
session._sendMayFail("Target.setAutoAttach", { autoAttach: true, waitForDebuggerOnStart: true, flatten: true });
|
|
696
687
|
session.on("Target.attachedToTarget", (event2) => this._onAttachedToTarget(event2));
|
|
697
688
|
session.on("Target.detachedFromTarget", (event2) => this._onDetachedFromTarget(event2));
|
|
698
689
|
session.on("Runtime.consoleAPICalled", (event2) => {
|
|
699
690
|
const args = event2.args.map((o) => (0, import_crExecutionContext.createHandle)(worker.existingExecutionContext, o));
|
|
700
691
|
this._page.addConsoleMessage(event2.type, args, (0, import_crProtocolHelper.toConsoleMessageLocation)(event2.stackTrace));
|
|
701
692
|
});
|
|
702
|
-
session.on("Runtime.exceptionThrown", (exception) => this._page.
|
|
693
|
+
session.on("Runtime.exceptionThrown", (exception) => this._page.addPageError((0, import_crProtocolHelper.exceptionToError)(exception.exceptionDetails)));
|
|
703
694
|
}
|
|
704
695
|
_onDetachedFromTarget(event) {
|
|
705
696
|
const workerSession = this._workerSessions.get(event.sessionId);
|
|
@@ -758,7 +749,7 @@ class FrameSession {
|
|
|
758
749
|
));
|
|
759
750
|
}
|
|
760
751
|
_handleException(exceptionDetails) {
|
|
761
|
-
this._page.
|
|
752
|
+
this._page.addPageError((0, import_crProtocolHelper.exceptionToError)(exceptionDetails));
|
|
762
753
|
}
|
|
763
754
|
async _onTargetCrashed() {
|
|
764
755
|
this._client._markAsCrashed();
|
|
@@ -109,9 +109,13 @@ class CSharpLanguageGenerator {
|
|
|
109
109
|
const options = (0, import_language.toClickOptionsForSourceCode)(action);
|
|
110
110
|
if (!Object.entries(options).length)
|
|
111
111
|
return `await ${subject}.${this._asLocator(action.selector)}.${method}Async();`;
|
|
112
|
-
const optionsString = formatObject(options, " "
|
|
112
|
+
const optionsString = formatObject(options, " ");
|
|
113
113
|
return `await ${subject}.${this._asLocator(action.selector)}.${method}Async(${optionsString});`;
|
|
114
114
|
}
|
|
115
|
+
case "hover": {
|
|
116
|
+
const optionsString = action.position ? formatObject({ position: action.position }, " ") : "";
|
|
117
|
+
return `await ${subject}.${this._asLocator(action.selector)}.HoverAsync(${optionsString});`;
|
|
118
|
+
}
|
|
115
119
|
case "check":
|
|
116
120
|
return `await ${subject}.${this._asLocator(action.selector)}.CheckAsync();`;
|
|
117
121
|
case "uncheck":
|
|
@@ -159,11 +163,11 @@ class CSharpLanguageGenerator {
|
|
|
159
163
|
using System.Threading.Tasks;
|
|
160
164
|
|
|
161
165
|
using var playwright = await Playwright.CreateAsync();
|
|
162
|
-
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, " "
|
|
166
|
+
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, " ")});
|
|
163
167
|
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
|
164
168
|
if (options.contextOptions.recordHar) {
|
|
165
169
|
const url = options.contextOptions.recordHar.urlFilter;
|
|
166
|
-
formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)}${url ? `, ${formatObject({ url }, " "
|
|
170
|
+
formatter.add(` await context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)}${url ? `, ${formatObject({ url }, " ")}` : ""});`);
|
|
167
171
|
}
|
|
168
172
|
formatter.newLine();
|
|
169
173
|
return formatter.format();
|
|
@@ -191,14 +195,14 @@ class CSharpLanguageGenerator {
|
|
|
191
195
|
{`);
|
|
192
196
|
if (options.contextOptions.recordHar) {
|
|
193
197
|
const url = options.contextOptions.recordHar.urlFilter;
|
|
194
|
-
formatter.add(` await Context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)}${url ? `, ${formatObject({ url }, " "
|
|
198
|
+
formatter.add(` await Context.RouteFromHARAsync(${quote(options.contextOptions.recordHar.path)}${url ? `, ${formatObject({ url }, " ")}` : ""});`);
|
|
195
199
|
}
|
|
196
200
|
return formatter.format();
|
|
197
201
|
}
|
|
198
202
|
generateFooter(saveStorage) {
|
|
199
203
|
const offset = this._mode === "library" ? "" : " ";
|
|
200
204
|
let storageStateLine = saveStorage ? `
|
|
201
|
-
${offset}await context.StorageStateAsync(new
|
|
205
|
+
${offset}await context.StorageStateAsync(new()
|
|
202
206
|
${offset}{
|
|
203
207
|
${offset} Path = ${quote(saveStorage)}
|
|
204
208
|
${offset}});
|
|
@@ -212,8 +216,8 @@ ${offset}});
|
|
|
212
216
|
}
|
|
213
217
|
function formatObject(value, indent = " ", name = "") {
|
|
214
218
|
if (typeof value === "string") {
|
|
215
|
-
if (["
|
|
216
|
-
return `${
|
|
219
|
+
if (["colorScheme", "modifiers", "button", "recordHarContent", "recordHarMode", "serviceWorkers"].includes(name))
|
|
220
|
+
return `${getEnumName(name)}.${toPascal(value)}`;
|
|
217
221
|
return quote(value);
|
|
218
222
|
}
|
|
219
223
|
if (Array.isArray(value))
|
|
@@ -221,35 +225,24 @@ function formatObject(value, indent = " ", name = "") {
|
|
|
221
225
|
if (typeof value === "object") {
|
|
222
226
|
const keys = Object.keys(value).filter((key) => value[key] !== void 0).sort();
|
|
223
227
|
if (!keys.length)
|
|
224
|
-
return
|
|
228
|
+
return `new()`;
|
|
225
229
|
const tokens = [];
|
|
226
230
|
for (const key of keys) {
|
|
227
231
|
const property = getPropertyName(key);
|
|
228
232
|
tokens.push(`${property} = ${formatObject(value[key], indent, key)},`);
|
|
229
233
|
}
|
|
230
|
-
|
|
231
|
-
return `new ${getClassName(name)}
|
|
234
|
+
return `new()
|
|
232
235
|
{
|
|
233
236
|
${indent}${tokens.join(`
|
|
234
237
|
${indent}`)}
|
|
235
|
-
${indent}}`;
|
|
236
|
-
return `{
|
|
237
|
-
${indent}${tokens.join(`
|
|
238
|
-
${indent}`)}
|
|
239
238
|
${indent}}`;
|
|
240
239
|
}
|
|
241
240
|
if (name === "latitude" || name === "longitude")
|
|
242
241
|
return String(value) + "m";
|
|
243
242
|
return String(value);
|
|
244
243
|
}
|
|
245
|
-
function
|
|
244
|
+
function getEnumName(value) {
|
|
246
245
|
switch (value) {
|
|
247
|
-
case "viewport":
|
|
248
|
-
return "ViewportSize";
|
|
249
|
-
case "proxy":
|
|
250
|
-
return "ProxySettings";
|
|
251
|
-
case "permissions":
|
|
252
|
-
return "ContextPermission";
|
|
253
246
|
case "modifiers":
|
|
254
247
|
return "KeyboardModifier";
|
|
255
248
|
case "button":
|
|
@@ -278,18 +271,18 @@ function toPascal(value) {
|
|
|
278
271
|
return value[0].toUpperCase() + value.slice(1);
|
|
279
272
|
}
|
|
280
273
|
function formatContextOptions(contextOptions, deviceName) {
|
|
281
|
-
|
|
274
|
+
const options = { ...contextOptions };
|
|
282
275
|
delete options.recordHar;
|
|
283
276
|
const device = deviceName && import_deviceDescriptors.deviceDescriptors[deviceName];
|
|
284
277
|
if (!device) {
|
|
285
278
|
if (!Object.entries(options).length)
|
|
286
279
|
return "";
|
|
287
|
-
return formatObject(options, " "
|
|
280
|
+
return formatObject(options, " ");
|
|
288
281
|
}
|
|
289
|
-
|
|
290
|
-
if (!Object.entries(options).length)
|
|
282
|
+
if (!Object.entries((0, import_language.sanitizeDeviceOptions)(device, options)).length)
|
|
291
283
|
return `playwright.Devices[${quote(deviceName)}]`;
|
|
292
|
-
|
|
284
|
+
delete options["defaultBrowserType"];
|
|
285
|
+
return formatObject(options, " ");
|
|
293
286
|
}
|
|
294
287
|
class CSharpFormatter {
|
|
295
288
|
constructor(offset = 0) {
|
|
@@ -91,6 +91,10 @@ class JavaLanguageGenerator {
|
|
|
91
91
|
const optionsText = formatClickOptions(options);
|
|
92
92
|
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.${method}(${optionsText});`;
|
|
93
93
|
}
|
|
94
|
+
case "hover": {
|
|
95
|
+
const optionsText = action.position ? `new Locator.HoverOptions().setPosition(${action.position.x}, ${action.position.y})` : "";
|
|
96
|
+
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.hover(${optionsText});`;
|
|
97
|
+
}
|
|
94
98
|
case "check":
|
|
95
99
|
return `${subject}.${this._asLocator(action.selector, inFrameLocator)}.check();`;
|
|
96
100
|
case "uncheck":
|
|
@@ -81,6 +81,8 @@ class JavaScriptLanguageGenerator {
|
|
|
81
81
|
const optionsString = formatOptions(options, false);
|
|
82
82
|
return `await ${subject}.${this._asLocator(action.selector)}.${method}(${optionsString});`;
|
|
83
83
|
}
|
|
84
|
+
case "hover":
|
|
85
|
+
return `await ${subject}.${this._asLocator(action.selector)}.hover(${formatOptions({ position: action.position }, false)});`;
|
|
84
86
|
case "check":
|
|
85
87
|
return `await ${subject}.${this._asLocator(action.selector)}.check();`;
|
|
86
88
|
case "uncheck":
|
|
@@ -166,7 +168,7 @@ ${useText ? "\ntest.use(" + useText + ");\n" : ""}
|
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
170
|
function formatOptions(value, hasArguments) {
|
|
169
|
-
const keys = Object.keys(value);
|
|
171
|
+
const keys = Object.keys(value).filter((key) => value[key] !== void 0);
|
|
170
172
|
if (!keys.length)
|
|
171
173
|
return "";
|
|
172
174
|
return (hasArguments ? ", " : "") + formatObject(value);
|
|
@@ -83,6 +83,8 @@ class PythonLanguageGenerator {
|
|
|
83
83
|
const optionsString = formatOptions(options, false);
|
|
84
84
|
return `${subject}.${this._asLocator(action.selector)}.${method}(${optionsString})`;
|
|
85
85
|
}
|
|
86
|
+
case "hover":
|
|
87
|
+
return `${subject}.${this._asLocator(action.selector)}.hover(${formatOptions({ position: action.position }, false)})`;
|
|
86
88
|
case "check":
|
|
87
89
|
return `${subject}.${this._asLocator(action.selector)}.check()`;
|
|
88
90
|
case "uncheck":
|