Bug 1528296 - remove SideMenuWidget and dependancies; r=vporof
authoryulia <ystartsev@mozilla.com>
Wed, 13 Mar 2019 16:49:42 +0000
changeset 521740 864974f824cff660b1ae64dbb2bdb8d20b06b32b
parent 521739 6aa5ba09b849422276d4984560cb282ae2b89fba
child 521741 aaef9f2c3b39ed880de604a590319f6140d7e483
push id10867
push userdvarga@mozilla.com
push dateThu, 14 Mar 2019 15:20:45 +0000
treeherdermozilla-beta@abad13547875 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvporof
bugs1528296
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1528296 - remove SideMenuWidget and dependancies; r=vporof Differential Revision: https://phabricator.services.mozilla.com/D22493
devtools/client/locales/en-US/shared.properties
devtools/client/netmonitor/test/browser_net_accessibility-01.js
devtools/client/shared/widgets/SideMenuWidget.jsm
devtools/client/shared/widgets/moz.build
devtools/client/shared/widgets/view-helpers.js
devtools/client/shared/widgets/widgets.css
devtools/client/themes/widgets.css
--- a/devtools/client/locales/en-US/shared.properties
+++ b/devtools/client/locales/en-US/shared.properties
@@ -1,11 +1,8 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # LOCALIZATION NOTE (dimensions): This is used to display the dimensions
 # of a node or image, like 100×200.
 dimensions=%S\u00D7%S
 
