From ddf47bc29105305f4db50bc052387261e798a0b1 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Tue, 21 Jan 2020 10:41:04 -0800 Subject: [PATCH] feat(worker): report workers network activity (#545) --- src/chromium/crPage.ts | 40 ++++++++++++++++++++++++++++++----- src/chromium/crWorkers.ts | 44 --------------------------------------- test/workers.spec.js | 30 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 49 deletions(-) delete mode 100644 src/chromium/crWorkers.ts diff --git a/src/chromium/crPage.ts b/src/chromium/crPage.ts index a624e0309e..b31a744a35 100644 --- a/src/chromium/crPage.ts +++ b/src/chromium/crPage.ts @@ -20,10 +20,10 @@ import * as js from '../javascript'; import * as frames from '../frames'; import { debugError, helper, RegisteredListener } from '../helper'; import * as network from '../network'; -import { CRSession } from './crConnection'; +import { CRSession, CRConnection } from './crConnection'; import { EVALUATION_SCRIPT_URL, CRExecutionContext } from './crExecutionContext'; import { CRNetworkManager } from './crNetworkManager'; -import { Page, Coverage } from '../page'; +import { Page, Coverage, Worker } from '../page'; import { Protocol } from './protocol'; import { Events } from '../events'; import { toConsoleMessageLocation, exceptionToError, releaseObject } from './crProtocolHelper'; @@ -33,7 +33,6 @@ import { RawMouseImpl, RawKeyboardImpl } from './crInput'; import { getAccessibilityTree } from './crAccessibility'; import { CRCoverage } from './crCoverage'; import { CRPDF } from './crPdf'; -import { CRWorkers } from './crWorkers'; import { CRBrowser } from './crBrowser'; import { BrowserContext } from '../browserContext'; import * as types from '../types'; @@ -46,7 +45,6 @@ export class CRPage implements PageDelegate { _client: CRSession; private readonly _page: Page; readonly _networkManager: CRNetworkManager; - private _workers: CRWorkers; private _contextIdToContext = new Map(); private _isolatedWorlds = new Set(); private _eventListeners: RegisteredListener[]; @@ -65,7 +63,6 @@ export class CRPage implements PageDelegate { this._coverage = new CRCoverage(client); this._page = new Page(this, browserContext); this._networkManager = new CRNetworkManager(client, this._page); - this._workers = new CRWorkers(client, this._page); this._eventListeners = [ helper.addEventListener(client, 'Inspector.targetCrashed', event => this._onTargetCrashed()), @@ -84,6 +81,8 @@ export class CRPage implements PageDelegate { helper.addEventListener(client, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)), helper.addEventListener(client, 'Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId)), helper.addEventListener(client, 'Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()), + helper.addEventListener(client, 'Target.attachedToTarget', event => this._onAttachedToTarget(event)), + helper.addEventListener(client, 'Target.detachedFromTarget', event => this._onDetachedFromTarget(event)), ]; } @@ -223,6 +222,37 @@ export class CRPage implements PageDelegate { this._onExecutionContextDestroyed(contextId); } + _onAttachedToTarget(event: Protocol.Target.attachedToTargetPayload) { + if (event.targetInfo.type !== 'worker') + return; + const url = event.targetInfo.url; + const session = CRConnection.fromSession(this._client).session(event.sessionId)!; + const worker = new Worker(url); + this._page._addWorker(event.sessionId, worker); + session.once('Runtime.executionContextCreated', async event => { + worker._createExecutionContext(new CRExecutionContext(session, event.context)); + }); + Promise.all([ + session.send('Runtime.enable'), + session.send('Network.enable'), + ]).catch(debugError); // This might fail if the target is closed before we initialize. + session.on('Runtime.consoleAPICalled', event => { + const args = event.args.map(o => worker._existingExecutionContext!._createHandle(o)); + this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace)); + }); + session.on('Runtime.exceptionThrown', exception => this._page.emit(Events.Page.PageError, exceptionToError(exception.exceptionDetails))); + session.on('Fetch.requestPaused', event => this._networkManager._onRequestPaused(event)); + session.on('Fetch.authRequired', event => this._networkManager._onAuthRequired(event)); + session.on('Network.requestWillBeSent', event => this._networkManager._onRequestWillBeSent(event)); + session.on('Network.responseReceived', event => this._networkManager._onResponseReceived(event)); + session.on('Network.loadingFinished', event => this._networkManager._onLoadingFinished(event)); + session.on('Network.loadingFailed', event => this._networkManager._onLoadingFailed(event)); + } + + _onDetachedFromTarget(event: Protocol.Target.detachedFromTargetPayload) { + this._page._removeWorker(event.sessionId); + } + async _onConsoleAPI(event: Protocol.Runtime.consoleAPICalledPayload) { if (event.executionContextId === 0) { // DevTools protocol stores the last 1000 console messages. These diff --git a/src/chromium/crWorkers.ts b/src/chromium/crWorkers.ts deleted file mode 100644 index 24e68e744b..0000000000 --- a/src/chromium/crWorkers.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2018 Google Inc. All rights reserved. - * Modifications copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Events } from '../events'; -import { debugError } from '../helper'; -import { Page, Worker } from '../page'; -import { CRConnection, CRSession } from './crConnection'; -import { CRExecutionContext } from './crExecutionContext'; -import { exceptionToError, toConsoleMessageLocation } from './crProtocolHelper'; - -export class CRWorkers { - constructor(client: CRSession, page: Page) { - client.on('Target.attachedToTarget', event => { - if (event.targetInfo.type !== 'worker') - return; - const url = event.targetInfo.url; - const session = CRConnection.fromSession(client).session(event.sessionId)!; - const worker = new Worker(url); - page._addWorker(event.sessionId, worker); - session.once('Runtime.executionContextCreated', async event => { - worker._createExecutionContext(new CRExecutionContext(session, event.context)); - }); - // This might fail if the target is closed before we recieve all execution contexts. - session.send('Runtime.enable', {}).catch(debugError); - session.on('Runtime.consoleAPICalled', event => page._addConsoleMessage(event.type, event.args.map(o => worker._existingExecutionContext!._createHandle(o)), toConsoleMessageLocation(event.stackTrace))); - session.on('Runtime.exceptionThrown', exception => page.emit(Events.Page.PageError, exceptionToError(exception.exceptionDetails))); - }); - client.on('Target.detachedFromTarget', event => page._removeWorker(event.sessionId)); - } -} diff --git a/test/workers.spec.js b/test/workers.spec.js index 370a12fd5b..16254af8a6 100644 --- a/test/workers.spec.js +++ b/test/workers.spec.js @@ -83,5 +83,35 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT}) await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); expect(page.workers().length).toBe(0); }); + it('should report network activity', async function({page, server}) { + const [worker] = await Promise.all([ + page.waitForEvent('workercreated'), + page.goto(server.PREFIX + '/worker/worker.html'), + ]); + const url = server.PREFIX + '/one-style.css'; + const requestPromise = page.waitForRequest(url); + const responsePromise = page.waitForResponse(url); + await worker.evaluate(url => fetch(url).then(response => response.text()).then(console.log), url); + const request = await requestPromise; + const response = await responsePromise; + expect(request.url()).toBe(url); + expect(response.request()).toBe(request); + expect(response.ok()).toBe(true); + }); + it.skip(CHROMIUM)('should report network activity on worker creation', async function({page, server}) { + // Chromium needs waitForDebugger enabled for this one. + await page.goto(server.EMPTY_PAGE); + const url = server.PREFIX + '/one-style.css'; + const requestPromise = page.waitForRequest(url); + const responsePromise = page.waitForResponse(url); + await page.evaluate(url => new Worker(URL.createObjectURL(new Blob([` + fetch("${url}").then(response => response.text()).then(console.log); + `], {type: 'application/javascript'}))), url); + const request = await requestPromise; + const response = await responsePromise; + expect(request.url()).toBe(url); + expect(response.request()).toBe(request); + expect(response.ok()).toBe(true); + }); }); };