Bug 1102240 - split inspector actor in smaller files;r=pbro,ochameau draft
authorJulian Descottes <jdescottes@mozilla.com>
Fri, 26 Jan 2018 13:11:58 +0100
changeset 748813 82ec49987a132cdf09722a891bca1ba02ca895cb
parent 748812 5f35db40dda61e7e476fae1153f10b3d580a6929
child 748814 3261e4b20359aa09363f5193e8411d5f7e15466e
push id97244
push userjdescottes@mozilla.com
push dateTue, 30 Jan 2018 14:06:36 +0000
reviewerspbro, ochameau
bugs1102240
milestone60.0a1
Bug 1102240 - split inspector actor in smaller files;r=pbro,ochameau MozReview-Commit-ID: LgZav4dMQRR
devtools/client/inspector/markup/test/actor_events_form.js
devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
devtools/server/actors/highlighters/box-model.js
devtools/server/actors/inspector/document-walker.js
devtools/server/actors/inspector/inspector.js
devtools/server/actors/inspector/moz.build
devtools/server/actors/inspector/node-actor.js
devtools/server/actors/inspector/utils.js
devtools/server/actors/inspector/walker-actor.js
devtools/server/actors/moz.build
devtools/server/actors/webextension-inspected-window.js
devtools/server/tests/mochitest/inspector-helpers.js
devtools/server/tests/mochitest/test_inspector-anonymous.html
devtools/server/tests/mochitest/test_inspector-insert.html
devtools/server/tests/mochitest/test_inspector-mutations-value.html
devtools/server/tests/mochitest/test_inspector-traversal.html
devtools/server/tests/unit/test_nodelistactor.js
--- a/devtools/client/inspector/markup/test/actor_events_form.js
+++ b/devtools/client/inspector/markup/test/actor_events_form.js
@@ -6,17 +6,17 @@
 // This test actor is used for testing the addition of custom form data
 // on NodeActor. Custom form property is set when 'form' event is sent
 // by NodeActor actor (see 'onNodeActorForm' method).
 
 const EventEmitter = require("devtools/shared/event-emitter");
 const {ActorClassWithSpec, Actor, FrontClassWithSpec, Front, generateActorSpec} =
   require("devtools/shared/protocol");
 
