diff --git a/lighthouse-core/gather/connections/connection.js b/lighthouse-core/gather/connections/connection.js index 2661554a5604..3655ecb80326 100644 --- a/lighthouse-core/gather/connections/connection.js +++ b/lighthouse-core/gather/connections/connection.js @@ -52,16 +52,17 @@ class Connection { * Call protocol methods * @template {keyof LH.CrdpCommands} C * @param {C} method + * @param {string|undefined} sessionId * @param {LH.CrdpCommands[C]['paramsType']} paramArgs, * @return {Promise} */ - sendCommand(method, ...paramArgs) { + sendCommand(method, sessionId, ...paramArgs) { // Reify params since we need it as a property so can't just spread again. const params = paramArgs.length ? paramArgs[0] : undefined; log.formatProtocol('method => browser', {method, params}, 'verbose'); const id = ++this._lastCommandId; - const message = JSON.stringify({id, method, params}); + const message = JSON.stringify({id, sessionId, method, params}); this.sendRawMessage(message); return new Promise(resolve => { @@ -117,7 +118,7 @@ class Connection { if (callback) { this._callbacks.delete(object.id); - return callback.resolve(Promise.resolve().then(_ => { + callback.resolve(Promise.resolve().then(_ => { if (object.error) { log.formatProtocol('method <= browser ERR', {method: callback.method}, 'error'); throw LHError.fromProtocolMessage(callback.method, object.error); diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index bc36000375ba..6b4b423787ce 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -69,21 +69,8 @@ class Driver { */ this._monitoredUrl = null; - /** - * Used for sending messages to subtargets. Each message needs a unique ID even if we don't bother - * reading back the result of each command. - * - * @type {number} - * @private - */ - this._targetProxyMessageId = 0; - this.on('Target.attachedToTarget', event => { - this._handleTargetAttached(event, []).catch(this._handleEventError); - }); - - this.on('Target.receivedMessageFromTarget', event => { - this._handleReceivedMessageFromTarget(event, []).catch(this._handleEventError); + this._handleTargetAttached(event).catch(this._handleEventError); }); // We use isolated execution contexts for `evaluateAsync` that can be destroyed through navigation @@ -247,14 +234,16 @@ class Driver { * user of that domain (e.g. two gatherers have enabled a domain, both need to * disable it for it to be disabled). Returns true otherwise. * @param {string} domain + * @param {string|undefined} sessionId * @param {boolean} enable * @return {boolean} * @private */ - _shouldToggleDomain(domain, enable) { - const enabledCount = this._domainEnabledCounts.get(domain) || 0; + _shouldToggleDomain(domain, sessionId, enable) { + const key = domain + (sessionId || ''); + const enabledCount = this._domainEnabledCounts.get(key) || 0; const newCount = enabledCount + (enable ? 1 : -1); - this._domainEnabledCounts.set(domain, Math.max(0, newCount)); + this._domainEnabledCounts.set(key, Math.max(0, newCount)); // Switching to enabled or disabled, respectively. if ((enable && newCount === 1) || (!enable && newCount === 0)) { @@ -300,108 +289,45 @@ class Driver { log.error('Driver', 'Unhandled event error', error.message); } - /** - * @param {LH.Crdp.Target.ReceivedMessageFromTargetEvent} event - * @param {string[]} parentSessionIds The list of session ids of the parents from youngest to oldest. - */ - async _handleReceivedMessageFromTarget(event, parentSessionIds) { - const {targetId, sessionId, message} = event; - /** @type {LH.Protocol.RawMessage} */ - const protocolMessage = JSON.parse(message); - - // Message was a response to some command, not an event, so we'll ignore it. - if ('id' in protocolMessage) return; - - // We receive messages from the outermost subtarget which wraps the messages from the inner subtargets. - // We are recursively processing them from outside in, so build the list of parentSessionIds accordingly. - const sessionIdPath = [sessionId, ...parentSessionIds]; - - if (protocolMessage.method === 'Target.receivedMessageFromTarget') { - // Unravel any messages from subtargets by recursively processing - await this._handleReceivedMessageFromTarget(protocolMessage.params, sessionIdPath); - } - - if (protocolMessage.method === 'Target.attachedToTarget') { - // Process any attachedToTarget messages from subtargets - await this._handleTargetAttached(protocolMessage.params, sessionIdPath); - } - - if (protocolMessage.method.startsWith('Network')) { - this._handleProtocolEvent({...protocolMessage, source: {targetId, sessionId}}); - } - } - /** * @param {LH.Crdp.Target.AttachedToTargetEvent} event - * @param {string[]} parentSessionIds The list of session ids of the parents from youngest to oldest. */ - async _handleTargetAttached(event, parentSessionIds) { - const sessionIdPath = [event.sessionId, ...parentSessionIds]; - + async _handleTargetAttached(event) { // We're only interested in network requests from iframes for now as those are "part of the page". // If it's not an iframe, just resume it and move on. if (event.targetInfo.type !== 'iframe') { // We suspended the target when we auto-attached, so make sure it goes back to being normal. - await this.sendMessageToTarget(sessionIdPath, 'Runtime.runIfWaitingForDebugger'); + await this.sendCommandToSession('Runtime.runIfWaitingForDebugger', event.sessionId); return; } // Events from subtargets will be stringified and sent back on `Target.receivedMessageFromTarget`. // We want to receive information about network requests from iframes, so enable the Network domain. - await this.sendMessageToTarget(sessionIdPath, 'Network.enable'); + await this.sendCommandToSession('Network.enable', event.sessionId); + // We also want to receive information about subtargets of subtargets, so make sure we autoattach recursively. - await this.sendMessageToTarget(sessionIdPath, 'Target.setAutoAttach', { + await this.sendCommandToSession('Target.setAutoAttach', event.sessionId, { autoAttach: true, + flatten: true, // Pause targets on startup so we don't miss anything waitForDebuggerOnStart: true, }); // We suspended the target when we auto-attached, so make sure it goes back to being normal. - await this.sendMessageToTarget(sessionIdPath, 'Runtime.runIfWaitingForDebugger'); - } - - /** - * Send protocol commands to a subtarget, wraps the message in as many `Target.sendMessageToTarget` - * commands as necessary. - * - * @template {keyof LH.CrdpCommands} C - * @param {string[]} sessionIdPath List of session ids to send to, from youngest-oldest/inside-out. - * @param {C} method - * @param {LH.CrdpCommands[C]['paramsType']} params - * @return {Promise} - */ - sendMessageToTarget(sessionIdPath, method, ...params) { - this._targetProxyMessageId++; - /** @type {LH.Crdp.Target.SendMessageToTargetRequest} */ - let payload = { - sessionId: sessionIdPath[0], - message: JSON.stringify({id: this._targetProxyMessageId, method, params: params[0]}), - }; - - for (const sessionId of sessionIdPath.slice(1)) { - this._targetProxyMessageId++; - payload = { - sessionId, - message: JSON.stringify({ - id: this._targetProxyMessageId, - method: 'Target.sendMessageToTarget', - params: payload, - }), - }; - } - - return this.sendCommand('Target.sendMessageToTarget', payload); + await this.sendCommandToSession('Runtime.runIfWaitingForDebugger', event.sessionId); } /** * Call protocol methods, with a timeout. * To configure the timeout for the next call, use 'setNextProtocolTimeout'. + * If 'sessionId' is undefined, the message is sent to the main session. * @template {keyof LH.CrdpCommands} C * @param {C} method + * @param {string|undefined} sessionId * @param {LH.CrdpCommands[C]['paramsType']} params * @return {Promise} */ - sendCommand(method, ...params) { + sendCommandToSession(method, sessionId, ...params) { const timeout = this._nextProtocolTimeout; this._nextProtocolTimeout = DEFAULT_PROTOCOL_TIMEOUT; return new Promise(async (resolve, reject) => { @@ -413,7 +339,7 @@ class Driver { reject(err); }), timeout); try { - const result = await this._innerSendCommand(method, ...params); + const result = await this._innerSendCommand(method, sessionId, ...params); resolve(result); } catch (err) { reject(err); @@ -423,23 +349,35 @@ class Driver { }); } + /** + * Alias for 'sendCommandToSession(method, undefined, ...params)' + * @template {keyof LH.CrdpCommands} C + * @param {C} method + * @param {LH.CrdpCommands[C]['paramsType']} params + * @return {Promise} + */ + sendCommand(method, ...params) { + return this.sendCommandToSession(method, undefined, ...params); + } + /** * Call protocol methods. * @private * @template {keyof LH.CrdpCommands} C * @param {C} method + * @param {string|undefined} sessionId * @param {LH.CrdpCommands[C]['paramsType']} params * @return {Promise} */ - _innerSendCommand(method, ...params) { + _innerSendCommand(method, sessionId, ...params) { const domainCommand = /^(\w+)\.(enable|disable)$/.exec(method); if (domainCommand) { const enable = domainCommand[2] === 'enable'; - if (!this._shouldToggleDomain(domainCommand[1], enable)) { + if (!this._shouldToggleDomain(domainCommand[1], sessionId, enable)) { return Promise.resolve(); } } - return this._connection.sendCommand(method, ...params); + return this._connection.sendCommand(method, sessionId, ...params); } /** @@ -1133,6 +1071,7 @@ class Driver { // Enable auto-attaching to subtargets so we receive iframe information await this.sendCommand('Target.setAutoAttach', { + flatten: true, autoAttach: true, // Pause targets on startup so we don't miss anything waitForDebuggerOnStart: true, @@ -1142,7 +1081,7 @@ class Driver { await this.sendCommand('Page.setLifecycleEventsEnabled', {enabled: true}); await this.sendCommand('Emulation.setScriptExecutionDisabled', {value: disableJS}); // No timeout needed for Page.navigate. See https://github.com/GoogleChrome/lighthouse/pull/6413. - const waitforPageNavigateCmd = this._innerSendCommand('Page.navigate', {url}); + const waitforPageNavigateCmd = this._innerSendCommand('Page.navigate', undefined, {url}); if (waitForNavigated) { await this._waitForFrameNavigated(); diff --git a/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js b/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js index 1c81e0afd676..c924991cb6a0 100644 --- a/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js +++ b/lighthouse-core/gather/gatherers/dobetterweb/optimized-images.js @@ -46,7 +46,7 @@ class OptimizedImages extends Gatherer { /** @type {Set} */ const seenUrls = new Set(); return networkRecords.reduce((prev, record) => { - // Skip records that we've seen before, never finished, or came from OOPIFs. + // Skip records that we've seen before, never finished, or came from child targets (OOPIFS). if (seenUrls.has(record.url) || !record.finished || record.sessionId) { return prev; } diff --git a/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js b/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js index a98c62c94851..beb2eb20bb21 100644 --- a/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +++ b/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js @@ -40,7 +40,7 @@ class ResponseCompression extends Gatherer { const unoptimizedResponses = []; networkRecords.forEach(record => { - // Ignore records from OOPIFs + // Ignore records from child targets (OOPIFS). if (record.sessionId) return; const mimeType = record.mimeType; diff --git a/lighthouse-core/lib/network-recorder.js b/lighthouse-core/lib/network-recorder.js index 70a109dd3437..6fe760ab1a4c 100644 --- a/lighthouse-core/lib/network-recorder.js +++ b/lighthouse-core/lib/network-recorder.js @@ -185,15 +185,16 @@ class NetworkRecorder extends EventEmitter { // DevTools SDK network layer. /** - * @param {{params: LH.Crdp.Network.RequestWillBeSentEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.RequestWillBeSentEvent, sessionId?: string}} event */ onRequestWillBeSent(event) { const data = event.params; - const originalRequest = this._findRealRequestAndSetSource(data.requestId, event.source); + const originalRequest = this._findRealRequestAndSetSession(data.requestId, event.sessionId); // This is a simple new request, create the NetworkRequest object and finish. if (!originalRequest) { const request = new NetworkRequest(); request.onRequestWillBeSent(data); + request.sessionId = event.sessionId; this.onRequestStarted(request); return; } @@ -225,63 +226,63 @@ class NetworkRecorder extends EventEmitter { } /** - * @param {{params: LH.Crdp.Network.RequestServedFromCacheEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.RequestServedFromCacheEvent, sessionId?: string}} event */ onRequestServedFromCache(event) { const data = event.params; - const request = this._findRealRequestAndSetSource(data.requestId, event.source); + const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId); if (!request) return; request.onRequestServedFromCache(); } /** - * @param {{params: LH.Crdp.Network.ResponseReceivedEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.ResponseReceivedEvent, sessionId?: string}} event */ onResponseReceived(event) { const data = event.params; - const request = this._findRealRequestAndSetSource(data.requestId, event.source); + const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId); if (!request) return; request.onResponseReceived(data); } /** - * @param {{params: LH.Crdp.Network.DataReceivedEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.DataReceivedEvent, sessionId?: string}} event */ onDataReceived(event) { const data = event.params; - const request = this._findRealRequestAndSetSource(data.requestId, event.source); + const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId); if (!request) return; request.onDataReceived(data); } /** - * @param {{params: LH.Crdp.Network.LoadingFinishedEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.LoadingFinishedEvent, sessionId?: string}} event */ onLoadingFinished(event) { const data = event.params; - const request = this._findRealRequestAndSetSource(data.requestId, event.source); + const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId); if (!request) return; request.onLoadingFinished(data); this.onRequestFinished(request); } /** - * @param {{params: LH.Crdp.Network.LoadingFailedEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.LoadingFailedEvent, sessionId?: string}} event */ onLoadingFailed(event) { const data = event.params; - const request = this._findRealRequestAndSetSource(data.requestId, event.source); + const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId); if (!request) return; request.onLoadingFailed(data); this.onRequestFinished(request); } /** - * @param {{params: LH.Crdp.Network.ResourceChangedPriorityEvent, source?: LH.Protocol.RawSource}} event + * @param {{params: LH.Crdp.Network.ResourceChangedPriorityEvent, sessionId?: string}} event */ onResourceChangedPriority(event) { const data = event.params; - const request = this._findRealRequestAndSetSource(data.requestId, event.source); + const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId); if (!request) return; request.onResourceChangedPriority(data); } @@ -310,10 +311,10 @@ class NetworkRecorder extends EventEmitter { * message is referring. * * @param {string} requestId - * @param {LH.Protocol.RawSource|undefined} source + * @param {string|undefined} sessionId * @return {NetworkRequest|undefined} */ - _findRealRequestAndSetSource(requestId, source) { + _findRealRequestAndSetSession(requestId, sessionId) { let request = this._recordsById.get(requestId); if (!request || !request.isValid) return undefined; @@ -321,7 +322,8 @@ class NetworkRecorder extends EventEmitter { request = request.redirectDestination; } - request.setSource(source); + request.setSession(sessionId); + return request; } diff --git a/lighthouse-core/lib/network-request.js b/lighthouse-core/lib/network-request.js index 0e980bc4c079..cce715d7f437 100644 --- a/lighthouse-core/lib/network-request.js +++ b/lighthouse-core/lib/network-request.js @@ -129,14 +129,8 @@ class NetworkRequest { this.frameId = ''; /** * @type {string|undefined} - * Only set for OOPIFs. This is the targetId of the protocol target from which this - * request came. Undefined means it came from the root. - */ - this.targetId = undefined; - /** - * @type {string|undefined} - * Only set for OOPIFs. This is the sessionId of the protocol connection on which this - * request was discovered. Undefined means it came from the root. + * Only set for child targets (OOPIFs). This is the sessionId of the protocol connection on + * which this request was discovered. `undefined` means it came from the root. */ this.sessionId = undefined; this.isLinkPreload = false; @@ -272,16 +266,10 @@ class NetworkRequest { } /** - * @param {LH.Protocol.RawSource|undefined} source + * @param {string=} sessionId */ - setSource(source) { - if (source) { - this.targetId = source.targetId; - this.sessionId = source.sessionId; - } else { - this.targetId = undefined; - this.sessionId = undefined; - } + setSession(sessionId) { + this.sessionId = sessionId; } /** diff --git a/lighthouse-core/test/gather/driver-test.js b/lighthouse-core/test/gather/driver-test.js index 8b87bf6f2fcc..6fb7d7854c03 100644 --- a/lighthouse-core/test/gather/driver-test.js +++ b/lighthouse-core/test/gather/driver-test.js @@ -198,7 +198,7 @@ describe('.evaluateAsync', () => { it('uses a high default timeout', async () => { connectionStub.sendCommand = createMockSendCommandFn() - .mockResponse('Runtime.evaluate', {result: {value: 2}}, 65000); + .mockResponse('Runtime.evaluate', {result: {value: 2}}, 65000); const evaluatePromise = makePromiseInspectable(driver.evaluateAsync('1 + 1')); jest.advanceTimersByTime(30000); @@ -213,7 +213,7 @@ describe('.evaluateAsync', () => { it('uses the specific timeout given', async () => { connectionStub.sendCommand = createMockSendCommandFn() - .mockResponse('Runtime.evaluate', {result: {value: 2}}, 10000); + .mockResponse('Runtime.evaluate', {result: {value: 2}}, 10000); driver.setNextProtocolTimeout(5000); const evaluatePromise = makePromiseInspectable(driver.evaluateAsync('1 + 1')); @@ -343,6 +343,7 @@ describe('.setExtraHTTPHeaders', () => { expect(connectionStub.sendCommand).toHaveBeenCalledWith( 'Network.setExtraHTTPHeaders', + undefined, expect.anything() ); }); @@ -870,76 +871,80 @@ describe('.goOnline', () => { }); }); -describe('Multi-target management', () => { - it('enables the Network domain for iframes', async () => { +describe('Domain.enable/disable State', () => { + it('dedupes (simple)', async () => { connectionStub.sendCommand = createMockSendCommandFn() - .mockResponse('Target.sendMessageToTarget', {}) - .mockResponse('Target.sendMessageToTarget', {}) - .mockResponse('Target.sendMessageToTarget', {}); + .mockResponse('Network.enable') + .mockResponse('Network.disable') + .mockResponse('Fetch.enable') + .mockResponse('Fetch.disable'); - driver._eventEmitter.emit('Target.attachedToTarget', { - sessionId: 123, - targetInfo: {type: 'iframe'}, - }); - await flushAllTimersAndMicrotasks(); + await driver.sendCommand('Network.enable', {}); + await driver.sendCommand('Network.enable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(1); - const sendMessageArgs = connectionStub.sendCommand - .findInvocation('Target.sendMessageToTarget'); - expect(sendMessageArgs).toEqual({ - message: '{"id":1,"method":"Network.enable"}', - sessionId: 123, - }); + await driver.sendCommand('Network.disable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(1); + // Network still has one enable. + + await driver.sendCommand('Fetch.enable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(2); + + await driver.sendCommand('Network.disable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(3); + // Network is now disabled. + + await driver.sendCommand('Fetch.disable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(4); }); - it('enables the Network domain for iframes of iframes of iframes', async () => { + it('dedupes (sessions)', async () => { connectionStub.sendCommand = createMockSendCommandFn() - .mockResponse('Target.sendMessageToTarget', {}) - .mockResponse('Target.sendMessageToTarget', {}) - .mockResponse('Target.sendMessageToTarget', {}); + .mockResponse('Network.enable') + .mockResponseToSession('Network.enable', '123') + .mockResponse('Network.disable') + .mockResponseToSession('Network.disable', '123'); - driver._eventEmitter.emit('Target.receivedMessageFromTarget', { - sessionId: 'Outer', - message: JSON.stringify({ - method: 'Target.receivedMessageFromTarget', - params: { - sessionId: 'Middle', - message: JSON.stringify({ - method: 'Target.attachedToTarget', - params: { - sessionId: 'Inner', - targetInfo: {type: 'iframe'}, - }, - }), - }, - }), - }); + await driver.sendCommand('Network.enable', {}); + await driver.sendCommandToSession('Network.enable', '123', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(2); - await flushAllTimersAndMicrotasks(); + await driver.sendCommand('Network.enable', {}); + await driver.sendCommandToSession('Network.enable', '123', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(2); - const sendMessageArgs = connectionStub.sendCommand - .findInvocation('Target.sendMessageToTarget'); - const stringified = `{ - "id": 3, - "method": "Target.sendMessageToTarget", - "params": { - "sessionId": "Middle", - "message": "{ - \\"id\\": 2, - \\"method\\": \\"Target.sendMessageToTarget\\", - \\"params\\": { - \\"sessionId\\": \\"Inner\\", - \\"message\\":\\ "{ - \\\\\\"id\\\\\\":1, - \\\\\\"method\\\\\\":\\\\\\"Network.enable\\\\\\" - }\\" - }}" - } - }`.replace(/\s+/g, ''); + await driver.sendCommandToSession('Network.disable', '123', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(2); - expect(sendMessageArgs).toEqual({ - message: stringified, - sessionId: 'Outer', + await driver.sendCommand('Network.disable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(2); + + await driver.sendCommandToSession('Network.disable', '123', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(3); + + await driver.sendCommand('Network.disable', {}); + expect(connectionStub.sendCommand).toHaveBeenCalledTimes(4); + }); +}); + +describe('Multi-target management', () => { + it('enables the Network domain for iframes', async () => { + connectionStub.sendCommand = createMockSendCommandFn() + .mockResponseToSession('Network.enable', '123', {}) + .mockResponseToSession('Target.setAutoAttach', '123', {}) + .mockResponseToSession('Runtime.runIfWaitingForDebugger', '123', {}); + + driver._eventEmitter.emit('Target.attachedToTarget', { + sessionId: '123', + targetInfo: {type: 'iframe'}, }); + await flushAllTimersAndMicrotasks(); + + expect(connectionStub.sendCommand).toHaveBeenNthCalledWith(1, 'Network.enable', '123'); + expect(connectionStub.sendCommand) + .toHaveBeenNthCalledWith(2, 'Target.setAutoAttach', '123', expect.anything()); + expect(connectionStub.sendCommand) + .toHaveBeenNthCalledWith(3, 'Runtime.runIfWaitingForDebugger', '123'); }); it('ignores other target types, but still resumes them', async () => { @@ -952,12 +957,7 @@ describe('Multi-target management', () => { }); await flushAllTimersAndMicrotasks(); - - const sendMessageArgs = connectionStub.sendCommand - .findInvocation('Target.sendMessageToTarget'); - expect(sendMessageArgs).toEqual({ - message: JSON.stringify({id: 1, method: 'Runtime.runIfWaitingForDebugger'}), - sessionId: 'SW1', - }); + expect(connectionStub.sendCommand) + .toHaveBeenNthCalledWith(1, 'Runtime.runIfWaitingForDebugger', 'SW1'); }); }); diff --git a/lighthouse-core/test/gather/gather-runner-test.js b/lighthouse-core/test/gather/gather-runner-test.js index a05e49d4ecd8..8a504ca04164 100644 --- a/lighthouse-core/test/gather/gather-runner-test.js +++ b/lighthouse-core/test/gather/gather-runner-test.js @@ -63,7 +63,7 @@ function getMockedEmulationDriver(emulationFn, netThrottleFn, cpuThrottleFn, clearDataForOrigin() {} }; const EmulationMock = class extends Connection { - sendCommand(command, params) { + sendCommand(command, sessionId, params) { let fn = null; switch (command) { case 'Network.emulateNetworkConditions': diff --git a/lighthouse-core/test/gather/mock-commands.js b/lighthouse-core/test/gather/mock-commands.js index 32d7872634cb..773935e04222 100644 --- a/lighthouse-core/test/gather/mock-commands.js +++ b/lighthouse-core/test/gather/mock-commands.js @@ -22,8 +22,9 @@ */ function createMockSendCommandFn() { const mockResponses = []; - const mockFn = jest.fn().mockImplementation((command, ...args) => { - const indexOfResponse = mockResponses.findIndex(entry => entry.command === command); + const mockFn = jest.fn().mockImplementation((command, sessionId, ...args) => { + const indexOfResponse = mockResponses + .findIndex(entry => entry.command === command && entry.sessionId === sessionId); if (indexOfResponse === -1) throw new Error(`${command} unimplemented`); const {response, delay} = mockResponses[indexOfResponse]; mockResponses.splice(indexOfResponse, 1); @@ -37,9 +38,14 @@ function createMockSendCommandFn() { return mockFn; }; - mockFn.findInvocation = command => { - expect(mockFn).toHaveBeenCalledWith(command, expect.anything()); - return mockFn.mock.calls.find(call => call[0] === command)[1]; + mockFn.mockResponseToSession = (command, sessionId, response, delay) => { + mockResponses.push({command, sessionId, response, delay}); + return mockFn; + }; + + mockFn.findInvocation = (command, sessionId) => { + expect(mockFn).toHaveBeenCalledWith(command, sessionId, expect.anything()); + return mockFn.mock.calls.find(call => call[0] === command && call[1] === sessionId)[2]; }; return mockFn; diff --git a/lighthouse-core/test/lib/network-recorder-test.js b/lighthouse-core/test/lib/network-recorder-test.js index bb318434e659..2c1c8c5def76 100644 --- a/lighthouse-core/test/lib/network-recorder-test.js +++ b/lighthouse-core/test/lib/network-recorder-test.js @@ -195,18 +195,18 @@ describe('network recorder', function() { ).params.requestId; for (const log of devtoolsLogs) { - if (log.params.requestId === requestId1) log.source = {sessionId: '1', targetId: 'a'}; + if (log.params.requestId === requestId1) log.sessionId = '1'; if (log.params.requestId === requestId2 && log.method === 'Network.loadingFinished') { - log.source = {sessionId: '2', targetId: 'b'}; + log.sessionId = '2'; } } const records = NetworkRecorder.recordsFromLogs(devtoolsLogs); expect(records).toMatchObject([ - {url: 'http://example.com', sessionId: undefined, targetId: undefined}, - {url: 'http://iframe.com', sessionId: '1', targetId: 'a'}, - {url: 'http://other-iframe.com', sessionId: '2', targetId: 'b'}, + {url: 'http://example.com', sessionId: undefined}, + {url: 'http://iframe.com', sessionId: '1'}, + {url: 'http://other-iframe.com', sessionId: '2'}, ]); }); diff --git a/types/protocol.d.ts b/types/protocol.d.ts index 61c3ec5804ef..c57881f2c110 100644 --- a/types/protocol.d.ts +++ b/types/protocol.d.ts @@ -12,11 +12,6 @@ declare global { */ export type RawEventMessage = RawEventMessageRecord[keyof RawEventMessageRecord]; - export interface RawSource { - targetId?: string; - sessionId: string; - } - /** * Raw (over the wire) message format of all possible Crdp command responses. */ @@ -70,8 +65,8 @@ type RawEventMessageRecord = { method: K, // Drop [] for `undefined` (so a JS value is valid). params: LH.CrdpEvents[K] extends [] ? undefined: LH.CrdpEvents[K][number] - // If the source is not set, it means the event was from the root target. - source?: LH.Protocol.RawSource + // If sessionId is not set, it means the event was from the root target. + sessionId?: string; }; }