phantomwright-driver-core 1.58.2 → 1.58.3

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.
@@ -120,11 +120,11 @@ class BrowserContext extends import_channelOwner.ChannelOwner {
120
120
  hasListeners = page.emit(import_events.Events.Page.Dialog, dialogObject) || hasListeners;
121
121
  if (!hasListeners) {
122
122
  if (dialogObject.type() === "beforeunload")
123
- dialog.accept({}).catch(() => {
124
- });
123
+ dialogObject._wrapApiCall(() => dialog.accept({}).catch(() => {
124
+ }), { internal: true });
125
125
  else
126
- dialog.dismiss().catch(() => {
127
- });
126
+ dialogObject._wrapApiCall(() => dialog.dismiss().catch(() => {
127
+ }), { internal: true });
128
128
  }
129
129
  });
130
130
  this._channel.on("request", ({ request, page }) => this._onRequest(network.Request.from(request), import_page.Page.fromNullable(page)));
@@ -527,6 +527,7 @@ class BrowserContext extends import_channelOwner.ChannelOwner {
527
527
  await route.fallback();
528
528
  }
529
529
  });
530
+ this.routeInjecting = true;
530
531
  }
531
532
  }
532
533
  async function prepareStorageState(platform, storageState) {
@@ -162,7 +162,15 @@ class Frame extends import_channelOwner.ChannelOwner {
162
162
  async waitForURL(url, options = {}) {
163
163
  if ((0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, this.url(), url))
164
164
  return await this.waitForLoadState(options.waitUntil, options);
165
- await this.waitForNavigation({ url, ...options });
165
+ try {
166
+ await this.waitForNavigation({ url, ...options });
167
+ } catch (error) {
168
+ if ((0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, this.url(), url)) {
169
+ await this.waitForLoadState(options.waitUntil, options);
170
+ return;
171
+ }
172
+ throw error;
173
+ }
166
174
  }
167
175
  async frameElement() {
168
176
  return import_elementHandle.ElementHandle.from((await this._channel.frameElement()).element);
@@ -99,6 +99,10 @@ class Locator {
99
99
  });
100
100
  }
101
101
  async evaluate(pageFunction, arg, options, isolatedContext = true) {
102
+ if (typeof options === "boolean") {
103
+ isolatedContext = options;
104
+ options = void 0;
105
+ }
102
106
  return await this._withElement(
103
107
  async (h) => (0, import_jsHandle.parseResult)(
104
108
  (await h._channel.evaluateExpression({
@@ -118,6 +122,10 @@ class Locator {
118
122
  return await this._frame.$$eval(this._selector, pageFunction, arg, isolatedContext);
119
123
  }
120
124
  async evaluateHandle(pageFunction, arg, options, isolatedContext = true) {
125
+ if (typeof options === "boolean") {
126
+ isolatedContext = options;
127
+ options = void 0;
128
+ }
121
129
  return await this._withElement(
122
130
  async (h) => import_jsHandle.JSHandle.from(
123
131
  (await h._channel.evaluateExpressionHandle({
@@ -128,7 +128,11 @@ class Request extends import_channelOwner.ChannelOwner {
128
128
  return await this._actualHeadersPromise;
129
129
  }
130
130
  async allHeaders() {
131
- return (await this._actualHeaders()).headers();
131
+ const headers = await this._actualHeaders();
132
+ const page = this._safePage();
133
+ if (page?._closeWasCalled)
134
+ throw new import_errors.TargetClosedError();
135
+ return headers.headers();
132
136
  }
133
137
  async headersArray() {
134
138
  return (await this._actualHeaders()).headersArray();
@@ -721,6 +721,7 @@ class Page extends import_channelOwner.ChannelOwner {
721
721
  await route.fallback();
722
722
  }
723
723
  });
724
+ this.routeInjecting = true;
724
725
  }
725
726
  }
726
727
  class BindingCall extends import_channelOwner.ChannelOwner {
@@ -34,7 +34,7 @@ class Tracing extends import_channelOwner.ChannelOwner {
34
34
  return channel._object;
35
35
  }
36
36
  async start(options = {}) {
37
- await this._parent.installInjectRoute();
37
+ if (typeof this._parent.installInjectRoute === "function") await this._parent.installInjectRoute();
38
38
  await this._wrapApiCall(async () => {
39
39
  this._includeSources = !!options.sources;
40
40
  this._isLive = !!options._live;
@@ -277,7 +277,6 @@ class Chromium extends import_browserType.BrowserType {
277
277
  if (args.find((arg) => !arg.startsWith("-")))
278
278
  throw new Error("Arguments can not specify page to be opened");
279
279
  const chromeArguments = [...(0, import_chromiumSwitches.chromiumSwitches)(options.assistantMode, options.channel)];
280
- chromeArguments.push("--enable-unsafe-swiftshader");
281
280
  if (options.headless) {
282
281
  chromeArguments.push("--headless");
283
282
  chromeArguments.push(
@@ -66,6 +66,7 @@ class JSCoverage {
66
66
  this._eventListeners = [
67
67
  import_eventsHelper.eventsHelper.addEventListener(this._client, "Debugger.scriptParsed", this._onScriptParsed.bind(this)),
68
68
  import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)),
69
+ import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.frameNavigated", this._onFrameNavigated.bind(this)),
69
70
  import_eventsHelper.eventsHelper.addEventListener(this._client, "Debugger.paused", this._onDebuggerPaused.bind(this))
70
71
  ];
71
72
  await Promise.all([
@@ -117,6 +118,11 @@ class JSCoverage {
117
118
  }
118
119
  return coverage;
119
120
  }
121
+ _onFrameNavigated(event) {
122
+ if (event.frame.parentId)
123
+ return;
124
+ this._onExecutionContextsCleared();
125
+ }
120
126
  }
121
127
  class CSSCoverage {
122
128
  constructor(client) {
@@ -136,7 +142,8 @@ class CSSCoverage {
136
142
  this._stylesheetSources.clear();
137
143
  this._eventListeners = [
138
144
  import_eventsHelper.eventsHelper.addEventListener(this._client, "CSS.styleSheetAdded", this._onStyleSheet.bind(this)),
139
- import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this))
145
+ import_eventsHelper.eventsHelper.addEventListener(this._client, "Runtime.executionContextsCleared", this._onExecutionContextsCleared.bind(this)),
146
+ import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.frameNavigated", this._onFrameNavigated.bind(this))
140
147
  ];
141
148
  await Promise.all([
142
149
  this._client.send("DOM.enable"),
@@ -192,6 +199,11 @@ class CSSCoverage {
192
199
  }
193
200
  return coverage;
194
201
  }
202
+ _onFrameNavigated(event) {
203
+ if (event.frame.parentId)
204
+ return;
205
+ this._onExecutionContextsCleared();
206
+ }
195
207
  }
196
208
  function convertToDisjointRanges(nestedRanges) {
197
209
  const points = [];
@@ -141,6 +141,8 @@ class CRNetworkManager {
141
141
  async setRequestInterception(value) {
142
142
  this._userRequestInterceptionEnabled = value;
143
143
  await this._updateProtocolRequestInterception();
144
+ if (this._page)
145
+ await this._forEachSession((info) => info.session.send("Network.setCacheDisabled", { cacheDisabled: this._page.needsRequestInterception() }));
144
146
  }
145
147
  async _updateProtocolRequestInterception() {
146
148
  const enabled = this._userRequestInterceptionEnabled || !!this._credentials;
@@ -153,7 +155,9 @@ class CRNetworkManager {
153
155
  const enabled = this._protocolRequestInterceptionEnabled;
154
156
  if (initial && !enabled)
155
157
  return;
156
- const cachePromise = info.session.send("Network.setCacheDisabled", { cacheDisabled: false });
158
+ const hasHarRecorders = !!this._page?.browserContext?._harRecorders?.size;
159
+ const userInterception = this._page ? this._page.needsRequestInterception() : false;
160
+ const cachePromise = info.session.send("Network.setCacheDisabled", { cacheDisabled: userInterception || hasHarRecorders });
157
161
  let fetchPromise = Promise.resolve(void 0);
158
162
  if (!info.workerFrame) {
159
163
  if (enabled)
@@ -265,13 +269,17 @@ class CRNetworkManager {
265
269
  redirectedFrom = request2;
266
270
  }
267
271
  }
272
+ const isInterceptedOptionsPreflight = !!requestPausedEvent && requestPausedEvent.request.method === "OPTIONS" && requestWillBeSentEvent.initiator.type === "preflight";
273
+ if (isInterceptedOptionsPreflight && !(this._page || this._serviceWorker).needsRequestInterception()) {
274
+ requestPausedSessionInfo.session._sendMayFail("Fetch.continueRequest", { requestId: requestPausedEvent.requestId });
275
+ return;
276
+ }
268
277
  let frame = requestWillBeSentEvent.frameId ? this._page?.frameManager.frame(requestWillBeSentEvent.frameId) : requestWillBeSentSessionInfo.workerFrame;
269
278
  if (!frame && this._page && requestPausedEvent && requestPausedEvent.frameId)
270
279
  frame = this._page.frameManager.frame(requestPausedEvent.frameId);
271
280
  if (!frame && this._page && requestWillBeSentEvent.frameId === (this._page?.delegate)._targetId) {
272
281
  frame = this._page.frameManager.frameAttached(requestWillBeSentEvent.frameId, null);
273
282
  }
274
- const isInterceptedOptionsPreflight = !!requestPausedEvent && requestPausedEvent.request.method === "OPTIONS" && requestWillBeSentEvent.initiator.type === "preflight";
275
283
  if (isInterceptedOptionsPreflight && (this._page || this._serviceWorker).needsRequestInterception()) {
276
284
  const requestHeaders = requestPausedEvent.request.headers;
277
285
  const responseHeaders = [
@@ -518,7 +526,7 @@ class RouteImpl {
518
526
  method: overrides.method,
519
527
  postData: overrides.postData ? overrides.postData.toString("base64") : void 0
520
528
  };
521
- if (overrides.url && (overrides.url === "http://patchright-init-script-inject.internal/" || overrides.url === "https://patchright-init-script-inject.internal/")) {
529
+ if (overrides.url && (overrides.url.startsWith("http://patchright-init-script-inject.internal") || overrides.url.startsWith("https://patchright-init-script-inject.internal"))) {
522
530
  await catchDisallowedErrors(async () => {
523
531
  this._sessionManager._alreadyTrackedNetworkIds.add(this._networkId);
524
532
  this._session._sendMayFail("Fetch.continueRequest", { requestId: this._interceptionId, interceptResponse: true });
@@ -531,8 +539,12 @@ class RouteImpl {
531
539
  }
532
540
  async fulfill(response) {
533
541
  const isTextHtml = response.headers.some((header) => header.name.toLowerCase() === "content-type" && header.value.includes("text/html"));
534
- var allInjections = [...this._page.delegate._mainFrameSession._evaluateOnNewDocumentScripts];
535
- if (isTextHtml && allInjections.length) {
542
+ const pageDelegate = this._page?.delegate || null;
543
+ const initScriptTag = pageDelegate?.initScriptTag || "";
544
+ let allInjections = [];
545
+ if (pageDelegate)
546
+ allInjections = [...pageDelegate._mainFrameSession._evaluateOnNewDocumentScripts];
547
+ if (isTextHtml && allInjections.length && initScriptTag) {
536
548
  let useNonce = false;
537
549
  let scriptNonce = null;
538
550
  if (response.isBase64) {
@@ -583,7 +595,7 @@ class RouteImpl {
583
595
  let scriptId = import_crypto.default.randomBytes(22).toString("hex");
584
596
  let scriptSource = script.source || script;
585
597
  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>`;
598
+ injectionHTML += `<script class="${initScriptTag}" ${nonceAttr} id="${scriptId}" type="text/javascript">document.getElementById("${scriptId}")?.remove();${scriptSource}</script>`;
587
599
  });
588
600
  const lower = response.body.toLowerCase();
589
601
  const headStartIndex = lower.indexOf("<head");
@@ -771,7 +783,11 @@ class RouteImpl {
771
783
  });
772
784
  }
773
785
  } catch (error) {
774
- await this._session._sendMayFail("Fetch.continueRequest", { requestId: event.requestId });
786
+ if (error.message.includes("Can only get response body on HeadersReceived pattern matched requests.")) {
787
+ await this._session.send("Fetch.continueRequest", { requestId: event.requestId, interceptResponse: true });
788
+ } else {
789
+ await this._session._sendMayFail("Fetch.continueRequest", { requestId: event.requestId });
790
+ }
775
791
  }
776
792
  }
777
793
  }
@@ -110,7 +110,7 @@ class CRPage {
110
110
  while (!this._sessions.has(frame._id)) {
111
111
  const parent = frame.parentFrame();
112
112
  if (!parent)
113
- throw new Error(`Frame has been detached.`);
113
+ throw new Error(`Frame was detached`);
114
114
  frame = parent;
115
115
  }
116
116
  return this._sessions.get(frame._id);
@@ -368,6 +368,7 @@ class FrameSession {
368
368
  ]);
369
369
  }
370
370
  async _initialize(hasUIWindow) {
371
+ const pageEnablePromise = this._client.send("Page.enable");
371
372
  if (!this._page.isStorageStatePage && hasUIWindow && !this._crPage._browserContext._browser.isClank() && !this._crPage._browserContext._options.noDefaultViewport) {
372
373
  const { windowId } = await this._client.send("Browser.getWindowForTarget");
373
374
  this._windowId = windowId;
@@ -378,14 +379,21 @@ class FrameSession {
378
379
  let lifecycleEventsEnabled;
379
380
  if (!this._isMainFrame())
380
381
  this._addRendererListeners();
382
+ let bufferedDialogEvents = this._isMainFrame() ? [] : void 0;
383
+ if (bufferedDialogEvents)
384
+ this._eventListeners.push(import_eventsHelper.eventsHelper.addEventListener(this._client, "Page.javascriptDialogOpening", (event) => bufferedDialogEvents ? bufferedDialogEvents.push(event) : void 0));
381
385
  this._addBrowserListeners();
382
386
  this._bufferedAttachedToTargetEvents = [];
383
387
  const promises = [
384
- this._client.send("Page.enable"),
388
+ pageEnablePromise,
385
389
  this._client.send("Page.getFrameTree").then(({ frameTree }) => {
386
390
  if (this._isMainFrame()) {
387
391
  this._handleFrameTree(frameTree);
388
392
  this._addRendererListeners();
393
+ const pendingDialogEvents = bufferedDialogEvents || [];
394
+ bufferedDialogEvents = void 0;
395
+ for (const event of pendingDialogEvents)
396
+ this._onDialog(event);
389
397
  }
390
398
  const attachedToTargetEvents = this._bufferedAttachedToTargetEvents || [];
391
399
  this._bufferedAttachedToTargetEvents = void 0;
@@ -400,12 +408,16 @@ class FrameSession {
400
408
  } else {
401
409
  const localFrames = this._isMainFrame() ? this._page.frames() : [this._page.frameManager.frame(this._targetId)];
402
410
  for (const frame of localFrames) {
403
- this._page.frameManager.frame(frame._id)._context("utility");
411
+ this._page.frameManager.frame(frame._id)._context("utility").catch(() => {
412
+ });
404
413
  for (const binding of this._crPage._browserContext._pageBindings.values())
405
414
  frame.evaluateExpression(binding.source).catch((e) => {
406
415
  });
407
416
  for (const source of this._crPage._browserContext.initScripts)
408
- frame.evaluateExpression(source).catch((e) => {
417
+ frame.evaluateExpression(source.source).catch((e) => {
418
+ });
419
+ for (const source of this._crPage._page.initScripts)
420
+ frame.evaluateExpression(source.source).catch((e) => {
409
421
  });
410
422
  }
411
423
  this._firstNonInitialNavigationCommittedFulfill();
@@ -484,6 +496,10 @@ class FrameSession {
484
496
  this._page.frameManager.frameLifecycleEvent(event.frameId, "load");
485
497
  else if (event.name === "DOMContentLoaded")
486
498
  this._page.frameManager.frameLifecycleEvent(event.frameId, "domcontentloaded");
499
+ if (event.name !== "load") {
500
+ await this._client._sendMayFail("Runtime.runIfWaitingForDebugger");
501
+ return;
502
+ }
487
503
  await this._client._sendMayFail("Runtime.runIfWaitingForDebugger");
488
504
  var document = await this._client._sendMayFail("DOM.getDocument");
489
505
  if (!document) return;
@@ -536,6 +552,14 @@ class FrameSession {
536
552
  if (!initial)
537
553
  this._firstNonInitialNavigationCommittedFulfill();
538
554
  await this._client._sendMayFail("Runtime.runIfWaitingForDebugger");
555
+ if (!initial) {
556
+ try {
557
+ await this._page.frameManager.frame(this._targetId)._context("utility");
558
+ } catch {
559
+ }
560
+ ;
561
+ return;
562
+ }
539
563
  var document = await this._client._sendMayFail("DOM.getDocument");
540
564
  if (!document) return;
541
565
  var query = await this._client._sendMayFail("DOM.querySelectorAll", {
@@ -584,7 +608,7 @@ class FrameSession {
584
608
  const delegate = new import_crExecutionContext.CRExecutionContext(this._client, contextPayload);
585
609
  let worldName = contextPayload.name;
586
610
  const context = new dom.FrameExecutionContext(delegate, frame, worldName);
587
- if (worldName)
611
+ if (worldName && (worldName === "main" || worldName === "utility"))
588
612
  frame._contextCreated(worldName, context);
589
613
  this._contextIdToContext.set(contextPayload.id, context);
590
614
  for (const source of this._exposedBindingScripts) {
@@ -978,28 +1002,20 @@ class FrameSession {
978
1002
  return (0, import_crExecutionContext.createHandle)(to, result.object).asElement();
979
1003
  }
980
1004
  async _initBinding(binding = import_page.PageBinding) {
981
- var result = await this._client._sendMayFail("Page.createIsolatedWorld", {
982
- frameId: this._targetId,
983
- grantUniveralAccess: true,
984
- worldName: "utility"
985
- });
986
- if (!result) return;
987
- var isolatedContextId = result.executionContextId;
988
- var globalThis = await this._client._sendMayFail("Runtime.evaluate", {
989
- expression: "globalThis",
990
- serializationOptions: { serialization: "idOnly" }
991
- });
992
- if (!globalThis) return;
993
- var globalThisObjId = globalThis["result"]["objectId"];
994
- var mainContextId = parseInt(globalThisObjId.split(".")[1], 10);
1005
+ this._exposedBindingNames.push(binding.name);
1006
+ this._exposedBindingScripts.push(binding.source);
1007
+ const contextIds = Array.from(this._contextIdToContext.keys());
995
1008
  await Promise.all([
996
1009
  this._client._sendMayFail("Runtime.addBinding", { name: binding.name }),
997
- this._client._sendMayFail("Runtime.addBinding", { name: binding.name, executionContextId: mainContextId }),
998
- this._client._sendMayFail("Runtime.addBinding", { name: binding.name, executionContextId: isolatedContextId })
999
- // this._client._sendMayFail("Runtime.evaluate", { expression: binding.source, contextId: mainContextId, awaitPromise: true })
1010
+ ...contextIds.map((executionContextId) => this._client._sendMayFail("Runtime.addBinding", { name: binding.name, executionContextId }))
1000
1011
  ]);
1001
- this._exposedBindingNames.push(binding.name);
1002
- this._exposedBindingScripts.push(binding.source);
1012
+ for (const contextId of contextIds) {
1013
+ this._client._sendMayFail("Runtime.evaluate", {
1014
+ expression: binding.source,
1015
+ contextId,
1016
+ awaitPromise: true
1017
+ });
1018
+ }
1003
1019
  }
1004
1020
  async _removeExposedBindings() {
1005
1021
  const toRetain = [];
@@ -116,8 +116,6 @@ class BrowserContextDispatcher extends import_dispatcher.Dispatcher {
116
116
  }
117
117
  });
118
118
  this._dialogHandler = (dialog) => {
119
- if (!this._shouldDispatchEvent(dialog.page(), "dialog"))
120
- return false;
121
119
  this._dispatchEvent("dialog", { dialog: new import_dialogDispatcher.DialogDispatcher(this, dialog) });
122
120
  return true;
123
121
  };
@@ -304,6 +304,11 @@ class FrameSelectors {
304
304
  const childIndex = this._findElementPositionInDomTree(element, shadowRootHandle, documentScope, currentIndex);
305
305
  if (childIndex !== null) return childIndex;
306
306
  }
307
+ for (const shadowChild of shadowRoot.children || []) {
308
+ const shadowChildIndex = (shadowRoot.children || []).indexOf(shadowChild);
309
+ const childIndex = this._findElementPositionInDomTree(element, shadowChild, documentScope, currentIndex + "." + shadowChildIndex.toString());
310
+ if (childIndex !== null) return childIndex;
311
+ }
307
312
  }
308
313
  }
309
314
  return null;
@@ -457,7 +457,11 @@ class Frame extends import_instrumentation.SdkObject {
457
457
  });
458
458
  }
459
459
  nonStallingEvaluateInExistingContext(expression, world) {
460
- return this.raceAgainstEvaluationStallingEvents(() => {
460
+ return this.raceAgainstEvaluationStallingEvents(async () => {
461
+ try {
462
+ await this._context(world);
463
+ } catch {
464
+ }
461
465
  const context = this._contextData.get(world)?.context;
462
466
  if (!context)
463
467
  throw new Error("Frame does not yet have the execution context");
@@ -596,7 +600,7 @@ class Frame extends import_instrumentation.SdkObject {
596
600
  var executionContextId = iframeExecutionContextId;
597
601
  var crContext = new import_crExecutionContext.CRExecutionContext(client, { id: executionContextId }, this._id);
598
602
  this._iframeWorld = new import_dom.FrameExecutionContext(crContext, this, world);
599
- this._page.delegate._mainFrameSession._onExecutionContextCreated({
603
+ this._page.delegate._sessionForFrame(this)._onExecutionContextCreated({
600
604
  id: executionContextId,
601
605
  origin: world,
602
606
  name: world,
@@ -608,13 +612,14 @@ class Frame extends import_instrumentation.SdkObject {
608
612
  serializationOptions: { serialization: "idOnly" }
609
613
  });
610
614
  if (!globalThis2) {
615
+ if (this.isDetached()) throw new Error("Frame was detached");
611
616
  return;
612
617
  }
613
618
  var globalThisObjId = globalThis2["result"]["objectId"];
614
619
  var executionContextId = parseInt(globalThisObjId.split(".")[1], 10);
615
620
  var crContext = new import_crExecutionContext.CRExecutionContext(client, { id: executionContextId }, this._id);
616
621
  this._mainWorld = new import_dom.FrameExecutionContext(crContext, this, world);
617
- this._page.delegate._mainFrameSession._onExecutionContextCreated({
622
+ this._page.delegate._sessionForFrame(this)._onExecutionContextCreated({
618
623
  id: executionContextId,
619
624
  origin: world,
620
625
  name: world,
@@ -630,12 +635,13 @@ class Frame extends import_instrumentation.SdkObject {
630
635
  worldName: world
631
636
  });
632
637
  if (!result) {
638
+ if (this.isDetached()) throw new Error("Frame was detached");
633
639
  return;
634
640
  }
635
641
  var executionContextId = result.executionContextId;
636
642
  var crContext = new import_crExecutionContext.CRExecutionContext(client, { id: executionContextId }, this._id);
637
643
  this._isolatedWorld = new import_dom.FrameExecutionContext(crContext, this, world);
638
- this._page.delegate._mainFrameSession._onExecutionContextCreated({
644
+ this._page.delegate._sessionForFrame(this)._onExecutionContextCreated({
639
645
  id: executionContextId,
640
646
  origin: world,
641
647
  name: world,
@@ -644,7 +650,7 @@ class Frame extends import_instrumentation.SdkObject {
644
650
  }
645
651
  if (world != "main") {
646
652
  return this._isolatedWorld;
647
- } else if (this != this._page.mainFrame() && iframeExecutionContextId) {
653
+ } else if (this != this._page.mainFrame() && this._iframeWorld) {
648
654
  return this._iframeWorld;
649
655
  } else {
650
656
  return this._mainWorld;
@@ -660,13 +666,13 @@ class Frame extends import_instrumentation.SdkObject {
660
666
  return this._context("utility");
661
667
  }
662
668
  async evaluateExpression(expression, options = {}, arg) {
663
- const context = await this._context(options.world ?? "main");
664
- const value = await context.evaluateExpression(expression, options, arg);
669
+ const context = await this._detachedScope.race(this._context(options.world ?? "main"));
670
+ const value = await this._detachedScope.race(context.evaluateExpression(expression, options, arg));
665
671
  return value;
666
672
  }
667
673
  async evaluateExpressionHandle(expression, options = {}, arg) {
668
- const context = await this._context(options.world ?? "utility");
669
- const value = await context.evaluateExpressionHandle(expression, options, arg);
674
+ const context = await this._detachedScope.race(this._context(options.world ?? "utility"));
675
+ const value = await this._detachedScope.race(context.evaluateExpressionHandle(expression, options, arg));
670
676
  return value;
671
677
  }
672
678
  async querySelector(selector, options) {
@@ -688,7 +694,15 @@ class Frame extends import_instrumentation.SdkObject {
688
694
  throw new Error(`state: expected one of (attached|detached|visible|hidden)`);
689
695
  if (performActionPreChecksAndLog)
690
696
  progress.log(`waiting for ${this._asLocator(selector)}${state === "attached" ? "" : " to be " + state}`);
691
- const promise = this._retryWithProgressIfNotConnected(progress, selector, options.strict, true, async (handle) => {
697
+ const promise = this._retryWithProgressIfNotConnected(progress, selector, { ...options, performActionPreChecks: true, __patchrightWaitForSelector: true, __patchrightInitialScope: scope }, async (handle) => {
698
+ if (scope) {
699
+ const scopeIsConnected = await scope.evaluateInUtility(([injected, node]) => node.isConnected, {}).catch(() => false);
700
+ if (scopeIsConnected !== true) {
701
+ if (state === "hidden" || state === "detached")
702
+ return null;
703
+ throw new dom.NonRecoverableDOMError("Element is not attached to the DOM");
704
+ }
705
+ }
692
706
  const attached = !!handle;
693
707
  var visible = false;
694
708
  if (attached) {
@@ -719,12 +733,50 @@ class Frame extends import_instrumentation.SdkObject {
719
733
  return "internal:continuepolling";
720
734
  }
721
735
  }, "returnOnNotResolved");
722
- return scope ? scope._context._raceAgainstContextDestroyed(promise) : promise;
736
+ const resultPromise = scope ? scope._context._raceAgainstContextDestroyed(promise) : promise;
737
+ return resultPromise.catch((e) => {
738
+ if (this.isDetached() && e?.message?.includes("Execution context was destroyed"))
739
+ throw new Error("Frame was detached");
740
+ throw e;
741
+ });
723
742
  }
724
743
  async dispatchEvent(progress, selector, type, eventInit = {}, options, scope) {
725
- await this._callOnElementOnceMatches(progress, selector, (injectedScript, element, data) => {
744
+ const eventInitHandles = [];
745
+ const visited = /* @__PURE__ */ new WeakSet();
746
+ const collectHandles = (value) => {
747
+ if (!value || typeof value !== "object")
748
+ return;
749
+ if (value instanceof js.JSHandle) {
750
+ eventInitHandles.push(value);
751
+ return;
752
+ }
753
+ if (visited.has(value))
754
+ return;
755
+ visited.add(value);
756
+ if (Array.isArray(value)) {
757
+ for (const item of value)
758
+ collectHandles(item);
759
+ return;
760
+ }
761
+ for (const propertyValue of Object.values(value))
762
+ collectHandles(propertyValue);
763
+ };
764
+ collectHandles(eventInit);
765
+ const allHandlesFromSameFrame = eventInitHandles.length > 0 && eventInitHandles.every((handle) => handle._context?.frame === eventInitHandles[0]?._context?.frame);
766
+ const handlesFrame = eventInitHandles[0]?._context?.frame;
767
+ const canRetryInSecondaryContext = allHandlesFromSameFrame && (handlesFrame !== this || !selector.includes("internal:control=enter-frame"));
768
+ const callback = (injectedScript, element, data) => {
726
769
  injectedScript.dispatchEvent(element, data.type, data.eventInit);
727
- }, { type, eventInit }, { mainWorld: true, ...options }, scope);
770
+ };
771
+ try {
772
+ await this._callOnElementOnceMatches(progress, selector, callback, { type, eventInit }, { mainWorld: true, ...options }, scope);
773
+ } catch (e) {
774
+ if ("JSHandles can be evaluated only in the context they were created!" === e.message && canRetryInSecondaryContext) {
775
+ await this._callOnElementOnceMatches(progress, selector, callback, { type, eventInit }, { ...options }, scope);
776
+ return;
777
+ }
778
+ throw e;
779
+ }
728
780
  }
729
781
  async evalOnSelector(selector, strict, expression, isFunction, arg, scope) {
730
782
  const handle = await this.selectors.query(selector, { strict }, scope);
@@ -760,7 +812,7 @@ class Frame extends import_instrumentation.SdkObject {
760
812
  metadata,
761
813
  race: (promise) => Promise.race(Array.isArray(promise) ? promise : [promise])
762
814
  };
763
- return await this._retryWithoutProgress(progress, selector, null, false, async (result) => {
815
+ return await this._retryWithoutProgress(progress, selector, { strict: null, performActionPreChecks: false }, async (result) => {
764
816
  if (!result || !result[0]) return [];
765
817
  return result[1];
766
818
  }, "returnAll", null);
@@ -772,7 +824,7 @@ class Frame extends import_instrumentation.SdkObject {
772
824
  metadata,
773
825
  race: (promise) => Promise.race(Array.isArray(promise) ? promise : [promise])
774
826
  };
775
- return await this._retryWithoutProgress(progress, selector, null, false, async (result) => {
827
+ return await this._retryWithoutProgress(progress, selector, { strict: null, performActionPreChecks: false }, async (result) => {
776
828
  if (!result) return 0;
777
829
  const handle = result[0];
778
830
  const handles = result[1];
@@ -804,7 +856,7 @@ class Frame extends import_instrumentation.SdkObject {
804
856
  this._onClearLifecycle();
805
857
  this.waitForLoadState(progress, waitUntil).then(resolve).catch(reject);
806
858
  });
807
- const setContentPromise = this._page.delegate._mainFrameSession._client.send("Page.setDocumentContent", {
859
+ const setContentPromise = this._page.delegate._sessionForFrame(this)._client.send("Page.setDocumentContent", {
808
860
  frameId: this._id,
809
861
  html
810
862
  });
@@ -969,10 +1021,11 @@ class Frame extends import_instrumentation.SdkObject {
969
1021
  return true;
970
1022
  return false;
971
1023
  }
972
- async _retryWithProgressIfNotConnected(progress, selector, options, strict, performActionPreChecks, action, returnAction) {
973
- progress.log("waiting for " + this._asLocator(selector));
1024
+ async _retryWithProgressIfNotConnected(progress, selector, options, action, returnAction) {
1025
+ if (!options?.__patchrightSkipRetryLogWaiting)
1026
+ progress.log("waiting for " + this._asLocator(selector));
974
1027
  return this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async (continuePolling) => {
975
- return this._retryWithoutProgress(progress, selector, strict, performActionPreChecks, action, returnAction, continuePolling);
1028
+ return this._retryWithoutProgress(progress, selector, options, action, returnAction, continuePolling);
976
1029
  });
977
1030
  }
978
1031
  async rafrafTimeoutScreenshotElementWithProgress(progress, selector, timeout, options) {
@@ -1132,7 +1185,7 @@ class Frame extends import_instrumentation.SdkObject {
1132
1185
  }, { scope });
1133
1186
  }
1134
1187
  } else {
1135
- return await this._retryWithoutProgress(progress2, selector, options.strict, false, async (handle) => {
1188
+ return await this._retryWithoutProgress(progress2, selector, { ...options, performActionPreChecks: false }, async (handle) => {
1136
1189
  if (!handle) return false;
1137
1190
  if (handle.parentNode.constructor.name == "ElementHandle") {
1138
1191
  return await handle.parentNode.evaluateInUtility(([injected, node, { handle: handle2 }]) => {
@@ -1253,11 +1306,17 @@ class Frame extends import_instrumentation.SdkObject {
1253
1306
  const isArray = options.expression === "to.have.count" || options.expression.endsWith(".array");
1254
1307
  var log, matches, received, missingReceived;
1255
1308
  if (selector) {
1256
- const { frame, info } = await race(this.selectors.resolveFrameForSelector(selector, { strict: true }));
1309
+ var frame, info;
1310
+ try {
1311
+ var { frame, info } = await race(this.selectors.resolveFrameForSelector(selector, { strict: true }));
1312
+ } catch (e) {
1313
+ }
1257
1314
  const action = async (result) => {
1258
1315
  if (!result) {
1259
1316
  if (options.expectedNumber === 0)
1260
1317
  return { matches: true };
1318
+ if (options.isNot && options.expectedNumber)
1319
+ return { matches: false, received: 0 };
1261
1320
  if (!options.isNot && options.expression === "to.be.hidden")
1262
1321
  return { matches: true };
1263
1322
  if (options.isNot && options.expression === "to.be.visible")
@@ -1268,6 +1327,12 @@ class Frame extends import_instrumentation.SdkObject {
1268
1327
  return { matches: false };
1269
1328
  if (options.isNot && options.expression === "to.be.in.viewport")
1270
1329
  return { matches: false };
1330
+ if (options.expression === "to.have.text.array") {
1331
+ if (options.expectedText.length === 0)
1332
+ return { matches: true, received: [] };
1333
+ if (options.isNot && options.expectedText.length !== 0)
1334
+ return { matches: false, received: [] };
1335
+ }
1271
1336
  return { matches: options.isNot, missingReceived: true };
1272
1337
  }
1273
1338
  const handle = result[0];
@@ -1283,9 +1348,9 @@ class Frame extends import_instrumentation.SdkObject {
1283
1348
  }
1284
1349
  };
1285
1350
  if (noAbort) {
1286
- var { log, matches, received, missingReceived } = await this._retryWithoutProgress(progress, selector, !isArray, false, action, "returnAll", null);
1351
+ var { log, matches, received, missingReceived } = await this._retryWithoutProgress(progress, selector, { strict: !isArray, performActionPreChecks: false }, action, "returnAll", null);
1287
1352
  } else {
1288
- var { log, matches, received, missingReceived } = await race(this._retryWithProgressIfNotConnected(progress, selector, !isArray, false, action, "returnAll"));
1353
+ var { log, matches, received, missingReceived } = await race(this._retryWithProgressIfNotConnected(progress, selector, { strict: !isArray, performActionPreChecks: false, __patchrightSkipRetryLogWaiting: true }, action, "returnAll"));
1289
1354
  }
1290
1355
  } else {
1291
1356
  const world = options.expression === "to.have.property" ? "main" : "utility";
@@ -1298,10 +1363,18 @@ class Frame extends import_instrumentation.SdkObject {
1298
1363
  if (log)
1299
1364
  progress.log(log);
1300
1365
  if (matches === options.isNot) {
1301
- lastIntermediateResult.received = missingReceived ? "<element(s) not found>" : received;
1366
+ if (missingReceived) {
1367
+ lastIntermediateResult.errorMessage = "Error: element(s) not found";
1368
+ } else {
1369
+ lastIntermediateResult.errorMessage = void 0;
1370
+ lastIntermediateResult.received = received;
1371
+ }
1302
1372
  lastIntermediateResult.isSet = true;
1303
- if (!missingReceived && !Array.isArray(received))
1304
- progress.log(` unexpected value "${renderUnexpectedValue(options.expression, received)}"`);
1373
+ if (!missingReceived) {
1374
+ const rendered = renderUnexpectedValue(options.expression, received);
1375
+ if (rendered !== void 0)
1376
+ progress.log(' unexpected value "' + rendered + '"');
1377
+ }
1305
1378
  }
1306
1379
  return { matches, received };
1307
1380
  }
@@ -1357,7 +1430,7 @@ class Frame extends import_instrumentation.SdkObject {
1357
1430
  return { result, abort: () => aborted = true };
1358
1431
  }, { expression, isFunction, polling: options.pollingInterval, arg }));
1359
1432
  try {
1360
- return await progress.race(handle.evaluateHandle((h) => h.result));
1433
+ return await progress.race(this._detachedScope.race(handle.evaluateHandle((h) => h.result)));
1361
1434
  } catch (error) {
1362
1435
  await handle.evaluate((h) => h.abort()).catch(() => {
1363
1436
  });
@@ -1403,6 +1476,12 @@ class Frame extends import_instrumentation.SdkObject {
1403
1476
  data.context.contextDestroyed("Frame was detached");
1404
1477
  data.contextPromise.resolve({ destroyedReason: "Frame was detached" });
1405
1478
  }
1479
+ if (this._mainWorld)
1480
+ this._mainWorld.contextDestroyed("Frame was detached");
1481
+ if (this._iframeWorld)
1482
+ this._iframeWorld.contextDestroyed("Frame was detached");
1483
+ if (this._isolatedWorld)
1484
+ this._isolatedWorld.contextDestroyed("Frame was detached");
1406
1485
  if (this._parentFrame)
1407
1486
  this._parentFrame._childFrames.delete(this);
1408
1487
  this._parentFrame = null;
@@ -1414,14 +1493,33 @@ class Frame extends import_instrumentation.SdkObject {
1414
1493
  if (selector === ":scope") {
1415
1494
  const scopeParentNode = scope.parentNode || scope;
1416
1495
  if (scopeParentNode.constructor.name == "ElementHandle") {
1417
- promise = scopeParentNode.evaluateInUtility(([injected, node, { callbackText: callbackText2, scope: handle2, taskData: taskData2 }]) => {
1418
- const callback = injected.eval(callbackText2);
1419
- return callback(injected, handle2, taskData2);
1420
- }, {
1421
- callbackText,
1422
- scope,
1423
- taskData
1424
- });
1496
+ if (options?.mainWorld) {
1497
+ promise = (async () => {
1498
+ const mainContext = await this._mainContext();
1499
+ const adoptedScope = await this._page.delegate.adoptElementHandle(scope, mainContext);
1500
+ try {
1501
+ return await mainContext.evaluate(([injected, node, { callbackText: callbackText2, scope: handle2, taskData: taskData2 }]) => {
1502
+ const callback = injected.eval(callbackText2);
1503
+ return callback(injected, handle2, taskData2);
1504
+ }, [
1505
+ await mainContext.injectedScript(),
1506
+ adoptedScope,
1507
+ { callbackText, scope: adoptedScope, taskData }
1508
+ ]);
1509
+ } finally {
1510
+ adoptedScope.dispose();
1511
+ }
1512
+ })();
1513
+ } else {
1514
+ promise = scopeParentNode.evaluateInUtility(([injected, node, { callbackText: callbackText2, scope: handle2, taskData: taskData2 }]) => {
1515
+ const callback = injected.eval(callbackText2);
1516
+ return callback(injected, handle2, taskData2);
1517
+ }, {
1518
+ callbackText,
1519
+ scope,
1520
+ taskData
1521
+ });
1522
+ }
1425
1523
  } else {
1426
1524
  promise = scopeParentNode.evaluate((injected, { callbackText: callbackText2, scope: handle2, taskData: taskData2 }) => {
1427
1525
  const callback = injected.eval(callbackText2);
@@ -1433,8 +1531,24 @@ class Frame extends import_instrumentation.SdkObject {
1433
1531
  });
1434
1532
  }
1435
1533
  } else {
1436
- promise = this._retryWithProgressIfNotConnected(progress, selector, options.strict, false, async (handle) => {
1534
+ promise = this._retryWithProgressIfNotConnected(progress, selector, { ...options, performActionPreChecks: false }, async (handle) => {
1437
1535
  if (handle.parentNode.constructor.name == "ElementHandle") {
1536
+ if (options?.mainWorld) {
1537
+ const mainContext = await handle._frame._mainContext();
1538
+ const adoptedHandle = await this._page.delegate.adoptElementHandle(handle, mainContext);
1539
+ try {
1540
+ return await mainContext.evaluate(([injected, node, { callbackText: callbackText2, handle: handle2, taskData: taskData2 }]) => {
1541
+ const callback = injected.eval(callbackText2);
1542
+ return callback(injected, handle2, taskData2);
1543
+ }, [
1544
+ await mainContext.injectedScript(),
1545
+ adoptedHandle,
1546
+ { callbackText, handle: adoptedHandle, taskData }
1547
+ ]);
1548
+ } finally {
1549
+ adoptedHandle.dispose();
1550
+ }
1551
+ }
1438
1552
  const [taskScope] = Object.values(taskData?.eventInit ?? {});
1439
1553
  if (taskScope) {
1440
1554
  const taskScopeContext = taskScope._context;
@@ -1527,21 +1641,23 @@ class Frame extends import_instrumentation.SdkObject {
1527
1641
  var describedNode = await client._sendMayFail("DOM.describeNode", {
1528
1642
  backendNodeId: globalDocument.backendNodeId
1529
1643
  });
1530
- if (describedNode) {
1644
+ if (describedNode && describedNode.node.contentDocument) {
1531
1645
  var resolvedNode = await client._sendMayFail("DOM.resolveNode", {
1532
- nodeId: describedNode.node.contentDocument.nodeId
1646
+ backendNodeId: describedNode.node.contentDocument.backendNodeId
1533
1647
  });
1534
- var _executionContextId = parseInt(resolvedNode.object.objectId.split(".")[1], 10);
1535
- return _executionContextId;
1648
+ if (resolvedNode && resolvedNode.object && resolvedNode.object.objectId) {
1649
+ var _executionContextId = parseInt(resolvedNode.object.objectId.split(".")[1], 10);
1650
+ return _executionContextId;
1651
+ }
1536
1652
  }
1537
1653
  }
1538
1654
  } catch (e) {
1539
1655
  }
1540
1656
  return 0;
1541
1657
  }
1542
- async _retryWithoutProgress(progress, selector, strict, performActionPreChecks, action, returnAction, continuePolling) {
1543
- if (performActionPreChecks) await this._page.performActionPreChecks(progress);
1544
- const resolved = await this.selectors.resolveInjectedForSelector(selector, { strict });
1658
+ async _retryWithoutProgress(progress, selector, options, action, returnAction, continuePolling) {
1659
+ if (options.performActionPreChecks) await this._page.performActionPreChecks(progress);
1660
+ const resolved = await this.selectors.resolveInjectedForSelector(selector, { strict: options.strict }, options.__patchrightInitialScope);
1545
1661
  if (!resolved) {
1546
1662
  if (returnAction === "returnOnNotResolved" || returnAction === "returnAll") {
1547
1663
  const result2 = await action(null);
@@ -1564,24 +1680,51 @@ class Frame extends import_instrumentation.SdkObject {
1564
1680
  contextId: utilityContext.delegate._contextId
1565
1681
  });
1566
1682
  if (!documentNode) return continuePolling;
1567
- const documentScope = new dom.ElementHandle(utilityContext, documentNode.result.objectId);
1683
+ let documentScope = new dom.ElementHandle(utilityContext, documentNode.result.objectId);
1684
+ let initialScope = documentScope;
1685
+ if (resolved.scope) {
1686
+ const scopeBackendNodeId = resolved.scope._objectId ? (await client._sendMayFail("DOM.describeNode", { objectId: resolved.scope._objectId }))?.node?.backendNodeId : null;
1687
+ if (scopeBackendNodeId) {
1688
+ const scopeInUtility = await client._sendMayFail("DOM.resolveNode", { backendNodeId: scopeBackendNodeId, executionContextId: utilityContext.delegate._contextId });
1689
+ if (scopeInUtility?.object?.objectId)
1690
+ initialScope = new dom.ElementHandle(utilityContext, scopeInUtility.object.objectId);
1691
+ }
1692
+ }
1693
+ progress.__patchrightInitialScope = resolved.scope;
1694
+ const parsedSnapshot = options.__patchrightWaitForSelector ? JSON.parse(JSON.stringify(resolved.info.parsed)) : null;
1568
1695
  let currentScopingElements;
1569
1696
  try {
1570
- currentScopingElements = await this._customFindElementsByParsed(resolved, client, mainContext, documentScope, progress, resolved.info.parsed);
1697
+ currentScopingElements = await this._customFindElementsByParsed(resolved, client, mainContext, initialScope, progress, resolved.info.parsed);
1571
1698
  } catch (e) {
1572
- if ("JSHandles can be evaluated only in the context they were created!" === e.message) return continuePolling3;
1699
+ if ("JSHandles can be evaluated only in the context they were created!" === e.message) return continuePolling;
1700
+ if (e instanceof TypeError && e.message.includes("is not a function")) return continuePolling;
1573
1701
  await progress.race(resolved.injected.evaluateHandle((injected, { error }) => {
1574
1702
  throw error;
1575
1703
  }, { error: e }));
1576
1704
  }
1577
1705
  if (currentScopingElements.length == 0) {
1706
+ if (options.__testHookNoAutoWaiting || options.noAutoWaiting)
1707
+ throw new dom.NonRecoverableDOMError("Element(s) not found");
1708
+ if (parsedSnapshot && (returnAction === "returnOnNotResolved" || returnAction === "returnAll")) {
1709
+ const elementCount = await resolved.injected.evaluate((injected, { parsed }) => {
1710
+ return injected.querySelectorAll(parsed, document).length;
1711
+ }, { parsed: parsedSnapshot }).catch(() => 0);
1712
+ if (elementCount > 0)
1713
+ return continuePolling;
1714
+ }
1578
1715
  if (returnAction === "returnOnNotResolved" || returnAction === "returnAll") {
1579
1716
  const result2 = await action(null);
1580
- return result2 === "internal:continuepolling" ? continuePolling2 : result2;
1717
+ return result2 === "internal:continuepolling" ? continuePolling : result2;
1581
1718
  }
1582
1719
  return continuePolling;
1583
1720
  }
1584
1721
  const resultElement = currentScopingElements[0];
1722
+ await resultElement._initializePreview().catch(() => {
1723
+ });
1724
+ let visibilityQualifier = "";
1725
+ if (options && options.__patchrightWaitForSelector) {
1726
+ visibilityQualifier = await resultElement.evaluateInUtility(([injected, node]) => injected.utils.isElementVisible(node) ? "visible" : "hidden", {}).catch(() => "");
1727
+ }
1585
1728
  if (currentScopingElements.length > 1) {
1586
1729
  if (resolved.info.strict) {
1587
1730
  await progress.race(resolved.injected.evaluateHandle((injected, {
@@ -1596,7 +1739,7 @@ class Frame extends import_instrumentation.SdkObject {
1596
1739
  }
1597
1740
  progress.log(" locator resolved to " + currentScopingElements.length + " elements. Proceeding with the first one: " + resultElement.preview());
1598
1741
  } else if (resultElement) {
1599
- progress.log(" locator resolved to " + resultElement.preview());
1742
+ progress.log(" locator resolved to " + (visibilityQualifier ? visibilityQualifier + " " : "") + resultElement.preview().replace("JSHandle@", ""));
1600
1743
  }
1601
1744
  try {
1602
1745
  var result = null;
@@ -1611,6 +1754,14 @@ class Frame extends import_instrumentation.SdkObject {
1611
1754
  } else if (result === "internal:continuepolling") {
1612
1755
  return continuePolling;
1613
1756
  }
1757
+ if (parsedSnapshot && result === null && (options.state === "hidden" || options.state === "detached")) {
1758
+ const visibleCount = await resolved.injected.evaluate((injected, { parsed }) => {
1759
+ const elements = injected.querySelectorAll(parsed, document);
1760
+ return elements.filter((e) => injected.utils.isElementVisible(e)).length;
1761
+ }, { parsed: parsedSnapshot }).catch(() => 0);
1762
+ if (visibleCount > 0)
1763
+ return continuePolling;
1764
+ }
1614
1765
  return result;
1615
1766
  } finally {
1616
1767
  }
@@ -122,6 +122,8 @@ class JSHandle extends import_instrumentation.SdkObject {
122
122
  else if (isolatedContext === false) context = await frame._mainContext();
123
123
  }
124
124
  }
125
+ if (context !== this._context && context.adoptIfNeeded(this) === null)
126
+ context = this._context;
125
127
  const value = await evaluateExpression(context, expression, { ...options, returnByValue: true }, this, arg);
126
128
  await context.doSlowMo();
127
129
  return value;
@@ -135,6 +137,8 @@ class JSHandle extends import_instrumentation.SdkObject {
135
137
  else if (isolatedContext === false) context = await frame._mainContext();
136
138
  }
137
139
  }
140
+ if (context !== this._context && context.adoptIfNeeded(this) === null)
141
+ context = this._context;
138
142
  const value = await evaluateExpression(context, expression, { ...options, returnByValue: false }, this, arg);
139
143
  await context.doSlowMo();
140
144
  return value;
@@ -114,7 +114,8 @@ async function syncLocalStorageWithSettings(page, appName) {
114
114
  return;
115
115
  Object.entries(settings2).map(([k, v]) => localStorage[k] = v);
116
116
  window.saveSettings = () => {
117
- window._saveSerializedSettings(JSON.stringify({ ...localStorage }));
117
+ if (typeof window._saveSerializedSettings === "function")
118
+ window._saveSerializedSettings(JSON.stringify({ ...localStorage }));
118
119
  };
119
120
  })})(${settings});
120
121
  `
@@ -711,6 +711,32 @@ class PageBinding {
711
711
  }
712
712
  static async dispatch(page, payload, context) {
713
713
  const { name, seq, serializedArgs } = JSON.parse(payload);
714
+ const deliver = async (deliverPayload) => {
715
+ let deliveryError;
716
+ try {
717
+ await context.evaluate(import_pageBinding.deliverBindingResult, deliverPayload);
718
+ return;
719
+ } catch (e) {
720
+ deliveryError = e;
721
+ }
722
+ const frame = context.frame;
723
+ if (!frame) {
724
+ import_debugLogger.debugLogger.log("error", deliveryError);
725
+ return;
726
+ }
727
+ const mainContext = await frame._mainContext().catch(() => null);
728
+ const utilityContext = await frame._utilityContext().catch(() => null);
729
+ for (const ctx of [mainContext, utilityContext]) {
730
+ if (!ctx || ctx === context)
731
+ continue;
732
+ try {
733
+ await ctx.evaluate(import_pageBinding.deliverBindingResult, deliverPayload);
734
+ return;
735
+ } catch {
736
+ }
737
+ }
738
+ import_debugLogger.debugLogger.log("error", deliveryError);
739
+ };
714
740
  try {
715
741
  (0, import_utils.assert)(context.world);
716
742
  const binding = page.getBinding(name);
@@ -726,9 +752,9 @@ class PageBinding {
726
752
  const args = serializedArgs.map((a) => (0, import_utilityScriptSerializers.parseEvaluationResultValue)(a));
727
753
  result = await binding.playwrightFunction({ frame: context.frame, page, context: page._browserContext }, ...args);
728
754
  }
729
- context.evaluate(import_pageBinding.deliverBindingResult, { name, seq, result }).catch((e) => import_debugLogger.debugLogger.log("error", e));
755
+ await deliver({ name, seq, result });
730
756
  } catch (error) {
731
- context.evaluate(import_pageBinding.deliverBindingResult, { name, seq, error }).catch((e) => import_debugLogger.debugLogger.log("error", e));
757
+ await deliver({ name, seq, error });
732
758
  }
733
759
  }
734
760
  }
@@ -213,6 +213,12 @@ class Screenshotter {
213
213
  if (disableAnimations)
214
214
  progress.log(" disabled all CSS animations");
215
215
  const syncAnimations = this._page.delegate.shouldToggleStyleSheetToSyncAnimations();
216
+ await Promise.all(this._page.frames().map(async (f) => {
217
+ try {
218
+ await f._utilityContext();
219
+ } catch {
220
+ }
221
+ }));
216
222
  await progress.race(this._page.safeNonStallingEvaluateInAllFrames("(" + inPagePrepareForScreenshots.toString() + `)(${JSON.stringify(screenshotStyle)}, ${hideCaret}, ${disableAnimations}, ${syncAnimations})`, "utility"));
217
223
  try {
218
224
  if (!process.env.PW_TEST_SCREENSHOT_NO_FONTS_READY) {
@@ -49,7 +49,7 @@ class Snapshotter {
49
49
  }
50
50
  async reset() {
51
51
  if (this._started)
52
- await this._context.safeNonStallingEvaluateInAllFrames(`window["${this._snapshotStreamer}"].reset()`, "main");
52
+ await this._context.safeNonStallingEvaluateInAllFrames(`window["${this._snapshotStreamer}"].reset()`, "utility");
53
53
  }
54
54
  stop() {
55
55
  this._started = false;
@@ -57,20 +57,20 @@ class Snapshotter {
57
57
  async resetForReuse() {
58
58
  if (this._initScript) {
59
59
  import_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
60
- await this._context.removeInitScripts([this._initScript]);
61
60
  this._initScript = void 0;
61
+ this._initScriptSource = void 0;
62
62
  }
63
63
  }
64
64
  async _initialize() {
65
+ const { javaScriptEnabled } = this._context._options;
66
+ this._initScriptSource = `(${import_snapshotterInjected.frameSnapshotStreamer})("${this._snapshotStreamer}", ${javaScriptEnabled || javaScriptEnabled === void 0})`;
67
+ this._initScript = true;
65
68
  for (const page of this._context.pages())
66
69
  this._onPage(page);
67
70
  this._eventListeners = [
68
71
  import_eventsHelper.eventsHelper.addEventListener(this._context, import_browserContext.BrowserContext.Events.Page, this._onPage.bind(this))
69
72
  ];
70
- const { javaScriptEnabled } = this._context._options;
71
- const initScriptSource = `(${import_snapshotterInjected.frameSnapshotStreamer})("${this._snapshotStreamer}", ${javaScriptEnabled || javaScriptEnabled === void 0})`;
72
- this._initScript = await this._context.addInitScript(void 0, initScriptSource);
73
- await this._context.safeNonStallingEvaluateInAllFrames(initScriptSource, "main");
73
+ await this._context.safeNonStallingEvaluateInAllFrames(this._initScriptSource, "utility");
74
74
  }
75
75
  dispose() {
76
76
  import_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
@@ -80,7 +80,7 @@ class Snapshotter {
80
80
  frame[kNeedsResetSymbol] = false;
81
81
  const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${needsReset ? "true" : "false"})`;
82
82
  try {
83
- return await frame.nonStallingRawEvaluateInExistingMainContext(expression);
83
+ return await frame.nonStallingEvaluateInExistingContext(expression, "utility");
84
84
  } catch (e) {
85
85
  frame[kNeedsResetSymbol] = true;
86
86
  import_debugLogger.debugLogger.log("error", e);
@@ -124,6 +124,7 @@ class Snapshotter {
124
124
  for (const frame of page.frames())
125
125
  this._annotateFrameHierarchy(frame);
126
126
  this._eventListeners.push(import_eventsHelper.eventsHelper.addEventListener(page, import_page.Page.Events.FrameAttached, (frame) => this._annotateFrameHierarchy(frame)));
127
+ this._eventListeners.push(import_eventsHelper.eventsHelper.addEventListener(page, import_page.Page.Events.InternalFrameNavigatedToNewDocument, (frame) => this._onFrameNavigated(frame)));
127
128
  }
128
129
  async _annotateFrameHierarchy(frame) {
129
130
  try {
@@ -131,7 +132,7 @@ class Snapshotter {
131
132
  const parent = frame.parentFrame();
132
133
  if (!parent)
133
134
  return;
134
- const context = await parent._mainContext();
135
+ const context = await parent._utilityContext();
135
136
  await context?.evaluate(({ snapshotStreamer, frameElement: frameElement2, frameId }) => {
136
137
  window[snapshotStreamer].markIframe(frameElement2, frameId);
137
138
  }, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.guid });
@@ -139,6 +140,14 @@ class Snapshotter {
139
140
  } catch (e) {
140
141
  }
141
142
  }
143
+ async _onFrameNavigated(frame) {
144
+ if (!this._initScriptSource)
145
+ return;
146
+ try {
147
+ await frame.nonStallingEvaluateInExistingContext(this._initScriptSource, "utility");
148
+ } catch (e) {
149
+ }
150
+ }
142
151
  }
143
152
  const kNeedsResetSymbol = Symbol("kNeedsReset");
144
153
  // Annotate the CommonJS export names for ESM import in node:
@@ -60,28 +60,11 @@ function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
60
60
  class Streamer {
61
61
  constructor() {
62
62
  this._lastSnapshotNumber = 0;
63
- this._staleStyleSheets = /* @__PURE__ */ new Set();
64
63
  this._modifiedStyleSheets = /* @__PURE__ */ new Set();
65
- this._readingStyleSheet = false;
66
- const invalidateCSSGroupingRule = (rule) => {
67
- if (rule.parentStyleSheet)
68
- this._invalidateStyleSheet(rule.parentStyleSheet);
69
- };
70
- this._interceptNativeMethod(window.CSSStyleSheet.prototype, "insertRule", (sheet) => this._invalidateStyleSheet(sheet));
71
- this._interceptNativeMethod(window.CSSStyleSheet.prototype, "deleteRule", (sheet) => this._invalidateStyleSheet(sheet));
72
- this._interceptNativeMethod(window.CSSStyleSheet.prototype, "addRule", (sheet) => this._invalidateStyleSheet(sheet));
73
- this._interceptNativeMethod(window.CSSStyleSheet.prototype, "removeRule", (sheet) => this._invalidateStyleSheet(sheet));
74
- this._interceptNativeGetter(window.CSSStyleSheet.prototype, "rules", (sheet) => this._invalidateStyleSheet(sheet));
75
- this._interceptNativeGetter(window.CSSStyleSheet.prototype, "cssRules", (sheet) => this._invalidateStyleSheet(sheet));
76
- this._interceptNativeMethod(window.CSSStyleSheet.prototype, "replaceSync", (sheet) => this._invalidateStyleSheet(sheet));
77
- this._interceptNativeMethod(window.CSSGroupingRule.prototype, "insertRule", invalidateCSSGroupingRule);
78
- this._interceptNativeMethod(window.CSSGroupingRule.prototype, "deleteRule", invalidateCSSGroupingRule);
79
- this._interceptNativeGetter(window.CSSGroupingRule.prototype, "cssRules", invalidateCSSGroupingRule);
80
64
  this._interceptNativeSetter(window.StyleSheet.prototype, "disabled", (sheet) => {
81
65
  if (sheet instanceof CSSStyleSheet)
82
66
  this._invalidateStyleSheet(sheet);
83
67
  });
84
- this._interceptNativeAsyncMethod(window.CSSStyleSheet.prototype, "replace", (sheet) => this._invalidateStyleSheet(sheet));
85
68
  this._fakeBase = document.createElement("base");
86
69
  this._observer = new MutationObserver((list) => this._handleMutations(list));
87
70
  const observerConfig = { attributes: true, subtree: true };
@@ -122,37 +105,6 @@ function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
122
105
  delete event.composedPath()[0].__playwright_target__;
123
106
  });
124
107
  }
125
- _interceptNativeMethod(obj, method, cb) {
126
- const native = obj[method];
127
- if (!native)
128
- return;
129
- obj[method] = function(...args) {
130
- const result = native.call(this, ...args);
131
- cb(this, result);
132
- return result;
133
- };
134
- }
135
- _interceptNativeAsyncMethod(obj, method, cb) {
136
- const native = obj[method];
137
- if (!native)
138
- return;
139
- obj[method] = async function(...args) {
140
- const result = await native.call(this, ...args);
141
- cb(this, result);
142
- return result;
143
- };
144
- }
145
- _interceptNativeGetter(obj, prop, cb) {
146
- const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
147
- Object.defineProperty(obj, prop, {
148
- ...descriptor,
149
- get: function() {
150
- const result = descriptor.get.call(this);
151
- cb(this, result);
152
- return result;
153
- }
154
- });
155
- }
156
108
  _interceptNativeSetter(obj, prop, cb) {
157
109
  const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
158
110
  Object.defineProperty(obj, prop, {
@@ -168,44 +120,37 @@ function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
168
120
  for (const mutation of list)
169
121
  ensureCachedData(mutation.target).attributesCached = void 0;
170
122
  }
171
- _invalidateStyleSheet(sheet) {
172
- if (this._readingStyleSheet)
173
- return;
174
- this._staleStyleSheets.add(sheet);
175
- if (sheet.href !== null)
176
- this._modifiedStyleSheets.add(sheet);
177
- }
178
123
  _updateStyleElementStyleSheetTextIfNeeded(sheet, forceText) {
179
124
  const data = ensureCachedData(sheet);
180
- if (this._staleStyleSheets.has(sheet) || forceText && data.cssText === void 0) {
181
- this._staleStyleSheets.delete(sheet);
182
- try {
183
- data.cssText = this._getSheetText(sheet);
184
- } catch (e) {
185
- data.cssText = "";
186
- }
125
+ try {
126
+ data.cssText = this._getSheetText(sheet);
127
+ } catch (e) {
128
+ data.cssText = "";
187
129
  }
188
130
  return data.cssText;
189
131
  }
190
132
  // Returns either content, ref, or no override.
191
133
  _updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber) {
192
134
  const data = ensureCachedData(sheet);
193
- if (this._staleStyleSheets.has(sheet)) {
194
- this._staleStyleSheets.delete(sheet);
195
- try {
196
- data.cssText = this._getSheetText(sheet);
197
- data.cssRef = snapshotNumber;
198
- return data.cssText;
199
- } catch (e) {
135
+ try {
136
+ const currentText = this._getSheetText(sheet);
137
+ if (data.cssText === void 0) {
138
+ data.cssText = currentText;
139
+ return void 0;
200
140
  }
141
+ if (currentText === data.cssText)
142
+ return data.cssRef === void 0 ? void 0 : snapshotNumber - data.cssRef;
143
+ data.cssText = currentText;
144
+ data.cssRef = snapshotNumber;
145
+ return data.cssText;
146
+ } catch (e) {
147
+ return void 0;
201
148
  }
202
- return data.cssRef === void 0 ? void 0 : snapshotNumber - data.cssRef;
203
149
  }
204
150
  markIframe(iframeElement, frameId) {
205
151
  iframeElement[kSnapshotFrameId] = frameId;
206
152
  }
207
153
  reset() {
208
- this._staleStyleSheets.clear();
209
154
  const visitNode = (node) => {
210
155
  resetCachedData(node);
211
156
  if (node.nodeType === Node.ELEMENT_NODE) {
@@ -264,17 +209,10 @@ function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
264
209
  return document.baseURI;
265
210
  }
266
211
  _getSheetText(sheet) {
267
- this._readingStyleSheet = true;
268
- try {
269
- if (sheet.disabled)
270
- return "";
271
- const rules = [];
272
- for (const rule of sheet.cssRules)
273
- rules.push(rule.cssText);
274
- return rules.join("\n");
275
- } finally {
276
- this._readingStyleSheet = false;
277
- }
212
+ const rules = [];
213
+ for (const rule of sheet.cssRules)
214
+ rules.push(rule.cssText);
215
+ return rules.join("\n");
278
216
  }
279
217
  captureSnapshot(needsReset) {
280
218
  const timestamp = performance.now();
@@ -550,6 +550,8 @@ function shouldCaptureSnapshot(metadata) {
550
550
  return !!metainfo?.snapshot;
551
551
  }
552
552
  function createBeforeActionTraceEvent(metadata, parentId) {
553
+ if (metadata.type === "Route" && metadata.method === "continue" && metadata.params?.isFallback)
554
+ return null;
553
555
  if (metadata.internal || metadata.method.startsWith("tracing"))
554
556
  return null;
555
557
  const event = {
@@ -568,6 +570,8 @@ function createBeforeActionTraceEvent(metadata, parentId) {
568
570
  return event;
569
571
  }
570
572
  function createInputActionTraceEvent(metadata) {
573
+ if (metadata.type === "Route" && metadata.method === "continue" && metadata.params?.isFallback)
574
+ return null;
571
575
  if (metadata.internal || metadata.method.startsWith("tracing"))
572
576
  return null;
573
577
  return {
@@ -577,6 +581,8 @@ function createInputActionTraceEvent(metadata) {
577
581
  };
578
582
  }
579
583
  function createActionLogTraceEvent(metadata, message) {
584
+ if (metadata.type === "Route" && metadata.method === "continue" && metadata.params?.isFallback)
585
+ return null;
580
586
  if (metadata.internal || metadata.method.startsWith("tracing"))
581
587
  return null;
582
588
  return {
@@ -587,6 +593,8 @@ function createActionLogTraceEvent(metadata, message) {
587
593
  };
588
594
  }
589
595
  function createAfterActionTraceEvent(metadata) {
596
+ if (metadata.type === "Route" && metadata.method === "continue" && metadata.params?.isFallback)
597
+ return null;
590
598
  if (metadata.internal || metadata.method.startsWith("tracing"))
591
599
  return null;
592
600
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phantomwright-driver-core",
3
- "version": "v1.58.2",
3
+ "version": "1.58.3",
4
4
  "description": "A high-level API to automate web browsers",
5
5
  "repository": {
6
6
  "type": "git",