rebrowser-playwright-core 1.47.2 → 1.48.2
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/README.md +2 -0
- package/browsers.json +14 -10
- package/lib/cli/program.js +26 -14
- package/lib/client/api.js +6 -0
- package/lib/client/browserContext.js +20 -2
- package/lib/client/channelOwner.js +5 -1
- package/lib/client/connection.js +3 -0
- package/lib/client/fetch.js +16 -21
- package/lib/client/jsHandle.js +0 -8
- package/lib/client/localUtils.js +1 -0
- package/lib/client/network.js +175 -17
- package/lib/client/page.js +21 -0
- package/lib/client/playwright.js +6 -3
- package/lib/client/tracing.js +16 -20
- package/lib/generated/consoleApiSource.js +1 -1
- package/lib/generated/injectedScriptSource.js +1 -1
- package/lib/generated/pollingRecorderSource.js +7 -0
- package/lib/generated/webSocketMockSource.js +7 -0
- package/lib/protocol/validator.js +80 -14
- package/lib/server/bidi/bidiBrowser.js +23 -8
- package/lib/server/bidi/bidiChromium.js +124 -0
- package/lib/server/bidi/bidiConnection.js +1 -1
- package/lib/server/bidi/bidiExecutionContext.js +0 -3
- package/lib/server/bidi/bidiFirefox.js +15 -21
- package/lib/server/bidi/bidiInput.js +16 -32
- package/lib/server/bidi/bidiNetworkManager.js +39 -5
- package/lib/server/bidi/bidiOverCdp.js +103 -0
- package/lib/server/bidi/bidiPage.js +98 -25
- package/lib/server/bidi/bidiPdf.js +140 -0
- package/lib/server/bidi/third_party/firefoxPrefs.js +221 -0
- package/lib/server/browser.js +13 -2
- package/lib/server/browserContext.js +6 -23
- package/lib/server/browserType.js +39 -11
- package/lib/server/chromium/chromium.js +3 -15
- package/lib/server/chromium/chromiumSwitches.js +3 -1
- package/lib/server/chromium/crBrowser.js +4 -3
- package/lib/server/chromium/crExecutionContext.js +0 -7
- package/lib/server/chromium/crPage.js +16 -6
- package/lib/server/chromium/videoRecorder.js +1 -1
- package/lib/server/codegen/csharp.js +1 -1
- package/lib/server/codegen/java.js +1 -1
- package/lib/server/codegen/javascript.js +1 -1
- package/lib/server/codegen/language.js +14 -2
- package/lib/server/codegen/python.js +1 -1
- package/lib/server/cookieStore.js +73 -0
- package/lib/server/debugController.js +2 -2
- package/lib/server/deviceDescriptorsSource.json +50 -50
- package/lib/server/dialog.js +1 -0
- package/lib/server/dispatchers/browserContextDispatcher.js +19 -2
- package/lib/server/dispatchers/jsHandleDispatcher.js +0 -5
- package/lib/server/dispatchers/pageDispatcher.js +9 -0
- package/lib/server/dispatchers/playwrightDispatcher.js +2 -1
- package/lib/server/dispatchers/webSocketRouteDispatcher.js +189 -0
- package/lib/server/download.js +9 -2
- package/lib/server/fetch.js +90 -94
- package/lib/server/firefox/ffBrowser.js +6 -4
- package/lib/server/firefox/ffExecutionContext.js +0 -3
- package/lib/server/firefox/ffPage.js +6 -3
- package/lib/server/firefox/firefox.js +2 -13
- package/lib/server/frameSelectors.js +1 -1
- package/lib/server/frames.js +164 -28
- package/lib/server/har/harTracer.js +11 -0
- package/lib/server/input.js +0 -1
- package/lib/server/javascript.js +0 -7
- package/lib/server/page.js +5 -1
- package/lib/server/playwright.js +5 -2
- package/lib/server/recorder/contextRecorder.js +33 -50
- package/lib/server/recorder/recorderApp.js +16 -10
- package/lib/server/recorder/recorderCollection.js +68 -79
- package/lib/server/recorder/recorderInTraceViewer.js +144 -0
- package/lib/server/recorder/recorderRunner.js +75 -97
- package/lib/server/recorder/recorderUtils.js +47 -6
- package/lib/server/recorder.js +30 -25
- package/lib/server/registry/index.js +85 -4
- package/lib/server/socksClientCertificatesInterceptor.js +15 -3
- package/lib/server/trace/recorder/snapshotter.js +1 -0
- package/lib/server/trace/recorder/snapshotterInjected.js +2 -2
- package/lib/server/trace/recorder/tracing.js +58 -2
- package/lib/server/trace/test/inMemorySnapshotter.js +1 -1
- package/lib/server/trace/viewer/traceViewer.js +2 -5
- package/lib/server/webkit/webkit.js +1 -1
- package/lib/server/webkit/wkBrowser.js +6 -5
- package/lib/server/webkit/wkExecutionContext.js +0 -3
- package/lib/server/webkit/wkPage.js +7 -4
- package/lib/utils/happy-eyeballs.js +13 -0
- package/lib/utils/hostPlatform.js +2 -2
- package/lib/utils/httpServer.js +1 -0
- package/lib/utils/isomorphic/locatorGenerators.js +9 -18
- package/lib/utils/isomorphic/locatorParser.js +2 -2
- package/lib/utils/isomorphic/recorderUtils.js +195 -0
- package/lib/vite/htmlReport/index.html +12 -12
- package/lib/vite/recorder/assets/{codeMirrorModule-aW6Vz3gf.js → codeMirrorModule-baozm8ur.js} +1 -1
- package/lib/vite/recorder/assets/{index-DCpXOVIh.js → index-2ElAIWFB.js} +10 -15
- package/lib/vite/recorder/assets/{index-B-MT5gKo.css → index-BW-aOBcL.css} +1 -1
- package/lib/vite/recorder/index.html +2 -2
- package/lib/vite/traceViewer/assets/{codeMirrorModule-eFKCUIu2.js → codeMirrorModule-Bh1rfd2w.js} +1 -1
- package/lib/vite/traceViewer/assets/inspectorTab-7GHnKvSD.js +64 -0
- package/lib/vite/traceViewer/assets/workbench-DPQnTHYP.js +9 -0
- package/lib/vite/traceViewer/embedded.BlHoW5LY.js +2 -0
- package/lib/vite/traceViewer/embedded.html +5 -3
- package/lib/vite/traceViewer/index.DaWVfou1.js +2 -0
- package/lib/vite/traceViewer/index.html +5 -3
- package/lib/vite/traceViewer/inspectorTab.DLjBDrQR.css +1 -0
- package/lib/vite/traceViewer/recorder.B_SY1GJM.css +0 -0
- package/lib/vite/traceViewer/recorder.C4zxcvd2.js +2 -0
- package/lib/vite/traceViewer/recorder.html +17 -0
- package/lib/vite/traceViewer/sw.bundle.js +3 -3
- package/lib/vite/traceViewer/{uiMode.D3cNFP6u.css → uiMode.CAYqod-m.css} +1 -1
- package/lib/vite/traceViewer/uiMode.html +6 -4
- package/lib/vite/traceViewer/uiMode.mTXWniJb.js +5 -0
- package/lib/vite/traceViewer/workbench.D3JVcA9K.css +1 -0
- package/package.json +1 -1
- package/types/protocol.d.ts +79 -8
- package/types/types.d.ts +2037 -949
- package/lib/generated/recorderSource.js +0 -7
- package/lib/vite/traceViewer/assets/workbench-D0cirtfA.js +0 -72
- package/lib/vite/traceViewer/embedded.BkuvsLJp.js +0 -2
- package/lib/vite/traceViewer/index.C968kAiC.js +0 -2
- package/lib/vite/traceViewer/uiMode.BkFnr4V0.js +0 -10
- package/lib/vite/traceViewer/workbench.DjbIuxix.css +0 -1
- /package/lib/server/recorder/{recorderActions.js → recorderFrontend.js} +0 -0
|
@@ -74,11 +74,6 @@ class JSHandleDispatcher extends _dispatcher.Dispatcher {
|
|
|
74
74
|
value: serializeResult(await this._object.jsonValue())
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
async objectCount(params) {
|
|
78
|
-
return {
|
|
79
|
-
count: await this._object.objectCount()
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
77
|
async dispose(_, metadata) {
|
|
83
78
|
metadata.potentiallyClosesScope = true;
|
|
84
79
|
this._object.dispose();
|
|
@@ -13,6 +13,7 @@ var _jsHandleDispatcher = require("./jsHandleDispatcher");
|
|
|
13
13
|
var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
|
|
14
14
|
var _artifactDispatcher = require("./artifactDispatcher");
|
|
15
15
|
var _utils = require("../../utils");
|
|
16
|
+
var _webSocketRouteDispatcher = require("./webSocketRouteDispatcher");
|
|
16
17
|
/**
|
|
17
18
|
* Copyright (c) Microsoft Corporation.
|
|
18
19
|
*
|
|
@@ -54,6 +55,7 @@ class PageDispatcher extends _dispatcher.Dispatcher {
|
|
|
54
55
|
this._type_Page = true;
|
|
55
56
|
this._page = void 0;
|
|
56
57
|
this._subscriptions = new Set();
|
|
58
|
+
this._webSocketInterceptionPatterns = [];
|
|
57
59
|
this.adopt(mainFrame);
|
|
58
60
|
this._page = page;
|
|
59
61
|
this.addObjectListener(_page.Page.Events.Close, () => {
|
|
@@ -133,6 +135,9 @@ class PageDispatcher extends _dispatcher.Dispatcher {
|
|
|
133
135
|
response: _networkDispatchers.ResponseDispatcher.fromNullable(this.parentScope(), await this._page.goForward(metadata, params))
|
|
134
136
|
};
|
|
135
137
|
}
|
|
138
|
+
async requestGC(params, metadata) {
|
|
139
|
+
await this._page.requestGC();
|
|
140
|
+
}
|
|
136
141
|
async registerLocatorHandler(params, metadata) {
|
|
137
142
|
const uid = this._page.registerLocatorHandler(params.selector, params.noWaitAfter);
|
|
138
143
|
return {
|
|
@@ -174,6 +179,10 @@ class PageDispatcher extends _dispatcher.Dispatcher {
|
|
|
174
179
|
return true;
|
|
175
180
|
});
|
|
176
181
|
}
|
|
182
|
+
async setWebSocketInterceptionPatterns(params, metadata) {
|
|
183
|
+
this._webSocketInterceptionPatterns = params.patterns;
|
|
184
|
+
if (params.patterns.length) await _webSocketRouteDispatcher.WebSocketRouteDispatcher.installIfNeeded(this.parentScope(), this._page);
|
|
185
|
+
}
|
|
177
186
|
async expectScreenshot(params, metadata) {
|
|
178
187
|
const mask = (params.mask || []).map(({
|
|
179
188
|
frame,
|
|
@@ -39,9 +39,10 @@ class PlaywrightDispatcher extends _dispatcher.Dispatcher {
|
|
|
39
39
|
const prelaunchedAndroidDeviceDispatcher = prelaunchedAndroidDevice ? new _androidDispatcher.AndroidDeviceDispatcher(android, prelaunchedAndroidDevice) : undefined;
|
|
40
40
|
super(scope, playwright, 'Playwright', {
|
|
41
41
|
chromium: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.chromium),
|
|
42
|
-
bidi: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidi),
|
|
43
42
|
firefox: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.firefox),
|
|
44
43
|
webkit: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.webkit),
|
|
44
|
+
bidiChromium: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidiChromium),
|
|
45
|
+
bidiFirefox: new _browserTypeDispatcher.BrowserTypeDispatcher(scope, playwright.bidiFirefox),
|
|
45
46
|
android,
|
|
46
47
|
electron: new _electronDispatcher.ElectronDispatcher(scope, playwright.electron),
|
|
47
48
|
utils: playwright.options.isServer ? undefined : new _localUtilsDispatcher.LocalUtilsDispatcher(scope, playwright),
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.WebSocketRouteDispatcher = void 0;
|
|
7
|
+
var _page = require("../page");
|
|
8
|
+
var _dispatcher = require("./dispatcher");
|
|
9
|
+
var _utils = require("../../utils");
|
|
10
|
+
var _pageDispatcher = require("./pageDispatcher");
|
|
11
|
+
var webSocketMockSource = _interopRequireWildcard(require("../../generated/webSocketMockSource"));
|
|
12
|
+
var _eventsHelper = require("../../utils/eventsHelper");
|
|
13
|
+
var _class;
|
|
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
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
30
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
31
|
+
const kBindingInstalledSymbol = Symbol('webSocketRouteBindingInstalled');
|
|
32
|
+
const kInitScriptInstalledSymbol = Symbol('webSocketRouteInitScriptInstalled');
|
|
33
|
+
class WebSocketRouteDispatcher extends _dispatcher.Dispatcher {
|
|
34
|
+
constructor(scope, id, url, frame) {
|
|
35
|
+
super(scope, {
|
|
36
|
+
guid: 'webSocketRoute@' + (0, _utils.createGuid)()
|
|
37
|
+
}, 'WebSocketRoute', {
|
|
38
|
+
url
|
|
39
|
+
});
|
|
40
|
+
this._type_WebSocketRoute = true;
|
|
41
|
+
this._id = void 0;
|
|
42
|
+
this._frame = void 0;
|
|
43
|
+
this._id = id;
|
|
44
|
+
this._frame = frame;
|
|
45
|
+
this._eventListeners.push(
|
|
46
|
+
// When the frame navigates or detaches, there will be no more communication
|
|
47
|
+
// from the mock websocket, so pretend like it was closed.
|
|
48
|
+
_eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.InternalFrameNavigatedToNewDocument, frame => {
|
|
49
|
+
if (frame === this._frame) this._executionContextGone();
|
|
50
|
+
}), _eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.FrameDetached, frame => {
|
|
51
|
+
if (frame === this._frame) this._executionContextGone();
|
|
52
|
+
}), _eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.Close, () => this._executionContextGone()), _eventsHelper.eventsHelper.addEventListener(frame._page, _page.Page.Events.Crash, () => this._executionContextGone()));
|
|
53
|
+
WebSocketRouteDispatcher._idToDispatcher.set(this._id, this);
|
|
54
|
+
scope._dispatchEvent('webSocketRoute', {
|
|
55
|
+
webSocketRoute: this
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
static async installIfNeeded(contextDispatcher, target) {
|
|
59
|
+
const context = target instanceof _page.Page ? target.context() : target;
|
|
60
|
+
if (!context[kBindingInstalledSymbol]) {
|
|
61
|
+
context[kBindingInstalledSymbol] = true;
|
|
62
|
+
await context.exposeBinding('__pwWebSocketBinding', false, (source, payload) => {
|
|
63
|
+
if (payload.type === 'onCreate') {
|
|
64
|
+
const pageDispatcher = _pageDispatcher.PageDispatcher.fromNullable(contextDispatcher, source.page);
|
|
65
|
+
let scope;
|
|
66
|
+
if (pageDispatcher && matchesPattern(pageDispatcher, context._options.baseURL, payload.url)) scope = pageDispatcher;else if (matchesPattern(contextDispatcher, context._options.baseURL, payload.url)) scope = contextDispatcher;
|
|
67
|
+
if (scope) {
|
|
68
|
+
new WebSocketRouteDispatcher(scope, payload.id, payload.url, source.frame);
|
|
69
|
+
} else {
|
|
70
|
+
const request = {
|
|
71
|
+
id: payload.id,
|
|
72
|
+
type: 'passthrough'
|
|
73
|
+
};
|
|
74
|
+
source.frame.evaluateExpression(`globalThis.__pwWebSocketDispatch(${JSON.stringify(request)})`).catch(() => {});
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const dispatcher = WebSocketRouteDispatcher._idToDispatcher.get(payload.id);
|
|
79
|
+
if (payload.type === 'onMessageFromPage') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('messageFromPage', {
|
|
80
|
+
message: payload.data.data,
|
|
81
|
+
isBase64: payload.data.isBase64
|
|
82
|
+
});
|
|
83
|
+
if (payload.type === 'onMessageFromServer') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('messageFromServer', {
|
|
84
|
+
message: payload.data.data,
|
|
85
|
+
isBase64: payload.data.isBase64
|
|
86
|
+
});
|
|
87
|
+
if (payload.type === 'onClosePage') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('closePage', {
|
|
88
|
+
code: payload.code,
|
|
89
|
+
reason: payload.reason,
|
|
90
|
+
wasClean: payload.wasClean
|
|
91
|
+
});
|
|
92
|
+
if (payload.type === 'onCloseServer') dispatcher === null || dispatcher === void 0 || dispatcher._dispatchEvent('closeServer', {
|
|
93
|
+
code: payload.code,
|
|
94
|
+
reason: payload.reason,
|
|
95
|
+
wasClean: payload.wasClean
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (!target[kInitScriptInstalledSymbol]) {
|
|
100
|
+
target[kInitScriptInstalledSymbol] = true;
|
|
101
|
+
await target.addInitScript(`
|
|
102
|
+
(() => {
|
|
103
|
+
const module = {};
|
|
104
|
+
${webSocketMockSource.source}
|
|
105
|
+
(module.exports.inject())(globalThis);
|
|
106
|
+
})();
|
|
107
|
+
`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async connect(params) {
|
|
111
|
+
await this._evaluateAPIRequest({
|
|
112
|
+
id: this._id,
|
|
113
|
+
type: 'connect'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async ensureOpened(params) {
|
|
117
|
+
await this._evaluateAPIRequest({
|
|
118
|
+
id: this._id,
|
|
119
|
+
type: 'ensureOpened'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async sendToPage(params) {
|
|
123
|
+
await this._evaluateAPIRequest({
|
|
124
|
+
id: this._id,
|
|
125
|
+
type: 'sendToPage',
|
|
126
|
+
data: {
|
|
127
|
+
data: params.message,
|
|
128
|
+
isBase64: params.isBase64
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async sendToServer(params) {
|
|
133
|
+
await this._evaluateAPIRequest({
|
|
134
|
+
id: this._id,
|
|
135
|
+
type: 'sendToServer',
|
|
136
|
+
data: {
|
|
137
|
+
data: params.message,
|
|
138
|
+
isBase64: params.isBase64
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
async closePage(params) {
|
|
143
|
+
await this._evaluateAPIRequest({
|
|
144
|
+
id: this._id,
|
|
145
|
+
type: 'closePage',
|
|
146
|
+
code: params.code,
|
|
147
|
+
reason: params.reason,
|
|
148
|
+
wasClean: params.wasClean
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async closeServer(params) {
|
|
152
|
+
await this._evaluateAPIRequest({
|
|
153
|
+
id: this._id,
|
|
154
|
+
type: 'closeServer',
|
|
155
|
+
code: params.code,
|
|
156
|
+
reason: params.reason,
|
|
157
|
+
wasClean: params.wasClean
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async _evaluateAPIRequest(request) {
|
|
161
|
+
await this._frame.evaluateExpression(`globalThis.__pwWebSocketDispatch(${JSON.stringify(request)})`).catch(() => {});
|
|
162
|
+
}
|
|
163
|
+
_onDispose() {
|
|
164
|
+
WebSocketRouteDispatcher._idToDispatcher.delete(this._id);
|
|
165
|
+
}
|
|
166
|
+
_executionContextGone() {
|
|
167
|
+
// We could enter here after being disposed upon page closure:
|
|
168
|
+
// - first from the recursive dispose inintiated by PageDispatcher;
|
|
169
|
+
// - then from our own page.on('close') listener.
|
|
170
|
+
if (!this._disposed) {
|
|
171
|
+
this._dispatchEvent('closePage', {
|
|
172
|
+
wasClean: true
|
|
173
|
+
});
|
|
174
|
+
this._dispatchEvent('closeServer', {
|
|
175
|
+
wasClean: true
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.WebSocketRouteDispatcher = WebSocketRouteDispatcher;
|
|
181
|
+
_class = WebSocketRouteDispatcher;
|
|
182
|
+
WebSocketRouteDispatcher._idToDispatcher = new Map();
|
|
183
|
+
function matchesPattern(dispatcher, baseURL, url) {
|
|
184
|
+
for (const pattern of dispatcher._webSocketInterceptionPatterns || []) {
|
|
185
|
+
const urlMatch = pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob;
|
|
186
|
+
if ((0, _utils.urlMatches)(baseURL, url, urlMatch)) return true;
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
package/lib/server/download.js
CHANGED
|
@@ -39,15 +39,22 @@ class Download {
|
|
|
39
39
|
this.url = url;
|
|
40
40
|
this._suggestedFilename = suggestedFilename;
|
|
41
41
|
page._browserContext._downloads.add(this);
|
|
42
|
-
if (suggestedFilename !== undefined) this.
|
|
42
|
+
if (suggestedFilename !== undefined) this._fireDownloadEvent();
|
|
43
|
+
}
|
|
44
|
+
page() {
|
|
45
|
+
return this._page;
|
|
43
46
|
}
|
|
44
47
|
_filenameSuggested(suggestedFilename) {
|
|
45
48
|
(0, _utils.assert)(this._suggestedFilename === undefined);
|
|
46
49
|
this._suggestedFilename = suggestedFilename;
|
|
47
|
-
this.
|
|
50
|
+
this._fireDownloadEvent();
|
|
48
51
|
}
|
|
49
52
|
suggestedFilename() {
|
|
50
53
|
return this._suggestedFilename;
|
|
51
54
|
}
|
|
55
|
+
_fireDownloadEvent() {
|
|
56
|
+
this._page.instrumentation.onDownload(this._page, this);
|
|
57
|
+
this._page.emit(_page.Page.Events.Download, this);
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
60
|
exports.Download = Download;
|
package/lib/server/fetch.js
CHANGED
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.GlobalAPIRequestContext = exports.BrowserContextAPIRequestContext = exports.APIRequestContext = void 0;
|
|
7
|
+
exports.createProxyAgent = createProxyAgent;
|
|
7
8
|
var _http = _interopRequireDefault(require("http"));
|
|
8
9
|
var _https = _interopRequireDefault(require("https"));
|
|
9
10
|
var _stream = require("stream");
|
|
@@ -20,8 +21,8 @@ var _happyEyeballs = require("../utils/happy-eyeballs");
|
|
|
20
21
|
var _instrumentation = require("./instrumentation");
|
|
21
22
|
var _progress = require("./progress");
|
|
22
23
|
var _tracing = require("./trace/recorder/tracing");
|
|
23
|
-
var _network = require("./network");
|
|
24
24
|
var _socksClientCertificatesInterceptor = require("./socksClientCertificatesInterceptor");
|
|
25
|
+
var _tls = require("tls");
|
|
25
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
26
27
|
/**
|
|
27
28
|
* Copyright (c) Microsoft Corporation.
|
|
@@ -71,7 +72,7 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
71
72
|
return uid;
|
|
72
73
|
}
|
|
73
74
|
async fetch(params, metadata) {
|
|
74
|
-
var _params$method
|
|
75
|
+
var _params$method;
|
|
75
76
|
const defaults = this._defaultOptions();
|
|
76
77
|
const headers = {
|
|
77
78
|
'user-agent': defaults.userAgent,
|
|
@@ -91,7 +92,9 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
91
92
|
} of params.headers) setHeader(headers, name, value);
|
|
92
93
|
}
|
|
93
94
|
const requestUrl = new URL(params.url, defaults.baseURL);
|
|
94
|
-
if (params.
|
|
95
|
+
if (params.encodedParams) {
|
|
96
|
+
requestUrl.search = params.encodedParams;
|
|
97
|
+
} else if (params.params) {
|
|
95
98
|
for (const {
|
|
96
99
|
name,
|
|
97
100
|
value
|
|
@@ -102,23 +105,9 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
102
105
|
const method = ((_params$method = params.method) === null || _params$method === void 0 ? void 0 : _params$method.toUpperCase()) || 'GET';
|
|
103
106
|
const proxy = defaults.proxy;
|
|
104
107
|
let agent;
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
if (proxy && !((_defaults$clientCerti = defaults.clientCertificates) !== null && _defaults$clientCerti !== void 0 && _defaults$clientCerti.length) && proxy.server !== 'per-context' && !shouldBypassProxy(requestUrl, proxy.bypass)) {
|
|
109
|
-
var _proxyOpts$protocol;
|
|
110
|
-
const proxyOpts = _url.default.parse(proxy.server);
|
|
111
|
-
if ((_proxyOpts$protocol = proxyOpts.protocol) !== null && _proxyOpts$protocol !== void 0 && _proxyOpts$protocol.startsWith('socks')) {
|
|
112
|
-
agent = new _utilsBundle.SocksProxyAgent({
|
|
113
|
-
host: proxyOpts.hostname,
|
|
114
|
-
port: proxyOpts.port || undefined
|
|
115
|
-
});
|
|
116
|
-
} else {
|
|
117
|
-
if (proxy.username) proxyOpts.auth = `${proxy.username}:${proxy.password || ''}`;
|
|
118
|
-
// TODO: We should use HttpProxyAgent conditional on proxyOpts.protocol instead of always using CONNECT method.
|
|
119
|
-
agent = new _utilsBundle.HttpsProxyAgent(proxyOpts);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
108
|
+
// We skip 'per-context' in order to not break existing users. 'per-context' was previously used to
|
|
109
|
+
// workaround an upstream Chromium bug. Can be removed in the future.
|
|
110
|
+
if (proxy && proxy.server !== 'per-context' && !shouldBypassProxy(requestUrl, proxy.bypass)) agent = createProxyAgent(proxy);
|
|
122
111
|
const timeout = defaults.timeoutSettings.timeout(params);
|
|
123
112
|
const deadline = timeout && (0, _utils.monotonicTime)() + timeout;
|
|
124
113
|
const options = {
|
|
@@ -190,6 +179,7 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
190
179
|
try {
|
|
191
180
|
return await this._sendRequest(progress, url, options, postData);
|
|
192
181
|
} catch (e) {
|
|
182
|
+
e = (0, _socksClientCertificatesInterceptor.rewriteOpenSSLErrorIfNeeded)(e);
|
|
193
183
|
if (maxRetries === 0) throw e;
|
|
194
184
|
if (i === maxRetries || options.deadline && (0, _utils.monotonicTime)() + backoff > options.deadline) throw new Error(`Failed after ${i + 1} attempt(s): ${e}`);
|
|
195
185
|
// Retry on connection reset only.
|
|
@@ -227,8 +217,31 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
227
217
|
...options,
|
|
228
218
|
agent
|
|
229
219
|
};
|
|
220
|
+
const startAt = (0, _utils.monotonicTime)();
|
|
221
|
+
let dnsLookupAt;
|
|
222
|
+
let tcpConnectionAt;
|
|
223
|
+
let tlsHandshakeAt;
|
|
224
|
+
let requestFinishAt;
|
|
225
|
+
let serverIPAddress;
|
|
226
|
+
let serverPort;
|
|
227
|
+
let securityDetails;
|
|
228
|
+
const listeners = [];
|
|
230
229
|
const request = requestConstructor(url, requestOptions, async response => {
|
|
230
|
+
const responseAt = (0, _utils.monotonicTime)();
|
|
231
231
|
const notifyRequestFinished = body => {
|
|
232
|
+
var _tlsHandshakeAt;
|
|
233
|
+
const endAt = (0, _utils.monotonicTime)();
|
|
234
|
+
// spec: http://www.softwareishard.com/blog/har-12-spec/#timings
|
|
235
|
+
const timings = {
|
|
236
|
+
send: requestFinishAt - startAt,
|
|
237
|
+
wait: responseAt - requestFinishAt,
|
|
238
|
+
receive: endAt - responseAt,
|
|
239
|
+
dns: dnsLookupAt ? dnsLookupAt - startAt : -1,
|
|
240
|
+
connect: ((_tlsHandshakeAt = tlsHandshakeAt) !== null && _tlsHandshakeAt !== void 0 ? _tlsHandshakeAt : tcpConnectionAt) - startAt,
|
|
241
|
+
// "If [ssl] is defined then the time is also included in the connect field "
|
|
242
|
+
ssl: tlsHandshakeAt ? tlsHandshakeAt - tcpConnectionAt : -1,
|
|
243
|
+
blocked: -1
|
|
244
|
+
};
|
|
232
245
|
const requestFinishedEvent = {
|
|
233
246
|
requestEvent,
|
|
234
247
|
httpVersion: response.httpVersion,
|
|
@@ -237,7 +250,11 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
237
250
|
headers: response.headers,
|
|
238
251
|
rawHeaders: response.rawHeaders,
|
|
239
252
|
cookies,
|
|
240
|
-
body
|
|
253
|
+
body,
|
|
254
|
+
timings,
|
|
255
|
+
serverIPAddress,
|
|
256
|
+
serverPort,
|
|
257
|
+
securityDetails
|
|
241
258
|
};
|
|
242
259
|
this.emit(APIRequestContext.Events.RequestFinished, requestFinishedEvent);
|
|
243
260
|
};
|
|
@@ -363,13 +380,43 @@ class APIRequestContext extends _instrumentation.SdkObject {
|
|
|
363
380
|
body.on('data', chunk => chunks.push(chunk));
|
|
364
381
|
body.on('end', notifyBodyFinished);
|
|
365
382
|
});
|
|
366
|
-
request.on('error',
|
|
367
|
-
|
|
383
|
+
request.on('error', reject);
|
|
384
|
+
listeners.push(_utils.eventsHelper.addEventListener(this, APIRequestContext.Events.Dispose, () => {
|
|
368
385
|
reject(new Error('Request context disposed.'));
|
|
369
386
|
request.destroy();
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
request.on('
|
|
387
|
+
}));
|
|
388
|
+
request.on('close', () => _utils.eventsHelper.removeEventListeners(listeners));
|
|
389
|
+
request.on('socket', socket => {
|
|
390
|
+
// happy eyeballs don't emit lookup and connect events, so we use our custom ones
|
|
391
|
+
const happyEyeBallsTimings = (0, _happyEyeballs.timingForSocket)(socket);
|
|
392
|
+
dnsLookupAt = happyEyeBallsTimings.dnsLookupAt;
|
|
393
|
+
tcpConnectionAt = happyEyeBallsTimings.tcpConnectionAt;
|
|
394
|
+
|
|
395
|
+
// non-happy-eyeballs sockets
|
|
396
|
+
listeners.push(_utils.eventsHelper.addEventListener(socket, 'lookup', () => {
|
|
397
|
+
dnsLookupAt = (0, _utils.monotonicTime)();
|
|
398
|
+
}), _utils.eventsHelper.addEventListener(socket, 'connect', () => {
|
|
399
|
+
tcpConnectionAt = (0, _utils.monotonicTime)();
|
|
400
|
+
}), _utils.eventsHelper.addEventListener(socket, 'secureConnect', () => {
|
|
401
|
+
tlsHandshakeAt = (0, _utils.monotonicTime)();
|
|
402
|
+
if (socket instanceof _tls.TLSSocket) {
|
|
403
|
+
var _socket$getProtocol;
|
|
404
|
+
const peerCertificate = socket.getPeerCertificate();
|
|
405
|
+
securityDetails = {
|
|
406
|
+
protocol: (_socket$getProtocol = socket.getProtocol()) !== null && _socket$getProtocol !== void 0 ? _socket$getProtocol : undefined,
|
|
407
|
+
subjectName: peerCertificate.subject.CN,
|
|
408
|
+
validFrom: new Date(peerCertificate.valid_from).getTime() / 1000,
|
|
409
|
+
validTo: new Date(peerCertificate.valid_to).getTime() / 1000,
|
|
410
|
+
issuer: peerCertificate.issuer.CN
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}));
|
|
414
|
+
serverIPAddress = socket.remoteAddress;
|
|
415
|
+
serverPort = socket.remotePort;
|
|
416
|
+
});
|
|
417
|
+
request.on('finish', () => {
|
|
418
|
+
requestFinishAt = (0, _utils.monotonicTime)();
|
|
419
|
+
});
|
|
373
420
|
progress.log(`→ ${options.method} ${url.toString()}`);
|
|
374
421
|
if (options.headers) {
|
|
375
422
|
for (const [name, value] of Object.entries(options.headers)) progress.log(` ${name}: ${value}`);
|
|
@@ -470,7 +517,6 @@ class GlobalAPIRequestContext extends APIRequestContext {
|
|
|
470
517
|
let url = proxy === null || proxy === void 0 ? void 0 : proxy.server.trim();
|
|
471
518
|
if (!/^\w+:\/\//.test(url)) url = 'http://' + url;
|
|
472
519
|
proxy.server = url;
|
|
473
|
-
if (options.clientCertificates) throw new Error('Cannot specify both proxy and clientCertificates');
|
|
474
520
|
}
|
|
475
521
|
if (options.storageState) {
|
|
476
522
|
this._origins = options.storageState.origins;
|
|
@@ -515,6 +561,19 @@ class GlobalAPIRequestContext extends APIRequestContext {
|
|
|
515
561
|
}
|
|
516
562
|
}
|
|
517
563
|
exports.GlobalAPIRequestContext = GlobalAPIRequestContext;
|
|
564
|
+
function createProxyAgent(proxy) {
|
|
565
|
+
var _proxyOpts$protocol;
|
|
566
|
+
const proxyOpts = _url.default.parse(proxy.server);
|
|
567
|
+
if ((_proxyOpts$protocol = proxyOpts.protocol) !== null && _proxyOpts$protocol !== void 0 && _proxyOpts$protocol.startsWith('socks')) {
|
|
568
|
+
return new _utilsBundle.SocksProxyAgent({
|
|
569
|
+
host: proxyOpts.hostname,
|
|
570
|
+
port: proxyOpts.port || undefined
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (proxy.username) proxyOpts.auth = `${proxy.username}:${proxy.password || ''}`;
|
|
574
|
+
// TODO: We should use HttpProxyAgent conditional on proxyOpts.protocol instead of always using CONNECT method.
|
|
575
|
+
return new _utilsBundle.HttpsProxyAgent(proxyOpts);
|
|
576
|
+
}
|
|
518
577
|
function toHeadersArray(rawHeaders) {
|
|
519
578
|
const result = [];
|
|
520
579
|
for (let i = 0; i < rawHeaders.length; i += 2) result.push({
|
|
@@ -525,26 +584,9 @@ function toHeadersArray(rawHeaders) {
|
|
|
525
584
|
}
|
|
526
585
|
const redirectStatus = [301, 302, 303, 307, 308];
|
|
527
586
|
function parseCookie(header) {
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
let value = '';
|
|
531
|
-
const separatorPos = p.indexOf('=');
|
|
532
|
-
if (separatorPos === -1) {
|
|
533
|
-
// If only a key is specified, the value is left undefined.
|
|
534
|
-
key = p.trim();
|
|
535
|
-
} else {
|
|
536
|
-
// Otherwise we assume that the key is the element before the first `=`
|
|
537
|
-
key = p.slice(0, separatorPos).trim();
|
|
538
|
-
// And the value is the rest of the string.
|
|
539
|
-
value = p.slice(separatorPos + 1).trim();
|
|
540
|
-
}
|
|
541
|
-
return [key, value];
|
|
542
|
-
});
|
|
543
|
-
if (!pairs.length) return null;
|
|
544
|
-
const [name, value] = pairs[0];
|
|
587
|
+
const raw = (0, _cookieStore.parseRawCookie)(header);
|
|
588
|
+
if (!raw) return null;
|
|
545
589
|
const cookie = {
|
|
546
|
-
name,
|
|
547
|
-
value,
|
|
548
590
|
domain: '',
|
|
549
591
|
path: '',
|
|
550
592
|
expires: -1,
|
|
@@ -552,55 +594,9 @@ function parseCookie(header) {
|
|
|
552
594
|
secure: false,
|
|
553
595
|
// From https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
|
554
596
|
// The cookie-sending behavior if SameSite is not specified is SameSite=Lax.
|
|
555
|
-
sameSite: 'Lax'
|
|
597
|
+
sameSite: 'Lax',
|
|
598
|
+
...raw
|
|
556
599
|
};
|
|
557
|
-
for (let i = 1; i < pairs.length; i++) {
|
|
558
|
-
const [name, value] = pairs[i];
|
|
559
|
-
switch (name.toLowerCase()) {
|
|
560
|
-
case 'expires':
|
|
561
|
-
const expiresMs = +new Date(value);
|
|
562
|
-
// https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.1
|
|
563
|
-
if (isFinite(expiresMs)) {
|
|
564
|
-
if (expiresMs <= 0) cookie.expires = 0;else cookie.expires = Math.min(expiresMs / 1000, _network.kMaxCookieExpiresDateInSeconds);
|
|
565
|
-
}
|
|
566
|
-
break;
|
|
567
|
-
case 'max-age':
|
|
568
|
-
const maxAgeSec = parseInt(value, 10);
|
|
569
|
-
if (isFinite(maxAgeSec)) {
|
|
570
|
-
// From https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.2
|
|
571
|
-
// If delta-seconds is less than or equal to zero (0), let expiry-time
|
|
572
|
-
// be the earliest representable date and time.
|
|
573
|
-
if (maxAgeSec <= 0) cookie.expires = 0;else cookie.expires = Math.min(Date.now() / 1000 + maxAgeSec, _network.kMaxCookieExpiresDateInSeconds);
|
|
574
|
-
}
|
|
575
|
-
break;
|
|
576
|
-
case 'domain':
|
|
577
|
-
cookie.domain = value.toLocaleLowerCase() || '';
|
|
578
|
-
if (cookie.domain && !cookie.domain.startsWith('.') && cookie.domain.includes('.')) cookie.domain = '.' + cookie.domain;
|
|
579
|
-
break;
|
|
580
|
-
case 'path':
|
|
581
|
-
cookie.path = value || '';
|
|
582
|
-
break;
|
|
583
|
-
case 'secure':
|
|
584
|
-
cookie.secure = true;
|
|
585
|
-
break;
|
|
586
|
-
case 'httponly':
|
|
587
|
-
cookie.httpOnly = true;
|
|
588
|
-
break;
|
|
589
|
-
case 'samesite':
|
|
590
|
-
switch (value.toLowerCase()) {
|
|
591
|
-
case 'none':
|
|
592
|
-
cookie.sameSite = 'None';
|
|
593
|
-
break;
|
|
594
|
-
case 'lax':
|
|
595
|
-
cookie.sameSite = 'Lax';
|
|
596
|
-
break;
|
|
597
|
-
case 'strict':
|
|
598
|
-
cookie.sameSite = 'Strict';
|
|
599
|
-
break;
|
|
600
|
-
}
|
|
601
|
-
break;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
600
|
return cookie;
|
|
605
601
|
}
|
|
606
602
|
function serializePostData(params, headers) {
|
|
@@ -53,7 +53,8 @@ class FFBrowser extends _browser.Browser {
|
|
|
53
53
|
browser._defaultContext = new FFBrowserContext(browser, undefined, options.persistent);
|
|
54
54
|
promises.push(browser._defaultContext._initialize());
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
const proxy = options.originalLaunchOptions.proxyOverride || options.proxy;
|
|
57
|
+
if (proxy) promises.push(browser.session.send('Browser.setBrowserProxy', toJugglerProxyOptions(proxy)));
|
|
57
58
|
await Promise.all(promises);
|
|
58
59
|
return browser;
|
|
59
60
|
}
|
|
@@ -205,7 +206,7 @@ class FFBrowserContext extends _browserContext.BrowserContext {
|
|
|
205
206
|
browserContextId,
|
|
206
207
|
bypassCSP: true
|
|
207
208
|
}));
|
|
208
|
-
if (this._options.ignoreHTTPSErrors) promises.push(this._browser.session.send('Browser.setIgnoreHTTPSErrors', {
|
|
209
|
+
if (this._options.ignoreHTTPSErrors || this._options.internalIgnoreHTTPSErrors) promises.push(this._browser.session.send('Browser.setIgnoreHTTPSErrors', {
|
|
209
210
|
browserContextId,
|
|
210
211
|
ignoreHTTPSErrors: true
|
|
211
212
|
}));
|
|
@@ -255,10 +256,11 @@ class FFBrowserContext extends _browserContext.BrowserContext {
|
|
|
255
256
|
});
|
|
256
257
|
}));
|
|
257
258
|
}
|
|
258
|
-
|
|
259
|
+
const proxy = this._options.proxyOverride || this._options.proxy;
|
|
260
|
+
if (proxy) {
|
|
259
261
|
promises.push(this._browser.session.send('Browser.setContextProxy', {
|
|
260
262
|
browserContextId: this._browserContextId,
|
|
261
|
-
...toJugglerProxyOptions(
|
|
263
|
+
...toJugglerProxyOptions(proxy)
|
|
262
264
|
}));
|
|
263
265
|
}
|
|
264
266
|
await Promise.all(promises);
|
|
@@ -101,9 +101,6 @@ class FFExecutionContext {
|
|
|
101
101
|
objectId
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
-
objectCount(objectId) {
|
|
105
|
-
throw new Error('Method not implemented in Firefox.');
|
|
106
|
-
}
|
|
107
104
|
}
|
|
108
105
|
exports.FFExecutionContext = FFExecutionContext;
|
|
109
106
|
function checkException(exceptionDetails) {
|
|
@@ -121,11 +121,11 @@ class FFPage {
|
|
|
121
121
|
const frame = this._page._frameManager.frame(auxData.frameId);
|
|
122
122
|
if (!frame) return;
|
|
123
123
|
const delegate = new _ffExecutionContext.FFExecutionContext(this._session, executionContextId);
|
|
124
|
-
let worldName;
|
|
125
|
-
if (auxData.name === UTILITY_WORLD_NAME) worldName = 'utility';else if (!auxData.name) worldName = 'main';
|
|
124
|
+
let worldName = null;
|
|
125
|
+
if (auxData.name === UTILITY_WORLD_NAME) worldName = 'utility';else if (!auxData.name) worldName = 'main';
|
|
126
126
|
const context = new dom.FrameExecutionContext(delegate, frame, worldName);
|
|
127
127
|
context[contextDelegateSymbol] = delegate;
|
|
128
|
-
frame._contextCreated(worldName, context);
|
|
128
|
+
if (worldName) frame._contextCreated(worldName, context);
|
|
129
129
|
this._contextIdToContext.set(executionContextId, context);
|
|
130
130
|
}
|
|
131
131
|
_onExecutionContextDestroyed(payload) {
|
|
@@ -360,6 +360,9 @@ class FFPage {
|
|
|
360
360
|
});
|
|
361
361
|
return success;
|
|
362
362
|
}
|
|
363
|
+
async requestGC() {
|
|
364
|
+
await this._session.send('Heap.collectGarbage');
|
|
365
|
+
}
|
|
363
366
|
async addInitScript(initScript, worldName) {
|
|
364
367
|
this._initScripts.push({
|
|
365
368
|
initScript,
|
|
@@ -92,19 +92,8 @@ class Firefox extends _browserType.BrowserType {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
exports.Firefox = Firefox;
|
|
95
|
-
class JugglerReadyState {
|
|
96
|
-
constructor() {
|
|
97
|
-
this._jugglerPromise = new _utils.ManualPromise();
|
|
98
|
-
}
|
|
95
|
+
class JugglerReadyState extends _browserType.BrowserReadyState {
|
|
99
96
|
onBrowserOutput(message) {
|
|
100
|
-
if (message.includes('Juggler listening to the pipe')) this.
|
|
101
|
-
}
|
|
102
|
-
onBrowserExit() {
|
|
103
|
-
// Unblock launch when browser prematurely exits.
|
|
104
|
-
this._jugglerPromise.resolve();
|
|
105
|
-
}
|
|
106
|
-
async waitUntilReady() {
|
|
107
|
-
await this._jugglerPromise;
|
|
108
|
-
return {};
|
|
97
|
+
if (message.includes('Juggler listening to the pipe')) this._wsEndpoint.resolve(undefined);
|
|
109
98
|
}
|
|
110
99
|
}
|
|
@@ -165,7 +165,7 @@ class FrameSelectors {
|
|
|
165
165
|
exports.FrameSelectors = FrameSelectors;
|
|
166
166
|
async function adoptIfNeeded(handle, context) {
|
|
167
167
|
if (handle._context === context) return handle;
|
|
168
|
-
const adopted = handle._page._delegate.adoptElementHandle(handle, context);
|
|
168
|
+
const adopted = await handle._page._delegate.adoptElementHandle(handle, context);
|
|
169
169
|
handle.dispose();
|
|
170
170
|
return adopted;
|
|
171
171
|
}
|