-const {NodeActor} = require("devtools/server/actors/inspector/inspector");
+const {NodeActor} = require("devtools/server/actors/inspector/node-actor");
 
 var eventsSpec = generateActorSpec({
   typeName: "eventsFormActor",
 
   methods: {
     attach: {
       request: {},
       response: {}
--- a/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
+++ b/devtools/client/inspector/markup/test/browser_markup_textcontent_edit_01.js
@@ -2,17 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test editing a node's text content
 
 const TEST_URL = URL_ROOT + "doc_markup_edit.html";
-const {DEFAULT_VALUE_SUMMARY_LENGTH} = require("devtools/server/actors/inspector/inspector");
+const {DEFAULT_VALUE_SUMMARY_LENGTH} = require("devtools/server/actors/inspector/walker-actor");
 
 add_task(function* () {
   let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
 
   info("Expanding all nodes");
   yield inspector.markup.expandAll();
   yield waitForMultipleChildrenUpdates(inspector);
 
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -13,17 +13,17 @@ const {
   hasPseudoClassLock,
   isNodeValid,
   moveInfobar,
 } = require("./utils/markup");
 const {
   setIgnoreLayoutChanges,
   getCurrentZoom,
  } = require("devtools/shared/layout/utils");
-const inspector = require("devtools/server/actors/inspector/inspector");
+const { getNodeDisplayName } = require("devtools/server/actors/inspector/utils");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 
 // Note that the order of items in this array is important because it is used
 // for drawing the BoxModelHighlighter's path elements correctly.
 const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
 const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
 // Width of boxmodelhighlighter guides
 const GUIDE_STROKE_WIDTH = 1;
@@ -671,17 +671,17 @@ class BoxModelHighlighter extends AutoRe
     if (!this.currentNode) {
       return;
     }
 
     let {bindingElement: node, pseudo} =
         getBindingElementAndPseudo(this.currentNode);
 
     // Update the tag, id, classes, pseudo-classes and dimensions
-    let displayName = inspector.getNodeDisplayName(node);
+    let displayName = getNodeDisplayName(node);
 
     let id = node.id ? "#" + node.id : "";
 
     let classList = (node.classList || []).length
                     ? "." + [...node.classList].join(".")
                     : "";
 
     let pseudos = this._getPseudoClasses(node).join("");
copy from devtools/server/actors/inspector/inspector.js
copy to devtools/server/actors/inspector/document-walker.js
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/document-walker.js
@@ -1,3055 +1,24 @@
 /* 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";
 
-/**
- * Here's the server side of the remote inspector.
- *
- * The WalkerActor is the client's view of the debuggee's DOM.  It's gives
- * the client a tree of NodeActor objects.
- *
- * The walker presents the DOM tree mostly unmodified from the source DOM
- * tree, but with a few key differences:
- *
- *  - Empty text nodes are ignored.  This is pretty typical of developer
- *    tools, but maybe we should reconsider that on the server side.
- *  - iframes with documents loaded have the loaded document as the child,
- *    the walker provides one big tree for the whole document tree.
- *
- * There are a few ways to get references to NodeActors:
- *
- *   - When you first get a WalkerActor reference, it comes with a free
- *     reference to the root document's node.
- *   - Given a node, you can ask for children, siblings, and parents.
- *   - You can issue querySelector and querySelectorAll requests to find
- *     other elements.
- *   - Requests that return arbitrary nodes from the tree (like querySelector
- *     and querySelectorAll) will also return any nodes the client hasn't
- *     seen in order to have a complete set of parents.
- *
- * Once you have a NodeFront, you should be able to answer a few questions
- * without further round trips, like the node's name, namespace/tagName,
- * attributes, etc.  Other questions (like a text node's full nodeValue)
- * might require another round trip.
- *
- * The protocol guarantees that the client will always know the parent of
- * any node that is returned by the server.  This means that some requests
- * (like querySelector) will include the extra nodes needed to satisfy this
- * requirement.  The client keeps track of this parent relationship, so the
- * node fronts form a tree that is a subset of the actual DOM tree.
- *
- *
- * We maintain this guarantee to support the ability to release subtrees on
- * the client - when a node is disconnected from the DOM tree we want to be
- * able to free the client objects for all the children nodes.
- *
- * So to be able to answer "all the children of a given node that we have
- * seen on the client side", we guarantee that every time we've seen a node,
- * we connect it up through its parents.
- */
-
 const {Cc, Ci, Cu} = require("chrome");
-const Services = require("Services");
-const protocol = require("devtools/shared/protocol");
-const {LongStringActor} = require("devtools/server/actors/string");
-const promise = require("promise");
-const defer = require("devtools/shared/defer");
-const {Task} = require("devtools/shared/task");
-const EventEmitter = require("devtools/shared/event-emitter");
-const InspectorUtils = require("InspectorUtils");
 
-const {walkerSpec, inspectorSpec} = require("devtools/shared/specs/inspector");
-const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");
-
-loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
-loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
-loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
-loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "getCssPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "getXPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
-loader.lazyRequireGetter(this, "EyeDropper", "devtools/server/actors/highlighters/eye-dropper", true);
-loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);
-loader.lazyRequireGetter(this, "PageStyleActor", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "getFontPreviewData", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
-loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
-loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
-loader.lazyRequireGetter(this, "HighlighterActor", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "HighlighterEnvironment", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "EventParsers", "devtools/server/event-parsers", true);
-loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isXBLAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isShadowAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getFrameElement", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
 loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");
-
-loader.lazyServiceGetter(this, "DOMParser",
-  "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
-
-loader.lazyServiceGetter(this, "eventListenerService",
-  "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
-
-const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
-const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
-const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
-const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
-const SVG_NS = "http://www.w3.org/2000/svg";
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const IMAGE_FETCHING_TIMEOUT = 500;
-
-// Minimum delay between two "new-mutations" events.
-const MUTATIONS_THROTTLING_DELAY = 100;
-// List of mutation types that should -not- be throttled.
-const IMMEDIATE_MUTATIONS = [
-  "documentUnload",
-  "frameLoad",
-  "newRoot",
-  "pseudoClassLock",
-];
+loader.lazyRequireGetter(this, "standardTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);
 
 // SKIP_TO_* arguments are used with the DocumentWalker, driving the strategy to use if
 // the starting node is incompatible with the filter function of the walker.
 const SKIP_TO_PARENT = "SKIP_TO_PARENT";
 const SKIP_TO_SIBLING = "SKIP_TO_SIBLING";
 
-// The possible completions to a ':' with added score to give certain values
-// some preference.
-const PSEUDO_SELECTORS = [
-  [":active", 1],
-  [":hover", 1],
-  [":focus", 1],
-  [":visited", 0],
-  [":link", 0],
-  [":first-letter", 0],
-  [":first-child", 2],
-  [":before", 2],
-  [":after", 2],
-  [":lang(", 0],
-  [":not(", 3],
-  [":first-of-type", 0],
-  [":last-of-type", 0],
-  [":only-of-type", 0],
-  [":only-child", 2],
-  [":nth-child(", 3],
-  [":nth-last-child(", 0],
-  [":nth-of-type(", 0],
-  [":nth-last-of-type(", 0],
-  [":last-child", 2],
-  [":root", 0],
-  [":empty", 0],
-  [":target", 0],
-  [":enabled", 0],
-  [":disabled", 0],
-  [":checked", 1],
-  ["::selection", 0]
-];
-
-var HELPER_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
-  .__fx-devtools-hide-shortcut__ {
-    visibility: hidden !important;
-  }
-
-  :-moz-devtools-highlighted {
-    outline: 2px dashed #F06!important;
-    outline-offset: -2px !important;
-  }
-`);
-
-/**
- * We only send nodeValue up to a certain size by default.  This stuff
- * controls that size.
- */
-exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50;
-var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH;
-
-exports.getValueSummaryLength = function () {
-  return gValueSummaryLength;
-};
-
-exports.setValueSummaryLength = function (val) {
-  gValueSummaryLength = val;
-};
-
-/**
- * Returns the properly cased version of the node's tag name, which can be
- * used when displaying said name in the UI.
- *
- * @param  {Node} rawNode
- *         Node for which we want the display name
- * @return {String}
- *         Properly cased version of the node tag name
- */
-const getNodeDisplayName = function (rawNode) {
-  if (rawNode.nodeName && !rawNode.localName) {
-    // The localName & prefix APIs have been moved from the Node interface to the Element
-    // interface. Use Node.nodeName as a fallback.
-    return rawNode.nodeName;
-  }
-  return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
-};
-exports.getNodeDisplayName = getNodeDisplayName;
-
-/**
- * Server side of the node actor.
- */
-var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
-  initialize: function (walker, node) {
-    protocol.Actor.prototype.initialize.call(this, null);
-    this.walker = walker;
-    this.rawNode = node;
-    this._eventParsers = new EventParsers().parsers;
-
-    // Storing the original display of the node, to track changes when reflows
-    // occur
-    this.wasDisplayed = this.isDisplayed;
-  },
-
-  toString: function () {
-    return "[NodeActor " + this.actorID + " for " +
-      this.rawNode.toString() + "]";
-  },
-
-  /**
-   * Instead of storing a connection object, the NodeActor gets its connection
-   * from its associated walker.
-   */
-  get conn() {
-    return this.walker.conn;
-  },
-
-  isDocumentElement: function () {
-    return this.rawNode.ownerDocument &&
-           this.rawNode.ownerDocument.documentElement === this.rawNode;
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-
-    if (this.mutationObserver) {
-      if (!Cu.isDeadWrapper(this.mutationObserver)) {
-        this.mutationObserver.disconnect();
-      }
-      this.mutationObserver = null;
-    }
-    this.rawNode = null;
-    this.walker = null;
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function (detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-
-    let parentNode = this.walker.parentNode(this);
-    let inlineTextChild = this.walker.inlineTextChild(this);
-
-    let form = {
-      actor: this.actorID,
-      baseURI: this.rawNode.baseURI,
-      parent: parentNode ? parentNode.actorID : undefined,
-      nodeType: this.rawNode.nodeType,
-      namespaceURI: this.rawNode.namespaceURI,
-      nodeName: this.rawNode.nodeName,
-      nodeValue: this.rawNode.nodeValue,
-      displayName: getNodeDisplayName(this.rawNode),
-      numChildren: this.numChildren,
-      inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
-
-      // doctype attributes
-      name: this.rawNode.name,
-      publicId: this.rawNode.publicId,
-      systemId: this.rawNode.systemId,
-
-      attrs: this.writeAttrs(),
-      isBeforePseudoElement: this.isBeforePseudoElement,
-      isAfterPseudoElement: this.isAfterPseudoElement,
-      isAnonymous: isAnonymous(this.rawNode),
-      isNativeAnonymous: isNativeAnonymous(this.rawNode),
-      isXBLAnonymous: isXBLAnonymous(this.rawNode),
-      isShadowAnonymous: isShadowAnonymous(this.rawNode),
-      pseudoClassLocks: this.writePseudoClassLocks(),
-
-      isDisplayed: this.isDisplayed,
-      isInHTMLDocument: this.rawNode.ownerDocument &&
-        this.rawNode.ownerDocument.contentType === "text/html",
-      hasEventListeners: this._hasEventListeners,
-    };
-
-    if (this.isDocumentElement()) {
-      form.isDocumentElement = true;
-    }
-
-    // Add an extra API for custom properties added by other
-    // modules/extensions.
-    form.setFormProperty = (name, value) => {
-      if (!form.props) {
-        form.props = {};
-      }
-      form.props[name] = value;
-    };
-
-    // Fire an event so, other modules can create its own properties
-    // that should be passed to the client (within the form.props field).
-    EventEmitter.emit(NodeActor, "form", {
-      target: this,
-      data: form
-    });
-
-    return form;
-  },
-
-  /**
-   * Watch the given document node for mutations using the DOM observer
-   * API.
-   */
-  watchDocument: function (callback) {
-    let node = this.rawNode;
-    // Create the observer on the node's actor.  The node will make sure
-    // the observer is cleaned up when the actor is released.
-    let observer = new node.defaultView.MutationObserver(callback);
-    observer.mergeAttributeRecords = true;
-    observer.observe(node, {
-      nativeAnonymousChildList: true,
-      attributes: true,
-      characterData: true,
-      characterDataOldValue: true,
-      childList: true,
-      subtree: true
-    });
-    this.mutationObserver = observer;
-  },
-
-  get isBeforePseudoElement() {
-    return this.rawNode.nodeName === "_moz_generated_content_before";
-  },
-
-  get isAfterPseudoElement() {
-    return this.rawNode.nodeName === "_moz_generated_content_after";
-  },
-
-  // Estimate the number of children that the walker will return without making
-  // a call to children() if possible.
-  get numChildren() {
-    // For pseudo elements, childNodes.length returns 1, but the walker
-    // will return 0.
-    if (this.isBeforePseudoElement || this.isAfterPseudoElement) {
-      return 0;
-    }
-
-    let rawNode = this.rawNode;
-    let numChildren = rawNode.childNodes.length;
-    let hasAnonChildren = rawNode.nodeType === Ci.nsIDOMNode.ELEMENT_NODE &&
-                          rawNode.ownerDocument.getAnonymousNodes(rawNode);
-
-    let hasContentDocument = rawNode.contentDocument;
-    let hasSVGDocument = rawNode.getSVGDocument && rawNode.getSVGDocument();
-    if (numChildren === 0 && (hasContentDocument || hasSVGDocument)) {
-      // This might be an iframe with virtual children.
-      numChildren = 1;
-    }
-
-    // Normal counting misses ::before/::after.  Also, some anonymous children
-    // may ultimately be skipped, so we have to consult with the walker.
-    if (numChildren === 0 || hasAnonChildren) {
-      numChildren = this.walker.children(this).nodes.length;
-    }
-
-    return numChildren;
-  },
-
-  get computedStyle() {
-    return CssLogic.getComputedStyle(this.rawNode);
-  },
-
-  /**
-   * Is the node's display computed style value other than "none"
-   */
-  get isDisplayed() {
-    // Consider all non-element nodes as displayed.
-    if (isNodeDead(this) ||
-        this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
-        this.isAfterPseudoElement ||
-        this.isBeforePseudoElement) {
-      return true;
-    }
-
-    let style = this.computedStyle;
-    if (!style) {
-      return true;
-    }
-
-    return style.display !== "none";
-  },
-
-  /**
-   * Are there event listeners that are listening on this node? This method
-   * uses all parsers registered via event-parsers.js.registerEventParser() to
-   * check if there are any event listeners.
-   */
-  get _hasEventListeners() {
-    let parsers = this._eventParsers;
-    for (let [, {hasListeners}] of parsers) {
-      try {
-        if (hasListeners && hasListeners(this.rawNode)) {
-          return true;
-        }
-      } catch (e) {
-        // An object attached to the node looked like a listener but wasn't...
-        // do nothing.
-      }
-    }
-    return false;
-  },
-
-  writeAttrs: function () {
-    if (!this.rawNode.attributes) {
-      return undefined;
-    }
-
-    return [...this.rawNode.attributes].map(attr => {
-      return {namespace: attr.namespace, name: attr.name, value: attr.value };
-    });
-  },
-
-  writePseudoClassLocks: function () {
-    if (this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
-      return undefined;
-    }
-    let ret = undefined;
-    for (let pseudo of PSEUDO_CLASSES) {
-      if (InspectorUtils.hasPseudoClassLock(this.rawNode, pseudo)) {
-        ret = ret || [];
-        ret.push(pseudo);
-      }
-    }
-    return ret;
-  },
-
-  /**
-   * Gets event listeners and adds their information to the events array.
-   *
-   * @param  {Node} node
-   *         Node for which we are to get listeners.
-   */
-  getEventListeners: function (node) {
-    let parsers = this._eventParsers;
-    let dbg = this.parent().tabActor.makeDebugger();
-    let listenerArray = [];
-
-    for (let [, {getListeners, normalizeListener}] of parsers) {
-      try {
-        let listeners = getListeners(node);
-
-        if (!listeners) {
-          continue;
-        }
-
-        for (let listener of listeners) {
-          if (normalizeListener) {
-            listener.normalizeListener = normalizeListener;
-          }
-
-          this.processHandlerForEvent(node, listenerArray, dbg, listener);
-        }
-      } catch (e) {
-        // An object attached to the node looked like a listener but wasn't...
-        // do nothing.
-      }
-    }
-
-    listenerArray.sort((a, b) => {
-      return a.type.localeCompare(b.type);
-    });
-
-    return listenerArray;
-  },
-
-  /**
-   * Process a handler
-   *
-   * @param  {Node} node
-   *         The node for which we want information.
-   * @param  {Array} listenerArray
-   *         listenerArray contains all event objects that we have gathered
-   *         so far.
-   * @param  {Debugger} dbg
-   *         JSDebugger instance.
-   * @param  {Object} eventInfo
-   *         See event-parsers.js.registerEventParser() for a description of the
-   *         eventInfo object.
-   *
-   * @return {Array}
-   *         An array of objects where a typical object looks like this:
-   *           {
-   *             type: "click",
-   *             handler: function() { doSomething() },
-   *             origin: "http://www.mozilla.com",
-   *             searchString: 'onclick="doSomething()"',
-   *             tags: tags,
-   *             DOM0: true,
-   *             capturing: true,
-   *             hide: {
-   *               DOM0: true
-   *             },
-   *             native: false
-   *           }
-   */
-  processHandlerForEvent: function (node, listenerArray, dbg, listener) {
-    let { handler } = listener;
-    let global = Cu.getGlobalForObject(handler);
-    let globalDO = dbg.addDebuggee(global);
-    let listenerDO = globalDO.makeDebuggeeValue(handler);
-
-    let { normalizeListener } = listener;
-
-    if (normalizeListener) {
-      listenerDO = normalizeListener(listenerDO, listener);
-    }
-
-    let { capturing } = listener;
-    let dom0 = false;
-    let functionSource = handler.toString();
-    let hide = listener.hide || {};
-    let line = 0;
-    let native = false;
-    let override = listener.override || {};
-    let tags = listener.tags || "";
-    let type = listener.type || "";
-    let url = "";
-
-    // If the listener is an object with a 'handleEvent' method, use that.
-    if (listenerDO.class === "Object" || listenerDO.class === "XULElement") {
-      let desc;
-
-      while (!desc && listenerDO) {
-        desc = listenerDO.getOwnPropertyDescriptor("handleEvent");
-        listenerDO = listenerDO.proto;
-      }
-
-      if (desc && desc.value) {
-        listenerDO = desc.value;
-      }
-    }
-
-    // If the listener is bound to a different context then we need to switch
-    // to the bound function.
-    if (listenerDO.isBoundFunction) {
-      listenerDO = listenerDO.boundTargetFunction;
-    }
-
-    let { isArrowFunction, name, script, parameterNames } = listenerDO;
-
-    if (script) {
-      let scriptSource = script.source.text;
-
-      // Scripts are provided via script tags. If it wasn't provided by a
-      // script tag it must be a DOM0 event.
-      if (script.source.element) {
-        dom0 = script.source.element.class !== "HTMLScriptElement";
-      } else {
-        dom0 = false;
-      }
-
-      line = script.startLine;
-      url = script.url;
-
-      // Checking for the string "[native code]" is the only way at this point
-      // to check for native code. Even if this provides a false positive then
-      // grabbing the source code a second time is harmless.
-      if (functionSource === "[object Object]" ||
-          functionSource === "[object XULElement]" ||
-          functionSource.includes("[native code]")) {
-        functionSource =
-          scriptSource.substr(script.sourceStart, script.sourceLength);
-
-        // At this point the script looks like this:
-        // () { ... }
-        // We prefix this with "function" if it is not a fat arrow function.
-        if (!isArrowFunction) {
-          functionSource = "function " + functionSource;
-        }
-      }
-    } else {
-      // If the listener is a native one (provided by C++ code) then we have no
-      // access to the script. We use the native flag to prevent showing the
-      // debugger button because the script is not available.
-      native = true;
-    }
-
-    // Fat arrow function text always contains the parameters. Function
-    // parameters are often missing e.g. if Array.sort is used as a handler.
-    // If they are missing we provide the parameters ourselves.
-    if (parameterNames && parameterNames.length > 0) {
-      let prefix = "function " + name + "()";
-      let paramString = parameterNames.join(", ");
-
-      if (functionSource.startsWith(prefix)) {
-        functionSource = functionSource.substr(prefix.length);
-
-        functionSource = `function ${name} (${paramString})${functionSource}`;
-      }
-    }
-
-    // If the listener is native code we display the filename "[native code]."
-    // This is the official string and should *not* be translated.
-    let origin;
-    if (native) {
-      origin = "[native code]";
-    } else {
-      origin = url + ((dom0 || line === 0) ? "" : ":" + line);
-    }
-
-    let eventObj = {
-      type: override.type || type,
-      handler: override.handler || functionSource.trim(),
-      origin: override.origin || origin,
-      tags: override.tags || tags,
-      DOM0: typeof override.dom0 !== "undefined" ? override.dom0 : dom0,
-      capturing: typeof override.capturing !== "undefined" ?
-                 override.capturing : capturing,
-      hide: typeof override.hide !== "undefined" ? override.hide : hide,
-      native
-    };
-
-    // Hide the debugger icon for DOM0 and native listeners. DOM0 listeners are
-    // generated dynamically from e.g. an onclick="" attribute so the script
-    // doesn't actually exist.
-    if (native || dom0) {
-      eventObj.hide.debugger = true;
-    }
-
-    listenerArray.push(eventObj);
-
-    dbg.removeDebuggee(globalDO);
-  },
-
-  /**
-   * Returns a LongStringActor with the node's value.
-   */
-  getNodeValue: function () {
-    return new LongStringActor(this.conn, this.rawNode.nodeValue || "");
-  },
-
-  /**
-   * Set the node's value to a given string.
-   */
-  setNodeValue: function (value) {
-    this.rawNode.nodeValue = value;
-  },
-
-  /**
-   * Get a unique selector string for this node.
-   */
-  getUniqueSelector: function () {
-    if (Cu.isDeadWrapper(this.rawNode)) {
-      return "";
-    }
-    return findCssSelector(this.rawNode);
-  },
-
-  /**
-   * Get the full CSS path for this node.
-   *
-   * @return {String} A CSS selector with a part for the node and each of its ancestors.
-   */
-  getCssPath: function () {
-    if (Cu.isDeadWrapper(this.rawNode)) {
-      return "";
-    }
-    return getCssPath(this.rawNode);
-  },
-
-  /**
-   * Get the XPath for this node.
-   *
-   * @return {String} The XPath for finding this node on the page.
-   */
-  getXPath: function () {
-    if (Cu.isDeadWrapper(this.rawNode)) {
-      return "";
-    }
-    return getXPath(this.rawNode);
-  },
-
-  /**
-   * Scroll the selected node into view.
-   */
-  scrollIntoView: function () {
-    this.rawNode.scrollIntoView(true);
-  },
-
-  /**
-   * Get the node's image data if any (for canvas and img nodes).
-   * Returns an imageData object with the actual data being a LongStringActor
-   * and a size json object.
-   * The image data is transmitted as a base64 encoded png data-uri.
-   * The method rejects if the node isn't an image or if the image is missing
-   *
-   * Accepts a maxDim request parameter to resize images that are larger. This
-   * is important as the resizing occurs server-side so that image-data being
-   * transfered in the longstring back to the client will be that much smaller
-   */
-  getImageData: function (maxDim) {
-    return imageToImageData(this.rawNode, maxDim).then(imageData => {
-      return {
-        data: LongStringActor(this.conn, imageData.data),
-        size: imageData.size
-      };
-    });
-  },
-
-  /**
-   * Get all event listeners that are listening on this node.
-   */
-  getEventListenerInfo: function () {
-    let node = this.rawNode;
-
-    if (this.rawNode.nodeName.toLowerCase() === "html") {
-      let winListeners = this.getEventListeners(node.ownerGlobal) || [];
-      let docElementListeners = this.getEventListeners(node) || [];
-      let docListeners = this.getEventListeners(node.parentNode) || [];
-
-      return [...winListeners, ...docElementListeners, ...docListeners];
-    }
-    return this.getEventListeners(node);
-  },
-
-  /**
-   * Modify a node's attributes.  Passed an array of modifications
-   * similar in format to "attributes" mutations.
-   * {
-   *   attributeName: <string>
-   *   attributeNamespace: <optional string>
-   *   newValue: <optional string> - If null or undefined, the attribute
-   *     will be removed.
-   * }
-   *
-   * Returns when the modifications have been made.  Mutations will
-   * be queued for any changes made.
-   */
-  modifyAttributes: function (modifications) {
-    let rawNode = this.rawNode;
-    for (let change of modifications) {
-      if (change.newValue == null) {
-        if (change.attributeNamespace) {
-          rawNode.removeAttributeNS(change.attributeNamespace,
-                                    change.attributeName);
-        } else {
-          rawNode.removeAttribute(change.attributeName);
-        }
-      } else if (change.attributeNamespace) {
-        rawNode.setAttributeNS(change.attributeNamespace, change.attributeName,
-                               change.newValue);
-      } else {
-        rawNode.setAttribute(change.attributeName, change.newValue);
-      }
-    }
-  },
-
-  /**
-   * Given the font and fill style, get the image data of a canvas with the
-   * preview text and font.
-   * Returns an imageData object with the actual data being a LongStringActor
-   * and the width of the text as a string.
-   * The image data is transmitted as a base64 encoded png data-uri.
-   */
-  getFontFamilyDataURL: function (font, fillStyle = "black") {
-    let doc = this.rawNode.ownerDocument;
-    let options = {
-      previewText: FONT_FAMILY_PREVIEW_TEXT,
-      previewFontSize: FONT_FAMILY_PREVIEW_TEXT_SIZE,
-      fillStyle: fillStyle
-    };
-    let { dataURL, size } = getFontPreviewData(font, doc, options);
-
-    return { data: LongStringActor(this.conn, dataURL), size: size };
-  },
-
-  /**
-   * Finds the computed background color of the closest parent with
-   * a set background color.
-   * Returns a string with the background color of the form
-   * rgba(r, g, b, a). Defaults to rgba(255, 255, 255, 1) if no
-   * background color is found.
-   */
-  getClosestBackgroundColor: function () {
-    let current = this.rawNode;
-    while (current) {
-      let computedStyle = CssLogic.getComputedStyle(current);
-      let currentStyle = computedStyle.getPropertyValue("background-color");
-      if (colorUtils.isValidCSSColor(currentStyle)) {
-        let currentCssColor = new colorUtils.CssColor(currentStyle);
-        if (!currentCssColor.isTransparent()) {
-          return currentCssColor.rgba;
-        }
-      }
-      current = current.parentNode;
-    }
-    return "rgba(255, 255, 255, 1)";
-  }
-});
-
-/**
- * Server side of a node list as returned by querySelectorAll()
- */
-var NodeListActor = exports.NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
-  typeName: "domnodelist",
-
-  initialize: function (walker, nodeList) {
-    protocol.Actor.prototype.initialize.call(this);
-    this.walker = walker;
-    this.nodeList = nodeList || [];
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-  },
-
-  /**
-   * Instead of storing a connection object, the NodeActor gets its connection
-   * from its associated walker.
-   */
-  get conn() {
-    return this.walker.conn;
-  },
-
-  /**
-   * Items returned by this actor should belong to the parent walker.
-   */
-  marshallPool: function () {
-    return this.walker;
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function () {
-    return {
-      actor: this.actorID,
-      length: this.nodeList ? this.nodeList.length : 0
-    };
-  },
-
-  /**
-   * Get a single node from the node list.
-   */
-  item: function (index) {
-    return this.walker.attachElement(this.nodeList[index]);
-  },
-
-  /**
-   * Get a range of the items from the node list.
-   */
-  items: function (start = 0, end = this.nodeList.length) {
-    let items = Array.prototype.slice.call(this.nodeList, start, end)
-      .map(item => this.walker._ref(item));
-    return this.walker.attachElements(items);
-  },
-
-  release: function () {}
-});
-
-/**
- * Server side of the DOM walker.
- */
-var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
-  /**
-   * Create the WalkerActor
-   * @param DebuggerServerConnection conn
-   *    The server connection.
-   */
-  initialize: function (conn, tabActor, options) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this.tabActor = tabActor;
-    this.rootWin = tabActor.window;
-    this.rootDoc = this.rootWin.document;
-    this._refMap = new Map();
-    this._pendingMutations = [];
-    this._activePseudoClassLocks = new Set();
-    this.showAllAnonymousContent = options.showAllAnonymousContent;
-
-    this.walkerSearch = new WalkerSearch(this);
-
-    // Nodes which have been removed from the client's known
-    // ownership tree are considered "orphaned", and stored in
-    // this set.
-    this._orphaned = new Set();
-
-    // The client can tell the walker that it is interested in a node
-    // even when it is orphaned with the `retainNode` method.  This
-    // list contains orphaned nodes that were so retained.
-    this._retainedOrphans = new Set();
-
-    this.onMutations = this.onMutations.bind(this);
-    this.onFrameLoad = this.onFrameLoad.bind(this);
-    this.onFrameUnload = this.onFrameUnload.bind(this);
-    this._throttledEmitNewMutations = throttle(this._emitNewMutations.bind(this),
-      MUTATIONS_THROTTLING_DELAY);
-
-    tabActor.on("will-navigate", this.onFrameUnload);
-    tabActor.on("window-ready", this.onFrameLoad);
-
-    // Ensure that the root document node actor is ready and
-    // managed.
-    this.rootNode = this.document();
-
-    this.layoutChangeObserver = getLayoutChangesObserver(this.tabActor);
-    this._onReflows = this._onReflows.bind(this);
-    this.layoutChangeObserver.on("reflows", this._onReflows);
-    this._onResize = this._onResize.bind(this);
-    this.layoutChangeObserver.on("resize", this._onResize);
-
-    this._onEventListenerChange = this._onEventListenerChange.bind(this);
-    eventListenerService.addListenerChangeListener(this._onEventListenerChange);
-  },
-
-  /**
-   * Callback for eventListenerService.addListenerChangeListener
-   * @param nsISimpleEnumerator changesEnum
-   *    enumerator of nsIEventListenerChange
-   */
-  _onEventListenerChange: function (changesEnum) {
-    let changes = changesEnum.enumerate();
-    while (changes.hasMoreElements()) {
-      let current = changes.getNext().QueryInterface(Ci.nsIEventListenerChange);
-      let target = current.target;
-
-      if (this._refMap.has(target)) {
-        let actor = this.getNode(target);
-        let mutation = {
-          type: "events",
-          target: actor.actorID,
-          hasEventListeners: actor._hasEventListeners
-        };
-        this.queueMutation(mutation);
-      }
-    }
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function () {
-    return {
-      actor: this.actorID,
-      root: this.rootNode.form(),
-      traits: {
-        // FF42+ Inspector starts managing the Walker, while the inspector also
-        // starts cleaning itself up automatically on client disconnection.
-        // So that there is no need to manually release the walker anymore.
-        autoReleased: true,
-        // XXX: It seems silly that we need to tell the front which capabilities
-        // its actor has in this way when the target can use actorHasMethod. If
-        // this was ported to the protocol (Bug 1157048) we could call that
-        // inside of custom front methods and not need to do traits for this.
-        multiFrameQuerySelectorAll: true,
-        textSearch: true,
-      }
-    };
-  },
-
-  toString: function () {
-    return "[WalkerActor " + this.actorID + "]";
-  },
-
-  getDocumentWalker: function (node, whatToShow, skipTo) {
-    // Allow native anon content (like <video> controls) if preffed on
-    let nodeFilter = this.showAllAnonymousContent
-                    ? allAnonymousContentTreeWalkerFilter
-                    : standardTreeWalkerFilter;
-    return new DocumentWalker(node, this.rootWin, whatToShow, nodeFilter, skipTo);
-  },
-
-  destroy: function () {
-    if (this._destroyed) {
-      return;
-    }
-    this._destroyed = true;
-    protocol.Actor.prototype.destroy.call(this);
-    try {
-      this.clearPseudoClassLocks();
-      this._activePseudoClassLocks = null;
-
-      this._hoveredNode = null;
-      this.rootWin = null;
-      this.rootDoc = null;
-      this.rootNode = null;
-      this.layoutHelpers = null;
-      this._orphaned = null;
-      this._retainedOrphans = null;
-      this._refMap = null;
-
-      this.tabActor.off("will-navigate", this.onFrameUnload);
-      this.tabActor.off("window-ready", this.onFrameLoad);
-
-      this.onFrameLoad = null;
-      this.onFrameUnload = null;
-
-      this.walkerSearch.destroy();
-
-      this.layoutChangeObserver.off("reflows", this._onReflows);
-      this.layoutChangeObserver.off("resize", this._onResize);
-      this.layoutChangeObserver = null;
-      releaseLayoutChangesObserver(this.tabActor);
-
-      eventListenerService.removeListenerChangeListener(
-        this._onEventListenerChange);
-
-      this.onMutations = null;
-
-      this.layoutActor = null;
-      this.tabActor = null;
-
-      this.emit("destroyed");
-    } catch (e) {
-      console.error(e);
-    }
-  },
-
-  release: function () {},
-
-  unmanage: function (actor) {
-    if (actor instanceof NodeActor) {
-      if (this._activePseudoClassLocks &&
-          this._activePseudoClassLocks.has(actor)) {
-        this.clearPseudoClassLocks(actor);
-      }
-      this._refMap.delete(actor.rawNode);
-    }
-    protocol.Actor.prototype.unmanage.call(this, actor);
-  },
-
-  /**
-   * Determine if the walker has come across this DOM node before.
-   * @param {DOMNode} rawNode
-   * @return {Boolean}
-   */
-  hasNode: function (rawNode) {
-    return this._refMap.has(rawNode);
-  },
-
-  /**
-   * If the walker has come across this DOM node before, then get the
-   * corresponding node actor.
-   * @param {DOMNode} rawNode
-   * @return {NodeActor}
-   */
-  getNode: function (rawNode) {
-    return this._refMap.get(rawNode);
-  },
-
-  _ref: function (node) {
-    let actor = this.getNode(node);
-    if (actor) {
-      return actor;
-    }
-
-    actor = new NodeActor(this, node);
-
-    // Add the node actor as a child of this walker actor, assigning
-    // it an actorID.
-    this.manage(actor);
-    this._refMap.set(node, actor);
-
-    if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
-      actor.watchDocument(this.onMutations);
-    }
-    return actor;
-  },
-
-  _onReflows: function (reflows) {
-    // Going through the nodes the walker knows about, see which ones have
-    // had their display changed and send a display-change event if any
-    let changes = [];
-    for (let [node, actor] of this._refMap) {
-      if (Cu.isDeadWrapper(node)) {
-        continue;
-      }
-
-      let isDisplayed = actor.isDisplayed;
-      if (isDisplayed !== actor.wasDisplayed) {
-        changes.push(actor);
-        // Updating the original value
-        actor.wasDisplayed = isDisplayed;
-      }
-    }
-
-    if (changes.length) {
-      this.emit("display-change", changes);
-    }
-  },
-
-  /**
-   * When the browser window gets resized, relay the event to the front.
-   */
-  _onResize: function () {
-    this.emit("resize");
-  },
-
-  /**
-   * This is kept for backward-compatibility reasons with older remote targets.
-   * Targets prior to bug 916443.
-   *
-   * pick/cancelPick are used to pick a node on click on the content
-   * document. But in their implementation prior to bug 916443, they don't allow
-   * highlighting on hover.
-   * The client-side now uses the highlighter actor's pick and cancelPick
-   * methods instead. The client-side uses the the highlightable trait found in
-   * the root actor to determine which version of pick to use.
-   *
-   * As for highlight, the new highlighter actor is used instead of the walker's
-   * highlight method. Same here though, the client-side uses the highlightable
-   * trait to dertermine which to use.
-   *
-   * Keeping these actor methods for now allows newer client-side debuggers to
-   * inspect fxos 1.2 remote targets or older firefox desktop remote targets.
-   */
-  pick: function () {},
-  cancelPick: function () {},
-  highlight: function (node) {},
-
-  /**
-   * Ensures that the node is attached and it can be accessed from the root.
-   *
-   * @param {(Node|NodeActor)} nodes The nodes
-   * @return {Object} An object compatible with the disconnectedNode type.
-   */
-  attachElement: function (node) {
-    let { nodes, newParents } = this.attachElements([node]);
-    return {
-      node: nodes[0],
-      newParents: newParents
-    };
-  },
-
-  /**
-   * Ensures that the nodes are attached and they can be accessed from the root.
-   *
-   * @param {(Node[]|NodeActor[])} nodes The nodes
-   * @return {Object} An object compatible with the disconnectedNodeArray type.
-   */
-  attachElements: function (nodes) {
-    let nodeActors = [];
-    let newParents = new Set();
-    for (let node of nodes) {
-      if (!(node instanceof NodeActor)) {
-        // If an anonymous node was passed in and we aren't supposed to know
-        // about it, then consult with the document walker as the source of
-        // truth about which elements exist.
-        if (!this.showAllAnonymousContent && isAnonymous(node)) {
-          node = this.getDocumentWalker(node).currentNode;
-        }
-
-        node = this._ref(node);
-      }
-
-      this.ensurePathToRoot(node, newParents);
-      // If nodes may be an array of raw nodes, we're sure to only have
-      // NodeActors with the following array.
-      nodeActors.push(node);
-    }
-
-    return {
-      nodes: nodeActors,
-      newParents: [...newParents]
-    };
-  },
-
-  /**
-   * Return the document node that contains the given node,
-   * or the root node if no node is specified.
-   * @param NodeActor node
-   *        The node whose document is needed, or null to
-   *        return the root.
-   */
-  document: function (node) {
-    let doc = isNodeDead(node) ? this.rootDoc : nodeDocument(node.rawNode);
-    return this._ref(doc);
-  },
-
-  /**
-   * Return the documentElement for the document containing the
-   * given node.
-   * @param NodeActor node
-   *        The node whose documentElement is requested, or null
-   *        to use the root document.
-   */
-  documentElement: function (node) {
-    let elt = isNodeDead(node)
-              ? this.rootDoc.documentElement
-              : nodeDocument(node.rawNode).documentElement;
-    return this._ref(elt);
-  },
-
-  /**
-   * Return all parents of the given node, ordered from immediate parent
-   * to root.
-   * @param NodeActor node
-   *    The node whose parents are requested.
-   * @param object options
-   *    Named options, including:
-   *    `sameDocument`: If true, parents will be restricted to the same
-   *      document as the node.
-   *    `sameTypeRootTreeItem`: If true, this will not traverse across
-   *     different types of docshells.
-   */
-  parents: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return [];
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let parents = [];
-    let cur;
-    while ((cur = walker.parentNode())) {
-      if (options.sameDocument &&
-          nodeDocument(cur) != nodeDocument(node.rawNode)) {
-        break;
-      }
-
-      if (options.sameTypeRootTreeItem &&
-          nodeDocshell(cur).sameTypeRootTreeItem !=
-          nodeDocshell(node.rawNode).sameTypeRootTreeItem) {
-        break;
-      }
-
-      parents.push(this._ref(cur));
-    }
-    return parents;
-  },
-
-  parentNode: function (node) {
-    let walker = this.getDocumentWalker(node.rawNode);
-    let parent = walker.parentNode();
-    if (parent) {
-      return this._ref(parent);
-    }
-    return null;
-  },
-
-  /**
-   * If the given NodeActor only has a single text node as a child with a text
-   * content small enough to be inlined, return that child's NodeActor.
-   *
-   * @param NodeActor node
-   */
-  inlineTextChild: function (node) {
-    // Quick checks to prevent creating a new walker if possible.
-    if (node.isBeforePseudoElement ||
-        node.isAfterPseudoElement ||
-        node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
-        node.rawNode.children.length > 0) {
-      return undefined;
-    }
-
-    let docWalker = this.getDocumentWalker(node.rawNode);
-    let firstChild = docWalker.firstChild();
-
-    // Bail out if:
-    // - more than one child
-    // - unique child is not a text node
-    // - unique child is a text node, but is too long to be inlined
-    if (!firstChild ||
-        docWalker.nextSibling() ||
-        firstChild.nodeType !== Ci.nsIDOMNode.TEXT_NODE ||
-        firstChild.nodeValue.length > gValueSummaryLength
-        ) {
-      return undefined;
-    }
-
-    return this._ref(firstChild);
-  },
-
-  /**
-   * Mark a node as 'retained'.
-   *
-   * A retained node is not released when `releaseNode` is called on its
-   * parent, or when a parent is released with the `cleanup` option to
-   * `getMutations`.
-   *
-   * When a retained node's parent is released, a retained mode is added to
-   * the walker's "retained orphans" list.
-   *
-   * Retained nodes can be deleted by providing the `force` option to
-   * `releaseNode`.  They will also be released when their document
-   * has been destroyed.
-   *
-   * Retaining a node makes no promise about its children;  They can
-   * still be removed by normal means.
-   */
-  retainNode: function (node) {
-    node.retained = true;
-  },
-
-  /**
-   * Remove the 'retained' mark from a node.  If the node was a
-   * retained orphan, release it.
-   */
-  unretainNode: function (node) {
-    node.retained = false;
-    if (this._retainedOrphans.has(node)) {
-      this._retainedOrphans.delete(node);
-      this.releaseNode(node);
-    }
-  },
-
-  /**
-   * Release actors for a node and all child nodes.
-   */
-  releaseNode: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    if (node.retained && !options.force) {
-      this._retainedOrphans.add(node);
-      return;
-    }
-
-    if (node.retained) {
-      // Forcing a retained node to go away.
-      this._retainedOrphans.delete(node);
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-
-    let child = walker.firstChild();
-    while (child) {
-      let childActor = this.getNode(child);
-      if (childActor) {
-        this.releaseNode(childActor, options);
-      }
-      child = walker.nextSibling();
-    }
-
-    node.destroy();
-  },
-
-  /**
-   * Add any nodes between `node` and the walker's root node that have not
-   * yet been seen by the client.
-   */
-  ensurePathToRoot: function (node, newParents = new Set()) {
-    if (!node) {
-      return newParents;
-    }
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let parent = this.getNode(cur);
-      if (!parent) {
-        // This parent didn't exist, so hasn't been seen by the client yet.
-        newParents.add(this._ref(cur));
-      } else {
-        // This parent did exist, so the client knows about it.
-        return newParents;
-      }
-    }
-    return newParents;
-  },
-
-  /**
-   * Return children of the given node.  By default this method will return
-   * all children of the node, but there are options that can restrict this
-   * to a more manageable subset.
-   *
-   * @param NodeActor node
-   *    The node whose children you're curious about.
-   * @param object options
-   *    Named options:
-   *    `maxNodes`: The set of nodes returned by the method will be no longer
-   *       than maxNodes.
-   *    `start`: If a node is specified, the list of nodes will start
-   *       with the given child.  Mutally exclusive with `center`.
-   *    `center`: If a node is specified, the given node will be as centered
-   *       as possible in the list, given how close to the ends of the child
-   *       list it is.  Mutually exclusive with `start`.
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   *
-   * @returns an object with three items:
-   *    hasFirst: true if the first child of the node is included in the list.
-   *    hasLast: true if the last child of the node is included in the list.
-   *    nodes: Child nodes returned by the request.
-   */
-  children: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    if (options.center && options.start) {
-      throw Error("Can't specify both 'center' and 'start' options.");
-    }
-    let maxNodes = options.maxNodes || -1;
-    if (maxNodes == -1) {
-      maxNodes = Number.MAX_VALUE;
-    }
-
-    // We're going to create a few document walkers with the same filter,
-    // make it easier.
-    let getFilteredWalker = documentWalkerNode => {
-      let { whatToShow } = options;
-      // Use SKIP_TO_SIBLING to force the walker to use a sibling of the provided node
-      // in case this one is incompatible with the walker's filter function.
-      return this.getDocumentWalker(documentWalkerNode, whatToShow, SKIP_TO_SIBLING);
-    };
-
-    // Need to know the first and last child.
-    let rawNode = node.rawNode;
-    let firstChild = getFilteredWalker(rawNode).firstChild();
-    let lastChild = getFilteredWalker(rawNode).lastChild();
-
-    if (!firstChild) {
-      // No children, we're done.
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    let start;
-    if (options.center) {
-      start = options.center.rawNode;
-    } else if (options.start) {
-      start = options.start.rawNode;
-    } else {
-      start = firstChild;
-    }
-
-    let nodes = [];
-
-    // Start by reading backward from the starting point if we're centering...
-    let backwardWalker = getFilteredWalker(start);
-    if (backwardWalker.currentNode != firstChild && options.center) {
-      backwardWalker.previousSibling();
-      let backwardCount = Math.floor(maxNodes / 2);
-      let backwardNodes = this._readBackward(backwardWalker, backwardCount);
-      nodes = backwardNodes;
-    }
-
-    // Then read forward by any slack left in the max children...
-    let forwardWalker = getFilteredWalker(start);
-    let forwardCount = maxNodes - nodes.length;
-    nodes = nodes.concat(this._readForward(forwardWalker, forwardCount));
-
-    // If there's any room left, it means we've run all the way to the end.
-    // If we're centering, check if there are more items to read at the front.
-    let remaining = maxNodes - nodes.length;
-    if (options.center && remaining > 0 && nodes[0].rawNode != firstChild) {
-      let firstNodes = this._readBackward(backwardWalker, remaining);
-
-      // Then put it all back together.
-      nodes = firstNodes.concat(nodes);
-    }
-
-    return {
-      hasFirst: nodes[0].rawNode == firstChild,
-      hasLast: nodes[nodes.length - 1].rawNode == lastChild,
-      nodes: nodes
-    };
-  },
-
-  /**
-   * Return siblings of the given node.  By default this method will return
-   * all siblings of the node, but there are options that can restrict this
-   * to a more manageable subset.
-   *
-   * If `start` or `center` are not specified, this method will center on the
-   * node whose siblings are requested.
-   *
-   * @param NodeActor node
-   *    The node whose children you're curious about.
-   * @param object options
-   *    Named options:
-   *    `maxNodes`: The set of nodes returned by the method will be no longer
-   *       than maxNodes.
-   *    `start`: If a node is specified, the list of nodes will start
-   *       with the given child.  Mutally exclusive with `center`.
-   *    `center`: If a node is specified, the given node will be as centered
-   *       as possible in the list, given how close to the ends of the child
-   *       list it is.  Mutually exclusive with `start`.
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   *
-   * @returns an object with three items:
-   *    hasFirst: true if the first child of the node is included in the list.
-   *    hasLast: true if the last child of the node is included in the list.
-   *    nodes: Child nodes returned by the request.
-   */
-  siblings: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    let parentNode = this.getDocumentWalker(node.rawNode, options.whatToShow)
-                         .parentNode();
-    if (!parentNode) {
-      return {
-        hasFirst: true,
-        hasLast: true,
-        nodes: [node]
-      };
-    }
-
-    if (!(options.start || options.center)) {
-      options.center = node;
-    }
-
-    return this.children(this._ref(parentNode), options);
-  },
-
-  /**
-   * Get the next sibling of a given node.  Getting nodes one at a time
-   * might be inefficient, be careful.
-   *
-   * @param object options
-   *    Named options:
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   */
-  nextSibling: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
-    let sibling = walker.nextSibling();
-    return sibling ? this._ref(sibling) : null;
-  },
-
-  /**
-   * Get the previous sibling of a given node.  Getting nodes one at a time
-   * might be inefficient, be careful.
-   *
-   * @param object options
-   *    Named options:
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   */
-  previousSibling: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
-    let sibling = walker.previousSibling();
-    return sibling ? this._ref(sibling) : null;
-  },
-
-  /**
-   * Helper function for the `children` method: Read forward in the sibling
-   * list into an array with `count` items, including the current node.
-   */
-  _readForward: function (walker, count) {
-    let ret = [];
-
-    let node = walker.currentNode;
-    do {
-      if (!walker.isSkippedNode(node)) {
-        // The walker can be on a node that would be filtered out if it didn't find any
-        // other node to fallback to.
-        ret.push(this._ref(node));
-      }
-      node = walker.nextSibling();
-    } while (node && --count);
-    return ret;
-  },
-
-  /**
-   * Helper function for the `children` method: Read backward in the sibling
-   * list into an array with `count` items, including the current node.
-   */
-  _readBackward: function (walker, count) {
-    let ret = [];
-
-    let node = walker.currentNode;
-    do {
-      if (!walker.isSkippedNode(node)) {
-        // The walker can be on a node that would be filtered out if it didn't find any
-        // other node to fallback to.
-        ret.push(this._ref(node));
-      }
-      node = walker.previousSibling();
-    } while (node && --count);
-    ret.reverse();
-    return ret;
-  },
-
-  /**
-   * Return the first node in the document that matches the given selector.
-   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelector
-   *
-   * @param NodeActor baseNode
-   * @param string selector
-   */
-  querySelector: function (baseNode, selector) {
-    if (isNodeDead(baseNode)) {
-      return {};
-    }
-
-    let node = baseNode.rawNode.querySelector(selector);
-    if (!node) {
-      return {};
-    }
-
-    return this.attachElement(node);
-  },
-
-  /**
-   * Return a NodeListActor with all nodes that match the given selector.
-   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll
-   *
-   * @param NodeActor baseNode
-   * @param string selector
-   */
-  querySelectorAll: function (baseNode, selector) {
-    let nodeList = null;
-
-    try {
-      nodeList = baseNode.rawNode.querySelectorAll(selector);
-    } catch (e) {
-      // Bad selector. Do nothing as the selector can come from a searchbox.
-    }
-
-    return new NodeListActor(this, nodeList);
-  },
-
-  /**
-   * Get a list of nodes that match the given selector in all known frames of
-   * the current content page.
-   * @param {String} selector.
-   * @return {Array}
-   */
-  _multiFrameQuerySelectorAll: function (selector) {
-    let nodes = [];
-
-    for (let {document} of this.tabActor.windows) {
-      try {
-        nodes = [...nodes, ...document.querySelectorAll(selector)];
-      } catch (e) {
-        // Bad selector. Do nothing as the selector can come from a searchbox.
-      }
-    }
-
-    return nodes;
-  },
-
-  /**
-   * Return a NodeListActor with all nodes that match the given selector in all
-   * frames of the current content page.
-   * @param {String} selector
-   */
-  multiFrameQuerySelectorAll: function (selector) {
-    return new NodeListActor(this, this._multiFrameQuerySelectorAll(selector));
-  },
-
-  /**
-   * Search the document for a given string.
-   * Results will be searched with the walker-search module (searches through
-   * tag names, attribute names and values, and text contents).
-   *
-   * @returns {searchresult}
-   *            - {NodeList} list
-   *            - {Array<Object>} metadata. Extra information with indices that
-   *                              match up with node list.
-   */
-  search: function (query) {
-    let results = this.walkerSearch.search(query);
-    let nodeList = new NodeListActor(this, results.map(r => r.node));
-
-    return {
-      list: nodeList,
-      metadata: []
-    };
-  },
-
-  /**
-   * Returns a list of matching results for CSS selector autocompletion.
-   *
-   * @param string query
-   *        The selector query being completed
-   * @param string completing
-   *        The exact token being completed out of the query
-   * @param string selectorState
-   *        One of "pseudo", "id", "tag", "class", "null"
-   */
-  getSuggestionsForQuery: function (query, completing, selectorState) {
-    let sugs = {
-      classes: new Map(),
-      tags: new Map(),
-      ids: new Map()
-    };
-    let result = [];
-    let nodes = null;
-    // Filtering and sorting the results so that protocol transfer is miminal.
-    switch (selectorState) {
-      case "pseudo":
-        result = PSEUDO_SELECTORS.filter(item => {
-          return item[0].startsWith(":" + completing);
-        });
-        break;
-
-      case "class":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("[class]");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          for (let className of node.classList) {
-            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
-          }
-        }
-        sugs.classes.delete("");
-        sugs.classes.delete(HIDDEN_CLASS);
-        for (let [className, count] of sugs.classes) {
-          if (className.startsWith(completing)) {
-            result.push(["." + CSS.escape(className), count, selectorState]);
-          }
-        }
-        break;
-
-      case "id":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("[id]");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
-        }
-        for (let [id, count] of sugs.ids) {
-          if (id.startsWith(completing) && id !== "") {
-            result.push(["#" + CSS.escape(id), count, selectorState]);
-          }
-        }
-        break;
-
-      case "tag":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("*");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          let tag = node.localName;
-          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
-        }
-        for (let [tag, count] of sugs.tags) {
-          if ((new RegExp("^" + completing + ".*", "i")).test(tag)) {
-            result.push([tag, count, selectorState]);
-          }
-        }
-
-        // For state 'tag' (no preceding # or .) and when there's no query (i.e.
-        // only one word) then search for the matching classes and ids
-        if (!query) {
-          result = [
-            ...result,
-            ...this.getSuggestionsForQuery(null, completing, "class")
-                   .suggestions,
-            ...this.getSuggestionsForQuery(null, completing, "id")
-                   .suggestions
-          ];
-        }
-
-        break;
-
-      case "null":
-        nodes = this._multiFrameQuerySelectorAll(query);
-        for (let node of nodes) {
-          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
-          let tag = node.localName;
-          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
-          for (let className of node.classList) {
-            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
-          }
-        }
-        for (let [tag, count] of sugs.tags) {
-          tag && result.push([tag, count]);
-        }
-        for (let [id, count] of sugs.ids) {
-          id && result.push(["#" + id, count]);
-        }
-        sugs.classes.delete("");
-        sugs.classes.delete(HIDDEN_CLASS);
-        for (let [className, count] of sugs.classes) {
-          className && result.push(["." + className, count]);
-        }
-    }
-
-    // Sort by count (desc) and name (asc)
-    result = result.sort((a, b) => {
-      // Computed a sortable string with first the inverted count, then the name
-      let sortA = (10000 - a[1]) + a[0];
-      let sortB = (10000 - b[1]) + b[0];
-
-      // Prefixing ids, classes and tags, to group results
-      let firstA = a[0].substring(0, 1);
-      let firstB = b[0].substring(0, 1);
-
-      if (firstA === "#") {
-        sortA = "2" + sortA;
-      } else if (firstA === ".") {
-        sortA = "1" + sortA;
-      } else {
-        sortA = "0" + sortA;
-      }
-
-      if (firstB === "#") {
-        sortB = "2" + sortB;
-      } else if (firstB === ".") {
-        sortB = "1" + sortB;
-      } else {
-        sortB = "0" + sortB;
-      }
-
-      // String compare
-      return sortA.localeCompare(sortB);
-    });
-
-    result.slice(0, 25);
-
-    return {
-      query: query,
-      suggestions: result
-    };
-  },
-
-  /**
-   * Add a pseudo-class lock to a node.
-   *
-   * @param NodeActor node
-   * @param string pseudo
-   *    A pseudoclass: ':hover', ':active', ':focus'
-   * @param options
-   *    Options object:
-   *    `parents`: True if the pseudo-class should be added
-   *      to parent nodes.
-   *    `enabled`: False if the pseudo-class should be locked
-   *      to 'off'. Defaults to true.
-   *
-   * @returns An empty packet.  A "pseudoClassLock" mutation will
-   *    be queued for any changed nodes.
-   */
-  addPseudoClassLock: function (node, pseudo, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    // There can be only one node locked per pseudo, so dismiss all existing
-    // ones
-    for (let locked of this._activePseudoClassLocks) {
-      if (InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
-        this._removePseudoClassLock(locked, pseudo);
-      }
-    }
-
-    let enabled = options.enabled === undefined ||
-                  options.enabled;
-    this._addPseudoClassLock(node, pseudo, enabled);
-
-    if (!options.parents) {
-      return;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let curNode = this._ref(cur);
-      this._addPseudoClassLock(curNode, pseudo, enabled);
-    }
-  },
-
-  _queuePseudoClassMutation: function (node) {
-    this.queueMutation({
-      target: node.actorID,
-      type: "pseudoClassLock",
-      pseudoClassLocks: node.writePseudoClassLocks()
-    });
-  },
-
-  _addPseudoClassLock: function (node, pseudo, enabled) {
-    if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
-      return false;
-    }
-    InspectorUtils.addPseudoClassLock(node.rawNode, pseudo, enabled);
-    this._activePseudoClassLocks.add(node);
-    this._queuePseudoClassMutation(node);
-    return true;
-  },
-
-  hideNode: function (node) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    loadSheet(node.rawNode.ownerGlobal, HELPER_SHEET);
-    node.rawNode.classList.add(HIDDEN_CLASS);
-  },
-
-  unhideNode: function (node) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    node.rawNode.classList.remove(HIDDEN_CLASS);
-  },
-
-  /**
-   * Remove a pseudo-class lock from a node.
-   *
-   * @param NodeActor node
-   * @param string pseudo
-   *    A pseudoclass: ':hover', ':active', ':focus'
-   * @param options
-   *    Options object:
-   *    `parents`: True if the pseudo-class should be removed
-   *      from parent nodes.
-   *
-   * @returns An empty response.  "pseudoClassLock" mutations
-   *    will be emitted for any changed nodes.
-   */
-  removePseudoClassLock: function (node, pseudo, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    this._removePseudoClassLock(node, pseudo);
-
-    // Remove pseudo class for children as we don't want to allow
-    // turning it on for some childs without setting it on some parents
-    for (let locked of this._activePseudoClassLocks) {
-      if (node.rawNode.contains(locked.rawNode) &&
-          InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
-        this._removePseudoClassLock(locked, pseudo);
-      }
-    }
-
-    if (!options.parents) {
-      return;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let curNode = this._ref(cur);
-      this._removePseudoClassLock(curNode, pseudo);
-    }
-  },
-
-  _removePseudoClassLock: function (node, pseudo) {
-    if (node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) {
-      return false;
-    }
-    InspectorUtils.removePseudoClassLock(node.rawNode, pseudo);
-    if (!node.writePseudoClassLocks()) {
-      this._activePseudoClassLocks.delete(node);
-    }
-
-    this._queuePseudoClassMutation(node);
-    return true;
-  },
-
-  /**
-   * Clear all the pseudo-classes on a given node or all nodes.
-   * @param {NodeActor} node Optional node to clear pseudo-classes on
-   */
-  clearPseudoClassLocks: function (node) {
-    if (node && isNodeDead(node)) {
-      return;
-    }
-
-    if (node) {
-      InspectorUtils.clearPseudoClassLocks(node.rawNode);
-      this._activePseudoClassLocks.delete(node);
-      this._queuePseudoClassMutation(node);
-    } else {
-      for (let locked of this._activePseudoClassLocks) {
-        InspectorUtils.clearPseudoClassLocks(locked.rawNode);
-        this._activePseudoClassLocks.delete(locked);
-        this._queuePseudoClassMutation(locked);
-      }
-    }
-  },
-
-  /**
-   * Get a node's innerHTML property.
-   */
-  innerHTML: function (node) {
-    let html = "";
-    if (!isNodeDead(node)) {
-      html = node.rawNode.innerHTML;
-    }
-    return LongStringActor(this.conn, html);
-  },
-
-  /**
-   * Set a node's innerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   * @param {string} value The piece of HTML content.
-   */
-  setInnerHTML: function (node, value) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    let rawNode = node.rawNode;
-    if (rawNode.nodeType !== rawNode.ownerDocument.ELEMENT_NODE) {
-      throw new Error("Can only change innerHTML to element nodes");
-    }
-    // eslint-disable-next-line no-unsanitized/property
-    rawNode.innerHTML = value;
-  },
-
-  /**
-   * Get a node's outerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   */
-  outerHTML: function (node) {
-    let outerHTML = "";
-    if (!isNodeDead(node)) {
-      outerHTML = node.rawNode.outerHTML;
-    }
-    return LongStringActor(this.conn, outerHTML);
-  },
-
-  /**
-   * Set a node's outerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   * @param {string} value The piece of HTML content.
-   */
-  setOuterHTML: function (node, value) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    let parsedDOM = DOMParser.parseFromString(value, "text/html");
-    let rawNode = node.rawNode;
-    let parentNode = rawNode.parentNode;
-
-    // Special case for head and body.  Setting document.body.outerHTML
-    // creates an extra <head> tag, and document.head.outerHTML creates
-    // an extra <body>.  So instead we will call replaceChild with the
-    // parsed DOM, assuming that they aren't trying to set both tags at once.
-    if (rawNode.tagName === "BODY") {
-      if (parsedDOM.head.innerHTML === "") {
-        parentNode.replaceChild(parsedDOM.body, rawNode);
-      } else {
-      // eslint-disable-next-line no-unsanitized/property
-        rawNode.outerHTML = value;
-      }
-    } else if (rawNode.tagName === "HEAD") {
-      if (parsedDOM.body.innerHTML === "") {
-        parentNode.replaceChild(parsedDOM.head, rawNode);
-      } else {
-        // eslint-disable-next-line no-unsanitized/property
-        rawNode.outerHTML = value;
-      }
-    } else if (node.isDocumentElement()) {
-      // Unable to set outerHTML on the document element.  Fall back by
-      // setting attributes manually, then replace the body and head elements.
-      let finalAttributeModifications = [];
-      let attributeModifications = {};
-      for (let attribute of rawNode.attributes) {
-        attributeModifications[attribute.name] = null;
-      }
-      for (let attribute of parsedDOM.documentElement.attributes) {
-        attributeModifications[attribute.name] = attribute.value;
-      }
-      for (let key in attributeModifications) {
-        finalAttributeModifications.push({
-          attributeName: key,
-          newValue: attributeModifications[key]
-        });
-      }
-      node.modifyAttributes(finalAttributeModifications);
-      rawNode.replaceChild(parsedDOM.head, rawNode.querySelector("head"));
-      rawNode.replaceChild(parsedDOM.body, rawNode.querySelector("body"));
-    } else {
-      // eslint-disable-next-line no-unsanitized/property
-      rawNode.outerHTML = value;
-    }
-  },
-
-  /**
-   * Insert adjacent HTML to a node.
-   *
-   * @param {Node} node
-   * @param {string} position One of "beforeBegin", "afterBegin", "beforeEnd",
-   *                          "afterEnd" (see Element.insertAdjacentHTML).
-   * @param {string} value The HTML content.
-   */
-  insertAdjacentHTML: function (node, position, value) {
-    if (isNodeDead(node)) {
-      return {node: [], newParents: []};
-    }
-
-    let rawNode = node.rawNode;
-    let isInsertAsSibling = position === "beforeBegin" ||
-      position === "afterEnd";
-
-    // Don't insert anything adjacent to the document element.
-    if (isInsertAsSibling && node.isDocumentElement()) {
-      throw new Error("Can't insert adjacent element to the root.");
-    }
-
-    let rawParentNode = rawNode.parentNode;
-    if (!rawParentNode && isInsertAsSibling) {
-      throw new Error("Can't insert as sibling without parent node.");
-    }
-
-    // We can't use insertAdjacentHTML, because we want to return the nodes
-    // being created (so the front can remove them if the user undoes
-    // the change). So instead, use Range.createContextualFragment().
-    let range = rawNode.ownerDocument.createRange();
-    if (position === "beforeBegin" || position === "afterEnd") {
-      range.selectNode(rawNode);
-    } else {
-      range.selectNodeContents(rawNode);
-    }
-    let docFrag = range.createContextualFragment(value);
-    let newRawNodes = Array.from(docFrag.childNodes);
-    switch (position) {
-      case "beforeBegin":
-        rawParentNode.insertBefore(docFrag, rawNode);
-        break;
-      case "afterEnd":
-        // Note: if the second argument is null, rawParentNode.insertBefore
-        // behaves like rawParentNode.appendChild.
-        rawParentNode.insertBefore(docFrag, rawNode.nextSibling);
-        break;
-      case "afterBegin":
-        rawNode.insertBefore(docFrag, rawNode.firstChild);
-        break;
-      case "beforeEnd":
-        rawNode.appendChild(docFrag);
-        break;
-      default:
-        throw new Error("Invalid position value. Must be either " +
-          "'beforeBegin', 'beforeEnd', 'afterBegin' or 'afterEnd'.");
-    }
-
-    return this.attachElements(newRawNodes);
-  },
-
-  /**
-   * Duplicate a specified node
-   *
-   * @param {NodeActor} node The node to duplicate.
-   */
-  duplicateNode: function ({rawNode}) {
-    let clonedNode = rawNode.cloneNode(true);
-    rawNode.parentNode.insertBefore(clonedNode, rawNode.nextSibling);
-  },
-
-  /**
-   * Test whether a node is a document or a document element.
-   *
-   * @param {NodeActor} node The node to remove.
-   * @return {boolean} True if the node is a document or a document element.
-   */
-  isDocumentOrDocumentElementNode: function (node) {
-    return ((node.rawNode.ownerDocument &&
-      node.rawNode.ownerDocument.documentElement === this.rawNode) ||
-      node.rawNode.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE);
-  },
-
-  /**
-   * Removes a node from its parent node.
-   *
-   * @param {NodeActor} node The node to remove.
-   * @returns The node's nextSibling before it was removed.
-   */
-  removeNode: function (node) {
-    if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
-      throw Error("Cannot remove document, document elements or dead nodes.");
-    }
-
-    let nextSibling = this.nextSibling(node);
-    node.rawNode.remove();
-    // Mutation events will take care of the rest.
-    return nextSibling;
-  },
-
-  /**
-   * Removes an array of nodes from their parent node.
-   *
-   * @param {NodeActor[]} nodes The nodes to remove.
-   */
-  removeNodes: function (nodes) {
-    // Check that all nodes are valid before processing the removals.
-    for (let node of nodes) {
-      if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
-        throw Error("Cannot remove document, document elements or dead nodes");
-      }
-    }
-
-    for (let node of nodes) {
-      node.rawNode.remove();
-      // Mutation events will take care of the rest.
-    }
-  },
-
-  /**
-   * Insert a node into the DOM.
-   */
-  insertBefore: function (node, parent, sibling) {
-    if (isNodeDead(node) ||
-        isNodeDead(parent) ||
-        (sibling && isNodeDead(sibling))) {
-      return;
-    }
-
-    let rawNode = node.rawNode;
-    let rawParent = parent.rawNode;
-    let rawSibling = sibling ? sibling.rawNode : null;
-
-    // Don't bother inserting a node if the document position isn't going
-    // to change. This prevents needless iframes reloading and mutations.
-    if (rawNode.parentNode === rawParent) {
-      let currentNextSibling = this.nextSibling(node);
-      currentNextSibling = currentNextSibling ? currentNextSibling.rawNode :
-                                                null;
-
-      if (rawNode === rawSibling || currentNextSibling === rawSibling) {
-        return;
-      }
-    }
-
-    rawParent.insertBefore(rawNode, rawSibling);
-  },
-
-  /**
-   * Editing a node's tagname actually means creating a new node with the same
-   * attributes, removing the node and inserting the new one instead.
-   * This method does not return anything as mutation events are taking care of
-   * informing the consumers about changes.
-   */
-  editTagName: function (node, tagName) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let oldNode = node.rawNode;
-
-    // Create a new element with the same attributes as the current element and
-    // prepare to replace the current node with it.
-    let newNode;
-    try {
-      newNode = nodeDocument(oldNode).createElement(tagName);
-    } catch (x) {
-      // Failed to create a new element with that tag name, ignore the change,
-      // and signal the error to the front.
-      return Promise.reject(new Error("Could not change node's tagName to " + tagName));
-    }
-
-    let attrs = oldNode.attributes;
-    for (let i = 0; i < attrs.length; i++) {
-      newNode.setAttribute(attrs[i].name, attrs[i].value);
-    }
-
-    // Insert the new node, and transfer the old node's children.
-    oldNode.parentNode.insertBefore(newNode, oldNode);
-    while (oldNode.firstChild) {
-      newNode.appendChild(oldNode.firstChild);
-    }
-
-    oldNode.remove();
-    return null;
-  },
-
-  /**
-   * Get any pending mutation records.  Must be called by the client after
-   * the `new-mutations` notification is received.  Returns an array of
-   * mutation records.
-   *
-   * Mutation records have a basic structure:
-   *
-   * {
-   *   type: attributes|characterData|childList,
-   *   target: <domnode actor ID>,
-   * }
-   *
-   * And additional attributes based on the mutation type:
-   *
-   * `attributes` type:
-   *   attributeName: <string> - the attribute that changed
-   *   attributeNamespace: <string> - the attribute's namespace URI, if any.
-   *   newValue: <string> - The new value of the attribute, if any.
-   *
-   * `characterData` type:
-   *   newValue: <string> - the new nodeValue for the node
-   *
-   * `childList` type is returned when the set of children for a node
-   * has changed.  Includes extra data, which can be used by the client to
-   * maintain its ownership subtree.
-   *
-   *   added: array of <domnode actor ID> - The list of actors *previously
-   *     seen by the client* that were added to the target node.
-   *   removed: array of <domnode actor ID> The list of actors *previously
-   *     seen by the client* that were removed from the target node.
-   *   inlineTextChild: If the node now has a single text child, it will
-   *     be sent here.
-   *
-   * Actors that are included in a MutationRecord's `removed` but
-   * not in an `added` have been removed from the client's ownership
-   * tree (either by being moved under a node the client has seen yet
-   * or by being removed from the tree entirely), and is considered
-   * 'orphaned'.
-   *
-   * Keep in mind that if a node that the client hasn't seen is moved
-   * into or out of the target node, it will not be included in the
-   * removedNodes and addedNodes list, so if the client is interested
-   * in the new set of children it needs to issue a `children` request.
-   */
-  getMutations: function (options = {}) {
-    let pending = this._pendingMutations || [];
-    this._pendingMutations = [];
-    this._waitingForGetMutations = false;
-
-    if (options.cleanup) {
-      for (let node of this._orphaned) {
-        // Release the orphaned node.  Nodes or children that have been
-        // retained will be moved to this._retainedOrphans.
-        this.releaseNode(node);
-      }
-      this._orphaned = new Set();
-    }
-
-    return pending;
-  },
-
-  queueMutation: function (mutation) {
-    if (!this.actorID || this._destroyed) {
-      // We've been destroyed, don't bother queueing this mutation.
-      return;
-    }
-
-    // Add the mutation to the list of mutations to be retrieved next.
-    this._pendingMutations.push(mutation);
-
-    // Bail out if we already emitted a new-mutations event and are waiting for a client
-    // to retrieve them.
-    if (this._waitingForGetMutations) {
-      return;
-    }
-
-    if (IMMEDIATE_MUTATIONS.includes(mutation.type)) {
-      this._emitNewMutations();
-    } else {
-      /**
-       * If many mutations are fired at the same time, clients might sequentially request
-       * children/siblings for updated nodes, which can be costly. By throttling the calls
-       * to getMutations, duplicated mutations will be ignored.
-       */
-      this._throttledEmitNewMutations();
-    }
-  },
-
-  _emitNewMutations: function () {
-    if (!this.actorID || this._destroyed) {
-      // Bail out if the actor was destroyed after throttling this call.
-      return;
-    }
-
-    if (this._waitingForGetMutations || this._pendingMutations.length == 0) {
-      // Bail out if we already fired the new-mutation event or if no mutations are
-      // waiting to be retrieved.
-      return;
-    }
-
-    this._waitingForGetMutations = true;
-    this.emit("new-mutations");
-  },
-
-  /**
-   * Handles mutations from the DOM mutation observer API.
-   *
-   * @param array[MutationRecord] mutations
-   *    See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationRecord
-   */
-  onMutations: function (mutations) {
-    // Notify any observers that want *all* mutations (even on nodes that aren't
-    // referenced).  This is not sent over the protocol so can only be used by
-    // scripts running in the server process.
-    this.emit("any-mutation");
-
-    for (let change of mutations) {
-      let targetActor = this.getNode(change.target);
-      if (!targetActor) {
-        continue;
-      }
-      let targetNode = change.target;
-      let type = change.type;
-      let mutation = {
-        type: type,
-        target: targetActor.actorID,
-      };
-
-      if (type === "attributes") {
-        mutation.attributeName = change.attributeName;
-        mutation.attributeNamespace = change.attributeNamespace || undefined;
-        mutation.newValue = targetNode.hasAttribute(mutation.attributeName) ?
-                            targetNode.getAttribute(mutation.attributeName)
-                            : null;
-      } else if (type === "characterData") {
-        mutation.newValue = targetNode.nodeValue;
-        this._maybeQueueInlineTextChildMutation(change, targetNode);
-      } else if (type === "childList" || type === "nativeAnonymousChildList") {
-        // Get the list of removed and added actors that the client has seen
-        // so that it can keep its ownership tree up to date.
-        let removedActors = [];
-        let addedActors = [];
-        for (let removed of change.removedNodes) {
-          let removedActor = this.getNode(removed);
-          if (!removedActor) {
-            // If the client never encountered this actor we don't need to
-            // mention that it was removed.
-            continue;
-          }
-          // While removed from the tree, nodes are saved as orphaned.
-          this._orphaned.add(removedActor);
-          removedActors.push(removedActor.actorID);
-        }
-        for (let added of change.addedNodes) {
-          let addedActor = this.getNode(added);
-          if (!addedActor) {
-            // If the client never encounted this actor we don't need to tell
-            // it about its addition for ownership tree purposes - if the
-            // client wants to see the new nodes it can ask for children.
-            continue;
-          }
-          // The actor is reconnected to the ownership tree, unorphan
-          // it and let the client know so that its ownership tree is up
-          // to date.
-          this._orphaned.delete(addedActor);
-          addedActors.push(addedActor.actorID);
-        }
-
-        mutation.numChildren = targetActor.numChildren;
-        mutation.removed = removedActors;
-        mutation.added = addedActors;
-
-        let inlineTextChild = this.inlineTextChild(targetActor);
-        if (inlineTextChild) {
-          mutation.inlineTextChild = inlineTextChild.form();
-        }
-      }
-      this.queueMutation(mutation);
-    }
-  },
-
-  /**
-   * Check if the provided mutation could change the way the target element is
-   * inlined with its parent node. If it might, a custom mutation of type
-   * "inlineTextChild" will be queued.
-   *
-   * @param {MutationRecord} mutation
-   *        A characterData type mutation
-   */
-  _maybeQueueInlineTextChildMutation: function (mutation) {
-    let {oldValue, target} = mutation;
-    let newValue = target.nodeValue;
-    let limit = gValueSummaryLength;
-
-    if ((oldValue.length <= limit && newValue.length <= limit) ||
-        (oldValue.length > limit && newValue.length > limit)) {
-      // Bail out if the new & old values are both below/above the size limit.
-      return;
-    }
-
-    let parentActor = this.getNode(target.parentNode);
-    if (!parentActor || parentActor.rawNode.children.length > 0) {
-      // If the parent node has other children, a character data mutation will
-      // not change anything regarding inlining text nodes.
-      return;
-    }
-
-    let inlineTextChild = this.inlineTextChild(parentActor);
-    this.queueMutation({
-      type: "inlineTextChild",
-      target: parentActor.actorID,
-      inlineTextChild:
-        inlineTextChild ? inlineTextChild.form() : undefined
-    });
-  },
-
-  onFrameLoad: function ({ window, isTopLevel }) {
-    let { readyState } = window.document;
-    if (readyState != "interactive" && readyState != "complete") {
-      window.addEventListener("DOMContentLoaded",
-        this.onFrameLoad.bind(this, { window, isTopLevel }),
-        { once: true });
-      return;
-    }
-    if (isTopLevel) {
-      // If we initialize the inspector while the document is loading,
-      // we may already have a root document set in the constructor.
-      if (this.rootDoc && !Cu.isDeadWrapper(this.rootDoc) &&
-          this.rootDoc.defaultView) {
-        this.onFrameUnload({ window: this.rootDoc.defaultView });
-      }
-      // Update all DOM objects references to target the new document.
-      this.rootWin = window;
-      this.rootDoc = window.document;
-      this.rootNode = this.document();
-      this.queueMutation({
-        type: "newRoot",
-        target: this.rootNode.form()
-      });
-      return;
-    }
-    let frame = getFrameElement(window);
-    let frameActor = this.getNode(frame);
-    if (!frameActor) {
-      return;
-    }
-
-    this.queueMutation({
-      type: "frameLoad",
-      target: frameActor.actorID,
-    });
-
-    // Send a childList mutation on the frame.
-    this.queueMutation({
-      type: "childList",
-      target: frameActor.actorID,
-      added: [],
-      removed: []
-    });
-  },
-
-  // Returns true if domNode is in window or a subframe.
-  _childOfWindow: function (window, domNode) {
-    let win = nodeDocument(domNode).defaultView;
-    while (win) {
-      if (win === window) {
-        return true;
-      }
-      win = getFrameElement(win);
-    }
-    return false;
-  },
-
-  onFrameUnload: function ({ window }) {
-    // Any retained orphans that belong to this document
-    // or its children need to be released, and a mutation sent
-    // to notify of that.
-    let releasedOrphans = [];
-
-    for (let retained of this._retainedOrphans) {
-      if (Cu.isDeadWrapper(retained.rawNode) ||
-          this._childOfWindow(window, retained.rawNode)) {
-        this._retainedOrphans.delete(retained);
-        releasedOrphans.push(retained.actorID);
-        this.releaseNode(retained, { force: true });
-      }
-    }
-
-    if (releasedOrphans.length > 0) {
-      this.queueMutation({
-        target: this.rootNode.actorID,
-        type: "unretained",
-        nodes: releasedOrphans
-      });
-    }
-
-    let doc = window.document;
-    let documentActor = this.getNode(doc);
-    if (!documentActor) {
-      return;
-    }
-
-    if (this.rootDoc === doc) {
-      this.rootDoc = null;
-      this.rootNode = null;
-    }
-
-    this.queueMutation({
-      type: "documentUnload",
-      target: documentActor.actorID
-    });
-
-    let walker = this.getDocumentWalker(doc);
-    let parentNode = walker.parentNode();
-    if (parentNode) {
-      // Send a childList mutation on the frame so that clients know
-      // they should reread the children list.
-      this.queueMutation({
-        type: "childList",
-        target: this.getNode(parentNode).actorID,
-        added: [],
-        removed: []
-      });
-    }
-
-    // Need to force a release of this node, because those nodes can't
-    // be accessed anymore.
-    this.releaseNode(documentActor, { force: true });
-  },
-
-  /**
-   * Check if a node is attached to the DOM tree of the current page.
-   * @param {nsIDomNode} rawNode
-   * @return {Boolean} false if the node is removed from the tree or within a
-   * document fragment
-   */
-  _isInDOMTree: function (rawNode) {
-    let walker = this.getDocumentWalker(rawNode);
-    let current = walker.currentNode;
-
-    // Reaching the top of tree
-    while (walker.parentNode()) {
-      current = walker.currentNode;
-    }
-
-    // The top of the tree is a fragment or is not rootDoc, hence rawNode isn't
-    // attached
-    if (current.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE ||
-        current !== this.rootDoc) {
-      return false;
-    }
-
-    // Otherwise the top of the tree is rootDoc, hence rawNode is in rootDoc
-    return true;
-  },
-
-  /**
-   * @see _isInDomTree
-   */
-  isInDOMTree: function (node) {
-    if (isNodeDead(node)) {
-      return false;
-    }
-    return this._isInDOMTree(node.rawNode);
-  },
-
-  /**
-   * Given an ObjectActor (identified by its ID), commonly used in the debugger,
-   * webconsole and variablesView, return the corresponding inspector's
-   * NodeActor
-   */
-  getNodeActorFromObjectActor: function (objectActorID) {
-    let actor = this.conn.getActor(objectActorID);
-    if (!actor) {
-      return null;
-    }
-
-    let debuggerObject = this.conn.getActor(objectActorID).obj;
-    let rawNode = debuggerObject.unsafeDereference();
-
-    if (!this._isInDOMTree(rawNode)) {
-      return null;
-    }
-
-    // This is a special case for the document object whereby it is considered
-    // as document.documentElement (the <html> node)
-    if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
-      rawNode = rawNode.documentElement;
-    }
-
-    return this.attachElement(rawNode);
-  },
-
-  /**
-   * Given a windowID return the NodeActor for the corresponding frameElement,
-   * unless it's the root window
-   */
-  getNodeActorFromWindowID: function (windowID) {
-    let win;
-
-    try {
-      win = Services.wm.getOuterWindowWithId(windowID);
-    } catch (e) {
-      // ignore
-    }
-
-    if (!win) {
-      return { error: "noWindow",
-               message: "The related docshell is destroyed or not found" };
-    } else if (!win.frameElement) {
-      // the frame element of the root document is privileged & thus
-      // inaccessible, so return the document body/element instead
-      return this.attachElement(win.document.body || win.document.documentElement);
-    }
-
-    return this.attachElement(win.frameElement);
-  },
-
-  /**
-   * Given a StyleSheetActor (identified by its ID), commonly used in the
-   * style-editor, get its ownerNode and return the corresponding walker's
-   * NodeActor.
-   * Note that getNodeFromActor was added later and can now be used instead.
-   */
-  getStyleSheetOwnerNode: function (styleSheetActorID) {
-    return this.getNodeFromActor(styleSheetActorID, ["ownerNode"]);
-  },
-
-  /**
-   * This method can be used to retrieve NodeActor for DOM nodes from other
-   * actors in a way that they can later be highlighted in the page, or
-   * selected in the inspector.
-   * If an actor has a reference to a DOM node, and the UI needs to know about
-   * this DOM node (and possibly select it in the inspector), the UI should
-   * first retrieve a reference to the walkerFront:
-   *
-   * // Make sure the inspector/walker have been initialized first.
-   * toolbox.initInspector().then(() => {
-   *  // Retrieve the walker.
-   *  let walker = toolbox.walker;
-   * });
-   *
-   * And then call this method:
-   *
-   * // Get the nodeFront from my actor, passing the ID and properties path.
-   * walker.getNodeFromActor(myActorID, ["element"]).then(nodeFront => {
-   *   // Use the nodeFront, e.g. select the node in the inspector.
-   *   toolbox.getPanel("inspector").selection.setNodeFront(nodeFront);
-   * });
-   *
-   * @param {String} actorID The ID for the actor that has a reference to the
-   * DOM node.
-   * @param {Array} path Where, on the actor, is the DOM node stored. If in the
-   * scope of the actor, the node is available as `this.data.node`, then this
-   * should be ["data", "node"].
-   * @return {NodeActor} The attached NodeActor, or null if it couldn't be
-   * found.
-   */
-  getNodeFromActor: function (actorID, path) {
-    let actor = this.conn.getActor(actorID);
-    if (!actor) {
-      return null;
-    }
-
-    let obj = actor;
-    for (let name of path) {
-      if (!(name in obj)) {
-        return null;
-      }
-      obj = obj[name];
-    }
-
-    return this.attachElement(obj);
-  },
-
-  /**
-   * Returns an instance of the LayoutActor that is used to retrieve CSS layout-related
-   * information.
-   *
-   * @return {LayoutActor}
-   */
-  getLayoutInspector: function () {
-    if (!this.layoutActor) {
-      this.layoutActor = new LayoutActor(this.conn, this.tabActor, this);
-    }
-
-    return this.layoutActor;
-  },
-
-  /**
-   * Returns the offset parent DOMNode of the given node if it exists, otherwise, it
-   * returns null.
-   */
-  getOffsetParent: function (node) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let offsetParent = node.rawNode.offsetParent;
-
-    if (!offsetParent) {
-      return null;
-    }
-
-    return this._ref(offsetParent);
-  },
-});
-
-/**
- * Server side of the inspector actor, which is used to create
- * inspector-related actors, including the walker.
- */
-exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
-  initialize: function (conn, tabActor) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this.tabActor = tabActor;
-
-    this._onColorPicked = this._onColorPicked.bind(this);
-    this._onColorPickCanceled = this._onColorPickCanceled.bind(this);
-    this.destroyEyeDropper = this.destroyEyeDropper.bind(this);
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-
-    this.destroyEyeDropper();
-
-    this._highlighterPromise = null;
-    this._pageStylePromise = null;
-    this._walkerPromise = null;
-    this.walker = null;
-    this.tabActor = null;
-  },
-
-  get window() {
-    return this.tabActor.window;
-  },
-
-  getWalker: function (options = {}) {
-    if (this._walkerPromise) {
-      return this._walkerPromise;
-    }
-
-    let deferred = defer();
-    this._walkerPromise = deferred.promise;
-
-    let window = this.window;
-    let domReady = () => {
-      let tabActor = this.tabActor;
-      window.removeEventListener("DOMContentLoaded", domReady, true);
-      this.walker = WalkerActor(this.conn, tabActor, options);
-      this.manage(this.walker);
-      this.walker.once("destroyed", () => {
-        this._walkerPromise = null;
-        this._pageStylePromise = null;
-      });
-      deferred.resolve(this.walker);
-    };
-
-    if (window.document.readyState === "loading") {
-      window.addEventListener("DOMContentLoaded", domReady, true);
-    } else {
-      domReady();
-    }
-
-    return this._walkerPromise;
-  },
-
-  getPageStyle: function () {
-    if (this._pageStylePromise) {
-      return this._pageStylePromise;
-    }
-
-    this._pageStylePromise = this.getWalker().then(walker => {
-      let pageStyle = PageStyleActor(this);
-      this.manage(pageStyle);
-      return pageStyle;
-    });
-    return this._pageStylePromise;
-  },
-
-  /**
-   * The most used highlighter actor is the HighlighterActor which can be
-   * conveniently retrieved by this method.
-   * The same instance will always be returned by this method when called
-   * several times.
-   * The highlighter actor returned here is used to highlighter elements's
-   * box-models from the markup-view, box model, console, debugger, ... as
-   * well as select elements with the pointer (pick).
-   *
-   * @param {Boolean} autohide Optionally autohide the highlighter after an
-   * element has been picked
-   * @return {HighlighterActor}
-   */
-  getHighlighter: function (autohide) {
-    if (this._highlighterPromise) {
-      return this._highlighterPromise;
-    }
-
-    this._highlighterPromise = this.getWalker().then(walker => {
-      let highlighter = HighlighterActor(this, autohide);
-      this.manage(highlighter);
-      return highlighter;
-    });
-    return this._highlighterPromise;
-  },
-
-  /**
-   * If consumers need to display several highlighters at the same time or
-   * different types of highlighters, then this method should be used, passing
-   * the type name of the highlighter needed as argument.
-   * A new instance will be created everytime the method is called, so it's up
-   * to the consumer to release it when it is not needed anymore
-   *
-   * @param {String} type The type of highlighter to create
-   * @return {Highlighter} The highlighter actor instance or null if the
-   * typeName passed doesn't match any available highlighter
-   */
-  getHighlighterByType: function (typeName) {
-    if (isTypeRegistered(typeName)) {
-      return CustomHighlighterActor(this, typeName);
-    }
-    return null;
-  },
-
-  /**
-   * Get the node's image data if any (for canvas and img nodes).
-   * Returns an imageData object with the actual data being a LongStringActor
-   * and a size json object.
-   * The image data is transmitted as a base64 encoded png data-uri.
-   * The method rejects if the node isn't an image or if the image is missing
-   *
-   * Accepts a maxDim request parameter to resize images that are larger. This
-   * is important as the resizing occurs server-side so that image-data being
-   * transfered in the longstring back to the client will be that much smaller
-   */
-  getImageDataFromURL: function (url, maxDim) {
-    let img = new this.window.Image();
-    img.src = url;
-
-    // imageToImageData waits for the image to load.
-    return imageToImageData(img, maxDim).then(imageData => {
-      return {
-        data: LongStringActor(this.conn, imageData.data),
-        size: imageData.size
-      };
-    });
-  },
-
-  /**
-   * Resolve a URL to its absolute form, in the scope of a given content window.
-   * @param {String} url.
-   * @param {NodeActor} node If provided, the owner window of this node will be
-   * used to resolve the URL. Otherwise, the top-level content window will be
-   * used instead.
-   * @return {String} url.
-   */
-  resolveRelativeURL: function (url, node) {
-    let document = isNodeDead(node)
-                   ? this.window.document
-                   : nodeDocument(node.rawNode);
-
-    if (!document) {
-      return url;
-    }
-
-    let baseURI = Services.io.newURI(document.location.href);
-    return Services.io.newURI(url, null, baseURI).spec;
-  },
-
-  /**
-   * Create an instance of the eye-dropper highlighter and store it on this._eyeDropper.
-   * Note that for now, a new instance is created every time to deal with page navigation.
-   */
-  createEyeDropper: function () {
-    this.destroyEyeDropper();
-    this._highlighterEnv = new HighlighterEnvironment();
-    this._highlighterEnv.initFromTabActor(this.tabActor);
-    this._eyeDropper = new EyeDropper(this._highlighterEnv);
-  },
-
-  /**
-   * Destroy the current eye-dropper highlighter instance.
-   */
-  destroyEyeDropper: function () {
-    if (this._eyeDropper) {
-      this.cancelPickColorFromPage();
-      this._eyeDropper.destroy();
-      this._eyeDropper = null;
-      this._highlighterEnv.destroy();
-      this._highlighterEnv = null;
-    }
-  },
-
-  /**
-   * Pick a color from the page using the eye-dropper. This method doesn't return anything
-   * but will cause events to be sent to the front when a color is picked or when the user
-   * cancels the picker.
-   * @param {Object} options
-   */
-  pickColorFromPage: function (options) {
-    this.createEyeDropper();
-    this._eyeDropper.show(this.window.document.documentElement, options);
-    this._eyeDropper.once("selected", this._onColorPicked);
-    this._eyeDropper.once("canceled", this._onColorPickCanceled);
-    this.tabActor.once("will-navigate", this.destroyEyeDropper);
-  },
-
-  /**
-   * After the pickColorFromPage method is called, the only way to dismiss the eye-dropper
-   * highlighter is for the user to click in the page and select a color. If you need to
-   * dismiss the eye-dropper programatically instead, use this method.
-   */
-  cancelPickColorFromPage: function () {
-    if (this._eyeDropper) {
-      this._eyeDropper.hide();
-      this._eyeDropper.off("selected", this._onColorPicked);
-      this._eyeDropper.off("canceled", this._onColorPickCanceled);
-      this.tabActor.off("will-navigate", this.destroyEyeDropper);
-    }
-  },
-
-  /**
-   * Check if the current document supports highlighters using a canvasFrame anonymous
-   * content container (ie all highlighters except the SimpleOutlineHighlighter).
-   * It is impossible to detect the feature programmatically as some document types simply
-   * don't render the canvasFrame without throwing any error.
-   */
-  supportsHighlighters: function () {
-    let doc = this.tabActor.window.document;
-    let ns = doc.documentElement.namespaceURI;
-
-    // XUL documents do not support insertAnonymousContent().
-    if (ns === XUL_NS) {
-      return false;
-    }
-
-    // SVG documents do not render the canvasFrame (see Bug 1157592).
-    if (ns === SVG_NS) {
-      return false;
-    }
-
-    return true;
-  },
-
-  _onColorPicked: function (e, color) {
-    this.emit("color-picked", color);
-  },
-
-  _onColorPickCanceled: function () {
-    this.emit("color-pick-canceled");
-  }
-});
-
-// Exported for test purposes.
-exports._documentWalker = DocumentWalker;
-
-function nodeDocument(node) {
-  if (Cu.isDeadWrapper(node)) {
-    return null;
-  }
-  return node.ownerDocument ||
-         (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
-}
-
-function nodeDocshell(node) {
-  let doc = node ? nodeDocument(node) : null;
-  let win = doc ? doc.defaultView : null;
-  if (win) {
-    return win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDocShell);
-  }
-  return null;
-}
-
-function isNodeDead(node) {
-  return !node || !node.rawNode || Cu.isDeadWrapper(node.rawNode);
-}
-
 /**
  * Wrapper for inDeepTreeWalker.  Adds filtering to the traversal methods.
  * See inDeepTreeWalker for more information about the methods.
  *
  * @param {DOMNode} node
  * @param {Window} rootWin
  * @param {Number} whatToShow
  *        See nodeFilterConstants / inIDeepTreeWalker for options.
@@ -3057,19 +26,19 @@ function isNodeDead(node) {
  *        A custom filter function Taking in a DOMNode and returning an Int. See
  *        WalkerActor.nodeFilter for an example.
  * @param {String} skipTo
  *        Either SKIP_TO_PARENT or SKIP_TO_SIBLING. If the provided node is not compatible
  *        with the filter function for this walker, try to find a compatible one either
  *        in the parents or in the siblings of the node.
  */
 function DocumentWalker(node, rootWin,
-    whatToShow = nodeFilterConstants.SHOW_ALL,
-    filter = standardTreeWalkerFilter,
-    skipTo = SKIP_TO_PARENT) {
+  whatToShow = nodeFilterConstants.SHOW_ALL,
+  filter = standardTreeWalkerFilter,
+  skipTo = SKIP_TO_PARENT) {
   if (Cu.isDeadWrapper(rootWin) || !rootWin.location) {
     throw new Error("Got an invalid root window in DocumentWalker");
   }
 
   this.walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
     .createInstance(Ci.inIDeepTreeWalker);
   this.walker.showAnonymousContent = true;
   this.walker.showSubDocuments = true;
@@ -3078,16 +47,17 @@ function DocumentWalker(node, rootWin,
   this.filter = filter;
 
   // Make sure that the walker knows about the initial node (which could
   // be skipped due to a filter).
   this.walker.currentNode = this.getStartingNode(node, skipTo);
 }
 
 DocumentWalker.prototype = {
+
   get whatToShow() {
     return this.walker.whatToShow;
   },
   get currentNode() {
     return this.walker.currentNode;
   },
   set currentNode(val) {
     this.walker.currentNode = val;
@@ -3202,201 +172,11 @@ DocumentWalker.prototype = {
     return null;
   },
 
   isSkippedNode: function (node) {
     return this.filter(node) === nodeFilterConstants.FILTER_SKIP;
   },
 };
 
-function isInXULDocument(el) {
-  let doc = nodeDocument(el);
-  return doc &&
-         doc.documentElement &&
-         doc.documentElement.namespaceURI === XUL_NS;
-}
-
-/**
- * This DeepTreeWalker filter skips whitespace text nodes and anonymous
- * content with the exception of ::before and ::after and anonymous content
- * in XUL document (needed to show all elements in the browser toolbox).
- */
-function standardTreeWalkerFilter(node) {
-  // ::before and ::after are native anonymous content, but we always
-  // want to show them
-  if (node.nodeName === "_moz_generated_content_before" ||
-      node.nodeName === "_moz_generated_content_after") {
-    return nodeFilterConstants.FILTER_ACCEPT;
-  }
-
-  // Ignore empty whitespace text nodes that do not impact the layout.
-  if (isWhitespaceTextNode(node)) {
-    return nodeHasSize(node)
-           ? nodeFilterConstants.FILTER_ACCEPT
-           : nodeFilterConstants.FILTER_SKIP;
-  }
-
-  // Ignore all native and XBL anonymous content inside a non-XUL document.
-  // We need to do this to skip things like form controls, scrollbars,
-  // video controls, etc (see bug 1187482).
-  if (!isInXULDocument(node) && (isXBLAnonymous(node) ||
-                                  isNativeAnonymous(node))) {
-    return nodeFilterConstants.FILTER_SKIP;
-  }
-
-  return nodeFilterConstants.FILTER_ACCEPT;
-}
-
-/**
- * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
- * it also includes all anonymous content (like internal form controls).
- */
-function allAnonymousContentTreeWalkerFilter(node) {
-  // Ignore empty whitespace text nodes that do not impact the layout.
-  if (isWhitespaceTextNode(node)) {
-    return nodeHasSize(node)
-           ? nodeFilterConstants.FILTER_ACCEPT
-           : nodeFilterConstants.FILTER_SKIP;
-  }
-  return nodeFilterConstants.FILTER_ACCEPT;
-}
-
-/**
- * Is the given node a text node composed of whitespace only?
- * @param {DOMNode} node
- * @return {Boolean}
- */
-function isWhitespaceTextNode(node) {
-  return node.nodeType == Ci.nsIDOMNode.TEXT_NODE && !/[^\s]/.exec(node.nodeValue);
-}
-
-/**
- * Does the given node have non-0 width and height?
- * @param {DOMNode} node
- * @return {Boolean}
- */
-function nodeHasSize(node) {
-  if (!node.getBoxQuads) {
-    return false;
-  }
-
-  let quads = node.getBoxQuads();
-  return quads.length && quads.some(quad => quad.bounds.width && quad.bounds.height);
-}
-
-/**
- * Returns a promise that is settled once the given HTMLImageElement has
- * finished loading.
- *
- * @param {HTMLImageElement} image - The image element.
- * @param {Number} timeout - Maximum amount of time the image is allowed to load
- * before the waiting is aborted. Ignored if flags.testing is set.
- *
- * @return {Promise} that is fulfilled once the image has loaded. If the image
- * fails to load or the load takes too long, the promise is rejected.
- */
-function ensureImageLoaded(image, timeout) {
-  let { HTMLImageElement } = image.ownerGlobal;
-  if (!(image instanceof HTMLImageElement)) {
-    return promise.reject("image must be an HTMLImageELement");
-  }
-
-  if (image.complete) {
-    // The image has already finished loading.
-    return promise.resolve();
-  }
-
-  // This image is still loading.
-  let onLoad = AsyncUtils.listenOnce(image, "load");
-
-  // Reject if loading fails.
-  let onError = AsyncUtils.listenOnce(image, "error").then(() => {
-    return promise.reject("Image '" + image.src + "' failed to load.");
-  });
-
-  // Don't timeout when testing. This is never settled.
-  let onAbort = new Promise(() => {});
-
-  if (!flags.testing) {
-    // Tests are not running. Reject the promise after given timeout.
-    onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
-      return promise.reject("Image '" + image.src + "' took too long to load.");
-    });
-  }
-
-  // See which happens first.
-  return promise.race([onLoad, onError, onAbort]);
-}
-
-/**
- * Given an <img> or <canvas> element, return the image data-uri. If @param node
- * is an <img> element, the method waits a while for the image to load before
- * the data is generated. If the image does not finish loading in a reasonable
- * time (IMAGE_FETCHING_TIMEOUT milliseconds) the process aborts.
- *
- * @param {HTMLImageElement|HTMLCanvasElement} node - The <img> or <canvas>
- * element, or Image() object. Other types cause the method to reject.
- * @param {Number} maxDim - Optionally pass a maximum size you want the longest
- * side of the image to be resized to before getting the image data.
-
- * @return {Promise} A promise that is fulfilled with an object containing the
- * data-uri and size-related information:
- * { data: "...",
- *   size: {
- *     naturalWidth: 400,
- *     naturalHeight: 300,
- *     resized: true }
- *  }.
- *
- * If something goes wrong, the promise is rejected.
- */
-var imageToImageData = Task.async(function* (node, maxDim) {
-  let { HTMLCanvasElement, HTMLImageElement } = node.ownerGlobal;
-
-  let isImg = node instanceof HTMLImageElement;
-  let isCanvas = node instanceof HTMLCanvasElement;
-
-  if (!isImg && !isCanvas) {
-    throw new Error("node is not a <canvas> or <img> element.");
-  }
-
-  if (isImg) {
-    // Ensure that the image is ready.
-    yield ensureImageLoaded(node, IMAGE_FETCHING_TIMEOUT);
-  }
-
-  // Get the image resize ratio if a maxDim was provided
-  let resizeRatio = 1;
-  let imgWidth = node.naturalWidth || node.width;
-  let imgHeight = node.naturalHeight || node.height;
-  let imgMax = Math.max(imgWidth, imgHeight);
-  if (maxDim && imgMax > maxDim) {
-    resizeRatio = maxDim / imgMax;
-  }
-
-  // Extract the image data
-  let imageData;
-  // The image may already be a data-uri, in which case, save ourselves the
-  // trouble of converting via the canvas.drawImage.toDataURL method, but only
-  // if the image doesn't need resizing
-  if (isImg && node.src.startsWith("data:") && resizeRatio === 1) {
-    imageData = node.src;
-  } else {
-    // Create a canvas to copy the rawNode into and get the imageData from
-    let canvas = node.ownerDocument.createElementNS(XHTML_NS, "canvas");
-    canvas.width = imgWidth * resizeRatio;
-    canvas.height = imgHeight * resizeRatio;
-    let ctx = canvas.getContext("2d");
-
-    // Copy the rawNode image or canvas in the new canvas and extract data
-    ctx.drawImage(node, 0, 0, canvas.width, canvas.height);
-    imageData = canvas.toDataURL("image/png");
-  }
-
-  return {
-    data: imageData,
-    size: {
-      naturalWidth: imgWidth,
-      naturalHeight: imgHeight,
-      resized: resizeRatio !== 1
-    }
-  };
-});
+exports.DocumentWalker = DocumentWalker;
+exports.SKIP_TO_PARENT = SKIP_TO_PARENT;
+exports.SKIP_TO_SIBLING = SKIP_TO_SIBLING;
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/inspector.js
@@ -45,2739 +45,34 @@
  * the client - when a node is disconnected from the DOM tree we want to be
  * able to free the client objects for all the children nodes.
  *
  * So to be able to answer "all the children of a given node that we have
  * seen on the client side", we guarantee that every time we've seen a node,
  * we connect it up through its parents.
  */
 
-const {Cc, Ci, Cu} = require("chrome");
 const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
-const promise = require("promise");
 const defer = require("devtools/shared/defer");
-const {Task} = require("devtools/shared/task");
-const EventEmitter = require("devtools/shared/event-emitter");
-const InspectorUtils = require("InspectorUtils");
 
-const {walkerSpec, inspectorSpec} = require("devtools/shared/specs/inspector");
-const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");
+const {inspectorSpec} = require("devtools/shared/specs/inspector");
 
-loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
-loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
-loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
-loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "getCssPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "getXPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
+loader.lazyRequireGetter(this, "InspectorActorUtils", "devtools/server/actors/inspector/utils");
+loader.lazyRequireGetter(this, "WalkerActor", "devtools/server/actors/inspector/walker-actor", true);
 loader.lazyRequireGetter(this, "EyeDropper", "devtools/server/actors/highlighters/eye-dropper", true);
-loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);
 loader.lazyRequireGetter(this, "PageStyleActor", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "getFontPreviewData", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
-loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
-loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
 loader.lazyRequireGetter(this, "HighlighterActor", "devtools/server/actors/highlighters", true);
 loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true);
 loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
 loader.lazyRequireGetter(this, "HighlighterEnvironment", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "EventParsers", "devtools/server/event-parsers", true);
-loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isXBLAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isShadowAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getFrameElement", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");
 
-loader.lazyServiceGetter(this, "DOMParser",
-  "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
-
-loader.lazyServiceGetter(this, "eventListenerService",
-  "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
-
-const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
-const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
-const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
-const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
 const SVG_NS = "http://www.w3.org/2000/svg";
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const IMAGE_FETCHING_TIMEOUT = 500;
-
-// Minimum delay between two "new-mutations" events.
-const MUTATIONS_THROTTLING_DELAY = 100;
-// List of mutation types that should -not- be throttled.
-const IMMEDIATE_MUTATIONS = [
-  "documentUnload",
-  "frameLoad",
-  "newRoot",
-  "pseudoClassLock",
-];
-
-// SKIP_TO_* arguments are used with the DocumentWalker, driving the strategy to use if
-// the starting node is incompatible with the filter function of the walker.
-const SKIP_TO_PARENT = "SKIP_TO_PARENT";
-const SKIP_TO_SIBLING = "SKIP_TO_SIBLING";
-
-// The possible completions to a ':' with added score to give certain values
-// some preference.
-const PSEUDO_SELECTORS = [
-  [":active", 1],
-  [":hover", 1],
-  [":focus", 1],
-  [":visited", 0],
-  [":link", 0],
-  [":first-letter", 0],
-  [":first-child", 2],
-  [":before", 2],
-  [":after", 2],
-  [":lang(", 0],
-  [":not(", 3],
-  [":first-of-type", 0],
-  [":last-of-type", 0],
-  [":only-of-type", 0],
-  [":only-child", 2],
-  [":nth-child(", 3],
-  [":nth-last-child(", 0],
-  [":nth-of-type(", 0],
-  [":nth-last-of-type(", 0],
-  [":last-child", 2],
-  [":root", 0],
-  [":empty", 0],
-  [":target", 0],
-  [":enabled", 0],
-  [":disabled", 0],
-  [":checked", 1],
-  ["::selection", 0]
-];
-
-var HELPER_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
-  .__fx-devtools-hide-shortcut__ {
-    visibility: hidden !important;
-  }
-
-  :-moz-devtools-highlighted {
-    outline: 2px dashed #F06!important;
-    outline-offset: -2px !important;
-  }
-`);
-
-/**
- * We only send nodeValue up to a certain size by default.  This stuff
- * controls that size.
- */
-exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50;
-var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH;
-
-exports.getValueSummaryLength = function () {
-  return gValueSummaryLength;
-};
-
-exports.setValueSummaryLength = function (val) {
-  gValueSummaryLength = val;
-};
-
-/**
- * Returns the properly cased version of the node's tag name, which can be
- * used when displaying said name in the UI.
- *
- * @param  {Node} rawNode
- *         Node for which we want the display name
- * @return {String}
- *         Properly cased version of the node tag name
- */
-const getNodeDisplayName = function (rawNode) {
-  if (rawNode.nodeName && !rawNode.localName) {
-    // The localName & prefix APIs have been moved from the Node interface to the Element
-    // interface. Use Node.nodeName as a fallback.
-    return rawNode.nodeName;
-  }
-  return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
-};
-exports.getNodeDisplayName = getNodeDisplayName;
-
-/**
- * Server side of the node actor.
- */
-var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
-  initialize: function (walker, node) {
-    protocol.Actor.prototype.initialize.call(this, null);
-    this.walker = walker;
-    this.rawNode = node;
-    this._eventParsers = new EventParsers().parsers;
-
-    // Storing the original display of the node, to track changes when reflows
-    // occur
-    this.wasDisplayed = this.isDisplayed;
-  },
-
-  toString: function () {
-    return "[NodeActor " + this.actorID + " for " +
-      this.rawNode.toString() + "]";
-  },
-
-  /**
-   * Instead of storing a connection object, the NodeActor gets its connection
-   * from its associated walker.
-   */
-  get conn() {
-    return this.walker.conn;
-  },
-
-  isDocumentElement: function () {
-    return this.rawNode.ownerDocument &&
-           this.rawNode.ownerDocument.documentElement === this.rawNode;
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-
-    if (this.mutationObserver) {
-      if (!Cu.isDeadWrapper(this.mutationObserver)) {
-        this.mutationObserver.disconnect();
-      }
-      this.mutationObserver = null;
-    }
-    this.rawNode = null;
-    this.walker = null;
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function (detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-
-    let parentNode = this.walker.parentNode(this);
-    let inlineTextChild = this.walker.inlineTextChild(this);
-
-    let form = {
-      actor: this.actorID,
-      baseURI: this.rawNode.baseURI,
-      parent: parentNode ? parentNode.actorID : undefined,
-      nodeType: this.rawNode.nodeType,
-      namespaceURI: this.rawNode.namespaceURI,
-      nodeName: this.rawNode.nodeName,
-      nodeValue: this.rawNode.nodeValue,
-      displayName: getNodeDisplayName(this.rawNode),
-      numChildren: this.numChildren,
-      inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
-
-      // doctype attributes
-      name: this.rawNode.name,
-      publicId: this.rawNode.publicId,
-      systemId: this.rawNode.systemId,
-
-      attrs: this.writeAttrs(),
-      isBeforePseudoElement: this.isBeforePseudoElement,
-      isAfterPseudoElement: this.isAfterPseudoElement,
-      isAnonymous: isAnonymous(this.rawNode),
-      isNativeAnonymous: isNativeAnonymous(this.rawNode),
-      isXBLAnonymous: isXBLAnonymous(this.rawNode),
-      isShadowAnonymous: isShadowAnonymous(this.rawNode),
-      pseudoClassLocks: this.writePseudoClassLocks(),
-
-      isDisplayed: this.isDisplayed,
-      isInHTMLDocument: this.rawNode.ownerDocument &&
-        this.rawNode.ownerDocument.contentType === "text/html",
-      hasEventListeners: this._hasEventListeners,
-    };
-
-    if (this.isDocumentElement()) {
-      form.isDocumentElement = true;
-    }
-
-    // Add an extra API for custom properties added by other
-    // modules/extensions.
-    form.setFormProperty = (name, value) => {
-      if (!form.props) {
-        form.props = {};
-      }
-      form.props[name] = value;
-    };
-
-    // Fire an event so, other modules can create its own properties
-    // that should be passed to the client (within the form.props field).
-    EventEmitter.emit(NodeActor, "form", {
-      target: this,
-      data: form
-    });
-
-    return form;
-  },
-
-  /**
-   * Watch the given document node for mutations using the DOM observer
-   * API.
-   */
-  watchDocument: function (callback) {
-    let node = this.rawNode;
-    // Create the observer on the node's actor.  The node will make sure
-    // the observer is cleaned up when the actor is released.
-    let observer = new node.defaultView.MutationObserver(callback);
-    observer.mergeAttributeRecords = true;
-    observer.observe(node, {
-      nativeAnonymousChildList: true,
-      attributes: true,
-      characterData: true,
-      characterDataOldValue: true,
-      childList: true,
-      subtree: true
-    });
-    this.mutationObserver = observer;
-  },
-
-  get isBeforePseudoElement() {
-    return this.rawNode.nodeName === "_moz_generated_content_before";
-  },
-
-  get isAfterPseudoElement() {
-    return this.rawNode.nodeName === "_moz_generated_content_after";
-  },
-
-  // Estimate the number of children that the walker will return without making
-  // a call to children() if possible.
-  get numChildren() {
-    // For pseudo elements, childNodes.length returns 1, but the walker
-    // will return 0.
-    if (this.isBeforePseudoElement || this.isAfterPseudoElement) {
-      return 0;
-    }
-
-    let rawNode = this.rawNode;
-    let numChildren = rawNode.childNodes.length;
-    let hasAnonChildren = rawNode.nodeType === Ci.nsIDOMNode.ELEMENT_NODE &&
-                          rawNode.ownerDocument.getAnonymousNodes(rawNode);
-
-    let hasContentDocument = rawNode.contentDocument;
-    let hasSVGDocument = rawNode.getSVGDocument && rawNode.getSVGDocument();
-    if (numChildren === 0 && (hasContentDocument || hasSVGDocument)) {
-      // This might be an iframe with virtual children.
-      numChildren = 1;
-    }
-
-    // Normal counting misses ::before/::after.  Also, some anonymous children
-    // may ultimately be skipped, so we have to consult with the walker.
-    if (numChildren === 0 || hasAnonChildren) {
-      numChildren = this.walker.children(this).nodes.length;
-    }
-
-    return numChildren;
-  },
-
-  get computedStyle() {
-    return CssLogic.getComputedStyle(this.rawNode);
-  },
-
-  /**
-   * Is the node's display computed style value other than "none"
-   */
-  get isDisplayed() {
-    // Consider all non-element nodes as displayed.
-    if (isNodeDead(this) ||
-        this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
-        this.isAfterPseudoElement ||
-        this.isBeforePseudoElement) {
-      return true;
-    }
-
-    let style = this.computedStyle;
-    if (!style) {
-      return true;
-    }
-
-    return style.display !== "none";
-  },
-
-  /**
-   * Are there event listeners that are listening on this node? This method
-   * uses all parsers registered via event-parsers.js.registerEventParser() to
-   * check if there are any event listeners.
-   */
-  get _hasEventListeners() {
-    let parsers = this._eventParsers;
-    for (let [, {hasListeners}] of parsers) {
-      try {
-        if (hasListeners && hasListeners(this.rawNode)) {
-          return true;
-        }
-      } catch (e) {
-        // An object attached to the node looked like a listener but wasn't...
-        // do nothing.
-      }
-    }
-    return false;
-  },
-
-  writeAttrs: function () {
-    if (!this.rawNode.attributes) {
-      return undefined;
-    }
-
-    return [...this.rawNode.attributes].map(attr => {
-      return {namespace: attr.namespace, name: attr.name, value: attr.value };
-    });
-  },
-
-  writePseudoClassLocks: function () {
-    if (this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
-      return undefined;
-    }
-    let ret = undefined;
-    for (let pseudo of PSEUDO_CLASSES) {
-      if (InspectorUtils.hasPseudoClassLock(this.rawNode, pseudo)) {
-        ret = ret || [];
-        ret.push(pseudo);
-      }
-    }
-    return ret;
-  },
-
-  /**
-   * Gets event listeners and adds their information to the events array.
-   *
-   * @param  {Node} node
-   *         Node for which we are to get listeners.
-   */
-  getEventListeners: function (node) {
-    let parsers = this._eventParsers;
-    let dbg = this.parent().tabActor.makeDebugger();
-    let listenerArray = [];
-
-    for (let [, {getListeners, normalizeListener}] of parsers) {
-      try {
-        let listeners = getListeners(node);
-
-        if (!listeners) {
-          continue;
-        }
-
-        for (let listener of listeners) {
-          if (normalizeListener) {
-            listener.normalizeListener = normalizeListener;
-          }
-
-          this.processHandlerForEvent(node, listenerArray, dbg, listener);
-        }
-      } catch (e) {
-        // An object attached to the node looked like a listener but wasn't...
-        // do nothing.
-      }
-    }
-
-    listenerArray.sort((a, b) => {
-      return a.type.localeCompare(b.type);
-    });
-
-    return listenerArray;
-  },
-
-  /**
-   * Process a handler
-   *
-   * @param  {Node} node
-   *         The node for which we want information.
-   * @param  {Array} listenerArray
-   *         listenerArray contains all event objects that we have gathered
-   *         so far.
-   * @param  {Debugger} dbg
-   *         JSDebugger instance.
-   * @param  {Object} eventInfo
-   *         See event-parsers.js.registerEventParser() for a description of the
-   *         eventInfo object.
-   *
-   * @return {Array}
-   *         An array of objects where a typical object looks like this:
-   *           {
-   *             type: "click",
-   *             handler: function() { doSomething() },
-   *             origin: "http://www.mozilla.com",
-   *             searchString: 'onclick="doSomething()"',
-   *             tags: tags,
-   *             DOM0: true,
-   *             capturing: true,
-   *             hide: {
-   *               DOM0: true
-   *             },
-   *             native: false
-   *           }
-   */
-  processHandlerForEvent: function (node, listenerArray, dbg, listener) {
-    let { handler } = listener;
-    let global = Cu.getGlobalForObject(handler);
-    let globalDO = dbg.addDebuggee(global);
-    let listenerDO = globalDO.makeDebuggeeValue(handler);
-
-    let { normalizeListener } = listener;
-
-    if (normalizeListener) {
-      listenerDO = normalizeListener(listenerDO, listener);
-    }
-
-    let { capturing } = listener;
-    let dom0 = false;
-    let functionSource = handler.toString();
-    let hide = listener.hide || {};
-    let line = 0;
-    let native = false;
-    let override = listener.override || {};
-    let tags = listener.tags || "";
-    let type = listener.type || "";
-    let url = "";
-
-    // If the listener is an object with a 'handleEvent' method, use that.
-    if (listenerDO.class === "Object" || listenerDO.class === "XULElement") {
-      let desc;
-
-      while (!desc && listenerDO) {
-        desc = listenerDO.getOwnPropertyDescriptor("handleEvent");
-        listenerDO = listenerDO.proto;
-      }
-
-      if (desc && desc.value) {
-        listenerDO = desc.value;
-      }
-    }
-
-    // If the listener is bound to a different context then we need to switch
-    // to the bound function.
-    if (listenerDO.isBoundFunction) {
-      listenerDO = listenerDO.boundTargetFunction;
-    }
-
-    let { isArrowFunction, name, script, parameterNames } = listenerDO;
-
-    if (script) {
-      let scriptSource = script.source.text;
-
-      // Scripts are provided via script tags. If it wasn't provided by a
-      // script tag it must be a DOM0 event.
-      if (script.source.element) {
-        dom0 = script.source.element.class !== "HTMLScriptElement";
-      } else {
-        dom0 = false;
-      }
-
-      line = script.startLine;
-      url = script.url;
-
-      // Checking for the string "[native code]" is the only way at this point
-      // to check for native code. Even if this provides a false positive then
-      // grabbing the source code a second time is harmless.
-      if (functionSource === "[object Object]" ||
-          functionSource === "[object XULElement]" ||
-          functionSource.includes("[native code]")) {
-        functionSource =
-          scriptSource.substr(script.sourceStart, script.sourceLength);
-
-        // At this point the script looks like this:
-        // () { ... }
-        // We prefix this with "function" if it is not a fat arrow function.
-        if (!isArrowFunction) {
-          functionSource = "function " + functionSource;
-        }
-      }
-    } else {
-      // If the listener is a native one (provided by C++ code) then we have no
-      // access to the script. We use the native flag to prevent showing the
-      // debugger button because the script is not available.
-      native = true;
-    }
-
-    // Fat arrow function text always contains the parameters. Function
-    // parameters are often missing e.g. if Array.sort is used as a handler.
-    // If they are missing we provide the parameters ourselves.
-    if (parameterNames && parameterNames.length > 0) {
-      let prefix = "function " + name + "()";
-      let paramString = parameterNames.join(", ");
-
-      if (functionSource.startsWith(prefix)) {
-        functionSource = functionSource.substr(prefix.length);
-
-        functionSource = `function ${name} (${paramString})${functionSource}`;
-      }
-    }
-
-    // If the listener is native code we display the filename "[native code]."
-    // This is the official string and should *not* be translated.
-    let origin;
-    if (native) {
-      origin = "[native code]";
-    } else {
-      origin = url + ((dom0 || line === 0) ? "" : ":" + line);
-    }
-
-    let eventObj = {
-      type: override.type || type,
-      handler: override.handler || functionSource.trim(),
-      origin: override.origin || origin,
-      tags: override.tags || tags,
-      DOM0: typeof override.dom0 !== "undefined" ? override.dom0 : dom0,
-      capturing: typeof override.capturing !== "undefined" ?
-                 override.capturing : capturing,
-      hide: typeof override.hide !== "undefined" ? override.hide : hide,
-      native
-    };
-
-    // Hide the debugger icon for DOM0 and native listeners. DOM0 listeners are
-    // generated dynamically from e.g. an onclick="" attribute so the script
-    // doesn't actually exist.
-    if (native || dom0) {
-      eventObj.hide.debugger = true;
-    }
-
-    listenerArray.push(eventObj);
-
-    dbg.removeDebuggee(globalDO);
-  },
-
-  /**
-   * Returns a LongStringActor with the node's value.
-   */
-  getNodeValue: function () {
-    return new LongStringActor(this.conn, this.rawNode.nodeValue || "");
-  },
-
-  /**
-   * Set the node's value to a given string.
-   */
-  setNodeValue: function (value) {
-    this.rawNode.nodeValue = value;
-  },
-
-  /**
-   * Get a unique selector string for this node.
-   */
-  getUniqueSelector: function () {
-    if (Cu.isDeadWrapper(this.rawNode)) {
-      return "";
-    }
-    return findCssSelector(this.rawNode);
-  },
-
-  /**
-   * Get the full CSS path for this node.
-   *
-   * @return {String} A CSS selector with a part for the node and each of its ancestors.
-   */
-  getCssPath: function () {
-    if (Cu.isDeadWrapper(this.rawNode)) {
-      return "";
-    }
-    return getCssPath(this.rawNode);
-  },
-
-  /**
-   * Get the XPath for this node.
-   *
-   * @return {String} The XPath for finding this node on the page.
-   */
-  getXPath: function () {
-    if (Cu.isDeadWrapper(this.rawNode)) {
-      return "";
-    }
-    return getXPath(this.rawNode);
-  },
-
-  /**
-   * Scroll the selected node into view.
-   */
-  scrollIntoView: function () {
-    this.rawNode.scrollIntoView(true);
-  },
-
-  /**
-   * Get the node's image data if any (for canvas and img nodes).
-   * Returns an imageData object with the actual data being a LongStringActor
-   * and a size json object.
-   * The image data is transmitted as a base64 encoded png data-uri.
-   * The method rejects if the node isn't an image or if the image is missing
-   *
-   * Accepts a maxDim request parameter to resize images that are larger. This
-   * is important as the resizing occurs server-side so that image-data being
-   * transfered in the longstring back to the client will be that much smaller
-   */
-  getImageData: function (maxDim) {
-    return imageToImageData(this.rawNode, maxDim).then(imageData => {
-      return {
-        data: LongStringActor(this.conn, imageData.data),
-        size: imageData.size
-      };
-    });
-  },
-
-  /**
-   * Get all event listeners that are listening on this node.
-   */
-  getEventListenerInfo: function () {
-    let node = this.rawNode;
-
-    if (this.rawNode.nodeName.toLowerCase() === "html") {
-      let winListeners = this.getEventListeners(node.ownerGlobal) || [];
-      let docElementListeners = this.getEventListeners(node) || [];
-      let docListeners = this.getEventListeners(node.parentNode) || [];
-
-      return [...winListeners, ...docElementListeners, ...docListeners];
-    }
-    return this.getEventListeners(node);
-  },
-
-  /**
-   * Modify a node's attributes.  Passed an array of modifications
-   * similar in format to "attributes" mutations.
-   * {
-   *   attributeName: <string>
-   *   attributeNamespace: <optional string>
-   *   newValue: <optional string> - If null or undefined, the attribute
-   *     will be removed.
-   * }
-   *
-   * Returns when the modifications have been made.  Mutations will
-   * be queued for any changes made.
-   */
-  modifyAttributes: function (modifications) {
-    let rawNode = this.rawNode;
-    for (let change of modifications) {
-      if (change.newValue == null) {
-        if (change.attributeNamespace) {
-          rawNode.removeAttributeNS(change.attributeNamespace,
-                                    change.attributeName);
-        } else {
-          rawNode.removeAttribute(change.attributeName);
-        }
-      } else if (change.attributeNamespace) {
-        rawNode.setAttributeNS(change.attributeNamespace, change.attributeName,
-                               change.newValue);
-      } else {
-        rawNode.setAttribute(change.attributeName, change.newValue);
-      }
-    }
-  },
-
-  /**
-   * Given the font and fill style, get the image data of a canvas with the
-   * preview text and font.
-   * Returns an imageData object with the actual data being a LongStringActor
-   * and the width of the text as a string.
-   * The image data is transmitted as a base64 encoded png data-uri.
-   */
-  getFontFamilyDataURL: function (font, fillStyle = "black") {
-    let doc = this.rawNode.ownerDocument;
-    let options = {
-      previewText: FONT_FAMILY_PREVIEW_TEXT,
-      previewFontSize: FONT_FAMILY_PREVIEW_TEXT_SIZE,
-      fillStyle: fillStyle
-    };
-    let { dataURL, size } = getFontPreviewData(font, doc, options);
-
-    return { data: LongStringActor(this.conn, dataURL), size: size };
-  },
-
-  /**
-   * Finds the computed background color of the closest parent with
-   * a set background color.
-   * Returns a string with the background color of the form
-   * rgba(r, g, b, a). Defaults to rgba(255, 255, 255, 1) if no
-   * background color is found.
-   */
-  getClosestBackgroundColor: function () {
-    let current = this.rawNode;
-    while (current) {
-      let computedStyle = CssLogic.getComputedStyle(current);
-      let currentStyle = computedStyle.getPropertyValue("background-color");
-      if (colorUtils.isValidCSSColor(currentStyle)) {
-        let currentCssColor = new colorUtils.CssColor(currentStyle);
-        if (!currentCssColor.isTransparent()) {
-          return currentCssColor.rgba;
-        }
-      }
-      current = current.parentNode;
-    }
-    return "rgba(255, 255, 255, 1)";
-  }
-});
-
-/**
- * Server side of a node list as returned by querySelectorAll()
- */
-var NodeListActor = exports.NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
-  typeName: "domnodelist",
-
-  initialize: function (walker, nodeList) {
-    protocol.Actor.prototype.initialize.call(this);
-    this.walker = walker;
-    this.nodeList = nodeList || [];
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-  },
-
-  /**
-   * Instead of storing a connection object, the NodeActor gets its connection
-   * from its associated walker.
-   */
-  get conn() {
-    return this.walker.conn;
-  },
-
-  /**
-   * Items returned by this actor should belong to the parent walker.
-   */
-  marshallPool: function () {
-    return this.walker;
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function () {
-    return {
-      actor: this.actorID,
-      length: this.nodeList ? this.nodeList.length : 0
-    };
-  },
-
-  /**
-   * Get a single node from the node list.
-   */
-  item: function (index) {
-    return this.walker.attachElement(this.nodeList[index]);
-  },
-
-  /**
-   * Get a range of the items from the node list.
-   */
-  items: function (start = 0, end = this.nodeList.length) {
-    let items = Array.prototype.slice.call(this.nodeList, start, end)
-      .map(item => this.walker._ref(item));
-    return this.walker.attachElements(items);
-  },
-
-  release: function () {}
-});
-
-/**
- * Server side of the DOM walker.
- */
-var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
-  /**
-   * Create the WalkerActor
-   * @param DebuggerServerConnection conn
-   *    The server connection.
-   */
-  initialize: function (conn, tabActor, options) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this.tabActor = tabActor;
-    this.rootWin = tabActor.window;
-    this.rootDoc = this.rootWin.document;
-    this._refMap = new Map();
-    this._pendingMutations = [];
-    this._activePseudoClassLocks = new Set();
-    this.showAllAnonymousContent = options.showAllAnonymousContent;
-
-    this.walkerSearch = new WalkerSearch(this);
-
-    // Nodes which have been removed from the client's known
-    // ownership tree are considered "orphaned", and stored in
-    // this set.
-    this._orphaned = new Set();
-
-    // The client can tell the walker that it is interested in a node
-    // even when it is orphaned with the `retainNode` method.  This
-    // list contains orphaned nodes that were so retained.
-    this._retainedOrphans = new Set();
-
-    this.onMutations = this.onMutations.bind(this);
-    this.onFrameLoad = this.onFrameLoad.bind(this);
-    this.onFrameUnload = this.onFrameUnload.bind(this);
-    this._throttledEmitNewMutations = throttle(this._emitNewMutations.bind(this),
-      MUTATIONS_THROTTLING_DELAY);
-
-    tabActor.on("will-navigate", this.onFrameUnload);
-    tabActor.on("window-ready", this.onFrameLoad);
-
-    // Ensure that the root document node actor is ready and
-    // managed.
-    this.rootNode = this.document();
-
-    this.layoutChangeObserver = getLayoutChangesObserver(this.tabActor);
-    this._onReflows = this._onReflows.bind(this);
-    this.layoutChangeObserver.on("reflows", this._onReflows);
-    this._onResize = this._onResize.bind(this);
-    this.layoutChangeObserver.on("resize", this._onResize);
-
-    this._onEventListenerChange = this._onEventListenerChange.bind(this);
-    eventListenerService.addListenerChangeListener(this._onEventListenerChange);
-  },
-
-  /**
-   * Callback for eventListenerService.addListenerChangeListener
-   * @param nsISimpleEnumerator changesEnum
-   *    enumerator of nsIEventListenerChange
-   */
-  _onEventListenerChange: function (changesEnum) {
-    let changes = changesEnum.enumerate();
-    while (changes.hasMoreElements()) {
-      let current = changes.getNext().QueryInterface(Ci.nsIEventListenerChange);
-      let target = current.target;
-
-      if (this._refMap.has(target)) {
-        let actor = this.getNode(target);
-        let mutation = {
-          type: "events",
-          target: actor.actorID,
-          hasEventListeners: actor._hasEventListeners
-        };
-        this.queueMutation(mutation);
-      }
-    }
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function () {
-    return {
-      actor: this.actorID,
-      root: this.rootNode.form(),
-      traits: {
-        // FF42+ Inspector starts managing the Walker, while the inspector also
-        // starts cleaning itself up automatically on client disconnection.
-        // So that there is no need to manually release the walker anymore.
-        autoReleased: true,
-        // XXX: It seems silly that we need to tell the front which capabilities
-        // its actor has in this way when the target can use actorHasMethod. If
-        // this was ported to the protocol (Bug 1157048) we could call that
-        // inside of custom front methods and not need to do traits for this.
-        multiFrameQuerySelectorAll: true,
-        textSearch: true,
-      }
-    };
-  },
-
-  toString: function () {
-    return "[WalkerActor " + this.actorID + "]";
-  },
-
-  getDocumentWalker: function (node, whatToShow, skipTo) {
-    // Allow native anon content (like <video> controls) if preffed on
-    let nodeFilter = this.showAllAnonymousContent
-                    ? allAnonymousContentTreeWalkerFilter
-                    : standardTreeWalkerFilter;
-    return new DocumentWalker(node, this.rootWin, whatToShow, nodeFilter, skipTo);
-  },
-
-  destroy: function () {
-    if (this._destroyed) {
-      return;
-    }
-    this._destroyed = true;
-    protocol.Actor.prototype.destroy.call(this);
-    try {
-      this.clearPseudoClassLocks();
-      this._activePseudoClassLocks = null;
-
-      this._hoveredNode = null;
-      this.rootWin = null;
-      this.rootDoc = null;
-      this.rootNode = null;
-      this.layoutHelpers = null;
-      this._orphaned = null;
-      this._retainedOrphans = null;
-      this._refMap = null;
-
-      this.tabActor.off("will-navigate", this.onFrameUnload);
-      this.tabActor.off("window-ready", this.onFrameLoad);
-
-      this.onFrameLoad = null;
-      this.onFrameUnload = null;
-
-      this.walkerSearch.destroy();
-
-      this.layoutChangeObserver.off("reflows", this._onReflows);
-      this.layoutChangeObserver.off("resize", this._onResize);
-      this.layoutChangeObserver = null;
-      releaseLayoutChangesObserver(this.tabActor);
-
-      eventListenerService.removeListenerChangeListener(
-        this._onEventListenerChange);
-
-      this.onMutations = null;
-
-      this.layoutActor = null;
-      this.tabActor = null;
-
-      this.emit("destroyed");
-    } catch (e) {
-      console.error(e);
-    }
-  },
-
-  release: function () {},
-
-  unmanage: function (actor) {
-    if (actor instanceof NodeActor) {
-      if (this._activePseudoClassLocks &&
-          this._activePseudoClassLocks.has(actor)) {
-        this.clearPseudoClassLocks(actor);
-      }
-      this._refMap.delete(actor.rawNode);
-    }
-    protocol.Actor.prototype.unmanage.call(this, actor);
-  },
-
-  /**
-   * Determine if the walker has come across this DOM node before.
-   * @param {DOMNode} rawNode
-   * @return {Boolean}
-   */
-  hasNode: function (rawNode) {
-    return this._refMap.has(rawNode);
-  },
-
-  /**
-   * If the walker has come across this DOM node before, then get the
-   * corresponding node actor.
-   * @param {DOMNode} rawNode
-   * @return {NodeActor}
-   */
-  getNode: function (rawNode) {
-    return this._refMap.get(rawNode);
-  },
-
-  _ref: function (node) {
-    let actor = this.getNode(node);
-    if (actor) {
-      return actor;
-    }
-
-    actor = new NodeActor(this, node);
-
-    // Add the node actor as a child of this walker actor, assigning
-    // it an actorID.
-    this.manage(actor);
-    this._refMap.set(node, actor);
-
-    if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
-      actor.watchDocument(this.onMutations);
-    }
-    return actor;
-  },
-
-  _onReflows: function (reflows) {
-    // Going through the nodes the walker knows about, see which ones have
-    // had their display changed and send a display-change event if any
-    let changes = [];
-    for (let [node, actor] of this._refMap) {
-      if (Cu.isDeadWrapper(node)) {
-        continue;
-      }
-
-      let isDisplayed = actor.isDisplayed;
-      if (isDisplayed !== actor.wasDisplayed) {
-        changes.push(actor);
-        // Updating the original value
-        actor.wasDisplayed = isDisplayed;
-      }
-    }
-
-    if (changes.length) {
-      this.emit("display-change", changes);
-    }
-  },
-
-  /**
-   * When the browser window gets resized, relay the event to the front.
-   */
-  _onResize: function () {
-    this.emit("resize");
-  },
-
-  /**
-   * This is kept for backward-compatibility reasons with older remote targets.
-   * Targets prior to bug 916443.
-   *
-   * pick/cancelPick are used to pick a node on click on the content
-   * document. But in their implementation prior to bug 916443, they don't allow
-   * highlighting on hover.
-   * The client-side now uses the highlighter actor's pick and cancelPick
-   * methods instead. The client-side uses the the highlightable trait found in
-   * the root actor to determine which version of pick to use.
-   *
-   * As for highlight, the new highlighter actor is used instead of the walker's
-   * highlight method. Same here though, the client-side uses the highlightable
-   * trait to dertermine which to use.
-   *
-   * Keeping these actor methods for now allows newer client-side debuggers to
-   * inspect fxos 1.2 remote targets or older firefox desktop remote targets.
-   */
-  pick: function () {},
-  cancelPick: function () {},
-  highlight: function (node) {},
-
-  /**
-   * Ensures that the node is attached and it can be accessed from the root.
-   *
-   * @param {(Node|NodeActor)} nodes The nodes
-   * @return {Object} An object compatible with the disconnectedNode type.
-   */
-  attachElement: function (node) {
-    let { nodes, newParents } = this.attachElements([node]);
-    return {
-      node: nodes[0],
-      newParents: newParents
-    };
-  },
-
-  /**
-   * Ensures that the nodes are attached and they can be accessed from the root.
-   *
-   * @param {(Node[]|NodeActor[])} nodes The nodes
-   * @return {Object} An object compatible with the disconnectedNodeArray type.
-   */
-  attachElements: function (nodes) {
-    let nodeActors = [];
-    let newParents = new Set();
-    for (let node of nodes) {
-      if (!(node instanceof NodeActor)) {
-        // If an anonymous node was passed in and we aren't supposed to know
-        // about it, then consult with the document walker as the source of
-        // truth about which elements exist.
-        if (!this.showAllAnonymousContent && isAnonymous(node)) {
-          node = this.getDocumentWalker(node).currentNode;
-        }
-
-        node = this._ref(node);
-      }
-
-      this.ensurePathToRoot(node, newParents);
-      // If nodes may be an array of raw nodes, we're sure to only have
-      // NodeActors with the following array.
-      nodeActors.push(node);
-    }
-
-    return {
-      nodes: nodeActors,
-      newParents: [...newParents]
-    };
-  },
-
-  /**
-   * Return the document node that contains the given node,
-   * or the root node if no node is specified.
-   * @param NodeActor node
-   *        The node whose document is needed, or null to
-   *        return the root.
-   */
-  document: function (node) {
-    let doc = isNodeDead(node) ? this.rootDoc : nodeDocument(node.rawNode);
-    return this._ref(doc);
-  },
-
-  /**
-   * Return the documentElement for the document containing the
-   * given node.
-   * @param NodeActor node
-   *        The node whose documentElement is requested, or null
-   *        to use the root document.
-   */
-  documentElement: function (node) {
-    let elt = isNodeDead(node)
-              ? this.rootDoc.documentElement
-              : nodeDocument(node.rawNode).documentElement;
-    return this._ref(elt);
-  },
-
-  /**
-   * Return all parents of the given node, ordered from immediate parent
-   * to root.
-   * @param NodeActor node
-   *    The node whose parents are requested.
-   * @param object options
-   *    Named options, including:
-   *    `sameDocument`: If true, parents will be restricted to the same
-   *      document as the node.
-   *    `sameTypeRootTreeItem`: If true, this will not traverse across
-   *     different types of docshells.
-   */
-  parents: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return [];
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let parents = [];
-    let cur;
-    while ((cur = walker.parentNode())) {
-      if (options.sameDocument &&
-          nodeDocument(cur) != nodeDocument(node.rawNode)) {
-        break;
-      }
-
-      if (options.sameTypeRootTreeItem &&
-          nodeDocshell(cur).sameTypeRootTreeItem !=
-          nodeDocshell(node.rawNode).sameTypeRootTreeItem) {
-        break;
-      }
-
-      parents.push(this._ref(cur));
-    }
-    return parents;
-  },
-
-  parentNode: function (node) {
-    let walker = this.getDocumentWalker(node.rawNode);
-    let parent = walker.parentNode();
-    if (parent) {
-      return this._ref(parent);
-    }
-    return null;
-  },
-
-  /**
-   * If the given NodeActor only has a single text node as a child with a text
-   * content small enough to be inlined, return that child's NodeActor.
-   *
-   * @param NodeActor node
-   */
-  inlineTextChild: function (node) {
-    // Quick checks to prevent creating a new walker if possible.
-    if (node.isBeforePseudoElement ||
-        node.isAfterPseudoElement ||
-        node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
-        node.rawNode.children.length > 0) {
-      return undefined;
-    }
-
-    let docWalker = this.getDocumentWalker(node.rawNode);
-    let firstChild = docWalker.firstChild();
-
-    // Bail out if:
-    // - more than one child
-    // - unique child is not a text node
-    // - unique child is a text node, but is too long to be inlined
-    if (!firstChild ||
-        docWalker.nextSibling() ||
-        firstChild.nodeType !== Ci.nsIDOMNode.TEXT_NODE ||
-        firstChild.nodeValue.length > gValueSummaryLength
-        ) {
-      return undefined;
-    }
-
-    return this._ref(firstChild);
-  },
-
-  /**
-   * Mark a node as 'retained'.
-   *
-   * A retained node is not released when `releaseNode` is called on its
-   * parent, or when a parent is released with the `cleanup` option to
-   * `getMutations`.
-   *
-   * When a retained node's parent is released, a retained mode is added to
-   * the walker's "retained orphans" list.
-   *
-   * Retained nodes can be deleted by providing the `force` option to
-   * `releaseNode`.  They will also be released when their document
-   * has been destroyed.
-   *
-   * Retaining a node makes no promise about its children;  They can
-   * still be removed by normal means.
-   */
-  retainNode: function (node) {
-    node.retained = true;
-  },
-
-  /**
-   * Remove the 'retained' mark from a node.  If the node was a
-   * retained orphan, release it.
-   */
-  unretainNode: function (node) {
-    node.retained = false;
-    if (this._retainedOrphans.has(node)) {
-      this._retainedOrphans.delete(node);
-      this.releaseNode(node);
-    }
-  },
-
-  /**
-   * Release actors for a node and all child nodes.
-   */
-  releaseNode: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    if (node.retained && !options.force) {
-      this._retainedOrphans.add(node);
-      return;
-    }
-
-    if (node.retained) {
-      // Forcing a retained node to go away.
-      this._retainedOrphans.delete(node);
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-
-    let child = walker.firstChild();
-    while (child) {
-      let childActor = this.getNode(child);
-      if (childActor) {
-        this.releaseNode(childActor, options);
-      }
-      child = walker.nextSibling();
-    }
-
-    node.destroy();
-  },
-
-  /**
-   * Add any nodes between `node` and the walker's root node that have not
-   * yet been seen by the client.
-   */
-  ensurePathToRoot: function (node, newParents = new Set()) {
-    if (!node) {
-      return newParents;
-    }
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let parent = this.getNode(cur);
-      if (!parent) {
-        // This parent didn't exist, so hasn't been seen by the client yet.
-        newParents.add(this._ref(cur));
-      } else {
-        // This parent did exist, so the client knows about it.
-        return newParents;
-      }
-    }
-    return newParents;
-  },
-
-  /**
-   * Return children of the given node.  By default this method will return
-   * all children of the node, but there are options that can restrict this
-   * to a more manageable subset.
-   *
-   * @param NodeActor node
-   *    The node whose children you're curious about.
-   * @param object options
-   *    Named options:
-   *    `maxNodes`: The set of nodes returned by the method will be no longer
-   *       than maxNodes.
-   *    `start`: If a node is specified, the list of nodes will start
-   *       with the given child.  Mutally exclusive with `center`.
-   *    `center`: If a node is specified, the given node will be as centered
-   *       as possible in the list, given how close to the ends of the child
-   *       list it is.  Mutually exclusive with `start`.
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   *
-   * @returns an object with three items:
-   *    hasFirst: true if the first child of the node is included in the list.
-   *    hasLast: true if the last child of the node is included in the list.
-   *    nodes: Child nodes returned by the request.
-   */
-  children: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    if (options.center && options.start) {
-      throw Error("Can't specify both 'center' and 'start' options.");
-    }
-    let maxNodes = options.maxNodes || -1;
-    if (maxNodes == -1) {
-      maxNodes = Number.MAX_VALUE;
-    }
-
-    // We're going to create a few document walkers with the same filter,
-    // make it easier.
-    let getFilteredWalker = documentWalkerNode => {
-      let { whatToShow } = options;
-      // Use SKIP_TO_SIBLING to force the walker to use a sibling of the provided node
-      // in case this one is incompatible with the walker's filter function.
-      return this.getDocumentWalker(documentWalkerNode, whatToShow, SKIP_TO_SIBLING);
-    };
-
-    // Need to know the first and last child.
-    let rawNode = node.rawNode;
-    let firstChild = getFilteredWalker(rawNode).firstChild();
-    let lastChild = getFilteredWalker(rawNode).lastChild();
-
-    if (!firstChild) {
-      // No children, we're done.
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    let start;
-    if (options.center) {
-      start = options.center.rawNode;
-    } else if (options.start) {
-      start = options.start.rawNode;
-    } else {
-      start = firstChild;
-    }
-
-    let nodes = [];
-
-    // Start by reading backward from the starting point if we're centering...
-    let backwardWalker = getFilteredWalker(start);
-    if (backwardWalker.currentNode != firstChild && options.center) {
-      backwardWalker.previousSibling();
-      let backwardCount = Math.floor(maxNodes / 2);
-      let backwardNodes = this._readBackward(backwardWalker, backwardCount);
-      nodes = backwardNodes;
-    }
-
-    // Then read forward by any slack left in the max children...
-    let forwardWalker = getFilteredWalker(start);
-    let forwardCount = maxNodes - nodes.length;
-    nodes = nodes.concat(this._readForward(forwardWalker, forwardCount));
-
-    // If there's any room left, it means we've run all the way to the end.
-    // If we're centering, check if there are more items to read at the front.
-    let remaining = maxNodes - nodes.length;
-    if (options.center && remaining > 0 && nodes[0].rawNode != firstChild) {
-      let firstNodes = this._readBackward(backwardWalker, remaining);
-
-      // Then put it all back together.
-      nodes = firstNodes.concat(nodes);
-    }
-
-    return {
-      hasFirst: nodes[0].rawNode == firstChild,
-      hasLast: nodes[nodes.length - 1].rawNode == lastChild,
-      nodes: nodes
-    };
-  },
-
-  /**
-   * Return siblings of the given node.  By default this method will return
-   * all siblings of the node, but there are options that can restrict this
-   * to a more manageable subset.
-   *
-   * If `start` or `center` are not specified, this method will center on the
-   * node whose siblings are requested.
-   *
-   * @param NodeActor node
-   *    The node whose children you're curious about.
-   * @param object options
-   *    Named options:
-   *    `maxNodes`: The set of nodes returned by the method will be no longer
-   *       than maxNodes.
-   *    `start`: If a node is specified, the list of nodes will start
-   *       with the given child.  Mutally exclusive with `center`.
-   *    `center`: If a node is specified, the given node will be as centered
-   *       as possible in the list, given how close to the ends of the child
-   *       list it is.  Mutually exclusive with `start`.
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   *
-   * @returns an object with three items:
-   *    hasFirst: true if the first child of the node is included in the list.
-   *    hasLast: true if the last child of the node is included in the list.
-   *    nodes: Child nodes returned by the request.
-   */
-  siblings: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    let parentNode = this.getDocumentWalker(node.rawNode, options.whatToShow)
-                         .parentNode();
-    if (!parentNode) {
-      return {
-        hasFirst: true,
-        hasLast: true,
-        nodes: [node]
-      };
-    }
-
-    if (!(options.start || options.center)) {
-      options.center = node;
-    }
-
-    return this.children(this._ref(parentNode), options);
-  },
-
-  /**
-   * Get the next sibling of a given node.  Getting nodes one at a time
-   * might be inefficient, be careful.
-   *
-   * @param object options
-   *    Named options:
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   */
-  nextSibling: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
-    let sibling = walker.nextSibling();
-    return sibling ? this._ref(sibling) : null;
-  },
-
-  /**
-   * Get the previous sibling of a given node.  Getting nodes one at a time
-   * might be inefficient, be careful.
-   *
-   * @param object options
-   *    Named options:
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   */
-  previousSibling: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
-    let sibling = walker.previousSibling();
-    return sibling ? this._ref(sibling) : null;
-  },
-
-  /**
-   * Helper function for the `children` method: Read forward in the sibling
-   * list into an array with `count` items, including the current node.
-   */
-  _readForward: function (walker, count) {
-    let ret = [];
-
-    let node = walker.currentNode;
-    do {
-      if (!walker.isSkippedNode(node)) {
-        // The walker can be on a node that would be filtered out if it didn't find any
-        // other node to fallback to.
-        ret.push(this._ref(node));
-      }
-      node = walker.nextSibling();
-    } while (node && --count);
-    return ret;
-  },
-
-  /**
-   * Helper function for the `children` method: Read backward in the sibling
-   * list into an array with `count` items, including the current node.
-   */
-  _readBackward: function (walker, count) {
-    let ret = [];
-
-    let node = walker.currentNode;
-    do {
-      if (!walker.isSkippedNode(node)) {
-        // The walker can be on a node that would be filtered out if it didn't find any
-        // other node to fallback to.
-        ret.push(this._ref(node));
-      }
-      node = walker.previousSibling();
-    } while (node && --count);
-    ret.reverse();
-    return ret;
-  },
-
-  /**
-   * Return the first node in the document that matches the given selector.
-   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelector
-   *
-   * @param NodeActor baseNode
-   * @param string selector
-   */
-  querySelector: function (baseNode, selector) {
-    if (isNodeDead(baseNode)) {
-      return {};
-    }
-
-    let node = baseNode.rawNode.querySelector(selector);
-    if (!node) {
-      return {};
-    }
-
-    return this.attachElement(node);
-  },
-
-  /**
-   * Return a NodeListActor with all nodes that match the given selector.
-   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll
-   *
-   * @param NodeActor baseNode
-   * @param string selector
-   */
-  querySelectorAll: function (baseNode, selector) {
-    let nodeList = null;
-
-    try {
-      nodeList = baseNode.rawNode.querySelectorAll(selector);
-    } catch (e) {
-      // Bad selector. Do nothing as the selector can come from a searchbox.
-    }
-
-    return new NodeListActor(this, nodeList);
-  },
-
-  /**
-   * Get a list of nodes that match the given selector in all known frames of
-   * the current content page.
-   * @param {String} selector.
-   * @return {Array}
-   */
-  _multiFrameQuerySelectorAll: function (selector) {
-    let nodes = [];
-
-    for (let {document} of this.tabActor.windows) {
-      try {
-        nodes = [...nodes, ...document.querySelectorAll(selector)];
-      } catch (e) {
-        // Bad selector. Do nothing as the selector can come from a searchbox.
-      }
-    }
-
-    return nodes;
-  },
-
-  /**
-   * Return a NodeListActor with all nodes that match the given selector in all
-   * frames of the current content page.
-   * @param {String} selector
-   */
-  multiFrameQuerySelectorAll: function (selector) {
-    return new NodeListActor(this, this._multiFrameQuerySelectorAll(selector));
-  },
-
-  /**
-   * Search the document for a given string.
-   * Results will be searched with the walker-search module (searches through
-   * tag names, attribute names and values, and text contents).
-   *
-   * @returns {searchresult}
-   *            - {NodeList} list
-   *            - {Array<Object>} metadata. Extra information with indices that
-   *                              match up with node list.
-   */
-  search: function (query) {
-    let results = this.walkerSearch.search(query);
-    let nodeList = new NodeListActor(this, results.map(r => r.node));
-
-    return {
-      list: nodeList,
-      metadata: []
-    };
-  },
-
-  /**
-   * Returns a list of matching results for CSS selector autocompletion.
-   *
-   * @param string query
-   *        The selector query being completed
-   * @param string completing
-   *        The exact token being completed out of the query
-   * @param string selectorState
-   *        One of "pseudo", "id", "tag", "class", "null"
-   */
-  getSuggestionsForQuery: function (query, completing, selectorState) {
-    let sugs = {
-      classes: new Map(),
-      tags: new Map(),
-      ids: new Map()
-    };
-    let result = [];
-    let nodes = null;
-    // Filtering and sorting the results so that protocol transfer is miminal.
-    switch (selectorState) {
-      case "pseudo":
-        result = PSEUDO_SELECTORS.filter(item => {
-          return item[0].startsWith(":" + completing);
-        });
-        break;
-
-      case "class":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("[class]");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          for (let className of node.classList) {
-            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
-          }
-        }
-        sugs.classes.delete("");
-        sugs.classes.delete(HIDDEN_CLASS);
-        for (let [className, count] of sugs.classes) {
-          if (className.startsWith(completing)) {
-            result.push(["." + CSS.escape(className), count, selectorState]);
-          }
-        }
-        break;
-
-      case "id":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("[id]");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
-        }
-        for (let [id, count] of sugs.ids) {
-          if (id.startsWith(completing) && id !== "") {
-            result.push(["#" + CSS.escape(id), count, selectorState]);
-          }
-        }
-        break;
-
-      case "tag":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("*");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          let tag = node.localName;
-          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
-        }
-        for (let [tag, count] of sugs.tags) {
-          if ((new RegExp("^" + completing + ".*", "i")).test(tag)) {
-            result.push([tag, count, selectorState]);
-          }
-        }
-
-        // For state 'tag' (no preceding # or .) and when there's no query (i.e.
-        // only one word) then search for the matching classes and ids
-        if (!query) {
-          result = [
-            ...result,
-            ...this.getSuggestionsForQuery(null, completing, "class")
-                   .suggestions,
-            ...this.getSuggestionsForQuery(null, completing, "id")
-                   .suggestions
-          ];
-        }
-
-        break;
-
-      case "null":
-        nodes = this._multiFrameQuerySelectorAll(query);
-        for (let node of nodes) {
-          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
-          let tag = node.localName;
-          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
-          for (let className of node.classList) {
-            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
-          }
-        }
-        for (let [tag, count] of sugs.tags) {
-          tag && result.push([tag, count]);
-        }
-        for (let [id, count] of sugs.ids) {
-          id && result.push(["#" + id, count]);
-        }
-        sugs.classes.delete("");
-        sugs.classes.delete(HIDDEN_CLASS);
-        for (let [className, count] of sugs.classes) {
-          className && result.push(["." + className, count]);
-        }
-    }
-
-    // Sort by count (desc) and name (asc)
-    result = result.sort((a, b) => {
-      // Computed a sortable string with first the inverted count, then the name
-      let sortA = (10000 - a[1]) + a[0];
-      let sortB = (10000 - b[1]) + b[0];
-
-      // Prefixing ids, classes and tags, to group results
-      let firstA = a[0].substring(0, 1);
-      let firstB = b[0].substring(0, 1);
-
-      if (firstA === "#") {
-        sortA = "2" + sortA;
-      } else if (firstA === ".") {
-        sortA = "1" + sortA;
-      } else {
-        sortA = "0" + sortA;
-      }
-
-      if (firstB === "#") {
-        sortB = "2" + sortB;
-      } else if (firstB === ".") {
-        sortB = "1" + sortB;
-      } else {
-        sortB = "0" + sortB;
-      }
-
-      // String compare
-      return sortA.localeCompare(sortB);
-    });
-
-    result.slice(0, 25);
-
-    return {
-      query: query,
-      suggestions: result
-    };
-  },
-
-  /**
-   * Add a pseudo-class lock to a node.
-   *
-   * @param NodeActor node
-   * @param string pseudo
-   *    A pseudoclass: ':hover', ':active', ':focus'
-   * @param options
-   *    Options object:
-   *    `parents`: True if the pseudo-class should be added
-   *      to parent nodes.
-   *    `enabled`: False if the pseudo-class should be locked
-   *      to 'off'. Defaults to true.
-   *
-   * @returns An empty packet.  A "pseudoClassLock" mutation will
-   *    be queued for any changed nodes.
-   */
-  addPseudoClassLock: function (node, pseudo, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    // There can be only one node locked per pseudo, so dismiss all existing
-    // ones
-    for (let locked of this._activePseudoClassLocks) {
-      if (InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
-        this._removePseudoClassLock(locked, pseudo);
-      }
-    }
-
-    let enabled = options.enabled === undefined ||
-                  options.enabled;
-    this._addPseudoClassLock(node, pseudo, enabled);
-
-    if (!options.parents) {
-      return;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let curNode = this._ref(cur);
-      this._addPseudoClassLock(curNode, pseudo, enabled);
-    }
-  },
-
-  _queuePseudoClassMutation: function (node) {
-    this.queueMutation({
-      target: node.actorID,
-      type: "pseudoClassLock",
-      pseudoClassLocks: node.writePseudoClassLocks()
-    });
-  },
-
-  _addPseudoClassLock: function (node, pseudo, enabled) {
-    if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
-      return false;
-    }
-    InspectorUtils.addPseudoClassLock(node.rawNode, pseudo, enabled);
-    this._activePseudoClassLocks.add(node);
-    this._queuePseudoClassMutation(node);
-    return true;
-  },
-
-  hideNode: function (node) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    loadSheet(node.rawNode.ownerGlobal, HELPER_SHEET);
-    node.rawNode.classList.add(HIDDEN_CLASS);
-  },
-
-  unhideNode: function (node) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    node.rawNode.classList.remove(HIDDEN_CLASS);
-  },
-
-  /**
-   * Remove a pseudo-class lock from a node.
-   *
-   * @param NodeActor node
-   * @param string pseudo
-   *    A pseudoclass: ':hover', ':active', ':focus'
-   * @param options
-   *    Options object:
-   *    `parents`: True if the pseudo-class should be removed
-   *      from parent nodes.
-   *
-   * @returns An empty response.  "pseudoClassLock" mutations
-   *    will be emitted for any changed nodes.
-   */
-  removePseudoClassLock: function (node, pseudo, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    this._removePseudoClassLock(node, pseudo);
-
-    // Remove pseudo class for children as we don't want to allow
-    // turning it on for some childs without setting it on some parents
-    for (let locked of this._activePseudoClassLocks) {
-      if (node.rawNode.contains(locked.rawNode) &&
-          InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
-        this._removePseudoClassLock(locked, pseudo);
-      }
-    }
-
-    if (!options.parents) {
-      return;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let curNode = this._ref(cur);
-      this._removePseudoClassLock(curNode, pseudo);
-    }
-  },
-
-  _removePseudoClassLock: function (node, pseudo) {
-    if (node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) {
-      return false;
-    }
-    InspectorUtils.removePseudoClassLock(node.rawNode, pseudo);
-    if (!node.writePseudoClassLocks()) {
-      this._activePseudoClassLocks.delete(node);
-    }
-
-    this._queuePseudoClassMutation(node);
-    return true;
-  },
-
-  /**
-   * Clear all the pseudo-classes on a given node or all nodes.
-   * @param {NodeActor} node Optional node to clear pseudo-classes on
-   */
-  clearPseudoClassLocks: function (node) {
-    if (node && isNodeDead(node)) {
-      return;
-    }
-
-    if (node) {
-      InspectorUtils.clearPseudoClassLocks(node.rawNode);
-      this._activePseudoClassLocks.delete(node);
-      this._queuePseudoClassMutation(node);
-    } else {
-      for (let locked of this._activePseudoClassLocks) {
-        InspectorUtils.clearPseudoClassLocks(locked.rawNode);
-        this._activePseudoClassLocks.delete(locked);
-        this._queuePseudoClassMutation(locked);
-      }
-    }
-  },
-
-  /**
-   * Get a node's innerHTML property.
-   */
-  innerHTML: function (node) {
-    let html = "";
-    if (!isNodeDead(node)) {
-      html = node.rawNode.innerHTML;
-    }
-    return LongStringActor(this.conn, html);
-  },
-
-  /**
-   * Set a node's innerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   * @param {string} value The piece of HTML content.
-   */
-  setInnerHTML: function (node, value) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    let rawNode = node.rawNode;
-    if (rawNode.nodeType !== rawNode.ownerDocument.ELEMENT_NODE) {
-      throw new Error("Can only change innerHTML to element nodes");
-    }
-    // eslint-disable-next-line no-unsanitized/property
-    rawNode.innerHTML = value;
-  },
-
-  /**
-   * Get a node's outerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   */
-  outerHTML: function (node) {
-    let outerHTML = "";
-    if (!isNodeDead(node)) {
-      outerHTML = node.rawNode.outerHTML;
-    }
-    return LongStringActor(this.conn, outerHTML);
-  },
-
-  /**
-   * Set a node's outerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   * @param {string} value The piece of HTML content.
-   */
-  setOuterHTML: function (node, value) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    let parsedDOM = DOMParser.parseFromString(value, "text/html");
-    let rawNode = node.rawNode;
-    let parentNode = rawNode.parentNode;
-
-    // Special case for head and body.  Setting document.body.outerHTML
-    // creates an extra <head> tag, and document.head.outerHTML creates
-    // an extra <body>.  So instead we will call replaceChild with the
-    // parsed DOM, assuming that they aren't trying to set both tags at once.
-    if (rawNode.tagName === "BODY") {
-      if (parsedDOM.head.innerHTML === "") {
-        parentNode.replaceChild(parsedDOM.body, rawNode);
-      } else {
-      // eslint-disable-next-line no-unsanitized/property
-        rawNode.outerHTML = value;
-      }
-    } else if (rawNode.tagName === "HEAD") {
-      if (parsedDOM.body.innerHTML === "") {
-        parentNode.replaceChild(parsedDOM.head, rawNode);
-      } else {
-        // eslint-disable-next-line no-unsanitized/property
-        rawNode.outerHTML = value;
-      }
-    } else if (node.isDocumentElement()) {
-      // Unable to set outerHTML on the document element.  Fall back by
-      // setting attributes manually, then replace the body and head elements.
-      let finalAttributeModifications = [];
-      let attributeModifications = {};
-      for (let attribute of rawNode.attributes) {
-        attributeModifications[attribute.name] = null;
-      }
-      for (let attribute of parsedDOM.documentElement.attributes) {
-        attributeModifications[attribute.name] = attribute.value;
-      }
-      for (let key in attributeModifications) {
-        finalAttributeModifications.push({
-          attributeName: key,
-          newValue: attributeModifications[key]
-        });
-      }
-      node.modifyAttributes(finalAttributeModifications);
-      rawNode.replaceChild(parsedDOM.head, rawNode.querySelector("head"));
-      rawNode.replaceChild(parsedDOM.body, rawNode.querySelector("body"));
-    } else {
-      // eslint-disable-next-line no-unsanitized/property
-      rawNode.outerHTML = value;
-    }
-  },
-
-  /**
-   * Insert adjacent HTML to a node.
-   *
-   * @param {Node} node
-   * @param {string} position One of "beforeBegin", "afterBegin", "beforeEnd",
-   *                          "afterEnd" (see Element.insertAdjacentHTML).
-   * @param {string} value The HTML content.
-   */
-  insertAdjacentHTML: function (node, position, value) {
-    if (isNodeDead(node)) {
-      return {node: [], newParents: []};
-    }
-
-    let rawNode = node.rawNode;
-    let isInsertAsSibling = position === "beforeBegin" ||
-      position === "afterEnd";
-
-    // Don't insert anything adjacent to the document element.
-    if (isInsertAsSibling && node.isDocumentElement()) {
-      throw new Error("Can't insert adjacent element to the root.");
-    }
-
-    let rawParentNode = rawNode.parentNode;
-    if (!rawParentNode && isInsertAsSibling) {
-      throw new Error("Can't insert as sibling without parent node.");
-    }
-
-    // We can't use insertAdjacentHTML, because we want to return the nodes
-    // being created (so the front can remove them if the user undoes
-    // the change). So instead, use Range.createContextualFragment().
-    let range = rawNode.ownerDocument.createRange();
-    if (position === "beforeBegin" || position === "afterEnd") {
-      range.selectNode(rawNode);
-    } else {
-      range.selectNodeContents(rawNode);
-    }
-    let docFrag = range.createContextualFragment(value);
-    let newRawNodes = Array.from(docFrag.childNodes);
-    switch (position) {
-      case "beforeBegin":
-        rawParentNode.insertBefore(docFrag, rawNode);
-        break;
-      case "afterEnd":
-        // Note: if the second argument is null, rawParentNode.insertBefore
-        // behaves like rawParentNode.appendChild.
-        rawParentNode.insertBefore(docFrag, rawNode.nextSibling);
-        break;
-      case "afterBegin":
-        rawNode.insertBefore(docFrag, rawNode.firstChild);
-        break;
-      case "beforeEnd":
-        rawNode.appendChild(docFrag);
-        break;
-      default:
-        throw new Error("Invalid position value. Must be either " +
-          "'beforeBegin', 'beforeEnd', 'afterBegin' or 'afterEnd'.");
-    }
-
-    return this.attachElements(newRawNodes);
-  },
-
-  /**
-   * Duplicate a specified node
-   *
-   * @param {NodeActor} node The node to duplicate.
-   */
-  duplicateNode: function ({rawNode}) {
-    let clonedNode = rawNode.cloneNode(true);
-    rawNode.parentNode.insertBefore(clonedNode, rawNode.nextSibling);
-  },
-
-  /**
-   * Test whether a node is a document or a document element.
-   *
-   * @param {NodeActor} node The node to remove.
-   * @return {boolean} True if the node is a document or a document element.
-   */
-  isDocumentOrDocumentElementNode: function (node) {
-    return ((node.rawNode.ownerDocument &&
-      node.rawNode.ownerDocument.documentElement === this.rawNode) ||
-      node.rawNode.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE);
-  },
-
-  /**
-   * Removes a node from its parent node.
-   *
-   * @param {NodeActor} node The node to remove.
-   * @returns The node's nextSibling before it was removed.
-   */
-  removeNode: function (node) {
-    if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
-      throw Error("Cannot remove document, document elements or dead nodes.");
-    }
-
-    let nextSibling = this.nextSibling(node);
-    node.rawNode.remove();
-    // Mutation events will take care of the rest.
-    return nextSibling;
-  },
-
-  /**
-   * Removes an array of nodes from their parent node.
-   *
-   * @param {NodeActor[]} nodes The nodes to remove.
-   */
-  removeNodes: function (nodes) {
-    // Check that all nodes are valid before processing the removals.
-    for (let node of nodes) {
-      if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
-        throw Error("Cannot remove document, document elements or dead nodes");
-      }
-    }
-
-    for (let node of nodes) {
-      node.rawNode.remove();
-      // Mutation events will take care of the rest.
-    }
-  },
-
-  /**
-   * Insert a node into the DOM.
-   */
-  insertBefore: function (node, parent, sibling) {
-    if (isNodeDead(node) ||
-        isNodeDead(parent) ||
-        (sibling && isNodeDead(sibling))) {
-      return;
-    }
-
-    let rawNode = node.rawNode;
-    let rawParent = parent.rawNode;
-    let rawSibling = sibling ? sibling.rawNode : null;
-
-    // Don't bother inserting a node if the document position isn't going
-    // to change. This prevents needless iframes reloading and mutations.
-    if (rawNode.parentNode === rawParent) {
-      let currentNextSibling = this.nextSibling(node);
-      currentNextSibling = currentNextSibling ? currentNextSibling.rawNode :
-                                                null;
-
-      if (rawNode === rawSibling || currentNextSibling === rawSibling) {
-        return;
-      }
-    }
-
-    rawParent.insertBefore(rawNode, rawSibling);
-  },
-
-  /**
-   * Editing a node's tagname actually means creating a new node with the same
-   * attributes, removing the node and inserting the new one instead.
-   * This method does not return anything as mutation events are taking care of
-   * informing the consumers about changes.
-   */
-  editTagName: function (node, tagName) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let oldNode = node.rawNode;
-
-    // Create a new element with the same attributes as the current element and
-    // prepare to replace the current node with it.
-    let newNode;
-    try {
-      newNode = nodeDocument(oldNode).createElement(tagName);
-    } catch (x) {
-      // Failed to create a new element with that tag name, ignore the change,
-      // and signal the error to the front.
-      return Promise.reject(new Error("Could not change node's tagName to " + tagName));
-    }
-
-    let attrs = oldNode.attributes;
-    for (let i = 0; i < attrs.length; i++) {
-      newNode.setAttribute(attrs[i].name, attrs[i].value);
-    }
-
-    // Insert the new node, and transfer the old node's children.
-    oldNode.parentNode.insertBefore(newNode, oldNode);
-    while (oldNode.firstChild) {
-      newNode.appendChild(oldNode.firstChild);
-    }
-
-    oldNode.remove();
-    return null;
-  },
-
-  /**
-   * Get any pending mutation records.  Must be called by the client after
-   * the `new-mutations` notification is received.  Returns an array of
-   * mutation records.
-   *
-   * Mutation records have a basic structure:
-   *
-   * {
-   *   type: attributes|characterData|childList,
-   *   target: <domnode actor ID>,
-   * }
-   *
-   * And additional attributes based on the mutation type:
-   *
-   * `attributes` type:
-   *   attributeName: <string> - the attribute that changed
-   *   attributeNamespace: <string> - the attribute's namespace URI, if any.
-   *   newValue: <string> - The new value of the attribute, if any.
-   *
-   * `characterData` type:
-   *   newValue: <string> - the new nodeValue for the node
-   *
-   * `childList` type is returned when the set of children for a node
-   * has changed.  Includes extra data, which can be used by the client to
-   * maintain its ownership subtree.
-   *
-   *   added: array of <domnode actor ID> - The list of actors *previously
-   *     seen by the client* that were added to the target node.
-   *   removed: array of <domnode actor ID> The list of actors *previously
-   *     seen by the client* that were removed from the target node.
-   *   inlineTextChild: If the node now has a single text child, it will
-   *     be sent here.
-   *
-   * Actors that are included in a MutationRecord's `removed` but
-   * not in an `added` have been removed from the client's ownership
-   * tree (either by being moved under a node the client has seen yet
-   * or by being removed from the tree entirely), and is considered
-   * 'orphaned'.
-   *
-   * Keep in mind that if a node that the client hasn't seen is moved
-   * into or out of the target node, it will not be included in the
-   * removedNodes and addedNodes list, so if the client is interested
-   * in the new set of children it needs to issue a `children` request.
-   */
-  getMutations: function (options = {}) {
-    let pending = this._pendingMutations || [];
-    this._pendingMutations = [];
-    this._waitingForGetMutations = false;
-
-    if (options.cleanup) {
-      for (let node of this._orphaned) {
-        // Release the orphaned node.  Nodes or children that have been
-        // retained will be moved to this._retainedOrphans.
-        this.releaseNode(node);
-      }
-      this._orphaned = new Set();
-    }
-
-    return pending;
-  },
-
-  queueMutation: function (mutation) {
-    if (!this.actorID || this._destroyed) {
-      // We've been destroyed, don't bother queueing this mutation.
-      return;
-    }
-
-    // Add the mutation to the list of mutations to be retrieved next.
-    this._pendingMutations.push(mutation);
-
-    // Bail out if we already emitted a new-mutations event and are waiting for a client
-    // to retrieve them.
-    if (this._waitingForGetMutations) {
-      return;
-    }
-
-    if (IMMEDIATE_MUTATIONS.includes(mutation.type)) {
-      this._emitNewMutations();
-    } else {
-      /**
-       * If many mutations are fired at the same time, clients might sequentially request
-       * children/siblings for updated nodes, which can be costly. By throttling the calls
-       * to getMutations, duplicated mutations will be ignored.
-       */
-      this._throttledEmitNewMutations();
-    }
-  },
-
-  _emitNewMutations: function () {
-    if (!this.actorID || this._destroyed) {
-      // Bail out if the actor was destroyed after throttling this call.
-      return;
-    }
-
-    if (this._waitingForGetMutations || this._pendingMutations.length == 0) {
-      // Bail out if we already fired the new-mutation event or if no mutations are
-      // waiting to be retrieved.
-      return;
-    }
-
-    this._waitingForGetMutations = true;
-    this.emit("new-mutations");
-  },
-
-  /**
-   * Handles mutations from the DOM mutation observer API.
-   *
-   * @param array[MutationRecord] mutations
-   *    See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationRecord
-   */
-  onMutations: function (mutations) {
-    // Notify any observers that want *all* mutations (even on nodes that aren't
-    // referenced).  This is not sent over the protocol so can only be used by
-    // scripts running in the server process.
-    this.emit("any-mutation");
-
-    for (let change of mutations) {
-      let targetActor = this.getNode(change.target);
-      if (!targetActor) {
-        continue;
-      }
-      let targetNode = change.target;
-      let type = change.type;
-      let mutation = {
-        type: type,
-        target: targetActor.actorID,
-      };
-
-      if (type === "attributes") {
-        mutation.attributeName = change.attributeName;
-        mutation.attributeNamespace = change.attributeNamespace || undefined;
-        mutation.newValue = targetNode.hasAttribute(mutation.attributeName) ?
-                            targetNode.getAttribute(mutation.attributeName)
-                            : null;
-      } else if (type === "characterData") {
-        mutation.newValue = targetNode.nodeValue;
-        this._maybeQueueInlineTextChildMutation(change, targetNode);
-      } else if (type === "childList" || type === "nativeAnonymousChildList") {
-        // Get the list of removed and added actors that the client has seen
-        // so that it can keep its ownership tree up to date.
-        let removedActors = [];
-        let addedActors = [];
-        for (let removed of change.removedNodes) {
-          let removedActor = this.getNode(removed);
-          if (!removedActor) {
-            // If the client never encountered this actor we don't need to
-            // mention that it was removed.
-            continue;
-          }
-          // While removed from the tree, nodes are saved as orphaned.
-          this._orphaned.add(removedActor);
-          removedActors.push(removedActor.actorID);
-        }
-        for (let added of change.addedNodes) {
-          let addedActor = this.getNode(added);
-          if (!addedActor) {
-            // If the client never encounted this actor we don't need to tell
-            // it about its addition for ownership tree purposes - if the
-            // client wants to see the new nodes it can ask for children.
-            continue;
-          }
-          // The actor is reconnected to the ownership tree, unorphan
-          // it and let the client know so that its ownership tree is up
-          // to date.
-          this._orphaned.delete(addedActor);
-          addedActors.push(addedActor.actorID);
-        }
-
-        mutation.numChildren = targetActor.numChildren;
-        mutation.removed = removedActors;
-        mutation.added = addedActors;
-
-        let inlineTextChild = this.inlineTextChild(targetActor);
-        if (inlineTextChild) {
-          mutation.inlineTextChild = inlineTextChild.form();
-        }
-      }
-      this.queueMutation(mutation);
-    }
-  },
-
-  /**
-   * Check if the provided mutation could change the way the target element is
-   * inlined with its parent node. If it might, a custom mutation of type
-   * "inlineTextChild" will be queued.
-   *
-   * @param {MutationRecord} mutation
-   *        A characterData type mutation
-   */
-  _maybeQueueInlineTextChildMutation: function (mutation) {
-    let {oldValue, target} = mutation;
-    let newValue = target.nodeValue;
-    let limit = gValueSummaryLength;
-
-    if ((oldValue.length <= limit && newValue.length <= limit) ||
-        (oldValue.length > limit && newValue.length > limit)) {
-      // Bail out if the new & old values are both below/above the size limit.
-      return;
-    }
-
-    let parentActor = this.getNode(target.parentNode);
-    if (!parentActor || parentActor.rawNode.children.length > 0) {
-      // If the parent node has other children, a character data mutation will
-      // not change anything regarding inlining text nodes.
-      return;
-    }
-
-    let inlineTextChild = this.inlineTextChild(parentActor);
-    this.queueMutation({
-      type: "inlineTextChild",
-      target: parentActor.actorID,
-      inlineTextChild:
-        inlineTextChild ? inlineTextChild.form() : undefined
-    });
-  },
-
-  onFrameLoad: function ({ window, isTopLevel }) {
-    let { readyState } = window.document;
-    if (readyState != "interactive" && readyState != "complete") {
-      window.addEventListener("DOMContentLoaded",
-        this.onFrameLoad.bind(this, { window, isTopLevel }),
-        { once: true });
-      return;
-    }
-    if (isTopLevel) {
-      // If we initialize the inspector while the document is loading,
-      // we may already have a root document set in the constructor.
-      if (this.rootDoc && !Cu.isDeadWrapper(this.rootDoc) &&
-          this.rootDoc.defaultView) {
-        this.onFrameUnload({ window: this.rootDoc.defaultView });
-      }
-      // Update all DOM objects references to target the new document.
-      this.rootWin = window;
-      this.rootDoc = window.document;
-      this.rootNode = this.document();
-      this.queueMutation({
-        type: "newRoot",
-        target: this.rootNode.form()
-      });
-      return;
-    }
-    let frame = getFrameElement(window);
-    let frameActor = this.getNode(frame);
-    if (!frameActor) {
-      return;
-    }
-
-    this.queueMutation({
-      type: "frameLoad",
-      target: frameActor.actorID,
-    });
-
-    // Send a childList mutation on the frame.
-    this.queueMutation({
-      type: "childList",
-      target: frameActor.actorID,
-      added: [],
-      removed: []
-    });
-  },
-
-  // Returns true if domNode is in window or a subframe.
-  _childOfWindow: function (window, domNode) {
-    let win = nodeDocument(domNode).defaultView;
-    while (win) {
-      if (win === window) {
-        return true;
-      }
-      win = getFrameElement(win);
-    }
-    return false;
-  },
-
-  onFrameUnload: function ({ window }) {
-    // Any retained orphans that belong to this document
-    // or its children need to be released, and a mutation sent
-    // to notify of that.
-    let releasedOrphans = [];
-
-    for (let retained of this._retainedOrphans) {
-      if (Cu.isDeadWrapper(retained.rawNode) ||
-          this._childOfWindow(window, retained.rawNode)) {
-        this._retainedOrphans.delete(retained);
-        releasedOrphans.push(retained.actorID);
-        this.releaseNode(retained, { force: true });
-      }
-    }
-
-    if (releasedOrphans.length > 0) {
-      this.queueMutation({
-        target: this.rootNode.actorID,
-        type: "unretained",
-        nodes: releasedOrphans
-      });
-    }
-
-    let doc = window.document;
-    let documentActor = this.getNode(doc);
-    if (!documentActor) {
-      return;
-    }
-
-    if (this.rootDoc === doc) {
-      this.rootDoc = null;
-      this.rootNode = null;
-    }
-
-    this.queueMutation({
-      type: "documentUnload",
-      target: documentActor.actorID
-    });
-
-    let walker = this.getDocumentWalker(doc);
-    let parentNode = walker.parentNode();
-    if (parentNode) {
-      // Send a childList mutation on the frame so that clients know
-      // they should reread the children list.
-      this.queueMutation({
-        type: "childList",
-        target: this.getNode(parentNode).actorID,
-        added: [],
-        removed: []
-      });
-    }
-
-    // Need to force a release of this node, because those nodes can't
-    // be accessed anymore.
-    this.releaseNode(documentActor, { force: true });
-  },
-
-  /**
-   * Check if a node is attached to the DOM tree of the current page.
-   * @param {nsIDomNode} rawNode
-   * @return {Boolean} false if the node is removed from the tree or within a
-   * document fragment
-   */
-  _isInDOMTree: function (rawNode) {
-    let walker = this.getDocumentWalker(rawNode);
-    let current = walker.currentNode;
-
-    // Reaching the top of tree
-    while (walker.parentNode()) {
-      current = walker.currentNode;
-    }
-
-    // The top of the tree is a fragment or is not rootDoc, hence rawNode isn't
-    // attached
-    if (current.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE ||
-        current !== this.rootDoc) {
-      return false;
-    }
-
-    // Otherwise the top of the tree is rootDoc, hence rawNode is in rootDoc
-    return true;
-  },
-
-  /**
-   * @see _isInDomTree
-   */
-  isInDOMTree: function (node) {
-    if (isNodeDead(node)) {
-      return false;
-    }
-    return this._isInDOMTree(node.rawNode);
-  },
-
-  /**
-   * Given an ObjectActor (identified by its ID), commonly used in the debugger,
-   * webconsole and variablesView, return the corresponding inspector's
-   * NodeActor
-   */
-  getNodeActorFromObjectActor: function (objectActorID) {
-    let actor = this.conn.getActor(objectActorID);
-    if (!actor) {
-      return null;
-    }
-
-    let debuggerObject = this.conn.getActor(objectActorID).obj;
-    let rawNode = debuggerObject.unsafeDereference();
-
-    if (!this._isInDOMTree(rawNode)) {
-      return null;
-    }
-
-    // This is a special case for the document object whereby it is considered
-    // as document.documentElement (the <html> node)
-    if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
-      rawNode = rawNode.documentElement;
-    }
-
-    return this.attachElement(rawNode);
-  },
-
-  /**
-   * Given a windowID return the NodeActor for the corresponding frameElement,
-   * unless it's the root window
-   */
-  getNodeActorFromWindowID: function (windowID) {
-    let win;
-
-    try {
-      win = Services.wm.getOuterWindowWithId(windowID);
-    } catch (e) {
-      // ignore
-    }
-
-    if (!win) {
-      return { error: "noWindow",
-               message: "The related docshell is destroyed or not found" };
-    } else if (!win.frameElement) {
-      // the frame element of the root document is privileged & thus
-      // inaccessible, so return the document body/element instead
-      return this.attachElement(win.document.body || win.document.documentElement);
-    }
-
-    return this.attachElement(win.frameElement);
-  },
-
-  /**
-   * Given a StyleSheetActor (identified by its ID), commonly used in the
-   * style-editor, get its ownerNode and return the corresponding walker's
-   * NodeActor.
-   * Note that getNodeFromActor was added later and can now be used instead.
-   */
-  getStyleSheetOwnerNode: function (styleSheetActorID) {
-    return this.getNodeFromActor(styleSheetActorID, ["ownerNode"]);
-  },
-
-  /**
-   * This method can be used to retrieve NodeActor for DOM nodes from other
-   * actors in a way that they can later be highlighted in the page, or
-   * selected in the inspector.
-   * If an actor has a reference to a DOM node, and the UI needs to know about
-   * this DOM node (and possibly select it in the inspector), the UI should
-   * first retrieve a reference to the walkerFront:
-   *
-   * // Make sure the inspector/walker have been initialized first.
-   * toolbox.initInspector().then(() => {
-   *  // Retrieve the walker.
-   *  let walker = toolbox.walker;
-   * });
-   *
-   * And then call this method:
-   *
-   * // Get the nodeFront from my actor, passing the ID and properties path.
-   * walker.getNodeFromActor(myActorID, ["element"]).then(nodeFront => {
-   *   // Use the nodeFront, e.g. select the node in the inspector.
-   *   toolbox.getPanel("inspector").selection.setNodeFront(nodeFront);
-   * });
-   *
-   * @param {String} actorID The ID for the actor that has a reference to the
-   * DOM node.
-   * @param {Array} path Where, on the actor, is the DOM node stored. If in the
-   * scope of the actor, the node is available as `this.data.node`, then this
-   * should be ["data", "node"].
-   * @return {NodeActor} The attached NodeActor, or null if it couldn't be
-   * found.
-   */
-  getNodeFromActor: function (actorID, path) {
-    let actor = this.conn.getActor(actorID);
-    if (!actor) {
-      return null;
-    }
-
-    let obj = actor;
-    for (let name of path) {
-      if (!(name in obj)) {
-        return null;
-      }
-      obj = obj[name];
-    }
-
-    return this.attachElement(obj);
-  },
-
-  /**
-   * Returns an instance of the LayoutActor that is used to retrieve CSS layout-related
-   * information.
-   *
-   * @return {LayoutActor}
-   */
-  getLayoutInspector: function () {
-    if (!this.layoutActor) {
-      this.layoutActor = new LayoutActor(this.conn, this.tabActor, this);
-    }
-
-    return this.layoutActor;
-  },
-
-  /**
-   * Returns the offset parent DOMNode of the given node if it exists, otherwise, it
-   * returns null.
-   */
-  getOffsetParent: function (node) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let offsetParent = node.rawNode.offsetParent;
-
-    if (!offsetParent) {
-      return null;
-    }
-
-    return this._ref(offsetParent);
-  },
-});
 
 /**
  * Server side of the inspector actor, which is used to create
  * inspector-related actors, including the walker.
  */
 exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
