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.

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

mercurial