Bug 1449162 - Refactor the NetworkEventActor to use protocol.js. r=jryans

MozReview-Commit-ID: 7HW8sO9dMuD

--HG--
extra : rebase_source : 488c916c35b2352085a22a70ace76dbff8c8d95f
This commit is contained in:
Alexandre Poirot 2018-03-27 06:33:11 -07:00
Родитель e9a2771d52
Коммит 3e4f25c18e
6 изменённых файлов: 322 добавлений и 166 удалений

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

@ -493,7 +493,9 @@ class FirefoxDataProvider {
// e.g. CustomRequestPanel will clone a request with additional '-clone' actor id // e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
this.webConsoleClient[clientMethodName](actor.replace("-clone", ""), (res) => { this.webConsoleClient[clientMethodName](actor.replace("-clone", ""), (res) => {
if (res.error) { if (res.error) {
reject(new Error(res.message)); reject(
new Error(`Error while calling method ${clientMethodName}: ${res.message}`)
);
} }
resolve(res); resolve(res);
}); });

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

@ -4,6 +4,9 @@
"use strict"; "use strict";
const protocol = require("devtools/shared/protocol");
const { networkEventSpec } = require("devtools/shared/specs/network-event");
/** /**
* Creates an actor for a network event. * Creates an actor for a network event.
* *
@ -11,46 +14,49 @@
* @param object webConsoleActor * @param object webConsoleActor
* The parent WebConsoleActor instance for this object. * The parent WebConsoleActor instance for this object.
*/ */
function NetworkEventActor(webConsoleActor) { const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
this.parent = webConsoleActor; initialize(webConsoleActor) {
this.conn = this.parent.conn; // Necessary to get the events to work
protocol.Actor.prototype.initialize.call(this, webConsoleActor.conn);
this._request = { this.webConsoleActor = webConsoleActor;
method: null, this.conn = this.webConsoleActor.conn;
url: null,
httpVersion: null,
headers: [],
cookies: [],
headersSize: null,
postData: {},
};
this._response = { this._request = {
headers: [], method: null,
cookies: [], url: null,
content: {}, httpVersion: null,
}; headers: [],
cookies: [],
headersSize: null,
postData: {},
};
this._timings = {}; this._response = {
this._stackTrace = {}; headers: [],
cookies: [],
content: {},
};
// Keep track of LongStringActors owned by this NetworkEventActor. this._timings = {};
this._longStringActors = new Set(); this._stackTrace = {};
}
this._discardRequestBody = false;
this._discardResponseBody = false;
// Keep track of LongStringActors owned by this NetworkEventActor.
this._longStringActors = new Set();
},
NetworkEventActor.prototype =
{
_request: null, _request: null,
_response: null, _response: null,
_timings: null, _timings: null,
_longStringActors: null, _longStringActors: null,
actorPrefix: "netEvent",
/** /**
* Returns a grip for this actor for returning in a protocol message. * Returns a grip for this actor for returning in a protocol message.
*/ */
grip: function() { form() {
return { return {
actor: this.actorID, actor: this.actorID,
startedDateTime: this._startedDateTime, startedDateTime: this._startedDateTime,
@ -68,30 +74,36 @@ NetworkEventActor.prototype =
/** /**
* Releases this actor from the pool. * Releases this actor from the pool.
*/ */
release: function() { destroy(conn) {
for (let grip of this._longStringActors) { for (let grip of this._longStringActors) {
let actor = this.parent.getActorByID(grip.actor); let actor = this.webConsoleActor.getActorByID(grip.actor);
if (actor) { if (actor) {
this.parent.releaseActor(actor); this.webConsoleActor.releaseActor(actor);
} }
} }
this._longStringActors = new Set(); this._longStringActors = new Set();
if (!this.webConsoleActor) {
return;
}
if (this._request.url) { if (this._request.url) {
this.parent._networkEventActorsByURL.delete(this._request.url); this.webConsoleActor._networkEventActorsByURL.delete(this._request.url);
} }
if (this.channel) { 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);
}, },
/** release() {
* Handle a protocol request to release a grip. // Per spec, destroy is automatically going to be called after this request
*/
onRelease: function() {
this.release();
return {};
}, },
/** /**
@ -101,7 +113,7 @@ NetworkEventActor.prototype =
* @param object networkEvent * @param object networkEvent
* The network event associated with this actor. * The network event associated with this actor.
*/ */
init: function(networkEvent) { init(networkEvent) {
this._startedDateTime = networkEvent.startedDateTime; this._startedDateTime = networkEvent.startedDateTime;
this._isXHR = networkEvent.isXHR; this._isXHR = networkEvent.isXHR;
this._cause = networkEvent.cause; this._cause = networkEvent.cause;
@ -120,8 +132,10 @@ NetworkEventActor.prototype =
this._request[prop] = networkEvent[prop]; this._request[prop] = networkEvent[prop];
} }
this._discardRequestBody = networkEvent.discardRequestBody; // Consider as not discarded if networkEvent.discard*Body is undefined
this._discardResponseBody = networkEvent.discardResponseBody; this._discardRequestBody = !!networkEvent.discardRequestBody;
this._discardResponseBody = !!networkEvent.discardResponseBody;
this._truncated = false; this._truncated = false;
this._private = networkEvent.private; this._private = networkEvent.private;
}, },
@ -132,9 +146,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network request headers. * The response packet - network request headers.
*/ */
onGetRequestHeaders: function() { getRequestHeaders() {
return { return {
from: this.actorID,
headers: this._request.headers, headers: this._request.headers,
headersSize: this._request.headersSize, headersSize: this._request.headersSize,
rawHeaders: this._request.rawHeaders, rawHeaders: this._request.rawHeaders,
@ -147,9 +160,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network request cookies. * The response packet - network request cookies.
*/ */
onGetRequestCookies: function() { getRequestCookies() {
return { return {
from: this.actorID,
cookies: this._request.cookies, cookies: this._request.cookies,
}; };
}, },
@ -160,9 +172,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network POST data. * The response packet - network POST data.
*/ */
onGetRequestPostData: function() { getRequestPostData() {
return { return {
from: this.actorID,
postData: this._request.postData, postData: this._request.postData,
postDataDiscarded: this._discardRequestBody, postDataDiscarded: this._discardRequestBody,
}; };
@ -174,9 +185,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - connection security information. * The response packet - connection security information.
*/ */
onGetSecurityInfo: function() { getSecurityInfo() {
return { return {
from: this.actorID,
securityInfo: this._securityInfo, securityInfo: this._securityInfo,
}; };
}, },
@ -187,9 +197,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network response headers. * The response packet - network response headers.
*/ */
onGetResponseHeaders: function() { getResponseHeaders() {
return { return {
from: this.actorID,
headers: this._response.headers, headers: this._response.headers,
headersSize: this._response.headersSize, headersSize: this._response.headersSize,
rawHeaders: this._response.rawHeaders, rawHeaders: this._response.rawHeaders,
@ -202,9 +211,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The cache packet - network cache information. * The cache packet - network cache information.
*/ */
onGetResponseCache: function() { getResponseCache: function() {
return { return {
from: this.actorID,
cache: this._response.responseCache, cache: this._response.responseCache,
}; };
}, },
@ -215,9 +223,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network response cookies. * The response packet - network response cookies.
*/ */
onGetResponseCookies: function() { getResponseCookies() {
return { return {
from: this.actorID,
cookies: this._response.cookies, cookies: this._response.cookies,
}; };
}, },
@ -228,9 +235,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network response content. * The response packet - network response content.
*/ */
onGetResponseContent: function() { getResponseContent() {
return { return {
from: this.actorID,
content: this._response.content, content: this._response.content,
contentDiscarded: this._discardResponseBody, contentDiscarded: this._discardResponseBody,
}; };
@ -242,9 +248,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - network event timings. * The response packet - network event timings.
*/ */
onGetEventTimings: function() { getEventTimings() {
return { return {
from: this.actorID,
timings: this._timings, timings: this._timings,
totalTime: this._totalTime, totalTime: this._totalTime,
offsets: this._offsets offsets: this._offsets
@ -257,9 +262,8 @@ NetworkEventActor.prototype =
* @return object * @return object
* The response packet - stack trace. * The response packet - stack trace.
*/ */
onGetStackTrace: function() { getStackTrace() {
return { return {
from: this.actorID,
stacktrace: this._stackTrace, stacktrace: this._stackTrace,
}; };
}, },
@ -276,25 +280,20 @@ NetworkEventActor.prototype =
* @param string rawHeaders * @param string rawHeaders
* The raw headers source. * The raw headers source.
*/ */
addRequestHeaders: function(headers, rawHeaders) { addRequestHeaders(headers, rawHeaders) {
this._request.headers = headers; this._request.headers = headers;
this._prepareHeaders(headers); this._prepareHeaders(headers);
rawHeaders = this.parent._createStringGrip(rawHeaders); rawHeaders = this.webConsoleActor._createStringGrip(rawHeaders);
if (typeof rawHeaders == "object") { if (typeof rawHeaders == "object") {
this._longStringActors.add(rawHeaders); this._longStringActors.add(rawHeaders);
} }
this._request.rawHeaders = rawHeaders; this._request.rawHeaders = rawHeaders;
let packet = { this.emit("network-event-update:headers", "requestHeaders", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "requestHeaders",
headers: headers.length, headers: headers.length,
headersSize: this._request.headersSize, headersSize: this._request.headersSize,
}; });
this.conn.send(packet);
}, },
/** /**
@ -303,18 +302,13 @@ NetworkEventActor.prototype =
* @param array cookies * @param array cookies
* The request cookies array. * The request cookies array.
*/ */
addRequestCookies: function(cookies) { addRequestCookies(cookies) {
this._request.cookies = cookies; this._request.cookies = cookies;
this._prepareHeaders(cookies); this._prepareHeaders(cookies);
let packet = { this.emit("network-event-update:cookies", "requestCookies", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "requestCookies",
cookies: cookies.length, cookies: cookies.length,
}; });
this.conn.send(packet);
}, },
/** /**
@ -323,22 +317,17 @@ NetworkEventActor.prototype =
* @param object postData * @param object postData
* The request POST data. * The request POST data.
*/ */
addRequestPostData: function(postData) { addRequestPostData(postData) {
this._request.postData = postData; this._request.postData = postData;
postData.text = this.parent._createStringGrip(postData.text); postData.text = this.webConsoleActor._createStringGrip(postData.text);
if (typeof postData.text == "object") { if (typeof postData.text == "object") {
this._longStringActors.add(postData.text); this._longStringActors.add(postData.text);
} }
let packet = { this.emit("network-event-update:post-data", "requestPostData", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "requestPostData",
dataSize: postData.text.length, dataSize: postData.text.length,
discardRequestBody: this._discardRequestBody, discardRequestBody: this._discardRequestBody,
}; });
this.conn.send(packet);
}, },
/** /**
@ -349,8 +338,8 @@ NetworkEventActor.prototype =
* @param string rawHeaders * @param string rawHeaders
* The raw headers source. * The raw headers source.
*/ */
addResponseStart: function(info, rawHeaders) { addResponseStart(info, rawHeaders) {
rawHeaders = this.parent._createStringGrip(rawHeaders); rawHeaders = this.webConsoleActor._createStringGrip(rawHeaders);
if (typeof rawHeaders == "object") { if (typeof rawHeaders == "object") {
this._longStringActors.add(rawHeaders); this._longStringActors.add(rawHeaders);
} }
@ -360,16 +349,12 @@ NetworkEventActor.prototype =
this._response.status = info.status; this._response.status = info.status;
this._response.statusText = info.statusText; this._response.statusText = info.statusText;
this._response.headersSize = info.headersSize; this._response.headersSize = info.headersSize;
this._discardResponseBody = info.discardResponseBody; // Consider as not discarded if info.discardResponseBody is undefined
this._discardResponseBody = !!info.discardResponseBody;
let packet = { this.emit("network-event-update:response-start", "responseStart", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "responseStart",
response: info response: info
}; });
this.conn.send(packet);
}, },
/** /**
@ -378,17 +363,12 @@ NetworkEventActor.prototype =
* @param object info * @param object info
* The object containing security information. * The object containing security information.
*/ */
addSecurityInfo: function(info) { addSecurityInfo(info) {
this._securityInfo = info; this._securityInfo = info;
let packet = { this.emit("network-event-update:security-info", "securityInfo", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "securityInfo",
state: info.state, state: info.state,
}; });
this.conn.send(packet);
}, },
/** /**
@ -397,19 +377,14 @@ NetworkEventActor.prototype =
* @param array headers * @param array headers
* The response headers array. * The response headers array.
*/ */
addResponseHeaders: function(headers) { addResponseHeaders(headers) {
this._response.headers = headers; this._response.headers = headers;
this._prepareHeaders(headers); this._prepareHeaders(headers);
let packet = { this.emit("network-event-update:headers", "responseHeaders", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "responseHeaders",
headers: headers.length, headers: headers.length,
headersSize: this._response.headersSize, headersSize: this._response.headersSize,
}; });
this.conn.send(packet);
}, },
/** /**
@ -418,18 +393,13 @@ NetworkEventActor.prototype =
* @param array cookies * @param array cookies
* The response cookies array. * The response cookies array.
*/ */
addResponseCookies: function(cookies) { addResponseCookies(cookies) {
this._response.cookies = cookies; this._response.cookies = cookies;
this._prepareHeaders(cookies); this._prepareHeaders(cookies);
let packet = { this.emit("network-event-update:cookies", "responseCookies", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "responseCookies",
cookies: cookies.length, cookies: cookies.length,
}; });
this.conn.send(packet);
}, },
/** /**
@ -443,36 +413,26 @@ NetworkEventActor.prototype =
* - boolean truncated * - boolean truncated
* Tells if the some of the response content is missing. * Tells if the some of the response content is missing.
*/ */
addResponseContent: function(content, {discardResponseBody, truncated}) { addResponseContent(content, {discardResponseBody, truncated}) {
this._truncated = truncated; this._truncated = truncated;
this._response.content = content; this._response.content = content;
content.text = this.parent._createStringGrip(content.text); content.text = this.webConsoleActor._createStringGrip(content.text);
if (typeof content.text == "object") { if (typeof content.text == "object") {
this._longStringActors.add(content.text); this._longStringActors.add(content.text);
} }
let packet = { this.emit("network-event-update:response-content", "responseContent", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "responseContent",
mimeType: content.mimeType, mimeType: content.mimeType,
contentSize: content.size, contentSize: content.size,
encoding: content.encoding, encoding: content.encoding,
transferredSize: content.transferredSize, transferredSize: content.transferredSize,
discardResponseBody, discardResponseBody,
}; });
this.conn.send(packet);
}, },
addResponseCache: function(content) { addResponseCache: function(content) {
this._response.responseCache = content.responseCache; this._response.responseCache = content.responseCache;
let packet = { this.emit("network-event-update:response-cache", "responseCache");
from: this.actorID,
type: "networkEventUpdate",
updateType: "responseCache",
};
this.conn.send(packet);
}, },
/** /**
@ -483,19 +443,14 @@ NetworkEventActor.prototype =
* @param object timings * @param object timings
* Timing details about the network event. * Timing details about the network event.
*/ */
addEventTimings: function(total, timings, offsets) { addEventTimings(total, timings, offsets) {
this._totalTime = total; this._totalTime = total;
this._timings = timings; this._timings = timings;
this._offsets = offsets; this._offsets = offsets;
let packet = { this.emit("network-event-update:event-timings", "eventTimings", {
from: this.actorID,
type: "networkEventUpdate",
updateType: "eventTimings",
totalTime: total totalTime: total
}; });
this.conn.send(packet);
}, },
/** /**
@ -505,29 +460,14 @@ NetworkEventActor.prototype =
* @private * @private
* @param array aHeaders * @param array aHeaders
*/ */
_prepareHeaders: function(headers) { _prepareHeaders(headers) {
for (let header of 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") { if (typeof header.value == "object") {
this._longStringActors.add(header.value); 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; exports.NetworkEventActor = NetworkEventActor;

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

@ -1643,7 +1643,7 @@ WebConsoleActor.prototype =
let packet = { let packet = {
from: this.actorID, from: this.actorID,
type: "networkEvent", type: "networkEvent",
eventActor: actor.grip() eventActor: actor.form()
}; };
this.conn.send(packet); this.conn.send(packet);
@ -1735,7 +1735,7 @@ WebConsoleActor.prototype =
return { return {
from: this.actorID, from: this.actorID,
eventActor: actor.grip() eventActor: actor.form()
}; };
}, },

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

@ -116,6 +116,11 @@ const Types = exports.__TypesForTests = [
spec: "devtools/shared/specs/memory", spec: "devtools/shared/specs/memory",
front: "devtools/shared/fronts/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 */ /* imageData isn't an actor but just a DictType */
{ {
types: ["imageData", "disconnectedNode", "disconnectedNodeArray"], types: ["imageData", "disconnectedNode", "disconnectedNodeArray"],

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

@ -26,6 +26,7 @@ DevToolsModules(
'inspector.js', 'inspector.js',
'layout.js', 'layout.js',
'memory.js', 'memory.js',
'network-event.js',
'node.js', 'node.js',
'perf.js', 'perf.js',
'performance-recording.js', 'performance-recording.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;