-
Notifications
You must be signed in to change notification settings - Fork 9.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
always construct networkRecords from devtoolsLog #2133
Conversation
simplify devtoolsLog recording, url redirect tracking, and eliminate explicit network recording
8913cb4
to
43d4da6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like these changes, moving towards a cleaner 🚗 + 🏃
lighthouse-core/gather/driver.js
Outdated
* Used for monitoring network status events. | ||
* @private {?NetworkRecorder} | ||
*/ | ||
this._networkStatus = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not in love with this name change, network status makes me thing it's only watching for offline/online or something yet we listen for individual requests, dispatch all network messages to it, etc. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I just wanted to move away from the "records" part since we aren't using it for records. We're using it for busy
, idle
, and requestloaded
events. Not sure what to call that :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about _networkStatusRecorder
or _networkStatusMonitor
?, just networkStatus makes me think it's literally a string 'offline' or something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, I see what you're saying. _networkStatusMonitor
sounds great. @paulirish? (I can do the same thing for the begin/end methods)
lighthouse-core/gather/driver.js
Outdated
this._networkStatus = new NetworkRecorder([], this); | ||
this.enableUrlUpdateIfRedirected(options); | ||
|
||
this.sendCommand('Network.enable').then(resolve, reject); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't we just return the result of this.sendCommand
instead of wrapping in another promise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh it was pre-existing, might as well fix though :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can't we just return the result of this.sendCommand instead of wrapping in another promise?
done
lighthouse-core/gather/driver.js
Outdated
@@ -506,10 +543,17 @@ class Driver { | |||
const maxWaitMs = (options.flags && options.flags.maxWaitForLoad) || | |||
Driver.MAX_WAIT_FOR_FULLY_LOADED; | |||
|
|||
return this.sendCommand('Page.enable') | |||
const redirectTracking = {url}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's hard to tell what this is for and why it's passed into _beginNetworkMontoring
's options
argument, maybe redirectTrackingOptions/Container/Something
at least?
lighthouse-core/gather/driver.js
Outdated
*/ | ||
enableUrlUpdateIfRedirected(opts) { | ||
this._networkRecorder.on('requestloaded', redirectRequest => { | ||
enableUrlUpdateIfRedirected(options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's rename away from options now that it's not mutating the actual options object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lighthouse-core/gather/driver.js
Outdated
* @return {!Promise} | ||
* @private | ||
*/ | ||
_beginNetworkMonitoring(options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lighthouse-core/gather/driver.js
Outdated
.then(_ => waitForLoad && this._waitForFullyLoaded(pauseAfterLoadMs, maxWaitMs)) | ||
.then(_ => { | ||
this._endNetworkMonitoring(); | ||
return redirectTracking.url; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since we're cleaning this up to not touch options
it feels weird to still do this object mutation, what if it were restructured slightly so that we called _monitorUrlChanges()
and could call getLatestUrl()
at any point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
went more or less with this, but only internal to driver. Didn't do a getLatestUrl()
as driver is only semi-stateful, so it's not always clear that's what "latest" would be referring to, but got rid of all the options.url business. gotoURL
still resolves on the final URL since during that method is the only time redirects are being tracked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on the whole i like it. :)
lighthouse-core/gather/driver.js
Outdated
if (!this._networkStatus) return; | ||
|
||
const regexFilter = /^Network\./; | ||
if (!regexFilter.test(method)) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while you're here can you change this to be free of regex:
if (!method.startsWith('Network.')) return;
my fault. thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just moved it into NetworkRecorder.dispatch
(to mirror devtools-log.record
), though we could arguably drop it altogether because the switch statement rejects anything not recognized
lighthouse-core/gather/driver.js
Outdated
* @return {!Promise} | ||
* @private | ||
*/ | ||
_beginNetworkMonitoring(options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to beginNetworkIdleMonitoring
since its for busy idle detection. not to be confused with devtoolsLog like stuff?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to beginNetworkIdleMonitoring since its for busy idle detection. not to be confused with devtoolsLog like stuff?
we also watch for requestloaded
to watch for url redirect. Not sure if that matters :)
7ed58df
to
62949d4
Compare
fixed up and added a test that replays a wikipedia load to test redirect tracking. PTAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm % nit
lighthouse-core/gather/driver.js
Outdated
|
||
// Update startingUrl if it's ever redirected. | ||
this._monitoredUrl = startingUrl; | ||
this._networkStatusMonitor.on('requestloaded', redirectRequest => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice this is so much more understandable 👍
@@ -155,6 +148,10 @@ class NetworkRecorder extends EventEmitter { | |||
* @param {!Object<string, *>=} params | |||
*/ | |||
dispatch(method, params) { | |||
if (!/^Network\./.test(method)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I believe paul wanted startsWith instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
startsWith
done
* Construct network records from a log of devtools protocol messages. If a | ||
* driver is provided, it will be used for request content lookups. | ||
* @param {!DevtoolsLog} devtoolsLog | ||
* @param {!Driver=} driver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated, but I still vote for a driver helper function instead, this is a really annoying artifact of having it be the method on network records :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated, but I still vote for a driver helper function instead, this is a really annoying artifact of having it be the method on network records :(
the problem is we need a live driver
to get resource content, which will only be accessible in gatherers, but we need to create records from a devtools log during audits too (via a computed artifact) when there is no live driver. The first one argues for an instance method on driver
, the second argues for a static method. So...it could be a static driver
method, but still take a driver
instance? But that seems like it has the same issue.
Not sure if there's a good solution :S
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, audits shouldn't need to live request content if gatherers didn't save them into a separate artifact meaning it can just be a method on a driver to getResourceContent
and pass in a network record id, the construction of network records is not capable of reconstructing the content anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, audits shouldn't need to live request content if gatherers didn't save them into a separate artifact
right, it's not possible for audits to do that on current master already, as we throw away the driver by that point
meaning it can just be a method on a driver to getResourceContent and pass in a network record id, the construction of network records is not capable of reconstructing the content anyway
well I wish I had thought of that before I spent all that time reverse engineering that 'web-inspector' madness and then making it work again with this PR :)
I think Paul was going for the current approach because it makes accessing the content pretty seamless. We already use devtools' WebInspector.NetworkRequest
objects, and they have requestContent
on them, so it makes some sense to hook that method up similarly to how devtools does and have it send the command on your behalf.
It seems like we have a "this thing works here but doesn't work there" in both cases, so the choice would be:
- (current behavior) Support
record.requestContent()
. Meanwhile in audits throw onrecord.requestContent()
(called when a driver wasn't used to initialize the network records) and document that it only works in audits, not gatherers - If an audit wants to access content, they have to do
driver.getRequestContent(record)
or whatever. Meanwhile always throw onrecord.requestContent()
and document driver method if you want content.
I do kind of like the explicitness of the second approach, making it crystal clear that you'll only be able to access content through the driver (so if you don't have a driver anymore, you won't get content). OTOH we're breaking the API of NetworkRequest
and breaking away from the way devtools code does things. OTOOH probably no one would have known that record.requestContent
existed without paul anyways and would have happily used a driver method (analogous to getObjectProperty
).
@paulirish wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, it appears that all the other request*()
methods on WebInspector.NetworkRequest
don't send additional protocol commands, they already have what they need locally
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh let's pick 2, pick 2, pick 2, 😄 , can we @paulirish please? it's a bit silly to try to conform to the interface only here, when it makes the rest of the code more complicated especially when the assumptions of devtools are just very different than the assumptions in LH
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya cool let's pick 2.
make record.requestContent()
throw and go back through protocol manually if you need responseBodies.
works for me.
do we need to change all that in this PR? would be nice not to..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated!
@@ -47,7 +47,7 @@ class ResponseCompression extends Gatherer { | |||
|
|||
if (!isContentEncoded) { | |||
unoptimizedResponses.push({ | |||
record: record, | |||
requestId: record.requestId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed the full request record
from the extendedInfo
as it wasn't used and I assumed it would have problems with serialization of the report JSON? Or was that just the full set of network records?
Regardless, it wasn't used except to get requestId
, so seemed fine to remove :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ya good call. we shouldn't put records in extendedInfo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets do it
lighthouse-core/gather/driver.js
Outdated
getRequestContent(requestId) { | ||
return this.sendCommand('Network.getResponseBody', { | ||
requestId, | ||
}).then(result => result.body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a comment in here that we're ignoring base64Encoded
?
// ignoring the result.base64Encoded boolean, which indicates if body is already encoded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💥
simplify devtoolsLog recording, url redirect tracking, and eliminate explicit network recording
simplify devtoolsLog recording, url redirect tracking, and eliminate explicit network recording
PR building on (and to merge into) #2062
Hopefully simplifies network and devtoolsLog recording. In total (with existing #2062 changes):
driver.networkRecorder
was pulling triple duty, serving as network monitor, redirect monitor, and recorder of network activity for gatherers. Its replacementdriver._networkStatus
is only used for network events and is entirely internal to driverdevtoolsLog
is taken for every passpassData.networkRecords
(passed intoGatherer.afterPass()
) is constructed from thedevtoolsLog
for that pass. This eliminates questions of if network or devtoolsLog should start recording first and in what order, and it means that thenetworkRecords
inafterPass()
is the exact same as what audits get from the computed artifact. Downside is thatnetworkRecords
is reconstructed for every pass + possibly for computed artifacts, but this adds maybe 30-40ms total to a theverge.com run, so not a big deal.enableUrlUpdateIfRedirected
becomes a purely internaldriver
affair, as it no longer updatesoptions.url
directly, which lets driver worry about redirects andGatherRunner
worry about options. Instead,driver.goToUrl()
resolves on the final loaded (possibly redirected) URL.options.url
is then set inGatherRunner.loadPage
so that everything outside of that doesn't need to know anything changed.beginTrace
andbeginDevtoolsLog
are moved out ofGatherRunner.loadPage
toGatherRunner.pass
as that makes a lot more sense and it matches theafterPass()
location ofendTrace
andendDevtoolsLog
(previouslyendNetworkCollect
)