mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge m-c into fx-team
This commit is contained in:
commit
e19ed01397
@ -1141,6 +1141,7 @@ pref("devtools.netmonitor.enabled", true);
|
||||
// The default Network Monitor UI settings
|
||||
pref("devtools.netmonitor.panes-network-details-width", 450);
|
||||
pref("devtools.netmonitor.panes-network-details-height", 450);
|
||||
pref("devtools.netmonitor.statistics", true);
|
||||
|
||||
// Enable the Tilt inspector
|
||||
pref("devtools.tilt.enabled", true);
|
||||
|
@ -57,35 +57,64 @@ const EVENTS = {
|
||||
// When the response body is displayed in the UI.
|
||||
RESPONSE_BODY_DISPLAYED: "NetMonitor:ResponseBodyAvailable",
|
||||
|
||||
// When `onTabSelect` is fired and subsequently rendered
|
||||
// When `onTabSelect` is fired and subsequently rendered.
|
||||
TAB_UPDATED: "NetMonitor:TabUpdated",
|
||||
|
||||
// Fired when Sidebar is finished being populated
|
||||
// Fired when Sidebar has finished being populated.
|
||||
SIDEBAR_POPULATED: "NetMonitor:SidebarPopulated",
|
||||
|
||||
// Fired when NetworkDetailsView is finished being populated
|
||||
// Fired when NetworkDetailsView has finished being populated.
|
||||
NETWORKDETAILSVIEW_POPULATED: "NetMonitor:NetworkDetailsViewPopulated",
|
||||
|
||||
// Fired when NetworkDetailsView is finished being populated
|
||||
CUSTOMREQUESTVIEW_POPULATED: "NetMonitor:CustomRequestViewPopulated"
|
||||
// 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"
|
||||
};
|
||||
|
||||
// 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
|
||||
},
|
||||
|
||||
// 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://gre/modules/Task.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/VariablesViewController.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.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/shared/event-emitter");
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Chart",
|
||||
"resource:///modules/devtools/Chart.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.defineLazyModuleGetter(this, "devtools",
|
||||
"resource://gre/modules/devtools/Loader.jsm");
|
||||
|
||||
@ -255,9 +284,81 @@ let NetMonitorController = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 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_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"));
|
||||
},
|
||||
|
||||
_startup: null,
|
||||
_shutdown: null,
|
||||
_connection: null,
|
||||
_currentActivity: null,
|
||||
client: null,
|
||||
tabClient: null,
|
||||
webConsoleClient: null
|
||||
@ -314,6 +415,11 @@ TargetEventsHandler.prototype = {
|
||||
NetMonitorView.Sidebar.reset();
|
||||
NetMonitorView.NetworkDetails.reset();
|
||||
|
||||
// Switch to the default network traffic inspector view.
|
||||
if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
|
||||
NetMonitorView.showNetworkInspectorView();
|
||||
}
|
||||
|
||||
window.emit(EVENTS.TARGET_WILL_NAVIGATE);
|
||||
break;
|
||||
}
|
||||
@ -383,7 +489,6 @@ NetworkEventsHandler.prototype = {
|
||||
_onNetworkEvent: function(aType, aPacket) {
|
||||
let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor;
|
||||
NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR);
|
||||
|
||||
window.emit(EVENTS.NETWORK_EVENT);
|
||||
},
|
||||
|
||||
@ -585,7 +690,8 @@ let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
|
||||
*/
|
||||
let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
|
||||
networkDetailsWidth: ["Int", "panes-network-details-width"],
|
||||
networkDetailsHeight: ["Int", "panes-network-details-height"]
|
||||
networkDetailsHeight: ["Int", "panes-network-details-height"],
|
||||
statistics: ["Bool", "statistics"]
|
||||
});
|
||||
|
||||
/**
|
||||
@ -616,6 +722,39 @@ Object.defineProperties(window, {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -21,6 +21,7 @@ const REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB = [128, 136, 144];
|
||||
const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32; // byte
|
||||
const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32; // byte
|
||||
const DEFAULT_HTTP_VERSION = "HTTP/1.1";
|
||||
const REQUEST_TIME_DECIMALS = 2;
|
||||
const HEADERS_SIZE_DECIMALS = 3;
|
||||
const CONTENT_SIZE_DECIMALS = 2;
|
||||
const CONTENT_MIME_TYPE_ABBREVIATIONS = {
|
||||
@ -57,6 +58,7 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = {
|
||||
eval: () => {},
|
||||
switch: () => {}
|
||||
};
|
||||
const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200; // px
|
||||
|
||||
/**
|
||||
* Object defining the network monitor view components.
|
||||
@ -102,6 +104,14 @@ let NetMonitorView = {
|
||||
this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
|
||||
this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
|
||||
this.toggleDetailsPane({ visible: false });
|
||||
|
||||
// Disable the performance statistics mode.
|
||||
if (!Prefs.statistics) {
|
||||
$("#request-menu-context-perf").hidden = true;
|
||||
$("#notice-perf-message").hidden = true;
|
||||
$("#requests-menu-network-summary-button").hidden = true;
|
||||
$("#requests-menu-network-summary-label").hidden = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -121,8 +131,9 @@ let NetMonitorView = {
|
||||
* Gets the visibility state of the network details pane.
|
||||
* @return boolean
|
||||
*/
|
||||
get detailsPaneHidden()
|
||||
this._detailsPane.hasAttribute("pane-collapsed"),
|
||||
get detailsPaneHidden() {
|
||||
return this._detailsPane.hasAttribute("pane-collapsed");
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the network details pane hidden or visible.
|
||||
@ -157,6 +168,66 @@ let NetMonitorView = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current mode for this tool.
|
||||
* @return string (e.g, "network-inspector-view" or "network-statistics-view")
|
||||
*/
|
||||
get currentFrontendMode() {
|
||||
return this._body.selectedPanel.id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles between the frontend view modes ("Inspector" vs. "Statistics").
|
||||
*/
|
||||
toggleFrontendMode: function() {
|
||||
if (this.currentFrontendMode != "network-inspector-view") {
|
||||
this.showNetworkInspectorView();
|
||||
} else {
|
||||
this.showNetworkStatisticsView();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Switches to the "Inspector" frontend view mode.
|
||||
*/
|
||||
showNetworkInspectorView: function() {
|
||||
this._body.selectedPanel = $("#network-inspector-view");
|
||||
this.RequestsMenu._flushWaterfallViews(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switches to the "Statistics" frontend view mode.
|
||||
*/
|
||||
showNetworkStatisticsView: function() {
|
||||
this._body.selectedPanel = $("#network-statistics-view");
|
||||
|
||||
let controller = NetMonitorController;
|
||||
let requestsView = this.RequestsMenu;
|
||||
let statisticsView = this.PerformanceStatistics;
|
||||
|
||||
Task.spawn(function() {
|
||||
statisticsView.displayPlaceholderCharts();
|
||||
yield controller.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
|
||||
|
||||
try {
|
||||
// • The response headers and status code are required for determining
|
||||
// whether a response is "fresh" (cacheable).
|
||||
// • The response content size and request total time are necessary for
|
||||
// populating the statistics view.
|
||||
// • The response mime type is used for categorization.
|
||||
yield whenDataAvailable(requestsView.attachments, [
|
||||
"responseHeaders", "status", "contentSize", "mimeType", "totalTime"
|
||||
]);
|
||||
} catch (ex) {
|
||||
// Timed out while waiting for data. Continue with what we have.
|
||||
DevToolsUtils.reportException("showNetworkStatisticsView", ex);
|
||||
}
|
||||
|
||||
statisticsView.createPrimedCacheChart(requestsView.items);
|
||||
statisticsView.createEmptyCacheChart(requestsView.items);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Lazily initializes and returns a promise for a Editor instance.
|
||||
*
|
||||
@ -263,8 +334,9 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
dumpn("Initializing the RequestsMenuView");
|
||||
|
||||
this.widget = new SideMenuWidget($("#requests-menu-contents"));
|
||||
this._splitter = $('#splitter');
|
||||
this._summary = $("#request-menu-network-summary");
|
||||
this._splitter = $("#network-inspector-view-splitter");
|
||||
this._summary = $("#requests-menu-network-summary-label");
|
||||
this._summary.setAttribute("value", L10N.getStr("networkMenu.empty"));
|
||||
|
||||
this.allowFocusOnRightClick = true;
|
||||
this.widget.maintainSelectionVisible = false;
|
||||
@ -276,11 +348,12 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
|
||||
this.requestsMenuSortEvent = getKeyWithEvent(this.sortBy.bind(this));
|
||||
this.requestsMenuFilterEvent = getKeyWithEvent(this.filterOn.bind(this));
|
||||
this.clearEvent = this.clear.bind(this);
|
||||
this.reqeustsMenuClearEvent = this.clear.bind(this);
|
||||
this._onContextShowing = this._onContextShowing.bind(this);
|
||||
this._onContextNewTabCommand = this.openRequestInTab.bind(this);
|
||||
this._onContextCopyUrlCommand = this.copyUrl.bind(this);
|
||||
this._onContextResendCommand = this.cloneSelectedRequest.bind(this);
|
||||
this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode();
|
||||
|
||||
this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
|
||||
this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
|
||||
@ -288,11 +361,16 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
|
||||
$("#toolbar-labels").addEventListener("click", this.requestsMenuSortEvent, false);
|
||||
$("#requests-menu-footer").addEventListener("click", this.requestsMenuFilterEvent, false);
|
||||
$("#requests-menu-clear-button").addEventListener("click", this.clearEvent, false);
|
||||
$("#requests-menu-clear-button").addEventListener("click", this.reqeustsMenuClearEvent, false);
|
||||
$("#network-request-popup").addEventListener("popupshowing", this._onContextShowing, false);
|
||||
$("#request-menu-context-newtab").addEventListener("command", this._onContextNewTabCommand, false);
|
||||
$("#request-menu-context-copy-url").addEventListener("command", this._onContextCopyUrlCommand, false);
|
||||
$("#request-menu-context-resend").addEventListener("command", this._onContextResendCommand, false);
|
||||
$("#request-menu-context-perf").addEventListener("command", this._onContextPerfCommand, false);
|
||||
|
||||
$("#requests-menu-perf-notice-button").addEventListener("command", this._onContextPerfCommand, false);
|
||||
$("#requests-menu-network-summary-button").addEventListener("command", this._onContextPerfCommand, false);
|
||||
$("#requests-menu-network-summary-label").addEventListener("click", this._onContextPerfCommand, false);
|
||||
|
||||
$("#custom-request-send-button").addEventListener("click", this.sendCustomRequestEvent, false);
|
||||
$("#custom-request-close-button").addEventListener("click", this.closeCustomRequestEvent, false);
|
||||
@ -311,11 +389,16 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
|
||||
$("#toolbar-labels").removeEventListener("click", this.requestsMenuSortEvent, false);
|
||||
$("#requests-menu-footer").removeEventListener("click", this.requestsMenuFilterEvent, false);
|
||||
$("#requests-menu-clear-button").removeEventListener("click", this.clearEvent, false);
|
||||
$("#requests-menu-clear-button").removeEventListener("click", this.reqeustsMenuClearEvent, false);
|
||||
$("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false);
|
||||
$("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false);
|
||||
$("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
|
||||
$("#request-menu-context-resend").removeEventListener("command", this._onContextResendCommand, false);
|
||||
$("#request-menu-context-perf").removeEventListener("command", this._onContextPerfCommand, false);
|
||||
|
||||
$("#requests-menu-perf-notice-button").removeEventListener("command", this._onContextPerfCommand, false);
|
||||
$("#requests-menu-network-summary-button").removeEventListener("command", this._onContextPerfCommand, false);
|
||||
$("#requests-menu-network-summary-label").removeEventListener("click", this._onContextPerfCommand, false);
|
||||
|
||||
$("#custom-request-send-button").removeEventListener("click", this.sendCustomRequestEvent, false);
|
||||
$("#custom-request-close-button").removeEventListener("click", this.closeCustomRequestEvent, false);
|
||||
@ -327,6 +410,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
*/
|
||||
reset: function() {
|
||||
this.empty();
|
||||
this.filterOn("all");
|
||||
this._firstRequestStartedMillis = -1;
|
||||
this._lastRequestEndedMillis = -1;
|
||||
},
|
||||
@ -394,6 +478,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
// Create the element node for the network request item.
|
||||
let menuView = this._createMenuView(selected.method, selected.url);
|
||||
|
||||
// Append a network request item to this container.
|
||||
let newItem = this.push([menuView], {
|
||||
attachment: Object.create(selected, {
|
||||
isCustom: { value: true }
|
||||
@ -457,7 +542,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
*
|
||||
* @param string aType
|
||||
* Either "all", "html", "css", "js", "xhr", "fonts", "images", "media"
|
||||
* or "flash".
|
||||
* "flash" or "other".
|
||||
*/
|
||||
filterOn: function(aType = "all") {
|
||||
let target = $("#requests-menu-filter-" + aType + "-button");
|
||||
@ -477,28 +562,31 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this.filterContents(() => true);
|
||||
break;
|
||||
case "html":
|
||||
this.filterContents(this._onHtml);
|
||||
this.filterContents(e => this.isHtml(e));
|
||||
break;
|
||||
case "css":
|
||||
this.filterContents(this._onCss);
|
||||
this.filterContents(e => this.isCss(e));
|
||||
break;
|
||||
case "js":
|
||||
this.filterContents(this._onJs);
|
||||
this.filterContents(e => this.isJs(e));
|
||||
break;
|
||||
case "xhr":
|
||||
this.filterContents(this._onXhr);
|
||||
this.filterContents(e => this.isXHR(e));
|
||||
break;
|
||||
case "fonts":
|
||||
this.filterContents(this._onFonts);
|
||||
this.filterContents(e => this.isFont(e));
|
||||
break;
|
||||
case "images":
|
||||
this.filterContents(this._onImages);
|
||||
this.filterContents(e => this.isImage(e));
|
||||
break;
|
||||
case "media":
|
||||
this.filterContents(this._onMedia);
|
||||
this.filterContents(e => this.isMedia(e));
|
||||
break;
|
||||
case "flash":
|
||||
this.filterContents(this._onFlash);
|
||||
this.filterContents(e => this.isFlash(e));
|
||||
break;
|
||||
case "other":
|
||||
this.filterContents(e => this.isOther(e));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -611,22 +699,22 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* @return boolean
|
||||
* True if the item should be visible, false otherwise.
|
||||
*/
|
||||
_onHtml: function({ attachment: { mimeType } })
|
||||
isHtml: function({ attachment: { mimeType } })
|
||||
mimeType && mimeType.contains("/html"),
|
||||
|
||||
_onCss: function({ attachment: { mimeType } })
|
||||
isCss: function({ attachment: { mimeType } })
|
||||
mimeType && mimeType.contains("/css"),
|
||||
|
||||
_onJs: function({ attachment: { mimeType } })
|
||||
isJs: function({ attachment: { mimeType } })
|
||||
mimeType && (
|
||||
mimeType.contains("/ecmascript") ||
|
||||
mimeType.contains("/javascript") ||
|
||||
mimeType.contains("/x-javascript")),
|
||||
|
||||
_onXhr: function({ attachment: { isXHR } })
|
||||
isXHR: function({ attachment: { isXHR } })
|
||||
isXHR,
|
||||
|
||||
_onFonts: function({ attachment: { url, mimeType } }) // Fonts are a mess.
|
||||
isFont: function({ attachment: { url, mimeType } }) // Fonts are a mess.
|
||||
(mimeType && (
|
||||
mimeType.contains("font/") ||
|
||||
mimeType.contains("/font"))) ||
|
||||
@ -635,22 +723,26 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
url.contains(".otf") ||
|
||||
url.contains(".woff"),
|
||||
|
||||
_onImages: function({ attachment: { mimeType } })
|
||||
isImage: function({ attachment: { mimeType } })
|
||||
mimeType && mimeType.contains("image/"),
|
||||
|
||||
_onMedia: function({ attachment: { mimeType } }) // Not including images.
|
||||
isMedia: function({ attachment: { mimeType } }) // Not including images.
|
||||
mimeType && (
|
||||
mimeType.contains("audio/") ||
|
||||
mimeType.contains("video/") ||
|
||||
mimeType.contains("model/")),
|
||||
|
||||
_onFlash: function({ attachment: { url, mimeType } }) // Flash is a mess.
|
||||
isFlash: function({ attachment: { url, mimeType } }) // Flash is a mess.
|
||||
(mimeType && (
|
||||
mimeType.contains("/x-flv") ||
|
||||
mimeType.contains("/x-shockwave-flash"))) ||
|
||||
url.contains(".swf") ||
|
||||
url.contains(".flv"),
|
||||
|
||||
isOther: function(e)
|
||||
!this.isHtml(e) && !this.isCss(e) && !this.isJs(e) && !this.isXHR(e) &&
|
||||
!this.isFont(e) && !this.isImage(e) && !this.isMedia(e) && !this.isFlash(e),
|
||||
|
||||
/**
|
||||
* Predicates used when sorting items.
|
||||
*
|
||||
@ -724,8 +816,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
let str = PluralForm.get(visibleRequestsCount, L10N.getStr("networkMenu.summary"));
|
||||
this._summary.setAttribute("value", str
|
||||
.replace("#1", visibleRequestsCount)
|
||||
.replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2))
|
||||
.replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2))
|
||||
.replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, CONTENT_SIZE_DECIMALS))
|
||||
.replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, REQUEST_TIME_DECIMALS))
|
||||
);
|
||||
},
|
||||
|
||||
@ -838,6 +930,12 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
break;
|
||||
case "responseContent":
|
||||
requestItem.attachment.responseContent = value;
|
||||
// If there's no mime type available when the response content
|
||||
// is received, assume text/plain as a fallback.
|
||||
if (!requestItem.attachment.mimeType) {
|
||||
requestItem.attachment.mimeType = "text/plain";
|
||||
this.updateMenuView(requestItem, "mimeType", "text/plain");
|
||||
}
|
||||
break;
|
||||
case "totalTime":
|
||||
requestItem.attachment.totalTime = value;
|
||||
@ -1021,6 +1119,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
startCapNode.hidden = false;
|
||||
endCapNode.hidden = false;
|
||||
|
||||
// Don't paint things while the waterfall view isn't even visible.
|
||||
if (NetMonitorView.currentFrontendMode != "network-inspector-view") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rescale all the waterfalls so that everything is visible at once.
|
||||
this._flushWaterfallViews();
|
||||
},
|
||||
@ -1134,7 +1237,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
if (divisionScale == "millisecond") {
|
||||
normalizedTime |= 0;
|
||||
} else {
|
||||
normalizedTime = L10N.numberWithDecimals(normalizedTime, 2);
|
||||
normalizedTime = L10N.numberWithDecimals(normalizedTime, REQUEST_TIME_DECIMALS);
|
||||
}
|
||||
|
||||
let node = document.createElement("label");
|
||||
@ -1263,6 +1366,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The resize listener for this container's window.
|
||||
*/
|
||||
_onResize: function(e) {
|
||||
// Don't paint things while the waterfall view isn't even visible.
|
||||
if (NetMonitorView.currentFrontendMode != "network-inspector-view") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow requests to settle down first.
|
||||
setNamedTimeout(
|
||||
"resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
|
||||
@ -1453,7 +1561,7 @@ SidebarView.prototype = {
|
||||
|
||||
return view.populate(aData).then(() => {
|
||||
$("#details-pane").selectedIndex = isCustom ? 0 : 1
|
||||
window.emit(EVENTS.SIDEBAR_POPULATED)
|
||||
window.emit(EVENTS.SIDEBAR_POPULATED);
|
||||
});
|
||||
},
|
||||
|
||||
@ -1480,7 +1588,6 @@ CustomRequestView.prototype = {
|
||||
dumpn("Initializing the CustomRequestView");
|
||||
|
||||
this.updateCustomRequestEvent = getKeyWithEvent(this.onUpdate.bind(this));
|
||||
|
||||
$("#custom-pane").addEventListener("input", this.updateCustomRequestEvent, false);
|
||||
},
|
||||
|
||||
@ -1555,18 +1662,12 @@ CustomRequestView.prototype = {
|
||||
break;
|
||||
case 'body':
|
||||
value = $("#custom-postdata-value").value;
|
||||
selectedItem.attachment.requestPostData = {
|
||||
postData: {
|
||||
text: value
|
||||
}
|
||||
};
|
||||
selectedItem.attachment.requestPostData = { postData: { text: value } };
|
||||
break;
|
||||
case 'headers':
|
||||
let headersText = $("#custom-headers-value").value;
|
||||
value = parseHeaderText(headersText);
|
||||
selectedItem.attachment.requestHeaders = {
|
||||
headers: value
|
||||
};
|
||||
selectedItem.attachment.requestHeaders = { headers: value };
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2172,6 +2273,170 @@ NetworkDetailsView.prototype = {
|
||||
_responseCookies: ""
|
||||
};
|
||||
|
||||
/**
|
||||
* Functions handling the performance statistics view.
|
||||
*/
|
||||
function PerformanceStatisticsView() {
|
||||
}
|
||||
|
||||
PerformanceStatisticsView.prototype = {
|
||||
/**
|
||||
* Initializes and displays empty charts in this container.
|
||||
*/
|
||||
displayPlaceholderCharts: function() {
|
||||
this._createChart({
|
||||
id: "#primed-cache-chart",
|
||||
title: "charts.cacheEnabled"
|
||||
});
|
||||
this._createChart({
|
||||
id: "#empty-cache-chart",
|
||||
title: "charts.cacheDisabled"
|
||||
});
|
||||
window.emit(EVENTS.PLACEHOLDER_CHARTS_DISPLAYED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates and displays the primed cache chart in this container.
|
||||
*
|
||||
* @param array aItems
|
||||
* @see this._sanitizeChartDataSource
|
||||
*/
|
||||
createPrimedCacheChart: function(aItems) {
|
||||
this._createChart({
|
||||
id: "#primed-cache-chart",
|
||||
title: "charts.cacheEnabled",
|
||||
data: this._sanitizeChartDataSource(aItems),
|
||||
sorted: true,
|
||||
totals: {
|
||||
size: L10N.getStr("charts.totalSize"),
|
||||
time: L10N.getStr("charts.totalTime"),
|
||||
cached: L10N.getStr("charts.totalCached"),
|
||||
count: L10N.getStr("charts.totalCount")
|
||||
}
|
||||
});
|
||||
window.emit(EVENTS.PRIMED_CACHE_CHART_DISPLAYED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates and displays the empty cache chart in this container.
|
||||
*
|
||||
* @param array aItems
|
||||
* @see this._sanitizeChartDataSource
|
||||
*/
|
||||
createEmptyCacheChart: function(aItems) {
|
||||
this._createChart({
|
||||
id: "#empty-cache-chart",
|
||||
title: "charts.cacheDisabled",
|
||||
data: this._sanitizeChartDataSource(aItems, true),
|
||||
sorted: true,
|
||||
totals: {
|
||||
size: L10N.getStr("charts.totalSize"),
|
||||
time: L10N.getStr("charts.totalTime"),
|
||||
cached: L10N.getStr("charts.totalCached"),
|
||||
count: L10N.getStr("charts.totalCount")
|
||||
}
|
||||
});
|
||||
window.emit(EVENTS.EMPTY_CACHE_CHART_DISPLAYED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a specific chart to this container.
|
||||
*
|
||||
* @param object
|
||||
* An object containing all or some the following properties:
|
||||
* - id: either "#primed-cache-chart" or "#empty-cache-chart"
|
||||
* - title/data/sorted/totals: @see Chart.jsm for details
|
||||
*/
|
||||
_createChart: function({ id, title, data, sorted, totals }) {
|
||||
let container = $(id);
|
||||
|
||||
// Nuke all existing charts of the specified type.
|
||||
while (container.hasChildNodes()) {
|
||||
container.firstChild.remove();
|
||||
}
|
||||
|
||||
// Create a new chart.
|
||||
let chart = Chart.PieTable(document, {
|
||||
diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
|
||||
title: L10N.getStr(title),
|
||||
data: data,
|
||||
sorted: sorted,
|
||||
totals: totals
|
||||
});
|
||||
|
||||
chart.on("click", (_, item) => {
|
||||
NetMonitorView.RequestsMenu.filterOn(item.label);
|
||||
NetMonitorView.showNetworkInspectorView();
|
||||
});
|
||||
|
||||
container.appendChild(chart.node);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sanitizes the data source used for creating charts, to follow the
|
||||
* data format spec defined in Chart.jsm.
|
||||
*
|
||||
* @param array aItems
|
||||
* A collection of request items used as the data source for the chart.
|
||||
* @param boolean aEmptyCache
|
||||
* True if the cache is considered enabled, false for disabled.
|
||||
*/
|
||||
_sanitizeChartDataSource: function(aItems, aEmptyCache) {
|
||||
let data = [
|
||||
"html", "css", "js", "xhr", "fonts", "images", "media", "flash", "other"
|
||||
].map(e => ({
|
||||
cached: 0,
|
||||
count: 0,
|
||||
label: e,
|
||||
size: 0,
|
||||
time: 0
|
||||
}));
|
||||
|
||||
for (let requestItem of aItems) {
|
||||
let details = requestItem.attachment;
|
||||
let type;
|
||||
|
||||
if (RequestsMenuView.prototype.isHtml(requestItem)) {
|
||||
type = 0; // "html"
|
||||
} else if (RequestsMenuView.prototype.isCss(requestItem)) {
|
||||
type = 1; // "css"
|
||||
} else if (RequestsMenuView.prototype.isJs(requestItem)) {
|
||||
type = 2; // "js"
|
||||
} else if (RequestsMenuView.prototype.isFont(requestItem)) {
|
||||
type = 4; // "fonts"
|
||||
} else if (RequestsMenuView.prototype.isImage(requestItem)) {
|
||||
type = 5; // "images"
|
||||
} else if (RequestsMenuView.prototype.isMedia(requestItem)) {
|
||||
type = 6; // "media"
|
||||
} else if (RequestsMenuView.prototype.isFlash(requestItem)) {
|
||||
type = 7; // "flash"
|
||||
} else if (RequestsMenuView.prototype.isXHR(requestItem)) {
|
||||
// Verify XHR last, to categorize other mime types in their own blobs.
|
||||
type = 3; // "xhr"
|
||||
} else {
|
||||
type = 8; // "other"
|
||||
}
|
||||
|
||||
if (aEmptyCache || !responseIsFresh(details)) {
|
||||
data[type].time += details.totalTime || 0;
|
||||
data[type].size += details.contentSize || 0;
|
||||
} else {
|
||||
data[type].cached++;
|
||||
}
|
||||
data[type].count++;
|
||||
}
|
||||
|
||||
for (let chartItem of data) {
|
||||
let size = L10N.numberWithDecimals(chartItem.size / 1024, CONTENT_SIZE_DECIMALS);
|
||||
let time = L10N.numberWithDecimals(chartItem.time / 1000, REQUEST_TIME_DECIMALS);
|
||||
chartItem.size = L10N.getFormatStr("charts.sizeKB", size);
|
||||
chartItem.time = L10N.getFormatStr("charts.totalMS", time);
|
||||
}
|
||||
|
||||
return data.filter(e => e.count > 0);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* DOM query helper.
|
||||
*/
|
||||
@ -2194,8 +2459,8 @@ nsIURL.store = new Map();
|
||||
/**
|
||||
* Parse a url's query string into its components
|
||||
*
|
||||
* @param string aQueryString
|
||||
* The query part of a url
|
||||
* @param string aQueryString
|
||||
* The query part of a url
|
||||
* @return array
|
||||
* Array of query params {name, value}
|
||||
*/
|
||||
@ -2216,8 +2481,8 @@ function parseQueryString(aQueryString) {
|
||||
/**
|
||||
* Parse text representation of HTTP headers.
|
||||
*
|
||||
* @param string aText
|
||||
* Text of headers
|
||||
* @param string aText
|
||||
* Text of headers
|
||||
* @return array
|
||||
* Array of headers info {name, value}
|
||||
*/
|
||||
@ -2228,8 +2493,8 @@ function parseHeaderText(aText) {
|
||||
/**
|
||||
* Parse readable text list of a query string.
|
||||
*
|
||||
* @param string aText
|
||||
* Text of query string represetation
|
||||
* @param string aText
|
||||
* Text of query string represetation
|
||||
* @return array
|
||||
* Array of query params {name, value}
|
||||
*/
|
||||
@ -2241,8 +2506,8 @@ function parseQueryText(aText) {
|
||||
* Parse a text representation of a name:value list with
|
||||
* the given name:value divider character.
|
||||
*
|
||||
* @param string aText
|
||||
* Text of list
|
||||
* @param string aText
|
||||
* Text of list
|
||||
* @return array
|
||||
* Array of headers info {name, value}
|
||||
*/
|
||||
@ -2295,6 +2560,44 @@ function writeQueryString(aParams) {
|
||||
return [(name + "=" + value) for ({name, value} of aParams)].join("&");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the "Expiration Calculations" defined in section 13.2.4 of the
|
||||
* "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
|
||||
*
|
||||
* @param object
|
||||
* An object containing the { responseHeaders, status } properties.
|
||||
* @return boolean
|
||||
* True if the response is fresh and loaded from cache.
|
||||
*/
|
||||
function responseIsFresh({ responseHeaders, status }) {
|
||||
// Check for a "304 Not Modified" status and response headers availability.
|
||||
if (status != 304 || !responseHeaders) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let list = responseHeaders.headers;
|
||||
let cacheControl = list.filter(e => e.name.toLowerCase() == "cache-control")[0];
|
||||
let expires = list.filter(e => e.name.toLowerCase() == "expires")[0];
|
||||
|
||||
// Check the "Cache-Control" header for a maximum age value.
|
||||
if (cacheControl) {
|
||||
let maxAgeMatch =
|
||||
cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) ||
|
||||
cacheControl.value.match(/max-age\s*=\s*(\d+)/);
|
||||
|
||||
if (maxAgeMatch && maxAgeMatch.pop() > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the "Expires" header for a valid date.
|
||||
if (expires && Date.parse(expires.value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get a wrapped function which can be bound to as an event listener directly and is executed only when data-key is present in event.target.
|
||||
*
|
||||
@ -2320,3 +2623,4 @@ NetMonitorView.RequestsMenu = new RequestsMenuView();
|
||||
NetMonitorView.Sidebar = new SidebarView();
|
||||
NetMonitorView.CustomRequest = new CustomRequestView();
|
||||
NetMonitorView.NetworkDetails = new NetworkDetailsView();
|
||||
NetMonitorView.PerformanceStatistics = new PerformanceStatisticsView();
|
||||
|
@ -12,11 +12,11 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#response-content-image-box {
|
||||
#custom-pane {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#custom-pane {
|
||||
#response-content-image-box {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
@ -24,6 +24,10 @@
|
||||
display: none; /* This doesn't work yet. */
|
||||
}
|
||||
|
||||
#network-statistics-charts {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Responsive sidebar */
|
||||
@media (max-width: 700px) {
|
||||
#toolbar-spacer,
|
||||
@ -35,18 +39,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 701px) and (max-width: 1024px) {
|
||||
#body:not([pane-collapsed]) .requests-menu-footer-button,
|
||||
@media (min-width: 701px) and (max-width: 1280px) {
|
||||
#body:not([pane-collapsed]) .requests-menu-filter-button,
|
||||
#body:not([pane-collapsed]) .requests-menu-footer-spacer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 701px) {
|
||||
#requests-menu-spacer-start {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#network-table[waterfall-overflows] .requests-menu-waterfall {
|
||||
display: none;
|
||||
}
|
||||
|
@ -31,12 +31,16 @@
|
||||
<menuitem id="request-menu-context-resend"
|
||||
label="&netmonitorUI.summary.editAndResend;"
|
||||
accesskey="&netmonitorUI.summary.editAndResend.accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="request-menu-context-perf"
|
||||
label="&netmonitorUI.context.perfTools;"
|
||||
accesskey="&netmonitorUI.context.perfTools.accesskey;"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
|
||||
<box id="body"
|
||||
class="devtools-responsive-container theme-sidebar"
|
||||
flex="1">
|
||||
<deck id="body" class="theme-sidebar" flex="1">
|
||||
|
||||
<box id="network-inspector-view" class="devtools-responsive-container">
|
||||
<vbox id="network-table" flex="1">
|
||||
<toolbar id="requests-menu-toolbar"
|
||||
class="devtools-toolbar"
|
||||
@ -118,9 +122,20 @@
|
||||
disabled="true"
|
||||
tabindex="0"/>
|
||||
</toolbar>
|
||||
<label id="requests-menu-empty-notice"
|
||||
class="side-menu-widget-empty-text"
|
||||
value="&netmonitorUI.emptyNotice2;"/>
|
||||
|
||||
<vbox id="requests-menu-empty-notice"
|
||||
class="side-menu-widget-empty-text">
|
||||
<hbox id="notice-perf-message" align="center">
|
||||
<label value="&netmonitorUI.perfNotice1;"/>
|
||||
<button id="requests-menu-perf-notice-button"
|
||||
class="devtools-toolbarbutton"/>
|
||||
<label value="&netmonitorUI.perfNotice2;"/>
|
||||
</hbox>
|
||||
<hbox id="notice-reload-message" align="center">
|
||||
<label value="&netmonitorUI.emptyNotice3;"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<vbox id="requests-menu-contents" flex="1" context="network-request-popup">
|
||||
<hbox id="requests-menu-item-template" hidden="true">
|
||||
<hbox class="requests-menu-subitem requests-menu-status-and-method"
|
||||
@ -151,70 +166,70 @@
|
||||
</hbox>
|
||||
</vbox>
|
||||
<hbox id="requests-menu-footer">
|
||||
<spacer id="requests-menu-spacer-start"
|
||||
class="requests-menu-footer-spacer"
|
||||
flex="100"/>
|
||||
<button id="requests-menu-filter-all-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
checked="true"
|
||||
data-key="all"
|
||||
label="&netmonitorUI.footer.filterAll;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-html-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="html"
|
||||
label="&netmonitorUI.footer.filterHTML;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-css-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="css"
|
||||
label="&netmonitorUI.footer.filterCSS;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-js-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="js"
|
||||
label="&netmonitorUI.footer.filterJS;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-xhr-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="xhr"
|
||||
label="&netmonitorUI.footer.filterXHR;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-fonts-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="fonts"
|
||||
label="&netmonitorUI.footer.filterFonts;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-images-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="images"
|
||||
label="&netmonitorUI.footer.filterImages;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-media-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="media"
|
||||
label="&netmonitorUI.footer.filterMedia;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-flash-button"
|
||||
class="requests-menu-footer-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="flash"
|
||||
label="&netmonitorUI.footer.filterFlash;">
|
||||
</button>
|
||||
<spacer id="requests-menu-spacer-end"
|
||||
<spacer id="requests-menu-spacer"
|
||||
class="requests-menu-footer-spacer"
|
||||
flex="100"/>
|
||||
<label id="request-menu-network-summary"
|
||||
<button id="requests-menu-network-summary-button"
|
||||
class="requests-menu-footer-button"
|
||||
tooltiptext="&netmonitorUI.footer.perf;"/>
|
||||
<label id="requests-menu-network-summary-label"
|
||||
class="plain requests-menu-footer-label"
|
||||
flex="1"
|
||||
crop="end"/>
|
||||
crop="end"
|
||||
tooltiptext="&netmonitorUI.footer.perf;"/>
|
||||
<button id="requests-menu-clear-button"
|
||||
class="requests-menu-footer-button"
|
||||
label="&netmonitorUI.footer.clear;">
|
||||
</button>
|
||||
class="requests-menu-footer-button"
|
||||
label="&netmonitorUI.footer.clear;"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<splitter id="splitter" class="devtools-side-splitter"/>
|
||||
<splitter id="network-inspector-view-splitter"
|
||||
class="devtools-side-splitter"/>
|
||||
|
||||
<deck id="details-pane"
|
||||
hidden="true">
|
||||
@ -460,4 +475,24 @@
|
||||
</deck>
|
||||
</box>
|
||||
|
||||
<box id="network-statistics-view">
|
||||
<toolbar id="network-statistics-toolbar"
|
||||
class="devtools-toolbar">
|
||||
<button id="network-statistics-back-button"
|
||||
class="devtools-toolbarbutton"
|
||||
onclick="NetMonitorView.toggleFrontendMode()"
|
||||
label="&netmonitorUI.backButton;"/>
|
||||
</toolbar>
|
||||
<box id="network-statistics-charts"
|
||||
class="devtools-responsive-container"
|
||||
flex="1">
|
||||
<vbox id="primed-cache-chart" pack="center" flex="1"/>
|
||||
<splitter id="network-statistics-view-splitter"
|
||||
class="devtools-side-splitter"/>
|
||||
<vbox id="empty-cache-chart" pack="center" flex="1"/>
|
||||
</box>
|
||||
</box>
|
||||
|
||||
</deck>
|
||||
|
||||
</window>
|
||||
|
@ -15,6 +15,7 @@ support-files =
|
||||
html_post-raw-test-page.html
|
||||
html_simple-test-page.html
|
||||
html_sorting-test-page.html
|
||||
html_statistics-test-page.html
|
||||
html_status-codes-test-page.html
|
||||
sjs_content-type-test-server.sjs
|
||||
sjs_simple-test-server.sjs
|
||||
@ -26,6 +27,11 @@ support-files =
|
||||
[browser_net_accessibility-01.js]
|
||||
[browser_net_accessibility-02.js]
|
||||
[browser_net_autoscroll.js]
|
||||
[browser_net_charts-01.js]
|
||||
[browser_net_charts-02.js]
|
||||
[browser_net_charts-03.js]
|
||||
[browser_net_charts-04.js]
|
||||
[browser_net_charts-05.js]
|
||||
[browser_net_clear.js]
|
||||
[browser_net_content-type.js]
|
||||
[browser_net_copy_url.js]
|
||||
@ -57,6 +63,8 @@ support-files =
|
||||
[browser_net_sort-01.js]
|
||||
[browser_net_sort-02.js]
|
||||
[browser_net_sort-03.js]
|
||||
[browser_net_statistics-01.js]
|
||||
[browser_net_statistics-02.js]
|
||||
[browser_net_status-codes.js]
|
||||
[browser_net_timeline_ticks.js]
|
||||
[browser_net_timing-division.js]
|
||||
|
70
browser/devtools/netmonitor/test/browser_net_charts-01.js
Normal file
70
browser/devtools/netmonitor/test/browser_net_charts-01.js
Normal file
@ -0,0 +1,70 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Makes sure Pie Charts have the right internal structure.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let pie = Chart.Pie(document, {
|
||||
width: 100,
|
||||
height: 100,
|
||||
data: [{
|
||||
size: 1,
|
||||
label: "foo"
|
||||
}, {
|
||||
size: 2,
|
||||
label: "bar"
|
||||
}, {
|
||||
size: 3,
|
||||
label: "baz"
|
||||
}]
|
||||
});
|
||||
|
||||
let node = pie.node;
|
||||
let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob");
|
||||
let labels = node.querySelectorAll(".pie-chart-label");
|
||||
|
||||
ok(node.classList.contains("pie-chart-container") &&
|
||||
node.classList.contains("generic-chart-container"),
|
||||
"A pie chart container was created successfully.");
|
||||
|
||||
is(slices.length, 3,
|
||||
"There should be 3 pie chart slices created.");
|
||||
ok(slices[0].getAttribute("d").match(/\s*M 50,50 L 49\.\d+,97\.\d+ A 47\.5,47\.5 0 0 1 49\.\d+,2\.5\d* Z/),
|
||||
"The first slice has the correct data.");
|
||||
ok(slices[1].getAttribute("d").match(/\s*M 50,50 L 91\.\d+,26\.\d+ A 47\.5,47\.5 0 0 1 49\.\d+,97\.\d+ Z/),
|
||||
"The second slice has the correct data.");
|
||||
ok(slices[2].getAttribute("d").match(/\s*M 50,50 L 50\.\d+,2\.5\d* A 47\.5,47\.5 0 0 1 91\.\d+,26\.\d+ Z/),
|
||||
"The third slice has the correct data.");
|
||||
|
||||
ok(slices[0].hasAttribute("largest"),
|
||||
"The first slice should be the largest one.");
|
||||
ok(slices[2].hasAttribute("smallest"),
|
||||
"The third slice should be the smallest one.");
|
||||
|
||||
ok(slices[0].getAttribute("name"), "baz",
|
||||
"The first slice's name is correct.");
|
||||
ok(slices[1].getAttribute("name"), "bar",
|
||||
"The first slice's name is correct.");
|
||||
ok(slices[2].getAttribute("name"), "foo",
|
||||
"The first slice's name is correct.");
|
||||
|
||||
is(labels.length, 3,
|
||||
"There should be 3 pie chart labels created.");
|
||||
is(labels[0].textContent, "baz",
|
||||
"The first label's text is correct.");
|
||||
is(labels[1].textContent, "bar",
|
||||
"The first label's text is correct.");
|
||||
is(labels[2].textContent, "foo",
|
||||
"The first label's text is correct.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
}
|
49
browser/devtools/netmonitor/test/browser_net_charts-02.js
Normal file
49
browser/devtools/netmonitor/test/browser_net_charts-02.js
Normal file
@ -0,0 +1,49 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Makes sure Pie Charts have the right internal structure when
|
||||
* initialized with empty data.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, L10N, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let pie = Chart.Pie(document, {
|
||||
data: null,
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
|
||||
let node = pie.node;
|
||||
let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob");
|
||||
let labels = node.querySelectorAll(".pie-chart-label");
|
||||
|
||||
ok(node.classList.contains("pie-chart-container") &&
|
||||
node.classList.contains("generic-chart-container"),
|
||||
"A pie chart container was created successfully.");
|
||||
|
||||
is(slices.length, 1,
|
||||
"There should be 1 pie chart slice created.");
|
||||
ok(slices[0].getAttribute("d").match(/\s*M 50,50 L 50\.\d+,2\.5\d* A 47\.5,47\.5 0 1 1 49\.\d+,2\.5\d* Z/),
|
||||
"The first slice has the correct data.");
|
||||
|
||||
ok(slices[0].hasAttribute("largest"),
|
||||
"The first slice should be the largest one.");
|
||||
ok(slices[0].hasAttribute("smallest"),
|
||||
"The first slice should also be the smallest one.");
|
||||
ok(slices[0].getAttribute("name"), L10N.getStr("pieChart.empty"),
|
||||
"The first slice's name is correct.");
|
||||
|
||||
is(labels.length, 1,
|
||||
"There should be 1 pie chart label created.");
|
||||
is(labels[0].textContent, "Loading",
|
||||
"The first label's text is correct.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
}
|
100
browser/devtools/netmonitor/test/browser_net_charts-03.js
Normal file
100
browser/devtools/netmonitor/test/browser_net_charts-03.js
Normal file
@ -0,0 +1,100 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Makes sure Table Charts have the right internal structure.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let table = Chart.Table(document, {
|
||||
title: "Table title",
|
||||
data: [{
|
||||
label1: 1,
|
||||
label2: "11.1foo"
|
||||
}, {
|
||||
label1: 2,
|
||||
label2: "12.2bar"
|
||||
}, {
|
||||
label1: 3,
|
||||
label2: "13.3baz"
|
||||
}],
|
||||
totals: {
|
||||
label1: "Hello %S",
|
||||
label2: "World %S"
|
||||
}
|
||||
});
|
||||
|
||||
let node = table.node;
|
||||
let title = node.querySelector(".table-chart-title");
|
||||
let grid = node.querySelector(".table-chart-grid");
|
||||
let totals = node.querySelector(".table-chart-totals");
|
||||
let rows = grid.querySelectorAll(".table-chart-row");
|
||||
let sums = node.querySelectorAll(".table-chart-summary-label");
|
||||
|
||||
ok(node.classList.contains("table-chart-container") &&
|
||||
node.classList.contains("generic-chart-container"),
|
||||
"A table chart container was created successfully.");
|
||||
|
||||
ok(title,
|
||||
"A title node was created successfully.");
|
||||
is(title.getAttribute("value"), "Table title",
|
||||
"The title node displays the correct text.");
|
||||
|
||||
is(rows.length, 3,
|
||||
"There should be 3 table chart rows created.");
|
||||
|
||||
ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
|
||||
"A colored blob exists for the firt row.");
|
||||
is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "label1",
|
||||
"The first column of the first row exists.");
|
||||
is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label2",
|
||||
"The second column of the first row exists.");
|
||||
is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "1",
|
||||
"The first column of the first row displays the correct text.");
|
||||
is(rows[0].querySelectorAll("label")[1].getAttribute("value"), "11.1foo",
|
||||
"The second column of the first row displays the correct text.");
|
||||
|
||||
ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
|
||||
"A colored blob exists for the second row.");
|
||||
is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "label1",
|
||||
"The first column of the second row exists.");
|
||||
is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label2",
|
||||
"The second column of the second row exists.");
|
||||
is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "2",
|
||||
"The first column of the second row displays the correct text.");
|
||||
is(rows[1].querySelectorAll("label")[1].getAttribute("value"), "12.2bar",
|
||||
"The second column of the first row displays the correct text.");
|
||||
|
||||
ok(rows[2].querySelector(".table-chart-row-box.chart-colored-blob"),
|
||||
"A colored blob exists for the third row.");
|
||||
is(rows[2].querySelectorAll("label")[0].getAttribute("name"), "label1",
|
||||
"The first column of the third row exists.");
|
||||
is(rows[2].querySelectorAll("label")[1].getAttribute("name"), "label2",
|
||||
"The second column of the third row exists.");
|
||||
is(rows[2].querySelectorAll("label")[0].getAttribute("value"), "3",
|
||||
"The first column of the third row displays the correct text.");
|
||||
is(rows[2].querySelectorAll("label")[1].getAttribute("value"), "13.3baz",
|
||||
"The second column of the third row displays the correct text.");
|
||||
|
||||
is(sums.length, 2,
|
||||
"There should be 2 total summaries created.");
|
||||
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"), "label1",
|
||||
"The first sum's type is correct.");
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"), "Hello 6",
|
||||
"The first sum's value is correct.");
|
||||
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"), "label2",
|
||||
"The second sum's type is correct.");
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"), "World 36.60",
|
||||
"The second sum's value is correct.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
}
|
70
browser/devtools/netmonitor/test/browser_net_charts-04.js
Normal file
70
browser/devtools/netmonitor/test/browser_net_charts-04.js
Normal file
@ -0,0 +1,70 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Makes sure Pie Charts have the right internal structure when
|
||||
* initialized with empty data.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, L10N, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let table = Chart.Table(document, {
|
||||
title: "Table title",
|
||||
data: null,
|
||||
totals: {
|
||||
label1: "Hello %S",
|
||||
label2: "World %S"
|
||||
}
|
||||
});
|
||||
|
||||
let node = table.node;
|
||||
let title = node.querySelector(".table-chart-title");
|
||||
let grid = node.querySelector(".table-chart-grid");
|
||||
let totals = node.querySelector(".table-chart-totals");
|
||||
let rows = grid.querySelectorAll(".table-chart-row");
|
||||
let sums = node.querySelectorAll(".table-chart-summary-label");
|
||||
|
||||
ok(node.classList.contains("table-chart-container") &&
|
||||
node.classList.contains("generic-chart-container"),
|
||||
"A table chart container was created successfully.");
|
||||
|
||||
ok(title,
|
||||
"A title node was created successfully.");
|
||||
is(title.getAttribute("value"), "Table title",
|
||||
"The title node displays the correct text.");
|
||||
|
||||
is(rows.length, 1,
|
||||
"There should be 1 table chart row created.");
|
||||
|
||||
ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
|
||||
"A colored blob exists for the firt row.");
|
||||
is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
|
||||
"The first column of the first row exists.");
|
||||
is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
|
||||
"The second column of the first row exists.");
|
||||
is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
|
||||
"The first column of the first row displays the correct text.");
|
||||
is(rows[0].querySelectorAll("label")[1].getAttribute("value"), L10N.getStr("tableChart.empty"),
|
||||
"The second column of the first row displays the correct text.");
|
||||
|
||||
is(sums.length, 2,
|
||||
"There should be 2 total summaries created.");
|
||||
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("name"), "label1",
|
||||
"The first sum's type is correct.");
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[0].getAttribute("value"), "Hello 0",
|
||||
"The first sum's value is correct.");
|
||||
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("name"), "label2",
|
||||
"The second sum's type is correct.");
|
||||
is(totals.querySelectorAll(".table-chart-summary-label")[1].getAttribute("value"), "World 0",
|
||||
"The second sum's value is correct.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
}
|
60
browser/devtools/netmonitor/test/browser_net_charts-05.js
Normal file
60
browser/devtools/netmonitor/test/browser_net_charts-05.js
Normal file
@ -0,0 +1,60 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Makes sure Pie+Table Charts have the right internal structure.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let chart = Chart.PieTable(document, {
|
||||
title: "Table title",
|
||||
data: [{
|
||||
size: 1,
|
||||
label: "11.1foo"
|
||||
}, {
|
||||
size: 2,
|
||||
label: "12.2bar"
|
||||
}, {
|
||||
size: 3,
|
||||
label: "13.3baz"
|
||||
}],
|
||||
totals: {
|
||||
size: "Hello %S",
|
||||
label: "World %S"
|
||||
}
|
||||
});
|
||||
|
||||
ok(chart.pie, "The pie chart proxy is accessible.");
|
||||
ok(chart.table, "The table chart proxy is accessible.");
|
||||
|
||||
let node = chart.node;
|
||||
let slices = node.querySelectorAll(".pie-chart-slice");
|
||||
let rows = node.querySelectorAll(".table-chart-row");
|
||||
let sums = node.querySelectorAll(".table-chart-summary-label");
|
||||
|
||||
ok(node.classList.contains("pie-table-chart-container"),
|
||||
"A pie+table chart container was created successfully.");
|
||||
|
||||
ok(node.querySelector(".table-chart-title"),
|
||||
"A title node was created successfully.");
|
||||
ok(node.querySelector(".pie-chart-container"),
|
||||
"A pie chart was created successfully.");
|
||||
ok(node.querySelector(".table-chart-container"),
|
||||
"A table chart was created successfully.");
|
||||
|
||||
is(rows.length, 3,
|
||||
"There should be 3 pie chart slices created.");
|
||||
is(rows.length, 3,
|
||||
"There should be 3 table chart rows created.");
|
||||
is(sums.length, 2,
|
||||
"There should be 2 total summaries created.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
}
|
@ -80,7 +80,7 @@ function test() {
|
||||
})
|
||||
|
||||
function testStatus() {
|
||||
let summary = $("#request-menu-network-summary");
|
||||
let summary = $("#requests-menu-network-summary-label");
|
||||
let value = summary.getAttribute("value");
|
||||
info("Current summary: " + value);
|
||||
|
||||
@ -89,13 +89,7 @@ function test() {
|
||||
let totalRequestsCount = RequestsMenu.itemCount;
|
||||
info("Current requests: " + visibleRequestsCount + " of " + totalRequestsCount + ".");
|
||||
|
||||
if (!totalRequestsCount) {
|
||||
is(value, "",
|
||||
"The current summary text is incorrect, expected an empty string.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!visibleRequestsCount) {
|
||||
if (!totalRequestsCount || !visibleRequestsCount) {
|
||||
is(value, L10N.getStr("networkMenu.empty"),
|
||||
"The current summary text is incorrect, expected an 'empty' label.");
|
||||
return;
|
||||
|
@ -0,0 +1,57 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the statistics view is populated correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(STATISTICS_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let panel = aMonitor.panelWin;
|
||||
let { document, $, $all, EVENTS, NetMonitorView } = panel;
|
||||
|
||||
is(NetMonitorView.currentFrontendMode, "network-inspector-view",
|
||||
"The initial frontend mode is correct.");
|
||||
|
||||
is($("#primed-cache-chart").childNodes.length, 0,
|
||||
"There should be no primed cache chart created yet.");
|
||||
is($("#empty-cache-chart").childNodes.length, 0,
|
||||
"There should be no empty cache chart created yet.");
|
||||
|
||||
waitFor(panel, EVENTS.PLACEHOLDER_CHARTS_DISPLAYED).then(() => {
|
||||
is($("#primed-cache-chart").childNodes.length, 1,
|
||||
"There should be a placeholder primed cache chart created now.");
|
||||
is($("#empty-cache-chart").childNodes.length, 1,
|
||||
"There should be a placeholder empty cache chart created now.");
|
||||
|
||||
is($all(".pie-chart-container[placeholder=true]").length, 2,
|
||||
"Two placeholder pie chart appear to be rendered correctly.");
|
||||
is($all(".table-chart-container[placeholder=true]").length, 2,
|
||||
"Two placeholder table chart appear to be rendered correctly.");
|
||||
|
||||
promise.all([
|
||||
waitFor(panel, EVENTS.PRIMED_CACHE_CHART_DISPLAYED),
|
||||
waitFor(panel, EVENTS.EMPTY_CACHE_CHART_DISPLAYED)
|
||||
]).then(() => {
|
||||
is($("#primed-cache-chart").childNodes.length, 1,
|
||||
"There should be a real primed cache chart created now.");
|
||||
is($("#empty-cache-chart").childNodes.length, 1,
|
||||
"There should be a real empty cache chart created now.");
|
||||
|
||||
is($all(".pie-chart-container:not([placeholder=true])").length, 2,
|
||||
"Two real pie chart appear to be rendered correctly.");
|
||||
is($all(".table-chart-container:not([placeholder=true])").length, 2,
|
||||
"Two real table chart appear to be rendered correctly.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
});
|
||||
|
||||
NetMonitorView.toggleFrontendMode();
|
||||
|
||||
is(NetMonitorView.currentFrontendMode, "network-statistics-view",
|
||||
"The current frontend mode is correct.");
|
||||
});
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if the network inspector view is shown when the target navigates
|
||||
* away while in the statistics view.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(STATISTICS_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let panel = aMonitor.panelWin;
|
||||
let { document, EVENTS, NetMonitorView } = panel;
|
||||
|
||||
is(NetMonitorView.currentFrontendMode, "network-inspector-view",
|
||||
"The initial frontend mode is correct.");
|
||||
|
||||
promise.all([
|
||||
waitFor(panel, EVENTS.PRIMED_CACHE_CHART_DISPLAYED),
|
||||
waitFor(panel, EVENTS.EMPTY_CACHE_CHART_DISPLAYED)
|
||||
]).then(() => {
|
||||
is(NetMonitorView.currentFrontendMode, "network-statistics-view",
|
||||
"The frontend mode is currently in the statistics view.");
|
||||
|
||||
waitFor(panel, EVENTS.TARGET_WILL_NAVIGATE).then(() => {
|
||||
is(NetMonitorView.currentFrontendMode, "network-inspector-view",
|
||||
"The frontend mode switched back to the inspector view.");
|
||||
|
||||
waitFor(panel, EVENTS.TARGET_DID_NAVIGATE).then(() => {
|
||||
is(NetMonitorView.currentFrontendMode, "network-inspector-view",
|
||||
"The frontend mode is still in the inspector view.");
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
});
|
||||
});
|
||||
|
||||
aDebuggee.location.reload();
|
||||
});
|
||||
|
||||
NetMonitorView.toggleFrontendMode();
|
||||
});
|
||||
}
|
@ -29,6 +29,7 @@ const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
|
||||
const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
|
||||
const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
|
||||
const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html";
|
||||
const STATISTICS_URL = EXAMPLE_URL + "html_statistics-test-page.html";
|
||||
|
||||
const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
|
||||
const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
|
||||
|
@ -0,0 +1,37 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Network Monitor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Statistics test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
function get(aAddress) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", aAddress, true);
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=txt");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=xml");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=html");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=css");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=js");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=json");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=jsonp");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=font");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=image");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=audio");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=video");
|
||||
get("sjs_content-type-test-server.sjs?sts=304&fmt=flash");
|
||||
get("test-image.png");
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -7,118 +7,151 @@ function handleRequest(request, response) {
|
||||
response.processAsync();
|
||||
|
||||
let params = request.queryString.split("&");
|
||||
let format = params.filter((s) => s.contains("fmt="))[0].split("=")[1];
|
||||
let format = (params.filter((s) => s.contains("fmt="))[0] || "").split("=")[1];
|
||||
let status = (params.filter((s) => s.contains("sts="))[0] || "").split("=")[1] || 200;
|
||||
|
||||
let cachedCount = 0;
|
||||
let cacheExpire = 60; // seconds
|
||||
|
||||
function maybeMakeCached() {
|
||||
if (status != 304) {
|
||||
return;
|
||||
}
|
||||
// Spice things up a little!
|
||||
if (cachedCount % 2) {
|
||||
response.setHeader("Cache-Control", "max-age=" + cacheExpire, false);
|
||||
} else {
|
||||
response.setHeader("Expires", Date(Date.now() + cacheExpire * 1000), false);
|
||||
}
|
||||
cachedCount++;
|
||||
}
|
||||
|
||||
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
|
||||
switch (format) {
|
||||
case "txt": {
|
||||
response.setStatusLine(request.httpVersion, 200, "DA DA DA");
|
||||
response.setStatusLine(request.httpVersion, status, "DA DA DA");
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
maybeMakeCached();
|
||||
response.write("Братан, ты вообще качаешься?");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "xml": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/xml; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("<label value='greeting'>Hello XML!</label>");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "html": {
|
||||
let content = params.filter((s) => s.contains("res="))[0].split("=")[1];
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write(content || "<p>Hello HTML!</p>");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "html-long": {
|
||||
let str = new Array(102400 /* 100 KB in bytes */).join(".");
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("<p>" + str + "</p>");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "css": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/css; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("body:pre { content: 'Hello CSS!' }");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "js": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "application/javascript; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("function() { return 'Hello JS!'; }");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "application/json; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("{ \"greeting\": \"Hello JSON!\" }");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "jsonp": {
|
||||
let fun = params.filter((s) => s.contains("jsonp="))[0].split("=")[1];
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/json; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write(fun + "({ \"greeting\": \"Hello JSONP!\" })");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json-long": {
|
||||
let str = "{ \"greeting\": \"Hello long string JSON!\" },";
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/json; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("[" + new Array(2048).join(str).slice(0, -1) + "]");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json-malformed": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/json; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("{ \"greeting\": \"Hello malformed JSON!\" },");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json-custom-mime": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/x-bigcorp-json; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("{ \"greeting\": \"Hello oddly-named JSON!\" }");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "font": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "font/woff", false);
|
||||
maybeMakeCached();
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "image": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
maybeMakeCached();
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "audio": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "audio/ogg", false);
|
||||
maybeMakeCached();
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "video": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "video/webm", false);
|
||||
maybeMakeCached();
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "flash": {
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "application/x-shockwave-flash", false);
|
||||
maybeMakeCached();
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
|
422
browser/devtools/shared/widgets/Chart.jsm
Normal file
422
browser/devtools/shared/widgets/Chart.jsm
Normal file
@ -0,0 +1,422 @@
|
||||
/* -*- 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 Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const NET_STRINGS_URI = "chrome://browser/locale/devtools/netmonitor.properties";
|
||||
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||
const PI = Math.PI;
|
||||
const TAU = PI * 2;
|
||||
const EPSILON = 0.0000001;
|
||||
const NAMED_SLICE_MIN_ANGLE = TAU / 8;
|
||||
const NAMED_SLICE_TEXT_DISTANCE_RATIO = 1.9;
|
||||
const HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO = 20;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Chart"];
|
||||
|
||||
/**
|
||||
* Localization convenience methods.
|
||||
*/
|
||||
let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
|
||||
|
||||
/**
|
||||
* A factory for creating charts.
|
||||
* Example usage: let myChart = Chart.Pie(document, { ... });
|
||||
*/
|
||||
let Chart = {
|
||||
Pie: createPieChart,
|
||||
Table: createTableChart,
|
||||
PieTable: createPieTableChart
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple pie chart proxy for the underlying view.
|
||||
* Each item in the `slices` property represents a [data, node] pair containing
|
||||
* the data used to create the slice and the nsIDOMNode displaying it.
|
||||
*
|
||||
* @param nsIDOMNode node
|
||||
* The node representing the view for this chart.
|
||||
*/
|
||||
function PieChart(node) {
|
||||
this.node = node;
|
||||
this.slices = new WeakMap();
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple table chart proxy for the underlying view.
|
||||
* Each item in the `rows` property represents a [data, node] pair containing
|
||||
* the data used to create the row and the nsIDOMNode displaying it.
|
||||
*
|
||||
* @param nsIDOMNode node
|
||||
* The node representing the view for this chart.
|
||||
*/
|
||||
function TableChart(node) {
|
||||
this.node = node;
|
||||
this.rows = new WeakMap();
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple pie+table chart proxy for the underlying view.
|
||||
*
|
||||
* @param nsIDOMNode node
|
||||
* The node representing the view for this chart.
|
||||
* @param PieChart pie
|
||||
* The pie chart proxy.
|
||||
* @param TableChart table
|
||||
* The table chart proxy.
|
||||
*/
|
||||
function PieTableChart(node, pie, table) {
|
||||
this.node = node;
|
||||
this.pie = pie;
|
||||
this.table = table;
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the DOM for a pie+table chart.
|
||||
*
|
||||
* @param nsIDocument document
|
||||
* The document responsible with creating the DOM.
|
||||
* @param object
|
||||
* An object containing all or some of the following properties:
|
||||
* - title: a string displayed as the table chart's (description)/local
|
||||
* - diameter: the diameter of the pie chart, in pixels
|
||||
* - data: an array of items used to display each slice in the pie
|
||||
* and each row in the table;
|
||||
* @see `createPieChart` and `createTableChart` for details.
|
||||
* - sorted: a flag specifying if the `data` should be sorted
|
||||
* ascending by `size`.
|
||||
* - totals: @see `createTableChart` for details.
|
||||
* @return PieTableChart
|
||||
* A pie+table chart proxy instance, which emits the following events:
|
||||
* - "mouseenter", when the mouse enters a slice or a row
|
||||
* - "mouseleave", when the mouse leaves a slice or a row
|
||||
* - "click", when the mouse enters a slice or a row
|
||||
*/
|
||||
function createPieTableChart(document, { sorted, title, diameter, data, totals }) {
|
||||
if (sorted) {
|
||||
data = data.slice().sort((a, b) => +(parseFloat(a.size) < parseFloat(b.size)));
|
||||
}
|
||||
|
||||
let pie = Chart.Pie(document, {
|
||||
width: diameter,
|
||||
data: data
|
||||
});
|
||||
|
||||
let table = Chart.Table(document, {
|
||||
title: title,
|
||||
data: data,
|
||||
totals: totals
|
||||
});
|
||||
|
||||
let container = document.createElement("hbox");
|
||||
container.className = "pie-table-chart-container";
|
||||
container.appendChild(pie.node);
|
||||
container.appendChild(table.node);
|
||||
|
||||
let proxy = new PieTableChart(container, pie, table);
|
||||
|
||||
pie.on("click", (event, item) => {
|
||||
proxy.emit(event, item)
|
||||
});
|
||||
|
||||
table.on("click", (event, item) => {
|
||||
proxy.emit(event, item)
|
||||
});
|
||||
|
||||
pie.on("mouseenter", (event, item) => {
|
||||
proxy.emit(event, item);
|
||||
if (table.rows.has(item)) {
|
||||
table.rows.get(item).setAttribute("focused", "");
|
||||
}
|
||||
});
|
||||
|
||||
pie.on("mouseleave", (event, item) => {
|
||||
proxy.emit(event, item);
|
||||
if (table.rows.has(item)) {
|
||||
table.rows.get(item).removeAttribute("focused");
|
||||
}
|
||||
});
|
||||
|
||||
table.on("mouseenter", (event, item) => {
|
||||
proxy.emit(event, item);
|
||||
if (pie.slices.has(item)) {
|
||||
pie.slices.get(item).setAttribute("focused", "");
|
||||
}
|
||||
});
|
||||
|
||||
table.on("mouseleave", (event, item) => {
|
||||
proxy.emit(event, item);
|
||||
if (pie.slices.has(item)) {
|
||||
pie.slices.get(item).removeAttribute("focused");
|
||||
}
|
||||
});
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the DOM for a pie chart based on the specified properties.
|
||||
*
|
||||
* @param nsIDocument document
|
||||
* The document responsible with creating the DOM.
|
||||
* @param object
|
||||
* An object containing all or some of the following properties:
|
||||
* - data: an array of items used to display each slice; all the items
|
||||
* should be objects containing a `size` and a `label` property.
|
||||
* e.g: [{
|
||||
* size: 1,
|
||||
* label: "foo"
|
||||
* }, {
|
||||
* size: 2,
|
||||
* label: "bar"
|
||||
* }];
|
||||
* - width: the width of the chart, in pixels
|
||||
* - height: optional, the height of the chart, in pixels.
|
||||
* - centerX: optional, the X-axis center of the chart, in pixels.
|
||||
* - centerY: optional, the Y-axis center of the chart, in pixels.
|
||||
* - radius: optional, the radius of the chart, in pixels.
|
||||
* @return PieChart
|
||||
* A pie chart proxy instance, which emits the following events:
|
||||
* - "mouseenter", when the mouse enters a slice
|
||||
* - "mouseleave", when the mouse leaves a slice
|
||||
* - "click", when the mouse clicks a slice
|
||||
*/
|
||||
function createPieChart(document, { data, width, height, centerX, centerY, radius }) {
|
||||
height = height || width;
|
||||
centerX = centerX || width / 2;
|
||||
centerY = centerY || height / 2;
|
||||
radius = radius || (width + height) / 4;
|
||||
let isPlaceholder = false;
|
||||
|
||||
// Filter out very small sizes, as they'll just render invisible slices.
|
||||
data = data ? data.filter(e => parseFloat(e.size) > EPSILON) : null;
|
||||
|
||||
// If there's no data available, display an empty placeholder.
|
||||
if (!data || !data.length) {
|
||||
data = emptyPieChartData;
|
||||
isPlaceholder = true;
|
||||
}
|
||||
|
||||
let container = document.createElementNS(SVG_NS, "svg");
|
||||
container.setAttribute("class", "generic-chart-container pie-chart-container");
|
||||
container.setAttribute("pack", "center");
|
||||
container.setAttribute("flex", "1");
|
||||
container.setAttribute("width", width);
|
||||
container.setAttribute("height", height);
|
||||
container.setAttribute("viewBox", "0 0 " + width + " " + height);
|
||||
container.setAttribute("slices", data.length);
|
||||
container.setAttribute("placeholder", isPlaceholder);
|
||||
|
||||
let proxy = new PieChart(container);
|
||||
|
||||
let total = data.reduce((acc, e) => acc + parseFloat(e.size), 0);
|
||||
let angles = data.map(e => parseFloat(e.size) / total * (TAU - EPSILON));
|
||||
let largest = data.reduce((a, b) => parseFloat(a.size) > parseFloat(b.size) ? a : b);
|
||||
let smallest = data.reduce((a, b) => parseFloat(a.size) < parseFloat(b.size) ? a : b);
|
||||
|
||||
let textDistance = radius / NAMED_SLICE_TEXT_DISTANCE_RATIO;
|
||||
let translateDistance = radius / HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO;
|
||||
let startAngle = TAU;
|
||||
let endAngle = 0;
|
||||
let midAngle = 0;
|
||||
radius -= translateDistance;
|
||||
|
||||
for (let i = data.length - 1; i >= 0; i--) {
|
||||
let sliceInfo = data[i];
|
||||
let sliceAngle = angles[i];
|
||||
if (!sliceInfo.size || sliceAngle < EPSILON) {
|
||||
continue;
|
||||
}
|
||||
|
||||
endAngle = startAngle - sliceAngle;
|
||||
midAngle = (startAngle + endAngle) / 2;
|
||||
|
||||
let x1 = centerX + radius * Math.sin(startAngle);
|
||||
let y1 = centerY - radius * Math.cos(startAngle);
|
||||
let x2 = centerX + radius * Math.sin(endAngle);
|
||||
let y2 = centerY - radius * Math.cos(endAngle);
|
||||
let largeArcFlag = Math.abs(startAngle - endAngle) > PI ? 1 : 0;
|
||||
|
||||
let pathNode = document.createElementNS(SVG_NS, "path");
|
||||
pathNode.setAttribute("class", "pie-chart-slice chart-colored-blob");
|
||||
pathNode.setAttribute("name", sliceInfo.label);
|
||||
pathNode.setAttribute("d",
|
||||
" M " + centerX + "," + centerY +
|
||||
" L " + x2 + "," + y2 +
|
||||
" A " + radius + "," + radius +
|
||||
" 0 " + largeArcFlag +
|
||||
" 1 " + x1 + "," + y1 +
|
||||
" Z");
|
||||
|
||||
if (sliceInfo == largest) {
|
||||
pathNode.setAttribute("largest", "");
|
||||
}
|
||||
if (sliceInfo == smallest) {
|
||||
pathNode.setAttribute("smallest", "");
|
||||
}
|
||||
|
||||
let hoverX = translateDistance * Math.sin(midAngle);
|
||||
let hoverY = -translateDistance * Math.cos(midAngle);
|
||||
let hoverTransform = "transform: translate(" + hoverX + "px, " + hoverY + "px)";
|
||||
pathNode.setAttribute("style", hoverTransform);
|
||||
|
||||
proxy.slices.set(sliceInfo, pathNode);
|
||||
delegate(proxy, ["click", "mouseenter", "mouseleave"], pathNode, sliceInfo);
|
||||
container.appendChild(pathNode);
|
||||
|
||||
if (sliceInfo.label && sliceAngle > NAMED_SLICE_MIN_ANGLE) {
|
||||
let textX = centerX + textDistance * Math.sin(midAngle);
|
||||
let textY = centerY - textDistance * Math.cos(midAngle);
|
||||
let label = document.createElementNS(SVG_NS, "text");
|
||||
label.appendChild(document.createTextNode(sliceInfo.label));
|
||||
label.setAttribute("class", "pie-chart-label");
|
||||
label.setAttribute("style", data.length > 1 ? hoverTransform : "");
|
||||
label.setAttribute("x", data.length > 1 ? textX : centerX);
|
||||
label.setAttribute("y", data.length > 1 ? textY : centerY);
|
||||
container.appendChild(label);
|
||||
}
|
||||
|
||||
startAngle = endAngle;
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the DOM for a table chart based on the specified properties.
|
||||
*
|
||||
* @param nsIDocument document
|
||||
* The document responsible with creating the DOM.
|
||||
* @param object
|
||||
* An object containing all or some of the following properties:
|
||||
* - title: a string displayed as the chart's (description)/local
|
||||
* - data: an array of items used to display each row; all the items
|
||||
* should be objects representing columns, for which the
|
||||
* properties' values will be displayed in each cell of a row.
|
||||
* e.g: [{
|
||||
* size: 1,
|
||||
* label2: "1foo",
|
||||
* label3: "2yolo"
|
||||
* }, {
|
||||
* size: 2,
|
||||
* label2: "3bar",
|
||||
* label3: "4swag"
|
||||
* }];
|
||||
* - totals: an object specifying for which rows in the `data` array
|
||||
* the sum of their cells is to be displayed in the chart;
|
||||
* e.g: {
|
||||
* label1: "Total size: %S",
|
||||
* label3: "Total lolz: %S"
|
||||
* }
|
||||
* @return TableChart
|
||||
* A table chart proxy instance, which emits the following events:
|
||||
* - "mouseenter", when the mouse enters a row
|
||||
* - "mouseleave", when the mouse leaves a row
|
||||
* - "click", when the mouse clicks a row
|
||||
*/
|
||||
function createTableChart(document, { data, totals, title }) {
|
||||
let isPlaceholder = false;
|
||||
|
||||
// If there's no data available, display an empty placeholder.
|
||||
if (!data || !data.length) {
|
||||
data = emptyTableChartData;
|
||||
isPlaceholder = true;
|
||||
}
|
||||
|
||||
let container = document.createElement("vbox");
|
||||
container.className = "generic-chart-container table-chart-container";
|
||||
container.setAttribute("pack", "center");
|
||||
container.setAttribute("flex", "1");
|
||||
container.setAttribute("rows", data.length);
|
||||
container.setAttribute("placeholder", isPlaceholder);
|
||||
|
||||
let proxy = new TableChart(container);
|
||||
|
||||
let titleNode = document.createElement("label");
|
||||
titleNode.className = "plain table-chart-title";
|
||||
titleNode.setAttribute("value", title);
|
||||
container.appendChild(titleNode);
|
||||
|
||||
let tableNode = document.createElement("vbox");
|
||||
tableNode.className = "plain table-chart-grid";
|
||||
container.appendChild(tableNode);
|
||||
|
||||
for (let rowInfo of data) {
|
||||
let rowNode = document.createElement("hbox");
|
||||
rowNode.className = "table-chart-row";
|
||||
rowNode.setAttribute("align", "center");
|
||||
|
||||
let boxNode = document.createElement("hbox");
|
||||
boxNode.className = "table-chart-row-box chart-colored-blob";
|
||||
boxNode.setAttribute("name", rowInfo.label);
|
||||
rowNode.appendChild(boxNode);
|
||||
|
||||
for (let [key, value] in Iterator(rowInfo)) {
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.className = "plain table-chart-row-label";
|
||||
labelNode.setAttribute("name", key);
|
||||
labelNode.setAttribute("value", value);
|
||||
rowNode.appendChild(labelNode);
|
||||
}
|
||||
|
||||
proxy.rows.set(rowInfo, rowNode);
|
||||
delegate(proxy, ["click", "mouseenter", "mouseleave"], rowNode, rowInfo);
|
||||
tableNode.appendChild(rowNode);
|
||||
}
|
||||
|
||||
let totalsNode = document.createElement("vbox");
|
||||
totalsNode.className = "table-chart-totals";
|
||||
|
||||
for (let [key, value] in Iterator(totals || {})) {
|
||||
let total = data.reduce((acc, e) => acc + parseFloat(e[key]), 0);
|
||||
let formatted = !isNaN(total) ? L10N.numberWithDecimals(total, 2) : 0;
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.className = "plain table-chart-summary-label";
|
||||
labelNode.setAttribute("name", key);
|
||||
labelNode.setAttribute("value", value.replace("%S", formatted));
|
||||
totalsNode.appendChild(labelNode);
|
||||
}
|
||||
|
||||
container.appendChild(totalsNode);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "emptyPieChartData", () => {
|
||||
return [{ size: 1, label: L10N.getStr("pieChart.empty") }];
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "emptyTableChartData", () => {
|
||||
return [{ size: "", label: L10N.getStr("tableChart.empty") }];
|
||||
});
|
||||
|
||||
/**
|
||||
* Delegates DOM events emitted by an nsIDOMNode to an EventEmitter proxy.
|
||||
*
|
||||
* @param EventEmitter emitter
|
||||
* The event emitter proxy instance.
|
||||
* @param array events
|
||||
* An array of events, e.g. ["mouseenter", "mouseleave"].
|
||||
* @param nsIDOMNode node
|
||||
* The element firing the DOM events.
|
||||
* @param any args
|
||||
* The arguments passed when emitting events through the proxy.
|
||||
*/
|
||||
function delegate(emitter, events, node, args) {
|
||||
for (let event of events) {
|
||||
node.addEventListener(event, emitter.emit.bind(emitter, event, args));
|
||||
}
|
||||
}
|
@ -11,9 +11,14 @@
|
||||
- A good criteria is the language in which you'd find the best
|
||||
- documentation on web development on the web. -->
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.emptyNotice2): This is the label displayed
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.perfNotice1/2): These are the labels displayed
|
||||
- in the network table when empty to start performance analysis. -->
|
||||
<!ENTITY netmonitorUI.perfNotice1 "• Click on the">
|
||||
<!ENTITY netmonitorUI.perfNotice2 "button to start performance analysis.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.emptyNotice3): This is the label displayed
|
||||
- in the network table when empty. -->
|
||||
<!ENTITY netmonitorUI.emptyNotice2 "Perform a request or reload the page to see detailed information about network activity.">
|
||||
<!ENTITY netmonitorUI.emptyNotice3 "• Perform a request or reload the page to see detailed information about network activity.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.toolbar.status2): This is the label displayed
|
||||
- in the network table toolbar, above the "status" column. -->
|
||||
@ -99,10 +104,18 @@
|
||||
- in the network details footer for the "Flash" filtering button. -->
|
||||
<!ENTITY netmonitorUI.footer.filterFlash "Flash">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterOther): This is the label displayed
|
||||
- in the network details footer for the "Other" filtering button. -->
|
||||
<!ENTITY netmonitorUI.footer.filterOther "Other">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.footer.clear): This is the label displayed
|
||||
- in the network details footer for the "Clear" button. -->
|
||||
<!ENTITY netmonitorUI.footer.clear "Clear">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.footer.clear): This is the label displayed
|
||||
- in the network details footer for the performance analysis button. -->
|
||||
<!ENTITY netmonitorUI.footer.perf "Toggle performance analysis...">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.panesButton.tooltip): This is the tooltip for
|
||||
- the button that toggles the panes visible or hidden in the netmonitor UI. -->
|
||||
<!ENTITY netmonitorUI.panesButton.tooltip "Toggle network info">
|
||||
@ -173,22 +186,30 @@
|
||||
- in a "receive" state. -->
|
||||
<!ENTITY netmonitorUI.timings.receive "Receiving:">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.perfTools): This is the label displayed
|
||||
- on the context menu that shows the performance analysis tools -->
|
||||
<!ENTITY netmonitorUI.context.perfTools "Start Performance Analysis...">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.perfTools.accesskey): This is the access key
|
||||
- for the performance analysis menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.context.perfTools.accesskey "S">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyUrl): This is the label displayed
|
||||
- on the context menu that copies the selected request's url -->
|
||||
<!ENTITY netmonitorUI.context.copyUrl "Copy URL">
|
||||
<!ENTITY netmonitorUI.context.copyUrl "Copy URL">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.copyUrl.accesskey): This is the access key
|
||||
- for the Copy URL menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
|
||||
<!ENTITY netmonitorUI.context.copyUrl.accesskey "C">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.summary.editAndResend): This is the label displayed
|
||||
- on the button in the headers tab that opens a form to edit and resend the currently
|
||||
displayed request -->
|
||||
<!ENTITY netmonitorUI.summary.editAndResend "Edit and Resend">
|
||||
<!ENTITY netmonitorUI.summary.editAndResend "Edit and Resend">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.summary.editAndResend.accesskey): This is the access key
|
||||
- for the "Edit and Resend" menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.summary.editAndResend.accesskey "R">
|
||||
<!ENTITY netmonitorUI.summary.editAndResend.accesskey "R">
|
||||
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.newTab): This is the label
|
||||
- for the Open in New Tab menu item displayed in the context menu of the
|
||||
@ -198,7 +219,7 @@
|
||||
<!-- LOCALIZATION NOTE (netmonitorUI.context.newTab.accesskey): This is the access key
|
||||
- for the Open in New Tab menu item displayed in the context menu of the
|
||||
- network container -->
|
||||
<!ENTITY netmonitorUI.context.newTab.accesskey "O">
|
||||
<!ENTITY netmonitorUI.context.newTab.accesskey "O">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.newRequest): This is the label displayed
|
||||
- as the title of the new custom request form -->
|
||||
@ -223,3 +244,7 @@
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.cancel): This is the label displayed
|
||||
- on the button which cancels and closes the custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.cancel "Cancel">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.backButton): This is the label displayed
|
||||
- on the button which exists the performance statistics view -->
|
||||
<!ENTITY netmonitorUI.backButton "Back">
|
||||
|
@ -135,3 +135,49 @@ networkMenu.second=%S s
|
||||
# LOCALIZATION NOTE (networkMenu.minute): This is the label displayed
|
||||
# in the network menu specifying timing interval divisions (in minutes).
|
||||
networkMenu.minute=%S min
|
||||
|
||||
# LOCALIZATION NOTE (networkMenu.minute): This is the label displayed
|
||||
# in the network menu specifying timing interval divisions (in minutes).
|
||||
networkMenu.minute=%S min
|
||||
|
||||
# LOCALIZATION NOTE (pieChart.empty): This is the label displayed
|
||||
# for pie charts (e.g., in the performance analysis view) when there is
|
||||
# no data available yet.
|
||||
pieChart.empty=Loading
|
||||
|
||||
# LOCALIZATION NOTE (tableChart.empty): This is the label displayed
|
||||
# for table charts (e.g., in the performance analysis view) when there is
|
||||
# no data available yet.
|
||||
tableChart.empty=Please wait…
|
||||
|
||||
# LOCALIZATION NOTE (charts.sizeKB): This is the label displayed
|
||||
# in pie or table charts specifying the size of a request (in kilobytes).
|
||||
charts.sizeKB=%S KB
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalMS): This is the label displayed
|
||||
# in pie or table charts specifying the time for a request to finish (in milliseconds).
|
||||
charts.totalMS=%S ms
|
||||
|
||||
# LOCALIZATION NOTE (charts.cacheEnabled): This is the label displayed
|
||||
# in the performance analysis view for "cache enabled" charts.
|
||||
charts.cacheEnabled=Primed cache
|
||||
|
||||
# LOCALIZATION NOTE (charts.cacheDisabled): This is the label displayed
|
||||
# in the performance analysis view for "cache disabled" charts.
|
||||
charts.cacheDisabled=Empty cache
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalSize): This is the label displayed
|
||||
# in the performance analysis view for total requests size, in kilobytes.
|
||||
charts.totalSize=Size: %S KB
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalTime): This is the label displayed
|
||||
# in the performance analysis view for total requests time, in milliseconds.
|
||||
charts.totalTime=Time: %S ms
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalCached): This is the label displayed
|
||||
# in the performance analysis view for total cached responses.
|
||||
charts.totalCached=Cached responses: %S
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalCount): This is the label displayed
|
||||
# in the performance analysis view for total requests.
|
||||
charts.totalCount=Total requests: %S
|
||||
|
@ -6,7 +6,7 @@
|
||||
#requests-menu-empty-notice {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
font-size: 110%;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.theme-dark #requests-menu-empty-notice {
|
||||
@ -17,6 +17,18 @@
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
#requests-menu-perf-notice-button {
|
||||
min-width: 30px;
|
||||
min-height: 28px;
|
||||
margin: 0;
|
||||
list-style-image: url(profiler-stopwatch.png);
|
||||
-moz-image-region: rect(0px,16px,16px,0px);
|
||||
}
|
||||
|
||||
#requests-menu-perf-notice-button .button-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
%filter substitution
|
||||
%define table_itemDarkStartBorder rgba(0,0,0,0.2)
|
||||
%define table_itemDarkEndBorder rgba(128,128,128,0.15)
|
||||
@ -475,12 +487,11 @@ box.requests-menu-status {
|
||||
/* Network request details tabpanels */
|
||||
|
||||
.theme-dark .tabpanel-content {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-dark .tabpanel-summary-label {
|
||||
color: #f5f7fa; /* Dark foreground text */
|
||||
}
|
||||
/* Summary tabpanel */
|
||||
|
||||
.tabpanel-summary-container {
|
||||
padding: 1px;
|
||||
@ -578,7 +589,7 @@ box.requests-menu-status {
|
||||
min-width: 1em;
|
||||
margin: 0;
|
||||
border: none;
|
||||
padding: 2px 1.5vw;
|
||||
padding: 2px 0.75vw;
|
||||
}
|
||||
|
||||
.theme-dark .requests-menu-footer-button,
|
||||
@ -595,14 +606,14 @@ box.requests-menu-status {
|
||||
min-width: 2px;
|
||||
}
|
||||
|
||||
.theme-dark .requests-menu-footer-spacer:not(:first-of-type),
|
||||
.theme-dark .requests-menu-footer-button:not(:first-of-type) {
|
||||
.theme-dark .requests-menu-footer-spacer:not(:first-child),
|
||||
.theme-dark .requests-menu-footer-button:not(:first-child) {
|
||||
-moz-border-start: 1px solid @table_itemDarkStartBorder@;
|
||||
box-shadow: -1px 0 0 @table_itemDarkEndBorder@;
|
||||
}
|
||||
|
||||
.theme-light .requests-menu-footer-spacer:not(:first-of-type),
|
||||
.theme-light .requests-menu-footer-button:not(:first-of-type) {
|
||||
.theme-light .requests-menu-footer-spacer:not(:first-child),
|
||||
.theme-light .requests-menu-footer-button:not(:first-child) {
|
||||
-moz-border-start: 1px solid @table_itemLightStartBorder@;
|
||||
box-shadow: -1px 0 0 @table_itemLightEndBorder@;
|
||||
}
|
||||
@ -628,10 +639,179 @@ box.requests-menu-status {
|
||||
}
|
||||
|
||||
.requests-menu-footer-label {
|
||||
padding-top: 2px;
|
||||
padding-top: 3px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Performance analysis buttons */
|
||||
|
||||
#requests-menu-network-summary-button {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
border-color: transparent;
|
||||
list-style-image: url(profiler-stopwatch.png);
|
||||
-moz-image-region: rect(0px,16px,16px,0px);
|
||||
-moz-padding-end: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#requests-menu-network-summary-label {
|
||||
-moz-padding-start: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#requests-menu-network-summary-label:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Performance analysis view */
|
||||
|
||||
#network-statistics-toolbar {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#network-statistics-back-button {
|
||||
min-width: 4em;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
-moz-border-start: none;
|
||||
}
|
||||
|
||||
#network-statistics-view-splitter {
|
||||
border-color: rgba(0,0,0,0.2);
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#network-statistics-charts {
|
||||
min-height: 1px;
|
||||
}
|
||||
|
||||
.theme-dark #network-statistics-charts {
|
||||
background: url(background-noise-toolbar.png), #343c45; /* Toolbars */
|
||||
}
|
||||
|
||||
.theme-light #network-statistics-charts {
|
||||
background: url(background-noise-toolbar.png), #f0f1f2; /* Toolbars */
|
||||
}
|
||||
|
||||
#network-statistics-charts .pie-chart-container {
|
||||
-moz-margin-start: 3vw;
|
||||
-moz-margin-end: 1vw;
|
||||
}
|
||||
|
||||
#network-statistics-charts .table-chart-container {
|
||||
-moz-margin-start: 1vw;
|
||||
-moz-margin-end: 3vw;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=html] {
|
||||
fill: #5e88b0; /* Blue-Grey highlight */
|
||||
background: #5e88b0;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=html] {
|
||||
fill: #5f88b0; /* Blue-Grey highlight */
|
||||
background: #5f88b0;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=css] {
|
||||
fill: #46afe3; /* Blue highlight */
|
||||
background: #46afe3;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=css] {
|
||||
fill: #0088cc; /* Blue highlight */
|
||||
background: #0088cc;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=js] {
|
||||
fill: #d99b28; /* Light Orange highlight */
|
||||
background: #d99b28;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=js] {
|
||||
fill: #d97e00; /* Light Orange highlight */
|
||||
background: #d97e00;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=xhr] {
|
||||
fill: #d96629; /* Orange highlight */
|
||||
background: #d96629;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=xhr] {
|
||||
fill: #f13c00; /* Orange highlight */
|
||||
background: #f13c00;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=fonts] {
|
||||
fill: #6b7abb; /* Purple highlight */
|
||||
background: #6b7abb;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=fonts] {
|
||||
fill: #5b5fff; /* Purple highlight */
|
||||
background: #5b5fff;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=images] {
|
||||
fill: #df80ff; /* Pink highlight */
|
||||
background: #df80ff;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=images] {
|
||||
fill: #b82ee5; /* Pink highlight */
|
||||
background: #b82ee5;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=media] {
|
||||
fill: #70bf53; /* Green highlight */
|
||||
background: #70bf53;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=media] {
|
||||
fill: #2cbb0f; /* Green highlight */
|
||||
background: #2cbb0f;
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob[name=flash] {
|
||||
fill: #eb5368; /* Red highlight */
|
||||
background: #eb5368;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob[name=flash] {
|
||||
fill: #ed2655; /* Red highlight */
|
||||
background: #ed2655;
|
||||
}
|
||||
|
||||
.table-chart-row-label[name=cached] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.table-chart-row-label[name=count] {
|
||||
width: 3em;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.table-chart-row-label[name=label] {
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
.table-chart-row-label[name=size] {
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
.table-chart-row-label[name=time] {
|
||||
width: 7em;
|
||||
}
|
||||
|
||||
/* Responsive sidebar */
|
||||
@media (max-width: 700px) {
|
||||
#requests-menu-toolbar {
|
||||
@ -644,7 +824,7 @@ box.requests-menu-status {
|
||||
|
||||
.requests-menu-footer-button,
|
||||
.requests-menu-footer-label {
|
||||
padding: 2px 2vw;
|
||||
padding: 2px 1vw;
|
||||
}
|
||||
|
||||
#details-pane {
|
||||
|
@ -723,7 +723,9 @@
|
||||
.theme-light #breadcrumb-separator-normal,
|
||||
.theme-light .scrollbutton-up > .toolbarbutton-icon,
|
||||
.theme-light .scrollbutton-down > .toolbarbutton-icon,
|
||||
.theme-light #black-boxed-message-button .button-icon {
|
||||
.theme-light #black-boxed-message-button .button-icon,
|
||||
.theme-light #requests-menu-perf-notice-button .button-icon,
|
||||
.theme-light #requests-menu-network-summary-button .button-icon {
|
||||
filter: url(filters.svg#invert);
|
||||
}
|
||||
|
||||
|
@ -792,4 +792,145 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Charts */
|
||||
|
||||
.generic-chart-container {
|
||||
/* Hack: force hardware acceleration */
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
.theme-dark .generic-chart-container {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light .generic-chart-container {
|
||||
color: #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
.theme-dark .chart-colored-blob {
|
||||
fill: #b8c8d9; /* Light content text */
|
||||
background: #b8c8d9;
|
||||
}
|
||||
|
||||
.theme-light .chart-colored-blob {
|
||||
fill: #8fa1b2; /* Grey content text */
|
||||
background: #8fa1b2;
|
||||
}
|
||||
|
||||
/* Charts: Pie */
|
||||
|
||||
.pie-chart-slice {
|
||||
stroke-width: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.theme-dark .pie-chart-slice {
|
||||
stroke: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.theme-light .pie-chart-slice {
|
||||
stroke: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
.theme-dark .pie-chart-slice[largest] {
|
||||
stroke-width: 2px;
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
.theme-light .pie-chart-slice[largest] {
|
||||
stroke: #000;
|
||||
}
|
||||
|
||||
.pie-chart-label {
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.theme-dark .pie-chart-label {
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
.theme-light .pie-chart-label {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.pie-chart-container[slices="1"] > .pie-chart-slice {
|
||||
stroke-width: 0px;
|
||||
}
|
||||
|
||||
.pie-chart-slice,
|
||||
.pie-chart-label {
|
||||
transition: all 0.1s ease-out;
|
||||
}
|
||||
|
||||
.pie-chart-slice:not(:hover):not([focused]),
|
||||
.pie-chart-slice:not(:hover):not([focused]) + .pie-chart-label {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
/* Charts: Table */
|
||||
|
||||
.table-chart-title {
|
||||
padding-bottom: 10px;
|
||||
font-size: 120%;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.table-chart-row {
|
||||
margin-top: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-chart-grid:hover > .table-chart-row {
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.table-chart-grid:not(:hover) > .table-chart-row {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.generic-chart-container:hover > .table-chart-grid:hover > .table-chart-row:not(:hover),
|
||||
.generic-chart-container:hover ~ .table-chart-container > .table-chart-grid > .table-chart-row:not([focused]) {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.table-chart-row-box {
|
||||
width: 8px;
|
||||
height: 1.5em;
|
||||
-moz-margin-end: 10px;
|
||||
}
|
||||
|
||||
.table-chart-row-label {
|
||||
width: 8em;
|
||||
-moz-padding-end: 6px;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
.table-chart-totals {
|
||||
margin-top: 8px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.theme-dark .table-chart-totals {
|
||||
border-top: 1px solid #b6babf; /* Grey foreground text */
|
||||
}
|
||||
|
||||
.theme-light .table-chart-totals {
|
||||
border-top: 1px solid #585959; /* Grey foreground text */
|
||||
}
|
||||
|
||||
.table-chart-summary-label {
|
||||
font-weight: 600;
|
||||
padding: 1px 0px;
|
||||
}
|
||||
|
||||
.theme-dark .table-chart-summary-label {
|
||||
color: #f5f7fa; /* Light foreground text */
|
||||
}
|
||||
|
||||
.theme-light .table-chart-summary-label {
|
||||
color: #18191a; /* Dark foreground text */
|
||||
}
|
||||
|
||||
%include ../../shared/devtools/app-manager/manifest-editor.inc.css
|
||||
|
@ -781,7 +781,12 @@ BrowserTabActor.prototype = {
|
||||
reload = true;
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
// Reload if:
|
||||
// - there's an explicit `performReload` flag and it's true
|
||||
// - there's no `performReload` flag, but it makes sense to do so
|
||||
let hasExplicitReloadFlag = "performReload" in options;
|
||||
if ((hasExplicitReloadFlag && options.performReload) ||
|
||||
(!hasExplicitReloadFlag && reload)) {
|
||||
this.onReload();
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user