From 5614c757517a71ea2ec40da008e22c50f2a49bea Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Mon, 27 Jan 2020 17:06:09 -0800 Subject: [PATCH] feat(worker): expose worker's owner frame --- docs/api.md | 4 ++++ package.json | 2 +- src/chromium/crPage.ts | 2 +- src/chromium/crTarget.ts | 5 ++++- src/firefox/ffPage.ts | 2 +- src/frames.ts | 1 + src/page.ts | 14 +++++++++++--- src/webkit/wkWorkers.ts | 3 +-- test/workers.spec.js | 19 +++++++++++++++++++ 9 files changed, 43 insertions(+), 9 deletions(-) diff --git a/docs/api.md b/docs/api.md index 0ab40f0aeb..069efe42a1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3248,6 +3248,7 @@ for (const worker of page.workers()) - [worker.evaluate(pageFunction[, ...args])](#workerevaluatepagefunction-args) - [worker.evaluateHandle(pageFunction[, ...args])](#workerevaluatehandlepagefunction-args) +- [worker.frame()](#workerframe) - [worker.url()](#workerurl) @@ -3269,6 +3270,9 @@ The only difference between `worker.evaluate` and `worker.evaluateHandle` is tha If the function passed to the `worker.evaluateHandle` returns a [Promise], then `worker.evaluateHandle` would wait for the promise to resolve and return its value. +#### worker.frame() +- returns: <[Frame]> Frame which has created this worker. + #### worker.url() - returns: <[string]> diff --git a/package.json b/package.json index bde9132976..388e71c282 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "playwright": { "chromium_revision": "733125", "firefox_revision": "1018", - "webkit_revision": "1113" + "webkit_revision": "1118" }, "scripts": { "unit": "node test/test.js", diff --git a/src/chromium/crPage.ts b/src/chromium/crPage.ts index 578ce07966..5ca5ff41fb 100644 --- a/src/chromium/crPage.ts +++ b/src/chromium/crPage.ts @@ -219,7 +219,7 @@ export class CRPage implements PageDelegate { return; const url = event.targetInfo.url; const session = CRConnection.fromSession(this._client).session(event.sessionId)!; - const worker = new Worker(url); + const worker = new Worker(url, null as any); this._page._addWorker(event.sessionId, worker); session.once('Runtime.executionContextCreated', async event => { worker._createExecutionContext(new CRExecutionContext(session, event.context)); diff --git a/src/chromium/crTarget.ts b/src/chromium/crTarget.ts index 8c58bb10f7..8e567ec366 100644 --- a/src/chromium/crTarget.ts +++ b/src/chromium/crTarget.ts @@ -104,7 +104,8 @@ export class CRTarget { if (!this._workerPromise) { // TODO(einbinder): Make workers send their console logs. this._workerPromise = this._sessionFactory().then(session => { - const worker = new Worker(this._targetInfo.url); + // TODO: we should not reuse Worker class (which is web worker) for service workers. + const worker = new Worker(this._targetInfo.url, null as any); session.once('Runtime.executionContextCreated', async event => { worker._createExecutionContext(new CRExecutionContext(session, event.context)); }); @@ -132,6 +133,8 @@ export class CRTarget { } opener(): CRTarget | null { + if (this._targetInfo.type === 'worker') + return null; const { openerId } = this._targetInfo; if (!openerId) return null; diff --git a/src/firefox/ffPage.ts b/src/firefox/ffPage.ts index 7b53ee3797..92882f58f4 100644 --- a/src/firefox/ffPage.ts +++ b/src/firefox/ffPage.ts @@ -203,7 +203,7 @@ export class FFPage implements PageDelegate { async _onWorkerCreated(event: Protocol.Page.workerCreatedPayload) { const workerId = event.workerId; - const worker = new Worker(event.url); + const worker = new Worker(event.url, this._page._frameManager.frame(event.frameId)!); const workerSession = new FFSession(this._session._connection, 'worker', workerId, (message: any) => { this._session.send('Page.sendMessageToWorker', { frameId: event.frameId, diff --git a/src/frames.ts b/src/frames.ts index 6bbe0f6b3b..8ab1e3f970 100644 --- a/src/frames.ts +++ b/src/frames.ts @@ -120,6 +120,7 @@ export class FrameManager { frame._lastDocumentId = documentId; this.clearFrameLifecycle(frame); this.clearWebSockets(frame); + this._page._clearWorkers(frame); if (!initial) { for (const watcher of this._lifecycleWatchers) watcher._onCommittedNewDocumentNavigation(frame); diff --git a/src/page.ts b/src/page.ts index 8838e87178..3679270126 100644 --- a/src/page.ts +++ b/src/page.ts @@ -519,22 +519,26 @@ export class Page extends platform.EventEmitter { this._workers.delete(workerId); } - _clearWorkers() { + _clearWorkers(frame: frames.Frame) { for (const [workerId, worker] of this._workers) { - this.emit(Events.Page.WorkerDestroyed, worker); + if (worker.frame() !== frame) + continue; this._workers.delete(workerId); + this.emit(Events.Page.WorkerDestroyed, worker); } } } export class Worker { private _url: string; + private _frame: frames.Frame; private _executionContextPromise: Promise; private _executionContextCallback: (value?: js.ExecutionContext) => void; _existingExecutionContext: js.ExecutionContext | null = null; - constructor(url: string) { + constructor(url: string, frame: frames.Frame) { this._url = url; + this._frame = frame; this._executionContextCallback = () => {}; this._executionContextPromise = new Promise(x => this._executionContextCallback = x); } @@ -548,6 +552,10 @@ export class Worker { return this._url; } + frame(): frames.Frame { + return this._frame; + } + evaluate: types.Evaluate = async (pageFunction, ...args) => { return (await this._executionContextPromise).evaluate(pageFunction, ...args as any); } diff --git a/src/webkit/wkWorkers.ts b/src/webkit/wkWorkers.ts index 13de199ace..1429ade667 100644 --- a/src/webkit/wkWorkers.ts +++ b/src/webkit/wkWorkers.ts @@ -34,7 +34,7 @@ export class WKWorkers { this.clear(); this._sessionListeners = [ helper.addEventListener(session, 'Worker.workerCreated', async (event: Protocol.Worker.workerCreatedPayload) => { - const worker = new Worker(event.url); + const worker = new Worker(event.url, this._page._frameManager.frame(event.frameId)); const workerSession = new WKSession(session.connection, event.workerId, 'Most likely the worker has been closed.', (message: any) => { session.send('Worker.sendMessageToWorker', { workerId: event.workerId, @@ -77,7 +77,6 @@ export class WKWorkers { } clear() { - this._page._clearWorkers(); this._workerSessions.clear(); } diff --git a/test/workers.spec.js b/test/workers.spec.js index 726563c7f2..1c57a48aa1 100644 --- a/test/workers.spec.js +++ b/test/workers.spec.js @@ -86,6 +86,25 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) expect(destroyed).toBe(true); expect(page.workers().length).toBe(0); }); + it.skip(CHROMIUM)('should clear upon frame navigation', async function({server, page}) { + await page.goto(server.PREFIX + '/frames/one-frame.html'); + const workerCreatedPromise = page.waitForEvent('workercreated'); + const frame = page.mainFrame().childFrames()[0]; + frame.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))); + await workerCreatedPromise; + expect(page.workers().length).toBe(1); + expect(page.workers()[0].frame()).toBe(frame); + let destroyed = false; + page.once('workerdestroyed', () => destroyed = true); + let navigated = false; + page.once('framenavigated', () => { + expect(destroyed).toBe(true); + expect(page.workers().length).toBe(0); + navigated = true; + }); + await frame.goto(server.PREFIX + '/one-style.html'); + expect(navigated).toBe(true); + }); it('should clear upon cross-process navigation', async function({server, page}) { await page.goto(server.EMPTY_PAGE); const workerCreatedPromise = page.waitForEvent('workercreated');