Skip to content

Commit

Permalink
core(fr): extract network monitor class (#12006)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickhulce authored Jan 28, 2021
1 parent 20bd056 commit 5c32e64
Show file tree
Hide file tree
Showing 14 changed files with 720 additions and 442 deletions.
4 changes: 2 additions & 2 deletions lighthouse-core/computed/metrics/interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const makeComputedArtifact = require('../computed-artifact.js');
const ComputedMetric = require('./metric.js');
const LanternInteractive = require('./lantern-interactive.js');

const NetworkRecorder = require('../../lib/network-recorder.js');
const NetworkMonitor = require('../../gather/driver/network-monitor.js');
const TracingProcessor = require('../../lib/tracehouse/trace-processor.js');
const LHError = require('../../lib/lh-error.js');

Expand Down Expand Up @@ -37,7 +37,7 @@ class Interactive extends ComputedMetric {
// Consider network records that had 4xx/5xx status code as "failed"
record.statusCode < 400;
});
return NetworkRecorder.findNetworkQuietPeriods(filteredNetworkRecords,
return NetworkMonitor.findNetworkQuietPeriods(filteredNetworkRecords,
ALLOWED_CONCURRENT_REQUESTS, traceEndTsInMs);
}

Expand Down
3 changes: 2 additions & 1 deletion lighthouse-core/fraggle-rock/gather/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ const defaultSession = {
setNextProtocolTimeout: throwNotConnectedFn,
on: throwNotConnectedFn,
once: throwNotConnectedFn,
onAnyProtocolMessage: throwNotConnectedFn,
off: throwNotConnectedFn,
addProtocolMessageListener: throwNotConnectedFn,
removeProtocolMessageListener: throwNotConnectedFn,
sendCommand: throwNotConnectedFn,
};

