Files
tubestation/browser/devtools/netmonitor/netmonitor-controller.js
Jim Blandy 39dc0c6097 Bug 914753: Make Emacs file variable header lines correct, or at least consistent. DONTBUILD r=ehsan
The -*- file variable lines -*- establish per-file settings that Emacs will
pick up. This patch makes the following changes to those lines (and touches
nothing else):

 - Never set the buffer's mode.

   Years ago, Emacs did not have a good JavaScript mode, so it made sense
   to use Java or C++ mode in .js files. However, Emacs has had js-mode for
   years now; it's perfectly serviceable, and is available and enabled by
   default in all major Emacs packagings.

   Selecting a mode in the -*- file variable line -*- is almost always the
   wrong thing to do anyway. It overrides Emacs's default choice, which is
   (now) reasonable; and even worse, it overrides settings the user might
   have made in their '.emacs' file for that file extension. It's only
   useful when there's something specific about that particular file that
   makes a particular mode appropriate.

 - Correctly propagate settings that establish the correct indentation
   level for this file: c-basic-offset and js2-basic-offset should be
   js-indent-level. Whatever value they're given should be preserved;
   different parts of our tree use different indentation styles.

 - We don't use tabs in Mozilla JS code. Always set indent-tabs-mode: nil.
   Remove tab-width: settings, at least in files that don't contain tab
   characters.

 - Remove js2-mode settings that belong in the user's .emacs file, like
   js2-skip-preprocessor-directives.
2014-06-24 22:12:07 -07:00

