Bug 1870580 - added fileChannel observer to netmonitor to display file urls. r=devtools-reviewers,bomsy

Differential Revision: https://phabricator.services.mozilla.com/D191317
This commit is contained in:
dylan 2024-02-10 00:19:45 +00:00
parent 81fac6ce12
commit ab3210c5ce
5 changed files with 154 additions and 48 deletions

View File

@ -3,7 +3,7 @@
"use strict";
requestLongerTimeout(2);
requestLongerTimeout(3);
/**
* Test different text filtering flags

View File

@ -73,6 +73,26 @@ class NetworkEventActor extends Actor {
// Store the channelId which will act as resource id.
this._channelId = channel.channelId;
this._timings = {};
this._serverTimings = [];
this._discardRequestBody = !!networkEventOptions.discardRequestBody;
this._discardResponseBody = !!networkEventOptions.discardResponseBody;
this._response = {
headers: [],
cookies: [],
content: {},
};
if (channel instanceof Ci.nsIFileChannel) {
this._innerWindowId = null;
this._isNavigationRequest = false;
this._resource = this._createResource(networkEventOptions, channel);
return;
}
// innerWindowId and isNavigationRequest are used to check if the actor
// should be destroyed when a window is destroyed. See network-events.js.
this._innerWindowId = lazy.NetworkUtils.getChannelInnerWindowId(channel);
@ -89,18 +109,6 @@ class NetworkEventActor extends Actor {
rawHeaders: networkEventOptions.rawHeaders,
};
this._response = {
headers: [],
cookies: [],
content: {},
};
this._timings = {};
this._serverTimings = [];
this._discardRequestBody = !!networkEventOptions.discardRequestBody;
this._discardResponseBody = !!networkEventOptions.discardResponseBody;
this._resource = this._createResource(networkEventOptions, channel);
}
@ -119,8 +127,18 @@ class NetworkEventActor extends Actor {
* Create the resource corresponding to this actor.
*/
_createResource(networkEventOptions, channel) {
channel = channel.QueryInterface(Ci.nsIHttpChannel);
const wsChannel = lazy.NetworkUtils.getWebSocketChannel(channel);
let wsChannel;
let method;
if (channel instanceof Ci.nsIFileChannel) {
channel = channel.QueryInterface(Ci.nsIFileChannel);
channel.QueryInterface(Ci.nsIChannel);
wsChannel = null;
method = "GET";
} else {
channel = channel.QueryInterface(Ci.nsIHttpChannel);
wsChannel = lazy.NetworkUtils.getWebSocketChannel(channel);
method = channel.requestMethod;
}
// Use the WebSocket channel URL for websockets.
const url = wsChannel ? wsChannel.URI.spec : channel.URI.spec;
@ -188,10 +206,11 @@ class NetworkEventActor extends Actor {
fromServiceWorker: networkEventOptions.fromServiceWorker,
innerWindowId: this._innerWindowId,
isNavigationRequest: this._isNavigationRequest,
isFileRequest: channel instanceof Ci.nsIFileChannel,
isThirdPartyTrackingResource:
lazy.NetworkUtils.isThirdPartyTrackingResource(channel),
isXHR,
method: channel.requestMethod,
method,
priority: lazy.NetworkUtils.getChannelPriority(channel),
private: lazy.NetworkUtils.isChannelPrivate(channel),
referrerPolicy: lazy.NetworkUtils.getReferrerPolicy(channel),
@ -451,7 +470,7 @@ class NetworkEventActor extends Actor {
// Read response headers and cookies.
let responseHeaders = [];
let responseCookies = [];
if (!this._blockedReason) {
if (!this._blockedReason && !(channel instanceof Ci.nsIFileChannel)) {
const { cookies, headers } =
lazy.NetworkUtils.fetchResponseHeadersAndCookies(channel);
responseCookies = cookies;
@ -481,10 +500,13 @@ class NetworkEventActor extends Actor {
mimeType = contentTypeHeader.value;
}
const timedChannel = channel.QueryInterface(Ci.nsITimedChannel);
const waitingTime = Math.round(
(timedChannel.responseStartTime - timedChannel.requestStartTime) / 1000
);
let waitingTime = null;
if (!(channel instanceof Ci.nsIFileChannel)) {
const timedChannel = channel.QueryInterface(Ci.nsITimedChannel);
waitingTime = Math.round(
(timedChannel.responseStartTime - timedChannel.requestStartTime) / 1000
);
}
let proxyInfo = [];
if (proxyResponseRawHeaders) {
@ -493,13 +515,16 @@ class NetworkEventActor extends Actor {
proxyInfo = proxyResponseRawHeaders.split("\r\n")[0].split(" ");
}
const isFileChannel = channel instanceof Ci.nsIFileChannel;
this._onEventUpdate("responseStart", {
httpVersion: lazy.NetworkUtils.getHttpVersion(channel),
httpVersion: isFileChannel
? null
: lazy.NetworkUtils.getHttpVersion(channel),
mimeType,
remoteAddress: fromCache ? "" : channel.remoteAddress,
remotePort: fromCache ? "" : channel.remotePort,
status: channel.responseStatus + "",
statusText: channel.responseStatusText,
status: isFileChannel ? "200" : channel.responseStatus + "",
statusText: isFileChannel ? "0K" : channel.responseStatusText,
waitingTime,
isResolvedByTRR: channel.isResolvedByTRR,
proxyHttpVersion: proxyInfo[0],

View File

@ -275,7 +275,7 @@ class NetworkEventWatcher {
}
onNetworkEvent(networkEventOptions, channel) {
if (this.networkEvents.has(channel.channelId)) {
if (channel.channelId && this.networkEvents.has(channel.channelId)) {
throw new Error(
`Got notified about channel ${channel.channelId} more than once.`
);
@ -301,6 +301,7 @@ class NetworkEventWatcher {
resourceId: resource.resourceId,
resourceType: resource.resourceType,
isBlocked,
isFileRequest: resource.isFileRequest,
receivedUpdates: [],
resourceUpdates: {
// Requests already come with request cookies and headers, so those
@ -375,10 +376,11 @@ class NetworkEventWatcher {
resourceUpdates[`${updateResource.updateType}Available`] = true;
receivedUpdates.push(updateResource.updateType);
const isComplete =
receivedUpdates.includes("eventTimings") &&
receivedUpdates.includes("responseContent") &&
receivedUpdates.includes("securityInfo");
const isComplete = networkEvent.isFileRequest
? receivedUpdates.includes("responseStart")
: receivedUpdates.includes("eventTimings") &&
receivedUpdates.includes("responseContent") &&
receivedUpdates.includes("securityInfo");
if (isComplete) {
this._emitUpdate(networkEvent);

View File

@ -156,7 +156,7 @@ export class NetworkObserver {
*/
#onNetworkEvent;
/**
* Object that holds the HTTP activity objects for ongoing requests.
* Object that holds the activity objects for ongoing requests.
*
* @type {ChannelMap}
*/
@ -222,6 +222,10 @@ export class NetworkObserver {
this.#httpModifyExaminer,
"http-on-modify-request"
);
Services.obs.addObserver(
this.#fileChannelExaminer,
"file-channel-opened"
);
Services.obs.addObserver(this.#httpStopRequest, "http-on-stop-request");
} else {
Services.obs.addObserver(
@ -500,6 +504,48 @@ export class NetworkObserver {
}
});
/**
* Observe notifications for the file-channel-opened topic
*
* @private
* @param nsIFileChannel subject
* @param string topic
* @returns void
*/
#fileChannelExaminer = DevToolsInfaillibleUtils.makeInfallible(
(subject, topic) => {
if (
this.#isDestroyed ||
topic != "file-channel-opened" ||
!(subject instanceof Ci.nsIFileChannel)
) {
return;
}
const channel = subject.QueryInterface(Ci.nsIFileChannel);
channel.QueryInterface(Ci.nsIIdentChannel);
channel.QueryInterface(Ci.nsIChannel);
if (this.#ignoreChannelFunction(channel)) {
return;
}
logPlatformEvent(topic, channel);
const fileActivity = this.#createOrGetActivityObject(channel);
this.#createNetworkEvent(subject, {});
if (fileActivity.owner) {
fileActivity.owner.addResponseStart({
channel: fileActivity.channel,
fromCache: fileActivity.fromCache || fileActivity.fromServiceWorker,
rawHeaders: fileActivity.responseRawHeaders,
proxyResponseRawHeaders: fileActivity.proxyResponseRawHeaders,
});
}
}
);
/**
* A helper function for observeActivity. This does whatever work
* is required by a particular http activity event. Arguments are
@ -666,6 +712,7 @@ export class NetworkObserver {
* - Cancel requests blocked by DevTools
* - Fetch request headers/cookies
* - Set a few attributes on http activity object
* - Set a few attributes on file activity object
* - Register listener to record response content
*/
#createNetworkEvent(
@ -680,6 +727,21 @@ export class NetworkObserver {
inProgressRequest,
}
) {
if (channel instanceof Ci.nsIFileChannel) {
const fileActivity = this.#createOrGetActivityObject(channel);
if (timestamp) {
fileActivity.timings.REQUEST_HEADER = {
first: timestamp,
last: timestamp,
};
}
fileActivity.owner = this.#onNetworkEvent({}, channel);
return fileActivity;
}
const httpActivity = this.#createOrGetActivityObject(channel);
if (timestamp) {
@ -777,7 +839,7 @@ export class NetworkObserver {
}
/**
* Find an existing HTTP activity object, or create a new one. This
* Find an existing activity object, or create a new one. This
* object is used for storing all the request and response
* information.
*
@ -785,26 +847,29 @@ export class NetworkObserver {
* this point.
*
* @see http://www.softwareishard.com/blog/har-12-spec
* @param nsIHttpChannel channel
* @param {(nsIHttpChannel|nsIFileChannel)} channel
* The HTTP channel for which the HTTP activity object is created.
* @return object
* The new HTTP activity object.
*/
#createOrGetActivityObject(channel) {
let httpActivity = this.#findActivityObject(channel);
if (!httpActivity) {
const win = lazy.NetworkHelper.getWindowForRequest(channel);
const charset = win ? win.document.characterSet : null;
let activity = this.#findActivityObject(channel);
if (!activity) {
const isHttpChannel = channel instanceof Ci.nsIHttpChannel;
// Most of the data needed from the channel is only available via the
// nsIHttpChannelInternal interface.
channel.QueryInterface(Ci.nsIHttpChannelInternal);
if (isHttpChannel) {
// Most of the data needed from the channel is only available via the
// nsIHttpChannelInternal interface.
channel.QueryInterface(Ci.nsIHttpChannelInternal);
} else {
channel.QueryInterface(Ci.nsIChannel);
}
httpActivity = {
activity = {
// The nsIChannel for which this activity object was created.
channel,
// See #prepareRequestBody()
charset,
charset: isHttpChannel ? lazy.NetworkUtils.getCharset(channel) : null,
// The postData sent by this request.
sentBody: null,
// The URL for the current channel.
@ -813,20 +878,24 @@ export class NetworkObserver {
bodySize: 0,
// The response headers size.
headersSize: 0,
// needed for host specific security info
hostname: channel.URI.host,
discardRequestBody: !this.#saveRequestAndResponseBodies,
discardResponseBody: !this.#saveRequestAndResponseBodies,
// needed for host specific security info but file urls do not have hostname
hostname: isHttpChannel ? channel.URI.host : null,
discardRequestBody: isHttpChannel
? !this.#saveRequestAndResponseBodies
: false,
discardResponseBody: isHttpChannel
? !this.#saveRequestAndResponseBodies
: false,
// internal timing information, see observeActivity()
timings: {},
// the activity owner which is notified when changes happen
owner: null,
};
this.#openRequests.set(channel, httpActivity);
this.#openRequests.set(channel, activity);
}
return httpActivity;
return activity;
}
/**
@ -1432,6 +1501,10 @@ export class NetworkObserver {
this.#httpModifyExaminer,
"http-on-modify-request"
);
Services.obs.removeObserver(
this.#fileChannelExaminer,
"file-channel-opened"
);
Services.obs.removeObserver(
this.#httpStopRequest,
"http-on-stop-request"

View File

@ -466,7 +466,7 @@ function fetchResponseHeadersAndCookies(channel) {
* Check if a given network request should be logged by a network monitor
* based on the specified filters.
*
* @param nsIHttpChannel channel
* @param {(nsIHttpChannel|nsIFileChannel)} channel
* Request to check.
* @param filters
* NetworkObserver filters to match against. An object with one of the following attributes:
@ -662,6 +662,11 @@ function getBlockedReason(channel, fromCache = false) {
return { blockingExtension, blockedReason };
}
function getCharset(channel) {
const win = lazy.NetworkHelper.getWindowForRequest(channel);
return win ? win.document.characterSet : null;
}
export const NetworkUtils = {
causeTypeToString,
fetchRequestHeadersAndCookies,
@ -684,4 +689,5 @@ export const NetworkUtils = {
matchRequest,
stringToCauseType,
getBlockedReason,
getCharset,
};