toolkit/devtools/server/actors/webconsole.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 "use strict";
     9 let {Cc, Ci, Cu} = require("chrome");
    11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    13 let { DebuggerServer, ActorPool } = require("devtools/server/main");
    14 // Symbols from script.js
    15 let { ThreadActor, EnvironmentActor, ObjectActor, LongStringActor } = DebuggerServer;
    17 Cu.import("resource://gre/modules/jsdebugger.jsm");
    18 addDebuggerToGlobal(this);
    20 XPCOMUtils.defineLazyModuleGetter(this, "Services",
    21                                   "resource://gre/modules/Services.jsm");
    22 XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
    23   return require("devtools/toolkit/webconsole/network-monitor")
    24          .NetworkMonitor;
    25 });
    26 XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
    27   return require("devtools/toolkit/webconsole/network-monitor")
    28          .NetworkMonitorChild;
    29 });
    30 XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
    31   return require("devtools/toolkit/webconsole/network-monitor")
    32          .ConsoleProgressListener;
    33 });
    34 XPCOMUtils.defineLazyGetter(this, "events", () => {
    35   return require("sdk/event/core");
    36 });
    38 for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
    39                   "ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
    40                   "ConsoleReflowListener"]) {
    41   Object.defineProperty(this, name, {
    42     get: function(prop) {
    43       if (prop == "WebConsoleUtils") {
    44         prop = "Utils";
    45       }
    46       return require("devtools/toolkit/webconsole/utils")[prop];
    47     }.bind(null, name),
    48     configurable: true,
    49     enumerable: true
    50   });
    51 }
    54 /**
    55  * The WebConsoleActor implements capabilities needed for the Web Console
    56  * feature.
    57  *
    58  * @constructor
    59  * @param object aConnection
    60  *        The connection to the client, DebuggerServerConnection.
    61  * @param object [aParentActor]
    62  *        Optional, the parent actor.
    63  */
    64 function WebConsoleActor(aConnection, aParentActor)
    65 {
    66   this.conn = aConnection;
    67   this.parentActor = aParentActor;
    69   this._actorPool = new ActorPool(this.conn);
    70   this.conn.addActorPool(this._actorPool);
    72   this._prefs = {};
    74   this.dbg = new Debugger();
    76   this._netEvents = new Map();
    77   this._gripDepth = 0;
    79   this._onWillNavigate = this._onWillNavigate.bind(this);
    80   this._onObserverNotification = this._onObserverNotification.bind(this);
    81   if (this.parentActor.isRootActor) {
    82     Services.obs.addObserver(this._onObserverNotification,
    83                              "last-pb-context-exited", false);
    84   }
    86   this.traits = {
    87     customNetworkRequest: !this._parentIsContentActor,
    88   };
    89 }
    91 WebConsoleActor.l10n = new WebConsoleUtils.l10n("chrome://global/locale/console.properties");
    93 WebConsoleActor.prototype =
    94 {
    95   /**
    96    * Debugger instance.
    97    *
    98    * @see jsdebugger.jsm
    99    */
   100   dbg: null,
   102   /**
   103    * This is used by the ObjectActor to keep track of the depth of grip() calls.
   104    * @private
   105    * @type number
   106    */
   107   _gripDepth: null,
   109   /**
   110    * Actor pool for all of the actors we send to the client.
   111    * @private
   112    * @type object
   113    * @see ActorPool
   114    */
   115   _actorPool: null,
   117   /**
   118    * Web Console-related preferences.
   119    * @private
   120    * @type object
   121    */
   122   _prefs: null,
   124   /**
   125    * Holds a map between nsIChannel objects and NetworkEventActors for requests
   126    * created with sendHTTPRequest.
   127    *
   128    * @private
   129    * @type Map
   130    */
   131   _netEvents: null,
   133   /**
   134    * The debugger server connection instance.
   135    * @type object
   136    */
   137   conn: null,
   139   /**
   140    * List of supported features by the console actor.
   141    * @type object
   142    */
   143   traits: null,
   145   /**
   146    * Boolean getter that tells if the parent actor is a ContentActor.
   147    *
   148    * @private
   149    * @type boolean
   150    */
   151   get _parentIsContentActor() {
   152     return "ContentActor" in DebuggerServer &&
   153             this.parentActor instanceof DebuggerServer.ContentActor;
   154   },
   156   /**
   157    * The window we work with.
   158    * @type nsIDOMWindow
   159    */
   160   get window() {
   161     if (this.parentActor.isRootActor) {
   162       return this._getWindowForBrowserConsole();
   163     }
   164     return this.parentActor.window;
   165   },
   167   /**
   168    * Get a window to use for the browser console.
   169    *
   170    * @private
   171    * @return nsIDOMWindow
   172    *         The window to use, or null if no window could be found.
   173    */
   174   _getWindowForBrowserConsole: function WCA__getWindowForBrowserConsole()
   175   {
   176     // Check if our last used chrome window is still live.
   177     let window = this._lastChromeWindow && this._lastChromeWindow.get();
   178     // If not, look for a new one.
   179     if (!window || window.closed) {
   180       window = this.parentActor.window;
   181       if (!window) {
   182         // Try to find the Browser Console window to use instead.
   183         window = Services.wm.getMostRecentWindow("devtools:webconsole");
   184         // We prefer the normal chrome window over the console window,
   185         // so we'll look for those windows in order to replace our reference.
   186         let onChromeWindowOpened = () => {
   187           // We'll look for this window when someone next requests window()
   188           Services.obs.removeObserver(onChromeWindowOpened, "domwindowopened");
   189           this._lastChromeWindow = null;
   190         };
   191         Services.obs.addObserver(onChromeWindowOpened, "domwindowopened", false);
   192       }
   194       this._handleNewWindow(window);
   195     }
   197     return window;
   198   },
   200   /**
   201    * Store a newly found window on the actor to be used in the future.
   202    *
   203    * @private
   204    * @param nsIDOMWindow window
   205    *        The window to store on the actor (can be null).
   206    */
   207   _handleNewWindow: function WCA__handleNewWindow(window)
   208   {
   209     if (window) {
   210       if (this._hadChromeWindow) {
   211         let contextChangedMsg = WebConsoleActor.l10n.getStr("evaluationContextChanged");
   212         Services.console.logStringMessage(contextChangedMsg);
   213       }
   214       this._lastChromeWindow = Cu.getWeakReference(window);
   215       this._hadChromeWindow = true;
   216     } else {
   217       this._lastChromeWindow = null;
   218     }
   219   },
   221   /**
   222    * Whether we've been using a window before.
   223    *
   224    * @private
   225    * @type boolean
   226    */
   227   _hadChromeWindow: false,
   229   /**
   230    * A weak reference to the last chrome window we used to work with.
   231    *
   232    * @private
   233    * @type nsIWeakReference
   234    */
   235   _lastChromeWindow: null,
   237   // The evalWindow is used at the scope for JS evaluation.
   238   _evalWindow: null,
   239   get evalWindow() {
   240     return this._evalWindow || this.window;
   241   },
   243   set evalWindow(aWindow) {
   244     this._evalWindow = aWindow;
   246     if (!this._progressListenerActive) {
   247       events.on(this.parentActor, "will-navigate", this._onWillNavigate);
   248       this._progressListenerActive = true;
   249     }
   250   },
   252   /**
   253    * Flag used to track if we are listening for events from the progress
   254    * listener of the tab actor. We use the progress listener to clear
   255    * this.evalWindow on page navigation.
   256    *
   257    * @private
   258    * @type boolean
   259    */
   260   _progressListenerActive: false,
   262   /**
   263    * The ConsoleServiceListener instance.
   264    * @type object
   265    */
   266   consoleServiceListener: null,
   268   /**
   269    * The ConsoleAPIListener instance.
   270    */
   271   consoleAPIListener: null,
   273   /**
   274    * The NetworkMonitor instance.
   275    */
   276   networkMonitor: null,
   278   /**
   279    * The ConsoleProgressListener instance.
   280    */
   281   consoleProgressListener: null,
   283   /**
   284    * The ConsoleReflowListener instance.
   285    */
   286   consoleReflowListener: null,
   288   /**
   289    * The JSTerm Helpers names cache.
   290    * @private
   291    * @type array
   292    */
   293   _jstermHelpersCache: null,
   295   actorPrefix: "console",
   297   grip: function WCA_grip()
   298   {
   299     return { actor: this.actorID };
   300   },
   302   hasNativeConsoleAPI: function WCA_hasNativeConsoleAPI(aWindow) {
   303     let isNative = false;
   304     try {
   305       // We are very explicitly examining the "console" property of
   306       // the non-Xrayed object here.
   307       let console = aWindow.wrappedJSObject.console;
   308       isNative = console instanceof aWindow.Console;
   309     }
   310     catch (ex) { }
   311     return isNative;
   312   },
   314   _createValueGrip: ThreadActor.prototype.createValueGrip,
   315   _stringIsLong: ThreadActor.prototype._stringIsLong,
   316   _findProtoChain: ThreadActor.prototype._findProtoChain,
   317   _removeFromProtoChain: ThreadActor.prototype._removeFromProtoChain,
   319   /**
   320    * Destroy the current WebConsoleActor instance.
   321    */
   322   disconnect: function WCA_disconnect()
   323   {
   324     if (this.consoleServiceListener) {
   325       this.consoleServiceListener.destroy();
   326       this.consoleServiceListener = null;
   327     }
   328     if (this.consoleAPIListener) {
   329       this.consoleAPIListener.destroy();
   330       this.consoleAPIListener = null;
   331     }
   332     if (this.networkMonitor) {
   333       this.networkMonitor.destroy();
   334       this.networkMonitor = null;
   335     }
   336     if (this.consoleProgressListener) {
   337       this.consoleProgressListener.destroy();
   338       this.consoleProgressListener = null;
   339     }
   340     if (this.consoleReflowListener) {
   341       this.consoleReflowListener.destroy();
   342       this.consoleReflowListener = null;
   343     }
   344     this.conn.removeActorPool(this._actorPool);
   345     if (this.parentActor.isRootActor) {
   346       Services.obs.removeObserver(this._onObserverNotification,
   347                                   "last-pb-context-exited");
   348     }
   349     this._actorPool = null;
   351     this._jstermHelpersCache = null;
   352     this._evalWindow = null;
   353     this._netEvents.clear();
   354     this.dbg.enabled = false;
   355     this.dbg = null;
   356     this.conn = null;
   357   },
   359   /**
   360    * Create and return an environment actor that corresponds to the provided
   361    * Debugger.Environment. This is a straightforward clone of the ThreadActor's
   362    * method except that it stores the environment actor in the web console
   363    * actor's pool.
   364    *
   365    * @param Debugger.Environment aEnvironment
   366    *        The lexical environment we want to extract.
   367    * @return The EnvironmentActor for aEnvironment or undefined for host
   368    *         functions or functions scoped to a non-debuggee global.
   369    */
   370   createEnvironmentActor: function WCA_createEnvironmentActor(aEnvironment) {
   371     if (!aEnvironment) {
   372       return undefined;
   373     }
   375     if (aEnvironment.actor) {
   376       return aEnvironment.actor;
   377     }
   379     let actor = new EnvironmentActor(aEnvironment, this);
   380     this._actorPool.addActor(actor);
   381     aEnvironment.actor = actor;
   383     return actor;
   384   },
   386   /**
   387    * Create a grip for the given value.
   388    *
   389    * @param mixed aValue
   390    * @return object
   391    */
   392   createValueGrip: function WCA_createValueGrip(aValue)
   393   {
   394     return this._createValueGrip(aValue, this._actorPool);
   395   },
   397   /**
   398    * Make a debuggee value for the given value.
   399    *
   400    * @param mixed aValue
   401    *        The value you want to get a debuggee value for.
   402    * @param boolean aUseObjectGlobal
   403    *        If |true| the object global is determined and added as a debuggee,
   404    *        otherwise |this.window| is used when makeDebuggeeValue() is invoked.
   405    * @return object
   406    *         Debuggee value for |aValue|.
   407    */
   408   makeDebuggeeValue: function WCA_makeDebuggeeValue(aValue, aUseObjectGlobal)
   409   {
   410     let global = this.window;
   411     if (aUseObjectGlobal && typeof aValue == "object") {
   412       try {
   413         global = Cu.getGlobalForObject(aValue);
   414       }
   415       catch (ex) {
   416         // The above can throw an exception if aValue is not an actual object.
   417       }
   418     }
   419     let dbgGlobal = this.dbg.makeGlobalObjectReference(global);
   420     return dbgGlobal.makeDebuggeeValue(aValue);
   421   },
   423   /**
   424    * Create a grip for the given object.
   425    *
   426    * @param object aObject
   427    *        The object you want.
   428    * @param object aPool
   429    *        An ActorPool where the new actor instance is added.
   430    * @param object
   431    *        The object grip.
   432    */
   433   objectGrip: function WCA_objectGrip(aObject, aPool)
   434   {
   435     let actor = new ObjectActor(aObject, this);
   436     aPool.addActor(actor);
   437     return actor.grip();
   438   },
   440   /**
   441    * Create a grip for the given string.
   442    *
   443    * @param string aString
   444    *        The string you want to create the grip for.
   445    * @param object aPool
   446    *        An ActorPool where the new actor instance is added.
   447    * @return object
   448    *         A LongStringActor object that wraps the given string.
   449    */
   450   longStringGrip: function WCA_longStringGrip(aString, aPool)
   451   {
   452     let actor = new LongStringActor(aString, this);
   453     aPool.addActor(actor);
   454     return actor.grip();
   455   },
   457   /**
   458    * Create a long string grip if needed for the given string.
   459    *
   460    * @private
   461    * @param string aString
   462    *        The string you want to create a long string grip for.
   463    * @return string|object
   464    *         A string is returned if |aString| is not a long string.
   465    *         A LongStringActor grip is returned if |aString| is a long string.
   466    */
   467   _createStringGrip: function NEA__createStringGrip(aString)
   468   {
   469     if (aString && this._stringIsLong(aString)) {
   470       return this.longStringGrip(aString, this._actorPool);
   471     }
   472     return aString;
   473   },
   475   /**
   476    * Get an object actor by its ID.
   477    *
   478    * @param string aActorID
   479    * @return object
   480    */
   481   getActorByID: function WCA_getActorByID(aActorID)
   482   {
   483     return this._actorPool.get(aActorID);
   484   },
   486   /**
   487    * Release an actor.
   488    *
   489    * @param object aActor
   490    *        The actor instance you want to release.
   491    */
   492   releaseActor: function WCA_releaseActor(aActor)
   493   {
   494     this._actorPool.removeActor(aActor.actorID);
   495   },
   497   //////////////////
   498   // Request handlers for known packet types.
   499   //////////////////
   501   /**
   502    * Handler for the "startListeners" request.
   503    *
   504    * @param object aRequest
   505    *        The JSON request object received from the Web Console client.
   506    * @return object
   507    *         The response object which holds the startedListeners array.
   508    */
   509   onStartListeners: function WCA_onStartListeners(aRequest)
   510   {
   511     let startedListeners = [];
   512     let window = !this.parentActor.isRootActor ? this.window : null;
   513     let appId = null;
   514     let messageManager = null;
   516     if (this._parentIsContentActor) {
   517       appId = this.parentActor.docShell.appId;
   518       messageManager = this.parentActor.messageManager;
   519     }
   521     while (aRequest.listeners.length > 0) {
   522       let listener = aRequest.listeners.shift();
   523       switch (listener) {
   524         case "PageError":
   525           if (!this.consoleServiceListener) {
   526             this.consoleServiceListener =
   527               new ConsoleServiceListener(window, this);
   528             this.consoleServiceListener.init();
   529           }
   530           startedListeners.push(listener);
   531           break;
   532         case "ConsoleAPI":
   533           if (!this.consoleAPIListener) {
   534             this.consoleAPIListener =
   535               new ConsoleAPIListener(window, this);
   536             this.consoleAPIListener.init();
   537           }
   538           startedListeners.push(listener);
   539           break;
   540         case "NetworkActivity":
   541           if (!this.networkMonitor) {
   542             if (appId || messageManager) {
   543               this.networkMonitor =
   544                 new NetworkMonitorChild(appId, messageManager,
   545                                         this.parentActor.actorID, this);
   546             }
   547             else {
   548               this.networkMonitor = new NetworkMonitor({ window: window }, this);
   549             }
   550             this.networkMonitor.init();
   551           }
   552           startedListeners.push(listener);
   553           break;
   554         case "FileActivity":
   555           if (!this.consoleProgressListener) {
   556             this.consoleProgressListener =
   557               new ConsoleProgressListener(this.window, this);
   558           }
   559           this.consoleProgressListener.startMonitor(this.consoleProgressListener.
   560                                                     MONITOR_FILE_ACTIVITY);
   561           startedListeners.push(listener);
   562           break;
   563         case "ReflowActivity":
   564           if (!this.consoleReflowListener) {
   565             this.consoleReflowListener =
   566               new ConsoleReflowListener(this.window, this);
   567           }
   568           startedListeners.push(listener);
   569           break;
   570       }
   571     }
   572     return {
   573       startedListeners: startedListeners,
   574       nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
   575       traits: this.traits,
   576     };
   577   },
   579   /**
   580    * Handler for the "stopListeners" request.
   581    *
   582    * @param object aRequest
   583    *        The JSON request object received from the Web Console client.
   584    * @return object
   585    *         The response packet to send to the client: holds the
   586    *         stoppedListeners array.
   587    */
   588   onStopListeners: function WCA_onStopListeners(aRequest)
   589   {
   590     let stoppedListeners = [];
   592     // If no specific listeners are requested to be detached, we stop all
   593     // listeners.
   594     let toDetach = aRequest.listeners ||
   595                    ["PageError", "ConsoleAPI", "NetworkActivity",
   596                     "FileActivity"];
   598     while (toDetach.length > 0) {
   599       let listener = toDetach.shift();
   600       switch (listener) {
   601         case "PageError":
   602           if (this.consoleServiceListener) {
   603             this.consoleServiceListener.destroy();
   604             this.consoleServiceListener = null;
   605           }
   606           stoppedListeners.push(listener);
   607           break;
   608         case "ConsoleAPI":
   609           if (this.consoleAPIListener) {
   610             this.consoleAPIListener.destroy();
   611             this.consoleAPIListener = null;
   612           }
   613           stoppedListeners.push(listener);
   614           break;
   615         case "NetworkActivity":
   616           if (this.networkMonitor) {
   617             this.networkMonitor.destroy();
   618             this.networkMonitor = null;
   619           }
   620           stoppedListeners.push(listener);
   621           break;
   622         case "FileActivity":
   623           if (this.consoleProgressListener) {
   624             this.consoleProgressListener.stopMonitor(this.consoleProgressListener.
   625                                                      MONITOR_FILE_ACTIVITY);
   626             this.consoleProgressListener = null;
   627           }
   628           stoppedListeners.push(listener);
   629           break;
   630         case "ReflowActivity":
   631           if (this.consoleReflowListener) {
   632             this.consoleReflowListener.destroy();
   633             this.consoleReflowListener = null;
   634           }
   635           stoppedListeners.push(listener);
   636           break;
   637       }
   638     }
   640     return { stoppedListeners: stoppedListeners };
   641   },
   643   /**
   644    * Handler for the "getCachedMessages" request. This method sends the cached
   645    * error messages and the window.console API calls to the client.
   646    *
   647    * @param object aRequest
   648    *        The JSON request object received from the Web Console client.
   649    * @return object
   650    *         The response packet to send to the client: it holds the cached
   651    *         messages array.
   652    */
   653   onGetCachedMessages: function WCA_onGetCachedMessages(aRequest)
   654   {
   655     let types = aRequest.messageTypes;
   656     if (!types) {
   657       return {
   658         error: "missingParameter",
   659         message: "The messageTypes parameter is missing.",
   660       };
   661     }
   663     let messages = [];
   665     while (types.length > 0) {
   666       let type = types.shift();
   667       switch (type) {
   668         case "ConsoleAPI": {
   669           if (!this.consoleAPIListener) {
   670             break;
   671           }
   672           let cache = this.consoleAPIListener
   673                       .getCachedMessages(!this.parentActor.isRootActor);
   674           cache.forEach((aMessage) => {
   675             let message = this.prepareConsoleMessageForRemote(aMessage);
   676             message._type = type;
   677             messages.push(message);
   678           });
   679           break;
   680         }
   681         case "PageError": {
   682           if (!this.consoleServiceListener) {
   683             break;
   684           }
   685           let cache = this.consoleServiceListener
   686                       .getCachedMessages(!this.parentActor.isRootActor);
   687           cache.forEach((aMessage) => {
   688             let message = null;
   689             if (aMessage instanceof Ci.nsIScriptError) {
   690               message = this.preparePageErrorForRemote(aMessage);
   691               message._type = type;
   692             }
   693             else {
   694               message = {
   695                 _type: "LogMessage",
   696                 message: this._createStringGrip(aMessage.message),
   697                 timeStamp: aMessage.timeStamp,
   698               };
   699             }
   700             messages.push(message);
   701           });
   702           break;
   703         }
   704       }
   705     }
   707     messages.sort(function(a, b) { return a.timeStamp - b.timeStamp; });
   709     return {
   710       from: this.actorID,
   711       messages: messages,
   712     };
   713   },
   715   /**
   716    * Handler for the "evaluateJS" request. This method evaluates the given
   717    * JavaScript string and sends back the result.
   718    *
   719    * @param object aRequest
   720    *        The JSON request object received from the Web Console client.
   721    * @return object
   722    *         The evaluation response packet.
   723    */
   724   onEvaluateJS: function WCA_onEvaluateJS(aRequest)
   725   {
   726     let input = aRequest.text;
   727     let timestamp = Date.now();
   729     let evalOptions = {
   730       bindObjectActor: aRequest.bindObjectActor,
   731       frameActor: aRequest.frameActor,
   732       url: aRequest.url,
   733     };
   734     let evalInfo = this.evalWithDebugger(input, evalOptions);
   735     let evalResult = evalInfo.result;
   736     let helperResult = evalInfo.helperResult;
   738     let result, errorMessage, errorGrip = null;
   739     if (evalResult) {
   740       if ("return" in evalResult) {
   741         result = evalResult.return;
   742       }
   743       else if ("yield" in evalResult) {
   744         result = evalResult.yield;
   745       }
   746       else if ("throw" in evalResult) {
   747         let error = evalResult.throw;
   748         errorGrip = this.createValueGrip(error);
   749         let errorToString = evalInfo.window
   750                             .evalInGlobalWithBindings("ex + ''", {ex: error});
   751         if (errorToString && typeof errorToString.return == "string") {
   752           errorMessage = errorToString.return;
   753         }
   754       }
   755     }
   757     return {
   758       from: this.actorID,
   759       input: input,
   760       result: this.createValueGrip(result),
   761       timestamp: timestamp,
   762       exception: errorGrip,
   763       exceptionMessage: errorMessage,
   764       helperResult: helperResult,
   765     };
   766   },
   768   /**
   769    * The Autocomplete request handler.
   770    *
   771    * @param object aRequest
   772    *        The request message - what input to autocomplete.
   773    * @return object
   774    *         The response message - matched properties.
   775    */
   776   onAutocomplete: function WCA_onAutocomplete(aRequest)
   777   {
   778     let frameActorId = aRequest.frameActor;
   779     let dbgObject = null;
   780     let environment = null;
   782     // This is the case of the paused debugger
   783     if (frameActorId) {
   784       let frameActor = this.conn.getActor(frameActorId);
   785       if (frameActor) {
   786         let frame = frameActor.frame;
   787         environment = frame.environment;
   788       }
   789       else {
   790         Cu.reportError("Web Console Actor: the frame actor was not found: " +
   791                        frameActorId);
   792       }
   793     }
   794     // This is the general case (non-paused debugger)
   795     else {
   796       dbgObject = this.dbg.makeGlobalObjectReference(this.evalWindow);
   797     }
   799     let result = JSPropertyProvider(dbgObject, environment, aRequest.text,
   800                                     aRequest.cursor, frameActorId) || {};
   801     let matches = result.matches || [];
   802     let reqText = aRequest.text.substr(0, aRequest.cursor);
   804     // We consider '$' as alphanumerc because it is used in the names of some
   805     // helper functions.
   806     let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText);
   807     if (!lastNonAlphaIsDot) {
   808       if (!this._jstermHelpersCache) {
   809         let helpers = {
   810           sandbox: Object.create(null)
   811         };
   812         JSTermHelpers(helpers);
   813         this._jstermHelpersCache = Object.getOwnPropertyNames(helpers.sandbox);
   814       }
   815       matches = matches.concat(this._jstermHelpersCache.filter(n => n.startsWith(result.matchProp)));
   816     }
   818     return {
   819       from: this.actorID,
   820       matches: matches.sort(),
   821       matchProp: result.matchProp,
   822     };
   823   },
   825   /**
   826    * The "clearMessagesCache" request handler.
   827    */
   828   onClearMessagesCache: function WCA_onClearMessagesCache()
   829   {
   830     // TODO: Bug 717611 - Web Console clear button does not clear cached errors
   831     let windowId = !this.parentActor.isRootActor ?
   832                    WebConsoleUtils.getInnerWindowId(this.window) : null;
   833     let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
   834                               .getService(Ci.nsIConsoleAPIStorage);
   835     ConsoleAPIStorage.clearEvents(windowId);
   837     if (this.parentActor.isRootActor) {
   838       Services.console.logStringMessage(null); // for the Error Console
   839       Services.console.reset();
   840     }
   841     return {};
   842   },
   844   /**
   845    * The "getPreferences" request handler.
   846    *
   847    * @param object aRequest
   848    *        The request message - which preferences need to be retrieved.
   849    * @return object
   850    *         The response message - a { key: value } object map.
   851    */
   852   onGetPreferences: function WCA_onGetPreferences(aRequest)
   853   {
   854     let prefs = Object.create(null);
   855     for (let key of aRequest.preferences) {
   856       prefs[key] = !!this._prefs[key];
   857     }
   858     return { preferences: prefs };
   859   },
   861   /**
   862    * The "setPreferences" request handler.
   863    *
   864    * @param object aRequest
   865    *        The request message - which preferences need to be updated.
   866    */
   867   onSetPreferences: function WCA_onSetPreferences(aRequest)
   868   {
   869     for (let key in aRequest.preferences) {
   870       this._prefs[key] = aRequest.preferences[key];
   872       if (key == "NetworkMonitor.saveRequestAndResponseBodies" &&
   873           this.networkMonitor) {
   874         this.networkMonitor.saveRequestAndResponseBodies = this._prefs[key];
   875       }
   876     }
   877     return { updated: Object.keys(aRequest.preferences) };
   878   },
   880   //////////////////
   881   // End of request handlers.
   882   //////////////////
   884   /**
   885    * Create an object with the API we expose to the Web Console during
   886    * JavaScript evaluation.
   887    * This object inherits properties and methods from the Web Console actor.
   888    *
   889    * @private
   890    * @param object aDebuggerGlobal
   891    *        A Debugger.Object that wraps a content global. This is used for the
   892    *        JSTerm helpers.
   893    * @return object
   894    *         The same object as |this|, but with an added |sandbox| property.
   895    *         The sandbox holds methods and properties that can be used as
   896    *         bindings during JS evaluation.
   897    */
   898   _getJSTermHelpers: function WCA__getJSTermHelpers(aDebuggerGlobal)
   899   {
   900     let helpers = {
   901       window: this.evalWindow,
   902       chromeWindow: this.chromeWindow.bind(this),
   903       makeDebuggeeValue: aDebuggerGlobal.makeDebuggeeValue.bind(aDebuggerGlobal),
   904       createValueGrip: this.createValueGrip.bind(this),
   905       sandbox: Object.create(null),
   906       helperResult: null,
   907       consoleActor: this,
   908     };
   909     JSTermHelpers(helpers);
   911     // Make sure the helpers can be used during eval.
   912     for (let name in helpers.sandbox) {
   913       let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name);
   914       if (desc.get || desc.set) {
   915         continue;
   916       }
   917       helpers.sandbox[name] = aDebuggerGlobal.makeDebuggeeValue(desc.value);
   918     }
   919     return helpers;
   920   },
   922   /**
   923    * Evaluates a string using the debugger API.
   924    *
   925    * To allow the variables view to update properties from the Web Console we
   926    * provide the "bindObjectActor" mechanism: the Web Console tells the
   927    * ObjectActor ID for which it desires to evaluate an expression. The
   928    * Debugger.Object pointed at by the actor ID is bound such that it is
   929    * available during expression evaluation (evalInGlobalWithBindings()).
   930    *
   931    * Example:
   932    *   _self['foobar'] = 'test'
   933    * where |_self| refers to the desired object.
   934    *
   935    * The |frameActor| property allows the Web Console client to provide the
   936    * frame actor ID, such that the expression can be evaluated in the
   937    * user-selected stack frame.
   938    *
   939    * For the above to work we need the debugger and the Web Console to share
   940    * a connection, otherwise the Web Console actor will not find the frame
   941    * actor.
   942    *
   943    * The Debugger.Frame comes from the jsdebugger's Debugger instance, which
   944    * is different from the Web Console's Debugger instance. This means that
   945    * for evaluation to work, we need to create a new instance for the jsterm
   946    * helpers - they need to be Debugger.Objects coming from the jsdebugger's
   947    * Debugger instance.
   948    *
   949    * When |bindObjectActor| is used objects can come from different iframes,
   950    * from different domains. To avoid permission-related errors when objects
   951    * come from a different window, we also determine the object's own global,
   952    * such that evaluation happens in the context of that global. This means that
   953    * evaluation will happen in the object's iframe, rather than the top level
   954    * window.
   955    *
   956    * @param string aString
   957    *        String to evaluate.
   958    * @param object [aOptions]
   959    *        Options for evaluation:
   960    *        - bindObjectActor: the ObjectActor ID to use for evaluation.
   961    *          |evalWithBindings()| will be called with one additional binding:
   962    *          |_self| which will point to the Debugger.Object of the given
   963    *          ObjectActor.
   964    *        - frameActor: the FrameActor ID to use for evaluation. The given
   965    *        debugger frame is used for evaluation, instead of the global window.
   966    * @return object
   967    *         An object that holds the following properties:
   968    *         - dbg: the debugger where the string was evaluated.
   969    *         - frame: (optional) the frame where the string was evaluated.
   970    *         - window: the Debugger.Object for the global where the string was
   971    *         evaluated.
   972    *         - result: the result of the evaluation.
   973    *         - helperResult: any result coming from a JSTerm helper function.
   974    *         - url: the url to evaluate the script as. Defaults to
   975    *         "debugger eval code".
   976    */
   977   evalWithDebugger: function WCA_evalWithDebugger(aString, aOptions = {})
   978   {
   979     // The help function needs to be easy to guess, so we make the () optional.
   980     if (aString.trim() == "help" || aString.trim() == "?") {
   981       aString = "help()";
   982     }
   984     // Find the Debugger.Frame of the given FrameActor.
   985     let frame = null, frameActor = null;
   986     if (aOptions.frameActor) {
   987       frameActor = this.conn.getActor(aOptions.frameActor);
   988       if (frameActor) {
   989         frame = frameActor.frame;
   990       }
   991       else {
   992         Cu.reportError("Web Console Actor: the frame actor was not found: " +
   993                        aOptions.frameActor);
   994       }
   995     }
   997     // If we've been given a frame actor in whose scope we should evaluate the
   998     // expression, be sure to use that frame's Debugger (that is, the JavaScript
   999     // debugger's Debugger) for the whole operation, not the console's Debugger.
  1000     // (One Debugger will treat a different Debugger's Debugger.Object instances
  1001     // as ordinary objects, not as references to be followed, so mixing
  1002     // debuggers causes strange behaviors.)
  1003     let dbg = frame ? frameActor.threadActor.dbg : this.dbg;
  1004     let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
  1006     // If we have an object to bind to |_self|, create a Debugger.Object
  1007     // referring to that object, belonging to dbg.
  1008     let bindSelf = null;
  1009     let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow);
  1010     if (aOptions.bindObjectActor) {
  1011       let objActor = this.getActorByID(aOptions.bindObjectActor);
  1012       if (objActor) {
  1013         let jsObj = objActor.obj.unsafeDereference();
  1014         // If we use the makeDebuggeeValue method of jsObj's own global, then
  1015         // we'll get a D.O that sees jsObj as viewed from its own compartment -
  1016         // that is, without wrappers. The evalWithBindings call will then wrap
  1017         // jsObj appropriately for the evaluation compartment.
  1018         let global = Cu.getGlobalForObject(jsObj);
  1019         dbgWindow = dbg.makeGlobalObjectReference(global);
  1020         bindSelf = dbgWindow.makeDebuggeeValue(jsObj);
  1024     // Get the JSTerm helpers for the given debugger window.
  1025     let helpers = this._getJSTermHelpers(dbgWindow);
  1026     let bindings = helpers.sandbox;
  1027     if (bindSelf) {
  1028       bindings._self = bindSelf;
  1031     // Check if the Debugger.Frame or Debugger.Object for the global include
  1032     // $ or $$. We will not overwrite these functions with the jsterm helpers.
  1033     let found$ = false, found$$ = false;
  1034     if (frame) {
  1035       let env = frame.environment;
  1036       if (env) {
  1037         found$ = !!env.find("$");
  1038         found$$ = !!env.find("$$");
  1041     else {
  1042       found$ = !!dbgWindow.getOwnPropertyDescriptor("$");
  1043       found$$ = !!dbgWindow.getOwnPropertyDescriptor("$$");
  1046     let $ = null, $$ = null;
  1047     if (found$) {
  1048       $ = bindings.$;
  1049       delete bindings.$;
  1051     if (found$$) {
  1052       $$ = bindings.$$;
  1053       delete bindings.$$;
  1056     // Ready to evaluate the string.
  1057     helpers.evalInput = aString;
  1059     let evalOptions;
  1060     if (typeof aOptions.url == "string") {
  1061       evalOptions = { url: aOptions.url };
  1064     let result;
  1065     if (frame) {
  1066       result = frame.evalWithBindings(aString, bindings, evalOptions);
  1068     else {
  1069       result = dbgWindow.evalInGlobalWithBindings(aString, bindings, evalOptions);
  1072     let helperResult = helpers.helperResult;
  1073     delete helpers.evalInput;
  1074     delete helpers.helperResult;
  1076     if ($) {
  1077       bindings.$ = $;
  1079     if ($$) {
  1080       bindings.$$ = $$;
  1083     if (bindings._self) {
  1084       delete bindings._self;
  1087     return {
  1088       result: result,
  1089       helperResult: helperResult,
  1090       dbg: dbg,
  1091       frame: frame,
  1092       window: dbgWindow,
  1093     };
  1094   },
  1096   //////////////////
  1097   // Event handlers for various listeners.
  1098   //////////////////
  1100   /**
  1101    * Handler for messages received from the ConsoleServiceListener. This method
  1102    * sends the nsIConsoleMessage to the remote Web Console client.
  1104    * @param nsIConsoleMessage aMessage
  1105    *        The message we need to send to the client.
  1106    */
  1107   onConsoleServiceMessage: function WCA_onConsoleServiceMessage(aMessage)
  1109     let packet;
  1110     if (aMessage instanceof Ci.nsIScriptError) {
  1111       packet = {
  1112         from: this.actorID,
  1113         type: "pageError",
  1114         pageError: this.preparePageErrorForRemote(aMessage),
  1115       };
  1117     else {
  1118       packet = {
  1119         from: this.actorID,
  1120         type: "logMessage",
  1121         message: this._createStringGrip(aMessage.message),
  1122         timeStamp: aMessage.timeStamp,
  1123       };
  1125     this.conn.send(packet);
  1126   },
  1128   /**
  1129    * Prepare an nsIScriptError to be sent to the client.
  1131    * @param nsIScriptError aPageError
  1132    *        The page error we need to send to the client.
  1133    * @return object
  1134    *         The object you can send to the remote client.
  1135    */
  1136   preparePageErrorForRemote: function WCA_preparePageErrorForRemote(aPageError)
  1138     let lineText = aPageError.sourceLine;
  1139     if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) {
  1140       lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
  1143     return {
  1144       errorMessage: this._createStringGrip(aPageError.errorMessage),
  1145       sourceName: aPageError.sourceName,
  1146       lineText: lineText,
  1147       lineNumber: aPageError.lineNumber,
  1148       columnNumber: aPageError.columnNumber,
  1149       category: aPageError.category,
  1150       timeStamp: aPageError.timeStamp,
  1151       warning: !!(aPageError.flags & aPageError.warningFlag),
  1152       error: !!(aPageError.flags & aPageError.errorFlag),
  1153       exception: !!(aPageError.flags & aPageError.exceptionFlag),
  1154       strict: !!(aPageError.flags & aPageError.strictFlag),
  1155       private: aPageError.isFromPrivateWindow,
  1156     };
  1157   },
  1159   /**
  1160    * Handler for window.console API calls received from the ConsoleAPIListener.
  1161    * This method sends the object to the remote Web Console client.
  1163    * @see ConsoleAPIListener
  1164    * @param object aMessage
  1165    *        The console API call we need to send to the remote client.
  1166    */
  1167   onConsoleAPICall: function WCA_onConsoleAPICall(aMessage)
  1169     let packet = {
  1170       from: this.actorID,
  1171       type: "consoleAPICall",
  1172       message: this.prepareConsoleMessageForRemote(aMessage),
  1173     };
  1174     this.conn.send(packet);
  1175   },
  1177   /**
  1178    * Handler for network events. This method is invoked when a new network event
  1179    * is about to be recorded.
  1181    * @see NetworkEventActor
  1182    * @see NetworkMonitor from webconsole/utils.js
  1184    * @param object aEvent
  1185    *        The initial network request event information.
  1186    * @param nsIHttpChannel aChannel
  1187    *        The network request nsIHttpChannel object.
  1188    * @return object
  1189    *         A new NetworkEventActor is returned. This is used for tracking the
  1190    *         network request and response.
  1191    */
  1192   onNetworkEvent: function WCA_onNetworkEvent(aEvent, aChannel)
  1194     let actor = this.getNetworkEventActor(aChannel);
  1195     actor.init(aEvent);
  1197     let packet = {
  1198       from: this.actorID,
  1199       type: "networkEvent",
  1200       eventActor: actor.grip(),
  1201     };
  1203     this.conn.send(packet);
  1205     return actor;
  1206   },
  1208   /**
  1209    * Get the NetworkEventActor for a nsIChannel, if it exists,
  1210    * otherwise create a new one.
  1212    * @param nsIHttpChannel aChannel
  1213    *        The channel for the network event.
  1214    * @return object
  1215    *         The NetworkEventActor for the given channel.
  1216    */
  1217   getNetworkEventActor: function WCA_getNetworkEventActor(aChannel) {
  1218     let actor = this._netEvents.get(aChannel);
  1219     if (actor) {
  1220       // delete from map as we should only need to do this check once
  1221       this._netEvents.delete(aChannel);
  1222       actor.channel = null;
  1223       return actor;
  1226     actor = new NetworkEventActor(aChannel, this);
  1227     this._actorPool.addActor(actor);
  1228     return actor;
  1229   },
  1231   /**
  1232    * Send a new HTTP request from the target's window.
  1234    * @param object aMessage
  1235    *        Object with 'request' - the HTTP request details.
  1236    */
  1237   onSendHTTPRequest: function WCA_onSendHTTPRequest(aMessage)
  1239     let details = aMessage.request;
  1241     // send request from target's window
  1242     let request = new this.window.XMLHttpRequest();
  1243     request.open(details.method, details.url, true);
  1245     for (let {name, value} of details.headers) {
  1246       request.setRequestHeader(name, value);
  1248     request.send(details.body);
  1250     let actor = this.getNetworkEventActor(request.channel);
  1252     // map channel to actor so we can associate future events with it
  1253     this._netEvents.set(request.channel, actor);
  1255     return {
  1256       from: this.actorID,
  1257       eventActor: actor.grip()
  1258     };
  1259   },
  1261   /**
  1262    * Handler for file activity. This method sends the file request information
  1263    * to the remote Web Console client.
  1265    * @see ConsoleProgressListener
  1266    * @param string aFileURI
  1267    *        The requested file URI.
  1268    */
  1269   onFileActivity: function WCA_onFileActivity(aFileURI)
  1271     let packet = {
  1272       from: this.actorID,
  1273       type: "fileActivity",
  1274       uri: aFileURI,
  1275     };
  1276     this.conn.send(packet);
  1277   },
  1279   /**
  1280    * Handler for reflow activity. This method forwards reflow events to the
  1281    * remote Web Console client.
  1283    * @see ConsoleReflowListener
  1284    * @param Object aReflowInfo
  1285    */
  1286   onReflowActivity: function WCA_onReflowActivity(aReflowInfo)
  1288     let packet = {
  1289       from: this.actorID,
  1290       type: "reflowActivity",
  1291       interruptible: aReflowInfo.interruptible,
  1292       start: aReflowInfo.start,
  1293       end: aReflowInfo.end,
  1294       sourceURL: aReflowInfo.sourceURL,
  1295       sourceLine: aReflowInfo.sourceLine,
  1296       functionName: aReflowInfo.functionName
  1297     };
  1299     this.conn.send(packet);
  1300   },
  1302   //////////////////
  1303   // End of event handlers for various listeners.
  1304   //////////////////
  1306   /**
  1307    * Prepare a message from the console API to be sent to the remote Web Console
  1308    * instance.
  1310    * @param object aMessage
  1311    *        The original message received from console-api-log-event.
  1312    * @return object
  1313    *         The object that can be sent to the remote client.
  1314    */
  1315   prepareConsoleMessageForRemote:
  1316   function WCA_prepareConsoleMessageForRemote(aMessage)
  1318     let result = WebConsoleUtils.cloneObject(aMessage);
  1319     delete result.wrappedJSObject;
  1320     delete result.ID;
  1321     delete result.innerID;
  1323     result.arguments = Array.map(aMessage.arguments || [], (aObj) => {
  1324       let dbgObj = this.makeDebuggeeValue(aObj, true);
  1325       return this.createValueGrip(dbgObj);
  1326     });
  1328     result.styles = Array.map(aMessage.styles || [], (aString) => {
  1329       return this.createValueGrip(aString);
  1330     });
  1332     return result;
  1333   },
  1335   /**
  1336    * Find the XUL window that owns the content window.
  1338    * @return Window
  1339    *         The XUL window that owns the content window.
  1340    */
  1341   chromeWindow: function WCA_chromeWindow()
  1343     let window = null;
  1344     try {
  1345       window = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
  1346              .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell)
  1347              .chromeEventHandler.ownerDocument.defaultView;
  1349     catch (ex) {
  1350       // The above can fail because chromeEventHandler is not available for all
  1351       // kinds of |this.window|.
  1354     return window;
  1355   },
  1357   /**
  1358    * Notification observer for the "last-pb-context-exited" topic.
  1360    * @private
  1361    * @param object aSubject
  1362    *        Notification subject - in this case it is the inner window ID that
  1363    *        was destroyed.
  1364    * @param string aTopic
  1365    *        Notification topic.
  1366    */
  1367   _onObserverNotification: function WCA__onObserverNotification(aSubject, aTopic)
  1369     switch (aTopic) {
  1370       case "last-pb-context-exited":
  1371         this.conn.send({
  1372           from: this.actorID,
  1373           type: "lastPrivateContextExited",
  1374         });
  1375         break;
  1377   },
  1379   /**
  1380    * The "will-navigate" progress listener. This is used to clear the current
  1381    * eval scope.
  1382    */
  1383   _onWillNavigate: function WCA__onWillNavigate({ window, isTopLevel })
  1385     if (isTopLevel) {
  1386       this._evalWindow = null;
  1387       events.off(this.parentActor, "will-navigate", this._onWillNavigate);
  1388       this._progressListenerActive = false;
  1390   },
  1391 };
  1393 WebConsoleActor.prototype.requestTypes =
  1395   startListeners: WebConsoleActor.prototype.onStartListeners,
  1396   stopListeners: WebConsoleActor.prototype.onStopListeners,
  1397   getCachedMessages: WebConsoleActor.prototype.onGetCachedMessages,
  1398   evaluateJS: WebConsoleActor.prototype.onEvaluateJS,
  1399   autocomplete: WebConsoleActor.prototype.onAutocomplete,
  1400   clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
  1401   getPreferences: WebConsoleActor.prototype.onGetPreferences,
  1402   setPreferences: WebConsoleActor.prototype.onSetPreferences,
  1403   sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest
  1404 };
  1406 /**
  1407  * Creates an actor for a network event.
  1409  * @constructor
  1410  * @param object aChannel
  1411  *        The nsIChannel associated with this event.
  1412  * @param object aWebConsoleActor
  1413  *        The parent WebConsoleActor instance for this object.
  1414  */
  1415 function NetworkEventActor(aChannel, aWebConsoleActor)
  1417   this.parent = aWebConsoleActor;
  1418   this.conn = this.parent.conn;
  1419   this.channel = aChannel;
  1421   this._request = {
  1422     method: null,
  1423     url: null,
  1424     httpVersion: null,
  1425     headers: [],
  1426     cookies: [],
  1427     headersSize: null,
  1428     postData: {},
  1429   };
  1431   this._response = {
  1432     headers: [],
  1433     cookies: [],
  1434     content: {},
  1435   };
  1437   this._timings = {};
  1439   // Keep track of LongStringActors owned by this NetworkEventActor.
  1440   this._longStringActors = new Set();
  1443 NetworkEventActor.prototype =
  1445   _request: null,
  1446   _response: null,
  1447   _timings: null,
  1448   _longStringActors: null,
  1450   actorPrefix: "netEvent",
  1452   /**
  1453    * Returns a grip for this actor for returning in a protocol message.
  1454    */
  1455   grip: function NEA_grip()
  1457     return {
  1458       actor: this.actorID,
  1459       startedDateTime: this._startedDateTime,
  1460       url: this._request.url,
  1461       method: this._request.method,
  1462       isXHR: this._isXHR,
  1463       private: this._private,
  1464     };
  1465   },
  1467   /**
  1468    * Releases this actor from the pool.
  1469    */
  1470   release: function NEA_release()
  1472     for (let grip of this._longStringActors) {
  1473       let actor = this.parent.getActorByID(grip.actor);
  1474       if (actor) {
  1475         this.parent.releaseActor(actor);
  1478     this._longStringActors = new Set();
  1480     if (this.channel) {
  1481       this.parent._netEvents.delete(this.channel);
  1483     this.parent.releaseActor(this);
  1484   },
  1486   /**
  1487    * Handle a protocol request to release a grip.
  1488    */
  1489   onRelease: function NEA_onRelease()
  1491     this.release();
  1492     return {};
  1493   },
  1495   /**
  1496    * Set the properties of this actor based on it's corresponding
  1497    * network event.
  1499    * @param object aNetworkEvent
  1500    *        The network event associated with this actor.
  1501    */
  1502   init: function NEA_init(aNetworkEvent)
  1504     this._startedDateTime = aNetworkEvent.startedDateTime;
  1505     this._isXHR = aNetworkEvent.isXHR;
  1507     for (let prop of ['method', 'url', 'httpVersion', 'headersSize']) {
  1508       this._request[prop] = aNetworkEvent[prop];
  1511     this._discardRequestBody = aNetworkEvent.discardRequestBody;
  1512     this._discardResponseBody = aNetworkEvent.discardResponseBody;
  1513     this._private = aNetworkEvent.private;
  1514   },
  1516   /**
  1517    * The "getRequestHeaders" packet type handler.
  1519    * @return object
  1520    *         The response packet - network request headers.
  1521    */
  1522   onGetRequestHeaders: function NEA_onGetRequestHeaders()
  1524     return {
  1525       from: this.actorID,
  1526       headers: this._request.headers,
  1527       headersSize: this._request.headersSize,
  1528     };
  1529   },
  1531   /**
  1532    * The "getRequestCookies" packet type handler.
  1534    * @return object
  1535    *         The response packet - network request cookies.
  1536    */
  1537   onGetRequestCookies: function NEA_onGetRequestCookies()
  1539     return {
  1540       from: this.actorID,
  1541       cookies: this._request.cookies,
  1542     };
  1543   },
  1545   /**
  1546    * The "getRequestPostData" packet type handler.
  1548    * @return object
  1549    *         The response packet - network POST data.
  1550    */
  1551   onGetRequestPostData: function NEA_onGetRequestPostData()
  1553     return {
  1554       from: this.actorID,
  1555       postData: this._request.postData,
  1556       postDataDiscarded: this._discardRequestBody,
  1557     };
  1558   },
  1560   /**
  1561    * The "getResponseHeaders" packet type handler.
  1563    * @return object
  1564    *         The response packet - network response headers.
  1565    */
  1566   onGetResponseHeaders: function NEA_onGetResponseHeaders()
  1568     return {
  1569       from: this.actorID,
  1570       headers: this._response.headers,
  1571       headersSize: this._response.headersSize,
  1572     };
  1573   },
  1575   /**
  1576    * The "getResponseCookies" packet type handler.
  1578    * @return object
  1579    *         The response packet - network response cookies.
  1580    */
  1581   onGetResponseCookies: function NEA_onGetResponseCookies()
  1583     return {
  1584       from: this.actorID,
  1585       cookies: this._response.cookies,
  1586     };
  1587   },
  1589   /**
  1590    * The "getResponseContent" packet type handler.
  1592    * @return object
  1593    *         The response packet - network response content.
  1594    */
  1595   onGetResponseContent: function NEA_onGetResponseContent()
  1597     return {
  1598       from: this.actorID,
  1599       content: this._response.content,
  1600       contentDiscarded: this._discardResponseBody,
  1601     };
  1602   },
  1604   /**
  1605    * The "getEventTimings" packet type handler.
  1607    * @return object
  1608    *         The response packet - network event timings.
  1609    */
  1610   onGetEventTimings: function NEA_onGetEventTimings()
  1612     return {
  1613       from: this.actorID,
  1614       timings: this._timings,
  1615       totalTime: this._totalTime,
  1616     };
  1617   },
  1619   /******************************************************************
  1620    * Listeners for new network event data coming from NetworkMonitor.
  1621    ******************************************************************/
  1623   /**
  1624    * Add network request headers.
  1626    * @param array aHeaders
  1627    *        The request headers array.
  1628    */
  1629   addRequestHeaders: function NEA_addRequestHeaders(aHeaders)
  1631     this._request.headers = aHeaders;
  1632     this._prepareHeaders(aHeaders);
  1634     let packet = {
  1635       from: this.actorID,
  1636       type: "networkEventUpdate",
  1637       updateType: "requestHeaders",
  1638       headers: aHeaders.length,
  1639       headersSize: this._request.headersSize,
  1640     };
  1642     this.conn.send(packet);
  1643   },
  1645   /**
  1646    * Add network request cookies.
  1648    * @param array aCookies
  1649    *        The request cookies array.
  1650    */
  1651   addRequestCookies: function NEA_addRequestCookies(aCookies)
  1653     this._request.cookies = aCookies;
  1654     this._prepareHeaders(aCookies);
  1656     let packet = {
  1657       from: this.actorID,
  1658       type: "networkEventUpdate",
  1659       updateType: "requestCookies",
  1660       cookies: aCookies.length,
  1661     };
  1663     this.conn.send(packet);
  1664   },
  1666   /**
  1667    * Add network request POST data.
  1669    * @param object aPostData
  1670    *        The request POST data.
  1671    */
  1672   addRequestPostData: function NEA_addRequestPostData(aPostData)
  1674     this._request.postData = aPostData;
  1675     aPostData.text = this.parent._createStringGrip(aPostData.text);
  1676     if (typeof aPostData.text == "object") {
  1677       this._longStringActors.add(aPostData.text);
  1680     let packet = {
  1681       from: this.actorID,
  1682       type: "networkEventUpdate",
  1683       updateType: "requestPostData",
  1684       dataSize: aPostData.text.length,
  1685       discardRequestBody: this._discardRequestBody,
  1686     };
  1688     this.conn.send(packet);
  1689   },
  1691   /**
  1692    * Add the initial network response information.
  1694    * @param object aInfo
  1695    *        The response information.
  1696    */
  1697   addResponseStart: function NEA_addResponseStart(aInfo)
  1699     this._response.httpVersion = aInfo.httpVersion;
  1700     this._response.status = aInfo.status;
  1701     this._response.statusText = aInfo.statusText;
  1702     this._response.headersSize = aInfo.headersSize;
  1703     this._discardResponseBody = aInfo.discardResponseBody;
  1705     let packet = {
  1706       from: this.actorID,
  1707       type: "networkEventUpdate",
  1708       updateType: "responseStart",
  1709       response: aInfo,
  1710     };
  1712     this.conn.send(packet);
  1713   },
  1715   /**
  1716    * Add network response headers.
  1718    * @param array aHeaders
  1719    *        The response headers array.
  1720    */
  1721   addResponseHeaders: function NEA_addResponseHeaders(aHeaders)
  1723     this._response.headers = aHeaders;
  1724     this._prepareHeaders(aHeaders);
  1726     let packet = {
  1727       from: this.actorID,
  1728       type: "networkEventUpdate",
  1729       updateType: "responseHeaders",
  1730       headers: aHeaders.length,
  1731       headersSize: this._response.headersSize,
  1732     };
  1734     this.conn.send(packet);
  1735   },
  1737   /**
  1738    * Add network response cookies.
  1740    * @param array aCookies
  1741    *        The response cookies array.
  1742    */
  1743   addResponseCookies: function NEA_addResponseCookies(aCookies)
  1745     this._response.cookies = aCookies;
  1746     this._prepareHeaders(aCookies);
  1748     let packet = {
  1749       from: this.actorID,
  1750       type: "networkEventUpdate",
  1751       updateType: "responseCookies",
  1752       cookies: aCookies.length,
  1753     };
  1755     this.conn.send(packet);
  1756   },
  1758   /**
  1759    * Add network response content.
  1761    * @param object aContent
  1762    *        The response content.
  1763    * @param boolean aDiscardedResponseBody
  1764    *        Tells if the response content was recorded or not.
  1765    */
  1766   addResponseContent:
  1767   function NEA_addResponseContent(aContent, aDiscardedResponseBody)
  1769     this._response.content = aContent;
  1770     aContent.text = this.parent._createStringGrip(aContent.text);
  1771     if (typeof aContent.text == "object") {
  1772       this._longStringActors.add(aContent.text);
  1775     let packet = {
  1776       from: this.actorID,
  1777       type: "networkEventUpdate",
  1778       updateType: "responseContent",
  1779       mimeType: aContent.mimeType,
  1780       contentSize: aContent.text.length,
  1781       discardResponseBody: aDiscardedResponseBody,
  1782     };
  1784     this.conn.send(packet);
  1785   },
  1787   /**
  1788    * Add network event timing information.
  1790    * @param number aTotal
  1791    *        The total time of the network event.
  1792    * @param object aTimings
  1793    *        Timing details about the network event.
  1794    */
  1795   addEventTimings: function NEA_addEventTimings(aTotal, aTimings)
  1797     this._totalTime = aTotal;
  1798     this._timings = aTimings;
  1800     let packet = {
  1801       from: this.actorID,
  1802       type: "networkEventUpdate",
  1803       updateType: "eventTimings",
  1804       totalTime: aTotal,
  1805     };
  1807     this.conn.send(packet);
  1808   },
  1810   /**
  1811    * Prepare the headers array to be sent to the client by using the
  1812    * LongStringActor for the header values, when needed.
  1814    * @private
  1815    * @param array aHeaders
  1816    */
  1817   _prepareHeaders: function NEA__prepareHeaders(aHeaders)
  1819     for (let header of aHeaders) {
  1820       header.value = this.parent._createStringGrip(header.value);
  1821       if (typeof header.value == "object") {
  1822         this._longStringActors.add(header.value);
  1825   },
  1826 };
  1828 NetworkEventActor.prototype.requestTypes =
  1830   "release": NetworkEventActor.prototype.onRelease,
  1831   "getRequestHeaders": NetworkEventActor.prototype.onGetRequestHeaders,
  1832   "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies,
  1833   "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData,
  1834   "getResponseHeaders": NetworkEventActor.prototype.onGetResponseHeaders,
  1835   "getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies,
  1836   "getResponseContent": NetworkEventActor.prototype.onGetResponseContent,
  1837   "getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
  1838 };
  1840 exports.register = function(handle) {
  1841   handle.addGlobalActor(WebConsoleActor, "consoleActor");
  1842   handle.addTabActor(WebConsoleActor, "consoleActor");
  1843 };
  1845 exports.unregister = function(handle) {
  1846   handle.removeGlobalActor(WebConsoleActor, "consoleActor");
  1847   handle.removeTabActor(WebConsoleActor, "consoleActor");
  1848 };

mercurial