824 lines
26 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
const NET_STRINGS_URI = "chrome://browser/locale/devtools/netmonitor.properties";
const LISTENERS = [ "NetworkActivity" ];
const NET_PREFS = { "NetworkMonitor.saveRequestAndResponseBodies": true };
// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
// When the monitored target begins and finishes navigating.
TARGET_WILL_NAVIGATE: "NetMonitor:TargetWillNavigate",
TARGET_DID_NAVIGATE: "NetMonitor:TargetNavigate",
// When a network event is received.
// See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for
// more information about what each packet is supposed to deliver.
NETWORK_EVENT: "NetMonitor:NetworkEvent",
// When request headers begin and finish receiving.
UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",
// When request cookies begin and finish receiving.
UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies",
RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies",
// When request post data begins and finishes receiving.
UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",
RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",
// When response headers begin and finish receiving.
UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",
// When response cookies begin and finish receiving.
UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies",
RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies",
// When event timings begin and finish receiving.
UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",
// When response content begins, updates and finishes receiving.
STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",
UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
// When the request post params are displayed in the UI.
REQUEST_POST_PARAMS_DISPLAYED: "NetMonitor:RequestPostParamsAvailable",
// When the response body is displayed in the UI.
RESPONSE_BODY_DISPLAYED: "NetMonitor:ResponseBodyAvailable",
// When the html response preview is displayed in the UI.
RESPONSE_HTML_PREVIEW_DISPLAYED: "NetMonitor:ResponseHtmlPreviewAvailable",
// When the image response thumbnail is displayed in the UI.
RESPONSE_IMAGE_THUMBNAIL_DISPLAYED: "NetMonitor:ResponseImageThumbnailAvailable",
// When a tab is selected in the NetworkDetailsView and subsequently rendered.
TAB_UPDATED: "NetMonitor:TabUpdated",
// Fired when Sidebar has finished being populated.
SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated",
// Fired when NetworkDetailsView has finished being populated.
NETWORKDETAILSVIEW_POPULATED: "NetMonitor:NetworkDetailsViewPopulated",
// Fired when CustomRequestView has finished being populated.
CUSTOMREQUESTVIEW_POPULATED: "NetMonitor:CustomRequestViewPopulated",
// Fired when charts have been displayed in the PerformanceStatisticsView.
PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
// Fired once the NetMonitorController establishes a connection to the debug
// target.
CONNECTED: "connected",
};
// Descriptions for what this frontend is currently doing.
const ACTIVITY_TYPE = {
// Standing by and handling requests normally.
NONE: 0,
// Forcing the target to reload with cache enabled or disabled.
RELOAD: {
WITH_CACHE_ENABLED: 1,
WITH_CACHE_DISABLED: 2,
WITH_CACHE_DEFAULT: 3
},
// Enabling or disabling the cache without triggering a reload.
ENABLE_CACHE: 3,
DISABLE_CACHE: 4
};
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
Cu.import("resource:///modules/devtools/VariablesView.jsm");
Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
const EventEmitter = require("devtools/toolkit/event-emitter");
const Editor = require("devtools/sourceeditor/editor");
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
XPCOMUtils.defineLazyModuleGetter(this, "Chart",
"resource:///modules/devtools/Chart.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Curl",
"resource:///modules/devtools/Curl.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CurlUtils",
"resource:///modules/devtools/Curl.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils",
"resource://gre/modules/devtools/DevToolsUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
Object.defineProperty(this, "NetworkHelper", {
get: function() {
return require("devtools/toolkit/webconsole/network-helper");
},
configurable: true,
enumerable: true
});
/**
* Object defining the network monitor controller components.
*/
let NetMonitorController = {
/**
* Initializes the view.
*
* @return object
* A promise that is resolved when the monitor finishes startup.
*/
startupNetMonitor: function() {
if (this._startup) {
return this._startup;
}
NetMonitorView.initialize();
// Startup is synchronous, for now.
return this._startup = promise.resolve();
},
/**
* Destroys the view and disconnects the monitor client from the server.
*
* @return object
* A promise that is resolved when the monitor finishes shutdown.
*/
shutdownNetMonitor: function() {
if (this._shutdown) {
return this._shutdown;
}
NetMonitorView.destroy();
this.TargetEventsHandler.disconnect();
this.NetworkEventsHandler.disconnect();
this.disconnect();
// Shutdown is synchronous, for now.
return this._shutdown = promise.resolve();
},
/**
* Initiates remote or chrome network monitoring based on the current target,
* wiring event handlers as necessary.
*
* @return object
* A promise that is resolved when the monitor finishes connecting.
*/
connect: Task.async(function*() {
if (this._connection) {
return this._connection;
}
let deferred = promise.defer();
this._connection = deferred.promise;
let target = this._target;
let { client, form } = target;
if (target.chrome) {
this._startChromeMonitoring(client, form.consoleActor, deferred.resolve);
} else {
this._startMonitoringTab(client, form, deferred.resolve);
}
yield deferred.promise;
window.emit(EVENTS.CONNECTED);
}),
/**
* Disconnects the debugger client and removes event handlers as necessary.
*/
disconnect: function() {
// When debugging local or a remote instance, the connection is closed by
// the RemoteTarget.
this._connection = null;
this.client = null;
this.tabClient = null;
this.webConsoleClient = null;
},
/**
* Checks whether the netmonitor connection is active.
* @return boolean
*/
isConnected: function() {
return !!this.client;
},
/**
* Sets up a monitoring session.
*
* @param DebuggerClient aClient
* The debugger client.
* @param object aTabGrip
* The remote protocol grip of the tab.
* @param function aCallback
* A function to invoke once the client attached to the console client.
*/
_startMonitoringTab: function(aClient, aTabGrip, aCallback) {
if (!aClient) {
Cu.reportError("No client found!");
return;
}
this.client = aClient;
aClient.attachTab(aTabGrip.actor, (aResponse, aTabClient) => {
if (!aTabClient) {
Cu.reportError("No tab client found!");
return;
}
this.tabClient = aTabClient;
aClient.attachConsole(aTabGrip.consoleActor, LISTENERS, (aResponse, aWebConsoleClient) => {
if (!aWebConsoleClient) {
Cu.reportError("Couldn't attach to console: " + aResponse.error);
return;
}
this.webConsoleClient = aWebConsoleClient;
this.webConsoleClient.setPreferences(NET_PREFS, () => {
this.TargetEventsHandler.connect();
this.NetworkEventsHandler.connect();
if (aCallback) {
aCallback();
}
});
});
});
},
/**
* Sets up a chrome monitoring session.
*
* @param DebuggerClient aClient
* The debugger client.
* @param object aConsoleActor
* The remote protocol grip of the chrome debugger.
* @param function aCallback
* A function to invoke once the client attached to the console client.
*/
_startChromeMonitoring: function(aClient, aConsoleActor, aCallback) {
if (!aClient) {
Cu.reportError("No client found!");
return;
}
this.client = aClient;
aClient.attachConsole(aConsoleActor, LISTENERS, (aResponse, aWebConsoleClient) => {
if (!aWebConsoleClient) {
Cu.reportError("Couldn't attach to console: " + aResponse.error);
return;
}
this.webConsoleClient = aWebConsoleClient;
this.webConsoleClient.setPreferences(NET_PREFS, () => {
this.TargetEventsHandler.connect();
this.NetworkEventsHandler.connect();
if (aCallback) {
aCallback();
}
});
});
},
/**
* Gets the activity currently performed by the frontend.
* @return number
*/
getCurrentActivity: function() {
return this._currentActivity || ACTIVITY_TYPE.NONE;
},
/**
* Triggers a specific "activity" to be performed by the frontend. This can be,
* for example, triggering reloads or enabling/disabling cache.
*
* @param number aType
* The activity type. See the ACTIVITY_TYPE const.
* @return object
* A promise resolved once the activity finishes and the frontend
* is back into "standby" mode.
*/
triggerActivity: function(aType) {
// Puts the frontend into "standby" (when there's no particular activity).
let standBy = () => {
this._currentActivity = ACTIVITY_TYPE.NONE;
};
// Waits for a series of "navigation start" and "navigation stop" events.
let waitForNavigation = () => {
let deferred = promise.defer();
this._target.once("will-navigate", () => {
this._target.once("navigate", () => {
deferred.resolve();
});
});
return deferred.promise;
};
// Reconfigures the tab, optionally triggering a reload.
let reconfigureTab = aOptions => {
let deferred = promise.defer();
this._target.activeTab.reconfigure(aOptions, deferred.resolve);
return deferred.promise;
};
// Reconfigures the tab and waits for the target to finish navigating.
let reconfigureTabAndWaitForNavigation = aOptions => {
aOptions.performReload = true;
let navigationFinished = waitForNavigation();
return reconfigureTab(aOptions).then(() => navigationFinished);
}
if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT) {
return reconfigureTabAndWaitForNavigation({}).then(standBy);
}
if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
this._target.once("will-navigate", () => this._currentActivity = aType);
return reconfigureTabAndWaitForNavigation({ cacheEnabled: true }).then(standBy);
}
if (aType == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
this._target.once("will-navigate", () => this._currentActivity = aType);
return reconfigureTabAndWaitForNavigation({ cacheEnabled: false }).then(standBy);
}
if (aType == ACTIVITY_TYPE.ENABLE_CACHE) {
this._currentActivity = aType;
return reconfigureTab({ cacheEnabled: true, performReload: false }).then(standBy);
}
if (aType == ACTIVITY_TYPE.DISABLE_CACHE) {
this._currentActivity = aType;
return reconfigureTab({ cacheEnabled: false, performReload: false }).then(standBy);
}
this._currentActivity = ACTIVITY_TYPE.NONE;
return promise.reject(new Error("Invalid activity type"));
},
/**
* Getter that tells if the server supports sending custom network requests.
* @type boolean
*/
get supportsCustomRequest() {
return this.webConsoleClient &&
(this.webConsoleClient.traits.customNetworkRequest ||
!this._target.isApp);
},
/**
* Getter that tells if the server can do network performance statistics.
* @type boolean
*/
get supportsPerfStats() {
return this.tabClient &&
(this.tabClient.traits.reconfigure || !this._target.isApp);
},
_startup: null,
_shutdown: null,
_connection: null,
_currentActivity: null,
client: null,
tabClient: null,
webConsoleClient: null
};
/**
* Functions handling target-related lifetime events.
*/
function TargetEventsHandler() {
this._onTabNavigated = this._onTabNavigated.bind(this);
this._onTabDetached = this._onTabDetached.bind(this);
}
TargetEventsHandler.prototype = {
get target() NetMonitorController._target,
get webConsoleClient() NetMonitorController.webConsoleClient,
/**
* Listen for events emitted by the current tab target.
*/
connect: function() {
dumpn("TargetEventsHandler is connecting...");
this.target.on("close", this._onTabDetached);
this.target.on("navigate", this._onTabNavigated);
this.target.on("will-navigate", this._onTabNavigated);
},
/**
* Remove events emitted by the current tab target.
*/
disconnect: function() {
if (!this.target) {
return;
}
dumpn("TargetEventsHandler is disconnecting...");
this.target.off("close", this._onTabDetached);
this.target.off("navigate", this._onTabNavigated);
this.target.off("will-navigate", this._onTabNavigated);
},
/**
* Called for each location change in the monitored tab.
*
* @param string aType
* Packet type.
* @param object aPacket
* Packet received from the server.
*/
_onTabNavigated: function(aType, aPacket) {
switch (aType) {
case "will-navigate": {
// Reset UI.
if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
NetMonitorView.RequestsMenu.reset();
NetMonitorView.Sidebar.toggle(false);
}
// Switch to the default network traffic inspector view.
if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
NetMonitorView.showNetworkInspectorView();
}
window.emit(EVENTS.TARGET_WILL_NAVIGATE);
break;
}
case "navigate": {
window.emit(EVENTS.TARGET_DID_NAVIGATE);
break;
}
}
},
/**
* Called when the monitored tab is closed.
*/
_onTabDetached: function() {
NetMonitorController.shutdownNetMonitor();
}
};
/**
* Functions handling target network events.
*/
function NetworkEventsHandler() {
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this._onRequestHeaders = this._onRequestHeaders.bind(this);
this._onRequestCookies = this._onRequestCookies.bind(this);
this._onRequestPostData = this._onRequestPostData.bind(this);
this._onResponseHeaders = this._onResponseHeaders.bind(this);
this._onResponseCookies = this._onResponseCookies.bind(this);
this._onResponseContent = this._onResponseContent.bind(this);
this._onEventTimings = this._onEventTimings.bind(this);
}
NetworkEventsHandler.prototype = {
get client() NetMonitorController._target.client,
get webConsoleClient() NetMonitorController.webConsoleClient,
/**
* Connect to the current target client.
*/
connect: function() {
dumpn("NetworkEventsHandler is connecting...");
this.client.addListener("networkEvent", this._onNetworkEvent);
this.client.addListener("networkEventUpdate", this._onNetworkEventUpdate);
},
/**
* Disconnect from the client.
*/
disconnect: function() {
if (!this.client) {
return;
}
dumpn("NetworkEventsHandler is disconnecting...");
this.client.removeListener("networkEvent", this._onNetworkEvent);
this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
},
/**
* The "networkEvent" message type handler.
*
* @param string aType
* Message type.
* @param object aPacket
* The message received from the server.
*/
_onNetworkEvent: function(aType, aPacket) {
if (aPacket.from != this.webConsoleClient.actor) {
// Skip events from different console actors.
return;
}
let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor;
NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR);
window.emit(EVENTS.NETWORK_EVENT, actor);
},
/**
* The "networkEventUpdate" message type handler.
*
* @param string aType
* Message type.
* @param object aPacket
* The message received from the server.
*/
_onNetworkEventUpdate: function(aType, aPacket) {
let actor = aPacket.from;
if (!NetMonitorView.RequestsMenu.getItemByValue(actor)) {
// Skip events from unknown actors.
return;
}
switch (aPacket.updateType) {
case "requestHeaders":
this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
break;
case "requestCookies":
this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
break;
case "requestPostData":
this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break;
case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
break;
case "responseCookies":
this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
break;
case "responseStart":
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
httpVersion: aPacket.response.httpVersion,
status: aPacket.response.status,
statusText: aPacket.response.statusText,
headersSize: aPacket.response.headersSize
});
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
break;
case "responseContent":
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
contentSize: aPacket.contentSize,
mimeType: aPacket.mimeType
});
this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
break;
case "eventTimings":
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
totalTime: aPacket.totalTime
});
this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
break;
}
},
/**
* Handles additional information received for a "requestHeaders" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onRequestHeaders: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestHeaders: aResponse
});
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from);
},
/**
* Handles additional information received for a "requestCookies" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onRequestCookies: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestCookies: aResponse
});
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from);
},
/**
* Handles additional information received for a "requestPostData" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onRequestPostData: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestPostData: aResponse
});
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from);
},
/**
* Handles additional information received for a "responseHeaders" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onResponseHeaders: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseHeaders: aResponse
});
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from);
},
/**
* Handles additional information received for a "responseCookies" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onResponseCookies: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseCookies: aResponse
});
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from);
},
/**
* Handles additional information received for a "responseContent" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onResponseContent: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseContent: aResponse
});
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from);
},
/**
* Handles additional information received for a "eventTimings" packet.
*
* @param object aResponse
* The message received from the server.
*/
_onEventTimings: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
eventTimings: aResponse
});
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, aResponse.from);
},
/**
* Fetches the full text of a LongString.
*
* @param object | string aStringGrip
* The long string grip containing the corresponding actor.
* If you pass in a plain string (by accident or because you're lazy),
* then a promise of the same string is simply returned.
* @return object Promise
* A promise that is resolved when the full string contents
* are available, or rejected if something goes wrong.
*/
getString: function(aStringGrip) {
// Make sure this is a long string.
if (typeof aStringGrip != "object" || aStringGrip.type != "longString") {
return promise.resolve(aStringGrip); // Go home string, you're drunk.
}
// Fetch the long string only once.
if (aStringGrip._fullText) {
return aStringGrip._fullText.promise;
}
let deferred = aStringGrip._fullText = promise.defer();
let { actor, initial, length } = aStringGrip;
let longStringClient = this.webConsoleClient.longString(aStringGrip);
longStringClient.substring(initial.length, length, aResponse => {
if (aResponse.error) {
Cu.reportError(aResponse.error + ": " + aResponse.message);
deferred.reject(aResponse);
return;
}
deferred.resolve(initial + aResponse.substring);
});
return deferred.promise;
}
};
/**
* Localization convenience methods.
*/
let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
/**
* Shortcuts for accessing various network monitor preferences.
*/
let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
networkDetailsWidth: ["Int", "panes-network-details-width"],
networkDetailsHeight: ["Int", "panes-network-details-height"],
statistics: ["Bool", "statistics"],
filters: ["Json", "filters"]
});
/**
* Returns true if this is document is in RTL mode.
* @return boolean
*/
XPCOMUtils.defineLazyGetter(window, "isRTL", function() {
return window.getComputedStyle(document.documentElement, null).direction == "rtl";
});
/**
* Convenient way of emitting events from the panel window.
*/
EventEmitter.decorate(this);
/**
* Preliminary setup for the NetMonitorController object.
*/
NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
/**
* Export some properties to the global scope for easier access.
*/
Object.defineProperties(window, {
"gNetwork": {
get: function() NetMonitorController.NetworkEventsHandler
}
});
/**
* Makes sure certain properties are available on all objects in a data store.
*
* @param array aDataStore
* A list of objects for which to check the availability of properties.
* @param array aMandatoryFields
* A list of strings representing properties of objects in aDataStore.
* @return object
* A promise resolved when all objects in aDataStore contain the
* properties defined in aMandatoryFields.
*/
function whenDataAvailable(aDataStore, aMandatoryFields) {
let deferred = promise.defer();
let interval = setInterval(() => {
if (aDataStore.every(item => aMandatoryFields.every(field => field in item))) {
clearInterval(interval);
clearTimeout(timer);
deferred.resolve();
}
}, WDA_DEFAULT_VERIFY_INTERVAL);
let timer = setTimeout(() => {
clearInterval(interval);
deferred.reject(new Error("Timed out while waiting for data"));
}, WDA_DEFAULT_GIVE_UP_TIMEOUT);
return deferred.promise;
};
const WDA_DEFAULT_VERIFY_INTERVAL = 50; // ms
const WDA_DEFAULT_GIVE_UP_TIMEOUT = 2000; // ms
/**
* Helper method for debugging.
* @param string
*/
function dumpn(str) {
if (wantLogging) {
dump("NET-FRONTEND: " + str + "\n");
}
}
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");