Bug 1265722 - Move fronts to shared directory;r=jryans
authorEddy Bruel <ejpbruel@mozilla.com>
Tue, 17 May 2016 14:59:21 +0200
changeset 297723 c480fcee4fc0af1e22f08124937cc7602ed16032
parent 297722 c11343f547559a1fc96da1e5d36ca3db82cd84d5
child 297724 e51df628d6f718ec8c76085e9a7ecab1ee1c3bb8
push id30266
push userryanvm@gmail.com
push dateWed, 18 May 2016 15:57:57 +0000
treeherdermozilla-central@47b83c8478a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1265722
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1265722 - Move fronts to shared directory;r=jryans
devtools/client/framework/toolbox.js
devtools/client/fronts/highlighters.js
devtools/client/fronts/inspector.js
devtools/client/fronts/moz.build
devtools/client/fronts/storage.js
devtools/client/fronts/styles.js
devtools/client/fronts/stylesheets.js
devtools/client/moz.build
devtools/client/sourceeditor/test/browser_css_autocompletion.js
devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
devtools/client/storage/panel.js
devtools/client/styleeditor/styleeditor-panel.js
devtools/server/tests/browser/browser_storage_dynamic_windows.js
devtools/server/tests/browser/browser_storage_listings.js
devtools/server/tests/browser/browser_storage_updates.js
devtools/server/tests/browser/browser_stylesheets_getTextEmpty.js
devtools/server/tests/browser/browser_stylesheets_nested-iframes.js
devtools/server/tests/browser/head.js
devtools/server/tests/mochitest/test_animation_actor-lifetime.html
devtools/server/tests/mochitest/test_inspector-anonymous.html
devtools/server/tests/mochitest/test_inspector-changeattrs.html
devtools/server/tests/mochitest/test_inspector-changevalue.html
devtools/server/tests/mochitest/test_inspector-dead-nodes.html
devtools/server/tests/mochitest/test_inspector-duplicate-node.html
devtools/server/tests/mochitest/test_inspector-hide.html
devtools/server/tests/mochitest/test_inspector-insert.html
devtools/server/tests/mochitest/test_inspector-mutations-attr.html
devtools/server/tests/mochitest/test_inspector-mutations-childlist.html
devtools/server/tests/mochitest/test_inspector-mutations-events.html
devtools/server/tests/mochitest/test_inspector-mutations-frameload.html
devtools/server/tests/mochitest/test_inspector-mutations-value.html
devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html
devtools/server/tests/mochitest/test_inspector-release.html
devtools/server/tests/mochitest/test_inspector-reload.html
devtools/server/tests/mochitest/test_inspector-remove.html
devtools/server/tests/mochitest/test_inspector-resize.html
devtools/server/tests/mochitest/test_inspector-resolve-url.html
devtools/server/tests/mochitest/test_inspector-retain.html
devtools/server/tests/mochitest/test_inspector-scroll-into-view.html
devtools/server/tests/mochitest/test_inspector-search-front.html
devtools/server/tests/mochitest/test_inspector-search.html
devtools/server/tests/mochitest/test_inspector-traversal.html
devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
devtools/server/tests/mochitest/test_inspector_getImageData.html
devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
devtools/server/tests/mochitest/test_inspector_getNodeFromActor.html
devtools/server/tests/mochitest/test_styles-applied.html
devtools/server/tests/mochitest/test_styles-computed.html
devtools/server/tests/mochitest/test_styles-layout.html
devtools/server/tests/mochitest/test_styles-matched.html
devtools/server/tests/mochitest/test_styles-modify.html
devtools/server/tests/mochitest/test_styles-svg.html
devtools/shared/fronts/highlighters.js
devtools/shared/fronts/inspector.js
devtools/shared/fronts/moz.build
devtools/shared/fronts/storage.js
devtools/shared/fronts/styles.js
devtools/shared/fronts/stylesheets.js
devtools/shared/moz.build
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -52,17 +52,17 @@ loader.lazyRequireGetter(this, "CommandU
   "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "getHighlighterUtils",
   "devtools/client/framework/toolbox-highlighter-utils", true);
 loader.lazyRequireGetter(this, "Hosts",
   "devtools/client/framework/toolbox-hosts", true);
 loader.lazyRequireGetter(this, "Selection",
   "devtools/client/framework/selection", true);
 loader.lazyRequireGetter(this, "InspectorFront",
-  "devtools/client/fronts/inspector", true);
+  "devtools/shared/fronts/inspector", true);
 loader.lazyRequireGetter(this, "DevToolsUtils",
   "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "showDoorhanger",
   "devtools/client/shared/doorhanger", true);
 loader.lazyRequireGetter(this, "createPerformanceFront",
   "devtools/server/actors/performance", true);
 loader.lazyRequireGetter(this, "system",
   "devtools/shared/system");
