fix: make network manager multi session (#10793)
This commit is contained in:
parent
6f2e3db883
commit
085936bd7e
@ -113,7 +113,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
super();
|
super();
|
||||||
this.#client = client;
|
this.#client = client;
|
||||||
this.#page = page;
|
this.#page = page;
|
||||||
this.#networkManager = new NetworkManager(client, ignoreHTTPSErrors, this);
|
this.#networkManager = new NetworkManager(ignoreHTTPSErrors, this);
|
||||||
this.#timeoutSettings = timeoutSettings;
|
this.#timeoutSettings = timeoutSettings;
|
||||||
this.setupEventListeners(this.#client);
|
this.setupEventListeners(this.#client);
|
||||||
client.once(CDPSessionEmittedEvents.Disconnected, () => {
|
client.once(CDPSessionEmittedEvents.Disconnected, () => {
|
||||||
@ -176,12 +176,16 @@ export class FrameManager extends EventEmitter {
|
|||||||
this.#onClientDisconnect().catch(debugError);
|
this.#onClientDisconnect().catch(debugError);
|
||||||
});
|
});
|
||||||
await this.initialize(client);
|
await this.initialize(client);
|
||||||
await this.#networkManager.updateClient(client);
|
await this.#networkManager.addClient(client);
|
||||||
if (frame) {
|
if (frame) {
|
||||||
frame.emit(FrameEmittedEvents.FrameSwappedByActivation);
|
frame.emit(FrameEmittedEvents.FrameSwappedByActivation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async registerSecondaryPage(client: CDPSessionImpl): Promise<void> {
|
||||||
|
await this.#networkManager.addClient(client);
|
||||||
|
}
|
||||||
|
|
||||||
private setupEventListeners(session: CDPSession) {
|
private setupEventListeners(session: CDPSession) {
|
||||||
session.on('Page.frameAttached', event => {
|
session.on('Page.frameAttached', event => {
|
||||||
this.#onFrameAttached(session, event.frameId, event.parentFrameId);
|
this.#onFrameAttached(session, event.frameId, event.parentFrameId);
|
||||||
@ -222,13 +226,13 @@ export class FrameManager extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize(client: CDPSession = this.#client): Promise<void> {
|
async initialize(client: CDPSession): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
const networkInit = this.#networkManager.addClient(client);
|
||||||
const result = await Promise.all([
|
const result = await Promise.all([
|
||||||
client.send('Page.enable'),
|
client.send('Page.enable'),
|
||||||
client.send('Page.getFrameTree'),
|
client.send('Page.getFrameTree'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const {frameTree} = result[1];
|
const {frameTree} = result[1];
|
||||||
this.#handleFrameTree(client, frameTree);
|
this.#handleFrameTree(client, frameTree);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -236,10 +240,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
client.send('Runtime.enable').then(() => {
|
client.send('Runtime.enable').then(() => {
|
||||||
return this.#createIsolatedWorld(client, UTILITY_WORLD_NAME);
|
return this.#createIsolatedWorld(client, UTILITY_WORLD_NAME);
|
||||||
}),
|
}),
|
||||||
// TODO: Network manager is not aware of OOP iframes yet.
|
networkInit,
|
||||||
client === this.#client
|
|
||||||
? this.#networkManager.initialize()
|
|
||||||
: Promise.resolve(),
|
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// The target might have been closed before the initialization finished.
|
// The target might have been closed before the initialization finished.
|
||||||
@ -295,7 +296,7 @@ export class FrameManager extends EventEmitter {
|
|||||||
frame.updateClient(target._session()!);
|
frame.updateClient(target._session()!);
|
||||||
}
|
}
|
||||||
this.setupEventListeners(target._session()!);
|
this.setupEventListeners(target._session()!);
|
||||||
void this.initialize(target._session());
|
void this.initialize(target._session()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,11 +45,12 @@ class MockCDPSession extends EventEmitter {
|
|||||||
describe('NetworkManager', () => {
|
describe('NetworkManager', () => {
|
||||||
it('should process extra info on multiple redirects', async () => {
|
it('should process extra info on multiple redirects', async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
mockCDPSession.emit('Network.requestWillBeSent', {
|
mockCDPSession.emit('Network.requestWillBeSent', {
|
||||||
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
requestId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
loaderId: '7760711DEFCFA23132D98ABA6B4E175C',
|
||||||
@ -476,11 +477,12 @@ describe('NetworkManager', () => {
|
|||||||
});
|
});
|
||||||
it(`should handle "double pause" (crbug.com/1196004) Fetch.requestPaused events for the same Network.requestWillBeSent event`, async () => {
|
it(`should handle "double pause" (crbug.com/1196004) Fetch.requestPaused events for the same Network.requestWillBeSent event`, async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
const manager = new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
await manager.setRequestInterception(true);
|
await manager.setRequestInterception(true);
|
||||||
|
|
||||||
const requests: HTTPRequest[] = [];
|
const requests: HTTPRequest[] = [];
|
||||||
@ -562,11 +564,12 @@ describe('NetworkManager', () => {
|
|||||||
});
|
});
|
||||||
it(`should handle Network.responseReceivedExtraInfo event after Network.responseReceived event (github.com/puppeteer/puppeteer/issues/8234)`, async () => {
|
it(`should handle Network.responseReceivedExtraInfo event after Network.responseReceived event (github.com/puppeteer/puppeteer/issues/8234)`, async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
const manager = new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
|
|
||||||
const requests: HTTPRequest[] = [];
|
const requests: HTTPRequest[] = [];
|
||||||
manager.on(
|
manager.on(
|
||||||
@ -680,11 +683,12 @@ describe('NetworkManager', () => {
|
|||||||
|
|
||||||
it(`should resolve the response once the late responseReceivedExtraInfo event arrives`, async () => {
|
it(`should resolve the response once the late responseReceivedExtraInfo event arrives`, async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
const manager = new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
|
|
||||||
const finishedRequests: HTTPRequest[] = [];
|
const finishedRequests: HTTPRequest[] = [];
|
||||||
const pendingRequests: HTTPRequest[] = [];
|
const pendingRequests: HTTPRequest[] = [];
|
||||||
@ -832,11 +836,12 @@ describe('NetworkManager', () => {
|
|||||||
|
|
||||||
it(`should send responses for iframe that don't receive loadingFinished event`, async () => {
|
it(`should send responses for iframe that don't receive loadingFinished event`, async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
const manager = new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
|
|
||||||
const responses: HTTPResponse[] = [];
|
const responses: HTTPResponse[] = [];
|
||||||
const requests: HTTPRequest[] = [];
|
const requests: HTTPRequest[] = [];
|
||||||
@ -995,11 +1000,12 @@ describe('NetworkManager', () => {
|
|||||||
|
|
||||||
it(`should send responses for iframe that don't receive loadingFinished event`, async () => {
|
it(`should send responses for iframe that don't receive loadingFinished event`, async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
const manager = new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
|
|
||||||
const responses: HTTPResponse[] = [];
|
const responses: HTTPResponse[] = [];
|
||||||
const requests: HTTPRequest[] = [];
|
const requests: HTTPRequest[] = [];
|
||||||
@ -1141,11 +1147,12 @@ describe('NetworkManager', () => {
|
|||||||
|
|
||||||
it(`should handle cached redirects`, async () => {
|
it(`should handle cached redirects`, async () => {
|
||||||
const mockCDPSession = new MockCDPSession();
|
const mockCDPSession = new MockCDPSession();
|
||||||
const manager = new NetworkManager(mockCDPSession, true, {
|
const manager = new NetworkManager(true, {
|
||||||
frame(): Frame | null {
|
frame(): Frame | null {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
await manager.addClient(mockCDPSession);
|
||||||
|
|
||||||
const responses: HTTPResponse[] = [];
|
const responses: HTTPResponse[] = [];
|
||||||
const requests: HTTPRequest[] = [];
|
const requests: HTTPRequest[] = [];
|
||||||
|
@ -16,13 +16,11 @@
|
|||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {Frame} from '../api/Frame.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {createDebuggableDeferred} from '../util/DebuggableDeferred.js';
|
|
||||||
import {Deferred} from '../util/Deferred.js';
|
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession, CDPSessionEmittedEvents} from './Connection.js';
|
||||||
import {EventEmitter, Handler} from './EventEmitter.js';
|
import {EventEmitter, Handler} from './EventEmitter.js';
|
||||||
import {FrameManager} from './FrameManager.js';
|
|
||||||
import {HTTPRequest} from './HTTPRequest.js';
|
import {HTTPRequest} from './HTTPRequest.js';
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
|
import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
|
||||||
@ -68,102 +66,103 @@ export const NetworkManagerEmittedEvents = {
|
|||||||
RequestFinished: Symbol('NetworkManager.RequestFinished'),
|
RequestFinished: Symbol('NetworkManager.RequestFinished'),
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
interface FrameProvider {
|
||||||
|
frame(id: string): Frame | null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class NetworkManager extends EventEmitter {
|
export class NetworkManager extends EventEmitter {
|
||||||
#client: CDPSession;
|
|
||||||
#ignoreHTTPSErrors: boolean;
|
#ignoreHTTPSErrors: boolean;
|
||||||
#frameManager: Pick<FrameManager, 'frame'>;
|
#frameManager: FrameProvider;
|
||||||
#networkEventManager = new NetworkEventManager();
|
#networkEventManager = new NetworkEventManager();
|
||||||
#extraHTTPHeaders: Record<string, string> = {};
|
#extraHTTPHeaders?: Record<string, string>;
|
||||||
#credentials?: Credentials;
|
#credentials?: Credentials;
|
||||||
#attemptedAuthentications = new Set<string>();
|
#attemptedAuthentications = new Set<string>();
|
||||||
#userRequestInterceptionEnabled = false;
|
#userRequestInterceptionEnabled = false;
|
||||||
#protocolRequestInterceptionEnabled = false;
|
#protocolRequestInterceptionEnabled = false;
|
||||||
#userCacheDisabled = false;
|
#userCacheDisabled?: boolean;
|
||||||
#emulatedNetworkConditions: InternalNetworkConditions = {
|
#emulatedNetworkConditions?: InternalNetworkConditions;
|
||||||
offline: false,
|
#userAgent?: string;
|
||||||
upload: -1,
|
#userAgentMetadata?: Protocol.Emulation.UserAgentMetadata;
|
||||||
download: -1,
|
|
||||||
latency: 0,
|
|
||||||
};
|
|
||||||
#deferredInit?: Deferred<void>;
|
|
||||||
|
|
||||||
#handlers = new Map<string, Handler<any>>([
|
#handlers = new Map<string, Function>([
|
||||||
['Fetch.requestPaused', this.#onRequestPaused.bind(this)],
|
['Fetch.requestPaused', this.#onRequestPaused],
|
||||||
['Fetch.authRequired', this.#onAuthRequired.bind(this)],
|
['Fetch.authRequired', this.#onAuthRequired],
|
||||||
['Network.requestWillBeSent', this.#onRequestWillBeSent.bind(this)],
|
['Network.requestWillBeSent', this.#onRequestWillBeSent],
|
||||||
[
|
['Network.requestServedFromCache', this.#onRequestServedFromCache],
|
||||||
'Network.requestServedFromCache',
|
['Network.responseReceived', this.#onResponseReceived],
|
||||||
this.#onRequestServedFromCache.bind(this),
|
['Network.loadingFinished', this.#onLoadingFinished],
|
||||||
],
|
['Network.loadingFailed', this.#onLoadingFailed],
|
||||||
['Network.responseReceived', this.#onResponseReceived.bind(this)],
|
['Network.responseReceivedExtraInfo', this.#onResponseReceivedExtraInfo],
|
||||||
['Network.loadingFinished', this.#onLoadingFinished.bind(this)],
|
|
||||||
['Network.loadingFailed', this.#onLoadingFailed.bind(this)],
|
|
||||||
[
|
|
||||||
'Network.responseReceivedExtraInfo',
|
|
||||||
this.#onResponseReceivedExtraInfo.bind(this),
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
constructor(
|
#clients = new Map<
|
||||||
client: CDPSession,
|
CDPSession,
|
||||||
ignoreHTTPSErrors: boolean,
|
Array<{event: string | symbol; handler: Handler}>
|
||||||
frameManager: Pick<FrameManager, 'frame'>
|
>();
|
||||||
) {
|
|
||||||
|
constructor(ignoreHTTPSErrors: boolean, frameManager: FrameProvider) {
|
||||||
super();
|
super();
|
||||||
this.#client = client;
|
|
||||||
this.#ignoreHTTPSErrors = ignoreHTTPSErrors;
|
this.#ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||||
this.#frameManager = frameManager;
|
this.#frameManager = frameManager;
|
||||||
|
|
||||||
for (const [event, handler] of this.#handlers) {
|
|
||||||
this.#client.on(event, handler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateClient(client: CDPSession): Promise<void> {
|
async addClient(client: CDPSession): Promise<void> {
|
||||||
this.#client = client;
|
if (this.#clients.has(client)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const listeners: Array<{event: string | symbol; handler: Handler}> = [];
|
||||||
|
this.#clients.set(client, listeners);
|
||||||
for (const [event, handler] of this.#handlers) {
|
for (const [event, handler] of this.#handlers) {
|
||||||
this.#client.on(event, handler);
|
listeners.push({
|
||||||
|
event,
|
||||||
|
handler: handler.bind(this, client),
|
||||||
|
});
|
||||||
|
client.on(event, listeners.at(-1)!.handler);
|
||||||
}
|
}
|
||||||
this.#deferredInit = undefined;
|
listeners.push({
|
||||||
await this.initialize();
|
event: CDPSessionEmittedEvents.Disconnected,
|
||||||
}
|
handler: this.#removeClient.bind(this, client),
|
||||||
|
});
|
||||||
/**
|
client.on(CDPSessionEmittedEvents.Disconnected, listeners.at(-1)!.handler);
|
||||||
* Initialize calls should avoid async dependencies between CDP calls as those
|
await Promise.all([
|
||||||
* might not resolve until after the target is resumed causing a deadlock.
|
|
||||||
*/
|
|
||||||
initialize(): Promise<void> {
|
|
||||||
if (this.#deferredInit) {
|
|
||||||
return this.#deferredInit.valueOrThrow();
|
|
||||||
}
|
|
||||||
this.#deferredInit = createDebuggableDeferred(
|
|
||||||
'NetworkManager initialization timed out'
|
|
||||||
);
|
|
||||||
const init = Promise.all([
|
|
||||||
this.#ignoreHTTPSErrors
|
this.#ignoreHTTPSErrors
|
||||||
? this.#client.send('Security.setIgnoreCertificateErrors', {
|
? client.send('Security.setIgnoreCertificateErrors', {
|
||||||
ignore: true,
|
ignore: true,
|
||||||
})
|
})
|
||||||
: null,
|
: null,
|
||||||
this.#client.send('Network.enable'),
|
client.send('Network.enable'),
|
||||||
|
this.#applyExtraHTTPHeaders(client),
|
||||||
|
this.#applyNetworkConditions(client),
|
||||||
|
this.#applyProtocolCacheDisabled(client),
|
||||||
|
this.#applyProtocolRequestInterception(client),
|
||||||
|
this.#applyUserAgent(client),
|
||||||
]);
|
]);
|
||||||
const deferredInitPromise = this.#deferredInit;
|
}
|
||||||
init
|
|
||||||
.then(() => {
|
async #removeClient(client: CDPSession) {
|
||||||
deferredInitPromise.resolve();
|
const listeners = this.#clients.get(client);
|
||||||
})
|
for (const {event, handler} of listeners || []) {
|
||||||
.catch(err => {
|
client.off(event, handler);
|
||||||
deferredInitPromise.reject(err);
|
}
|
||||||
});
|
this.#clients.delete(client);
|
||||||
return this.#deferredInit.valueOrThrow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(credentials?: Credentials): Promise<void> {
|
async authenticate(credentials?: Credentials): Promise<void> {
|
||||||
this.#credentials = credentials;
|
this.#credentials = credentials;
|
||||||
await this.#updateProtocolRequestInterception();
|
const enabled = this.#userRequestInterceptionEnabled || !!this.#credentials;
|
||||||
|
if (enabled === this.#protocolRequestInterceptionEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#protocolRequestInterceptionEnabled = enabled;
|
||||||
|
await this.#applyToAllClients(
|
||||||
|
this.#applyProtocolRequestInterception.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExtraHTTPHeaders(
|
async setExtraHTTPHeaders(
|
||||||
@ -178,7 +177,15 @@ export class NetworkManager extends EventEmitter {
|
|||||||
);
|
);
|
||||||
this.#extraHTTPHeaders[key.toLowerCase()] = value;
|
this.#extraHTTPHeaders[key.toLowerCase()] = value;
|
||||||
}
|
}
|
||||||
await this.#client.send('Network.setExtraHTTPHeaders', {
|
|
||||||
|
await this.#applyToAllClients(this.#applyExtraHTTPHeaders.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async #applyExtraHTTPHeaders(client: CDPSession) {
|
||||||
|
if (this.#extraHTTPHeaders === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await client.send('Network.setExtraHTTPHeaders', {
|
||||||
headers: this.#extraHTTPHeaders,
|
headers: this.#extraHTTPHeaders,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -192,13 +199,29 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setOfflineMode(value: boolean): Promise<void> {
|
async setOfflineMode(value: boolean): Promise<void> {
|
||||||
|
if (!this.#emulatedNetworkConditions) {
|
||||||
|
this.#emulatedNetworkConditions = {
|
||||||
|
offline: false,
|
||||||
|
upload: -1,
|
||||||
|
download: -1,
|
||||||
|
latency: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
this.#emulatedNetworkConditions.offline = value;
|
this.#emulatedNetworkConditions.offline = value;
|
||||||
await this.#updateNetworkConditions();
|
await this.#applyToAllClients(this.#applyNetworkConditions.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async emulateNetworkConditions(
|
async emulateNetworkConditions(
|
||||||
networkConditions: NetworkConditions | null
|
networkConditions: NetworkConditions | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (!this.#emulatedNetworkConditions) {
|
||||||
|
this.#emulatedNetworkConditions = {
|
||||||
|
offline: false,
|
||||||
|
upload: -1,
|
||||||
|
download: -1,
|
||||||
|
latency: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
this.#emulatedNetworkConditions.upload = networkConditions
|
this.#emulatedNetworkConditions.upload = networkConditions
|
||||||
? networkConditions.upload
|
? networkConditions.upload
|
||||||
: -1;
|
: -1;
|
||||||
@ -209,11 +232,22 @@ export class NetworkManager extends EventEmitter {
|
|||||||
? networkConditions.latency
|
? networkConditions.latency
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
await this.#updateNetworkConditions();
|
await this.#applyToAllClients(this.#applyNetworkConditions.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async #updateNetworkConditions(): Promise<void> {
|
async #applyToAllClients(fn: (client: CDPSession) => Promise<unknown>) {
|
||||||
await this.#client.send('Network.emulateNetworkConditions', {
|
await Promise.all(
|
||||||
|
Array.from(this.#clients.keys()).map(client => {
|
||||||
|
return fn(client);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #applyNetworkConditions(client: CDPSession): Promise<void> {
|
||||||
|
if (this.#emulatedNetworkConditions === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await client.send('Network.emulateNetworkConditions', {
|
||||||
offline: this.#emulatedNetworkConditions.offline,
|
offline: this.#emulatedNetworkConditions.offline,
|
||||||
latency: this.#emulatedNetworkConditions.latency,
|
latency: this.#emulatedNetworkConditions.latency,
|
||||||
uploadThroughput: this.#emulatedNetworkConditions.upload,
|
uploadThroughput: this.#emulatedNetworkConditions.upload,
|
||||||
@ -225,55 +259,71 @@ export class NetworkManager extends EventEmitter {
|
|||||||
userAgent: string,
|
userAgent: string,
|
||||||
userAgentMetadata?: Protocol.Emulation.UserAgentMetadata
|
userAgentMetadata?: Protocol.Emulation.UserAgentMetadata
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.#client.send('Network.setUserAgentOverride', {
|
this.#userAgent = userAgent;
|
||||||
userAgent: userAgent,
|
this.#userAgentMetadata = userAgentMetadata;
|
||||||
userAgentMetadata: userAgentMetadata,
|
await this.#applyToAllClients(this.#applyUserAgent.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async #applyUserAgent(client: CDPSession) {
|
||||||
|
if (this.#userAgent === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await client.send('Network.setUserAgentOverride', {
|
||||||
|
userAgent: this.#userAgent,
|
||||||
|
userAgentMetadata: this.#userAgentMetadata,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCacheEnabled(enabled: boolean): Promise<void> {
|
async setCacheEnabled(enabled: boolean): Promise<void> {
|
||||||
this.#userCacheDisabled = !enabled;
|
this.#userCacheDisabled = !enabled;
|
||||||
await this.#updateProtocolCacheDisabled();
|
await this.#applyToAllClients(this.#applyProtocolCacheDisabled.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async setRequestInterception(value: boolean): Promise<void> {
|
async setRequestInterception(value: boolean): Promise<void> {
|
||||||
this.#userRequestInterceptionEnabled = value;
|
this.#userRequestInterceptionEnabled = value;
|
||||||
await this.#updateProtocolRequestInterception();
|
|
||||||
}
|
|
||||||
|
|
||||||
async #updateProtocolRequestInterception(): Promise<void> {
|
|
||||||
const enabled = this.#userRequestInterceptionEnabled || !!this.#credentials;
|
const enabled = this.#userRequestInterceptionEnabled || !!this.#credentials;
|
||||||
if (enabled === this.#protocolRequestInterceptionEnabled) {
|
if (enabled === this.#protocolRequestInterceptionEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#protocolRequestInterceptionEnabled = enabled;
|
this.#protocolRequestInterceptionEnabled = enabled;
|
||||||
if (enabled) {
|
await this.#applyToAllClients(
|
||||||
|
this.#applyProtocolRequestInterception.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #applyProtocolRequestInterception(client: CDPSession): Promise<void> {
|
||||||
|
if (this.#userCacheDisabled === undefined) {
|
||||||
|
this.#userCacheDisabled = false;
|
||||||
|
}
|
||||||
|
if (this.#protocolRequestInterceptionEnabled) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.#updateProtocolCacheDisabled(),
|
this.#applyProtocolCacheDisabled(client),
|
||||||
this.#client.send('Fetch.enable', {
|
client.send('Fetch.enable', {
|
||||||
handleAuthRequests: true,
|
handleAuthRequests: true,
|
||||||
patterns: [{urlPattern: '*'}],
|
patterns: [{urlPattern: '*'}],
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.#updateProtocolCacheDisabled(),
|
this.#applyProtocolCacheDisabled(client),
|
||||||
this.#client.send('Fetch.disable'),
|
client.send('Fetch.disable'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#cacheDisabled(): boolean {
|
async #applyProtocolCacheDisabled(client: CDPSession): Promise<void> {
|
||||||
return this.#userCacheDisabled;
|
if (this.#userCacheDisabled === undefined) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
async #updateProtocolCacheDisabled(): Promise<void> {
|
await client.send('Network.setCacheDisabled', {
|
||||||
await this.#client.send('Network.setCacheDisabled', {
|
cacheDisabled: this.#userCacheDisabled,
|
||||||
cacheDisabled: this.#cacheDisabled(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#onRequestWillBeSent(event: Protocol.Network.RequestWillBeSentEvent): void {
|
#onRequestWillBeSent(
|
||||||
|
client: CDPSession,
|
||||||
|
event: Protocol.Network.RequestWillBeSentEvent
|
||||||
|
): void {
|
||||||
// Request interception doesn't happen for data URLs with Network Service.
|
// Request interception doesn't happen for data URLs with Network Service.
|
||||||
if (
|
if (
|
||||||
this.#userRequestInterceptionEnabled &&
|
this.#userRequestInterceptionEnabled &&
|
||||||
@ -291,16 +341,19 @@ export class NetworkManager extends EventEmitter {
|
|||||||
if (requestPausedEvent) {
|
if (requestPausedEvent) {
|
||||||
const {requestId: fetchRequestId} = requestPausedEvent;
|
const {requestId: fetchRequestId} = requestPausedEvent;
|
||||||
this.#patchRequestEventHeaders(event, requestPausedEvent);
|
this.#patchRequestEventHeaders(event, requestPausedEvent);
|
||||||
this.#onRequest(event, fetchRequestId);
|
this.#onRequest(client, event, fetchRequestId);
|
||||||
this.#networkEventManager.forgetRequestPaused(networkRequestId);
|
this.#networkEventManager.forgetRequestPaused(networkRequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#onRequest(event, undefined);
|
this.#onRequest(client, event, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
#onAuthRequired(event: Protocol.Fetch.AuthRequiredEvent): void {
|
#onAuthRequired(
|
||||||
|
client: CDPSession,
|
||||||
|
event: Protocol.Fetch.AuthRequiredEvent
|
||||||
|
): void {
|
||||||
let response: Protocol.Fetch.AuthChallengeResponse['response'] = 'Default';
|
let response: Protocol.Fetch.AuthChallengeResponse['response'] = 'Default';
|
||||||
if (this.#attemptedAuthentications.has(event.requestId)) {
|
if (this.#attemptedAuthentications.has(event.requestId)) {
|
||||||
response = 'CancelAuth';
|
response = 'CancelAuth';
|
||||||
@ -312,7 +365,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
username: undefined,
|
username: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
};
|
};
|
||||||
this.#client
|
client
|
||||||
.send('Fetch.continueWithAuth', {
|
.send('Fetch.continueWithAuth', {
|
||||||
requestId: event.requestId,
|
requestId: event.requestId,
|
||||||
authChallengeResponse: {response, username, password},
|
authChallengeResponse: {response, username, password},
|
||||||
@ -327,12 +380,15 @@ export class NetworkManager extends EventEmitter {
|
|||||||
* CDP may send multiple Fetch.requestPaused
|
* CDP may send multiple Fetch.requestPaused
|
||||||
* for the same Network.requestWillBeSent.
|
* for the same Network.requestWillBeSent.
|
||||||
*/
|
*/
|
||||||
#onRequestPaused(event: Protocol.Fetch.RequestPausedEvent): void {
|
#onRequestPaused(
|
||||||
|
client: CDPSession,
|
||||||
|
event: Protocol.Fetch.RequestPausedEvent
|
||||||
|
): void {
|
||||||
if (
|
if (
|
||||||
!this.#userRequestInterceptionEnabled &&
|
!this.#userRequestInterceptionEnabled &&
|
||||||
this.#protocolRequestInterceptionEnabled
|
this.#protocolRequestInterceptionEnabled
|
||||||
) {
|
) {
|
||||||
this.#client
|
client
|
||||||
.send('Fetch.continueRequest', {
|
.send('Fetch.continueRequest', {
|
||||||
requestId: event.requestId,
|
requestId: event.requestId,
|
||||||
})
|
})
|
||||||
@ -342,7 +398,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
const {networkId: networkRequestId, requestId: fetchRequestId} = event;
|
const {networkId: networkRequestId, requestId: fetchRequestId} = event;
|
||||||
|
|
||||||
if (!networkRequestId) {
|
if (!networkRequestId) {
|
||||||
this.#onRequestWithoutNetworkInstrumentation(event);
|
this.#onRequestWithoutNetworkInstrumentation(client, event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +420,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
|
|
||||||
if (requestWillBeSentEvent) {
|
if (requestWillBeSentEvent) {
|
||||||
this.#patchRequestEventHeaders(requestWillBeSentEvent, event);
|
this.#patchRequestEventHeaders(requestWillBeSentEvent, event);
|
||||||
this.#onRequest(requestWillBeSentEvent, fetchRequestId);
|
this.#onRequest(client, requestWillBeSentEvent, fetchRequestId);
|
||||||
} else {
|
} else {
|
||||||
this.#networkEventManager.storeRequestPaused(networkRequestId, event);
|
this.#networkEventManager.storeRequestPaused(networkRequestId, event);
|
||||||
}
|
}
|
||||||
@ -382,6 +438,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#onRequestWithoutNetworkInstrumentation(
|
#onRequestWithoutNetworkInstrumentation(
|
||||||
|
client: CDPSession,
|
||||||
event: Protocol.Fetch.RequestPausedEvent
|
event: Protocol.Fetch.RequestPausedEvent
|
||||||
): void {
|
): void {
|
||||||
// If an event has no networkId it should not have any network events. We
|
// If an event has no networkId it should not have any network events. We
|
||||||
@ -391,7 +448,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const request = new HTTPRequest(
|
const request = new HTTPRequest(
|
||||||
this.#client,
|
client,
|
||||||
frame,
|
frame,
|
||||||
event.requestId,
|
event.requestId,
|
||||||
this.#userRequestInterceptionEnabled,
|
this.#userRequestInterceptionEnabled,
|
||||||
@ -403,6 +460,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#onRequest(
|
#onRequest(
|
||||||
|
client: CDPSession,
|
||||||
event: Protocol.Network.RequestWillBeSentEvent,
|
event: Protocol.Network.RequestWillBeSentEvent,
|
||||||
fetchRequestId?: FetchRequestId
|
fetchRequestId?: FetchRequestId
|
||||||
): void {
|
): void {
|
||||||
@ -434,6 +492,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
// requestWillBeSent event.
|
// requestWillBeSent event.
|
||||||
if (request) {
|
if (request) {
|
||||||
this.#handleRequestRedirect(
|
this.#handleRequestRedirect(
|
||||||
|
client,
|
||||||
request,
|
request,
|
||||||
event.redirectResponse,
|
event.redirectResponse,
|
||||||
redirectResponseExtraInfo
|
redirectResponseExtraInfo
|
||||||
@ -446,7 +505,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
const request = new HTTPRequest(
|
const request = new HTTPRequest(
|
||||||
this.#client,
|
client,
|
||||||
frame,
|
frame,
|
||||||
fetchRequestId,
|
fetchRequestId,
|
||||||
this.#userRequestInterceptionEnabled,
|
this.#userRequestInterceptionEnabled,
|
||||||
@ -459,6 +518,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#onRequestServedFromCache(
|
#onRequestServedFromCache(
|
||||||
|
_client: CDPSession,
|
||||||
event: Protocol.Network.RequestServedFromCacheEvent
|
event: Protocol.Network.RequestServedFromCacheEvent
|
||||||
): void {
|
): void {
|
||||||
const request = this.#networkEventManager.getRequest(event.requestId);
|
const request = this.#networkEventManager.getRequest(event.requestId);
|
||||||
@ -469,12 +529,13 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#handleRequestRedirect(
|
#handleRequestRedirect(
|
||||||
|
client: CDPSession,
|
||||||
request: HTTPRequest,
|
request: HTTPRequest,
|
||||||
responsePayload: Protocol.Network.Response,
|
responsePayload: Protocol.Network.Response,
|
||||||
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
||||||
): void {
|
): void {
|
||||||
const response = new HTTPResponse(
|
const response = new HTTPResponse(
|
||||||
this.#client,
|
client,
|
||||||
request,
|
request,
|
||||||
responsePayload,
|
responsePayload,
|
||||||
extraInfo
|
extraInfo
|
||||||
@ -490,6 +551,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#emitResponseEvent(
|
#emitResponseEvent(
|
||||||
|
client: CDPSession,
|
||||||
responseReceived: Protocol.Network.ResponseReceivedEvent,
|
responseReceived: Protocol.Network.ResponseReceivedEvent,
|
||||||
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
extraInfo: Protocol.Network.ResponseReceivedExtraInfoEvent | null
|
||||||
): void {
|
): void {
|
||||||
@ -521,7 +583,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const response = new HTTPResponse(
|
const response = new HTTPResponse(
|
||||||
this.#client,
|
client,
|
||||||
request,
|
request,
|
||||||
responseReceived.response,
|
responseReceived.response,
|
||||||
extraInfo
|
extraInfo
|
||||||
@ -530,7 +592,10 @@ export class NetworkManager extends EventEmitter {
|
|||||||
this.emit(NetworkManagerEmittedEvents.Response, response);
|
this.emit(NetworkManagerEmittedEvents.Response, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
#onResponseReceived(event: Protocol.Network.ResponseReceivedEvent): void {
|
#onResponseReceived(
|
||||||
|
client: CDPSession,
|
||||||
|
event: Protocol.Network.ResponseReceivedEvent
|
||||||
|
): void {
|
||||||
const request = this.#networkEventManager.getRequest(event.requestId);
|
const request = this.#networkEventManager.getRequest(event.requestId);
|
||||||
let extraInfo = null;
|
let extraInfo = null;
|
||||||
if (request && !request._fromMemoryCache && event.hasExtraInfo) {
|
if (request && !request._fromMemoryCache && event.hasExtraInfo) {
|
||||||
@ -545,10 +610,11 @@ export class NetworkManager extends EventEmitter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.#emitResponseEvent(event, extraInfo);
|
this.#emitResponseEvent(client, event, extraInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#onResponseReceivedExtraInfo(
|
#onResponseReceivedExtraInfo(
|
||||||
|
client: CDPSession,
|
||||||
event: Protocol.Network.ResponseReceivedExtraInfoEvent
|
event: Protocol.Network.ResponseReceivedExtraInfoEvent
|
||||||
): void {
|
): void {
|
||||||
// We may have skipped a redirect response/request pair due to waiting for
|
// We may have skipped a redirect response/request pair due to waiting for
|
||||||
@ -559,7 +625,7 @@ export class NetworkManager extends EventEmitter {
|
|||||||
);
|
);
|
||||||
if (redirectInfo) {
|
if (redirectInfo) {
|
||||||
this.#networkEventManager.responseExtraInfo(event.requestId).push(event);
|
this.#networkEventManager.responseExtraInfo(event.requestId).push(event);
|
||||||
this.#onRequest(redirectInfo.event, redirectInfo.fetchRequestId);
|
this.#onRequest(client, redirectInfo.event, redirectInfo.fetchRequestId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,7 +636,11 @@ export class NetworkManager extends EventEmitter {
|
|||||||
);
|
);
|
||||||
if (queuedEvents) {
|
if (queuedEvents) {
|
||||||
this.#networkEventManager.forgetQueuedEventGroup(event.requestId);
|
this.#networkEventManager.forgetQueuedEventGroup(event.requestId);
|
||||||
this.#emitResponseEvent(queuedEvents.responseReceivedEvent, event);
|
this.#emitResponseEvent(
|
||||||
|
client,
|
||||||
|
queuedEvents.responseReceivedEvent,
|
||||||
|
event
|
||||||
|
);
|
||||||
if (queuedEvents.loadingFinishedEvent) {
|
if (queuedEvents.loadingFinishedEvent) {
|
||||||
this.#emitLoadingFinished(queuedEvents.loadingFinishedEvent);
|
this.#emitLoadingFinished(queuedEvents.loadingFinishedEvent);
|
||||||
}
|
}
|
||||||
@ -597,7 +667,10 @@ export class NetworkManager extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#onLoadingFinished(event: Protocol.Network.LoadingFinishedEvent): void {
|
#onLoadingFinished(
|
||||||
|
_client: CDPSession,
|
||||||
|
event: Protocol.Network.LoadingFinishedEvent
|
||||||
|
): void {
|
||||||
// If the response event for this request is still waiting on a
|
// If the response event for this request is still waiting on a
|
||||||
// corresponding ExtraInfo event, then wait to emit this event too.
|
// corresponding ExtraInfo event, then wait to emit this event too.
|
||||||
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(
|
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(
|
||||||
@ -627,7 +700,10 @@ export class NetworkManager extends EventEmitter {
|
|||||||
this.emit(NetworkManagerEmittedEvents.RequestFinished, request);
|
this.emit(NetworkManagerEmittedEvents.RequestFinished, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
#onLoadingFailed(event: Protocol.Network.LoadingFailedEvent): void {
|
#onLoadingFailed(
|
||||||
|
_client: CDPSession,
|
||||||
|
event: Protocol.Network.LoadingFailedEvent
|
||||||
|
): void {
|
||||||
// If the response event for this request is still waiting on a
|
// If the response event for this request is still waiting on a
|
||||||
// corresponding ExtraInfo event, then wait to emit this event too.
|
// corresponding ExtraInfo event, then wait to emit this event too.
|
||||||
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(
|
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(
|
||||||
|
@ -329,6 +329,15 @@ export class CDPPage extends Page {
|
|||||||
await this.#frameManager.swapFrameTree(newSession);
|
await this.#frameManager.swapFrameTree(newSession);
|
||||||
this.#setupEventListeners();
|
this.#setupEventListeners();
|
||||||
});
|
});
|
||||||
|
this.#tabSession?.on(
|
||||||
|
CDPSessionEmittedEvents.Ready,
|
||||||
|
(session: CDPSessionImpl) => {
|
||||||
|
if (session._target()._subtype() !== 'prerender') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#frameManager.registerSecondaryPage(session).catch(debugError);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#setupEventListeners() {
|
#setupEventListeners() {
|
||||||
@ -399,7 +408,7 @@ export class CDPPage extends Page {
|
|||||||
async #initialize(): Promise<void> {
|
async #initialize(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.#frameManager.initialize(),
|
this.#frameManager.initialize(this.#client),
|
||||||
this.#client.send('Performance.enable'),
|
this.#client.send('Performance.enable'),
|
||||||
this.#client.send('Log.enable'),
|
this.#client.send('Log.enable'),
|
||||||
]);
|
]);
|
||||||
|
@ -3911,6 +3911,12 @@
|
|||||||
"parameters": ["firefox", "webDriverBiDi"],
|
"parameters": ["firefox", "webDriverBiDi"],
|
||||||
"expectations": ["SKIP"]
|
"expectations": ["SKIP"]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"testIdPattern": "[prerender.spec] Prerender with network requests can receive requests from the prerendered page",
|
||||||
|
"platforms": ["darwin", "linux", "win32"],
|
||||||
|
"parameters": ["firefox", "webDriverBiDi"],
|
||||||
|
"expectations": ["SKIP"]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"testIdPattern": "[proxy.spec] request proxy in incognito browser context should proxy requests when configured at browser level",
|
"testIdPattern": "[proxy.spec] request proxy in incognito browser context should proxy requests when configured at browser level",
|
||||||
"platforms": ["darwin", "linux", "win32"],
|
"platforms": ["darwin", "linux", "win32"],
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
<a id="navigate-within-document" href="#nav">Navigate within document</a>
|
<a id="navigate-within-document" href="#nav">Navigate within document</a>
|
||||||
<a name="nav"></a>
|
<a name="nav"></a>
|
||||||
|
<script>
|
||||||
|
fetch('oopif.html?requestFromOOPIF')
|
||||||
|
</script>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<head></head>
|
<head>
|
||||||
|
<script>fetch('target.html?fromPrerendered')</script>
|
||||||
</head>
|
</head>
|
||||||
<body>target</body>
|
<body>target</body>
|
||||||
|
@ -307,17 +307,23 @@ describeWithDebugLogs('OOPIF', function () {
|
|||||||
it('should load oopif iframes with subresources and request interception', async () => {
|
it('should load oopif iframes with subresources and request interception', async () => {
|
||||||
const {server, page, context} = state;
|
const {server, page, context} = state;
|
||||||
|
|
||||||
const frame = page.waitForFrame(frame => {
|
const framePromise = page.waitForFrame(frame => {
|
||||||
return frame.url().endsWith('/oopif.html');
|
return frame.url().endsWith('/oopif.html');
|
||||||
});
|
});
|
||||||
await page.setRequestInterception(true);
|
|
||||||
page.on('request', request => {
|
page.on('request', request => {
|
||||||
return request.continue();
|
void request.continue();
|
||||||
|
});
|
||||||
|
await page.setRequestInterception(true);
|
||||||
|
const requestPromise = page.waitForRequest(request => {
|
||||||
|
return request.url().includes('requestFromOOPIF');
|
||||||
});
|
});
|
||||||
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
await page.goto(server.PREFIX + '/dynamic-oopif.html');
|
||||||
await frame;
|
const frame = await framePromise;
|
||||||
|
const request = await requestPromise;
|
||||||
expect(oopifs(context)).toHaveLength(1);
|
expect(oopifs(context)).toHaveLength(1);
|
||||||
|
expect(request.frame()).toBe(frame);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support frames within OOP iframes', async () => {
|
it('should support frames within OOP iframes', async () => {
|
||||||
const {server, page} = state;
|
const {server, page} = state;
|
||||||
|
|
||||||
|
@ -89,4 +89,44 @@ describe('Prerender', function () {
|
|||||||
expect(mainFrame).toBe(page.mainFrame());
|
expect(mainFrame).toBe(page.mainFrame());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with network requests', () => {
|
||||||
|
it('can receive requests from the prerendered page', async () => {
|
||||||
|
const {page, server} = await getTestState();
|
||||||
|
|
||||||
|
const urls: string[] = [];
|
||||||
|
page.on('request', request => {
|
||||||
|
urls.push(request.url());
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto(server.PREFIX + '/prerender/index.html');
|
||||||
|
const button = await page.waitForSelector('button');
|
||||||
|
await button?.click();
|
||||||
|
const mainFrame = page.mainFrame();
|
||||||
|
const link = await mainFrame.waitForSelector('a');
|
||||||
|
await Promise.all([mainFrame.waitForNavigation(), link?.click()]);
|
||||||
|
expect(mainFrame).toBe(page.mainFrame());
|
||||||
|
expect(
|
||||||
|
await mainFrame.evaluate(() => {
|
||||||
|
return document.body.innerText;
|
||||||
|
})
|
||||||
|
).toBe('target');
|
||||||
|
expect(mainFrame).toBe(page.mainFrame());
|
||||||
|
expect(
|
||||||
|
urls.find(url => {
|
||||||
|
return url.endsWith('prerender/target.html');
|
||||||
|
})
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
urls.find(url => {
|
||||||
|
return url.includes('prerender/index.html');
|
||||||
|
})
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
urls.find(url => {
|
||||||
|
return url.includes('prerender/target.html?fromPrerendered');
|
||||||
|
})
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user