-# LOCALIZATION NOTE (sideMenu.groupCheckbox.tooltip): This is used in the SideMenuWidget
-# as the default tooltip of a group checkbox
-sideMenu.groupCheckbox.tooltip=Toggle all checkboxes in this group
\ No newline at end of file
--- a/devtools/client/netmonitor/test/browser_net_accessibility-01.js
+++ b/devtools/client/netmonitor/test/browser_net_accessibility-01.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /**
- * Tests if focus modifiers work for the SideMenuWidget.
+ * Tests if focus modifiers work for the Side Menu.
  */
 
 add_task(async function() {
   const { tab, monitor } = await initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
   // It seems that this test may be slow on Ubuntu builds running on ec2.
   requestLongerTimeout(2);
deleted file mode 100644
--- a/devtools/client/shared/widgets/SideMenuWidget.jsm
+++ /dev/null
@@ -1,724 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* 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 SHARED_STRINGS_URI = "devtools/client/locales/shared.properties";
-
-const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
-const EventEmitter = require("devtools/shared/event-emitter");
-const { LocalizationHelper } = require("devtools/shared/l10n");
-const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
-
-this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
-
-/**
- * Localization convenience methods.
- */
-var L10N = new LocalizationHelper(SHARED_STRINGS_URI);
-
-/**
- * A simple side menu, with the ability of grouping menu items.
- *
- * Note: this widget should be used in tandem with the WidgetMethods in
- * view-helpers.js.
- *
- * @param Node aNode
- *        The element associated with the widget.
- * @param Object aOptions
- *        - contextMenu: optional element or element ID that serves as a context menu.
- *        - showArrows: specifies if items should display horizontal arrows.
- *        - showItemCheckboxes: specifies if items should display checkboxes.
- *        - showGroupCheckboxes: specifies if groups should display checkboxes.
- */
-this.SideMenuWidget = function SideMenuWidget(aNode, aOptions = {}) {
-  this.document = aNode.ownerDocument;
-  this.window = this.document.defaultView;
-  this._parent = aNode;
-
-  const { contextMenu, showArrows, showItemCheckboxes, showGroupCheckboxes } = aOptions;
-  this._contextMenu = contextMenu || null;
-  this._showArrows = showArrows || false;
-  this._showItemCheckboxes = showItemCheckboxes || false;
-  this._showGroupCheckboxes = showGroupCheckboxes || false;
-
-  // Create an internal scrollbox container.
-  this._list = this.document.createXULElement("scrollbox");
-  this._list.className = "side-menu-widget-container theme-sidebar";
-  this._list.setAttribute("flex", "1");
-  this._list.setAttribute("orient", "vertical");
-  this._list.setAttribute("with-arrows", this._showArrows);
-  this._list.setAttribute("with-item-checkboxes", this._showItemCheckboxes);
-  this._list.setAttribute("with-group-checkboxes", this._showGroupCheckboxes);
-  this._list.setAttribute("tabindex", "0");
-  this._list.addEventListener("contextmenu", e => this._showContextMenu(e));
-  this._list.addEventListener("keydown", e => this.emit("keyDown", e));
-  this._list.addEventListener("mousedown", e => this.emit("mousePress", e));
-  this._parent.appendChild(this._list);
-
-  // Menu items can optionally be grouped.
-  this._groupsByName = new Map(); // Can't use a WeakMap because keys are strings.
-  this._orderedGroupElementsArray = [];
-  this._orderedMenuElementsArray = [];
-  this._itemsByElement = new Map();
-
-  // This widget emits events that can be handled in a MenuContainer.
-  EventEmitter.decorate(this);
-
-  // Delegate some of the associated node's methods to satisfy the interface
-  // required by MenuContainer instances.
-  ViewHelpers.delegateWidgetAttributeMethods(this, aNode);
-  ViewHelpers.delegateWidgetEventMethods(this, aNode);
-};
-
-SideMenuWidget.prototype = {
-  /**
-   * Specifies if groups in this container should be sorted.
-   */
-  sortedGroups: true,
-
-  /**
-   * The comparator used to sort groups.
-   */
-  groupSortPredicate: (a, b) => a.localeCompare(b),
-
-  /**
-   * Inserts an item in this container at the specified index, optionally
-   * grouping by name.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this item.
-   * @param Node aContents
-   *        The node displayed in the container.
-   * @param object aAttachment [optional]
-   *        Some attached primitive/object. Custom options supported:
-   *          - group: a string specifying the group to place this item into
-   *          - checkboxState: the checked state of the checkbox, if shown
-   *          - checkboxTooltip: the tooltip text for the checkbox, if shown
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  insertItemAt: function(aIndex, aContents, aAttachment = {}) {
-    const group = this._getMenuGroupForName(aAttachment.group);
-    const item = this._getMenuItemForGroup(group, aContents, aAttachment);
-    const element = item.insertSelfAt(aIndex);
-
-    return element;
-  },
-
-  /**
-   * Checks to see if the list is scrolled all the way to the bottom.
-   * Uses getBoundsWithoutFlushing to limit the performance impact
-   * of this function.
-   *
-   * @return bool
-   */
-  isScrolledToBottom: function() {
-    if (this._list.lastElementChild) {
-      const utils = this.window.windowUtils;
-      const childRect = utils.getBoundsWithoutFlushing(this._list.lastElementChild);
-      const listRect = utils.getBoundsWithoutFlushing(this._list);
-
-      // Cheap way to check if it's scrolled all the way to the bottom.
-      return (childRect.height + childRect.top) <= listRect.bottom;
-    }
-
-    return false;
-  },
-
-  /**
-   * Scroll the list to the bottom after a timeout.
-   * If the user scrolls in the meantime, cancel this operation.
-   */
-  scrollToBottom: function() {
-    this._list.scrollTop = this._list.scrollHeight;
-    this.emit("scroll-to-bottom");
-  },
-
-  /**
-   * Returns the child node in this container situated at the specified index.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this item.
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  getItemAtIndex: function(aIndex) {
-    return this._orderedMenuElementsArray[aIndex];
-  },
-
-  /**
-   * Removes the specified child node from this container.
-   *
-   * @param Node aChild
-   *        The element associated with the displayed item.
-   */
-  removeChild: function(aChild) {
-    this._getNodeForContents(aChild).remove();
-
-    this._orderedMenuElementsArray.splice(
-      this._orderedMenuElementsArray.indexOf(aChild), 1);
-
-    this._itemsByElement.delete(aChild);
-
-    if (this._selectedItem == aChild) {
-      this._selectedItem = null;
-    }
-  },
-
-  /**
-   * Removes all of the child nodes from this container.
-   */
-  removeAllItems: function() {
-    const list = this._list;
-
-    while (list.hasChildNodes()) {
-      list.firstChild.remove();
-    }
-
-    this._selectedItem = null;
-
-    this._groupsByName.clear();
-    this._orderedGroupElementsArray.length = 0;
-    this._orderedMenuElementsArray.length = 0;
-    this._itemsByElement.clear();
-  },
-
-  /**
-   * Gets the currently selected child node in this container.
-   * @return Node
-   */
-  get selectedItem() {
-    return this._selectedItem;
-  },
-
-  /**
-   * Sets the currently selected child node in this container.
-   * @param Node aChild
-   */
-  set selectedItem(aChild) {
-    const menuArray = this._orderedMenuElementsArray;
-
-    if (!aChild) {
-      this._selectedItem = null;
-    }
-    for (const node of menuArray) {
-      if (node == aChild) {
-        this._getNodeForContents(node).classList.add("selected");
-        this._selectedItem = node;
-      } else {
-        this._getNodeForContents(node).classList.remove("selected");
-      }
-    }
-  },
-
-  /**
-   * Ensures the specified element is visible.
-   *
-   * @param Node aElement
-   *        The element to make visible.
-   */
-  ensureElementIsVisible: function(aElement) {
-    if (!aElement) {
-      return;
-    }
-
-    // Ensure the element is visible but not scrolled horizontally.
-    aElement.scrollIntoView({ block: "nearest" });
-    this._list.scrollBy(-this._list.clientWidth, 0);
-  },
-
-  /**
-   * Shows all the groups, even the ones with no visible children.
-   */
-  showEmptyGroups: function() {
-    for (const group of this._orderedGroupElementsArray) {
-      group.hidden = false;
-    }
-  },
-
-  /**
-   * Hides all the groups which have no visible children.
-   */
-  hideEmptyGroups: function() {
-    const visibleChildNodes = ".side-menu-widget-item-contents:not([hidden=true])";
-
-    for (const group of this._orderedGroupElementsArray) {
-      group.hidden = group.querySelectorAll(visibleChildNodes).length == 0;
-    }
-    for (const menuItem of this._orderedMenuElementsArray) {
-      menuItem.parentNode.hidden = menuItem.hidden;
-    }
-  },
-
-  /**
-   * Adds a new attribute or changes an existing attribute on this container.
-   *
-   * @param string aName
-   *        The name of the attribute.
-   * @param string aValue
-   *        The desired attribute value.
-   */
-  setAttribute: function(aName, aValue) {
-    this._parent.setAttribute(aName, aValue);
-
-    if (aName == "emptyText") {
-      this._textWhenEmpty = aValue;
-    }
-  },
-
-  /**
-   * Removes an attribute on this container.
-   *
-   * @param string aName
-   *        The name of the attribute.
-   */
-  removeAttribute: function(aName) {
-    this._parent.removeAttribute(aName);
-
-    if (aName == "emptyText") {
-      this._removeEmptyText();
-    }
-  },
-
-  /**
-   * Set the checkbox state for the item associated with the given node.
-   *
-   * @param Node aNode
-   *        The dom node for an item we want to check.
-   * @param boolean aCheckState
-   *        True to check, false to uncheck.
-   */
-  checkItem: function(aNode, aCheckState) {
-    const widgetItem = this._itemsByElement.get(aNode);
-    if (!widgetItem) {
-      throw new Error("No item for " + aNode);
-    }
-    widgetItem.check(aCheckState);
-  },
-
-  /**
-   * Sets the text displayed in this container when empty.
-   * @param string aValue
-   */
-  set _textWhenEmpty(aValue) {
-    if (this._emptyTextNode) {
-      this._emptyTextNode.setAttribute("value", aValue);
-    }
-    this._emptyTextValue = aValue;
-    this._showEmptyText();
-  },
-
-  /**
-   * Creates and appends a label signaling that this container is empty.
-   */
-  _showEmptyText: function() {
-    if (this._emptyTextNode || !this._emptyTextValue) {
-      return;
-    }
-    const label = this.document.createXULElement("label");
-    label.className = "plain side-menu-widget-empty-text";
-    label.setAttribute("value", this._emptyTextValue);
-
-    this._parent.insertBefore(label, this._list);
-    this._emptyTextNode = label;
-  },
-
-  /**
-   * Removes the label representing a notice in this container.
-   */
-  _removeEmptyText: function() {
-    if (!this._emptyTextNode) {
-      return;
-    }
-
-    this._parent.removeChild(this._emptyTextNode);
-    this._emptyTextNode = null;
-  },
-
-  /**
-   * Gets a container representing a group for menu items. If the container
-   * is not available yet, it is immediately created.
-   *
-   * @param string aName
-   *        The required group name.
-   * @return SideMenuGroup
-   *         The newly created group.
-   */
-  _getMenuGroupForName: function(aName) {
-    const cachedGroup = this._groupsByName.get(aName);
-    if (cachedGroup) {
-      return cachedGroup;
-    }
-
-    const group = new SideMenuGroup(this, aName, {
-      showCheckbox: this._showGroupCheckboxes,
-    });
-
-    this._groupsByName.set(aName, group);
-    group.insertSelfAt(this.sortedGroups ? group.findExpectedIndexForSelf(this.groupSortPredicate) : -1);
-
-    return group;
-  },
-
-  /**
-   * Gets a menu item to be displayed inside a group.
-   * @see SideMenuWidget.prototype._getMenuGroupForName
-   *
-   * @param SideMenuGroup aGroup
-   *        The group to contain the menu item.
-   * @param Node aContents
-   *        The node displayed in the container.
-   * @param object aAttachment [optional]
-   *        Some attached primitive/object.
-   */
-  _getMenuItemForGroup: function(aGroup, aContents, aAttachment) {
-    return new SideMenuItem(aGroup, aContents, aAttachment, {
-      showArrow: this._showArrows,
-      showCheckbox: this._showItemCheckboxes,
-    });
-  },
-
-  /**
-   * Returns the .side-menu-widget-item node corresponding to a SideMenuItem.
-   * To optimize the markup, some redundant elemenst are skipped when creating
-   * these child items, in which case we need to be careful on which nodes
-   * .selected class names are added, or which nodes are removed.
-   *
-   * @param Node aChild
-   *        An element which is the target node of a SideMenuItem.
-   * @return Node
-   *         The wrapper node if there is one, or the same child otherwise.
-   */
-  _getNodeForContents: function(aChild) {
-    if (aChild.hasAttribute("merged-item-contents")) {
-      return aChild;
-    }
-    return aChild.parentNode;
-  },
-
-  /**
-   * Shows the contextMenu element.
-   */
-  _showContextMenu: function(e) {
-    if (!this._contextMenu) {
-      return;
-    }
-
-    // Don't show the menu if a descendant node is going to be visible also.
-    let node = e.originalTarget;
-    while (node && node !== this._list) {
-      if (node.hasAttribute("contextmenu")) {
-        return;
-      }
-      node = node.parentNode;
-    }
-
-    // Call stopPropagation() and preventDefault() here so that avoid to show default
-    // context menu in about:devtools-toolbox. See Bug 1515265.
-    e.stopPropagation();
-    e.preventDefault();
-    this._contextMenu.openPopupAtScreen(e.screenX, e.screenY, true);
-  },
-
-  window: null,
-  document: null,
-  _showArrows: false,
-  _showItemCheckboxes: false,
-  _showGroupCheckboxes: false,
-  _parent: null,
-  _list: null,
-  _selectedItem: null,
-  _groupsByName: null,
-  _orderedGroupElementsArray: null,
-  _orderedMenuElementsArray: null,
-  _itemsByElement: null,
-  _emptyTextNode: null,
-  _emptyTextValue: "",
-};
-
-/**
- * A SideMenuGroup constructor for the BreadcrumbsWidget.
- * Represents a group which should contain SideMenuItems.
- *
- * @param SideMenuWidget aWidget
- *        The widget to contain this menu item.
- * @param string aName
- *        The string displayed in the container.
- * @param object aOptions [optional]
- *        An object containing the following properties:
- *          - showCheckbox: specifies if a checkbox should be displayed.
- */
-function SideMenuGroup(aWidget, aName, aOptions = {}) {
-  this.document = aWidget.document;
-  this.window = aWidget.window;
-  this.ownerView = aWidget;
-  this.identifier = aName;
-
-  // Create an internal title and list container.
-  if (aName) {
-    const target = this._target = this.document.createXULElement("vbox");
-    target.className = "side-menu-widget-group";
-    target.setAttribute("name", aName);
-
-    const list = this._list = this.document.createXULElement("vbox");
-    list.className = "side-menu-widget-group-list";
-
-    const title = this._title = this.document.createXULElement("hbox");
-    title.className = "side-menu-widget-group-title";
-
-    const name = this._name = this.document.createXULElement("label");
-    name.className = "plain name";
-    name.setAttribute("value", aName);
-    name.setAttribute("crop", "end");
-    name.setAttribute("flex", "1");
-
-    // Show a checkbox before the content.
-    if (aOptions.showCheckbox) {
-      const checkbox = this._checkbox = makeCheckbox(title, {
-        description: aName,
-        checkboxTooltip: L10N.getStr("sideMenu.groupCheckbox.tooltip"),
-      });
-      checkbox.className = "side-menu-widget-group-checkbox";
-    }
-
-    title.appendChild(name);
-    target.appendChild(title);
-    target.appendChild(list);
-  } else {
-    // Skip a few redundant nodes when no title is shown.
-    const target = this._target = this._list = this.document.createXULElement("vbox");
-    target.className = "side-menu-widget-group side-menu-widget-group-list";
-    target.setAttribute("merged-group-contents", "");
-  }
-}
-
-SideMenuGroup.prototype = {
-  get _orderedGroupElementsArray() {
-    return this.ownerView._orderedGroupElementsArray;
-  },
-  get _orderedMenuElementsArray() {
-    return this.ownerView._orderedMenuElementsArray;
-  },
-  get _itemsByElement() {
-    return this.ownerView._itemsByElement;
-  },
-
-  /**
-   * Inserts this group in the parent container at the specified index.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this group.
-   */
-  insertSelfAt: function(aIndex) {
-    const ownerList = this.ownerView._list;
-    const groupsArray = this._orderedGroupElementsArray;
-
-    if (aIndex >= 0) {
-      ownerList.insertBefore(this._target, groupsArray[aIndex]);
-      groupsArray.splice(aIndex, 0, this._target);
-    } else {
-      ownerList.appendChild(this._target);
-      groupsArray.push(this._target);
-    }
-  },
-
-  /**
-   * Finds the expected index of this group based on its name.
-   *
-   * @return number
-   *         The expected index.
-   */
-  findExpectedIndexForSelf: function(sortPredicate) {
-    const identifier = this.identifier;
-    const groupsArray = this._orderedGroupElementsArray;
-
-    for (const group of groupsArray) {
-      const name = group.getAttribute("name");
-      if (sortPredicate(name, identifier) > 0 && // Insertion sort at its best :)
-          !name.includes(identifier)) { // Least significant group should be last.
-        return groupsArray.indexOf(group);
-      }
-    }
-    return -1;
-  },
-
-  window: null,
-  document: null,
-  ownerView: null,
-  identifier: "",
-  _target: null,
-  _checkbox: null,
-  _title: null,
-  _name: null,
-  _list: null,
-};
-
-/**
- * A SideMenuItem constructor for the BreadcrumbsWidget.
- *
- * @param SideMenuGroup aGroup
- *        The group to contain this menu item.
- * @param Node aContents
- *        The node displayed in the container.
- * @param object aAttachment [optional]
- *        The attachment object.
- * @param object aOptions [optional]
- *        An object containing the following properties:
- *          - showArrow: specifies if a horizontal arrow should be displayed.
- *          - showCheckbox: specifies if a checkbox should be displayed.
- */
-function SideMenuItem(aGroup, aContents, aAttachment = {}, aOptions = {}) {
-  this.document = aGroup.document;
-  this.window = aGroup.window;
-  this.ownerView = aGroup;
-
-  if (aOptions.showArrow || aOptions.showCheckbox) {
-    const container = this._container = this.document.createXULElement("hbox");
-    container.className = "side-menu-widget-item";
-
-    const target = this._target = this.document.createXULElement("vbox");
-    target.className = "side-menu-widget-item-contents";
-
-    // Show a checkbox before the content.
-    if (aOptions.showCheckbox) {
-      const checkbox = this._checkbox = makeCheckbox(container, aAttachment);
-      checkbox.className = "side-menu-widget-item-checkbox";
-    }
-
-    container.appendChild(target);
-
-    // Show a horizontal arrow towards the content.
-    if (aOptions.showArrow) {
-      const arrow = this._arrow = this.document.createXULElement("hbox");
-      arrow.className = "side-menu-widget-item-arrow";
-      container.appendChild(arrow);
-    }
-  } else {
-    // Skip a few redundant nodes when no horizontal arrow or checkbox is shown.
-    const target = this._target = this._container = this.document.createXULElement("hbox");
-    target.className = "side-menu-widget-item side-menu-widget-item-contents";
-    target.setAttribute("merged-item-contents", "");
-  }
-
-  this._target.setAttribute("flex", "1");
-  this.contents = aContents;
-}
-
-SideMenuItem.prototype = {
-  get _orderedGroupElementsArray() {
-    return this.ownerView._orderedGroupElementsArray;
-  },
-  get _orderedMenuElementsArray() {
-    return this.ownerView._orderedMenuElementsArray;
-  },
-  get _itemsByElement() {
-    return this.ownerView._itemsByElement;
-  },
-
-  /**
-   * Inserts this item in the parent group at the specified index.
-   *
-   * @param number aIndex
-   *        The position in the container intended for this item.
-   * @return Node
-   *         The element associated with the displayed item.
-   */
-  insertSelfAt: function(aIndex) {
-    const ownerList = this.ownerView._list;
-    const menuArray = this._orderedMenuElementsArray;
-
-    if (aIndex >= 0) {
-      ownerList.insertBefore(this._container, ownerList.childNodes[aIndex]);
-      menuArray.splice(aIndex, 0, this._target);
-    } else {
-      ownerList.appendChild(this._container);
-      menuArray.push(this._target);
-    }
-    this._itemsByElement.set(this._target, this);
-
-    return this._target;
-  },
-
-  /**
-   * Check or uncheck the checkbox associated with this item.
-   *
-   * @param boolean aCheckState
-   *        True to check, false to uncheck.
-   */
-  check: function(aCheckState) {
-    if (!this._checkbox) {
-      throw new Error("Cannot check items that do not have checkboxes.");
-    }
-    // Don't set or remove the "checked" attribute, assign the property instead.
-    // Otherwise, the "CheckboxStateChange" event will not be fired. XUL!!
-    this._checkbox.checked = !!aCheckState;
-  },
-
-  /**
-   * Sets the contents displayed in this item's view.
-   *
-   * @param string | Node aContents
-   *        The string or node displayed in the container.
-   */
-  set contents(aContents) {
-    // If there are already some contents displayed, replace them.
-    if (this._target.hasChildNodes()) {
-      this._target.replaceChild(aContents, this._target.firstChild);
-      return;
-    }
-    // These are the first contents ever displayed.
-    this._target.appendChild(aContents);
-  },
-
-  window: null,
-  document: null,
-  ownerView: null,
-  _target: null,
-  _container: null,
-  _checkbox: null,
-  _arrow: null,
-};
-
-/**
- * Creates a checkbox to a specified parent node. Emits a "check" event
- * whenever the checkbox is checked or unchecked by the user.
- *
- * @param Node aParentNode
- *        The parent node to contain this checkbox.
- * @param object aOptions
- *        An object containing some or all of the following properties:
- *          - description: defaults to "item" if unspecified
- *          - checkboxState: true for checked, false for unchecked
- *          - checkboxTooltip: the tooltip text of the checkbox
- */
-function makeCheckbox(aParentNode, aOptions) {
-  const checkbox = aParentNode.ownerDocument.createXULElement("checkbox");
-
-  checkbox.setAttribute("tooltiptext", aOptions.checkboxTooltip || "");
-
-  if (aOptions.checkboxState) {
-    checkbox.setAttribute("checked", true);
-  } else {
-    checkbox.removeAttribute("checked");
-  }
-
-  // Stop the toggling of the checkbox from selecting the list item.
-  checkbox.addEventListener("mousedown", e => {
-    e.stopPropagation();
-  });
-
-  // Emit an event from the checkbox when it is toggled. Don't listen for the
-  // "command" event! It won't fire for programmatic changes. XUL!!
-  checkbox.addEventListener("CheckboxStateChange", e => {
-    ViewHelpers.dispatchEvent(checkbox, "check", {
-      description: aOptions.description || "item",
-      checked: checkbox.checked,
-    });
-  });
-
-  aParentNode.appendChild(checkbox);
-  return checkbox;
-}
--- a/devtools/client/shared/widgets/moz.build
+++ b/devtools/client/shared/widgets/moz.build
@@ -16,16 +16,15 @@ DevToolsModules(
     'CubicBezierWidget.js',
     'FilterWidget.js',
     'FlameGraph.js',
     'Graphs.js',
     'GraphsWorker.js',
     'LineGraphWidget.js',
     'MountainGraphWidget.js',
     'ShapesInContextEditor.js',
-    'SideMenuWidget.jsm',
     'Spectrum.js',
     'TableWidget.js',
     'TreeWidget.js',
     'VariablesView.jsm',
     'VariablesViewController.jsm',
     'view-helpers.js',
 )
--- a/devtools/client/shared/widgets/view-helpers.js
+++ b/devtools/client/shared/widgets/view-helpers.js
@@ -4,18 +4,16 @@
  * 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 {Cu} = require("chrome");
 const {KeyCodes} = require("devtools/client/shared/keycodes");
 
 const PANE_APPEARANCE_DELAY = 50;
-const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
-const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
 var namedTimeoutsStore = new Map();
 
 /**
  * Helper for draining a rapid succession of events and invoking a callback
  * once everything settles down.
  *
  * @param string id
@@ -86,17 +84,17 @@ exports.setConditionalTimeout = setCondi
 const clearConditionalTimeout = function clearConditionalTimeout(id) {
   clearNamedTimeout(id);
 };
 exports.clearConditionalTimeout = clearConditionalTimeout;
 
 /**
  * Helpers for creating and messaging between UI components.
  */
-const ViewHelpers = exports.ViewHelpers = {
+exports.ViewHelpers = {
   /**
    * Convenience method, dispatching a custom event.
    *
    * @param Node target
    *        A custom target element to dispatch the event from.
    * @param string type
    *        The name of the event.
    * @param any detail
@@ -454,1147 +452,8 @@ Item.prototype = {
 
   _value: "",
   _target: null,
   _prebuiltNode: null,
   finalize: null,
   attachment: null,
 };
 
-/**
- * Some generic Widget methods handling Item instances.
- * Iterable via "for (let childItem of wrappedView) { }".
- *
- * Usage:
- *   function MyView() {
- *     this.widget = new MyWidget(document.querySelector(".my-node"));
- *   }
- *
- *   MyView.prototype = extend(WidgetMethods, {
- *     myMethod: function() {},
- *     ...
- *   });
- *
- * See https://gist.github.com/victorporof/5749386 for more details.
- *
- * Language:
- *   - An "item" is an instance of an Item.
- *   - An "element" or "node" is a Node.
- *
- * The supplied widget can be any object implementing the following
- * methods:
- *   - function:Node insertItemAt(aIndex:number, aNode:Node,
- *                                      aValue:string)
- *   - function:Node getItemAtIndex(aIndex:number)
- *   - function removeChild(aChild:Node)
- *   - function removeAllItems()
- *   - get:Node selectedItem()
- *   - set selectedItem(aChild:Node)
- *   - function getAttribute(aName:string)
- *   - function setAttribute(aName:string, aValue:string)
- *   - function removeAttribute(aName:string)
- *   - function addEventListener(aName:string, aCallback:function,
- *                               aBubbleFlag:boolean)
- *   - function removeEventListener(aName:string, aCallback:function,
- *                                  aBubbleFlag:boolean)
- *
- * Optional methods that can be implemented by the widget:
- *   - function ensureElementIsVisible(aChild:Node)
- *
- * Optional attributes that may be handled (when calling
- * get/set/removeAttribute):
- *   - "emptyText": label temporarily added when there are no items present
- *   - "headerText": label permanently added as a header
- *
- * For automagical keyboard and mouse accessibility, the widget should be an
- * event emitter with the following events:
- *   - "keyDown" -> (aName:string, aEvent:KeyboardEvent)
- *   - "mousePress" -> (aName:string, aEvent:MouseEvent)
- */
-const WidgetMethods = exports.WidgetMethods = {
-  /**
-   * Sets the element node or widget associated with this container.
-   * @param Node | object widget
-   */
-  set widget(widget) {
-    this._widget = widget;
-
-    // Can't use a WeakMap for _itemsByValue because keys are strings, and
-    // can't use one for _itemsByElement either, since it needs to be iterable.
-    this._itemsByValue = new Map();
-    this._itemsByElement = new Map();
-    this._stagedItems = [];
-
-    // Handle internal events emitted by the widget if necessary.
-    if (ViewHelpers.isEventEmitter(widget)) {
-      widget.on("keyDown", this._onWidgetKeyDown.bind(this));
-      widget.on("mousePress", this._onWidgetMousePress.bind(this));
-    }
-  },
-
-  /**
-   * Gets the element node or widget associated with this container.
-   * @return Node | object
-   */
-  get widget() {
-    return this._widget;
-  },
-
-  /**
-   * Prepares an item to be added to this container. This allows, for example,
-   * for a large number of items to be batched up before being sorted & added.
-   *
-   * If the "staged" flag is *not* set to true, the item will be immediately
-   * inserted at the correct position in this container, so that all the items
-   * still remain sorted. This can (possibly) be much slower than batching up
-   * multiple items.
-   *
-   * By default, this container assumes that all the items should be displayed
-   * sorted by their value. This can be overridden with the "index" flag,
-   * specifying on which position should an item be appended. The "staged" and
-   * "index" flags are mutually exclusive, meaning that all staged items
-   * will always be appended.
-   *
-   * @param Node element
-   *        A prebuilt node to be wrapped.
-   * @param string value
-   *        A string identifying the node.
-   * @param object options [optional]
-   *        Additional options or flags supported by this operation:
-   *          - attachment: some attached primitive/object for the item
-   *          - staged: true to stage the item to be appended later
-   *          - index: specifies on which position should the item be appended
-   *          - attributes: a batch of attributes set to the displayed element
-   *          - finalize: function invoked when the item is removed
-   * @return Item
-   *         The item associated with the displayed element if an unstaged push,
-   *         undefined if the item was staged for a later commit.
-   */
-  push: function([element, value], options = {}) {
-    const item = new Item(this, element, value, options.attachment);
-
-    // Batch the item to be added later.
-    if (options.staged) {
-      // An ulterior commit operation will ignore any specified index, so
-      // no reason to keep it around.
-      options.index = undefined;
-      return void this._stagedItems.push({ item: item, options: options });
-    }
-    // Find the target position in this container and insert the item there.
-    if (!("index" in options)) {
-      return this._insertItemAt(this._findExpectedIndexFor(item), item,
-                                options);
-    }
-    // Insert the item at the specified index. If negative or out of bounds,
-    // the item will be simply appended.
-    return this._insertItemAt(options.index, item, options);
-  },
-
-  /**
-   * Flushes all the prepared items into this container.
-   * Any specified index on the items will be ignored. Everything is appended.
-   *
-   * @param object options [optional]
-   *        Additional options or flags supported by this operation:
-   *          - sorted: true to sort all the items before adding them
-   */
-  commit: function(options = {}) {
-    const stagedItems = this._stagedItems;
-
-    // Sort the items before adding them to this container, if preferred.
-    if (options.sorted) {
-      stagedItems.sort((a, b) => this._currentSortPredicate(a.item, b.item));
-    }
-    // Append the prepared items to this container.
-    for (const { item, opt } of stagedItems) {
-      this._insertItemAt(-1, item, opt);
-    }
-    // Recreate the temporary items list for ulterior pushes.
-    this._stagedItems.length = 0;
-  },
-
-  /**
-   * Immediately removes the specified item from this container.
-   *
-   * @param Item item
-   *        The item associated with the element to remove.
-   */
-  remove: function(item) {
-    if (!item) {
-      return;
-    }
-    this._widget.removeChild(item._target);
-    this._untangleItem(item);
-
-    if (!this._itemsByElement.size) {
-      this._preferredValue = this.selectedValue;
-      this._widget.selectedItem = null;
-      this._widget.setAttribute("emptyText", this._emptyText);
-    }
-  },
-
-  /**
-   * Removes the item at the specified index from this container.
-   *
-   * @param number index
-   *        The index of the item to remove.
-   */
-  removeAt: function(index) {
-    this.remove(this.getItemAtIndex(index));
-  },
-
-  /**
-   * Removes the items in this container based on a predicate.
-   */
-  removeForPredicate: function(predicate) {
-    let item;
-    while ((item = this.getItemForPredicate(predicate))) {
-      this.remove(item);
-    }
-  },
-
-  /**
-   * Removes all items from this container.
-   */
-  empty: function() {
-    this._preferredValue = this.selectedValue;
-    this._widget.selectedItem = null;
-    this._widget.removeAllItems();
-    this._widget.setAttribute("emptyText", this._emptyText);
-
-    for (const [, item] of this._itemsByElement) {
-      this._untangleItem(item);
-    }
-
-    this._itemsByValue.clear();
-    this._itemsByElement.clear();
-    this._stagedItems.length = 0;
-  },
-
-  /**
-   * Ensures the specified item is visible in this container.
-   *
-   * @param Item item
-   *        The item to bring into view.
-   */
-  ensureItemIsVisible: function(item) {
-    this._widget.ensureElementIsVisible(item._target);
-  },
-
-  /**
-   * Ensures the item at the specified index is visible in this container.
-   *
-   * @param number index
-   *        The index of the item to bring into view.
-   */
-  ensureIndexIsVisible: function(index) {
-    this.ensureItemIsVisible(this.getItemAtIndex(index));
-  },
-
-  /**
-   * Sugar for ensuring the selected item is visible in this container.
-   */
-  ensureSelectedItemIsVisible: function() {
-    this.ensureItemIsVisible(this.selectedItem);
-  },
-
-  /**
-   * If supported by the widget, the label string temporarily added to this
-   * container when there are no child items present.
-   */
-  set emptyText(value) {
-    this._emptyText = value;
-
-    // Apply the emptyText attribute right now if there are no child items.
-    if (!this._itemsByElement.size) {
-      this._widget.setAttribute("emptyText", value);
-    }
-  },
-
-  /**
-   * If supported by the widget, the label string permanently added to this
-   * container as a header.
-   * @param string value
-   */
-  set headerText(value) {
-    this._headerText = value;
-    this._widget.setAttribute("headerText", value);
-  },
-
-  /**
-   * Toggles all the items in this container hidden or visible.
-   *
-   * This does not change the default filtering predicate, so newly inserted
-   * items will always be visible. Use WidgetMethods.filterContents if you care.
-   *
-   * @param boolean visibleFlag
-   *        Specifies the intended visibility.
-   */
-  toggleContents: function(visibleFlag) {
-    for (const [element] of this._itemsByElement) {
-      element.hidden = !visibleFlag;
-    }
-  },
-
-  /**
-   * Toggles all items in this container hidden or visible based on a predicate.
-   *
-   * @param function predicate [optional]
-   *        Items are toggled according to the return value of this function,
-   *        which will become the new default filtering predicate in this
-   *        container.
-   *        If unspecified, all items will be toggled visible.
-   */
-  filterContents: function(predicate = this._currentFilterPredicate) {
-    this._currentFilterPredicate = predicate;
-
-    for (const [element, item] of this._itemsByElement) {
-      element.hidden = !predicate(item);
-    }
-  },
-
-  /**
-   * Sorts all the items in this container based on a predicate.
-   *
-   * @param function predicate [optional]
-   *        Items are sorted according to the return value of the function,
-   *        which will become the new default sorting predicate in this
-   *        container. If unspecified, all items will be sorted by their value.
-   */
-  sortContents: function(predicate = this._currentSortPredicate) {
-    const sortedItems = this.items.sort(this._currentSortPredicate = predicate);
-
-    for (let i = 0, len = sortedItems.length; i < len; i++) {
-      this.swapItems(this.getItemAtIndex(i), sortedItems[i]);
-    }
-  },
-
-  /**
-   * Visually swaps two items in this container.
-   *
-   * @param Item first
-   *        The first item to be swapped.
-   * @param Item second
-   *        The second item to be swapped.
-   */
-  swapItems: function(first, second) {
-    if (first == second) {
-      // We're just dandy, thank you.
-      return;
-    }
-    const { _prebuiltNode: firstPrebuiltTarget, _target: firstTarget } = first;
-    const { _prebuiltNode: secondPrebuiltTarget, _target: secondTarget } = second;
-
-    // If the two items were constructed with prebuilt nodes as
-    // DocumentFragments, then those DocumentFragments are now
-    // empty and need to be reassembled.
-    if (Cu.getClassName(firstPrebuiltTarget) == "DocumentFragment") {
-      for (const node of firstTarget.childNodes) {
-        firstPrebuiltTarget.appendChild(node.cloneNode(true));
-      }
-    }
-    if (Cu.getClassName(secondPrebuiltTarget) == "DocumentFragment") {
-      for (const node of secondTarget.childNodes) {
-        secondPrebuiltTarget.appendChild(node.cloneNode(true));
-      }
-    }
-
-    // 1. Get the indices of the two items to swap.
-    const i = this._indexOfElement(firstTarget);
-    const j = this._indexOfElement(secondTarget);
-
-    // 2. Remeber the selection index, to reselect an item, if necessary.
-    const selectedTarget = this._widget.selectedItem;
-    let selectedIndex = -1;
-    if (selectedTarget == firstTarget) {
-      selectedIndex = i;
-    } else if (selectedTarget == secondTarget) {
-      selectedIndex = j;
-    }
-
-    // 3. Silently nuke both items, nobody needs to know about this.
-    this._widget.removeChild(firstTarget);
-    this._widget.removeChild(secondTarget);
-    this._unlinkItem(first);
-    this._unlinkItem(second);
-
-    // 4. Add the items again, but reversing their indices.
-    this._insertItemAt.apply(this, i < j ? [i, second] : [j, first]);
-    this._insertItemAt.apply(this, i < j ? [j, first] : [i, second]);
-
-    // 5. Restore the previous selection, if necessary.
-    if (selectedIndex == i) {
-      this._widget.selectedItem = first._target;
-    } else if (selectedIndex == j) {
-      this._widget.selectedItem = second._target;
-    }
-
-    // 6. Let the outside world know that these two items were swapped.
-    ViewHelpers.dispatchEvent(first.target, "swap", [second, first]);
-  },
-
-  /**
-   * Visually swaps two items in this container at specific indices.
-   *
-   * @param number first
-   *        The index of the first item to be swapped.
-   * @param number second
-   *        The index of the second item to be swapped.
-   */
-  swapItemsAtIndices: function(first, second) {
-    this.swapItems(this.getItemAtIndex(first), this.getItemAtIndex(second));
-  },
-
-  /**
-   * Checks whether an item with the specified value is among the elements
-   * shown in this container.
-   *
-   * @param string value
-   *        The item's value.
-   * @return boolean
-   *         True if the value is known, false otherwise.
-   */
-  containsValue: function(value) {
-    return this._itemsByValue.has(value) ||
-           this._stagedItems.some(({ item }) => item._value == value);
-  },
-
-  /**
-   * Gets the "preferred value". This is the latest selected item's value,
-   * remembered just before emptying this container.
-   * @return string
-   */
-  get preferredValue() {
-    return this._preferredValue;
-  },
-
-  /**
-   * Retrieves the item associated with the selected element.
-   * @return Item | null
-   */
-  get selectedItem() {
-    const selectedElement = this._widget.selectedItem;
-    if (selectedElement) {
-      return this._itemsByElement.get(selectedElement);
-    }
-    return null;
-  },
-
-  /**
-   * Retrieves the selected element's index in this container.
-   * @return number
-   */
-  get selectedIndex() {
-    const selectedElement = this._widget.selectedItem;
-    if (selectedElement) {
-      return this._indexOfElement(selectedElement);
-    }
-    return -1;
-  },
-
-  /**
-   * Retrieves the value of the selected element.
-   * @return string
-   */
-  get selectedValue() {
-    const selectedElement = this._widget.selectedItem;
-    if (selectedElement) {
-      return this._itemsByElement.get(selectedElement)._value;
-    }
-    return "";
-  },
-
-  /**
-   * Retrieves the attachment of the selected element.
-   * @return object | null
-   */
-  get selectedAttachment() {
-    const selectedElement = this._widget.selectedItem;
-    if (selectedElement) {
-      return this._itemsByElement.get(selectedElement).attachment;
-    }
-    return null;
-  },
-
-  _selectItem: function(item) {
-    // A falsy item is allowed to invalidate the current selection.
-    const targetElement = item ? item._target : null;
-    const prevElement = this._widget.selectedItem;
-
-    // Make sure the selected item's target element is focused and visible.
-    if (this.autoFocusOnSelection && targetElement) {
-      targetElement.focus();
-    }
-
-    if (targetElement != prevElement) {
-      this._widget.selectedItem = targetElement;
-    }
-  },
-
-  /**
-   * Selects the element with the entangled item in this container.
-   * @param Item | function item
-   */
-  set selectedItem(item) {
-    // A predicate is allowed to select a specific item.
-    // If no item is matched, then the current selection is removed.
-    if (typeof item == "function") {
-      item = this.getItemForPredicate(item);
-    }
-
-    const targetElement = item ? item._target : null;
-    const prevElement = this._widget.selectedItem;
-
-    if (this.maintainSelectionVisible && targetElement) {
-      // Some methods are optional. See the WidgetMethods object documentation
-      // for a comprehensive list.
-      if ("ensureElementIsVisible" in this._widget) {
-        this._widget.ensureElementIsVisible(targetElement);
-      }
-    }
-
-    this._selectItem(item);
-
-    // Prevent selecting the same item again and avoid dispatching
-    // a redundant selection event, so return early.
-    if (targetElement != prevElement) {
-      const dispTarget = targetElement || prevElement;
-      const dispName = this.suppressSelectionEvents ? "suppressed-select"
-                                                  : "select";
-      ViewHelpers.dispatchEvent(dispTarget, dispName, item);
-    }
-  },
-
-  /**
-   * Selects the element at the specified index in this container.
-   * @param number index
-   */
-  set selectedIndex(index) {
-    const targetElement = this._widget.getItemAtIndex(index);
-    if (targetElement) {
-      this.selectedItem = this._itemsByElement.get(targetElement);
-      return;
-    }
-    this.selectedItem = null;
-  },
-
-  /**
-   * Selects the element with the specified value in this container.
-   * @param string value
-   */
-  set selectedValue(value) {
-    this.selectedItem = this._itemsByValue.get(value);
-  },
-
-  /**
-   * Deselects and re-selects an item in this container.
-   *
-   * Useful when you want a "select" event to be emitted, even though
-   * the specified item was already selected.
-   *
-   * @param Item | function item
-   * @see `set selectedItem`
-   */
-  forceSelect: function(item) {
-    this.selectedItem = null;
-    this.selectedItem = item;
-  },
-
-  /**
-   * Specifies if this container should try to keep the selected item visible.
-   * (For example, when new items are added the selection is brought into view).
-   */
-  maintainSelectionVisible: true,
-
-  /**
-   * Specifies if "select" events dispatched from the elements in this container
-   * when their respective items are selected should be suppressed or not.
-   *
-   * If this flag is set to true, then consumers of this container won't
-   * be normally notified when items are selected.
-   */
-  suppressSelectionEvents: false,
-
-  /**
-   * Focus this container the first time an element is inserted?
-   *
-   * If this flag is set to true, then when the first item is inserted in
-   * this container (and thus it's the only item available), its corresponding
-   * target element is focused as well.
-   */
-  autoFocusOnFirstItem: true,
-
-  /**
-   * Focus on selection?
-   *
-   * If this flag is set to true, then whenever an item is selected in
-   * this container (e.g. via the selectedIndex or selectedItem setters),
-   * its corresponding target element is focused as well.
-   *
-   * You can disable this flag, for example, to maintain a certain node
-   * focused but visually indicate a different selection in this container.
-   */
-  autoFocusOnSelection: true,
-
-  /**
-   * Focus on input (e.g. mouse click)?
-   *
-   * If this flag is set to true, then whenever an item receives user input in
-   * this container, its corresponding target element is focused as well.
-   */
-  autoFocusOnInput: true,
-
-  /**
-   * When focusing on input, allow right clicks?
-   * @see WidgetMethods.autoFocusOnInput
-   */
-  allowFocusOnRightClick: false,
-
-  /**
-   * The number of elements in this container to jump when Page Up or Page Down
-   * keys are pressed. If falsy, then the page size will be based on the
-   * number of visible items in the container.
-   */
-  pageSize: 0,
-
-  /**
-   * Focuses the first visible item in this container.
-   */
-  focusFirstVisibleItem: function() {
-    this.focusItemAtDelta(-this.itemCount);
-  },
-
-  /**
-   * Focuses the last visible item in this container.
-   */
-  focusLastVisibleItem: function() {
-    this.focusItemAtDelta(+this.itemCount);
-  },
-
-  /**
-   * Focuses the next item in this container.
-   */
-  focusNextItem: function() {
-    this.focusItemAtDelta(+1);
-  },
-
-  /**
-   * Focuses the previous item in this container.
-   */
-  focusPrevItem: function() {
-    this.focusItemAtDelta(-1);
-  },
-
-  /**
-   * Focuses another item in this container based on the index distance
-   * from the currently focused item.
-   *
-   * @param number delta
-   *        A scalar specifying by how many items should the selection change.
-   */
-  focusItemAtDelta: function(delta) {
-    // Make sure the currently selected item is also focused, so that the
-    // command dispatcher mechanism has a relative node to work with.
-    // If there's no selection, just select an item at a corresponding index
-    // (e.g. the first item in this container if delta <= 1).
-    const selectedElement = this._widget.selectedItem;
-    if (selectedElement) {
-      selectedElement.focus();
-    } else {
-      this.selectedIndex = Math.max(0, delta - 1);
-      return;
-    }
-
-    const direction = delta > 0 ? "advanceFocus" : "rewindFocus";
-    let distance = Math.abs(Math[delta > 0 ? "ceil" : "floor"](delta));
-    while (distance--) {
-      if (!this._focusChange(direction)) {
-        // Out of bounds.
-        break;
-      }
-    }
-
-    // Synchronize the selected item as being the currently focused element.
-    this.selectedItem = this.getItemForElement(this._focusedElement);
-  },
-
-  /**
-   * Focuses the next or previous item in this container.
-   *
-   * @param string direction
-   *        Either "advanceFocus" or "rewindFocus".
-   * @return boolean
-   *         False if the focus went out of bounds and the first or last item
-   *         in this container was focused instead.
-   */
-  _focusChange: function(direction) {
-    const commandDispatcher = this._commandDispatcher;
-    const prevFocusedElement = commandDispatcher.focusedElement;
-    let currFocusedElement;
-
-    do {
-      commandDispatcher[direction]();
-      currFocusedElement = commandDispatcher.focusedElement;
-
-      // Make sure the newly focused item is a part of this container. If the
-      // focus goes out of bounds, revert the previously focused item.
-      if (!this.getItemForElement(currFocusedElement)) {
-        prevFocusedElement.focus();
-        return false;
-      }
-    } while (!WIDGET_FOCUSABLE_NODES.has(currFocusedElement.tagName));
-
-    // Focus remained within bounds.
-    return true;
-  },
-
-  /**
-   * Gets the command dispatcher instance associated with this container's DOM.
-   * If there are no items displayed in this container, null is returned.
-   * @return nsIDOMXULCommandDispatcher | null
-   */
-  get _commandDispatcher() {
-    if (this._cachedCommandDispatcher) {
-      return this._cachedCommandDispatcher;
-    }
-    const someElement = this._widget.getItemAtIndex(0);
-    if (someElement) {
-      const commandDispatcher = someElement.ownerDocument.commandDispatcher;
-      this._cachedCommandDispatcher = commandDispatcher;
-      return commandDispatcher;
-    }
-    return null;
-  },
-
-  /**
-   * Gets the currently focused element in this container.
-   *
-   * @return Node
-   *         The focused element, or null if nothing is found.
-   */
-  get _focusedElement() {
-    const commandDispatcher = this._commandDispatcher;
-    if (commandDispatcher) {
-      return commandDispatcher.focusedElement;
-    }
-    return null;
-  },
-
-  /**
-   * Gets the item in the container having the specified index.
-   *
-   * @param number index
-   *        The index used to identify the element.
-   * @return Item
-   *         The matched item, or null if nothing is found.
-   */
-  getItemAtIndex: function(index) {
-    return this.getItemForElement(this._widget.getItemAtIndex(index));
-  },
-
-  /**
-   * Gets the item in the container having the specified value.
-   *
-   * @param string value
-   *        The value used to identify the element.
-   * @return Item
-   *         The matched item, or null if nothing is found.
-   */
-  getItemByValue: function(value) {
-    return this._itemsByValue.get(value);
-  },
-
-  /**
-   * Gets the item in the container associated with the specified element.
-   *
-   * @param Node element
-   *        The element used to identify the item.
-   * @param object flags [optional]
-   *        Additional options for showing the source. Supported options:
-   *          - noSiblings: if siblings shouldn't be taken into consideration
-   *                        when searching for the associated item.
-   * @return Item
-   *         The matched item, or null if nothing is found.
-   */
-  getItemForElement: function(element, flags = {}) {
-    while (element) {
-      let item = this._itemsByElement.get(element);
-
-      // Also search the siblings if allowed.
-      if (!flags.noSiblings) {
-        item = item ||
-          this._itemsByElement.get(element.nextElementSibling) ||
-          this._itemsByElement.get(element.previousElementSibling);
-      }
-      if (item) {
-        return item;
-      }
-      element = element.parentNode;
-    }
-    return null;
-  },
-
-  /**
-   * Gets a visible item in this container validating a specified predicate.
-   *
-   * @param function predicate
-   *        The first item which validates this predicate is returned
-   * @return Item
-   *         The matched item, or null if nothing is found.
-   */
-  getItemForPredicate: function(predicate, owner = this) {
-    // Recursively check the items in this widget for a predicate match.
-    for (const [element, item] of owner._itemsByElement) {
-      let match;
-      if (predicate(item) && !element.hidden) {
-        match = item;
-      } else {
-        match = this.getItemForPredicate(predicate, item);
-      }
-      if (match) {
-        return match;
-      }
-    }
-    // Also check the staged items. No need to do this recursively since
-    // they're not even appended to the view yet.
-    for (const { item } of this._stagedItems) {
-      if (predicate(item)) {
-        return item;
-      }
-    }
-    return null;
-  },
-
-  /**
-   * Shortcut function for getItemForPredicate which works on item attachments.
-   * @see getItemForPredicate
-   */
-  getItemForAttachment: function(predicate, owner = this) {
-    return this.getItemForPredicate(e => predicate(e.attachment));
-  },
-
-  /**
-   * Finds the index of an item in the container.
-   *
-   * @param Item item
-   *        The item get the index for.
-   * @return number
-   *         The index of the matched item, or -1 if nothing is found.
-   */
-  indexOfItem: function(item) {
-    return this._indexOfElement(item._target);
-  },
-
-  /**
-   * Finds the index of an element in the container.
-   *
-   * @param Node element
-   *        The element get the index for.
-   * @return number
-   *         The index of the matched element, or -1 if nothing is found.
-   */
-  _indexOfElement: function(element) {
-    for (let i = 0; i < this._itemsByElement.size; i++) {
-      if (this._widget.getItemAtIndex(i) == element) {
-        return i;
-      }
-    }
-    return -1;
-  },
-
-  /**
-   * Gets the total number of items in this container.
-   * @return number
-   */
-  get itemCount() {
-    return this._itemsByElement.size;
-  },
-
-  /**
-   * Returns a list of items in this container, in the displayed order.
-   * @return array
-   */
-  get items() {
-    const store = [];
-    const itemCount = this.itemCount;
-    for (let i = 0; i < itemCount; i++) {
-      store.push(this.getItemAtIndex(i));
-    }
-    return store;
-  },
-
-  /**
-   * Returns a list of values in this container, in the displayed order.
-   * @return array
-   */
-  get values() {
-    return this.items.map(e => e._value);
-  },
-
-  /**
-   * Returns a list of attachments in this container, in the displayed order.
-   * @return array
-   */
-  get attachments() {
-    return this.items.map(e => e.attachment);
-  },
-
-  /**
-   * Returns a list of all the visible (non-hidden) items in this container,
-   * in the displayed order
-   * @return array
-   */
-  get visibleItems() {
-    return this.items.filter(e => !e._target.hidden);
-  },
-
-  /**
-   * Checks if an item is unique in this container. If an item's value is an
-   * empty string, "undefined" or "null", it is considered unique.
-   *
-   * @param Item item
-   *        The item for which to verify uniqueness.
-   * @return boolean
-   *         True if the item is unique, false otherwise.
-   */
-  isUnique: function(item) {
-    const value = item._value;
-    if (value == "" || value == "undefined" || value == "null") {
-      return true;
-    }
-    return !this._itemsByValue.has(value);
-  },
-
-  /**
-   * Checks if an item is eligible for this container. By default, this checks
-   * whether an item is unique and has a prebuilt target node.
-   *
-   * @param Item item
-   *        The item for which to verify eligibility.
-   * @return boolean
-   *         True if the item is eligible, false otherwise.
-   */
-  isEligible: function(item) {
-    return this.isUnique(item) && item._prebuiltNode;
-  },
-
-  /**
-   * Finds the expected item index in this container based on the default
-   * sort predicate.
-   *
-   * @param Item item
-   *        The item for which to get the expected index.
-   * @return number
-   *         The expected item index.
-   */
-  _findExpectedIndexFor: function(item) {
-    const itemCount = this.itemCount;
-    for (let i = 0; i < itemCount; i++) {
-      if (this._currentSortPredicate(this.getItemAtIndex(i), item) > 0) {
-        return i;
-      }
-    }
-    return itemCount;
-  },
-
-  /**
-   * Immediately inserts an item in this container at the specified index.
-   *
-   * @param number index
-   *        The position in the container intended for this item.
-   * @param Item item
-   *        The item describing a target element.
-   * @param object options [optional]
-   *        Additional options or flags supported by this operation:
-   *          - attributes: a batch of attributes set to the displayed element
-   *          - finalize: function when the item is untangled (removed)
-   * @return Item
-   *         The item associated with the displayed element, null if rejected.
-   */
-  _insertItemAt: function(index, item, options = {}) {
-    if (!this.isEligible(item)) {
-      return null;
-    }
-
-    // Entangle the item with the newly inserted node.
-    // Make sure this is done with the value returned by insertItemAt(),
-    // to avoid storing a potential DocumentFragment.
-    const node = item._prebuiltNode;
-    const attachment = item.attachment;
-    this._entangleItem(item,
-                       this._widget.insertItemAt(index, node, attachment));
-
-    // Handle any additional options after entangling the item.
-    if (!this._currentFilterPredicate(item)) {
-      item._target.hidden = true;
-    }
-    if (this.autoFocusOnFirstItem && this._itemsByElement.size == 1) {
-      item._target.focus();
-    }
-    if (options.attributes) {
-      options.attributes.forEach(e => item._target.setAttribute(e[0], e[1]));
-    }
-    if (options.finalize) {
-      item.finalize = options.finalize;
-    }
-
-    // Hide the empty text if the selection wasn't lost.
-    this._widget.removeAttribute("emptyText");
-
-    // Return the item associated with the displayed element.
-    return item;
-  },
-
-  /**
-   * Entangles an item (model) with a displayed node element (view).
-   *
-   * @param Item item
-   *        The item describing a target element.
-   * @param Node element
-   *        The element displaying the item.
-   */
-  _entangleItem: function(item, element) {
-    this._itemsByValue.set(item._value, item);
-    this._itemsByElement.set(element, item);
-    item._target = element;
-  },
-
-  /**
-   * Untangles an item (model) from a displayed node element (view).
-   *
-   * @param Item item
-   *        The item describing a target element.
-   */
-  _untangleItem: function(item) {
-    if (item.finalize) {
-      item.finalize(item);
-    }
-    for (const childItem of item) {
-      item.remove(childItem);
-    }
-
-    this._unlinkItem(item);
-    item._target = null;
-  },
-
-  /**
-   * Deletes an item from the its parent's storage maps.
-   *
-   * @param Item item
-   *        The item describing a target element.
-   */
-  _unlinkItem: function(item) {
-    this._itemsByValue.delete(item._value);
-    this._itemsByElement.delete(item._target);
-  },
-
-  /**
-   * The keyDown event listener for this container.
-   * @param string name
-   * @param KeyboardEvent event
-   */
-  _onWidgetKeyDown: function(event) {
-    // Prevent scrolling when pressing navigation keys.
-    ViewHelpers.preventScrolling(event);
-
-    switch (event.keyCode) {
-      case KeyCodes.DOM_VK_UP:
-      case KeyCodes.DOM_VK_LEFT:
-        this.focusPrevItem();
-        break;
-      case KeyCodes.DOM_VK_DOWN:
-      case KeyCodes.DOM_VK_RIGHT:
-        this.focusNextItem();
-        break;
-      case KeyCodes.DOM_VK_PAGE_UP:
-        this.focusItemAtDelta(-(this.pageSize ||
-                               (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
-        break;
-      case KeyCodes.DOM_VK_PAGE_DOWN:
-        this.focusItemAtDelta(+(this.pageSize ||
-                               (this.itemCount / PAGE_SIZE_ITEM_COUNT_RATIO)));
-        break;
-      case KeyCodes.DOM_VK_HOME:
-        this.focusFirstVisibleItem();
-        break;
-      case KeyCodes.DOM_VK_END:
-        this.focusLastVisibleItem();
-        break;
-    }
-  },
-
-  /**
-   * The mousePress event listener for this container.
-   * @param string name
-   * @param MouseEvent event
-   */
-  _onWidgetMousePress: function(event) {
-    if (event.button != 0 && !this.allowFocusOnRightClick) {
-      // Only allow left-click to trigger this event.
-      return;
-    }
-
-    const item = this.getItemForElement(event.target);
-    if (item) {
-      // The container is not empty and we clicked on an actual item.
-      this.selectedItem = item;
-      // Make sure the current event's target element is also focused.
-      this.autoFocusOnInput && item._target.focus();
-    }
-  },
-
-  /**
-   * The predicate used when filtering items. By default, all items in this
-   * view are visible.
-   *
-   * @param Item item
-   *        The item passing through the filter.
-   * @return boolean
-   *         True if the item should be visible, false otherwise.
-   */
-  _currentFilterPredicate: function(item) {
-    return true;
-  },
-
-  /**
-   * The predicate used when sorting items. By default, items in this view
-   * are sorted by their label.
-   *
-   * @param Item first
-   *        The first item used in the comparison.
-   * @param Item second
-   *        The second item used in the comparison.
-   * @return number
-   *         -1 to sort first to a lower index than second
-   *          0 to leave first and second unchanged with respect to each other
-   *          1 to sort second to a lower index than first
-   */
-  _currentSortPredicate: function(first, second) {
-    return +(first._value.toLowerCase() > second._value.toLowerCase());
-  },
-
-  /**
-   * Call a method on this widget named `methodName`. Any further arguments are
-   * passed on to the method. Returns the result of the method call.
-   *
-   * @param String methodName
-   *        The name of the method you want to call.
-   * @param args
-   *        Optional. Any arguments you want to pass through to the method.
-   */
-  callMethod: function(methodName, ...args) {
-    return this._widget[methodName].apply(this._widget, args);
-  },
-
-  _widget: null,
-  _emptyText: "",
-  _headerText: "",
-  _preferredValue: "",
-  _cachedCommandDispatcher: null,
-};
-
-/**
- * A generator-iterator over all the items in this container.
- */
-Item.prototype[Symbol.iterator] =
-WidgetMethods[Symbol.iterator] = function* () {
-  yield* this._itemsByElement.values();
-};
--- a/devtools/client/shared/widgets/widgets.css
+++ b/devtools/client/shared/widgets/widgets.css
@@ -8,32 +8,16 @@
 .breadcrumbs-widget-item {
   direction: ltr;
 }
 
 .breadcrumbs-widget-item {
   -moz-user-focus: normal;
 }
 
-/* SideMenuWidget */
-
-.side-menu-widget-container {
-  overflow-x: hidden;
-  overflow-y: auto;
-}
-
-.side-menu-widget-item-contents {
-  -moz-user-focus: normal;
-}
-
-.side-menu-widget-group-checkbox .checkbox-label-box,
-.side-menu-widget-item-checkbox .checkbox-label-box {
-  display: none; /* See bug 669507 */
-}
-
 /* VariablesView */
 
 .variables-view-container {
   overflow-x: hidden;
   overflow-y: auto;
   direction: ltr;
 }
 
--- a/devtools/client/themes/widgets.css
+++ b/devtools/client/themes/widgets.css
@@ -96,129 +96,16 @@
     /* To hide generic-toggled-pane, negative margins are applied dynamically.
      * If a vertical layout, the pane is on the bottom and should be hidden
      * using negative bottom margin only.
      */
     margin-inline-end: 0 !important;
   }
 }
 
-/* SideMenuWidget */
-
-.side-menu-widget-container {
-  /* Hack: force hardware acceleration */
-  transform: translateZ(1px);
-}
-
-/* SideMenuWidget container */
-
-.side-menu-widget-container[with-arrows=true] .side-menu-widget-item {
-  /* To compensate for the arrow image's dark margin. */
-  margin-inline-end: -1px;
-}
-
-/* SideMenuWidget groups */
-
-.side-menu-widget-group-title {
-  padding: 4px;
-  font-weight: 600;
-  border-bottom: 1px solid rgba(128,128,128,0.15);
-}
-
-.side-menu-widget-group-title + .side-menu-widget-group-list .side-menu-widget-item-contents {
-  padding-inline-start: 20px;
-}
-
-/* SideMenuWidget items */
-
-.side-menu-widget-item {
-  border-bottom: 1px solid rgba(128,128,128,0.15);
-  background-clip: padding-box;
-}
-
-.side-menu-widget-item.selected {
-  background-color: var(--theme-selection-background);
-  color: var(--theme-selection-color);
-}
-
-.side-menu-widget-item-arrow {
-  margin-inline-start: -7px;
-  width: 7px; /* The image's width is 7 pixels */
-}
-
-.side-menu-widget-item.selected > .side-menu-widget-item-arrow {
-  background-image: var(--sidemenu-selected-arrow);
-  background-size: auto;
-  background-repeat: no-repeat;
-  background-position: center right;
-}
-
-.side-menu-widget-item.selected > .side-menu-widget-item-arrow:-moz-locale-dir(rtl) {
-  background-image: var(--sidemenu-selected-arrow-rtl);
-  background-position: center left;
-}
-
-/* SideMenuWidget items contents */
-
-.side-menu-widget-item-contents {
-  padding: 4px;
-  /* To avoid having content overlapping the arrow image. */
-  padding-inline-end: 8px;
-}
-
-.side-menu-widget-item-other {
-  /* To avoid having content overlapping the arrow image. */
-  padding-inline-end: 8px;
-  /* To compensate for the .side-menu-widget-item-contents padding. */
-  margin-inline-start: -4px;
-  margin-inline-end: -8px;
-}
-
-.side-menu-widget-group-title + .side-menu-widget-group-list .side-menu-widget-item-other {
-  /* To compensate for the .side-menu-widget-item-contents padding. */
-  margin-inline-start: -20px;
-}
-
-.side-menu-widget-item.selected .side-menu-widget-item-other:not(.selected) {
-  background-color: var(--theme-sidebar-background);
-  box-shadow: inset 2px 0 0 var(--theme-selection-background);
-  color: var(--theme-body-color);
-}
-
-.side-menu-widget-item.selected .side-menu-widget-item-other.selected {
-  background-color: var(--theme-selection-background);
-}
-
-.side-menu-widget-item-other:first-of-type {
-  margin-top: 4px;
-}
-
-.side-menu-widget-item-other:last-of-type {
-  margin-bottom: -4px;
-}
-
-/* SideMenuWidget checkboxes */
-
-.side-menu-widget-group-checkbox {
-  margin: 0;
-  margin-inline-end: 4px;
-}
-
-.side-menu-widget-item-checkbox {
-  margin: 0;
-  margin-inline-start: 4px;
-}
-
-/* SideMenuWidget misc */
-
-.side-menu-widget-empty-text {
-  padding: 4px 8px;
-  background-color: var(--theme-sidebar-background);
-}
-
 /* VariablesView */
 
 .variables-view-container {
   /* Hack: force hardware acceleration */
   transform: translateZ(1px);
 }
 
 .variables-view-empty-notice {