feat(workers): create workers from service workers and shared workers (#4397)

This allows users to easily evaluate javascript inside service workers and shared workers by creating a Worker object for them.
This commit is contained in:
Joel Einbinder 2019-05-09 20:29:18 -04:00 committed by Andrey Lushnikov
parent ef24c69c62
commit 1516e0df21
3 changed files with 51 additions and 4 deletions

View File

@ -304,6 +304,7 @@
* [target.page()](#targetpage)
* [target.type()](#targettype)
* [target.url()](#targeturl)
* [target.worker()](#targetworker)
- [class: CDPSession](#class-cdpsession)
* [cdpSession.detach()](#cdpsessiondetach)
* [cdpSession.send(method[, params])](#cdpsessionsendmethod-params)
@ -3479,13 +3480,18 @@ Get the target that opened this target. Top-level targets return `null`.
If the target is not of type `"page"` or `"background_page"`, returns `null`.
#### target.type()
- returns: <"page"|"background_page"|"service_worker"|"other"|"browser">
- returns: <"page"|"background_page"|"service_worker"|"shared_worker"|"other"|"browser">
Identifies what kind of target this is. Can be `"page"`, [`"background_page"`](https://developer.chrome.com/extensions/background_pages), `"service_worker"`, `"browser"` or `"other"`.
Identifies what kind of target this is. Can be `"page"`, [`"background_page"`](https://developer.chrome.com/extensions/background_pages), `"service_worker"`, `"shared_worker"`, `"browser"` or `"other"`.
#### target.url()
- returns: <[string]>
#### target.worker()
- returns: <[Promise]<?[Worker]>>
If the target is not of type `"service_worker"` or `"shared_worker"`, returns `null`.
### class: CDPSession
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)

View File

@ -16,6 +16,8 @@
const {Events} = require('./Events');
const {Page} = require('./Page');
const {Worker} = require('./Worker');
const {Connection} = require('./Connection');
class Target {
/**
@ -36,6 +38,8 @@ class Target {
this._screenshotTaskQueue = screenshotTaskQueue;
/** @type {?Promise<!Puppeteer.Page>} */
this._pagePromise = null;
/** @type {?Promise<!Worker>} */
this._workerPromise = null;
this._initializedPromise = new Promise(fulfill => this._initializedCallback = fulfill).then(async success => {
if (!success)
return false;
@ -73,6 +77,27 @@ class Target {
return this._pagePromise;
}
/**
* @return {!Promise<?Worker>}
*/
async worker() {
if (this._targetInfo.type !== 'service_worker' && this._targetInfo.type !== 'shared_worker')
return null;
if (!this._workerPromise) {
this._workerPromise = this._sessionFactory().then(async client => {
// Top level workers have a fake page wrapping the actual worker.
const [targetAttached] = await Promise.all([
new Promise(x => client.once('Target.attachedToTarget', x)),
client.send('Target.setAutoAttach', {autoAttach: true, waitForDebuggerOnStart: false, flatten: true}),
]);
const session = Connection.fromSession(client).session(targetAttached.sessionId);
// TODO(einbinder): Make workers send their console logs.
return new Worker(session, this._targetInfo.url, () => {} /* consoleAPICalled */, () => {} /* exceptionThrown */);
});
}
return this._workerPromise;
}
/**
* @return {string}
*/
@ -81,11 +106,11 @@ class Target {
}
/**
* @return {"page"|"background_page"|"service_worker"|"other"|"browser"}
* @return {"page"|"background_page"|"service_worker"|"shared_worker"|"other"|"browser"}
*/
type() {
const type = this._targetInfo.type;
if (type === 'page' || type === 'background_page' || type === 'service_worker' || type === 'browser')
if (type === 'page' || type === 'background_page' || type === 'service_worker' || type === 'shared_worker' || type === 'browser')
return type;
return 'other';
}

View File

@ -83,6 +83,22 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister()));
expect(await destroyedTarget).toBe(await createdTarget);
});
it_fails_ffox('should create a worker from a service worker', async({page, server, context}) => {
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
const target = await context.waitForTarget(target => target.type() === 'service_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
});
it_fails_ffox('should create a worker from a shared worker', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
new SharedWorker('data:text/javascript,console.log("hi")');
});
const target = await context.waitForTarget(target => target.type() === 'shared_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object SharedWorkerGlobalScope]');
});
it('should report when a target url changes', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
let changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target)));