1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/server/actors/webconsole.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1848 @@ 1.4 +/* -*- js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +"use strict"; 1.11 + 1.12 +let {Cc, Ci, Cu} = require("chrome"); 1.13 + 1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.15 + 1.16 +let { DebuggerServer, ActorPool } = require("devtools/server/main"); 1.17 +// Symbols from script.js 1.18 +let { ThreadActor, EnvironmentActor, ObjectActor, LongStringActor } = DebuggerServer; 1.19 + 1.20 +Cu.import("resource://gre/modules/jsdebugger.jsm"); 1.21 +addDebuggerToGlobal(this); 1.22 + 1.23 +XPCOMUtils.defineLazyModuleGetter(this, "Services", 1.24 + "resource://gre/modules/Services.jsm"); 1.25 +XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => { 1.26 + return require("devtools/toolkit/webconsole/network-monitor") 1.27 + .NetworkMonitor; 1.28 +}); 1.29 +XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => { 1.30 + return require("devtools/toolkit/webconsole/network-monitor") 1.31 + .NetworkMonitorChild; 1.32 +}); 1.33 +XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => { 1.34 + return require("devtools/toolkit/webconsole/network-monitor") 1.35 + .ConsoleProgressListener; 1.36 +}); 1.37 +XPCOMUtils.defineLazyGetter(this, "events", () => { 1.38 + return require("sdk/event/core"); 1.39 +}); 1.40 + 1.41 +for (let name of ["WebConsoleUtils", "ConsoleServiceListener", 1.42 + "ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider", 1.43 + "ConsoleReflowListener"]) { 1.44 + Object.defineProperty(this, name, { 1.45 + get: function(prop) { 1.46 + if (prop == "WebConsoleUtils") { 1.47 + prop = "Utils"; 1.48 + } 1.49 + return require("devtools/toolkit/webconsole/utils")[prop]; 1.50 + }.bind(null, name), 1.51 + configurable: true, 1.52 + enumerable: true 1.53 + }); 1.54 +} 1.55 + 1.56 + 1.57 +/** 1.58 + * The WebConsoleActor implements capabilities needed for the Web Console 1.59 + * feature. 1.60 + * 1.61 + * @constructor 1.62 + * @param object aConnection 1.63 + * The connection to the client, DebuggerServerConnection. 1.64 + * @param object [aParentActor] 1.65 + * Optional, the parent actor. 1.66 + */ 1.67 +function WebConsoleActor(aConnection, aParentActor) 1.68 +{ 1.69 + this.conn = aConnection; 1.70 + this.parentActor = aParentActor; 1.71 + 1.72 + this._actorPool = new ActorPool(this.conn); 1.73 + this.conn.addActorPool(this._actorPool); 1.74 + 1.75 + this._prefs = {}; 1.76 + 1.77 + this.dbg = new Debugger(); 1.78 + 1.79 + this._netEvents = new Map(); 1.80 + this._gripDepth = 0; 1.81 + 1.82 + this._onWillNavigate = this._onWillNavigate.bind(this); 1.83 + this._onObserverNotification = this._onObserverNotification.bind(this); 1.84 + if (this.parentActor.isRootActor) { 1.85 + Services.obs.addObserver(this._onObserverNotification, 1.86 + "last-pb-context-exited", false); 1.87 + } 1.88 + 1.89 + this.traits = { 1.90 + customNetworkRequest: !this._parentIsContentActor, 1.91 + }; 1.92 +} 1.93 + 1.94 +WebConsoleActor.l10n = new WebConsoleUtils.l10n("chrome://global/locale/console.properties"); 1.95 + 1.96 +WebConsoleActor.prototype = 1.97 +{ 1.98 + /** 1.99 + * Debugger instance. 1.100 + * 1.101 + * @see jsdebugger.jsm 1.102 + */ 1.103 + dbg: null, 1.104 + 1.105 + /** 1.106 + * This is used by the ObjectActor to keep track of the depth of grip() calls. 1.107 + * @private 1.108 + * @type number 1.109 + */ 1.110 + _gripDepth: null, 1.111 + 1.112 + /** 1.113 + * Actor pool for all of the actors we send to the client. 1.114 + * @private 1.115 + * @type object 1.116 + * @see ActorPool 1.117 + */ 1.118 + _actorPool: null, 1.119 + 1.120 + /** 1.121 + * Web Console-related preferences. 1.122 + * @private 1.123 + * @type object 1.124 + */ 1.125 + _prefs: null, 1.126 + 1.127 + /** 1.128 + * Holds a map between nsIChannel objects and NetworkEventActors for requests 1.129 + * created with sendHTTPRequest. 1.130 + * 1.131 + * @private 1.132 + * @type Map 1.133 + */ 1.134 + _netEvents: null, 1.135 + 1.136 + /** 1.137 + * The debugger server connection instance. 1.138 + * @type object 1.139 + */ 1.140 + conn: null, 1.141 + 1.142 + /** 1.143 + * List of supported features by the console actor. 1.144 + * @type object 1.145 + */ 1.146 + traits: null, 1.147 + 1.148 + /** 1.149 + * Boolean getter that tells if the parent actor is a ContentActor. 1.150 + * 1.151 + * @private 1.152 + * @type boolean 1.153 + */ 1.154 + get _parentIsContentActor() { 1.155 + return "ContentActor" in DebuggerServer && 1.156 + this.parentActor instanceof DebuggerServer.ContentActor; 1.157 + }, 1.158 + 1.159 + /** 1.160 + * The window we work with. 1.161 + * @type nsIDOMWindow 1.162 + */ 1.163 + get window() { 1.164 + if (this.parentActor.isRootActor) { 1.165 + return this._getWindowForBrowserConsole(); 1.166 + } 1.167 + return this.parentActor.window; 1.168 + }, 1.169 + 1.170 + /** 1.171 + * Get a window to use for the browser console. 1.172 + * 1.173 + * @private 1.174 + * @return nsIDOMWindow 1.175 + * The window to use, or null if no window could be found. 1.176 + */ 1.177 + _getWindowForBrowserConsole: function WCA__getWindowForBrowserConsole() 1.178 + { 1.179 + // Check if our last used chrome window is still live. 1.180 + let window = this._lastChromeWindow && this._lastChromeWindow.get(); 1.181 + // If not, look for a new one. 1.182 + if (!window || window.closed) { 1.183 + window = this.parentActor.window; 1.184 + if (!window) { 1.185 + // Try to find the Browser Console window to use instead. 1.186 + window = Services.wm.getMostRecentWindow("devtools:webconsole"); 1.187 + // We prefer the normal chrome window over the console window, 1.188 + // so we'll look for those windows in order to replace our reference. 1.189 + let onChromeWindowOpened = () => { 1.190 + // We'll look for this window when someone next requests window() 1.191 + Services.obs.removeObserver(onChromeWindowOpened, "domwindowopened"); 1.192 + this._lastChromeWindow = null; 1.193 + }; 1.194 + Services.obs.addObserver(onChromeWindowOpened, "domwindowopened", false); 1.195 + } 1.196 + 1.197 + this._handleNewWindow(window); 1.198 + } 1.199 + 1.200 + return window; 1.201 + }, 1.202 + 1.203 + /** 1.204 + * Store a newly found window on the actor to be used in the future. 1.205 + * 1.206 + * @private 1.207 + * @param nsIDOMWindow window 1.208 + * The window to store on the actor (can be null). 1.209 + */ 1.210 + _handleNewWindow: function WCA__handleNewWindow(window) 1.211 + { 1.212 + if (window) { 1.213 + if (this._hadChromeWindow) { 1.214 + let contextChangedMsg = WebConsoleActor.l10n.getStr("evaluationContextChanged"); 1.215 + Services.console.logStringMessage(contextChangedMsg); 1.216 + } 1.217 + this._lastChromeWindow = Cu.getWeakReference(window); 1.218 + this._hadChromeWindow = true; 1.219 + } else { 1.220 + this._lastChromeWindow = null; 1.221 + } 1.222 + }, 1.223 + 1.224 + /** 1.225 + * Whether we've been using a window before. 1.226 + * 1.227 + * @private 1.228 + * @type boolean 1.229 + */ 1.230 + _hadChromeWindow: false, 1.231 + 1.232 + /** 1.233 + * A weak reference to the last chrome window we used to work with. 1.234 + * 1.235 + * @private 1.236 + * @type nsIWeakReference 1.237 + */ 1.238 + _lastChromeWindow: null, 1.239 + 1.240 + // The evalWindow is used at the scope for JS evaluation. 1.241 + _evalWindow: null, 1.242 + get evalWindow() { 1.243 + return this._evalWindow || this.window; 1.244 + }, 1.245 + 1.246 + set evalWindow(aWindow) { 1.247 + this._evalWindow = aWindow; 1.248 + 1.249 + if (!this._progressListenerActive) { 1.250 + events.on(this.parentActor, "will-navigate", this._onWillNavigate); 1.251 + this._progressListenerActive = true; 1.252 + } 1.253 + }, 1.254 + 1.255 + /** 1.256 + * Flag used to track if we are listening for events from the progress 1.257 + * listener of the tab actor. We use the progress listener to clear 1.258 + * this.evalWindow on page navigation. 1.259 + * 1.260 + * @private 1.261 + * @type boolean 1.262 + */ 1.263 + _progressListenerActive: false, 1.264 + 1.265 + /** 1.266 + * The ConsoleServiceListener instance. 1.267 + * @type object 1.268 + */ 1.269 + consoleServiceListener: null, 1.270 + 1.271 + /** 1.272 + * The ConsoleAPIListener instance. 1.273 + */ 1.274 + consoleAPIListener: null, 1.275 + 1.276 + /** 1.277 + * The NetworkMonitor instance. 1.278 + */ 1.279 + networkMonitor: null, 1.280 + 1.281 + /** 1.282 + * The ConsoleProgressListener instance. 1.283 + */ 1.284 + consoleProgressListener: null, 1.285 + 1.286 + /** 1.287 + * The ConsoleReflowListener instance. 1.288 + */ 1.289 + consoleReflowListener: null, 1.290 + 1.291 + /** 1.292 + * The JSTerm Helpers names cache. 1.293 + * @private 1.294 + * @type array 1.295 + */ 1.296 + _jstermHelpersCache: null, 1.297 + 1.298 + actorPrefix: "console", 1.299 + 1.300 + grip: function WCA_grip() 1.301 + { 1.302 + return { actor: this.actorID }; 1.303 + }, 1.304 + 1.305 + hasNativeConsoleAPI: function WCA_hasNativeConsoleAPI(aWindow) { 1.306 + let isNative = false; 1.307 + try { 1.308 + // We are very explicitly examining the "console" property of 1.309 + // the non-Xrayed object here. 1.310 + let console = aWindow.wrappedJSObject.console; 1.311 + isNative = console instanceof aWindow.Console; 1.312 + } 1.313 + catch (ex) { } 1.314 + return isNative; 1.315 + }, 1.316 + 1.317 + _createValueGrip: ThreadActor.prototype.createValueGrip, 1.318 + _stringIsLong: ThreadActor.prototype._stringIsLong, 1.319 + _findProtoChain: ThreadActor.prototype._findProtoChain, 1.320 + _removeFromProtoChain: ThreadActor.prototype._removeFromProtoChain, 1.321 + 1.322 + /** 1.323 + * Destroy the current WebConsoleActor instance. 1.324 + */ 1.325 + disconnect: function WCA_disconnect() 1.326 + { 1.327 + if (this.consoleServiceListener) { 1.328 + this.consoleServiceListener.destroy(); 1.329 + this.consoleServiceListener = null; 1.330 + } 1.331 + if (this.consoleAPIListener) { 1.332 + this.consoleAPIListener.destroy(); 1.333 + this.consoleAPIListener = null; 1.334 + } 1.335 + if (this.networkMonitor) { 1.336 + this.networkMonitor.destroy(); 1.337 + this.networkMonitor = null; 1.338 + } 1.339 + if (this.consoleProgressListener) { 1.340 + this.consoleProgressListener.destroy(); 1.341 + this.consoleProgressListener = null; 1.342 + } 1.343 + if (this.consoleReflowListener) { 1.344 + this.consoleReflowListener.destroy(); 1.345 + this.consoleReflowListener = null; 1.346 + } 1.347 + this.conn.removeActorPool(this._actorPool); 1.348 + if (this.parentActor.isRootActor) { 1.349 + Services.obs.removeObserver(this._onObserverNotification, 1.350 + "last-pb-context-exited"); 1.351 + } 1.352 + this._actorPool = null; 1.353 + 1.354 + this._jstermHelpersCache = null; 1.355 + this._evalWindow = null; 1.356 + this._netEvents.clear(); 1.357 + this.dbg.enabled = false; 1.358 + this.dbg = null; 1.359 + this.conn = null; 1.360 + }, 1.361 + 1.362 + /** 1.363 + * Create and return an environment actor that corresponds to the provided 1.364 + * Debugger.Environment. This is a straightforward clone of the ThreadActor's 1.365 + * method except that it stores the environment actor in the web console 1.366 + * actor's pool. 1.367 + * 1.368 + * @param Debugger.Environment aEnvironment 1.369 + * The lexical environment we want to extract. 1.370 + * @return The EnvironmentActor for aEnvironment or undefined for host 1.371 + * functions or functions scoped to a non-debuggee global. 1.372 + */ 1.373 + createEnvironmentActor: function WCA_createEnvironmentActor(aEnvironment) { 1.374 + if (!aEnvironment) { 1.375 + return undefined; 1.376 + } 1.377 + 1.378 + if (aEnvironment.actor) { 1.379 + return aEnvironment.actor; 1.380 + } 1.381 + 1.382 + let actor = new EnvironmentActor(aEnvironment, this); 1.383 + this._actorPool.addActor(actor); 1.384 + aEnvironment.actor = actor; 1.385 + 1.386 + return actor; 1.387 + }, 1.388 + 1.389 + /** 1.390 + * Create a grip for the given value. 1.391 + * 1.392 + * @param mixed aValue 1.393 + * @return object 1.394 + */ 1.395 + createValueGrip: function WCA_createValueGrip(aValue) 1.396 + { 1.397 + return this._createValueGrip(aValue, this._actorPool); 1.398 + }, 1.399 + 1.400 + /** 1.401 + * Make a debuggee value for the given value. 1.402 + * 1.403 + * @param mixed aValue 1.404 + * The value you want to get a debuggee value for. 1.405 + * @param boolean aUseObjectGlobal 1.406 + * If |true| the object global is determined and added as a debuggee, 1.407 + * otherwise |this.window| is used when makeDebuggeeValue() is invoked. 1.408 + * @return object 1.409 + * Debuggee value for |aValue|. 1.410 + */ 1.411 + makeDebuggeeValue: function WCA_makeDebuggeeValue(aValue, aUseObjectGlobal) 1.412 + { 1.413 + let global = this.window; 1.414 + if (aUseObjectGlobal && typeof aValue == "object") { 1.415 + try { 1.416 + global = Cu.getGlobalForObject(aValue); 1.417 + } 1.418 + catch (ex) { 1.419 + // The above can throw an exception if aValue is not an actual object. 1.420 + } 1.421 + } 1.422 + let dbgGlobal = this.dbg.makeGlobalObjectReference(global); 1.423 + return dbgGlobal.makeDebuggeeValue(aValue); 1.424 + }, 1.425 + 1.426 + /** 1.427 + * Create a grip for the given object. 1.428 + * 1.429 + * @param object aObject 1.430 + * The object you want. 1.431 + * @param object aPool 1.432 + * An ActorPool where the new actor instance is added. 1.433 + * @param object 1.434 + * The object grip. 1.435 + */ 1.436 + objectGrip: function WCA_objectGrip(aObject, aPool) 1.437 + { 1.438 + let actor = new ObjectActor(aObject, this); 1.439 + aPool.addActor(actor); 1.440 + return actor.grip(); 1.441 + }, 1.442 + 1.443 + /** 1.444 + * Create a grip for the given string. 1.445 + * 1.446 + * @param string aString 1.447 + * The string you want to create the grip for. 1.448 + * @param object aPool 1.449 + * An ActorPool where the new actor instance is added. 1.450 + * @return object 1.451 + * A LongStringActor object that wraps the given string. 1.452 + */ 1.453 + longStringGrip: function WCA_longStringGrip(aString, aPool) 1.454 + { 1.455 + let actor = new LongStringActor(aString, this); 1.456 + aPool.addActor(actor); 1.457 + return actor.grip(); 1.458 + }, 1.459 + 1.460 + /** 1.461 + * Create a long string grip if needed for the given string. 1.462 + * 1.463 + * @private 1.464 + * @param string aString 1.465 + * The string you want to create a long string grip for. 1.466 + * @return string|object 1.467 + * A string is returned if |aString| is not a long string. 1.468 + * A LongStringActor grip is returned if |aString| is a long string. 1.469 + */ 1.470 + _createStringGrip: function NEA__createStringGrip(aString) 1.471 + { 1.472 + if (aString && this._stringIsLong(aString)) { 1.473 + return this.longStringGrip(aString, this._actorPool); 1.474 + } 1.475 + return aString; 1.476 + }, 1.477 + 1.478 + /** 1.479 + * Get an object actor by its ID. 1.480 + * 1.481 + * @param string aActorID 1.482 + * @return object 1.483 + */ 1.484 + getActorByID: function WCA_getActorByID(aActorID) 1.485 + { 1.486 + return this._actorPool.get(aActorID); 1.487 + }, 1.488 + 1.489 + /** 1.490 + * Release an actor. 1.491 + * 1.492 + * @param object aActor 1.493 + * The actor instance you want to release. 1.494 + */ 1.495 + releaseActor: function WCA_releaseActor(aActor) 1.496 + { 1.497 + this._actorPool.removeActor(aActor.actorID); 1.498 + }, 1.499 + 1.500 + ////////////////// 1.501 + // Request handlers for known packet types. 1.502 + ////////////////// 1.503 + 1.504 + /** 1.505 + * Handler for the "startListeners" request. 1.506 + * 1.507 + * @param object aRequest 1.508 + * The JSON request object received from the Web Console client. 1.509 + * @return object 1.510 + * The response object which holds the startedListeners array. 1.511 + */ 1.512 + onStartListeners: function WCA_onStartListeners(aRequest) 1.513 + { 1.514 + let startedListeners = []; 1.515 + let window = !this.parentActor.isRootActor ? this.window : null; 1.516 + let appId = null; 1.517 + let messageManager = null; 1.518 + 1.519 + if (this._parentIsContentActor) { 1.520 + appId = this.parentActor.docShell.appId; 1.521 + messageManager = this.parentActor.messageManager; 1.522 + } 1.523 + 1.524 + while (aRequest.listeners.length > 0) { 1.525 + let listener = aRequest.listeners.shift(); 1.526 + switch (listener) { 1.527 + case "PageError": 1.528 + if (!this.consoleServiceListener) { 1.529 + this.consoleServiceListener = 1.530 + new ConsoleServiceListener(window, this); 1.531 + this.consoleServiceListener.init(); 1.532 + } 1.533 + startedListeners.push(listener); 1.534 + break; 1.535 + case "ConsoleAPI": 1.536 + if (!this.consoleAPIListener) { 1.537 + this.consoleAPIListener = 1.538 + new ConsoleAPIListener(window, this); 1.539 + this.consoleAPIListener.init(); 1.540 + } 1.541 + startedListeners.push(listener); 1.542 + break; 1.543 + case "NetworkActivity": 1.544 + if (!this.networkMonitor) { 1.545 + if (appId || messageManager) { 1.546 + this.networkMonitor = 1.547 + new NetworkMonitorChild(appId, messageManager, 1.548 + this.parentActor.actorID, this); 1.549 + } 1.550 + else { 1.551 + this.networkMonitor = new NetworkMonitor({ window: window }, this); 1.552 + } 1.553 + this.networkMonitor.init(); 1.554 + } 1.555 + startedListeners.push(listener); 1.556 + break; 1.557 + case "FileActivity": 1.558 + if (!this.consoleProgressListener) { 1.559 + this.consoleProgressListener = 1.560 + new ConsoleProgressListener(this.window, this); 1.561 + } 1.562 + this.consoleProgressListener.startMonitor(this.consoleProgressListener. 1.563 + MONITOR_FILE_ACTIVITY); 1.564 + startedListeners.push(listener); 1.565 + break; 1.566 + case "ReflowActivity": 1.567 + if (!this.consoleReflowListener) { 1.568 + this.consoleReflowListener = 1.569 + new ConsoleReflowListener(this.window, this); 1.570 + } 1.571 + startedListeners.push(listener); 1.572 + break; 1.573 + } 1.574 + } 1.575 + return { 1.576 + startedListeners: startedListeners, 1.577 + nativeConsoleAPI: this.hasNativeConsoleAPI(this.window), 1.578 + traits: this.traits, 1.579 + }; 1.580 + }, 1.581 + 1.582 + /** 1.583 + * Handler for the "stopListeners" request. 1.584 + * 1.585 + * @param object aRequest 1.586 + * The JSON request object received from the Web Console client. 1.587 + * @return object 1.588 + * The response packet to send to the client: holds the 1.589 + * stoppedListeners array. 1.590 + */ 1.591 + onStopListeners: function WCA_onStopListeners(aRequest) 1.592 + { 1.593 + let stoppedListeners = []; 1.594 + 1.595 + // If no specific listeners are requested to be detached, we stop all 1.596 + // listeners. 1.597 + let toDetach = aRequest.listeners || 1.598 + ["PageError", "ConsoleAPI", "NetworkActivity", 1.599 + "FileActivity"]; 1.600 + 1.601 + while (toDetach.length > 0) { 1.602 + let listener = toDetach.shift(); 1.603 + switch (listener) { 1.604 + case "PageError": 1.605 + if (this.consoleServiceListener) { 1.606 + this.consoleServiceListener.destroy(); 1.607 + this.consoleServiceListener = null; 1.608 + } 1.609 + stoppedListeners.push(listener); 1.610 + break; 1.611 + case "ConsoleAPI": 1.612 + if (this.consoleAPIListener) { 1.613 + this.consoleAPIListener.destroy(); 1.614 + this.consoleAPIListener = null; 1.615 + } 1.616 + stoppedListeners.push(listener); 1.617 + break; 1.618 + case "NetworkActivity": 1.619 + if (this.networkMonitor) { 1.620 + this.networkMonitor.destroy(); 1.621 + this.networkMonitor = null; 1.622 + } 1.623 + stoppedListeners.push(listener); 1.624 + break; 1.625 + case "FileActivity": 1.626 + if (this.consoleProgressListener) { 1.627 + this.consoleProgressListener.stopMonitor(this.consoleProgressListener. 1.628 + MONITOR_FILE_ACTIVITY); 1.629 + this.consoleProgressListener = null; 1.630 + } 1.631 + stoppedListeners.push(listener); 1.632 + break; 1.633 + case "ReflowActivity": 1.634 + if (this.consoleReflowListener) { 1.635 + this.consoleReflowListener.destroy(); 1.636 + this.consoleReflowListener = null; 1.637 + } 1.638 + stoppedListeners.push(listener); 1.639 + break; 1.640 + } 1.641 + } 1.642 + 1.643 + return { stoppedListeners: stoppedListeners }; 1.644 + }, 1.645 + 1.646 + /** 1.647 + * Handler for the "getCachedMessages" request. This method sends the cached 1.648 + * error messages and the window.console API calls to the client. 1.649 + * 1.650 + * @param object aRequest 1.651 + * The JSON request object received from the Web Console client. 1.652 + * @return object 1.653 + * The response packet to send to the client: it holds the cached 1.654 + * messages array. 1.655 + */ 1.656 + onGetCachedMessages: function WCA_onGetCachedMessages(aRequest) 1.657 + { 1.658 + let types = aRequest.messageTypes; 1.659 + if (!types) { 1.660 + return { 1.661 + error: "missingParameter", 1.662 + message: "The messageTypes parameter is missing.", 1.663 + }; 1.664 + } 1.665 + 1.666 + let messages = []; 1.667 + 1.668 + while (types.length > 0) { 1.669 + let type = types.shift(); 1.670 + switch (type) { 1.671 + case "ConsoleAPI": { 1.672 + if (!this.consoleAPIListener) { 1.673 + break; 1.674 + } 1.675 + let cache = this.consoleAPIListener 1.676 + .getCachedMessages(!this.parentActor.isRootActor); 1.677 + cache.forEach((aMessage) => { 1.678 + let message = this.prepareConsoleMessageForRemote(aMessage); 1.679 + message._type = type; 1.680 + messages.push(message); 1.681 + }); 1.682 + break; 1.683 + } 1.684 + case "PageError": { 1.685 + if (!this.consoleServiceListener) { 1.686 + break; 1.687 + } 1.688 + let cache = this.consoleServiceListener 1.689 + .getCachedMessages(!this.parentActor.isRootActor); 1.690 + cache.forEach((aMessage) => { 1.691 + let message = null; 1.692 + if (aMessage instanceof Ci.nsIScriptError) { 1.693 + message = this.preparePageErrorForRemote(aMessage); 1.694 + message._type = type; 1.695 + } 1.696 + else { 1.697 + message = { 1.698 + _type: "LogMessage", 1.699 + message: this._createStringGrip(aMessage.message), 1.700 + timeStamp: aMessage.timeStamp, 1.701 + }; 1.702 + } 1.703 + messages.push(message); 1.704 + }); 1.705 + break; 1.706 + } 1.707 + } 1.708 + } 1.709 + 1.710 + messages.sort(function(a, b) { return a.timeStamp - b.timeStamp; }); 1.711 + 1.712 + return { 1.713 + from: this.actorID, 1.714 + messages: messages, 1.715 + }; 1.716 + }, 1.717 + 1.718 + /** 1.719 + * Handler for the "evaluateJS" request. This method evaluates the given 1.720 + * JavaScript string and sends back the result. 1.721 + * 1.722 + * @param object aRequest 1.723 + * The JSON request object received from the Web Console client. 1.724 + * @return object 1.725 + * The evaluation response packet. 1.726 + */ 1.727 + onEvaluateJS: function WCA_onEvaluateJS(aRequest) 1.728 + { 1.729 + let input = aRequest.text; 1.730 + let timestamp = Date.now(); 1.731 + 1.732 + let evalOptions = { 1.733 + bindObjectActor: aRequest.bindObjectActor, 1.734 + frameActor: aRequest.frameActor, 1.735 + url: aRequest.url, 1.736 + }; 1.737 + let evalInfo = this.evalWithDebugger(input, evalOptions); 1.738 + let evalResult = evalInfo.result; 1.739 + let helperResult = evalInfo.helperResult; 1.740 + 1.741 + let result, errorMessage, errorGrip = null; 1.742 + if (evalResult) { 1.743 + if ("return" in evalResult) { 1.744 + result = evalResult.return; 1.745 + } 1.746 + else if ("yield" in evalResult) { 1.747 + result = evalResult.yield; 1.748 + } 1.749 + else if ("throw" in evalResult) { 1.750 + let error = evalResult.throw; 1.751 + errorGrip = this.createValueGrip(error); 1.752 + let errorToString = evalInfo.window 1.753 + .evalInGlobalWithBindings("ex + ''", {ex: error}); 1.754 + if (errorToString && typeof errorToString.return == "string") { 1.755 + errorMessage = errorToString.return; 1.756 + } 1.757 + } 1.758 + } 1.759 + 1.760 + return { 1.761 + from: this.actorID, 1.762 + input: input, 1.763 + result: this.createValueGrip(result), 1.764 + timestamp: timestamp, 1.765 + exception: errorGrip, 1.766 + exceptionMessage: errorMessage, 1.767 + helperResult: helperResult, 1.768 + }; 1.769 + }, 1.770 + 1.771 + /** 1.772 + * The Autocomplete request handler. 1.773 + * 1.774 + * @param object aRequest 1.775 + * The request message - what input to autocomplete. 1.776 + * @return object 1.777 + * The response message - matched properties. 1.778 + */ 1.779 + onAutocomplete: function WCA_onAutocomplete(aRequest) 1.780 + { 1.781 + let frameActorId = aRequest.frameActor; 1.782 + let dbgObject = null; 1.783 + let environment = null; 1.784 + 1.785 + // This is the case of the paused debugger 1.786 + if (frameActorId) { 1.787 + let frameActor = this.conn.getActor(frameActorId); 1.788 + if (frameActor) { 1.789 + let frame = frameActor.frame; 1.790 + environment = frame.environment; 1.791 + } 1.792 + else { 1.793 + Cu.reportError("Web Console Actor: the frame actor was not found: " + 1.794 + frameActorId); 1.795 + } 1.796 + } 1.797 + // This is the general case (non-paused debugger) 1.798 + else { 1.799 + dbgObject = this.dbg.makeGlobalObjectReference(this.evalWindow); 1.800 + } 1.801 + 1.802 + let result = JSPropertyProvider(dbgObject, environment, aRequest.text, 1.803 + aRequest.cursor, frameActorId) || {}; 1.804 + let matches = result.matches || []; 1.805 + let reqText = aRequest.text.substr(0, aRequest.cursor); 1.806 + 1.807 + // We consider '$' as alphanumerc because it is used in the names of some 1.808 + // helper functions. 1.809 + let lastNonAlphaIsDot = /[.][a-zA-Z0-9$]*$/.test(reqText); 1.810 + if (!lastNonAlphaIsDot) { 1.811 + if (!this._jstermHelpersCache) { 1.812 + let helpers = { 1.813 + sandbox: Object.create(null) 1.814 + }; 1.815 + JSTermHelpers(helpers); 1.816 + this._jstermHelpersCache = Object.getOwnPropertyNames(helpers.sandbox); 1.817 + } 1.818 + matches = matches.concat(this._jstermHelpersCache.filter(n => n.startsWith(result.matchProp))); 1.819 + } 1.820 + 1.821 + return { 1.822 + from: this.actorID, 1.823 + matches: matches.sort(), 1.824 + matchProp: result.matchProp, 1.825 + }; 1.826 + }, 1.827 + 1.828 + /** 1.829 + * The "clearMessagesCache" request handler. 1.830 + */ 1.831 + onClearMessagesCache: function WCA_onClearMessagesCache() 1.832 + { 1.833 + // TODO: Bug 717611 - Web Console clear button does not clear cached errors 1.834 + let windowId = !this.parentActor.isRootActor ? 1.835 + WebConsoleUtils.getInnerWindowId(this.window) : null; 1.836 + let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"] 1.837 + .getService(Ci.nsIConsoleAPIStorage); 1.838 + ConsoleAPIStorage.clearEvents(windowId); 1.839 + 1.840 + if (this.parentActor.isRootActor) { 1.841 + Services.console.logStringMessage(null); // for the Error Console 1.842 + Services.console.reset(); 1.843 + } 1.844 + return {}; 1.845 + }, 1.846 + 1.847 + /** 1.848 + * The "getPreferences" request handler. 1.849 + * 1.850 + * @param object aRequest 1.851 + * The request message - which preferences need to be retrieved. 1.852 + * @return object 1.853 + * The response message - a { key: value } object map. 1.854 + */ 1.855 + onGetPreferences: function WCA_onGetPreferences(aRequest) 1.856 + { 1.857 + let prefs = Object.create(null); 1.858 + for (let key of aRequest.preferences) { 1.859 + prefs[key] = !!this._prefs[key]; 1.860 + } 1.861 + return { preferences: prefs }; 1.862 + }, 1.863 + 1.864 + /** 1.865 + * The "setPreferences" request handler. 1.866 + * 1.867 + * @param object aRequest 1.868 + * The request message - which preferences need to be updated. 1.869 + */ 1.870 + onSetPreferences: function WCA_onSetPreferences(aRequest) 1.871 + { 1.872 + for (let key in aRequest.preferences) { 1.873 + this._prefs[key] = aRequest.preferences[key]; 1.874 + 1.875 + if (key == "NetworkMonitor.saveRequestAndResponseBodies" && 1.876 + this.networkMonitor) { 1.877 + this.networkMonitor.saveRequestAndResponseBodies = this._prefs[key]; 1.878 + } 1.879 + } 1.880 + return { updated: Object.keys(aRequest.preferences) }; 1.881 + }, 1.882 + 1.883 + ////////////////// 1.884 + // End of request handlers. 1.885 + ////////////////// 1.886 + 1.887 + /** 1.888 + * Create an object with the API we expose to the Web Console during 1.889 + * JavaScript evaluation. 1.890 + * This object inherits properties and methods from the Web Console actor. 1.891 + * 1.892 + * @private 1.893 + * @param object aDebuggerGlobal 1.894 + * A Debugger.Object that wraps a content global. This is used for the 1.895 + * JSTerm helpers. 1.896 + * @return object 1.897 + * The same object as |this|, but with an added |sandbox| property. 1.898 + * The sandbox holds methods and properties that can be used as 1.899 + * bindings during JS evaluation. 1.900 + */ 1.901 + _getJSTermHelpers: function WCA__getJSTermHelpers(aDebuggerGlobal) 1.902 + { 1.903 + let helpers = { 1.904 + window: this.evalWindow, 1.905 + chromeWindow: this.chromeWindow.bind(this), 1.906 + makeDebuggeeValue: aDebuggerGlobal.makeDebuggeeValue.bind(aDebuggerGlobal), 1.907 + createValueGrip: this.createValueGrip.bind(this), 1.908 + sandbox: Object.create(null), 1.909 + helperResult: null, 1.910 + consoleActor: this, 1.911 + }; 1.912 + JSTermHelpers(helpers); 1.913 + 1.914 + // Make sure the helpers can be used during eval. 1.915 + for (let name in helpers.sandbox) { 1.916 + let desc = Object.getOwnPropertyDescriptor(helpers.sandbox, name); 1.917 + if (desc.get || desc.set) { 1.918 + continue; 1.919 + } 1.920 + helpers.sandbox[name] = aDebuggerGlobal.makeDebuggeeValue(desc.value); 1.921 + } 1.922 + return helpers; 1.923 + }, 1.924 + 1.925 + /** 1.926 + * Evaluates a string using the debugger API. 1.927 + * 1.928 + * To allow the variables view to update properties from the Web Console we 1.929 + * provide the "bindObjectActor" mechanism: the Web Console tells the 1.930 + * ObjectActor ID for which it desires to evaluate an expression. The 1.931 + * Debugger.Object pointed at by the actor ID is bound such that it is 1.932 + * available during expression evaluation (evalInGlobalWithBindings()). 1.933 + * 1.934 + * Example: 1.935 + * _self['foobar'] = 'test' 1.936 + * where |_self| refers to the desired object. 1.937 + * 1.938 + * The |frameActor| property allows the Web Console client to provide the 1.939 + * frame actor ID, such that the expression can be evaluated in the 1.940 + * user-selected stack frame. 1.941 + * 1.942 + * For the above to work we need the debugger and the Web Console to share 1.943 + * a connection, otherwise the Web Console actor will not find the frame 1.944 + * actor. 1.945 + * 1.946 + * The Debugger.Frame comes from the jsdebugger's Debugger instance, which 1.947 + * is different from the Web Console's Debugger instance. This means that 1.948 + * for evaluation to work, we need to create a new instance for the jsterm 1.949 + * helpers - they need to be Debugger.Objects coming from the jsdebugger's 1.950 + * Debugger instance. 1.951 + * 1.952 + * When |bindObjectActor| is used objects can come from different iframes, 1.953 + * from different domains. To avoid permission-related errors when objects 1.954 + * come from a different window, we also determine the object's own global, 1.955 + * such that evaluation happens in the context of that global. This means that 1.956 + * evaluation will happen in the object's iframe, rather than the top level 1.957 + * window. 1.958 + * 1.959 + * @param string aString 1.960 + * String to evaluate. 1.961 + * @param object [aOptions] 1.962 + * Options for evaluation: 1.963 + * - bindObjectActor: the ObjectActor ID to use for evaluation. 1.964 + * |evalWithBindings()| will be called with one additional binding: 1.965 + * |_self| which will point to the Debugger.Object of the given 1.966 + * ObjectActor. 1.967 + * - frameActor: the FrameActor ID to use for evaluation. The given 1.968 + * debugger frame is used for evaluation, instead of the global window. 1.969 + * @return object 1.970 + * An object that holds the following properties: 1.971 + * - dbg: the debugger where the string was evaluated. 1.972 + * - frame: (optional) the frame where the string was evaluated. 1.973 + * - window: the Debugger.Object for the global where the string was 1.974 + * evaluated. 1.975 + * - result: the result of the evaluation. 1.976 + * - helperResult: any result coming from a JSTerm helper function. 1.977 + * - url: the url to evaluate the script as. Defaults to 1.978 + * "debugger eval code". 1.979 + */ 1.980 + evalWithDebugger: function WCA_evalWithDebugger(aString, aOptions = {}) 1.981 + { 1.982 + // The help function needs to be easy to guess, so we make the () optional. 1.983 + if (aString.trim() == "help" || aString.trim() == "?") { 1.984 + aString = "help()"; 1.985 + } 1.986 + 1.987 + // Find the Debugger.Frame of the given FrameActor. 1.988 + let frame = null, frameActor = null; 1.989 + if (aOptions.frameActor) { 1.990 + frameActor = this.conn.getActor(aOptions.frameActor); 1.991 + if (frameActor) { 1.992 + frame = frameActor.frame; 1.993 + } 1.994 + else { 1.995 + Cu.reportError("Web Console Actor: the frame actor was not found: " + 1.996 + aOptions.frameActor); 1.997 + } 1.998 + } 1.999 + 1.1000 + // If we've been given a frame actor in whose scope we should evaluate the 1.1001 + // expression, be sure to use that frame's Debugger (that is, the JavaScript 1.1002 + // debugger's Debugger) for the whole operation, not the console's Debugger. 1.1003 + // (One Debugger will treat a different Debugger's Debugger.Object instances 1.1004 + // as ordinary objects, not as references to be followed, so mixing 1.1005 + // debuggers causes strange behaviors.) 1.1006 + let dbg = frame ? frameActor.threadActor.dbg : this.dbg; 1.1007 + let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow); 1.1008 + 1.1009 + // If we have an object to bind to |_self|, create a Debugger.Object 1.1010 + // referring to that object, belonging to dbg. 1.1011 + let bindSelf = null; 1.1012 + let dbgWindow = dbg.makeGlobalObjectReference(this.evalWindow); 1.1013 + if (aOptions.bindObjectActor) { 1.1014 + let objActor = this.getActorByID(aOptions.bindObjectActor); 1.1015 + if (objActor) { 1.1016 + let jsObj = objActor.obj.unsafeDereference(); 1.1017 + // If we use the makeDebuggeeValue method of jsObj's own global, then 1.1018 + // we'll get a D.O that sees jsObj as viewed from its own compartment - 1.1019 + // that is, without wrappers. The evalWithBindings call will then wrap 1.1020 + // jsObj appropriately for the evaluation compartment. 1.1021 + let global = Cu.getGlobalForObject(jsObj); 1.1022 + dbgWindow = dbg.makeGlobalObjectReference(global); 1.1023 + bindSelf = dbgWindow.makeDebuggeeValue(jsObj); 1.1024 + } 1.1025 + } 1.1026 + 1.1027 + // Get the JSTerm helpers for the given debugger window. 1.1028 + let helpers = this._getJSTermHelpers(dbgWindow); 1.1029 + let bindings = helpers.sandbox; 1.1030 + if (bindSelf) { 1.1031 + bindings._self = bindSelf; 1.1032 + } 1.1033 + 1.1034 + // Check if the Debugger.Frame or Debugger.Object for the global include 1.1035 + // $ or $$. We will not overwrite these functions with the jsterm helpers. 1.1036 + let found$ = false, found$$ = false; 1.1037 + if (frame) { 1.1038 + let env = frame.environment; 1.1039 + if (env) { 1.1040 + found$ = !!env.find("$"); 1.1041 + found$$ = !!env.find("$$"); 1.1042 + } 1.1043 + } 1.1044 + else { 1.1045 + found$ = !!dbgWindow.getOwnPropertyDescriptor("$"); 1.1046 + found$$ = !!dbgWindow.getOwnPropertyDescriptor("$$"); 1.1047 + } 1.1048 + 1.1049 + let $ = null, $$ = null; 1.1050 + if (found$) { 1.1051 + $ = bindings.$; 1.1052 + delete bindings.$; 1.1053 + } 1.1054 + if (found$$) { 1.1055 + $$ = bindings.$$; 1.1056 + delete bindings.$$; 1.1057 + } 1.1058 + 1.1059 + // Ready to evaluate the string. 1.1060 + helpers.evalInput = aString; 1.1061 + 1.1062 + let evalOptions; 1.1063 + if (typeof aOptions.url == "string") { 1.1064 + evalOptions = { url: aOptions.url }; 1.1065 + } 1.1066 + 1.1067 + let result; 1.1068 + if (frame) { 1.1069 + result = frame.evalWithBindings(aString, bindings, evalOptions); 1.1070 + } 1.1071 + else { 1.1072 + result = dbgWindow.evalInGlobalWithBindings(aString, bindings, evalOptions); 1.1073 + } 1.1074 + 1.1075 + let helperResult = helpers.helperResult; 1.1076 + delete helpers.evalInput; 1.1077 + delete helpers.helperResult; 1.1078 + 1.1079 + if ($) { 1.1080 + bindings.$ = $; 1.1081 + } 1.1082 + if ($$) { 1.1083 + bindings.$$ = $$; 1.1084 + } 1.1085 + 1.1086 + if (bindings._self) { 1.1087 + delete bindings._self; 1.1088 + } 1.1089 + 1.1090 + return { 1.1091 + result: result, 1.1092 + helperResult: helperResult, 1.1093 + dbg: dbg, 1.1094 + frame: frame, 1.1095 + window: dbgWindow, 1.1096 + }; 1.1097 + }, 1.1098 + 1.1099 + ////////////////// 1.1100 + // Event handlers for various listeners. 1.1101 + ////////////////// 1.1102 + 1.1103 + /** 1.1104 + * Handler for messages received from the ConsoleServiceListener. This method 1.1105 + * sends the nsIConsoleMessage to the remote Web Console client. 1.1106 + * 1.1107 + * @param nsIConsoleMessage aMessage 1.1108 + * The message we need to send to the client. 1.1109 + */ 1.1110 + onConsoleServiceMessage: function WCA_onConsoleServiceMessage(aMessage) 1.1111 + { 1.1112 + let packet; 1.1113 + if (aMessage instanceof Ci.nsIScriptError) { 1.1114 + packet = { 1.1115 + from: this.actorID, 1.1116 + type: "pageError", 1.1117 + pageError: this.preparePageErrorForRemote(aMessage), 1.1118 + }; 1.1119 + } 1.1120 + else { 1.1121 + packet = { 1.1122 + from: this.actorID, 1.1123 + type: "logMessage", 1.1124 + message: this._createStringGrip(aMessage.message), 1.1125 + timeStamp: aMessage.timeStamp, 1.1126 + }; 1.1127 + } 1.1128 + this.conn.send(packet); 1.1129 + }, 1.1130 + 1.1131 + /** 1.1132 + * Prepare an nsIScriptError to be sent to the client. 1.1133 + * 1.1134 + * @param nsIScriptError aPageError 1.1135 + * The page error we need to send to the client. 1.1136 + * @return object 1.1137 + * The object you can send to the remote client. 1.1138 + */ 1.1139 + preparePageErrorForRemote: function WCA_preparePageErrorForRemote(aPageError) 1.1140 + { 1.1141 + let lineText = aPageError.sourceLine; 1.1142 + if (lineText && lineText.length > DebuggerServer.LONG_STRING_INITIAL_LENGTH) { 1.1143 + lineText = lineText.substr(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH); 1.1144 + } 1.1145 + 1.1146 + return { 1.1147 + errorMessage: this._createStringGrip(aPageError.errorMessage), 1.1148 + sourceName: aPageError.sourceName, 1.1149 + lineText: lineText, 1.1150 + lineNumber: aPageError.lineNumber, 1.1151 + columnNumber: aPageError.columnNumber, 1.1152 + category: aPageError.category, 1.1153 + timeStamp: aPageError.timeStamp, 1.1154 + warning: !!(aPageError.flags & aPageError.warningFlag), 1.1155 + error: !!(aPageError.flags & aPageError.errorFlag), 1.1156 + exception: !!(aPageError.flags & aPageError.exceptionFlag), 1.1157 + strict: !!(aPageError.flags & aPageError.strictFlag), 1.1158 + private: aPageError.isFromPrivateWindow, 1.1159 + }; 1.1160 + }, 1.1161 + 1.1162 + /** 1.1163 + * Handler for window.console API calls received from the ConsoleAPIListener. 1.1164 + * This method sends the object to the remote Web Console client. 1.1165 + * 1.1166 + * @see ConsoleAPIListener 1.1167 + * @param object aMessage 1.1168 + * The console API call we need to send to the remote client. 1.1169 + */ 1.1170 + onConsoleAPICall: function WCA_onConsoleAPICall(aMessage) 1.1171 + { 1.1172 + let packet = { 1.1173 + from: this.actorID, 1.1174 + type: "consoleAPICall", 1.1175 + message: this.prepareConsoleMessageForRemote(aMessage), 1.1176 + }; 1.1177 + this.conn.send(packet); 1.1178 + }, 1.1179 + 1.1180 + /** 1.1181 + * Handler for network events. This method is invoked when a new network event 1.1182 + * is about to be recorded. 1.1183 + * 1.1184 + * @see NetworkEventActor 1.1185 + * @see NetworkMonitor from webconsole/utils.js 1.1186 + * 1.1187 + * @param object aEvent 1.1188 + * The initial network request event information. 1.1189 + * @param nsIHttpChannel aChannel 1.1190 + * The network request nsIHttpChannel object. 1.1191 + * @return object 1.1192 + * A new NetworkEventActor is returned. This is used for tracking the 1.1193 + * network request and response. 1.1194 + */ 1.1195 + onNetworkEvent: function WCA_onNetworkEvent(aEvent, aChannel) 1.1196 + { 1.1197 + let actor = this.getNetworkEventActor(aChannel); 1.1198 + actor.init(aEvent); 1.1199 + 1.1200 + let packet = { 1.1201 + from: this.actorID, 1.1202 + type: "networkEvent", 1.1203 + eventActor: actor.grip(), 1.1204 + }; 1.1205 + 1.1206 + this.conn.send(packet); 1.1207 + 1.1208 + return actor; 1.1209 + }, 1.1210 + 1.1211 + /** 1.1212 + * Get the NetworkEventActor for a nsIChannel, if it exists, 1.1213 + * otherwise create a new one. 1.1214 + * 1.1215 + * @param nsIHttpChannel aChannel 1.1216 + * The channel for the network event. 1.1217 + * @return object 1.1218 + * The NetworkEventActor for the given channel. 1.1219 + */ 1.1220 + getNetworkEventActor: function WCA_getNetworkEventActor(aChannel) { 1.1221 + let actor = this._netEvents.get(aChannel); 1.1222 + if (actor) { 1.1223 + // delete from map as we should only need to do this check once 1.1224 + this._netEvents.delete(aChannel); 1.1225 + actor.channel = null; 1.1226 + return actor; 1.1227 + } 1.1228 + 1.1229 + actor = new NetworkEventActor(aChannel, this); 1.1230 + this._actorPool.addActor(actor); 1.1231 + return actor; 1.1232 + }, 1.1233 + 1.1234 + /** 1.1235 + * Send a new HTTP request from the target's window. 1.1236 + * 1.1237 + * @param object aMessage 1.1238 + * Object with 'request' - the HTTP request details. 1.1239 + */ 1.1240 + onSendHTTPRequest: function WCA_onSendHTTPRequest(aMessage) 1.1241 + { 1.1242 + let details = aMessage.request; 1.1243 + 1.1244 + // send request from target's window 1.1245 + let request = new this.window.XMLHttpRequest(); 1.1246 + request.open(details.method, details.url, true); 1.1247 + 1.1248 + for (let {name, value} of details.headers) { 1.1249 + request.setRequestHeader(name, value); 1.1250 + } 1.1251 + request.send(details.body); 1.1252 + 1.1253 + let actor = this.getNetworkEventActor(request.channel); 1.1254 + 1.1255 + // map channel to actor so we can associate future events with it 1.1256 + this._netEvents.set(request.channel, actor); 1.1257 + 1.1258 + return { 1.1259 + from: this.actorID, 1.1260 + eventActor: actor.grip() 1.1261 + }; 1.1262 + }, 1.1263 + 1.1264 + /** 1.1265 + * Handler for file activity. This method sends the file request information 1.1266 + * to the remote Web Console client. 1.1267 + * 1.1268 + * @see ConsoleProgressListener 1.1269 + * @param string aFileURI 1.1270 + * The requested file URI. 1.1271 + */ 1.1272 + onFileActivity: function WCA_onFileActivity(aFileURI) 1.1273 + { 1.1274 + let packet = { 1.1275 + from: this.actorID, 1.1276 + type: "fileActivity", 1.1277 + uri: aFileURI, 1.1278 + }; 1.1279 + this.conn.send(packet); 1.1280 + }, 1.1281 + 1.1282 + /** 1.1283 + * Handler for reflow activity. This method forwards reflow events to the 1.1284 + * remote Web Console client. 1.1285 + * 1.1286 + * @see ConsoleReflowListener 1.1287 + * @param Object aReflowInfo 1.1288 + */ 1.1289 + onReflowActivity: function WCA_onReflowActivity(aReflowInfo) 1.1290 + { 1.1291 + let packet = { 1.1292 + from: this.actorID, 1.1293 + type: "reflowActivity", 1.1294 + interruptible: aReflowInfo.interruptible, 1.1295 + start: aReflowInfo.start, 1.1296 + end: aReflowInfo.end, 1.1297 + sourceURL: aReflowInfo.sourceURL, 1.1298 + sourceLine: aReflowInfo.sourceLine, 1.1299 + functionName: aReflowInfo.functionName 1.1300 + }; 1.1301 + 1.1302 + this.conn.send(packet); 1.1303 + }, 1.1304 + 1.1305 + ////////////////// 1.1306 + // End of event handlers for various listeners. 1.1307 + ////////////////// 1.1308 + 1.1309 + /** 1.1310 + * Prepare a message from the console API to be sent to the remote Web Console 1.1311 + * instance. 1.1312 + * 1.1313 + * @param object aMessage 1.1314 + * The original message received from console-api-log-event. 1.1315 + * @return object 1.1316 + * The object that can be sent to the remote client. 1.1317 + */ 1.1318 + prepareConsoleMessageForRemote: 1.1319 + function WCA_prepareConsoleMessageForRemote(aMessage) 1.1320 + { 1.1321 + let result = WebConsoleUtils.cloneObject(aMessage); 1.1322 + delete result.wrappedJSObject; 1.1323 + delete result.ID; 1.1324 + delete result.innerID; 1.1325 + 1.1326 + result.arguments = Array.map(aMessage.arguments || [], (aObj) => { 1.1327 + let dbgObj = this.makeDebuggeeValue(aObj, true); 1.1328 + return this.createValueGrip(dbgObj); 1.1329 + }); 1.1330 + 1.1331 + result.styles = Array.map(aMessage.styles || [], (aString) => { 1.1332 + return this.createValueGrip(aString); 1.1333 + }); 1.1334 + 1.1335 + return result; 1.1336 + }, 1.1337 + 1.1338 + /** 1.1339 + * Find the XUL window that owns the content window. 1.1340 + * 1.1341 + * @return Window 1.1342 + * The XUL window that owns the content window. 1.1343 + */ 1.1344 + chromeWindow: function WCA_chromeWindow() 1.1345 + { 1.1346 + let window = null; 1.1347 + try { 1.1348 + window = this.window.QueryInterface(Ci.nsIInterfaceRequestor) 1.1349 + .getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell) 1.1350 + .chromeEventHandler.ownerDocument.defaultView; 1.1351 + } 1.1352 + catch (ex) { 1.1353 + // The above can fail because chromeEventHandler is not available for all 1.1354 + // kinds of |this.window|. 1.1355 + } 1.1356 + 1.1357 + return window; 1.1358 + }, 1.1359 + 1.1360 + /** 1.1361 + * Notification observer for the "last-pb-context-exited" topic. 1.1362 + * 1.1363 + * @private 1.1364 + * @param object aSubject 1.1365 + * Notification subject - in this case it is the inner window ID that 1.1366 + * was destroyed. 1.1367 + * @param string aTopic 1.1368 + * Notification topic. 1.1369 + */ 1.1370 + _onObserverNotification: function WCA__onObserverNotification(aSubject, aTopic) 1.1371 + { 1.1372 + switch (aTopic) { 1.1373 + case "last-pb-context-exited": 1.1374 + this.conn.send({ 1.1375 + from: this.actorID, 1.1376 + type: "lastPrivateContextExited", 1.1377 + }); 1.1378 + break; 1.1379 + } 1.1380 + }, 1.1381 + 1.1382 + /** 1.1383 + * The "will-navigate" progress listener. This is used to clear the current 1.1384 + * eval scope. 1.1385 + */ 1.1386 + _onWillNavigate: function WCA__onWillNavigate({ window, isTopLevel }) 1.1387 + { 1.1388 + if (isTopLevel) { 1.1389 + this._evalWindow = null; 1.1390 + events.off(this.parentActor, "will-navigate", this._onWillNavigate); 1.1391 + this._progressListenerActive = false; 1.1392 + } 1.1393 + }, 1.1394 +}; 1.1395 + 1.1396 +WebConsoleActor.prototype.requestTypes = 1.1397 +{ 1.1398 + startListeners: WebConsoleActor.prototype.onStartListeners, 1.1399 + stopListeners: WebConsoleActor.prototype.onStopListeners, 1.1400 + getCachedMessages: WebConsoleActor.prototype.onGetCachedMessages, 1.1401 + evaluateJS: WebConsoleActor.prototype.onEvaluateJS, 1.1402 + autocomplete: WebConsoleActor.prototype.onAutocomplete, 1.1403 + clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache, 1.1404 + getPreferences: WebConsoleActor.prototype.onGetPreferences, 1.1405 + setPreferences: WebConsoleActor.prototype.onSetPreferences, 1.1406 + sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest 1.1407 +}; 1.1408 + 1.1409 +/** 1.1410 + * Creates an actor for a network event. 1.1411 + * 1.1412 + * @constructor 1.1413 + * @param object aChannel 1.1414 + * The nsIChannel associated with this event. 1.1415 + * @param object aWebConsoleActor 1.1416 + * The parent WebConsoleActor instance for this object. 1.1417 + */ 1.1418 +function NetworkEventActor(aChannel, aWebConsoleActor) 1.1419 +{ 1.1420 + this.parent = aWebConsoleActor; 1.1421 + this.conn = this.parent.conn; 1.1422 + this.channel = aChannel; 1.1423 + 1.1424 + this._request = { 1.1425 + method: null, 1.1426 + url: null, 1.1427 + httpVersion: null, 1.1428 + headers: [], 1.1429 + cookies: [], 1.1430 + headersSize: null, 1.1431 + postData: {}, 1.1432 + }; 1.1433 + 1.1434 + this._response = { 1.1435 + headers: [], 1.1436 + cookies: [], 1.1437 + content: {}, 1.1438 + }; 1.1439 + 1.1440 + this._timings = {}; 1.1441 + 1.1442 + // Keep track of LongStringActors owned by this NetworkEventActor. 1.1443 + this._longStringActors = new Set(); 1.1444 +} 1.1445 + 1.1446 +NetworkEventActor.prototype = 1.1447 +{ 1.1448 + _request: null, 1.1449 + _response: null, 1.1450 + _timings: null, 1.1451 + _longStringActors: null, 1.1452 + 1.1453 + actorPrefix: "netEvent", 1.1454 + 1.1455 + /** 1.1456 + * Returns a grip for this actor for returning in a protocol message. 1.1457 + */ 1.1458 + grip: function NEA_grip() 1.1459 + { 1.1460 + return { 1.1461 + actor: this.actorID, 1.1462 + startedDateTime: this._startedDateTime, 1.1463 + url: this._request.url, 1.1464 + method: this._request.method, 1.1465 + isXHR: this._isXHR, 1.1466 + private: this._private, 1.1467 + }; 1.1468 + }, 1.1469 + 1.1470 + /** 1.1471 + * Releases this actor from the pool. 1.1472 + */ 1.1473 + release: function NEA_release() 1.1474 + { 1.1475 + for (let grip of this._longStringActors) { 1.1476 + let actor = this.parent.getActorByID(grip.actor); 1.1477 + if (actor) { 1.1478 + this.parent.releaseActor(actor); 1.1479 + } 1.1480 + } 1.1481 + this._longStringActors = new Set(); 1.1482 + 1.1483 + if (this.channel) { 1.1484 + this.parent._netEvents.delete(this.channel); 1.1485 + } 1.1486 + this.parent.releaseActor(this); 1.1487 + }, 1.1488 + 1.1489 + /** 1.1490 + * Handle a protocol request to release a grip. 1.1491 + */ 1.1492 + onRelease: function NEA_onRelease() 1.1493 + { 1.1494 + this.release(); 1.1495 + return {}; 1.1496 + }, 1.1497 + 1.1498 + /** 1.1499 + * Set the properties of this actor based on it's corresponding 1.1500 + * network event. 1.1501 + * 1.1502 + * @param object aNetworkEvent 1.1503 + * The network event associated with this actor. 1.1504 + */ 1.1505 + init: function NEA_init(aNetworkEvent) 1.1506 + { 1.1507 + this._startedDateTime = aNetworkEvent.startedDateTime; 1.1508 + this._isXHR = aNetworkEvent.isXHR; 1.1509 + 1.1510 + for (let prop of ['method', 'url', 'httpVersion', 'headersSize']) { 1.1511 + this._request[prop] = aNetworkEvent[prop]; 1.1512 + } 1.1513 + 1.1514 + this._discardRequestBody = aNetworkEvent.discardRequestBody; 1.1515 + this._discardResponseBody = aNetworkEvent.discardResponseBody; 1.1516 + this._private = aNetworkEvent.private; 1.1517 + }, 1.1518 + 1.1519 + /** 1.1520 + * The "getRequestHeaders" packet type handler. 1.1521 + * 1.1522 + * @return object 1.1523 + * The response packet - network request headers. 1.1524 + */ 1.1525 + onGetRequestHeaders: function NEA_onGetRequestHeaders() 1.1526 + { 1.1527 + return { 1.1528 + from: this.actorID, 1.1529 + headers: this._request.headers, 1.1530 + headersSize: this._request.headersSize, 1.1531 + }; 1.1532 + }, 1.1533 + 1.1534 + /** 1.1535 + * The "getRequestCookies" packet type handler. 1.1536 + * 1.1537 + * @return object 1.1538 + * The response packet - network request cookies. 1.1539 + */ 1.1540 + onGetRequestCookies: function NEA_onGetRequestCookies() 1.1541 + { 1.1542 + return { 1.1543 + from: this.actorID, 1.1544 + cookies: this._request.cookies, 1.1545 + }; 1.1546 + }, 1.1547 + 1.1548 + /** 1.1549 + * The "getRequestPostData" packet type handler. 1.1550 + * 1.1551 + * @return object 1.1552 + * The response packet - network POST data. 1.1553 + */ 1.1554 + onGetRequestPostData: function NEA_onGetRequestPostData() 1.1555 + { 1.1556 + return { 1.1557 + from: this.actorID, 1.1558 + postData: this._request.postData, 1.1559 + postDataDiscarded: this._discardRequestBody, 1.1560 + }; 1.1561 + }, 1.1562 + 1.1563 + /** 1.1564 + * The "getResponseHeaders" packet type handler. 1.1565 + * 1.1566 + * @return object 1.1567 + * The response packet - network response headers. 1.1568 + */ 1.1569 + onGetResponseHeaders: function NEA_onGetResponseHeaders() 1.1570 + { 1.1571 + return { 1.1572 + from: this.actorID, 1.1573 + headers: this._response.headers, 1.1574 + headersSize: this._response.headersSize, 1.1575 + }; 1.1576 + }, 1.1577 + 1.1578 + /** 1.1579 + * The "getResponseCookies" packet type handler. 1.1580 + * 1.1581 + * @return object 1.1582 + * The response packet - network response cookies. 1.1583 + */ 1.1584 + onGetResponseCookies: function NEA_onGetResponseCookies() 1.1585 + { 1.1586 + return { 1.1587 + from: this.actorID, 1.1588 + cookies: this._response.cookies, 1.1589 + }; 1.1590 + }, 1.1591 + 1.1592 + /** 1.1593 + * The "getResponseContent" packet type handler. 1.1594 + * 1.1595 + * @return object 1.1596 + * The response packet - network response content. 1.1597 + */ 1.1598 + onGetResponseContent: function NEA_onGetResponseContent() 1.1599 + { 1.1600 + return { 1.1601 + from: this.actorID, 1.1602 + content: this._response.content, 1.1603 + contentDiscarded: this._discardResponseBody, 1.1604 + }; 1.1605 + }, 1.1606 + 1.1607 + /** 1.1608 + * The "getEventTimings" packet type handler. 1.1609 + * 1.1610 + * @return object 1.1611 + * The response packet - network event timings. 1.1612 + */ 1.1613 + onGetEventTimings: function NEA_onGetEventTimings() 1.1614 + { 1.1615 + return { 1.1616 + from: this.actorID, 1.1617 + timings: this._timings, 1.1618 + totalTime: this._totalTime, 1.1619 + }; 1.1620 + }, 1.1621 + 1.1622 + /****************************************************************** 1.1623 + * Listeners for new network event data coming from NetworkMonitor. 1.1624 + ******************************************************************/ 1.1625 + 1.1626 + /** 1.1627 + * Add network request headers. 1.1628 + * 1.1629 + * @param array aHeaders 1.1630 + * The request headers array. 1.1631 + */ 1.1632 + addRequestHeaders: function NEA_addRequestHeaders(aHeaders) 1.1633 + { 1.1634 + this._request.headers = aHeaders; 1.1635 + this._prepareHeaders(aHeaders); 1.1636 + 1.1637 + let packet = { 1.1638 + from: this.actorID, 1.1639 + type: "networkEventUpdate", 1.1640 + updateType: "requestHeaders", 1.1641 + headers: aHeaders.length, 1.1642 + headersSize: this._request.headersSize, 1.1643 + }; 1.1644 + 1.1645 + this.conn.send(packet); 1.1646 + }, 1.1647 + 1.1648 + /** 1.1649 + * Add network request cookies. 1.1650 + * 1.1651 + * @param array aCookies 1.1652 + * The request cookies array. 1.1653 + */ 1.1654 + addRequestCookies: function NEA_addRequestCookies(aCookies) 1.1655 + { 1.1656 + this._request.cookies = aCookies; 1.1657 + this._prepareHeaders(aCookies); 1.1658 + 1.1659 + let packet = { 1.1660 + from: this.actorID, 1.1661 + type: "networkEventUpdate", 1.1662 + updateType: "requestCookies", 1.1663 + cookies: aCookies.length, 1.1664 + }; 1.1665 + 1.1666 + this.conn.send(packet); 1.1667 + }, 1.1668 + 1.1669 + /** 1.1670 + * Add network request POST data. 1.1671 + * 1.1672 + * @param object aPostData 1.1673 + * The request POST data. 1.1674 + */ 1.1675 + addRequestPostData: function NEA_addRequestPostData(aPostData) 1.1676 + { 1.1677 + this._request.postData = aPostData; 1.1678 + aPostData.text = this.parent._createStringGrip(aPostData.text); 1.1679 + if (typeof aPostData.text == "object") { 1.1680 + this._longStringActors.add(aPostData.text); 1.1681 + } 1.1682 + 1.1683 + let packet = { 1.1684 + from: this.actorID, 1.1685 + type: "networkEventUpdate", 1.1686 + updateType: "requestPostData", 1.1687 + dataSize: aPostData.text.length, 1.1688 + discardRequestBody: this._discardRequestBody, 1.1689 + }; 1.1690 + 1.1691 + this.conn.send(packet); 1.1692 + }, 1.1693 + 1.1694 + /** 1.1695 + * Add the initial network response information. 1.1696 + * 1.1697 + * @param object aInfo 1.1698 + * The response information. 1.1699 + */ 1.1700 + addResponseStart: function NEA_addResponseStart(aInfo) 1.1701 + { 1.1702 + this._response.httpVersion = aInfo.httpVersion; 1.1703 + this._response.status = aInfo.status; 1.1704 + this._response.statusText = aInfo.statusText; 1.1705 + this._response.headersSize = aInfo.headersSize; 1.1706 + this._discardResponseBody = aInfo.discardResponseBody; 1.1707 + 1.1708 + let packet = { 1.1709 + from: this.actorID, 1.1710 + type: "networkEventUpdate", 1.1711 + updateType: "responseStart", 1.1712 + response: aInfo, 1.1713 + }; 1.1714 + 1.1715 + this.conn.send(packet); 1.1716 + }, 1.1717 + 1.1718 + /** 1.1719 + * Add network response headers. 1.1720 + * 1.1721 + * @param array aHeaders 1.1722 + * The response headers array. 1.1723 + */ 1.1724 + addResponseHeaders: function NEA_addResponseHeaders(aHeaders) 1.1725 + { 1.1726 + this._response.headers = aHeaders; 1.1727 + this._prepareHeaders(aHeaders); 1.1728 + 1.1729 + let packet = { 1.1730 + from: this.actorID, 1.1731 + type: "networkEventUpdate", 1.1732 + updateType: "responseHeaders", 1.1733 + headers: aHeaders.length, 1.1734 + headersSize: this._response.headersSize, 1.1735 + }; 1.1736 + 1.1737 + this.conn.send(packet); 1.1738 + }, 1.1739 + 1.1740 + /** 1.1741 + * Add network response cookies. 1.1742 + * 1.1743 + * @param array aCookies 1.1744 + * The response cookies array. 1.1745 + */ 1.1746 + addResponseCookies: function NEA_addResponseCookies(aCookies) 1.1747 + { 1.1748 + this._response.cookies = aCookies; 1.1749 + this._prepareHeaders(aCookies); 1.1750 + 1.1751 + let packet = { 1.1752 + from: this.actorID, 1.1753 + type: "networkEventUpdate", 1.1754 + updateType: "responseCookies", 1.1755 + cookies: aCookies.length, 1.1756 + }; 1.1757 + 1.1758 + this.conn.send(packet); 1.1759 + }, 1.1760 + 1.1761 + /** 1.1762 + * Add network response content. 1.1763 + * 1.1764 + * @param object aContent 1.1765 + * The response content. 1.1766 + * @param boolean aDiscardedResponseBody 1.1767 + * Tells if the response content was recorded or not. 1.1768 + */ 1.1769 + addResponseContent: 1.1770 + function NEA_addResponseContent(aContent, aDiscardedResponseBody) 1.1771 + { 1.1772 + this._response.content = aContent; 1.1773 + aContent.text = this.parent._createStringGrip(aContent.text); 1.1774 + if (typeof aContent.text == "object") { 1.1775 + this._longStringActors.add(aContent.text); 1.1776 + } 1.1777 + 1.1778 + let packet = { 1.1779 + from: this.actorID, 1.1780 + type: "networkEventUpdate", 1.1781 + updateType: "responseContent", 1.1782 + mimeType: aContent.mimeType, 1.1783 + contentSize: aContent.text.length, 1.1784 + discardResponseBody: aDiscardedResponseBody, 1.1785 + }; 1.1786 + 1.1787 + this.conn.send(packet); 1.1788 + }, 1.1789 + 1.1790 + /** 1.1791 + * Add network event timing information. 1.1792 + * 1.1793 + * @param number aTotal 1.1794 + * The total time of the network event. 1.1795 + * @param object aTimings 1.1796 + * Timing details about the network event. 1.1797 + */ 1.1798 + addEventTimings: function NEA_addEventTimings(aTotal, aTimings) 1.1799 + { 1.1800 + this._totalTime = aTotal; 1.1801 + this._timings = aTimings; 1.1802 + 1.1803 + let packet = { 1.1804 + from: this.actorID, 1.1805 + type: "networkEventUpdate", 1.1806 + updateType: "eventTimings", 1.1807 + totalTime: aTotal, 1.1808 + }; 1.1809 + 1.1810 + this.conn.send(packet); 1.1811 + }, 1.1812 + 1.1813 + /** 1.1814 + * Prepare the headers array to be sent to the client by using the 1.1815 + * LongStringActor for the header values, when needed. 1.1816 + * 1.1817 + * @private 1.1818 + * @param array aHeaders 1.1819 + */ 1.1820 + _prepareHeaders: function NEA__prepareHeaders(aHeaders) 1.1821 + { 1.1822 + for (let header of aHeaders) { 1.1823 + header.value = this.parent._createStringGrip(header.value); 1.1824 + if (typeof header.value == "object") { 1.1825 + this._longStringActors.add(header.value); 1.1826 + } 1.1827 + } 1.1828 + }, 1.1829 +}; 1.1830 + 1.1831 +NetworkEventActor.prototype.requestTypes = 1.1832 +{ 1.1833 + "release": NetworkEventActor.prototype.onRelease, 1.1834 + "getRequestHeaders": NetworkEventActor.prototype.onGetRequestHeaders, 1.1835 + "getRequestCookies": NetworkEventActor.prototype.onGetRequestCookies, 1.1836 + "getRequestPostData": NetworkEventActor.prototype.onGetRequestPostData, 1.1837 + "getResponseHeaders": NetworkEventActor.prototype.onGetResponseHeaders, 1.1838 + "getResponseCookies": NetworkEventActor.prototype.onGetResponseCookies, 1.1839 + "getResponseContent": NetworkEventActor.prototype.onGetResponseContent, 1.1840 + "getEventTimings": NetworkEventActor.prototype.onGetEventTimings, 1.1841 +}; 1.1842 + 1.1843 +exports.register = function(handle) { 1.1844 + handle.addGlobalActor(WebConsoleActor, "consoleActor"); 1.1845 + handle.addTabActor(WebConsoleActor, "consoleActor"); 1.1846 +}; 1.1847 + 1.1848 +exports.unregister = function(handle) { 1.1849 + handle.removeGlobalActor(WebConsoleActor, "consoleActor"); 1.1850 + handle.removeTabActor(WebConsoleActor, "consoleActor"); 1.1851 +};