mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
e82936d001
Differential Revision: https://phabricator.services.mozilla.com/D5028 --HG-- extra : moz-landing-system : lando
221 lines
7.3 KiB
JavaScript
221 lines
7.3 KiB
JavaScript
/* 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 { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
|
|
const { networkMonitorSpec } = require("devtools/shared/specs/network-monitor");
|
|
|
|
loader.lazyRequireGetter(this, "NetworkObserver", "devtools/server/actors/network-monitor/network-observer", true);
|
|
loader.lazyRequireGetter(this, "NetworkEventActor", "devtools/server/actors/network-event", true);
|
|
|
|
const NetworkMonitorActor = ActorClassWithSpec(networkMonitorSpec, {
|
|
_netEvents: new Map(),
|
|
_networkEventActorsByURL: new Map(),
|
|
|
|
/**
|
|
* NetworkMonitorActor is instanciated from WebConsoleActor.startListeners
|
|
* Either in the same process, for debugging service worker requests or when debugging
|
|
* the parent process itself and tracking chrome requests.
|
|
* Or in another process, for tracking content requests that are actually done in the
|
|
* parent process.
|
|
*
|
|
* @param object filters
|
|
* Contains an `outerWindowID` attribute when this is used across processes.
|
|
* Or a `window` attribute when instanciated in the same process.
|
|
* @param number parentID (optional)
|
|
* To be removed, specify the ID of the Web console actor.
|
|
* This is used to fake emitting an event from it to prevent changing RDP
|
|
* behavior.
|
|
* @param nsIMessageManager messageManager
|
|
* This is the manager to use to communicate with the console actor. When both
|
|
* netmonitor and console actor runs in the same process, this is an instance
|
|
* of MockMessageManager instead of a real message manager.
|
|
*/
|
|
initialize(conn, filters, parentID, messageManager) {
|
|
Actor.prototype.initialize.call(this, conn);
|
|
|
|
this.parentID = parentID;
|
|
this.messageManager = messageManager;
|
|
|
|
// Immediately start watching for new request according to `filters`.
|
|
// NetworkMonitor will call `onNetworkEvent` method.
|
|
this.observer = new NetworkObserver(filters, this);
|
|
this.observer.init();
|
|
|
|
this.stackTraces = new Set();
|
|
|
|
this.onStackTraceAvailable = this.onStackTraceAvailable.bind(this);
|
|
this.onRequestContent = this.onRequestContent.bind(this);
|
|
this.onSetPreference = this.onSetPreference.bind(this);
|
|
this.onGetNetworkEventActor = this.onGetNetworkEventActor.bind(this);
|
|
this.onDestroyMessage = this.onDestroyMessage.bind(this);
|
|
|
|
this.startListening();
|
|
},
|
|
|
|
onDestroyMessage({ data }) {
|
|
if (data.actorID == this.parentID) {
|
|
this.destroy();
|
|
}
|
|
},
|
|
|
|
startListening() {
|
|
this.messageManager.addMessageListener("debug:request-stack-available",
|
|
this.onStackTraceAvailable);
|
|
this.messageManager.addMessageListener("debug:request-content:request",
|
|
this.onRequestContent);
|
|
this.messageManager.addMessageListener("debug:netmonitor-preference",
|
|
this.onSetPreference);
|
|
this.messageManager.addMessageListener("debug:get-network-event-actor:request",
|
|
this.onGetNetworkEventActor);
|
|
this.messageManager.addMessageListener("debug:destroy-network-monitor",
|
|
this.onDestroyMessage);
|
|
},
|
|
|
|
stopListening() {
|
|
this.messageManager.removeMessageListener("debug:request-stack-available",
|
|
this.onStackTraceAvailable);
|
|
this.messageManager.removeMessageListener("debug:request-content:request",
|
|
this.onRequestContent);
|
|
this.messageManager.removeMessageListener("debug:netmonitor-preference",
|
|
this.onSetPreference);
|
|
this.messageManager.removeMessageListener("debug:get-network-event-actor:request",
|
|
this.onGetNetworkEventActor);
|
|
this.messageManager.removeMessageListener("debug:destroy-network-monitor",
|
|
this.onDestroyMessage);
|
|
},
|
|
|
|
destroy() {
|
|
Actor.prototype.destroy.call(this);
|
|
|
|
if (this.observer) {
|
|
this.observer.destroy();
|
|
this.observer = null;
|
|
}
|
|
|
|
this.stackTraces.clear();
|
|
if (this.messageManager) {
|
|
this.stopListening();
|
|
this.messageManager = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* onBrowserSwap is called by the server when a browser frame swap occurs (typically
|
|
* switching on/off RDM) and a new message manager should be used.
|
|
*/
|
|
onBrowserSwap(mm) {
|
|
this.stopListening();
|
|
this.messageManager = mm;
|
|
this.stackTraces = new Set();
|
|
this.startListening();
|
|
},
|
|
|
|
onStackTraceAvailable(msg) {
|
|
const { channelId } = msg.data;
|
|
if (!msg.data.stacktrace) {
|
|
this.stackTraces.delete(channelId);
|
|
} else {
|
|
this.stackTraces.add(channelId);
|
|
}
|
|
},
|
|
|
|
getRequestContentForActor(actor) {
|
|
const content = actor._response.content;
|
|
if (actor._discardResponseBody || actor._truncated || !content || !content.size) {
|
|
// Do not return the stylesheet text if there is no meaningful content or if it's
|
|
// still loading. Let the caller handle it by doing its own separate request.
|
|
return null;
|
|
}
|
|
|
|
if (content.text.type != "longString") {
|
|
// For short strings, the text is available directly.
|
|
return {
|
|
content: content.text,
|
|
contentType: content.mimeType,
|
|
};
|
|
}
|
|
// For long strings, look up the actor that holds the full text.
|
|
const longStringActor = this.conn._getOrCreateActor(content.text.actor);
|
|
if (!longStringActor) {
|
|
return null;
|
|
}
|
|
return {
|
|
content: longStringActor.str,
|
|
contentType: content.mimeType,
|
|
};
|
|
},
|
|
|
|
onRequestContent(msg) {
|
|
const { url } = msg.data;
|
|
const actor = this._networkEventActorsByURL.get(url);
|
|
// Always reply with a message, but with a null `content` if this instance
|
|
// did not processed this request
|
|
const content = actor ? this.getRequestContentForActor(actor) : null;
|
|
this.messageManager.sendAsyncMessage("debug:request-content:response", {
|
|
url,
|
|
content,
|
|
});
|
|
},
|
|
|
|
onSetPreference({ data }) {
|
|
if ("saveRequestAndResponseBodies" in data) {
|
|
this.observer.saveRequestAndResponseBodies = data.saveRequestAndResponseBodies;
|
|
}
|
|
if ("throttleData" in data) {
|
|
this.observer.throttleData = data.throttleData;
|
|
}
|
|
},
|
|
|
|
onGetNetworkEventActor({ data }) {
|
|
const actor = this.getNetworkEventActor(data.channelId);
|
|
this.messageManager.sendAsyncMessage("debug:get-network-event-actor:response", {
|
|
channelId: data.channelId,
|
|
actor: actor.form()
|
|
});
|
|
},
|
|
|
|
getNetworkEventActor(channelId) {
|
|
let actor = this._netEvents.get(channelId);
|
|
if (actor) {
|
|
return actor;
|
|
}
|
|
|
|
actor = new NetworkEventActor(this);
|
|
this.manage(actor);
|
|
|
|
// map channel to actor so we can associate future events with it
|
|
this._netEvents.set(channelId, actor);
|
|
return actor;
|
|
},
|
|
|
|
// This method is called by NetworkMonitor instance when a new request is fired
|
|
onNetworkEvent(event) {
|
|
const { channelId } = event;
|
|
|
|
const actor = this.getNetworkEventActor(channelId);
|
|
this._netEvents.set(channelId, actor);
|
|
|
|
event.cause.stacktrace = this.stackTraces.has(channelId);
|
|
if (event.cause.stacktrace) {
|
|
this.stackTraces.delete(channelId);
|
|
}
|
|
actor.init(event);
|
|
|
|
this._networkEventActorsByURL.set(actor._request.url, actor);
|
|
|
|
const packet = {
|
|
from: this.parentID,
|
|
type: "networkEvent",
|
|
eventActor: actor.form()
|
|
};
|
|
|
|
this.conn.send(packet);
|
|
return actor;
|
|
},
|
|
|
|
});
|
|
exports.NetworkMonitorActor = NetworkMonitorActor;
|