1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383 |
- /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
- /* 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 Services = require("Services");
- const { Cc, Ci, Cu } = require("chrome");
- const { DebuggerServer, ActorPool } = require("devtools/server/main");
- const { EnvironmentActor } = require("devtools/server/actors/environment");
- const { ThreadActor } = require("devtools/server/actors/script");
- const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
- const DevToolsUtils = require("devtools/shared/DevToolsUtils");
- const ErrorDocs = require("devtools/server/actors/errordocs");
- loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true);
- loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
- loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
- loader.lazyRequireGetter(this, "StackTraceCollector", "devtools/shared/webconsole/network-monitor", true);
- loader.lazyRequireGetter(this, "events", "sdk/event/core");
- loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
- loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
- loader.lazyRequireGetter(this, "Parser", "resource://devtools/shared/Parser.jsm", true);
- loader.lazyRequireGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm", true);
- for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
- "ConsoleAPIListener", "addWebConsoleCommands",
- "ConsoleReflowListener", "CONSOLE_WORKER_IDS"]) {
- Object.defineProperty(this, name, {
- get: function (prop) {
- if (prop == "WebConsoleUtils") {
- prop = "Utils";
- }
- if (isWorker) {
- return require("devtools/server/actors/utils/webconsole-worker-utils")[prop];
- } else {
- return require("devtools/server/actors/utils/webconsole-utils")[prop];
- }
- }.bind(null, name),
- configurable: true,
- enumerable: true
- });
- }
- /**
- * The WebConsoleActor implements capabilities needed for the Web Console
- * feature.
- *
- * @constructor
- * @param object aConnection
- * The connection to the client, DebuggerServerConnection.
- * @param object [aParentActor]
- * Optional, the parent actor.
- */
- function WebConsoleActor(aConnection, aParentActor)
- {
- this.conn = aConnection;
- this.parentActor = aParentActor;
- this._actorPool = new ActorPool(this.conn);
- this.conn.addActorPool(this._actorPool);
- this._prefs = {};
- this.dbg = this.parentActor.makeDebugger();
- this._netEvents = new Map();
- this._gripDepth = 0;
- this._listeners = new Set();
- this._lastConsoleInputEvaluation = undefined;
- this.objectGrip = this.objectGrip.bind(this);
- this._onWillNavigate = this._onWillNavigate.bind(this);
- this._onChangedToplevelDocument = this._onChangedToplevelDocument.bind(this);
- events.on(this.parentActor, "changed-toplevel-document", this._onChangedToplevelDocument);
- this._onObserverNotification = this._onObserverNotification.bind(this);
- if (this.parentActor.isRootActor) {
- Services.obs.addObserver(this._onObserverNotification,
- "last-pb-context-exited", false);
- }
- this.traits = {
- customNetworkRequest: !this._parentIsContentActor,
- evaluateJSAsync: true,
- transferredResponseSize: true,
- selectedObjectActor: true, // 44+
- };
- }
- WebConsoleActor.prototype =
- {
- /**
- * Debugger instance.
- *
- * @see jsdebugger.jsm
- */
- dbg: null,
- /**
- * This is used by the ObjectActor to keep track of the depth of grip() calls.
- * @private
- * @type number
- */
- _gripDepth: null,
- /**
- * Actor pool for all of the actors we send to the client.
- * @private
- * @type object
- * @see ActorPool
- */
- _actorPool: null,
- /**
- * Web Console-related preferences.
- * @private
- * @type object
- */
- _prefs: null,
- /**
- * Holds a map between nsIChannel objects and NetworkEventActors for requests
- * created with sendHTTPRequest.
- *
- * @private
- * @type Map
- */
- _netEvents: null,
- /**
- * Holds a set of all currently registered listeners.
- *
- * @private
- * @type Set
- */
- _listeners: null,
- /**
- * The debugger server connection instance.
- * @type object
- */
- conn: null,
- /**
- * List of supported features by the console actor.
- * @type object
- */
- traits: null,
- /**
- * Boolean getter that tells if the parent actor is a ContentActor.
- *
- * @private
- * @type boolean
- */
- get _parentIsContentActor() {
- return "ContentActor" in DebuggerServer &&
- this.parentActor instanceof DebuggerServer.ContentActor;
- },
- /**
- * The window or sandbox we work with.
- * Note that even if it is named `window` it refers to the current
- * global we are debugging, which can be a Sandbox for addons
- * or browser content toolbox.
- *
- * @type nsIDOMWindow or Sandbox
- */
- get window() {
- if (this.parentActor.isRootActor) {
- return this._getWindowForBrowserConsole();
- }
- return this.parentActor.window;
- },
- /**
- * Get a window to use for the browser console.
- *
- * @private
- * @return nsIDOMWindow
- * The window to use, or null if no window could be found.
- */
- _getWindowForBrowserConsole: function WCA__getWindowForBrowserConsole()
- {
- // Check if our last used chrome window is still live.
- let window = this._lastChromeWindow && this._lastChromeWindow.get();
- // If not, look for a new one.
- if (!window || window.closed) {
- window = this.parentActor.window;
- if (!window) {
- // Try to find the Browser Console window to use instead.
- window = Services.wm.getMostRecentWindow("devtools:webconsole");
- // We prefer the normal chrome window over the console window,
- // so we'll look for those windows in order to replace our reference.
- let onChromeWindowOpened = () => {
- // We'll look for this window when someone next requests window()
- Services.obs.removeObserver(onChromeWindowOpened, "domwindowopened");
- this._lastChromeWindow = null;
- };
- Services.obs.addObserver(onChromeWindowOpened, "domwindowopened", false);
- }
- this._handleNewWindow(window);
- }
- return window;
- },
- /**
- * Store a newly found window on the actor to be used in the future.
- *
- * @private
- * @param nsIDOMWindow window
- * The window to store on the actor (can be null).
- */
- _handleNewWindow: function WCA__handleNewWindow(window)
- {
- if (window) {
- if (this._hadChromeWindow) {
- Services.console.logStringMessage('Webconsole context has changed');
- }
- this._lastChromeWindow = Cu.getWeakReference(window);
- this._hadChromeWindow = true;
- } else {
- this._lastChromeWindow = null;
- }
- },
- /**
- * Whether we've been using a window before.
- *
- * @private
- * @type boolean
- */
- _hadChromeWindow: false,
- /**
- * A weak reference to the last chrome window we used to work with.
- *
- * @private
- * @type nsIWeakReference
- */
- _lastChromeWindow: null,
- // The evalWindow is used at the scope for JS evaluation.
- _evalWindow: null,
- get evalWindow() {
- return this._evalWindow || this.window;
- },
- set evalWindow(aWindow) {
- this._evalWindow = aWindow;
- if (!this._progressListenerActive) {
- events.on(this.parentActor, "will-navigate", this._onWillNavigate);
- this._progressListenerActive = true;
- }
- },
- /**
- * Flag used to track if we are listening for events from the progress
- * listener of the tab actor. We use the progress listener to clear
- * this.evalWindow on page navigation.
- *
- * @private
- * @type boolean
- */
- _progressListenerActive: false,
- /**
- * The ConsoleServiceListener instance.
- * @type object
- */
- consoleServiceListener: null,
- /**
- * The ConsoleAPIListener instance.
- */
- consoleAPIListener: null,
- /**
- * The NetworkMonitor instance.
- */
- networkMonitor: null,
- /**
- * The NetworkMonitor instance living in the same (child) process.
- */
- networkMonitorChild: null,
- /**
- * The ConsoleProgressListener instance.
- */
- consoleProgressListener: null,
- /**
- * The ConsoleReflowListener instance.
- */
- consoleReflowListener: null,
- /**
- * The Web Console Commands names cache.
- * @private
- * @type array
- */
- _webConsoleCommandsCache: null,
- actorPrefix: "console",
- get globalDebugObject() {
- return this.parentActor.threadActor.globalDebugObject;
- },
- grip: function WCA_grip()
- {
- return { actor: this.actorID };
- },
- hasNativeConsoleAPI: function WCA_hasNativeConsoleAPI(aWindow) {
- let isNative = false;
- try {
- // We are very explicitly examining the "console" property of
- // the non-Xrayed object here.
- let console = aWindow.wrappedJSObject.console;
- isNative = new XPCNativeWrapper(console).IS_NATIVE_CONSOLE
- }
- catch (ex) { }
- return isNative;
- },
- _findProtoChain: ThreadActor.prototype._findProtoChain,
- _removeFromProtoChain: ThreadActor.prototype._removeFromProtoChain,
- /**
- * Destroy the current WebConsoleActor instance.
- */
- disconnect: function WCA_disconnect()
- {
- if (this.consoleServiceListener) {
- this.consoleServiceListener.destroy();
- this.consoleServiceListener = null;
- }
- if (this.consoleAPIListener) {
- this.consoleAPIListener.destroy();
- this.consoleAPIListener = null;
- }
- if (this.networkMonitor) {
- this.networkMonitor.destroy();
- this.networkMonitor = null;
- }
- if (this.networkMonitorChild) {
- this.networkMonitorChild.destroy();
- this.networkMonitorChild = null;
- }
- if (this.stackTraceCollector) {
- this.stackTraceCollector.destroy();
- this.stackTraceCollector = null;
- }
- if (this.consoleProgressListener) {
- this.consoleProgressListener.destroy();
- this.consoleProgressListener = null;
- }
- if (this.consoleReflowListener) {
- this.consoleReflowListener.destroy();
- this.consoleReflowListener = null;
- }
- if (this.serverLoggingListener) {
- this.serverLoggingListener.destroy();
- this.serverLoggingListener = null;
- }
- events.off(this.parentActor, "changed-toplevel-document",
- this._onChangedToplevelDocument);
- this.conn.removeActorPool(this._actorPool);
- if (this.parentActor.isRootActor) {
- Services.obs.removeObserver(this._onObserverNotification,
- "last-pb-context-exited");
- }
- this._actorPool = null;
- this._webConsoleCommandsCache = null;
- this._lastConsoleInputEvaluation = null;
- this._evalWindow = null;
- this._netEvents.clear();
- this.dbg.enabled = false;
- this.dbg = null;
- this.conn = null;
- },
- /**
- * Create and return an environment actor that corresponds to the provided
- * Debugger.Environment. This is a straightforward clone of the ThreadActor's
- * method except that it stores the environment actor in the web console
- * actor's pool.
- *
- * @param Debugger.Environment aEnvironment
- * The lexical environment we want to extract.
- * @return The EnvironmentActor for aEnvironment or undefined for host
- * functions or functions scoped to a non-debuggee global.
- */
- createEnvironmentActor: function WCA_createEnvironmentActor(aEnvironment) {
- if (!aEnvironment) {
- return undefined;
- }
- if (aEnvironment.actor) {
- return aEnvironment.actor;
- }
- let actor = new EnvironmentActor(aEnvironment, this);
- this._actorPool.addActor(actor);
- aEnvironment.actor = actor;
- return actor;
- },
- /**
- * Create a grip for the given value.
- *
- * @param mixed aValue
- * @return object
- */
- createValueGrip: function WCA_createValueGrip(aValue)
- {
- return createValueGrip(aValue, this._actorPool, this.objectGrip);
- },
- /**
- * Make a debuggee value for the given value.
- *
- * @param mixed aValue
- * The value you want to get a debuggee value for.
- * @param boolean aUseObjectGlobal
- * If |true| the object global is determined and added as a debuggee,
- * otherwise |this.window| is used when makeDebuggeeValue() is invoked.
- * @return object
- * Debuggee value for |aValue|.
- */
- makeDebuggeeValue: function WCA_makeDebuggeeValue(aValue, aUseObjectGlobal)
- {
- if (aUseObjectGlobal && typeof aValue == "object") {
- try {
- let global = Cu.getGlobalForObject(aValue);
- let dbgGlobal = this.dbg.makeGlobalObjectReference(global);
- return dbgGlobal.makeDebuggeeValue(aValue);
- }
- catch (ex) {
- // The above can throw an exception if aValue is not an actual object
- // or 'Object in compartment marked as invisible to Debugger'
- }
- }
- let dbgGlobal = this.dbg.makeGlobalObjectReference(this.window);
- return dbgGlobal.makeDebuggeeValue(aValue);
- },
- /**
- * Create a grip for the given object.
- *
- * @param object aObject
- * The object you want.
- * @param object aPool
- * An ActorPool where the new actor instance is added.
- * @param object
- * The object grip.
- */
- objectGrip: function WCA_objectGrip(aObject, aPool)
- {
- let actor = new ObjectActor(aObject, {
- getGripDepth: () => this._gripDepth,
- incrementGripDepth: () => this._gripDepth++,
- decrementGripDepth: () => this._gripDepth--,
- createValueGrip: v => this.createValueGrip(v),
- sources: () => DevToolsUtils.reportException("WebConsoleActor",
- Error("sources not yet implemented")),
- createEnvironmentActor: (env) => this.createEnvironmentActor(env),
- getGlobalDebugObject: () => this.globalDebugObject
- });
- aPool.addActor(actor);
- return actor.grip();
- },
- /**
- * Create a grip for the given string.
- *
- * @param string aString
- * The string you want to create the grip for.
- * @param object aPool
- * An ActorPool where the new actor instance is added.
- * @return object
- * A LongStringActor object that wraps the given string.
- */
- longStringGrip: function WCA_longStringGrip(aString, aPool)
- {
- let actor = new LongStringActor(aString);
- aPool.addActor(actor);
- return actor.grip();
- },
- /**
- * Create a long string grip if needed for the given string.
- *
- * @private
- * @param string aString
- * The string you want to create a long string grip for.
- * @return string|object
- * A string is returned if |aString| is not a long string.
- * A LongStringActor grip is returned if |aString| is a long string.
- */
- _createStringGrip: function NEA__createStringGrip(aString)
- {
- if (aString && stringIsLong(aString)) {
- return this.longStringGrip(aString, this._actorPool);
- }
- return aString;
- },
- /**
- * Get an object actor by its ID.
- *
- * @param string aActorID
- * @return object
- */
- getActorByID: function WCA_getActorByID(aActorID)
- {
- return this._actorPool.get(aActorID);
- },
- /**
- * Release an actor.
- *
- * @param object aActor
- * The actor instance you want to release.
- */
- releaseActor: function WCA_releaseActor(aActor)
- {
- this._actorPool.removeActor(aActor.actorID);
- },
- /**
- * Returns the latest web console input evaluation.
- * This is undefined if no evaluations have been completed.
- *
- * @return object
- */
- getLastConsoleInputEvaluation: function WCU_getLastConsoleInputEvaluation()
- {
- return this._lastConsoleInputEvaluation;
- },
- // Request handlers for known packet types.
- /**
- * Handler for the "startListeners" request.
- *
- * @param object aRequest
- * The JSON request object received from the Web Console client.
- * @return object
- * The response object which holds the startedListeners array.
- */
- onStartListeners: function WCA_onStartListeners(aRequest)
- {
- // XXXworkers: Not handling the Console API yet for workers (Bug 1209353).
- if (isWorker) {
- aRequest.listeners = [];
- }
- let startedListeners = [];
- let window = !this.parentActor.isRootActor ? this.window : null;
- let appId = null;
- let messageManager = null;
- if (this._parentIsContentActor) {
- appId = this.parentActor.docShell.appId;
- messageManager = this.parentActor.messageManager;
- }
- while (aRequest.listeners.length > 0) {
- let listener = aRequest.listeners.shift();
- switch (listener) {
- case "PageError":
- if (!this.consoleServiceListener) {
- this.consoleServiceListener =
- new ConsoleServiceListener(window, this);
- this.consoleServiceListener.init();
- }
- startedListeners.push(listener);
- break;
- case "ConsoleAPI":
- if (!this.consoleAPIListener) {
- // Create the consoleAPIListener (and apply the filtering options defined
- // in the parent actor).
- this.consoleAPIListener =
- new ConsoleAPIListener(window, this,
- this.parentActor.consoleAPIListenerOptions);
- this.consoleAPIListener.init();
- }
- startedListeners.push(listener);
- break;
- case "NetworkActivity":
- if (!this.networkMonitor) {
- // Create a StackTraceCollector that's going to be shared both by the
- // NetworkMonitorChild (getting messages about requests from parent) and
- // by the NetworkMonitor that directly watches service workers requests.
- this.stackTraceCollector = new StackTraceCollector({ window, appId });
- this.stackTraceCollector.init();
- let processBoundary = Services.appinfo.processType !=
- Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
- if ((appId || messageManager) && processBoundary) {
- // Start a network monitor in the parent process to listen to
- // most requests than happen in parent
- this.networkMonitor =
- new NetworkMonitorChild(appId, this.parentActor.outerWindowID,
- messageManager, this.conn, this);
- this.networkMonitor.init();
- // Spawn also one in the child to listen to service workers
- this.networkMonitorChild = new NetworkMonitor({ window }, this);
- this.networkMonitorChild.init();
- } else {
- this.networkMonitor = new NetworkMonitor({ window }, this);
- this.networkMonitor.init();
- }
- }
- startedListeners.push(listener);
- break;
- case "FileActivity":
- if (this.window instanceof Ci.nsIDOMWindow) {
- if (!this.consoleProgressListener) {
- this.consoleProgressListener =
- new ConsoleProgressListener(this.window, this);
- }
- this.consoleProgressListener.startMonitor(this.consoleProgressListener.
- MONITOR_FILE_ACTIVITY);
- startedListeners.push(listener);
- }
- break;
- case "ReflowActivity":
- if (!this.consoleReflowListener) {
- this.consoleReflowListener =
- new ConsoleReflowListener(this.window, this);
- }
- startedListeners.push(listener);
- break;
- case "ServerLogging":
- if (!this.serverLoggingListener) {
- this.serverLoggingListener =
- new ServerLoggingListener(this.window, this);
- }
- startedListeners.push(listener);
- break;
- }
- }
- // Update the live list of running listeners
- startedListeners.forEach(this._listeners.add, this._listeners);
- return {
- startedListeners: startedListeners,
- nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
- traits: this.traits,
- };
- },
- /**
- * Handler for the "stopListeners" request.
- *
- * @param object aRequest
- * The JSON request object received from the Web Console client.
- * @return object
- * The response packet to send to the client: holds the
- * stoppedListeners array.
- */
- onStopListeners: function WCA_onStopListeners(aRequest)
- {
- let stoppedListeners = [];
- // If no specific listeners are requested to be detached, we stop all
- // listeners.
- let toDetach = aRequest.listeners ||
- ["PageError", "ConsoleAPI", "NetworkActivity",
- "FileActivity", "ServerLogging"];
- while (toDetach.length > 0) {
- let listener = toDetach.shift();
- switch (listener) {
- case "PageError":
- if (this.consoleServiceListener) {
- this.consoleServiceListener.destroy();
- this.consoleServiceListener = null;
- }
- stoppedListeners.push(listener);
- break;
- case "ConsoleAPI":
- if (this.consoleAPIListener) {
- this.consoleAPIListener.destroy();
- this.consoleAPIListener = null;
- }
- stoppedListeners.push(listener);
- break;
- case "NetworkActivity":
- if (this.networkMonitor) {
- this.networkMonitor.destroy();
- this.networkMonitor = null;
- }
- if (this.networkMonitorChild) {
- this.networkMonitorChild.destroy();
- this.networkMonitorChild = null;
- }
- if (this.stackTraceCollector) {
- this.stackTraceCollector.destroy();
- this.stackTraceCollector = null;
- }
- stoppedListeners.push(listener);
- break;
- case "FileActivity":
- if (this.consoleProgressListener) {
- this.consoleProgressListener.stopMonitor(this.consoleProgressListener.
- MONITOR_FILE_ACTIVITY);
- this.consoleProgressListener = null;
- }
- stoppedListeners.push(listener);
- break;
- case "ReflowActivity":
- if (this.consoleReflowListener) {
- this.consoleReflowListener.destroy();
- this.consoleReflowListener = null;
- }
- stoppedListeners.push(listener);
- break;
- case "ServerLogging":
- if (this.serverLoggingListener) {
- this.serverLoggingListener.destroy();
- this.serverLoggingListener = null;
- }
- stoppedListeners.push(listener);
- break;
- }
- }
- // Update the live list of running listeners
- stoppedListeners.forEach(this._listeners.delete, this._listeners);
- return { stoppedListeners: stoppedListeners };
- },
- /**
- * Handler for the "getCachedMessages" request. This method sends the cached
- * error messages and the window.console API calls to the client.
- *
- * @param object aRequest
- * The JSON request object received from the Web Console client.
- * @return object
- * The response packet to send to the client: it holds the cached
- * messages array.
- */
- onGetCachedMessages: function WCA_onGetCachedMessages(aRequest)
- {
- let types = aRequest.messageTypes;
- if (!types) {
- return {
- error: "missingParameter",
- message: "The messageTypes parameter is missing.",
- };
- }
- let messages = [];
- while (types.length > 0) {
- let type = types.shift();
- switch (type) {
- case "ConsoleAPI": {
- if (!this.consoleAPIListener) {
- break;
- }
- // See `window` definition. It isn't always a DOM Window.
- let winStartTime = this.window && this.window.performance ?
- this.window.performance.timing.navigationStart : 0;
- let cache = this.consoleAPIListener
- .getCachedMessages(!this.parentActor.isRootActor);
- cache.forEach((aMessage) => {
- // Filter out messages that came from a ServiceWorker but happened
- // before the page was requested.
- if (aMessage.innerID === "ServiceWorker" &&
- winStartTime > aMessage.timeStamp) {
- return;
- }
- let message = this.prepareConsoleMessageForRemote(aMessage);
- message._type = type;
- messages.push(message);
- });
- break;
- }
- case "PageError": {
- if (!this.consoleServiceListener) {
- break;
- }
- let cache = this.consoleServiceListener
- .getCachedMessages(!this.parentActor.isRootActor);
- cache.forEach((aMessage) => {
- let message = null;
- if (aMessage instanceof Ci.nsIScriptError) {
- message = this.preparePageErrorForRemote(aMessage);
- message._type = type;
- }
- else {
- message = {
- _type: "LogMessage",
- message: this._createStringGrip(aMessage.message),
- timeStamp: aMessage.timeStamp,
- };
- }
- messages.push(message);
- });
- break;
- }
- }
- }
- return {
- from: this.actorID,
- messages: messages,
- };
- },
- /**
- * Handler for the "evaluateJSAsync" request. This method evaluates the given
- * JavaScript string and sends back a packet with a unique ID.
- * The result will be returned later as an unsolicited `evaluationResult`,
- * that can be associated back to this request via the `resultID` field.
- *
- * @param object aRequest
- * The JSON request object received from the Web Console client.
- * @return object
- * The response packet to send to with the unique id in the
- * `resultID` field.
- */
- onEvaluateJSAsync: function WCA_onEvaluateJSAsync(aRequest)
- {
- // We want to be able to run console commands without waiting
- // for the first to return (see Bug 1088861).
- // First, send a response packet with the id only.
- let resultID = Date.now();
- this.conn.send({
- from: this.actorID,
- resultID: resultID
- });
- // Then, execute the script that may pause.
- let response = this.onEvaluateJS(aRequest);
- response.resultID = resultID;
- // Finally, send an unsolicited evaluationResult packet with
- // the normal return value
- this.conn.sendActorEvent(this.actorID, "evaluationResult", response);
- },
- /**
- * Handler for the "evaluateJS" request. This method evaluates the given
- * JavaScript string and sends back the result.
- *
- * @param object aRequest
- * The JSON request object received from the Web Console client.
- * @return object
- * The evaluation response packet.
- */
- onEvaluateJS: function WCA_onEvaluateJS(aRequest)
- {
- let input = aRequest.text;
- let timestamp = Date.now();
- let evalOptions = {
- bindObjectActor: aRequest.bindObjectActor,
- frameActor: aRequest.frameActor,
- url: aRequest.url,
- selectedNodeActor: aRequest.selectedNodeActor,
- selectedObjectActor: aRequest.selectedObjectActor,
- };
- let evalInfo = this.evalWithDebugger(input, evalOptions);
- let evalResult = evalInfo.result;
- let helperResult = evalInfo.helperResult;
- let result, errorDocURL, errorMessage, errorNotes = null, errorGrip = null,
- frame = null;
- if (evalResult) {
- if ("return" in evalResult) {
- result = evalResult.return;
- } else if ("yield" in evalResult) {
- result = evalResult.yield;
- } else if ("throw" in evalResult) {
- let error = evalResult.throw;
- errorGrip = this.createValueGrip(error);
- errorMessage = String(error);
- if (typeof error === "object" && error !== null) {
- try {
- errorMessage = DevToolsUtils.callPropertyOnObject(error, "toString");
- } catch (e) {
- // If the debuggee is not allowed to access the "toString" property
- // of the error object, calling this property from the debuggee's
- // compartment will fail. The debugger should show the error object
- // as it is seen by the debuggee, so this behavior is correct.
- //
- // Unfortunately, we have at least one test that assumes calling the
- // "toString" property of an error object will succeed if the
- // debugger is allowed to access it, regardless of whether the
- // debuggee is allowed to access it or not.
- //
- // To accomodate these tests, if calling the "toString" property
- // from the debuggee compartment fails, we rewrap the error object
- // in the debugger's compartment, and then call the "toString"
- // property from there.
- if (typeof error.unsafeDereference === "function") {
- errorMessage = error.unsafeDereference().toString();
- }
- }
- }
- // It is possible that we won't have permission to unwrap an
- // object and retrieve its errorMessageName.
- try {
- errorDocURL = ErrorDocs.GetURL(error);
- } catch (ex) {}
- try {
- let line = error.errorLineNumber;
- let column = error.errorColumnNumber;
- if (typeof line === "number" && typeof column === "number") {
- // Set frame only if we have line/column numbers.
- frame = {
- source: "debugger eval code",
- line,
- column
- };
- }
- } catch (ex) {}
- try {
- let notes = error.errorNotes;
- if (notes && notes.length) {
- errorNotes = [];
- for (let note of notes) {
- errorNotes.push({
- messageBody: this._createStringGrip(note.message),
- frame: {
- source: note.fileName,
- line: note.lineNumber,
- column: note.columnNumber,
- }
- });
- }
- }
- } catch (ex) {}
- }
- }
- // If a value is encountered that the debugger server doesn't support yet,
- // the console should remain functional.
- let resultGrip;
- try {
- resultGrip = this.createValueGrip(result);
- } catch (e) {
- errorMessage = e;
- }
- this._lastConsoleInputEvaluation = result;
- return {
- from: this.actorID,
- input: input,
- result: resultGrip,
- timestamp: timestamp,
- exception: errorGrip,
- exceptionMessage: this._createStringGrip(errorMessage),
- exceptionDocURL: errorDocURL,
- frame,
- helperResult: helperResult,
- notes: errorNotes,
- };
- },
- /**
- * The Autocomplete request handler.
- *
- * @param object aRequest
- * The request message - what input to autocomplete.
- * @return object
- * The response message - matched properties.
- */
- onAutocomplete: function WCA_onAutocomplete(aRequest)
- {
- let frameActorId = aRequest.frameActor;
- let dbgObject = null;
- let environment = null;
- let hadDebuggee = false;
- // This is the case of the paused debugger
- if (frameActorId) {
- let frameActor = this.conn.getActor(frameActorId);
- try {
- // Need to try/catch since accessing frame.environment
- // can throw "Debugger.Frame is not live"
- let frame = frameActor.frame;
- environment = frame.environment;
- } catch (e) {
- DevToolsUtils.reportException("onAutocomplete",
- Error("The frame actor was not found: " + frameActorId));
- }
- }
- // This is the general case (non-paused debugger)
- else {
- hadDebuggee = this.dbg.hasDebuggee(this.evalWindow);
- dbgObject = this.dbg.addDebuggee(this.evalWindow);
- }
- let result = JSPropertyProvider(dbgObject, environment, aRequest.text,
- aRequest.cursor, frameActorId) || {};
- if (!hadDebuggee && dbgObject) {
- this.dbg.removeDebuggee(this.evalWindow);
- }
- let matches = result.matches || [];
- let reqText = aRequest.text.substr(0, aRequest.cursor);
- // We consider '$' as alphanumerc because it is used in the names of some
- // helper functions.
- let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
- if (!lastNonAlphaIsDot) {
- if (!this._webConsoleCommandsCache) {
- let helpers = {
- sandbox: Object.create(null)
- };
- addWebConsoleCommands(helpers);
- this._webConsoleCommandsCache =
- Object.getOwnPropertyNames(helpers.sandbox);
- }
- matches = matches.concat(this._webConsoleCommandsCache
- .filter(n => n.startsWith(result.matchProp)));
- }
- return {
- from: this.actorID,
- matches: matches.sort(),
- matchProp: result.matchProp,
- };
- },
- /**
- * The "clearMessagesCache" request handler.
- */
- onClearMessagesCache: function WCA_onClearMessagesCache()
- {
- // TODO: Bug 717611 - Web Console clear button does not clear cached errors
- let windowId = !this.parentActor.isRootActor ?
- WebConsoleUtils.getInnerWindowId(this.window) : null;
- let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
- .getService(Ci.nsIConsoleAPIStorage);
- ConsoleAPIStorage.clearEvents(windowId);
- CONSOLE_WORKER_IDS.forEach((aId) => {
- ConsoleAPIStorage.clearEvents(aId);
- });
- if (this.parentActor.isRootActor) {
- Services.console.logStringMessage(null); // for the Error Console
- Services.console.reset();
- }
- return {};
- },
- /**
- * The "getPreferences" request handler.
- *
- * @param object aRequest
- * The request message - which preferences need to be retrieved.
- * @return object
- * The response message - a { key: value } object map.
- */
- onGetPreferences: function WCA_onGetPreferences(aRequest)
- {
- let prefs = Object.create(null);
- for (let key of aRequest.preferences) {
- prefs[key] = this._prefs[key];
- }
- return { preferences: prefs };
- },
- /**
- * The "setPreferences" request handler.
- *
- * @param object aRequest
- * The request message - which preferences need to be updated.
- */
- onSetPreferences: function WCA_onSetPreferences(aRequest)
- {
- for (let key in aRequest.preferences) {
- this._prefs[key] = aRequest.preferences[key];
- if (this.networkMonitor) {
- if (key == "NetworkMonitor.saveRequestAndResponseBodies") {
- this.networkMonitor.saveRequestAndResponseBodies = this._prefs[key];
- if (this.networkMonitorChild) {
- this.networkMonitorChild.saveRequestAndResponseBodies =
- this._prefs[key];
- }
- } else if (key == "NetworkMonitor.throttleData") {
- this.networkMonitor.throttleData = this._prefs[key];
- if (this.networkMonitorChild) {
- this.networkMonitorChild.throttleData = this._prefs[key];
- }
- }
- }
- }
- return { updated: Object.keys(aRequest.preferences) };
- },
- // End of request handlers.
- /**
- * Create an object with the API we expose to the Web Console during
- * JavaScript evaluation.
- * This object inherits properties and methods from the Web Console actor.
- *
- * @private
- * @param object aDebuggerGlobal
- * A Debugger.Object that wraps a content global. This is used for the
- * Web Console Commands.
- * @return object
- * The same object as |this|, but with an added |sandbox| property.
- * The sandbox holds methods and properties that can be used as
- * bindings during JS evaluation.
- */
- _getWebConsoleCommands: function (aDebuggerGlobal)
- {
- let helpers = {
- window: this.evalWindow,
- chromeWindow: this.chromeWindow.bind(this),
- makeDebuggeeValue: aDebuggerGlobal.makeDebuggeeValue.bind(aDebuggerGlobal),
- createValueGrip: this.createValueGrip.bind(this),
- sandbox: Object.create(null),
- helperResult: null,
- consoleActor: this,
- };
- addWebConsoleCommands(helpers);
- let evalWindow = this.evalWindow;
- function maybeExport(obj, name) {
- if (typeof obj[name] != "function") {
- return;
- }
- // By default, chrome-implemented functions that are exposed to content
- // refuse to accept arguments that are cross-origin for the caller. This
- // is generally the safe thing, but causes problems for certain console
- // helpers like cd(), where we users sometimes want to pass a cross-origin
- // window. To circumvent this restriction, we use exportFunction along
- // with a special option designed for this purpose. See bug 1051224.
- obj[name] =
- Cu.exportFunction(obj[name], evalWindow, { allowCrossOriginArguments: true });
- }
- for (let name in helpers.sandbox) {
- let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name);
- // Workers don't have access to Cu so won't be able to exportFunction.
- if (!isWorker) {
- maybeExport(desc, "get");
- maybeExport(desc, "set");
- maybeExport(desc, "value");
- }
- if (desc.value) {
- // Make sure the helpers can be used during eval.
- desc.value = aDebuggerGlobal.makeDebuggeeValue(desc.value);
- }
- Object.defineProperty(helpers.sandbox, name, desc);
- }
- return helpers;
- },
- /**
- * Evaluates a string using the debugger API.
- *
- * To allow the variables view to update properties from the Web Console we
- * provide the "bindObjectActor" mechanism: the Web Console tells the
- * ObjectActor ID for which it desires to evaluate an expression. The
- * Debugger.Object pointed at by the actor ID is bound such that it is
- * available during expression evaluation (executeInGlobalWithBindings()).
- *
- * Example:
- * _self['foobar'] = 'test'
- * where |_self| refers to the desired object.
- *
- * The |frameActor| property allows the Web Console client to provide the
- * frame actor ID, such that the expression can be evaluated in the
- * user-selected stack frame.
- *
- * For the above to work we need the debugger and the Web Console to share
- * a connection, otherwise the Web Console actor will not find the frame
- * actor.
- *
- * The Debugger.Frame comes from the jsdebugger's Debugger instance, which
- * is different from the Web Console's Debugger instance. This means that
- * for evaluation to work, we need to create a new instance for the Web
- * Console Commands helpers - they need to be Debugger.Objects coming from the
- * jsdebugger's Debugger instance.
- *
- * When |bindObjectActor| is used objects can come from different iframes,
- * from different domains. To avoid permission-related errors when objects
- * come from a different window, we also determine the object's own global,
- * such that evaluation happens in the context of that global. This means that
- * evaluation will happen in the object's iframe, rather than the top level
- * window.
- *
- * @param string aString
- * String to evaluate.
- * @param object [aOptions]
- * Options for evaluation:
- * - bindObjectActor: the ObjectActor ID to use for evaluation.
- * |evalWithBindings()| will be called with one additional binding:
- * |_self| which will point to the Debugger.Object of the given
- * ObjectActor.
- * - selectedObjectActor: Like bindObjectActor, but executes with the
- * top level window as the global.
- * - frameActor: the FrameActor ID to use for evaluation. The given
- * debugger frame is used for evaluation, instead of the global window.
- * - selectedNodeActor: the NodeActor ID of the currently selected node
- * in the Inspector (or null, if there is no selection). This is used
- * for helper functions that make reference to the currently selected
- * node, like $0.
- * - url: the url to evaluate the script as. Defaults to
- * "debugger eval code".
- * @return object
- * An object that holds the following properties:
- * - dbg: the debugger where the string was evaluated.
- * - frame: (optional) the frame where the string was evaluated.
- * - window: the Debugger.Object for the global where the string was
- * evaluated.
- * - result: the result of the evaluation.
- * - helperResult: any result coming from a Web Console commands
- * function.
- */
- evalWithDebugger: function WCA_evalWithDebugger(aString, aOptions = {})
- {
- let trimmedString = aString.trim();
- // The help function needs to be easy to guess, so we make the () optional.
- if (trimmedString == "help" || trimmedString == "?") {
- aString = "help()";
- }
- // Add easter egg for console.mihai().
- if (trimmedString == "console.mihai()" || trimmedString == "console.mihai();") {
- aString = "\"http://incompleteness.me/blog/2015/02/09/console-dot-mihai/\"";
- }
- // Find the Debugger.Frame of the given FrameActor.
- let frame = null, frameActor = null;
- if (aOptions.frameActor) {
- frameActor = this.conn.getActor(aOptions.frameActor);
- if (frameActor) {
- frame = frameActor.frame;
- }
- else {
- DevToolsUtils.reportException("evalWithDebugger",
- Error("The frame actor was not found: " + aOptions.frameActor));
- }
- }
- // If we've been given a frame actor in whose scope we should evaluate the
- // expression, be sure to use that frame's Debugger (that is, the JavaScript
- // debugger's Debugger) for the whole operation, not the console's Debugger.
- // (One Debugger will treat a different Debugger's Debugger.Object instances
- // as ordinary objects, not as references to be followed, so mixing
- // debuggers causes strange behaviors.)
- let dbg = frame ? frameActor.threadActor.dbg : this.dbg;
- let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
- // If we have an object to bind to |_self|, create a Debugger.Object
- // referring to that object, belonging to dbg.
- let bindSelf = null;
- if (aOptions.bindObjectActor || aOptions.selectedObjectActor) {
- let objActor = this.getActorByID(aOptions.bindObjectActor ||
- aOptions.selectedObjectActor);
- if (objActor) {
- let jsObj = objActor.obj.unsafeDereference();
- // If we use the makeDebuggeeValue method of jsObj's own global, then
- // we'll get a D.O that sees jsObj as viewed from its own compartment -
- // that is, without wrappers. The evalWithBindings call will then wrap
- // jsObj appropriately for the evaluation compartment.
- let global = Cu.getGlobalForObject(jsObj);
- let _dbgWindow = dbg.makeGlobalObjectReference(global);
- bindSelf = dbgWindow.makeDebuggeeValue(jsObj);
- if (aOptions.bindObjectActor) {
- dbgWindow = _dbgWindow;
- }
- }
- }
- // Get the Web Console commands for the given debugger window.
- let helpers = this._getWebConsoleCommands(dbgWindow);
- let bindings = helpers.sandbox;
- if (bindSelf) {
- bindings._self = bindSelf;
- }
- if (aOptions.selectedNodeActor) {
- let actor = this.conn.getActor(aOptions.selectedNodeActor);
- if (actor) {
- helpers.selectedNode = actor.rawNode;
- }
- }
- // Check if the Debugger.Frame or Debugger.Object for the global include
- // $ or $$. We will not overwrite these functions with the Web Console
- // commands.
- let found$ = false, found$$ = false;
- if (frame) {
- let env = frame.environment;
- if (env) {
- found$ = !!env.find("$");
- found$$ = !!env.find("$$");
- }
- }
- else {
- found$ = !!dbgWindow.getOwnPropertyDescriptor("$");
- found$$ = !!dbgWindow.getOwnPropertyDescriptor("$$");
- }
- let $ = null, $$ = null;
- if (found$) {
- $ = bindings.$;
- delete bindings.$;
- }
- if (found$$) {
- $$ = bindings.$$;
- delete bindings.$$;
- }
- // Ready to evaluate the string.
- helpers.evalInput = aString;
- let evalOptions;
- if (typeof aOptions.url == "string") {
- evalOptions = { url: aOptions.url };
- }
- // If the debugger object is changed from the last evaluation,
- // adopt this._lastConsoleInputEvaluation value in the new debugger,
- // to prevents "Debugger.Object belongs to a different Debugger" exceptions
- // related to the $_ bindings.
- if (this._lastConsoleInputEvaluation &&
- this._lastConsoleInputEvaluation.global !== dbgWindow) {
- this._lastConsoleInputEvaluation = dbg.adoptDebuggeeValue(
- this._lastConsoleInputEvaluation
- );
- }
- let result;
- if (frame) {
- result = frame.evalWithBindings(aString, bindings, evalOptions);
- }
- else {
- result = dbgWindow.executeInGlobalWithBindings(aString, bindings, evalOptions);
- // Attempt to initialize any declarations found in the evaluated string
- // since they may now be stuck in an "initializing" state due to the
- // error. Already-initialized bindings will be ignored.
- if ("throw" in result) {
- let ast;
- // Parse errors will raise an exception. We can/should ignore the error
- // since it's already being handled elsewhere and we are only interested
- // in initializing bindings.
- try {
- ast = Parser.reflectionAPI.parse(aString);
- } catch (ex) {
- ast = {"body": []};
- }
- for (let line of ast.body) {
- // Only let and const declarations put bindings into an
- // "initializing" state.
- if (!(line.kind == "let" || line.kind == "const"))
- continue;
- let identifiers = [];
- for (let decl of line.declarations) {
- switch (decl.id.type) {
- case "Identifier":
- // let foo = bar;
- identifiers.push(decl.id.name);
- break;
- case "ArrayPattern":
- // let [foo, bar] = [1, 2];
- // let [foo=99, bar] = [1, 2];
- for (let e of decl.id.elements) {
- if (e.type == "Identifier") {
- identifiers.push(e.name);
- } else if (e.type == "AssignmentExpression") {
- identifiers.push(e.left.name);
- }
- }
- break;
- case "ObjectPattern":
- // let {bilbo, my} = {bilbo: "baggins", my: "precious"};
- // let {blah: foo} = {blah: yabba()}
- // let {blah: foo=99} = {blah: yabba()}
- for (let prop of decl.id.properties) {
- // key
- if (prop.key.type == "Identifier")
- identifiers.push(prop.key.name);
- // value
- if (prop.value.type == "Identifier") {
- identifiers.push(prop.value.name);
- } else if (prop.value.type == "AssignmentExpression") {
- identifiers.push(prop.value.left.name);
- }
- }
- break;
- }
- }
- for (let name of identifiers)
- dbgWindow.forceLexicalInitializationByName(name);
- }
- }
- }
- let helperResult = helpers.helperResult;
- delete helpers.evalInput;
- delete helpers.helperResult;
- delete helpers.selectedNode;
- if ($) {
- bindings.$ = $;
- }
- if ($$) {
- bindings.$$ = $$;
- }
- if (bindings._self) {
- delete bindings._self;
- }
- return {
- result: result,
- helperResult: helperResult,
- dbg: dbg,
- frame: frame,
- window: dbgWindow,
- };
- },
- // Event handlers for various listeners.
- /**
- * Handler for messages received from the ConsoleServiceListener. This method
- * sends the nsIConsoleMessage to the remote Web Console client.
- *
- * @param nsIConsoleMessage aMessage
- * The message we need to send to the client.
- */
- onConsoleServiceMessage: function WCA_onConsoleServiceMessage(aMessage)
- {
- let packet;
- if (aMessage instanceof Ci.nsIScriptError) {
- packet = {
- from: this.actorID,
- type: "pageError",
- pageError: this.preparePageErrorForRemote(aMessage),
- };
- }
- else {
- packet = {
- from: this.actorID,
- type: "logMessage",
- message: this._createStringGrip(aMessage.message),
- timeStamp: aMessage.timeStamp,
- };
- }
- this.conn.send(packet);
- },
- /**
- * Prepare an nsIScriptError to be sent to the client.
- *
- * @param nsIScriptError aPageError
- * The page error we need to send to the client.
- * @return object
- * The object you can send to the remote client.
- */
- preparePageErrorForRemote: function WCA_preparePageErrorForRemote(aPageError)
- {
- let stack = null;
- // Convert stack objects to the JSON attributes expected by client code
- if (aPageError.stack) {
- stack = [];
- let s = aPageError.stack;
- while (s !== null) {
- stack.push({
- filename: s.source,
- lineNumber: s.line,
- columnNumber: s.column,
- functionName: s.functionDisplayName
- });
- s = s.parent;
- }
- }
- let lineText = aPageError.sourceLine;
- if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
- lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
- }
- let notesArray = null;
- let notes = aPageError.notes;
- if (notes && notes.length) {
- notesArray = [];
- for (let i = 0, len = notes.length; i < len; i++) {
- let note = notes.queryElementAt(i, Ci.nsIScriptErrorNote);
- notesArray.push({
- messageBody: this._createStringGrip(note.errorMessage),
- frame: {
- source: note.sourceName,
- line: note.lineNumber,
- column: note.columnNumber,
- }
- });
- }
- }
- return {
- errorMessage: this._createStringGrip(aPageError.errorMessage),
- errorMessageName: aPageError.errorMessageName,
- exceptionDocURL: ErrorDocs.GetURL(aPageError),
- sourceName: aPageError.sourceName,
- lineText: lineText,
- lineNumber: aPageError.lineNumber,
- columnNumber: aPageError.columnNumber,
- category: aPageError.category,
- timeStamp: aPageError.timeStamp,
- warning: !!(aPageError.flags & aPageError.warningFlag),
- error: !!(aPageError.flags & aPageError.errorFlag),
- exception: !!(aPageError.flags & aPageError.exceptionFlag),
- strict: !!(aPageError.flags & aPageError.strictFlag),
- info: !!(aPageError.flags & aPageError.infoFlag),
- private: aPageError.isFromPrivateWindow,
- stacktrace: stack,
- notes: notesArray,
- };
- },
- /**
- * Handler for window.console API calls received from the ConsoleAPIListener.
- * This method sends the object to the remote Web Console client.
- *
- * @see ConsoleAPIListener
- * @param object aMessage
- * The console API call we need to send to the remote client.
- */
- onConsoleAPICall: function WCA_onConsoleAPICall(aMessage)
- {
- let packet = {
- from: this.actorID,
- type: "consoleAPICall",
- message: this.prepareConsoleMessageForRemote(aMessage),
- };
- this.conn.send(packet);
- },
- /**
- * Handler for network events. This method is invoked when a new network event
- * is about to be recorded.
- *
- * @see NetworkEventActor
- * @see NetworkMonitor from webconsole/utils.js
- *
- * @param object aEvent
- * The initial network request event information.
- * @return object
- * A new NetworkEventActor is returned. This is used for tracking the
- * network request and response.
- */
- onNetworkEvent: function WCA_onNetworkEvent(aEvent)
- {
- let actor = this.getNetworkEventActor(aEvent.channelId);
- actor.init(aEvent);
- let packet = {
- from: this.actorID,
- type: "networkEvent",
- eventActor: actor.grip()
- };
- this.conn.send(packet);
- return actor;
- },
- /**
- * Get the NetworkEventActor for a nsIHttpChannel, if it exists,
- * otherwise create a new one.
- *
- * @param string channelId
- * The id of the channel for the network event.
- * @return object
- * The NetworkEventActor for the given channel.
- */
- getNetworkEventActor: function WCA_getNetworkEventActor(channelId) {
- let actor = this._netEvents.get(channelId);
- if (actor) {
- // delete from map as we should only need to do this check once
- this._netEvents.delete(channelId);
- return actor;
- }
- actor = new NetworkEventActor(this);
- this._actorPool.addActor(actor);
- return actor;
- },
- /**
- * Send a new HTTP request from the target's window.
- *
- * @param object message
- * Object with 'request' - the HTTP request details.
- */
- onSendHTTPRequest(message) {
- let { url, method, headers, body } = message.request;
- // Set the loadingNode and loadGroup to the target document - otherwise the
- // request won't show up in the opened netmonitor.
- let doc = this.window.document;
- let channel = NetUtil.newChannel({
- uri: NetUtil.newURI(url),
- loadingNode: doc,
- securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
- contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
- });
- channel.QueryInterface(Ci.nsIHttpChannel);
- channel.loadGroup = doc.documentLoadGroup;
- channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
- Ci.nsIRequest.INHIBIT_CACHING |
- Ci.nsIRequest.LOAD_ANONYMOUS;
- channel.requestMethod = method;
- for (let {name, value} of headers) {
- channel.setRequestHeader(name, value, false);
- }
- if (body) {
- channel.QueryInterface(Ci.nsIUploadChannel2);
- let bodyStream = Cc["@mozilla.org/io/string-input-stream;1"]
- .createInstance(Ci.nsIStringInputStream);
- bodyStream.setData(body, body.length);
- channel.explicitSetUploadStream(bodyStream, null, -1, method, false);
- }
- NetUtil.asyncFetch(channel, () => {});
- 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()
- };
- },
- /**
- * Handler for file activity. This method sends the file request information
- * to the remote Web Console client.
- *
- * @see ConsoleProgressListener
- * @param string aFileURI
- * The requested file URI.
- */
- onFileActivity: function WCA_onFileActivity(aFileURI)
- {
- let packet = {
- from: this.actorID,
- type: "fileActivity",
- uri: aFileURI,
- };
- this.conn.send(packet);
- },
- /**
- * Handler for reflow activity. This method forwards reflow events to the
- * remote Web Console client.
- *
- * @see ConsoleReflowListener
- * @param Object aReflowInfo
- */
- onReflowActivity: function WCA_onReflowActivity(aReflowInfo)
- {
- let packet = {
- from: this.actorID,
- type: "reflowActivity",
- interruptible: aReflowInfo.interruptible,
- start: aReflowInfo.start,
- end: aReflowInfo.end,
- sourceURL: aReflowInfo.sourceURL,
- sourceLine: aReflowInfo.sourceLine,
- functionName: aReflowInfo.functionName
- };
- this.conn.send(packet);
- },
- /**
- * Handler for server logging. This method forwards log events to the
- * remote Web Console client.
- *
- * @see ServerLoggingListener
- * @param object aMessage
- * The console API call on the server we need to send to the remote client.
- */
- onServerLogCall: function WCA_onServerLogCall(aMessage)
- {
- // Clone all data into the content scope (that's where
- // passed arguments comes from).
- let msg = Cu.cloneInto(aMessage, this.window);
- // All arguments within the message need to be converted into
- // debuggees to properly send it to the client side.
- // Use the default target: this.window as the global object
- // since that's the correct scope for data in the message.
- // The 'false' argument passed into prepareConsoleMessageForRemote()
- // ensures that makeDebuggeeValue uses content debuggee.
- // See also:
- // * makeDebuggeeValue()
- // * prepareConsoleMessageForRemote()
- msg = this.prepareConsoleMessageForRemote(msg, false);
- let packet = {
- from: this.actorID,
- type: "serverLogCall",
- message: msg,
- };
- this.conn.send(packet);
- },
- // End of event handlers for various listeners.
- /**
- * Prepare a message from the console API to be sent to the remote Web Console
- * instance.
- *
- * @param object aMessage
- * The original message received from console-api-log-event.
- * @param boolean aUseObjectGlobal
- * If |true| the object global is determined and added as a debuggee,
- * otherwise |this.window| is used when makeDebuggeeValue() is invoked.
- * @return object
- * The object that can be sent to the remote client.
- */
- prepareConsoleMessageForRemote:
- function WCA_prepareConsoleMessageForRemote(aMessage, aUseObjectGlobal = true)
- {
- let result = WebConsoleUtils.cloneObject(aMessage);
- result.workerType = WebConsoleUtils.getWorkerType(result) || "none";
- delete result.wrappedJSObject;
- delete result.ID;
- delete result.innerID;
- delete result.consoleID;
- result.arguments = Array.map(aMessage.arguments || [], (aObj) => {
- let dbgObj = this.makeDebuggeeValue(aObj, aUseObjectGlobal);
- return this.createValueGrip(dbgObj);
- });
- result.styles = Array.map(aMessage.styles || [], (aString) => {
- return this.createValueGrip(aString);
- });
- result.category = aMessage.category || "webdev";
- return result;
- },
- /**
- * Find the XUL window that owns the content window.
- *
- * @return Window
- * The XUL window that owns the content window.
- */
- chromeWindow: function WCA_chromeWindow()
- {
- let window = null;
- try {
- window = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
- .chromeEventHandler.ownerDocument.defaultView;
- }
- catch (ex) {
- // The above can fail because chromeEventHandler is not available for all
- // kinds of |this.window|.
- }
- return window;
- },
- /**
- * Notification observer for the "last-pb-context-exited" topic.
- *
- * @private
- * @param object aSubject
- * Notification subject - in this case it is the inner window ID that
- * was destroyed.
- * @param string aTopic
- * Notification topic.
- */
- _onObserverNotification: function WCA__onObserverNotification(aSubject, aTopic)
- {
- switch (aTopic) {
- case "last-pb-context-exited":
- this.conn.send({
- from: this.actorID,
- type: "lastPrivateContextExited",
- });
- break;
- }
- },
- /**
- * The "will-navigate" progress listener. This is used to clear the current
- * eval scope.
- */
- _onWillNavigate: function WCA__onWillNavigate({ window, isTopLevel })
- {
- if (isTopLevel) {
- this._evalWindow = null;
- events.off(this.parentActor, "will-navigate", this._onWillNavigate);
- this._progressListenerActive = false;
- }
- },
- /**
- * This listener is called when we switch to another frame,
- * mostly to unregister previous listeners and start listening on the new document.
- */
- _onChangedToplevelDocument: function WCA__onChangedToplevelDocument()
- {
- // Convert the Set to an Array
- let listeners = [...this._listeners];
- // Unregister existing listener on the previous document
- // (pass a copy of the array as it will shift from it)
- this.onStopListeners({listeners: listeners.slice()});
- // This method is called after this.window is changed,
- // so we register new listener on this new window
- this.onStartListeners({listeners: listeners});
- // Also reset the cached top level chrome window being targeted
- this._lastChromeWindow = null;
- },
- };
- WebConsoleActor.prototype.requestTypes =
- {
- startListeners: WebConsoleActor.prototype.onStartListeners,
- stopListeners: WebConsoleActor.prototype.onStopListeners,
- getCachedMessages: WebConsoleActor.prototype.onGetCachedMessages,
- evaluateJS: WebConsoleActor.prototype.onEvaluateJS,
- evaluateJSAsync: WebConsoleActor.prototype.onEvaluateJSAsync,
- autocomplete: WebConsoleActor.prototype.onAutocomplete,
- clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
- getPreferences: WebConsoleActor.prototype.onGetPreferences,
- setPreferences: WebConsoleActor.prototype.onSetPreferences,
- sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest
- };
- exports.WebConsoleActor = WebConsoleActor;
- /**
- * 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;
- this._request = {
- method: null,
- url: null,
- httpVersion: null,
- headers: [],
- cookies: [],
- headersSize: null,
- postData: {},
- };
- this._response = {
- headers: [],
- cookies: [],
- content: {},
- };
- this._timings = {};
- // 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 NEA_grip()
- {
- 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,
- fromCache: this._fromCache,
- fromServiceWorker: this._fromServiceWorker,
- private: this._private,
- };
- },
- /**
- * Releases this actor from the pool.
- */
- release: function NEA_release()
- {
- for (let grip of this._longStringActors) {
- let actor = this.parent.getActorByID(grip.actor);
- if (actor) {
- this.parent.releaseActor(actor);
- }
- }
- this._longStringActors = new Set();
- if (this.channel) {
- this.parent._netEvents.delete(this.channel);
- }
- this.parent.releaseActor(this);
- },
- /**
- * Handle a protocol request to release a grip.
- */
- onRelease: function NEA_onRelease()
- {
- this.release();
- return {};
- },
- /**
- * Set the properties of this actor based on it's corresponding
- * network event.
- *
- * @param object aNetworkEvent
- * The network event associated with this actor.
- */
- init: function NEA_init(aNetworkEvent)
- {
- this._startedDateTime = aNetworkEvent.startedDateTime;
- this._isXHR = aNetworkEvent.isXHR;
- this._cause = aNetworkEvent.cause;
- this._fromCache = aNetworkEvent.fromCache;
- this._fromServiceWorker = aNetworkEvent.fromServiceWorker;
- for (let prop of ["method", "url", "httpVersion", "headersSize"]) {
- this._request[prop] = aNetworkEvent[prop];
- }
- this._discardRequestBody = aNetworkEvent.discardRequestBody;
- this._discardResponseBody = aNetworkEvent.discardResponseBody;
- this._private = aNetworkEvent.private;
- },
- /**
- * The "getRequestHeaders" packet type handler.
- *
- * @return object
- * The response packet - network request headers.
- */
- onGetRequestHeaders: function NEA_onGetRequestHeaders()
- {
- 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 NEA_onGetRequestCookies()
- {
- return {
- from: this.actorID,
- cookies: this._request.cookies,
- };
- },
- /**
- * The "getRequestPostData" packet type handler.
- *
- * @return object
- * The response packet - network POST data.
- */
- onGetRequestPostData: function NEA_onGetRequestPostData()
- {
- 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 NEA_onGetSecurityInfo()
- {
- return {
- from: this.actorID,
- securityInfo: this._securityInfo,
- };
- },
- /**
- * The "getResponseHeaders" packet type handler.
- *
- * @return object
- * The response packet - network response headers.
- */
- onGetResponseHeaders: function NEA_onGetResponseHeaders()
- {
- return {
- from: this.actorID,
- headers: this._response.headers,
- headersSize: this._response.headersSize,
- rawHeaders: this._response.rawHeaders,
- };
- },
- /**
- * The "getResponseCookies" packet type handler.
- *
- * @return object
- * The response packet - network response cookies.
- */
- onGetResponseCookies: function NEA_onGetResponseCookies()
- {
- return {
- from: this.actorID,
- cookies: this._response.cookies,
- };
- },
- /**
- * The "getResponseContent" packet type handler.
- *
- * @return object
- * The response packet - network response content.
- */
- onGetResponseContent: function NEA_onGetResponseContent()
- {
- 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 NEA_onGetEventTimings()
- {
- return {
- from: this.actorID,
- timings: this._timings,
- totalTime: this._totalTime
- };
- },
- /** ****************************************************************
- * Listeners for new network event data coming from NetworkMonitor.
- ******************************************************************/
- /**
- * Add network request headers.
- *
- * @param array aHeaders
- * The request headers array.
- * @param string aRawHeaders
- * The raw headers source.
- */
- addRequestHeaders: function NEA_addRequestHeaders(aHeaders, aRawHeaders)
- {
- this._request.headers = aHeaders;
- this._prepareHeaders(aHeaders);
- var rawHeaders = this.parent._createStringGrip(aRawHeaders);
- if (typeof rawHeaders == "object") {
- this._longStringActors.add(rawHeaders);
- }
- this._request.rawHeaders = rawHeaders;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "requestHeaders",
- headers: aHeaders.length,
- headersSize: this._request.headersSize,
- };
- this.conn.send(packet);
- },
- /**
- * Add network request cookies.
- *
- * @param array aCookies
- * The request cookies array.
- */
- addRequestCookies: function NEA_addRequestCookies(aCookies)
- {
- this._request.cookies = aCookies;
- this._prepareHeaders(aCookies);
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "requestCookies",
- cookies: aCookies.length,
- };
- this.conn.send(packet);
- },
- /**
- * Add network request POST data.
- *
- * @param object aPostData
- * The request POST data.
- */
- addRequestPostData: function NEA_addRequestPostData(aPostData)
- {
- this._request.postData = aPostData;
- aPostData.text = this.parent._createStringGrip(aPostData.text);
- if (typeof aPostData.text == "object") {
- this._longStringActors.add(aPostData.text);
- }
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "requestPostData",
- dataSize: aPostData.text.length,
- discardRequestBody: this._discardRequestBody,
- };
- this.conn.send(packet);
- },
- /**
- * Add the initial network response information.
- *
- * @param object aInfo
- * The response information.
- * @param string aRawHeaders
- * The raw headers source.
- */
- addResponseStart: function NEA_addResponseStart(aInfo, aRawHeaders)
- {
- var rawHeaders = this.parent._createStringGrip(aRawHeaders);
- if (typeof rawHeaders == "object") {
- this._longStringActors.add(rawHeaders);
- }
- this._response.rawHeaders = rawHeaders;
- this._response.httpVersion = aInfo.httpVersion;
- this._response.status = aInfo.status;
- this._response.statusText = aInfo.statusText;
- this._response.headersSize = aInfo.headersSize;
- this._discardResponseBody = aInfo.discardResponseBody;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseStart",
- response: aInfo
- };
- this.conn.send(packet);
- },
- /**
- * Add connection security information.
- *
- * @param object info
- * The object containing security information.
- */
- addSecurityInfo: function NEA_addSecurityInfo(info)
- {
- this._securityInfo = info;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "securityInfo",
- state: info.state,
- };
- this.conn.send(packet);
- },
- /**
- * Add network response headers.
- *
- * @param array aHeaders
- * The response headers array.
- */
- addResponseHeaders: function NEA_addResponseHeaders(aHeaders)
- {
- this._response.headers = aHeaders;
- this._prepareHeaders(aHeaders);
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseHeaders",
- headers: aHeaders.length,
- headersSize: this._response.headersSize,
- };
- this.conn.send(packet);
- },
- /**
- * Add network response cookies.
- *
- * @param array aCookies
- * The response cookies array.
- */
- addResponseCookies: function NEA_addResponseCookies(aCookies)
- {
- this._response.cookies = aCookies;
- this._prepareHeaders(aCookies);
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseCookies",
- cookies: aCookies.length,
- };
- this.conn.send(packet);
- },
- /**
- * Add network response content.
- *
- * @param object aContent
- * The response content.
- * @param boolean aDiscardedResponseBody
- * Tells if the response content was recorded or not.
- */
- addResponseContent:
- function NEA_addResponseContent(aContent, aDiscardedResponseBody)
- {
- this._response.content = aContent;
- aContent.text = this.parent._createStringGrip(aContent.text);
- if (typeof aContent.text == "object") {
- this._longStringActors.add(aContent.text);
- }
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "responseContent",
- mimeType: aContent.mimeType,
- contentSize: aContent.size,
- encoding: aContent.encoding,
- transferredSize: aContent.transferredSize,
- discardResponseBody: aDiscardedResponseBody,
- };
- this.conn.send(packet);
- },
- /**
- * Add network event timing information.
- *
- * @param number aTotal
- * The total time of the network event.
- * @param object aTimings
- * Timing details about the network event.
- */
- addEventTimings: function NEA_addEventTimings(aTotal, aTimings)
- {
- this._totalTime = aTotal;
- this._timings = aTimings;
- let packet = {
- from: this.actorID,
- type: "networkEventUpdate",
- updateType: "eventTimings",
- totalTime: aTotal
- };
- 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 NEA__prepareHeaders(aHeaders)
- {
- for (let header of aHeaders) {
- header.value = this.parent._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,
- "getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
- "getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
- "getSecurityInfo": NetworkEventActor.prototype.onGetSecurityInfo,
- };
|