@@ -2902,36 +197,36 @@ exports.InspectorActor = protocol.ActorC
    * is important as the resizing occurs server-side so that image-data being
    * transfered in the longstring back to the client will be that much smaller
    */
   getImageDataFromURL: function (url, maxDim) {
     let img = new this.window.Image();
     img.src = url;
 
     // imageToImageData waits for the image to load.
-    return imageToImageData(img, maxDim).then(imageData => {
+    return InspectorActorUtils.imageToImageData(img, maxDim).then(imageData => {
       return {
         data: LongStringActor(this.conn, imageData.data),
         size: imageData.size
       };
     });
   },
 
   /**
    * Resolve a URL to its absolute form, in the scope of a given content window.
    * @param {String} url.
    * @param {NodeActor} node If provided, the owner window of this node will be
    * used to resolve the URL. Otherwise, the top-level content window will be
    * used instead.
    * @return {String} url.
    */
   resolveRelativeURL: function (url, node) {
-    let document = isNodeDead(node)
+    let document = InspectorActorUtils.isNodeDead(node)
                    ? this.window.document
-                   : nodeDocument(node.rawNode);
+                   : InspectorActorUtils.nodeDocument(node.rawNode);
 
     if (!document) {
       return url;
     }
 
     let baseURI = Services.io.newURI(document.location.href);
     return Services.io.newURI(url, null, baseURI).spec;
   },
@@ -3014,389 +309,8 @@ exports.InspectorActor = protocol.ActorC
   _onColorPicked: function (e, color) {
     this.emit("color-picked", color);
   },
 
   _onColorPickCanceled: function () {
     this.emit("color-pick-canceled");
   }
 });
