lighthouse 9.5.0-dev.20220621 → 9.5.0-dev.20220624

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/lighthouse-cli/bin.js +8 -2
  2. package/lighthouse-cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +43 -0
  3. package/lighthouse-cli/test/smokehouse/report-assert-test.js +96 -30
  4. package/lighthouse-cli/test/smokehouse/report-assert.js +36 -35
  5. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
  6. package/lighthouse-core/config/config-helpers.js +15 -0
  7. package/lighthouse-core/fraggle-rock/config/config.js +45 -1
  8. package/lighthouse-core/fraggle-rock/gather/driver.js +13 -5
  9. package/lighthouse-core/fraggle-rock/gather/session.js +23 -67
  10. package/lighthouse-core/gather/driver/navigation.js +1 -1
  11. package/lighthouse-core/gather/driver/network-monitor.js +19 -81
  12. package/lighthouse-core/gather/driver/target-manager.js +110 -45
  13. package/lighthouse-core/gather/driver.js +24 -16
  14. package/lighthouse-core/gather/gatherers/devtools-log.js +7 -11
  15. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +1 -1
  16. package/lighthouse-core/index.js +24 -9
  17. package/lighthouse-core/lib/cdt/Common.js +13 -0
  18. package/lighthouse-core/lib/cdt/Platform.js +3 -0
  19. package/lighthouse-core/lib/cdt/generated/ParsedURL.js +178 -0
  20. package/lighthouse-core/lib/cdt/generated/SourceMap.js +155 -62
  21. package/lighthouse-core/lib/network-recorder.js +9 -18
  22. package/package.json +2 -2
  23. package/third-party/devtools-tests/README.md +14 -0
  24. package/third-party/devtools-tests/e2e/lighthouse/generate-report_test.ts +29 -0
  25. package/third-party/devtools-tests/e2e/lighthouse/indexeddb-warning_test.ts +29 -0
  26. package/third-party/devtools-tests/e2e/resources/lighthouse/lighthouse-storage.html +11 -0
  27. package/third-party/download-content-shell/README.md +1 -0
  28. package/third-party/download-content-shell/download-content-shell.js +6 -1
  29. package/types/gatherer.d.ts +5 -2
  30. package/types/protocol.d.ts +26 -17
@@ -5,31 +5,45 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
+ const EventEmitter = require('events').EventEmitter;
8
9
  const LHError = require('../../lib/lh-error.js');
9
10
 
10
11
  // Controls how long to wait for a response after sending a DevTools protocol command.
11
12
  const DEFAULT_PROTOCOL_TIMEOUT = 30000;
12
13
 
14
+ /** @typedef {LH.Protocol.StrictEventEmitterClass<LH.CrdpEvents>} CrdpEventMessageEmitter */
15
+ const CrdpEventEmitter = /** @type {CrdpEventMessageEmitter} */ (EventEmitter);
16
+
13
17
  /** @implements {LH.Gatherer.FRProtocolSession} */
