browser/devtools/netmonitor/netmonitor-controller.js

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

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

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

     1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 "use strict";
     8 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
    10 const NET_STRINGS_URI = "chrome://browser/locale/devtools/netmonitor.properties";
    11 const LISTENERS = [ "NetworkActivity" ];
    12 const NET_PREFS = { "NetworkMonitor.saveRequestAndResponseBodies": true };
    14 // The panel's window global is an EventEmitter firing the following events:
    15 const EVENTS = {
    16   // When the monitored target begins and finishes navigating.
    17   TARGET_WILL_NAVIGATE: "NetMonitor:TargetWillNavigate",
    18   TARGET_DID_NAVIGATE: "NetMonitor:TargetNavigate",
    20   // When a network event is received.
    21   // See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for
    22   // more information about what each packet is supposed to deliver.
    23   NETWORK_EVENT: "NetMonitor:NetworkEvent",
    25   // When request headers begin and finish receiving.
    26   UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
    27   RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",
    29   // When request cookies begin and finish receiving.
    30   UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies",
    31   RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies",
    33   // When request post data begins and finishes receiving.
    34   UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",
    35   RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",
    37   // When response headers begin and finish receiving.
    38   UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
    39   RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",
    41   // When response cookies begin and finish receiving.
    42   UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies",
    43   RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies",
    45   // When event timings begin and finish receiving.
    46   UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
    47   RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
    49   // When response content begins, updates and finishes receiving.
    50   STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
    51   UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
    52   RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
    54   // When the request post params are displayed in the UI.
    55   REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable",
    57   // When the response body is displayed in the UI.
    58   RESPONSE_BODY_DISPLAYED: "NetMonitor:ResponseBodyAvailable",
    60   // When the html response preview is displayed in the UI.
    61   RESPONSE_HTML_PREVIEW_DISPLAYED: "NetMonitor:ResponseHtmlPreviewAvailable",
    63   // When the image response thumbnail is displayed in the UI.
    64   RESPONSE_IMAGE_THUMBNAIL_DISPLAYED: "NetMonitor:ResponseImageThumbnailAvailable",
    66   // When a tab is selected in the NetworkDetailsView and subsequently rendered.
    67   TAB_UPDATED: "NetMonitor:TabUpdated",
    69   // Fired when Sidebar has finished being populated.
    70   SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated",
    72   // Fired when NetworkDetailsView has finished being populated.
    73   NETWORKDETAILSVIEW_POPULATED: "NetMonitor:NetworkDetailsViewPopulated",
    75   // Fired when CustomRequestView has finished being populated.
    76   CUSTOMREQUESTVIEW_POPULATED: "NetMonitor:CustomRequestViewPopulated",
    78   // Fired when charts have been displayed in the PerformanceStatisticsView.
    79   PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
    80   PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
    81   EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
    83   // Fired once the NetMonitorController establishes a connection to the debug
    84   // target.
    85   CONNECTED: "connected",
    86 };
    88 // Descriptions for what this frontend is currently doing.
    89 const ACTIVITY_TYPE = {
    90   // Standing by and handling requests normally.
    91   NONE: 0,
    93   // Forcing the target to reload with cache enabled or disabled.
    94   RELOAD: {
    95     WITH_CACHE_ENABLED: 1,
    96     WITH_CACHE_DISABLED: 2
    97   },
    99   // Enabling or disabling the cache without triggering a reload.
   100   ENABLE_CACHE: 3,
   101   DISABLE_CACHE: 4
   102 };
   104 Cu.import("resource://gre/modules/Services.jsm");
   105 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
   106 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
   107 Cu.import("resource:///modules/devtools/VariablesView.jsm");
   108 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
   109 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
   111 const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
   112 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
   113 const EventEmitter = require("devtools/toolkit/event-emitter");
   114 const Editor = require("devtools/sourceeditor/editor");
   115 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
   117 XPCOMUtils.defineLazyModuleGetter(this, "Chart",
   118   "resource:///modules/devtools/Chart.jsm");
   120 XPCOMUtils.defineLazyModuleGetter(this, "Curl",
   121   "resource:///modules/devtools/Curl.jsm");
   123 XPCOMUtils.defineLazyModuleGetter(this, "CurlUtils",
   124   "resource:///modules/devtools/Curl.jsm");
   126 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   127   "resource://gre/modules/Task.jsm");
   129 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   130   "resource://gre/modules/PluralForm.jsm");
   132 XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils",
   133   "resource://gre/modules/devtools/DevToolsUtils.jsm");
   135 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   136   "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
   138 Object.defineProperty(this, "NetworkHelper", {
   139   get: function() {
   140     return require("devtools/toolkit/webconsole/network-helper");
   141   },
   142   configurable: true,
   143   enumerable: true
   144 });
   146 /**
   147  * Object defining the network monitor controller components.
   148  */
   149 let NetMonitorController = {
   150   /**
   151    * Initializes the view.
   152    *
   153    * @return object
   154    *         A promise that is resolved when the monitor finishes startup.
   155    */
   156   startupNetMonitor: function() {
   157     if (this._startup) {
   158       return this._startup;
   159     }
   161     NetMonitorView.initialize();
   163     // Startup is synchronous, for now.
   164     return this._startup = promise.resolve();
   165   },
   167   /**
   168    * Destroys the view and disconnects the monitor client from the server.
   169    *
   170    * @return object
   171    *         A promise that is resolved when the monitor finishes shutdown.
   172    */
   173   shutdownNetMonitor: function() {
   174     if (this._shutdown) {
   175       return this._shutdown;
   176     }
   178     NetMonitorView.destroy();
   179     this.TargetEventsHandler.disconnect();
   180     this.NetworkEventsHandler.disconnect();
   181     this.disconnect();
   183     // Shutdown is synchronous, for now.
   184     return this._shutdown = promise.resolve();
   185   },
   187   /**
   188    * Initiates remote or chrome network monitoring based on the current target,
   189    * wiring event handlers as necessary.
   190    *
   191    * @return object
   192    *         A promise that is resolved when the monitor finishes connecting.
   193    */
   194   connect: Task.async(function*() {
   195     if (this._connection) {
   196       return this._connection;
   197     }
   199     let deferred = promise.defer();
   200     this._connection = deferred.promise;
   202     let target = this._target;
   203     let { client, form } = target;
   204     if (target.chrome) {
   205       this._startChromeMonitoring(client, form.consoleActor, deferred.resolve);
   206     } else {
   207       this._startMonitoringTab(client, form, deferred.resolve);
   208     }
   210     yield deferred.promise;
   211     window.emit(EVENTS.CONNECTED);
   212   }),
   214   /**
   215    * Disconnects the debugger client and removes event handlers as necessary.
   216    */
   217   disconnect: function() {
   218     // When debugging local or a remote instance, the connection is closed by
   219     // the RemoteTarget.
   220     this._connection = null;
   221     this.client = null;
   222     this.tabClient = null;
   223     this.webConsoleClient = null;
   224   },
   226   /**
   227    * Sets up a monitoring session.
   228    *
   229    * @param DebuggerClient aClient
   230    *        The debugger client.
   231    * @param object aTabGrip
   232    *        The remote protocol grip of the tab.
   233    * @param function aCallback
   234    *        A function to invoke once the client attached to the console client.
   235    */
   236   _startMonitoringTab: function(aClient, aTabGrip, aCallback) {
   237     if (!aClient) {
   238       Cu.reportError("No client found!");
   239       return;
   240     }
   241     this.client = aClient;
   243     aClient.attachTab(aTabGrip.actor, (aResponse, aTabClient) => {
   244       if (!aTabClient) {
   245         Cu.reportError("No tab client found!");
   246         return;
   247       }
   248       this.tabClient = aTabClient;
   250       aClient.attachConsole(aTabGrip.consoleActor, LISTENERS, (aResponse, aWebConsoleClient) => {
   251         if (!aWebConsoleClient) {
   252           Cu.reportError("Couldn't attach to console: " + aResponse.error);
   253           return;
   254         }
   255         this.webConsoleClient = aWebConsoleClient;
   256         this.webConsoleClient.setPreferences(NET_PREFS, () => {
   257           this.TargetEventsHandler.connect();
   258           this.NetworkEventsHandler.connect();
   260           if (aCallback) {
   261             aCallback();
   262           }
   263         });
   264       });
   265     });
   266   },
   268   /**
   269    * Sets up a chrome monitoring session.
   270    *
   271    * @param DebuggerClient aClient
   272    *        The debugger client.
   273    * @param object aConsoleActor
   274    *        The remote protocol grip of the chrome debugger.
   275    * @param function aCallback
   276    *        A function to invoke once the client attached to the console client.
   277    */
   278   _startChromeMonitoring: function(aClient, aConsoleActor, aCallback) {
   279     if (!aClient) {
   280       Cu.reportError("No client found!");
   281       return;
   282     }
   283     this.client = aClient;
   285     aClient.attachConsole(aConsoleActor, LISTENERS, (aResponse, aWebConsoleClient) => {
   286       if (!aWebConsoleClient) {
   287         Cu.reportError("Couldn't attach to console: " + aResponse.error);
   288         return;
   289       }
   290       this.webConsoleClient = aWebConsoleClient;
   291       this.webConsoleClient.setPreferences(NET_PREFS, () => {
   292         this.TargetEventsHandler.connect();
   293         this.NetworkEventsHandler.connect();
   295         if (aCallback) {
   296           aCallback();
   297         }
   298       });
   299     });
   300   },
   302   /**
   303    * Gets the activity currently performed by the frontend.
   304    * @return number
   305    */
   306   getCurrentActivity: function() {
   307     return this._currentActivity || ACTIVITY_TYPE.NONE;
   308   },
   310   /**
   311    * Triggers a specific "activity" to be performed by the frontend. This can be,
   312    * for example, triggering reloads or enabling/disabling cache.
   313    *
   314    * @param number aType
   315    *        The activity type. See the ACTIVITY_TYPE const.
   316    * @return object
   317    *         A promise resolved once the activity finishes and the frontend
   318    *         is back into "standby" mode.
   319    */
   320   triggerActivity: function(aType) {
   321     // Puts the frontend into "standby" (when there's no particular activity).
   322     let standBy = () => {
   323       this._currentActivity = ACTIVITY_TYPE.NONE;
   324     };
   326     // Waits for a series of "navigation start" and "navigation stop" events.
   327     let waitForNavigation = () => {
   328       let deferred = promise.defer();
   329       this._target.once("will-navigate", () => {
   330         this._target.once("navigate", () => {
   331           deferred.resolve();
   332         });
   333       });
   334       return deferred.promise;
   335     };
   337     // Reconfigures the tab, optionally triggering a reload.
   338     let reconfigureTab = aOptions => {
   339       let deferred = promise.defer();
   340       this._target.activeTab.reconfigure(aOptions, deferred.resolve);
   341       return deferred.promise;
   342     };
   344     // Reconfigures the tab and waits for the target to finish navigating.
   345     let reconfigureTabAndWaitForNavigation = aOptions => {
   346       aOptions.performReload = true;
   347       let navigationFinished = waitForNavigation();
   348       return reconfigureTab(aOptions).then(() => navigationFinished);
   349     }
   351     if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
   352       this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
   353       this._target.once("will-navigate", () => this._currentActivity = aType);
   354       return reconfigureTabAndWaitForNavigation({ cacheEnabled: true }).then(standBy);
   355     }
   356     if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
   357       this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
   358       this._target.once("will-navigate", () => this._currentActivity = aType);
   359       return reconfigureTabAndWaitForNavigation({ cacheEnabled: false }).then(standBy);
   360     }
   361     if (aType == ACTIVITY_TYPE.ENABLE_CACHE) {
   362       this._currentActivity = aType;
   363       return reconfigureTab({ cacheEnabled: true, performReload: false }).then(standBy);
   364     }
   365     if (aType == ACTIVITY_TYPE.DISABLE_CACHE) {
   366       this._currentActivity = aType;
   367       return reconfigureTab({ cacheEnabled: false, performReload: false }).then(standBy);
   368     }
   369     this._currentActivity = ACTIVITY_TYPE.NONE;
   370     return promise.reject(new Error("Invalid activity type"));
   371   },
   373   /**
   374    * Getter that tells if the server supports sending custom network requests.
   375    * @type boolean
   376    */
   377   get supportsCustomRequest() {
   378     return this.webConsoleClient &&
   379            (this.webConsoleClient.traits.customNetworkRequest ||
   380             !this._target.isApp);
   381   },
   383   /**
   384    * Getter that tells if the server can do network performance statistics.
   385    * @type boolean
   386    */
   387   get supportsPerfStats() {
   388     return this.tabClient &&
   389            (this.tabClient.traits.reconfigure || !this._target.isApp);
   390   },
   392   _startup: null,
   393   _shutdown: null,
   394   _connection: null,
   395   _currentActivity: null,
   396   client: null,
   397   tabClient: null,
   398   webConsoleClient: null
   399 };
   401 /**
   402  * Functions handling target-related lifetime events.
   403  */
   404 function TargetEventsHandler() {
   405   this._onTabNavigated = this._onTabNavigated.bind(this);
   406   this._onTabDetached = this._onTabDetached.bind(this);
   407 }
   409 TargetEventsHandler.prototype = {
   410   get target() NetMonitorController._target,
   411   get webConsoleClient() NetMonitorController.webConsoleClient,
   413   /**
   414    * Listen for events emitted by the current tab target.
   415    */
   416   connect: function() {
   417     dumpn("TargetEventsHandler is connecting...");
   418     this.target.on("close", this._onTabDetached);
   419     this.target.on("navigate", this._onTabNavigated);
   420     this.target.on("will-navigate", this._onTabNavigated);
   421   },
   423   /**
   424    * Remove events emitted by the current tab target.
   425    */
   426   disconnect: function() {
   427     if (!this.target) {
   428       return;
   429     }
   430     dumpn("TargetEventsHandler is disconnecting...");
   431     this.target.off("close", this._onTabDetached);
   432     this.target.off("navigate", this._onTabNavigated);
   433     this.target.off("will-navigate", this._onTabNavigated);
   434   },
   436   /**
   437    * Called for each location change in the monitored tab.
   438    *
   439    * @param string aType
   440    *        Packet type.
   441    * @param object aPacket
   442    *        Packet received from the server.
   443    */
   444   _onTabNavigated: function(aType, aPacket) {
   445     switch (aType) {
   446       case "will-navigate": {
   447         // Reset UI.
   448         if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
   449           NetMonitorView.RequestsMenu.reset();
   450           NetMonitorView.Sidebar.toggle(false);
   451         }
   452         // Switch to the default network traffic inspector view.
   453         if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
   454           NetMonitorView.showNetworkInspectorView();
   455         }
   457         window.emit(EVENTS.TARGET_WILL_NAVIGATE);
   458         break;
   459       }
   460       case "navigate": {
   461         window.emit(EVENTS.TARGET_DID_NAVIGATE);
   462         break;
   463       }
   464     }
   465   },
   467   /**
   468    * Called when the monitored tab is closed.
   469    */
   470   _onTabDetached: function() {
   471     NetMonitorController.shutdownNetMonitor();
   472   }
   473 };
   475 /**
   476  * Functions handling target network events.
   477  */
   478 function NetworkEventsHandler() {
   479   this._onNetworkEvent = this._onNetworkEvent.bind(this);
   480   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
   481   this._onRequestHeaders = this._onRequestHeaders.bind(this);
   482   this._onRequestCookies = this._onRequestCookies.bind(this);
   483   this._onRequestPostData = this._onRequestPostData.bind(this);
   484   this._onResponseHeaders = this._onResponseHeaders.bind(this);
   485   this._onResponseCookies = this._onResponseCookies.bind(this);
   486   this._onResponseContent = this._onResponseContent.bind(this);
   487   this._onEventTimings = this._onEventTimings.bind(this);
   488 }
   490 NetworkEventsHandler.prototype = {
   491   get client() NetMonitorController._target.client,
   492   get webConsoleClient() NetMonitorController.webConsoleClient,
   494   /**
   495    * Connect to the current target client.
   496    */
   497   connect: function() {
   498     dumpn("NetworkEventsHandler is connecting...");
   499     this.client.addListener("networkEvent", this._onNetworkEvent);
   500     this.client.addListener("networkEventUpdate", this._onNetworkEventUpdate);
   501   },
   503   /**
   504    * Disconnect from the client.
   505    */
   506   disconnect: function() {
   507     if (!this.client) {
   508       return;
   509     }
   510     dumpn("NetworkEventsHandler is disconnecting...");
   511     this.client.removeListener("networkEvent", this._onNetworkEvent);
   512     this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
   513   },
   515   /**
   516    * The "networkEvent" message type handler.
   517    *
   518    * @param string aType
   519    *        Message type.
   520    * @param object aPacket
   521    *        The message received from the server.
   522    */
   523   _onNetworkEvent: function(aType, aPacket) {
   524     if (aPacket.from != this.webConsoleClient.actor) {
   525       // Skip events from different console actors.
   526       return;
   527     }
   529     let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor;
   530     NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR);
   531     window.emit(EVENTS.NETWORK_EVENT);
   532   },
   534   /**
   535    * The "networkEventUpdate" message type handler.
   536    *
   537    * @param string aType
   538    *        Message type.
   539    * @param object aPacket
   540    *        The message received from the server.
   541    */
   542   _onNetworkEventUpdate: function(aType, aPacket) {
   543     let actor = aPacket.from;
   544     if (!NetMonitorView.RequestsMenu.getItemByValue(actor)) {
   545       // Skip events from unknown actors.
   546       return;
   547     }
   549     switch (aPacket.updateType) {
   550       case "requestHeaders":
   551         this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
   552         window.emit(EVENTS.UPDATING_REQUEST_HEADERS);
   553         break;
   554       case "requestCookies":
   555         this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
   556         window.emit(EVENTS.UPDATING_REQUEST_COOKIES);
   557         break;
   558       case "requestPostData":
   559         this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
   560         window.emit(EVENTS.UPDATING_REQUEST_POST_DATA);
   561         break;
   562       case "responseHeaders":
   563         this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
   564         window.emit(EVENTS.UPDATING_RESPONSE_HEADERS);
   565         break;
   566       case "responseCookies":
   567         this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
   568         window.emit(EVENTS.UPDATING_RESPONSE_COOKIES);
   569         break;
   570       case "responseStart":
   571         NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
   572           httpVersion: aPacket.response.httpVersion,
   573           status: aPacket.response.status,
   574           statusText: aPacket.response.statusText,
   575           headersSize: aPacket.response.headersSize
   576         });
   577         window.emit(EVENTS.STARTED_RECEIVING_RESPONSE);
   578         break;
   579       case "responseContent":
   580         NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
   581           contentSize: aPacket.contentSize,
   582           mimeType: aPacket.mimeType
   583         });
   584         this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
   585         window.emit(EVENTS.UPDATING_RESPONSE_CONTENT);
   586         break;
   587       case "eventTimings":
   588         NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
   589           totalTime: aPacket.totalTime
   590         });
   591         this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
   592         window.emit(EVENTS.UPDATING_EVENT_TIMINGS);
   593         break;
   594     }
   595   },
   597   /**
   598    * Handles additional information received for a "requestHeaders" packet.
   599    *
   600    * @param object aResponse
   601    *        The message received from the server.
   602    */
   603   _onRequestHeaders: function(aResponse) {
   604     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   605       requestHeaders: aResponse
   606     });
   607     window.emit(EVENTS.RECEIVED_REQUEST_HEADERS);
   608   },
   610   /**
   611    * Handles additional information received for a "requestCookies" packet.
   612    *
   613    * @param object aResponse
   614    *        The message received from the server.
   615    */
   616   _onRequestCookies: function(aResponse) {
   617     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   618       requestCookies: aResponse
   619     });
   620     window.emit(EVENTS.RECEIVED_REQUEST_COOKIES);
   621   },
   623   /**
   624    * Handles additional information received for a "requestPostData" packet.
   625    *
   626    * @param object aResponse
   627    *        The message received from the server.
   628    */
   629   _onRequestPostData: function(aResponse) {
   630     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   631       requestPostData: aResponse
   632     });
   633     window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA);
   634   },
   636   /**
   637    * Handles additional information received for a "responseHeaders" packet.
   638    *
   639    * @param object aResponse
   640    *        The message received from the server.
   641    */
   642   _onResponseHeaders: function(aResponse) {
   643     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   644       responseHeaders: aResponse
   645     });
   646     window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS);
   647   },
   649   /**
   650    * Handles additional information received for a "responseCookies" packet.
   651    *
   652    * @param object aResponse
   653    *        The message received from the server.
   654    */
   655   _onResponseCookies: function(aResponse) {
   656     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   657       responseCookies: aResponse
   658     });
   659     window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES);
   660   },
   662   /**
   663    * Handles additional information received for a "responseContent" packet.
   664    *
   665    * @param object aResponse
   666    *        The message received from the server.
   667    */
   668   _onResponseContent: function(aResponse) {
   669     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   670       responseContent: aResponse
   671     });
   672     window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT);
   673   },
   675   /**
   676    * Handles additional information received for a "eventTimings" packet.
   677    *
   678    * @param object aResponse
   679    *        The message received from the server.
   680    */
   681   _onEventTimings: function(aResponse) {
   682     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
   683       eventTimings: aResponse
   684     });
   685     window.emit(EVENTS.RECEIVED_EVENT_TIMINGS);
   686   },
   688   /**
   689    * Fetches the full text of a LongString.
   690    *
   691    * @param object | string aStringGrip
   692    *        The long string grip containing the corresponding actor.
   693    *        If you pass in a plain string (by accident or because you're lazy),
   694    *        then a promise of the same string is simply returned.
   695    * @return object Promise
   696    *         A promise that is resolved when the full string contents
   697    *         are available, or rejected if something goes wrong.
   698    */
   699   getString: function(aStringGrip) {
   700     // Make sure this is a long string.
   701     if (typeof aStringGrip != "object" || aStringGrip.type != "longString") {
   702       return promise.resolve(aStringGrip); // Go home string, you're drunk.
   703     }
   704     // Fetch the long string only once.
   705     if (aStringGrip._fullText) {
   706       return aStringGrip._fullText.promise;
   707     }
   709     let deferred = aStringGrip._fullText = promise.defer();
   710     let { actor, initial, length } = aStringGrip;
   711     let longStringClient = this.webConsoleClient.longString(aStringGrip);
   713     longStringClient.substring(initial.length, length, aResponse => {
   714       if (aResponse.error) {
   715         Cu.reportError(aResponse.error + ": " + aResponse.message);
   716         deferred.reject(aResponse);
   717         return;
   718       }
   719       deferred.resolve(initial + aResponse.substring);
   720     });
   722     return deferred.promise;
   723   }
   724 };
   726 /**
   727  * Localization convenience methods.
   728  */
   729 let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
   731 /**
   732  * Shortcuts for accessing various network monitor preferences.
   733  */
   734 let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
   735   networkDetailsWidth: ["Int", "panes-network-details-width"],
   736   networkDetailsHeight: ["Int", "panes-network-details-height"],
   737   statistics: ["Bool", "statistics"],
   738   filters: ["Json", "filters"]
   739 });
   741 /**
   742  * Returns true if this is document is in RTL mode.
   743  * @return boolean
   744  */
   745 XPCOMUtils.defineLazyGetter(window, "isRTL", function() {
   746   return window.getComputedStyle(document.documentElement, null).direction == "rtl";
   747 });
   749 /**
   750  * Convenient way of emitting events from the panel window.
   751  */
   752 EventEmitter.decorate(this);
   754 /**
   755  * Preliminary setup for the NetMonitorController object.
   756  */
   757 NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
   758 NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
   760 /**
   761  * Export some properties to the global scope for easier access.
   762  */
   763 Object.defineProperties(window, {
   764   "gNetwork": {
   765     get: function() NetMonitorController.NetworkEventsHandler
   766   }
   767 });
   769 /**
   770  * Makes sure certain properties are available on all objects in a data store.
   771  *
   772  * @param array aDataStore
   773  *        A list of objects for which to check the availability of properties.
   774  * @param array aMandatoryFields
   775  *        A list of strings representing properties of objects in aDataStore.
   776  * @return object
   777  *         A promise resolved when all objects in aDataStore contain the
   778  *         properties defined in aMandatoryFields.
   779  */
   780 function whenDataAvailable(aDataStore, aMandatoryFields) {
   781   let deferred = promise.defer();
   783   let interval = setInterval(() => {
   784     if (aDataStore.every(item => aMandatoryFields.every(field => field in item))) {
   785       clearInterval(interval);
   786       clearTimeout(timer);
   787       deferred.resolve();
   788     }
   789   }, WDA_DEFAULT_VERIFY_INTERVAL);
   791   let timer = setTimeout(() => {
   792     clearInterval(interval);
   793     deferred.reject(new Error("Timed out while waiting for data"));
   794   }, WDA_DEFAULT_GIVE_UP_TIMEOUT);
   796   return deferred.promise;
   797 };
   799 const WDA_DEFAULT_VERIFY_INTERVAL = 50; // ms
   800 const WDA_DEFAULT_GIVE_UP_TIMEOUT = 2000; // ms
   802 /**
   803  * Helper method for debugging.
   804  * @param string
   805  */
   806 function dumpn(str) {
   807   if (wantLogging) {
   808     dump("NET-FRONTEND: " + str + "\n");
   809   }
   810 }
   812 let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");

mercurial