deleted file mode 100644
--- a/devtools/client/fronts/highlighters.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { FrontClassWithSpec } = require("devtools/shared/protocol");
-const {
-  customHighlighterSpec,
-  highlighterSpec
-} = require("devtools/shared/specs/highlighters");
-
-const HighlighterFront = FrontClassWithSpec(highlighterSpec, {
-  // Update the object given a form representation off the wire.
-  form: function (json) {
-    this.actorID = json.actor;
-    // FF42+ HighlighterActors starts exposing custom form, with traits object
-    this.traits = json.traits || {};
-  }
-});
-
-exports.HighlighterFront = HighlighterFront;
-
-const CustomHighlighterFront = FrontClassWithSpec(customHighlighterSpec, {});
-
-exports.CustomHighlighterFront = CustomHighlighterFront;
deleted file mode 100644
--- a/devtools/client/fronts/inspector.js
+++ /dev/null
@@ -1,976 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const Services = require("Services");
-const { Ci } = require("chrome");
-require("devtools/client/fronts/styles");
-require("devtools/client/fronts/highlighters");
-const { ShortLongString } = require("devtools/server/actors/string");
-const {
-  Front,
-  FrontClassWithSpec,
-  custom,
-  preEvent,
-  types
-} = require("devtools/shared/protocol.js");
-const { makeInfallible } = require("devtools/shared/DevToolsUtils");
-const {
-  inspectorSpec,
-  nodeSpec,
-  nodeListSpec,
-  walkerSpec
-} = require("devtools/shared/specs/inspector");
-const promise = require("promise");
-const { Task } = require("resource://gre/modules/Task.jsm");
-const { Class } = require("sdk/core/heritage");
-const events = require("sdk/event/core");
-const object = require("sdk/util/object");
-
-const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
-
-/**
- * Convenience API for building a list of attribute modifications
- * for the `modifyAttributes` request.
- */
-const AttributeModificationList = Class({
-  initialize: function (node) {
-    this.node = node;
-    this.modifications = [];
-  },
-
-  apply: function () {
-    let ret = this.node.modifyAttributes(this.modifications);
-    return ret;
-  },
-
-  destroy: function () {
-    this.node = null;
-    this.modification = null;
-  },
-
-  setAttributeNS: function (ns, name, value) {
-    this.modifications.push({
-      attributeNamespace: ns,
-      attributeName: name,
-      newValue: value
-    });
-  },
-
-  setAttribute: function (name, value) {
-    this.setAttributeNS(undefined, name, value);
-  },
-
-  removeAttributeNS: function (ns, name) {
-    this.setAttributeNS(ns, name, undefined);
-  },
-
-  removeAttribute: function (name) {
-    this.setAttributeNS(undefined, name, undefined);
-  }
-});
-
-// A resolve that hits the main loop first.
-function delayedResolve(value) {
-  let deferred = promise.defer();
-  Services.tm.mainThread.dispatch(makeInfallible(() => {
-    deferred.resolve(value);
-  }), 0);
-  return deferred.promise;
-}
-
-/**
- * Client side of the node actor.
- *
- * Node fronts are strored in a tree that mirrors the DOM tree on the
- * server, but with a few key differences:
- *  - Not all children will be necessary loaded for each node.
- *  - The order of children isn't guaranteed to be the same as the DOM.
- * Children are stored in a doubly-linked list, to make addition/removal
- * and traversal quick.
- *
- * Due to the order/incompleteness of the child list, it is safe to use
- * the parent node from clients, but the `children` request should be used
- * to traverse children.
- */
-const NodeFront = FrontClassWithSpec(nodeSpec, {
-  initialize: function (conn, form, detail, ctx) {
-    // The parent node
-    this._parent = null;
-    // The first child of this node.
-    this._child = null;
-    // The next sibling of this node.
-    this._next = null;
-    // The previous sibling of this node.
-    this._prev = null;
-    Front.prototype.initialize.call(this, conn, form, detail, ctx);
-  },
-
-  /**
-   * Destroy a node front.  The node must have been removed from the
-   * ownership tree before this is called, unless the whole walker front
-   * is being destroyed.
-   */
-  destroy: function () {
-    Front.prototype.destroy.call(this);
-  },
-
-  // Update the object given a form representation off the wire.
-  form: function (form, detail, ctx) {
-    if (detail === "actorid") {
-      this.actorID = form;
-      return;
-    }
-    // Shallow copy of the form.  We could just store a reference, but
-    // eventually we'll want to update some of the data.
-    this._form = object.merge(form);
-    this._form.attrs = this._form.attrs ? this._form.attrs.slice() : [];
-
-    if (form.parent) {
-      // Get the owner actor for this actor (the walker), and find the
-      // parent node of this actor from it, creating a standin node if
-      // necessary.
-      let parentNodeFront = ctx.marshallPool().ensureParentFront(form.parent);
-      this.reparent(parentNodeFront);
-    }
-
-    if (form.singleTextChild) {
-      this.singleTextChild =
-        types.getType("domnode").read(form.singleTextChild, ctx);
-    } else {
-      this.singleTextChild = undefined;
-    }
-  },
-
-  /**
-   * Returns the parent NodeFront for this NodeFront.
-   */
-  parentNode: function () {
-    return this._parent;
-  },
-
-  /**
-   * Process a mutation entry as returned from the walker's `getMutations`
-   * request.  Only tries to handle changes of the node's contents
-   * themselves (character data and attribute changes), the walker itself
-   * will keep the ownership tree up to date.
-   */
-  updateMutation: function (change) {
-    if (change.type === "attributes") {
-      // We'll need to lazily reparse the attributes after this change.
-      this._attrMap = undefined;
-
-      // Update any already-existing attributes.
-      let found = false;
-      for (let i = 0; i < this.attributes.length; i++) {
-        let attr = this.attributes[i];
-        if (attr.name == change.attributeName &&
-            attr.namespace == change.attributeNamespace) {
-          if (change.newValue !== null) {
-            attr.value = change.newValue;
-          } else {
-            this.attributes.splice(i, 1);
-          }
-          found = true;
-          break;
-        }
-      }
-      // This is a new attribute. The null check is because of Bug 1192270,
-      // in the case of a newly added then removed attribute
-      if (!found && change.newValue !== null) {
-        this.attributes.push({
-          name: change.attributeName,
-          namespace: change.attributeNamespace,
-          value: change.newValue
-        });
-      }
-    } else if (change.type === "characterData") {
-      this._form.shortValue = change.newValue;
-      this._form.incompleteValue = change.incompleteValue;
-    } else if (change.type === "pseudoClassLock") {
-      this._form.pseudoClassLocks = change.pseudoClassLocks;
-    } else if (change.type === "events") {
-      this._form.hasEventListeners = change.hasEventListeners;
-    }
-  },
-
-  // Some accessors to make NodeFront feel more like an nsIDOMNode
-
-  get id() {
-    return this.getAttribute("id");
-  },
-
-  get nodeType() {
-    return this._form.nodeType;
-  },
-  get namespaceURI() {
-    return this._form.namespaceURI;
-  },
-  get nodeName() {
-    return this._form.nodeName;
-  },
-  get doctypeString() {
-    return "<!DOCTYPE " + this._form.name +
-     (this._form.publicId ? " PUBLIC \"" + this._form.publicId + "\"" : "") +
-     (this._form.systemId ? " \"" + this._form.systemId + "\"" : "") +
-     ">";
-  },
-
-  get baseURI() {
-    return this._form.baseURI;
-  },
-
-  get className() {
-    return this.getAttribute("class") || "";
-  },
-
-  get hasChildren() {
-    return this._form.numChildren > 0;
-  },
-  get numChildren() {
-    return this._form.numChildren;
-  },
-  get hasEventListeners() {
-    return this._form.hasEventListeners;
-  },
-
-  get isBeforePseudoElement() {
-    return this._form.isBeforePseudoElement;
-  },
-  get isAfterPseudoElement() {
-    return this._form.isAfterPseudoElement;
-  },
-  get isPseudoElement() {
-    return this.isBeforePseudoElement || this.isAfterPseudoElement;
-  },
-  get isAnonymous() {
-    return this._form.isAnonymous;
-  },
-  get isInHTMLDocument() {
-    return this._form.isInHTMLDocument;
-  },
-  get tagName() {
-    return this.nodeType === Ci.nsIDOMNode.ELEMENT_NODE ? this.nodeName : null;
-  },
-  get shortValue() {
-    return this._form.shortValue;
-  },
-  get incompleteValue() {
-    return !!this._form.incompleteValue;
-  },
-
-  get isDocumentElement() {
-    return !!this._form.isDocumentElement;
-  },
-
-  // doctype properties
-  get name() {
-    return this._form.name;
-  },
-  get publicId() {
-    return this._form.publicId;
-  },
-  get systemId() {
-    return this._form.systemId;
-  },
-
-  getAttribute: function (name) {
-    let attr = this._getAttribute(name);
-    return attr ? attr.value : null;
-  },
-  hasAttribute: function (name) {
-    this._cacheAttributes();
-    return (name in this._attrMap);
-  },
-
-  get hidden() {
-    let cls = this.getAttribute("class");
-    return cls && cls.indexOf(HIDDEN_CLASS) > -1;
-  },
-
-  get attributes() {
-    return this._form.attrs;
-  },
-
-  get pseudoClassLocks() {
-    return this._form.pseudoClassLocks || [];
-  },
-  hasPseudoClassLock: function (pseudo) {
-    return this.pseudoClassLocks.some(locked => locked === pseudo);
-  },
-
-  get isDisplayed() {
-    // The NodeActor's form contains the isDisplayed information as a boolean
-    // starting from FF32. Before that, the property is missing
-    return "isDisplayed" in this._form ? this._form.isDisplayed : true;
-  },
-
-  get isTreeDisplayed() {
-    let parent = this;
-    while (parent) {
-      if (!parent.isDisplayed) {
-        return false;
-      }
-      parent = parent.parentNode();
-    }
-    return true;
-  },
-
-  getNodeValue: custom(function () {
-    if (!this.incompleteValue) {
-      return delayedResolve(new ShortLongString(this.shortValue));
-    }
-
-    return this._getNodeValue();
-  }, {
-    impl: "_getNodeValue"
-  }),
-
-  // Accessors for custom form properties.
-
-  getFormProperty: function (name) {
-    return this._form.props ? this._form.props[name] : null;
-  },
-
-  hasFormProperty: function (name) {
-    return this._form.props ? (name in this._form.props) : null;
-  },
-
-  get formProperties() {
-    return this._form.props;
-  },
-
-  /**
-   * Return a new AttributeModificationList for this node.
-   */
-  startModifyingAttributes: function () {
-    return AttributeModificationList(this);
-  },
-
-  _cacheAttributes: function () {
-    if (typeof this._attrMap != "undefined") {
-      return;
-    }
-    this._attrMap = {};
-    for (let attr of this.attributes) {
-      this._attrMap[attr.name] = attr;
-    }
-  },
-
-  _getAttribute: function (name) {
-    this._cacheAttributes();
-    return this._attrMap[name] || undefined;
-  },
-
-  /**
-   * Set this node's parent.  Note that the children saved in
-   * this tree are unordered and incomplete, so shouldn't be used
-   * instead of a `children` request.
-   */
-  reparent: function (parent) {
-    if (this._parent === parent) {
-      return;
-    }
-
-    if (this._parent && this._parent._child === this) {
-      this._parent._child = this._next;
-    }
-    if (this._prev) {
-      this._prev._next = this._next;
-    }
-    if (this._next) {
-      this._next._prev = this._prev;
-    }
-    this._next = null;
-    this._prev = null;
-    this._parent = parent;
-    if (!parent) {
-      // Subtree is disconnected, we're done
-      return;
-    }
-    this._next = parent._child;
-    if (this._next) {
-      this._next._prev = this;
-    }
-    parent._child = this;
-  },
-
-  /**
-   * Return all the known children of this node.
-   */
-  treeChildren: function () {
-    let ret = [];
-    for (let child = this._child; child != null; child = child._next) {
-      ret.push(child);
-    }
-    return ret;
-  },
-
-  /**
-   * Do we use a local target?
-   * Useful to know if a rawNode is available or not.
-   *
-   * This will, one day, be removed. External code should
-   * not need to know if the target is remote or not.
-   */
-  isLocalToBeDeprecated: function () {
-    return !!this.conn._transport._serverConnection;
-  },
-
-  /**
-   * Get an nsIDOMNode for the given node front.  This only works locally,
-   * and is only intended as a stopgap during the transition to the remote
-   * protocol.  If you depend on this you're likely to break soon.
-   */
-  rawNode: function (rawNode) {
-    if (!this.conn._transport._serverConnection) {
-      console.warn("Tried to use rawNode on a remote connection.");
-      return null;
-    }
-    let actor = this.conn._transport._serverConnection.getActor(this.actorID);
-    if (!actor) {
-      // Can happen if we try to get the raw node for an already-expired
-      // actor.
-      return null;
-    }
-    return actor.rawNode;
-  }
-});
-
-exports.NodeFront = NodeFront;
-
-/**
- * Client side of a node list as returned by querySelectorAll()
- */
-const NodeListFront = FrontClassWithSpec(nodeListSpec, {
-  initialize: function (client, form) {
-    Front.prototype.initialize.call(this, client, form);
-  },
-
-  destroy: function () {
-    Front.prototype.destroy.call(this);
-  },
-
-  marshallPool: function () {
-    return this.parent();
-  },
-
-  // Update the object given a form representation off the wire.
-  form: function (json) {
-    this.length = json.length;
-  },
-
-  item: custom(function (index) {
-    return this._item(index).then(response => {
-      return response.node;
-    });
-  }, {
-    impl: "_item"
-  }),
-
-  items: custom(function (start, end) {
-    return this._items(start, end).then(response => {
-      return response.nodes;
-    });
-  }, {
-    impl: "_items"
-  })
-});
-
-exports.NodeListFront = NodeListFront;
-
-/**
- * Client side of the DOM walker.
- */
-const WalkerFront = FrontClassWithSpec(walkerSpec, {
-  // Set to true if cleanup should be requested after every mutation list.
-  autoCleanup: true,
-
-  /**
-   * This is kept for backward-compatibility reasons with older remote target.
-   * Targets previous to bug 916443
-   */
-  pick: custom(function () {
-    return this._pick().then(response => {
-      return response.node;
-    });
-  }, {impl: "_pick"}),
-
-  initialize: function (client, form) {
-    this._createRootNodePromise();
-    Front.prototype.initialize.call(this, client, form);
-    this._orphaned = new Set();
-    this._retainedOrphans = new Set();
-  },
-
-  destroy: function () {
-    Front.prototype.destroy.call(this);
-  },
-
-  // Update the object given a form representation off the wire.
-  form: function (json) {
-    this.actorID = json.actor;
-    this.rootNode = types.getType("domnode").read(json.root, this);
-    this._rootNodeDeferred.resolve(this.rootNode);
-    // FF42+ the actor starts exposing traits
-    this.traits = json.traits || {};
-  },
-
-  /**
-   * Clients can use walker.rootNode to get the current root node of the
-   * walker, but during a reload the root node might be null.  This
-   * method returns a promise that will resolve to the root node when it is
-   * set.
-   */
-  getRootNode: function () {
-    return this._rootNodeDeferred.promise;
-  },
-
-  /**
-   * Create the root node promise, triggering the "new-root" notification
-   * on resolution.
-   */
-  _createRootNodePromise: function () {
-    this._rootNodeDeferred = promise.defer();
-    this._rootNodeDeferred.promise.then(() => {
-      events.emit(this, "new-root");
-    });
-  },
-
-  /**
-   * When reading an actor form off the wire, we want to hook it up to its
-   * parent front.  The protocol guarantees that the parent will be seen
-   * by the client in either a previous or the current request.
-   * So if we've already seen this parent return it, otherwise create
-   * a bare-bones stand-in node.  The stand-in node will be updated
-   * with a real form by the end of the deserialization.
-   */
-  ensureParentFront: function (id) {
-    let front = this.get(id);
-    if (front) {
-      return front;
-    }
-
-    return types.getType("domnode").read({ actor: id }, this, "standin");
-  },
-
-  /**
-   * See the documentation for WalkerActor.prototype.retainNode for
-   * information on retained nodes.
-   *
-   * From the client's perspective, `retainNode` can fail if the node in
-   * question is removed from the ownership tree before the `retainNode`
-   * request reaches the server.  This can only happen if the client has
-   * asked the server to release nodes but hasn't gotten a response
-   * yet: Either a `releaseNode` request or a `getMutations` with `cleanup`
-   * set is outstanding.
-   *
-   * If either of those requests is outstanding AND releases the retained
-   * node, this request will fail with noSuchActor, but the ownership tree
-   * will stay in a consistent state.
-   *
-   * Because the protocol guarantees that requests will be processed and
-   * responses received in the order they were sent, we get the right
-   * semantics by setting our local retained flag on the node only AFTER
-   * a SUCCESSFUL retainNode call.
-   */
-  retainNode: custom(function (node) {
-    return this._retainNode(node).then(() => {
-      node.retained = true;
-    });
-  }, {
-    impl: "_retainNode",
-  }),
-
-  unretainNode: custom(function (node) {
-    return this._unretainNode(node).then(() => {
-      node.retained = false;
-      if (this._retainedOrphans.has(node)) {
-        this._retainedOrphans.delete(node);
-        this._releaseFront(node);
-      }
-    });
-  }, {
-    impl: "_unretainNode"
-  }),
-
-  releaseNode: custom(function (node, options = {}) {
-    // NodeFront.destroy will destroy children in the ownership tree too,
-    // mimicking what the server will do here.
-    let actorID = node.actorID;
-    this._releaseFront(node, !!options.force);
-    return this._releaseNode({ actorID: actorID });
-  }, {
-    impl: "_releaseNode"
-  }),
-
-  findInspectingNode: custom(function () {
-    return this._findInspectingNode().then(response => {
-      return response.node;
-    });
-  }, {
-    impl: "_findInspectingNode"
-  }),
-
-  querySelector: custom(function (queryNode, selector) {
-    return this._querySelector(queryNode, selector).then(response => {
-      return response.node;
-    });
-  }, {
-    impl: "_querySelector"
-  }),
-
-  getNodeActorFromObjectActor: custom(function (objectActorID) {
-    return this._getNodeActorFromObjectActor(objectActorID).then(response => {
-      return response ? response.node : null;
-    });
-  }, {
-    impl: "_getNodeActorFromObjectActor"
-  }),
-
-  getStyleSheetOwnerNode: custom(function (styleSheetActorID) {
-    return this._getStyleSheetOwnerNode(styleSheetActorID).then(response => {
-      return response ? response.node : null;
-    });
-  }, {
-    impl: "_getStyleSheetOwnerNode"
-  }),
-
-  getNodeFromActor: custom(function (actorID, path) {
-    return this._getNodeFromActor(actorID, path).then(response => {
-      return response ? response.node : null;
-    });
-  }, {
-    impl: "_getNodeFromActor"
-  }),
-
-  /*
-   * Incrementally search the document for a given string.
-   * For modern servers, results will be searched with using the WalkerActor
-   * `search` function (includes tag names, attributes, and text contents).
-   * Only 1 result is sent back, and calling the method again with the same
-   * query will send the next result. When there are no more results to be sent
-   * back, null is sent.
-   * @param {String} query
-   * @param {Object} options
-   *    - "reverse": search backwards
-   *    - "selectorOnly": treat input as a selector string (don't search text
-   *                      tags, attributes, etc)
-   */
-  search: custom(Task.async(function* (query, options = { }) {
-    let nodeList;
-    let searchType;
-    let searchData = this.searchData = this.searchData || { };
-    let selectorOnly = !!options.selectorOnly;
-
-    // Backwards compat.  Use selector only search if the new
-    // search functionality isn't implemented, or if the caller (tests)
-    // want it.
-    if (selectorOnly || !this.traits.textSearch) {
-      searchType = "selector";
-      if (this.traits.multiFrameQuerySelectorAll) {
-        nodeList = yield this.multiFrameQuerySelectorAll(query);
-      } else {
-        nodeList = yield this.querySelectorAll(this.rootNode, query);
-      }
-    } else {
-      searchType = "search";
-      let result = yield this._search(query, options);
-      nodeList = result.list;
-    }
-
-    // If this is a new search, start at the beginning.
-    if (searchData.query !== query ||
-        searchData.selectorOnly !== selectorOnly) {
-      searchData.selectorOnly = selectorOnly;
-      searchData.query = query;
-      searchData.index = -1;
-    }
-
-    if (!nodeList.length) {
-      return null;
-    }
-
-    // Move search result cursor and cycle if necessary.
-    searchData.index = options.reverse ? searchData.index - 1 :
-                                         searchData.index + 1;
-    if (searchData.index >= nodeList.length) {
-      searchData.index = 0;
-    }
-    if (searchData.index < 0) {
-      searchData.index = nodeList.length - 1;
-    }
-
-    // Send back the single node, along with any relevant search data
-    let node = yield nodeList.item(searchData.index);
-    return {
-      type: searchType,
-      node: node,
-      resultsLength: nodeList.length,
-      resultsIndex: searchData.index,
-    };
-  }), {
-    impl: "_search"
-  }),
-
-  _releaseFront: function (node, force) {
-    if (node.retained && !force) {
-      node.reparent(null);
-      this._retainedOrphans.add(node);
-      return;
-    }
-
-    if (node.retained) {
-      // Forcing a removal.
-      this._retainedOrphans.delete(node);
-    }
-
-    // Release any children
-    for (let child of node.treeChildren()) {
-      this._releaseFront(child, force);
-    }
-
-    // All children will have been removed from the node by this point.
-    node.reparent(null);
-    node.destroy();
-  },
-
-  /**
-   * Get any unprocessed mutation records and process them.
-   */
-  getMutations: custom(function (options = {}) {
-    return this._getMutations(options).then(mutations => {
-      let emitMutations = [];
-      for (let change of mutations) {
-        // The target is only an actorID, get the associated front.
-        let targetID;
-        let targetFront;
-
-        if (change.type === "newRoot") {
-          this.rootNode = types.getType("domnode").read(change.target, this);
-          this._rootNodeDeferred.resolve(this.rootNode);
-          targetID = this.rootNode.actorID;
-          targetFront = this.rootNode;
-        } else {
-          targetID = change.target;
-          targetFront = this.get(targetID);
-        }
-
-        if (!targetFront) {
-          console.trace("Got a mutation for an unexpected actor: " + targetID +
-            ", please file a bug on bugzilla.mozilla.org!");
-          continue;
-        }
-
-        let emittedMutation = object.merge(change, { target: targetFront });
-
-        if (change.type === "childList" ||
-            change.type === "nativeAnonymousChildList") {
-          // Update the ownership tree according to the mutation record.
-          let addedFronts = [];
-          let removedFronts = [];
-          for (let removed of change.removed) {
-            let removedFront = this.get(removed);
-            if (!removedFront) {
-              console.error("Got a removal of an actor we didn't know about: " +
-                removed);
-              continue;
-            }
-            // Remove from the ownership tree
-            removedFront.reparent(null);
-
-            // This node is orphaned unless we get it in the 'added' list
-            // eventually.
-            this._orphaned.add(removedFront);
-            removedFronts.push(removedFront);
-          }
-          for (let added of change.added) {
-            let addedFront = this.get(added);
-            if (!addedFront) {
-              console.error("Got an addition of an actor we didn't know " +
-                "about: " + added);
-              continue;
-            }
-            addedFront.reparent(targetFront);
-
-            // The actor is reconnected to the ownership tree, unorphan
-            // it.
-            this._orphaned.delete(addedFront);
-            addedFronts.push(addedFront);
-          }
-
-          if (change.singleTextChild) {
-            targetFront.singleTextChild =
-              types.getType("domnode").read(change.singleTextChild, this);
-          } else {
-            targetFront.singleTextChild = undefined;
-          }
-
-          // Before passing to users, replace the added and removed actor
-          // ids with front in the mutation record.
-          emittedMutation.added = addedFronts;
-          emittedMutation.removed = removedFronts;
-
-          // If this is coming from a DOM mutation, the actor's numChildren
-          // was passed in. Otherwise, it is simulated from a frame load or
-          // unload, so don't change the front's form.
-          if ("numChildren" in change) {
-            targetFront._form.numChildren = change.numChildren;
-          }
-        } else if (change.type === "frameLoad") {
-          // Nothing we need to do here, except verify that we don't have any
-          // document children, because we should have gotten a documentUnload
-          // first.
-          for (let child of targetFront.treeChildren()) {
-            if (child.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
-              console.trace("Got an unexpected frameLoad in the inspector, " +
-                "please file a bug on bugzilla.mozilla.org!");
-            }
-          }
-        } else if (change.type === "documentUnload") {
-          if (targetFront === this.rootNode) {
-            this._createRootNodePromise();
-          }
-
-          // We try to give fronts instead of actorIDs, but these fronts need
-          // to be destroyed now.
-          emittedMutation.target = targetFront.actorID;
-          emittedMutation.targetParent = targetFront.parentNode();
-
-          // Release the document node and all of its children, even retained.
-          this._releaseFront(targetFront, true);
-        } else if (change.type === "unretained") {
-          // Retained orphans were force-released without the intervention of
-          // client (probably a navigated frame).
-          for (let released of change.nodes) {
-            let releasedFront = this.get(released);
-            this._retainedOrphans.delete(released);
-            this._releaseFront(releasedFront, true);
-          }
-        } else {
-          targetFront.updateMutation(change);
-        }
-
-        emitMutations.push(emittedMutation);
-      }
-
-      if (options.cleanup) {
-        for (let node of this._orphaned) {
-          // This will move retained nodes to this._retainedOrphans.
-          this._releaseFront(node);
-        }
-        this._orphaned = new Set();
-      }
-
-      events.emit(this, "mutations", emitMutations);
-    });
-  }, {
-    impl: "_getMutations"
-  }),
-
-  /**
-   * Handle the `new-mutations` notification by fetching the
-   * available mutation records.
-   */
-  onMutations: preEvent("new-mutations", function () {
-    // Fetch and process the mutations.
-    this.getMutations({cleanup: this.autoCleanup}).catch(() => {});
-  }),
-
-  isLocal: function () {
-    return !!this.conn._transport._serverConnection;
-  },
-
-  // XXX hack during transition to remote inspector: get a proper NodeFront
-  // for a given local node.  Only works locally.
-  frontForRawNode: function (rawNode) {
-    if (!this.isLocal()) {
-      console.warn("Tried to use frontForRawNode on a remote connection.");
-      return null;
-    }
-    let walkerActor = this.conn._transport._serverConnection
-      .getActor(this.actorID);
-    if (!walkerActor) {
-      throw Error("Could not find client side for actor " + this.actorID);
-    }
-    let nodeActor = walkerActor._ref(rawNode);
-
-    // Pass the node through a read/write pair to create the client side actor.
-    let nodeType = types.getType("domnode");
-    let returnNode = nodeType.read(
-      nodeType.write(nodeActor, walkerActor), this);
-    let top = returnNode;
-    let extras = walkerActor.parents(nodeActor, {sameTypeRootTreeItem: true});
-    for (let extraActor of extras) {
-      top = nodeType.read(nodeType.write(extraActor, walkerActor), this);
-    }
-
-    if (top !== this.rootNode) {
-      // Imported an already-orphaned node.
-      this._orphaned.add(top);
-      walkerActor._orphaned
-        .add(this.conn._transport._serverConnection.getActor(top.actorID));
-    }
-    return returnNode;
-  },
-
-  removeNode: custom(Task.async(function* (node) {
-    let previousSibling = yield this.previousSibling(node);
-    let nextSibling = yield this._removeNode(node);
-    return {
-      previousSibling: previousSibling,
-      nextSibling: nextSibling,
-    };
-  }), {
-    impl: "_removeNode"
-  }),
-});
-
-exports.WalkerFront = WalkerFront;
-
-/**
- * Client side of the inspector actor, which is used to create
- * inspector-related actors, including the walker.
- */
-var InspectorFront = FrontClassWithSpec(inspectorSpec, {
-  initialize: function (client, tabForm) {
-    Front.prototype.initialize.call(this, client);
-    this.actorID = tabForm.inspectorActor;
-
-    // XXX: This is the first actor type in its hierarchy to use the protocol
-    // library, so we're going to self-own on the client side for now.
-    this.manage(this);
-  },
-
-  destroy: function () {
-    delete this.walker;
-    Front.prototype.destroy.call(this);
-  },
-
-  getWalker: custom(function (options = {}) {
-    return this._getWalker(options).then(walker => {
-      this.walker = walker;
-      return walker;
-    });
-  }, {
-    impl: "_getWalker"
-  }),
-
-  getPageStyle: custom(function () {
-    return this._getPageStyle().then(pageStyle => {
-      // We need a walker to understand node references from the
-      // node style.
-      if (this.walker) {
-        return pageStyle;
-      }
-      return this.getWalker().then(() => {
-        return pageStyle;
-      });
-    });
-  }, {
-    impl: "_getPageStyle"
-  })
-});
-
-exports.InspectorFront = InspectorFront;
deleted file mode 100644
--- a/devtools/client/fronts/moz.build
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; 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(
-    'highlighters.js',
-    'inspector.js',
-    'storage.js',
-    'styles.js',
-    'stylesheets.js'
-)
deleted file mode 100644
--- a/devtools/client/fronts/storage.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const protocol = require("devtools/shared/protocol");
-const specs = require("devtools/shared/specs/storage");
-
-for (let childSpec of Object.values(specs.childSpecs)) {
-  protocol.FrontClassWithSpec(childSpec, {
-    form(form, detail) {
-      if (detail === "actorid") {
-        this.actorID = form;
-        return null;
-      }
-
-      this.actorID = form.actor;
-      this.hosts = form.hosts;
-      return null;
-    }
-  });
-}
-
-const StorageFront = protocol.FrontClassWithSpec(specs.storageSpec, {
-  initialize(client, tabForm) {
-    protocol.Front.prototype.initialize.call(this, client);
-    this.actorID = tabForm.storageActor;
-    this.manage(this);
-  }
-});
-
-exports.StorageFront = StorageFront;
deleted file mode 100644
--- a/devtools/client/fronts/styles.js
+++ /dev/null
@@ -1,419 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-require("devtools/client/fronts/stylesheets");
-const {
-  Front,
-  FrontClassWithSpec,
-  custom,
-  preEvent
-} = require("devtools/shared/protocol.js");
-const {
-  pageStyleSpec,
-  styleRuleSpec
-} = require("devtools/shared/specs/styles.js");
-const promise = require("promise");
-const { Task } = require("resource://gre/modules/Task.jsm");
-const { Class } = require("sdk/core/heritage");
-
-loader.lazyGetter(this, "RuleRewriter", () => {
-  return require("devtools/client/shared/css-parsing-utils").RuleRewriter;
-});
-
-/**
- * PageStyleFront, the front object for the PageStyleActor
- */
-const PageStyleFront = FrontClassWithSpec(pageStyleSpec, {
-  initialize: function (conn, form, ctx, detail) {
-    Front.prototype.initialize.call(this, conn, form, ctx, detail);
-    this.inspector = this.parent();
-  },
-
-  form: function (form, detail) {
-    if (detail === "actorid") {
-      this.actorID = form;
-      return;
-    }
-    this._form = form;
-  },
-
-  destroy: function () {
-    Front.prototype.destroy.call(this);
-  },
-
-  get walker() {
-    return this.inspector.walker;
-  },
-
-  get supportsAuthoredStyles() {
-    return this._form.traits && this._form.traits.authoredStyles;
-  },
-
-  getMatchedSelectors: custom(function (node, property, options) {
-    return this._getMatchedSelectors(node, property, options).then(ret => {
-      return ret.matched;
-    });
-  }, {
-    impl: "_getMatchedSelectors"
-  }),
-
-  getApplied: custom(Task.async(function* (node, options = {}) {
-    // If the getApplied method doesn't recreate the style cache itself, this
-    // means a call to cssLogic.highlight is required before trying to access
-    // the applied rules. Issue a request to getLayout if this is the case.
-    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1103993#c16.
-    if (!this._form.traits || !this._form.traits.getAppliedCreatesStyleCache) {
-      yield this.getLayout(node);
-    }
-    let ret = yield this._getApplied(node, options);
-    return ret.entries;
-  }), {
-    impl: "_getApplied"
-  }),
-
-  addNewRule: custom(function (node, pseudoClasses) {
-    let addPromise;
-    if (this.supportsAuthoredStyles) {
-      addPromise = this._addNewRule(node, pseudoClasses, true);
-    } else {
-      addPromise = this._addNewRule(node, pseudoClasses);
-    }
-    return addPromise.then(ret => {
-      return ret.entries[0];
-    });
-  }, {
-    impl: "_addNewRule"
-  })
-});
-
-exports.PageStyleFront = PageStyleFront;
-
-/**
- * StyleRuleFront, the front for the StyleRule actor.
- */
-const StyleRuleFront = FrontClassWithSpec(styleRuleSpec, {
-  initialize: function (client, form, ctx, detail) {
-    Front.prototype.initialize.call(this, client, form, ctx, detail);
-  },
-
-  destroy: function () {
-    Front.prototype.destroy.call(this);
-  },
-
-  form: function (form, detail) {
-    if (detail === "actorid") {
-      this.actorID = form;
-      return;
-    }
-    this.actorID = form.actor;
-    this._form = form;
-    if (this._mediaText) {
-      this._mediaText = null;
-    }
-  },
-
-  /**
-   * Ensure _form is updated when location-changed is emitted.
-   */
-  _locationChangedPre: preEvent("location-changed", function (line, column) {
-    this._clearOriginalLocation();
-    this._form.line = line;
-    this._form.column = column;
-  }),
-
-  /**
-   * Return a new RuleModificationList or RuleRewriter for this node.
-   * A RuleRewriter will be returned when the rule's canSetRuleText
-   * trait is true; otherwise a RuleModificationList will be
-   * returned.
-   */
-  startModifyingProperties: function () {
-    if (this.canSetRuleText) {
-      return new RuleRewriter(this, this.authoredText);
-    }
-    return new RuleModificationList(this);
-  },
-
-  get type() {
-    return this._form.type;
-  },
-  get line() {
-    return this._form.line || -1;
-  },
-  get column() {
-    return this._form.column || -1;
-  },
-  get cssText() {
-    return this._form.cssText;
-  },
-  get authoredText() {
-    return this._form.authoredText || this._form.cssText;
-  },
-  get declarations() {
-    return this._form.declarations || [];
-  },
-  get keyText() {
-    return this._form.keyText;
-  },
-  get name() {
-    return this._form.name;
-  },
-  get selectors() {
-    return this._form.selectors;
-  },
-  get media() {
-    return this._form.media;
-  },
-  get mediaText() {
-    if (!this._form.media) {
-      return null;
-    }
-    if (this._mediaText) {
-      return this._mediaText;
-    }
-    this._mediaText = this.media.join(", ");
-    return this._mediaText;
-  },
-
-  get parentRule() {
-    return this.conn.getActor(this._form.parentRule);
-  },
-
-  get parentStyleSheet() {
-    return this.conn.getActor(this._form.parentStyleSheet);
-  },
-
-  get element() {
-    return this.conn.getActor(this._form.element);
-  },
-
-  get href() {
-    if (this._form.href) {
-      return this._form.href;
-    }
-    let sheet = this.parentStyleSheet;
-    return sheet ? sheet.href : "";
-  },
-
-  get nodeHref() {
-    let sheet = this.parentStyleSheet;
-    return sheet ? sheet.nodeHref : "";
-  },
-
-  get supportsModifySelectorUnmatched() {
-    return this._form.traits && this._form.traits.modifySelectorUnmatched;
-  },
-
-  get canSetRuleText() {
-    return this._form.traits && this._form.traits.canSetRuleText;
-  },
-
-  get location() {
-    return {
-      source: this.parentStyleSheet,
-      href: this.href,
-      line: this.line,
-      column: this.column
-    };
-  },
-
-  _clearOriginalLocation: function () {
-    this._originalLocation = null;
-  },
-
-  getOriginalLocation: function () {
-    if (this._originalLocation) {
-      return promise.resolve(this._originalLocation);
-    }
-    let parentSheet = this.parentStyleSheet;
-    if (!parentSheet) {
-      // This rule doesn't belong to a stylesheet so it is an inline style.
-      // Inline styles do not have any mediaText so we can return early.
-      return promise.resolve(this.location);
-    }
-    return parentSheet.getOriginalLocation(this.line, this.column)
-      .then(({ fromSourceMap, source, line, column }) => {
-        let location = {
-          href: source,
-          line: line,
-          column: column,
-          mediaText: this.mediaText
-        };
-        if (fromSourceMap === false) {
-          location.source = this.parentStyleSheet;
-        }
-        if (!source) {
-          location.href = this.href;
-        }
-        this._originalLocation = location;
-        return location;
-      });
-  },
-
-  modifySelector: custom(Task.async(function* (node, value) {
-    let response;
-    if (this.supportsModifySelectorUnmatched) {
-      // If the debugee supports adding unmatched rules (post FF41)
-      if (this.canSetRuleText) {
-        response = yield this.modifySelector2(node, value, true);
-      } else {
-        response = yield this.modifySelector2(node, value);
-      }
-    } else {
-      response = yield this._modifySelector(value);
-    }
-
-    if (response.ruleProps) {
-      response.ruleProps = response.ruleProps.entries[0];
-    }
-    return response;
-  }), {
-    impl: "_modifySelector"
-  }),
-
-  setRuleText: custom(function (newText) {
-    this._form.authoredText = newText;
-    return this._setRuleText(newText);
-  }, {
-    impl: "_setRuleText"
-  })
-});
-
-exports.StyleRuleFront = StyleRuleFront;
-
-/**
- * Convenience API for building a list of attribute modifications
- * for the `modifyProperties` request.  A RuleModificationList holds a
- * list of modifications that will be applied to a StyleRuleActor.
- * The modifications are processed in the order in which they are
- * added to the RuleModificationList.
- *
- * Objects of this type expose the same API as @see RuleRewriter.
- * This lets the inspector use (mostly) the same code, regardless of
- * whether the server implements setRuleText.
- */
-var RuleModificationList = Class({
-  /**
-   * Initialize a RuleModificationList.
-   * @param {StyleRuleFront} rule the associated rule
-   */
-  initialize: function (rule) {
-    this.rule = rule;
-    this.modifications = [];
-  },
-
-  /**
-   * Apply the modifications in this object to the associated rule.
-   *
-   * @return {Promise} A promise which will be resolved when the modifications
-   *         are complete; @see StyleRuleActor.modifyProperties.
-   */
-  apply: function () {
-    return this.rule.modifyProperties(this.modifications);
-  },
-
-  /**
-   * Add a "set" entry to the modification list.
-   *
-   * @param {Number} index index of the property in the rule.
-   *                       This can be -1 in the case where
-   *                       the rule does not support setRuleText;
-   *                       generally for setting properties
-   *                       on an element's style.
-   * @param {String} name the property's name
-   * @param {String} value the property's value
-   * @param {String} priority the property's priority, either the empty
-   *                          string or "important"
-   */
-  setProperty: function (index, name, value, priority) {
-    this.modifications.push({
-      type: "set",
-      name: name,
-      value: value,
-      priority: priority
-    });
-  },
-
-  /**
-   * Add a "remove" entry to the modification list.
-   *
-   * @param {Number} index index of the property in the rule.
-   *                       This can be -1 in the case where
-   *                       the rule does not support setRuleText;
-   *                       generally for setting properties
-   *                       on an element's style.
-   * @param {String} name the name of the property to remove
-   */
-  removeProperty: function (index, name) {
-    this.modifications.push({
-      type: "remove",
-      name: name
-    });
-  },
-
-  /**
-   * Rename a property.  This implementation acts like
-   * |removeProperty|, because |setRuleText| is not available.
-   *
-   * @param {Number} index index of the property in the rule.
-   *                       This can be -1 in the case where
-   *                       the rule does not support setRuleText;
-   *                       generally for setting properties
-   *                       on an element's style.
-   * @param {String} name current name of the property
-   *
-   * This parameter is also passed, but as it is not used in this
-   * implementation, it is omitted.  It is documented here as this
-   * code also defined the interface implemented by @see RuleRewriter.
-   * @param {String} newName new name of the property
-   */
-  renameProperty: function (index, name) {
-    this.removeProperty(index, name);
-  },
-
-  /**
-   * Enable or disable a property.  This implementation acts like
-   * |removeProperty| when disabling, or a no-op when enabling,
-   * because |setRuleText| is not available.
-   *
-   * @param {Number} index index of the property in the rule.
-   *                       This can be -1 in the case where
-   *                       the rule does not support setRuleText;
-   *                       generally for setting properties
-   *                       on an element's style.
-   * @param {String} name current name of the property
-   * @param {Boolean} isEnabled true if the property should be enabled;
-   *                        false if it should be disabled
-   */
-  setPropertyEnabled: function (index, name, isEnabled) {
-    if (!isEnabled) {
-      this.removeProperty(index, name);
-    }
-  },
-
-  /**
-   * Create a new property.  This implementation does nothing, because
-   * |setRuleText| is not available.
-   *
-   * These parameter are passed, but as they are not used in this
-   * implementation, they are omitted.  They are documented here as
-   * this code also defined the interface implemented by @see
-   * RuleRewriter.
-   *
-   * @param {Number} index index of the property in the rule.
-   *                       This can be -1 in the case where
-   *                       the rule does not support setRuleText;
-   *                       generally for setting properties
-   *                       on an element's style.
-   * @param {String} name name of the new property
-   * @param {String} value value of the new property
-   * @param {String} priority priority of the new property; either
-   *                          the empty string or "important"
-   */
-  createProperty: function () {
-    // Nothing.
-  },
-});
-
deleted file mode 100644
--- a/devtools/client/fronts/stylesheets.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
-const {
-  getIndentationFromPrefs,
-  getIndentationFromString
-} = require("devtools/shared/indentation");
-const {
-  originalSourceSpec,
-  mediaRuleSpec,
-  styleSheetSpec,
-  styleSheetsSpec
-} = require("devtools/shared/specs/stylesheets");
-const promise = require("promise");
-const { Task } = require("resource://gre/modules/Task.jsm");
-const events = require("sdk/event/core");
-
-/**
- * The client-side counterpart for an OriginalSourceActor.
- */
-const OriginalSourceFront = FrontClassWithSpec(originalSourceSpec, {
-  initialize: function (client, form) {
-    Front.prototype.initialize.call(this, client, form);
-
-    this.isOriginalSource = true;
-  },
-
-  form: function (form, detail) {
-    if (detail === "actorid") {
-      this.actorID = form;
-      return;
-    }
-    this.actorID = form.actor;
-    this._form = form;
-  },
-
-  get href() {
-    return this._form.url;
-  },
-  get url() {
-    return this._form.url;
-  }
-});
-
-exports.OriginalSourceFront = OriginalSourceFront;
-
-/**
- * Corresponding client-side front for a MediaRuleActor.
- */
-const MediaRuleFront = FrontClassWithSpec(mediaRuleSpec, {
-  initialize: function (client, form) {
-    Front.prototype.initialize.call(this, client, form);
-
-    this._onMatchesChange = this._onMatchesChange.bind(this);
-    events.on(this, "matches-change", this._onMatchesChange);
-  },
-
-  _onMatchesChange: function (matches) {
-    this._form.matches = matches;
-  },
-
-  form: function (form, detail) {
-    if (detail === "actorid") {
-      this.actorID = form;
-      return;
-    }
-    this.actorID = form.actor;
-    this._form = form;
-  },
-
-  get mediaText() {
-    return this._form.mediaText;
-  },
-  get conditionText() {
-    return this._form.conditionText;
-  },
-  get matches() {
-    return this._form.matches;
-  },
-  get line() {
-    return this._form.line || -1;
-  },
-  get column() {
-    return this._form.column || -1;
-  },
-  get parentStyleSheet() {
-    return this.conn.getActor(this._form.parentStyleSheet);
-  }
-});
-
-exports.MediaRuleFront = MediaRuleFront;
-
-/**
- * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
- */
-const StyleSheetFront = FrontClassWithSpec(styleSheetSpec, {
-  initialize: function (conn, form) {
-    Front.prototype.initialize.call(this, conn, form);
-
-    this._onPropertyChange = this._onPropertyChange.bind(this);
-    events.on(this, "property-change", this._onPropertyChange);
-  },
-
-  destroy: function () {
-    events.off(this, "property-change", this._onPropertyChange);
-    Front.prototype.destroy.call(this);
-  },
-
-  _onPropertyChange: function (property, value) {
-    this._form[property] = value;
-  },
-
-  form: function (form, detail) {
-    if (detail === "actorid") {
-      this.actorID = form;
-      return;
-    }
-    this.actorID = form.actor;
-    this._form = form;
-  },
-
-  get href() {
-    return this._form.href;
-  },
-  get nodeHref() {
-    return this._form.nodeHref;
-  },
-  get disabled() {
-    return !!this._form.disabled;
-  },
-  get title() {
-    return this._form.title;
-  },
-  get isSystem() {
-    return this._form.system;
-  },
-  get styleSheetIndex() {
-    return this._form.styleSheetIndex;
-  },
-  get ruleCount() {
-    return this._form.ruleCount;
-  },
-
-  /**
-   * Get the indentation to use for edits to this style sheet.
-   *
-   * @return {Promise} A promise that will resolve to a string that
-   * should be used to indent a block in this style sheet.
-   */
-  guessIndentation: function () {
-    let prefIndent = getIndentationFromPrefs();
-    if (prefIndent) {
-      let {indentUnit, indentWithTabs} = prefIndent;
-      return promise.resolve(indentWithTabs ? "\t" : " ".repeat(indentUnit));
-    }
-
-    return Task.spawn(function* () {
-      let longStr = yield this.getText();
-      let source = yield longStr.string();
-
-      let {indentUnit, indentWithTabs} = getIndentationFromString(source);
-
-      return indentWithTabs ? "\t" : " ".repeat(indentUnit);
-    }.bind(this));
-  }
-});
-
-exports.StyleSheetFront = StyleSheetFront;
-
-/**
- * The corresponding Front object for the StyleSheetsActor.
- */
-const StyleSheetsFront = FrontClassWithSpec(styleSheetsSpec, {
-  initialize: function (client, tabForm) {
-    Front.prototype.initialize.call(this, client);
-    this.actorID = tabForm.styleSheetsActor;
-    this.manage(this);
-  }
-});
-
-exports.StyleSheetsFront = StyleSheetsFront;
--- a/devtools/client/moz.build
+++ b/devtools/client/moz.build
@@ -10,17 +10,16 @@ DIRS += [
     'aboutdebugging',
     'animationinspector',
     'canvasdebugger',
     'commandline',
     'debugger',
     'dom',
     'eyedropper',
     'framework',
-    'fronts',
     'inspector',
     'jsonview',
     'locales',
     'memory',
     'netmonitor',
     'performance',
     'preferences',
     'projecteditor',
--- a/devtools/client/sourceeditor/test/browser_css_autocompletion.js
+++ b/devtools/client/sourceeditor/test/browser_css_autocompletion.js
@@ -1,16 +1,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
-const {InspectorFront} = require("devtools/client/fronts/inspector");
+const {InspectorFront} = require("devtools/shared/fronts/inspector");
 const {TargetFactory} = require("devtools/client/framework/target");
 const { Cc, Ci } = require("chrome");
 
 const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
                 "/test/css_statemachine_testcases.css";
 const TESTS_URI = "http://mochi.test:8888/browser/devtools/client" +
                   "/sourceeditor/test/css_autocompletion_tests.json";
 
--- a/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
+++ b/devtools/client/sourceeditor/test/browser_editor_autocomplete_events.js
@@ -1,15 +1,15 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {InspectorFront} = require("devtools/client/fronts/inspector");
+const {InspectorFront} = require("devtools/shared/fronts/inspector");
 const {TargetFactory} = require("devtools/client/framework/target");
 const AUTOCOMPLETION_PREF = "devtools.editor.autocomplete";
 const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
                  "<div id='baz'></div><body></html>";
 
 const wait = (delay) => new Promise(resolve => setTimeout(resolve, delay));
 
 add_task(function*() {
--- a/devtools/client/storage/panel.js
+++ b/devtools/client/storage/panel.js
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
 
 loader.lazyRequireGetter(this, "StorageFront",
-                         "devtools/client/fronts/storage", true);
+                         "devtools/shared/fronts/storage", true);
 loader.lazyRequireGetter(this, "StorageUI",
                          "devtools/client/storage/ui", true);
 
 var StoragePanel = this.StoragePanel =
 function StoragePanel(panelWin, toolbox) {
   EventEmitter.decorate(this);
 
   this._toolbox = toolbox;
--- a/devtools/client/styleeditor/styleeditor-panel.js
+++ b/devtools/client/styleeditor/styleeditor-panel.js
@@ -14,20 +14,20 @@ var {Task} = require("resource://gre/mod
 var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 var EventEmitter = require("devtools/shared/event-emitter");
 
 Cu.import("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
 /* import-globals-from StyleEditorUtil.jsm */
 Cu.import("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
 
 loader.lazyGetter(this, "StyleSheetsFront",
-  () => require("devtools/client/fronts/stylesheets").StyleSheetsFront);
+  () => require("devtools/shared/fronts/stylesheets").StyleSheetsFront);
 
 loader.lazyGetter(this, "StyleEditorFront",
-  () => require("devtools/client/fronts/styleeditor").StyleEditorFront);
+  () => require("devtools/shared/fronts/styleeditor").StyleEditorFront);
 
 var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
   EventEmitter.decorate(this);
 
   this._toolbox = toolbox;
   this._target = toolbox.target;
   this._panelWin = panelWin;
   this._panelDoc = panelWin.document;
--- a/devtools/server/tests/browser/browser_storage_dynamic_windows.js
+++ b/devtools/server/tests/browser/browser_storage_dynamic_windows.js
@@ -1,15 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {StorageFront} = require("devtools/client/fronts/storage");
+const {StorageFront} = require("devtools/shared/fronts/storage");
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
 
 const beforeReload = {
   cookies: {
     "test1.example.org": ["c1", "cs2", "c3", "uc1"],
     "sectest1.example.org": ["uc1", "cs2"]
   },
   localStorage: {
--- a/devtools/server/tests/browser/browser_storage_listings.js
+++ b/devtools/server/tests/browser/browser_storage_listings.js
@@ -1,15 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {StorageFront} = require("devtools/client/fronts/storage");
+const {StorageFront} = require("devtools/shared/fronts/storage");
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/server/tests/browser/storage-helpers.js", this);
 
 const storeMap = {
   cookies: {
     "test1.example.org": [
       {
         name: "c1",
         value: "foobar",
--- a/devtools/server/tests/browser/browser_storage_updates.js
+++ b/devtools/server/tests/browser/browser_storage_updates.js
@@ -1,15 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {StorageFront} = require("devtools/client/fronts/storage");
+const {StorageFront} = require("devtools/shared/fronts/storage");
 const beforeReload = {
   cookies: ["test1.example.org", "sectest1.example.org"],
   localStorage: ["http://test1.example.org", "http://sectest1.example.org"],
   sessionStorage: ["http://test1.example.org", "http://sectest1.example.org"],
 };
 
 const TESTS = [
   // index 0
--- a/devtools/server/tests/browser/browser_stylesheets_getTextEmpty.js
+++ b/devtools/server/tests/browser/browser_stylesheets_getTextEmpty.js
@@ -1,17 +1,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that StyleSheetActor.getText handles empty text correctly.
 
-const {StyleSheetsFront} = require("devtools/client/fronts/stylesheets");
+const {StyleSheetsFront} = require("devtools/shared/fronts/stylesheets");
 
 const CONTENT = "<style>body { background-color: #f0c; }</style>";
 const TEST_URI = "data:text/html;charset=utf-8," + encodeURIComponent(CONTENT);
 
 add_task(function*() {
   yield addTab(TEST_URI);
 
   info("Initialising the debugger server and client.");
--- a/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js
+++ b/devtools/server/tests/browser/browser_stylesheets_nested-iframes.js
@@ -2,17 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that StyleSheetsActor.getStyleSheets() works if an iframe does not have
 // a content document.
 
-const {StyleSheetsFront} = require("devtools/client/fronts/stylesheets");
+const {StyleSheetsFront} = require("devtools/shared/fronts/stylesheets");
 
 add_task(function*() {
   let browser = yield addTab(MAIN_DOMAIN + "stylesheets-nested-iframes.html");
   let doc = browser.contentDocument;
 
   info("Initialising the debugger server and client.");
   initDebuggerServer();
   let client = new DebuggerClient(DebuggerServer.connectPipe());
--- a/devtools/server/tests/browser/head.js
+++ b/devtools/server/tests/browser/head.js
@@ -45,17 +45,17 @@ var addTab = Task.async(function* (url) 
     waitForFocus(resolve, content, isBlank);
   });
 
   return tab.linkedBrowser;
 });
 
 function* initAnimationsFrontForUrl(url) {
   const {AnimationsFront} = require("devtools/server/actors/animation");
-  const {InspectorFront} = require("devtools/client/fronts/inspector");
+  const {InspectorFront} = require("devtools/shared/fronts/inspector");
 
   yield addTab(url);
 
   initDebuggerServer();
   let client = new DebuggerClient(DebuggerServer.connectPipe());
   let form = yield connectDebuggerClient(client);
   let inspector = InspectorFront(client, form);
   let walker = yield inspector.getWalker();
--- a/devtools/server/tests/mochitest/test_animation_actor-lifetime.html
+++ b/devtools/server/tests/mochitest/test_animation_actor-lifetime.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const Ci = Components.interfaces;
   const {AnimationsFront} = require("devtools/server/actors/animation");
-  const {InspectorFront} = require("devtools/client/fronts/inspector");
+  const {InspectorFront} = require("devtools/shared/fronts/inspector");
 
   SimpleTest.waitForExplicitFinish();
 
   let gWalker = null;
   let gClient = null;
   let animationsFront = null;
 
   addTest(function setup() {
--- a/devtools/server/tests/mochitest/test_inspector-anonymous.html
+++ b/devtools/server/tests/mochitest/test_inspector-anonymous.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const Ci = Components.interfaces;
   const {InspectorFront} =
-    require("devtools/client/fronts/inspector");
+    require("devtools/shared/fronts/inspector");
   const {_documentWalker} =
     require("devtools/server/actors/inspector");
   const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.webcomponents.enabled", true]
   ]});
   SimpleTest.waitForExplicitFinish();
--- a/devtools/server/tests/mochitest/test_inspector-changeattrs.html
+++ b/devtools/server/tests/mochitest/test_inspector-changeattrs.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gClient = null;
@@ -26,17 +26,17 @@ var checkActorIDs = [];
 function assertOwnership() {
   assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-changevalue.html
+++ b/devtools/server/tests/mochitest/test_inspector-changevalue.html
@@ -7,17 +7,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 const Ci = Components.interfaces;
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gClient = null;
@@ -26,17 +26,17 @@ var gWalker = null;
 function assertOwnership() {
   assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
+++ b/devtools/server/tests/mochitest/test_inspector-dead-nodes.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1121528</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker, gDoc;
 
@@ -25,17 +25,17 @@ addAsyncTest(function() {
 
   let def = promise.defer();
   attachURL(url, function(err, client, tab, doc) {
     def.resolve({client, tab, doc});
   });
   let {client, tab, doc} = yield def.promise;
   gDoc = doc;
 
-  let {InspectorFront} = require("devtools/client/fronts/inspector");
+  let {InspectorFront} = require("devtools/shared/fronts/inspector");
   let inspector = InspectorFront(client, tab);
   gWalker = yield inspector.getWalker();
 
   runNextTest();
 });
 
 addAsyncTest(function() {
   info("Getting a nodeFront, reloading the page, and calling " +
--- a/devtools/server/tests/mochitest/test_inspector-duplicate-node.html
+++ b/devtools/server/tests/mochitest/test_inspector-duplicate-node.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1208864</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gClient = null;
@@ -25,17 +25,17 @@ var gWalker = null;
 function assertOwnership() {
   assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-hide.html
+++ b/devtools/server/tests/mochitest/test_inspector-hide.html
@@ -6,31 +6,31 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker = null;
 var gClient = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-insert.html
+++ b/devtools/server/tests/mochitest/test_inspector-insert.html
@@ -24,17 +24,17 @@ var gClient = null;
 function assertOwnership() {
   return assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-mutations-attr.html
+++ b/devtools/server/tests/mochitest/test_inspector-mutations-attr.html
@@ -6,34 +6,34 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gWalker = null;
 var gClient = null;
 var attrNode;
 var attrFront;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html
+++ b/devtools/server/tests/mochitest/test_inspector-mutations-childlist.html
@@ -6,33 +6,33 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gWalker = null;
 var gClient = null;
 var gCleanupConnection = null;
 
 function setup(callback) {
   let url = document.getElementById("inspectorContent").href;
   gCleanupConnection = attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       gClient = client;
       gWalker = walker;
     }).then(callback));
   });
 }
 
--- a/devtools/server/tests/mochitest/test_inspector-mutations-events.html
+++ b/devtools/server/tests/mochitest/test_inspector-mutations-events.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 
 window.onload = function() {
 
   const Cu = Components.utils;
   Cu.import("resource://devtools/shared/Loader.jsm");
   const {InspectorFront} =
-    devtools.require("devtools/client/fronts/inspector");
+    devtools.require("devtools/shared/fronts/inspector");
 
   SimpleTest.waitForExplicitFinish();
 
   let inspectee = null;
   let inspector = null;
   let walker = null;
   let eventListener1 = function () {};
   let eventListener2 = function () {};
--- a/devtools/server/tests/mochitest/test_inspector-mutations-frameload.html
+++ b/devtools/server/tests/mochitest/test_inspector-mutations-frameload.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gWalker = null;
@@ -24,17 +24,17 @@ var gClient = null;
 var gChildFrame = null;
 var gChildDocument = null;
 var gCleanupConnection = null;
 
 function setup(callback) {
   let url = document.getElementById("inspectorContent").href;
   gCleanupConnection = attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       gClient = client;
       gWalker = walker;
     }).then(callback));
   });
 }
 
--- a/devtools/server/tests/mochitest/test_inspector-mutations-value.html
+++ b/devtools/server/tests/mochitest/test_inspector-mutations-value.html
@@ -33,17 +33,17 @@ var longString = "stringstringstringstri
 var truncatedLongString = longString.substring(0, testSummaryLength);
 var shortString = "str";
 var shortString2 = "str2";
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html
+++ b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 const DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"].
                    getService(Components.interfaces.inIDOMUtils);
 
 const KNOWN_PSEUDOCLASSES = [':hover', ':active', ':focus']
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
@@ -26,17 +26,17 @@ var gInspectee = null;
 var gWalker = null;
 var gClient = null;
 var gCleanupConnection = null;
 
 function setup(callback) {
   let url = document.getElementById("inspectorContent").href;
   gCleanupConnection = attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       gClient = client;
       gWalker = walker;
     }).then(callback));
   });
 }
 
--- a/devtools/server/tests/mochitest/test_inspector-release.html
+++ b/devtools/server/tests/mochitest/test_inspector-release.html
@@ -6,34 +6,34 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker = null;
 var gClient = null;
 
 function assertOwnership() {
   return assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-reload.html
+++ b/devtools/server/tests/mochitest/test_inspector-reload.html
@@ -6,32 +6,32 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gClient = null;
 var gWalker = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
       return inspector.getWalker();
     }).then(walker => {
       dump(walker.actorID + "\n");
--- a/devtools/server/tests/mochitest/test_inspector-remove.html
+++ b/devtools/server/tests/mochitest/test_inspector-remove.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker = null;
 var gClient = null;
@@ -30,17 +30,17 @@ function ignoreNode(node) {
   return node.nodeType === Components.interfaces.nsIDOMNode.TEXT_NODE &&
     !/[^\s]/.test(node.nodeValue);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-resize.html
+++ b/devtools/server/tests/mochitest/test_inspector-resize.html
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const Cu = Components.utils;
   Cu.import("resource://devtools/shared/Loader.jsm");
   const {Promise: promise} =
     Cu.import("resource://gre/modules/Promise.jsm", {});
   const {InspectorFront} =
-    devtools.require("devtools/client/fronts/inspector");
+    devtools.require("devtools/shared/fronts/inspector");
   const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 
   SimpleTest.waitForExplicitFinish();
 
   let win = null;
   let inspector = null;
 
   addAsyncTest(function* setup() {
--- a/devtools/server/tests/mochitest/test_inspector-resolve-url.html
+++ b/devtools/server/tests/mochitest/test_inspector-resolve-url.html
@@ -6,31 +6,31 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 921102</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspector;
 var gDoc;
 
 addTest(function() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gDoc = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     gInspector = InspectorFront(client, tab);
     runNextTest();
   });
 });
 
 addTest(function() {
   info("Resolve a relative URL without providing a context node");
   gInspector.resolveRelativeURL("test.png?id=4#wow").then(url => {
--- a/devtools/server/tests/mochitest/test_inspector-retain.html
+++ b/devtools/server/tests/mochitest/test_inspector-retain.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker = null;
 var gClient = null;
@@ -25,17 +25,17 @@ var gInspectee = null;
 function assertOwnership() {
   return assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-scroll-into-view.html
+++ b/devtools/server/tests/mochitest/test_inspector-scroll-into-view.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 901250</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gInspectee = null;
 var gClient = null;
@@ -25,17 +25,17 @@ var gWalker = null;
 function assertOwnership() {
   assertOwnershipTrees(gWalker);
 }
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector-search-front.html
+++ b/devtools/server/tests/mochitest/test_inspector-search-front.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const Cu = Components.utils;
   Cu.import("resource://devtools/shared/Loader.jsm");
   const {Promise: promise} =
     Cu.import("resource://gre/modules/Promise.jsm", {});
   const {InspectorFront} =
-    devtools.require("devtools/client/fronts/inspector");
+    devtools.require("devtools/shared/fronts/inspector");
   const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 
   SimpleTest.waitForExplicitFinish();
 
   let walkerFront = null;
   let inspectee = null;
   let inspector = null;
 
--- a/devtools/server/tests/mochitest/test_inspector-search.html
+++ b/devtools/server/tests/mochitest/test_inspector-search.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 window.onload = function() {
   const Cu = Components.utils;
   Cu.import("resource://devtools/shared/Loader.jsm");
   const {Promise: promise} =
     Cu.import("resource://gre/modules/Promise.jsm", {});
   const {InspectorFront} =
-    devtools.require("devtools/client/fronts/inspector");
+    devtools.require("devtools/shared/fronts/inspector");
   const {WalkerSearch, WalkerIndex} =
     devtools.require("devtools/server/actors/utils/walker-search");
   const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
 
   SimpleTest.waitForExplicitFinish();
 
   let walkerActor = null;
   let walkerSearch = null;
--- a/devtools/server/tests/mochitest/test_inspector-traversal.html
+++ b/devtools/server/tests/mochitest/test_inspector-traversal.html
@@ -25,17 +25,17 @@ var checkActorIDs = [];
 
 function assertOwnership() {
   assertOwnershipTrees(gWalker);
 }
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
     }).then(runNextTest));
   });
 });
--- a/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageData-wait-for-load.html
@@ -35,17 +35,17 @@ window.onload = function() {
 
 var gImg = null;
 var gNodeFront = null;
 var gWalker = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
 
     promiseDone(inspector.getWalker().then(walker => {
       gWalker = walker;
       return walker.querySelector(gWalker.rootNode, "img.custom").then(img => {
         gNodeFront = img;
         gImg = doc.querySelector("img.custom");
 
--- a/devtools/server/tests/mochitest/test_inspector_getImageData.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageData.html
@@ -6,29 +6,29 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 932937</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
 
     promiseDone(inspector.getWalker().then(walker => {
       gWalker = walker;
     }).then(runNextTest));
   });
 });
 
--- a/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
+++ b/devtools/server/tests/mochitest/test_inspector_getImageDataFromURL.html
@@ -32,17 +32,17 @@ window.onload = function() {
   runNextTest();
 }
 
 var gInspector = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     gInspector = InspectorFront(client, tab);
     runNextTest();
   });
 });
 
 addTest(function testTimeout() {
   info("Testing that the method aborts if the image takes too long to load.");
 
--- a/devtools/server/tests/mochitest/test_inspector_getNodeFromActor.html
+++ b/devtools/server/tests/mochitest/test_inspector_getNodeFromActor.html
@@ -6,29 +6,29 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1155653</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const inspector = require("devtools/client/fronts/inspector");
+const inspector = require("devtools/shared/fronts/inspector");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker;
 
 addTest(function() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
 
     promiseDone(inspector.getWalker().then(walker => {
       gWalker = walker;
     }).then(runNextTest));
   });
 });
 
--- a/devtools/server/tests/mochitest/test_styles-applied.html
+++ b/devtools/server/tests/mochitest/test_styles-applied.html
@@ -20,17 +20,17 @@ window.onload = function() {
 
 var gWalker = null;
 var gStyles = null;
 var gClient = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
       return inspector.getPageStyle();
     }).then(styles => {
       gStyles = styles;
--- a/devtools/server/tests/mochitest/test_styles-computed.html
+++ b/devtools/server/tests/mochitest/test_styles-computed.html
@@ -21,17 +21,17 @@ window.onload = function() {
 var gWalker = null;
 var gStyles = null;
 var gClient = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
       return inspector.getPageStyle();
     }).then(styles => {
       gStyles = styles;
--- a/devtools/server/tests/mochitest/test_styles-layout.html
+++ b/devtools/server/tests/mochitest/test_styles-layout.html
@@ -15,17 +15,17 @@ window.onload = function() {
 };
 
 let gWalker = null;
 let gStyles = null;
 
 addTest(function() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gWalker = walker;
       return inspector.getPageStyle();
     }).then(styles => {
       gStyles = styles;
     }).then(runNextTest));
--- a/devtools/server/tests/mochitest/test_styles-matched.html
+++ b/devtools/server/tests/mochitest/test_styles-matched.html
@@ -22,17 +22,17 @@ window.onload = function() {
 var gWalker = null;
 var gStyles = null;
 var gClient = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
     gInspectee = doc;
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
       return inspector.getPageStyle();
     }).then(styles => {
       gStyles = styles;
--- a/devtools/server/tests/mochitest/test_styles-modify.html
+++ b/devtools/server/tests/mochitest/test_styles-modify.html
@@ -25,17 +25,17 @@ var gClient = null;
 addAsyncTest(function* setup() {
   let url = document.getElementById("inspectorContent").href;
   let inspector;
 
   yield new Promise(resolve => {
     attachURL(url, function(err, client, tab, doc) {
       gInspectee = doc;
       gClient = client;
-      let {InspectorFront} = require("devtools/client/fronts/inspector");
+      let {InspectorFront} = require("devtools/shared/fronts/inspector");
       inspector = InspectorFront(client, tab);
       resolve();
     });
   });
 
   gWalker = yield inspector.getWalker();
   gStyles = yield inspector.getPageStyle();
 
--- a/devtools/server/tests/mochitest/test_styles-svg.html
+++ b/devtools/server/tests/mochitest/test_styles-svg.html
@@ -21,17 +21,17 @@ window.onload = function() {
 
 var gWalker = null;
 var gStyles = null;
 var gClient = null;
 
 addTest(function setup() {
   let url = document.getElementById("inspectorContent").href;
   attachURL(url, function(err, client, tab, doc) {
-    let {InspectorFront} = require("devtools/client/fronts/inspector");
+    let {InspectorFront} = require("devtools/shared/fronts/inspector");
     let inspector = InspectorFront(client, tab);
     promiseDone(inspector.getWalker().then(walker => {
       ok(walker, "getWalker() should return an actor.");
       gClient = client;
       gWalker = walker;
       return inspector.getPageStyle();
     }).then(styles => {
       gStyles = styles;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/highlighters.js
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { FrontClassWithSpec } = require("devtools/shared/protocol");
+const {
+  customHighlighterSpec,
+  highlighterSpec
+} = require("devtools/shared/specs/highlighters");
+
+const HighlighterFront = FrontClassWithSpec(highlighterSpec, {
+  // Update the object given a form representation off the wire.
+  form: function (json) {
+    this.actorID = json.actor;
+    // FF42+ HighlighterActors starts exposing custom form, with traits object
+    this.traits = json.traits || {};
+  }
+});
+
+exports.HighlighterFront = HighlighterFront;
+
+const CustomHighlighterFront = FrontClassWithSpec(customHighlighterSpec, {});
+
+exports.CustomHighlighterFront = CustomHighlighterFront;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/inspector.js
@@ -0,0 +1,976 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const Services = require("Services");
+const { Ci } = require("chrome");
+require("devtools/shared/fronts/styles");
+require("devtools/shared/fronts/highlighters");
+const { ShortLongString } = require("devtools/server/actors/string");
+const {
+  Front,
+  FrontClassWithSpec,
+  custom,
+  preEvent,
+  types
+} = require("devtools/shared/protocol.js");
+const { makeInfallible } = require("devtools/shared/DevToolsUtils");
+const {
+  inspectorSpec,
+  nodeSpec,
+  nodeListSpec,
+  walkerSpec
+} = require("devtools/shared/specs/inspector");
+const promise = require("promise");
+const { Task } = require("resource://gre/modules/Task.jsm");
+const { Class } = require("sdk/core/heritage");
+const events = require("sdk/event/core");
+const object = require("sdk/util/object");
+
+const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
+
+/**
+ * Convenience API for building a list of attribute modifications
+ * for the `modifyAttributes` request.
+ */
+const AttributeModificationList = Class({
+  initialize: function (node) {
+    this.node = node;
+    this.modifications = [];
+  },
+
+  apply: function () {
+    let ret = this.node.modifyAttributes(this.modifications);
+    return ret;
+  },
+
+  destroy: function () {
+    this.node = null;
+    this.modification = null;
+  },
+
+  setAttributeNS: function (ns, name, value) {
+    this.modifications.push({
+      attributeNamespace: ns,
+      attributeName: name,
+      newValue: value
+    });
+  },
+
+  setAttribute: function (name, value) {
+    this.setAttributeNS(undefined, name, value);
+  },
+
+  removeAttributeNS: function (ns, name) {
+    this.setAttributeNS(ns, name, undefined);
+  },
+
+  removeAttribute: function (name) {
+    this.setAttributeNS(undefined, name, undefined);
+  }
+});
+
+// A resolve that hits the main loop first.
+function delayedResolve(value) {
+  let deferred = promise.defer();
+  Services.tm.mainThread.dispatch(makeInfallible(() => {
+    deferred.resolve(value);
+  }), 0);
+  return deferred.promise;
+}
+
+/**
+ * Client side of the node actor.
+ *
+ * Node fronts are strored in a tree that mirrors the DOM tree on the
+ * server, but with a few key differences:
+ *  - Not all children will be necessary loaded for each node.
+ *  - The order of children isn't guaranteed to be the same as the DOM.
+ * Children are stored in a doubly-linked list, to make addition/removal
+ * and traversal quick.
+ *
+ * Due to the order/incompleteness of the child list, it is safe to use
+ * the parent node from clients, but the `children` request should be used
+ * to traverse children.
+ */
+const NodeFront = FrontClassWithSpec(nodeSpec, {
+  initialize: function (conn, form, detail, ctx) {
+    // The parent node
+    this._parent = null;
+    // The first child of this node.
+    this._child = null;
+    // The next sibling of this node.
+    this._next = null;
+    // The previous sibling of this node.
+    this._prev = null;
+    Front.prototype.initialize.call(this, conn, form, detail, ctx);
+  },
+
+  /**
+   * Destroy a node front.  The node must have been removed from the
+   * ownership tree before this is called, unless the whole walker front
+   * is being destroyed.
+   */
+  destroy: function () {
+    Front.prototype.destroy.call(this);
+  },
+
+  // Update the object given a form representation off the wire.
+  form: function (form, detail, ctx) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    // Shallow copy of the form.  We could just store a reference, but
+    // eventually we'll want to update some of the data.
+    this._form = object.merge(form);
+    this._form.attrs = this._form.attrs ? this._form.attrs.slice() : [];
+
+    if (form.parent) {
+      // Get the owner actor for this actor (the walker), and find the
+      // parent node of this actor from it, creating a standin node if
+      // necessary.
+      let parentNodeFront = ctx.marshallPool().ensureParentFront(form.parent);
+      this.reparent(parentNodeFront);
+    }
+
+    if (form.singleTextChild) {
+      this.singleTextChild =
+        types.getType("domnode").read(form.singleTextChild, ctx);
+    } else {
+      this.singleTextChild = undefined;
+    }
+  },
+
+  /**
+   * Returns the parent NodeFront for this NodeFront.
+   */
+  parentNode: function () {
+    return this._parent;
+  },
+
+  /**
+   * Process a mutation entry as returned from the walker's `getMutations`
+   * request.  Only tries to handle changes of the node's contents
+   * themselves (character data and attribute changes), the walker itself
+   * will keep the ownership tree up to date.
+   */
+  updateMutation: function (change) {
+    if (change.type === "attributes") {
+      // We'll need to lazily reparse the attributes after this change.
+      this._attrMap = undefined;
+
+      // Update any already-existing attributes.
+      let found = false;
+      for (let i = 0; i < this.attributes.length; i++) {
+        let attr = this.attributes[i];
+        if (attr.name == change.attributeName &&
+            attr.namespace == change.attributeNamespace) {
+          if (change.newValue !== null) {
+            attr.value = change.newValue;
+          } else {
+            this.attributes.splice(i, 1);
+          }
+          found = true;
+          break;
+        }
+      }
+      // This is a new attribute. The null check is because of Bug 1192270,
+      // in the case of a newly added then removed attribute
+      if (!found && change.newValue !== null) {
+        this.attributes.push({
+          name: change.attributeName,
+          namespace: change.attributeNamespace,
+          value: change.newValue
+        });
+      }
+    } else if (change.type === "characterData") {
+      this._form.shortValue = change.newValue;
+      this._form.incompleteValue = change.incompleteValue;
+    } else if (change.type === "pseudoClassLock") {
+      this._form.pseudoClassLocks = change.pseudoClassLocks;
+    } else if (change.type === "events") {
+      this._form.hasEventListeners = change.hasEventListeners;
+    }
+  },
+
+  // Some accessors to make NodeFront feel more like an nsIDOMNode
+
+  get id() {
+    return this.getAttribute("id");
+  },
+
+  get nodeType() {
+    return this._form.nodeType;
+  },
+  get namespaceURI() {
+    return this._form.namespaceURI;
+  },
+  get nodeName() {
+    return this._form.nodeName;
+  },
+  get doctypeString() {
+    return "<!DOCTYPE " + this._form.name +
+     (this._form.publicId ? " PUBLIC \"" + this._form.publicId + "\"" : "") +
+     (this._form.systemId ? " \"" + this._form.systemId + "\"" : "") +
+     ">";
+  },
+
+  get baseURI() {
+    return this._form.baseURI;
+  },
+
+  get className() {
+    return this.getAttribute("class") || "";
+  },
+
+  get hasChildren() {
+    return this._form.numChildren > 0;
+  },
+  get numChildren() {
+    return this._form.numChildren;
+  },
+  get hasEventListeners() {
+    return this._form.hasEventListeners;
+  },
+
+  get isBeforePseudoElement() {
+    return this._form.isBeforePseudoElement;
+  },
+  get isAfterPseudoElement() {
+    return this._form.isAfterPseudoElement;
+  },
+  get isPseudoElement() {
+    return this.isBeforePseudoElement || this.isAfterPseudoElement;
+  },
+  get isAnonymous() {
+    return this._form.isAnonymous;
+  },
+  get isInHTMLDocument() {
+    return this._form.isInHTMLDocument;
+  },
+  get tagName() {
+    return this.nodeType === Ci.nsIDOMNode.ELEMENT_NODE ? this.nodeName : null;
+  },
+  get shortValue() {
+    return this._form.shortValue;
+  },
+  get incompleteValue() {
+    return !!this._form.incompleteValue;
+  },
+
+  get isDocumentElement() {
+    return !!this._form.isDocumentElement;
+  },
+
+  // doctype properties
+  get name() {
+    return this._form.name;
+  },
+  get publicId() {
+    return this._form.publicId;
+  },
+  get systemId() {
+    return this._form.systemId;
+  },
+
+  getAttribute: function (name) {
+    let attr = this._getAttribute(name);
+    return attr ? attr.value : null;
+  },
+  hasAttribute: function (name) {
+    this._cacheAttributes();
+    return (name in this._attrMap);
+  },
+
+  get hidden() {
+    let cls = this.getAttribute("class");
+    return cls && cls.indexOf(HIDDEN_CLASS) > -1;
+  },
+
+  get attributes() {
+    return this._form.attrs;
+  },
+
+  get pseudoClassLocks() {
+    return this._form.pseudoClassLocks || [];
+  },
+  hasPseudoClassLock: function (pseudo) {
+    return this.pseudoClassLocks.some(locked => locked === pseudo);
+  },
+
+  get isDisplayed() {
+    // The NodeActor's form contains the isDisplayed information as a boolean
+    // starting from FF32. Before that, the property is missing
+    return "isDisplayed" in this._form ? this._form.isDisplayed : true;
+  },
+
+  get isTreeDisplayed() {
+    let parent = this;
+    while (parent) {
+      if (!parent.isDisplayed) {
+        return false;
+      }
+      parent = parent.parentNode();
+    }
+    return true;
+  },
+
+  getNodeValue: custom(function () {
+    if (!this.incompleteValue) {
+      return delayedResolve(new ShortLongString(this.shortValue));
+    }
+
+    return this._getNodeValue();
+  }, {
+    impl: "_getNodeValue"
+  }),
+
+  // Accessors for custom form properties.
+
+  getFormProperty: function (name) {
+    return this._form.props ? this._form.props[name] : null;
+  },
+
+  hasFormProperty: function (name) {
+    return this._form.props ? (name in this._form.props) : null;
+  },
+
+  get formProperties() {
+    return this._form.props;
+  },
+
+  /**
+   * Return a new AttributeModificationList for this node.
+   */
+  startModifyingAttributes: function () {
+    return AttributeModificationList(this);
+  },
+
+  _cacheAttributes: function () {
+    if (typeof this._attrMap != "undefined") {
+      return;
+    }
+    this._attrMap = {};
+    for (let attr of this.attributes) {
+      this._attrMap[attr.name] = attr;
+    }
+  },
+
+  _getAttribute: function (name) {
+    this._cacheAttributes();
+    return this._attrMap[name] || undefined;
+  },
+
+  /**
+   * Set this node's parent.  Note that the children saved in
+   * this tree are unordered and incomplete, so shouldn't be used
+   * instead of a `children` request.
+   */
+  reparent: function (parent) {
+    if (this._parent === parent) {
+      return;
+    }
+
+    if (this._parent && this._parent._child === this) {
+      this._parent._child = this._next;
+    }
+    if (this._prev) {
+      this._prev._next = this._next;
+    }
+    if (this._next) {
+      this._next._prev = this._prev;
+    }
+    this._next = null;
+    this._prev = null;
+    this._parent = parent;
+    if (!parent) {
+      // Subtree is disconnected, we're done
+      return;
+    }
+    this._next = parent._child;
+    if (this._next) {
+      this._next._prev = this;
+    }
+    parent._child = this;
+  },
+
+  /**
+   * Return all the known children of this node.
+   */
+  treeChildren: function () {
+    let ret = [];
+    for (let child = this._child; child != null; child = child._next) {
+      ret.push(child);
+    }
+    return ret;
+  },
+
+  /**
+   * Do we use a local target?
+   * Useful to know if a rawNode is available or not.
+   *
+   * This will, one day, be removed. External code should
+   * not need to know if the target is remote or not.
+   */
+  isLocalToBeDeprecated: function () {
+    return !!this.conn._transport._serverConnection;
+  },
+
+  /**
+   * Get an nsIDOMNode for the given node front.  This only works locally,
+   * and is only intended as a stopgap during the transition to the remote
+   * protocol.  If you depend on this you're likely to break soon.
+   */
+  rawNode: function (rawNode) {
+    if (!this.conn._transport._serverConnection) {
+      console.warn("Tried to use rawNode on a remote connection.");
+      return null;
+    }
+    let actor = this.conn._transport._serverConnection.getActor(this.actorID);
+    if (!actor) {
+      // Can happen if we try to get the raw node for an already-expired
+      // actor.
+      return null;
+    }
+    return actor.rawNode;
+  }
+});
+
+exports.NodeFront = NodeFront;
+
+/**
+ * Client side of a node list as returned by querySelectorAll()
+ */
+const NodeListFront = FrontClassWithSpec(nodeListSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client, form);
+  },
+
+  destroy: function () {
+    Front.prototype.destroy.call(this);
+  },
+
+  marshallPool: function () {
+    return this.parent();
+  },
+
+  // Update the object given a form representation off the wire.
+  form: function (json) {
+    this.length = json.length;
+  },
+
+  item: custom(function (index) {
+    return this._item(index).then(response => {
+      return response.node;
+    });
+  }, {
+    impl: "_item"
+  }),
+
+  items: custom(function (start, end) {
+    return this._items(start, end).then(response => {
+      return response.nodes;
+    });
+  }, {
+    impl: "_items"
+  })
+});
+
+exports.NodeListFront = NodeListFront;
+
+/**
+ * Client side of the DOM walker.
+ */
+const WalkerFront = FrontClassWithSpec(walkerSpec, {
+  // Set to true if cleanup should be requested after every mutation list.
+  autoCleanup: true,
+
+  /**
+   * This is kept for backward-compatibility reasons with older remote target.
+   * Targets previous to bug 916443
+   */
+  pick: custom(function () {
+    return this._pick().then(response => {
+      return response.node;
+    });
+  }, {impl: "_pick"}),
+
+  initialize: function (client, form) {
+    this._createRootNodePromise();
+    Front.prototype.initialize.call(this, client, form);
+    this._orphaned = new Set();
+    this._retainedOrphans = new Set();
+  },
+
+  destroy: function () {
+    Front.prototype.destroy.call(this);
+  },
+
+  // Update the object given a form representation off the wire.
+  form: function (json) {
+    this.actorID = json.actor;
+    this.rootNode = types.getType("domnode").read(json.root, this);
+    this._rootNodeDeferred.resolve(this.rootNode);
+    // FF42+ the actor starts exposing traits
+    this.traits = json.traits || {};
+  },
+
+  /**
+   * Clients can use walker.rootNode to get the current root node of the
+   * walker, but during a reload the root node might be null.  This
+   * method returns a promise that will resolve to the root node when it is
+   * set.
+   */
+  getRootNode: function () {
+    return this._rootNodeDeferred.promise;
+  },
+
+  /**
+   * Create the root node promise, triggering the "new-root" notification
+   * on resolution.
+   */
+  _createRootNodePromise: function () {
+    this._rootNodeDeferred = promise.defer();
+    this._rootNodeDeferred.promise.then(() => {
+      events.emit(this, "new-root");
+    });
+  },
+
+  /**
+   * When reading an actor form off the wire, we want to hook it up to its
+   * parent front.  The protocol guarantees that the parent will be seen
+   * by the client in either a previous or the current request.
+   * So if we've already seen this parent return it, otherwise create
+   * a bare-bones stand-in node.  The stand-in node will be updated
+   * with a real form by the end of the deserialization.
+   */
+  ensureParentFront: function (id) {
+    let front = this.get(id);
+    if (front) {
+      return front;
+    }
+
+    return types.getType("domnode").read({ actor: id }, this, "standin");
+  },
+
+  /**
+   * See the documentation for WalkerActor.prototype.retainNode for
+   * information on retained nodes.
+   *
+   * From the client's perspective, `retainNode` can fail if the node in
+   * question is removed from the ownership tree before the `retainNode`
+   * request reaches the server.  This can only happen if the client has
+   * asked the server to release nodes but hasn't gotten a response
+   * yet: Either a `releaseNode` request or a `getMutations` with `cleanup`
+   * set is outstanding.
+   *
+   * If either of those requests is outstanding AND releases the retained
+   * node, this request will fail with noSuchActor, but the ownership tree
+   * will stay in a consistent state.
+   *
+   * Because the protocol guarantees that requests will be processed and
+   * responses received in the order they were sent, we get the right
+   * semantics by setting our local retained flag on the node only AFTER
+   * a SUCCESSFUL retainNode call.
+   */
+  retainNode: custom(function (node) {
+    return this._retainNode(node).then(() => {
+      node.retained = true;
+    });
+  }, {
+    impl: "_retainNode",
+  }),
+
+  unretainNode: custom(function (node) {
+    return this._unretainNode(node).then(() => {
+      node.retained = false;
+      if (this._retainedOrphans.has(node)) {
+        this._retainedOrphans.delete(node);
+        this._releaseFront(node);
+      }
+    });
+  }, {
+    impl: "_unretainNode"
+  }),
+
+  releaseNode: custom(function (node, options = {}) {
+    // NodeFront.destroy will destroy children in the ownership tree too,
+    // mimicking what the server will do here.
+    let actorID = node.actorID;
+    this._releaseFront(node, !!options.force);
+    return this._releaseNode({ actorID: actorID });
+  }, {
+    impl: "_releaseNode"
+  }),
+
+  findInspectingNode: custom(function () {
+    return this._findInspectingNode().then(response => {
+      return response.node;
+    });
+  }, {
+    impl: "_findInspectingNode"
+  }),
+
+  querySelector: custom(function (queryNode, selector) {
+    return this._querySelector(queryNode, selector).then(response => {
+      return response.node;
+    });
+  }, {
+    impl: "_querySelector"
+  }),
+
+  getNodeActorFromObjectActor: custom(function (objectActorID) {
+    return this._getNodeActorFromObjectActor(objectActorID).then(response => {
+      return response ? response.node : null;
+    });
+  }, {
+    impl: "_getNodeActorFromObjectActor"
+  }),
+
+  getStyleSheetOwnerNode: custom(function (styleSheetActorID) {
+    return this._getStyleSheetOwnerNode(styleSheetActorID).then(response => {
+      return response ? response.node : null;
+    });
+  }, {
+    impl: "_getStyleSheetOwnerNode"
+  }),
+
+  getNodeFromActor: custom(function (actorID, path) {
+    return this._getNodeFromActor(actorID, path).then(response => {
+      return response ? response.node : null;
+    });
+  }, {
+    impl: "_getNodeFromActor"
+  }),
+
+  /*
+   * Incrementally search the document for a given string.
+   * For modern servers, results will be searched with using the WalkerActor
+   * `search` function (includes tag names, attributes, and text contents).
+   * Only 1 result is sent back, and calling the method again with the same
+   * query will send the next result. When there are no more results to be sent
+   * back, null is sent.
+   * @param {String} query
+   * @param {Object} options
+   *    - "reverse": search backwards
+   *    - "selectorOnly": treat input as a selector string (don't search text
+   *                      tags, attributes, etc)
+   */
+  search: custom(Task.async(function* (query, options = { }) {
+    let nodeList;
+    let searchType;
+    let searchData = this.searchData = this.searchData || { };
+    let selectorOnly = !!options.selectorOnly;
+
+    // Backwards compat.  Use selector only search if the new
+    // search functionality isn't implemented, or if the caller (tests)
+    // want it.
+    if (selectorOnly || !this.traits.textSearch) {
+      searchType = "selector";
+      if (this.traits.multiFrameQuerySelectorAll) {
+        nodeList = yield this.multiFrameQuerySelectorAll(query);
+      } else {
+        nodeList = yield this.querySelectorAll(this.rootNode, query);
+      }
+    } else {
+      searchType = "search";
+      let result = yield this._search(query, options);
+      nodeList = result.list;
+    }
+
+    // If this is a new search, start at the beginning.
+    if (searchData.query !== query ||
+        searchData.selectorOnly !== selectorOnly) {
+      searchData.selectorOnly = selectorOnly;
+      searchData.query = query;
+      searchData.index = -1;
+    }
+
+    if (!nodeList.length) {
+      return null;
+    }
+
+    // Move search result cursor and cycle if necessary.
+    searchData.index = options.reverse ? searchData.index - 1 :
+                                         searchData.index + 1;
+    if (searchData.index >= nodeList.length) {
+      searchData.index = 0;
+    }
+    if (searchData.index < 0) {
+      searchData.index = nodeList.length - 1;
+    }
+
+    // Send back the single node, along with any relevant search data
+    let node = yield nodeList.item(searchData.index);
+    return {
+      type: searchType,
+      node: node,
+      resultsLength: nodeList.length,
+      resultsIndex: searchData.index,
+    };
+  }), {
+    impl: "_search"
+  }),
+
+  _releaseFront: function (node, force) {
+    if (node.retained && !force) {
+      node.reparent(null);
+      this._retainedOrphans.add(node);
+      return;
+    }
+
+    if (node.retained) {
+      // Forcing a removal.
+      this._retainedOrphans.delete(node);
+    }
+
+    // Release any children
+    for (let child of node.treeChildren()) {
+      this._releaseFront(child, force);
+    }
+
+    // All children will have been removed from the node by this point.
+    node.reparent(null);
+    node.destroy();
+  },
+
+  /**
+   * Get any unprocessed mutation records and process them.
+   */
+  getMutations: custom(function (options = {}) {
+    return this._getMutations(options).then(mutations => {
+      let emitMutations = [];
+      for (let change of mutations) {
+        // The target is only an actorID, get the associated front.
+        let targetID;
+        let targetFront;
+
+        if (change.type === "newRoot") {
+          this.rootNode = types.getType("domnode").read(change.target, this);
+          this._rootNodeDeferred.resolve(this.rootNode);
+          targetID = this.rootNode.actorID;
+          targetFront = this.rootNode;
+        } else {
+          targetID = change.target;
+          targetFront = this.get(targetID);
+        }
+
+        if (!targetFront) {
+          console.trace("Got a mutation for an unexpected actor: " + targetID +
+            ", please file a bug on bugzilla.mozilla.org!");
+          continue;
+        }
+
+        let emittedMutation = object.merge(change, { target: targetFront });
+
+        if (change.type === "childList" ||
+            change.type === "nativeAnonymousChildList") {
+          // Update the ownership tree according to the mutation record.
+          let addedFronts = [];
+          let removedFronts = [];
+          for (let removed of change.removed) {
+            let removedFront = this.get(removed);
+            if (!removedFront) {
+              console.error("Got a removal of an actor we didn't know about: " +
+                removed);
+              continue;
+            }
+            // Remove from the ownership tree
+            removedFront.reparent(null);
+
+            // This node is orphaned unless we get it in the 'added' list
+            // eventually.
+            this._orphaned.add(removedFront);
+            removedFronts.push(removedFront);
+          }
+          for (let added of change.added) {
+            let addedFront = this.get(added);
+            if (!addedFront) {
+              console.error("Got an addition of an actor we didn't know " +
+                "about: " + added);
+              continue;
+            }
+            addedFront.reparent(targetFront);
+
+            // The actor is reconnected to the ownership tree, unorphan
+            // it.
+            this._orphaned.delete(addedFront);
+            addedFronts.push(addedFront);
+          }
+
+          if (change.singleTextChild) {
+            targetFront.singleTextChild =
+              types.getType("domnode").read(change.singleTextChild, this);
+          } else {
+            targetFront.singleTextChild = undefined;
+          }
+
+          // Before passing to users, replace the added and removed actor
+          // ids with front in the mutation record.
+          emittedMutation.added = addedFronts;
+          emittedMutation.removed = removedFronts;
+
+          // If this is coming from a DOM mutation, the actor's numChildren
+          // was passed in. Otherwise, it is simulated from a frame load or
+          // unload, so don't change the front's form.
+          if ("numChildren" in change) {
+            targetFront._form.numChildren = change.numChildren;
+          }
+        } else if (change.type === "frameLoad") {
+          // Nothing we need to do here, except verify that we don't have any
+          // document children, because we should have gotten a documentUnload
+          // first.
+          for (let child of targetFront.treeChildren()) {
+            if (child.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
+              console.trace("Got an unexpected frameLoad in the inspector, " +
+                "please file a bug on bugzilla.mozilla.org!");
+            }
+          }
+        } else if (change.type === "documentUnload") {
+          if (targetFront === this.rootNode) {
+            this._createRootNodePromise();
+          }
+
+          // We try to give fronts instead of actorIDs, but these fronts need
+          // to be destroyed now.
+          emittedMutation.target = targetFront.actorID;
+          emittedMutation.targetParent = targetFront.parentNode();
+
+          // Release the document node and all of its children, even retained.
+          this._releaseFront(targetFront, true);
+        } else if (change.type === "unretained") {
+          // Retained orphans were force-released without the intervention of
+          // client (probably a navigated frame).
+          for (let released of change.nodes) {
+            let releasedFront = this.get(released);
+            this._retainedOrphans.delete(released);
+            this._releaseFront(releasedFront, true);
+          }
+        } else {
+          targetFront.updateMutation(change);
+        }
+
+        emitMutations.push(emittedMutation);
+      }
+
+      if (options.cleanup) {
+        for (let node of this._orphaned) {
+          // This will move retained nodes to this._retainedOrphans.
+          this._releaseFront(node);
+        }
+        this._orphaned = new Set();
+      }
+
+      events.emit(this, "mutations", emitMutations);
+    });
+  }, {
+    impl: "_getMutations"
+  }),
+
+  /**
+   * Handle the `new-mutations` notification by fetching the
+   * available mutation records.
+   */
+  onMutations: preEvent("new-mutations", function () {
+    // Fetch and process the mutations.
+    this.getMutations({cleanup: this.autoCleanup}).catch(() => {});
+  }),
+
+  isLocal: function () {
+    return !!this.conn._transport._serverConnection;
+  },
+
+  // XXX hack during transition to remote inspector: get a proper NodeFront
+  // for a given local node.  Only works locally.
+  frontForRawNode: function (rawNode) {
+    if (!this.isLocal()) {
+      console.warn("Tried to use frontForRawNode on a remote connection.");
+      return null;
+    }
+    let walkerActor = this.conn._transport._serverConnection
+      .getActor(this.actorID);
+    if (!walkerActor) {
+      throw Error("Could not find client side for actor " + this.actorID);
+    }
+    let nodeActor = walkerActor._ref(rawNode);
+
+    // Pass the node through a read/write pair to create the client side actor.
+    let nodeType = types.getType("domnode");
+    let returnNode = nodeType.read(
+      nodeType.write(nodeActor, walkerActor), this);
+    let top = returnNode;
+    let extras = walkerActor.parents(nodeActor, {sameTypeRootTreeItem: true});
+    for (let extraActor of extras) {
+      top = nodeType.read(nodeType.write(extraActor, walkerActor), this);
+    }
+
+    if (top !== this.rootNode) {
+      // Imported an already-orphaned node.
+      this._orphaned.add(top);
+      walkerActor._orphaned
+        .add(this.conn._transport._serverConnection.getActor(top.actorID));
+    }
+    return returnNode;
+  },
+
+  removeNode: custom(Task.async(function* (node) {
+    let previousSibling = yield this.previousSibling(node);
+    let nextSibling = yield this._removeNode(node);
+    return {
+      previousSibling: previousSibling,
+      nextSibling: nextSibling,
+    };
+  }), {
+    impl: "_removeNode"
+  }),
+});
+
+exports.WalkerFront = WalkerFront;
+
+/**
+ * Client side of the inspector actor, which is used to create
+ * inspector-related actors, including the walker.
+ */
+var InspectorFront = FrontClassWithSpec(inspectorSpec, {
+  initialize: function (client, tabForm) {
+    Front.prototype.initialize.call(this, client);
+    this.actorID = tabForm.inspectorActor;
+
+    // XXX: This is the first actor type in its hierarchy to use the protocol
+    // library, so we're going to self-own on the client side for now.
+    this.manage(this);
+  },
+
+  destroy: function () {
+    delete this.walker;
+    Front.prototype.destroy.call(this);
+  },
+
+  getWalker: custom(function (options = {}) {
+    return this._getWalker(options).then(walker => {
+      this.walker = walker;
+      return walker;
+    });
+  }, {
+    impl: "_getWalker"
+  }),
+
+  getPageStyle: custom(function () {
+    return this._getPageStyle().then(pageStyle => {
+      // We need a walker to understand node references from the
+      // node style.
+      if (this.walker) {
+        return pageStyle;
+      }
+      return this.getWalker().then(() => {
+        return pageStyle;
+      });
+    });
+  }, {
+    impl: "_getPageStyle"
+  })
+});
+
+exports.InspectorFront = InspectorFront;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; c-basic-offset: 4; 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(
+    'highlighters.js',
+    'inspector.js',
+    'storage.js',
+    'styles.js',
+    'stylesheets.js'
+)
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/storage.js
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const protocol = require("devtools/shared/protocol");
+const specs = require("devtools/shared/specs/storage");
+
+for (let childSpec of Object.values(specs.childSpecs)) {
+  protocol.FrontClassWithSpec(childSpec, {
+    form(form, detail) {
+      if (detail === "actorid") {
+        this.actorID = form;
+        return null;
+      }
+
+      this.actorID = form.actor;
+      this.hosts = form.hosts;
+      return null;
+    }
+  });
+}
+
+const StorageFront = protocol.FrontClassWithSpec(specs.storageSpec, {
+  initialize(client, tabForm) {
+    protocol.Front.prototype.initialize.call(this, client);
+    this.actorID = tabForm.storageActor;
+    this.manage(this);
+  }
+});
+
+exports.StorageFront = StorageFront;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/styles.js
@@ -0,0 +1,419 @@
+/* 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";
+
+require("devtools/shared/fronts/stylesheets");
+const {
+  Front,
+  FrontClassWithSpec,
+  custom,
+  preEvent
+} = require("devtools/shared/protocol.js");
+const {
+  pageStyleSpec,
+  styleRuleSpec
+} = require("devtools/shared/specs/styles.js");
+const promise = require("promise");
+const { Task } = require("resource://gre/modules/Task.jsm");
+const { Class } = require("sdk/core/heritage");
+
+loader.lazyGetter(this, "RuleRewriter", () => {
+  return require("devtools/client/shared/css-parsing-utils").RuleRewriter;
+});
+
+/**
+ * PageStyleFront, the front object for the PageStyleActor
+ */
+const PageStyleFront = FrontClassWithSpec(pageStyleSpec, {
+  initialize: function (conn, form, ctx, detail) {
+    Front.prototype.initialize.call(this, conn, form, ctx, detail);
+    this.inspector = this.parent();
+  },
+
+  form: function (form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    this._form = form;
+  },
+
+  destroy: function () {
+    Front.prototype.destroy.call(this);
+  },
+
+  get walker() {
+    return this.inspector.walker;
+  },
+
+  get supportsAuthoredStyles() {
+    return this._form.traits && this._form.traits.authoredStyles;
+  },
+
+  getMatchedSelectors: custom(function (node, property, options) {
+    return this._getMatchedSelectors(node, property, options).then(ret => {
+      return ret.matched;
+    });
+  }, {
+    impl: "_getMatchedSelectors"
+  }),
+
+  getApplied: custom(Task.async(function* (node, options = {}) {
+    // If the getApplied method doesn't recreate the style cache itself, this
+    // means a call to cssLogic.highlight is required before trying to access
+    // the applied rules. Issue a request to getLayout if this is the case.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1103993#c16.
+    if (!this._form.traits || !this._form.traits.getAppliedCreatesStyleCache) {
+      yield this.getLayout(node);
+    }
+    let ret = yield this._getApplied(node, options);
+    return ret.entries;
+  }), {
+    impl: "_getApplied"
+  }),
+
+  addNewRule: custom(function (node, pseudoClasses) {
+    let addPromise;
+    if (this.supportsAuthoredStyles) {
+      addPromise = this._addNewRule(node, pseudoClasses, true);
+    } else {
+      addPromise = this._addNewRule(node, pseudoClasses);
+    }
+    return addPromise.then(ret => {
+      return ret.entries[0];
+    });
+  }, {
+    impl: "_addNewRule"
+  })
+});
+
+exports.PageStyleFront = PageStyleFront;
+
+/**
+ * StyleRuleFront, the front for the StyleRule actor.
+ */
+const StyleRuleFront = FrontClassWithSpec(styleRuleSpec, {
+  initialize: function (client, form, ctx, detail) {
+    Front.prototype.initialize.call(this, client, form, ctx, detail);
+  },
+
+  destroy: function () {
+    Front.prototype.destroy.call(this);
+  },
+
+  form: function (form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    this.actorID = form.actor;
+    this._form = form;
+    if (this._mediaText) {
+      this._mediaText = null;
+    }
+  },
+
+  /**
+   * Ensure _form is updated when location-changed is emitted.
+   */
+  _locationChangedPre: preEvent("location-changed", function (line, column) {
+    this._clearOriginalLocation();
+    this._form.line = line;
+    this._form.column = column;
+  }),
+
+  /**
+   * Return a new RuleModificationList or RuleRewriter for this node.
+   * A RuleRewriter will be returned when the rule's canSetRuleText
+   * trait is true; otherwise a RuleModificationList will be
+   * returned.
+   */
+  startModifyingProperties: function () {
+    if (this.canSetRuleText) {
+      return new RuleRewriter(this, this.authoredText);
+    }
+    return new RuleModificationList(this);
+  },
+
+  get type() {
+    return this._form.type;
+  },
+  get line() {
+    return this._form.line || -1;
+  },
+  get column() {
+    return this._form.column || -1;
+  },
+  get cssText() {
+    return this._form.cssText;
+  },
+  get authoredText() {
+    return this._form.authoredText || this._form.cssText;
+  },
+  get declarations() {
+    return this._form.declarations || [];
+  },
+  get keyText() {
+    return this._form.keyText;
+  },
+  get name() {
+    return this._form.name;
+  },
+  get selectors() {
+    return this._form.selectors;
+  },
+  get media() {
+    return this._form.media;
+  },
+  get mediaText() {
+    if (!this._form.media) {
+      return null;
+    }
+    if (this._mediaText) {
+      return this._mediaText;
+    }
+    this._mediaText = this.media.join(", ");
+    return this._mediaText;
+  },
+
+  get parentRule() {
+    return this.conn.getActor(this._form.parentRule);
+  },
+
+  get parentStyleSheet() {
+    return this.conn.getActor(this._form.parentStyleSheet);
+  },
+
+  get element() {
+    return this.conn.getActor(this._form.element);
+  },
+
+  get href() {
+    if (this._form.href) {
+      return this._form.href;
+    }
+    let sheet = this.parentStyleSheet;
+    return sheet ? sheet.href : "";
+  },
+
+  get nodeHref() {
+    let sheet = this.parentStyleSheet;
+    return sheet ? sheet.nodeHref : "";
+  },
+
+  get supportsModifySelectorUnmatched() {
+    return this._form.traits && this._form.traits.modifySelectorUnmatched;
+  },
+
+  get canSetRuleText() {
+    return this._form.traits && this._form.traits.canSetRuleText;
+  },
+
+  get location() {
+    return {
+      source: this.parentStyleSheet,
+      href: this.href,
+      line: this.line,
+      column: this.column
+    };
+  },
+
+  _clearOriginalLocation: function () {
+    this._originalLocation = null;
+  },
+
+  getOriginalLocation: function () {
+    if (this._originalLocation) {
+      return promise.resolve(this._originalLocation);
+    }
+    let parentSheet = this.parentStyleSheet;
+    if (!parentSheet) {
+      // This rule doesn't belong to a stylesheet so it is an inline style.
+      // Inline styles do not have any mediaText so we can return early.
+      return promise.resolve(this.location);
+    }
+    return parentSheet.getOriginalLocation(this.line, this.column)
+      .then(({ fromSourceMap, source, line, column }) => {
+        let location = {
+          href: source,
+          line: line,
+          column: column,
+          mediaText: this.mediaText
+        };
+        if (fromSourceMap === false) {
+          location.source = this.parentStyleSheet;
+        }
+        if (!source) {
+          location.href = this.href;
+        }
+        this._originalLocation = location;
+        return location;
+      });
+  },
+
+  modifySelector: custom(Task.async(function* (node, value) {
+    let response;
+    if (this.supportsModifySelectorUnmatched) {
+      // If the debugee supports adding unmatched rules (post FF41)
+      if (this.canSetRuleText) {
+        response = yield this.modifySelector2(node, value, true);
+      } else {
+        response = yield this.modifySelector2(node, value);
+      }
+    } else {
+      response = yield this._modifySelector(value);
+    }
+
+    if (response.ruleProps) {
+      response.ruleProps = response.ruleProps.entries[0];
+    }
+    return response;
+  }), {
+    impl: "_modifySelector"
+  }),
+
+  setRuleText: custom(function (newText) {
+    this._form.authoredText = newText;
+    return this._setRuleText(newText);
+  }, {
+    impl: "_setRuleText"
+  })
+});
+
+exports.StyleRuleFront = StyleRuleFront;
+
+/**
+ * Convenience API for building a list of attribute modifications
+ * for the `modifyProperties` request.  A RuleModificationList holds a
+ * list of modifications that will be applied to a StyleRuleActor.
+ * The modifications are processed in the order in which they are
+ * added to the RuleModificationList.
+ *
+ * Objects of this type expose the same API as @see RuleRewriter.
+ * This lets the inspector use (mostly) the same code, regardless of
+ * whether the server implements setRuleText.
+ */
+var RuleModificationList = Class({
+  /**
+   * Initialize a RuleModificationList.
+   * @param {StyleRuleFront} rule the associated rule
+   */
+  initialize: function (rule) {
+    this.rule = rule;
+    this.modifications = [];
+  },
+
+  /**
+   * Apply the modifications in this object to the associated rule.
+   *
+   * @return {Promise} A promise which will be resolved when the modifications
+   *         are complete; @see StyleRuleActor.modifyProperties.
+   */
+  apply: function () {
+    return this.rule.modifyProperties(this.modifications);
+  },
+
+  /**
+   * Add a "set" entry to the modification list.
+   *
+   * @param {Number} index index of the property in the rule.
+   *                       This can be -1 in the case where
+   *                       the rule does not support setRuleText;
+   *                       generally for setting properties
+   *                       on an element's style.
+   * @param {String} name the property's name
+   * @param {String} value the property's value
+   * @param {String} priority the property's priority, either the empty
+   *                          string or "important"
+   */
+  setProperty: function (index, name, value, priority) {
+    this.modifications.push({
+      type: "set",
+      name: name,
+      value: value,
+      priority: priority
+    });
+  },
+
+  /**
+   * Add a "remove" entry to the modification list.
+   *
+   * @param {Number} index index of the property in the rule.
+   *                       This can be -1 in the case where
+   *                       the rule does not support setRuleText;
+   *                       generally for setting properties
+   *                       on an element's style.
+   * @param {String} name the name of the property to remove
+   */
+  removeProperty: function (index, name) {
+    this.modifications.push({
+      type: "remove",
+      name: name
+    });
+  },
+
+  /**
+   * Rename a property.  This implementation acts like
+   * |removeProperty|, because |setRuleText| is not available.
+   *
+   * @param {Number} index index of the property in the rule.
+   *                       This can be -1 in the case where
+   *                       the rule does not support setRuleText;
+   *                       generally for setting properties
+   *                       on an element's style.
+   * @param {String} name current name of the property
+   *
+   * This parameter is also passed, but as it is not used in this
+   * implementation, it is omitted.  It is documented here as this
+   * code also defined the interface implemented by @see RuleRewriter.
+   * @param {String} newName new name of the property
+   */
+  renameProperty: function (index, name) {
+    this.removeProperty(index, name);
+  },
+
+  /**
+   * Enable or disable a property.  This implementation acts like
+   * |removeProperty| when disabling, or a no-op when enabling,
+   * because |setRuleText| is not available.
+   *
+   * @param {Number} index index of the property in the rule.
+   *                       This can be -1 in the case where
+   *                       the rule does not support setRuleText;
+   *                       generally for setting properties
+   *                       on an element's style.
+   * @param {String} name current name of the property
+   * @param {Boolean} isEnabled true if the property should be enabled;
+   *                        false if it should be disabled
+   */
+  setPropertyEnabled: function (index, name, isEnabled) {
+    if (!isEnabled) {
+      this.removeProperty(index, name);
+    }
+  },
+
+  /**
+   * Create a new property.  This implementation does nothing, because
+   * |setRuleText| is not available.
+   *
+   * These parameter are passed, but as they are not used in this
+   * implementation, they are omitted.  They are documented here as
+   * this code also defined the interface implemented by @see
+   * RuleRewriter.
+   *
+   * @param {Number} index index of the property in the rule.
+   *                       This can be -1 in the case where
+   *                       the rule does not support setRuleText;
+   *                       generally for setting properties
+   *                       on an element's style.
+   * @param {String} name name of the new property
+   * @param {String} value value of the new property
+   * @param {String} priority priority of the new property; either
+   *                          the empty string or "important"
+   */
+  createProperty: function () {
+    // Nothing.
+  },
+});
+
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/stylesheets.js
@@ -0,0 +1,184 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { Front, FrontClassWithSpec } = require("devtools/shared/protocol");
+const {
+  getIndentationFromPrefs,
+  getIndentationFromString
+} = require("devtools/shared/indentation");
+const {
+  originalSourceSpec,
+  mediaRuleSpec,
+  styleSheetSpec,
+  styleSheetsSpec
+} = require("devtools/shared/specs/stylesheets");
+const promise = require("promise");
+const { Task } = require("resource://gre/modules/Task.jsm");
+const events = require("sdk/event/core");
+
+/**
+ * The client-side counterpart for an OriginalSourceActor.
+ */
+const OriginalSourceFront = FrontClassWithSpec(originalSourceSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client, form);
+
+    this.isOriginalSource = true;
+  },
+
+  form: function (form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    this.actorID = form.actor;
+    this._form = form;
+  },
+
+  get href() {
+    return this._form.url;
+  },
+  get url() {
+    return this._form.url;
+  }
+});
+
+exports.OriginalSourceFront = OriginalSourceFront;
+
+/**
+ * Corresponding client-side front for a MediaRuleActor.
+ */
+const MediaRuleFront = FrontClassWithSpec(mediaRuleSpec, {
+  initialize: function (client, form) {
+    Front.prototype.initialize.call(this, client, form);
+
+    this._onMatchesChange = this._onMatchesChange.bind(this);
+    events.on(this, "matches-change", this._onMatchesChange);
+  },
+
+  _onMatchesChange: function (matches) {
+    this._form.matches = matches;
+  },
+
+  form: function (form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    this.actorID = form.actor;
+    this._form = form;
+  },
+
+  get mediaText() {
+    return this._form.mediaText;
+  },
+  get conditionText() {
+    return this._form.conditionText;
+  },
+  get matches() {
+    return this._form.matches;
+  },
+  get line() {
+    return this._form.line || -1;
+  },
+  get column() {
+    return this._form.column || -1;
+  },
+  get parentStyleSheet() {
+    return this.conn.getActor(this._form.parentStyleSheet);
+  }
+});
+
+exports.MediaRuleFront = MediaRuleFront;
+
+/**
+ * StyleSheetFront is the client-side counterpart to a StyleSheetActor.
+ */
+const StyleSheetFront = FrontClassWithSpec(styleSheetSpec, {
+  initialize: function (conn, form) {
+    Front.prototype.initialize.call(this, conn, form);
+
+    this._onPropertyChange = this._onPropertyChange.bind(this);
+    events.on(this, "property-change", this._onPropertyChange);
+  },
+
+  destroy: function () {
+    events.off(this, "property-change", this._onPropertyChange);
+    Front.prototype.destroy.call(this);
+  },
+
+  _onPropertyChange: function (property, value) {
+    this._form[property] = value;
+  },
+
+  form: function (form, detail) {
+    if (detail === "actorid") {
+      this.actorID = form;
+      return;
+    }
+    this.actorID = form.actor;
+    this._form = form;
+  },
+
+  get href() {
+    return this._form.href;
+  },
+  get nodeHref() {
+    return this._form.nodeHref;
+  },
+  get disabled() {
+    return !!this._form.disabled;
+  },
+  get title() {
+    return this._form.title;
+  },
+  get isSystem() {
+    return this._form.system;
+  },
+  get styleSheetIndex() {
+    return this._form.styleSheetIndex;
+  },
+  get ruleCount() {
+    return this._form.ruleCount;
+  },
+
+  /**
+   * Get the indentation to use for edits to this style sheet.
+   *
+   * @return {Promise} A promise that will resolve to a string that
+   * should be used to indent a block in this style sheet.
+   */
+  guessIndentation: function () {
+    let prefIndent = getIndentationFromPrefs();
+    if (prefIndent) {
+      let {indentUnit, indentWithTabs} = prefIndent;
+      return promise.resolve(indentWithTabs ? "\t" : " ".repeat(indentUnit));
+    }
+
+    return Task.spawn(function* () {
+      let longStr = yield this.getText();
+      let source = yield longStr.string();
+
+      let {indentUnit, indentWithTabs} = getIndentationFromString(source);
+
+      return indentWithTabs ? "\t" : " ".repeat(indentUnit);
+    }.bind(this));
+  }
+});
+
+exports.StyleSheetFront = StyleSheetFront;
+
+/**
+ * The corresponding Front object for the StyleSheetsActor.
+ */
+const StyleSheetsFront = FrontClassWithSpec(styleSheetsSpec, {
+  initialize: function (client, tabForm) {
+    Front.prototype.initialize.call(this, client);
+    this.actorID = tabForm.styleSheetsActor;
+    this.manage(this);
+  }
+});
+
+exports.StyleSheetsFront = StyleSheetsFront;
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -6,16 +6,17 @@
 
 include('../templates.mozbuild')
 
 DIRS += [
     'acorn',
     'apps',
     'client',
     'discovery',
+    'fronts',
     'gcli',
     'heapsnapshot',
     'inspector',
     'jsbeautify',
     'layout',
     'locales',
     'performance',
     'pretty-fast',