feat: expose other sessions from connection (#6863)

This commit is contained in:
Patrick Hulce 2021-05-07 03:31:39 -05:00 committed by GitHub
parent 2605309f74
commit cb285a2379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 2 deletions

View File

@ -357,6 +357,7 @@
* [target.url()](#targeturl) * [target.url()](#targeturl)
* [target.worker()](#targetworker) * [target.worker()](#targetworker)
- [class: CDPSession](#class-cdpsession) - [class: CDPSession](#class-cdpsession)
* [cdpSession.connection()](#cdpsessionconnection)
* [cdpSession.detach()](#cdpsessiondetach) * [cdpSession.detach()](#cdpsessiondetach)
* [cdpSession.send(method[, ...paramArgs])](#cdpsessionsendmethod-paramargs) * [cdpSession.send(method[, ...paramArgs])](#cdpsessionsendmethod-paramargs)
- [class: Coverage](#class-coverage) - [class: Coverage](#class-coverage)
@ -4558,6 +4559,12 @@ await client.send('Animation.setPlaybackRate', {
}); });
``` ```
#### cdpSession.connection()
- returns: <[Connection]>
Returns the underlying connection associated with the session. Can be used to obtain other related sessions.
#### cdpSession.detach() #### cdpSession.detach()
- returns: <[Promise]> - returns: <[Promise]>

View File

@ -125,11 +125,13 @@ export class Connection extends EventEmitter {
sessionId sessionId
); );
this._sessions.set(sessionId, session); this._sessions.set(sessionId, session);
this.emit('sessionattached', session);
} else if (object.method === 'Target.detachedFromTarget') { } else if (object.method === 'Target.detachedFromTarget') {
const session = this._sessions.get(object.params.sessionId); const session = this._sessions.get(object.params.sessionId);
if (session) { if (session) {
session._onClosed(); session._onClosed();
this._sessions.delete(object.params.sessionId); this._sessions.delete(object.params.sessionId);
this.emit('sessiondetached', session);
} }
} }
if (object.sessionId) { if (object.sessionId) {
@ -253,6 +255,10 @@ export class CDPSession extends EventEmitter {
this._sessionId = sessionId; this._sessionId = sessionId;
} }
connection(): Connection {
return this._connection;
}
send<T extends keyof ProtocolMapping.Commands>( send<T extends keyof ProtocolMapping.Commands>(
method: T, method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType'] ...paramArgs: ProtocolMapping.Commands[T]['paramsType']

View File

@ -488,8 +488,16 @@ export class Page extends EventEmitter {
this._viewport = null; this._viewport = null;
client.on('Target.attachedToTarget', (event) => { client.on('Target.attachedToTarget', (event) => {
if (event.targetInfo.type !== 'worker') { if (
event.targetInfo.type !== 'worker' &&
event.targetInfo.type !== 'iframe'
) {
// If we don't detach from service workers, they will never die. // If we don't detach from service workers, they will never die.
// We still want to attach to workers for emitting events.
// We still want to attach to iframes so sessions may interact with them.
// We detach from all other types out of an abundance of caution.
// See https://source.chromium.org/chromium/chromium/src/+/master:content/browser/devtools/devtools_agent_host_impl.cc?q=f:devtools%20-f:out%20%22::kTypePage%5B%5D%22&ss=chromium
// for the complete list of available types.
client client
.send('Target.detachFromTarget', { .send('Target.detachFromTarget', {
sessionId: event.sessionId, sessionId: event.sessionId,

View File

@ -42,9 +42,10 @@ describeChromeOnly('headful tests', function () {
let headfulOptions; let headfulOptions;
let headlessOptions; let headlessOptions;
let extensionOptions; let extensionOptions;
let forcedOopifOptions;
beforeEach(() => { beforeEach(() => {
const { defaultBrowserOptions } = getTestState(); const { server, defaultBrowserOptions } = getTestState();
headfulOptions = Object.assign({}, defaultBrowserOptions, { headfulOptions = Object.assign({}, defaultBrowserOptions, {
headless: false, headless: false,
}); });
@ -59,6 +60,18 @@ describeChromeOnly('headful tests', function () {
`--load-extension=${extensionPath}`, `--load-extension=${extensionPath}`,
], ],
}); });
forcedOopifOptions = Object.assign({}, defaultBrowserOptions, {
headless: false,
devtools: true,
args: [
`--host-rules=MAP oopifdomain 127.0.0.1`,
`--isolate-origins=${server.PREFIX.replace(
'localhost',
'oopifdomain'
)}`,
],
});
}); });
describe('HEADFUL', function () { describe('HEADFUL', function () {
@ -147,6 +160,58 @@ describeChromeOnly('headful tests', function () {
expect(urls).toEqual([server.EMPTY_PAGE, 'https://google.com/']); expect(urls).toEqual([server.EMPTY_PAGE, 'https://google.com/']);
await browser.close(); await browser.close();
}); });
it('OOPIF: should expose events within OOPIFs', async () => {
const { server, puppeteer } = getTestState();
const browser = await puppeteer.launch(forcedOopifOptions);
const page = await browser.newPage();
// Setup our session listeners to observe OOPIF activity.
const session = await page.target().createCDPSession();
const networkEvents = [];
const otherSessions = [];
await session.send('Target.setAutoAttach', {
autoAttach: true,
flatten: true,
waitForDebuggerOnStart: true,
});
session.connection().on('sessionattached', async (session) => {
otherSessions.push(session);
session.on('Network.requestWillBeSent', (params) =>
networkEvents.push(params)
);
await session.send('Network.enable');
});
// Navigate to the empty page and add an OOPIF iframe with at least one request.
await page.goto(server.EMPTY_PAGE);
await page.evaluate((frameUrl) => {
const frame = document.createElement('iframe');
frame.setAttribute('src', frameUrl);
document.body.appendChild(frame);
return new Promise((x, y) => {
frame.onload = x;
frame.onerror = y;
});
}, server.PREFIX.replace('localhost', 'oopifdomain') + '/one-style.html');
await page.waitForSelector('iframe');
// Ensure we found the iframe session.
expect(otherSessions).toHaveLength(1);
// Resume the iframe and trigger another request.
const iframeSession = otherSessions[0];
await iframeSession.send('Runtime.runIfWaitingForDebugger');
await iframeSession.send('Runtime.evaluate', {
expression: `fetch('/fetch')`,
awaitPromise: true,
});
await browser.close();
const requests = networkEvents.map((event) => event.request.url);
expect(requests).toContain(`http://oopifdomain:${server.PORT}/fetch`);
});
it('should close browser with beforeunload page', async () => { it('should close browser with beforeunload page', async () => {
const { server, puppeteer } = getTestState(); const { server, puppeteer } = getTestState();