--- a/devtools/client/netmonitor/index.js
+++ b/devtools/client/netmonitor/index.js
@@ -33,16 +33,17 @@ pref("devtools.netmonitor.har.jsonpCallb
pref("devtools.netmonitor.har.includeResponseBodies", true);
pref("devtools.netmonitor.har.compress", false);
pref("devtools.netmonitor.har.forceExport", false);
pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
pref("devtools.netmonitor.har.enableAutoExportToFile", false);
pref("devtools.netmonitor.persistlog", false);
pref("devtools.styleeditor.enabled", true);
+require("./src/assets/styles/fixed-data-table.css");
require("./src/assets/styles/netmonitor.css");
const EventEmitter = require("devtools-modules/src/utils/event-emitter");
EventEmitter.decorate(window);
const { configureStore } = require("./src/utils/create-store");
const App = require("./src/components/App");
const { Connector } = require("./src/connector/index");
--- a/devtools/client/netmonitor/package.json
+++ b/devtools/client/netmonitor/package.json
@@ -7,19 +7,21 @@
"description": "Network monitor in developer tools",
"dependencies": {
"codemirror": "^5.24.2",
"devtools-config": "=0.0.12",
"devtools-contextmenu": "=0.0.3",
"devtools-launchpad": "=0.0.103",
"devtools-modules": "=0.0.32",
"devtools-source-editor": "=0.0.3",
+ "fixed-data-table-2": "^0.8.5",
"immutable": "^3.8.1",
"jszip": "^3.1.3",
"react": "=15.6.1",
+ "react-dimensions": "^1.3.1",
"react-dom": "=15.6.1",
"react-redux": "=5.0.3",
"redux": "^3.6.0",
"reselect": "^3.0.1"
},
"devDependencies": {
"babel-register": "^6.24.0",
"file-loader": "^0.10.1"
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/assets/styles/fixed-data-table.css
@@ -0,0 +1,560 @@
+/**
+ * FixedDataTable v0.8.5
+ *
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableCellGroupLayout
+ */
+
+.fixedDataTableCellGroupLayout_cellGroup {
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ left: 0;
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ white-space: nowrap;
+}
+
+.fixedDataTableCellGroupLayout_cellGroup > .public_fixedDataTableCell_main {
+ display: inline-block;
+ vertical-align: top;
+ white-space: normal;
+}
+
+.fixedDataTableCellGroupLayout_cellGroupWrapper {
+ position: absolute;
+ top: 0;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableCellLayout
+ */
+
+.fixedDataTableCellLayout_main {
+ border-right-style: solid;
+ border-right-width: 1px;
+ border-width: 0 1px 0 0;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ display: block;
+ overflow: hidden;
+ position: absolute;
+ white-space: normal;
+}
+
+.fixedDataTableCellLayout_lastChild {
+ border-width: 0 1px 1px 0;
+}
+
+.fixedDataTableCellLayout_alignRight {
+ text-align: right;
+}
+
+.fixedDataTableCellLayout_alignCenter {
+ text-align: center;
+}
+
+.fixedDataTableCellLayout_wrap1 {
+ display: table;
+}
+
+.fixedDataTableCellLayout_wrap2 {
+ display: table-row;
+}
+
+.fixedDataTableCellLayout_wrap3 {
+ display: table-cell;
+ vertical-align: middle;
+}
+
+.fixedDataTableCellLayout_columnResizerContainer {
+ position: absolute;
+ right: 0px;
+ width: 6px;
+ z-index: 1;
+}
+
+.fixedDataTableCellLayout_columnResizerContainer:hover {
+ cursor: ew-resize;
+}
+
+.fixedDataTableCellLayout_columnResizerContainer:hover .fixedDataTableCellLayout_columnResizerKnob {
+ visibility: visible;
+}
+
+.fixedDataTableCellLayout_columnResizerKnob {
+ position: absolute;
+ right: 0px;
+ visibility: hidden;
+ width: 4px;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableColumnResizerLineLayout
+ */
+
+.fixedDataTableColumnResizerLineLayout_mouseArea {
+ cursor: ew-resize;
+ position: absolute;
+ right: -5px;
+ width: 12px;
+}
+
+.fixedDataTableColumnResizerLineLayout_main {
+ border-right-style: solid;
+ border-right-width: 1px;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ position: absolute;
+ z-index: 10;
+}
+
+body[dir="rtl"] .fixedDataTableColumnResizerLineLayout_main {
+ /* the resizer line is in the wrong position in RTL with no easy fix.
+ * Disabling is more useful than displaying it.
+ * #167 (github) should look into this and come up with a permanent fix.
+ */
+ display: none !important;
+}
+
+.fixedDataTableColumnResizerLineLayout_hiddenElem {
+ display: none !important;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableLayout
+ */
+
+.fixedDataTableLayout_main {
+ border-style: solid;
+ border-width: 1px;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ overflow: hidden;
+ position: relative;
+}
+
+.fixedDataTableLayout_header,
+.fixedDataTableLayout_hasBottomBorder {
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+}
+
+.fixedDataTableLayout_footer .public_fixedDataTableCell_main {
+ border-top-style: solid;
+ border-top-width: 1px;
+}
+
+.fixedDataTableLayout_topShadow,
+.fixedDataTableLayout_bottomShadow {
+ height: 4px;
+ left: 0;
+ position: absolute;
+ right: 0;
+ z-index: 1;
+}
+
+.fixedDataTableLayout_bottomShadow {
+ margin-top: -4px;
+}
+
+.fixedDataTableLayout_rowsContainer {
+ overflow: hidden;
+ position: relative;
+}
+
+.fixedDataTableLayout_horizontalScrollbar {
+ bottom: 0;
+ position: absolute;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableRowLayout
+ */
+
+.fixedDataTableRowLayout_main {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+}
+
+.fixedDataTableRowLayout_body {
+ left: 0;
+ position: absolute;
+ top: 0;
+}
+
+.fixedDataTableRowLayout_rowExpanded {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ left: 0;
+ position: absolute;
+}
+
+.fixedDataTableRowLayout_fixedColumnsDivider {
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ border-left-style: solid;
+ border-left-width: 1px;
+ left: 0;
+ position: absolute;
+ top: 0;
+ width: 0;
+}
+
+.fixedDataTableRowLayout_columnsShadow {
+ position: absolute;
+ width: 4px;
+}
+
+.fixedDataTableRowLayout_columnsRightShadow {
+ right: 1px;
+}
+
+.fixedDataTableRowLayout_rowWrapper {
+ position: absolute;
+ top: 0;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule ScrollbarLayout
+ */
+
+.ScrollbarLayout_main {
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ outline: none;
+ overflow: hidden;
+ position: absolute;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.ScrollbarLayout_mainVertical {
+ bottom: 0;
+ right: 0;
+ top: 0;
+ width: 15px;
+}
+
+.ScrollbarLayout_mainHorizontal {
+ bottom: 0;
+ height: 15px;
+ left: 0;
+ -webkit-transition-property: background-color height;
+ transition-property: background-color height;
+}
+
+/* Touching the scroll-track directly makes the scroll-track bolder */
+.ScrollbarLayout_mainHorizontal.public_Scrollbar_mainActive,
+.ScrollbarLayout_mainHorizontal:hover {
+ height: 17px;
+}
+
+.ScrollbarLayout_face {
+ left: 0;
+ overflow: hidden;
+ position: absolute;
+ z-index: 1;
+ -webkit-transition-duration: 250ms;
+ transition-duration: 250ms;
+ -webkit-transition-timing-function: ease;
+ transition-timing-function: ease;
+ -webkit-transition-property: background-color width position;
+ transition-property: background-color width position;
+}
+
+/**
+ * This selector renders the "nub" of the scrollface. The nub must
+ * be rendered as pseudo-element so that it won't receive any UI events then
+ * we can get the correct `event.offsetX` and `event.offsetY` from the
+ * scrollface element while dragging it.
+ */
+.ScrollbarLayout_face:after {
+ border-radius: 6px;
+ content: '';
+ display: block;
+ position: absolute;
+ -webkit-transition: background-color 250ms ease;
+ transition: background-color 250ms ease;
+}
+
+.ScrollbarLayout_faceHorizontal {
+ bottom: 0;
+ left: 0;
+ top: 0;
+}
+
+.ScrollbarLayout_faceHorizontal:after {
+ bottom: 4px;
+ left: 0;
+ top: 4px;
+ width: 100%;
+}
+
+.ScrollbarLayout_faceHorizontal.public_Scrollbar_faceActive:after,
+.ScrollbarLayout_main:hover .ScrollbarLayout_faceHorizontal:after {
+ bottom: calc(4px/2);
+}
+
+.ScrollbarLayout_faceVertical {
+ left: 0;
+ right: 0;
+ top: 0;
+}
+
+.ScrollbarLayout_faceVertical:after {
+ height: 100%;
+ left: 4px;
+ right: 4px;
+ top: 0;
+}
+
+.ScrollbarLayout_main:hover .ScrollbarLayout_faceVertical:after,
+.ScrollbarLayout_faceVertical.public_Scrollbar_faceActive:after {
+ left: calc(4px/2);
+ right: calc(4px/2);
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTable
+ *
+ */
+
+/**
+ * Table.
+ */
+.public_fixedDataTable_main {
+ border-color: #d3d3d3;
+}
+
+.public_fixedDataTable_header,
+.public_fixedDataTable_hasBottomBorder {
+ border-color: #d3d3d3;
+}
+
+.public_fixedDataTable_header .public_fixedDataTableCell_main {
+ font-weight: bold;
+}
+
+.public_fixedDataTable_header,
+.public_fixedDataTable_header .public_fixedDataTableCell_main {
+ background-color: #f6f7f8;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#efefef));
+ background-image: linear-gradient(#fff, #efefef);
+}
+
+.public_fixedDataTable_footer .public_fixedDataTableCell_main {
+ background-color: #f6f7f8;
+ border-color: #d3d3d3;
+}
+
+.public_fixedDataTable_topShadow {
+ background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAF0lEQVR4AWPUkNeSBhHCjJoK2twgFisAFagCCp3pJlAAAAAASUVORK5CYII=) repeat-x;
+}
+
+.public_fixedDataTable_bottomShadow {
+ background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAHElEQVQI12MwNjZmZdAT1+Nm0JDWEGZQk1GTBgAWkwIeAEp52AAAAABJRU5ErkJggg==) repeat-x;
+}
+
+.public_fixedDataTable_horizontalScrollbar .public_Scrollbar_mainHorizontal {
+ background-color: #fff;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableCell
+ */
+
+/**
+ * Table cell.
+ */
+.public_fixedDataTableCell_main {
+ background-color: #fff;
+ border-color: #d3d3d3;
+}
+
+.public_fixedDataTableCell_highlighted {
+ background-color: #f4f4f4;
+}
+
+.public_fixedDataTableCell_cellContent {
+ padding: 8px;
+}
+
+.public_fixedDataTableCell_columnResizerKnob {
+ background-color: #0284ff;
+}
+.public_fixedDataTableCell_hasReorderHandle .public_fixedDataTableCell_cellContent {
+ margin-left: 12px;
+}
+/**
+ * Column reorder goodies.
+ */
+.fixedDataTableCellLayout_columnReorderContainer {
+ border-color: #0284ff;
+ background-color: rgba(0,0,0,0.1);
+ width: 12px;
+ margin-right: -12px;
+ float: left;
+ cursor: move;
+}
+.fixedDataTableCellLayout_columnReorderContainer:after {
+ content: '::';
+ position: absolute;
+ top: 50%;
+ left: 1px;
+ -webkit-transform: translateY(-50%);
+ transform: translateY(-50%);
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableColumnResizerLine
+ *
+ */
+
+/**
+ * Column resizer line.
+ */
+.public_fixedDataTableColumnResizerLine_main {
+ border-color: #0284ff;
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule fixedDataTableRow
+ */
+
+/**
+ * Table row.
+ */
+.public_fixedDataTableRow_main {
+ background-color: #fff;
+}
+
+.public_fixedDataTableRow_highlighted,
+.public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main {
+ background-color: #f6f7f8;
+}
+
+.public_fixedDataTableRow_fixedColumnsDivider {
+ border-color: #d3d3d3;
+}
+
+.public_fixedDataTableRow_columnsShadow {
+ background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAYAAAD5PA/NAAAAFklEQVQIHWPSkNeSBmJhTQVtbiDNCgASagIIuJX8OgAAAABJRU5ErkJggg==) repeat-y;
+}
+
+.public_fixedDataTableRow_columnsRightShadow {
+ -webkit-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+/**
+ * Copyright Schrodinger, LLC
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @providesModule Scrollbar
+ *
+ */
+
+/**
+ * Scrollbars.
+ */
+
+/* Touching the scroll-track directly makes the scroll-track bolder */
+.public_Scrollbar_main.public_Scrollbar_mainActive,
+.public_Scrollbar_main {
+ background-color: #fff;
+ border-left: 1px solid #d3d3d3;
+}
+
+.public_Scrollbar_mainOpaque,
+.public_Scrollbar_mainOpaque.public_Scrollbar_mainActive,
+.public_Scrollbar_mainOpaque:hover {
+ background-color: #fff;
+}
+
+.public_Scrollbar_face:after {
+ background-color: #c2c2c2;
+}
+
+.public_Scrollbar_main:hover .public_Scrollbar_face:after,
+.public_Scrollbar_mainActive .public_Scrollbar_face:after,
+.public_Scrollbar_faceActive:after {
+ background-color: #7d7d7d;
+}
--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
@@ -194,24 +194,16 @@ body,
.requests-list-reload-notice-button {
font-size: inherit;
min-height: 26px;
margin: 0 5px;
}
/* Requests list table */
-.request-list-container {
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
- overflow: hidden;
-}
-
.requests-list-wrapper {
width: 100%;
height: 100%;
}
.requests-list-table {
display: table;
position: relative;
@@ -228,25 +220,23 @@ body,
right: 0;
overflow-x: hidden;
overflow-y: auto;
--timings-scale: 1;
--timings-rev-scale: 1;
}
.requests-list-column {
- display: table-cell;
cursor: default;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
- max-width: 50px;
- min-width: 50px;
+ line-height: 24px;
}
.requests-list-column > * {
display: inline-block;
}
.theme-firebug .requests-list-column {
padding: 1px;
@@ -268,16 +258,21 @@ body,
padding: 0;
width: 100%;
}
.requests-list-headers .requests-list-column:first-child .requests-list-header-button {
border-width: 0;
}
+.requests-list-header {
+ line-height: 24px;
+ text-align: center;
+}
+
.requests-list-header-button {
background-color: transparent;
border-image: linear-gradient(transparent 15%,
var(--theme-splitter-color) 15%,
var(--theme-splitter-color) 85%,
transparent 85%) 1 1;
border-width: 0;
border-inline-start-width: 1px;
@@ -365,17 +360,16 @@ body,
transparent);
}
/* Requests list column */
/* Status column */
.requests-list-status {
- width: 8%;
/* Don't ellipsize status codes */
text-overflow: initial;
}
.theme-firebug .requests-list-status {
font-weight: bold;
}
@@ -435,117 +429,45 @@ body,
.requests-list-status-icon[data-code^="5"] {
background-color: var(--theme-highlight-pink);
border-radius: 0;
transform: rotate(45deg);
}
/* Method column */
-.requests-list-method {
- width: 8%;
-}
-
.theme-firebug .requests-list-method {
color: rgb(128, 128, 128);
}
/* File column */
-.requests-list-file {
- width: 22%;
-}
-
.requests-list-file.requests-list-column {
text-align: start;
}
.requests-list-icon {
background: transparent;
width: 15px;
height: 15px;
margin: 0 4px;
outline: 1px solid var(--table-splitter-color);
- vertical-align: top;
-}
-
-/* Protocol column */
-
-.requests-list-protocol {
- width: 8%;
-}
-
-/* Cookies column */
-
-.requests-list-cookies {
- width: 6%;
-}
-
-/* Set Cookies column */
-
-.requests-list-set-cookies {
- width: 8%;
-}
-
-/* Scheme column */
-
-.requests-list-scheme {
- width: 8%;
-}
-
-/* Domain column */
-
-.requests-list-domain {
- width: 13%;
-}
-
-/* Start Time column */
-
-.requests-list-start-time {
- width: 8%;
-}
-
-/* End Time column */
-
-.requests-list-end-time {
- width: 8%;
-}
-
-/* Response Time column */
-
-.requests-list-response-time {
- width: 10%;
-}
-
-/* Duration column */
-
-.requests-list-duration {
- width: 8%;
-}
-
-/* Latency column */
-
-.requests-list-latency {
- width: 8%;
-}
-
-.requests-list-response-header {
- width: 13%;
+ vertical-align: middle;
}
.requests-list-domain.requests-list-column {
text-align: start;
}
.requests-security-state-icon {
display: inline-block;
width: 16px;
height: 16px;
margin: 0 4px;
- vertical-align: top;
+ vertical-align: middle;
}
.request-list-item.selected .requests-security-state-icon {
filter: brightness(1.3);
}
.security-state-insecure {
background-image: url(chrome://devtools/skin/images/security-state-insecure.svg);
@@ -562,59 +484,31 @@ body,
.security-state-broken {
background-image: url(chrome://devtools/skin/images/security-state-broken.svg);
}
.security-state-local {
background-image: url(chrome://devtools/skin/images/globe.svg);
}
-/* RemoteIP column */
-
-.requests-list-remoteip {
- width: 9%;
-}
-
-/* Cause column */
-
-.requests-list-cause {
- width: 9%;
-}
-
.requests-list-cause-stack {
display: inline-block;
background-color: var(--theme-body-color-alt);
color: var(--theme-body-background);
font-size: 8px;
font-weight: bold;
line-height: 10px;
border-radius: 3px;
padding: 0 2px;
margin: 0;
margin-inline-end: 3px;
}
-/* Type column */
-
-.requests-list-type {
- width: 6%;
-}
-
-/* Transferred column */
-
-.requests-list-transferred {
- width: 9%;
-}
-
/* Size column */
-.requests-list-size {
- width: 7%;
-}
-
.theme-firebug .requests-list-size {
justify-content: end;
padding-inline-end: 4px;
}
/* Waterfall column */
.requests-list-waterfall {
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -10,17 +10,16 @@ const {
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Actions = require("../actions/index");
const { updateFormDataSections } = require("../utils/request-utils");
-const { getSelectedRequest } = require("../selectors/index");
// Components
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
const NetworkDetailsPanel = createFactory(require("./NetworkDetailsPanel"));
const RequestList = createFactory(require("./RequestList"));
const Toolbar = createFactory(require("./Toolbar"));
const { div } = DOM;
const MediaQueryList = window.matchMedia("(min-width: 700px)");
@@ -28,20 +27,18 @@ const MediaQueryList = window.matchMedia
/**
* Monitor panel component
* The main panel for displaying various network request information
*/
class MonitorPanel extends Component {
static get propTypes() {
return {
connector: PropTypes.object.isRequired,
- isEmpty: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
openNetworkDetails: PropTypes.func.isRequired,
- request: PropTypes.object,
sourceMapService: PropTypes.object,
openLink: PropTypes.func,
updateRequest: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
@@ -80,17 +77,16 @@ class MonitorPanel extends Component {
this.setState({
isVerticalSpliter: MediaQueryList.matches,
});
}
render() {
let {
connector,
- isEmpty,
networkDetailsOpen,
openLink,
sourceMapService,
} = this.props;
let initialWidth = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-width");
let initialHeight = Services.prefs.getIntPref(
@@ -101,17 +97,17 @@ class MonitorPanel extends Component {
Toolbar(),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${initialWidth}px`,
initialHeight: `${initialHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
- startPanel: RequestList({ isEmpty, connector }),
+ startPanel: RequestList({ connector }),
endPanel: networkDetailsOpen && NetworkDetailsPanel({
ref: "endPanel",
connector,
openLink,
sourceMapService,
}),
endPanelCollapsed: !networkDetailsOpen,
endPanelControl: true,
@@ -119,17 +115,15 @@ class MonitorPanel extends Component {
}),
)
);
}
}
module.exports = connect(
(state) => ({
- isEmpty: state.requests.requests.isEmpty(),
networkDetailsOpen: state.ui.networkDetailsOpen,
- request: getSelectedRequest(state),
}),
(dispatch) => ({
openNetworkDetails: (open) => dispatch(Actions.openNetworkDetails(open)),
updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
}),
)(MonitorPanel);
--- a/devtools/client/netmonitor/src/components/RequestList.js
+++ b/devtools/client/netmonitor/src/components/RequestList.js
@@ -1,42 +1,158 @@
/* 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 {
+ Component,
+ PropTypes,
createFactory,
- DOM,
- PropTypes,
} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const Dimensions = require("react-dimensions");
+const {
+ getDisplayedRequests,
+ getWaterfallScale,
+} = require("../selectors/index");
// Components
-const RequestListContent = createFactory(require("./RequestListContent"));
+const Table = createFactory(require("fixed-data-table-2").Table);
+const Column = createFactory(require("fixed-data-table-2").Column);
+const RequestListCellStatus = createFactory(require("./RequestListCellStatus"));
+const RequestListCellMethod = createFactory(require("./RequestListCellMethod"));
+const RequestListCellFile = createFactory(require("./RequestListCellFile"));
+const RequestListCellScheme = createFactory(require("./RequestListCellScheme"));
+const RequestListCellCause = createFactory(require("./RequestListCellCause"));
+const RequestListCellType = createFactory(require("./RequestListCellType"));
+const RequestListCellDomain = createFactory(require("./RequestListCellDomain"));
const RequestListEmptyNotice = createFactory(require("./RequestListEmptyNotice"));
-const StatusBar = createFactory(require("./StatusBar"));
-
-const { div } = DOM;
+const RequestListHeader = createFactory(require("./RequestListHeader"));
/**
* Request panel component
*/
-function RequestList({
- connector,
- isEmpty,
-}) {
- return (
- div({ className: "request-list-container" },
- isEmpty ? RequestListEmptyNotice({ connector }) : RequestListContent({ connector }),
- StatusBar({ connector }),
- )
- );
+class RequestList extends Component {
+ static get propTypes() {
+ return {
+ connector: PropTypes.object.isRequired,
+ containerHeight: PropTypes.number.isRequired,
+ containerWidth: PropTypes.number.isRequired,
+ isEmpty: PropTypes.bool.isRequired,
+ requests: PropTypes.object.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ columnWidths: {
+ status: 100,
+ method: 100,
+ file: 300,
+ scheme: 100,
+ cause: 100,
+ type: 100,
+ domain: 150,
+ },
+ };
+
+ this.onColumnResizeEndCallback = this.onColumnResizeEndCallback.bind(this);
+ }
+
+ onColumnResizeEndCallback(newColumnWidth, columnKey) {
+ this.setState(({
+ columnWidths: Object.assign(this.state.columnWidths, {
+ [columnKey]: newColumnWidth,
+ })
+ }));
+ }
+
+ render() {
+ let {
+ connector,
+ containerHeight,
+ containerWidth,
+ isEmpty,
+ requests,
+ } = this.props;
+ let { columnWidths } = this.state;
+
+ return (
+ isEmpty ?
+ RequestListEmptyNotice({ connector })
+ :
+ Table({
+ headerHeight: 24,
+ height: containerHeight,
+ width: containerWidth,
+ rowHeight: 24,
+ rowsCount: requests.size,
+ onColumnResizeEndCallback: this.onColumnResizeEndCallback,
+ },
+ Column({
+ columnKey: "status",
+ header: RequestListHeader(),
+ cell: RequestListCellStatus({ requests }),
+ isResizable: true,
+ width: columnWidths.status,
+ }),
+ Column({
+ columnKey: "method",
+ header: RequestListHeader(),
+ cell: RequestListCellMethod({ requests }),
+ isResizable: true,
+ width: columnWidths.method,
+ }),
+ Column({
+ columnKey: "file",
+ header: RequestListHeader(),
+ cell: RequestListCellFile({ requests }),
+ isResizable: true,
+ width: columnWidths.file,
+ }),
+ Column({
+ columnKey: "scheme",
+ header: RequestListHeader(),
+ cell: RequestListCellScheme({ requests }),
+ isResizable: true,
+ width: columnWidths.scheme,
+ }),
+ Column({
+ columnKey: "cause",
+ header: RequestListHeader(),
+ cell: RequestListCellCause({ requests }),
+ isResizable: true,
+ width: columnWidths.cause,
+ }),
+ Column({
+ columnKey: "type",
+ header: RequestListHeader(),
+ cell: RequestListCellType({ requests }),
+ isResizable: true,
+ width: columnWidths.type,
+ }),
+ Column({
+ columnKey: "domain",
+ header: RequestListHeader(),
+ cell: RequestListCellDomain({ requests }),
+ flexGrow: 1,
+ isResizable: true,
+ width: columnWidths.domain,
+ }),
+ )
+ );
+ }
}
-RequestList.displayName = "RequestList";
-
-RequestList.propTypes = {
- connector: PropTypes.object.isRequired,
- isEmpty: PropTypes.bool.isRequired,
-};
-
-module.exports = RequestList;
+module.exports = connect(
+ (state) => ({
+ columns: state.ui.columns,
+ isEmpty: state.requests.requests.isEmpty(),
+ requests: getDisplayedRequests(state),
+ firstRequestStartedMillis: state.requests.firstStartedMillis,
+ selectedRequestId: state.requests.selectedId,
+ scale: getWaterfallScale(state),
+ }),
+)(Dimensions()(RequestList));
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellCause.js
@@ -0,0 +1,54 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+
+const { div } = DOM;
+
+class RequestListColumnCause extends Component {
+ static get propTypes() {
+ return {
+ onCauseBadgeMouseDown: PropTypes.func.isRequired,
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ };
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return requests.get(rowIndex).cause !== nextProps.requests.get(rowIndex).cause;
+ }
+
+ render() {
+ let { onCauseBadgeMouseDown, requests, rowIndex } = this.props;
+ let { cause } = requests.get(rowIndex);
+
+ let causeType = "unknown";
+ let causeHasStack = false;
+
+ if (cause) {
+ // Legacy server might send a numeric value. Display it as "unknown"
+ causeType = typeof cause.type === "string" ? cause.type : "unknown";
+ causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
+ }
+
+ return (
+ div({ className: "requests-list-column requests-list-cause", title: causeType },
+ causeHasStack && div({
+ className: "requests-list-cause-stack",
+ onMouseDown: onCauseBadgeMouseDown,
+ }, "JS"),
+ causeType,
+ )
+ );
+ }
+}
+
+module.exports = RequestListColumnCause;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellDomain.js
@@ -0,0 +1,73 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { getFormattedIPAndPort } = require("../utils/format-utils");
+const { L10N } = require("../utils/l10n");
+const { propertiesEqual } = require("../utils/request-utils");
+
+const { div } = DOM;
+
+const UPDATED_DOMAIN_PROPS = [
+ "remoteAddress",
+ "securityState",
+ "urlDetails",
+];
+
+class RequestListColumnDomain extends Component {
+ static get propTypes() {
+ return {
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ onSecurityIconMouseDown: PropTypes.func.isRequired,
+ };
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return !propertiesEqual(UPDATED_DOMAIN_PROPS,
+ requests.get(rowIndex), nextProps.requests.get(rowIndex));
+ }
+
+ render() {
+ let { onSecurityIconMouseDown, requests, rowIndex } = this.props;
+ let {
+ remoteAddress,
+ remotePort,
+ securityState,
+ urlDetails: { host, isLocal },
+ } = requests.get(rowIndex);
+ let iconClassList = ["requests-security-state-icon"];
+ let iconTitle;
+ let title = host + (remoteAddress ?
+ ` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
+
+ if (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}`);
+ }
+
+ return (
+ div({ className: "requests-list-column requests-list-domain", title },
+ div({
+ className: iconClassList.join(" "),
+ onMouseDown: onSecurityIconMouseDown,
+ title: iconTitle,
+ }),
+ host,
+ )
+ );
+ }
+}
+
+module.exports = RequestListColumnDomain;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellFile.js
@@ -0,0 +1,56 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { propertiesEqual } = require("../utils/request-utils");
+
+const { div, img } = DOM;
+
+const UPDATED_FILE_PROPS = [
+ "responseContentDataUri",
+ "urlDetails",
+];
+
+class RequestListColumnFile extends Component {
+ static get propTypes() {
+ return {
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ onThumbnailMouseDown: PropTypes.func.isRequired,
+ };
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return !propertiesEqual(UPDATED_FILE_PROPS,
+ requests.get(rowIndex), nextProps.requests.get(rowIndex));
+ }
+
+ render() {
+ let { onThumbnailMouseDown, requests, rowIndex } = this.props;
+ let { responseContentDataUri, urlDetails } = requests.get(rowIndex);
+
+ return (
+ div({
+ className: "requests-list-column requests-list-file",
+ title: urlDetails.unicodeUrl,
+ },
+ img({
+ className: "requests-list-icon",
+ src: responseContentDataUri,
+ onMouseDown: onThumbnailMouseDown,
+ }),
+ urlDetails.baseNameWithQuery
+ )
+ );
+ }
+}
+
+module.exports = RequestListColumnFile;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellMethod.js
@@ -0,0 +1,35 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+
+const { div } = DOM;
+
+class RequestListColumnMethod extends Component {
+ static get propTypes() {
+ return {
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ };
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return requests.get(rowIndex).method !== nextProps.requests.get(rowIndex).method;
+ }
+
+ render() {
+ let { requests, rowIndex } = this.props;
+ let { method } = requests.get(rowIndex);
+ return div({ className: "requests-list-column requests-list-method" }, method);
+ }
+}
+
+module.exports = RequestListColumnMethod;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellScheme.js
@@ -0,0 +1,44 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+
+const { div } = DOM;
+
+class RequestListColumnScheme extends Component {
+ static get propTypes() {
+ return {
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ };
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return requests.get(rowIndex).urlDetails.scheme !==
+ nextProps.requests.get(rowIndex).urlDetails.scheme;
+ }
+
+ render() {
+ let { requests, rowIndex } = this.props;
+ let { urlDetails } = requests.get(rowIndex);
+
+ return (
+ div({
+ className: "requests-list-column requests-list-scheme",
+ title: urlDetails.scheme,
+ },
+ urlDetails.scheme,
+ )
+ );
+ }
+}
+
+module.exports = RequestListColumnScheme;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellStatus.js
@@ -0,0 +1,91 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { L10N } = require("../utils/l10n");
+const { propertiesEqual } = require("../utils/request-utils");
+
+const { div } = DOM;
+
+const UPDATED_STATUS_PROPS = [
+ "fromCache",
+ "fromServiceWorker",
+ "status",
+ "statusText",
+];
+
+class RequestListColumnStatus extends Component {
+ static get propTypes() {
+ return {
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onMouseOver = this.onMouseOver.bind(this);
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return !propertiesEqual(UPDATED_STATUS_PROPS,
+ requests.get(rowIndex), nextProps.requests.get(rowIndex));
+ }
+
+ onMouseOver({ target }) {
+ let { requests, rowIndex } = this.props;
+ let { fromCache, fromServiceWorker, status, statusText } = requests.get(rowIndex);
+
+ if (status && statusText && !target.title) {
+ if (fromCache && fromServiceWorker) {
+ target.title = L10N.getFormatStr("netmonitor.status.tooltip.cachedworker",
+ status, statusText);
+ } else if (fromCache) {
+ target.title = L10N.getFormatStr("netmonitor.status.tooltip.cached",
+ status, statusText);
+ } else if (fromServiceWorker) {
+ target.title = L10N.getFormatStr("netmonitor.status.tooltip.worker",
+ status, statusText);
+ } else {
+ target.title = L10N.getFormatStr("netmonitor.status.tooltip.simple",
+ status, statusText);
+ }
+ }
+ }
+
+ render() {
+ let { requests, rowIndex } = this.props;
+ let { fromCache, fromServiceWorker, status } = requests.get(rowIndex);
+ let code;
+
+ if (status) {
+ if (fromCache) {
+ code = "cached";
+ } else if (fromServiceWorker) {
+ code = "service worker";
+ } else {
+ code = status;
+ }
+ }
+
+ return (
+ div({
+ className: "requests-list-column requests-list-status",
+ onMouseOver: this.onMouseOver,
+ },
+ div({ className: "requests-list-status-icon", "data-code": code }),
+ div({ className: "requests-list-status-code" }, status)
+ )
+ );
+ }
+}
+
+module.exports = RequestListColumnStatus;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListCellType.js
@@ -0,0 +1,49 @@
+/* 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 {
+ Component,
+ DOM,
+ PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { getAbbreviatedMimeType } = require("../utils/request-utils");
+
+const { div } = DOM;
+
+class RequestListColumnType extends Component {
+ static get propTypes() {
+ return {
+ requests: PropTypes.object.isRequired,
+ rowIndex: PropTypes.number,
+ };
+ }
+
+ shouldComponentUpdate(nextProps) {
+ let { requests, rowIndex } = this.props;
+ return requests.get(rowIndex).mimeType !== nextProps.requests.get(rowIndex).mimeType;
+ }
+
+ render() {
+ let { requests, rowIndex } = this.props;
+ let { mimeType } = requests.get(rowIndex);
+ let abbrevType;
+
+ if (mimeType) {
+ abbrevType = getAbbreviatedMimeType(mimeType);
+ }
+
+ return (
+ div({
+ className: "requests-list-column requests-list-type",
+ title: mimeType,
+ },
+ abbrevType
+ )
+ );
+ }
+}
+
+module.exports = RequestListColumnType;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnCause.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-
-const { div } = DOM;
-
-class RequestListColumnCause extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- onCauseBadgeMouseDown: PropTypes.func.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return this.props.item.cause !== nextProps.item.cause;
- }
-
- render() {
- let {
- item: { cause },
- onCauseBadgeMouseDown,
- } = this.props;
-
- let causeType = "unknown";
- let causeHasStack = false;
-
- if (cause) {
- // Legacy server might send a numeric value. Display it as "unknown"
- causeType = typeof cause.type === "string" ? cause.type : "unknown";
- causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
- }
-
- return (
- div({ className: "requests-list-column requests-list-cause", title: causeType },
- causeHasStack && div({
- className: "requests-list-cause-stack",
- onMouseDown: onCauseBadgeMouseDown,
- }, "JS"),
- causeType
- )
- );
- }
-}
-
-module.exports = RequestListColumnCause;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnDomain.js
+++ /dev/null
@@ -1,66 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-const { getFormattedIPAndPort } = require("../utils/format-utils");
-const { L10N } = require("../utils/l10n");
-const { propertiesEqual } = require("../utils/request-utils");
-
-const { div } = DOM;
-
-const UPDATED_DOMAIN_PROPS = [
- "remoteAddress",
- "securityState",
- "urlDetails",
-];
-
-class RequestListColumnDomain extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- onSecurityIconMouseDown: PropTypes.func.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
- }
-
- render() {
- let { item, onSecurityIconMouseDown } = this.props;
- let { remoteAddress, remotePort, securityState,
- urlDetails: { host, isLocal } } = item;
- let iconClassList = ["requests-security-state-icon"];
- let iconTitle;
- let title = host + (remoteAddress ?
- ` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
-
- if (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}`);
- }
-
- return (
- div({ className: "requests-list-column requests-list-domain", title },
- div({
- className: iconClassList.join(" "),
- onMouseDown: onSecurityIconMouseDown,
- title: iconTitle,
- }),
- host,
- )
- );
- }
-}
-
-module.exports = RequestListColumnDomain;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnFile.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-const { propertiesEqual } = require("../utils/request-utils");
-
-const { div, img } = DOM;
-
-const UPDATED_FILE_PROPS = [
- "responseContentDataUri",
- "urlDetails",
-];
-
-class RequestListColumnFile extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- onThumbnailMouseDown: PropTypes.func.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
- }
-
- render() {
- let {
- item: { responseContentDataUri, urlDetails },
- onThumbnailMouseDown
- } = this.props;
-
- return (
- div({
- className: "requests-list-column requests-list-file",
- title: urlDetails.unicodeUrl,
- },
- img({
- className: "requests-list-icon",
- src: responseContentDataUri,
- onMouseDown: onThumbnailMouseDown,
- }),
- urlDetails.baseNameWithQuery
- )
- );
- }
-}
-
-module.exports = RequestListColumnFile;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnMethod.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-
-const { div } = DOM;
-
-class RequestListColumnMethod extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return this.props.item.method !== nextProps.item.method;
- }
-
- render() {
- let { method } = this.props.item;
- return div({ className: "requests-list-column requests-list-method" }, method);
- }
-}
-
-module.exports = RequestListColumnMethod;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnScheme.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-
-const { div } = DOM;
-
-class RequestListColumnScheme extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return this.props.item.urlDetails.scheme !== nextProps.item.urlDetails.scheme;
- }
-
- render() {
- const { urlDetails } = this.props.item;
- return (
- div({
- className: "requests-list-column requests-list-scheme",
- title: urlDetails.scheme
- },
- urlDetails.scheme
- )
- );
- }
-}
-
-module.exports = RequestListColumnScheme;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnStatus.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-const { L10N } = require("../utils/l10n");
-const { propertiesEqual } = require("../utils/request-utils");
-
-const { div } = DOM;
-
-const UPDATED_STATUS_PROPS = [
- "fromCache",
- "fromServiceWorker",
- "status",
- "statusText",
-];
-
-class RequestListColumnStatus extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
- }
-
- render() {
- let { item } = this.props;
- let { fromCache, fromServiceWorker, status, statusText } = item;
- let code;
-
- if (status) {
- if (fromCache) {
- code = "cached";
- } else if (fromServiceWorker) {
- code = "service worker";
- } else {
- code = status;
- }
- }
-
- return (
- div({
- className: "requests-list-column requests-list-status",
- onMouseOver: function ({ target }) {
- if (status && statusText && !target.title) {
- target.title = getColumnTitle(item);
- }
- },
- },
- div({ className: "requests-list-status-icon", "data-code": code }),
- div({ className: "requests-list-status-code" }, status)
- )
- );
- }
-}
-
-function getColumnTitle(item) {
- let { fromCache, fromServiceWorker, status, statusText } = item;
- let title;
- if (fromCache && fromServiceWorker) {
- title = L10N.getFormatStr("netmonitor.status.tooltip.cachedworker",
- status, statusText);
- } else if (fromCache) {
- title = L10N.getFormatStr("netmonitor.status.tooltip.cached",
- status, statusText);
- } else if (fromServiceWorker) {
- title = L10N.getFormatStr("netmonitor.status.tooltip.worker",
- status, statusText);
- } else {
- title = L10N.getFormatStr("netmonitor.status.tooltip.simple",
- status, statusText);
- }
- return title;
-}
-
-module.exports = RequestListColumnStatus;
deleted file mode 100644
--- a/devtools/client/netmonitor/src/components/RequestListColumnType.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* 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 {
- Component,
- DOM,
- PropTypes,
-} = require("devtools/client/shared/vendor/react");
-const { getAbbreviatedMimeType } = require("../utils/request-utils");
-
-const { div } = DOM;
-
-class RequestListColumnType extends Component {
- static get propTypes() {
- return {
- item: PropTypes.object.isRequired,
- };
- }
-
- shouldComponentUpdate(nextProps) {
- return this.props.item.mimeType !== nextProps.item.mimeType;
- }
-
- render() {
- let { mimeType } = this.props.item;
- let abbrevType;
-
- if (mimeType) {
- abbrevType = getAbbreviatedMimeType(mimeType);
- }
-
- return (
- div({
- className: "requests-list-column requests-list-type",
- title: mimeType,
- },
- abbrevType
- )
- );
- }
-}
-
-module.exports = RequestListColumnType;
--- a/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
+++ b/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
@@ -13,17 +13,16 @@ const {
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index");
const { ACTIVITY_TYPE } = require("../constants");
const { L10N } = require("../utils/l10n");
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
// Components
const MDNLink = createFactory(require("./MdnLink"));
-const RequestListHeader = createFactory(require("./RequestListHeader"));
const { button, div, span } = DOM;
const RELOAD_NOTICE_1 = L10N.getStr("netmonitor.reloadNotice1");
const RELOAD_NOTICE_2 = L10N.getStr("netmonitor.reloadNotice2");
const RELOAD_NOTICE_3 = L10N.getStr("netmonitor.reloadNotice3");
const PERFORMANCE_NOTICE_1 = L10N.getStr("netmonitor.perfNotice1");
const PERFORMANCE_NOTICE_2 = L10N.getStr("netmonitor.perfNotice2");
@@ -38,43 +37,41 @@ class RequestListEmptyNotice extends Com
return {
connector: PropTypes.object.isRequired,
onReloadClick: PropTypes.func.isRequired,
onPerfClick: PropTypes.func.isRequired,
};
}
render() {
- return div(
- {
- className: "request-list-empty-notice",
- },
- RequestListHeader(),
- div({ className: "notice-reload-message empty-notice-element" },
- span(null, RELOAD_NOTICE_1),
- button(
- {
- className: "devtools-button requests-list-reload-notice-button",
+ return (
+ div({ className: "request-list-empty-notice" },
+ div({ className: "notice-reload-message empty-notice-element" },
+ span(null, RELOAD_NOTICE_1),
+ button(
+ {
+ className: "devtools-button requests-list-reload-notice-button",
+ "data-standalone": true,
+ onClick: this.props.onReloadClick,
+ },
+ RELOAD_NOTICE_2
+ ),
+ span(null, RELOAD_NOTICE_3)
+ ),
+ div({ className: "notice-perf-message empty-notice-element" },
+ span(null, PERFORMANCE_NOTICE_1),
+ button({
+ title: PERFORMANCE_NOTICE_3,
+ className: "devtools-button requests-list-perf-notice-button",
"data-standalone": true,
- onClick: this.props.onReloadClick,
- },
- RELOAD_NOTICE_2
- ),
- span(null, RELOAD_NOTICE_3)
- ),
- div({ className: "notice-perf-message empty-notice-element" },
- span(null, PERFORMANCE_NOTICE_1),
- button({
- title: PERFORMANCE_NOTICE_3,
- className: "devtools-button requests-list-perf-notice-button",
- "data-standalone": true,
- onClick: this.props.onPerfClick,
- }),
- span(null, PERFORMANCE_NOTICE_2),
- MDNLink({ url: getPerformanceAnalysisURL() })
+ onClick: this.props.onPerfClick,
+ }),
+ span(null, PERFORMANCE_NOTICE_2),
+ MDNLink({ url: getPerformanceAnalysisURL() })
+ )
)
);
}
}
module.exports = connect(
undefined,
(dispatch, props) => ({
--- a/devtools/client/netmonitor/src/components/RequestListHeader.js
+++ b/devtools/client/netmonitor/src/components/RequestListHeader.js
@@ -1,233 +1,34 @@
/* 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 {
- Component,
+ PureComponent,
PropTypes,
DOM,
} = require("devtools/client/shared/vendor/react");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { getTheme, addThemeObserver, removeThemeObserver } =
- require("devtools/client/shared/theme");
-const Actions = require("../actions/index");
-const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
-const { getWaterfallScale } = require("../selectors/index");
-const { getFormattedTime } = require("../utils/format-utils");
-const { L10N } = require("../utils/l10n");
-const WaterfallBackground = require("../waterfall-background");
-const RequestListHeaderContextMenu = require("../request-list-header-context-menu");
-const { div, button } = DOM;
+const { div } = DOM;
/**
* Render the request list header with sorting arrows for columns.
* Displays tick marks in the waterfall column header.
* Also draws the waterfall background canvas and updates it when needed.
*/
-class RequestListHeader extends Component {
+class RequestListHeader extends PureComponent {
static get propTypes() {
return {
- columns: PropTypes.object.isRequired,
- resetColumns: PropTypes.func.isRequired,
- resizeWaterfall: PropTypes.func.isRequired,
- scale: PropTypes.number,
- sort: PropTypes.object,
- sortBy: PropTypes.func.isRequired,
- toggleColumn: PropTypes.func.isRequired,
- waterfallWidth: PropTypes.number,
+ columnKey: PropTypes.string,
};
}
- constructor(props) {
- super(props);
- this.onContextMenu = this.onContextMenu.bind(this);
- this.drawBackground = this.drawBackground.bind(this);
- this.resizeWaterfall = this.resizeWaterfall.bind(this);
- }
-
- componentWillMount() {
- const { resetColumns, toggleColumn } = this.props;
- this.contextMenu = new RequestListHeaderContextMenu({
- resetColumns,
- toggleColumn,
- });
- }
-
- componentDidMount() {
- // Create the object that takes care of drawing the waterfall canvas background
- this.background = new WaterfallBackground(document);
- this.drawBackground();
- this.resizeWaterfall();
- window.addEventListener("resize", this.resizeWaterfall);
- addThemeObserver(this.drawBackground);
- }
-
- componentDidUpdate() {
- this.drawBackground();
- }
-
- componentWillUnmount() {
- this.background.destroy();
- this.background = null;
- window.removeEventListener("resize", this.resizeWaterfall);
- removeThemeObserver(this.drawBackground);
- }
-
- onContextMenu(evt) {
- evt.preventDefault();
- this.contextMenu.open(evt);
- }
-
- drawBackground() {
- // The background component is theme dependent, so add the current theme to the props.
- let props = Object.assign({}, this.props, {
- theme: getTheme()
- });
- this.background.draw(props);
- }
-
- resizeWaterfall() {
- let waterfallHeader = this.refs.waterfallHeader;
- if (waterfallHeader) {
- // Measure its width and update the 'waterfallWidth' property in the store.
- // The 'waterfallWidth' will be further updated on every window resize.
- window.cancelIdleCallback(this._resizeTimerId);
- this._resizeTimerId = window.requestIdleCallback(() =>
- this.props.resizeWaterfall(waterfallHeader.getBoundingClientRect().width));
- }
- }
-
render() {
- let { columns, scale, sort, sortBy, waterfallWidth } = this.props;
-
- return (
- div({ className: "devtools-toolbar requests-list-headers-wrapper" },
- div({ className: "devtools-toolbar requests-list-headers" },
- HEADERS.filter((header) => columns.get(header.name)).map((header) => {
- let name = header.name;
- let boxName = header.boxName || name;
- let label = header.noLocalization
- ? name : L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
- let sorted, sortedTitle;
- let active = sort.type == name ? true : undefined;
-
- if (active) {
- sorted = sort.ascending ? "ascending" : "descending";
- sortedTitle = L10N.getStr(sort.ascending
- ? "networkMenu.sortedAsc"
- : "networkMenu.sortedDesc");
- }
+ let { columnKey } = this.props;
- return (
- div({
- id: `requests-list-${boxName}-header-box`,
- className: `requests-list-column requests-list-${boxName}`,
- key: name,
- ref: `${name}Header`,
- // Used to style the next column.
- "data-active": active,
- onContextMenu: this.onContextMenu,
- },
- button({
- id: `requests-list-${name}-button`,
- className: `requests-list-header-button`,
- "data-sorted": sorted,
- title: sortedTitle ? `${label} (${sortedTitle})` : label,
- onClick: () => sortBy(name),
- },
- name === "waterfall"
- ? WaterfallLabel(waterfallWidth, scale, label)
- : div({ className: "button-text" }, label),
- div({ className: "button-icon" })
- )
- )
- );
- })
- )
- )
- );
+ return div({ className: "requests-list-header" }, columnKey);
}
}
-/**
- * Build the waterfall header - timing tick marks with the right spacing
- */
-function waterfallDivisionLabels(waterfallWidth, scale) {
- let labels = [];
-
- // Build new millisecond tick labels...
- let timingStep = REQUESTS_WATERFALL.HEADER_TICKS_MULTIPLE;
- let scaledStep = scale * timingStep;
-
- // Ignore any divisions that would end up being too close to each other.
- while (scaledStep < REQUESTS_WATERFALL.HEADER_TICKS_SPACING_MIN) {
- scaledStep *= 2;
- }
-
- // Insert one label for each division on the current scale.
- for (let x = 0; x < waterfallWidth; x += scaledStep) {
- let millisecondTime = x / scale;
- let divisionScale = "millisecond";
-
- // If the division is greater than 1 minute.
- if (millisecondTime > 60000) {
- divisionScale = "minute";
- } else if (millisecondTime > 1000) {
- // If the division is greater than 1 second.
- divisionScale = "second";
- }
-
- let width = (x + scaledStep | 0) - (x | 0);
- // Adjust the first marker for the borders
- if (x == 0) {
- width -= 2;
- }
- // Last marker doesn't need a width specified at all
- if (x + scaledStep >= waterfallWidth) {
- width = undefined;
- }
-
- labels.push(div(
- {
- key: labels.length,
- className: "requests-list-timings-division",
- "data-division-scale": divisionScale,
- style: { width }
- },
- getFormattedTime(millisecondTime)
- ));
- }
-
- return labels;
-}
-
-function WaterfallLabel(waterfallWidth, scale, label) {
- let className = "button-text requests-list-waterfall-label-wrapper";
-
- if (waterfallWidth !== null && scale !== null) {
- label = waterfallDivisionLabels(waterfallWidth, scale);
- className += " requests-list-waterfall-visible";
- }
-
- return div({ className }, label);
-}
-
-module.exports = connect(
- (state) => ({
- columns: state.ui.columns,
- firstRequestStartedMillis: state.requests.firstStartedMillis,
- scale: getWaterfallScale(state),
- sort: state.sort,
- timingMarkers: state.timingMarkers,
- waterfallWidth: state.ui.waterfallWidth,
- }),
- (dispatch) => ({
- resetColumns: () => dispatch(Actions.resetColumns()),
- resizeWaterfall: (width) => dispatch(Actions.resizeWaterfall(width)),
- sortBy: (type) => dispatch(Actions.sortBy(type)),
- toggleColumn: (column) => dispatch(Actions.toggleColumn(column)),
- })
-)(RequestListHeader);
+module.exports = RequestListHeader;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/RequestListHeaders.js
@@ -0,0 +1,233 @@
+/* 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 {
+ Component,
+ PropTypes,
+ DOM,
+} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { getTheme, addThemeObserver, removeThemeObserver } =
+ require("devtools/client/shared/theme");
+const Actions = require("../actions/index");
+const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
+const { getWaterfallScale } = require("../selectors/index");
+const { getFormattedTime } = require("../utils/format-utils");
+const { L10N } = require("../utils/l10n");
+const WaterfallBackground = require("../waterfall-background");
+const RequestListHeaderContextMenu = require("../request-list-header-context-menu");
+
+const { div, button } = DOM;
+
+/**
+ * Render the request list header with sorting arrows for columns.
+ * Displays tick marks in the waterfall column header.
+ * Also draws the waterfall background canvas and updates it when needed.
+ */
+class RequestListHeader extends Component {
+ static get propTypes() {
+ return {
+ columns: PropTypes.object.isRequired,
+ resetColumns: PropTypes.func.isRequired,
+ resizeWaterfall: PropTypes.func.isRequired,
+ scale: PropTypes.number,
+ sort: PropTypes.object,
+ sortBy: PropTypes.func.isRequired,
+ toggleColumn: PropTypes.func.isRequired,
+ waterfallWidth: PropTypes.number,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onContextMenu = this.onContextMenu.bind(this);
+ this.drawBackground = this.drawBackground.bind(this);
+ this.resizeWaterfall = this.resizeWaterfall.bind(this);
+ }
+
+ componentWillMount() {
+ const { resetColumns, toggleColumn } = this.props;
+ this.contextMenu = new RequestListHeaderContextMenu({
+ resetColumns,
+ toggleColumn,
+ });
+ }
+
+ componentDidMount() {
+ // Create the object that takes care of drawing the waterfall canvas background
+ this.background = new WaterfallBackground(document);
+ this.drawBackground();
+ this.resizeWaterfall();
+ window.addEventListener("resize", this.resizeWaterfall);
+ addThemeObserver(this.drawBackground);
+ }
+
+ componentDidUpdate() {
+ this.drawBackground();
+ }
+
+ componentWillUnmount() {
+ this.background.destroy();
+ this.background = null;
+ window.removeEventListener("resize", this.resizeWaterfall);
+ removeThemeObserver(this.drawBackground);
+ }
+
+ onContextMenu(evt) {
+ evt.preventDefault();
+ this.contextMenu.open(evt);
+ }
+
+ drawBackground() {
+ // The background component is theme dependent, so add the current theme to the props.
+ let props = Object.assign({}, this.props, {
+ theme: getTheme()
+ });
+ this.background.draw(props);
+ }
+
+ resizeWaterfall() {
+ let waterfallHeader = this.refs.waterfallHeader;
+ if (waterfallHeader) {
+ // Measure its width and update the 'waterfallWidth' property in the store.
+ // The 'waterfallWidth' will be further updated on every window resize.
+ window.cancelIdleCallback(this._resizeTimerId);
+ this._resizeTimerId = window.requestIdleCallback(() =>
+ this.props.resizeWaterfall(waterfallHeader.getBoundingClientRect().width));
+ }
+ }
+
+ render() {
+ let { columns, scale, sort, sortBy, waterfallWidth } = this.props;
+
+ return (
+ div({ className: "devtools-toolbar requests-list-headers-wrapper" },
+ div({ className: "devtools-toolbar requests-list-headers" },
+ HEADERS.filter((header) => columns.get(header.name)).map((header) => {
+ let name = header.name;
+ let boxName = header.boxName || name;
+ let label = header.noLocalization
+ ? name : L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
+ let sorted, sortedTitle;
+ let active = sort.type == name ? true : undefined;
+
+ if (active) {
+ sorted = sort.ascending ? "ascending" : "descending";
+ sortedTitle = L10N.getStr(sort.ascending
+ ? "networkMenu.sortedAsc"
+ : "networkMenu.sortedDesc");
+ }
+
+ return (
+ div({
+ id: `requests-list-${boxName}-header-box`,
+ className: `requests-list-column requests-list-${boxName}`,
+ key: name,
+ ref: `${name}Header`,
+ // Used to style the next column.
+ "data-active": active,
+ onContextMenu: this.onContextMenu,
+ },
+ button({
+ id: `requests-list-${name}-button`,
+ className: `requests-list-header-button`,
+ "data-sorted": sorted,
+ title: sortedTitle ? `${label} (${sortedTitle})` : label,
+ onClick: () => sortBy(name),
+ },
+ name === "waterfall"
+ ? WaterfallLabel(waterfallWidth, scale, label)
+ : div({ className: "button-text" }, label),
+ div({ className: "button-icon" })
+ )
+ )
+ );
+ })
+ )
+ )
+ );
+ }
+}
+
+/**
+ * Build the waterfall header - timing tick marks with the right spacing
+ */
+function waterfallDivisionLabels(waterfallWidth, scale) {
+ let labels = [];
+
+ // Build new millisecond tick labels...
+ let timingStep = REQUESTS_WATERFALL.HEADER_TICKS_MULTIPLE;
+ let scaledStep = scale * timingStep;
+
+ // Ignore any divisions that would end up being too close to each other.
+ while (scaledStep < REQUESTS_WATERFALL.HEADER_TICKS_SPACING_MIN) {
+ scaledStep *= 2;
+ }
+
+ // Insert one label for each division on the current scale.
+ for (let x = 0; x < waterfallWidth; x += scaledStep) {
+ let millisecondTime = x / scale;
+ let divisionScale = "millisecond";
+
+ // If the division is greater than 1 minute.
+ if (millisecondTime > 60000) {
+ divisionScale = "minute";
+ } else if (millisecondTime > 1000) {
+ // If the division is greater than 1 second.
+ divisionScale = "second";
+ }
+
+ let width = (x + scaledStep | 0) - (x | 0);
+ // Adjust the first marker for the borders
+ if (x == 0) {
+ width -= 2;
+ }
+ // Last marker doesn't need a width specified at all
+ if (x + scaledStep >= waterfallWidth) {
+ width = undefined;
+ }
+
+ labels.push(div(
+ {
+ key: labels.length,
+ className: "requests-list-timings-division",
+ "data-division-scale": divisionScale,
+ style: { width }
+ },
+ getFormattedTime(millisecondTime)
+ ));
+ }
+
+ return labels;
+}
+
+function WaterfallLabel(waterfallWidth, scale, label) {
+ let className = "button-text requests-list-waterfall-label-wrapper";
+
+ if (waterfallWidth !== null && scale !== null) {
+ label = waterfallDivisionLabels(waterfallWidth, scale);
+ className += " requests-list-waterfall-visible";
+ }
+
+ return div({ className }, label);
+}
+
+module.exports = connect(
+ (state) => ({
+ columns: state.ui.columns,
+ firstRequestStartedMillis: state.requests.firstStartedMillis,
+ scale: getWaterfallScale(state),
+ sort: state.sort,
+ timingMarkers: state.timingMarkers,
+ waterfallWidth: state.ui.waterfallWidth,
+ }),
+ (dispatch) => ({
+ resetColumns: () => dispatch(Actions.resetColumns()),
+ resizeWaterfall: (width) => dispatch(Actions.resizeWaterfall(width)),
+ sortBy: (type) => dispatch(Actions.sortBy(type)),
+ toggleColumn: (column) => dispatch(Actions.toggleColumn(column)),
+ })
+)(RequestListHeader);
--- a/devtools/client/netmonitor/src/middleware/batching.js
+++ b/devtools/client/netmonitor/src/middleware/batching.js
@@ -1,17 +1,17 @@
/* 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 { BATCH_ACTIONS, BATCH_ENABLE, BATCH_RESET } = require("../constants");
-const REQUESTS_REFRESH_RATE = 50; // ms
+const REQUESTS_REFRESH_RATE = 200; // ms
/**
* Middleware that watches for actions with a "batch = true" value in their meta field.
* These actions are queued and dispatched as one batch after a timeout.
* Special actions that are handled by this middleware:
* - BATCH_ENABLE can be used to enable and disable the batching.
* - BATCH_RESET discards the actions that are currently in the queue.
*/
--- a/devtools/client/netmonitor/yarn.lock
+++ b/devtools/client/netmonitor/yarn.lock
@@ -1279,17 +1279,17 @@ create-hmac@^1.1.0, create-hmac@^1.1.2,
dependencies:
cipher-base "^1.0.3"
create-hash "^1.1.0"
inherits "^2.0.1"
ripemd160 "^2.0.0"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-create-react-class@^15.6.0:
+create-react-class@^15.5.2, create-react-class@^15.6.0:
version "15.6.2"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a"
dependencies:
fbjs "^0.8.9"
loose-envify "^1.3.1"
object-assign "^4.1.1"
cross-spawn@^5.0.1:
@@ -1674,16 +1674,20 @@ ee-first@1.1.1:
electron-to-chromium@^1.2.7:
version "1.3.8"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.8.tgz#b2c8a2c79bb89fbbfd3724d9555e15095b5f5fb6"
electron-to-chromium@^1.3.18:
version "1.3.21"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2"
+element-resize-event@^2.0.4:
+ version "2.0.9"
+ resolved "https://registry.yarnpkg.com/element-resize-event/-/element-resize-event-2.0.9.tgz#2f5e1581a296eb5275210c141bc56342e218f876"
+
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
hash.js "^1.0.0"
hmac-drbg "^1.0.0"
@@ -2053,16 +2057,23 @@ find-up@^1.0.0:
pinkie-promise "^2.0.0"
find-up@^2.0.0, find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
dependencies:
locate-path "^2.0.0"
+fixed-data-table-2@^0.8.5:
+ version "0.8.5"
+ resolved "https://registry.yarnpkg.com/fixed-data-table-2/-/fixed-data-table-2-0.8.5.tgz#8f21a26a011c50b7fa959c1748d4762b7a7d629b"
+ dependencies:
+ create-react-class "^15.5.2"
+ prop-types "^15.5.8"
+
flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
for-in@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -4080,17 +4091,17 @@ process@^0.11.0:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
promise@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf"
dependencies:
asap "~2.0.3"
-prop-types@^15.5.10:
+prop-types@^15.5.10, prop-types@^15.5.8:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.3.1"
object-assign "^4.1.1"
properties-parser@^0.3.1:
@@ -4194,16 +4205,22 @@ rc@^1.1.7:
version "1.2.1"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
minimist "^1.2.0"
strip-json-comments "~2.0.1"
+react-dimensions@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/react-dimensions/-/react-dimensions-1.3.1.tgz#89c29bcd48828a74faeb07da1e461e1a354ccc48"
+ dependencies:
+ element-resize-event "^2.0.4"
+
react-dom@=15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.6.1.tgz#2cb0ed4191038e53c209eb3a79a23e2a4cf99470"
dependencies:
fbjs "^0.8.9"
loose-envify "^1.1.0"
object-assign "^4.1.0"
prop-types "^15.5.10"
@@ -4433,19 +4450,19 @@ require-directory@^2.1.1:
require-from-string@^1.1.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
-reselect@^2.5.4:
- version "2.5.4"
- resolved "https://registry.yarnpkg.com/reselect/-/reselect-2.5.4.tgz#b7d23fdf00b83fa7ad0279546f8dbbbd765c7047"
+reselect@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.2.0:
version "1.3.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5"