From 3e4f25c18ea3fc3708c7071e9ab26cf3256f1812 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Tue, 27 Mar 2018 06:33:11 -0700 Subject: [PATCH] Bug 1449162 - Refactor the NetworkEventActor to use protocol.js. r=jryans MozReview-Commit-ID: 7HW8sO9dMuD --HG-- extra : rebase_source : 488c916c35b2352085a22a70ace76dbff8c8d95f --- .../src/connector/firefox-data-provider.js | 4 +- devtools/server/actors/network-event.js | 266 +++++++----------- devtools/server/actors/webconsole.js | 4 +- devtools/shared/specs/index.js | 5 + devtools/shared/specs/moz.build | 1 + devtools/shared/specs/network-event.js | 208 ++++++++++++++ 6 files changed, 322 insertions(+), 166 deletions(-) create mode 100644 devtools/shared/specs/network-event.js diff --git a/devtools/client/netmonitor/src/connector/firefox-data-provider.js b/devtools/client/netmonitor/src/connector/firefox-data-provider.js index 4f5cdc823467..d1e6c4f2b13d 100644 --- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js +++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js @@ -493,7 +493,9 @@ class FirefoxDataProvider { // 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); }); diff --git a/devtools/server/actors/network-event.js b/devtools/server/actors/network-event.js index 5d9ff86d45c5..764cd55fb1b7 100644 --- a/devtools/server/actors/network-event.js +++ b/devtools/server/actors/network-event.js @@ -4,6 +4,9 @@ "use strict"; +const protocol = require("devtools/shared/protocol"); +const { networkEventSpec } = require("devtools/shared/specs/network-event"); + /** * Creates an actor for a network event. * @@ -11,46 +14,49 @@ * @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 = {}; + + this._discardRequestBody = false; + this._discardResponseBody = false; + + // Keep track of LongStringActors owned by this NetworkEventActor. + this._longStringActors = new Set(); + }, -NetworkEventActor.prototype = -{ _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, @@ -68,30 +74,36 @@ NetworkEventActor.prototype = /** * 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 }, /** @@ -101,7 +113,7 @@ NetworkEventActor.prototype = * @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; @@ -120,8 +132,10 @@ NetworkEventActor.prototype = 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; }, @@ -132,9 +146,8 @@ NetworkEventActor.prototype = * @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, @@ -147,9 +160,8 @@ NetworkEventActor.prototype = * @return object * The response packet - network request cookies. */ - onGetRequestCookies: function() { + getRequestCookies() { return { - from: this.actorID, cookies: this._request.cookies, }; }, @@ -160,9 +172,8 @@ NetworkEventActor.prototype = * @return object * The response packet - network POST data. */ - onGetRequestPostData: function() { + getRequestPostData() { return { - from: this.actorID, postData: this._request.postData, postDataDiscarded: this._discardRequestBody, }; @@ -174,9 +185,8 @@ NetworkEventActor.prototype = * @return object * The response packet - connection security information. */ - onGetSecurityInfo: function() { + getSecurityInfo() { return { - from: this.actorID, securityInfo: this._securityInfo, }; }, @@ -187,9 +197,8 @@ NetworkEventActor.prototype = * @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, @@ -202,9 +211,8 @@ NetworkEventActor.prototype = * @return object * The cache packet - network cache information. */ - onGetResponseCache: function() { + getResponseCache: function() { return { - from: this.actorID, cache: this._response.responseCache, }; }, @@ -215,9 +223,8 @@ NetworkEventActor.prototype = * @return object * The response packet - network response cookies. */ - onGetResponseCookies: function() { + getResponseCookies() { return { - from: this.actorID, cookies: this._response.cookies, }; }, @@ -228,9 +235,8 @@ NetworkEventActor.prototype = * @return object * The response packet - network response content. */ - onGetResponseContent: function() { + getResponseContent() { return { - from: this.actorID, content: this._response.content, contentDiscarded: this._discardResponseBody, }; @@ -242,9 +248,8 @@ NetworkEventActor.prototype = * @return object * The response packet - network event timings. */ - onGetEventTimings: function() { + getEventTimings() { return { - from: this.actorID, timings: this._timings, totalTime: this._totalTime, offsets: this._offsets @@ -257,9 +262,8 @@ NetworkEventActor.prototype = * @return object * The response packet - stack trace. */ - onGetStackTrace: function() { + getStackTrace() { return { - from: this.actorID, stacktrace: this._stackTrace, }; }, @@ -276,25 +280,20 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -303,18 +302,13 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -323,22 +317,17 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -349,8 +338,8 @@ NetworkEventActor.prototype = * @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); } @@ -360,16 +349,12 @@ NetworkEventActor.prototype = 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); + }); }, /** @@ -378,17 +363,12 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -397,19 +377,14 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -418,18 +393,13 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -443,36 +413,26 @@ NetworkEventActor.prototype = * - 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"); }, /** @@ -483,19 +443,14 @@ NetworkEventActor.prototype = * @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); + }); }, /** @@ -505,29 +460,14 @@ NetworkEventActor.prototype = * @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; diff --git a/devtools/server/actors/webconsole.js b/devtools/server/actors/webconsole.js index d34728511e1d..456e394d985c 100644 --- a/devtools/server/actors/webconsole.js +++ b/devtools/server/actors/webconsole.js @@ -1643,7 +1643,7 @@ WebConsoleActor.prototype = let packet = { from: this.actorID, type: "networkEvent", - eventActor: actor.grip() + eventActor: actor.form() }; this.conn.send(packet); @@ -1735,7 +1735,7 @@ WebConsoleActor.prototype = return { from: this.actorID, - eventActor: actor.grip() + eventActor: actor.form() }; }, diff --git a/devtools/shared/specs/index.js b/devtools/shared/specs/index.js index 8a1a34e181cf..05b0a470d543 100644 --- a/devtools/shared/specs/index.js +++ b/devtools/shared/specs/index.js @@ -116,6 +116,11 @@ const Types = exports.__TypesForTests = [ 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"], diff --git a/devtools/shared/specs/moz.build b/devtools/shared/specs/moz.build index c06e77437406..755a22bd051d 100644 --- a/devtools/shared/specs/moz.build +++ b/devtools/shared/specs/moz.build @@ -26,6 +26,7 @@ DevToolsModules( 'inspector.js', 'layout.js', 'memory.js', + 'network-event.js', 'node.js', 'perf.js', 'performance-recording.js', diff --git a/devtools/shared/specs/network-event.js b/devtools/shared/specs/network-event.js new file mode 100644 index 000000000000..cb367bd0db1e --- /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;