diff --git a/src/bidiMapper/domains/network/NetworkProcessor.ts b/src/bidiMapper/domains/network/NetworkProcessor.ts index 199d19ea42..e5e7c4b962 100644 --- a/src/bidiMapper/domains/network/NetworkProcessor.ts +++ b/src/bidiMapper/domains/network/NetworkProcessor.ts @@ -101,24 +101,45 @@ export class NetworkProcessor { async continueResponse( params: Network.ContinueResponseParameters ): Promise { - const networkId = params.request; - const {statusCode, reasonPhrase, headers} = params; - const request = this.#getBlockedRequestOrFail(networkId, [ - Network.InterceptPhase.ResponseStarted, - ]); + const {request: networkId, statusCode, reasonPhrase, headers} = params; const responseHeaders: Protocol.Fetch.HeaderEntry[] | undefined = cdpFetchHeadersFromBidiNetworkHeaders(headers); - // TODO: Set / expand. - // ; Step 10. cookies - // ; Step 11. credentials + const request = this.#getBlockedRequestOrFail(networkId, [ + Network.InterceptPhase.AuthRequired, + Network.InterceptPhase.ResponseStarted, + ]); - await request.continueResponse({ - responseCode: statusCode, - responsePhrase: reasonPhrase, - responseHeaders, - }); + if (request.interceptPhase === Network.InterceptPhase.AuthRequired) { + if (params.credentials) { + await Promise.all([ + request.waitNextPhase, + request.continueWithAuth({ + response: 'ProvideCredentials', + username: params.credentials.username, + password: params.credentials.password, + }), + ]); + } else { + // We need to use `ProvideCredentials` + // As `Default` may cancel the request + await request.continueWithAuth({ + response: 'ProvideCredentials', + }); + return {}; + } + } + + if (request.interceptPhase === Network.InterceptPhase.ResponseStarted) { + // TODO: Set / expand. + // ; Step 10. cookies + await request.continueResponse({ + responseCode: statusCode, + responsePhrase: reasonPhrase, + responseHeaders, + }); + } return {}; } @@ -164,12 +185,12 @@ export class NetworkProcessor { request: networkId, }: Network.FailRequestParameters): Promise { const request = this.#getRequestOrFail(networkId); - if (request.currentInterceptPhase === Network.InterceptPhase.AuthRequired) { + if (request.interceptPhase === Network.InterceptPhase.AuthRequired) { throw new InvalidArgumentException( `Request '${networkId}' in 'authRequired' phase cannot be failed` ); } - if (!request.currentInterceptPhase) { + if (!request.interceptPhase) { throw new NoSuchRequestException( `No blocked request found for network id '${networkId}'` ); @@ -205,6 +226,7 @@ export class NetworkProcessor { Network.InterceptPhase.ResponseStarted, Network.InterceptPhase.AuthRequired, ]); + await request.provideResponse({ responseCode: statusCode ?? request.statusCode, responsePhrase: reasonPhrase, @@ -245,17 +267,14 @@ export class NetworkProcessor { phases: Network.InterceptPhase[] ): NetworkRequest { const request = this.#getRequestOrFail(id); - if (!request.currentInterceptPhase) { + if (!request.interceptPhase) { throw new NoSuchRequestException( `No blocked request found for network id '${id}'` ); } - if ( - request.currentInterceptPhase && - !phases.includes(request.currentInterceptPhase) - ) { + if (request.interceptPhase && !phases.includes(request.interceptPhase)) { throw new InvalidArgumentException( - `Blocked request for network id '${id}' is in '${request.currentInterceptPhase}' phase` + `Blocked request for network id '${id}' is in '${request.interceptPhase}' phase` ); } diff --git a/src/bidiMapper/domains/network/NetworkRequest.ts b/src/bidiMapper/domains/network/NetworkRequest.ts index 5eb3263320..d22ad5dd01 100644 --- a/src/bidiMapper/domains/network/NetworkRequest.ts +++ b/src/bidiMapper/domains/network/NetworkRequest.ts @@ -29,6 +29,7 @@ import { type NetworkEvent, } from '../../../protocol/protocol.js'; import {assert} from '../../../utils/assert.js'; +import {Deferred} from '../../../utils/Deferred.js'; import {LogType, type LoggerFn} from '../../../utils/log.js'; import type {CdpTarget} from '../context/CdpTarget.js'; import type {EventManager} from '../session/EventManager.js'; @@ -94,6 +95,8 @@ export class NetworkRequest { [ChromiumBidi.Network.EventNames.ResponseStarted]: false, }; + waitNextPhase = new Deferred(); + constructor( id: Network.Request, eventManager: EventManager, @@ -121,7 +124,7 @@ export class NetworkRequest { /** * When blocked returns the phase for it */ - get currentInterceptPhase(): Network.InterceptPhase | undefined { + get interceptPhase(): Network.InterceptPhase | undefined { return this.#interceptPhase; } @@ -162,6 +165,11 @@ export class NetworkRequest { return Boolean(this.#request.info); } + #phaseChanged() { + this.waitNextPhase.resolve(); + this.waitNextPhase = new Deferred(); + } + #interceptsInPhase(phase: Network.InterceptPhase) { if (!this.#cdpTarget.isSubscribedTo(`network.${phase}`)) { return new Set(); @@ -281,6 +289,7 @@ export class NetworkRequest { this.#emitEventsIfReady({ hasFailed: true, }); + this.#emitEvent(() => { return { method: ChromiumBidi.Network.EventNames.FetchError, @@ -309,6 +318,7 @@ export class NetworkRequest { // CDP https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused if (event.responseStatusCode || event.responseErrorReason) { this.#response.paused = event; + if (this.#isBlockedInPhase(Network.InterceptPhase.ResponseStarted)) { this.#interceptPhase = Network.InterceptPhase.ResponseStarted; } else { @@ -329,6 +339,7 @@ export class NetworkRequest { onAuthRequired(event: Protocol.Fetch.AuthRequiredEvent) { this.#fetchId = event.requestId; this.#request.auth = event; + if (this.#isBlockedInPhase(Network.InterceptPhase.AuthRequired)) { this.#interceptPhase = Network.InterceptPhase.AuthRequired; } else { @@ -446,10 +457,15 @@ export class NetworkRequest { return; } - if (this.#isIgnoredEvent() || this.#emittedEvents[event.method]) { + if ( + this.#isIgnoredEvent() || + (this.#emittedEvents[event.method] && + // Special case this event can be emitted multiple times + event.method !== ChromiumBidi.Network.EventNames.AuthRequired) + ) { return; } - + this.#phaseChanged(); this.#emittedEvents[event.method] = true; this.#eventManager.registerEvent( Object.assign(event, { diff --git a/src/bidiMapper/domains/network/NetworkStorage.spec.ts b/src/bidiMapper/domains/network/NetworkStorage.spec.ts index 40f5376dd6..c3bb5f5024 100644 --- a/src/bidiMapper/domains/network/NetworkStorage.spec.ts +++ b/src/bidiMapper/domains/network/NetworkStorage.spec.ts @@ -41,10 +41,10 @@ function logger(...args: any[]) { } describe('NetworkStorage', () => { - let processedEvents = new Map< + let processedEvents: [ ChromiumBidi.Event['method'], - ChromiumBidi.Event['params'] - >(); + ChromiumBidi.Event['params'], + ][] = []; let eventManager!: EventManager; let networkStorage!: NetworkStorage; let cdpClient!: CdpClient; @@ -54,11 +54,19 @@ describe('NetworkStorage', () => { async function getEvent(name: ChromiumBidi.Event['method']) { await new Promise((resolve) => setTimeout(resolve, 0)); - return processedEvents.get(name); + return processedEvents + .reverse() + .find(([method]) => method === name) + ?.at(1); + } + async function getEvents(name: ChromiumBidi.Event['method']) { + await new Promise((resolve) => setTimeout(resolve, 0)); + + return processedEvents.filter(([method]) => method === name); } beforeEach(() => { - processedEvents = new Map(); + processedEvents = []; const browsingContextStorage = new BrowsingContextStorage(); const cdpTarget = new MockCdpTarget(logger) as unknown as CdpTarget; const browsingContext = { @@ -72,7 +80,7 @@ describe('NetworkStorage', () => { processingQueue = new ProcessingQueue( async ({message}) => { if (message.type === 'event') { - processedEvents.set(message.method, message.params); + processedEvents.push([message.method, message.params]); } return await Promise.resolve(); }, @@ -310,5 +318,16 @@ describe('NetworkStorage', () => { 'request.method': 'GET', }); }); + + it('should work report multiple authRequired', async () => { + const request = new MockCdpNetworkEvents(cdpClient); + + request.authRequired(); + let events = await getEvents('network.authRequired'); + expect(events).to.have.length(1); + request.authRequired(); + events = await getEvents('network.authRequired'); + expect(events).to.have.length(2); + }); }); }); diff --git a/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_response/credentials.py.ini b/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_response/credentials.py.ini deleted file mode 100644 index 96f7356a46..0000000000 --- a/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_response/credentials.py.ini +++ /dev/null @@ -1,12 +0,0 @@ -[credentials.py] - [test_wrong_credentials[fetch\]] - expected: FAIL - - [test_wrong_credentials[navigate\]] - expected: FAIL - - [test_correct_credentials[fetch\]] - expected: FAIL - - [test_correct_credentials[navigate\]] - expected: FAIL diff --git a/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_response/request.py.ini b/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_response/request.py.ini deleted file mode 100644 index 7270088df2..0000000000 --- a/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_response/request.py.ini +++ /dev/null @@ -1,6 +0,0 @@ -[request.py] - [test_continue_auth_required[fetch\]] - expected: FAIL - - [test_continue_auth_required[navigate\]] - expected: FAIL diff --git a/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_with_auth/action.py.ini b/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_with_auth/action.py.ini deleted file mode 100644 index bb985d9f27..0000000000 --- a/wpt-metadata/chromedriver/headful/webdriver/tests/bidi/network/continue_with_auth/action.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[action.py] - [test_provideCredentials_wrong_credentials] - expected: FAIL diff --git a/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_response/credentials.py.ini b/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_response/credentials.py.ini deleted file mode 100644 index 96f7356a46..0000000000 --- a/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_response/credentials.py.ini +++ /dev/null @@ -1,12 +0,0 @@ -[credentials.py] - [test_wrong_credentials[fetch\]] - expected: FAIL - - [test_wrong_credentials[navigate\]] - expected: FAIL - - [test_correct_credentials[fetch\]] - expected: FAIL - - [test_correct_credentials[navigate\]] - expected: FAIL diff --git a/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_response/request.py.ini b/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_response/request.py.ini deleted file mode 100644 index 7270088df2..0000000000 --- a/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_response/request.py.ini +++ /dev/null @@ -1,6 +0,0 @@ -[request.py] - [test_continue_auth_required[fetch\]] - expected: FAIL - - [test_continue_auth_required[navigate\]] - expected: FAIL diff --git a/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini b/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini deleted file mode 100644 index bb985d9f27..0000000000 --- a/wpt-metadata/chromedriver/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini +++ /dev/null @@ -1,3 +0,0 @@ -[action.py] - [test_provideCredentials_wrong_credentials] - expected: FAIL diff --git a/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_response/credentials.py.ini b/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_response/credentials.py.ini deleted file mode 100644 index 96f7356a46..0000000000 --- a/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_response/credentials.py.ini +++ /dev/null @@ -1,12 +0,0 @@ -[credentials.py] - [test_wrong_credentials[fetch\]] - expected: FAIL - - [test_wrong_credentials[navigate\]] - expected: FAIL - - [test_correct_credentials[fetch\]] - expected: FAIL - - [test_correct_credentials[navigate\]] - expected: FAIL diff --git a/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_response/request.py.ini b/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_response/request.py.ini deleted file mode 100644 index 7270088df2..0000000000 --- a/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_response/request.py.ini +++ /dev/null @@ -1,6 +0,0 @@ -[request.py] - [test_continue_auth_required[fetch\]] - expected: FAIL - - [test_continue_auth_required[navigate\]] - expected: FAIL diff --git a/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini b/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini index 35d2d14a58..4f30557e1e 100644 --- a/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini +++ b/wpt-metadata/mapper/headless/webdriver/tests/bidi/network/continue_with_auth/action.py.ini @@ -1,6 +1,3 @@ [action.py] [test_default] expected: FAIL - - [test_provideCredentials_wrong_credentials] - expected: FAIL