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.
Files changed (121) hide show
  1. package/README.md +2 -0
  2. package/browsers.json +14 -10
  3. package/lib/cli/program.js +26 -14
  4. package/lib/client/api.js +6 -0
  5. package/lib/client/browserContext.js +20 -2
  6. package/lib/client/channelOwner.js +5 -1
  7. package/lib/client/connection.js +3 -0
  8. package/lib/client/fetch.js +16 -21
  9. package/lib/client/jsHandle.js +0 -8
  10. package/lib/client/localUtils.js +1 -0
  11. package/lib/client/network.js +175 -17
  12. package/lib/client/page.js +21 -0
  13. package/lib/client/playwright.js +6 -3
  14. package/lib/client/tracing.js +16 -20
  15. package/lib/generated/consoleApiSource.js +1 -1
  16. package/lib/generated/injectedScriptSource.js +1 -1
  17. package/lib/generated/pollingRecorderSource.js +7 -0
  18. package/lib/generated/webSocketMockSource.js +7 -0
  19. package/lib/protocol/validator.js +80 -14
  20. package/lib/server/bidi/bidiBrowser.js +23 -8
  21. package/lib/server/bidi/bidiChromium.js +124 -0
  22. package/lib/server/bidi/bidiConnection.js +1 -1
  23. package/lib/server/bidi/bidiExecutionContext.js +0 -3
  24. package/lib/server/bidi/bidiFirefox.js +15 -21
  25. package/lib/server/bidi/bidiInput.js +16 -32
  26. package/lib/server/bidi/bidiNetworkManager.js +39 -5
  27. package/lib/server/bidi/bidiOverCdp.js +103 -0
  28. package/lib/server/bidi/bidiPage.js +98 -25
  29. package/lib/server/bidi/bidiPdf.js +140 -0
  30. package/lib/server/bidi/third_party/firefoxPrefs.js +221 -0
  31. package/lib/server/browser.js +13 -2
  32. package/lib/server/browserContext.js +6 -23
  33. package/lib/server/browserType.js +39 -11
  34. package/lib/server/chromium/chromium.js +3 -15
  35. package/lib/server/chromium/chromiumSwitches.js +3 -1
  36. package/lib/server/chromium/crBrowser.js +4 -3
  37. package/lib/server/chromium/crExecutionContext.js +0 -7
  38. package/lib/server/chromium/crPage.js +16 -6
  39. package/lib/server/chromium/videoRecorder.js +1 -1
  40. package/lib/server/codegen/csharp.js +1 -1
  41. package/lib/server/codegen/java.js +1 -1
  42. package/lib/server/codegen/javascript.js +1 -1
  43. package/lib/server/codegen/language.js +14 -2
  44. package/lib/server/codegen/python.js +1 -1
  45. package/lib/server/cookieStore.js +73 -0
  46. package/lib/server/debugController.js +2 -2
  47. package/lib/server/deviceDescriptorsSource.json +50 -50
  48. package/lib/server/dialog.js +1 -0
  49. package/lib/server/dispatchers/browserContextDispatcher.js +19 -2
  50. package/lib/server/dispatchers/jsHandleDispatcher.js +0 -5
  51. package/lib/server/dispatchers/pageDispatcher.js +9 -0
  52. package/lib/server/dispatchers/playwrightDispatcher.js +2 -1
  53. package/lib/server/dispatchers/webSocketRouteDispatcher.js +189 -0
  54. package/lib/server/download.js +9 -2
  55. package/lib/server/fetch.js +90 -94
  56. package/lib/server/firefox/ffBrowser.js +6 -4
  57. package/lib/server/firefox/ffExecutionContext.js +0 -3
  58. package/lib/server/firefox/ffPage.js +6 -3
  59. package/lib/server/firefox/firefox.js +2 -13
  60. package/lib/server/frameSelectors.js +1 -1
  61. package/lib/server/frames.js +164 -28
  62. package/lib/server/har/harTracer.js +11 -0
  63. package/lib/server/input.js +0 -1
  64. package/lib/server/javascript.js +0 -7
  65. package/lib/server/page.js +5 -1
  66. package/lib/server/playwright.js +5 -2
  67. package/lib/server/recorder/contextRecorder.js +33 -50
  68. package/lib/server/recorder/recorderApp.js +16 -10
  69. package/lib/server/recorder/recorderCollection.js +68 -79
  70. package/lib/server/recorder/recorderInTraceViewer.js +144 -0
  71. package/lib/server/recorder/recorderRunner.js +75 -97
  72. package/lib/server/recorder/recorderUtils.js +47 -6
  73. package/lib/server/recorder.js +30 -25
  74. package/lib/server/registry/index.js +85 -4
  75. package/lib/server/socksClientCertificatesInterceptor.js +15 -3
  76. package/lib/server/trace/recorder/snapshotter.js +1 -0
  77. package/lib/server/trace/recorder/snapshotterInjected.js +2 -2
  78. package/lib/server/trace/recorder/tracing.js +58 -2
  79. package/lib/server/trace/test/inMemorySnapshotter.js +1 -1
  80. package/lib/server/trace/viewer/traceViewer.js +2 -5
  81. package/lib/server/webkit/webkit.js +1 -1
  82. package/lib/server/webkit/wkBrowser.js +6 -5
  83. package/lib/server/webkit/wkExecutionContext.js +0 -3
  84. package/lib/server/webkit/wkPage.js +7 -4
  85. package/lib/utils/happy-eyeballs.js +13 -0
  86. package/lib/utils/hostPlatform.js +2 -2
  87. package/lib/utils/httpServer.js +1 -0
  88. package/lib/utils/isomorphic/locatorGenerators.js +9 -18
  89. package/lib/utils/isomorphic/locatorParser.js +2 -2
  90. package/lib/utils/isomorphic/recorderUtils.js +195 -0
  91. package/lib/vite/htmlReport/index.html +12 -12
  92. package/lib/vite/recorder/assets/{codeMirrorModule-aW6Vz3gf.js → codeMirrorModule-baozm8ur.js} +1 -1
  93. package/lib/vite/recorder/assets/{index-DCpXOVIh.js → index-2ElAIWFB.js} +10 -15
  94. package/lib/vite/recorder/assets/{index-B-MT5gKo.css → index-BW-aOBcL.css} +1 -1
  95. package/lib/vite/recorder/index.html +2 -2
  96. package/lib/vite/traceViewer/assets/{codeMirrorModule-eFKCUIu2.js → codeMirrorModule-Bh1rfd2w.js} +1 -1
  97. package/lib/vite/traceViewer/assets/inspectorTab-7GHnKvSD.js +64 -0
  98. package/lib/vite/traceViewer/assets/workbench-DPQnTHYP.js +9 -0
  99. package/lib/vite/traceViewer/embedded.BlHoW5LY.js +2 -0
  100. package/lib/vite/traceViewer/embedded.html +5 -3
  101. package/lib/vite/traceViewer/index.DaWVfou1.js +2 -0
  102. package/lib/vite/traceViewer/index.html +5 -3
  103. package/lib/vite/traceViewer/inspectorTab.DLjBDrQR.css +1 -0
  104. package/lib/vite/traceViewer/recorder.B_SY1GJM.css +0 -0
  105. package/lib/vite/traceViewer/recorder.C4zxcvd2.js +2 -0
  106. package/lib/vite/traceViewer/recorder.html +17 -0
  107. package/lib/vite/traceViewer/sw.bundle.js +3 -3
  108. package/lib/vite/traceViewer/{uiMode.D3cNFP6u.css → uiMode.CAYqod-m.css} +1 -1
  109. package/lib/vite/traceViewer/uiMode.html +6 -4
  110. package/lib/vite/traceViewer/uiMode.mTXWniJb.js +5 -0
  111. package/lib/vite/traceViewer/workbench.D3JVcA9K.css +1 -0
  112. package/package.json +1 -1
  113. package/types/protocol.d.ts +79 -8
  114. package/types/types.d.ts +2037 -949
  115. package/lib/generated/recorderSource.js +0 -7
  116. package/lib/vite/traceViewer/assets/workbench-D0cirtfA.js +0 -72
  117. package/lib/vite/traceViewer/embedded.BkuvsLJp.js +0 -2
  118. package/lib/vite/traceViewer/index.C968kAiC.js +0 -2
  119. package/lib/vite/traceViewer/uiMode.BkFnr4V0.js +0 -10
  120. package/lib/vite/traceViewer/workbench.DjbIuxix.css +0 -1
  121. /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
+ }
@@ -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._page.emit(_page.Page.Events.Download, 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._page.emit(_page.Page.Events.Download, 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;
@@ -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, _defaults$clientCerti;
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.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
- // When `clientCertificates` is present, we set the `proxy` property to our own socks proxy
106
- // for the browser to use. However, we don't need it here, because we already respect
107
- // `clientCertificates` when fetching from Node.js.
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', error => reject((0, _socksClientCertificatesInterceptor.rewriteOpenSSLErrorIfNeeded)(error)));
367
- const disposeListener = () => {
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
- this.on(APIRequestContext.Events.Dispose, disposeListener);
372
- request.on('close', () => this.off(APIRequestContext.Events.Dispose, disposeListener));
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 pairs = header.split(';').filter(s => s.trim().length > 0).map(p => {
529
- let key = '';
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
- if (options.proxy) promises.push(browser.session.send('Browser.setBrowserProxy', toJugglerProxyOptions(options.proxy)));
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
- if (this._options.proxy) {
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(this._options.proxy)
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';else return;
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._jugglerPromise.resolve();
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
  }