Skip to content
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

core: remove dependency on devtools-frontend NetworkRequest #5451

Merged
merged 26 commits into from
Jun 20, 2018

Conversation

patrickhulce
Copy link
Collaborator

@patrickhulce patrickhulce commented Jun 8, 2018

title speaks for itself, big change :)

part of the broader goal of removing our dependency on devtools-frontend

tests should be passing now! 🎉 I want to do more comparisons with my lantern traces and land our safety checks before this one

@patrickhulce patrickhulce changed the title [SUPER WIP] core: remove dependency on network records [Slightly WIP] core: remove dependency on network records Jun 8, 2018
Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

title speaks for itself

is it more removing dependency on devtools-frontend to create network records?

_isLinkPreload?: boolean;
initiatorRequest(): NetworkRequest | null;
redirects?: NetworkRequest[];
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we be able to get rid of this and use the class definition?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that jump seemed a bit bigger in the same go since I'm using this to typecheck the other class, but we definitely will be able to

@@ -317,5 +317,7 @@ module.exports = (function() {
// Restore setImmediate, see comment at top.
global.setImmediate = _setImmediate;

// TODO(phulce): replace client requires to not need this
WebInspector.resourceTypes = require('../../third-party/devtools/ResourceType').TYPES;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can more be removed from this file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eventually yes!

@patrickhulce
Copy link
Collaborator Author

is it more removing dependency on devtools-frontend to create network records?

er, yes! depends on what we're calling a network record, I guess that's somewhat ambiguous

@patrickhulce patrickhulce changed the title [Slightly WIP] core: remove dependency on network records [Slightly WIP] core: remove dependency on devtools-frontend NetworkRequest Jun 11, 2018
@@ -1,11 +1,11 @@
{
"33097.1:redirected.0": {
Copy link
Collaborator Author

@patrickhulce patrickhulce Jun 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

open question if we want to match exactly the old policy instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dun care. this wfm.

Copy link
Member

@paulirish paulirish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove some of the requires from lib/web-inspector now?

@@ -165,6 +166,7 @@ class UnusedBytes extends Audit {
const networkNode = /** @type {NetworkNode} */ (node);
const originalTransferSize = originalTransferSizes.get(networkNode.record.requestId);
if (originalTransferSize === undefined) return;
networkNode.record.transferSize = originalTransferSize;
networkNode.record._transferSize = originalTransferSize;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we just use one of these? this PR is switching to use the other already.
Also happy to fix the _prop use in a followup to minimize the diff.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah eliminated _transferSize, the dance was necessary for transferSize since the weird way the getter interacts with our simulation but the rest will be followup 👍

@@ -90,7 +91,7 @@ class UsesRelPreconnectAudit extends Audit {
// filter out all resources where timing info was invalid
!UsesRelPreconnectAudit.hasValidTiming(record) ||
// filter out all resources that are loaded by the document
record.initiatorRequest() === mainResource ||
record._initiator.url === mainResource.url ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these aren't totally equivalent, fwiw. but the difference may not matter. And I suppose devtools was making this assumption previously. :p

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I was kinda shocked that the whole initiatorRequest logic is literally just first one with matching URL and manager 😮

@@ -34,6 +38,10 @@ class NetworkRecorder extends EventEmitter {
);
}

getRecords() {
return this._records.slice();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.from() ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, man between you and dgozman I'm gonna lose myself! 😆

done

this._records = [];
/** @type {Map<string, NetworkRequest>} */
this._recordsById = new Map();

this.networkManager = NetworkManager.createWithFakeTarget();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need this around? followup?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that one's totally safe to go right now actually 👍

recordsByURL.set(record.url, record);
}

// set the initiator and redirects array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at first glance, it seems odd that this happens only when we're hydrating the records from logs. what about when records are created via this._networkStatusMonitor.dispatch(event); in driver?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it's a bit weird, AFAICT we actually only ever use the ones created by the recordsFromLogs, the ones created incrementally are just for the status notifications

const networkRecords = NetworkRecorder.recordsFromLogs(devtoolsLog);

(and obvs the computed artifact uses this)

long-term though I hope to eliminate the initiatorRequest logic here entirely and move it to the page-dependency-graph since it's just CRC-based ones that need it right now

redirects can also move to direct usage of source/destination which I think becomes more explicit and clear

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to do this incrementally as (presumably) NetworkManager or whatever does? If the same thing is available in the page dependency graph I guess that's fine too, but at that point do we need NetworkRecords...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really, really want to nix these entirely so network request can be trivially serializable. Because of that, I don't really want to do unnecessary work to make them nice only to remove them

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it's a bit weird, AFAICT we actually only ever use the ones created by the recordsFromLogs, the ones created incrementally are just for the status notifications

ha, I think it was in anticipation of exactly this sort of situation. Since audits can only access network records created by recordsFromLogs(devtoolsLogs), we wanted to make sure gatherers also saw exactly the same thing (in afterPass()) and so switched everything to using it.

So this impl and then migration from it sounds good. Nothing should be able to observe the difference since driver never actually inspects the records while listening to the quiet events

/**
* @param {LH.Crdp.Network.ResourceTiming} timing
*/
_recomputeTimesWithResourceTiming(timing) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -1,11 +1,11 @@
{
"33097.1:redirected.0": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dun care. this wfm.

case 'Network.loadingFailed': return this.onLoadingFailed(event.params);
case 'Network.resourceChangedPriority': return this.onResourceChangedPriority(event.params);
default: return;
case 'Network.requestWillBeSent':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert the ws change

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* @param {LH.Crdp.Network.Headers} headersMap
* @return {Array<LH.WebInspector.HeaderValue>}
*/
static _headersMapToHeadersArray(headersMap) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headersDict or headersObj or headersHash seem fine..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

static _headersMapToHeadersArray(headersMap) {
const result = [];
for (const name of Object.keys(headersMap)) {
const values = headersMap[name].split('\n');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which method in sdk.networkrequest is this disguised as? _computeHeaderValue

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patrickhulce patrickhulce changed the title [Slightly WIP] core: remove dependency on devtools-frontend NetworkRequest core: remove dependency on devtools-frontend NetworkRequest Jun 13, 2018
@patrickhulce
Copy link
Collaborator Author

Alright I think I'm comfortable taking this out of WIP, tests look good, Paul's initial feedback addressed, added some better coverage around the redirect cases

I'm still going to put this through the lantern wringer before being 100% cool to merge 👍

@patrickhulce
Copy link
Collaborator Author

so more fun discoveries as part of this, mixed-content has been broken for some time :) #5127 removed the URL gatherer, so the mixed content config tried to treat the url npm module as a gatherer and config craps out.

on the plus side it seems that chrome changes to interception also broke it (it seems to think example.com is not upgradeable?) so we didn't break anything very useful? 🤷‍♀️

@brendankenny
Copy link
Member

lol we really needed an integration test on that one... Config was hopefully catching this and we just weren't seeing it?

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments. This seems pretty nice, and considering how long it's been since we've updated our frontend and it still (mostly) works, I'm not too concerned about maintaining our own version :)

@@ -0,0 +1,326 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should add a LICENSE file (maybe this one?) in devtools/ as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure done

@@ -96,8 +93,11 @@ class NetworkRecorder extends EventEmitter {
* @return {boolean}
*/
static _isQUICAndFinished(record) {
const isQUIC = record._responseHeaders && record._responseHeaders
.some(header => header.name.toLowerCase() === 'alt-svc' && /quic/.test(header.value));
const isQUIC =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

data.timestamp, data.wallTime, data.initiator, data.redirectResponse,
data.type);
const originalRequest = this._findRealRequest(data.requestId);
if (originalRequest) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a comment on what this case is would be 👍👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return;
}

const request = new NetworkRequest();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe move this case to the early return as it's so much simpler/shorter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -270,18 +294,66 @@ class NetworkRecorder extends EventEmitter {
}
}

/**
* @param {string} requestId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment on what a "real" request is?

For my own confusion, the IDs of a chain of redirected requests are all the same? Hence the redirectDestination chain traversal? And the assumption is that if there's more data for a request it can only be the last in the redirect chain because the rest are finished?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my own confusion, the IDs of a chain of redirected requests are all the same?

Yup

And the assumption is that if there's more data for a request it can only be the last in the redirect chain because the rest are finished?

Yup, after another requestWillBeSent is issued signaling a redirect, that is the final message of the original request and the rest are about the redirected request.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment basically saying this 👍

recordsByURL.set(record.url, record);
}

// set the initiator and redirects array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to do this incrementally as (presumably) NetworkManager or whatever does? If the same thing is available in the page dependency graph I guess that's fine too, but at that point do we need NetworkRecords...

@@ -100,67 +100,13 @@ module.exports = (function() {
return this._moduleSettings[settingName];
};

// Enum from chromium//src/third_party/WebKit/Source/core/loader/MixedContentChecker.h
global.NetworkAgent = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 🎈 🎉

/** @type {number} */
this._responseReceivedTime = -1;

this.transferSize = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you just went with this._transferSize I feel like this PR would shrink by 30%, but presumable there was a reason :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well transferSize is the only one we have to standardize in the middle of this PR to get everything to work thanks to the way that the old network requests worked, since we want to end up on transferSize in the end, makes more sense to just go straight to it :)

@patrickhulce
Copy link
Collaborator Author

@brendankenny would you be able to give some post-initial thoughts? :)

@patrickhulce
Copy link
Collaborator Author

patrickhulce commented Jun 19, 2018

alright I'm pretty confident in this at this point, I've run it against the lantern expectations, found 2 more subtle places where we weren't matching DT behavior (though arguably we could've left it as is), and the only 8 sites that changed were small ordering differences that should be eliminated by #5362

the actual network record objects that were created were identical across all critical properties on all 100 sites

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, big feedback this time, but almost all clarification/style stuff, I think. Overall this looks great

@@ -17,7 +17,6 @@ describe('Web Inspector lib', function() {
assert.ok(WebInspector.FilmStripModel);
assert.ok(WebInspector.TimelineProfileTree);
assert.ok(WebInspector.TimelineAggregator);
assert.ok(WebInspector.NetworkManager);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol this test

@@ -6,8 +6,8 @@

declare global {
module LH.WebInspector {
// TODO(bckenny): standardize on underscored internal API
// externs for chrome-devtools-frontend/front_end/sdk/NetworkRequest.js
// TODO(phulce): standardize on non-underscored property names
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch this to "TODO(phulce): migrate to using network-request.js"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
redirectSource?: {url: string;};
redirectDestination?: {url: string;};
redirects?: NetworkRequest[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a /** ... */ comment to these? e.g. request that redirected to this request, if it exists, request this request redirected to, in order redirect chain to this request (if those are correct)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though maybe these comments should live in network-request.js

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done over there 👍

@@ -62,16 +65,10 @@ declare global {
}

export interface ResourceType {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop this and import ResourceType.js for the type?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

};

/**
* Keep these in sync with WebCore::InspectorPageAgent::resourceTypeJson
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could maybe type this with Record<LH.Crdp.Page.ResourceType, ResourceType> to make sure they stay in sync (at least with our types for the protocol)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, but this has more keys than that. I would have expected them to match. The types in the protocol repo have more of them (I should update us to use that), but still missing SourceMapScript and SourceMapStyleSheet

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW that was a devtools comment I didn't add that :)

we only need those ones that match so I'm cool with that, and honestly this entire file can be deleted after we stop that WebInspector.resourceTypes === nonsense :D

/**
* @return {NetworkRequest}
*/
clone() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used anywhere?

Copy link
Collaborator Author

@patrickhulce patrickhulce Jun 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah just testing, I'll remove

* @param {LH.Crdp.Network.LoadingFinishedEvent} data
*/
onLoadingFinished(data) {
if (this.finished) return;
Copy link
Member

@brendankenny brendankenny Jun 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when does this happen and is that bad or just expected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems to be expected for a certain class of request failures (aborted redirected requests)

not sure how to best comment it...maybe?
// On cancelled requests, DevTools can send duplicate erroneous events, prefer the first one for best timing data

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's 👍

this.endTime = data.timestamp;

this.failed = true;
this._resourceType = data.type && resourceTypes[data.type];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why updated here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because DT does :) https://cs.chromium.org/chromium/src/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js?type=cs&q=%22setResourceType%22+-f:out+lang:js&sq=package:chromium&g=0&l=600

I think it's because resourceTypes can always lie, so you can always get more information when the response comes back, also probably has to do with signed-exchanges and some corner cases we don't really care about

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, yeah, good to mirror the real deal. I was also wondering because onLoadingFinished doesn't do the same

// Take startTime and responseReceivedTime from timing data for better accuracy.
// Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis.
this.startTime = timing.requestTime;
const headersReceivedTime = timing.requestTime + timing.receiveHeadersEnd / 1000.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1000

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

this.endTime = Math.max(this.endTime, this._responseReceivedTime);
}

_updateResponseReceivedTimeIfNecessary() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment for what this is doing/when it's "necessary"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@brendankenny brendankenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some last comment suggestions, but otherwise LGTM! Let's do this

const redirectRequest = new NetworkRequest();

redirectRequest.onRequestWillBeSent(modifiedData);
originalRequest.onRedirectResponse(data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a comment so that anyone rearranging in the future won't accidentally do that?

this._timing = /** @type {LH.Crdp.Network.ResourceTiming|undefined} */ (undefined);
this._resourceType = /** @type {LH.WebInspector.ResourceType|undefined} */ (undefined);
this._mimeType = '';
this.priority = () => /** @type {LH.Crdp.Network.ResourcePriority} */ ('Low');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, don't want to add machinery for things I immediately want to get rid of :)

ha, oh right. SG

* @param {LH.Crdp.Network.LoadingFinishedEvent} data
*/
onLoadingFinished(data) {
if (this.finished) return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's 👍

this.endTime = data.timestamp;

this.failed = true;
this._resourceType = data.type && resourceTypes[data.type];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, yeah, good to mirror the real deal. I was also wondering because onLoadingFinished doesn't do the same

export interface ResourceCategory {
title: string;
}
export type ResourceType = _ResourceType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't seem like anything is referring to this type except the NetworkRequest interface, so can drop this declaration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants