feat(worker): report workers network activity (#545)

This commit is contained in:
Dmitry Gozman 2020-01-21 10:41:04 -08:00 committed by Pavel Feldman
parent c3e4f092d3
commit ddf47bc291
3 changed files with 65 additions and 49 deletions

View file

@ -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<number, dom.FrameExecutionContext>();
private _isolatedWorlds = new Set<string>();
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

View file

@ -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));
}
}

View file

@ -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);
});
});
};