Bug 1349173 - Use div table layout to reduce reflow r?honza draft
authorRicky Chien <ricky060709@gmail.com>
Mon, 24 Apr 2017 11:50:30 +0800
changeset 566882 ee65d1e6826bf959da043853af32cbcccb11dc62
parent 566881 73752931e273091185e1e4b5231c28beed657cc8
child 566883 042ab153d6b4003a697fa296b0d8ec8502ec68ee
push id55366
push userbmo:rchien@mozilla.com
push dateMon, 24 Apr 2017 03:51:25 +0000
reviewershonza
bugs1349173
milestone55.0a1
Bug 1349173 - Use div table layout to reduce reflow r?honza MozReview-Commit-ID: G4KSJxejLVy
devtools/client/netmonitor/src/assets/styles/netmonitor.css
devtools/client/netmonitor/src/components/request-list-column-cause.js
devtools/client/netmonitor/src/components/request-list-column-content-size.js
devtools/client/netmonitor/src/components/request-list-column-domain.js
devtools/client/netmonitor/src/components/request-list-column-file.js
devtools/client/netmonitor/src/components/request-list-column-method.js
devtools/client/netmonitor/src/components/request-list-column-protocol.js
devtools/client/netmonitor/src/components/request-list-column-remote-ip.js
devtools/client/netmonitor/src/components/request-list-column-status.js
devtools/client/netmonitor/src/components/request-list-column-transferred-size.js
devtools/client/netmonitor/src/components/request-list-column-type.js
devtools/client/netmonitor/src/components/request-list-column-waterfall.js
devtools/client/netmonitor/src/components/request-list-content.js
devtools/client/netmonitor/src/components/request-list-header.js
devtools/client/netmonitor/src/components/request-list-item.js
devtools/client/netmonitor/src/components/source-editor.js
devtools/client/netmonitor/src/components/status-bar.js
devtools/client/netmonitor/src/constants.js
devtools/client/netmonitor/src/reducers/ui.js
devtools/client/netmonitor/src/selectors/ui.js
devtools/client/netmonitor/src/waterfall-background.js
--- 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);
   },