mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
573 lines
17 KiB
JavaScript
573 lines
17 KiB
JavaScript
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
|
|
Cu.import("resource:///modules/source-editor.jsm");
|
|
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
|
|
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
|
|
Cu.import("resource:///modules/devtools/VariablesView.jsm");
|
|
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
|
"resource://gre/modules/PluralForm.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "NetworkHelper",
|
|
"resource://gre/modules/devtools/NetworkHelper.jsm");
|
|
|
|
const NET_STRINGS_URI = "chrome://browser/locale/devtools/netmonitor.properties";
|
|
const LISTENERS = [ "NetworkActivity" ];
|
|
const NET_PREFS = { "NetworkMonitor.saveRequestAndResponseBodies": 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._isInitialized) {
|
|
return this._startup.promise;
|
|
}
|
|
this._isInitialized = true;
|
|
|
|
let deferred = this._startup = Promise.defer();
|
|
|
|
NetMonitorView.initialize(() => {
|
|
NetMonitorView._isInitialized = true;
|
|
deferred.resolve();
|
|
});
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
/**
|
|
* 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._isDestroyed) {
|
|
return this._shutdown.promise;
|
|
}
|
|
this._isDestroyed = true;
|
|
this._startup = null;
|
|
|
|
let deferred = this._shutdown = Promise.defer();
|
|
|
|
NetMonitorView.destroy(() => {
|
|
NetMonitorView._isDestroyed = true;
|
|
this.TargetEventsHandler.disconnect();
|
|
this.NetworkEventsHandler.disconnect();
|
|
|
|
this.disconnect();
|
|
deferred.resolve();
|
|
});
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
/**
|
|
* 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: function() {
|
|
if (this._connection) {
|
|
return this._connection.promise;
|
|
}
|
|
|
|
let deferred = this._connection = Promise.defer();
|
|
|
|
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);
|
|
}
|
|
|
|
return deferred.promise;
|
|
},
|
|
|
|
/**
|
|
* 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;
|
|
},
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
_isInitialized: false,
|
|
_isDestroyed: false,
|
|
_startup: null,
|
|
_shutdown: null,
|
|
_connection: 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.
|
|
NetMonitorView.RequestsMenu.reset();
|
|
NetMonitorView.Sidebar.reset();
|
|
NetMonitorView.NetworkDetails.reset();
|
|
|
|
// Reset global helpers cache.
|
|
nsIURL.store.clear();
|
|
drain.store.clear();
|
|
|
|
window.emit("NetMonitor:TargetWillNavigate");
|
|
break;
|
|
}
|
|
case "navigate": {
|
|
window.emit("NetMonitor:TargetNavigate");
|
|
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) {
|
|
let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor;
|
|
NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR);
|
|
|
|
window.emit("NetMonitor:NetworkEvent");
|
|
},
|
|
|
|
/**
|
|
* 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;
|
|
|
|
switch (aPacket.updateType) {
|
|
case "requestHeaders":
|
|
this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
|
|
window.emit("NetMonitor:NetworkEventUpdating:RequestHeaders");
|
|
break;
|
|
case "requestCookies":
|
|
this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
|
|
window.emit("NetMonitor:NetworkEventUpdating:RequestCookies");
|
|
break;
|
|
case "requestPostData":
|
|
this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
|
|
window.emit("NetMonitor:NetworkEventUpdating:RequestPostData");
|
|
break;
|
|
case "responseHeaders":
|
|
this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
|
|
window.emit("NetMonitor:NetworkEventUpdating:ResponseHeaders");
|
|
break;
|
|
case "responseCookies":
|
|
this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
|
|
window.emit("NetMonitor:NetworkEventUpdating:ResponseCookies");
|
|
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("NetMonitor:NetworkEventUpdating:ResponseStart");
|
|
break;
|
|
case "responseContent":
|
|
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
|
|
contentSize: aPacket.contentSize,
|
|
mimeType: aPacket.mimeType
|
|
});
|
|
this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
|
|
window.emit("NetMonitor:NetworkEventUpdating:ResponseContent");
|
|
break;
|
|
case "eventTimings":
|
|
NetMonitorView.RequestsMenu.updateRequest(aPacket.from, {
|
|
totalTime: aPacket.totalTime
|
|
});
|
|
this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
|
|
window.emit("NetMonitor:NetworkEventUpdating:EventTimings");
|
|
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("NetMonitor:NetworkEventUpdated:RequestHeaders");
|
|
},
|
|
|
|
/**
|
|
* 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("NetMonitor:NetworkEventUpdated:RequestCookies");
|
|
},
|
|
|
|
/**
|
|
* 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("NetMonitor:NetworkEventUpdated:RequestPostData");
|
|
},
|
|
|
|
/**
|
|
* 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("NetMonitor:NetworkEventUpdated:ResponseHeaders");
|
|
},
|
|
|
|
/**
|
|
* 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("NetMonitor:NetworkEventUpdated:ResponseCookies");
|
|
},
|
|
|
|
/**
|
|
* 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("NetMonitor:NetworkEventUpdated:ResponseContent");
|
|
},
|
|
|
|
/**
|
|
* 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("NetMonitor:NetworkEventUpdated:EventTimings");
|
|
},
|
|
|
|
/**
|
|
* 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"]
|
|
});
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Helper method for debugging.
|
|
* @param string
|
|
*/
|
|
function dumpn(str) {
|
|
if (wantLogging) {
|
|
dump("NET-FRONTEND: " + str + "\n");
|
|
}
|
|
}
|
|
|
|
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|