Bug 1423502 - Clean up leftover files under netmonitor/src/* r?honza draft
authorRicky Chien <ricky060709@gmail.com>
Wed, 06 Dec 2017 16:36:53 +0800
changeset 709466 38c0b7b3886bef210b13c7f59b99ae0161356669
parent 709416 457b0fe91e0d49a5bc35014fb6f86729cd5bac9b
child 709512 581e092e4a5bcd5b9f7b44952d97431d16b631c3
child 709554 c7642ff9ab1572668081aa8757380a7274ca95d5
child 709567 3e5a2289e8f4d80082f352908c7c5ef4907a4119
child 709568 1a939390d2686cff33e7829a8bd2a138751e1cf1
push id92656
push userbmo:rchien@mozilla.com
push dateFri, 08 Dec 2017 04:41:46 +0000
reviewershonza
bugs1423502
milestone59.0a1
Bug 1423502 - Clean up leftover files under netmonitor/src/* r?honza MozReview-Commit-ID: Kvm788NVFz
devtools/client/netmonitor/src/components/RequestListContent.js
devtools/client/netmonitor/src/components/RequestListHeader.js
devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
devtools/client/netmonitor/src/har/test/browser_net_har_post_data_on_get.js
devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
devtools/client/netmonitor/src/moz.build
devtools/client/netmonitor/src/request-list-context-menu.js
devtools/client/netmonitor/src/request-list-header-context-menu.js
devtools/client/netmonitor/src/request-list-tooltip.js
devtools/client/netmonitor/src/waterfall-background.js
devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js
devtools/client/netmonitor/src/widgets/WaterfallBackground.js
devtools/client/netmonitor/src/widgets/moz.build
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -4,33 +4,40 @@
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+const {
+  setImageTooltip,
+  getImageDimensions,
+} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
 const Actions = require("../actions/index");
-const { setTooltipImageContent } = require("../request-list-tooltip");
+const { formDataURI } = require("../utils/request-utils");
 const {
   getDisplayedRequests,
   getSelectedRequest,
+  getSortedRequests,
   getWaterfallScale,
 } = require("../selectors/index");
 
 // Components
 const RequestListHeader = createFactory(require("./RequestListHeader"));
 const RequestListItem = createFactory(require("./RequestListItem"));
-const RequestListContextMenu = require("../request-list-context-menu");
+const RequestListContextMenu = require("../widgets/RequestListContextMenu");
 
 const { div } = dom;
 
 // Tooltip show / hide delay in ms
 const REQUESTS_TOOLTIP_TOGGLE_DELAY = 500;
+// Tooltip image maximum dimension in px
+const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
 // Gecko's scrollTop is int32_t, so the maximum value is 2^31 - 1 = 2147483647
 const MAX_SCROLL_HEIGHT = 2147483647;
 
 /**
  * Renders the actual contents of the request list.
  */
 class RequestListContent extends Component {
   static get propTypes() {
@@ -44,16 +51,18 @@ class RequestListContent extends Compone
       onCauseBadgeMouseDown: PropTypes.func.isRequired,
       onItemMouseDown: PropTypes.func.isRequired,
       onSecurityIconMouseDown: PropTypes.func.isRequired,
       onSelectDelta: PropTypes.func.isRequired,
       onWaterfallMouseDown: PropTypes.func.isRequired,
       openStatistics: PropTypes.func.isRequired,
       scale: PropTypes.number,
       selectedRequest: PropTypes.object,
+      sortedRequests: PropTypes.array.isRequired,
+      requestFilterTypes: PropTypes.string.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.isScrolledToBottom = this.isScrolledToBottom.bind(this);
     this.onHover = this.onHover.bind(this);
     this.onScroll = this.onScroll.bind(this);
@@ -124,36 +133,49 @@ class RequestListContent extends Compone
    * over a request item or not.
    *
    * @param nsIDOMNode target
    *        The element node currently being hovered.
    * @param object tooltip
    *        The current tooltip instance.
    * @return {Promise}
    */
-  onHover(target, tooltip) {
+  async onHover(target, tooltip) {
     let itemEl = target.closest(".request-list-item");
     if (!itemEl) {
       return false;
     }
     let itemId = itemEl.dataset.id;
     if (!itemId) {
       return false;
     }
     let requestItem = this.props.displayedRequests.find(r => r.id == itemId);
     if (!requestItem) {
       return false;
     }
 
-    let { connector } = this.props;
-    if (target.closest(".requests-list-file")) {
-      return setTooltipImageContent(connector, tooltip, itemEl, requestItem);
+    if (!target.closest(".requests-list-file")) {
+      return false;
+    }
+
+    let { mimeType } = requestItem;
+    if (!mimeType || !mimeType.includes("image/")) {
+      return false;
     }
 
-    return false;
+    let responseContent = await this.props.connector
+      .requestData(requestItem.id, "responseContent");
+    let { encoding, text } = responseContent.content;
+    let src = formDataURI(mimeType, encoding, text);
+    let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
+    let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
+    let options = { maxDim, naturalWidth, naturalHeight };
+    setImageTooltip(tooltip, tooltip.doc, src, options);
+
+    return itemEl.querySelector(".requests-list-file");
   }
 
   /**
    * Scroll listener for the requests menu view.
    */
   onScroll() {
     this.tooltip.hide();
   }
@@ -193,17 +215,18 @@ class RequestListContent extends Compone
       evt.preventDefault();
       evt.stopPropagation();
       this.props.onSelectDelta(delta);
     }
   }
 
   onContextMenu(evt) {
     evt.preventDefault();
-    this.contextMenu.open(evt);
+    let { selectedRequest, sortedRequests } = this.props;
+    this.contextMenu.open(evt, selectedRequest, sortedRequests);
   }
 
   /**
    * If selection has just changed (by keyboard navigation), don't keep the list
    * scrolled to bottom, but allow scrolling up with the selection.
    */
   onFocusedNodeChange() {
     this.shouldScrollBottom = false;
@@ -259,16 +282,18 @@ class RequestListContent extends Compone
 
 module.exports = connect(
   (state) => ({
     columns: state.ui.columns,
     displayedRequests: getDisplayedRequests(state),
     firstRequestStartedMillis: state.requests.firstStartedMillis,
     selectedRequest: getSelectedRequest(state),
     scale: getWaterfallScale(state),
+    sortedRequests: getSortedRequests(state),
+    requestFilterTypes: state.filters.requestFilterTypes,
   }),
   (dispatch, props) => ({
     cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
     openStatistics: (open) => dispatch(Actions.openStatistics(props.connector, open)),
     /**
      * A handler that opens the stack trace tab when a stack trace is available
      */
     onCauseBadgeMouseDown: (cause) => {
--- a/devtools/client/netmonitor/src/components/RequestListHeader.js
+++ b/devtools/client/netmonitor/src/components/RequestListHeader.js
@@ -10,18 +10,18 @@ const PropTypes = require("devtools/clie
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { getTheme, addThemeObserver, removeThemeObserver } =
   require("devtools/client/shared/theme");
 const Actions = require("../actions/index");
 const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
 const { getWaterfallScale } = require("../selectors/index");
 const { getFormattedTime } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
-const WaterfallBackground = require("../waterfall-background");
-const RequestListHeaderContextMenu = require("../request-list-header-context-menu");
+const RequestListHeaderContextMenu = require("../widgets/RequestListHeaderContextMenu");
+const WaterfallBackground = require("../widgets/WaterfallBackground");
 
 const { div, button } = dom;
 
 /**
  * Render the request list header with sorting arrows for columns.
  * Displays tick marks in the waterfall column header.
  * Also draws the waterfall background canvas and updates it when needed.
  */
@@ -71,17 +71,17 @@ class RequestListHeader extends Componen
     this.background.destroy();
     this.background = null;
     window.removeEventListener("resize", this.resizeWaterfall);
     removeThemeObserver(this.drawBackground);
   }
 
   onContextMenu(evt) {
     evt.preventDefault();
-    this.contextMenu.open(evt);
+    this.contextMenu.open(evt, this.props.columns);
   }
 
   drawBackground() {
     // The background component is theme dependent, so add the current theme to the props.
     let props = Object.assign({}, this.props, {
       theme: getTheme()
     });
     this.background.draw(props);
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
@@ -13,17 +13,17 @@ add_task(function* () {
   Services.prefs.setBoolPref("network.tcp.tcp_fastopen_enable", false);
   let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
 
   info("Starting test... ");
 
   let { connector, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
-    "devtools/client/netmonitor/src/request-list-context-menu");
+    "devtools/client/netmonitor/src/widgets/RequestListContextMenu");
   let { getSortedRequests } = windowRequire(
     "devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, 1);
   tab.linkedBrowser.reload();
   yield wait;
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
@@ -10,17 +10,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(
     HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
 
   info("Starting test... ");
 
   let { connector, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
-    "devtools/client/netmonitor/src/request-list-context-menu");
+    "devtools/client/netmonitor/src/widgets/RequestListContextMenu");
   let { getSortedRequests } = windowRequire(
     "devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute one POST request on the page and wait till its done.
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data_on_get.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data_on_get.js
@@ -10,17 +10,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(
     HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
 
   info("Starting test... ");
 
   let { connector, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
-    "devtools/client/netmonitor/src/request-list-context-menu");
+    "devtools/client/netmonitor/src/widgets/RequestListContextMenu");
   let { getSortedRequests } = windowRequire(
     "devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute one GET request on the page and wait till its done.
   let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
@@ -14,17 +14,17 @@ function* throttleUploadTest(actuallyThr
   let { tab, monitor } = yield initNetMonitor(
     HAR_EXAMPLE_URL + "html_har_post-data-test-page.html");
 
   info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
 
   let { connector, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let RequestListContextMenu = windowRequire(
-    "devtools/client/netmonitor/src/request-list-context-menu");
+    "devtools/client/netmonitor/src/widgets/RequestListContextMenu");
   let { getSortedRequests } = windowRequire(
     "devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   const size = 4096;
   const uploadSize = actuallyThrottle ? size / 3 : 0;
 
--- a/devtools/client/netmonitor/src/moz.build
+++ b/devtools/client/netmonitor/src/moz.build
@@ -6,17 +6,14 @@ DIRS += [
     'actions',
     'components',
     'connector',
     'har',
     'middleware',
     'reducers',
     'selectors',
     'utils',
+    'widgets',
 ]
 
 DevToolsModules(
     'constants.js',
-    'request-list-context-menu.js',
-    'request-list-header-context-menu.js',
-    'request-list-tooltip.js',
-    'waterfall-background.js',
 )
deleted file mode 100644
--- a/devtools/client/netmonitor/src/request-list-tooltip.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {
-  setImageTooltip,
-  getImageDimensions,
-} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
-const { formDataURI } = require("./utils/request-utils");
-
-const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
-
-async function setTooltipImageContent(connector, tooltip, itemEl, requestItem) {
-  let { mimeType } = requestItem;
-
-  if (!mimeType || !mimeType.includes("image/")) {
-    return false;
-  }
-
-  let responseContent = await connector.requestData(requestItem.id, "responseContent");
-  let { encoding, text } = responseContent.content;
-  let src = formDataURI(mimeType, encoding, text);
-  let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
-  let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
-  let options = { maxDim, naturalWidth, naturalHeight };
-  setImageTooltip(tooltip, tooltip.doc, src, options);
-
-  return itemEl.querySelector(".requests-list-file");
-}
-
-module.exports = {
-  setTooltipImageContent,
-};
rename from devtools/client/netmonitor/src/request-list-context-menu.js
rename to devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
@@ -5,60 +5,52 @@
 "use strict";
 
 const Services = require("Services");
 const { Curl } = require("devtools/client/shared/curl");
 const { gDevTools } = require("devtools/client/framework/devtools");
 const { saveAs } = require("devtools/client/shared/file-saver");
 const { copyString } = require("devtools/shared/platform/clipboard");
 const { showMenu } = require("devtools/client/netmonitor/src/utils/menu");
-const { HarExporter } = require("./har/har-exporter");
 const { openRequestInTab } = require("devtools/client/netmonitor/src/utils/firefox/open-request-in-tab");
-const {
-  getSelectedRequest,
-  getSortedRequests,
-} = require("./selectors/index");
-const { L10N } = require("./utils/l10n");
+const { HarExporter } = require("../har/har-exporter");
+const { L10N } = require("../utils/l10n");
 const {
   formDataURI,
   getUrlQuery,
   getUrlBaseName,
   parseQueryString,
-} = require("./utils/request-utils");
+} = require("../utils/request-utils");
 
 class RequestListContextMenu {
   constructor(props) {
     this.props = props;
   }
 
-  open(event) {
-    // FIXME: Bug 1336382 - Implement RequestListContextMenu React component
-    // Remove window.store.getState()
-    let selectedRequest = getSelectedRequest(window.store.getState());
-    let sortedRequests = getSortedRequests(window.store.getState());
-
-    let menu = [];
-    let copySubmenu = [];
+  open(event, selectedRequest, sortedRequests) {
     let {
       id,
       isCustom,
+      formDataSections,
       method,
       mimeType,
       httpVersion,
       requestHeaders,
       requestPostData,
       requestPostDataAvailable,
       responseHeaders,
       responseContentAvailable,
       url,
-    } = selectedRequest || {};
+    } = selectedRequest;
     let {
       cloneSelectedRequest,
       openStatistics,
     } = this.props;
+    let menu = [];
+    let copySubmenu = [];
 
     copySubmenu.push({
       id: "request-list-context-copy-url",
       label: L10N.getStr("netmonitor.context.copyUrl"),
       accesskey: L10N.getStr("netmonitor.context.copyUrl.accesskey"),
       visible: !!selectedRequest,
       click: () => this.copyUrl(url),
     });
@@ -71,17 +63,17 @@ class RequestListContextMenu {
       click: () => this.copyUrlParams(url),
     });
 
     copySubmenu.push({
       id: "request-list-context-copy-post-data",
       label: L10N.getStr("netmonitor.context.copyPostData"),
       accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
       visible: !!(selectedRequest && (requestPostDataAvailable || requestPostData)),
-      click: () => this.copyPostData(id),
+      click: () => this.copyPostData(id, formDataSections),
     });
 
     copySubmenu.push({
       id: "request-list-context-copy-as-curl",
       label: L10N.getStr("netmonitor.context.copyAsCurl"),
       accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
       visible: !!selectedRequest,
       click: () => this.copyAsCurl(id, url, method, requestHeaders, httpVersion),
@@ -253,20 +245,17 @@ class RequestListContextMenu {
     let params = getUrlQuery(url).split("&");
     copyString(params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n"));
   }
 
   /**
    * Copy the request form data parameters (or raw payload) from
    * the currently selected item.
    */
-  async copyPostData(id) {
-    // FIXME: Bug 1336382 - Implement RequestListContextMenu React component
-    // Remove window.store.getState()
-    let { formDataSections } = getSelectedRequest(window.store.getState());
+  async copyPostData(id, formDataSections) {
     let params = [];
     // Try to extract any form data parameters.
     formDataSections.forEach(section => {
       let paramsArray = parseQueryString(section);
       if (paramsArray) {
         params = [...params, ...paramsArray];
       }
     });
rename from devtools/client/netmonitor/src/request-list-header-context-menu.js
rename to devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js
--- a/devtools/client/netmonitor/src/request-list-header-context-menu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListHeaderContextMenu.js
@@ -1,71 +1,55 @@
 /* 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 { HEADERS } = require("./constants");
-const { L10N } = require("./utils/l10n");
 const { showMenu } = require("devtools/client/netmonitor/src/utils/menu");
+const { HEADERS } = require("../constants");
+const { L10N } = require("../utils/l10n");
 
 const stringMap = HEADERS
   .filter((header) => header.hasOwnProperty("label"))
   .reduce((acc, { name, label }) => Object.assign(acc, { [name]: label }), {});
 
 const subMenuMap = HEADERS
   .filter((header) => header.hasOwnProperty("subMenu"))
   .reduce((acc, { name, subMenu }) => Object.assign(acc, { [name]: subMenu }), {});
 
 const nonLocalizedHeaders = HEADERS
   .filter((header) => header.hasOwnProperty("noLocalization"))
   .map((header) => header.name);
 
 class RequestListHeaderContextMenu {
-  constructor({ toggleColumn, resetColumns }) {
-    this.toggleColumn = toggleColumn;
-    this.resetColumns = resetColumns;
-  }
-
-  get columns() {
-    // FIXME: Bug 1362059 - Implement RequestListHeaderContextMenu React component
-    // Remove window.store
-    return window.store.getState().ui.columns;
-  }
-
-  get visibleColumns() {
-    let visible = [];
-    for (let column in this.columns) {
-      if (this.columns[column]) {
-        visible.push(column);
-      }
-    }
-    return visible;
+  constructor(props) {
+    this.props = props;
   }
 
   /**
    * Handle the context menu opening.
    */
-  open(event = {}) {
+  open(event = {}, columns) {
     let menu = [];
     let subMenu = { timings: [], responseHeaders: [] };
-    let onlyOneColumn = this.visibleColumns.length === 1;
+    let visibleColumns = Object.entries(columns).filter(([column, shown]) => shown);
+    let onlyOneColumn = visibleColumns.length === 1;
 
-    for (let column in this.columns) {
-      let shown = this.columns[column];
+    for (let column in columns) {
+      let shown = columns[column];
       let label = nonLocalizedHeaders.includes(column)
           ? stringMap[column] || column
           : L10N.getStr(`netmonitor.toolbar.${stringMap[column] || column}`);
       let entry = {
         id: `request-list-header-${column}-toggle`,
         label,
         type: "checkbox",
         checked: shown,
-        click: () => this.toggleColumn(column),
+        click: () => this.props.toggleColumn(column),
         // We don't want to allow hiding the last visible column
         disabled: onlyOneColumn && shown,
       };
       subMenuMap.hasOwnProperty(column) ?
         subMenu[subMenuMap[column]].push(entry) :
         menu.push(entry);
     }
 
@@ -78,16 +62,16 @@ class RequestListHeaderContextMenu {
       label: L10N.getStr("netmonitor.toolbar.responseHeaders"),
       submenu: subMenu.responseHeaders,
     });
 
     menu.push({ type: "separator" });
     menu.push({
       id: "request-list-header-reset-columns",
       label: L10N.getStr("netmonitor.toolbar.resetColumns"),
-      click: () => this.resetColumns(),
+      click: () => this.props.resetColumns(),
     });
 
     return showMenu(event, menu);
   }
 }
 
 module.exports = RequestListHeaderContextMenu;
rename from devtools/client/netmonitor/src/waterfall-background.js
rename to devtools/client/netmonitor/src/widgets/WaterfallBackground.js
--- a/devtools/client/netmonitor/src/waterfall-background.js
+++ b/devtools/client/netmonitor/src/widgets/WaterfallBackground.js
@@ -1,61 +1,61 @@
 /* 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 { getColor } = require("devtools/client/shared/theme");
 const { colorUtils } = require("devtools/shared/css/color");
+const { REQUESTS_WATERFALL } = require("../constants");
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 const STATE_KEYS = [
   "firstRequestStartedMillis",
   "scale",
   "timingMarkers",
   "waterfallWidth",
 ];
 
 /**
  * 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");
-  this.prevState = {};
-}
+class WaterfallBackground {
+  constructor() {
+    this.canvas = document.createElementNS(HTML_NS, "canvas");
+    this.ctx = this.canvas.getContext("2d");
+    this.prevState = {};
+  }
 
-/**
- * Changes the element being used as the CSS background for a background
- * with a given background element ID.
- *
- * The funtion wrap the Firefox only API. Waterfall Will not draw the
- * vertical line when running on non-firefox browser.
- * Could be fixed by Bug 1308695
- */
-function setImageElement(imageElementId, imageElement) {
-  if (document.mozSetImageElement) {
-    document.mozSetImageElement(imageElementId, imageElement);
+  /**
+   * Changes the element being used as the CSS background for a background
+   * with a given background element ID.
+   *
+   * The funtion wrap the Firefox only API. Waterfall Will not draw the
+   * vertical line when running on non-firefox browser.
+   * Could be fixed by Bug 1308695
+   */
+  setImageElement(imageElementId, imageElement) {
+    if (document.mozSetImageElement) {
+      document.mozSetImageElement(imageElementId, imageElement);
+    }
   }
-}
 
-WaterfallBackground.prototype = {
   draw(state) {
     // Do a shallow compare of the previous and the new state
     const shouldUpdate = STATE_KEYS.some(key => this.prevState[key] !== state[key]);
     if (!shouldUpdate) {
       return;
     }
 
     this.prevState = state;
 
     if (state.waterfallWidth === null || state.scale === null) {
-      setImageElement("waterfall-background", null);
+      this.setImageElement("waterfall-background", null);
       return;
     }
 
     // Nuke the context.
     let canvasWidth = this.canvas.width =
       state.waterfallWidth - REQUESTS_WATERFALL.LABEL_WIDTH;
     // Awww yeah, 1px, repeats on Y axis.
     let canvasHeight = this.canvas.height = 1;
@@ -78,17 +78,18 @@ WaterfallBackground.prototype = {
       scaledStep = state.scale * timingStep;
       if (scaledStep < REQUESTS_WATERFALL.BACKGROUND_TICKS_SPACING_MIN) {
         timingStep <<= 1;
         continue;
       }
       optimalTickIntervalFound = true;
     }
 
-    const isRTL = isDocumentRTL(document);
+    const isRTL = document.defaultView
+      .getComputedStyle(document.documentElement).direction === "rtl";
     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;
     }
@@ -108,51 +109,43 @@ WaterfallBackground.prototype = {
       }
 
       let delta = Math.floor((timestamp - state.firstRequestStartedMillis) * state.scale);
       drawPixelAt(delta, color);
     }
 
     let { DOMCONTENTLOADED_TICKS_COLOR, LOAD_TICKS_COLOR } = REQUESTS_WATERFALL;
     drawTimestamp(state.timingMarkers.firstDocumentDOMContentLoadedTimestamp,
-                  this.getThemeColorAsRgba(DOMCONTENTLOADED_TICKS_COLOR, state.theme));
+      this.getThemeColorAsRgba(DOMCONTENTLOADED_TICKS_COLOR, state.theme));
 
     drawTimestamp(state.timingMarkers.firstDocumentLoadTimestamp,
-                  this.getThemeColorAsRgba(LOAD_TICKS_COLOR, state.theme));
+      this.getThemeColorAsRgba(LOAD_TICKS_COLOR, state.theme));
 
     // Flush the image data and cache the waterfall background.
     pixelArray.set(view8bit);
     this.ctx.putImageData(imageData, 0, 0);
 
-    setImageElement("waterfall-background", this.canvas);
-  },
+    this.setImageElement("waterfall-background", this.canvas);
+  }
 
   /**
    * Retrieve a color defined for the provided theme as a rgba array. The alpha channel is
    * forced to the waterfall constant TICKS_COLOR_OPACITY.
    *
    * @param {String} colorName
    *        The name of the theme color
    * @param {String} theme
    *        The name of the theme
    * @return {Array} RGBA array for the color.
    */
   getThemeColorAsRgba(colorName, theme) {
     let colorStr = getColor(colorName, theme);
     let color = new colorUtils.CssColor(colorStr);
     let { r, g, b } = color.getRGBATuple();
     return [r, g, b, REQUESTS_WATERFALL.TICKS_COLOR_OPACITY];
-  },
+  }
 
   destroy() {
-    setImageElement("waterfall-background", null);
+    this.setImageElement("waterfall-background", null);
   }
-};
-
-/**
- * Returns true if this is document is in RTL mode.
- * @return boolean
- */
-function isDocumentRTL(doc) {
-  return doc.defaultView.getComputedStyle(doc.documentElement).direction === "rtl";
 }
 
 module.exports = WaterfallBackground;
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/widgets/moz.build
@@ -0,0 +1,9 @@
+# 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/.
+
+DevToolsModules(
+    'RequestListContextMenu.js',
+    'RequestListHeaderContextMenu.js',
+    'WaterfallBackground.js',
+)