Expand Down
10 changes: 9 additions & 1 deletion lighthouse-core/fraggle-rock/gather/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,18 @@ class ProtocolSession {
* Bind to our custom event that fires for *any* protocol event.
* @param {(payload: LH.Protocol.RawEventMessage) => void} callback
*/
onAnyProtocolMessage(callback) {
addProtocolMessageListener(callback) {
this._session.on('*', /** @type {*} */ (callback));
}

/**
* Unbind to our custom event that fires for *any* protocol event.
* @param {(payload: LH.Protocol.RawEventMessage) => void} callback
*/
removeProtocolMessageListener(callback) {
this._session.off('*', /** @type {*} */ (callback));
}

/**
* Bind listeners for protocol events.
* @template {keyof LH.CrdpEvents} E
Expand Down
16 changes: 16 additions & 0 deletions lighthouse-core/gather/connections/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ class Connection {
this._eventEmitter.on(eventName, cb);
}

/**
* Unbind listeners for connection events.
* @param {'protocolevent'} eventName
* @param {function(LH.Protocol.RawEventMessage): void} cb
*/
off(eventName, cb) {
if (eventName !== 'protocolevent') {
throw new Error('Only supports "protocolevent" events');
}

if (!this._eventEmitter) {
throw new Error('Attempted to remove event listener after connection disposed.');
}
this._eventEmitter.removeListener(eventName, cb);
}

/* eslint-disable no-unused-vars */

/**
Expand Down
75 changes: 17 additions & 58 deletions lighthouse-core/gather/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
const Fetcher = require('./fetcher.js');
const ExecutionContext = require('./driver/execution-context.js');
const {waitForFullyLoaded, waitForFrameNavigated} = require('./driver/wait-for-condition.js');
const NetworkRecorder = require('../lib/network-recorder.js');
const emulation = require('../lib/emulation.js');
const LHElement = require('../lib/lh-element.js');
const LHError = require('../lib/lh-error.js');
Expand All @@ -26,6 +25,7 @@ const pageFunctions = require('../lib/page-functions.js');
// Pulled in for Connection type checking.
// eslint-disable-next-line no-unused-vars
const Connection = require('./connections/connection.js');
const NetworkMonitor = require('./driver/network-monitor.js');

const UIStrings = {
/**
Expand Down Expand Up @@ -85,13 +85,6 @@ class Driver {
*/
_domainEnabledCounts = new Map();

/**
* Used for monitoring network status events during gotoURL.
* @type {?NetworkRecorder}
* @private
*/
_networkStatusMonitor = null;

/**
* Used for monitoring url redirects during gotoURL.
* @type {?string}
Expand Down Expand Up @@ -128,6 +121,7 @@ class Driver {
*/
constructor(connection) {
this._connection = connection;
this._networkMonitor = new NetworkMonitor(this);

this.on('Target.attachedToTarget', event => {
this._handleTargetAttached(event).catch(this._handleEventError);
Expand Down Expand Up @@ -280,10 +274,18 @@ class Driver {
* Bind to *any* protocol event.
* @param {(payload: LH.Protocol.RawEventMessage) => void} callback
*/
onAnyProtocolMessage(callback) {
addProtocolMessageListener(callback) {
this._connection.on('protocolevent', callback);
}

/**
* Unbind to *any* protocol event.
* @param {(payload: LH.Protocol.RawEventMessage) => void} callback
*/
removeProtocolMessageListener(callback) {
this._connection.off('protocolevent', callback);
}

/**
* Debounce enabling or disabling domains to prevent driver users from
* stomping on each other. Maintains an internal count of the times a domain
Expand Down Expand Up @@ -344,9 +346,6 @@ class Driver {
*/
_handleProtocolEvent(event) {
this._devtoolsLog.record(event);
if (this._networkStatusMonitor) {
this._networkStatusMonitor.dispatch(event);
}

// @ts-expect-error TODO(bckenny): tsc can't type event.params correctly yet,
// typing as property of union instead of narrowing from union of
Expand Down Expand Up @@ -637,47 +636,6 @@ class Driver {
});
}

/**
* Set up listener for network quiet events and reset the monitored navigation events.
* @param {string} startingUrl
* @return {Promise<void>}
* @private
*/
_beginNetworkStatusMonitoring(startingUrl) {
this._networkStatusMonitor = new NetworkRecorder();

this._monitoredUrl = startingUrl;
// Reset back to empty
this._monitoredUrlNavigations = [];

return this.sendCommand('Network.enable');
}

/**
* End network status listening. Returns the final, possibly redirected,
* loaded URL starting with the one passed into _endNetworkStatusMonitoring.
* @return {Promise<string>}
* @private
*/
async _endNetworkStatusMonitoring() {
const startingUrl = this._monitoredUrl;
const frameNavigations = this._monitoredUrlNavigations;

const resourceTreeResponse = await this.sendCommand('Page.getResourceTree');
const mainFrameId = resourceTreeResponse.frameTree.frame.id;
const mainFrameNavigations = frameNavigations.filter(frame => frame.id === mainFrameId);
const finalNavigation = mainFrameNavigations[mainFrameNavigations.length - 1];

this._networkStatusMonitor = null;
this._monitoredUrl = null;
this._monitoredUrlNavigations = [];

const finalUrl = (finalNavigation && finalNavigation.url) || startingUrl;
if (!finalNavigation) log.warn('Driver', 'No detected navigations');
if (!finalUrl) throw new Error('Unable to determine finalUrl');
return finalUrl;
}

/**
* Navigate to the given URL. Direct use of this method isn't advised: if
* the current page is already at the given URL, navigation will not occur and
Expand All @@ -700,7 +658,7 @@ class Driver {
throw new Error('Cannot use both waitForNavigated and another event, pick just one');
}

await this._beginNetworkStatusMonitoring(url);
await this._networkMonitor.enable();
await this._executionContext.clearContextId();

// Enable auto-attaching to subtargets so we receive iframe information
Expand All @@ -720,7 +678,6 @@ class Driver {
if (waitForNavigated) {
await waitForFrameNavigated(this).promise;
} else if (waitForLoad) {
const networkMonitor = this._networkStatusMonitor;
/** @type {Partial<LH.Config.Pass>} */
const passConfig = passContext.passConfig || {};

Expand All @@ -735,21 +692,23 @@ class Driver {
if (typeof cpuQuietThresholdMs !== 'number') cpuQuietThresholdMs = DEFAULT_CPU_QUIET_THRESHOLD;
if (typeof maxWaitMs !== 'number') maxWaitMs = constants.defaultSettings.maxWaitForLoad;
if (typeof maxFCPMs !== 'number') maxFCPMs = constants.defaultSettings.maxWaitForFcp;
if (!networkMonitor) throw new Error('Failed to instantiate networkStatusMonitor');
/* eslint-enable max-len */

if (!waitForFcp) maxFCPMs = undefined;
const waitOptions = {pauseAfterFcpMs, pauseAfterLoadMs, networkQuietThresholdMs,
cpuQuietThresholdMs, maxWaitForLoadedMs: maxWaitMs, maxWaitForFcpMs: maxFCPMs};
const loadResult = await waitForFullyLoaded(this, networkMonitor, waitOptions);
const loadResult = await waitForFullyLoaded(this, this._networkMonitor, waitOptions);
timedOut = loadResult.timedOut;
}

const finalUrl = await this._networkMonitor.getFinalNavigationUrl() || url;

// Bring `Page.navigate` errors back into the promise chain. See https://github.com/GoogleChrome/lighthouse/pull/6739.
await waitforPageNavigateCmd;
await this._networkMonitor.disable();

return {
finalUrl: await this._endNetworkStatusMonitoring(),
finalUrl,
timedOut,
};
}
Expand Down
Loading

0 comments on commit 5c32e64

Please sign in to comment.