diff --git a/packages/puppeteer-core/src/common/Frame.ts b/packages/puppeteer-core/src/common/Frame.ts index cb67b48588d..3197fe97e41 100644 --- a/packages/puppeteer-core/src/common/Frame.ts +++ b/packages/puppeteer-core/src/common/Frame.ts @@ -63,7 +63,6 @@ export class Frame extends BaseFrame { _frameManager: FrameManager; override _id: string; _loaderId = ''; - override _hasStartedLoading = false; _lifecycleEvents = new Set(); override _parentId?: string; diff --git a/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts b/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts index d0cca4a326a..0ffb21f6892 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowsingContext.ts @@ -101,7 +101,7 @@ export class CDPSessionWrapper extends CDPSession { override async detach(): Promise { cdpSessions.delete(this.id()); - if (this.#context.supportsCDP()) { + if (!this.#detached && this.#context.supportsCDP()) { await this.#context.cdpSession.send('Target.detachFromTarget', { sessionId: this.id(), }); diff --git a/packages/puppeteer-core/src/common/bidi/Frame.ts b/packages/puppeteer-core/src/common/bidi/Frame.ts index 445abbe3b10..88824ca0c9b 100644 --- a/packages/puppeteer-core/src/common/bidi/Frame.ts +++ b/packages/puppeteer-core/src/common/bidi/Frame.ts @@ -49,6 +49,7 @@ export class Frame extends BaseFrame { #context: BrowsingContext; #timeoutSettings: TimeoutSettings; #abortDeferred = Deferred.create(); + #detached = false; sandboxes: SandboxChart; override _id: string; @@ -270,8 +271,15 @@ export class Frame extends BaseFrame { return this.#page.getNavigationResponse(info.navigation); } + override isDetached(): boolean { + return this.#detached; + } + dispose(): void { + this.#detached = true; this.#abortDeferred.reject(new Error('Frame detached')); this.#context.dispose(); + this.sandboxes[MAIN_SANDBOX].dispose(); + this.sandboxes[PUPPETEER_SANDBOX].dispose(); } } diff --git a/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts b/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts index eb021ac7e04..f991968cad2 100644 --- a/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts +++ b/packages/puppeteer-core/src/common/bidi/HTTPRequest.ts @@ -43,7 +43,7 @@ export class HTTPRequest extends BaseHTTPRequest { constructor( event: Bidi.Network.BeforeRequestSentParameters, frame: Frame | null, - redirectChain: HTTPRequest[] + redirectChain: HTTPRequest[] = [] ) { super(); @@ -55,7 +55,7 @@ export class HTTPRequest extends BaseHTTPRequest { this.#frame = frame; this._requestId = event.request.request; - this._redirectChain = redirectChain ?? []; + this._redirectChain = redirectChain; this._navigationId = event.navigation; for (const header of event.request.headers) { diff --git a/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts b/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts index a920f47eea7..120573203bf 100644 --- a/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts +++ b/packages/puppeteer-core/src/common/bidi/HTTPResponse.ts @@ -101,4 +101,8 @@ export class HTTPResponse extends BaseHTTPResponse { override frame(): Frame | null { return this.#request.frame(); } + + override fromServiceWorker(): boolean { + return false; + } } diff --git a/packages/puppeteer-core/src/common/bidi/NetworkManager.ts b/packages/puppeteer-core/src/common/bidi/NetworkManager.ts index 27ac4a39f32..23960c5c573 100644 --- a/packages/puppeteer-core/src/common/bidi/NetworkManager.ts +++ b/packages/puppeteer-core/src/common/bidi/NetworkManager.ts @@ -46,7 +46,7 @@ export class NetworkManager extends EventEmitter { this.#connection = connection; this.#page = page; - // TODO: Subscribe to the Frame indivutally + // TODO: Subscribe to the Frame individually for (const [event, subscriber] of this.#subscribedEvents) { this.#connection.on(event, subscriber); } @@ -130,7 +130,7 @@ export class NetworkManager extends EventEmitter { for (const [id, response] of this.#navigationMap.entries()) { if (response.frame() === frame) { - this.#requestMap.delete(id); + this.#navigationMap.delete(id); } } } diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index 96a470b14b9..514ad7f26d5 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -250,8 +250,11 @@ export class Page extends PageBase { #onFrameDOMContentLoaded(info: Bidi.BrowsingContext.NavigationInfo): void { const frame = this.frame(info.context); - if (frame && this.mainFrame() === frame) { - this.emit(PageEmittedEvents.DOMContentLoaded); + if (frame) { + frame._hasStartedLoading = true; + if (this.mainFrame() === frame) { + this.emit(PageEmittedEvents.DOMContentLoaded); + } } } diff --git a/packages/puppeteer-core/src/common/bidi/Sandbox.ts b/packages/puppeteer-core/src/common/bidi/Sandbox.ts index e83def6d23e..558c0e9dfe8 100644 --- a/packages/puppeteer-core/src/common/bidi/Sandbox.ts +++ b/packages/puppeteer-core/src/common/bidi/Sandbox.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; + import {ClickOptions, ElementHandle} from '../../api/ElementHandle.js'; import {Realm as RealmBase} from '../../api/Frame.js'; import {KeyboardTypeOptions} from '../../api/Input.js'; @@ -30,6 +32,7 @@ import { import {withSourcePuppeteerURLIfNone} from '../util.js'; import {TaskManager, WaitTask} from '../WaitTask.js'; +import {BrowsingContext} from './BrowsingContext.js'; import {JSHandle} from './JSHandle.js'; import {Realm} from './Realm.js'; /** @@ -65,9 +68,27 @@ export class Sandbox implements RealmBase { #timeoutSettings: TimeoutSettings; #taskManager = new TaskManager(); - constructor(context: Realm, timeoutSettings: TimeoutSettings) { - this.#realm = context; + constructor( + // TODO: We should split the Realm and BrowsingContext + realm: Realm | BrowsingContext, + timeoutSettings: TimeoutSettings + ) { + this.#realm = realm; this.#timeoutSettings = timeoutSettings; + + // TODO: Tack correct realm similar to BrowsingContexts + this.#realm.connection.on( + Bidi.ChromiumBidi.Script.EventNames.RealmCreated, + () => { + void this.#taskManager.rerunAll(); + } + ); + } + + dispose(): void { + this.#taskManager.terminateAll( + new Error('waitForFunction failed: frame got detached.') + ); } get taskManager(): TaskManager { diff --git a/test/TestExpectations.json b/test/TestExpectations.json index b2337fdb69a..ccc3aad99fa 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -23,6 +23,12 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[dialog.spec] Page.Events.Dialog *", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[elementhandle.spec] ElementHandle specs Custom queries *", "platforms": ["darwin", "linux", "win32"], @@ -564,10 +570,10 @@ "expectations": ["FAIL", "PASS"] }, { - "testIdPattern": "[dialog.spec] Page.Events.Dialog *", + "testIdPattern": "[dialog.spec] Page.Events.Dialog should allow accepting prompts", "platforms": ["darwin", "linux", "win32"], - "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["PASS"] + "parameters": ["webDriverBiDi"], + "expectations": ["FAIL"] }, { "testIdPattern": "[drag-and-drop.spec] *", @@ -1241,12 +1247,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["FAIL"] }, - { - "testIdPattern": "[network.spec] network Response.fromServiceWorker should return |false| for non-service-worker content", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["FAIL"] - }, { "testIdPattern": "[network.spec] network Response.timing returns timing information", "platforms": ["darwin", "linux", "win32"], @@ -1607,30 +1607,6 @@ "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, - { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive cross-process navigation", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["SKIP"] - }, - { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive navigations", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["SKIP"] - }, - { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work when resolved right before execution context disposal", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["SKIP"] - }, - { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work with strict CSP policy", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["SKIP"] - }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should be cancellable", "platforms": ["darwin", "linux", "win32"], @@ -1715,12 +1691,6 @@ "parameters": ["webDriverBiDi"], "expectations": ["PASS"] }, - { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible (display)", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["webDriverBiDi"], - "expectations": ["PASS"] - }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible (visibility)", "platforms": ["darwin", "linux", "win32"], @@ -2099,12 +2069,6 @@ "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, - { - "testIdPattern": "[dialog.spec] Page.Events.Dialog should allow accepting prompts", - "platforms": ["darwin", "linux", "win32"], - "parameters": ["chrome", "webDriverBiDi"], - "expectations": ["FAIL"] - }, { "testIdPattern": "[elementhandle.spec] ElementHandle specs ElementHandle.boundingBox should handle nested frames", "platforms": ["darwin", "linux", "win32"], @@ -2339,6 +2303,12 @@ "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[frame.spec] Frame specs Frame Management should report different frame instance when frame re-attaches", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[frame.spec] Frame specs Frame Management should report frame from-inside shadow DOM", "platforms": ["darwin", "linux", "win32"], @@ -2381,6 +2351,12 @@ "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[frame.spec] Frame specs Frame Management should send events when frames are manipulated dynamically", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[frame.spec] Frame specs Frame Management should support framesets", "platforms": ["darwin", "linux", "win32"], @@ -2447,6 +2423,12 @@ "parameters": ["chrome", "webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[headful.spec] headful tests Page.bringToFront should work", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[ignorehttpserrors.spec] ignoreHTTPSErrors Response.securityDetails Network redirects should report SecurityDetails", "platforms": ["darwin", "linux", "win32"], @@ -3161,6 +3143,12 @@ "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[network.spec] network Network Events Page.Events.RequestServedFromCache", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["FAIL", "PASS"] + }, { "testIdPattern": "[network.spec] network Network Events Page.Events.Response", "platforms": ["darwin", "linux", "win32"], @@ -3443,12 +3431,60 @@ "parameters": ["chrome", "webDriverBiDi"], "expectations": ["PASS"] }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF clickablePoint, boundingBox, boxModel should work for elements inside OOPIFs", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should provide access to elements", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should support evaluating in oop iframes", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should support frames within OOP frames", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should support frames within OOP iframes", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should support wait for navigation for transitions from local to OOPIF", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "chrome"], "expectations": ["PASS", "TIMEOUT"] }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should track navigations within OOP iframes", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should treat OOP iframes and normal iframes the same", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF waitForFrame should resolve immediately if the frame already exists", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[page.spec] Page \"after all\" hook in \"Page\"", "platforms": ["darwin", "linux", "win32"], @@ -4163,36 +4199,90 @@ "parameters": ["firefox", "webDriverBiDi"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive cross-process navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive cross-process navigation", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["FAIL", "PASS"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive cross-process navigation", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive navigations", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should survive navigations", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"] + }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work when resolved right before execution context disposal", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work when resolved right before execution context disposal", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work with strict CSP policy", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work with strict CSP policy", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["FAIL"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForFunction should work with strict CSP policy", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector Page.waitForSelector is shortcut for main frame", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector Page.waitForSelector is shortcut for main frame", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should run in specified frame", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should run in specified frame", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should survive cross-process navigation", "platforms": ["darwin", "linux", "win32"], @@ -4205,6 +4295,18 @@ "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should throw when frame is detached", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should wait for element to be visible (display)", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["chrome", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector should work with removed MutationObserver", "platforms": ["darwin", "linux", "win32"], @@ -4259,12 +4361,6 @@ "parameters": ["cdp", "chrome", "new-headless"], "expectations": ["FAIL", "PASS"] }, - { - "testIdPattern": "[click.spec] Page.click should click the button after navigation", - "platforms": ["darwin"], - "parameters": ["chrome", "headless", "webDriverBiDi"], - "expectations": ["PASS"] - }, { "testIdPattern": "[click.spec] Page.click should click the button with fixed position inside an iframe", "platforms": ["darwin", "linux", "win32"], @@ -4325,6 +4421,24 @@ "parameters": ["cdp", "chrome", "headful"], "expectations": ["FAIL", "PASS"] }, + { + "testIdPattern": "[oopif.spec] OOPIF-debug OOPIF should support lazy OOP frames", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "headless", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[prerender.spec] Prerender can navigate to a prerendered page via Puppeteer", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "headless", "webDriverBiDi"], + "expectations": ["PASS"] + }, + { + "testIdPattern": "[prerender.spec] Prerender via frame can navigate to a prerendered page via Puppeteer", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "headless", "webDriverBiDi"], + "expectations": ["PASS"] + }, { "testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should be abortable", "platforms": ["darwin", "linux", "win32"],