Bug 1625909 - Use the ResourceWatcher API to fetch Network Events r=ochameau,Honza,nchevobbe

This patch migrates to using the Resource Watcher API to handle Network events, it adds a legacy listeners for the network events

Differential Revision: https://phabricator.services.mozilla.com/D71543
This commit is contained in:
Hubert Boma Manilla 2020-07-12 20:38:06 +00:00
Родитель 185c6d1087
Коммит 9bcb3649ef
30 изменённых файлов: 769 добавлений и 780 удалений

Просмотреть файл

@ -135,4 +135,4 @@ async function assertResultIsTab(dbg, index) {
el && !!el.querySelector(".tab.result-item-icon"), el && !!el.querySelector(".tab.result-item-icon"),
"Result should be a tab" "Result should be a tab"
); );
} }

Просмотреть файл

@ -742,6 +742,9 @@ Toolbox.prototype = {
} }
}, },
_onResourceAvailable() {},
_onResourceUpdated() {},
/** /**
* This method focuses on attaching to one particular target. * This method focuses on attaching to one particular target.
* It ensure that the target actor is fully initialized and is watching for * It ensure that the target actor is fully initialized and is watching for
@ -752,10 +755,6 @@ Toolbox.prototype = {
async _attachTarget(targetFront) { async _attachTarget(targetFront) {
await targetFront.attach(); await targetFront.attach();
// Start tracking network activity on toolbox open for targets such as tabs.
const webConsoleFront = await targetFront.getFront("console");
await webConsoleFront.startListeners(["NetworkActivity"]);
// Do not attach to the thread of additional Frame targets, as they are // Do not attach to the thread of additional Frame targets, as they are
// already tracked by the content process targets. At least in the context // already tracked by the content process targets. At least in the context
// of the Browser Toolbox. // of the Browser Toolbox.
@ -843,6 +842,19 @@ Toolbox.prototype = {
this._onTargetDestroyed this._onTargetDestroyed
); );
// Start tracking network activity on toolbox open for targets such as tabs.
// The listeners attached here do nothing. Doing this just makes sure that
// there is always at least one listener existing for network events across
// the lifetime of the various panels, so stopping the resource watcher from
// clearing out its cache of network event resources.
await this.resourceWatcher.watchResources(
[this.resourceWatcher.TYPES.NETWORK_EVENT],
{
onAvailable: this._onResourceAvailable,
onUpdated: this._onResourceUpdated,
}
);
await domReady; await domReady;
this.browserRequire = BrowserLoader({ this.browserRequire = BrowserLoader({
@ -3702,6 +3714,14 @@ Toolbox.prototype = {
// Reset preferences set by the toolbox // Reset preferences set by the toolbox
outstanding.push(this.resetPreference()); outstanding.push(this.resetPreference());
await this.resourceWatcher.unwatchResources(
[this.resourceWatcher.TYPES.NETWORK_EVENT],
{
onAvailable: this._onResourceAvailable,
onUpdated: this._onResourceUpdated,
}
);
this.targetList.unwatchTargets( this.targetList.unwatchTargets(
TargetList.ALL_TYPES, TargetList.ALL_TYPES,
this._onTargetAvailable, this._onTargetAvailable,

Просмотреть файл

@ -32,145 +32,32 @@ class WebConsoleFront extends FrontClassWithSpec(webconsoleSpec) {
// Attribute name from which to retrieve the actorID out of the target actor's form // Attribute name from which to retrieve the actorID out of the target actor's form
this.formAttributeName = "consoleActor"; this.formAttributeName = "consoleActor";
/**
* Holds the network requests currently displayed by the Web Console. Each key
* represents the connection ID and the value is network request information.
* @private
* @type object
*/
this._networkRequests = new Map();
this.pendingEvaluationResults = new Map(); this.pendingEvaluationResults = new Map();
this.onEvaluationResult = this.onEvaluationResult.bind(this); this.onEvaluationResult = this.onEvaluationResult.bind(this);
this.onNetworkEvent = this._onNetworkEvent.bind(this); this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this.onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this.on("evaluationResult", this.onEvaluationResult); this.on("evaluationResult", this.onEvaluationResult);
this.on("serverNetworkEvent", this.onNetworkEvent);
this.before("consoleAPICall", this.beforeConsoleAPICall); this.before("consoleAPICall", this.beforeConsoleAPICall);
this.before("pageError", this.beforePageError); this.before("pageError", this.beforePageError);
this._client.on("networkEventUpdate", this.onNetworkEventUpdate); this._client.on("networkEventUpdate", this._onNetworkEventUpdate);
}
getNetworkRequest(actorId) {
return this._networkRequests.get(actorId);
}
getNetworkEvents() {
return this._networkRequests.values();
} }
get actor() { get actor() {
return this.actorID; return this.actorID;
} }
/**
* The "networkEvent" message type handler. We redirect any message to
* the UI for displaying.
*
* @private
* @param string type
* Message type.
* @param object packet
* The message received from the server.
*/
_onNetworkEvent(packet) {
const actor = packet.eventActor;
const networkInfo = {
type: "networkEvent",
timeStamp: actor.timeStamp,
node: null,
actor: actor.actor,
discardRequestBody: true,
discardResponseBody: true,
startedDateTime: actor.startedDateTime,
request: {
url: actor.url,
method: actor.method,
},
isXHR: actor.isXHR,
cause: actor.cause,
response: {},
timings: {},
// track the list of network event updates
updates: [],
private: actor.private,
fromCache: actor.fromCache,
fromServiceWorker: actor.fromServiceWorker,
isThirdPartyTrackingResource: actor.isThirdPartyTrackingResource,
referrerPolicy: actor.referrerPolicy,
blockedReason: actor.blockedReason,
blockingExtension: actor.blockingExtension,
channelId: actor.channelId,
};
this._networkRequests.set(actor.actor, networkInfo);
this.emit("networkEvent", networkInfo);
}
/** /**
* The "networkEventUpdate" message type handler. We redirect any message to * The "networkEventUpdate" message type handler. We redirect any message to
* the UI for displaying. * the UI for displaying.
* *
* @private * @private
* @param string type
* Message type.
* @param object packet * @param object packet
* The message received from the server. * The message received from the server.
*/ */
_onNetworkEventUpdate(packet) { _onNetworkEventUpdate(packet) {
const networkInfo = this.getNetworkRequest(packet.from); this.emit("serverNetworkUpdateEvent", packet);
if (!networkInfo) {
return;
}
networkInfo.updates.push(packet.updateType);
switch (packet.updateType) {
case "requestHeaders":
networkInfo.request.headersSize = packet.headersSize;
break;
case "requestPostData":
networkInfo.discardRequestBody = packet.discardRequestBody;
networkInfo.request.bodySize = packet.dataSize;
break;
case "responseStart":
networkInfo.response.httpVersion = packet.response.httpVersion;
networkInfo.response.status = packet.response.status;
networkInfo.response.statusText = packet.response.statusText;
networkInfo.response.headersSize = packet.response.headersSize;
networkInfo.response.remoteAddress = packet.response.remoteAddress;
networkInfo.response.remotePort = packet.response.remotePort;
networkInfo.discardResponseBody = packet.response.discardResponseBody;
networkInfo.response.waitingTime = packet.response.waitingTime;
networkInfo.response.content = {
mimeType: packet.response.mimeType,
};
break;
case "responseContent":
networkInfo.response.content = {
mimeType: packet.mimeType,
};
networkInfo.response.bodySize = packet.contentSize;
networkInfo.response.transferredSize = packet.transferredSize;
networkInfo.discardResponseBody = packet.discardResponseBody;
break;
case "eventTimings":
networkInfo.totalTime = packet.totalTime;
break;
case "securityInfo":
networkInfo.securityState = packet.state;
break;
case "responseCache":
networkInfo.response.responseCache = packet.responseCache;
break;
}
this.emit("networkEventUpdate", {
packet: packet,
networkInfo,
});
} }
/** /**
@ -587,13 +474,6 @@ class WebConsoleFront extends FrontClassWithSpec(webconsoleSpec) {
} }
} }
clearNetworkRequests() {
// Prevent exception if the front has already been destroyed.
if (this._networkRequests) {
this._networkRequests.clear();
}
}
/** /**
* Close the WebConsoleFront. * Close the WebConsoleFront.
* *
@ -603,18 +483,15 @@ class WebConsoleFront extends FrontClassWithSpec(webconsoleSpec) {
return null; return null;
} }
this._client.off("networkEventUpdate", this.onNetworkEventUpdate); this._client.off("networkEventUpdate", this._onNetworkEventUpdate);
// This will make future calls to this function harmless because of the early return // This will make future calls to this function harmless because of the early return
// at the top of the function. // at the top of the function.
this._client = null; this._client = null;
this.off("evaluationResult", this.onEvaluationResult); this.off("evaluationResult", this.onEvaluationResult);
this.off("serverNetworkEvent", this.onNetworkEvent);
this._longStrings = null; this._longStrings = null;
this.pendingEvaluationResults.clear(); this.pendingEvaluationResults.clear();
this.pendingEvaluationResults = null; this.pendingEvaluationResults = null;
this.clearNetworkRequests();
this._networkRequests = null;
return super.destroy(); return super.destroy();
} }
} }

Просмотреть файл

@ -6,7 +6,7 @@
// panel. // panel.
const TEST_URI = const TEST_URI =
"data:text/html;charset=UTF-8," + "<p>Switch to inspector on pick</p>"; "data:text/html;charset=UTF-8,<script>console.log(`hello`)</script><p>Switch to inspector on pick</p>";
const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS; const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS;
const DATA = [ const DATA = [
@ -20,7 +20,7 @@ const DATA = [
start_state: "initial_panel", start_state: "initial_panel",
panel_name: "webconsole", panel_name: "webconsole",
cold: "true", cold: "true",
message_count: "0", message_count: "1",
width: "1300", width: "1300",
}, },
}, },
@ -65,7 +65,7 @@ add_task(async function() {
await startPickerAndAssertSwitchToInspector(toolbox); await startPickerAndAssertSwitchToInspector(toolbox);
info("Stoppping element picker."); info("Stopping element picker.");
await toolbox.nodePicker.stop(); await toolbox.nodePicker.stop();
checkResults(); checkResults();

Просмотреть файл

@ -79,11 +79,6 @@ if (window.location.protocol === "chrome:" && url.search.length > 1) {
(async function() { (async function() {
try { try {
const target = await targetFromURL(url); const target = await targetFromURL(url);
// Start the network event listening as it is done in the toolbox code
const consoleFront = await target.getFront("console");
await consoleFront.startListeners(["NetworkActivity"]);
// Create a fake toolbox object // Create a fake toolbox object
const toolbox = { const toolbox = {
target, target,

Просмотреть файл

@ -32,7 +32,6 @@ class FirefoxConnector {
this.disconnect = this.disconnect.bind(this); this.disconnect = this.disconnect.bind(this);
this.willNavigate = this.willNavigate.bind(this); this.willNavigate = this.willNavigate.bind(this);
this.navigate = this.navigate.bind(this); this.navigate = this.navigate.bind(this);
this.displayCachedEvents = this.displayCachedEvents.bind(this);
this.sendHTTPRequest = this.sendHTTPRequest.bind(this); this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
this.setPreferences = this.setPreferences.bind(this); this.setPreferences = this.setPreferences.bind(this);
this.triggerActivity = this.triggerActivity.bind(this); this.triggerActivity = this.triggerActivity.bind(this);
@ -44,9 +43,9 @@ class FirefoxConnector {
// Internals // Internals
this.getLongString = this.getLongString.bind(this); this.getLongString = this.getLongString.bind(this);
this.getNetworkRequest = this.getNetworkRequest.bind(this);
this.onTargetAvailable = this.onTargetAvailable.bind(this); this.onTargetAvailable = this.onTargetAvailable.bind(this);
this.onResourceAvailable = this.onResourceAvailable.bind(this); this.onResourceAvailable = this.onResourceAvailable.bind(this);
this.onResourceUpdated = this.onResourceUpdated.bind(this);
} }
get currentTarget() { get currentTarget() {
@ -114,7 +113,9 @@ class FirefoxConnector {
} }
async resume() { async resume() {
await this.addListeners(); // On resume, we shoud prevent fetching all cached network events
// and only restart recording for the new ones.
await this.addListeners(true);
} }
async onTargetAvailable({ targetFront, isTargetSwitching }) { async onTargetAvailable({ targetFront, isTargetSwitching }) {
@ -146,26 +147,35 @@ class FirefoxConnector {
// Initialize Responsive Emulation front for network throttling. // Initialize Responsive Emulation front for network throttling.
this.responsiveFront = await this.currentTarget.getFront("responsive"); this.responsiveFront = await this.currentTarget.getFront("responsive");
// Displaying cache events is only intended for the UI panel.
if (this.actions) {
this.displayCachedEvents();
}
} }
async onResourceAvailable({ resourceType, targetFront, resource }) { async onResourceAvailable({ resourceType, targetFront, resource }) {
if (resourceType === this.toolbox.resourceWatcher.TYPES.DOCUMENT_EVENT) { const { TYPES } = this.toolbox.resourceWatcher;
if (resourceType === TYPES.DOCUMENT_EVENT) {
this.onDocEvent(resource); this.onDocEvent(resource);
return;
}
if (resourceType === TYPES.NETWORK_EVENT) {
this.dataProvider.onNetworkResourceAvailable(resource);
} }
} }
async addListeners() { async onResourceUpdated({ resourceType, targetFront, resource }) {
this.webConsoleFront.on("networkEvent", this.dataProvider.onNetworkEvent); if (resourceType === this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT) {
this.webConsoleFront.on( this.dataProvider.onNetworkResourceUpdated(resource);
"networkEventUpdate", }
this.dataProvider.onNetworkEventUpdate }
);
async addListeners(ignoreExistingResources = false) {
await this.toolbox.resourceWatcher.watchResources(
[this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT],
{
onAvailable: this.onResourceAvailable,
onUpdated: this.onResourceUpdated,
ignoreExistingResources,
}
);
// Support for WebSocket monitoring is currently hidden behind this pref. // Support for WebSocket monitoring is currently hidden behind this pref.
if (Services.prefs.getBoolPref("devtools.netmonitor.features.webSockets")) { if (Services.prefs.getBoolPref("devtools.netmonitor.features.webSockets")) {
try { try {
@ -206,6 +216,13 @@ class FirefoxConnector {
} }
removeListeners() { removeListeners() {
this.toolbox.resourceWatcher.unwatchResources(
[this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT],
{
onAvailable: this.onResourceAvailable,
onUpdated: this.onResourceUpdated,
}
);
const webSocketFront = this.currentTarget.getCachedFront("webSocket"); const webSocketFront = this.currentTarget.getCachedFront("webSocket");
if (webSocketFront) { if (webSocketFront) {
webSocketFront.off( webSocketFront.off(
@ -220,17 +237,6 @@ class FirefoxConnector {
webSocketFront.off("frameSent", this.dataProvider.onFrameSent); webSocketFront.off("frameSent", this.dataProvider.onFrameSent);
} }
if (this.webConsoleFront) {
this.webConsoleFront.off(
"networkEvent",
this.dataProvider.onNetworkEvent
);
this.webConsoleFront.off(
"networkEventUpdate",
this.dataProvider.onNetworkEventUpdate
);
}
const eventSourceFront = this.currentTarget.getCachedFront("eventSource"); const eventSourceFront = this.currentTarget.getCachedFront("eventSource");
if (eventSourceFront) { if (eventSourceFront) {
eventSourceFront.off( eventSourceFront.off(
@ -300,23 +306,6 @@ class FirefoxConnector {
} }
} }
/**
* Display any network events already in the cache.
*/
displayCachedEvents() {
for (const networkInfo of this.webConsoleFront.getNetworkEvents()) {
// First add the request to the timeline.
this.dataProvider.onNetworkEvent(networkInfo);
// Then replay any updates already received.
for (const updateType of networkInfo.updates) {
this.dataProvider.onNetworkEventUpdate({
packet: { updateType },
networkInfo,
});
}
}
}
/** /**
* The "DOMContentLoaded" and "Load" events sent by the console actor. * The "DOMContentLoaded" and "Load" events sent by the console actor.
* *
@ -465,16 +454,6 @@ class FirefoxConnector {
return Promise.reject(new Error("Invalid activity type")); return Promise.reject(new Error("Invalid activity type"));
} }
/**
* Fetches the network information packet from actor server
*
* @param {string} id request id
* @return {object} networkInfo data packet
*/
getNetworkRequest(id) {
return this.dataProvider.getNetworkRequest(id);
}
/** /**
* Fetches the full text of a LongString. * Fetches the full text of a LongString.
* *

Просмотреть файл

@ -48,8 +48,10 @@ class FirefoxDataProvider {
this.getLongString = this.getLongString.bind(this); this.getLongString = this.getLongString.bind(this);
// Event handlers // Event handlers
this.onNetworkEvent = this.onNetworkEvent.bind(this); this.onNetworkResourceAvailable = this.onNetworkResourceAvailable.bind(
this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this); this
);
this.onNetworkResourceUpdated = this.onNetworkResourceUpdated.bind(this);
this.onWebSocketOpened = this.onWebSocketOpened.bind(this); this.onWebSocketOpened = this.onWebSocketOpened.bind(this);
this.onWebSocketClosed = this.onWebSocketClosed.bind(this); this.onWebSocketClosed = this.onWebSocketClosed.bind(this);
@ -79,50 +81,26 @@ class FirefoxDataProvider {
* @param {object} data data payload will be added to application state * @param {object} data data payload will be added to application state
*/ */
async addRequest(id, data) { async addRequest(id, data) {
const { const { startedDateTime, ...payload } = data;
method,
url,
isXHR,
cause,
startedDateTime,
fromCache,
fromServiceWorker,
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
blockingExtension,
channelId,
} = data;
// Insert blocked reason in the payload queue as well, as we'll need it later // Insert blocked reason in the payload queue as well, as we'll need it later
// when deciding if the request is complete. // when deciding if the request is complete.
this.pushRequestToQueue(id, { this.pushRequestToQueue(id, {
blockedReason, blockedReason: payload.blockedReason,
}); });
if (this.actionsEnabled && this.actions.addRequest) { if (this.actionsEnabled && this.actions.addRequest) {
await this.actions.addRequest( await this.actions.addRequest(
id, id,
{ {
...payload,
// Convert the received date/time string to a unix timestamp. // Convert the received date/time string to a unix timestamp.
startedMs: Date.parse(startedDateTime), startedMs: Date.parse(startedDateTime),
method,
url,
isXHR,
cause,
// Compatibility code to support Firefox 58 and earlier that always // Compatibility code to support Firefox 58 and earlier that always
// send stack-trace immediately on networkEvent message. // send stack-trace immediately on networkEvent message.
// FF59+ supports fetching the traces lazily via requestData. // FF59+ supports fetching the traces lazily via requestData.
stacktrace: cause.stacktrace, stacktrace: payload.cause.stacktrace,
fromCache,
fromServiceWorker,
isThirdPartyTrackingResource,
referrerPolicy,
blockedReason,
blockingExtension,
channelId,
}, },
true true
); );
@ -324,16 +302,6 @@ class FirefoxDataProvider {
Object.assign(payloadFromQueue, payload); Object.assign(payloadFromQueue, payload);
} }
/**
* Fetches the network information packet from actor server
*
* @param {string} id request id
* @return {object} networkInfo data packet
*/
getNetworkRequest(id) {
return this.webConsoleFront.getNetworkRequest(id);
}
/** /**
* Fetches the full text of a LongString. * Fetches the full text of a LongString.
* *
@ -353,11 +321,11 @@ class FirefoxDataProvider {
} }
/** /**
* The "networkEvent" message type handler. * The handler for when the network event resource is available.
* *
* @param {object} networkInfo network request information * @param {object} resource The network event resource
*/ */
async onNetworkEvent(networkInfo) { async onNetworkResourceAvailable(resource) {
const { const {
actor, actor,
cause, cause,
@ -365,13 +333,34 @@ class FirefoxDataProvider {
fromServiceWorker, fromServiceWorker,
isXHR, isXHR,
request: { method, url }, request: { method, url },
response: { bodySize, ...responseProps },
startedDateTime, startedDateTime,
isThirdPartyTrackingResource, isThirdPartyTrackingResource,
referrerPolicy, referrerPolicy,
blockedReason, blockedReason,
blockingExtension, blockingExtension,
channelId, channelId,
} = networkInfo; } = resource;
// For resources from the resource watcher cache no updates are going to be fired
// as the resource already contains all the updated props. These need to be set so
// the UI knows the data is available on the backend.
const available = {};
[
"eventTimings",
"requestHeaders",
"requestPostData",
"responseHeaders",
"responseStart",
"responseContent",
"securityInfo",
"responseCache",
"responseCookies",
].forEach(updateType => {
if (resource.updates.includes(updateType)) {
available[`${updateType}Available`] = true;
}
});
await this.addRequest(actor, { await this.addRequest(actor, {
cause, cause,
@ -386,56 +375,55 @@ class FirefoxDataProvider {
blockedReason, blockedReason,
blockingExtension, blockingExtension,
channelId, channelId,
mimeType: resource?.content?.mimeType,
contentSize: bodySize,
...responseProps,
...available,
}); });
this.emitForTests(TEST_EVENTS.NETWORK_EVENT, resource);
this.emitForTests(TEST_EVENTS.NETWORK_EVENT, actor);
} }
/** /**
* The "networkEventUpdate" message type handler. * The handler for when the network event resource is updated.
* *
* @param {object} packet the message received from the server. * @param {object} resource The updated network event resource.
* @param {object} networkInfo the network request information.
*/ */
async onNetworkEventUpdate(data) { async onNetworkResourceUpdated(resource) {
const { packet, networkInfo } = data; switch (resource.updateType) {
const { actor } = networkInfo;
const { updateType } = packet;
switch (updateType) {
case "securityInfo": case "securityInfo":
this.pushRequestToQueue(actor, { this.pushRequestToQueue(resource.actor, {
securityState: networkInfo.securityState, securityState: resource.securityState,
isRacing: packet.isRacing, isRacing: resource.isRacing,
}); });
break; break;
case "responseStart": case "responseStart":
this.pushRequestToQueue(actor, { this.pushRequestToQueue(resource.actor, {
httpVersion: networkInfo.response.httpVersion, httpVersion: resource.response.httpVersion,
remoteAddress: networkInfo.response.remoteAddress, remoteAddress: resource.response.remoteAddress,
remotePort: networkInfo.response.remotePort, remotePort: resource.response.remotePort,
status: networkInfo.response.status, status: resource.response.status,
statusText: networkInfo.response.statusText, statusText: resource.response.statusText,
headersSize: networkInfo.response.headersSize, headersSize: resource.response.headersSize,
waitingTime: networkInfo.response.waitingTime, waitingTime: resource.response.waitingTime,
}); });
// Identify the channel as SSE if mimeType is event-stream. // Identify the channel as SSE if mimeType is event-stream.
if ( if (resource.response.content.mimeType?.includes("text/event-stream")) {
networkInfo.response.content.mimeType?.includes("text/event-stream") await this.setEventStreamFlag(resource.actor);
) {
await this.setEventStreamFlag(actor);
} }
this.emitForTests(TEST_EVENTS.STARTED_RECEIVING_RESPONSE, actor); this.emitForTests(
TEST_EVENTS.STARTED_RECEIVING_RESPONSE,
resource.actor
);
break; break;
case "responseContent": case "responseContent":
this.pushRequestToQueue(actor, { this.pushRequestToQueue(resource.actor, {
contentSize: networkInfo.response.bodySize, contentSize: resource.response.bodySize,
transferredSize: networkInfo.response.transferredSize, transferredSize: resource.response.transferredSize,
mimeType: networkInfo.response.content.mimeType, mimeType: resource.response.content.mimeType,
blockingExtension: packet.blockingExtension, blockingExtension: resource.blockingExtension,
blockedReason: packet.blockedReason, blockedReason: resource.blockedReason,
}); });
break; break;
case "eventTimings": case "eventTimings":
@ -443,19 +431,23 @@ class FirefoxDataProvider {
// in Console panel is using this method to fetch data when network log // in Console panel is using this method to fetch data when network log
// is expanded. So, make sure to not push undefined into the payload queue // is expanded. So, make sure to not push undefined into the payload queue
// (it could overwrite an existing value). // (it could overwrite an existing value).
if (typeof networkInfo.totalTime !== "undefined") { if (typeof resource.totalTime !== "undefined") {
this.pushRequestToQueue(actor, { totalTime: networkInfo.totalTime }); this.pushRequestToQueue(resource.actor, {
totalTime: resource.totalTime,
});
} }
break; break;
} }
// This available field helps knowing when/if updateType property is arrived // This available field helps knowing when/if updateType property is arrived
// and can be requested via `requestData` // and can be requested via `requestData`
this.pushRequestToQueue(actor, { [`${updateType}Available`]: true }); this.pushRequestToQueue(resource.actor, {
[`${resource.updateType}Available`]: true,
});
await this.onPayloadDataReceived(actor); await this.onPayloadDataReceived(resource);
this.emitForTests(TEST_EVENTS.NETWORK_EVENT_UPDATED, actor); this.emitForTests(TEST_EVENTS.NETWORK_EVENT_UPDATED, resource.actor);
} }
/** /**
@ -516,14 +508,14 @@ class FirefoxDataProvider {
} }
/** /**
* Notify actions when messages from onNetworkEventUpdate are done, networkEventUpdate * Notify actions when events from onNetworkResourceUpdated are done, updated network event
* messages contain initial network info for each updateType and then we can invoke * resources contain initial network info for each updateType and then we can invoke
* requestData to fetch its corresponded data lazily. * requestData to fetch its corresponded data lazily.
* Once all updateTypes of networkEventUpdate message are arrived, we flush merged * Once all updateTypes of updated network event resource are available, we flush the merged
* request payload from pending queue and then update component. * request payload from pending queue and then update the component.
*/ */
async onPayloadDataReceived(actor) { async onPayloadDataReceived(resource) {
const payload = this.payloadQueue.get(actor) || {}; const payload = this.payloadQueue.get(resource.actor) || {};
// For blocked requests, we should only expect the request portions and not // For blocked requests, we should only expect the request portions and not
// the response portions to be available. // the response portions to be available.
@ -538,17 +530,17 @@ class FirefoxDataProvider {
return; return;
} }
this.payloadQueue.delete(actor); this.payloadQueue.delete(resource.actor);
if (this.actionsEnabled && this.actions.updateRequest) { if (this.actionsEnabled && this.actions.updateRequest) {
await this.actions.updateRequest(actor, payload, true); await this.actions.updateRequest(resource.actor, payload, true);
} }
// This event is fired only once per request, once all the properties are fetched // This event is fired only once per request, once all the properties are fetched
// from `onNetworkEventUpdate`. There should be no more RDP requests after this. // from `onNetworkResourceUpdated`. There should be no more RDP requests after this.
// Note that this event might be consumed by extension so, emit it in production // Note that this event might be consumed by extension so, emit it in production
// release as well. // release as well.
this.emit(EVENTS.PAYLOAD_READY, actor); this.emit(EVENTS.PAYLOAD_READY, resource);
} }
/** /**
@ -674,7 +666,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
requestHeaders: response, requestHeaders: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_REQUEST_HEADERS, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_REQUEST_HEADERS, response);
return payload.requestHeaders; return payload.requestHeaders;
} }
@ -687,7 +679,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
responseHeaders: response, responseHeaders: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_HEADERS, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_HEADERS, response);
return payload.responseHeaders; return payload.responseHeaders;
} }
@ -700,7 +692,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
requestCookies: response, requestCookies: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_REQUEST_COOKIES, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_REQUEST_COOKIES, response);
return payload.requestCookies; return payload.requestCookies;
} }
@ -713,7 +705,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
requestPostData: response, requestPostData: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_REQUEST_POST_DATA, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_REQUEST_POST_DATA, response);
return payload.requestPostData; return payload.requestPostData;
} }
@ -726,7 +718,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
securityInfo: response.securityInfo, securityInfo: response.securityInfo,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_SECURITY_INFO, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_SECURITY_INFO, response);
return payload.securityInfo; return payload.securityInfo;
} }
@ -739,7 +731,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
responseCookies: response, responseCookies: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_COOKIES, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_COOKIES, response);
return payload.responseCookies; return payload.responseCookies;
} }
@ -751,7 +743,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
responseCache: response, responseCache: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_CACHE, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_CACHE, response);
return payload.responseCache; return payload.responseCache;
} }
@ -768,7 +760,7 @@ class FirefoxDataProvider {
mimeType: response.content.mimeType, mimeType: response.content.mimeType,
responseContent: response, responseContent: response,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_CONTENT, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_RESPONSE_CONTENT, response);
return payload.responseContent; return payload.responseContent;
} }
@ -786,7 +778,7 @@ class FirefoxDataProvider {
// and running DAMP test doesn't set the `devtools.testing` flag. // and running DAMP test doesn't set the `devtools.testing` flag.
// So, emit this event even in the production mode. // So, emit this event even in the production mode.
// See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1578215 // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1578215
this.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from); this.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response);
return payload.eventTimings; return payload.eventTimings;
} }
@ -799,7 +791,7 @@ class FirefoxDataProvider {
const payload = await this.updateRequest(response.from, { const payload = await this.updateRequest(response.from, {
stacktrace: response.stacktrace, stacktrace: response.stacktrace,
}); });
this.emitForTests(TEST_EVENTS.RECEIVED_EVENT_STACKTRACE, response.from); this.emitForTests(TEST_EVENTS.RECEIVED_EVENT_STACKTRACE, response);
return payload.stacktrace; return payload.stacktrace;
} }

Просмотреть файл

@ -19,7 +19,6 @@ class Connector {
this.connectChrome = this.connectChrome.bind(this); this.connectChrome = this.connectChrome.bind(this);
this.connectFirefox = this.connectFirefox.bind(this); this.connectFirefox = this.connectFirefox.bind(this);
this.getLongString = this.getLongString.bind(this); this.getLongString = this.getLongString.bind(this);
this.getNetworkRequest = this.getNetworkRequest.bind(this);
this.getTabTarget = this.getTabTarget.bind(this); this.getTabTarget = this.getTabTarget.bind(this);
this.sendHTTPRequest = this.sendHTTPRequest.bind(this); this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
this.setPreferences = this.setPreferences.bind(this); this.setPreferences = this.setPreferences.bind(this);
@ -84,10 +83,6 @@ class Connector {
return this.connector.getLongString(...arguments); return this.connector.getLongString(...arguments);
} }
getNetworkRequest() {
return this.connector.getNetworkRequest(...arguments);
}
getTabTarget() { getTabTarget() {
return this.connector.getTabTarget(); return this.connector.getTabTarget();
} }

Просмотреть файл

@ -88,7 +88,7 @@ HarAutomation.prototype = {
// data from events sent from the backend. // data from events sent from the backend.
this.collector = new HarCollector({ this.collector = new HarCollector({
webConsoleFront: this.webConsoleFront, webConsoleFront: this.webConsoleFront,
devToolsClient: this.devToolsClient, resourceWatcher: this.toolbox.resourceWatcher,
}); });
this.collector.start(); this.collector.start();

Просмотреть файл

@ -17,10 +17,10 @@ const trace = {
*/ */
function HarCollector(options) { function HarCollector(options) {
this.webConsoleFront = options.webConsoleFront; this.webConsoleFront = options.webConsoleFront;
this.devToolsClient = options.devToolsClient; this.resourceWatcher = options.resourceWatcher;
this.onNetworkEvent = this.onNetworkEvent.bind(this); this.onResourceAvailable = this.onResourceAvailable.bind(this);
this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this); this.onResourceUpdated = this.onResourceUpdated.bind(this);
this.onRequestHeaders = this.onRequestHeaders.bind(this); this.onRequestHeaders = this.onRequestHeaders.bind(this);
this.onRequestCookies = this.onRequestCookies.bind(this); this.onRequestCookies = this.onRequestCookies.bind(this);
this.onRequestPostData = this.onRequestPostData.bind(this); this.onRequestPostData = this.onRequestPostData.bind(this);
@ -35,14 +35,24 @@ function HarCollector(options) {
HarCollector.prototype = { HarCollector.prototype = {
// Connection // Connection
start: function() { start: async function() {
this.webConsoleFront.on("networkEvent", this.onNetworkEvent); await this.resourceWatcher.watchResources(
this.devToolsClient.on("networkEventUpdate", this.onNetworkEventUpdate); [this.resourceWatcher.TYPES.NETWORK_EVENT],
{
onAvailable: this.onResourceAvailable,
onUpdated: this.onResourceUpdated,
}
);
}, },
stop: function() { stop: async function() {
this.webConsoleFront.off("networkEvent", this.onNetworkEvent); await this.resourceWatcher.unwatchResources(
this.devToolsClient.off("networkEventUpdate", this.onNetworkEventUpdate); [this.resourceWatcher.TYPES.NETWORK_EVENT],
{
onAvailable: this.onResourceAvailable,
onUpdated: this.onResourceUpdated,
}
);
}, },
clear: function() { clear: function() {
@ -156,15 +166,15 @@ HarCollector.prototype = {
// Event Handlers // Event Handlers
onNetworkEvent: function(packet) { onResourceAvailable: function({ resourceType, targetFront, resource }) {
trace.log("HarCollector.onNetworkEvent; ", packet); trace.log("HarCollector.onNetworkEvent; ", resource);
const { const {
actor, actor,
startedDateTime, startedDateTime,
request: { method, url }, request: { method, url },
isXHR, isXHR,
} = packet; } = resource;
const startTime = Date.parse(startedDateTime); const startTime = Date.parse(startedDateTime);
if (this.firstRequestStart == -1) { if (this.firstRequestStart == -1) {
@ -198,21 +208,19 @@ HarCollector.prototype = {
this.items.push(file); this.items.push(file);
}, },
onNetworkEventUpdate: function(packet) { onResourceUpdated: function({ resourceType, targetFront, resource }) {
const actor = packet.from;
// Skip events from unknown actors (not in the list). // Skip events from unknown actors (not in the list).
// It can happen when there are zombie requests received after // It can happen when there are zombie requests received after
// the target is closed or multiple tabs are attached through // the target is closed or multiple tabs are attached through
// one connection (one DevToolsClient object). // one connection (one DevToolsClient object).
const file = this.getFile(packet.from); const file = this.getFile(resource.actor);
if (!file) { if (!file) {
return; return;
} }
trace.log( trace.log(
"HarCollector.onNetworkEventUpdate; " + packet.updateType, "HarCollector.onNetworkEventUpdate; " + resource.updateType,
packet resource
); );
const includeResponseBodies = Services.prefs.getBoolPref( const includeResponseBodies = Services.prefs.getBoolPref(
@ -220,62 +228,66 @@ HarCollector.prototype = {
); );
let request; let request;
switch (packet.updateType) { switch (resource.updateType) {
case "requestHeaders": case "requestHeaders":
request = this.getData( request = this.getData(
actor, resource.actor,
"getRequestHeaders", "getRequestHeaders",
this.onRequestHeaders this.onRequestHeaders
); );
break; break;
case "requestCookies": case "requestCookies":
request = this.getData( request = this.getData(
actor, resource.actor,
"getRequestCookies", "getRequestCookies",
this.onRequestCookies this.onRequestCookies
); );
break; break;
case "requestPostData": case "requestPostData":
request = this.getData( request = this.getData(
actor, resource.actor,
"getRequestPostData", "getRequestPostData",
this.onRequestPostData this.onRequestPostData
); );
break; break;
case "responseHeaders": case "responseHeaders":
request = this.getData( request = this.getData(
actor, resource.actor,
"getResponseHeaders", "getResponseHeaders",
this.onResponseHeaders this.onResponseHeaders
); );
break; break;
case "responseCookies": case "responseCookies":
request = this.getData( request = this.getData(
actor, resource.actor,
"getResponseCookies", "getResponseCookies",
this.onResponseCookies this.onResponseCookies
); );
break; break;
case "responseStart": case "responseStart":
file.httpVersion = packet.response.httpVersion; file.httpVersion = resource.response.httpVersion;
file.status = packet.response.status; file.status = resource.response.status;
file.statusText = packet.response.statusText; file.statusText = resource.response.statusText;
break; break;
case "responseContent": case "responseContent":
file.contentSize = packet.contentSize; file.contentSize = resource.contentSize;
file.mimeType = packet.mimeType; file.mimeType = resource.mimeType;
file.transferredSize = packet.transferredSize; file.transferredSize = resource.transferredSize;
if (includeResponseBodies) { if (includeResponseBodies) {
request = this.getData( request = this.getData(
actor, resource.actor,
"getResponseContent", "getResponseContent",
this.onResponseContent this.onResponseContent
); );
} }
break; break;
case "eventTimings": case "eventTimings":
request = this.getData(actor, "getEventTimings", this.onEventTimings); request = this.getData(
resource.actor,
"getEventTimings",
this.onEventTimings
);
break; break;
} }

Просмотреть файл

@ -82,7 +82,7 @@ async function performRequestAndWait(tab, monitor) {
* Execute simple GET request * Execute simple GET request
*/ */
async function performPausedRequest(connector, tab, monitor) { async function performPausedRequest(connector, tab, monitor) {
const wait = connector.connector.webConsoleFront.once("networkEvent"); const wait = connector.connector.webConsoleFront.once("serverNetworkEvent");
await SpecialPowers.spawn(tab.linkedBrowser, [SIMPLE_SJS], async function( await SpecialPowers.spawn(tab.linkedBrowser, [SIMPLE_SJS], async function(
url url
) { ) {

Просмотреть файл

@ -265,8 +265,8 @@ function startNetworkEventUpdateObserver(panelWin) {
); );
updatedTypes.forEach(type => updatedTypes.forEach(type =>
panelWin.api.on(type, actor => { panelWin.api.on(type, payload => {
const key = actor + "-" + type; const key = payload.from + "-" + type;
finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] - 1 : -1; finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] - 1 : -1;
}) })
); );
@ -279,7 +279,6 @@ async function waitForAllNetworkUpdateEvents() {
return false; return false;
} }
} }
return true; return true;
} }
info("Wait for completion of all NetworkUpdateEvents packets..."); info("Wait for completion of all NetworkUpdateEvents packets...");
@ -331,23 +330,7 @@ function initNetMonitor(url, { requestCount, enableCache = false }) {
const markersDone = waitForTimelineMarkers(monitor); const markersDone = waitForTimelineMarkers(monitor);
await toggleCache(target, true); await toggleCache(target, true);
await Promise.all([requestsDone, markersDone]); await Promise.all([requestsDone, markersDone]);
info(
"Cache disabled when the current and all future toolboxes are open."
);
const consoleFront = await target.getFront("console");
// Remove any requests generated by the reload while toggling the cache to
// avoid interfering with the test.
isnot(
[...consoleFront.getNetworkEvents()].length,
0,
"Request to reconfigure the tab was recorded."
);
info("Clearing requests in the console client.");
await consoleFront.clearNetworkRequests();
info("Clearing requests in the UI."); info("Clearing requests in the UI.");
store.dispatch(Actions.clearRequests()); store.dispatch(Actions.clearRequests());
} }
@ -397,50 +380,31 @@ function isFiltering(monitor) {
function waitForNetworkEvents(monitor, getRequests) { function waitForNetworkEvents(monitor, getRequests) {
return new Promise(resolve => { return new Promise(resolve => {
const panel = monitor.panelWin; const panel = monitor.panelWin;
const { getNetworkRequest } = panel.connector;
let networkEvent = 0; let networkEvent = 0;
let nonBlockedNetworkEvent = 0; let nonBlockedNetworkEvent = 0;
let payloadReady = 0; let payloadReady = 0;
let eventTimings = 0; let eventTimings = 0;
const filtering = isFiltering(monitor); const filtering = isFiltering(monitor);
function onNetworkEvent(actor) { function onNetworkEvent(resource) {
const networkInfo = getNetworkRequest(actor);
if (!networkInfo) {
// Must have been related to reloading document to disable cache.
// Ignore the event.
return;
}
networkEvent++; networkEvent++;
if (!networkInfo.blockedReason) { if (!resource.blockedReason) {
nonBlockedNetworkEvent++; nonBlockedNetworkEvent++;
} }
maybeResolve(TEST_EVENTS.NETWORK_EVENT, actor, networkInfo); maybeResolve(TEST_EVENTS.NETWORK_EVENT, resource.actor);
} }
function onPayloadReady(actor) { function onPayloadReady(resource) {
const networkInfo = getNetworkRequest(actor);
if (!networkInfo) {
// Must have been related to reloading document to disable cache.
// Ignore the event.
return;
}
payloadReady++; payloadReady++;
maybeResolve(EVENTS.PAYLOAD_READY, actor, networkInfo); maybeResolve(EVENTS.PAYLOAD_READY, resource.actor);
} }
function onEventTimings(actor) { function onEventTimings(response) {
const networkInfo = getNetworkRequest(actor);
if (!networkInfo) {
// Must have been related to reloading document to disable cache.
// Ignore the event.
return;
}
eventTimings++; eventTimings++;
maybeResolve(EVENTS.RECEIVED_EVENT_TIMINGS, actor, networkInfo); maybeResolve(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
} }
function maybeResolve(event, actor, networkInfo) { function maybeResolve(event, actor) {
info( info(
"> Network event progress: " + "> Network event progress: " +
"NetworkEvent: " + "NetworkEvent: " +

Просмотреть файл

@ -146,7 +146,7 @@ function messageRemove(id) {
}; };
} }
function networkMessageUpdate(packet, idGenerator = null, response) { function networkMessageUpdate(packet, idGenerator = null) {
if (idGenerator == null) { if (idGenerator == null) {
idGenerator = defaultIdGenerator; idGenerator = defaultIdGenerator;
} }
@ -156,7 +156,6 @@ function networkMessageUpdate(packet, idGenerator = null, response) {
return { return {
type: NETWORK_MESSAGE_UPDATE, type: NETWORK_MESSAGE_UPDATE,
message, message,
response,
}; };
} }

Просмотреть файл

@ -175,7 +175,6 @@ function NetworkEventMessage({
return serviceContainer.getLongString(grip); return serviceContainer.getLongString(grip);
}, },
getTabTarget: () => {}, getTabTarget: () => {},
getNetworkRequest: () => {},
sendHTTPRequest: () => {}, sendHTTPRequest: () => {},
setPreferences: () => {}, setPreferences: () => {},
triggerActivity: () => {}, triggerActivity: () => {},

Просмотреть файл

@ -17,7 +17,6 @@ function enableMessagesCacheClearing(webConsoleUI) {
state = reducer(state, action); state = reducer(state, action);
if (webConsoleUI && action.type === MESSAGES_CLEAR) { if (webConsoleUI && action.type === MESSAGES_CLEAR) {
webConsoleUI.clearNetworkRequests();
webConsoleUI.clearMessagesCache(); webConsoleUI.clearMessagesCache();
} }
return state; return state;

Просмотреть файл

@ -50,12 +50,9 @@ function enableNetProvider(webConsoleUI) {
const updates = getAllNetworkMessagesUpdateById(newState); const updates = getAllNetworkMessagesUpdateById(newState);
const message = updates[action.id]; const message = updates[action.id];
if (message && !message.openedOnce && message.source == "network") { if (message && !message.openedOnce && message.source == "network") {
dataProvider.onNetworkEvent(message); dataProvider.onNetworkResourceAvailable(message);
message.updates.forEach(updateType => { message.updates.forEach(updateType => {
dataProvider.onNetworkEventUpdate({ dataProvider.onNetworkResourceUpdated({ ...message, updateType });
packet: { updateType: updateType },
networkInfo: message,
});
}); });
} }
} }
@ -64,18 +61,15 @@ function enableNetProvider(webConsoleUI) {
// Network event update packets are sent in batches from: // Network event update packets are sent in batches from:
// `WebConsoleOutputWrapper.dispatchMessageUpdate` using // `WebConsoleOutputWrapper.dispatchMessageUpdate` using
// NETWORK_MESSAGE_UPDATE action. // NETWORK_MESSAGE_UPDATE action.
// Make sure to call `dataProvider.onNetworkEventUpdate` // Make sure to call `dataProvider.onNetworkResourceUpdated`
// to fetch data from the backend. // to fetch data from the backend.
if (type == NETWORK_MESSAGE_UPDATE) { if (type == NETWORK_MESSAGE_UPDATE) {
const { actor } = action.response.networkInfo; const { actor } = action.message;
const open = getAllMessagesUiById(state).includes(actor); const open = getAllMessagesUiById(state).includes(actor);
if (open) { if (open) {
const message = getMessage(state, actor); const message = getMessage(state, actor);
message.updates.forEach(updateType => { message.updates.forEach(updateType => {
dataProvider.onNetworkEventUpdate({ dataProvider.onNetworkResourceUpdated({ ...message, updateType });
packet: { updateType },
networkInfo: message,
});
}); });
} }
} }

Просмотреть файл

@ -60,15 +60,18 @@ add_task(async function task() {
async function testNetmonitor(toolbox) { async function testNetmonitor(toolbox) {
const monitor = toolbox.getCurrentPanel(); const monitor = toolbox.getCurrentPanel();
const { store, windowRequire } = monitor.panelWin; const { document, store, windowRequire } = monitor.panelWin;
const Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
const { getSortedRequests } = windowRequire( const { getSortedRequests } = windowRequire(
"devtools/client/netmonitor/src/selectors/index" "devtools/client/netmonitor/src/selectors/index"
); );
store.dispatch(Actions.batchEnable(false)); store.dispatch(Actions.batchEnable(false));
const requestItem = document.querySelector(".request-list-item");
await waitUntil(() => store.getState().requests.requests.length > 0); await waitUntil(() => store.getState().requests.requests.length > 0);
// Lets also wait until all the event timings data requested
// has completed and the column is rendered.
await waitForDOM(requestItem, ".requests-list-timings-total");
is( is(
store.getState().requests.requests.length, store.getState().requests.requests.length,

Просмотреть файл

@ -4,6 +4,7 @@
"use strict"; "use strict";
const { const {
createResourceWatcherForTab,
STUBS_UPDATE_ENV, STUBS_UPDATE_ENV,
getStubFile, getStubFile,
getCleanedPacket, getCleanedPacket,
@ -62,31 +63,52 @@ add_task(async function() {
}); });
async function generateNetworkEventStubs() { async function generateNetworkEventStubs() {
const packets = new Map(); const stubs = new Map();
const toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole"); const tab = await addTab(TEST_URI);
const { ui } = toolbox.getCurrentPanel().hud; const resourceWatcher = await createResourceWatcherForTab(tab);
let addNetworkStub = function() {};
let addNetworkUpdateStub = function() {};
const onAvailable = resource => {
addNetworkStub(resource);
};
const onUpdated = resource => {
addNetworkUpdateStub(resource);
};
await resourceWatcher.watchResources([resourceWatcher.TYPES.NETWORK_EVENT], {
onAvailable,
onUpdated,
});
for (const [key, code] of getCommands()) { for (const [key, code] of getCommands()) {
const consoleFront = await toolbox.target.getFront("console"); const noExpectedUpdates = 7;
const onNetwork = consoleFront.once("networkEvent", packet => { const networkEventDone = new Promise(resolve => {
packets.set(key, getCleanedPacket(key, packet)); addNetworkStub = ({ resourceType, targetFront, resource }) => {
}); stubs.set(key, getCleanedPacket(key, getOrderedResource(resource)));
resolve();
const onNetworkUpdate = ui.once("network-message-updated", res => { };
const updateKey = `${key} update`; });
// We cannot ensure the form of the network update packet, some properties const networkEventUpdateDone = new Promise(resolve => {
// might be in another order than in the original packet. let updateCount = 0;
// Hand-picking only what we need should prevent this. addNetworkUpdateStub = ({ resourceType, targetFront, resource }) => {
const packet = { const updateKey = `${key} update`;
networkInfo: { // make sure all the updates have been happened
type: res.networkInfo.type, if (updateCount >= noExpectedUpdates) {
actor: res.networkInfo.actor, stubs.set(
request: res.networkInfo.request, updateKey,
response: res.networkInfo.response, // We cannot ensure the form of the resource, some properties
totalTime: res.networkInfo.totalTime, // might be in another order than in the original resource.
}, // Hand-picking only what we need should prevent this.
getCleanedPacket(updateKey, getOrderedResource(resource))
);
resolve();
} else {
updateCount++;
}
}; };
packets.set(updateKey, getCleanedPacket(updateKey, packet));
}); });
await SpecialPowers.spawn(gBrowser.selectedBrowser, [code], function( await SpecialPowers.spawn(gBrowser.selectedBrowser, [code], function(
@ -100,12 +122,42 @@ async function generateNetworkEventStubs() {
content.wrappedJSObject.triggerPacket(); content.wrappedJSObject.triggerPacket();
script.remove(); script.remove();
}); });
await Promise.all([networkEventDone, networkEventUpdateDone]);
await Promise.all([onNetwork, onNetworkUpdate]);
} }
resourceWatcher.unwatchResources([resourceWatcher.TYPES.NETWORK_EVENT], {
await closeTabAndToolbox(); onAvailable,
return packets; onUpdated,
});
return stubs;
}
// Ensures the order of the resource properties
function getOrderedResource(resource) {
return {
resourceType: resource.resourceType,
_type: resource._type,
timeStamp: resource.timeStamp,
node: resource.node,
actor: resource.actor,
discardRequestBody: resource.discardRequestBody,
discardResponseBody: resource.discardResponseBody,
startedDateTime: resource.startedDateTime,
request: resource.request,
isXHR: resource.isXHR,
cause: resource.cause,
response: resource.response,
timings: resource.timings,
private: resource.private,
fromCache: resource.fromCache,
fromServiceWorker: resource.fromServiceWorker,
isThirdPartyTrackingResource: resource.isThirdPartyTrackingResource,
referrerPolicy: resource.referrerPolicy,
blockedReason: resource.blockedReason,
channelId: resource.channelId,
updates: resource.updates,
updateType: resource.updateType,
totalTime: resource.totalTime,
securityState: resource.securityState,
};
} }
function getCommands() { function getCommands() {

Просмотреть файл

@ -209,8 +209,8 @@ function getCleanedPacket(key, packet) {
if (res.eventActor) { if (res.eventActor) {
// Clean actor ids and startedDateTime on network messages. // Clean actor ids and startedDateTime on network messages.
res.eventActor.actor = existingPacket.eventActor.actor; res.eventActor.actor = existingPacket.actor;
res.eventActor.startedDateTime = existingPacket.eventActor.startedDateTime; res.eventActor.startedDateTime = existingPacket.startedDateTime;
} }
if (res.pageError) { if (res.pageError) {
@ -288,54 +288,37 @@ function getCleanedPacket(key, packet) {
res.packet = Object.assign({}, res.packet, override); res.packet = Object.assign({}, res.packet, override);
} }
if (res.networkInfo) { if (res.startedDateTime) {
if (res.networkInfo.startedDateTime) { res.startedDateTime = existingPacket.startedDateTime;
res.networkInfo.startedDateTime = }
existingPacket.networkInfo.startedDateTime;
}
if (res.networkInfo.totalTime) { if (res.totalTime && existingPacket.totalTime) {
res.networkInfo.totalTime = existingPacket.networkInfo.totalTime; res.totalTime = existingPacket.totalTime;
} }
if (res.networkInfo.actor) { if (res.actor && existingPacket.actor) {
res.networkInfo.actor = existingPacket.networkInfo.actor; res.actor = existingPacket.actor;
} }
if (res.networkInfo.request && res.networkInfo.request.headersSize) { if (res?.request?.headersSize && existingPacket?.request?.headersSize) {
res.networkInfo.request.headersSize = res.request.headersSize = existingPacket.request.headersSize;
existingPacket.networkInfo.request.headersSize; }
}
if ( if (res?.response?.headersSize && existingPacket?.response?.headersSize) {
res.networkInfo.response && res.response.headersSize = existingPacket.response.headersSize;
res.networkInfo.response.headersSize !== undefined }
) { if (res?.response?.bodySize && existingPacket?.response?.bodySize) {
res.networkInfo.response.headersSize = res.response.bodySize = existingPacket.response.bodySize;
existingPacket.networkInfo.response.headersSize; }
} if (
if ( res?.response?.transferredSize &&
res.networkInfo.response && existingPacket?.response?.transferredSize
res.networkInfo.response.bodySize !== undefined ) {
) { res.response.transferredSize = existingPacket.response.transferredSize;
res.networkInfo.response.bodySize = }
existingPacket.networkInfo.response.bodySize;
}
if (
res.networkInfo.response &&
res.networkInfo.response.transferredSize !== undefined
) {
res.networkInfo.response.transferredSize =
existingPacket.networkInfo.response.transferredSize;
}
if ( if (res?.response?.waitingTime && existingPacket?.response?.waitingTime) {
res.networkInfo.response && res.response.waitingTime = existingPacket.response.waitingTime;
res.networkInfo.response.waitingTime !== undefined
) {
res.networkInfo.response.waitingTime =
existingPacket.networkInfo.response.waitingTime;
}
} }
if (res.updates && Array.isArray(res.updates)) { if (res.updates && Array.isArray(res.updates)) {
@ -348,7 +331,6 @@ function getCleanedPacket(key, packet) {
existingPacket.helperResult.object existingPacket.helperResult.object
); );
} }
return res; return res;
} }
@ -356,11 +338,12 @@ function cleanTimeStamp(packet) {
// We want to have the same timestamp for every stub, so they won't be re-sorted when // We want to have the same timestamp for every stub, so they won't be re-sorted when
// adding them to the store. // adding them to the store.
const uniqueTimeStamp = 1572867483805; const uniqueTimeStamp = 1572867483805;
// lowercased timestamp
if (packet.timestamp) { if (packet.timestamp) {
packet.timestamp = uniqueTimeStamp; packet.timestamp = uniqueTimeStamp;
} }
// camelcased timestamp
if (packet.timeStamp) { if (packet.timeStamp) {
packet.timeStamp = uniqueTimeStamp; packet.timeStamp = uniqueTimeStamp;
} }
@ -392,10 +375,6 @@ function cleanTimeStamp(packet) {
if (packet?.pageError?.timeStamp) { if (packet?.pageError?.timeStamp) {
packet.pageError.timeStamp = uniqueTimeStamp; packet.pageError.timeStamp = uniqueTimeStamp;
} }
if (packet?.networkInfo?.timeStamp) {
packet.networkInfo.timeStamp = uniqueTimeStamp;
}
} }
function copyExistingActor(a, b) { function copyExistingActor(a, b) {
@ -462,9 +441,7 @@ const stubPackets = parsePacketsWithFronts(rawPackets);
const stubPreparedMessages = new Map(); const stubPreparedMessages = new Map();
for (const [key, packet] of Array.from(stubPackets.entries())) { for (const [key, packet] of Array.from(stubPackets.entries())) {
const transformedPacket = prepareMessage(${ const transformedPacket = prepareMessage(${"packet"}, {
isNetworkMessage ? "packet.networkInfo || packet" : "packet"
}, {
getNextId: () => "1", getNextId: () => "1",
}); });
const message = ${ const message = ${

Просмотреть файл

@ -68,8 +68,8 @@ describe("WebConsoleWrapper", () => {
getPrivatePacket("XHR POST request") getPrivatePacket("XHR POST request")
); );
const postId = Symbol(); const postId = "pid1";
const getId = Symbol(); const getId = "gid1";
// Add messages in the store to make sure that update to private requests are // Add messages in the store to make sure that update to private requests are
// removed from the queue. // removed from the queue.
@ -94,11 +94,11 @@ describe("WebConsoleWrapper", () => {
publicNetworkUpdate, publicNetworkUpdate,
{ {
...getPrivatePacket("XHR GET request update"), ...getPrivatePacket("XHR GET request update"),
networkInfo: { actor: getId }, actor: getId,
}, },
{ {
...getPrivatePacket("XHR POST request update"), ...getPrivatePacket("XHR POST request update"),
networkInfo: { actor: postId }, actor: postId,
} }
); );

Просмотреть файл

@ -19,7 +19,8 @@ const {
const rawPackets = new Map(); const rawPackets = new Map();
rawPackets.set(`GET request`, { rawPackets.set(`GET request`, {
"type": "networkEvent", "resourceType": "network-event",
"_type": "NetworkEvent",
"timeStamp": 1572867483805, "timeStamp": 1572867483805,
"node": null, "node": null,
"actor": "server0.conn0.netEvent4", "actor": "server0.conn0.netEvent4",
@ -29,20 +30,13 @@ rawPackets.set(`GET request`, {
"request": { "request": {
"url": "http://example.com/inexistent.html", "url": "http://example.com/inexistent.html",
"method": "GET", "method": "GET",
"headersSize": 396 "headersSize": 385
}, },
"isXHR": false, "isXHR": false,
"cause": { "cause": {
"type": "img", "type": "img",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html", "loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"lastFrame": { "stacktraceAvailable": false
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"lineNumber": 3,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
"stacktraceAvailable": true
}, },
"response": { "response": {
"httpVersion": "HTTP/1.1", "httpVersion": "HTTP/1.1",
@ -51,7 +45,6 @@ rawPackets.set(`GET request`, {
"headersSize": 160, "headersSize": 160,
"remoteAddress": "127.0.0.1", "remoteAddress": "127.0.0.1",
"remotePort": 8888, "remotePort": 8888,
"waitingTime": 2,
"content": { "content": {
"mimeType": "text/html; charset=utf-8" "mimeType": "text/html; charset=utf-8"
}, },
@ -59,57 +52,84 @@ rawPackets.set(`GET request`, {
"transferredSize": 578 "transferredSize": 578
}, },
"timings": {}, "timings": {},
"updates": [
"requestHeaders",
"requestCookies",
"responseStart",
"securityInfo",
"responseHeaders",
"responseCookies",
"eventTimings",
"responseContent"
],
"private": false, "private": false,
"isThirdPartyTrackingResource": false, "isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade", "referrerPolicy": "no-referrer-when-downgrade",
"channelId": 265845590720515 "channelId": 265845590720515,
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
]
}); });
rawPackets.set(`GET request update`, { rawPackets.set(`GET request update`, {
"networkInfo": { "resourceType": "network-event",
"type": "networkEvent", "_type": "NetworkEvent",
"actor": "server0.conn0.netEvent4", "timeStamp": 1572867483805,
"request": { "node": null,
"url": "http://example.com/inexistent.html", "actor": "server0.conn0.netEvent5",
"method": "GET", "discardRequestBody": true,
"headersSize": 396 "discardResponseBody": false,
"startedDateTime": "2020-07-07T14:41:14.572Z",
"request": {
"url": "http://example.com/inexistent.html",
"method": "GET",
"headersSize": 385
},
"isXHR": false,
"cause": {
"type": "img",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"stacktraceAvailable": false
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
}, },
"response": { "bodySize": 418,
"httpVersion": "HTTP/1.1", "transferredSize": 578
"status": "404", },
"statusText": "Not Found", "timings": {},
"headersSize": 160, "private": false,
"remoteAddress": "127.0.0.1", "isThirdPartyTrackingResource": false,
"remotePort": 8888, "referrerPolicy": "no-referrer-when-downgrade",
"waitingTime": 2, "channelId": 202499118071811,
"content": { "updates": [
"mimeType": "text/html; charset=utf-8" "eventTimings",
}, "requestCookies",
"bodySize": 418, "requestHeaders",
"transferredSize": 578 "responseContent",
}, "responseCookies",
"totalTime": 14 "responseHeaders",
} "responseStart",
"securityInfo"
],
"updateType": "responseContent",
"totalTime": 3,
"securityState": "insecure"
}); });
rawPackets.set(`XHR GET request`, { rawPackets.set(`XHR GET request`, {
"type": "networkEvent", "resourceType": "network-event",
"_type": "NetworkEvent",
"timeStamp": 1572867483805, "timeStamp": 1572867483805,
"node": null, "node": null,
"actor": "server0.conn0.netEvent20", "actor": "server0.conn0.netEvent21",
"discardRequestBody": true, "discardRequestBody": true,
"discardResponseBody": true, "discardResponseBody": true,
"startedDateTime": "2019-11-04T11:06:34.909Z", "startedDateTime": "2020-07-07T14:41:14.612Z",
"request": { "request": {
"url": "http://example.com/inexistent.html", "url": "http://example.com/inexistent.html",
"method": "GET", "method": "GET",
@ -135,7 +155,6 @@ rawPackets.set(`XHR GET request`, {
"headersSize": 160, "headersSize": 160,
"remoteAddress": "127.0.0.1", "remoteAddress": "127.0.0.1",
"remotePort": 8888, "remotePort": 8888,
"waitingTime": 1,
"content": { "content": {
"mimeType": "text/html; charset=utf-8" "mimeType": "text/html; charset=utf-8"
}, },
@ -143,61 +162,34 @@ rawPackets.set(`XHR GET request`, {
"transferredSize": 578 "transferredSize": 578
}, },
"timings": {}, "timings": {},
"updates": [
"requestHeaders",
"requestCookies",
"responseStart",
"securityInfo",
"responseHeaders",
"responseCookies",
"eventTimings",
"responseContent"
],
"private": false, "private": false,
"isThirdPartyTrackingResource": false, "isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade", "referrerPolicy": "no-referrer-when-downgrade",
"channelId": 265845590720516 "channelId": 202499118071812,
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
]
}); });
rawPackets.set(`XHR GET request update`, { rawPackets.set(`XHR GET request update`, {
"networkInfo": { "resourceType": "network-event",
"type": "networkEvent", "_type": "NetworkEvent",
"actor": "server0.conn0.netEvent20",
"request": {
"url": "http://example.com/inexistent.html",
"method": "GET",
"headersSize": 385
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"waitingTime": 1,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"bodySize": 418,
"transferredSize": 578
},
"totalTime": 0
}
});
rawPackets.set(`XHR POST request`, {
"type": "networkEvent",
"timeStamp": 1572867483805, "timeStamp": 1572867483805,
"node": null, "node": null,
"actor": "server0.conn0.netEvent36", "actor": "server0.conn0.netEvent20",
"discardRequestBody": true, "discardRequestBody": true,
"discardResponseBody": true, "discardResponseBody": false,
"startedDateTime": "2019-11-04T11:06:35.007Z",
"request": { "request": {
"url": "http://example.com/inexistent.html", "url": "http://example.com/inexistent.html",
"method": "POST", "method": "GET",
"headersSize": 433 "headersSize": 385
}, },
"isXHR": true, "isXHR": true,
"cause": { "cause": {
@ -219,7 +211,6 @@ rawPackets.set(`XHR POST request`, {
"headersSize": 160, "headersSize": 160,
"remoteAddress": "127.0.0.1", "remoteAddress": "127.0.0.1",
"remotePort": 8888, "remotePort": 8888,
"waitingTime": 1,
"content": { "content": {
"mimeType": "text/html; charset=utf-8" "mimeType": "text/html; charset=utf-8"
}, },
@ -227,47 +218,137 @@ rawPackets.set(`XHR POST request`, {
"transferredSize": 578 "transferredSize": 578
}, },
"timings": {}, "timings": {},
"updates": [
"requestHeaders",
"requestCookies",
"responseStart",
"securityInfo",
"responseHeaders",
"responseCookies",
"eventTimings",
"responseContent"
],
"private": false, "private": false,
"isThirdPartyTrackingResource": false, "isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade", "referrerPolicy": "no-referrer-when-downgrade",
"channelId": 265845590720517 "updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"updateType": "responseContent",
"totalTime": 1,
"securityState": "insecure"
});
rawPackets.set(`XHR POST request`, {
"resourceType": "network-event",
"_type": "NetworkEvent",
"timeStamp": 1572867483805,
"node": null,
"actor": "server0.conn0.netEvent36",
"discardRequestBody": true,
"discardResponseBody": true,
"startedDateTime": "2019-11-04T11:06:35.007Z",
"request": {
"url": "http://example.com/inexistent.html",
"method": "POST",
"headersSize": 385
},
"isXHR": true,
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"lineNumber": 4,
"columnNumber": 5,
"functionName": "triggerPacket",
"asyncCause": null
},
"stacktraceAvailable": true
},
"response": {
"httpVersion": "HTTP/1.1",
"status": "404",
"statusText": "Not Found",
"headersSize": 160,
"remoteAddress": "127.0.0.1",
"remotePort": 8888,
"content": {
"mimeType": "text/html; charset=utf-8"
},
"bodySize": 418,
"transferredSize": 578
},
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"channelId": 265845590720517,
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
]
}); });
rawPackets.set(`XHR POST request update`, { rawPackets.set(`XHR POST request update`, {
"networkInfo": { "resourceType": "network-event",
"type": "networkEvent", "_type": "NetworkEvent",
"actor": "server0.conn0.netEvent36", "timeStamp": 1572867483805,
"request": { "node": null,
"url": "http://example.com/inexistent.html", "actor": "server0.conn0.netEvent36",
"method": "POST", "discardRequestBody": true,
"headersSize": 433 "discardResponseBody": false,
"request": {
"url": "http://example.com/inexistent.html",
"method": "POST",
"headersSize": 385
},
"isXHR": true,
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"lastFrame": {
"filename": "http://example.com/browser/devtools/client/webconsole/test/browser/stub-generators/test-network-event.html",
"lineNumber": 4,
"columnNumber": 5,
"functionName": "triggerPacket",
"asyncCause": null
}, },
"response": { "stacktraceAvailable": true
"httpVersion": "HTTP/1.1", },
"status": "404", "response": {
"statusText": "Not Found", "httpVersion": "HTTP/1.1",
"headersSize": 160, "status": "404",
"remoteAddress": "127.0.0.1", "statusText": "Not Found",
"remotePort": 8888, "headersSize": 160,
"waitingTime": 1, "remoteAddress": "127.0.0.1",
"content": { "remotePort": 8888,
"mimeType": "text/html; charset=utf-8" "content": {
}, "mimeType": "text/html; charset=utf-8"
"bodySize": 418,
"transferredSize": 578
}, },
"totalTime": 0 "bodySize": 418,
} "transferredSize": 578
},
"timings": {},
"private": false,
"isThirdPartyTrackingResource": false,
"referrerPolicy": "no-referrer-when-downgrade",
"updates": [
"eventTimings",
"requestCookies",
"requestHeaders",
"responseContent",
"responseCookies",
"responseHeaders",
"responseStart",
"securityInfo"
],
"updateType": "responseContent",
"totalTime": 2,
"securityState": "insecure"
}); });
@ -275,7 +356,7 @@ const stubPackets = parsePacketsWithFronts(rawPackets);
const stubPreparedMessages = new Map(); const stubPreparedMessages = new Map();
for (const [key, packet] of Array.from(stubPackets.entries())) { for (const [key, packet] of Array.from(stubPackets.entries())) {
const transformedPacket = prepareMessage(packet.networkInfo || packet, { const transformedPacket = prepareMessage(packet, {
getNextId: () => "1", getNextId: () => "1",
}); });
const message = NetworkEventMessage(transformedPacket); const message = NetworkEventMessage(transformedPacket);

Просмотреть файл

@ -881,15 +881,9 @@ describe("Message reducer:", () => {
let updatePacket = clonePacket(stubPackets.get("GET request update")); let updatePacket = clonePacket(stubPackets.get("GET request update"));
packet.actor = "message1"; packet.actor = "message1";
updatePacket.networkInfo.actor = "message1"; updatePacket.actor = "message1";
dispatch(actions.messagesAdd([packet])); dispatch(actions.messagesAdd([packet]));
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(
updatePacket.networkInfo,
null,
updatePacket
)
);
let networkUpdates = getAllNetworkMessagesUpdateById(getState()); let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates)).toEqual(["message1"]); expect(Object.keys(networkUpdates)).toEqual(["message1"]);
@ -897,15 +891,9 @@ describe("Message reducer:", () => {
packet = clonePacket(stubPackets.get("GET request")); packet = clonePacket(stubPackets.get("GET request"));
updatePacket = stubPackets.get("XHR GET request update"); updatePacket = stubPackets.get("XHR GET request update");
packet.actor = "message2"; packet.actor = "message2";
updatePacket.networkInfo.actor = "message2"; updatePacket.actor = "message2";
dispatch(actions.messagesAdd([packet])); dispatch(actions.messagesAdd([packet]));
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(
updatePacket.networkInfo,
null,
updatePacket
)
);
networkUpdates = getAllNetworkMessagesUpdateById(getState()); networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates)).toEqual(["message1", "message2"]); expect(Object.keys(networkUpdates)).toEqual(["message1", "message2"]);
@ -915,13 +903,7 @@ describe("Message reducer:", () => {
const { dispatch, getState } = setupStore(["XHR GET request"]); const { dispatch, getState } = setupStore(["XHR GET request"]);
const updatePacket = stubPackets.get("XHR GET request update"); const updatePacket = stubPackets.get("XHR GET request update");
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(
updatePacket.networkInfo,
null,
updatePacket
)
);
let networkUpdates = getAllNetworkMessagesUpdateById(getState()); let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(Object.keys(networkUpdates).length > 0).toBe(true); expect(Object.keys(networkUpdates).length > 0).toBe(true);
@ -945,37 +927,19 @@ describe("Message reducer:", () => {
stubPackets.get("XHR GET request update") stubPackets.get("XHR GET request update")
); );
packet.actor = "message1"; packet.actor = "message1";
updatePacket.networkInfo.actor = "message1"; updatePacket.actor = "message1";
dispatch(actions.messagesAdd([packet])); dispatch(actions.messagesAdd([packet]));
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(
updatePacket.networkInfo,
null,
updatePacket
)
);
packet.actor = "message2"; packet.actor = "message2";
updatePacket.networkInfo.actor = "message2"; updatePacket.actor = "message2";
dispatch(actions.messagesAdd([packet])); dispatch(actions.messagesAdd([packet]));
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(
updatePacket.networkInfo,
null,
updatePacket
)
);
packet.actor = "message3"; packet.actor = "message3";
updatePacket.networkInfo.actor = "message3"; updatePacket.actor = "message3";
dispatch(actions.messagesAdd([packet])); dispatch(actions.messagesAdd([packet]));
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(
updatePacket.networkInfo,
null,
updatePacket
)
);
// Check that we have the expected data. // Check that we have the expected data.
const messages = getAllMessagesById(getState()); const messages = getAllMessagesById(getState());

Просмотреть файл

@ -35,11 +35,9 @@ describe("Network message reducer:", () => {
const updatePacket = clonePacket(stubPackets.get("GET request update")); const updatePacket = clonePacket(stubPackets.get("GET request update"));
packet.actor = "message1"; packet.actor = "message1";
updatePacket.networkInfo.actor = "message1"; updatePacket.actor = "message1";
dispatch(actions.messagesAdd([packet])); dispatch(actions.messagesAdd([packet]));
dispatch( dispatch(actions.networkMessageUpdate(updatePacket, null));
actions.networkMessageUpdate(updatePacket.networkInfo, null, updatePacket)
);
}); });
describe("networkMessagesUpdateById", () => { describe("networkMessagesUpdateById", () => {

Просмотреть файл

@ -115,7 +115,7 @@ function transformResource(resource) {
return transformCSSMessageResource(resource); return transformCSSMessageResource(resource);
} }
case "networkEvent": { case ResourceWatcher.TYPES.NETWORK_EVENT: {
return transformNetworkEventResource(resource); return transformNetworkEventResource(resource);
} }

Просмотреть файл

@ -34,11 +34,8 @@ class WebConsoleConnectionProxy {
this.webConsoleUI = webConsoleUI; this.webConsoleUI = webConsoleUI;
this.target = target; this.target = target;
this.needContentProcessMessagesListener = needContentProcessMessagesListener; this.needContentProcessMessagesListener = needContentProcessMessagesListener;
this._connecter = null; this._connecter = null;
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this._onTabNavigated = this._onTabNavigated.bind(this); this._onTabNavigated = this._onTabNavigated.bind(this);
this._onTabWillNavigate = this._onTabWillNavigate.bind(this); this._onTabWillNavigate = this._onTabWillNavigate.bind(this);
this._onLastPrivateContextExited = this._onLastPrivateContextExited.bind( this._onLastPrivateContextExited = this._onLastPrivateContextExited.bind(
@ -81,9 +78,6 @@ class WebConsoleConnectionProxy {
); );
await this.webConsoleUI.setSaveRequestAndResponseBodies(saveBodies); await this.webConsoleUI.setSaveRequestAndResponseBodies(saveBodies);
const networkMessages = this._getNetworkMessages();
this.dispatchMessagesAdd(networkMessages);
this._addWebConsoleFrontEventListeners(); this._addWebConsoleFrontEventListeners();
if (this.webConsoleFront && !this.webConsoleFront.hasNativeConsoleAPI) { if (this.webConsoleFront && !this.webConsoleFront.hasNativeConsoleAPI) {
@ -122,19 +116,15 @@ class WebConsoleConnectionProxy {
* @returns Promise * @returns Promise
*/ */
_attachConsole() { _attachConsole() {
if (!this.webConsoleFront) { if (!this.webConsoleFront || !this.needContentProcessMessagesListener) {
return null; return null;
} }
const listeners = ["NetworkActivity"];
// Enable the forwarding of console messages to the parent process // Enable the forwarding of console messages to the parent process
// when we open the Browser Console or Toolbox without fission support. If Fission // when we open the Browser Console or Toolbox without fission support. If Fission
// is enabled, we don't use the ContentProcessMessages listener, but attach to the // is enabled, we don't use the ContentProcessMessages listener, but attach to the
// content processes directly. // content processes directly.
if (this.needContentProcessMessagesListener) { return this.webConsoleFront.startListeners(["ContentProcessMessages"]);
listeners.push("ContentProcessMessages");
}
return this.webConsoleFront.startListeners(listeners);
} }
/** /**
@ -147,8 +137,6 @@ class WebConsoleConnectionProxy {
return; return;
} }
this.webConsoleFront.on("networkEvent", this._onNetworkEvent);
this.webConsoleFront.on("networkEventUpdate", this._onNetworkEventUpdate);
this.webConsoleFront.on( this.webConsoleFront.on(
"lastPrivateContextExited", "lastPrivateContextExited",
this._onLastPrivateContextExited this._onLastPrivateContextExited
@ -165,8 +153,6 @@ class WebConsoleConnectionProxy {
* @private * @private
*/ */
_removeWebConsoleFrontEventListeners() { _removeWebConsoleFrontEventListeners() {
this.webConsoleFront.off("networkEvent", this._onNetworkEvent);
this.webConsoleFront.off("networkEventUpdate", this._onNetworkEventUpdate);
this.webConsoleFront.off( this.webConsoleFront.off(
"lastPrivateContextExited", "lastPrivateContextExited",
this._onLastPrivateContextExited this._onLastPrivateContextExited
@ -177,51 +163,11 @@ class WebConsoleConnectionProxy {
); );
} }
/**
* Get network messages from the server.
*
* @private
* @returns An array of network messages.
*/
_getNetworkMessages() {
if (!this.webConsoleFront) {
return [];
}
return Array.from(this.webConsoleFront.getNetworkEvents());
}
_clearLogpointMessages(logpointId) { _clearLogpointMessages(logpointId) {
// Some message might try to update while we are closing the toolbox. // Some message might try to update while we are closing the toolbox.
if (!this.webConsoleUI?.wrapper) { if (this.webConsoleUI?.wrapper) {
return; this.webConsoleUI.wrapper.dispatchClearLogpointMessages(logpointId);
} }
this.webConsoleUI.wrapper.dispatchClearLogpointMessages(logpointId);
}
/**
* The "networkEvent" message type handler. We redirect any message to
* the UI for displaying.
*
* @private
* @param object networkInfo
* The network request information.
*/
_onNetworkEvent(networkInfo) {
this.dispatchMessageAdd(networkInfo);
}
/**
* The "networkEventUpdate" message type handler. We redirect any message to
* the UI for displaying.
*
* @private
* @param object response
* The update response received from the server.
*/
_onNetworkEventUpdate(response) {
this.dispatchMessageUpdate(response.networkInfo, response);
} }
/** /**
@ -270,39 +216,6 @@ class WebConsoleConnectionProxy {
this.webConsoleUI.handleTabWillNavigate(packet); this.webConsoleUI.handleTabWillNavigate(packet);
} }
/**
* Dispatch a message add on the new frontend and emit an event for tests.
*/
dispatchMessageAdd(packet) {
// Some message might try to update while we are closing the toolbox.
if (!this.webConsoleUI?.wrapper) {
return;
}
this.webConsoleUI.wrapper.dispatchMessageAdd(packet);
}
/**
* Batched dispatch of messages.
*/
dispatchMessagesAdd(packets) {
// Some message might try to update while we are closing the toolbox.
if (!this.webConsoleUI?.wrapper) {
return;
}
this.webConsoleUI.wrapper.dispatchMessagesAdd(packets);
}
/**
* Dispatch a message event on the new frontend and emit an event for tests.
*/
dispatchMessageUpdate(networkInfo, response) {
// Some message might try to update while we are closing the toolbox.
if (!this.webConsoleUI?.wrapper) {
return;
}
this.webConsoleUI.wrapper.dispatchMessageUpdate(networkInfo, response);
}
/** /**
* Disconnect the Web Console from the remote server. * Disconnect the Web Console from the remote server.
* *

Просмотреть файл

@ -79,6 +79,7 @@ class WebConsoleUI {
this._onTargetAvailable = this._onTargetAvailable.bind(this); this._onTargetAvailable = this._onTargetAvailable.bind(this);
this._onTargetDestroyed = this._onTargetDestroyed.bind(this); this._onTargetDestroyed = this._onTargetDestroyed.bind(this);
this._onResourceAvailable = this._onResourceAvailable.bind(this); this._onResourceAvailable = this._onResourceAvailable.bind(this);
this._onResourceUpdated = this._onResourceUpdated.bind(this);
EventEmitter.decorate(this); EventEmitter.decorate(this);
} }
@ -235,19 +236,13 @@ class WebConsoleUI {
if (this.wrapper) { if (this.wrapper) {
this.wrapper.dispatchMessagesClear(); this.wrapper.dispatchMessagesClear();
} }
this.clearNetworkRequests();
if (clearStorage) { if (clearStorage) {
this.clearMessagesCache(); this.clearMessagesCache();
} }
this.emitForTests("messages-cleared"); this.emitForTests("messages-cleared");
} }
clearNetworkRequests() {
for (const proxy of this.getAllProxies()) {
proxy.webConsoleFront.clearNetworkRequests();
}
}
clearMessagesCache() { clearMessagesCache() {
for (const proxy of this.getAllProxies()) { for (const proxy of this.getAllProxies()) {
proxy.webConsoleFront.clearMessagesCache(); proxy.webConsoleFront.clearMessagesCache();
@ -342,8 +337,12 @@ class WebConsoleUI {
resourceWatcher.TYPES.CONSOLE_MESSAGE, resourceWatcher.TYPES.CONSOLE_MESSAGE,
resourceWatcher.TYPES.ERROR_MESSAGE, resourceWatcher.TYPES.ERROR_MESSAGE,
resourceWatcher.TYPES.PLATFORM_MESSAGE, resourceWatcher.TYPES.PLATFORM_MESSAGE,
resourceWatcher.TYPES.NETWORK_EVENT,
], ],
{ onAvailable: this._onResourceAvailable } {
onAvailable: this._onResourceAvailable,
onUpdated: this._onResourceUpdated,
}
); );
} }
@ -370,6 +369,12 @@ class WebConsoleUI {
this.wrapper.dispatchMessageAdd(resource); this.wrapper.dispatchMessageAdd(resource);
} }
_onResourceUpdated({ resourceType, targetFront, resource }) {
if (resourceType == this.hud.resourceWatcher.TYPES.NETWORK_EVENT) {
this.wrapper.dispatchMessageUpdate(resource);
}
}
/** /**
* Called any time a new target is available. * Called any time a new target is available.
* i.e. it was already existing or has just been created. * i.e. it was already existing or has just been created.

Просмотреть файл

@ -168,9 +168,7 @@ class WebConsoleWrapper {
// since we can receive updates even if the message isn't rendered yet. // since we can receive updates even if the message isn't rendered yet.
const messages = [...getAllMessagesById(store.getState()).values()]; const messages = [...getAllMessagesById(store.getState()).values()];
this.queuedMessageUpdates = this.queuedMessageUpdates.filter( this.queuedMessageUpdates = this.queuedMessageUpdates.filter(
({ networkInfo }) => { ({ actor }) => {
const { actor } = networkInfo;
const queuedNetworkMessage = this.queuedMessageAdds.find( const queuedNetworkMessage = this.queuedMessageAdds.find(
p => p.actor === actor p => p.actor === actor
); );
@ -210,24 +208,24 @@ class WebConsoleWrapper {
store.dispatch(actions.privateMessagesClear()); store.dispatch(actions.privateMessagesClear());
} }
dispatchMessageUpdate(message, res) { dispatchMessageUpdate(message) {
// network-message-updated will emit when all the update message arrives. // network-message-updated will emit when all the update message arrives.
// Since we can't ensure the order of the network update, we check // Since we can't ensure the order of the network update, we check
// that networkInfo.updates has all we need. // that message.updates has all we need.
// Note that 'requestPostData' is sent only for POST requests, so we need // Note that 'requestPostData' is sent only for POST requests, so we need
// to count with that. // to count with that.
const NUMBER_OF_NETWORK_UPDATE = 8; const NUMBER_OF_NETWORK_UPDATE = 8;
let expectedLength = NUMBER_OF_NETWORK_UPDATE; let expectedLength = NUMBER_OF_NETWORK_UPDATE;
if (res.networkInfo.updates.includes("responseCache")) { if (message.updates.includes("responseCache")) {
expectedLength++; expectedLength++;
} }
if (res.networkInfo.updates.includes("requestPostData")) { if (message.updates.includes("requestPostData")) {
expectedLength++; expectedLength++;
} }
if (res.networkInfo.updates.length === expectedLength) { if (message.updates.length === expectedLength) {
this.batchedMessageUpdates({ res, message }); this.batchedMessageUpdates(message);
} }
} }
@ -257,7 +255,6 @@ class WebConsoleWrapper {
packet.timeStamp = Date.now(); packet.timeStamp = Date.now();
this.dispatchMessageAdd(packet); this.dispatchMessageAdd(packet);
} else { } else {
this.webConsoleUI.clearNetworkRequests();
this.dispatchMessagesClear(); this.dispatchMessagesClear();
store.dispatch({ store.dispatch({
type: Constants.WILL_NAVIGATE, type: Constants.WILL_NAVIGATE,
@ -265,8 +262,8 @@ class WebConsoleWrapper {
} }
} }
batchedMessageUpdates(info) { batchedMessageUpdates(message) {
this.queuedMessageUpdates.push(info); this.queuedMessageUpdates.push(message);
this.setTimeoutIfNeeded(); this.setTimeoutIfNeeded();
} }
@ -345,11 +342,9 @@ class WebConsoleWrapper {
this.queuedMessageAdds = []; this.queuedMessageAdds = [];
if (this.queuedMessageUpdates.length > 0) { if (this.queuedMessageUpdates.length > 0) {
for (const { message, res } of this.queuedMessageUpdates) { for (const message of this.queuedMessageUpdates) {
await store.dispatch( await store.dispatch(actions.networkMessageUpdate(message, null));
actions.networkMessageUpdate(message, null, res) this.webConsoleUI.emitForTests("network-message-updated", message);
);
this.webConsoleUI.emitForTests("network-message-updated", res);
} }
this.queuedMessageUpdates = []; this.queuedMessageUpdates = [];
} }

Просмотреть файл

@ -7,6 +7,7 @@ DevToolsModules(
'css-changes.js', 'css-changes.js',
'css-messages.js', 'css-messages.js',
'error-messages.js', 'error-messages.js',
'network-events.js',
'platform-messages.js', 'platform-messages.js',
'root-node.js', 'root-node.js',
'stylesheet.js', 'stylesheet.js',

Просмотреть файл

@ -0,0 +1,142 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
module.exports = async function({
targetList,
targetFront,
isFissionEnabledOnContentToolbox,
onAvailable,
onUpdated,
}) {
// Allow the top level target unconditionnally.
// Also allow frame, but only in content toolbox, when the fission/content toolbox pref is
// set. i.e. still ignore them in the content of the browser toolbox as we inspect
// messages via the process targets
// Also ignore workers as they are not supported yet. (see bug 1592584)
const isContentToolbox = targetList.targetFront.isLocalTab;
const listenForFrames = isContentToolbox && isFissionEnabledOnContentToolbox;
const isAllowed =
targetFront.isTopLevel ||
targetFront.targetType === targetList.TYPES.PROCESS ||
(targetFront.targetType === targetList.TYPES.FRAME && listenForFrames);
if (!isAllowed) {
return;
}
const webConsoleFront = await targetFront.getFront("console");
const _resources = new Map();
function onNetworkEvent(packet) {
const actor = packet.eventActor;
const resource = {
resourceType: ResourceWatcher.TYPES.NETWORK_EVENT,
_type: "NetworkEvent",
timeStamp: actor.timeStamp,
node: null,
actor: actor.actor,
discardRequestBody: true,
discardResponseBody: true,
startedDateTime: actor.startedDateTime,
request: {
url: actor.url,
method: actor.method,
},
isXHR: actor.isXHR,
cause: actor.cause,
response: {},
timings: {},
private: actor.private,
fromCache: actor.fromCache,
fromServiceWorker: actor.fromServiceWorker,
isThirdPartyTrackingResource: actor.isThirdPartyTrackingResource,
referrerPolicy: actor.referrerPolicy,
blockedReason: actor.blockedReason,
channelId: actor.channelId,
updates: [],
};
_resources.set(actor.actor, resource);
onAvailable([resource]);
}
function onNetworkEventUpdate(packet) {
const resource = _resources.get(packet.from);
if (!resource) {
return;
}
resource.updates.push(packet.updateType);
resource.updateType = packet.updateType;
switch (packet.updateType) {
case "requestHeaders":
resource.request.headersSize = packet.headersSize;
break;
case "requestPostData":
resource.discardRequestBody = packet.discardRequestBody;
resource.request.bodySize = packet.dataSize;
break;
case "responseStart":
resource.response.httpVersion = packet.response.httpVersion;
resource.response.status = packet.response.status;
resource.response.statusText = packet.response.statusText;
resource.response.headersSize = packet.response.headersSize;
resource.response.remoteAddress = packet.response.remoteAddress;
resource.response.remotePort = packet.response.remotePort;
resource.discardResponseBody = packet.response.discardResponseBody;
resource.response.content = {
mimeType: packet.response.mimeType,
};
break;
case "responseContent":
resource.response.content = {
mimeType: packet.mimeType,
};
resource.response.bodySize = packet.contentSize;
resource.response.transferredSize = packet.transferredSize;
resource.discardResponseBody = packet.discardResponseBody;
break;
case "eventTimings":
resource.totalTime = packet.totalTime;
break;
case "securityInfo":
resource.securityState = packet.state;
break;
case "responseCache":
resource.response.responseCache = packet.responseCache;
break;
}
onUpdated(resource);
if (resource.blockedReason) {
// Blocked requests
if (
resource.updates.includes("requestHeaders") &&
resource.updates.includes("requestCookies")
) {
_resources.delete(resource.actor);
}
} else if (
resource.updates.includes("requestHeaders") &&
resource.updates.includes("requestCookies") &&
resource.updates.includes("eventTimings") &&
resource.updates.includes("responseContent")
) {
_resources.delete(resource.actor);
}
}
webConsoleFront.on("serverNetworkEvent", onNetworkEvent);
webConsoleFront.on("serverNetworkUpdateEvent", onNetworkEventUpdate);
// Start listening to network events
webConsoleFront.startListeners(["NetworkActivity"]);
};

Просмотреть файл

@ -34,6 +34,7 @@ class ResourceWatcher {
this._onResourceDestroyed = this._onResourceDestroyed.bind(this); this._onResourceDestroyed = this._onResourceDestroyed.bind(this);
this._availableListeners = new EventEmitter(); this._availableListeners = new EventEmitter();
this._updatedListeners = new EventEmitter();
this._destroyedListeners = new EventEmitter(); this._destroyedListeners = new EventEmitter();
// Cache for all resources by the order that the resource was taken. // Cache for all resources by the order that the resource was taken.
@ -72,6 +73,7 @@ class ResourceWatcher {
async watchResources(resources, options) { async watchResources(resources, options) {
const { const {
onAvailable, onAvailable,
onUpdated,
onDestroyed, onDestroyed,
ignoreExistingResources = false, ignoreExistingResources = false,
} = options; } = options;
@ -105,7 +107,9 @@ class ResourceWatcher {
await this._startListening(resource); await this._startListening(resource);
} }
this._availableListeners.on(resource, onAvailable); this._availableListeners.on(resource, onAvailable);
if (onUpdated) {
this._updatedListeners.on(resource, onUpdated);
}
if (onDestroyed) { if (onDestroyed) {
this._destroyedListeners.on(resource, onDestroyed); this._destroyedListeners.on(resource, onDestroyed);
} }
@ -121,9 +125,12 @@ class ResourceWatcher {
* See `watchResources` for the arguments as both methods receive the same. * See `watchResources` for the arguments as both methods receive the same.
*/ */
unwatchResources(resources, options) { unwatchResources(resources, options) {
const { onAvailable, onDestroyed } = options; const { onAvailable, onUpdated, onDestroyed } = options;
for (const resource of resources) { for (const resource of resources) {
if (onUpdated) {
this._updatedListeners.off(resource, onUpdated);
}
if (onDestroyed) { if (onDestroyed) {
this._destroyedListeners.off(resource, onDestroyed); this._destroyedListeners.off(resource, onDestroyed);
} }
@ -295,6 +302,27 @@ class ResourceWatcher {
} }
} }
/**
* Method called either by:
* - the backward compatibility code (LegacyListeners)
* - target actors RDP events
* Called everytime a resource is updated in the remote target.
*
* @param {Front} targetFront
* The Target Front from which this resource comes from.
* @param {Array<json/Front>} resources
* Depending on the resource Type, it can be an Array composed of either JSON objects or Fronts,
* which describes the updated resource.
*/
_onResourceUpdated(targetFront, resource) {
const { resourceType } = resource;
this._updatedListeners.emit(resourceType, {
resourceType,
targetFront,
resource,
});
}
/** /**
* Called everytime a resource is destroyed in the remote target. * Called everytime a resource is destroyed in the remote target.
* See _onResourceAvailable for the argument description. * See _onResourceAvailable for the argument description.
@ -384,11 +412,13 @@ class ResourceWatcher {
*/ */
_watchResourcesForTarget(targetFront, resourceType) { _watchResourcesForTarget(targetFront, resourceType) {
const onAvailable = this._onResourceAvailable.bind(this, { targetFront }); const onAvailable = this._onResourceAvailable.bind(this, { targetFront });
const onUpdated = this._onResourceUpdated.bind(this, { targetFront });
return LegacyListeners[resourceType]({ return LegacyListeners[resourceType]({
targetList: this.targetList, targetList: this.targetList,
targetFront, targetFront,
isFissionEnabledOnContentToolbox: gDevTools.isFissionContentToolboxEnabled(), isFissionEnabledOnContentToolbox: gDevTools.isFissionContentToolboxEnabled(),
onAvailable, onAvailable,
onUpdated,
}); });
} }
@ -456,6 +486,7 @@ ResourceWatcher.TYPES = ResourceWatcher.prototype.TYPES = {
DOCUMENT_EVENT: "document-event", DOCUMENT_EVENT: "document-event",
ROOT_NODE: "root-node", ROOT_NODE: "root-node",
STYLESHEET: "stylesheet", STYLESHEET: "stylesheet",
NETWORK_EVENT: "network-event",
}; };
module.exports = { ResourceWatcher }; module.exports = { ResourceWatcher };
@ -494,6 +525,8 @@ const LegacyListeners = {
.ROOT_NODE]: require("devtools/shared/resources/legacy-listeners/root-node"), .ROOT_NODE]: require("devtools/shared/resources/legacy-listeners/root-node"),
[ResourceWatcher.TYPES [ResourceWatcher.TYPES
.STYLESHEET]: require("devtools/shared/resources/legacy-listeners/stylesheet"), .STYLESHEET]: require("devtools/shared/resources/legacy-listeners/stylesheet"),
[ResourceWatcher.TYPES
.NETWORK_EVENT]: require("devtools/shared/resources/legacy-listeners/network-events"),
}; };
// Optional transformers for each type of resource. // Optional transformers for each type of resource.