14
- class ProtocolSession {
18
+ class ProtocolSession extends CrdpEventEmitter {
15
19
  /**
16
20
  * @param {LH.Puppeteer.CDPSession} cdpSession
17
21
  */
18
22
  constructor(cdpSession) {
23
+ super();
24
+
19
25
  this._cdpSession = cdpSession;
20
26
  /** @type {LH.Crdp.Target.TargetInfo|undefined} */
21
27
  this._targetInfo = undefined;
22
28
  /** @type {number|undefined} */
23
29
  this._nextProtocolTimeout = undefined;
24
- /** @type {WeakMap<any, any>} */
25
- this._callbackMap = new WeakMap();
30
+
31
+ this._handleProtocolEvent = this._handleProtocolEvent.bind(this);
32
+ this._cdpSession.on('*', this._handleProtocolEvent);
33
+ }
34
+
35
+ id() {
36
+ return this._cdpSession.id();
26
37
  }
27
38
 
28
- sessionId() {
29
- return this._targetInfo && this._targetInfo.type === 'iframe' ?
30
- // TODO: use this._session.id() for real session id.
31
- this._targetInfo.targetId :
32
- undefined;
39
+ /**
40
+ * Re-emit protocol events from the underlying CDPSession.
41
+ * @template {keyof LH.CrdpEvents} E
42
+ * @param {E} method
43
+ * @param {LH.CrdpEvents[E]} params
44
+ */
45
+ _handleProtocolEvent(method, ...params) {
46
+ this.emit(method, ...params);
33
47
  }
34
48
 
35
49
  /** @param {LH.Crdp.Target.TargetInfo} targetInfo */
@@ -58,64 +72,6 @@ class ProtocolSession {
58
72
  this._nextProtocolTimeout = ms;
59
73
  }
60
74
 
61
- /**
62
- * Bind listeners for protocol events.
63
- * @template {keyof LH.CrdpEvents} E
64
- * @param {E} eventName
65
- * @param {(...args: LH.CrdpEvents[E]) => void} callback
66
- */
67
- on(eventName, callback) {
68
- this._cdpSession.on(eventName, /** @type {*} */ (callback));
69
- }
70
-
71
- /**
72
- * Bind listeners for protocol events.
73
- * @template {keyof LH.CrdpEvents} E
74
- * @param {E} eventName
75
- * @param {(...args: LH.CrdpEvents[E]) => void} callback
76
- */
77
- once(eventName, callback) {
78
- this._cdpSession.once(eventName, /** @type {*} */ (callback));
79
- }
80
-
81
- /**
82
- * Bind to puppeteer's '*' event that fires for *any* protocol event,
83
- * and wrap it with data about the protocol message instead of just the event.
84
- * @param {(payload: LH.Protocol.RawEventMessage) => void} callback
85
- */
86
- addProtocolMessageListener(callback) {
87
- /**
88
- * @param {any} method
89
- * @param {any} event
90
- */
91
- const listener = (method, event) => callback({
92
- method,
93
- params: event,
94
- sessionId: this.sessionId(),
95
- });
96
- this._callbackMap.set(callback, listener);
97
- this._cdpSession.on('*', /** @type {*} */ (listener));
98
- }
99
-
100
- /**
101
- * @param {(payload: LH.Protocol.RawEventMessage) => void} callback
102
- */
103
- removeProtocolMessageListener(callback) {
104
- const listener = this._callbackMap.get(callback);
105
- if (!listener) return;
106
- this._cdpSession.off('*', /** @type {*} */ (listener));
107
- }
108
-
109
- /**
110
- * Bind listeners for protocol events.
111
- * @template {keyof LH.CrdpEvents} E
112
- * @param {E} eventName
113
- * @param {(...args: LH.CrdpEvents[E]) => void} callback
114
- */
115
- off(eventName, callback) {
116
- this._cdpSession.off(eventName, /** @type {*} */ (callback));
117
- }
118
-
119
75
  /**
120
76
  * @template {keyof LH.CrdpCommands} C
121
77
  * @param {C} method
@@ -149,7 +105,7 @@ class ProtocolSession {
149
105
  * @return {Promise<void>}
150
106
  */
151
107
  async dispose() {
152
- this._cdpSession.removeAllListeners();
108
+ this._cdpSession.off('*', this._handleProtocolEvent);
153
109
  await this._cdpSession.detach();
154
110
  }
155
111
  }
@@ -89,7 +89,7 @@ async function gotoURL(driver, requestor, options) {
89
89
  log.time(status);
90
90
 
91
91
  const session = driver.defaultSession;
92
- const networkMonitor = new NetworkMonitor(driver.defaultSession);
92
+ const networkMonitor = new NetworkMonitor(driver.targetManager);
93
93
 
94
94
  // Enable the events and network monitor needed to track navigation progress.
95
95
  await networkMonitor.enable();
@@ -15,89 +15,53 @@ const {EventEmitter} = require('events');
15
15
  const NetworkRecorder = require('../../lib/network-recorder.js');
16
16
  const NetworkRequest = require('../../lib/network-request.js');
17
17
  const URL = require('../../lib/url-shim.js');
18
- const TargetManager = require('./target-manager.js');
19
18
 
20
- /** @typedef {import('../../lib/network-recorder.js').NetworkRecorderEvent} NetworkRecorderEvent */
19
+ /** @typedef {import('../../lib/network-recorder.js').NetworkRecorderEventMap} NetworkRecorderEventMap */
21
20
  /** @typedef {'network-2-idle'|'network-critical-idle'|'networkidle'|'networkbusy'|'network-critical-busy'|'network-2-busy'} NetworkMonitorEvent_ */
22
- /** @typedef {NetworkRecorderEvent|NetworkMonitorEvent_} NetworkMonitorEvent */
23
- /** @typedef {Record<NetworkMonitorEvent_, []> & Record<NetworkRecorderEvent, [NetworkRequest]> & {protocolmessage: [LH.Protocol.RawEventMessage]}} NetworkMonitorEventMap */
24
- /** @typedef {LH.Protocol.StrictEventEmitter<NetworkMonitorEventMap>} NetworkMonitorEmitter */
21
+ /** @typedef {Record<NetworkMonitorEvent_, []> & NetworkRecorderEventMap} NetworkMonitorEventMap */
22
+ /** @typedef {keyof NetworkMonitorEventMap} NetworkMonitorEvent */
23
+ /** @typedef {LH.Protocol.StrictEventEmitterClass<NetworkMonitorEventMap>} NetworkMonitorEmitter */
24
+ const NetworkMonitorEventEmitter = /** @type {NetworkMonitorEmitter} */ (EventEmitter);
25
25
 
26
- /** @implements {NetworkMonitorEmitter} */
27
- class NetworkMonitor {
26
+ class NetworkMonitor extends NetworkMonitorEventEmitter {
28
27
  /** @type {NetworkRecorder|undefined} */
29
28
  _networkRecorder = undefined;
30
- /** @type {TargetManager|undefined} */
31
- _targetManager = undefined;
32
29
  /** @type {Array<LH.Crdp.Page.Frame>} */
33
30
  _frameNavigations = [];
34
31
 
35
- /** @param {LH.Gatherer.FRProtocolSession} session */
36
- constructor(session) {
37
- this._session = session;
32
+ // TODO(FR-COMPAT): switch to real TargetManager when legacy removed.
33
+ /** @param {LH.Gatherer.FRTransitionalDriver['targetManager']} targetManager */
34
+ constructor(targetManager) {
35
+ super();
38
36
 
39
- this._onTargetAttached = this._onTargetAttached.bind(this);
37
+ /** @type {LH.Gatherer.FRTransitionalDriver['targetManager']} */
38
+ this._targetManager = targetManager;
40
39
 
41
- /** @type {Map<string, LH.Gatherer.FRProtocolSession>} */
42
- this._sessions = new Map();
40
+ /** @type {LH.Gatherer.FRProtocolSession} */
41
+ this._session = targetManager.rootSession();
43
42
 
44
43
  /** @param {LH.Crdp.Page.FrameNavigatedEvent} event */
45
44
  this._onFrameNavigated = event => this._frameNavigations.push(event.frame);
46
45
 
47
46
  /** @param {LH.Protocol.RawEventMessage} event */
48
47
  this._onProtocolMessage = event => {
49
- this.emit('protocolmessage', event);
50
48
  if (!this._networkRecorder) return;
51
49
  this._networkRecorder.dispatch(event);
52
50
  };
53
-
54
- // Attach the event emitter types to this class.
55
- const emitter = /** @type {NetworkMonitorEmitter} */ (new EventEmitter());
56
- /** @type {typeof emitter['emit']} */
57
- this.emit = emitter.emit.bind(emitter);
58
- /** @type {typeof emitter['on']} */
59
- this.on = emitter.on.bind(emitter);
60
- /** @type {typeof emitter['once']} */
61
- this.once = emitter.once.bind(emitter);
62
- /** @type {typeof emitter['off']} */
63
- this.off = emitter.off.bind(emitter);
64
- /** @type {typeof emitter['addListener']} */
65
- this.addListener = emitter.addListener.bind(emitter);
66
- /** @type {typeof emitter['removeListener']} */
67
- this.removeListener = emitter.removeListener.bind(emitter);
68
- /** @type {typeof emitter['removeAllListeners']} */
69
- this.removeAllListeners = emitter.removeAllListeners.bind(emitter);
70
- }
71
-
72
- /**
73
- * @param {{target: {targetId: string}, session: LH.Gatherer.FRProtocolSession}} session
74
- */
75
- async _onTargetAttached({session, target}) {
76
- const targetId = target.targetId;
77
-
78
- this._sessions.set(targetId, session);
79
- session.addProtocolMessageListener(this._onProtocolMessage);
80
-
81
- await session.sendCommand('Network.enable');
82
51
  }
83
52
 
84
53
  /**
85
54
  * @return {Promise<void>}
86
55
  */
87
56
  async enable() {
88
- if (this._targetManager) return;
57
+ if (this._networkRecorder) return;
89
58
 
90
59
  this._frameNavigations = [];
91
- this._sessions = new Map();
92
60
  this._networkRecorder = new NetworkRecorder();
93
- /** @type {LH.Puppeteer.CDPSession} */
94
- // @ts-expect-error - temporarily reach in to get CDPSession
95
- const rootCdpSession = this._session._cdpSession;
96
- this._targetManager = new TargetManager(rootCdpSession);
97
61
 
98
62
  /**
99
63
  * Reemit the same network recorder events.
100
- * @param {NetworkRecorderEvent} event
64
+ * @param {keyof NetworkRecorderEventMap} event
101
65
  * @return {(r: NetworkRequest) => void}
102
66
  */
103
67
  const reEmit = event => r => {
@@ -109,46 +73,20 @@ class NetworkMonitor {
109
73
  this._networkRecorder.on('requestfinished', reEmit('requestfinished'));
110
74
 
111
75
  this._session.on('Page.frameNavigated', this._onFrameNavigated);
112
- await this._session.sendCommand('Page.enable');
113
-
114
- // Legacy driver does its own target management.
115
- // @ts-expect-error
116
- const isLegacyRunner = Boolean(this._session._domainEnabledCounts);
117
- if (isLegacyRunner) {
118
- this._session.addProtocolMessageListener(this._onProtocolMessage);
119
- } else {
120
- this._targetManager.addTargetAttachedListener(this._onTargetAttached);
121
- await this._targetManager.enable();
122
- }
76
+ this._targetManager.on('protocolevent', this._onProtocolMessage);
123
77
  }
124
78
 
125
79
  /**
126
80
  * @return {Promise<void>}
127
81
  */
128
82
  async disable() {
129
- if (!this._targetManager) return;
83
+ if (!this._networkRecorder) return;
130
84
 
131
85
  this._session.off('Page.frameNavigated', this._onFrameNavigated);
132
-
133
- // Legacy driver does its own target management.
134
- // @ts-expect-error
135
- const isLegacyRunner = Boolean(this._session._domainEnabledCounts);
136
- if (isLegacyRunner) {
137
- this._session.removeProtocolMessageListener(this._onProtocolMessage);
138
- } else {
139
- this._targetManager.removeTargetAttachedListener(this._onTargetAttached);
140
-
141
- for (const session of this._sessions.values()) {
142
- session.removeProtocolMessageListener(this._onProtocolMessage);
143
- }
144
-
145
- await this._targetManager.disable();
146
- }
86
+ this._targetManager.off('protocolevent', this._onProtocolMessage);
147
87
 
148
88
  this._frameNavigations = [];
149
89
  this._networkRecorder = undefined;
150
- this._targetManager = undefined;
151
- this._sessions = new Map();
152
90
  }
153
91
 
154
92
  /** @return {Promise<{requestedUrl?: string, mainDocumentUrl?: string}>} */
@@ -5,70 +5,125 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- /**
9
- * @fileoverview This class tracks multiple targets (the page itself and its OOPIFs) and allows consumers to
10
- * listen for protocol events before each target is resumed.
11
- */
12
-
8
+ const EventEmitter = require('events').EventEmitter;
13
9
  const log = require('lighthouse-logger');
14
10
  const ProtocolSession = require('../../fraggle-rock/gather/session.js');
15
11
 
16
- /** @typedef {{target: LH.Crdp.Target.TargetInfo, session: LH.Gatherer.FRProtocolSession}} TargetWithSession */
12
+ /**
13
+ * @typedef {{
14
+ * target: LH.Crdp.Target.TargetInfo,
15
+ * cdpSession: LH.Puppeteer.CDPSession,
16
+ * session: LH.Gatherer.FRProtocolSession,
17
+ * protocolListener: (event: unknown) => void,
18
+ * }} TargetWithSession
19
+ */
20
+
21
+ // Add protocol event types to EventEmitter.
22
+ /** @typedef {{'protocolevent': [LH.Protocol.RawEventMessage]}} ProtocolEventMap */
23
+ /** @typedef {LH.Protocol.StrictEventEmitterClass<ProtocolEventMap>} ProtocolEventMessageEmitter */
24
+ const ProtocolEventEmitter = /** @type {ProtocolEventMessageEmitter} */ (EventEmitter);
17
25
 
18
- class TargetManager {
26
+ /**
27
+ * Tracks targets (the page itself, its iframes, their iframes, etc) as they
28
+ * appear and allows listeners to the flattened protocol events from all targets.
29
+ */
30
+ class TargetManager extends ProtocolEventEmitter {
19
31
  /** @param {LH.Puppeteer.CDPSession} cdpSession */
20
32
  constructor(cdpSession) {
33
+ super();
34
+
21
35
  this._enabled = false;
22
36
  this._rootCdpSession = cdpSession;
23
37
 
24
- /** @type {Array<(targetWithSession: TargetWithSession) => Promise<void>|void>} */
25
- this._listeners = [];
38
+ /**
39
+ * A map of target id to target/session information. Used to ensure unique
40
+ * attached targets.
41
+ * @type {Map<string, TargetWithSession>}
42
+ */
43
+ this._targetIdToTargets = new Map();
26
44
 
27
45
  this._onSessionAttached = this._onSessionAttached.bind(this);
46
+ this._onFrameNavigated = this._onFrameNavigated.bind(this);
47
+ }
28
48
 
29
- /** @type {Map<string, TargetWithSession>} */
30
- this._targetIdToTargets = new Map();
49
+ /**
50
+ * @param {LH.Crdp.Page.FrameNavigatedEvent} frameNavigatedEvent
51
+ */
52
+ async _onFrameNavigated(frameNavigatedEvent) {
53
+ // Child frames are handled in `_onSessionAttached`.
54
+ if (frameNavigatedEvent.frame.parentId) return;
55
+
56
+ // It's not entirely clear when this is necessary, but when the page switches processes on
57
+ // navigating from about:blank to the `requestedUrl`, resetting `setAutoAttach` has been
58
+ // necessary in the past.
59
+ await this._rootCdpSession.send('Target.setAutoAttach', {
60
+ autoAttach: true,
61
+ flatten: true,
62
+ waitForDebuggerOnStart: true,
63
+ });
64
+ }
31
65
 
32
- /** @param {LH.Crdp.Page.FrameNavigatedEvent} event */
33
- this._onFrameNavigated = async event => {
34
- // Child frames are handled in `_onSessionAttached`.
35
- if (event.frame.parentId) return;
66
+ /**
67
+ * @param {string} sessionId
68
+ * @return {LH.Gatherer.FRProtocolSession}
69
+ */
70
+ _findSession(sessionId) {
71
+ for (const {session, cdpSession} of this._targetIdToTargets.values()) {
72
+ if (cdpSession.id() === sessionId) return session;
73
+ }
36
74
 
37
- // It's not entirely clear when this is necessary, but when the page switches processes on
38
- // navigating from about:blank to the `requestedUrl`, resetting `setAutoAttach` has been
39
- // necessary in the past.
40
- await this._rootCdpSession.send('Target.setAutoAttach', {
41
- autoAttach: true,
42
- flatten: true,
43
- waitForDebuggerOnStart: true,
44
- });
45
- };
75
+ throw new Error(`session ${sessionId} not found`);
76
+ }
77
+
78
+ /**
79
+ * Returns the root session.
80
+ * @return {LH.Gatherer.FRProtocolSession}
81
+ */
82
+ rootSession() {
83
+ const rootSessionId = this._rootCdpSession.id();
84
+ return this._findSession(rootSessionId);
46
85
  }
47
86
 
48
87
  /**
49
88
  * @param {LH.Puppeteer.CDPSession} cdpSession
50
89
  */
51
90
  async _onSessionAttached(cdpSession) {
52
- const session = new ProtocolSession(cdpSession);
91
+ const newSession = new ProtocolSession(cdpSession);
53
92
 
54
93
  try {
55
- const target = await session.sendCommand('Target.getTargetInfo').catch(() => null);
94
+ const target = await newSession.sendCommand('Target.getTargetInfo').catch(() => null);
56
95
  const targetType = target?.targetInfo?.type;
57
96
  const hasValidTargetType = targetType === 'page' || targetType === 'iframe';
97
+ // TODO: should detach from target in this case?
98
+ // See pptr: https://github.com/puppeteer/puppeteer/blob/733cbecf487c71483bee8350e37030edb24bc021/src/common/Page.ts#L495-L526
58
99
  if (!target || !hasValidTargetType) return;
59
100
 
101
+ // No need to continue if target has already been seen.
60
102
  const targetId = target.targetInfo.targetId;
61
- session.setTargetInfo(target.targetInfo);
62
103
  if (this._targetIdToTargets.has(targetId)) return;
63
104
 
105
+ newSession.setTargetInfo(target.targetInfo);
64
106
  const targetName = target.targetInfo.url || target.targetInfo.targetId;
65
107
  log.verbose('target-manager', `target ${targetName} attached`);
66
108
 
67
- const targetWithSession = {target: target.targetInfo, session};
109
+ const trueProtocolListener = this._getProtocolEventListener(newSession.id());
110
+ /** @type {(event: unknown) => void} */
111
+ // @ts-expect-error - pptr currently typed only for single arg emits.
112
+ const protocolListener = trueProtocolListener;
113
+ cdpSession.on('*', protocolListener);
114
+
115
+ const targetWithSession = {
116
+ target: target.targetInfo,
117
+ cdpSession,
118
+ session: newSession,
119
+ protocolListener,
120
+ };
68
121
  this._targetIdToTargets.set(targetId, targetWithSession);
69
- for (const listener of this._listeners) await listener(targetWithSession);
70
122
 
71
- await session.sendCommand('Target.setAutoAttach', {
123
+ // We want to receive information about network requests from iframes, so enable the Network domain.
124
+ await newSession.sendCommand('Network.enable');
125
+ // We also want to receive information about subtargets of subtargets, so make sure we autoattach recursively.
126
+ await newSession.sendCommand('Target.setAutoAttach', {
72
127
  autoAttach: true,
73
128
  flatten: true,
74
129
  waitForDebuggerOnStart: true,
@@ -80,10 +135,30 @@ class TargetManager {
80
135
  throw err;
81
136
  } finally {
82
137
  // Resume the target if it was paused, but if it's unnecessary, we don't care about the error.
83
- await session.sendCommand('Runtime.runIfWaitingForDebugger').catch(() => {});
138
+ await newSession.sendCommand('Runtime.runIfWaitingForDebugger').catch(() => {});
84
139
  }
85
140
  }
86
141
 
142
+ /**
143
+ * Returns a listener for all protocol events from session, and augments the
144
+ * event with the sessionId.
145
+ * @param {string} sessionId
146
+ */
147
+ _getProtocolEventListener(sessionId) {
148
+ /**
149
+ * @template {keyof LH.Protocol.RawEventMessageRecord} EventName
150
+ * @param {EventName} method
151
+ * @param {LH.Protocol.RawEventMessageRecord[EventName]['params']} params
152
+ */
153
+ const onProtocolEvent = (method, params) => {
154
+ // Cast because tsc 4.7 still can't quite track the dependent parameters.
155
+ const payload = /** @type {LH.Protocol.RawEventMessage} */ ({method, params, sessionId});
156
+ this.emit('protocolevent', payload);
157
+ };
158
+
159
+ return onProtocolEvent;
160
+ }
161
+
87
162
  /**
88
163
  * @return {Promise<void>}
89
164
  */
@@ -115,23 +190,13 @@ class TargetManager {
115
190
  // No need to remove listener if connection is already closed.
116
191
  this._rootCdpSession.connection()?.off('sessionattached', this._onSessionAttached);
117
192
 
193
+ for (const {cdpSession, protocolListener} of this._targetIdToTargets.values()) {
194
+ cdpSession.off('*', protocolListener);
195
+ }
196
+
118
197
  this._enabled = false;
119
198
  this._targetIdToTargets = new Map();
120
199
  }
121
-
122
- /**
123
- * @param {(targetWithSession: TargetWithSession) => Promise<void>|void} listener
124
- */
125
- addTargetAttachedListener(listener) {
126
- this._listeners.push(listener);
127
- }
128
-
129
- /**
130
- * @param {(targetWithSession: TargetWithSession) => Promise<void>|void} listener
131
- */
132
- removeTargetAttachedListener(listener) {
133
- this._listeners = this._listeners.filter(entry => entry !== listener);
134
- }
135
200
  }
136
201
 
137
202
  module.exports = TargetManager;
@@ -96,6 +96,30 @@ class Driver {
96
96
  this.evaluate = this.executionContext.evaluate.bind(this.executionContext);
97
97
  /** @private @deprecated Only available for plugin backcompat. */
98
98
  this.evaluateAsync = this.executionContext.evaluateAsync.bind(this.executionContext);
99
+
100
+ // A shim for sufficient coverage of targetManager functionality. Exposes the target
101
+ // management that legacy driver already handles (see this._handleTargetAttached).
102
+ this.targetManager = {
103
+ rootSession: () => {
104
+ return this.defaultSession;
105
+ },
106
+ /**
107
+ * Bind to *any* protocol event.
108
+ * @param {'protocolevent'} event
109
+ * @param {(payload: LH.Protocol.RawEventMessage) => void} callback
110
+ */
111
+ on: (event, callback) => {
112
+ this._connection.on('protocolevent', callback);
113
+ },
114
+ /**
115
+ * Unbind to *any* protocol event.
116
+ * @param {'protocolevent'} event
117
+ * @param {(payload: LH.Protocol.RawEventMessage) => void} callback
118
+ */
119
+ off: (event, callback) => {
120
+ this._connection.off('protocolevent', callback);
121
+ },
122
+ };
99
123
  }
100
124
 
101
125
  /** @deprecated - Not available on Fraggle Rock driver. */
@@ -187,22 +211,6 @@ class Driver {
187
211
  this._eventEmitter.removeListener(eventName, cb);
188
212
  }
189
213
 
190
- /**
191
- * Bind to *any* protocol event.
192
- * @param {(payload: LH.Protocol.RawEventMessage) => void} callback
193
- */
194
- addProtocolMessageListener(callback) {
195
- this._connection.on('protocolevent', callback);
196
- }
197
-
198
- /**
199
- * Unbind to *any* protocol event.
200
- * @param {(payload: LH.Protocol.RawEventMessage) => void} callback
201
- */
202
- removeProtocolMessageListener(callback) {
203
- this._connection.off('protocolevent', callback);
204
- }
205
-
206
214
  /** @param {LH.Crdp.Target.TargetInfo} targetInfo */
207
215
  setTargetInfo(targetInfo) { // eslint-disable-line no-unused-vars
208
216
  // OOPIF handling in legacy driver is implicit.
@@ -11,7 +11,6 @@
11
11
  * This protocol log can be used to recreate the network records using lib/network-recorder.js.
12
12
  */
13
13
 
14
- const NetworkMonitor = require('../driver/network-monitor.js');
15
14
  const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
16
15
 
17
16
  class DevtoolsLog extends FRGatherer {
@@ -26,9 +25,6 @@ class DevtoolsLog extends FRGatherer {
26
25
  constructor() {
27
26
  super();
28
27
 
29
- /** @type {NetworkMonitor|undefined} */
30
- this._networkMonitor = undefined;
31
-
32
28
  this._messageLog = new DevtoolsMessageLog(/^(Page|Network|Target|Runtime)\./);
33
29
 
34
30
  /** @param {LH.Protocol.RawEventMessage} e */
@@ -42,16 +38,16 @@ class DevtoolsLog extends FRGatherer {
42
38
  this._messageLog.reset();
43
39
  this._messageLog.beginRecording();
44
40
 
45
- this._networkMonitor = new NetworkMonitor(driver.defaultSession);
46
- this._networkMonitor.on('protocolmessage', this._onProtocolMessage);
47
- this._networkMonitor.enable();
41
+ driver.targetManager.on('protocolevent', this._onProtocolMessage);
42
+ await driver.defaultSession.sendCommand('Page.enable');
48
43
  }
49
44
 
50
- async stopSensitiveInstrumentation() {
51
- if (!this._networkMonitor) return;
45
+ /**
46
+ * @param {LH.Gatherer.FRTransitionalContext} passContext
47
+ */
48
+ async stopSensitiveInstrumentation({driver}) {
52
49
  this._messageLog.endRecording();
53
- this._networkMonitor.disable();
54
- this._networkMonitor.off('protocolmessage', this._onProtocolMessage);
50
+ driver.targetManager.off('protocolevent', this._onProtocolMessage);
55
51
  }
56
52
 
57
53
  /**
@@ -89,7 +89,7 @@ class FullPageScreenshot extends FRGatherer {
89
89
  const height = Math.min(fullHeight, maxTextureSize);
90
90
 
91
91
  // Setup network monitor before we change the viewport.
92
- const networkMonitor = new NetworkMonitor(session);
92
+ const networkMonitor = new NetworkMonitor(context.driver.targetManager);
93
93
  const waitForNetworkIdleResult = waitForNetworkIdle(session, networkMonitor, {
94
94
  pretendDCLAlreadyFired: true,
95
95
  networkQuietThresholdMs: 1000,