--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
@@ -45,87 +45,81 @@
:root.theme-firebug {
--sort-ascending-image: url(chrome://devtools/skin/images/firebug/arrow-up.svg);
--sort-descending-image: url(chrome://devtools/skin/images/firebug/arrow-down.svg);
}
/* General */
+* {
+ box-sizing: border-box;
+}
+
html,
body,
#mount,
.launchpad-root,
.network-monitor,
.monitor-panel {
flex: initial;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
-* {
- box-sizing: border-box;
-}
-
.split-box {
overflow: hidden;
}
-.toolbar-labels {
- overflow: hidden;
- display: flex;
- flex: auto;
-}
+/* Toolbar */
.devtools-toolbar {
display: flex;
}
-.devtools-toolbar-container.devtools-toolbar {
- height: auto !important;
+.devtools-toolbar-container {
+ height: auto;
flex-wrap: wrap;
justify-content: space-between;
}
.devtools-toolbar-group {
display: flex;
flex: 0 0 auto;
flex-wrap: nowrap;
align-items: center;
}
-#response-content-image-box {
- overflow: auto;
-}
-
-.cropped-textbox .textbox-input {
- /* workaround for textbox not supporting the @crop attribute */
- text-overflow: ellipsis;
+.requests-list-filter-buttons {
+ display: flex;
+ flex-wrap: nowrap;
}
.learn-more-link {
color: var(--theme-highlight-blue);
cursor: pointer;
margin: 0 5px;
white-space: nowrap;
}
.learn-more-link:hover {
text-decoration: underline;
}
/* Status bar */
+.devtools-status-bar-label {
+ flex: 0;
+}
+
.status-bar-label {
display: inline-flex;
- align-content: stretch;
margin-inline-end: 10px;
-
/* Status bar has just one line so, don't wrap labels */
white-space: nowrap;
}
.status-bar-label::before {
content: "";
display: inline-block;
margin-inline-end: 10px;
@@ -138,25 +132,17 @@ body,
.status-bar-label.dom-content-loaded {
color: blue;
}
.status-bar-label.load {
color: red;
}
-/* Request list */
-
-.request-list-container {
- display: flex;
- flex-direction: column;
- width: 100%;
- height: 100%;
- overflow: hidden;
-}
+/* Request list empty panel */
.request-list-empty-notice {
margin: 0;
padding: 12px;
font-size: 120%;
flex: 1;
overflow: auto;
}
@@ -179,141 +165,146 @@ body,
}
.requests-list-reload-notice-button {
font-size: inherit;
min-height: 26px;
margin: 0 5px;
}
-/* Network requests table */
+/* Requests list table */
-.requests-list-toolbar {
+.request-list-container {
display: flex;
- padding: 0;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ overflow-x: hidden;
}
-.requests-list-filter-buttons {
- display: flex;
- flex-wrap: nowrap;
+.requests-list-wrapper {
+ width: 100%;
+ height: 100%;
}
-.theme-firebug .requests-list-toolbar {
- height: 19px !important;
+.requests-list-table {
+ display: table;
+ position: relative;
+ width: 100%;
+ height: 100%;
}
.requests-list-contents {
- height: 100%;
+ display: table-row-group;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
overflow-x: hidden;
overflow-y: auto;
--timings-scale: 1;
--timings-rev-scale: 1;
}
-.requests-list-subitem {
- display: flex;
- flex: none;
- box-sizing: border-box;
- align-items: center;
- padding: 3px;
+.requests-list-column {
+ display: table-cell;
cursor: default;
-}
-
-.subitem-label {
+ text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ vertical-align: middle;
+ max-width: 50px;
+ min-width: 50px;
+}
+
+.requests-list-column > * {
+ display: inline-block;
}
-/* Requests list header */
+.theme-firebug .requests-list-column {
+ padding: 1px;
+}
+
+/* Requests list headers */
-.requests-list-header {
- display: flex;
- flex: none;
+.requests-list-headers {
+ display: table-header-group;
+ height: 24px;
+ padding: 0;
+}
+
+.requests-list-headers .requests-list-column:first-child .requests-list-header-button {
+ border-width: 0;
}
.requests-list-header-button {
- display: flex;
- align-items: center;
- flex: auto;
- -moz-appearance: none; appearance: none;
background-color: transparent;
border-image: linear-gradient(transparent 15%,
var(--theme-splitter-color) 15%,
var(--theme-splitter-color) 85%,
transparent 85%) 1 1;
- border-style: solid;
border-width: 0;
border-inline-start-width: 1px;
- min-width: 1px;
- min-height: 24px;
- margin: 0;
- padding-top: 2px;
- padding-bottom: 2px;
padding-inline-start: 16px;
- padding-inline-end: 0;
+ width: 100%;
+ min-height: 23px;
text-align: center;
color: inherit;
- font-weight: inherit !important;
}
.requests-list-header-button::-moz-focus-inner {
border: 0;
padding: 0;
}
-.requests-list-header:first-child .requests-list-header-button {
- border-width: 0;
-}
-
.requests-list-header-button:hover {
background-color: rgba(0, 0, 0, 0.1);
}
.requests-list-header-button > .button-text {
- flex: auto;
- white-space: nowrap;
+ display: inline-block;
+ text-align: center;
+ vertical-align: middle;
+ /* Align button text to center */
+ width: calc(100% - 8px);
overflow: hidden;
text-overflow: ellipsis;
}
.requests-list-header-button > .button-icon {
- flex: none;
+ display: inline-block;
+ width: 7px;
height: 4px;
margin-inline-start: 3px;
margin-inline-end: 6px;
- width: 7px;
+ vertical-align: middle;
}
.requests-list-header-button[data-sorted=ascending] > .button-icon {
background-image: var(--sort-ascending-image);
}
.requests-list-header-button[data-sorted=descending] > .button-icon {
background-image: var(--sort-descending-image);
}
-.requests-list-waterfall-label-wrapper {
- display: flex;
-}
-
.requests-list-header-button[data-sorted],
.requests-list-header-button[data-sorted]:hover {
background-color: var(--theme-selection-background);
color: var(--theme-selection-color);
}
.requests-list-header-button[data-sorted],
-.requests-list-header[data-active] + .requests-list-header .requests-list-header-button {
+.requests-list-column[data-active] + .requests-list-column .requests-list-header-button {
border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
}
-/* Firebug theme support for Network panel header */
-
-.theme-firebug .requests-list-header {
+.theme-firebug .requests-list-headers {
padding: 0 !important;
font-weight: bold;
background: linear-gradient(rgba(255, 255, 255, 0.05),
rgba(0, 0, 0, 0.05)),
#C8D2DC;
}
.theme-firebug .requests-list-header-button {
@@ -328,147 +319,46 @@ body,
background-color: #AAC3DC;
}
:root[platform="linux"].theme-firebug .requests-list-header-button[data-sorted] {
background-color: #FAC8AF !important;
color: inherit !important;
}
-.theme-firebug .requests-list-header:hover:active {
+.theme-firebug .requests-list-header-button:hover:active {
background-image: linear-gradient(rgba(0, 0, 0, 0.1),
transparent);
}
+/* Requests list column */
-/* Network requests table: specific column dimensions */
+/* Status column */
.requests-list-status {
- max-width: 6em;
- text-align: center;
- width: 8vw;
-}
-
-.requests-list-method,
-.requests-list-method-box {
- max-width: 7em;
- text-align: center;
- width: 10vw;
-}
-
-.requests-list-icon-and-file {
- width: 22vw;
-}
-
-.requests-list-icon {
- background: transparent;
- width: 15px;
- height: 15px;
- margin-inline-end: 4px;
-}
-
-.requests-list-icon {
- outline: 1px solid var(--table-splitter-color);
-}
-
-.requests-list-security-and-domain {
- width: 14vw;
-}
-
-.requests-list-remoteip {
- width: 8vw;
-}
-
-.requests-list-protocol {
- width: 7vw;
-}
-
-.requests-security-state-icon {
- flex: none;
- width: 16px;
- height: 16px;
- margin-inline-end: 4px;
-}
-
-.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);
+ width: 8%;
}
-.security-state-secure {
- background-image: url(chrome://devtools/skin/images/security-state-secure.svg);
-}
-
-.security-state-weak {
- background-image: url(chrome://devtools/skin/images/security-state-weak.svg);
-}
-
-.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);
-}
-
-.requests-list-type,
-.requests-list-size {
- max-width: 6em;
- width: 8vw;
- justify-content: center;
-}
-
-.requests-list-transferred {
- max-width: 8em;
- width: 8vw;
- justify-content: center;
+.theme-firebug .requests-list-status {
+ font-weight: bold;
}
-.requests-list-cause {
- max-width: 8em;
- width: 8vw;
-}
-
-.requests-list-cause-stack {
- 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;
- -moz-user-select: none;
-}
-
-.request-list-item.selected .requests-list-transferred.theme-comment {
- color: var(--theme-selection-color);
-}
-
-/* Network requests table: status codes */
-
.requests-list-status-code {
- margin-inline-start: 3px !important;
+ margin-inline-start: 3px;
width: 3em;
- margin-inline-end: -3em !important;
}
.requests-list-status-icon {
background: #fff;
height: 10px;
width: 10px;
margin-inline-start: 5px;
margin-inline-end: 5px;
border-radius: 10px;
transition: box-shadow 0.5s ease-in-out;
- box-sizing: border-box;
}
.request-list-item.selected .requests-list-status-icon {
filter: brightness(1.3);
}
.requests-list-status-icon:not([data-code]) {
background-color: var(--theme-content-color2);
@@ -495,47 +385,191 @@ body,
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 10px solid var(--theme-highlight-lightorange);
border-radius: 0;
}
/* 4xx and 5xx are squares - error codes */
.requests-list-status-icon[data-code^="4"] {
- background-color: var(--theme-highlight-red);
+ background-color: var(--theme-highlight-red);
border-radius: 0; /* squares */
}
.requests-list-status-icon[data-code^="5"] {
background-color: var(--theme-highlight-pink);
border-radius: 0;
transform: rotate(45deg);
}
-/* Network requests table: waterfall header */
+/* 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%;
+}
+
+/* Domain column */
+
+.requests-list-domain {
+ width: 13%;
+}
+
+.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;
+}
+
+.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);
+}
+
+.security-state-secure {
+ background-image: url(chrome://devtools/skin/images/security-state-secure.svg);
+}
+
+.security-state-weak {
+ background-image: url(chrome://devtools/skin/images/security-state-weak.svg);
+}
+
+.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 {
- flex: auto;
+ width: 20vw;
+ max-width: 20vw;
+ min-width: 20vw;
+ background-repeat: repeat-y;
+ background-position: left center;
+ /* Background created on a <canvas> in js. */
+ /* @see devtools/client/netmonitor/src/waterfall-background.js */
+ background-image: -moz-element(#waterfall-background);
+}
+
+.requests-list-waterfall:dir(rtl) {
+ background-position: right center;
+}
+
+.requests-list-waterfall > .requests-list-header-button {
padding-inline-start: 0;
}
+.requests-list-waterfall > .requests-list-header-button > .button-text {
+ width: auto;
+}
+
.requests-list-waterfall-label-wrapper:not(.requests-list-waterfall-visible) {
padding-inline-start: 16px;
}
.requests-list-timings-division {
- padding-top: 2px;
+ display: inline-block;
padding-inline-start: 4px;
font-size: 75%;
pointer-events: none;
- box-sizing: border-box;
text-align: start;
- /* Allow the timing label to shrink if the container gets too narrow.
- * The container width then is not limited by the content. */
- flex: initial;
+}
+
+:root[platform="win"] .requests-list-timings-division {
+ padding-top: 1px;
+ font-size: 90%;
}
.requests-list-timings-division:not(:first-child) {
border-inline-start: 1px dashed;
}
.requests-list-timings-division:dir(ltr) {
transform-origin: left center;
@@ -553,65 +587,31 @@ body,
border-inline-start-color: #585959 !important;
}
.requests-list-timings-division[data-division-scale=second],
.requests-list-timings-division[data-division-scale=minute] {
font-weight: 600;
}
-/* Network requests table: waterfall items */
-
-.requests-list-subitem.requests-list-waterfall {
- padding-inline-start: 0;
- padding-inline-end: 4px;
- /* Background created on a <canvas> in js. */
- /* @see devtools/client/netmonitor/netmonitor-view.js */
- background-image: -moz-element(#waterfall-background);
- background-repeat: repeat-y;
- background-position: left center;
-}
-
-.requests-list-subitem.requests-list-waterfall:dir(rtl) {
- background-position: right center;
-}
-
.requests-list-timings {
display: flex;
flex: none;
align-items: center;
transform: scaleX(var(--timings-scale));
}
.requests-list-timings:dir(ltr) {
transform-origin: left center;
}
.requests-list-timings:dir(rtl) {
transform-origin: right center;
}
-.requests-list-timings-total:dir(ltr) {
- transform-origin: left center;
-}
-
-.requests-list-timings-total:dir(rtl) {
- transform-origin: right center;
-}
-
-.requests-list-timings-total {
- display: inline-block;
- padding-inline-start: 4px;
- font-size: 85%;
- font-weight: 600;
- white-space: nowrap;
- /* This node should not be scaled - apply a reversed transformation */
- transform: scaleX(var(--timings-rev-scale));
-}
-
.requests-list-timings-box {
display: inline-block;
height: 9px;
}
.theme-firebug .requests-list-timings-box {
background-image: linear-gradient(rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.2));
height: 16px;
@@ -636,92 +636,79 @@ body,
.requests-list-timings-box.wait {
background-color: var(--timing-wait-color);
}
.requests-list-timings-box.receive {
background-color: var(--timing-receive-color);
}
-/* SideMenuWidget */
-#network-table .request-list-empty-notice,
-#network-table .request-list-container {
- background-color: var(--theme-body-background);
+.requests-list-timings-total {
+ display: inline-block;
+ padding-inline-start: 4px;
+ font-size: 85%;
+ font-weight: 600;
+ white-space: nowrap;
+ /* This node should not be scaled - apply a reversed transformation */
+ transform: scaleX(var(--timings-rev-scale));
}
+.requests-list-timings-total:dir(ltr) {
+ transform-origin: left center;
+}
+
+.requests-list-timings-total:dir(rtl) {
+ transform-origin: right center;
+}
+
+/* Request list item */
+
.request-list-item {
- display: flex;
- border-top-color: transparent;
- border-bottom-color: transparent;
- padding: 0;
+ display: table-row;
+ height: 24px;
}
.request-list-item.selected {
background-color: var(--theme-selection-background);
color: var(--theme-selection-color);
}
.request-list-item:not(.selected).odd {
background-color: var(--table-zebra-background);
}
.request-list-item:not(.selected):hover {
background-color: var(--theme-selection-background-semitransparent);
}
-.request-list-item.fromCache > .requests-list-subitem:not(.requests-list-waterfall) {
- opacity: 0.6;
+.request-list-item.fromCache > .requests-list-column:not(.requests-list-waterfall) {
+ opacity: 0.6;
}
.theme-firebug .request-list-item:not(.selected):hover {
background: #EFEFEF;
}
-.theme-firebug .requests-list-subitem {
- padding: 1px;
-}
-
-/* HTTP Status Column */
-.theme-firebug .requests-list-subitem.requests-list-status {
- font-weight: bold;
-}
-
-/* Method Column */
-
-.theme-firebug .requests-list-subitem.requests-list-method-box {
- color: rgb(128, 128, 128);
-}
-
-.request-list-item.selected .requests-list-method {
- color: var(--theme-selection-color);
-}
-
-/* Size Column */
-.theme-firebug .requests-list-subitem.requests-list-size {
- justify-content: end;
- padding-inline-end: 4px;
-}
-
-/* Network details panel */
+/* Network details panel toggle */
.network-details-panel-toggle[disabled] {
display: none;
}
.network-details-panel-toggle:dir(ltr)::before,
.network-details-panel-toggle.pane-collapsed:dir(rtl)::before {
background-image: var(--theme-pane-collapse-image);
}
.network-details-panel-toggle.pane-collapsed:dir(ltr)::before,
.network-details-panel-toggle:dir(rtl)::before {
background-image: var(--theme-pane-expand-image);
}
-/* Network request details tabpanels */
+/* Network details panel */
.network-details-panel {
width: 100%;
height: 100%;
overflow: hidden;
}
.panel-container {
@@ -911,17 +898,16 @@ body,
padding: 0 4px;
}
.headers-summary .raw-headers textarea {
width: 100%;
height: 50vh;
font: message-box;
resize: none;
- box-sizing: border-box;
}
.headers-summary .raw-headers .tabpanel-summary-label {
padding: 0 0 4px 0;
}
.headers-summary .textbox-input {
margin-inline-end: 2px;
@@ -1047,17 +1033,17 @@ body,
}
@media (min-resolution: 1.1dppx) {
.security-warning-icon {
background-image: url(chrome://devtools/skin/images/alerticon-warning@2x.png);
}
}
-/* Custom request view */
+/* Custom request panel */
.custom-request-panel {
height: 100%;
overflow: auto;
padding: 0 4px;
background-color: var(--theme-sidebar-background);
}
@@ -1096,43 +1082,44 @@ body,
width: 4.5em;
}
.custom-url-value {
flex-grow: 1;
margin-inline-start: 6px;
}
-/* Performance analysis buttons */
+/* Statistics panel buttons */
.requests-list-network-summary-button {
- display: flex;
- flex-wrap: nowrap;
- align-items: center;
+ display: inline-flex;
+ cursor: pointer;
+ height: 18px;
background: none;
box-shadow: none;
border-color: transparent;
padding-inline-end: 0;
- cursor: pointer;
+ margin-top: 3px;
+ margin-bottom: 3px;
margin-inline-end: 1em;
}
.requests-list-network-summary-button > .summary-info-icon {
- background-image: url(chrome://devtools/skin/images/profiler-stopwatch.svg);
+ background: url(chrome://devtools/skin/images/profiler-stopwatch.svg) no-repeat;
filter: var(--icon-filter);
width: 16px;
height: 16px;
opacity: 0.8;
}
.requests-list-network-summary-button:hover > .summary-info-icon {
opacity: 1;
}
-/* Performance analysis view */
+/* Statistics panel */
.statistics-panel {
display: flex;
height: 100vh;
}
.statistics-panel .devtools-toolbarbutton.back-button {
min-width: 4em;
@@ -1258,17 +1245,17 @@ body,
text-align: start;
}
.table-chart-totals {
display: flex;
flex-direction: column;
}
-/* Firebug theme support for network charts */
+/* Firebug theme support for statistics panel 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);
}
.theme-firebug .chart-colored-blob[name=css] {
fill: rgba(70, 175, 227, 0.8); /* light blue */
@@ -1300,90 +1287,44 @@ body,
background: rgba(235, 235, 84, 0.8);
}
.theme-firebug .chart-colored-blob[name=flash] {
fill: rgba(84, 235, 159, 0.8); /* cyan */
background: rgba(84, 235, 159, 0.8);
}
-/* Responsive sidebar */
+/* Responsive web design support */
+
@media (max-width: 700px) {
- .requests-list-toolbar {
- height: 22px;
- }
-
.requests-list-header-button {
- min-height: 22px;
- padding-left: 8px;
- }
-
- .requests-list-status {
- max-width: none;
+ padding-inline-start: 8px;
}
.requests-list-status-code {
width: auto;
}
- .requests-list-method,
- .requests-list-method-box {
- max-width: none;
- width: 12vw;
- }
-
- .requests-list-icon-and-file {
- width: 22vw;
- }
-
- .requests-list-security-and-domain {
- width: 16vw;
- }
-
- .requests-list-remoteip {
- width: 8vw;
- }
-
- .requests-list-cause,
- .requests-list-type,
- .requests-list-transferred,
.requests-list-size {
- max-width: none;
- width: 8vw;
+ /* Given a fix max-width to display all columns in RWD mode */
+ max-width: 7%;
}
.requests-list-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: 100%;
height: 1px;
}
-}
-/* Platform overrides (copied in from the old platform specific files) */
-
-:root[platform="win"] .requests-list-timings-division {
- padding-top: 1px;
- font-size: 90%;
-}
-
-:root[platform="linux"] #headers-summary-resend {
- padding: 4px;
-}
-
-:root[platform="linux"] #toggle-raw-headers {
- padding: 4px;
-}
-
-/* Responsive sidebar */
-@media (max-width: 700px) {
:root[platform="linux"] .requests-list-header-button {
font-size: 85%;
}
}
+
--- a/devtools/client/netmonitor/src/components/request-list-column-cause.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-cause.js
@@ -5,58 +5,50 @@
"use strict";
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
-const { div, span } = DOM;
+const { div } = DOM;
const RequestListColumnCause = createClass({
displayName: "RequestListColumnCause",
propTypes: {
item: PropTypes.object.isRequired,
onCauseBadgeClick: PropTypes.func.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.cause !== nextProps.item.cause;
},
render() {
- const {
- item,
+ let {
+ item: { cause },
onCauseBadgeClick,
} = this.props;
- const { cause } = item;
-
- let causeType = "";
- let causeUri = undefined;
+ 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";
- causeUri = cause.loadingDocumentUri;
causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
}
return (
- div({
- className: "requests-list-subitem requests-list-cause",
- title: causeUri,
- },
- span({
+ div({ className: "requests-list-column requests-list-cause", title: causeType },
+ causeHasStack && div({
className: "requests-list-cause-stack",
- hidden: !causeHasStack,
onClick: onCauseBadgeClick,
}, "JS"),
- span({ className: "subitem-label" }, causeType),
+ causeType
)
);
}
});
module.exports = RequestListColumnCause;
--- a/devtools/client/netmonitor/src/components/request-list-column-content-size.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-content-size.js
@@ -6,41 +6,31 @@
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { getFormattedSize } = require("../utils/format-utils");
-const { div, span } = DOM;
+const { div } = DOM;
const RequestListColumnContentSize = createClass({
displayName: "RequestListColumnContentSize",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.contentSize !== nextProps.item.contentSize;
},
render() {
- const { contentSize } = this.props.item;
-
- let text;
- if (typeof contentSize == "number") {
- text = getFormattedSize(contentSize);
- }
-
+ let { contentSize } = this.props.item;
+ let size = typeof contentSize === "number" ? getFormattedSize(contentSize) : null;
return (
- div({
- className: "requests-list-subitem subitem-label requests-list-size",
- title: text,
- },
- span({ className: "subitem-label" }, text),
- )
+ div({ className: "requests-list-column requests-list-size", title: size }, size)
);
}
});
module.exports = RequestListColumnContentSize;
--- a/devtools/client/netmonitor/src/components/request-list-column-domain.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-domain.js
@@ -7,58 +7,57 @@
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");
-const { div, span } = DOM;
+const { div } = DOM;
const UPDATED_DOMAIN_PROPS = [
- "urlDetails",
"remoteAddress",
"securityState",
+ "urlDetails",
];
const RequestListColumnDomain = createClass({
displayName: "RequestListColumnDomain",
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 { item, onSecurityIconClick } = this.props;
+ let { remoteAddress, securityState, urlDetails: { host, isLocal } } = item;
let iconClassList = ["requests-security-state-icon"];
let iconTitle;
- if (urlDetails.isLocal) {
+ let title = host + (remoteAddress ? ` (${remoteAddress})` : "");
+
+ 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}`);
}
- let title = urlDetails.host + (remoteAddress ? ` (${remoteAddress})` : "");
-
return (
- div({ className: "requests-list-subitem requests-list-security-and-domain" },
+ div({ className: "requests-list-column requests-list-domain", title },
div({
className: iconClassList.join(" "),
+ onMouseDown: onSecurityIconClick,
title: iconTitle,
- onClick: onSecurityIconClick,
}),
- span({ className: "subitem-label requests-list-domain", title }, urlDetails.host),
+ host,
)
);
}
});
module.exports = RequestListColumnDomain;
--- a/devtools/client/netmonitor/src/components/request-list-column-file.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-file.js
@@ -9,46 +9,43 @@ const {
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { propertiesEqual } = require("../utils/request-utils");
const { div, img } = DOM;
const UPDATED_FILE_PROPS = [
+ "responseContentDataUri",
"urlDetails",
- "responseContentDataUri",
];
const RequestListColumnFile = createClass({
displayName: "RequestListColumnFile",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
},
render() {
- const { urlDetails, responseContentDataUri } = this.props.item;
+ let { responseContentDataUri, urlDetails } = this.props.item;
return (
- div({ className: "requests-list-subitem requests-list-icon-and-file" },
+ div({
+ className: "requests-list-column requests-list-file",
+ title: urlDetails.unicodeUrl,
+ },
img({
className: "requests-list-icon",
src: responseContentDataUri,
- hidden: !responseContentDataUri,
"data-type": responseContentDataUri ? "thumbnail" : undefined,
}),
- div({
- className: "subitem-label requests-list-file",
- title: urlDetails.unicodeUrl,
- },
- urlDetails.baseNameWithQuery,
- ),
+ urlDetails.baseNameWithQuery
)
);
}
});
module.exports = RequestListColumnFile;
--- a/devtools/client/netmonitor/src/components/request-list-column-method.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-method.js
@@ -5,32 +5,28 @@
"use strict";
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
-const { div, span } = DOM;
+const { div } = DOM;
const RequestListColumnMethod = createClass({
displayName: "RequestListColumnMethod",
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-list-subitem requests-list-method-box" },
- span({ className: "subitem-label requests-list-method" }, method)
- )
- );
+ let { method } = this.props.item;
+ return div({ className: "requests-list-column requests-list-method" }, method);
}
});
module.exports = RequestListColumnMethod;
--- a/devtools/client/netmonitor/src/components/request-list-column-protocol.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-protocol.js
@@ -5,32 +5,35 @@
"use strict";
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
-const { div, span } = DOM;
+const { div } = DOM;
const RequestListColumnProtocol = createClass({
displayName: "RequestListColumnProtocol",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.httpVersion !== nextProps.item.httpVersion;
},
render() {
- const { httpVersion } = this.props.item;
+ let { httpVersion = "" } = this.props.item;
return (
- div({ className: "requests-list-subitem requests-list-protocol" },
- span({ className: "subitem-label", title: httpVersion }, httpVersion),
+ div({
+ className: "requests-list-column requests-list-protocol",
+ title: httpVersion,
+ },
+ httpVersion
)
);
}
});
module.exports = RequestListColumnProtocol;
--- a/devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
@@ -5,34 +5,34 @@
"use strict";
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
-const { div, span } = DOM;
+const { div } = DOM;
const RequestListColumnRemoteIP = createClass({
displayName: "RequestListColumnRemoteIP",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.remoteAddress !== nextProps.item.remoteAddress;
},
render() {
- const { remoteAddress, remotePort } = this.props.item;
- let remoteSummary = remoteAddress ? `${remoteAddress}:${remotePort}` : "";
+ let { remoteAddress, remotePort } = this.props.item;
+ let remoteIP = remoteAddress ? `${remoteAddress}:${remotePort}` : "unknown";
return (
- div({ className: "requests-list-subitem requests-list-remoteip" },
- span({ className: "subitem-label", title: remoteSummary }, remoteSummary),
+ div({ className: "requests-list-column requests-list-remoteip", title: remoteIP },
+ remoteIP
)
);
}
});
module.exports = RequestListColumnRemoteIP;
--- a/devtools/client/netmonitor/src/components/request-list-column-status.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-status.js
@@ -7,17 +7,17 @@
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");
-const { div, span } = DOM;
+const { div } = DOM;
const UPDATED_STATUS_PROPS = [
"fromCache",
"fromServiceWorker",
"status",
"statusText",
];
@@ -28,18 +28,17 @@ const RequestListColumnStatus = createCl
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 { fromCache, fromServiceWorker, status, statusText } = this.props.item;
let code, title;
if (status) {
if (fromCache) {
code = "cached";
} else if (fromServiceWorker) {
code = "service worker";
} else {
@@ -59,17 +58,17 @@ const RequestListColumnStatus = createCl
} else {
title = L10N.getFormatStr("netmonitor.status.tooltip.simple",
status, statusText);
}
}
}
return (
- div({ className: "requests-list-subitem requests-list-status", title },
+ div({ className: "requests-list-column requests-list-status", title },
div({ className: "requests-list-status-icon", "data-code": code }),
- span({ className: "subitem-label requests-list-status-code" }, status)
+ div({ className: "requests-list-status-code" }, status)
)
);
}
});
module.exports = RequestListColumnStatus;
--- a/devtools/client/netmonitor/src/components/request-list-column-transferred-size.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-transferred-size.js
@@ -8,17 +8,17 @@ const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { getFormattedSize } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");
-const { div, span } = DOM;
+const { div } = DOM;
const UPDATED_TRANSFERRED_PROPS = [
"transferredSize",
"fromCache",
"fromServiceWorker",
];
const RequestListColumnTransferredSize = createClass({
@@ -28,36 +28,30 @@ const RequestListColumnTransferredSize =
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_TRANSFERRED_PROPS, this.props.item, nextProps.item);
},
render() {
- const { transferredSize, fromCache, fromServiceWorker, status } = this.props.item;
+ let { fromCache, fromServiceWorker, status, transferredSize } = this.props.item;
+ let text;
- let text;
- let className = "subitem-label";
if (fromCache || status === "304") {
text = L10N.getStr("networkMenu.sizeCached");
- className += " theme-comment";
} else if (fromServiceWorker) {
text = L10N.getStr("networkMenu.sizeServiceWorker");
- className += " theme-comment";
} else if (typeof transferredSize == "number") {
text = getFormattedSize(transferredSize);
} else if (transferredSize === null) {
text = L10N.getStr("networkMenu.sizeUnavailable");
}
return (
- div({
- className: "requests-list-subitem requests-list-transferred",
- title: text,
- },
- span({ className }, text),
+ div({ className: "requests-list-column requests-list-transferred", title: text },
+ text
)
);
}
});
module.exports = RequestListColumnTransferredSize;
--- a/devtools/client/netmonitor/src/components/request-list-column-type.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-type.js
@@ -6,40 +6,41 @@
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { getAbbreviatedMimeType } = require("../utils/request-utils");
-const { div, span } = DOM;
+const { div } = DOM;
const RequestListColumnType = createClass({
displayName: "RequestListColumnType",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.mimeType !== nextProps.item.mimeType;
},
render() {
- const { mimeType } = this.props.item;
+ let { mimeType } = this.props.item;
let abbrevType;
+
if (mimeType) {
abbrevType = getAbbreviatedMimeType(mimeType);
}
return (
div({
- className: "requests-list-subitem requests-list-type",
+ className: "requests-list-column requests-list-type",
title: mimeType,
},
- span({ className: "subitem-label" }, abbrevType),
+ abbrevType
)
);
}
});
module.exports = RequestListColumnType;
--- a/devtools/client/netmonitor/src/components/request-list-column-waterfall.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-waterfall.js
@@ -11,85 +11,88 @@ const {
} = require("devtools/client/shared/vendor/react");
const { L10N } = require("../utils/l10n");
const { propertiesEqual } = require("../utils/request-utils");
const { div } = DOM;
const UPDATED_WATERFALL_PROPS = [
"eventTimings",
- "totalTime",
"fromCache",
"fromServiceWorker",
+ "totalTime",
];
+// List of properties of the timing info we want to create boxes for
+const TIMING_KEYS = ["blocked", "dns", "connect", "send", "wait", "receive"];
const RequestListColumnWaterfall = createClass({
displayName: "RequestListColumnWaterfall",
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);
+ return !propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item) ||
+ this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis;
},
render() {
- const { item, firstRequestStartedMillis } = this.props;
+ let { firstRequestStartedMillis, item } = this.props;
return (
- div({ className: "requests-list-subitem requests-list-waterfall" },
+ div({ className: "requests-list-column requests-list-waterfall" },
div({
className: "requests-list-timings",
style: {
paddingInlineStart: `${item.startedMillis - firstRequestStartedMillis}px`,
},
},
timingBoxes(item),
)
)
);
}
});
-// List of properties of the timing info we want to create boxes for
-const TIMING_KEYS = ["blocked", "dns", "connect", "send", "wait", "receive"];
-
function timingBoxes(item) {
- const { eventTimings, totalTime, fromCache, fromServiceWorker } = item;
+ let { eventTimings, fromCache, fromServiceWorker, totalTime } = item;
let boxes = [];
if (fromCache || fromServiceWorker) {
return boxes;
}
if (eventTimings) {
// Add a set of boxes representing timing information.
for (let key of TIMING_KEYS) {
let width = eventTimings.timings[key];
// Don't render anything if it surely won't be visible.
// One millisecond == one unscaled pixel.
if (width > 0) {
- boxes.push(div({
- key,
- className: "requests-list-timings-box " + key,
- style: { width }
- }));
+ boxes.push(
+ div({
+ key,
+ className: `requests-list-timings-box ${key}`,
+ style: { width },
+ })
+ );
}
}
}
if (typeof totalTime === "number") {
- let text = L10N.getFormatStr("networkMenu.totalMS", totalTime);
- boxes.push(div({
- key: "total",
- className: "requests-list-timings-total",
- title: text
- }, text));
+ let title = L10N.getFormatStr("networkMenu.totalMS", totalTime);
+ boxes.push(
+ div({
+ key: "total",
+ className: "requests-list-timings-total",
+ title,
+ }, title)
+ );
}
return boxes;
}
module.exports = RequestListColumnWaterfall;
--- a/devtools/client/netmonitor/src/components/request-list-content.js
+++ b/devtools/client/netmonitor/src/components/request-list-content.js
@@ -149,17 +149,17 @@ const RequestListContent = createClass({
if (!itemId) {
return false;
}
let requestItem = this.props.displayedRequests.find(r => r.id == itemId);
if (!requestItem) {
return false;
}
- if (requestItem.responseContent && target.closest(".requests-list-icon-and-file")) {
+ if (requestItem.responseContent && target.closest(".requests-list-icon")) {
return setTooltipImageContent(tooltip, itemEl, requestItem);
}
return false;
},
/**
* Scroll listener for the requests menu view.
@@ -219,43 +219,47 @@ const RequestListContent = createClass({
this.shouldScrollBottom = false;
},
render() {
const {
columns,
displayedRequests,
firstRequestStartedMillis,
- selectedRequestId,
onCauseBadgeClick,
onItemMouseDown,
onSecurityIconClick,
+ selectedRequestId,
} = this.props;
return (
- div({
- ref: "contentEl",
- className: "requests-list-contents",
- tabIndex: 0,
- onKeyDown: this.onKeyDown,
- },
- displayedRequests.map((item, index) => RequestListItem({
- firstRequestStartedMillis,
- fromCache: item.status === "304" || item.fromCache,
- columns,
- item,
- index,
- isSelected: item.id === selectedRequestId,
- key: item.id,
- onContextMenu: this.onContextMenu,
- onFocusedNodeChange: this.onFocusedNodeChange,
- onMouseDown: () => onItemMouseDown(item.id),
- onCauseBadgeClick: () => onCauseBadgeClick(item.cause),
- onSecurityIconClick: () => onSecurityIconClick(item.securityState),
- }))
+ div({ className: "requests-list-wrapper"},
+ div({ className: "requests-list-table"},
+ div({
+ ref: "contentEl",
+ className: "requests-list-contents",
+ tabIndex: 0,
+ onKeyDown: this.onKeyDown,
+ },
+ displayedRequests.map((item, index) => RequestListItem({
+ firstRequestStartedMillis,
+ fromCache: item.status === "304" || item.fromCache,
+ columns,
+ item,
+ index,
+ isSelected: item.id === selectedRequestId,
+ key: item.id,
+ onContextMenu: this.onContextMenu,
+ onFocusedNodeChange: this.onFocusedNodeChange,
+ onMouseDown: () => onItemMouseDown(item.id),
+ onCauseBadgeClick: () => onCauseBadgeClick(item.cause),
+ onSecurityIconClick: () => onSecurityIconClick(item.securityState),
+ }))
+ )
+ )
)
);
},
});
module.exports = connect(
(state) => ({
columns: state.ui.columns,
--- a/devtools/client/netmonitor/src/components/request-list-header.js
+++ b/devtools/client/netmonitor/src/components/request-list-header.js
@@ -6,51 +6,49 @@
const {
createClass,
PropTypes,
DOM,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index");
+const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
const { getWaterfallScale } = require("../selectors/index");
const { getFormattedTime } = require("../utils/format-utils");
-const { HEADERS } = require("../constants");
const { L10N } = require("../utils/l10n");
const WaterfallBackground = require("../waterfall-background");
const RequestListHeaderContextMenu = require("../request-list-header-context-menu");
const { div, button } = DOM;
-const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5; // ms
-const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60; // px
-
/**
* 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.
*/
const RequestListHeader = createClass({
displayName: "RequestListHeader",
propTypes: {
columns: PropTypes.object.isRequired,
- dispatch: PropTypes.func.isRequired,
- sort: PropTypes.object,
+ 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,
- onHeaderClick: PropTypes.func.isRequired,
- resizeWaterfall: PropTypes.func.isRequired,
},
componentWillMount() {
- const { dispatch } = this.props;
+ const { resetColumns, toggleColumn } = this.props;
this.contextMenu = new RequestListHeaderContextMenu({
- toggleColumn: (column) => dispatch(Actions.toggleColumn(column)),
- resetColumns: () => dispatch(Actions.resetColumns()),
+ resetColumns,
+ toggleColumn,
});
},
componentDidMount() {
// Create the object that takes care of drawing the waterfall canvas background
this.background = new WaterfallBackground(document);
this.background.draw(this.props);
this.resizeWaterfall();
@@ -68,85 +66,87 @@ const RequestListHeader = createClass({
},
onContextMenu(evt) {
evt.preventDefault();
this.contextMenu.open(evt);
},
resizeWaterfall() {
- // Measure its width and update the 'waterfallWidth' property in the store.
- // The 'waterfallWidth' will be further updated on every window resize.
- setTimeout(() => {
- let { width } = this.refs.header.getBoundingClientRect();
- this.props.resizeWaterfall(width);
- }, 50);
+ 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.
+ setTimeout(() => {
+ this.props.resizeWaterfall(waterfallHeader.getBoundingClientRect().width);
+ }, 500);
+ }
},
render() {
- const { sort, scale, waterfallWidth, onHeaderClick, columns } = this.props;
+ let { columns, scale, sort, sortBy, waterfallWidth } = this.props;
- return div(
- { className: "devtools-toolbar requests-list-toolbar" },
- div({ className: "toolbar-labels" },
- HEADERS.filter(h => columns.get(h.name)).map(header => {
- const name = header.name;
- const boxName = header.boxName || name;
- const label = L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
+ return (
+ 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 = L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
+ let sorted, sortedTitle;
+ let active = sort.type == name ? true : undefined;
- let sorted, sortedTitle;
- const active = sort.type == name ? true : undefined;
if (active) {
sorted = sort.ascending ? "ascending" : "descending";
sortedTitle = L10N.getStr(sort.ascending
? "networkMenu.sortedAsc"
: "networkMenu.sortedDesc");
}
- return div(
- {
+ return (
+ div({
id: `requests-list-${boxName}-header-box`,
- className: `requests-list-header requests-list-${boxName}`,
+ className: `requests-list-column requests-list-${boxName}`,
key: name,
- ref: "header",
+ ref: `${name}Header`,
// Used to style the next column.
"data-active": active,
onContextMenu: this.onContextMenu,
},
- button(
- {
+ button({
id: `requests-list-${name}-button`,
- className: `requests-list-header-button requests-list-${name}`,
+ className: `requests-list-header-button`,
"data-sorted": sorted,
title: sortedTitle,
- onClick: () => onHeaderClick(name),
+ onClick: () => sortBy(name),
},
- name == "waterfall" ? WaterfallLabel(waterfallWidth, scale, label)
- : div({ className: "button-text" }, label),
- div({ className: "button-icon" })
+ 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 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) {
+ 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";
@@ -180,31 +180,32 @@ function waterfallDivisionLabels(waterfa
}
return labels;
}
function WaterfallLabel(waterfallWidth, scale, label) {
let className = "button-text requests-list-waterfall-label-wrapper";
- if (waterfallWidth != null && scale != null) {
+ if (waterfallWidth !== null && scale !== null) {
label = waterfallDivisionLabels(waterfallWidth, scale);
className += " requests-list-waterfall-visible";
}
return div({ className }, label);
}
module.exports = connect(
- state => ({
+ (state) => ({
columns: state.ui.columns,
- sort: state.sort,
- scale: getWaterfallScale(state),
- waterfallWidth: state.ui.waterfallWidth,
firstRequestStartedMillis: state.requests.firstStartedMillis,
+ scale: getWaterfallScale(state),
+ sort: state.sort,
timingMarkers: state.timingMarkers,
+ waterfallWidth: state.ui.waterfallWidth,
}),
- dispatch => ({
- dispatch,
- onHeaderClick: type => dispatch(Actions.sortBy(type)),
- resizeWaterfall: width => dispatch(Actions.resizeWaterfall(width)),
+ (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/components/request-list-item.js
+++ b/devtools/client/netmonitor/src/components/request-list-item.js
@@ -6,17 +6,16 @@
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const I = require("devtools/client/shared/vendor/immutable");
-
const { propertiesEqual } = require("../utils/request-utils");
// Components
const RequestListColumnCause = createFactory(require("./request-list-column-cause"));
const RequestListColumnContentSize = createFactory(require("./request-list-column-content-size"));
const RequestListColumnDomain = createFactory(require("./request-list-column-domain"));
const RequestListColumnFile = createFactory(require("./request-list-column-file"));
const RequestListColumnMethod = createFactory(require("./request-list-column-method"));
@@ -50,19 +49,20 @@ const UPDATED_REQ_ITEM_PROPS = [
"cause",
"contentSize",
"transferredSize",
"startedMillis",
"totalTime",
];
const UPDATED_REQ_PROPS = [
+ "firstRequestStartedMillis",
"index",
"isSelected",
- "firstRequestStartedMillis",
+ "waterfallWidth",
];
/**
* Render one row in the request list.
*/
const RequestListItem = createClass({
displayName: "RequestListItem",
@@ -73,67 +73,61 @@ const RequestListItem = createClass({
isSelected: PropTypes.bool.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
fromCache: PropTypes.bool,
onCauseBadgeClick: PropTypes.func.isRequired,
onContextMenu: PropTypes.func.isRequired,
onFocusedNodeChange: PropTypes.func,
onMouseDown: PropTypes.func.isRequired,
onSecurityIconClick: PropTypes.func.isRequired,
+ waterfallWidth: PropTypes.number,
},
componentDidMount() {
if (this.props.isSelected) {
- this.refs.el.focus();
+ this.refs.listItem.focus();
}
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_REQ_ITEM_PROPS, this.props.item, nextProps.item) ||
!propertiesEqual(UPDATED_REQ_PROPS, this.props, nextProps) ||
!I.is(this.props.columns, nextProps.columns);
},
componentDidUpdate(prevProps) {
if (!prevProps.isSelected && this.props.isSelected) {
- this.refs.el.focus();
+ this.refs.listItem.focus();
if (this.props.onFocusedNodeChange) {
this.props.onFocusedNodeChange();
}
}
},
render() {
- const {
+ let {
columns,
item,
index,
isSelected,
firstRequestStartedMillis,
fromCache,
onContextMenu,
onMouseDown,
onCauseBadgeClick,
- onSecurityIconClick
+ onSecurityIconClick,
} = this.props;
- let classList = ["request-list-item"];
- if (isSelected) {
- classList.push("selected");
- }
-
- if (fromCache) {
- classList.push("fromCache");
- }
-
- classList.push(index % 2 ? "odd" : "even");
+ let classList = ["request-list-item", index % 2 ? "odd" : "even"];
+ isSelected && classList.push("selected");
+ fromCache && classList.push("fromCache");
return (
div({
- ref: "el",
+ ref: "listItem",
className: classList.join(" "),
"data-id": item.id,
tabIndex: 0,
onContextMenu,
onMouseDown,
},
columns.get("status") && RequestListColumnStatus({ item }),
columns.get("method") && RequestListColumnMethod({ item }),
--- a/devtools/client/netmonitor/src/components/source-editor.js
+++ b/devtools/client/netmonitor/src/components/source-editor.js
@@ -5,19 +5,19 @@
"use strict";
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const Editor = require("devtools/client/sourceeditor/editor");
+const { SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE } = require("../constants");
const { div } = DOM;
-const SYNTAX_HIGHLIGHT_MAX_SIZE = 51200;
/**
* CodeMirror editor as a React component
*/
const SourceEditor = createClass({
displayName: "SourceEditor",
propTypes: {
@@ -28,32 +28,33 @@ const SourceEditor = createClass({
},
componentDidMount() {
const { mode, text } = this.props;
this.editor = new Editor({
lineNumbers: true,
lineWrapping: false,
- mode: text.length < SYNTAX_HIGHLIGHT_MAX_SIZE ? mode : null,
+ mode: text.length < SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE ? mode : null,
readOnly: true,
theme: "mozilla",
value: text,
});
// Delay to CodeMirror initialization content to prevent UI freezed
this.editorTimeout = setTimeout(() => {
this.editor.appendToLocalElement(this.refs.editorElement);
});
},
componentDidUpdate(prevProps) {
const { mode, text } = this.props;
- if (prevProps.mode !== mode && text.length < SYNTAX_HIGHLIGHT_MAX_SIZE) {
+ if (prevProps.mode !== mode &&
+ text.length < SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE) {
this.editor.setMode(mode);
}
if (prevProps.text !== text) {
this.editor.setText(text);
}
},
--- a/devtools/client/netmonitor/src/components/status-bar.js
+++ b/devtools/client/netmonitor/src/components/status-bar.js
@@ -5,30 +5,28 @@
"use strict";
const {
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { PluralForm } = require("devtools/shared/plural-form");
-
const Actions = require("../actions/index");
const {
getDisplayedRequestsSummary,
getDisplayedTimingMarker,
} = require("../selectors/index");
const {
getFormattedSize,
- getFormattedTime
+ getFormattedTime,
} = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
-// Components
-const { div, button, span } = DOM;
+const { button, div } = DOM;
function StatusBar({ summary, openStatistics, timingMarkers }) {
let { count, contentSize, transferredSize, millis } = summary;
let {
DOMContentLoaded,
load,
} = timingMarkers;
@@ -37,39 +35,42 @@ function StatusBar({ summary, openStatis
count, L10N.getFormatStrWithNumbers("networkMenu.summary.requestsCount", count)
);
let transferText = L10N.getFormatStrWithNumbers("networkMenu.summary.transferred",
getFormattedSize(contentSize), getFormattedSize(transferredSize));
let finishText = L10N.getFormatStrWithNumbers("networkMenu.summary.finish",
getFormattedTime(millis));
return (
- div({ className: "devtools-toolbar devtools-toolbar-bottom" },
+ div({ className: "devtools-toolbar devtools-status-bottom" },
button({
className: "devtools-button requests-list-network-summary-button",
onClick: openStatistics,
},
- span({ className: "summary-info-icon" }),
+ div({ className: "summary-info-icon" }),
+ ),
+ div({ className: "status-bar-label requests-list-network-summary-count" },
+ countText
),
- span({ className: "status-bar-label requests-list-network-summary-count" },
- countText),
count !== 0 &&
- span({ className: "status-bar-label requests-list-network-summary-transfer" },
- transferText),
+ div({ className: "status-bar-label requests-list-network-summary-transfer" },
+ transferText
+ ),
count !== 0 &&
- span({ className: "status-bar-label requests-list-network-summary-finish" },
- finishText),
-
+ div({ className: "status-bar-label requests-list-network-summary-finish" },
+ finishText
+ ),
DOMContentLoaded > -1 &&
- span({ className: "status-bar-label dom-content-loaded" },
- `DOMContentLoaded: ${getFormattedTime(DOMContentLoaded)}`),
-
+ div({ className: "status-bar-label dom-content-loaded" },
+ `DOMContentLoaded: ${getFormattedTime(DOMContentLoaded)}`
+ ),
load > -1 &&
- span({ className: "status-bar-label load" },
- `load: ${getFormattedTime(load)}`),
+ div({ className: "status-bar-label load" },
+ `load: ${getFormattedTime(load)}`
+ ),
)
);
}
StatusBar.displayName = "StatusBar";
StatusBar.propTypes = {
openStatistics: PropTypes.func.isRequired,
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -118,26 +118,24 @@ const HEADERS = [
filterKey: "status-code"
},
{
name: "method",
canFilter: true,
},
{
name: "file",
- boxName: "icon-and-file",
canFilter: false,
},
{
name: "protocol",
canFilter: true,
},
{
name: "domain",
- boxName: "security-and-domain",
canFilter: true,
},
{
name: "remoteip",
canFilter: true,
filterKey: "remote-ip",
},
{
@@ -159,19 +157,36 @@ const HEADERS = [
canFilter: true,
},
{
name: "waterfall",
canFilter: false,
}
];
+const REQUESTS_WATERFALL = {
+ BACKGROUND_TICKS_MULTIPLE: 5, // ms
+ BACKGROUND_TICKS_SCALES: 3,
+ BACKGROUND_TICKS_SPACING_MIN: 10, // px
+ BACKGROUND_TICKS_COLOR_RGB: [128, 136, 144],
+ // 8-bit value of the alpha component of the tick color
+ BACKGROUND_TICKS_OPACITY_MIN: 32,
+ BACKGROUND_TICKS_OPACITY_ADD: 32,
+ // RGBA colors for the timing markers
+ DOMCONTENTLOADED_TICKS_COLOR_RGBA: [0, 0, 255, 128],
+ HEADER_TICKS_MULTIPLE: 5, // ms
+ HEADER_TICKS_SPACING_MIN: 60, // px
+ LOAD_TICKS_COLOR_RGBA: [255, 0, 0, 128],
+ // Reserve extra space for rendering waterfall time label
+ LABEL_WIDTH: 50, // px
+};
+
const general = {
ACTIVITY_TYPE,
EVENTS,
FILTER_SEARCH_DELAY: 200,
HEADERS,
- // 100 KB in bytes
- SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE: 102400,
+ SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE: 51200, // 50 KB in bytes
+ REQUESTS_WATERFALL,
};
// flatten constants
module.exports = Object.assign({}, general, actionTypes);
--- a/devtools/client/netmonitor/src/reducers/ui.js
+++ b/devtools/client/netmonitor/src/reducers/ui.js
@@ -35,25 +35,22 @@ const Columns = I.Record({
const UI = I.Record({
columns: new Columns(),
detailsPanelSelectedTab: "headers",
networkDetailsOpen: false,
statisticsOpen: false,
waterfallWidth: null,
});
-// Safe bounds for waterfall width (px)
-const REQUESTS_WATERFALL_SAFE_BOUNDS = 90;
-
function resetColumns(state) {
return state.set("columns", new Columns());
}
function resizeWaterfall(state, action) {
- return state.set("waterfallWidth", action.width - REQUESTS_WATERFALL_SAFE_BOUNDS);
+ return state.set("waterfallWidth", action.width);
}
function openNetworkDetails(state, action) {
return state.set("networkDetailsOpen", action.open);
}
function openStatistics(state, action) {
return state.set("statisticsOpen", action.open);
--- a/devtools/client/netmonitor/src/selectors/ui.js
+++ b/devtools/client/netmonitor/src/selectors/ui.js
@@ -1,36 +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 { REQUESTS_WATERFALL } = require("../constants");
const { getDisplayedRequests } = require("./requests");
function isNetworkDetailsToggleButtonDisabled(state) {
return getDisplayedRequests(state).isEmpty();
}
const EPSILON = 0.001;
function getWaterfallScale(state) {
const { requests, timingMarkers, ui } = state;
- if (requests.firstStartedMillis == +Infinity) {
- return null;
- }
-
- if (ui.waterfallWidth == null) {
+ if (requests.firstStartedMillis === +Infinity || ui.waterfallWidth === null) {
return null;
}
const lastEventMillis = Math.max(requests.lastEndedMillis,
timingMarkers.firstDocumentDOMContentLoadedTimestamp,
timingMarkers.firstDocumentLoadTimestamp);
const longestWidth = lastEventMillis - requests.firstStartedMillis;
- return Math.min(Math.max(ui.waterfallWidth / longestWidth, EPSILON), 1);
+ return Math.min(Math.max(
+ (ui.waterfallWidth - REQUESTS_WATERFALL.LABEL_WIDTH) / longestWidth, EPSILON), 1);
}
module.exports = {
isNetworkDetailsToggleButtonDisabled,
getWaterfallScale,
};
--- a/devtools/client/netmonitor/src/waterfall-background.js
+++ b/devtools/client/netmonitor/src/waterfall-background.js
@@ -1,31 +1,22 @@
/* 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 { REQUESTS_WATERFALL } = require("./constants");
+
const HTML_NS = "http://www.w3.org/1999/xhtml";
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE = 5; // ms
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES = 3;
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN = 10; // px
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB = [128, 136, 144];
-// 8-bit value of the alpha component of the tick color
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN = 32;
-const REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD = 32;
-// RGBA colors for the timing markers
-const REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA = [0, 0, 255, 128];
-const REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA = [255, 0, 0, 128];
-
const STATE_KEYS = [
+ "firstRequestStartedMillis",
"scale",
+ "timingMarkers",
"waterfallWidth",
- "firstRequestStartedMillis",
- "timingMarkers",
];
/**
* Creates the background displayed on each waterfall view in this container.
*/
function WaterfallBackground() {
this.canvas = document.createElementNS(HTML_NS, "canvas");
this.ctx = this.canvas.getContext("2d");
@@ -57,76 +48,77 @@ WaterfallBackground.prototype = {
this.prevState = state;
if (state.waterfallWidth === null || state.scale === null) {
setImageElement("waterfall-background", null);
return;
}
// Nuke the context.
- let canvasWidth = this.canvas.width = state.waterfallWidth;
+ let canvasWidth = this.canvas.width =
+ state.waterfallWidth - REQUESTS_WATERFALL.LABEL_WIDTH;
// Awww yeah, 1px, repeats on Y axis.
let canvasHeight = this.canvas.height = 1;
// Start over.
let imageData = this.ctx.createImageData(canvasWidth, canvasHeight);
let pixelArray = imageData.data;
let buf = new ArrayBuffer(pixelArray.length);
let view8bit = new Uint8ClampedArray(buf);
let view32bit = new Uint32Array(buf);
// Build new millisecond tick lines...
- let timingStep = REQUESTS_WATERFALL_BACKGROUND_TICKS_MULTIPLE;
+ let timingStep = REQUESTS_WATERFALL.BACKGROUND_TICKS_MULTIPLE;
let optimalTickIntervalFound = false;
let scaledStep;
while (!optimalTickIntervalFound) {
// Ignore any divisions that would end up being too close to each other.
scaledStep = state.scale * timingStep;
- if (scaledStep < REQUESTS_WATERFALL_BACKGROUND_TICKS_SPACING_MIN) {
+ if (scaledStep < REQUESTS_WATERFALL.BACKGROUND_TICKS_SPACING_MIN) {
timingStep <<= 1;
continue;
}
optimalTickIntervalFound = true;
}
const isRTL = isDocumentRTL(document);
- const [r, g, b] = REQUESTS_WATERFALL_BACKGROUND_TICKS_COLOR_RGB;
- let alphaComponent = REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_MIN;
+ const [r, g, b] = REQUESTS_WATERFALL.BACKGROUND_TICKS_COLOR_RGB;
+ let alphaComponent = REQUESTS_WATERFALL.BACKGROUND_TICKS_OPACITY_MIN;
function drawPixelAt(offset, color) {
let position = (isRTL ? canvasWidth - offset : offset - 1) | 0;
let [rc, gc, bc, ac] = color;
view32bit[position] = (ac << 24) | (bc << 16) | (gc << 8) | rc;
}
// Insert one pixel for each division on each scale.
- for (let i = 1; i <= REQUESTS_WATERFALL_BACKGROUND_TICKS_SCALES; i++) {
+ for (let i = 1; i <= REQUESTS_WATERFALL.BACKGROUND_TICKS_SCALES; i++) {
let increment = scaledStep * Math.pow(2, i);
for (let x = 0; x < canvasWidth; x += increment) {
drawPixelAt(x, [r, g, b, alphaComponent]);
}
- alphaComponent += REQUESTS_WATERFALL_BACKGROUND_TICKS_OPACITY_ADD;
+ alphaComponent += REQUESTS_WATERFALL.BACKGROUND_TICKS_OPACITY_ADD;
}
function drawTimestamp(timestamp, color) {
if (timestamp === -1) {
return;
}
let delta = Math.floor((timestamp - state.firstRequestStartedMillis) * state.scale);
drawPixelAt(delta, color);
}
drawTimestamp(state.timingMarkers.firstDocumentDOMContentLoadedTimestamp,
- REQUESTS_WATERFALL_DOMCONTENTLOADED_TICKS_COLOR_RGBA);
+ REQUESTS_WATERFALL.DOMCONTENTLOADED_TICKS_COLOR_RGBA);
drawTimestamp(state.timingMarkers.firstDocumentLoadTimestamp,
- REQUESTS_WATERFALL_LOAD_TICKS_COLOR_RGBA);
+ REQUESTS_WATERFALL.LOAD_TICKS_COLOR_RGBA);
// Flush the image data and cache the waterfall background.
pixelArray.set(view8bit);
this.ctx.putImageData(imageData, 0, 0);
setImageElement("waterfall-background", this.canvas);
},