-
-// Exported for test purposes.
-exports._documentWalker = DocumentWalker;
-
-function nodeDocument(node) {
-  if (Cu.isDeadWrapper(node)) {
-    return null;
-  }
-  return node.ownerDocument ||
-         (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
-}
-
-function nodeDocshell(node) {
-  let doc = node ? nodeDocument(node) : null;
-  let win = doc ? doc.defaultView : null;
-  if (win) {
-    return win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDocShell);
-  }
-  return null;
-}
-
-function isNodeDead(node) {
-  return !node || !node.rawNode || Cu.isDeadWrapper(node.rawNode);
-}
-
-/**
- * Wrapper for inDeepTreeWalker.  Adds filtering to the traversal methods.
- * See inDeepTreeWalker for more information about the methods.
- *
- * @param {DOMNode} node
- * @param {Window} rootWin
- * @param {Number} whatToShow
- *        See nodeFilterConstants / inIDeepTreeWalker for options.
- * @param {Function} filter
- *        A custom filter function Taking in a DOMNode and returning an Int. See
- *        WalkerActor.nodeFilter for an example.
- * @param {String} skipTo
- *        Either SKIP_TO_PARENT or SKIP_TO_SIBLING. If the provided node is not compatible
- *        with the filter function for this walker, try to find a compatible one either
- *        in the parents or in the siblings of the node.
- */
-function DocumentWalker(node, rootWin,
-    whatToShow = nodeFilterConstants.SHOW_ALL,
-    filter = standardTreeWalkerFilter,
-    skipTo = SKIP_TO_PARENT) {
-  if (Cu.isDeadWrapper(rootWin) || !rootWin.location) {
-    throw new Error("Got an invalid root window in DocumentWalker");
-  }
-
-  this.walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
-    .createInstance(Ci.inIDeepTreeWalker);
-  this.walker.showAnonymousContent = true;
-  this.walker.showSubDocuments = true;
-  this.walker.showDocumentsAsNodes = true;
-  this.walker.init(rootWin.document, whatToShow);
-  this.filter = filter;
-
-  // Make sure that the walker knows about the initial node (which could
-  // be skipped due to a filter).
-  this.walker.currentNode = this.getStartingNode(node, skipTo);
-}
-
-DocumentWalker.prototype = {
-  get whatToShow() {
-    return this.walker.whatToShow;
-  },
-  get currentNode() {
-    return this.walker.currentNode;
-  },
-  set currentNode(val) {
-    this.walker.currentNode = val;
-  },
-
-  parentNode: function () {
-    return this.walker.parentNode();
-  },
-
-  nextNode: function () {
-    let node = this.walker.currentNode;
-    if (!node) {
-      return null;
-    }
-
-    let nextNode = this.walker.nextNode();
-    while (nextNode && this.isSkippedNode(nextNode)) {
-      nextNode = this.walker.nextNode();
-    }
-
-    return nextNode;
-  },
-
-  firstChild: function () {
-    let node = this.walker.currentNode;
-    if (!node) {
-      return null;
-    }
-
-    let firstChild = this.walker.firstChild();
-    while (firstChild && this.isSkippedNode(firstChild)) {
-      firstChild = this.walker.nextSibling();
-    }
-
-    return firstChild;
-  },
-
-  lastChild: function () {
-    let node = this.walker.currentNode;
-    if (!node) {
-      return null;
-    }
-
-    let lastChild = this.walker.lastChild();
-    while (lastChild && this.isSkippedNode(lastChild)) {
-      lastChild = this.walker.previousSibling();
-    }
-
-    return lastChild;
-  },
-
-  previousSibling: function () {
-    let node = this.walker.previousSibling();
-    while (node && this.isSkippedNode(node)) {
-      node = this.walker.previousSibling();
-    }
-    return node;
-  },
-
-  nextSibling: function () {
-    let node = this.walker.nextSibling();
-    while (node && this.isSkippedNode(node)) {
-      node = this.walker.nextSibling();
-    }
-    return node;
-  },
-
-  getStartingNode: function (node, skipTo) {
-    // Keep a reference on the starting node in case we can't find a node compatible with
-    // the filter.
-    let startingNode = node;
-
-    if (skipTo === SKIP_TO_PARENT) {
-      while (node && this.isSkippedNode(node)) {
-        node = node.parentNode;
-      }
-    } else if (skipTo === SKIP_TO_SIBLING) {
-      node = this.getClosestAcceptedSibling(node);
-    }
-
-    return node || startingNode;
-  },
-
-  /**
-   * Loop on all of the provided node siblings until finding one that is compliant with
-   * the filter function.
-   */
-  getClosestAcceptedSibling: function (node) {
-    if (this.filter(node) === nodeFilterConstants.FILTER_ACCEPT) {
-      // node is already valid, return immediately.
-      return node;
-    }
-
-    // Loop on starting node siblings.
-    let previous = node;
-    let next = node;
-    while (previous || next) {
-      previous = previous && previous.previousSibling;
-      next = next && next.nextSibling;
-
-      if (previous && this.filter(previous) === nodeFilterConstants.FILTER_ACCEPT) {
-        // A valid node was found in the previous siblings of the node.
-        return previous;
-      }
-
-      if (next && this.filter(next) === nodeFilterConstants.FILTER_ACCEPT) {
-        // A valid node was found in the next siblings of the node.
-        return next;
-      }
-    }
-
-    return null;
-  },
-
-  isSkippedNode: function (node) {
-    return this.filter(node) === nodeFilterConstants.FILTER_SKIP;
-  },
-};
-
-function isInXULDocument(el) {
-  let doc = nodeDocument(el);
-  return doc &&
-         doc.documentElement &&
-         doc.documentElement.namespaceURI === XUL_NS;
-}
-
-/**
- * This DeepTreeWalker filter skips whitespace text nodes and anonymous
- * content with the exception of ::before and ::after and anonymous content
- * in XUL document (needed to show all elements in the browser toolbox).
- */
-function standardTreeWalkerFilter(node) {
-  // ::before and ::after are native anonymous content, but we always
-  // want to show them
-  if (node.nodeName === "_moz_generated_content_before" ||
-      node.nodeName === "_moz_generated_content_after") {
-    return nodeFilterConstants.FILTER_ACCEPT;
-  }
-
-  // Ignore empty whitespace text nodes that do not impact the layout.
-  if (isWhitespaceTextNode(node)) {
-    return nodeHasSize(node)
-           ? nodeFilterConstants.FILTER_ACCEPT
-           : nodeFilterConstants.FILTER_SKIP;
-  }
-
-  // Ignore all native and XBL anonymous content inside a non-XUL document.
-  // We need to do this to skip things like form controls, scrollbars,
-  // video controls, etc (see bug 1187482).
-  if (!isInXULDocument(node) && (isXBLAnonymous(node) ||
-                                  isNativeAnonymous(node))) {
-    return nodeFilterConstants.FILTER_SKIP;
-  }
-
-  return nodeFilterConstants.FILTER_ACCEPT;
-}
-
-/**
- * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
- * it also includes all anonymous content (like internal form controls).
- */
-function allAnonymousContentTreeWalkerFilter(node) {
-  // Ignore empty whitespace text nodes that do not impact the layout.
-  if (isWhitespaceTextNode(node)) {
-    return nodeHasSize(node)
-           ? nodeFilterConstants.FILTER_ACCEPT
-           : nodeFilterConstants.FILTER_SKIP;
-  }
-  return nodeFilterConstants.FILTER_ACCEPT;
-}
-
-/**
- * Is the given node a text node composed of whitespace only?
- * @param {DOMNode} node
- * @return {Boolean}
- */
-function isWhitespaceTextNode(node) {
-  return node.nodeType == Ci.nsIDOMNode.TEXT_NODE && !/[^\s]/.exec(node.nodeValue);
-}
-
-/**
- * Does the given node have non-0 width and height?
- * @param {DOMNode} node
- * @return {Boolean}
- */
-function nodeHasSize(node) {
-  if (!node.getBoxQuads) {
-    return false;
-  }
-
-  let quads = node.getBoxQuads();
-  return quads.length && quads.some(quad => quad.bounds.width && quad.bounds.height);
-}
-
-/**
- * Returns a promise that is settled once the given HTMLImageElement has
- * finished loading.
- *
- * @param {HTMLImageElement} image - The image element.
- * @param {Number} timeout - Maximum amount of time the image is allowed to load
- * before the waiting is aborted. Ignored if flags.testing is set.
- *
- * @return {Promise} that is fulfilled once the image has loaded. If the image
- * fails to load or the load takes too long, the promise is rejected.
- */
-function ensureImageLoaded(image, timeout) {
-  let { HTMLImageElement } = image.ownerGlobal;
-  if (!(image instanceof HTMLImageElement)) {
-    return promise.reject("image must be an HTMLImageELement");
-  }
-
-  if (image.complete) {
-    // The image has already finished loading.
-    return promise.resolve();
-  }
-
-  // This image is still loading.
-  let onLoad = AsyncUtils.listenOnce(image, "load");
-
-  // Reject if loading fails.
-  let onError = AsyncUtils.listenOnce(image, "error").then(() => {
-    return promise.reject("Image '" + image.src + "' failed to load.");
-  });
-
-  // Don't timeout when testing. This is never settled.
-  let onAbort = new Promise(() => {});
-
-  if (!flags.testing) {
-    // Tests are not running. Reject the promise after given timeout.
-    onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
-      return promise.reject("Image '" + image.src + "' took too long to load.");
-    });
-  }
-
-  // See which happens first.
-  return promise.race([onLoad, onError, onAbort]);
-}
-
-/**
- * Given an <img> or <canvas> element, return the image data-uri. If @param node
- * is an <img> element, the method waits a while for the image to load before
- * the data is generated. If the image does not finish loading in a reasonable
- * time (IMAGE_FETCHING_TIMEOUT milliseconds) the process aborts.
- *
- * @param {HTMLImageElement|HTMLCanvasElement} node - The <img> or <canvas>
- * element, or Image() object. Other types cause the method to reject.
- * @param {Number} maxDim - Optionally pass a maximum size you want the longest
- * side of the image to be resized to before getting the image data.
-
- * @return {Promise} A promise that is fulfilled with an object containing the
- * data-uri and size-related information:
- * { data: "...",
- *   size: {
- *     naturalWidth: 400,
- *     naturalHeight: 300,
- *     resized: true }
- *  }.
- *
- * If something goes wrong, the promise is rejected.
- */
-var imageToImageData = Task.async(function* (node, maxDim) {
-  let { HTMLCanvasElement, HTMLImageElement } = node.ownerGlobal;
-
-  let isImg = node instanceof HTMLImageElement;
-  let isCanvas = node instanceof HTMLCanvasElement;
-
-  if (!isImg && !isCanvas) {
-    throw new Error("node is not a <canvas> or <img> element.");
-  }
-
-  if (isImg) {
-    // Ensure that the image is ready.
-    yield ensureImageLoaded(node, IMAGE_FETCHING_TIMEOUT);
-  }
-
-  // Get the image resize ratio if a maxDim was provided
-  let resizeRatio = 1;
-  let imgWidth = node.naturalWidth || node.width;
-  let imgHeight = node.naturalHeight || node.height;
-  let imgMax = Math.max(imgWidth, imgHeight);
-  if (maxDim && imgMax > maxDim) {
-    resizeRatio = maxDim / imgMax;
-  }
-
-  // Extract the image data
-  let imageData;
-  // The image may already be a data-uri, in which case, save ourselves the
-  // trouble of converting via the canvas.drawImage.toDataURL method, but only
-  // if the image doesn't need resizing
-  if (isImg && node.src.startsWith("data:") && resizeRatio === 1) {
-    imageData = node.src;
-  } else {
-    // Create a canvas to copy the rawNode into and get the imageData from
-    let canvas = node.ownerDocument.createElementNS(XHTML_NS, "canvas");
-    canvas.width = imgWidth * resizeRatio;
-    canvas.height = imgHeight * resizeRatio;
-    let ctx = canvas.getContext("2d");
-
-    // Copy the rawNode image or canvas in the new canvas and extract data
-    ctx.drawImage(node, 0, 0, canvas.width, canvas.height);
-    imageData = canvas.toDataURL("image/png");
-  }
-
-  return {
-    data: imageData,
-    size: {
-      naturalWidth: imgWidth,
-      naturalHeight: imgHeight,
-      resized: resizeRatio !== 1
-    }
-  };
-});
--- a/devtools/server/actors/inspector/moz.build
+++ b/devtools/server/actors/inspector/moz.build
@@ -1,12 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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(
-    'inspector.js',
+  'document-walker.js',
+  'inspector.js',
+  'node-actor.js',
+  'utils.js',
+  'walker-actor.js',
 )
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools: Inspector')
copy from devtools/server/actors/inspector/inspector.js
copy to devtools/server/actors/inspector/node-actor.js
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/node-actor.js
@@ -1,213 +1,48 @@
 /* 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";
 
-/**
- * Here's the server side of the remote inspector.
- *
- * The WalkerActor is the client's view of the debuggee's DOM.  It's gives
- * the client a tree of NodeActor objects.
- *
- * The walker presents the DOM tree mostly unmodified from the source DOM
- * tree, but with a few key differences:
- *
- *  - Empty text nodes are ignored.  This is pretty typical of developer
- *    tools, but maybe we should reconsider that on the server side.
- *  - iframes with documents loaded have the loaded document as the child,
- *    the walker provides one big tree for the whole document tree.
- *
- * There are a few ways to get references to NodeActors:
- *
- *   - When you first get a WalkerActor reference, it comes with a free
- *     reference to the root document's node.
- *   - Given a node, you can ask for children, siblings, and parents.
- *   - You can issue querySelector and querySelectorAll requests to find
- *     other elements.
- *   - Requests that return arbitrary nodes from the tree (like querySelector
- *     and querySelectorAll) will also return any nodes the client hasn't
- *     seen in order to have a complete set of parents.
- *
- * Once you have a NodeFront, you should be able to answer a few questions
- * without further round trips, like the node's name, namespace/tagName,
- * attributes, etc.  Other questions (like a text node's full nodeValue)
- * might require another round trip.
- *
- * The protocol guarantees that the client will always know the parent of
- * any node that is returned by the server.  This means that some requests
- * (like querySelector) will include the extra nodes needed to satisfy this
- * requirement.  The client keeps track of this parent relationship, so the
- * node fronts form a tree that is a subset of the actual DOM tree.
- *
- *
- * We maintain this guarantee to support the ability to release subtrees on
- * the client - when a node is disconnected from the DOM tree we want to be
- * able to free the client objects for all the children nodes.
- *
- * So to be able to answer "all the children of a given node that we have
- * seen on the client side", we guarantee that every time we've seen a node,
- * we connect it up through its parents.
- */
+const {Ci, Cu} = require("chrome");
 
