1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/netmonitor/netmonitor-controller.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,812 @@ 1.4 +/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ft=javascript 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 +"use strict"; 1.10 + 1.11 +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; 1.12 + 1.13 +const NET_STRINGS_URI = "chrome://browser/locale/devtools/netmonitor.properties"; 1.14 +const LISTENERS = [ "NetworkActivity" ]; 1.15 +const NET_PREFS = { "NetworkMonitor.saveRequestAndResponseBodies": true }; 1.16 + 1.17 +// The panel's window global is an EventEmitter firing the following events: 1.18 +const EVENTS = { 1.19 + // When the monitored target begins and finishes navigating. 1.20 + TARGET_WILL_NAVIGATE: "NetMonitor:TargetWillNavigate", 1.21 + TARGET_DID_NAVIGATE: "NetMonitor:TargetNavigate", 1.22 + 1.23 + // When a network event is received. 1.24 + // See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for 1.25 + // more information about what each packet is supposed to deliver. 1.26 + NETWORK_EVENT: "NetMonitor:NetworkEvent", 1.27 + 1.28 + // When request headers begin and finish receiving. 1.29 + UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders", 1.30 + RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders", 1.31 + 1.32 + // When request cookies begin and finish receiving. 1.33 + UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies", 1.34 + RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies", 1.35 + 1.36 + // When request post data begins and finishes receiving. 1.37 + UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData", 1.38 + RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData", 1.39 + 1.40 + // When response headers begin and finish receiving. 1.41 + UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders", 1.42 + RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders", 1.43 + 1.44 + // When response cookies begin and finish receiving. 1.45 + UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies", 1.46 + RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies", 1.47 + 1.48 + // When event timings begin and finish receiving. 1.49 + UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings", 1.50 + RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings", 1.51 + 1.52 + // When response content begins, updates and finishes receiving. 1.53 + STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart", 1.54 + UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent", 1.55 + RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent", 1.56 + 1.57 + // When the request post params are displayed in the UI. 1.58 + REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable", 1.59 + 1.60 + // When the response body is displayed in the UI. 1.61 + RESPONSE_BODY_DISPLAYED: "NetMonitor:ResponseBodyAvailable", 1.62 + 1.63 + // When the html response preview is displayed in the UI. 1.64 + RESPONSE_HTML_PREVIEW_DISPLAYED: "NetMonitor:ResponseHtmlPreviewAvailable", 1.65 + 1.66 + // When the image response thumbnail is displayed in the UI. 1.67 + RESPONSE_IMAGE_THUMBNAIL_DISPLAYED: "NetMonitor:ResponseImageThumbnailAvailable", 1.68 + 1.69 + // When a tab is selected in the NetworkDetailsView and subsequently rendered. 1.70 + TAB_UPDATED: "NetMonitor:TabUpdated", 1.71 + 1.72 + // Fired when Sidebar has finished being populated. 1.73 + SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated", 1.74 + 1.75 + // Fired when NetworkDetailsView has finished being populated. 1.76 + NETWORKDETAILSVIEW_POPULATED: "NetMonitor:NetworkDetailsViewPopulated", 1.77 + 1.78 + // Fired when CustomRequestView has finished being populated. 1.79 + CUSTOMREQUESTVIEW_POPULATED: "NetMonitor:CustomRequestViewPopulated", 1.80 + 1.81 + // Fired when charts have been displayed in the PerformanceStatisticsView. 1.82 + PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed", 1.83 + PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed", 1.84 + EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed", 1.85 + 1.86 + // Fired once the NetMonitorController establishes a connection to the debug 1.87 + // target. 1.88 + CONNECTED: "connected", 1.89 +}; 1.90 + 1.91 +// Descriptions for what this frontend is currently doing. 1.92 +const ACTIVITY_TYPE = { 1.93 + // Standing by and handling requests normally. 1.94 + NONE: 0, 1.95 + 1.96 + // Forcing the target to reload with cache enabled or disabled. 1.97 + RELOAD: { 1.98 + WITH_CACHE_ENABLED: 1, 1.99 + WITH_CACHE_DISABLED: 2 1.100 + }, 1.101 + 1.102 + // Enabling or disabling the cache without triggering a reload. 1.103 + ENABLE_CACHE: 3, 1.104 + DISABLE_CACHE: 4 1.105 +}; 1.106 + 1.107 +Cu.import("resource://gre/modules/Services.jsm"); 1.108 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.109 +Cu.import("resource:///modules/devtools/SideMenuWidget.jsm"); 1.110 +Cu.import("resource:///modules/devtools/VariablesView.jsm"); 1.111 +Cu.import("resource:///modules/devtools/VariablesViewController.jsm"); 1.112 +Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); 1.113 + 1.114 +const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require; 1.115 +const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; 1.116 +const EventEmitter = require("devtools/toolkit/event-emitter"); 1.117 +const Editor = require("devtools/sourceeditor/editor"); 1.118 +const {Tooltip} = require("devtools/shared/widgets/Tooltip"); 1.119 + 1.120 +XPCOMUtils.defineLazyModuleGetter(this, "Chart", 1.121 + "resource:///modules/devtools/Chart.jsm"); 1.122 + 1.123 +XPCOMUtils.defineLazyModuleGetter(this, "Curl", 1.124 + "resource:///modules/devtools/Curl.jsm"); 1.125 + 1.126 +XPCOMUtils.defineLazyModuleGetter(this, "CurlUtils", 1.127 + "resource:///modules/devtools/Curl.jsm"); 1.128 + 1.129 +XPCOMUtils.defineLazyModuleGetter(this, "Task", 1.130 + "resource://gre/modules/Task.jsm"); 1.131 + 1.132 +XPCOMUtils.defineLazyModuleGetter(this, "PluralForm", 1.133 + "resource://gre/modules/PluralForm.jsm"); 1.134 + 1.135 +XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils", 1.136 + "resource://gre/modules/devtools/DevToolsUtils.jsm"); 1.137 + 1.138 +XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper", 1.139 + "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); 1.140 + 1.141 +Object.defineProperty(this, "NetworkHelper", { 1.142 + get: function() { 1.143 + return require("devtools/toolkit/webconsole/network-helper"); 1.144 + }, 1.145 + configurable: true, 1.146 + enumerable: true 1.147 +}); 1.148 + 1.149 +/** 1.150 + * Object defining the network monitor controller components. 1.151 + */ 1.152 +let NetMonitorController = { 1.153 + /** 1.154 + * Initializes the view. 1.155 + * 1.156 + * @return object 1.157 + * A promise that is resolved when the monitor finishes startup. 1.158 + */ 1.159 + startupNetMonitor: function() { 1.160 + if (this._startup) { 1.161 + return this._startup; 1.162 + } 1.163 + 1.164 + NetMonitorView.initialize(); 1.165 + 1.166 + // Startup is synchronous, for now. 1.167 + return this._startup = promise.resolve(); 1.168 + }, 1.169 + 1.170 + /** 1.171 + * Destroys the view and disconnects the monitor client from the server. 1.172 + * 1.173 + * @return object 1.174 + * A promise that is resolved when the monitor finishes shutdown. 1.175 + */ 1.176 + shutdownNetMonitor: function() { 1.177 + if (this._shutdown) { 1.178 + return this._shutdown; 1.179 + } 1.180 + 1.181 + NetMonitorView.destroy(); 1.182 + this.TargetEventsHandler.disconnect(); 1.183 + this.NetworkEventsHandler.disconnect(); 1.184 + this.disconnect(); 1.185 + 1.186 + // Shutdown is synchronous, for now. 1.187 + return this._shutdown = promise.resolve(); 1.188 + }, 1.189 + 1.190 + /** 1.191 + * Initiates remote or chrome network monitoring based on the current target, 1.192 + * wiring event handlers as necessary. 1.193 + * 1.194 + * @return object 1.195 + * A promise that is resolved when the monitor finishes connecting. 1.196 + */ 1.197 + connect: Task.async(function*() { 1.198 + if (this._connection) { 1.199 + return this._connection; 1.200 + } 1.201 + 1.202 + let deferred = promise.defer(); 1.203 + this._connection = deferred.promise; 1.204 + 1.205 + let target = this._target; 1.206 + let { client, form } = target; 1.207 + if (target.chrome) { 1.208 + this._startChromeMonitoring(client, form.consoleActor, deferred.resolve); 1.209 + } else { 1.210 + this._startMonitoringTab(client, form, deferred.resolve); 1.211 + } 1.212 + 1.213 + yield deferred.promise; 1.214 + window.emit(EVENTS.CONNECTED); 1.215 + }), 1.216 + 1.217 + /** 1.218 + * Disconnects the debugger client and removes event handlers as necessary. 1.219 + */ 1.220 + disconnect: function() { 1.221 + // When debugging local or a remote instance, the connection is closed by 1.222 + // the RemoteTarget. 1.223 + this._connection = null; 1.224 + this.client = null; 1.225 + this.tabClient = null; 1.226 + this.webConsoleClient = null; 1.227 + }, 1.228 + 1.229 + /** 1.230 + * Sets up a monitoring session. 1.231 + * 1.232 + * @param DebuggerClient aClient 1.233 + * The debugger client. 1.234 + * @param object aTabGrip 1.235 + * The remote protocol grip of the tab. 1.236 + * @param function aCallback 1.237 + * A function to invoke once the client attached to the console client. 1.238 + */ 1.239 + _startMonitoringTab: function(aClient, aTabGrip, aCallback) { 1.240 + if (!aClient) { 1.241 + Cu.reportError("No client found!"); 1.242 + return; 1.243 + } 1.244 + this.client = aClient; 1.245 + 1.246 + aClient.attachTab(aTabGrip.actor, (aResponse, aTabClient) => { 1.247 + if (!aTabClient) { 1.248 + Cu.reportError("No tab client found!"); 1.249 + return; 1.250 + } 1.251 + this.tabClient = aTabClient; 1.252 + 1.253 + aClient.attachConsole(aTabGrip.consoleActor, LISTENERS, (aResponse, aWebConsoleClient) => { 1.254 + if (!aWebConsoleClient) { 1.255 + Cu.reportError("Couldn't attach to console: " + aResponse.error); 1.256 + return; 1.257 + } 1.258 + this.webConsoleClient = aWebConsoleClient; 1.259 + this.webConsoleClient.setPreferences(NET_PREFS, () => { 1.260 + this.TargetEventsHandler.connect(); 1.261 + this.NetworkEventsHandler.connect(); 1.262 + 1.263 + if (aCallback) { 1.264 + aCallback(); 1.265 + } 1.266 + }); 1.267 + }); 1.268 + }); 1.269 + }, 1.270 + 1.271 + /** 1.272 + * Sets up a chrome monitoring session. 1.273 + * 1.274 + * @param DebuggerClient aClient 1.275 + * The debugger client. 1.276 + * @param object aConsoleActor 1.277 + * The remote protocol grip of the chrome debugger. 1.278 + * @param function aCallback 1.279 + * A function to invoke once the client attached to the console client. 1.280 + */ 1.281 + _startChromeMonitoring: function(aClient, aConsoleActor, aCallback) { 1.282 + if (!aClient) { 1.283 + Cu.reportError("No client found!"); 1.284 + return; 1.285 + } 1.286 + this.client = aClient; 1.287 + 1.288 + aClient.attachConsole(aConsoleActor, LISTENERS, (aResponse, aWebConsoleClient) => { 1.289 + if (!aWebConsoleClient) { 1.290 + Cu.reportError("Couldn't attach to console: " + aResponse.error); 1.291 + return; 1.292 + } 1.293 + this.webConsoleClient = aWebConsoleClient; 1.294 + this.webConsoleClient.setPreferences(NET_PREFS, () => { 1.295 + this.TargetEventsHandler.connect(); 1.296 + this.NetworkEventsHandler.connect(); 1.297 + 1.298 + if (aCallback) { 1.299 + aCallback(); 1.300 + } 1.301 + }); 1.302 + }); 1.303 + }, 1.304 + 1.305 + /** 1.306 + * Gets the activity currently performed by the frontend. 1.307 + * @return number 1.308 + */ 1.309 + getCurrentActivity: function() { 1.310 + return this._currentActivity || ACTIVITY_TYPE.NONE; 1.311 + }, 1.312 + 1.313 + /** 1.314 + * Triggers a specific "activity" to be performed by the frontend. This can be, 1.315 + * for example, triggering reloads or enabling/disabling cache. 1.316 + * 1.317 + * @param number aType 1.318 + * The activity type. See the ACTIVITY_TYPE const. 1.319 + * @return object 1.320 + * A promise resolved once the activity finishes and the frontend 1.321 + * is back into "standby" mode. 1.322 + */ 1.323 + triggerActivity: function(aType) { 1.324 + // Puts the frontend into "standby" (when there's no particular activity). 1.325 + let standBy = () => { 1.326 + this._currentActivity = ACTIVITY_TYPE.NONE; 1.327 + }; 1.328 + 1.329 + // Waits for a series of "navigation start" and "navigation stop" events. 1.330 + let waitForNavigation = () => { 1.331 + let deferred = promise.defer(); 1.332 + this._target.once("will-navigate", () => { 1.333 + this._target.once("navigate", () => { 1.334 + deferred.resolve(); 1.335 + }); 1.336 + }); 1.337 + return deferred.promise; 1.338 + }; 1.339 + 1.340 + // Reconfigures the tab, optionally triggering a reload. 1.341 + let reconfigureTab = aOptions => { 1.342 + let deferred = promise.defer(); 1.343 + this._target.activeTab.reconfigure(aOptions, deferred.resolve); 1.344 + return deferred.promise; 1.345 + }; 1.346 + 1.347 + // Reconfigures the tab and waits for the target to finish navigating. 1.348 + let reconfigureTabAndWaitForNavigation = aOptions => { 1.349 + aOptions.performReload = true; 1.350 + let navigationFinished = waitForNavigation(); 1.351 + return reconfigureTab(aOptions).then(() => navigationFinished); 1.352 + } 1.353 + 1.354 + if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) { 1.355 + this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE; 1.356 + this._target.once("will-navigate", () => this._currentActivity = aType); 1.357 + return reconfigureTabAndWaitForNavigation({ cacheEnabled: true }).then(standBy); 1.358 + } 1.359 + if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) { 1.360 + this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE; 1.361 + this._target.once("will-navigate", () => this._currentActivity = aType); 1.362 + return reconfigureTabAndWaitForNavigation({ cacheEnabled: false }).then(standBy); 1.363 + } 1.364 + if (aType == ACTIVITY_TYPE.ENABLE_CACHE) { 1.365 + this._currentActivity = aType; 1.366 + return reconfigureTab({ cacheEnabled: true, performReload: false }).then(standBy); 1.367 + } 1.368 + if (aType == ACTIVITY_TYPE.DISABLE_CACHE) { 1.369 + this._currentActivity = aType; 1.370 + return reconfigureTab({ cacheEnabled: false, performReload: false }).then(standBy); 1.371 + } 1.372 + this._currentActivity = ACTIVITY_TYPE.NONE; 1.373 + return promise.reject(new Error("Invalid activity type")); 1.374 + }, 1.375 + 1.376 + /** 1.377 + * Getter that tells if the server supports sending custom network requests. 1.378 + * @type boolean 1.379 + */ 1.380 + get supportsCustomRequest() { 1.381 + return this.webConsoleClient && 1.382 + (this.webConsoleClient.traits.customNetworkRequest || 1.383 + !this._target.isApp); 1.384 + }, 1.385 + 1.386 + /** 1.387 + * Getter that tells if the server can do network performance statistics. 1.388 + * @type boolean 1.389 + */ 1.390 + get supportsPerfStats() { 1.391 + return this.tabClient && 1.392 + (this.tabClient.traits.reconfigure || !this._target.isApp); 1.393 + }, 1.394 + 1.395 + _startup: null, 1.396 + _shutdown: null, 1.397 + _connection: null, 1.398 + _currentActivity: null, 1.399 + client: null, 1.400 + tabClient: null, 1.401 + webConsoleClient: null 1.402 +}; 1.403 + 1.404 +/** 1.405 + * Functions handling target-related lifetime events. 1.406 + */ 1.407 +function TargetEventsHandler() { 1.408 + this._onTabNavigated = this._onTabNavigated.bind(this); 1.409 + this._onTabDetached = this._onTabDetached.bind(this); 1.410 +} 1.411 + 1.412 +TargetEventsHandler.prototype = { 1.413 + get target() NetMonitorController._target, 1.414 + get webConsoleClient() NetMonitorController.webConsoleClient, 1.415 + 1.416 + /** 1.417 + * Listen for events emitted by the current tab target. 1.418 + */ 1.419 + connect: function() { 1.420 + dumpn("TargetEventsHandler is connecting..."); 1.421 + this.target.on("close", this._onTabDetached); 1.422 + this.target.on("navigate", this._onTabNavigated); 1.423 + this.target.on("will-navigate", this._onTabNavigated); 1.424 + }, 1.425 + 1.426 + /** 1.427 + * Remove events emitted by the current tab target. 1.428 + */ 1.429 + disconnect: function() { 1.430 + if (!this.target) { 1.431 + return; 1.432 + } 1.433 + dumpn("TargetEventsHandler is disconnecting..."); 1.434 + this.target.off("close", this._onTabDetached); 1.435 + this.target.off("navigate", this._onTabNavigated); 1.436 + this.target.off("will-navigate", this._onTabNavigated); 1.437 + }, 1.438 + 1.439 + /** 1.440 + * Called for each location change in the monitored tab. 1.441 + * 1.442 + * @param string aType 1.443 + * Packet type. 1.444 + * @param object aPacket 1.445 + * Packet received from the server. 1.446 + */ 1.447 + _onTabNavigated: function(aType, aPacket) { 1.448 + switch (aType) { 1.449 + case "will-navigate": { 1.450 + // Reset UI. 1.451 + if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) { 1.452 + NetMonitorView.RequestsMenu.reset(); 1.453 + NetMonitorView.Sidebar.toggle(false); 1.454 + } 1.455 + // Switch to the default network traffic inspector view. 1.456 + if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) { 1.457 + NetMonitorView.showNetworkInspectorView(); 1.458 + } 1.459 + 1.460 + window.emit(EVENTS.TARGET_WILL_NAVIGATE); 1.461 + break; 1.462 + } 1.463 + case "navigate": { 1.464 + window.emit(EVENTS.TARGET_DID_NAVIGATE); 1.465 + break; 1.466 + } 1.467 + } 1.468 + }, 1.469 + 1.470 + /** 1.471 + * Called when the monitored tab is closed. 1.472 + */ 1.473 + _onTabDetached: function() { 1.474 + NetMonitorController.shutdownNetMonitor(); 1.475 + } 1.476 +}; 1.477 + 1.478 +/** 1.479 + * Functions handling target network events. 1.480 + */ 1.481 +function NetworkEventsHandler() { 1.482 + this._onNetworkEvent = this._onNetworkEvent.bind(this); 1.483 + this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this); 1.484 + this._onRequestHeaders = this._onRequestHeaders.bind(this); 1.485 + this._onRequestCookies = this._onRequestCookies.bind(this); 1.486 + this._onRequestPostData = this._onRequestPostData.bind(this); 1.487 + this._onResponseHeaders = this._onResponseHeaders.bind(this); 1.488 + this._onResponseCookies = this._onResponseCookies.bind(this); 1.489 + this._onResponseContent = this._onResponseContent.bind(this); 1.490 + this._onEventTimings = this._onEventTimings.bind(this); 1.491 +} 1.492 + 1.493 +NetworkEventsHandler.prototype = { 1.494 + get client() NetMonitorController._target.client, 1.495 + get webConsoleClient() NetMonitorController.webConsoleClient, 1.496 + 1.497 + /** 1.498 + * Connect to the current target client. 1.499 + */ 1.500 + connect: function() { 1.501 + dumpn("NetworkEventsHandler is connecting..."); 1.502 + this.client.addListener("networkEvent", this._onNetworkEvent); 1.503 + this.client.addListener("networkEventUpdate", this._onNetworkEventUpdate); 1.504 + }, 1.505 + 1.506 + /** 1.507 + * Disconnect from the client. 1.508 + */ 1.509 + disconnect: function() { 1.510 + if (!this.client) { 1.511 + return; 1.512 + } 1.513 + dumpn("NetworkEventsHandler is disconnecting..."); 1.514 + this.client.removeListener("networkEvent", this._onNetworkEvent); 1.515 + this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate); 1.516 + }, 1.517 + 1.518 + /** 1.519 + * The "networkEvent" message type handler. 1.520 + * 1.521 + * @param string aType 1.522 + * Message type. 1.523 + * @param object aPacket 1.524 + * The message received from the server. 1.525 + */ 1.526 + _onNetworkEvent: function(aType, aPacket) { 1.527 + if (aPacket.from != this.webConsoleClient.actor) { 1.528 + // Skip events from different console actors. 1.529 + return; 1.530 + } 1.531 + 1.532 + let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor; 1.533 + NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR); 1.534 + window.emit(EVENTS.NETWORK_EVENT); 1.535 + }, 1.536 + 1.537 + /** 1.538 + * The "networkEventUpdate" message type handler. 1.539 + * 1.540 + * @param string aType 1.541 + * Message type. 1.542 + * @param object aPacket 1.543 + * The message received from the server. 1.544 + */ 1.545 + _onNetworkEventUpdate: function(aType, aPacket) { 1.546 + let actor = aPacket.from; 1.547 + if (!NetMonitorView.RequestsMenu.getItemByValue(actor)) { 1.548 + // Skip events from unknown actors. 1.549 + return; 1.550 + } 1.551 + 1.552 + switch (aPacket.updateType) { 1.553 + case "requestHeaders": 1.554 + this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders); 1.555 + window.emit(EVENTS.UPDATING_REQUEST_HEADERS); 1.556 + break; 1.557 + case "requestCookies": 1.558 + this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies); 1.559 + window.emit(EVENTS.UPDATING_REQUEST_COOKIES); 1.560 + break; 1.561 + case "requestPostData": 1.562 + this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData); 1.563 + window.emit(EVENTS.UPDATING_REQUEST_POST_DATA); 1.564 + break; 1.565 + case "responseHeaders": 1.566 + this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders); 1.567 + window.emit(EVENTS.UPDATING_RESPONSE_HEADERS); 1.568 + break; 1.569 + case "responseCookies": 1.570 + this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies); 1.571 + window.emit(EVENTS.UPDATING_RESPONSE_COOKIES); 1.572 + break; 1.573 + case "responseStart": 1.574 + NetMonitorView.RequestsMenu.updateRequest(aPacket.from, { 1.575 + httpVersion: aPacket.response.httpVersion, 1.576 + status: aPacket.response.status, 1.577 + statusText: aPacket.response.statusText, 1.578 + headersSize: aPacket.response.headersSize 1.579 + }); 1.580 + window.emit(EVENTS.STARTED_RECEIVING_RESPONSE); 1.581 + break; 1.582 + case "responseContent": 1.583 + NetMonitorView.RequestsMenu.updateRequest(aPacket.from, { 1.584 + contentSize: aPacket.contentSize, 1.585 + mimeType: aPacket.mimeType 1.586 + }); 1.587 + this.webConsoleClient.getResponseContent(actor, this._onResponseContent); 1.588 + window.emit(EVENTS.UPDATING_RESPONSE_CONTENT); 1.589 + break; 1.590 + case "eventTimings": 1.591 + NetMonitorView.RequestsMenu.updateRequest(aPacket.from, { 1.592 + totalTime: aPacket.totalTime 1.593 + }); 1.594 + this.webConsoleClient.getEventTimings(actor, this._onEventTimings); 1.595 + window.emit(EVENTS.UPDATING_EVENT_TIMINGS); 1.596 + break; 1.597 + } 1.598 + }, 1.599 + 1.600 + /** 1.601 + * Handles additional information received for a "requestHeaders" packet. 1.602 + * 1.603 + * @param object aResponse 1.604 + * The message received from the server. 1.605 + */ 1.606 + _onRequestHeaders: function(aResponse) { 1.607 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.608 + requestHeaders: aResponse 1.609 + }); 1.610 + window.emit(EVENTS.RECEIVED_REQUEST_HEADERS); 1.611 + }, 1.612 + 1.613 + /** 1.614 + * Handles additional information received for a "requestCookies" packet. 1.615 + * 1.616 + * @param object aResponse 1.617 + * The message received from the server. 1.618 + */ 1.619 + _onRequestCookies: function(aResponse) { 1.620 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.621 + requestCookies: aResponse 1.622 + }); 1.623 + window.emit(EVENTS.RECEIVED_REQUEST_COOKIES); 1.624 + }, 1.625 + 1.626 + /** 1.627 + * Handles additional information received for a "requestPostData" packet. 1.628 + * 1.629 + * @param object aResponse 1.630 + * The message received from the server. 1.631 + */ 1.632 + _onRequestPostData: function(aResponse) { 1.633 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.634 + requestPostData: aResponse 1.635 + }); 1.636 + window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA); 1.637 + }, 1.638 + 1.639 + /** 1.640 + * Handles additional information received for a "responseHeaders" packet. 1.641 + * 1.642 + * @param object aResponse 1.643 + * The message received from the server. 1.644 + */ 1.645 + _onResponseHeaders: function(aResponse) { 1.646 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.647 + responseHeaders: aResponse 1.648 + }); 1.649 + window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS); 1.650 + }, 1.651 + 1.652 + /** 1.653 + * Handles additional information received for a "responseCookies" packet. 1.654 + * 1.655 + * @param object aResponse 1.656 + * The message received from the server. 1.657 + */ 1.658 + _onResponseCookies: function(aResponse) { 1.659 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.660 + responseCookies: aResponse 1.661 + }); 1.662 + window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES); 1.663 + }, 1.664 + 1.665 + /** 1.666 + * Handles additional information received for a "responseContent" packet. 1.667 + * 1.668 + * @param object aResponse 1.669 + * The message received from the server. 1.670 + */ 1.671 + _onResponseContent: function(aResponse) { 1.672 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.673 + responseContent: aResponse 1.674 + }); 1.675 + window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT); 1.676 + }, 1.677 + 1.678 + /** 1.679 + * Handles additional information received for a "eventTimings" packet. 1.680 + * 1.681 + * @param object aResponse 1.682 + * The message received from the server. 1.683 + */ 1.684 + _onEventTimings: function(aResponse) { 1.685 + NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { 1.686 + eventTimings: aResponse 1.687 + }); 1.688 + window.emit(EVENTS.RECEIVED_EVENT_TIMINGS); 1.689 + }, 1.690 + 1.691 + /** 1.692 + * Fetches the full text of a LongString. 1.693 + * 1.694 + * @param object | string aStringGrip 1.695 + * The long string grip containing the corresponding actor. 1.696 + * If you pass in a plain string (by accident or because you're lazy), 1.697 + * then a promise of the same string is simply returned. 1.698 + * @return object Promise 1.699 + * A promise that is resolved when the full string contents 1.700 + * are available, or rejected if something goes wrong. 1.701 + */ 1.702 + getString: function(aStringGrip) { 1.703 + // Make sure this is a long string. 1.704 + if (typeof aStringGrip != "object" || aStringGrip.type != "longString") { 1.705 + return promise.resolve(aStringGrip); // Go home string, you're drunk. 1.706 + } 1.707 + // Fetch the long string only once. 1.708 + if (aStringGrip._fullText) { 1.709 + return aStringGrip._fullText.promise; 1.710 + } 1.711 + 1.712 + let deferred = aStringGrip._fullText = promise.defer(); 1.713 + let { actor, initial, length } = aStringGrip; 1.714 + let longStringClient = this.webConsoleClient.longString(aStringGrip); 1.715 + 1.716 + longStringClient.substring(initial.length, length, aResponse => { 1.717 + if (aResponse.error) { 1.718 + Cu.reportError(aResponse.error + ": " + aResponse.message); 1.719 + deferred.reject(aResponse); 1.720 + return; 1.721 + } 1.722 + deferred.resolve(initial + aResponse.substring); 1.723 + }); 1.724 + 1.725 + return deferred.promise; 1.726 + } 1.727 +}; 1.728 + 1.729 +/** 1.730 + * Localization convenience methods. 1.731 + */ 1.732 +let L10N = new ViewHelpers.L10N(NET_STRINGS_URI); 1.733 + 1.734 +/** 1.735 + * Shortcuts for accessing various network monitor preferences. 1.736 + */ 1.737 +let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", { 1.738 + networkDetailsWidth: ["Int", "panes-network-details-width"], 1.739 + networkDetailsHeight: ["Int", "panes-network-details-height"], 1.740 + statistics: ["Bool", "statistics"], 1.741 + filters: ["Json", "filters"] 1.742 +}); 1.743 + 1.744 +/** 1.745 + * Returns true if this is document is in RTL mode. 1.746 + * @return boolean 1.747 + */ 1.748 +XPCOMUtils.defineLazyGetter(window, "isRTL", function() { 1.749 + return window.getComputedStyle(document.documentElement, null).direction == "rtl"; 1.750 +}); 1.751 + 1.752 +/** 1.753 + * Convenient way of emitting events from the panel window. 1.754 + */ 1.755 +EventEmitter.decorate(this); 1.756 + 1.757 +/** 1.758 + * Preliminary setup for the NetMonitorController object. 1.759 + */ 1.760 +NetMonitorController.TargetEventsHandler = new TargetEventsHandler(); 1.761 +NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler(); 1.762 + 1.763 +/** 1.764 + * Export some properties to the global scope for easier access. 1.765 + */ 1.766 +Object.defineProperties(window, { 1.767 + "gNetwork": { 1.768 + get: function() NetMonitorController.NetworkEventsHandler 1.769 + } 1.770 +}); 1.771 + 1.772 +/** 1.773 + * Makes sure certain properties are available on all objects in a data store. 1.774 + * 1.775 + * @param array aDataStore 1.776 + * A list of objects for which to check the availability of properties. 1.777 + * @param array aMandatoryFields 1.778 + * A list of strings representing properties of objects in aDataStore. 1.779 + * @return object 1.780 + * A promise resolved when all objects in aDataStore contain the 1.781 + * properties defined in aMandatoryFields. 1.782 + */ 1.783 +function whenDataAvailable(aDataStore, aMandatoryFields) { 1.784 + let deferred = promise.defer(); 1.785 + 1.786 + let interval = setInterval(() => { 1.787 + if (aDataStore.every(item => aMandatoryFields.every(field => field in item))) { 1.788 + clearInterval(interval); 1.789 + clearTimeout(timer); 1.790 + deferred.resolve(); 1.791 + } 1.792 + }, WDA_DEFAULT_VERIFY_INTERVAL); 1.793 + 1.794 + let timer = setTimeout(() => { 1.795 + clearInterval(interval); 1.796 + deferred.reject(new Error("Timed out while waiting for data")); 1.797 + }, WDA_DEFAULT_GIVE_UP_TIMEOUT); 1.798 + 1.799 + return deferred.promise; 1.800 +}; 1.801 + 1.802 +const WDA_DEFAULT_VERIFY_INTERVAL = 50; // ms 1.803 +const WDA_DEFAULT_GIVE_UP_TIMEOUT = 2000; // ms 1.804 + 1.805 +/** 1.806 + * Helper method for debugging. 1.807 + * @param string 1.808 + */ 1.809 +function dumpn(str) { 1.810 + if (wantLogging) { 1.811 + dump("NET-FRONTEND: " + str + "\n"); 1.812 + } 1.813 +} 1.814 + 1.815 +let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");