--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -488,17 +488,19 @@ class FirefoxDataProvider {
let response = await new Promise((resolve, reject) => {
// Do a RDP request to fetch data from the actor.
if (typeof this.webConsoleClient[clientMethodName] === "function") {
// Make sure we fetch the real actor data instead of cloned actor
// e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
this.webConsoleClient[clientMethodName](actor.replace("-clone", ""), (res) => {
if (res.error) {
- reject(new Error(res.message));
+ reject(
+ new Error(`Error while calling method ${clientMethodName}: ${res.message}`)
+ );
}
resolve(res);
});
} else {
reject(new Error(`Error: No such client method '${clientMethodName}'!`));
}
});
--- a/devtools/server/actors/network-event.js
+++ b/devtools/server/actors/network-event.js
@@ -1,61 +1,67 @@
/* 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 protocol = require("devtools/shared/protocol");
+const { networkEventSpec } = require("devtools/shared/specs/network-event");
+
/**
* Creates an actor for a network event.
*
* @constructor
* @param object webConsoleActor
* The parent WebConsoleActor instance for this object.
*/
-function NetworkEventActor(webConsoleActor) {
- this.parent = webConsoleActor;
- this.conn = this.parent.conn;
+const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
+ initialize(webConsoleActor) {
+ // Necessary to get the events to work
+ protocol.Actor.prototype.initialize.call(this, webConsoleActor.conn);
- this._request = {
- method: null,
- url: null,
- httpVersion: null,
- headers: [],
- cookies: [],
- headersSize: null,
- postData: {},
- };
+ this.webConsoleActor = webConsoleActor;
+ this.conn = this.webConsoleActor.conn;
- this._response = {
- headers: [],
- cookies: [],
- content: {},
- };
+ this._request = {
+ method: null,
+ url: null,
+ httpVersion: null,
+ headers: [],
+ cookies: [],
+ headersSize: null,
+ postData: {},
+ };
- this._timings = {};
- this._stackTrace = {};
+ this._response = {
+ headers: [],
+ cookies: [],
+ content: {},
+ };
- // Keep track of LongStringActors owned by this NetworkEventActor.
- this._longStringActors = new Set();
-}
+ this._timings = {};
+ this._stackTrace = {};
-NetworkEventActor.prototype =
-{
+ this._discardRequestBody = false;
+ this._discardResponseBody = false;
+
+ // Keep track of LongStringActors owned by this NetworkEventActor.
+ this._longStringActors = new Set();
+ },
+
_request: null,
_response: null,
_timings: null,
_longStringActors: null,
- actorPrefix: "netEvent",
-
/**
* Returns a grip for this actor for returning in a protocol message.
*/
- grip: function() {
+ form() {
return {
actor: this.actorID,
startedDateTime: this._startedDateTime,
timeStamp: Date.parse(this._startedDateTime),
url: this._request.url,
method: this._request.method,
isXHR: this._isXHR,
cause: this._cause,
@@ -63,50 +69,56 @@ NetworkEventActor.prototype =
fromServiceWorker: this._fromServiceWorker,
private: this._private,
};
},
/**
* Releases this actor from the pool.
*/
- release: function() {
+ destroy(conn) {
for (let grip of this._longStringActors) {
- let actor = this.parent.getActorByID(grip.actor);
+ let actor = this.webConsoleActor.getActorByID(grip.actor);
if (actor) {
- this.parent.releaseActor(actor);
+ this.webConsoleActor.releaseActor(actor);
}
}
this._longStringActors = new Set();
+ if (!this.webConsoleActor) {
+ return;
+ }
if (this._request.url) {
- this.parent._networkEventActorsByURL.delete(this._request.url);
+ this.webConsoleActor._networkEventActorsByURL.delete(this._request.url);
}
if (this.channel) {
- this.parent._netEvents.delete(this.channel);
+ this.webConsoleActor._netEvents.delete(this.channel);
}
- this.parent.releaseActor(this);
+
+ // Nullify webConsoleActor before calling releaseActor as it will recall this method
+ // To be removed once WebConsoleActor switches to protocol.js
+ let actor = this.webConsoleActor;
+ this.webConsoleActor = null;
+ actor.releaseActor(this);
+
+ protocol.Actor.prototype.destroy.call(this, conn);
},
- /**
- * Handle a protocol request to release a grip.
- */
- onRelease: function() {
- this.release();
- return {};
+ release() {
+ // Per spec, destroy is automatically going to be called after this request
},
/**
* Set the properties of this actor based on it's corresponding
* network event.
*
* @param object networkEvent
* The network event associated with this actor.
*/
- init: function(networkEvent) {
+ init(networkEvent) {
this._startedDateTime = networkEvent.startedDateTime;
this._isXHR = networkEvent.isXHR;
this._cause = networkEvent.cause;
this._fromCache = networkEvent.fromCache;
this._fromServiceWorker = networkEvent.fromServiceWorker;
// Stack trace info isn't sent automatically. The client
// needs to request it explicitly using getStackTrace
@@ -115,419 +127,347 @@ NetworkEventActor.prototype =
delete networkEvent.cause.stacktrace;
networkEvent.cause.stacktraceAvailable =
!!(this._stackTrace && this._stackTrace.length);
for (let prop of ["method", "url", "httpVersion", "headersSize"]) {
this._request[prop] = networkEvent[prop];
}
- this._discardRequestBody = networkEvent.discardRequestBody;
- this._discardResponseBody = networkEvent.discardResponseBody;
+ // Consider as not discarded if networkEvent.discard*Body is undefined
+ this._discardRequestBody = !!networkEvent.discardRequestBody;
+ this._discardResponseBody = !!networkEvent.discardResponseBody;
+
this._truncated = false;
this._private = networkEvent.private;
},
/**
* The "getRequestHeaders" packet type handler.
*
* @return object
* The response packet - network request headers.
*/
- onGetRequestHeaders: function() {
+ getRequestHeaders() {
return {
- from: this.actorID,
headers: this._request.headers,
headersSize: this._request.headersSize,
rawHeaders: this._request.rawHeaders,
};
},
/**
* The "getRequestCookies" packet type handler.
*
* @return object
* The response packet - network request cookies.
*/
- onGetRequestCookies: function() {
+ getRequestCookies() {
return {
- from: this.actorID,
cookies: this._request.cookies,
};
},
/**
* The "getRequestPostData" packet type handler.
*
* @return object
* The response packet - network POST data.
*/
- onGetRequestPostData: function() {
+ getRequestPostData() {
return {
- from: this.actorID,
postData: this._request.postData,
postDataDiscarded: this._discardRequestBody,
};
},
/**
* The "getSecurityInfo" packet type handler.
*
* @return object
* The response packet - connection security information.
*/
- onGetSecurityInfo: function() {
+ getSecurityInfo() {
return {
- from: this.actorID,
securityInfo: this._securityInfo,
};
},
/**
* The "getResponseHeaders" packet type handler.
*
* @return object
* The response packet - network response headers.
*/
- onGetResponseHeaders: function() {
+ getResponseHeaders() {
return {
- from: this.actorID,
headers: this._response.headers,
headersSize: this._response.headersSize,
rawHeaders: this._response.rawHeaders,
};
},
/**
* The "getResponseCache" packet type handler.
*
* @return object
* The cache packet - network cache information.
*/
- onGetResponseCache: function() {
+ getResponseCache: function() {
return {
- from: this.actorID,
cache: this._response.responseCache,
};
},
/**
* The "getResponseCookies" packet type handler.
*
* @return object
* The response packet - network response cookies.
*/
- onGetResponseCookies: function() {
+ getResponseCookies() {
return {
- from: this.actorID,
cookies: this._response.cookies,
};
},
/**
* The "getResponseContent" packet type handler.
*
* @return object
* The response packet - network response content.
*/
- onGetResponseContent: function() {
+ getResponseContent() {
return {
- from: this.actorID,
content: this._response.content,
contentDiscarded: this._discardResponseBody,
};
},
/**
* The "getEventTimings" packet type handler.
*
* @return object
* The response packet - network event timings.
*/
- onGetEventTimings: function() {
+ getEventTimings() {
return {
- from: this.actorID,
timings: this._timings,
totalTime: this._totalTime,
offsets: this._offsets
};
},
/**
* The "getStackTrace" packet type handler.
*
* @return object
* The response packet - stack trace.
*/
- onGetStackTrace: function() {
+ getStackTrace() {
return {
- from: this.actorID,
stacktrace: this._stackTrace,
};
},
/** ****************************************************************
* Listeners for new network event data coming from NetworkMonitor.
******************************************************************/
/**
* Add network request headers.
*
* @param array headers
* The request headers array.
* @param string rawHeaders
* The raw headers source.
*/
- addRequestHeaders: function(headers, rawHeaders) {
+ addRequestHeaders(headers, rawHeaders) {
this._request.headers = headers;
this._prepareHeaders(headers);
- rawHeaders = this.parent._createStringGrip(rawHeaders);
+ rawHeaders = this.webConsoleActor._createStringGrip(rawHeaders);
if (typeof rawHeaders == "object") {
this._longStringActors.add(rawHeaders);
}
this._request.rawHeaders = rawHeaders;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "requestHeaders",
+ this.emit("network-event-update:headers", "requestHeaders", {
headers: headers.length,
headersSize: this._request.headersSize,
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add network request cookies.
*
* @param array cookies
* The request cookies array.
*/
- addRequestCookies: function(cookies) {
+ addRequestCookies(cookies) {
this._request.cookies = cookies;
this._prepareHeaders(cookies);
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "requestCookies",
+ this.emit("network-event-update:cookies", "requestCookies", {
cookies: cookies.length,
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add network request POST data.
*
* @param object postData
* The request POST data.
*/
- addRequestPostData: function(postData) {
+ addRequestPostData(postData) {
this._request.postData = postData;
- postData.text = this.parent._createStringGrip(postData.text);
+ postData.text = this.webConsoleActor._createStringGrip(postData.text);
if (typeof postData.text == "object") {
this._longStringActors.add(postData.text);
}
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "requestPostData",
+ this.emit("network-event-update:post-data", "requestPostData", {
dataSize: postData.text.length,
discardRequestBody: this._discardRequestBody,
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add the initial network response information.
*
* @param object info
* The response information.
* @param string rawHeaders
* The raw headers source.
*/
- addResponseStart: function(info, rawHeaders) {
- rawHeaders = this.parent._createStringGrip(rawHeaders);
+ addResponseStart(info, rawHeaders) {
+ rawHeaders = this.webConsoleActor._createStringGrip(rawHeaders);
if (typeof rawHeaders == "object") {
this._longStringActors.add(rawHeaders);
}
this._response.rawHeaders = rawHeaders;
this._response.httpVersion = info.httpVersion;
this._response.status = info.status;
this._response.statusText = info.statusText;
this._response.headersSize = info.headersSize;
- this._discardResponseBody = info.discardResponseBody;
+ // Consider as not discarded if info.discardResponseBody is undefined
+ this._discardResponseBody = !!info.discardResponseBody;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseStart",
+ this.emit("network-event-update:response-start", "responseStart", {
response: info
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add connection security information.
*
* @param object info
* The object containing security information.
*/
- addSecurityInfo: function(info) {
+ addSecurityInfo(info) {
this._securityInfo = info;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "securityInfo",
+ this.emit("network-event-update:security-info", "securityInfo", {
state: info.state,
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add network response headers.
*
* @param array headers
* The response headers array.
*/
- addResponseHeaders: function(headers) {
+ addResponseHeaders(headers) {
this._response.headers = headers;
this._prepareHeaders(headers);
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseHeaders",
+ this.emit("network-event-update:headers", "responseHeaders", {
headers: headers.length,
headersSize: this._response.headersSize,
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add network response cookies.
*
* @param array cookies
* The response cookies array.
*/
- addResponseCookies: function(cookies) {
+ addResponseCookies(cookies) {
this._response.cookies = cookies;
this._prepareHeaders(cookies);
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseCookies",
+ this.emit("network-event-update:cookies", "responseCookies", {
cookies: cookies.length,
- };
-
- this.conn.send(packet);
+ });
},
/**
* Add network response content.
*
* @param object content
* The response content.
* @param object
* - boolean discardedResponseBody
* Tells if the response content was recorded or not.
* - boolean truncated
* Tells if the some of the response content is missing.
*/
- addResponseContent: function(content, {discardResponseBody, truncated}) {
+ addResponseContent(content, {discardResponseBody, truncated}) {
this._truncated = truncated;
this._response.content = content;
- content.text = this.parent._createStringGrip(content.text);
+ content.text = this.webConsoleActor._createStringGrip(content.text);
if (typeof content.text == "object") {
this._longStringActors.add(content.text);
}
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseContent",
+ this.emit("network-event-update:response-content", "responseContent", {
mimeType: content.mimeType,
contentSize: content.size,
encoding: content.encoding,
transferredSize: content.transferredSize,
discardResponseBody,
- };
-
- this.conn.send(packet);
+ });
},
addResponseCache: function(content) {
this._response.responseCache = content.responseCache;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseCache",
- };
- this.conn.send(packet);
+ this.emit("network-event-update:response-cache", "responseCache");
},
/**
* Add network event timing information.
*
* @param number total
* The total time of the network event.
* @param object timings
* Timing details about the network event.
*/
- addEventTimings: function(total, timings, offsets) {
+ addEventTimings(total, timings, offsets) {
this._totalTime = total;
this._timings = timings;
this._offsets = offsets;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "eventTimings",
+ this.emit("network-event-update:event-timings", "eventTimings", {
totalTime: total
- };
-
- this.conn.send(packet);
+ });
},
/**
* Prepare the headers array to be sent to the client by using the
* LongStringActor for the header values, when needed.
*
* @private
* @param array aHeaders
*/
- _prepareHeaders: function(headers) {
+ _prepareHeaders(headers) {
for (let header of headers) {
- header.value = this.parent._createStringGrip(header.value);
+ header.value = this.webConsoleActor._createStringGrip(header.value);
if (typeof header.value == "object") {
this._longStringActors.add(header.value);
}
}
},
-};
-
-NetworkEventActor.prototype.requestTypes =
-{
- "release": NetworkEventActor.prototype.onRelease,
- "getRequestHeaders": NetworkEventActor.prototype.onGetRequestHeaders,
- "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies,
- "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData,
- "getResponseHeaders": NetworkEventActor.prototype.onGetResponseHeaders,
- "getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies,
- "getResponseCache": NetworkEventActor.prototype.onGetResponseCache,
- "getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
- "getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
- "getSecurityInfo": NetworkEventActor.prototype.onGetSecurityInfo,
- "getStackTrace": NetworkEventActor.prototype.onGetStackTrace,
-};
+});
exports.NetworkEventActor = NetworkEventActor;
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1638,17 +1638,17 @@ WebConsoleActor.prototype =
let actor = this.getNetworkEventActor(event.channelId);
actor.init(event);
this._networkEventActorsByURL.set(actor._request.url, actor);
let packet = {
from: this.actorID,
type: "networkEvent",
- eventActor: actor.grip()
+ eventActor: actor.form()
};
this.conn.send(packet);
return actor;
},
/**
@@ -1730,17 +1730,17 @@ WebConsoleActor.prototype =
let actor = this.getNetworkEventActor(channel.channelId);
// map channel to actor so we can associate future events with it
this._netEvents.set(channel.channelId, actor);
return {
from: this.actorID,
- eventActor: actor.grip()
+ eventActor: actor.form()
};
},
/**
* Handler for file activity. This method sends the file request information
* to the remote Web Console client.
*
* @see ConsoleProgressListener
--- a/devtools/shared/specs/index.js
+++ b/devtools/shared/specs/index.js
@@ -111,16 +111,21 @@ const Types = exports.__TypesForTests =
spec: "devtools/shared/specs/layout",
front: "devtools/shared/fronts/layout",
},
{
types: ["memory"],
spec: "devtools/shared/specs/memory",
front: "devtools/shared/fronts/memory",
},
+ {
+ types: ["netEvent"],
+ spec: "devtools/shared/specs/network-event",
+ front: null,
+ },
/* imageData isn't an actor but just a DictType */
{
types: ["imageData", "disconnectedNode", "disconnectedNodeArray"],
spec: "devtools/shared/specs/node",
front: null,
},
{
types: ["domnode", "domnodelist"],
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -21,16 +21,17 @@ DevToolsModules(
'framerate.js',
'gcli.js',
'heap-snapshot-file.js',
'highlighters.js',
'index.js',
'inspector.js',
'layout.js',
'memory.js',
+ 'network-event.js',
'node.js',
'perf.js',
'performance-recording.js',
'performance.js',
'preference.js',
'promises.js',
'property-iterator.js',
'reflow.js',
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/network-event.js
@@ -0,0 +1,208 @@
+/* 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 {
+ Arg,
+ Option,
+ RetVal,
+ generateActorSpec,
+ types
+} = require("devtools/shared/protocol");
+
+types.addDictType("netevent.headers-cookies", {
+ name: "string",
+ value: "longstring",
+});
+
+types.addDictType("netevent.headers", {
+ headers: "array:netevent.headers-cookies",
+ headersSize: "number",
+ rawHeaders: "nullable:longstring",
+});
+
+types.addDictType("netevent.cookies", {
+ cookies: "array:netevent.headers-cookies",
+});
+
+types.addDictType("netevent.postdata.text", {
+ text: "longstring",
+});
+
+types.addDictType("netevent.postdata", {
+ postData: "netevent.postdata.text",
+ postDataDiscarded: "boolean",
+});
+
+types.addDictType("netevent.cache", {
+ content: "json",
+});
+
+types.addDictType("netevent.content.content", {
+ text: "longstring",
+});
+
+types.addDictType("netevent.content", {
+ content: "netevent.content.content",
+ contentDiscarded: "boolean",
+});
+
+types.addDictType("netevent.timings.data", {
+ blocked: "number",
+ dns: "number",
+ ssl: "number",
+ connect: "number",
+ send: "number",
+ wait: "number",
+ receive: "number",
+});
+
+types.addDictType("netevent.timings", {
+ timings: "netevent.timings.data",
+ totalTime: "number",
+ offsets: "netevent.timings.data",
+});
+
+// See NetworkHelper.parseCertificateInfo for more details
+types.addDictType("netevent.cert", {
+ subject: "json",
+ issuer: "json",
+ validity: "json",
+ fingerprint: "json",
+});
+
+types.addDictType("netevent.secinfo", {
+ state: "string",
+ weaknessReasons: "array:string",
+ cipherSuite: "string",
+ keaGroupName: "string",
+ signatureSchemeName: "string",
+ protocolVersion: "string",
+ cert: "nullable:netevent.cert",
+ certificateTransparency: "nullable:string",
+ hsts: "boolean",
+ hpkp: "boolean",
+ errorMessage: "nullable:string",
+});
+
+const networkEventSpec = generateActorSpec({
+ typeName: "netEvent",
+
+ events: {
+ // All these events end up emitting a `networkEventUpdate` RDP message
+ // `updateType` attribute allows to identify which kind of event is emitted.
+ // We use individual event at protocol.js level to workaround performance issue
+ // with `Option` types. (See bug 1449162)
+ "network-event-update:headers": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ headers: Option(1, "number"),
+ headersSize: Option(1, "number"),
+ },
+
+ "network-event-update:cookies": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ cookies: Option(1, "number"),
+ },
+
+ "network-event-update:post-data": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ dataSize: Option(1, "number"),
+ discardRequestBody: Option(1, "boolean"),
+ },
+
+ "network-event-update:response-start": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ response: Option(1, "json"),
+ },
+
+ "network-event-update:security-info": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ state: Option(1, "string"),
+ },
+
+ "network-event-update:response-content": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ mimeType: Option(1, "string"),
+ contentSize: Option(1, "number"),
+ encoding: Option(1, "string"),
+ transferredSize: Option(1, "number"),
+ discardResponseBody: Option(1, "boolean"),
+ },
+
+ "network-event-update:event-timings": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+
+ totalTime: Option(1, "number"),
+ },
+
+ "network-event-update:response-cache": {
+ type: "networkEventUpdate",
+ updateType: Arg(0, "string"),
+ },
+ },
+
+ methods: {
+ release: {
+ // This makes protocol.js call destroy method
+ release: true
+ },
+ getRequestHeaders: {
+ request: {},
+ response: RetVal("netevent.headers")
+ },
+ getRequestCookies: {
+ request: {},
+ response: RetVal("netevent.cookies")
+ },
+ getRequestPostData: {
+ request: {},
+ response: RetVal("netevent.postdata")
+ },
+ getResponseHeaders: {
+ request: {},
+ response: RetVal("netevent.headers")
+ },
+ getResponseCookies: {
+ request: {},
+ response: RetVal("netevent.cookies")
+ },
+ getResponseCache: {
+ request: {},
+ response: RetVal("netevent.cache")
+ },
+ getResponseContent: {
+ request: {},
+ response: RetVal("netevent.content")
+ },
+ getEventTimings: {
+ request: {},
+ response: RetVal("netevent.timings")
+ },
+ getSecurityInfo: {
+ request: {},
+ response: RetVal("netevent.secinfo")
+ },
+ getStackTrace: {
+ request: {},
+ // stacktrace is an "array:string", but not always.
+ response: RetVal("json")
+ },
+ }
+});
+
+exports.networkEventSpec = networkEventSpec;