Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose other sessions from connection #6863

Merged
merged 2 commits into from May 7, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/api.md
Expand Up @@ -357,6 +357,7 @@
* [target.url()](#targeturl)
* [target.worker()](#targetworker)
- [class: CDPSession](#class-cdpsession)
* [cdpSession.connection()](#cdpsessionconnection)
* [cdpSession.detach()](#cdpsessiondetach)
* [cdpSession.send(method[, ...paramArgs])](#cdpsessionsendmethod-paramargs)
- [class: Coverage](#class-coverage)
Expand Down Expand Up @@ -4557,6 +4558,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()

- returns: <[Promise]>
Expand Down
6 changes: 6 additions & 0 deletions src/common/Connection.ts
Expand Up @@ -125,11 +125,13 @@ export class Connection extends EventEmitter {
sessionId
);
this._sessions.set(sessionId, session);
this.emit('sessionattached', session);
} else if (object.method === 'Target.detachedFromTarget') {
const session = this._sessions.get(object.params.sessionId);
if (session) {
session._onClosed();
this._sessions.delete(object.params.sessionId);
this.emit('sessiondetached', session);
}
}
if (object.sessionId) {
Expand Down Expand Up @@ -253,6 +255,10 @@ export class CDPSession extends EventEmitter {
this._sessionId = sessionId;
}

connection(): Connection {
return this._connection;
}

sadym-chromium marked this conversation as resolved.
Show resolved Hide resolved
send<T extends keyof ProtocolMapping.Commands>(
method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
Expand Down
10 changes: 9 additions & 1 deletion src/common/Page.ts
Expand Up @@ -488,8 +488,16 @@ export class Page extends EventEmitter {
this._viewport = null;

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.
// 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
.send('Target.detachFromTarget', {
sessionId: event.sessionId,
Expand Down
67 changes: 66 additions & 1 deletion test/headful.spec.ts
Expand Up @@ -42,9 +42,10 @@ describeChromeOnly('headful tests', function () {
let headfulOptions;
let headlessOptions;
let extensionOptions;
let forcedOopifOptions;

beforeEach(() => {
const { defaultBrowserOptions } = getTestState();
const { server, defaultBrowserOptions } = getTestState();
headfulOptions = Object.assign({}, defaultBrowserOptions, {
headless: false,
});
Expand All @@ -59,6 +60,18 @@ describeChromeOnly('headful tests', function () {
`--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 () {
Expand Down Expand Up @@ -147,6 +160,58 @@ describeChromeOnly('headful tests', function () {
expect(urls).toEqual([server.EMPTY_PAGE, 'https://google.com/']);
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 () => {
const { server, puppeteer } = getTestState();

Expand Down