From 35de9e96a6e8b5e9af1d5844146e982412e02927 Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Thu, 3 Jan 2019 11:55:56 -0600 Subject: [PATCH 1/5] core(oopif): collect network requests from iframes --- lighthouse-cli/test/fixtures/oopif.html | 10 ++++++++ .../test/smokehouse/oopif-config.js | 18 +++++++++++++ .../test/smokehouse/oopif-expectations.js | 25 +++++++++++++++++++ lighthouse-cli/test/smokehouse/run-smoke.js | 5 ++++ lighthouse-core/gather/driver.js | 22 ++++++++++++++-- lighthouse-core/lib/network-recorder.js | 16 +++++++++--- 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 lighthouse-cli/test/fixtures/oopif.html create mode 100644 lighthouse-cli/test/smokehouse/oopif-config.js create mode 100644 lighthouse-cli/test/smokehouse/oopif-expectations.js diff --git a/lighthouse-cli/test/fixtures/oopif.html b/lighthouse-cli/test/fixtures/oopif.html new file mode 100644 index 000000000000..b27db2d3c848 --- /dev/null +++ b/lighthouse-cli/test/fixtures/oopif.html @@ -0,0 +1,10 @@ + + + + Where is my iframe? + + +

Hello frames

+ + + diff --git a/lighthouse-cli/test/smokehouse/oopif-config.js b/lighthouse-cli/test/smokehouse/oopif-config.js new file mode 100644 index 000000000000..8abf51254a48 --- /dev/null +++ b/lighthouse-cli/test/smokehouse/oopif-config.js @@ -0,0 +1,18 @@ +/** + * @license Copyright 2019 Google Inc. All Rights Reserved. + * 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. + */ +'use strict'; + +/** + * Config file for running PWA smokehouse audits. + */ +module.exports = { + extends: 'lighthouse:default', + settings: { + onlyAudits: [ + 'network-requests', + ], + }, +}; diff --git a/lighthouse-cli/test/smokehouse/oopif-expectations.js b/lighthouse-cli/test/smokehouse/oopif-expectations.js new file mode 100644 index 000000000000..3694cbdbf70f --- /dev/null +++ b/lighthouse-cli/test/smokehouse/oopif-expectations.js @@ -0,0 +1,25 @@ +/** + * @license Copyright 2019 Google Inc. All Rights Reserved. + * 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. + */ +'use strict'; + +/** + * Expected Lighthouse audit values for sites with various errors. + */ +module.exports = [ + { + requestedUrl: 'http://localhost:10200/oopif.html', + finalUrl: 'http://localhost:10200/oopif.html', + audits: { + 'network-requests': { + details: { + items: { + length: '>10', + }, + }, + }, + }, + }, +]; diff --git a/lighthouse-cli/test/smokehouse/run-smoke.js b/lighthouse-cli/test/smokehouse/run-smoke.js index 12f2cb86d7cf..96c971c8ba4f 100644 --- a/lighthouse-cli/test/smokehouse/run-smoke.js +++ b/lighthouse-cli/test/smokehouse/run-smoke.js @@ -34,6 +34,11 @@ const SMOKETESTS = [{ expectations: smokehouseDir + 'error-expectations.js', config: smokehouseDir + 'error-config.js', batch: 'errors', +}, { + id: 'oopif', + expectations: smokehouseDir + 'oopif-expectations.js', + config: smokehouseDir + 'oopif-config.js', + batch: 'parallel-first', }, { id: 'pwa', expectations: smokehouseDir + 'pwa-expectations.js', diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index aae4a4339a38..3f696826a056 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -47,8 +47,8 @@ class Driver { */ this._eventEmitter = /** @type {CrdpEventEmitter} */ (new EventEmitter()); this._connection = connection; - // currently only used by WPT where just Page and Network are needed - this._devtoolsLog = new DevtoolsLog(/^(Page|Network)\./); + // currently used by network-recorder just Page, Network, and Target are needed + this._devtoolsLog = new DevtoolsLog(/^(Page|Network|Target)\./); this.online = true; /** @type {Map} */ this._domainEnabledCounts = new Map(); @@ -69,6 +69,18 @@ class Driver { */ this._monitoredUrl = null; + let targetProxyMessageId = 0; + this.on('Target.attachedToTarget', event => { + targetProxyMessageId++; + + // We want to receive information about network requests from iframes, so enable the Network domain. + // Network events from subtargets will be stringified and sent back on `Target.receivedMessageFromTarget`. + this.sendCommand('Target.sendMessageToTarget', { + message: JSON.stringify({id: targetProxyMessageId, method: 'Network.enable'}), + sessionId: event.sessionId, + }); + }); + connection.on('protocolevent', event => { this._devtoolsLog.record(event); if (this._networkStatusMonitor) { @@ -890,6 +902,12 @@ class Driver { await this._beginNetworkStatusMonitoring(url); await this._clearIsolatedContextId(); + // Enable auto-attaching to subtargets so we receive iframe information + await this.sendCommand('Target.setAutoAttach', { + autoAttach: true, + waitForDebuggerOnStart: false, + }); + await this.sendCommand('Page.enable'); await this.sendCommand('Emulation.setScriptExecutionDisabled', {value: disableJS}); // No timeout needed for Page.navigate. See #6413. diff --git a/lighthouse-core/lib/network-recorder.js b/lighthouse-core/lib/network-recorder.js index 4de1df7ffe4e..131e25c31ade 100644 --- a/lighthouse-core/lib/network-recorder.js +++ b/lighthouse-core/lib/network-recorder.js @@ -304,15 +304,22 @@ class NetworkRecorder extends EventEmitter { request.onResourceChangedPriority(data); } + /** + * @param {LH.Crdp.Target.ReceivedMessageFromTargetEvent} data + */ + onReceivedMessageFromTarget(data) { + /** @type {LH.Protocol.RawMessage} */ + const protocolMessage = JSON.parse(data.message); + if ('id' in protocolMessage) return; + + this.dispatch(protocolMessage); + } + /** * Routes network events to their handlers, so we can construct networkRecords * @param {LH.Protocol.RawEventMessage} event */ dispatch(event) { - if (!event.method.startsWith('Network.')) { - return; - } - switch (event.method) { case 'Network.requestWillBeSent': return this.onRequestWillBeSent(event.params); case 'Network.requestServedFromCache': return this.onRequestServedFromCache(event.params); @@ -321,6 +328,7 @@ class NetworkRecorder extends EventEmitter { case 'Network.loadingFinished': return this.onLoadingFinished(event.params); case 'Network.loadingFailed': return this.onLoadingFailed(event.params); case 'Network.resourceChangedPriority': return this.onResourceChangedPriority(event.params); + case 'Target.receivedMessageFromTarget': return this.onReceivedMessageFromTarget(event.params); // eslint-disable-line max-len default: return; } } From d6e8aeaa492bb9c99ec1b01ee139f109f42b8d99 Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Thu, 3 Jan 2019 12:10:03 -0600 Subject: [PATCH 2/5] comment tweaks --- lighthouse-cli/test/smokehouse/oopif-config.js | 2 +- lighthouse-cli/test/smokehouse/oopif-expectations.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lighthouse-cli/test/smokehouse/oopif-config.js b/lighthouse-cli/test/smokehouse/oopif-config.js index 8abf51254a48..ebc5fa22bfe6 100644 --- a/lighthouse-cli/test/smokehouse/oopif-config.js +++ b/lighthouse-cli/test/smokehouse/oopif-config.js @@ -6,7 +6,7 @@ 'use strict'; /** - * Config file for running PWA smokehouse audits. + * Config file for running the OOPIF tests */ module.exports = { extends: 'lighthouse:default', diff --git a/lighthouse-cli/test/smokehouse/oopif-expectations.js b/lighthouse-cli/test/smokehouse/oopif-expectations.js index 3694cbdbf70f..d1871de079b7 100644 --- a/lighthouse-cli/test/smokehouse/oopif-expectations.js +++ b/lighthouse-cli/test/smokehouse/oopif-expectations.js @@ -6,7 +6,7 @@ 'use strict'; /** - * Expected Lighthouse audit values for sites with various errors. + * Expected Lighthouse audit values for sites with OOPIFS. */ module.exports = [ { From 1734ee018ae97f23fbe533204c9d9da6fae46b75 Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Fri, 1 Mar 2019 10:00:36 -0600 Subject: [PATCH 3/5] only listen for iframes --- lighthouse-core/gather/driver.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index b1e64473fa14..74bc5c20c4b3 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -72,6 +72,8 @@ class Driver { let targetProxyMessageId = 0; this.on('Target.attachedToTarget', event => { targetProxyMessageId++; + // We're only interested in network requests from iframes for now as those are "part of the page". + if (event.targetInfo.type !== 'iframe') return; // We want to receive information about network requests from iframes, so enable the Network domain. // Network events from subtargets will be stringified and sent back on `Target.receivedMessageFromTarget`. From f546378250a415015310eaf313ca412eed54daf5 Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Fri, 1 Mar 2019 10:14:40 -0600 Subject: [PATCH 4/5] driver tests --- lighthouse-core/test/gather/driver-test.js | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lighthouse-core/test/gather/driver-test.js b/lighthouse-core/test/gather/driver-test.js index 5befd18dd8b7..b69ff4ddc3cc 100644 --- a/lighthouse-core/test/gather/driver-test.js +++ b/lighthouse-core/test/gather/driver-test.js @@ -487,6 +487,7 @@ describe('.gotoURL', () => { .mockResponse('Page.setLifecycleEventsEnabled', {}) .mockResponse('Emulation.setScriptExecutionDisabled', {}) .mockResponse('Page.navigate', {}) + .mockResponse('Target.setAutoAttach', {}) .mockResponse('Runtime.evaluate', {}); }); @@ -930,3 +931,36 @@ describe('.goOnline', () => { }); }); }); + +describe('Multi-target management', () => { + it('enables the Network domain for iframes', async () => { + connectionStub.sendCommand = createMockSendCommandFn() + .mockResponse('Target.sendMessageToTarget', {}); + + driver._eventEmitter.emit('Target.attachedToTarget', { + sessionId: 123, + targetInfo: {type: 'iframe'}, + }); + await flushAllTimersAndMicrotasks(); + + const sendMessageArgs = connectionStub.sendCommand + .findInvocation('Target.sendMessageToTarget'); + expect(sendMessageArgs).toEqual({ + message: '{"id":1,"method":"Network.enable"}', + sessionId: 123, + }); + }); + + it('ignores other target types', async () => { + connectionStub.sendCommand = createMockSendCommandFn() + .mockResponse('Target.sendMessageToTarget', {}); + + driver._eventEmitter.emit('Target.attachedToTarget', { + sessionId: 123, + targetInfo: {type: 'service_worker'}, + }); + await flushAllTimersAndMicrotasks(); + + expect(connectionStub.sendCommand).not.toHaveBeenCalled(); + }); +}); From 3a10248d1e2da904c3e7d43cfff438693d8c4e67 Mon Sep 17 00:00:00 2001 From: Patrick Hulce Date: Sat, 2 Mar 2019 11:18:15 -0600 Subject: [PATCH 5/5] feedback --- lighthouse-cli/test/smokehouse/oopif-expectations.js | 3 +++ lighthouse-core/gather/driver.js | 2 +- lighthouse-core/lib/network-recorder.js | 6 +++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lighthouse-cli/test/smokehouse/oopif-expectations.js b/lighthouse-cli/test/smokehouse/oopif-expectations.js index d1871de079b7..94eac432dc3c 100644 --- a/lighthouse-cli/test/smokehouse/oopif-expectations.js +++ b/lighthouse-cli/test/smokehouse/oopif-expectations.js @@ -16,6 +16,9 @@ module.exports = [ 'network-requests': { details: { items: { + // The page itself only makes a few requests. + // We want to make sure we are finding the iframe's requests (airhorner) while being flexible enough + // to allow changes to the live site. length: '>10', }, }, diff --git a/lighthouse-core/gather/driver.js b/lighthouse-core/gather/driver.js index 74bc5c20c4b3..b3e593d29c86 100644 --- a/lighthouse-core/gather/driver.js +++ b/lighthouse-core/gather/driver.js @@ -47,7 +47,7 @@ class Driver { */ this._eventEmitter = /** @type {CrdpEventEmitter} */ (new EventEmitter()); this._connection = connection; - // currently used by network-recorder just Page, Network, and Target are needed + // Used to save network and lifecycle protocol traffic. Just Page, Network, and Target are needed. this._devtoolsLog = new DevtoolsLog(/^(Page|Network|Target)\./); this.online = true; /** @type {Map} */ diff --git a/lighthouse-core/lib/network-recorder.js b/lighthouse-core/lib/network-recorder.js index 54f2610059a6..ed38a3498b27 100644 --- a/lighthouse-core/lib/network-recorder.js +++ b/lighthouse-core/lib/network-recorder.js @@ -304,13 +304,17 @@ class NetworkRecorder extends EventEmitter { } /** + * Events from targets other than the main frame are proxied through `Target.receivedMessageFromTarget`. + * Their payloads are JSON-stringified into the `.message` property * @param {LH.Crdp.Target.ReceivedMessageFromTargetEvent} data */ onReceivedMessageFromTarget(data) { /** @type {LH.Protocol.RawMessage} */ const protocolMessage = JSON.parse(data.message); - if ('id' in protocolMessage) return; + // Message was a response to some command, not an event, so we'll ignore it. + if ('id' in protocolMessage) return; + // Message was an event, replay it through our normal dispatch process. this.dispatch(protocolMessage); }