--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -302,17 +302,17 @@ Tools.netMonitor = {
id: "netmonitor",
accesskey: l10n("netmonitor.accesskey"),
key: l10n("netmonitor.commandkey"),
ordinal: 9,
modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
visibilityswitch: "devtools.netmonitor.enabled",
icon: "chrome://devtools/skin/images/tool-network.svg",
invertIconForDarkTheme: true,
- url: "chrome://devtools/content/netmonitor/netmonitor.xul",
+ url: "chrome://devtools/content/netmonitor/netmonitor.xhtml",
label: l10n("netmonitor.label"),
panelLabel: l10n("netmonitor.panelLabel"),
get tooltip() {
return l10n("netmonitor.tooltip2",
(osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") + this.key);
},
inMenu: true,
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -5,17 +5,17 @@
devtools.jar:
% content devtools %content/
content/shared/vendor/d3.js (shared/vendor/d3.js)
content/shared/vendor/dagre-d3.js (shared/vendor/dagre-d3.js)
content/shared/widgets/widgets.css (shared/widgets/widgets.css)
content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
content/projecteditor/chrome/content/projecteditor.xul (projecteditor/chrome/content/projecteditor.xul)
content/projecteditor/lib/helpers/readdir.js (projecteditor/lib/helpers/readdir.js)
- content/netmonitor/netmonitor.xul (netmonitor/netmonitor.xul)
+ content/netmonitor/netmonitor.xhtml (netmonitor/netmonitor.xhtml)
content/netmonitor/netmonitor.js (netmonitor/netmonitor.js)
content/webconsole/webconsole.xul (webconsole/webconsole.xul)
* content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul)
content/scratchpad/scratchpad.js (scratchpad/scratchpad.js)
content/shared/splitview.css (shared/splitview.css)
content/shared/theme-switching.js (shared/theme-switching.js)
content/shared/frame-script-utils.js (shared/frame-script-utils.js)
content/styleeditor/styleeditor.xul (styleeditor/styleeditor.xul)
--- a/devtools/client/netmonitor/components/monitor-panel.js
+++ b/devtools/client/netmonitor/components/monitor-panel.js
@@ -98,17 +98,17 @@ const MonitorPanel = createClass({
},
render() {
let { isEmpty, networkDetailsOpen } = this.props;
return (
div({ className: "monitor-panel" },
Toolbar(),
SplitBox({
- className: "devtools-responsive-container split-box",
+ className: "devtools-responsive-container",
initialWidth: `${Prefs.networkDetailsWidth}px`,
initialHeight: `${Prefs.networkDetailsHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
startPanel: RequestList({ isEmpty }),
endPanel: networkDetailsOpen ?
NetworkDetailsPanel({
--- a/devtools/client/netmonitor/components/network-monitor.js
+++ b/devtools/client/netmonitor/components/network-monitor.js
@@ -17,17 +17,17 @@ const StatisticsPanel = createFactory(re
const { div } = DOM;
/*
* Network monitor component
*/
function NetworkMonitor({ statisticsOpen }) {
return (
- div({ className: "network-monitor theme-sidebar" },
+ div({ className: "network-monitor" },
!statisticsOpen ? MonitoPanel() : StatisticsPanel()
)
);
}
NetworkMonitor.displayName = "NetworkMonitor";
NetworkMonitor.propTypes = {
--- a/devtools/client/netmonitor/components/request-list-content.js
+++ b/devtools/client/netmonitor/components/request-list-content.js
@@ -8,17 +8,16 @@ const { KeyCodes } = require("devtools/c
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
-const { Task } = require("devtools/shared/task");
const Actions = require("../actions/index");
const {
setTooltipImageContent,
setTooltipStackTraceContent,
} = require("../request-list-tooltip");
const {
getDisplayedRequests,
getWaterfallScale,
@@ -140,17 +139,17 @@ const RequestListContent = createClass({
* over a request item or not.
*
* @param nsIDOMNode target
* The element node currently being hovered.
* @param object tooltip
* The current tooltip instance.
* @return {Promise}
*/
- onHover: Task.async(function* (target, tooltip) {
+ onHover(target, tooltip) {
let itemEl = target.closest(".request-list-item");
if (!itemEl) {
return false;
}
let itemId = itemEl.dataset.id;
if (!itemId) {
return false;
}
@@ -161,17 +160,17 @@ const RequestListContent = createClass({
if (requestItem.responseContent && target.closest(".requests-menu-icon-and-file")) {
return setTooltipImageContent(tooltip, itemEl, requestItem);
} else if (requestItem.cause && target.closest(".requests-menu-cause-stack")) {
return setTooltipStackTraceContent(tooltip, requestItem);
}
return false;
- }),
+ },
/**
* Scroll listener for the requests menu view.
*/
onScroll() {
this.tooltip.hide();
},
@@ -221,25 +220,16 @@ const RequestListContent = createClass({
/**
* If selection has just changed (by keyboard navigation), don't keep the list
* scrolled to bottom, but allow scrolling up with the selection.
*/
onFocusedNodeChange() {
this.shouldScrollBottom = false;
},
- /**
- * If a focused item was unmounted, transfer the focus to the container element.
- */
- onFocusedNodeUnmount() {
- if (this.refs.contentEl) {
- this.refs.contentEl.focus();
- }
- },
-
render() {
const {
displayedRequests,
firstRequestStartedMillis,
selectedRequestId,
onItemMouseDown,
onSecurityIconClick,
} = this.props;
@@ -247,25 +237,24 @@ const RequestListContent = createClass({
return (
div({
ref: "contentEl",
className: "requests-menu-contents",
tabIndex: 0,
onKeyDown: this.onKeyDown,
},
displayedRequests.map((item, index) => RequestListItem({
- key: item.id,
+ firstRequestStartedMillis,
item,
index,
isSelected: item.id === selectedRequestId,
- firstRequestStartedMillis,
- onMouseDown: () => onItemMouseDown(item.id),
+ key: item.id,
onContextMenu: this.onContextMenu,
onFocusedNodeChange: this.onFocusedNodeChange,
- onFocusedNodeUnmount: this.onFocusedNodeUnmount,
+ onMouseDown: () => onItemMouseDown(item.id),
onSecurityIconClick: () => onSecurityIconClick(item.securityState),
}))
)
);
},
});
module.exports = connect(
--- a/devtools/client/netmonitor/components/request-list-item.js
+++ b/devtools/client/netmonitor/components/request-list-item.js
@@ -1,14 +1,12 @@
/* 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/. */
-/* eslint-disable react/prop-types */
-
"use strict";
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
@@ -64,17 +62,16 @@ const RequestListItem = createClass({
propTypes: {
item: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
isSelected: PropTypes.bool.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
onContextMenu: PropTypes.func.isRequired,
onFocusedNodeChange: PropTypes.func,
- onFocusedNodeUnmount: PropTypes.func,
onMouseDown: PropTypes.func.isRequired,
onSecurityIconClick: PropTypes.func.isRequired,
},
componentDidMount() {
if (this.props.isSelected) {
this.refs.el.focus();
}
@@ -89,42 +86,28 @@ const RequestListItem = createClass({
if (!prevProps.isSelected && this.props.isSelected) {
this.refs.el.focus();
if (this.props.onFocusedNodeChange) {
this.props.onFocusedNodeChange();
}
}
},
- componentWillUnmount() {
- // If this node is being destroyed and has focus, transfer the focus manually
- // to the parent tree component. Otherwise, the focus will get lost and keyboard
- // navigation in the tree will stop working. This is a workaround for a XUL bug.
- // See bugs 1259228 and 1152441 for details.
- // DE-XUL: Remove this hack once all usages are only in HTML documents.
- if (this.props.isSelected) {
- this.refs.el.blur();
- if (this.props.onFocusedNodeUnmount) {
- this.props.onFocusedNodeUnmount();
- }
- }
- },
-
render() {
const {
item,
index,
isSelected,
firstRequestStartedMillis,
onContextMenu,
onMouseDown,
onSecurityIconClick
} = this.props;
- let classList = [ "request-list-item" ];
+ let classList = ["request-list-item"];
if (isSelected) {
classList.push("selected");
}
classList.push(index % 2 ? "odd" : "even");
return (
div({
ref: "el",
@@ -153,16 +136,20 @@ const UPDATED_STATUS_PROPS = [
"statusText",
"fromCache",
"fromServiceWorker",
];
const StatusColumn = createFactory(createClass({
displayName: "StatusColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
},
render() {
const { status, statusText, fromCache, fromServiceWorker } = this.props.item;
let code, title;
@@ -194,16 +181,20 @@ const StatusColumn = createFactory(creat
)
);
}
}));
const MethodColumn = createFactory(createClass({
displayName: "MethodColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return this.props.item.method !== nextProps.item.method;
},
render() {
const { method } = this.props.item;
return (
div({ className: "requests-menu-subitem requests-menu-method-box" },
@@ -216,16 +207,20 @@ const MethodColumn = createFactory(creat
const UPDATED_FILE_PROPS = [
"urlDetails",
"responseContentDataUri",
];
const FileColumn = createFactory(createClass({
displayName: "FileColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
},
render() {
const { urlDetails, responseContentDataUri } = this.props.item;
return (
@@ -251,25 +246,30 @@ const UPDATED_DOMAIN_PROPS = [
"urlDetails",
"remoteAddress",
"securityState",
];
const DomainColumn = createFactory(createClass({
displayName: "DomainColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ onSecurityIconClick: PropTypes.func.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
},
render() {
const { item, onSecurityIconClick } = this.props;
const { urlDetails, remoteAddress, securityState } = item;
- let iconClassList = [ "requests-security-state-icon" ];
+ let iconClassList = ["requests-security-state-icon"];
let iconTitle;
if (urlDetails.isLocal) {
iconClassList.push("security-state-local");
iconTitle = L10N.getStr("netmonitor.security.state.secure");
} else if (securityState) {
iconClassList.push(`security-state-${securityState}`);
iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
}
@@ -287,16 +287,20 @@ const DomainColumn = createFactory(creat
)
);
}
}));
const CauseColumn = createFactory(createClass({
displayName: "CauseColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return this.props.item.cause !== nextProps.item.cause;
},
render() {
const { cause } = this.props.item;
let causeType = "";
@@ -329,16 +333,20 @@ const CONTENT_MIME_TYPE_ABBREVIATIONS =
"ecmascript": "js",
"javascript": "js",
"x-javascript": "js"
};
const TypeColumn = createFactory(createClass({
displayName: "TypeColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return this.props.item.mimeType !== nextProps.item.mimeType;
},
render() {
const { mimeType } = this.props.item;
let abbrevType;
if (mimeType) {
@@ -361,16 +369,20 @@ const UPDATED_TRANSFERRED_PROPS = [
"transferredSize",
"fromCache",
"fromServiceWorker",
];
const TransferredSizeColumn = createFactory(createClass({
displayName: "TransferredSizeColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_TRANSFERRED_PROPS, this.props.item, nextProps.item);
},
render() {
const { transferredSize, fromCache, fromServiceWorker } = this.props.item;
let text;
@@ -396,16 +408,20 @@ const TransferredSizeColumn = createFact
)
);
}
}));
const ContentSizeColumn = createFactory(createClass({
displayName: "ContentSizeColumn",
+ propTypes: {
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return this.props.item.contentSize !== nextProps.item.contentSize;
},
render() {
const { contentSize } = this.props.item;
let text;
@@ -429,16 +445,21 @@ const UPDATED_WATERFALL_PROPS = [
"totalTime",
"fromCache",
"fromServiceWorker",
];
const WaterfallColumn = createFactory(createClass({
displayName: "WaterfallColumn",
+ propTypes: {
+ firstRequestStartedMillis: PropTypes.number.isRequired,
+ item: PropTypes.object.isRequired,
+ },
+
shouldComponentUpdate(nextProps) {
return this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis ||
!propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item);
},
render() {
const { item, firstRequestStartedMillis } = this.props;
@@ -480,23 +501,21 @@ function timingBoxes(item) {
key,
className: "requests-menu-timings-box " + key,
style: { width }
}));
}
}
}
- if (typeof totalTime == "number") {
+ if (typeof totalTime === "number") {
let text = L10N.getFormatStr("networkMenu.totalMS", totalTime);
boxes.push(div({
key: "total",
className: "requests-menu-timings-total",
- title: text
+ title: text,
}, text));
}
return boxes;
}
module.exports = RequestListItem;
-
-/* eslint-enable react/prop-types */
--- a/devtools/client/netmonitor/components/statistics-panel.js
+++ b/devtools/client/netmonitor/components/statistics-panel.js
@@ -16,16 +16,17 @@ const Actions = require("../actions/inde
const { Filters } = require("../filter-predicates");
const { L10N } = require("../l10n");
const {
getSizeWithDecimals,
getTimeWithDecimals
} = require("../utils/format-utils");
const { button, div } = DOM;
+const MediaQueryList = window.matchMedia("(min-width: 700px)");
const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
const CHARTS_CACHE_ENABLED = L10N.getStr("charts.cacheEnabled");
const CHARTS_CACHE_DISABLED = L10N.getStr("charts.cacheDisabled");
/*
* Statistics panel component
@@ -36,17 +37,25 @@ const StatisticsPanel = createClass({
displayName: "StatisticsPanel",
propTypes: {
closeStatistics: PropTypes.func.isRequired,
enableRequestFilterTypeOnly: PropTypes.func.isRequired,
requests: PropTypes.object,
},
+ getInitialState() {
+ return {
+ isVerticalSpliter: MediaQueryList.matches,
+ };
+ },
+
componentDidUpdate(prevProps) {
+ MediaQueryList.addListener(this.onLayoutChange);
+
const { requests } = this.props;
let ready = requests && !requests.isEmpty() && requests.every((req) =>
req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
req.status !== undefined && req.totalTime !== undefined
);
this.createChart({
id: "primedCacheChart",
@@ -56,16 +65,20 @@ const StatisticsPanel = createClass({
this.createChart({
id: "emptyCacheChart",
title: CHARTS_CACHE_DISABLED,
data: ready ? this.sanitizeChartDataSource(requests, true) : null,
});
},
+ componentWillUnmount() {
+ MediaQueryList.removeListener(this.onLayoutChange);
+ },
+
createChart({ id, title, data }) {
// Create a new chart.
let chart = Chart.PieTable(document, {
diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
title,
header: {
cached: "",
count: "",
@@ -212,29 +225,43 @@ const StatisticsPanel = createClass({
// Check the "Expires" header for a valid date.
if (expires && Date.parse(expires.value)) {
return true;
}
return false;
},
+ onLayoutChange() {
+ this.setState({
+ isVerticalSpliter: MediaQueryList.matches,
+ });
+ },
+
render() {
const { closeStatistics } = this.props;
+ let splitterClassName = ["splitter"];
+
+ if (this.state.isVerticalSpliter) {
+ splitterClassName.push("devtools-side-splitter");
+ } else {
+ splitterClassName.push("devtools-horizontal-splitter");
+ }
+
return (
div({ className: "statistics-panel" },
button({
className: "back-button devtools-button",
"data-text-only": "true",
title: BACK_BUTTON,
onClick: closeStatistics,
}, BACK_BUTTON),
- div({ className: "charts-container devtools-responsive-container" },
+ div({ className: "charts-container" },
div({ ref: "primedCacheChart", className: "charts primed-cache-chart" }),
- div({ className: "splitter devtools-side-splitter" }),
+ div({ className: splitterClassName.join(" ") }),
div({ ref: "emptyCacheChart", className: "charts empty-cache-chart" }),
),
)
);
}
});
module.exports = connect(
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -4,17 +4,16 @@
"use strict";
const promise = require("promise");
const Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const { CurlUtils } = require("devtools/client/shared/curl");
-const { Task } = require("devtools/shared/task");
const { ACTIVITY_TYPE } = require("./constants");
const { EVENTS } = require("./events");
const { configureStore } = require("./store");
const Actions = require("./actions/index");
const {
fetchHeaders,
formDataURI,
} = require("./request-utils");
@@ -30,55 +29,55 @@ const gStore = window.gStore = configure
*/
var NetMonitorController = {
/**
* Initializes the view and connects the monitor client.
*
* @return object
* A promise that is resolved when the monitor finishes startup.
*/
- startupNetMonitor: Task.async(function* () {
+ async startupNetMonitor() {
if (this._startup) {
return this._startup.promise;
}
this._startup = promise.defer();
- yield this.connect();
+ await this.connect();
this._startup.resolve();
return undefined;
- }),
+ },
/**
* Destroys the view and disconnects the monitor client from the server.
*
* @return object
* A promise that is resolved when the monitor finishes shutdown.
*/
- shutdownNetMonitor: Task.async(function* () {
+ async shutdownNetMonitor() {
if (this._shutdown) {
return this._shutdown.promise;
}
this._shutdown = promise.defer();
gStore.dispatch(Actions.batchReset());
this.TargetEventsHandler.disconnect();
this.NetworkEventsHandler.disconnect();
- yield this.disconnect();
+ await this.disconnect();
this._shutdown.resolve();
return undefined;
- }),
+ },
/**
* Initiates remote or chrome network monitoring based on the current target,
* wiring event handlers as necessary. Since the TabTarget will have already
* started listening to network requests by now, this is largely
* netmonitor-specific initialization.
*
* @return object
* A promise that is resolved when the monitor finishes connecting.
*/
- connect: Task.async(function* () {
+ async connect() {
if (this._connection) {
return this._connection.promise;
}
this._connection = promise.defer();
// Some actors like AddonActor or RootActor for chrome debugging
// aren't actual tabs.
if (this._target.isTabActor) {
@@ -92,58 +91,58 @@ var NetMonitorController = {
this.timelineFront = new TimelineFront(this._target.client,
this._target.form);
return this.timelineFront.start({ withDocLoadingEvents: true });
}
return undefined;
};
this.webConsoleClient = this._target.activeConsole;
- yield connectTimeline();
+ await connectTimeline();
this.TargetEventsHandler.connect();
this.NetworkEventsHandler.connect();
window.emit(EVENTS.CONNECTED);
this._connection.resolve();
this._connected = true;
return undefined;
- }),
+ },
/**
* Disconnects the debugger client and removes event handlers as necessary.
*/
- disconnect: Task.async(function* () {
+ async disconnect() {
if (this._disconnection) {
return this._disconnection.promise;
}
this._disconnection = promise.defer();
// Wait for the connection to finish first.
if (!this.isConnected()) {
- yield this._connection.promise;
+ await this._connection.promise;
}
// When debugging local or a remote instance, the connection is closed by
// the RemoteTarget. The webconsole actor is stopped on disconnect.
this.tabClient = null;
this.webConsoleClient = null;
// The timeline front wasn't initialized and started if the server wasn't
// recent enough to emit the markers we were interested in.
if (this._target.getTrait("documentLoadingMarkers")) {
- yield this.timelineFront.destroy();
+ await this.timelineFront.destroy();
this.timelineFront = null;
}
this._disconnection.resolve();
this._connected = false;
return undefined;
- }),
+ },
/**
* Checks whether the netmonitor connection is active.
* @return boolean
*/
isConnected: function () {
return !!this._connected;
},
@@ -561,133 +560,133 @@ NetworkEventsHandler.prototype = {
fromCache,
fromServiceWorker,
},
true
))
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
},
- updateRequest: Task.async(function* (id, data) {
+ async updateRequest(id, data) {
const action = Actions.updateRequest(id, data, true);
- yield gStore.dispatch(action);
+ await gStore.dispatch(action);
let {
responseContent,
responseCookies,
responseHeaders,
requestCookies,
requestHeaders,
requestPostData,
} = action.data;
let request = getRequestById(gStore.getState(), action.id);
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
- let headers = yield fetchHeaders(requestHeaders, this.getString);
+ let headers = await fetchHeaders(requestHeaders, this.getString);
if (headers) {
- yield gStore.dispatch(Actions.updateRequest(
+ await gStore.dispatch(Actions.updateRequest(
action.id,
{ requestHeaders: headers },
true,
));
}
}
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
- let headers = yield fetchHeaders(responseHeaders, this.getString);
+ let headers = await fetchHeaders(responseHeaders, this.getString);
if (headers) {
- yield gStore.dispatch(Actions.updateRequest(
+ await gStore.dispatch(Actions.updateRequest(
action.id,
{ responseHeaders: headers },
true,
));
}
}
if (request && responseContent && responseContent.content) {
let { mimeType } = request;
let { text, encoding } = responseContent.content;
- let response = yield this.getString(text);
+ let response = await this.getString(text);
let payload = {};
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
- yield gStore.dispatch(Actions.updateRequest(action.id, payload, true));
+ await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
if (mimeType.includes("image/")) {
window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
}
}
// Search the POST data upload stream for request headers and add
// them as a separate property, different from the classic headers.
if (requestPostData && requestPostData.postData) {
let { text } = requestPostData.postData;
- let postData = yield this.getString(text);
+ let postData = await this.getString(text);
const headers = CurlUtils.getHeadersFromMultipartText(postData);
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
}, 0);
let payload = {};
requestPostData.postData.text = postData;
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
- yield gStore.dispatch(Actions.updateRequest(action.id, payload, true));
+ await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
}
// Fetch request and response cookies long value.
// Actor does not provide full sized cookie value when the value is too long
// To display values correctly, we need fetch them in each request.
if (requestCookies) {
let reqCookies = [];
// request store cookies in requestCookies or requestCookies.cookies
let cookies = requestCookies.cookies ?
requestCookies.cookies : requestCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
reqCookies.push(Object.assign({}, cookie, {
- value: yield this.getString(cookie.value),
+ value: await this.getString(cookie.value),
}));
}
if (reqCookies.length) {
- yield gStore.dispatch(Actions.updateRequest(
+ await gStore.dispatch(Actions.updateRequest(
action.id,
{ requestCookies: reqCookies },
true));
}
}
}
if (responseCookies) {
let resCookies = [];
// response store cookies in responseCookies or responseCookies.cookies
let cookies = responseCookies.cookies ?
responseCookies.cookies : responseCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
resCookies.push(Object.assign({}, cookie, {
- value: yield this.getString(cookie.value),
+ value: await this.getString(cookie.value),
}));
}
if (resCookies.length) {
- yield gStore.dispatch(Actions.updateRequest(
+ await gStore.dispatch(Actions.updateRequest(
action.id,
{ responseCookies: resCookies },
true));
}
}
}
- }),
+ },
/**
* The "networkEventUpdate" message type handler.
*
* @param string type
* Message type.
* @param object packet
* The message received from the server.
@@ -897,22 +896,11 @@ NetworkEventsHandler.prototype = {
*/
EventEmitter.decorate(window);
/**
* Preliminary setup for the NetMonitorController object.
*/
NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
-
-/**
- * Export some properties to the global scope for easier access.
- */
-Object.defineProperties(window, {
- "gNetwork": {
- get: function () {
- return NetMonitorController.NetworkEventsHandler;
- },
- configurable: true
- }
-});
+window.gNetwork = NetMonitorController.NetworkEventsHandler;
exports.NetMonitorController = NetMonitorController;
--- a/devtools/client/netmonitor/netmonitor.js
+++ b/devtools/client/netmonitor/netmonitor.js
@@ -1,42 +1,31 @@
/* 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/. */
-/* exported Netmonitor, NetMonitorController */
-
"use strict";
-const Cu = Components.utils;
-const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
-
function Netmonitor(toolbox) {
- const require = window.windowRequire = BrowserLoader({
- baseURI: "resource://devtools/client/netmonitor/",
- window,
- commonLibRequire: toolbox.browserRequire,
- }).require;
-
window.NetMonitorController = require("./netmonitor-controller").NetMonitorController;
window.NetMonitorController._toolbox = toolbox;
window.NetMonitorController._target = toolbox.target;
}
Netmonitor.prototype = {
init() {
const require = window.windowRequire;
const { createFactory } = require("devtools/client/shared/vendor/react");
const { render } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
// Components
const NetworkMonitor = createFactory(require("./components/network-monitor"));
- this.networkMonitor = document.querySelector("#react-network-monitor-hook");
+ this.networkMonitor = document.querySelector(".root");
render(Provider(
{ store: window.gStore },
NetworkMonitor({ toolbox: window.NetMonitorController._toolbox }),
), this.networkMonitor);
return window.NetMonitorController.startupNetMonitor();
},
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/netmonitor.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" dir="">
+ <head>
+ <link rel="stylesheet" href="chrome://devtools/content/shared/widgets/widgets.css"/>
+ <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
+ <link rel="stylesheet" href="chrome://devtools/skin/netmonitor.css"/>
+
+ <script src="chrome://devtools/content/shared/theme-switching.js"/>
+ <script>
+ "use strict";
+
+ const { BrowserLoader } = Components.utils.import(
+ "resource://devtools/client/shared/browser-loader.js", {});
+ const { require } = BrowserLoader({
+ baseURI: "resource://devtools/client/netmonitor/",
+ window,
+ });
+ window.windowRequire = require;
+ </script>
+ </head>
+ <body class="theme-sidebar" role="application">
+ <div class="root"></div>
+ <script src="netmonitor.js" defer="true"/>
+ </body>
+</html>
deleted file mode 100644
--- a/devtools/client/netmonitor/netmonitor.xul
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
-<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
-<?xml-stylesheet href="chrome://devtools/skin/netmonitor.css" type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- xmlns:html="http://www.w3.org/1999/xhtml">
-
- <script type="application/javascript;version=1.8"
- src="chrome://devtools/content/shared/theme-switching.js"/>
- <script type="text/javascript" src="netmonitor.js"/>
-
- <html:div xmlns="http://www.w3.org/1999/xhtml"
- id="react-network-monitor-hook"/>
-</window>
--- a/devtools/client/netmonitor/panel.js
+++ b/devtools/client/netmonitor/panel.js
@@ -7,26 +7,26 @@
function NetMonitorPanel(iframeWindow, toolbox) {
this.panelWin = iframeWindow;
this.panelDoc = iframeWindow.document;
this.toolbox = toolbox;
this.netmonitor = new iframeWindow.Netmonitor(toolbox);
}
NetMonitorPanel.prototype = {
- open: async function () {
+ async open() {
if (!this.toolbox.target.isRemote) {
await this.toolbox.target.makeRemote();
}
await this.netmonitor.init();
this.emit("ready");
this.isReady = true;
return this;
},
- destroy: async function () {
+ async destroy() {
await this.netmonitor.destroy();
this.emit("destroyed");
return this;
},
};
exports.NetMonitorPanel = NetMonitorPanel;
--- a/devtools/client/netmonitor/request-list-context-menu.js
+++ b/devtools/client/netmonitor/request-list-context-menu.js
@@ -1,16 +1,15 @@
/* 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 Services = require("Services");
-const { Task } = require("devtools/shared/task");
const { Curl } = require("devtools/client/shared/curl");
const { gDevTools } = require("devtools/client/framework/devtools");
const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const { L10N } = require("./l10n");
const {
formDataURI,
getFormDataSections,
@@ -213,21 +212,21 @@ RequestListContextMenu.prototype = {
let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
clipboardHelper.copyString(string);
},
/**
* Copy the request form data parameters (or raw payload) from
* the currently selected item.
*/
- copyPostData: Task.async(function* () {
+ async copyPostData() {
let selected = this.selectedRequest;
// Try to extract any form data parameters.
- let formDataSections = yield getFormDataSections(
+ let formDataSections = await getFormDataSections(
selected.requestHeaders,
selected.requestHeadersFromUploadStream,
selected.requestPostData,
window.gNetwork.getString.bind(window.gNetwork));
let params = [];
formDataSections.forEach(section => {
let paramsArray = parseQueryString(section);
@@ -238,54 +237,54 @@ RequestListContextMenu.prototype = {
let string = params
.map(param => param.name + (param.value ? "=" + param.value : ""))
.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
// Fall back to raw payload.
if (!string) {
let postData = selected.requestPostData.postData.text;
- string = yield window.gNetwork.getString(postData);
+ string = await window.gNetwork.getString(postData);
if (Services.appinfo.OS !== "WINNT") {
string = string.replace(/\r/g, "");
}
}
clipboardHelper.copyString(string);
- }),
+ },
/**
* Copy a cURL command from the currently selected item.
*/
- copyAsCurl: Task.async(function* () {
+ async copyAsCurl() {
let selected = this.selectedRequest;
// Create a sanitized object for the Curl command generator.
let data = {
url: selected.url,
method: selected.method,
headers: [],
httpVersion: selected.httpVersion,
postDataText: null
};
// Fetch header values.
for (let { name, value } of selected.requestHeaders.headers) {
- let text = yield window.gNetwork.getString(value);
+ let text = await window.gNetwork.getString(value);
data.headers.push({ name: name, value: text });
}
// Fetch the request payload.
if (selected.requestPostData) {
let postData = selected.requestPostData.postData.text;
- data.postDataText = yield window.gNetwork.getString(postData);
+ data.postDataText = await window.gNetwork.getString(postData);
}
clipboardHelper.copyString(Curl.generateCommand(data));
- }),
+ },
/**
* Copy the raw request headers from the currently selected item.
*/
copyRequestHeaders() {
let rawHeaders = this.selectedRequest.requestHeaders.rawHeaders.trim();
if (Services.appinfo.OS !== "WINNT") {
rawHeaders = rawHeaders.replace(/\r/g, "");
--- a/devtools/client/netmonitor/request-list-tooltip.js
+++ b/devtools/client/netmonitor/request-list-tooltip.js
@@ -1,47 +1,46 @@
/* 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 { Task } = require("devtools/shared/task");
const {
setImageTooltip,
getImageDimensions,
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const { WEBCONSOLE_L10N } = require("./l10n");
const { formDataURI } = require("./request-utils");
// px
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
// px
const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600;
const HTML_NS = "http://www.w3.org/1999/xhtml";
-const setTooltipImageContent = Task.async(function* (tooltip, itemEl, requestItem) {
+async function setTooltipImageContent(tooltip, itemEl, requestItem) {
let { mimeType, text, encoding } = requestItem.responseContent.content;
if (!mimeType || !mimeType.includes("image/")) {
return false;
}
- let string = yield window.gNetwork.getString(text);
+ let string = await window.gNetwork.getString(text);
let src = formDataURI(mimeType, encoding, string);
let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
- let { naturalWidth, naturalHeight } = yield getImageDimensions(tooltip.doc, src);
+ let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
let options = { maxDim, naturalWidth, naturalHeight };
setImageTooltip(tooltip, tooltip.doc, src, options);
return itemEl.querySelector(".requests-menu-icon");
-});
+}
-const setTooltipStackTraceContent = Task.async(function* (tooltip, requestItem) {
+async function setTooltipStackTraceContent(tooltip, requestItem) {
let {stacktrace} = requestItem.cause;
if (!stacktrace || stacktrace.length == 0) {
return false;
}
let doc = tooltip.doc;
let el = doc.createElementNS(HTML_NS, "div");
@@ -94,14 +93,14 @@ const setTooltipStackTraceContent = Task
});
el.appendChild(frameEl);
}
tooltip.setContent(el, {width: REQUESTS_TOOLTIP_STACK_TRACE_WIDTH});
return true;
-});
+}
module.exports = {
setTooltipImageContent,
setTooltipStackTraceContent,
};
--- a/devtools/client/netmonitor/request-utils.js
+++ b/devtools/client/netmonitor/request-utils.js
@@ -1,74 +1,70 @@
/* 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/. */
/* eslint-disable mozilla/reject-some-requires */
"use strict";
-const { Task } = require("devtools/shared/task");
-
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
*
* @param {object} headers - the "requestHeaders".
* @param {object} uploadHeaders - the "requestHeadersFromUploadStream".
* @param {object} postData - the "requestPostData".
* @param {function} getString - callback to retrieve a string from a LongStringGrip.
* @return {array} a promise list that is resolved with the extracted form data.
*/
-const getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
- getString) {
+async function getFormDataSections(headers, uploadHeaders, postData, getString) {
let formDataSections = [];
let requestHeaders = headers.headers;
let payloadHeaders = uploadHeaders ? uploadHeaders.headers : [];
let allHeaders = [...payloadHeaders, ...requestHeaders];
let contentTypeHeader = allHeaders.find(e => {
return e.name.toLowerCase() == "content-type";
});
let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
- let contentType = yield getString(contentTypeLongString);
+ let contentType = await getString(contentTypeLongString);
if (contentType.includes("x-www-form-urlencoded")) {
let postDataLongString = postData.postData.text;
- let text = yield getString(postDataLongString);
+ let text = await getString(postDataLongString);
for (let section of text.split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
// isn't a line containing upload stream headers.
if (payloadHeaders.every(header => !section.startsWith(header.name))) {
formDataSections.push(section);
}
}
}
return formDataSections;
-});
+}
/**
* Fetch headers full content from actor server
*
* @param {object} headers - a object presents headers data
* @param {function} getString - callback to retrieve a string from a LongStringGrip
* @return {object} a headers object with updated content payload
*/
-const fetchHeaders = Task.async(function* (headers, getString) {
+async function fetchHeaders(headers, getString) {
for (let { value } of headers.headers) {
- headers.headers.value = yield getString(value);
+ headers.headers.value = await getString(value);
}
-
return headers;
-});
+}
/**
* Form a data: URI given a mime type, encoding, and some text.
*
* @param {string} mimeType - mime type
* @param {string} encoding - encoding to use; if not set, the
* text will be base64-encoded.
* @param {string} text - text of the URI.
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -244,26 +244,16 @@ function waitForNetworkEvents(aMonitor,
executeSoon(deferred.resolve);
}
}
awaitedEventsToListeners.forEach(([e, l]) => panel.on(EVENTS[e], l));
return deferred.promise;
}
-/**
- * Convert a store record (model) to the rendered element. Tests that need to use
- * this should be rewritten - test the rendered markup at unit level, integration
- * mochitest should check only the store state.
- */
-function getItemTarget(requestList, requestItem) {
- const items = requestList.mountPoint.querySelectorAll(".request-list-item");
- return [...items].find(el => el.dataset.id == requestItem.id);
-}
-
function verifyRequestItemTarget(document, requestList, requestItem, aMethod,
aUrl, aData = {}) {
info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource());
let visibleIndex = requestList.indexOf(requestItem);
info("Visible index of item: " + visibleIndex);
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -133,30 +133,27 @@
height: 19px !important;
}
.requests-menu-contents {
display: flex;
flex-direction: column;
overflow-x: hidden;
overflow-y: auto;
-
--timings-scale: 1;
--timings-rev-scale: 1;
-
- /* Devtools panel view height - tabbar height - toolbar height */
- height: calc(100vh - 48px);
}
.requests-menu-subitem {
display: flex;
flex: none;
box-sizing: border-box;
align-items: center;
padding: 3px;
+ cursor: default;
}
.subitem-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@@ -855,36 +852,46 @@
border-bottom: none;
border-inline-start: none;
}
.statistics-panel .splitter {
border-color: rgba(0,0,0,0.2);
cursor: default;
pointer-events: none;
- height: 100vh;
+ height: 100%;
}
.statistics-panel .charts-container {
display: flex;
width: 100%;
}
.statistics-panel .charts,
.statistics-panel .pie-table-chart-container {
width: 100%;
height: 100%;
}
+.pie-table-chart-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
.statistics-panel .pie-chart-container {
margin-inline-start: 3vw;
margin-inline-end: 1vw;
}
.statistics-panel .table-chart-container {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+ min-width: 240px;
margin-inline-start: 1vw;
margin-inline-end: 3vw;
}
.chart-colored-blob[name=html] {
fill: var(--theme-highlight-bluegrey);
background: var(--theme-highlight-bluegrey);
}
@@ -919,35 +926,47 @@
background: var(--theme-highlight-green);
}
.chart-colored-blob[name=flash] {
fill: var(--theme-highlight-red);
background: var(--theme-highlight-red);
}
+.table-chart-row {
+ display: flex;
+}
+
.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;
+ text-align: start;
}
.table-chart-row-label[name=size] {
width: 7em;
+ text-align: start;
}
.table-chart-row-label[name=time] {
width: 7em;
+ text-align: start;
+}
+
+.table-chart-totals {
+ display: flex;
+ flex-direction: column;
}
/* Firebug theme support for network charts */
.theme-firebug .chart-colored-blob[name=html] {
fill: rgba(94, 136, 176, 0.8); /* Blue-Grey highlight */
background: rgba(94, 136, 176, 0.8);
}
@@ -1036,21 +1055,23 @@
}
.requests-menu-waterfall {
display: none;
}
.statistics-panel .charts-container {
flex-direction: column;
+ /* Minus 4em for statistics back button width */
+ width: calc(100% - 4em);
}
.statistics-panel .splitter {
- width: 100vw;
- height: 0;
+ width: 100%;
+ height: 1px;
}
}
/* Platform overrides (copied in from the old platform specific files) */
:root[platform="win"] .requests-menu-header-button > .button-box {
padding: 0;
}
@@ -1250,28 +1271,28 @@
.editor-container,
.editor-mount,
.panel-container iframe {
border: none;
width: 100%;
height: 100%;
}
-.monitor-panel {
- width: 100vw;
- /* Devtools panel height - devtools toolbar height */
- height: calc(100vh - 24px);
-}
-
.network-details-panel {
width: 100%;
height: 100%;
overflow: hidden;
}
.split-box {
- width: 100vw;
+ overflow: hidden;
}
-.network-monitor {
- width: 100vw;
- height: 100vh;
+html,
+body,
+.root,
+.network-monitor,
+.monitor-panel {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ overflow: hidden;
}