-const {Cc, Ci, Cu} = require("chrome");
-const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
-const {LongStringActor} = require("devtools/server/actors/string");
-const promise = require("promise");
-const defer = require("devtools/shared/defer");
-const {Task} = require("devtools/shared/task");
-const EventEmitter = require("devtools/shared/event-emitter");
+const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");
+
 const InspectorUtils = require("InspectorUtils");
 
-const {walkerSpec, inspectorSpec} = require("devtools/shared/specs/inspector");
-const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");
+loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
 
-loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
-loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
-loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
-loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
 loader.lazyRequireGetter(this, "getCssPath", "devtools/shared/inspector/css-logic", true);
 loader.lazyRequireGetter(this, "getXPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
-loader.lazyRequireGetter(this, "EyeDropper", "devtools/server/actors/highlighters/eye-dropper", true);
-loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);
-loader.lazyRequireGetter(this, "PageStyleActor", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "getFontPreviewData", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
-loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
-loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
-loader.lazyRequireGetter(this, "HighlighterActor", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "HighlighterEnvironment", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "EventParsers", "devtools/server/event-parsers", true);
-loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
+loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
+
 loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isXBLAnonymous", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isShadowAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getFrameElement", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");
+loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
 
-loader.lazyServiceGetter(this, "DOMParser",
-  "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
+loader.lazyRequireGetter(this, "InspectorActorUtils", "devtools/server/actors/inspector/utils");
+loader.lazyRequireGetter(this, "LongStringActor", "devtools/server/actors/string", true);
+loader.lazyRequireGetter(this, "getFontPreviewData", "devtools/server/actors/styles", true);
+loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
+loader.lazyRequireGetter(this, "EventParsers", "devtools/server/event-parsers", true);
 
-loader.lazyServiceGetter(this, "eventListenerService",
-  "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
+const EventEmitter = require("devtools/shared/event-emitter");
 
+const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
 const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
-const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
-const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
-const SVG_NS = "http://www.w3.org/2000/svg";
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-const IMAGE_FETCHING_TIMEOUT = 500;
-
-// Minimum delay between two "new-mutations" events.
-const MUTATIONS_THROTTLING_DELAY = 100;
-// List of mutation types that should -not- be throttled.
-const IMMEDIATE_MUTATIONS = [
-  "documentUnload",
-  "frameLoad",
-  "newRoot",
-  "pseudoClassLock",
-];
-
-// SKIP_TO_* arguments are used with the DocumentWalker, driving the strategy to use if
-// the starting node is incompatible with the filter function of the walker.
-const SKIP_TO_PARENT = "SKIP_TO_PARENT";
-const SKIP_TO_SIBLING = "SKIP_TO_SIBLING";
-
-// The possible completions to a ':' with added score to give certain values
-// some preference.
-const PSEUDO_SELECTORS = [
-  [":active", 1],
-  [":hover", 1],
-  [":focus", 1],
-  [":visited", 0],
-  [":link", 0],
-  [":first-letter", 0],
-  [":first-child", 2],
-  [":before", 2],
-  [":after", 2],
-  [":lang(", 0],
-  [":not(", 3],
-  [":first-of-type", 0],
-  [":last-of-type", 0],
-  [":only-of-type", 0],
-  [":only-child", 2],
-  [":nth-child(", 3],
-  [":nth-last-child(", 0],
-  [":nth-of-type(", 0],
-  [":nth-last-of-type(", 0],
-  [":last-child", 2],
-  [":root", 0],
-  [":empty", 0],
-  [":target", 0],
-  [":enabled", 0],
-  [":disabled", 0],
-  [":checked", 1],
-  ["::selection", 0]
-];
-
-var HELPER_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
-  .__fx-devtools-hide-shortcut__ {
-    visibility: hidden !important;
-  }
-
-  :-moz-devtools-highlighted {
-    outline: 2px dashed #F06!important;
-    outline-offset: -2px !important;
-  }
-`);
-
-/**
- * We only send nodeValue up to a certain size by default.  This stuff
- * controls that size.
- */
-exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50;
-var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH;
-
-exports.getValueSummaryLength = function () {
-  return gValueSummaryLength;
-};
-
-exports.setValueSummaryLength = function (val) {
-  gValueSummaryLength = val;
-};
-
-/**
- * Returns the properly cased version of the node's tag name, which can be
- * used when displaying said name in the UI.
- *
- * @param  {Node} rawNode
- *         Node for which we want the display name
- * @return {String}
- *         Properly cased version of the node tag name
- */
-const getNodeDisplayName = function (rawNode) {
-  if (rawNode.nodeName && !rawNode.localName) {
-    // The localName & prefix APIs have been moved from the Node interface to the Element
-    // interface. Use Node.nodeName as a fallback.
-    return rawNode.nodeName;
-  }
-  return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
-};
-exports.getNodeDisplayName = getNodeDisplayName;
 
 /**
  * Server side of the node actor.
  */
-var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
+const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
   initialize: function (walker, node) {
     protocol.Actor.prototype.initialize.call(this, null);
     this.walker = walker;
     this.rawNode = node;
     this._eventParsers = new EventParsers().parsers;
 
     // Storing the original display of the node, to track changes when reflows
     // occur
@@ -257,17 +92,17 @@ var NodeActor = exports.NodeActor = prot
     let form = {
       actor: this.actorID,
       baseURI: this.rawNode.baseURI,
       parent: parentNode ? parentNode.actorID : undefined,
       nodeType: this.rawNode.nodeType,
       namespaceURI: this.rawNode.namespaceURI,
       nodeName: this.rawNode.nodeName,
       nodeValue: this.rawNode.nodeValue,
-      displayName: getNodeDisplayName(this.rawNode),
+      displayName: InspectorActorUtils.getNodeDisplayName(this.rawNode),
       numChildren: this.numChildren,
       inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
 
       // doctype attributes
       name: this.rawNode.name,
       publicId: this.rawNode.publicId,
       systemId: this.rawNode.systemId,
 
@@ -372,17 +207,17 @@ var NodeActor = exports.NodeActor = prot
     return CssLogic.getComputedStyle(this.rawNode);
   },
 
   /**
    * Is the node's display computed style value other than "none"
    */
   get isDisplayed() {
     // Consider all non-element nodes as displayed.
-    if (isNodeDead(this) ||
+    if (InspectorActorUtils.isNodeDead(this) ||
         this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
         this.isAfterPseudoElement ||
         this.isBeforePseudoElement) {
       return true;
     }
 
     let style = this.computedStyle;
     if (!style) {
@@ -696,17 +531,17 @@ var NodeActor = exports.NodeActor = prot
    * The image data is transmitted as a base64 encoded png data-uri.
    * The method rejects if the node isn't an image or if the image is missing
    *
    * Accepts a maxDim request parameter to resize images that are larger. This
    * is important as the resizing occurs server-side so that image-data being
    * transfered in the longstring back to the client will be that much smaller
    */
   getImageData: function (maxDim) {
-    return imageToImageData(this.rawNode, maxDim).then(imageData => {
+    return InspectorActorUtils.imageToImageData(this.rawNode, maxDim).then(imageData => {
       return {
         data: LongStringActor(this.conn, imageData.data),
         size: imageData.size
       };
     });
   },
 
   /**
@@ -798,17 +633,17 @@ var NodeActor = exports.NodeActor = prot
     }
     return "rgba(255, 255, 255, 1)";
   }
 });
 
 /**
  * Server side of a node list as returned by querySelectorAll()
  */
-var NodeListActor = exports.NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
+const NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
   typeName: "domnodelist",
 
   initialize: function (walker, nodeList) {
     protocol.Actor.prototype.initialize.call(this);
     this.walker = walker;
     this.nodeList = nodeList || [];
   },
 
@@ -853,2550 +688,10 @@ var NodeListActor = exports.NodeListActo
     let items = Array.prototype.slice.call(this.nodeList, start, end)
       .map(item => this.walker._ref(item));
     return this.walker.attachElements(items);
   },
 
   release: function () {}
 });
 
-/**
- * Server side of the DOM walker.
- */
-var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
-  /**
-   * Create the WalkerActor
-   * @param DebuggerServerConnection conn
-   *    The server connection.
-   */
-  initialize: function (conn, tabActor, options) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this.tabActor = tabActor;
-    this.rootWin = tabActor.window;
-    this.rootDoc = this.rootWin.document;
-    this._refMap = new Map();
-    this._pendingMutations = [];
-    this._activePseudoClassLocks = new Set();
-    this.showAllAnonymousContent = options.showAllAnonymousContent;
-
-    this.walkerSearch = new WalkerSearch(this);
-
-    // Nodes which have been removed from the client's known
-    // ownership tree are considered "orphaned", and stored in
-    // this set.
-    this._orphaned = new Set();
-
-    // The client can tell the walker that it is interested in a node
-    // even when it is orphaned with the `retainNode` method.  This
-    // list contains orphaned nodes that were so retained.
-    this._retainedOrphans = new Set();
-
-    this.onMutations = this.onMutations.bind(this);
-    this.onFrameLoad = this.onFrameLoad.bind(this);
-    this.onFrameUnload = this.onFrameUnload.bind(this);
-    this._throttledEmitNewMutations = throttle(this._emitNewMutations.bind(this),
-      MUTATIONS_THROTTLING_DELAY);
-
-    tabActor.on("will-navigate", this.onFrameUnload);
-    tabActor.on("window-ready", this.onFrameLoad);
-
-    // Ensure that the root document node actor is ready and
-    // managed.
-    this.rootNode = this.document();
-
-    this.layoutChangeObserver = getLayoutChangesObserver(this.tabActor);
-    this._onReflows = this._onReflows.bind(this);
-    this.layoutChangeObserver.on("reflows", this._onReflows);
-    this._onResize = this._onResize.bind(this);
-    this.layoutChangeObserver.on("resize", this._onResize);
-
-    this._onEventListenerChange = this._onEventListenerChange.bind(this);
-    eventListenerService.addListenerChangeListener(this._onEventListenerChange);
-  },
-
-  /**
-   * Callback for eventListenerService.addListenerChangeListener
-   * @param nsISimpleEnumerator changesEnum
-   *    enumerator of nsIEventListenerChange
-   */
-  _onEventListenerChange: function (changesEnum) {
-    let changes = changesEnum.enumerate();
-    while (changes.hasMoreElements()) {
-      let current = changes.getNext().QueryInterface(Ci.nsIEventListenerChange);
-      let target = current.target;
-
-      if (this._refMap.has(target)) {
-        let actor = this.getNode(target);
-        let mutation = {
-          type: "events",
-          target: actor.actorID,
-          hasEventListeners: actor._hasEventListeners
-        };
-        this.queueMutation(mutation);
-      }
-    }
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function () {
-    return {
-      actor: this.actorID,
-      root: this.rootNode.form(),
-      traits: {
-        // FF42+ Inspector starts managing the Walker, while the inspector also
-        // starts cleaning itself up automatically on client disconnection.
-        // So that there is no need to manually release the walker anymore.
-        autoReleased: true,
-        // XXX: It seems silly that we need to tell the front which capabilities
-        // its actor has in this way when the target can use actorHasMethod. If
-        // this was ported to the protocol (Bug 1157048) we could call that
-        // inside of custom front methods and not need to do traits for this.
-        multiFrameQuerySelectorAll: true,
-        textSearch: true,
-      }
-    };
-  },
-
-  toString: function () {
-    return "[WalkerActor " + this.actorID + "]";
-  },
-
-  getDocumentWalker: function (node, whatToShow, skipTo) {
-    // Allow native anon content (like <video> controls) if preffed on
-    let nodeFilter = this.showAllAnonymousContent
-                    ? allAnonymousContentTreeWalkerFilter
-                    : standardTreeWalkerFilter;
-    return new DocumentWalker(node, this.rootWin, whatToShow, nodeFilter, skipTo);
-  },
-
-  destroy: function () {
-    if (this._destroyed) {
-      return;
-    }
-    this._destroyed = true;
-    protocol.Actor.prototype.destroy.call(this);
-    try {
-      this.clearPseudoClassLocks();
-      this._activePseudoClassLocks = null;
-
-      this._hoveredNode = null;
-      this.rootWin = null;
-      this.rootDoc = null;
-      this.rootNode = null;
-      this.layoutHelpers = null;
-      this._orphaned = null;
-      this._retainedOrphans = null;
-      this._refMap = null;
-
-      this.tabActor.off("will-navigate", this.onFrameUnload);
-      this.tabActor.off("window-ready", this.onFrameLoad);
-
-      this.onFrameLoad = null;
-      this.onFrameUnload = null;
-
-      this.walkerSearch.destroy();
-
-      this.layoutChangeObserver.off("reflows", this._onReflows);
-      this.layoutChangeObserver.off("resize", this._onResize);
-      this.layoutChangeObserver = null;
-      releaseLayoutChangesObserver(this.tabActor);
-
-      eventListenerService.removeListenerChangeListener(
-        this._onEventListenerChange);
-
-      this.onMutations = null;
-
-      this.layoutActor = null;
-      this.tabActor = null;
-
-      this.emit("destroyed");
-    } catch (e) {
-      console.error(e);
-    }
-  },
-
-  release: function () {},
-
-  unmanage: function (actor) {
-    if (actor instanceof NodeActor) {
-      if (this._activePseudoClassLocks &&
-          this._activePseudoClassLocks.has(actor)) {
-        this.clearPseudoClassLocks(actor);
-      }
-      this._refMap.delete(actor.rawNode);
-    }
-    protocol.Actor.prototype.unmanage.call(this, actor);
-  },
-
-  /**
-   * Determine if the walker has come across this DOM node before.
-   * @param {DOMNode} rawNode
-   * @return {Boolean}
-   */
-  hasNode: function (rawNode) {
-    return this._refMap.has(rawNode);
-  },
-
-  /**
-   * If the walker has come across this DOM node before, then get the
-   * corresponding node actor.
-   * @param {DOMNode} rawNode
-   * @return {NodeActor}
-   */
-  getNode: function (rawNode) {
-    return this._refMap.get(rawNode);
-  },
-
-  _ref: function (node) {
-    let actor = this.getNode(node);
-    if (actor) {
-      return actor;
-    }
-
-    actor = new NodeActor(this, node);
-
-    // Add the node actor as a child of this walker actor, assigning
-    // it an actorID.
-    this.manage(actor);
-    this._refMap.set(node, actor);
-
-    if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
-      actor.watchDocument(this.onMutations);
-    }
-    return actor;
-  },
-
-  _onReflows: function (reflows) {
-    // Going through the nodes the walker knows about, see which ones have
-    // had their display changed and send a display-change event if any
-    let changes = [];
-    for (let [node, actor] of this._refMap) {
-      if (Cu.isDeadWrapper(node)) {
-        continue;
-      }
-
-      let isDisplayed = actor.isDisplayed;
-      if (isDisplayed !== actor.wasDisplayed) {
-        changes.push(actor);
-        // Updating the original value
-        actor.wasDisplayed = isDisplayed;
-      }
-    }
-
-    if (changes.length) {
-      this.emit("display-change", changes);
-    }
-  },
-
-  /**
-   * When the browser window gets resized, relay the event to the front.
-   */
-  _onResize: function () {
-    this.emit("resize");
-  },
-
-  /**
-   * This is kept for backward-compatibility reasons with older remote targets.
-   * Targets prior to bug 916443.
-   *
-   * pick/cancelPick are used to pick a node on click on the content
-   * document. But in their implementation prior to bug 916443, they don't allow
-   * highlighting on hover.
-   * The client-side now uses the highlighter actor's pick and cancelPick
-   * methods instead. The client-side uses the the highlightable trait found in
-   * the root actor to determine which version of pick to use.
-   *
-   * As for highlight, the new highlighter actor is used instead of the walker's
-   * highlight method. Same here though, the client-side uses the highlightable
-   * trait to dertermine which to use.
-   *
-   * Keeping these actor methods for now allows newer client-side debuggers to
-   * inspect fxos 1.2 remote targets or older firefox desktop remote targets.
-   */
-  pick: function () {},
-  cancelPick: function () {},
-  highlight: function (node) {},
-
-  /**
-   * Ensures that the node is attached and it can be accessed from the root.
-   *
-   * @param {(Node|NodeActor)} nodes The nodes
-   * @return {Object} An object compatible with the disconnectedNode type.
-   */
-  attachElement: function (node) {
-    let { nodes, newParents } = this.attachElements([node]);
-    return {
-      node: nodes[0],
-      newParents: newParents
-    };
-  },
-
-  /**
-   * Ensures that the nodes are attached and they can be accessed from the root.
-   *
-   * @param {(Node[]|NodeActor[])} nodes The nodes
-   * @return {Object} An object compatible with the disconnectedNodeArray type.
-   */
-  attachElements: function (nodes) {
-    let nodeActors = [];
-    let newParents = new Set();
-    for (let node of nodes) {
-      if (!(node instanceof NodeActor)) {
-        // If an anonymous node was passed in and we aren't supposed to know
-        // about it, then consult with the document walker as the source of
-        // truth about which elements exist.
-        if (!this.showAllAnonymousContent && isAnonymous(node)) {
-          node = this.getDocumentWalker(node).currentNode;
-        }
-
-        node = this._ref(node);
-      }
-
-      this.ensurePathToRoot(node, newParents);
-      // If nodes may be an array of raw nodes, we're sure to only have
-      // NodeActors with the following array.
-      nodeActors.push(node);
-    }
-
-    return {
-      nodes: nodeActors,
-      newParents: [...newParents]
-    };
-  },
-
-  /**
-   * Return the document node that contains the given node,
-   * or the root node if no node is specified.
-   * @param NodeActor node
-   *        The node whose document is needed, or null to
-   *        return the root.
-   */
-  document: function (node) {
-    let doc = isNodeDead(node) ? this.rootDoc : nodeDocument(node.rawNode);
-    return this._ref(doc);
-  },
-
-  /**
-   * Return the documentElement for the document containing the
-   * given node.
-   * @param NodeActor node
-   *        The node whose documentElement is requested, or null
-   *        to use the root document.
-   */
-  documentElement: function (node) {
-    let elt = isNodeDead(node)
-              ? this.rootDoc.documentElement
-              : nodeDocument(node.rawNode).documentElement;
-    return this._ref(elt);
-  },
-
-  /**
-   * Return all parents of the given node, ordered from immediate parent
-   * to root.
-   * @param NodeActor node
-   *    The node whose parents are requested.
-   * @param object options
-   *    Named options, including:
-   *    `sameDocument`: If true, parents will be restricted to the same
-   *      document as the node.
-   *    `sameTypeRootTreeItem`: If true, this will not traverse across
-   *     different types of docshells.
-   */
-  parents: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return [];
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let parents = [];
-    let cur;
-    while ((cur = walker.parentNode())) {
-      if (options.sameDocument &&
-          nodeDocument(cur) != nodeDocument(node.rawNode)) {
-        break;
-      }
-
-      if (options.sameTypeRootTreeItem &&
-          nodeDocshell(cur).sameTypeRootTreeItem !=
-          nodeDocshell(node.rawNode).sameTypeRootTreeItem) {
-        break;
-      }
-
-      parents.push(this._ref(cur));
-    }
-    return parents;
-  },
-
-  parentNode: function (node) {
-    let walker = this.getDocumentWalker(node.rawNode);
-    let parent = walker.parentNode();
-    if (parent) {
-      return this._ref(parent);
-    }
-    return null;
-  },
-
-  /**
-   * If the given NodeActor only has a single text node as a child with a text
-   * content small enough to be inlined, return that child's NodeActor.
-   *
-   * @param NodeActor node
-   */
-  inlineTextChild: function (node) {
-    // Quick checks to prevent creating a new walker if possible.
-    if (node.isBeforePseudoElement ||
-        node.isAfterPseudoElement ||
-        node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
-        node.rawNode.children.length > 0) {
-      return undefined;
-    }
-
-    let docWalker = this.getDocumentWalker(node.rawNode);
-    let firstChild = docWalker.firstChild();
-
-    // Bail out if:
-    // - more than one child
-    // - unique child is not a text node
-    // - unique child is a text node, but is too long to be inlined
-    if (!firstChild ||
-        docWalker.nextSibling() ||
-        firstChild.nodeType !== Ci.nsIDOMNode.TEXT_NODE ||
-        firstChild.nodeValue.length > gValueSummaryLength
-        ) {
-      return undefined;
-    }
-
-    return this._ref(firstChild);
-  },
-
-  /**
-   * Mark a node as 'retained'.
-   *
-   * A retained node is not released when `releaseNode` is called on its
-   * parent, or when a parent is released with the `cleanup` option to
-   * `getMutations`.
-   *
-   * When a retained node's parent is released, a retained mode is added to
-   * the walker's "retained orphans" list.
-   *
-   * Retained nodes can be deleted by providing the `force` option to
-   * `releaseNode`.  They will also be released when their document
-   * has been destroyed.
-   *
-   * Retaining a node makes no promise about its children;  They can
-   * still be removed by normal means.
-   */
-  retainNode: function (node) {
-    node.retained = true;
-  },
-
-  /**
-   * Remove the 'retained' mark from a node.  If the node was a
-   * retained orphan, release it.
-   */
-  unretainNode: function (node) {
-    node.retained = false;
-    if (this._retainedOrphans.has(node)) {
-      this._retainedOrphans.delete(node);
-      this.releaseNode(node);
-    }
-  },
-
-  /**
-   * Release actors for a node and all child nodes.
-   */
-  releaseNode: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    if (node.retained && !options.force) {
-      this._retainedOrphans.add(node);
-      return;
-    }
-
-    if (node.retained) {
-      // Forcing a retained node to go away.
-      this._retainedOrphans.delete(node);
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-
-    let child = walker.firstChild();
-    while (child) {
-      let childActor = this.getNode(child);
-      if (childActor) {
-        this.releaseNode(childActor, options);
-      }
-      child = walker.nextSibling();
-    }
-
-    node.destroy();
-  },
-
-  /**
-   * Add any nodes between `node` and the walker's root node that have not
-   * yet been seen by the client.
-   */
-  ensurePathToRoot: function (node, newParents = new Set()) {
-    if (!node) {
-      return newParents;
-    }
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let parent = this.getNode(cur);
-      if (!parent) {
-        // This parent didn't exist, so hasn't been seen by the client yet.
-        newParents.add(this._ref(cur));
-      } else {
-        // This parent did exist, so the client knows about it.
-        return newParents;
-      }
-    }
-    return newParents;
-  },
-
-  /**
-   * Return children of the given node.  By default this method will return
-   * all children of the node, but there are options that can restrict this
-   * to a more manageable subset.
-   *
-   * @param NodeActor node
-   *    The node whose children you're curious about.
-   * @param object options
-   *    Named options:
-   *    `maxNodes`: The set of nodes returned by the method will be no longer
-   *       than maxNodes.
-   *    `start`: If a node is specified, the list of nodes will start
-   *       with the given child.  Mutally exclusive with `center`.
-   *    `center`: If a node is specified, the given node will be as centered
-   *       as possible in the list, given how close to the ends of the child
-   *       list it is.  Mutually exclusive with `start`.
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   *
-   * @returns an object with three items:
-   *    hasFirst: true if the first child of the node is included in the list.
-   *    hasLast: true if the last child of the node is included in the list.
-   *    nodes: Child nodes returned by the request.
-   */
-  children: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    if (options.center && options.start) {
-      throw Error("Can't specify both 'center' and 'start' options.");
-    }
-    let maxNodes = options.maxNodes || -1;
-    if (maxNodes == -1) {
-      maxNodes = Number.MAX_VALUE;
-    }
-
-    // We're going to create a few document walkers with the same filter,
-    // make it easier.
-    let getFilteredWalker = documentWalkerNode => {
-      let { whatToShow } = options;
-      // Use SKIP_TO_SIBLING to force the walker to use a sibling of the provided node
-      // in case this one is incompatible with the walker's filter function.
-      return this.getDocumentWalker(documentWalkerNode, whatToShow, SKIP_TO_SIBLING);
-    };
-
-    // Need to know the first and last child.
-    let rawNode = node.rawNode;
-    let firstChild = getFilteredWalker(rawNode).firstChild();
-    let lastChild = getFilteredWalker(rawNode).lastChild();
-
-    if (!firstChild) {
-      // No children, we're done.
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    let start;
-    if (options.center) {
-      start = options.center.rawNode;
-    } else if (options.start) {
-      start = options.start.rawNode;
-    } else {
-      start = firstChild;
-    }
-
-    let nodes = [];
-
-    // Start by reading backward from the starting point if we're centering...
-    let backwardWalker = getFilteredWalker(start);
-    if (backwardWalker.currentNode != firstChild && options.center) {
-      backwardWalker.previousSibling();
-      let backwardCount = Math.floor(maxNodes / 2);
-      let backwardNodes = this._readBackward(backwardWalker, backwardCount);
-      nodes = backwardNodes;
-    }
-
-    // Then read forward by any slack left in the max children...
-    let forwardWalker = getFilteredWalker(start);
-    let forwardCount = maxNodes - nodes.length;
-    nodes = nodes.concat(this._readForward(forwardWalker, forwardCount));
-
-    // If there's any room left, it means we've run all the way to the end.
-    // If we're centering, check if there are more items to read at the front.
-    let remaining = maxNodes - nodes.length;
-    if (options.center && remaining > 0 && nodes[0].rawNode != firstChild) {
-      let firstNodes = this._readBackward(backwardWalker, remaining);
-
-      // Then put it all back together.
-      nodes = firstNodes.concat(nodes);
-    }
-
-    return {
-      hasFirst: nodes[0].rawNode == firstChild,
-      hasLast: nodes[nodes.length - 1].rawNode == lastChild,
-      nodes: nodes
-    };
-  },
-
-  /**
-   * Return siblings of the given node.  By default this method will return
-   * all siblings of the node, but there are options that can restrict this
-   * to a more manageable subset.
-   *
-   * If `start` or `center` are not specified, this method will center on the
-   * node whose siblings are requested.
-   *
-   * @param NodeActor node
-   *    The node whose children you're curious about.
-   * @param object options
-   *    Named options:
-   *    `maxNodes`: The set of nodes returned by the method will be no longer
-   *       than maxNodes.
-   *    `start`: If a node is specified, the list of nodes will start
-   *       with the given child.  Mutally exclusive with `center`.
-   *    `center`: If a node is specified, the given node will be as centered
-   *       as possible in the list, given how close to the ends of the child
-   *       list it is.  Mutually exclusive with `start`.
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   *
-   * @returns an object with three items:
-   *    hasFirst: true if the first child of the node is included in the list.
-   *    hasLast: true if the last child of the node is included in the list.
-   *    nodes: Child nodes returned by the request.
-   */
-  siblings: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return { hasFirst: true, hasLast: true, nodes: [] };
-    }
-
-    let parentNode = this.getDocumentWalker(node.rawNode, options.whatToShow)
-                         .parentNode();
-    if (!parentNode) {
-      return {
-        hasFirst: true,
-        hasLast: true,
-        nodes: [node]
-      };
-    }
-
-    if (!(options.start || options.center)) {
-      options.center = node;
-    }
-
-    return this.children(this._ref(parentNode), options);
-  },
-
-  /**
-   * Get the next sibling of a given node.  Getting nodes one at a time
-   * might be inefficient, be careful.
-   *
-   * @param object options
-   *    Named options:
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   */
-  nextSibling: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
-    let sibling = walker.nextSibling();
-    return sibling ? this._ref(sibling) : null;
-  },
-
-  /**
-   * Get the previous sibling of a given node.  Getting nodes one at a time
-   * might be inefficient, be careful.
-   *
-   * @param object options
-   *    Named options:
-   *    `whatToShow`: A bitmask of node types that should be included.  See
-   *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
-   */
-  previousSibling: function (node, options = {}) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode, options.whatToShow);
-    let sibling = walker.previousSibling();
-    return sibling ? this._ref(sibling) : null;
-  },
-
-  /**
-   * Helper function for the `children` method: Read forward in the sibling
-   * list into an array with `count` items, including the current node.
-   */
-  _readForward: function (walker, count) {
-    let ret = [];
-
-    let node = walker.currentNode;
-    do {
-      if (!walker.isSkippedNode(node)) {
-        // The walker can be on a node that would be filtered out if it didn't find any
-        // other node to fallback to.
-        ret.push(this._ref(node));
-      }
-      node = walker.nextSibling();
-    } while (node && --count);
-    return ret;
-  },
-
-  /**
-   * Helper function for the `children` method: Read backward in the sibling
-   * list into an array with `count` items, including the current node.
-   */
-  _readBackward: function (walker, count) {
-    let ret = [];
-
-    let node = walker.currentNode;
-    do {
-      if (!walker.isSkippedNode(node)) {
-        // The walker can be on a node that would be filtered out if it didn't find any
-        // other node to fallback to.
-        ret.push(this._ref(node));
-      }
-      node = walker.previousSibling();
-    } while (node && --count);
-    ret.reverse();
-    return ret;
-  },
-
-  /**
-   * Return the first node in the document that matches the given selector.
-   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelector
-   *
-   * @param NodeActor baseNode
-   * @param string selector
-   */
-  querySelector: function (baseNode, selector) {
-    if (isNodeDead(baseNode)) {
-      return {};
-    }
-
-    let node = baseNode.rawNode.querySelector(selector);
-    if (!node) {
-      return {};
-    }
-
-    return this.attachElement(node);
-  },
-
-  /**
-   * Return a NodeListActor with all nodes that match the given selector.
-   * See https://developer.mozilla.org/en-US/docs/Web/API/Element.querySelectorAll
-   *
-   * @param NodeActor baseNode
-   * @param string selector
-   */
-  querySelectorAll: function (baseNode, selector) {
-    let nodeList = null;
-
-    try {
-      nodeList = baseNode.rawNode.querySelectorAll(selector);
-    } catch (e) {
-      // Bad selector. Do nothing as the selector can come from a searchbox.
-    }
-
-    return new NodeListActor(this, nodeList);
-  },
-
-  /**
-   * Get a list of nodes that match the given selector in all known frames of
-   * the current content page.
-   * @param {String} selector.
-   * @return {Array}
-   */
-  _multiFrameQuerySelectorAll: function (selector) {
-    let nodes = [];
-
-    for (let {document} of this.tabActor.windows) {
-      try {
-        nodes = [...nodes, ...document.querySelectorAll(selector)];
-      } catch (e) {
-        // Bad selector. Do nothing as the selector can come from a searchbox.
-      }
-    }
-
-    return nodes;
-  },
-
-  /**
-   * Return a NodeListActor with all nodes that match the given selector in all
-   * frames of the current content page.
-   * @param {String} selector
-   */
-  multiFrameQuerySelectorAll: function (selector) {
-    return new NodeListActor(this, this._multiFrameQuerySelectorAll(selector));
-  },
-
-  /**
-   * Search the document for a given string.
-   * Results will be searched with the walker-search module (searches through
-   * tag names, attribute names and values, and text contents).
-   *
-   * @returns {searchresult}
-   *            - {NodeList} list
-   *            - {Array<Object>} metadata. Extra information with indices that
-   *                              match up with node list.
-   */
-  search: function (query) {
-    let results = this.walkerSearch.search(query);
-    let nodeList = new NodeListActor(this, results.map(r => r.node));
-
-    return {
-      list: nodeList,
-      metadata: []
-    };
-  },
-
-  /**
-   * Returns a list of matching results for CSS selector autocompletion.
-   *
-   * @param string query
-   *        The selector query being completed
-   * @param string completing
-   *        The exact token being completed out of the query
-   * @param string selectorState
-   *        One of "pseudo", "id", "tag", "class", "null"
-   */
-  getSuggestionsForQuery: function (query, completing, selectorState) {
-    let sugs = {
-      classes: new Map(),
-      tags: new Map(),
-      ids: new Map()
-    };
-    let result = [];
-    let nodes = null;
-    // Filtering and sorting the results so that protocol transfer is miminal.
-    switch (selectorState) {
-      case "pseudo":
-        result = PSEUDO_SELECTORS.filter(item => {
-          return item[0].startsWith(":" + completing);
-        });
-        break;
-
-      case "class":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("[class]");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          for (let className of node.classList) {
-            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
-          }
-        }
-        sugs.classes.delete("");
-        sugs.classes.delete(HIDDEN_CLASS);
-        for (let [className, count] of sugs.classes) {
-          if (className.startsWith(completing)) {
-            result.push(["." + CSS.escape(className), count, selectorState]);
-          }
-        }
-        break;
-
-      case "id":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("[id]");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
-        }
-        for (let [id, count] of sugs.ids) {
-          if (id.startsWith(completing) && id !== "") {
-            result.push(["#" + CSS.escape(id), count, selectorState]);
-          }
-        }
-        break;
-
-      case "tag":
-        if (!query) {
-          nodes = this._multiFrameQuerySelectorAll("*");
-        } else {
-          nodes = this._multiFrameQuerySelectorAll(query);
-        }
-        for (let node of nodes) {
-          let tag = node.localName;
-          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
-        }
-        for (let [tag, count] of sugs.tags) {
-          if ((new RegExp("^" + completing + ".*", "i")).test(tag)) {
-            result.push([tag, count, selectorState]);
-          }
-        }
-
-        // For state 'tag' (no preceding # or .) and when there's no query (i.e.
-        // only one word) then search for the matching classes and ids
-        if (!query) {
-          result = [
-            ...result,
-            ...this.getSuggestionsForQuery(null, completing, "class")
-                   .suggestions,
-            ...this.getSuggestionsForQuery(null, completing, "id")
-                   .suggestions
-          ];
-        }
-
-        break;
-
-      case "null":
-        nodes = this._multiFrameQuerySelectorAll(query);
-        for (let node of nodes) {
-          sugs.ids.set(node.id, (sugs.ids.get(node.id)|0) + 1);
-          let tag = node.localName;
-          sugs.tags.set(tag, (sugs.tags.get(tag)|0) + 1);
-          for (let className of node.classList) {
-            sugs.classes.set(className, (sugs.classes.get(className)|0) + 1);
-          }
-        }
-        for (let [tag, count] of sugs.tags) {
-          tag && result.push([tag, count]);
-        }
-        for (let [id, count] of sugs.ids) {
-          id && result.push(["#" + id, count]);
-        }
-        sugs.classes.delete("");
-        sugs.classes.delete(HIDDEN_CLASS);
-        for (let [className, count] of sugs.classes) {
-          className && result.push(["." + className, count]);
-        }
-    }
-
-    // Sort by count (desc) and name (asc)
-    result = result.sort((a, b) => {
-      // Computed a sortable string with first the inverted count, then the name
-      let sortA = (10000 - a[1]) + a[0];
-      let sortB = (10000 - b[1]) + b[0];
-
-      // Prefixing ids, classes and tags, to group results
-      let firstA = a[0].substring(0, 1);
-      let firstB = b[0].substring(0, 1);
-
-      if (firstA === "#") {
-        sortA = "2" + sortA;
-      } else if (firstA === ".") {
-        sortA = "1" + sortA;
-      } else {
-        sortA = "0" + sortA;
-      }
-
-      if (firstB === "#") {
-        sortB = "2" + sortB;
-      } else if (firstB === ".") {
-        sortB = "1" + sortB;
-      } else {
-        sortB = "0" + sortB;
-      }
-
-      // String compare
-      return sortA.localeCompare(sortB);
-    });
-
-    result.slice(0, 25);
-
-    return {
-      query: query,
-      suggestions: result
-    };
-  },
-
-  /**
-   * Add a pseudo-class lock to a node.
-   *
-   * @param NodeActor node
-   * @param string pseudo
-   *    A pseudoclass: ':hover', ':active', ':focus'
-   * @param options
-   *    Options object:
-   *    `parents`: True if the pseudo-class should be added
-   *      to parent nodes.
-   *    `enabled`: False if the pseudo-class should be locked
-   *      to 'off'. Defaults to true.
-   *
-   * @returns An empty packet.  A "pseudoClassLock" mutation will
-   *    be queued for any changed nodes.
-   */
-  addPseudoClassLock: function (node, pseudo, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    // There can be only one node locked per pseudo, so dismiss all existing
-    // ones
-    for (let locked of this._activePseudoClassLocks) {
-      if (InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
-        this._removePseudoClassLock(locked, pseudo);
-      }
-    }
-
-    let enabled = options.enabled === undefined ||
-                  options.enabled;
-    this._addPseudoClassLock(node, pseudo, enabled);
-
-    if (!options.parents) {
-      return;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let curNode = this._ref(cur);
-      this._addPseudoClassLock(curNode, pseudo, enabled);
-    }
-  },
-
-  _queuePseudoClassMutation: function (node) {
-    this.queueMutation({
-      target: node.actorID,
-      type: "pseudoClassLock",
-      pseudoClassLocks: node.writePseudoClassLocks()
-    });
-  },
-
-  _addPseudoClassLock: function (node, pseudo, enabled) {
-    if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
-      return false;
-    }
-    InspectorUtils.addPseudoClassLock(node.rawNode, pseudo, enabled);
-    this._activePseudoClassLocks.add(node);
-    this._queuePseudoClassMutation(node);
-    return true;
-  },
-
-  hideNode: function (node) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    loadSheet(node.rawNode.ownerGlobal, HELPER_SHEET);
-    node.rawNode.classList.add(HIDDEN_CLASS);
-  },
-
-  unhideNode: function (node) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    node.rawNode.classList.remove(HIDDEN_CLASS);
-  },
-
-  /**
-   * Remove a pseudo-class lock from a node.
-   *
-   * @param NodeActor node
-   * @param string pseudo
-   *    A pseudoclass: ':hover', ':active', ':focus'
-   * @param options
-   *    Options object:
-   *    `parents`: True if the pseudo-class should be removed
-   *      from parent nodes.
-   *
-   * @returns An empty response.  "pseudoClassLock" mutations
-   *    will be emitted for any changed nodes.
-   */
-  removePseudoClassLock: function (node, pseudo, options = {}) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    this._removePseudoClassLock(node, pseudo);
-
-    // Remove pseudo class for children as we don't want to allow
-    // turning it on for some childs without setting it on some parents
-    for (let locked of this._activePseudoClassLocks) {
-      if (node.rawNode.contains(locked.rawNode) &&
-          InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
-        this._removePseudoClassLock(locked, pseudo);
-      }
-    }
-
-    if (!options.parents) {
-      return;
-    }
-
-    let walker = this.getDocumentWalker(node.rawNode);
-    let cur;
-    while ((cur = walker.parentNode())) {
-      let curNode = this._ref(cur);
-      this._removePseudoClassLock(curNode, pseudo);
-    }
-  },
-
-  _removePseudoClassLock: function (node, pseudo) {
-    if (node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) {
-      return false;
-    }
-    InspectorUtils.removePseudoClassLock(node.rawNode, pseudo);
-    if (!node.writePseudoClassLocks()) {
-      this._activePseudoClassLocks.delete(node);
-    }
-
-    this._queuePseudoClassMutation(node);
-    return true;
-  },
-
-  /**
-   * Clear all the pseudo-classes on a given node or all nodes.
-   * @param {NodeActor} node Optional node to clear pseudo-classes on
-   */
-  clearPseudoClassLocks: function (node) {
-    if (node && isNodeDead(node)) {
-      return;
-    }
-
-    if (node) {
-      InspectorUtils.clearPseudoClassLocks(node.rawNode);
-      this._activePseudoClassLocks.delete(node);
-      this._queuePseudoClassMutation(node);
-    } else {
-      for (let locked of this._activePseudoClassLocks) {
-        InspectorUtils.clearPseudoClassLocks(locked.rawNode);
-        this._activePseudoClassLocks.delete(locked);
-        this._queuePseudoClassMutation(locked);
-      }
-    }
-  },
-
-  /**
-   * Get a node's innerHTML property.
-   */
-  innerHTML: function (node) {
-    let html = "";
-    if (!isNodeDead(node)) {
-      html = node.rawNode.innerHTML;
-    }
-    return LongStringActor(this.conn, html);
-  },
-
-  /**
-   * Set a node's innerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   * @param {string} value The piece of HTML content.
-   */
-  setInnerHTML: function (node, value) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    let rawNode = node.rawNode;
-    if (rawNode.nodeType !== rawNode.ownerDocument.ELEMENT_NODE) {
-      throw new Error("Can only change innerHTML to element nodes");
-    }
-    // eslint-disable-next-line no-unsanitized/property
-    rawNode.innerHTML = value;
-  },
-
-  /**
-   * Get a node's outerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   */
-  outerHTML: function (node) {
-    let outerHTML = "";
-    if (!isNodeDead(node)) {
-      outerHTML = node.rawNode.outerHTML;
-    }
-    return LongStringActor(this.conn, outerHTML);
-  },
-
-  /**
-   * Set a node's outerHTML property.
-   *
-   * @param {NodeActor} node The node.
-   * @param {string} value The piece of HTML content.
-   */
-  setOuterHTML: function (node, value) {
-    if (isNodeDead(node)) {
-      return;
-    }
-
-    let parsedDOM = DOMParser.parseFromString(value, "text/html");
-    let rawNode = node.rawNode;
-    let parentNode = rawNode.parentNode;
-
-    // Special case for head and body.  Setting document.body.outerHTML
-    // creates an extra <head> tag, and document.head.outerHTML creates
-    // an extra <body>.  So instead we will call replaceChild with the
-    // parsed DOM, assuming that they aren't trying to set both tags at once.
-    if (rawNode.tagName === "BODY") {
-      if (parsedDOM.head.innerHTML === "") {
-        parentNode.replaceChild(parsedDOM.body, rawNode);
-      } else {
-      // eslint-disable-next-line no-unsanitized/property
-        rawNode.outerHTML = value;
-      }
-    } else if (rawNode.tagName === "HEAD") {
-      if (parsedDOM.body.innerHTML === "") {
-        parentNode.replaceChild(parsedDOM.head, rawNode);
-      } else {
-        // eslint-disable-next-line no-unsanitized/property
-        rawNode.outerHTML = value;
-      }
-    } else if (node.isDocumentElement()) {
-      // Unable to set outerHTML on the document element.  Fall back by
-      // setting attributes manually, then replace the body and head elements.
-      let finalAttributeModifications = [];
-      let attributeModifications = {};
-      for (let attribute of rawNode.attributes) {
-        attributeModifications[attribute.name] = null;
-      }
-      for (let attribute of parsedDOM.documentElement.attributes) {
-        attributeModifications[attribute.name] = attribute.value;
-      }
-      for (let key in attributeModifications) {
-        finalAttributeModifications.push({
-          attributeName: key,
-          newValue: attributeModifications[key]
-        });
-      }
-      node.modifyAttributes(finalAttributeModifications);
-      rawNode.replaceChild(parsedDOM.head, rawNode.querySelector("head"));
-      rawNode.replaceChild(parsedDOM.body, rawNode.querySelector("body"));
-    } else {
-      // eslint-disable-next-line no-unsanitized/property
-      rawNode.outerHTML = value;
-    }
-  },
-
-  /**
-   * Insert adjacent HTML to a node.
-   *
-   * @param {Node} node
-   * @param {string} position One of "beforeBegin", "afterBegin", "beforeEnd",
-   *                          "afterEnd" (see Element.insertAdjacentHTML).
-   * @param {string} value The HTML content.
-   */
-  insertAdjacentHTML: function (node, position, value) {
-    if (isNodeDead(node)) {
-      return {node: [], newParents: []};
-    }
-
-    let rawNode = node.rawNode;
-    let isInsertAsSibling = position === "beforeBegin" ||
-      position === "afterEnd";
-
-    // Don't insert anything adjacent to the document element.
-    if (isInsertAsSibling && node.isDocumentElement()) {
-      throw new Error("Can't insert adjacent element to the root.");
-    }
-
-    let rawParentNode = rawNode.parentNode;
-    if (!rawParentNode && isInsertAsSibling) {
-      throw new Error("Can't insert as sibling without parent node.");
-    }
-
-    // We can't use insertAdjacentHTML, because we want to return the nodes
-    // being created (so the front can remove them if the user undoes
-    // the change). So instead, use Range.createContextualFragment().
-    let range = rawNode.ownerDocument.createRange();
-    if (position === "beforeBegin" || position === "afterEnd") {
-      range.selectNode(rawNode);
-    } else {
-      range.selectNodeContents(rawNode);
-    }
-    let docFrag = range.createContextualFragment(value);
-    let newRawNodes = Array.from(docFrag.childNodes);
-    switch (position) {
-      case "beforeBegin":
-        rawParentNode.insertBefore(docFrag, rawNode);
-        break;
-      case "afterEnd":
-        // Note: if the second argument is null, rawParentNode.insertBefore
-        // behaves like rawParentNode.appendChild.
-        rawParentNode.insertBefore(docFrag, rawNode.nextSibling);
-        break;
-      case "afterBegin":
-        rawNode.insertBefore(docFrag, rawNode.firstChild);
-        break;
-      case "beforeEnd":
-        rawNode.appendChild(docFrag);
-        break;
-      default:
-        throw new Error("Invalid position value. Must be either " +
-          "'beforeBegin', 'beforeEnd', 'afterBegin' or 'afterEnd'.");
-    }
-
-    return this.attachElements(newRawNodes);
-  },
-
-  /**
-   * Duplicate a specified node
-   *
-   * @param {NodeActor} node The node to duplicate.
-   */
-  duplicateNode: function ({rawNode}) {
-    let clonedNode = rawNode.cloneNode(true);
-    rawNode.parentNode.insertBefore(clonedNode, rawNode.nextSibling);
-  },
-
-  /**
-   * Test whether a node is a document or a document element.
-   *
-   * @param {NodeActor} node The node to remove.
-   * @return {boolean} True if the node is a document or a document element.
-   */
-  isDocumentOrDocumentElementNode: function (node) {
-    return ((node.rawNode.ownerDocument &&
-      node.rawNode.ownerDocument.documentElement === this.rawNode) ||
-      node.rawNode.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE);
-  },
-
-  /**
-   * Removes a node from its parent node.
-   *
-   * @param {NodeActor} node The node to remove.
-   * @returns The node's nextSibling before it was removed.
-   */
-  removeNode: function (node) {
-    if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
-      throw Error("Cannot remove document, document elements or dead nodes.");
-    }
-
-    let nextSibling = this.nextSibling(node);
-    node.rawNode.remove();
-    // Mutation events will take care of the rest.
-    return nextSibling;
-  },
-
-  /**
-   * Removes an array of nodes from their parent node.
-   *
-   * @param {NodeActor[]} nodes The nodes to remove.
-   */
-  removeNodes: function (nodes) {
-    // Check that all nodes are valid before processing the removals.
-    for (let node of nodes) {
-      if (isNodeDead(node) || this.isDocumentOrDocumentElementNode(node)) {
-        throw Error("Cannot remove document, document elements or dead nodes");
-      }
-    }
-
-    for (let node of nodes) {
-      node.rawNode.remove();
-      // Mutation events will take care of the rest.
-    }
-  },
-
-  /**
-   * Insert a node into the DOM.
-   */
-  insertBefore: function (node, parent, sibling) {
-    if (isNodeDead(node) ||
-        isNodeDead(parent) ||
-        (sibling && isNodeDead(sibling))) {
-      return;
-    }
-
-    let rawNode = node.rawNode;
-    let rawParent = parent.rawNode;
-    let rawSibling = sibling ? sibling.rawNode : null;
-
-    // Don't bother inserting a node if the document position isn't going
-    // to change. This prevents needless iframes reloading and mutations.
-    if (rawNode.parentNode === rawParent) {
-      let currentNextSibling = this.nextSibling(node);
-      currentNextSibling = currentNextSibling ? currentNextSibling.rawNode :
-                                                null;
-
-      if (rawNode === rawSibling || currentNextSibling === rawSibling) {
-        return;
-      }
-    }
-
-    rawParent.insertBefore(rawNode, rawSibling);
-  },
-
-  /**
-   * Editing a node's tagname actually means creating a new node with the same
-   * attributes, removing the node and inserting the new one instead.
-   * This method does not return anything as mutation events are taking care of
-   * informing the consumers about changes.
-   */
-  editTagName: function (node, tagName) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let oldNode = node.rawNode;
-
-    // Create a new element with the same attributes as the current element and
-    // prepare to replace the current node with it.
-    let newNode;
-    try {
-      newNode = nodeDocument(oldNode).createElement(tagName);
-    } catch (x) {
-      // Failed to create a new element with that tag name, ignore the change,
-      // and signal the error to the front.
-      return Promise.reject(new Error("Could not change node's tagName to " + tagName));
-    }
-
-    let attrs = oldNode.attributes;
-    for (let i = 0; i < attrs.length; i++) {
-      newNode.setAttribute(attrs[i].name, attrs[i].value);
-    }
-
-    // Insert the new node, and transfer the old node's children.
-    oldNode.parentNode.insertBefore(newNode, oldNode);
-    while (oldNode.firstChild) {
-      newNode.appendChild(oldNode.firstChild);
-    }
-
-    oldNode.remove();
-    return null;
-  },
-
-  /**
-   * Get any pending mutation records.  Must be called by the client after
-   * the `new-mutations` notification is received.  Returns an array of
-   * mutation records.
-   *
-   * Mutation records have a basic structure:
-   *
-   * {
-   *   type: attributes|characterData|childList,
-   *   target: <domnode actor ID>,
-   * }
-   *
-   * And additional attributes based on the mutation type:
-   *
-   * `attributes` type:
-   *   attributeName: <string> - the attribute that changed
-   *   attributeNamespace: <string> - the attribute's namespace URI, if any.
-   *   newValue: <string> - The new value of the attribute, if any.
-   *
-   * `characterData` type:
-   *   newValue: <string> - the new nodeValue for the node
-   *
-   * `childList` type is returned when the set of children for a node
-   * has changed.  Includes extra data, which can be used by the client to
-   * maintain its ownership subtree.
-   *
-   *   added: array of <domnode actor ID> - The list of actors *previously
-   *     seen by the client* that were added to the target node.
-   *   removed: array of <domnode actor ID> The list of actors *previously
-   *     seen by the client* that were removed from the target node.
-   *   inlineTextChild: If the node now has a single text child, it will
-   *     be sent here.
-   *
-   * Actors that are included in a MutationRecord's `removed` but
-   * not in an `added` have been removed from the client's ownership
-   * tree (either by being moved under a node the client has seen yet
-   * or by being removed from the tree entirely), and is considered
-   * 'orphaned'.
-   *
-   * Keep in mind that if a node that the client hasn't seen is moved
-   * into or out of the target node, it will not be included in the
-   * removedNodes and addedNodes list, so if the client is interested
-   * in the new set of children it needs to issue a `children` request.
-   */
-  getMutations: function (options = {}) {
-    let pending = this._pendingMutations || [];
-    this._pendingMutations = [];
-    this._waitingForGetMutations = false;
-
-    if (options.cleanup) {
-      for (let node of this._orphaned) {
-        // Release the orphaned node.  Nodes or children that have been
-        // retained will be moved to this._retainedOrphans.
-        this.releaseNode(node);
-      }
-      this._orphaned = new Set();
-    }
-
-    return pending;
-  },
-
-  queueMutation: function (mutation) {
-    if (!this.actorID || this._destroyed) {
-      // We've been destroyed, don't bother queueing this mutation.
-      return;
-    }
-
-    // Add the mutation to the list of mutations to be retrieved next.
-    this._pendingMutations.push(mutation);
-
-    // Bail out if we already emitted a new-mutations event and are waiting for a client
-    // to retrieve them.
-    if (this._waitingForGetMutations) {
-      return;
-    }
-
-    if (IMMEDIATE_MUTATIONS.includes(mutation.type)) {
-      this._emitNewMutations();
-    } else {
-      /**
-       * If many mutations are fired at the same time, clients might sequentially request
-       * children/siblings for updated nodes, which can be costly. By throttling the calls
-       * to getMutations, duplicated mutations will be ignored.
-       */
-      this._throttledEmitNewMutations();
-    }
-  },
-
-  _emitNewMutations: function () {
-    if (!this.actorID || this._destroyed) {
-      // Bail out if the actor was destroyed after throttling this call.
-      return;
-    }
-
-    if (this._waitingForGetMutations || this._pendingMutations.length == 0) {
-      // Bail out if we already fired the new-mutation event or if no mutations are
-      // waiting to be retrieved.
-      return;
-    }
-
-    this._waitingForGetMutations = true;
-    this.emit("new-mutations");
-  },
-
-  /**
-   * Handles mutations from the DOM mutation observer API.
-   *
-   * @param array[MutationRecord] mutations
-   *    See https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationRecord
-   */
-  onMutations: function (mutations) {
-    // Notify any observers that want *all* mutations (even on nodes that aren't
-    // referenced).  This is not sent over the protocol so can only be used by
-    // scripts running in the server process.
-    this.emit("any-mutation");
-
-    for (let change of mutations) {
-      let targetActor = this.getNode(change.target);
-      if (!targetActor) {
-        continue;
-      }
-      let targetNode = change.target;
-      let type = change.type;
-      let mutation = {
-        type: type,
-        target: targetActor.actorID,
-      };
-
-      if (type === "attributes") {
-        mutation.attributeName = change.attributeName;
-        mutation.attributeNamespace = change.attributeNamespace || undefined;
-        mutation.newValue = targetNode.hasAttribute(mutation.attributeName) ?
-                            targetNode.getAttribute(mutation.attributeName)
-                            : null;
-      } else if (type === "characterData") {
-        mutation.newValue = targetNode.nodeValue;
-        this._maybeQueueInlineTextChildMutation(change, targetNode);
-      } else if (type === "childList" || type === "nativeAnonymousChildList") {
-        // Get the list of removed and added actors that the client has seen
-        // so that it can keep its ownership tree up to date.
-        let removedActors = [];
-        let addedActors = [];
-        for (let removed of change.removedNodes) {
-          let removedActor = this.getNode(removed);
-          if (!removedActor) {
-            // If the client never encountered this actor we don't need to
-            // mention that it was removed.
-            continue;
-          }
-          // While removed from the tree, nodes are saved as orphaned.
-          this._orphaned.add(removedActor);
-          removedActors.push(removedActor.actorID);
-        }
-        for (let added of change.addedNodes) {
-          let addedActor = this.getNode(added);
-          if (!addedActor) {
-            // If the client never encounted this actor we don't need to tell
-            // it about its addition for ownership tree purposes - if the
-            // client wants to see the new nodes it can ask for children.
-            continue;
-          }
-          // The actor is reconnected to the ownership tree, unorphan
-          // it and let the client know so that its ownership tree is up
-          // to date.
-          this._orphaned.delete(addedActor);
-          addedActors.push(addedActor.actorID);
-        }
-
-        mutation.numChildren = targetActor.numChildren;
-        mutation.removed = removedActors;
-        mutation.added = addedActors;
-
-        let inlineTextChild = this.inlineTextChild(targetActor);
-        if (inlineTextChild) {
-          mutation.inlineTextChild = inlineTextChild.form();
-        }
-      }
-      this.queueMutation(mutation);
-    }
-  },
-
-  /**
-   * Check if the provided mutation could change the way the target element is
-   * inlined with its parent node. If it might, a custom mutation of type
-   * "inlineTextChild" will be queued.
-   *
-   * @param {MutationRecord} mutation
-   *        A characterData type mutation
-   */
-  _maybeQueueInlineTextChildMutation: function (mutation) {
-    let {oldValue, target} = mutation;
-    let newValue = target.nodeValue;
-    let limit = gValueSummaryLength;
-
-    if ((oldValue.length <= limit && newValue.length <= limit) ||
-        (oldValue.length > limit && newValue.length > limit)) {
-      // Bail out if the new & old values are both below/above the size limit.
-      return;
-    }
-
-    let parentActor = this.getNode(target.parentNode);
-    if (!parentActor || parentActor.rawNode.children.length > 0) {
-      // If the parent node has other children, a character data mutation will
-      // not change anything regarding inlining text nodes.
-      return;
-    }
-
-    let inlineTextChild = this.inlineTextChild(parentActor);
-    this.queueMutation({
-      type: "inlineTextChild",
-      target: parentActor.actorID,
-      inlineTextChild:
-        inlineTextChild ? inlineTextChild.form() : undefined
-    });
-  },
-
-  onFrameLoad: function ({ window, isTopLevel }) {
-    let { readyState } = window.document;
-    if (readyState != "interactive" && readyState != "complete") {
-      window.addEventListener("DOMContentLoaded",
-        this.onFrameLoad.bind(this, { window, isTopLevel }),
-        { once: true });
-      return;
-    }
-    if (isTopLevel) {
-      // If we initialize the inspector while the document is loading,
-      // we may already have a root document set in the constructor.
-      if (this.rootDoc && !Cu.isDeadWrapper(this.rootDoc) &&
-          this.rootDoc.defaultView) {
-        this.onFrameUnload({ window: this.rootDoc.defaultView });
-      }
-      // Update all DOM objects references to target the new document.
-      this.rootWin = window;
-      this.rootDoc = window.document;
-      this.rootNode = this.document();
-      this.queueMutation({
-        type: "newRoot",
-        target: this.rootNode.form()
-      });
-      return;
-    }
-    let frame = getFrameElement(window);
-    let frameActor = this.getNode(frame);
-    if (!frameActor) {
-      return;
-    }
-
-    this.queueMutation({
-      type: "frameLoad",
-      target: frameActor.actorID,
-    });
-
-    // Send a childList mutation on the frame.
-    this.queueMutation({
-      type: "childList",
-      target: frameActor.actorID,
-      added: [],
-      removed: []
-    });
-  },
-
-  // Returns true if domNode is in window or a subframe.
-  _childOfWindow: function (window, domNode) {
-    let win = nodeDocument(domNode).defaultView;
-    while (win) {
-      if (win === window) {
-        return true;
-      }
-      win = getFrameElement(win);
-    }
-    return false;
-  },
-
-  onFrameUnload: function ({ window }) {
-    // Any retained orphans that belong to this document
-    // or its children need to be released, and a mutation sent
-    // to notify of that.
-    let releasedOrphans = [];
-
-    for (let retained of this._retainedOrphans) {
-      if (Cu.isDeadWrapper(retained.rawNode) ||
-          this._childOfWindow(window, retained.rawNode)) {
-        this._retainedOrphans.delete(retained);
-        releasedOrphans.push(retained.actorID);
-        this.releaseNode(retained, { force: true });
-      }
-    }
-
-    if (releasedOrphans.length > 0) {
-      this.queueMutation({
-        target: this.rootNode.actorID,
-        type: "unretained",
-        nodes: releasedOrphans
-      });
-    }
-
-    let doc = window.document;
-    let documentActor = this.getNode(doc);
-    if (!documentActor) {
-      return;
-    }
-
-    if (this.rootDoc === doc) {
-      this.rootDoc = null;
-      this.rootNode = null;
-    }
-
-    this.queueMutation({
-      type: "documentUnload",
-      target: documentActor.actorID
-    });
-
-    let walker = this.getDocumentWalker(doc);
-    let parentNode = walker.parentNode();
-    if (parentNode) {
-      // Send a childList mutation on the frame so that clients know
-      // they should reread the children list.
-      this.queueMutation({
-        type: "childList",
-        target: this.getNode(parentNode).actorID,
-        added: [],
-        removed: []
-      });
-    }
-
-    // Need to force a release of this node, because those nodes can't
-    // be accessed anymore.
-    this.releaseNode(documentActor, { force: true });
-  },
-
-  /**
-   * Check if a node is attached to the DOM tree of the current page.
-   * @param {nsIDomNode} rawNode
-   * @return {Boolean} false if the node is removed from the tree or within a
-   * document fragment
-   */
-  _isInDOMTree: function (rawNode) {
-    let walker = this.getDocumentWalker(rawNode);
-    let current = walker.currentNode;
-
-    // Reaching the top of tree
-    while (walker.parentNode()) {
-      current = walker.currentNode;
-    }
-
-    // The top of the tree is a fragment or is not rootDoc, hence rawNode isn't
-    // attached
-    if (current.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE ||
-        current !== this.rootDoc) {
-      return false;
-    }
-
-    // Otherwise the top of the tree is rootDoc, hence rawNode is in rootDoc
-    return true;
-  },
-
-  /**
-   * @see _isInDomTree
-   */
-  isInDOMTree: function (node) {
-    if (isNodeDead(node)) {
-      return false;
-    }
-    return this._isInDOMTree(node.rawNode);
-  },
-
-  /**
-   * Given an ObjectActor (identified by its ID), commonly used in the debugger,
-   * webconsole and variablesView, return the corresponding inspector's
-   * NodeActor
-   */
-  getNodeActorFromObjectActor: function (objectActorID) {
-    let actor = this.conn.getActor(objectActorID);
-    if (!actor) {
-      return null;
-    }
-
-    let debuggerObject = this.conn.getActor(objectActorID).obj;
-    let rawNode = debuggerObject.unsafeDereference();
-
-    if (!this._isInDOMTree(rawNode)) {
-      return null;
-    }
-
-    // This is a special case for the document object whereby it is considered
-    // as document.documentElement (the <html> node)
-    if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
-      rawNode = rawNode.documentElement;
-    }
-
-    return this.attachElement(rawNode);
-  },
-
-  /**
-   * Given a windowID return the NodeActor for the corresponding frameElement,
-   * unless it's the root window
-   */
-  getNodeActorFromWindowID: function (windowID) {
-    let win;
-
-    try {
-      win = Services.wm.getOuterWindowWithId(windowID);
-    } catch (e) {
-      // ignore
-    }
-
-    if (!win) {
-      return { error: "noWindow",
-               message: "The related docshell is destroyed or not found" };
-    } else if (!win.frameElement) {
-      // the frame element of the root document is privileged & thus
-      // inaccessible, so return the document body/element instead
-      return this.attachElement(win.document.body || win.document.documentElement);
-    }
-
-    return this.attachElement(win.frameElement);
-  },
-
-  /**
-   * Given a StyleSheetActor (identified by its ID), commonly used in the
-   * style-editor, get its ownerNode and return the corresponding walker's
-   * NodeActor.
-   * Note that getNodeFromActor was added later and can now be used instead.
-   */
-  getStyleSheetOwnerNode: function (styleSheetActorID) {
-    return this.getNodeFromActor(styleSheetActorID, ["ownerNode"]);
-  },
-
-  /**
-   * This method can be used to retrieve NodeActor for DOM nodes from other
-   * actors in a way that they can later be highlighted in the page, or
-   * selected in the inspector.
-   * If an actor has a reference to a DOM node, and the UI needs to know about
-   * this DOM node (and possibly select it in the inspector), the UI should
-   * first retrieve a reference to the walkerFront:
-   *
-   * // Make sure the inspector/walker have been initialized first.
-   * toolbox.initInspector().then(() => {
-   *  // Retrieve the walker.
-   *  let walker = toolbox.walker;
-   * });
-   *
-   * And then call this method:
-   *
-   * // Get the nodeFront from my actor, passing the ID and properties path.
-   * walker.getNodeFromActor(myActorID, ["element"]).then(nodeFront => {
-   *   // Use the nodeFront, e.g. select the node in the inspector.
-   *   toolbox.getPanel("inspector").selection.setNodeFront(nodeFront);
-   * });
-   *
-   * @param {String} actorID The ID for the actor that has a reference to the
-   * DOM node.
-   * @param {Array} path Where, on the actor, is the DOM node stored. If in the
-   * scope of the actor, the node is available as `this.data.node`, then this
-   * should be ["data", "node"].
-   * @return {NodeActor} The attached NodeActor, or null if it couldn't be
-   * found.
-   */
-  getNodeFromActor: function (actorID, path) {
-    let actor = this.conn.getActor(actorID);
-    if (!actor) {
-      return null;
-    }
-
-    let obj = actor;
-    for (let name of path) {
-      if (!(name in obj)) {
-        return null;
-      }
-      obj = obj[name];
-    }
-
-    return this.attachElement(obj);
-  },
-
-  /**
-   * Returns an instance of the LayoutActor that is used to retrieve CSS layout-related
-   * information.
-   *
-   * @return {LayoutActor}
-   */
-  getLayoutInspector: function () {
-    if (!this.layoutActor) {
-      this.layoutActor = new LayoutActor(this.conn, this.tabActor, this);
-    }
-
-    return this.layoutActor;
-  },
-
-  /**
-   * Returns the offset parent DOMNode of the given node if it exists, otherwise, it
-   * returns null.
-   */
-  getOffsetParent: function (node) {
-    if (isNodeDead(node)) {
-      return null;
-    }
-
-    let offsetParent = node.rawNode.offsetParent;
-
-    if (!offsetParent) {
-      return null;
-    }
-
-    return this._ref(offsetParent);
-  },
-});
-
-/**
- * Server side of the inspector actor, which is used to create
- * inspector-related actors, including the walker.
- */
-exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
-  initialize: function (conn, tabActor) {
-    protocol.Actor.prototype.initialize.call(this, conn);
-    this.tabActor = tabActor;
-
-    this._onColorPicked = this._onColorPicked.bind(this);
-    this._onColorPickCanceled = this._onColorPickCanceled.bind(this);
-    this.destroyEyeDropper = this.destroyEyeDropper.bind(this);
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-
-    this.destroyEyeDropper();
-
-    this._highlighterPromise = null;
-    this._pageStylePromise = null;
-    this._walkerPromise = null;
-    this.walker = null;
-    this.tabActor = null;
-  },
-
-  get window() {
-    return this.tabActor.window;
-  },
-
-  getWalker: function (options = {}) {
-    if (this._walkerPromise) {
-      return this._walkerPromise;
-    }
-
-    let deferred = defer();
-    this._walkerPromise = deferred.promise;
-
-    let window = this.window;
-    let domReady = () => {
-      let tabActor = this.tabActor;
-      window.removeEventListener("DOMContentLoaded", domReady, true);
-      this.walker = WalkerActor(this.conn, tabActor, options);
-      this.manage(this.walker);
-      this.walker.once("destroyed", () => {
-        this._walkerPromise = null;
-        this._pageStylePromise = null;
-      });
-      deferred.resolve(this.walker);
-    };
-
-    if (window.document.readyState === "loading") {
-      window.addEventListener("DOMContentLoaded", domReady, true);
-    } else {
-      domReady();
-    }
-
-    return this._walkerPromise;
-  },
-
-  getPageStyle: function () {
-    if (this._pageStylePromise) {
-      return this._pageStylePromise;
-    }
-
-    this._pageStylePromise = this.getWalker().then(walker => {
-      let pageStyle = PageStyleActor(this);
-      this.manage(pageStyle);
-      return pageStyle;
-    });
-    return this._pageStylePromise;
-  },
-
-  /**
-   * The most used highlighter actor is the HighlighterActor which can be
-   * conveniently retrieved by this method.
-   * The same instance will always be returned by this method when called
-   * several times.
-   * The highlighter actor returned here is used to highlighter elements's
-   * box-models from the markup-view, box model, console, debugger, ... as
-   * well as select elements with the pointer (pick).
-   *
-   * @param {Boolean} autohide Optionally autohide the highlighter after an
-   * element has been picked
-   * @return {HighlighterActor}
-   */
-  getHighlighter: function (autohide) {
-    if (this._highlighterPromise) {
-      return this._highlighterPromise;
-    }
-
-    this._highlighterPromise = this.getWalker().then(walker => {
-      let highlighter = HighlighterActor(this, autohide);
-      this.manage(highlighter);
-      return highlighter;
-    });
-    return this._highlighterPromise;
-  },
-
-  /**
-   * If consumers need to display several highlighters at the same time or
-   * different types of highlighters, then this method should be used, passing
-   * the type name of the highlighter needed as argument.
-   * A new instance will be created everytime the method is called, so it's up
-   * to the consumer to release it when it is not needed anymore
-   *
-   * @param {String} type The type of highlighter to create
-   * @return {Highlighter} The highlighter actor instance or null if the
-   * typeName passed doesn't match any available highlighter
-   */
-  getHighlighterByType: function (typeName) {
-    if (isTypeRegistered(typeName)) {
-      return CustomHighlighterActor(this, typeName);
-    }
-    return null;
-  },
-
-  /**
-   * Get the node's image data if any (for canvas and img nodes).
-   * Returns an imageData object with the actual data being a LongStringActor
-   * and a size json object.
-   * The image data is transmitted as a base64 encoded png data-uri.
-   * The method rejects if the node isn't an image or if the image is missing
-   *
-   * Accepts a maxDim request parameter to resize images that are larger. This
-   * is important as the resizing occurs server-side so that image-data being
-   * transfered in the longstring back to the client will be that much smaller
-   */
-  getImageDataFromURL: function (url, maxDim) {
-    let img = new this.window.Image();
-    img.src = url;
-
-    // imageToImageData waits for the image to load.
-    return imageToImageData(img, maxDim).then(imageData => {
-      return {
-        data: LongStringActor(this.conn, imageData.data),
-        size: imageData.size
-      };
-    });
-  },
-
-  /**
-   * Resolve a URL to its absolute form, in the scope of a given content window.
-   * @param {String} url.
-   * @param {NodeActor} node If provided, the owner window of this node will be
-   * used to resolve the URL. Otherwise, the top-level content window will be
-   * used instead.
-   * @return {String} url.
-   */
-  resolveRelativeURL: function (url, node) {
-    let document = isNodeDead(node)
-                   ? this.window.document
-                   : nodeDocument(node.rawNode);
-
-    if (!document) {
-      return url;
-    }
-
-    let baseURI = Services.io.newURI(document.location.href);
-    return Services.io.newURI(url, null, baseURI).spec;
-  },
-
-  /**
-   * Create an instance of the eye-dropper highlighter and store it on this._eyeDropper.
-   * Note that for now, a new instance is created every time to deal with page navigation.
-   */
-  createEyeDropper: function () {
-    this.destroyEyeDropper();
-    this._highlighterEnv = new HighlighterEnvironment();
-    this._highlighterEnv.initFromTabActor(this.tabActor);
-    this._eyeDropper = new EyeDropper(this._highlighterEnv);
-  },
-
-  /**
-   * Destroy the current eye-dropper highlighter instance.
-   */
-  destroyEyeDropper: function () {
-    if (this._eyeDropper) {
-      this.cancelPickColorFromPage();
-      this._eyeDropper.destroy();
-      this._eyeDropper = null;
-      this._highlighterEnv.destroy();
-      this._highlighterEnv = null;
-    }
-  },
-
-  /**
-   * Pick a color from the page using the eye-dropper. This method doesn't return anything
-   * but will cause events to be sent to the front when a color is picked or when the user
-   * cancels the picker.
-   * @param {Object} options
-   */
-  pickColorFromPage: function (options) {
-    this.createEyeDropper();
-    this._eyeDropper.show(this.window.document.documentElement, options);
-    this._eyeDropper.once("selected", this._onColorPicked);
-    this._eyeDropper.once("canceled", this._onColorPickCanceled);
-    this.tabActor.once("will-navigate", this.destroyEyeDropper);
-  },
-
-  /**
-   * After the pickColorFromPage method is called, the only way to dismiss the eye-dropper
-   * highlighter is for the user to click in the page and select a color. If you need to
-   * dismiss the eye-dropper programatically instead, use this method.
-   */
-  cancelPickColorFromPage: function () {
-    if (this._eyeDropper) {
-      this._eyeDropper.hide();
-      this._eyeDropper.off("selected", this._onColorPicked);
-      this._eyeDropper.off("canceled", this._onColorPickCanceled);
-      this.tabActor.off("will-navigate", this.destroyEyeDropper);
-    }
-  },
-
-  /**
-   * Check if the current document supports highlighters using a canvasFrame anonymous
-   * content container (ie all highlighters except the SimpleOutlineHighlighter).
-   * It is impossible to detect the feature programmatically as some document types simply
-   * don't render the canvasFrame without throwing any error.
-   */
-  supportsHighlighters: function () {
-    let doc = this.tabActor.window.document;
-    let ns = doc.documentElement.namespaceURI;
-
-    // XUL documents do not support insertAnonymousContent().
-    if (ns === XUL_NS) {
-      return false;
-    }
-
-    // SVG documents do not render the canvasFrame (see Bug 1157592).
-    if (ns === SVG_NS) {
-      return false;
-    }
-
-    return true;
-  },
-
-  _onColorPicked: function (e, color) {
-    this.emit("color-picked", color);
-  },
-
-  _onColorPickCanceled: function () {
-    this.emit("color-pick-canceled");
-  }
-});
-
-// Exported for test purposes.
-exports._documentWalker = DocumentWalker;
-
-function nodeDocument(node) {
-  if (Cu.isDeadWrapper(node)) {
-    return null;
-  }
-  return node.ownerDocument ||
-         (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
-}
-
-function nodeDocshell(node) {
-  let doc = node ? nodeDocument(node) : null;
-  let win = doc ? doc.defaultView : null;
-  if (win) {
-    return win.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDocShell);
-  }
-  return null;
-}
-
-function isNodeDead(node) {
-  return !node || !node.rawNode || Cu.isDeadWrapper(node.rawNode);
-}
-
-/**
- * Wrapper for inDeepTreeWalker.  Adds filtering to the traversal methods.
- * See inDeepTreeWalker for more information about the methods.
- *
- * @param {DOMNode} node
- * @param {Window} rootWin
- * @param {Number} whatToShow
- *        See nodeFilterConstants / inIDeepTreeWalker for options.
- * @param {Function} filter
- *        A custom filter function Taking in a DOMNode and returning an Int. See
- *        WalkerActor.nodeFilter for an example.
- * @param {String} skipTo
- *        Either SKIP_TO_PARENT or SKIP_TO_SIBLING. If the provided node is not compatible
- *        with the filter function for this walker, try to find a compatible one either
- *        in the parents or in the siblings of the node.
- */
-function DocumentWalker(node, rootWin,
-    whatToShow = nodeFilterConstants.SHOW_ALL,
-    filter = standardTreeWalkerFilter,
-    skipTo = SKIP_TO_PARENT) {
-  if (Cu.isDeadWrapper(rootWin) || !rootWin.location) {
-    throw new Error("Got an invalid root window in DocumentWalker");
-  }
-
-  this.walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
-    .createInstance(Ci.inIDeepTreeWalker);
-  this.walker.showAnonymousContent = true;
-  this.walker.showSubDocuments = true;
-  this.walker.showDocumentsAsNodes = true;
-  this.walker.init(rootWin.document, whatToShow);
-  this.filter = filter;
-
-  // Make sure that the walker knows about the initial node (which could
-  // be skipped due to a filter).
-  this.walker.currentNode = this.getStartingNode(node, skipTo);
-}
-
-DocumentWalker.prototype = {
-  get whatToShow() {
-    return this.walker.whatToShow;
-  },
-  get currentNode() {
-    return this.walker.currentNode;
-  },
-  set currentNode(val) {
-    this.walker.currentNode = val;
-  },
-
-  parentNode: function () {
-    return this.walker.parentNode();
-  },
-
-  nextNode: function () {
-    let node = this.walker.currentNode;
-    if (!node) {
-      return null;
-    }
-
-    let nextNode = this.walker.nextNode();
-    while (nextNode && this.isSkippedNode(nextNode)) {
-      nextNode = this.walker.nextNode();
-    }
-
-    return nextNode;
-  },
-
-  firstChild: function () {
-    let node = this.walker.currentNode;
-    if (!node) {
-      return null;
-    }
-
-    let firstChild = this.walker.firstChild();
-    while (firstChild && this.isSkippedNode(firstChild)) {
-      firstChild = this.walker.nextSibling();
-    }
-
-    return firstChild;
-  },
-
-  lastChild: function () {
-    let node = this.walker.currentNode;
-    if (!node) {
-      return null;
-    }
-
-    let lastChild = this.walker.lastChild();
-    while (lastChild && this.isSkippedNode(lastChild)) {
-      lastChild = this.walker.previousSibling();
-    }
-
-    return lastChild;
-  },
-
-  previousSibling: function () {
-    let node = this.walker.previousSibling();
-    while (node && this.isSkippedNode(node)) {
-      node = this.walker.previousSibling();
-    }
-    return node;
-  },
-
-  nextSibling: function () {
-    let node = this.walker.nextSibling();
-    while (node && this.isSkippedNode(node)) {
-      node = this.walker.nextSibling();
-    }
-    return node;
-  },
-
-  getStartingNode: function (node, skipTo) {
-    // Keep a reference on the starting node in case we can't find a node compatible with
-    // the filter.
-    let startingNode = node;
-
-    if (skipTo === SKIP_TO_PARENT) {
-      while (node && this.isSkippedNode(node)) {
-        node = node.parentNode;
-      }
-    } else if (skipTo === SKIP_TO_SIBLING) {
-      node = this.getClosestAcceptedSibling(node);
-    }
-
-    return node || startingNode;
-  },
-
-  /**
-   * Loop on all of the provided node siblings until finding one that is compliant with
-   * the filter function.
-   */
-  getClosestAcceptedSibling: function (node) {
-    if (this.filter(node) === nodeFilterConstants.FILTER_ACCEPT) {
-      // node is already valid, return immediately.
-      return node;
-    }
-
-    // Loop on starting node siblings.
-    let previous = node;
-    let next = node;
-    while (previous || next) {
-      previous = previous && previous.previousSibling;
-      next = next && next.nextSibling;
-
-      if (previous && this.filter(previous) === nodeFilterConstants.FILTER_ACCEPT) {
-        // A valid node was found in the previous siblings of the node.
-        return previous;
-      }
-
-      if (next && this.filter(next) === nodeFilterConstants.FILTER_ACCEPT) {
-        // A valid node was found in the next siblings of the node.
-        return next;
-      }
-    }
-
-    return null;
-  },
-
-  isSkippedNode: function (node) {
-    return this.filter(node) === nodeFilterConstants.FILTER_SKIP;
-  },
-};
-
-function isInXULDocument(el) {
-  let doc = nodeDocument(el);
-  return doc &&
-         doc.documentElement &&
-         doc.documentElement.namespaceURI === XUL_NS;
-}
-
-/**
- * This DeepTreeWalker filter skips whitespace text nodes and anonymous
- * content with the exception of ::before and ::after and anonymous content
- * in XUL document (needed to show all elements in the browser toolbox).
- */
-function standardTreeWalkerFilter(node) {
-  // ::before and ::after are native anonymous content, but we always
-  // want to show them
-  if (node.nodeName === "_moz_generated_content_before" ||
-      node.nodeName === "_moz_generated_content_after") {
-    return nodeFilterConstants.FILTER_ACCEPT;
-  }
-
-  // Ignore empty whitespace text nodes that do not impact the layout.
-  if (isWhitespaceTextNode(node)) {
-    return nodeHasSize(node)
-           ? nodeFilterConstants.FILTER_ACCEPT
-           : nodeFilterConstants.FILTER_SKIP;
-  }
-
-  // Ignore all native and XBL anonymous content inside a non-XUL document.
-  // We need to do this to skip things like form controls, scrollbars,
-  // video controls, etc (see bug 1187482).
-  if (!isInXULDocument(node) && (isXBLAnonymous(node) ||
-                                  isNativeAnonymous(node))) {
-    return nodeFilterConstants.FILTER_SKIP;
-  }
-
-  return nodeFilterConstants.FILTER_ACCEPT;
-}
-
-/**
- * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
- * it also includes all anonymous content (like internal form controls).
- */
-function allAnonymousContentTreeWalkerFilter(node) {
-  // Ignore empty whitespace text nodes that do not impact the layout.
-  if (isWhitespaceTextNode(node)) {
-    return nodeHasSize(node)
-           ? nodeFilterConstants.FILTER_ACCEPT
-           : nodeFilterConstants.FILTER_SKIP;
-  }
-  return nodeFilterConstants.FILTER_ACCEPT;
-}
-
-/**
- * Is the given node a text node composed of whitespace only?
- * @param {DOMNode} node
- * @return {Boolean}
- */
-function isWhitespaceTextNode(node) {
-  return node.nodeType == Ci.nsIDOMNode.TEXT_NODE && !/[^\s]/.exec(node.nodeValue);
-}
-
-/**
- * Does the given node have non-0 width and height?
- * @param {DOMNode} node
- * @return {Boolean}
- */
-function nodeHasSize(node) {
-  if (!node.getBoxQuads) {
-    return false;
-  }
-
-  let quads = node.getBoxQuads();
-  return quads.length && quads.some(quad => quad.bounds.width && quad.bounds.height);
-}
-
-/**
- * Returns a promise that is settled once the given HTMLImageElement has
- * finished loading.
- *
- * @param {HTMLImageElement} image - The image element.
- * @param {Number} timeout - Maximum amount of time the image is allowed to load
- * before the waiting is aborted. Ignored if flags.testing is set.
- *
- * @return {Promise} that is fulfilled once the image has loaded. If the image
- * fails to load or the load takes too long, the promise is rejected.
- */
-function ensureImageLoaded(image, timeout) {
-  let { HTMLImageElement } = image.ownerGlobal;
-  if (!(image instanceof HTMLImageElement)) {
-    return promise.reject("image must be an HTMLImageELement");
-  }
-
-  if (image.complete) {
-    // The image has already finished loading.
-    return promise.resolve();
-  }
-
-  // This image is still loading.
-  let onLoad = AsyncUtils.listenOnce(image, "load");
-
-  // Reject if loading fails.
-  let onError = AsyncUtils.listenOnce(image, "error").then(() => {
-    return promise.reject("Image '" + image.src + "' failed to load.");
-  });
-
-  // Don't timeout when testing. This is never settled.
-  let onAbort = new Promise(() => {});
-
-  if (!flags.testing) {
-    // Tests are not running. Reject the promise after given timeout.
-    onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
-      return promise.reject("Image '" + image.src + "' took too long to load.");
-    });
-  }
-
-  // See which happens first.
-  return promise.race([onLoad, onError, onAbort]);
-}
-
-/**
- * Given an <img> or <canvas> element, return the image data-uri. If @param node
- * is an <img> element, the method waits a while for the image to load before
- * the data is generated. If the image does not finish loading in a reasonable
- * time (IMAGE_FETCHING_TIMEOUT milliseconds) the process aborts.
- *
- * @param {HTMLImageElement|HTMLCanvasElement} node - The <img> or <canvas>
- * element, or Image() object. Other types cause the method to reject.
- * @param {Number} maxDim - Optionally pass a maximum size you want the longest
- * side of the image to be resized to before getting the image data.
-
- * @return {Promise} A promise that is fulfilled with an object containing the
- * data-uri and size-related information:
- * { data: "...",
- *   size: {
- *     naturalWidth: 400,
- *     naturalHeight: 300,
- *     resized: true }
- *  }.
- *
- * If something goes wrong, the promise is rejected.
- */
-var imageToImageData = Task.async(function* (node, maxDim) {
-  let { HTMLCanvasElement, HTMLImageElement } = node.ownerGlobal;
-
-  let isImg = node instanceof HTMLImageElement;
-  let isCanvas = node instanceof HTMLCanvasElement;
-
-  if (!isImg && !isCanvas) {
-    throw new Error("node is not a <canvas> or <img> element.");
-  }
-
-  if (isImg) {
-    // Ensure that the image is ready.
-    yield ensureImageLoaded(node, IMAGE_FETCHING_TIMEOUT);
-  }
-
-  // Get the image resize ratio if a maxDim was provided
-  let resizeRatio = 1;
-  let imgWidth = node.naturalWidth || node.width;
-  let imgHeight = node.naturalHeight || node.height;
-  let imgMax = Math.max(imgWidth, imgHeight);
-  if (maxDim && imgMax > maxDim) {
-    resizeRatio = maxDim / imgMax;
-  }
-
-  // Extract the image data
-  let imageData;
-  // The image may already be a data-uri, in which case, save ourselves the
-  // trouble of converting via the canvas.drawImage.toDataURL method, but only
-  // if the image doesn't need resizing
-  if (isImg && node.src.startsWith("data:") && resizeRatio === 1) {
-    imageData = node.src;
-  } else {
-    // Create a canvas to copy the rawNode into and get the imageData from
-    let canvas = node.ownerDocument.createElementNS(XHTML_NS, "canvas");
-    canvas.width = imgWidth * resizeRatio;
-    canvas.height = imgHeight * resizeRatio;
-    let ctx = canvas.getContext("2d");
-
-    // Copy the rawNode image or canvas in the new canvas and extract data
-    ctx.drawImage(node, 0, 0, canvas.width, canvas.height);
-    imageData = canvas.toDataURL("image/png");
-  }
-
-  return {
-    data: imageData,
-    size: {
-      naturalWidth: imgWidth,
-      naturalHeight: imgHeight,
-      resized: resizeRatio !== 1
-    }
-  };
-});
+exports.NodeActor = NodeActor;
+exports.NodeListActor = NodeListActor;
copy from devtools/server/actors/inspector/inspector.js
copy to devtools/server/actors/inspector/utils.js
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/utils.js
@@ -1,190 +1,31 @@
 /* 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";
 
-/**
- * Here's the server side of the remote inspector.
- *
- * The WalkerActor is the client's view of the debuggee's DOM.  It's gives
- * the client a tree of NodeActor objects.
- *
- * The walker presents the DOM tree mostly unmodified from the source DOM
- * tree, but with a few key differences:
- *
- *  - Empty text nodes are ignored.  This is pretty typical of developer
- *    tools, but maybe we should reconsider that on the server side.
- *  - iframes with documents loaded have the loaded document as the child,
- *    the walker provides one big tree for the whole document tree.
- *
- * There are a few ways to get references to NodeActors:
- *
- *   - When you first get a WalkerActor reference, it comes with a free
- *     reference to the root document's node.
- *   - Given a node, you can ask for children, siblings, and parents.
- *   - You can issue querySelector and querySelectorAll requests to find
- *     other elements.
- *   - Requests that return arbitrary nodes from the tree (like querySelector
- *     and querySelectorAll) will also return any nodes the client hasn't
- *     seen in order to have a complete set of parents.
- *
- * Once you have a NodeFront, you should be able to answer a few questions
- * without further round trips, like the node's name, namespace/tagName,
- * attributes, etc.  Other questions (like a text node's full nodeValue)
- * might require another round trip.
- *
- * The protocol guarantees that the client will always know the parent of
- * any node that is returned by the server.  This means that some requests
- * (like querySelector) will include the extra nodes needed to satisfy this
- * requirement.  The client keeps track of this parent relationship, so the
- * node fronts form a tree that is a subset of the actual DOM tree.
- *
- *
- * We maintain this guarantee to support the ability to release subtrees on
- * the client - when a node is disconnected from the DOM tree we want to be
- * able to free the client objects for all the children nodes.
- *
- * So to be able to answer "all the children of a given node that we have
- * seen on the client side", we guarantee that every time we've seen a node,
- * we connect it up through its parents.
- */
+const {Ci, Cu} = require("chrome");
+
+const promise = require("promise");
+const {Task} = require("devtools/shared/task");
 
-const {Cc, Ci, Cu} = require("chrome");
-const Services = require("Services");
-const protocol = require("devtools/shared/protocol");
-const {LongStringActor} = require("devtools/server/actors/string");
-const promise = require("promise");
-const defer = require("devtools/shared/defer");
-const {Task} = require("devtools/shared/task");
-const EventEmitter = require("devtools/shared/event-emitter");
-const InspectorUtils = require("InspectorUtils");
-
-const {walkerSpec, inspectorSpec} = require("devtools/shared/specs/inspector");
-const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");
-
+loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
+loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
 loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
-loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
-loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
-loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "getCssPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "getXPath", "devtools/shared/inspector/css-logic", true);
-loader.lazyRequireGetter(this, "colorUtils", "devtools/shared/css/color", true);
-loader.lazyRequireGetter(this, "EyeDropper", "devtools/server/actors/highlighters/eye-dropper", true);
-loader.lazyRequireGetter(this, "WalkerSearch", "devtools/server/actors/utils/walker-search", true);
-loader.lazyRequireGetter(this, "PageStyleActor", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "getFontPreviewData", "devtools/server/actors/styles", true);
-loader.lazyRequireGetter(this, "flags", "devtools/shared/flags");
-loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
-loader.lazyRequireGetter(this, "LayoutActor", "devtools/server/actors/layout", true);
-loader.lazyRequireGetter(this, "HighlighterActor", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "CustomHighlighterActor", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "isTypeRegistered", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "HighlighterEnvironment", "devtools/server/actors/highlighters", true);
-loader.lazyRequireGetter(this, "EventParsers", "devtools/server/event-parsers", true);
-loader.lazyRequireGetter(this, "isAnonymous", "devtools/shared/layout/utils", true);
+loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");
+
 loader.lazyRequireGetter(this, "isNativeAnonymous", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isXBLAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "isShadowAnonymous", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getFrameElement", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
-loader.lazyRequireGetter(this, "getLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "releaseLayoutChangesObserver", "devtools/server/actors/reflow", true);
-loader.lazyRequireGetter(this, "nodeFilterConstants", "devtools/shared/dom-node-filter-constants");
 
-loader.lazyServiceGetter(this, "DOMParser",
-  "@mozilla.org/xmlextras/domparser;1", "nsIDOMParser");
-
-loader.lazyServiceGetter(this, "eventListenerService",
-  "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService");
-
-const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
-const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
-const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
-const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
-const SVG_NS = "http://www.w3.org/2000/svg";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const IMAGE_FETCHING_TIMEOUT = 500;
 
-// Minimum delay between two "new-mutations" events.
-const MUTATIONS_THROTTLING_DELAY = 100;
-// List of mutation types that should -not- be throttled.
-const IMMEDIATE_MUTATIONS = [
-  "documentUnload",
-  "frameLoad",
-  "newRoot",
-  "pseudoClassLock",
-];
-
-// SKIP_TO_* arguments are used with the DocumentWalker, driving the strategy to use if
-// the starting node is incompatible with the filter function of the walker.
-const SKIP_TO_PARENT = "SKIP_TO_PARENT";
-const SKIP_TO_SIBLING = "SKIP_TO_SIBLING";
-
-// The possible completions to a ':' with added score to give certain values
-// some preference.
-const PSEUDO_SELECTORS = [
-  [":active", 1],
-  [":hover", 1],
-  [":focus", 1],
-  [":visited", 0],
-  [":link", 0],
-  [":first-letter", 0],
-  [":first-child", 2],
-  [":before", 2],
-  [":after", 2],
-  [":lang(", 0],
-  [":not(", 3],
-  [":first-of-type", 0],
-  [":last-of-type", 0],
-  [":only-of-type", 0],
-  [":only-child", 2],
-  [":nth-child(", 3],
-  [":nth-last-child(", 0],
-  [":nth-of-type(", 0],
-  [":nth-last-of-type(", 0],
-  [":last-child", 2],
-  [":root", 0],
-  [":empty", 0],
-  [":target", 0],
-  [":enabled", 0],
-  [":disabled", 0],
-  [":checked", 1],
-  ["::selection", 0]
-];
-
-var HELPER_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
-  .__fx-devtools-hide-shortcut__ {
-    visibility: hidden !important;
-  }
-
-  :-moz-devtools-highlighted {
-    outline: 2px dashed #F06!important;
-    outline-offset: -2px !important;
-  }
-`);
-
-/**
- * We only send nodeValue up to a certain size by default.  This stuff
- * controls that size.
- */
-exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50;
-var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH;
-
-exports.getValueSummaryLength = function () {
-  return gValueSummaryLength;
-};
-
-exports.setValueSummaryLength = function (val) {
-  gValueSummaryLength = val;
-};
-
 /**
  * Returns the properly cased version of the node's tag name, which can be
  * used when displaying said name in the UI.
  *
  * @param  {Node} rawNode
  *         Node for which we want the display name
  * @return {String}
  *         Properly cased version of the node tag name
@@ -192,3026 +33,29 @@ exports.setValueSummaryLength = function
 const getNodeDisplayName = function (rawNode) {
   if (rawNode.nodeName && !rawNode.localName) {
     // The localName & prefix APIs have been moved from the Node interface to the Element
     // interface. Use Node.nodeName as a fallback.
     return rawNode.nodeName;
   }
   return (rawNode.prefix ? rawNode.prefix + ":" : "") + rawNode.localName;
 };
-exports.getNodeDisplayName = getNodeDisplayName;
-
-/**
- * Server side of the node actor.
- */
-var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
-  initialize: function (walker, node) {
-    protocol.Actor.prototype.initialize.call(this, null);
-    this.walker = walker;
-    this.rawNode = node;
-    this._eventParsers = new EventParsers().parsers;
-
-    // Storing the original display of the node, to track changes when reflows
-    // occur
-    this.wasDisplayed = this.isDisplayed;
-  },
-
-  toString: function () {
-    return "[NodeActor " + this.actorID + " for " +
-      this.rawNode.toString() + "]";
-  },
-
-  /**
-   * Instead of storing a connection object, the NodeActor gets its connection
-   * from its associated walker.
-   */
-  get conn() {
-    return this.walker.conn;
-  },
-
-  isDocumentElement: function () {
-    return this.rawNode.ownerDocument &&
-           this.rawNode.ownerDocument.documentElement === this.rawNode;
-  },
-
-  destroy: function () {
-    protocol.Actor.prototype.destroy.call(this);
-
-    if (this.mutationObserver) {
-      if (!Cu.isDeadWrapper(this.mutationObserver)) {
-        this.mutationObserver.disconnect();
-      }
-      this.mutationObserver = null;
-    }
-    this.rawNode = null;
-    this.walker = null;
-  },
-
-  // Returns the JSON representation of this object over the wire.
-  form: function (detail) {
-    if (detail === "actorid") {
-      return this.actorID;
-    }
-
-    let parentNode = this.walker.parentNode(this);
-    let inlineTextChild = this.walker.inlineTextChild(this);
-
-    let form = {
-      actor: this.actorID,
-      baseURI: this.rawNode.baseURI,
-      parent: parentNode ? parentNode.actorID : undefined,
-      nodeType: this.rawNode.nodeType,
-      namespaceURI: this.rawNode.namespaceURI,
-      nodeName: this.rawNode.nodeName,
-      nodeValue: this.rawNode.nodeValue,
-      displayName: getNodeDisplayName(this.rawNode),
-      numChildren: this.numChildren,
-      inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
-
-      // doctype attributes
-      name: this.rawNode.name,
-      publicId: this.rawNode.publicId,
-      systemId: this.rawNode.systemId,
-
-      attrs: this.writeAttrs(),
-      isBeforePseudoElement: this.isBeforePseudoElement,
-      isAfterPseudoElement: this.isAfterPseudoElement,
-      isAnonymous: isAnonymous(this.rawNode),
-      isNativeAnonymous: isNativeAnonymous(this.rawNode),
-      isXBLAnonymous: isXBLAnonymous(this.rawNode),
-      isShadowAnonymous: isShadowAnonymous(this.rawNode),
-      pseudoClassLocks: this.writePseudoClassLocks(),
-
-      isDisplayed: this.isDisplayed,
-      isInHTMLDocument: this.rawNode.ownerDocument &&
-        this.rawNode.ownerDocument.contentType === "text/html",
-      hasEventListeners: this._hasEventListeners,
-    };
-
-    if (this.isDocumentElement()) {
-      form.isDocumentElement = true;
-    }
-
-    // Add an extra API for custom properties added by other
-    // modules/extensions.
-    form.setFormProperty = (name, value) => {
-      if (!form.props) {
-        form.props = {};
-      }
-      form.props[name] = value;
-    };
-
-    // Fire an event so, other modules can create its own properties
-    // that should be passed to the client (within the form.props field).
-    EventEmitter.emit(NodeActor, "form", {
-      target: this,
-      data: form
-    });
-
-    return form;
-  },
-
-  /**
-   * Watch the given document node for mutations using the DOM observer
-   * API.
-   */
-  watchDocument: function (callback) {
-    let node = this.rawNode;
-    // Create the observer on the node's actor.  The node will make sure
-    // the observer is cleaned up when the actor is released.
-    let observer = new node.defaultView.MutationObserver(callback);
-    observer.mergeAttributeRecords = true;
-    observer.observe(node, {
-      nativeAnonymousChildList: true,
-      attributes: true,
-      characterData: true,
-      characterDataOldValue: true,
-      childList: true,
-      subtree: true
-    });
-    this.mutationObserver = observer;
-  },
-
-  get isBeforePseudoElement() {
-    return this.rawNode.nodeName === "_moz_generated_content_before";
-  },
-
-  get isAfterPseudoElement() {
-    return this.rawNode.nodeName === "_moz_generated_content_after";
-  },
-
-  // Estimate the number of children that the walker will return without making
-  // a call to children() if possible.
-  get numChildren() {
-    // For pseudo elements, childNodes.length returns 1, but the walker
-    // will return 0.
-    if (this.isBeforePseudoElement || this.isAfterPseudoElement) {
-      return 0;
-    }
-
-    let rawNode = this.rawNode;
-    let numChildren = rawNode.childNodes.length;
-    let hasAnonChildren = rawNode.nodeType === Ci.nsIDOMNode.ELEMENT_NODE &&
-                          rawNode.ownerDocument.getAnonymousNodes(rawNode);
-
-    let hasContentDocument = rawNode.contentDocument;
-    let hasSVGDocument = rawNode.getSVGDocument && rawNode.getSVGDocument();
-    if (numChildren === 0 && (hasContentDocument || hasSVGDocument)) {
-      // This might be an iframe with virtual children.
-      numChildren = 1;
-    }
-
-    // Normal counting misses ::before/::after.  Also, some anonymous children
-    // may ultimately be skipped, so we have to consult with the walker.
-    if (numChildren === 0 || hasAnonChildren) {
-      numChildren = this.walker.children(this).nodes.length;
-    }
-
-    return numChildren;
-  },
-
-  get computedStyle() {
-    return CssLogic.getComputedStyle(this.rawNode);
-  },
-
-  /**
-   * Is the node's display computed style value other than "none"
-   */
-  get isDisplayed() {
-    // Consider all non-element nodes as displayed.
-    if (isNodeDead(this) ||
-        this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
-        this.isAfterPseudoElement ||
-        this.isBeforePseudoElement) {
-      return true;
-    }
-
-    let style = this.computedStyle;
-    if (!style) {
-      return true;
-    }
-
-    return style.display !== "none";
-  },
-
-  /**
-   * Are there event listeners that are listening on this node? This method
-   * uses all parsers registered via event-parsers.js.registerEventParser() to
-   * check if there are any event listeners.
-   */
-  get _hasEventListeners() {
-    let parsers = this._eventParsers;