Updates
authorDave Camp <dcamp@mozilla.com>
Mon, 05 Aug 2013 15:00:52 -0700
changeset 18 9c563303d2f05e75c12bd5e26e3c1b5e9ab659fc
parent 17 482f78b4d3460c9a0a8eaafaa5cae39393e49f0c
child 19 2c215755a33619c0154866d04e6df60ad62cc017
push id19
push userdcamp@campd.org
push dateMon, 05 Aug 2013 22:00:55 +0000
Updates
breadcrumb-parents.diff
inspector-actorid-detail.diff
inspector-localdoc.diff
inspector-namespace-fix.diff
move-csslogic.diff
protocol-nullables.diff
protocol-optional.diff
protocol-string-form.diff
series
style-editor-baseuri.diff
style-inspector-port.diff
styles-actor-src.diff
styles-actor.diff
stylesheet-parent.diff
walker-reload.diff
warning-fixes.diff
window-targets.diff
deleted file mode 100644
--- a/breadcrumb-parents.diff
+++ /dev/null
@@ -1,77 +0,0 @@
-# HG changeset patch
-# Parent 73f2e30e015428ac7f3ba0b9c61c8024509b40af
-diff --git a/browser/devtools/inspector/breadcrumbs.js b/browser/devtools/inspector/breadcrumbs.js
---- a/browser/devtools/inspector/breadcrumbs.js
-+++ b/browser/devtools/inspector/breadcrumbs.js
-@@ -479,21 +479,23 @@ HTMLBreadcrumbs.prototype = {
-       let fragment = this.chromeDoc.createDocumentFragment();
-       let toAppend = aNode;
-       let lastButtonInserted = null;
-       let originalLength = this.nodeHierarchy.length;
-       let stopNode = null;
-       if (originalLength > 0) {
-         stopNode = this.nodeHierarchy[originalLength - 1].node;
-       }
--      while (toAppend && toAppend.tagName && toAppend != stopNode) {
--        let button = this.buildButton(toAppend);
--        fragment.insertBefore(button, lastButtonInserted);
--        lastButtonInserted = button;
--        this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
-+      while (toAppend && toAppend != stopNode) {
-+        if (toAppend.tagName) {
-+          let button = this.buildButton(toAppend);
-+          fragment.insertBefore(button, lastButtonInserted);
-+          lastButtonInserted = button;
-+          this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
-+        }
-         toAppend = toAppend.parentNode();
-       }
-       this.container.appendChild(fragment, this.container.firstChild);
-   },
- 
-   /**
-    * Get a child of a node that can be displayed in the breadcrumbs
-    * and that is probably visible. See LOW_PRIORITY_ELEMENTS.
-diff --git a/browser/devtools/inspector/test/browser_inspector_iframeTest.js b/browser/devtools/inspector/test/browser_inspector_iframeTest.js
---- a/browser/devtools/inspector/test/browser_inspector_iframeTest.js
-+++ b/browser/devtools/inspector/test/browser_inspector_iframeTest.js
-@@ -69,17 +69,38 @@ function performTestComparisons1()
- 
- function performTestComparisons2()
- {
-   let i = getActiveInspector();
- 
-   is(i.selection.node, div2, "selection matches div2 node");
-   is(getHighlitNode(), div2, "highlighter matches selection");
- 
--  finish();
-+  selectRoot();
-+}
-+
-+function selectRoot()
-+{
-+  // Select the root document element to clear the breadcrumbs.
-+  let i = getActiveInspector();
-+  i.selection.setNode(doc.documentElement);
-+  i.once("inspector-updated", selectIframe);
-+}
-+
-+function selectIframe()
-+{
-+  // Directly select an element in an iframe (without navigating to it
-+  // with mousemoves).
-+  let i = getActiveInspector();
-+  i.selection.setNode(div2);
-+  i.once("inspector-updated", () => {
-+    let breadcrumbs = i.breadcrumbs;
-+    is(breadcrumbs.nodeHierarchy.length, 9, "Should have 9 items");
-+    finish();
-+  });
- }
- 
- function test() {
-   waitForExplicitFinish();
- 
-   gBrowser.selectedTab = gBrowser.addTab();
-   gBrowser.selectedBrowser.addEventListener("load", function() {
-     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
--- a/inspector-actorid-detail.diff
+++ b/inspector-actorid-detail.diff
@@ -1,10 +1,16 @@
 # HG changeset patch
-# Parent 00722df4ef56b84ff2da9f8dce849e4150fc3abf
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374619700 25200
+#      Tue Jul 23 15:48:20 2013 -0700
+# Node ID a4f0be7874bc6617a254cf4e4176e6c743d3b87d
+# Parent  dc3046b986725036725fd2bf6a57e4d62a5ce947
+imported patch inspector-actorid-detail.diff
+
 diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
 --- a/toolkit/devtools/server/actors/inspector.js
 +++ b/toolkit/devtools/server/actors/inspector.js
 @@ -129,16 +129,20 @@ var NodeActor = protocol.ActorClass({
    /**
     * Instead of storing a connection object, the NodeActor gets its connection
     * from its associated walker.
     */
deleted file mode 100644
--- a/inspector-localdoc.diff
+++ /dev/null
@@ -1,35 +0,0 @@
-# HG changeset patch
-# Parent 8839734de1ae40f32a4a7d224847f4162bf058f0
-diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
---- a/toolkit/devtools/server/actors/inspector.js
-+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -180,16 +180,29 @@ var NodeActor = protocol.ActorClass({
-       } else {
-         form.shortValue = this.rawNode.nodeValue;
-       }
-     }
- 
-     return form;
-   },
- 
-+  /**
-+   * Returns a best guess at the owner document for the node.
-+   */
-+  connectedDocument: function() {
-+    let node = this;
-+    while (node) {
-+      if (node.nodeType === Ci.nsIDOMNode.DOCUMENT_NODE) {
-+        return node;
-+      }
-+    }
-+    return null;
-+  },
-+
-   writeAttrs: function() {
-     if (!this.rawNode.attributes) {
-       return undefined;
-     }
-     return [{namespace: attr.namespace, name: attr.name, value: attr.value }
-             for (attr of this.rawNode.attributes)];
-   },
- 
--- a/inspector-namespace-fix.diff
+++ b/inspector-namespace-fix.diff
@@ -1,10 +1,16 @@
 # HG changeset patch
-# Parent 6c266310582fdfc340c54a6a3a4b2334928ff732
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374619876 25200
+#      Tue Jul 23 15:51:16 2013 -0700
+# Node ID 26c89087a26174b5d75d4fe575580cd4362b4eeb
+# Parent  451135201aa048928428f3348b16a78b525b1fb4
+[mq]: inspector-namespace-fix.diff
+
 diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
 --- a/toolkit/devtools/server/actors/inspector.js
 +++ b/toolkit/devtools/server/actors/inspector.js
 @@ -147,17 +147,17 @@ var NodeActor = protocol.ActorClass({
        // This might be an iframe with virtual children.
        numChildren = 1;
      }
  
new file mode 100644
--- /dev/null
+++ b/move-csslogic.diff
@@ -0,0 +1,108 @@
+# HG changeset patch
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374688624 25200
+#      Wed Jul 24 10:57:04 2013 -0700
+# Node ID dc3046b986725036725fd2bf6a57e4d62a5ce947
+# Parent c057ecd8f9b4e82b7ab7ca6d54d8b752d9c5064d
+imported patch move-csslogic.diff
+
+diff --git a/toolkit/devtools/Loader.jsm b/toolkit/devtools/Loader.jsm
+--- a/toolkit/devtools/Loader.jsm
++++ b/toolkit/devtools/Loader.jsm
+@@ -46,16 +46,17 @@ var BuiltinProvider = {
+         "toolkit/loader": loader
+       },
+       paths: {
+         "": "resource://gre/modules/commonjs/",
+         "main": "resource:///modules/devtools/main.js",
+         "devtools": "resource:///modules/devtools",
+         "devtools/server": "resource://gre/modules/devtools/server",
+         "devtools/toolkit/webconsole": "resource://gre/modules/devtools/toolkit/webconsole",
++        "devtools/styleinspector/css-logic": "resource://gre/modules/devtools/styleinspector/css-logic",
+ 
+         // Allow access to xpcshell test items from the loader.
+         "xpcshell-test": "resource://test"
+       },
+       globals: loaderGlobals
+     });
+ 
+     return promise.resolve(undefined);
+@@ -80,26 +81,29 @@ var SrcdirProvider = {
+     let srcdir = Services.prefs.getComplexValue("devtools.loader.srcdir",
+                                                 Ci.nsISupportsString);
+     srcdir = OS.Path.normalize(srcdir.data.trim());
+     let devtoolsDir = OS.Path.join(srcdir, "browser", "devtools");
+     let devtoolsURI = this.fileURI(devtoolsDir);
+     let toolkitURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools"));
+     let serverURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "server"));
+     let webconsoleURI = this.fileURI(OS.Path.join(srcdir, "toolkit", "devtools", "webconsole"));
++    let cssLogicURI = this.fileURI(OS.Path.join(toolkitURI, "styleinspector", "css-logic"));
++
+     let mainURI = this.fileURI(OS.Path.join(srcdir, "browser", "devtools", "main.js"));
+     this.loader = new loader.Loader({
+       modules: {
+         "toolkit/loader": loader
+       },
+       paths: {
+         "": "resource://gre/modules/commonjs/",
+         "devtools/server": serverURI,
+         "devtools/toolkit/webconsole": webconsoleURI,
+         "devtools": devtoolsURI,
++        "devtools/styleinspector/css-logic": cssLogicURI,
+         "main": mainURI
+       },
+       globals: loaderGlobals
+     });
+ 
+     return this._writeManifest(devtoolsDir).then(null, Cu.reportError);
+   },
+ 
+diff --git a/toolkit/devtools/moz.build b/toolkit/devtools/moz.build
+--- a/toolkit/devtools/moz.build
++++ b/toolkit/devtools/moz.build
+@@ -5,10 +5,11 @@
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ PARALLEL_DIRS += [
+     'server',
+     'client',
+     'gcli',
+     'sourcemap',
+     'webconsole',
+-    'apps'
++    'apps',
++    'styleinspector'
+ ]
+diff --git a/toolkit/devtools/styleinspector/Makefile.in b/toolkit/devtools/styleinspector/Makefile.in
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/styleinspector/Makefile.in
+@@ -0,0 +1,15 @@
++# 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/.
++
++DEPTH = ../../..
++topsrcdir = @top_srcdir@
++srcdir = @srcdir@
++VPATH = @srcdir@
++
++include $(DEPTH)/config/autoconf.mk
++
++include $(topsrcdir)/config/rules.mk
++
++libs::
++	$(INSTALL) $(IFLAGS1) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools/styleinspector
+diff --git a/browser/devtools/styleinspector/css-logic.js b/toolkit/devtools/styleinspector/css-logic.js
+rename from browser/devtools/styleinspector/css-logic.js
+rename to toolkit/devtools/styleinspector/css-logic.js
+diff --git a/toolkit/devtools/styleinspector/moz.build b/toolkit/devtools/styleinspector/moz.build
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/styleinspector/moz.build
+@@ -0,0 +1,5 @@
++# -*- 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/.
deleted file mode 100644
--- a/protocol-nullables.diff
+++ /dev/null
@@ -1,65 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1374426959 25200
-#      Sun Jul 21 10:15:59 2013 -0700
-# Node ID 3e1dca078f18fdd846bfbd362c3ec1c6a7c994b5
-# Parent  8bc59ac5b61833921ae44356dc54ffb601925c93
-imported patch protocol-nullables.diff
-
-diff --git a/toolkit/devtools/server/protocol.js b/toolkit/devtools/server/protocol.js
---- a/toolkit/devtools/server/protocol.js
-+++ b/toolkit/devtools/server/protocol.js
-@@ -76,16 +76,18 @@ types.getType = function(type) {
-   // New type, see if it's a collection/lifetime type:
-   let sep = type.indexOf(":");
-   if (sep >= 0) {
-     let collection = type.substring(0, sep);
-     let subtype = types.getType(type.substring(sep + 1));
- 
-     if (collection === "array") {
-       return types.addArrayType(subtype);
-+    } else if (collection === "nullable") {
-+      return types.addNullableType(subtype);
-     }
- 
-     if (registeredLifetimes.has(collection)) {
-       return types.addLifetimeType(collection, subtype);
-     }
- 
-     throw Error("Unknown collection type: " + collection);
-   }
-@@ -267,16 +269,34 @@ types.addActorType = function(name) {
-   }, {
-     // We usually freeze types, but actor types are updated when clients are
-     // created, so don't freeze yet.
-     thawed: true
-   });
-   return type;
- }
- 
-+types.addNullableType = function(subtype) {
-+  subtype = types.getType(subtype);
-+  return types.addType("nullable:" + subtype.name, {
-+    read: (value, ctx) => {
-+      if (value == null) {
-+        return value;
-+      }
-+      return subtype.read(value, ctx);
-+    },
-+    write: (value, ctx) => {
-+      if (value == null) {
-+        return value;
-+      }
-+      return subtype.write(value, ctx);
-+    }
-+  });
-+}
-+
- /**
-  * Register an actor detail type.  This is just like an actor type, but
-  * will pass a detail hint to the actor's form method during serialization/
-  * deserialization.
-  *
-  * This is called by getType() when passed an 'actorType#detail' string.
-  *
-  * @param string name
deleted file mode 100644
--- a/protocol-optional.diff
+++ /dev/null
@@ -1,326 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1374428320 25200
-#      Sun Jul 21 10:38:40 2013 -0700
-# Node ID aa7102bc765bc7f8b4f213118649448d36fe24e3
-# Parent  3e1dca078f18fdd846bfbd362c3ec1c6a7c994b5
-[mq]: protocol-optional.diff
-
-diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
---- a/toolkit/devtools/server/actors/inspector.js
-+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -591,18 +591,18 @@ var NodeListActor = exports.NodeListActo
-       this.walker.ensurePathToRoot(item, newParents);
-     }
-     return {
-       nodes: items,
-       newParents: [node for (node of newParents)]
-     }
-   }, {
-     request: {
--      start: Arg(0, "number", { optional: true }),
--      end: Arg(1, "number", { optional: true })
-+      start: Arg(0, "nullable:number"),
-+      end: Arg(1, "nullable:number")
-     },
-     response: RetVal("disconnectedNodeArray")
-   }),
- 
-   release: method(function() {}, { release: true })
- });
- 
- /**
-@@ -659,17 +659,17 @@ let nodeArrayMethod = {
- };
- 
- let traversalMethod = {
-   request: {
-     node: Arg(0, "domnode"),
-     whatToShow: Option(1)
-   },
-   response: {
--    node: RetVal("domnode", {optional: true})
-+    node: RetVal("nullable:domnode")
-   }
- }
- 
- /**
-  * We need to know when a document is navigating away so that we can kill
-  * the nodes underneath it.  We also need to know when a document is
-  * navigated to so that we can send a mutation event for the iframe node.
-  *
-@@ -834,32 +834,32 @@ var WalkerActor = protocol.ActorClass({
-    * @param NodeActor node
-    *        The node whose document is needed, or null to
-    *        return the root.
-    */
-   document: method(function(node) {
-     let doc = node ? nodeDocument(node.rawNode) : this.rootDoc;
-     return this._ref(doc);
-   }, {
--    request: { node: Arg(0, "domnode", {optional: true}) },
-+    request: { node: Arg(0, "nullable:domnode") },
-     response: { node: RetVal("domnode") },
-   }),
- 
-   /**
-    * Return the documentElement for the document containing the
-    * given node.
-    * @param NodeActor node
-    *        The node whose documentElement is requested, or null
-    *        to use the root document.
-    */
-   documentElement: method(function(node) {
-     let elt = node ? nodeDocument(node.rawNode).documentElement : this.rootDoc.documentElement;
-     return this._ref(elt);
-   }, {
--    request: { node: Arg(0, "domnode", {optional: true}) },
-+    request: { node: Arg(0, "nullable:domnode") },
-     response: { node: RetVal("domnode") },
-   }),
- 
-   /**
-    * Return all parents of the given node, ordered from immediate parent
-    * to root.
-    * @param NodeActor node
-    *    The node whose parents are requested.
-@@ -1352,17 +1352,17 @@ var WalkerActor = protocol.ActorClass({
-       for (let locked of this._activePseudoClassLocks) {
-         DOMUtils.clearPseudoClassLocks(locked.rawNode);
-         this._activePseudoClassLocks.delete(locked);
-         this._queuePseudoClassMutation(locked);
-       }
-     }
-   }, {
-     request: {
--      node: Arg(0, "domnode", { optional: true }),
-+      node: Arg(0, "nullable:domnode")
-     },
-     response: {}
-   }),
- 
-   /**
-    * Get a node's innerHTML property.
-    */
-   innerHTML: method(function(node) {
-@@ -1407,30 +1407,30 @@ var WalkerActor = protocol.ActorClass({
-       // Mutation events will take care of the rest.
-     }
-     return nextSibling;
-   }, {
-     request: {
-       node: Arg(0, "domnode")
-     },
-     response: {
--      nextSibling: RetVal("domnode", { optional: true })
-+      nextSibling: RetVal("nullable:domnode")
-     }
-   }),
- 
-   /**
-    * Insert a node into the DOM.
-    */
-   insertBefore: method(function(node, parent, sibling) {
-     parent.rawNode.insertBefore(node.rawNode, sibling ? sibling.rawNode : null);
-   }, {
-     request: {
-       node: Arg(0, "domnode"),
-       parent: Arg(1, "domnode"),
--      sibling: Arg(2, "domnode", { optional: true })
-+      sibling: Arg(2, "nullable:domnode")
-     },
-     response: {}
-   }),
- 
-   /**
-    * Get any pending mutation records.  Must be called by the client after
-    * the `new-mutations` notification is received.  Returns an array of
-    * mutation records.
-diff --git a/toolkit/devtools/server/protocol.js b/toolkit/devtools/server/protocol.js
---- a/toolkit/devtools/server/protocol.js
-+++ b/toolkit/devtools/server/protocol.js
-@@ -103,16 +103,27 @@ types.getType = function(type) {
-     require("devtools/server/actors/string");
-     return registeredTypes.get("longstring");
-   }
- 
-   throw Error("Unknown type: " + type);
- }
- 
- /**
-+ * Don't allow undefined when writing primitive types to packets.  If
-+ * you want to allow undefined, use a nullable type.
-+ */
-+function identityWrite(v) {
-+  if (v === undefined) {
-+    throw Error("undefined passed where a value is required");
-+  }
-+  return v;
-+}
-+
-+/**
-  * Add a type to the type system.
-  *
-  * When registering a type, you can provide `read` and `write` methods.
-  *
-  * The `read` method will be passed a JS object value from the JSON
-  * packet and must return a native representation.  The `write` method will
-  * be passed a native representation and should provide a JSONable value.
-  *
-@@ -133,18 +144,18 @@ types.getType = function(type) {
- types.addType = function(name, typeObject={}, options={}) {
-   if (registeredTypes.has(name)) {
-     throw Error("Type '" + name + "' already exists.");
-   }
- 
-   let type = object.merge({
-     name: name,
-     primitive: !(typeObject.read || typeObject.write),
--    read: v => v,
--    write: v => v
-+    read: identityWrite,
-+    write: identityWrite
-   }, typeObject);
- 
-   registeredTypes.set(name, type);
- 
-   if (!options.thawed) {
-     Object.freeze(type);
-   }
- 
-@@ -379,42 +390,29 @@ types.JSON = types.addType("json");
- 
- /**
-  * Placeholder for simple arguments.
-  *
-  * @param number index
-  *    The argument index to place at this position.
-  * @param type type
-  *    The argument should be marshalled as this type.
-- * @param object options
-- *    Argument options:
-- *      optional: true if the argument can be undefined or null.
-  * @constructor
-  */
- let Arg = Class({
--  initialize: function(index, type, options={}) {
-+  initialize: function(index, type) {
-     this.index = index;
-     this.type = types.getType(type);
--    this.optional = !!options.optional;
-   },
- 
-   write: function(arg, ctx) {
--    if (arg === undefined || arg === null) {
--      if (!this.optional) throw Error("Required argument " + this.name + " not specified.");
--      return undefined;
--    }
-     return this.type.write(arg, ctx);
-   },
- 
-   read: function(v, ctx, outArgs) {
--    if (v === undefined || v === null) {
--      if (!this.optional) throw Error("Required argument " + this.name + " not specified.");
--      outArgs[this.index] = v;
--      return;
--    }
-     outArgs[this.index] = this.type.read(v, ctx);
-   }
- });
- exports.Arg = Arg;
- 
- /**
-  * Placeholder for an options argument value that should be hoisted
-  * into the packet.
-@@ -461,44 +459,28 @@ let Option = Class({
- 
- exports.Option = Option;
- 
- /**
-  * Placeholder for return values in a response template.
-  *
-  * @param type type
-  *    The return value should be marshalled as this type.
-- * @param object options
-- *    Argument options:
-- *      optional: true if the argument can be undefined or null.
-  */
- let RetVal = Class({
--  initialize: function(type, options={}) {
-+  initialize: function(type) {
-     this.type = types.getType(type);
--    this.optional = !!options.optional;
-   },
- 
-   write: function(v, ctx) {
--    if (v !== undefined && v != null) {
--      return this.type.write(v, ctx);
--    }
--    if (!this.optional) {
--      throw Error("Return value not specified.");
--    }
--    return v;
-+    return this.type.write(v, ctx);
-   },
- 
-   read: function(v, ctx) {
--    if (v !== undefined && v != null) {
--      return this.type.read(v, ctx);
--    }
--    if (!this.optional) {
--      throw Error("Return value not specified.");
--    }
--    return v;
-+    return this.type.read(v, ctx);
-   }
- });
- 
- exports.RetVal = RetVal;
- 
- /* Template handling functions */
- 
- /**
-diff --git a/toolkit/devtools/server/tests/unit/test_protocol_simple.js b/toolkit/devtools/server/tests/unit/test_protocol_simple.js
---- a/toolkit/devtools/server/tests/unit/test_protocol_simple.js
-+++ b/toolkit/devtools/server/tests/unit/test_protocol_simple.js
-@@ -76,17 +76,17 @@ let RootActor = protocol.ActorClass({
-     response: RetVal()
-   }),
- 
-   optionalArgs: method(function(a, b=200) {
-     return b;
-   }, {
-     request: {
-       a: Arg(0),
--      b: Arg(1, "number", { optional: true })
-+      b: Arg(1, "nullable:number")
-     },
-     response: {
-       value: RetVal("number")
-     },
-   }),
- 
-   arrayArgs: method(function(a) {
-     return a;
-@@ -183,16 +183,21 @@ function run_test()
-       do_check_eq(ret, 1);
-     }).then(() => {
-       return rootClient.promiseReturn();
-     }).then(ret => {
-       trace.expectSend({"type":"promiseReturn","to":"<actorid>"});
-       trace.expectReceive({"value":1,"from":"<actorid>"});
-       do_check_eq(ret, 1);
-     }).then(() => {
-+      // Missing argument should throw an exception
-+      check_except(() => {
-+        rootClient.simpleArgs(5);
-+      });
-+
-       return rootClient.simpleArgs(5, 10)
-     }).then(ret => {
-       trace.expectSend({"type":"simpleArgs","firstArg":5,"secondArg":10,"to":"<actorid>"});
-       trace.expectReceive({"firstResponse":6,"secondResponse":11,"from":"<actorid>"});
-       do_check_eq(ret.firstResponse, 6);
-       do_check_eq(ret.secondResponse, 11);
-     }).then(() => {
-       return rootClient.nestedArgs(1, 2, 3);
deleted file mode 100644
--- a/protocol-string-form.diff
+++ /dev/null
@@ -1,130 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1374429750 25200
-#      Sun Jul 21 11:02:30 2013 -0700
-# Node ID c8b76ff3e28674d0144cd3c281aa07df5b74a6a6
-# Parent  aa7102bc765bc7f8b4f213118649448d36fe24e3
-imported patch protocol-string-form.diff
-
-diff --git a/toolkit/devtools/server/protocol.js b/toolkit/devtools/server/protocol.js
---- a/toolkit/devtools/server/protocol.js
-+++ b/toolkit/devtools/server/protocol.js
-@@ -249,22 +249,23 @@ types.addActorType = function(name) {
-       // find the actor registered with this actorID.
-       if (ctx instanceof Actor) {
-         return ctx.conn.getActor(v);
-       }
- 
-       // Reading a response on the client side, check for an
-       // existing front on the connection, and create the front
-       // if it isn't found.
--      let front = ctx.conn.getActor(v.actor);
-+      let actorID = typeof(v) === "string" ? v : v.actor;
-+      let front = ctx.conn.getActor(actorID);
-       if (front) {
-         front.form(v, detail, ctx);
-       } else {
-         front = new type.frontClass(ctx.conn, v, detail, ctx)
--        front.actorID = v.actor;
-+        front.actorID = actorID;
-         ctx.marshallPool().manage(front);
-       }
-       return front;
-     },
-     write: (v, ctx, detail) => {
-       // If returning a response from the server side, make sure
-       // the actor is added to a parent object and return its form.
-       if (v instanceof Actor) {
-diff --git a/toolkit/devtools/server/tests/unit/test_protocol_children.js b/toolkit/devtools/server/tests/unit/test_protocol_children.js
---- a/toolkit/devtools/server/tests/unit/test_protocol_children.js
-+++ b/toolkit/devtools/server/tests/unit/test_protocol_children.js
-@@ -38,16 +38,19 @@ let ChildActor = protocol.ActorClass({
-   },
- 
-   destroy: function() {
-     protocol.Actor.prototype.destroy.call(this);
-     this.destroyed = true;
-   },
- 
-   form: function(detail) {
-+    if (detail === "actorid") {
-+      return this.actorID;
-+    }
-     return {
-       actor: this.actorID,
-       childID: this.childID,
-       detail: detail
-     };
-   },
- 
-   echo: method(function(str) {
-@@ -67,16 +70,24 @@ let ChildActor = protocol.ActorClass({
- 
-   getDetail2: method(function() {
-     return this;
-   }, {
-     // This also exercises return-value-as-packet.
-     response: RetVal("childActor#detail2"),
-   }),
- 
-+  getIDDetail: method(function() {
-+    return this;
-+  }, {
-+    response: {
-+      idDetail: RetVal("childActor#actorid")
-+    }
-+  }),
-+
-   getSibling: method(function(id) {
-     return this.parent().getChild(id);
-   }, {
-     request: { id: Arg(0) },
-     response: { sibling: RetVal("childActor") }
-   }),
- 
-   emitEvents: method(function() {
-@@ -122,17 +133,20 @@ let ChildFront = protocol.FrontClass(Chi
-     this.destroyed = true;
-     protocol.Front.prototype.destroy.call(this);
-   },
- 
-   marshallPool: function() { return this.parent() },
- 
-   toString: function() "[child front " + this.childID + "]",
- 
--  form: function(form) {
-+  form: function(form, detail) {
-+    if (detail === "actorid") {
-+      return;
-+    }
-     this.childID = form.childID;
-     this.detail = form.detail;
-   },
- 
-   onEvent1: preEvent("event1", function(a, b, c) {
-     this.event1arg3 = c;
-   }),
- });
-@@ -300,16 +314,22 @@ function run_test()
-     }).then(() => {
-       return childFront.getDetail2();
-     }).then(ret => {
-       trace.expectSend({"type":"getDetail2","to":"<actorid>"});
-       trace.expectReceive({"actor":"<actorid>","childID":"child1","detail":"detail2","from":"<actorid>"});
-       do_check_true(ret === childFront);
-       do_check_eq(childFront.detail, "detail2");
-     }).then(() => {
-+      return childFront.getIDDetail();
-+    }).then(ret => {
-+      trace.expectSend({"type":"getIDDetail","to":"<actorid>"});
-+      trace.expectReceive({"idDetail": childFront.actorID,"from":"<actorid>"});
-+      do_check_true(ret === childFront);
-+    }).then(() => {
-       return childFront.getSibling("siblingID");
-     }).then(ret => {
-       trace.expectSend({"type":"getSibling","id":"siblingID","to":"<actorid>"});
-       trace.expectReceive({"sibling":{"actor":"<actorid>","childID":"siblingID"},"from":"<actorid>"});
- 
-       expectRootChildren(2);
-     }).then(ret => {
-       return rootFront.getTemporaryChild("temp1").then(temp1 => {
--- a/series
+++ b/series
@@ -1,18 +1,11 @@
-breadcrumb-parents.diff
-stylesheet-parent.diff
-walker-reload.diff
-window-targets.diff
+inspector-actorid-detail.diff
+inspector-namespace-fix.diff
+style-editor-baseuri.diff
+move-csslogic.diff
+styles-actor.diff
 warning-fixes.diff
-protocol-nullables.diff
-protocol-optional.diff
-protocol-string-form.diff
-inspector-actorid-detail.diff
-styles-actor-src.diff
-style-editor-baseuri.diff
-inspector-namespace-fix.diff
-inspector-localdoc.diff
-styles-actor.diff
 style-inspector-port.diff
+window-targets.diff
 inspector-retain-root.diff #+obsolete
 protocol-clientserver-marshallers.diff #+experimental
 promise-return.diff #+experimental
--- a/style-editor-baseuri.diff
+++ b/style-editor-baseuri.diff
@@ -1,24 +1,52 @@
 # HG changeset patch
-# Parent feeb04157795a0b9e1bea6d5f89593d9da76caf9
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374619826 25200
+#      Tue Jul 23 15:50:26 2013 -0700
+# Node ID 451135201aa048928428f3348b16a78b525b1fb4
+# Parent f685c843c8fab9ef337eeea0bc198fd135f63781
+[mq]: style-editor-baseuri.diff
+
+diff --git a/browser/devtools/styleeditor/StyleEditorDebuggee.jsm b/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
+--- a/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
++++ b/browser/devtools/styleeditor/StyleEditorDebuggee.jsm
+@@ -118,17 +118,17 @@ StyleEditorDebuggee.prototype = {
+   },
+ 
+   /**
+    * request baseURIObject information from the document
+    */
+   _getBaseURI: function() {
+     let message = { type: "getBaseURI" };
+     this._sendRequest(message, (response) => {
+-      this.baseURI = response.baseURI;
++      this.baseURI = Services.io.newURI(response.baseURI, null, null);
+     });
+   },
+ 
+   /**
+    * Handler for document load, forward event with
+    * all the stylesheets available on load.
+    *
+    * @param  {string} type
 diff --git a/toolkit/devtools/server/actors/styleeditor.js b/toolkit/devtools/server/actors/styleeditor.js
 --- a/toolkit/devtools/server/actors/styleeditor.js
 +++ b/toolkit/devtools/server/actors/styleeditor.js
-@@ -115,17 +115,17 @@ StyleEditorActor.prototype = {
+@@ -99,17 +99,17 @@ StyleEditorActor.prototype = {
    },
  
    /**
     * Get the BaseURI for the document.
     *
     * @return {object} JSON message to with BaseURI
     */
    onGetBaseURI: function() {
--    return { baseURI: this.doc.baseURIObject };
-+    return { baseURI: this.doc.baseURIObject.spec };
+-    return { baseURI: this.document.baseURIObject };
++    return { baseURI: this.document.baseURIObject.spec };
    },
  
    /**
     * Called when target navigates to a new document.
     * Adds load listeners to document.
     */
    onNewDocument: function() {
      // delete previous document's actors
--- a/style-inspector-port.diff
+++ b/style-inspector-port.diff
@@ -1,40 +1,95 @@
 # HG changeset patch
-# Parent 4b4d2de331db3b199f1e0abcef71592cf1be5b86
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374619952 25200
+#      Tue Jul 23 15:52:32 2013 -0700
+# Node ID 52bc42b61b7ba9bed9931f9b9cef863f75214eca
+# Parent 2b17b5f7fca2b109e204e705a12bb66a81b30807
+imported patch style-inspector-port.diff
 
+diff --git a/browser/devtools/framework/target.js b/browser/devtools/framework/target.js
+--- a/browser/devtools/framework/target.js
++++ b/browser/devtools/framework/target.js
+@@ -287,16 +287,17 @@ TabTarget.prototype = {
+       // A local TabTarget will never perform chrome debugging.
+       this._chrome = false;
+     }
+ 
+     this._setupRemoteListeners();
+ 
+     let attachTab = () => {
+       this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
++        dump("DONE ATTACHING TAB\n");
+         if (!aTabClient) {
+           this._remote.reject("Unable to attach to the tab");
+           return;
+         }
+         this.threadActor = aResponse.threadActor;
+         this._remote.resolve(null);
+       });
+     };
 diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
 --- a/browser/devtools/inspector/inspector-panel.js
 +++ b/browser/devtools/inspector/inspector-panel.js
-@@ -40,22 +40,18 @@ function InspectorPanel(iframeWindow, to
+@@ -40,22 +40,19 @@ function InspectorPanel(iframeWindow, to
  exports.InspectorPanel = InspectorPanel;
  
  InspectorPanel.prototype = {
    /**
     * open is effectively an asynchronous constructor
     */
    open: function InspectorPanel_open() {
      return this.target.makeRemote().then(() => {
 -      return this.target.inspector.getWalker();
 -    }).then(walker => {
 -      if (this._destroyPromise) {
 -        walker.release().then(null, console.error);
 -      }
 -      this.walker = walker;
++      dump("DONE MAKING REMOTE\n");
 +      return this._getWalker();
 +    }).then(() => {
        return this._getDefaultNodeForSelection();
      }).then(defaultSelection => {
        return this._deferredOpen(defaultSelection);
      }).then(null, console.error);
    },
  
    _deferredOpen: function(defaultSelection) {
      let deferred = promise.defer();
-@@ -311,24 +307,21 @@ InspectorPanel.prototype = {
+@@ -144,16 +141,27 @@ InspectorPanel.prototype = {
+     }.bind(this));
+ 
+     this.setupSearchBox();
+     this.setupSidebar();
+ 
+     return deferred.promise;
+   },
+ 
++  _getWalker: function() {
++    let inspector = this.target.inspector;
++    return inspector.getWalker().then(walker => {
++      this.walker = walker;
++      return inspector.getPageStyle();
++    }).then(pageStyle => {
++      dump("SETTING PAGESTYLE TO " + this.pageStyle + "\n");
++      this.pageStyle = pageStyle;
++    });
++  },
++
+   /**
+    * Return a promise that will resolve to the default node for selection.
+    */
+   _getDefaultNodeForSelection: function() {
+     if (this._defaultNode) {
+       return this._defaultNode;
+     }
+     let walker = this.walker;
+@@ -307,24 +315,21 @@ InspectorPanel.prototype = {
     * When a new node is selected.
     */
    onNewSelection: function InspectorPanel_onNewSelection() {
      this.cancelLayoutChange();
  
      // Wait for all the known tools to finish updating and then let the
      // client know.
      let selection = this.selection.nodeFront;
@@ -52,26 +107,26 @@ diff --git a/browser/devtools/inspector/
    },
  
    /**
     * Delay the "inspector-updated" notification while a tool
     * is updating itself.  Returns a function that must be
     * invoked when the tool is done updating with the node
     * that the tool is viewing.
     */
-@@ -402,16 +395,17 @@ InspectorPanel.prototype = {
+@@ -398,16 +403,17 @@ InspectorPanel.prototype = {
     */
    destroy: function InspectorPanel__destroy() {
      if (this._destroyPromise) {
        return this._destroyPromise;
      }
      if (this.walker) {
        this._destroyPromise = this.walker.release().then(null, console.error);
        delete this.walker;
-+      delete this.nodeStyle;
++      delete this.pageStyle;
      } else {
        this._destroyPromise = promise.resolve(null);
      }
  
      this.cancelUpdate();
      this.cancelLayoutChange();
  
      if (this.browser) {
@@ -207,43 +262,20 @@ diff --git a/browser/devtools/inspector/
              testNavigate(() => {
                // close the inspector
                finishUp();
              });
            });
          });
        });
      });
-diff --git a/browser/devtools/main.js b/browser/devtools/main.js
---- a/browser/devtools/main.js
-+++ b/browser/devtools/main.js
-@@ -91,17 +91,18 @@ Tools.inspector = {
-   ordinal: 2,
-   modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
-   icon: "chrome://browser/skin/devtools/tool-inspector.png",
-   url: "chrome://browser/content/devtools/inspector/inspector.xul",
-   label: l10n("inspector.label", inspectorStrings),
-   tooltip: l10n("inspector.tooltip", inspectorStrings),
- 
-   isTargetSupported: function(target) {
--    return !target.isRemote;
-+//    return !target.isRemote;
-+return true;
-   },
- 
-   build: function(iframeWindow, toolbox) {
-     let panel = new InspectorPanel(iframeWindow, toolbox);
-     return panel.open();
-   }
- };
- 
 diff --git a/browser/devtools/markupview/markup-view.js b/browser/devtools/markupview/markup-view.js
 --- a/browser/devtools/markupview/markup-view.js
 +++ b/browser/devtools/markupview/markup-view.js
-@@ -279,21 +279,25 @@ MarkupView.prototype = {
+@@ -297,21 +297,25 @@ MarkupView.prototype = {
    navigate: function MT__navigate(aContainer, aIgnoreFocus)
    {
      if (!aContainer) {
        return;
      }
  
      let node = aContainer.node;
      this.markNodeAsSelected(node);
@@ -289,730 +321,16 @@ diff --git a/browser/devtools/styleedito
      });
    },
  
    /**
     * Handler for document load, forward event with
     * all the stylesheets available on load.
     *
     * @param  {string} type
-diff --git a/browser/devtools/styleinspector/computed-view.js b/browser/devtools/styleinspector/computed-view.js
---- a/browser/devtools/styleinspector/computed-view.js
-+++ b/browser/devtools/styleinspector/computed-view.js
-@@ -3,24 +3,27 @@
- /* 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/. */
- 
- const {Cc, Ci, Cu} = require("chrome");
- 
- let ToolDefinitions = require("main").Tools;
- let {CssLogic} = require("devtools/styleinspector/css-logic");
-+let promise = require("sdk/core/promise");
-+let {EventEmitter} = require("devtools/shared/event-emitter");
- 
- Cu.import("resource://gre/modules/Services.jsm");
- Cu.import("resource://gre/modules/PluralForm.jsm");
- Cu.import("resource://gre/modules/XPCOMUtils.jsm");
- Cu.import("resource://gre/modules/devtools/Templater.jsm");
- 
- Cu.import("resource:///modules/devtools/gDevTools.jsm");
- 
-+
- const FILTER_CHANGED_TIMEOUT = 300;
- 
- const HTML_NS = "http://www.w3.org/1999/xhtml";
- const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
- 
- /**
-  * Helper for long-running processes that should yield occasionally to
-  * the mainloop.
-@@ -85,16 +88,17 @@ UpdateProcess.prototype = {
-       this._runBatch();
-       this.schedule();
-     } catch(e) {
-       if (e instanceof StopIteration) {
-         this.onBatch();
-         this.onDone();
-         return;
-       }
-+      console.error(e);
-       throw e;
-     }
-   },
- 
-   _runBatch: function Y_runBatch()
-   {
-     let time = Date.now();
-     while(!this.canceled) {
-@@ -112,22 +116,22 @@ UpdateProcess.prototype = {
- /**
-  * CssHtmlTree is a panel that manages the display of a table sorted by style.
-  * There should be one instance of CssHtmlTree per style display (of which there
-  * will generally only be one).
-  *
-  * @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
-  * @constructor
-  */
--function CssHtmlTree(aStyleInspector)
-+function CssHtmlTree(aStyleInspector, aNodeStyle)
- {
-   this.styleWindow = aStyleInspector.window;
-   this.styleDocument = aStyleInspector.window.document;
-   this.styleInspector = aStyleInspector;
--  this.cssLogic = aStyleInspector.cssLogic;
-+  this.nodeStyle = aNodeStyle;
-   this.propertyViews = [];
- 
-   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
-     getService(Ci.nsIXULChromeRegistry);
-   this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
- 
-   // Create bound methods.
-   this.siFocusWindow = this.focusWindow.bind(this);
-@@ -139,16 +143,19 @@ function CssHtmlTree(aStyleInspector)
-   // Nodes used in templating
-   this.root = this.styleDocument.getElementById("root");
-   this.templateRoot = this.styleDocument.getElementById("templateRoot");
-   this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
- 
-   // No results text.
-   this.noResults = this.styleDocument.getElementById("noResults");
- 
-+
-+  CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
-+
-   // The element that we're inspecting, and the document that it comes from.
-   this.viewedElement = null;
-   this.createStyleViews();
- }
- 
- /**
-  * Memoized lookup of a l10n string from a string bundle.
-  * @param {string} aName The key to lookup.
-@@ -202,18 +209,16 @@ XPCOMUtils.defineLazyGetter(this, "clipb
-   return Cc["@mozilla.org/widget/clipboardhelper;1"].
-     getService(Ci.nsIClipboardHelper);
- });
- 
- CssHtmlTree.prototype = {
-   // Cache the list of properties that match the selected element.
-   _matchedProperties: null,
- 
--  htmlComplete: false,
--
-   // Used for cancelling timeouts in the style filter.
-   _filterChangedTimeout: null,
- 
-   // The search filter
-   searchField: null,
- 
-   // Reference to the "Include browser styles" checkbox.
-   includeBrowserStylesCheckbox: null,
-@@ -222,124 +227,147 @@ CssHtmlTree.prototype = {
-   _panelRefreshTimeout: null,
- 
-   // Toggle for zebra striping
-   _darkStripe: true,
- 
-   // Number of visible properties
-   numVisibleProperties: 0,
- 
-+  setNodeStyle: function(nodeStyle) {
-+    this.nodeStyle = nodeStyle;
-+  },
-+
-   get includeBrowserStyles()
-   {
-     return this.includeBrowserStylesCheckbox.checked;
-   },
- 
-   /**
-    * Update the highlighted element. The CssHtmlTree panel will show the style
-    * information for the given element.
-    * @param {nsIDOMElement} aElement The highlighted node to get styles for.
-    */
--  highlight: function CssHtmlTree_highlight(aElement)
--  {
--    this.viewedElement = aElement;
--    this._matchedProperties = null;
--
-+  highlight: function(aElement) {
-+    dump("Highlighting\n");
-     if (!aElement) {
-       if (this._refreshProcess) {
-         this._refreshProcess.cancel();
-       }
--      return;
-+      return promise.resolve(undefined)
-     }
- 
--    if (this.htmlComplete) {
--      this.refreshSourceFilter();
--      this.refreshPanel();
--    } else {
--      if (this._refreshProcess) {
--        this._refreshProcess.cancel();
-+    if (aElement === this.viewedElement) {
-+      return promise.resolve(undefined);
-+    }
-+
-+    this.viewedElement = aElement;
-+
-+    this.refreshSourceFilter();
-+    return this.refreshPanel();
-+  },
-+
-+  _createPropertyViews: function()
-+  {
-+    if (this._createViewsPromise) {
-+      return this._createViewsPromise;
-+    }
-+
-+    let deferred = promise.defer();
-+    this._createViewsPromise = deferred.promise;
-+
-+    this.refreshSourceFilter();
-+    this.numVisibleProperties = 0;
-+    let fragment = this.styleDocument.createDocumentFragment();
-+
-+    this._createViewsProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
-+      onItem: (aPropertyName) => {
-+        // Per-item callback.
-+        let propView = new PropertyView(this, aPropertyName);
-+        fragment.appendChild(propView.buildMain());
-+        fragment.appendChild(propView.buildSelectorContainer());
-+
-+        if (propView.visible) {
-+          this.numVisibleProperties++;
-+        }
-+        this.propertyViews.push(propView);
-+      },
-+      onCancel: () => {
-+        deferred.reject("_createPropertyViews cancelled");
-+      },
-+      onDone: () => {
-+        dump("DONE CREATING SELECTOR CONTAINER\n");
-+        // Completed callback.
-+        this.propertyContainer.appendChild(fragment);
-+        this.noResults.hidden = this.numVisibleProperties > 0;
-+        deferred.resolve(undefined);
-       }
-+    });
- 
--      CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
--
--      // Refresh source filter ... this must be done after templateRoot has been
--      // processed.
--      this.refreshSourceFilter();
--      this.numVisibleProperties = 0;
--      let fragment = this.styleDocument.createDocumentFragment();
--      this._refreshProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
--        onItem: function(aPropertyName) {
--          // Per-item callback.
--          let propView = new PropertyView(this, aPropertyName);
--          fragment.appendChild(propView.buildMain());
--          fragment.appendChild(propView.buildSelectorContainer());
--
--          if (propView.visible) {
--            this.numVisibleProperties++;
--          }
--          propView.refreshMatchedSelectors();
--          this.propertyViews.push(propView);
--        }.bind(this),
--        onDone: function() {
--          // Completed callback.
--          this.htmlComplete = true;
--          this.propertyContainer.appendChild(fragment);
--          this.noResults.hidden = this.numVisibleProperties > 0;
--          this._refreshProcess = null;
--
--          // If a refresh was scheduled during the building, complete it.
--          if (this._needsRefresh) {
--            delete this._needsRefresh;
--            this.refreshPanel();
--          } else {
--            Services.obs.notifyObservers(null, "StyleInspector-populated", null);
--          }
--        }.bind(this)});
--
--      this._refreshProcess.schedule();
--    }
-+    this._createViewsProcess.schedule();
-+    return deferred.promise;
-   },
- 
-   /**
-    * Refresh the panel content.
-    */
-   refreshPanel: function CssHtmlTree_refreshPanel()
-   {
--    // If we're still in the process of creating the initial layout,
--    // leave it alone.
--    if (!this.htmlComplete) {
-+    dump("--------ABOUT TO GET COMPUTED: " + this.viewedElement + "\n");
-+    console.trace();
-+    return promise.all([
-+      this._createPropertyViews(),
-+      this.nodeStyle.getComputed(this.viewedElement, {
-+        filter: this._sourceFilter,
-+        onlyMatched: !this.includeBrowserStyles,
-+        markMatched: true
-+      })
-+    ]).then(([createViews, computed]) => {
-+      dump("-0-0-=-=-0=-0=-0= GOT COMPUTED?!?!?!?!??!?!?!?!?");
-+      this._matchedProperties = new Set;
-+      for (let name in computed) {
-+        if (computed[name].matched) {
-+          this._matchedProperties.add(name);
-+        }
-+      }
-+      this._computed = computed;
-+
-       if (this._refreshProcess) {
--        this._needsRefresh = true;
-+        this._refreshProcess.cancel();
-       }
--      return;
--    }
- 
--    if (this._refreshProcess) {
--      this._refreshProcess.cancel();
--    }
-+      this.noResults.hidden = true;
- 
--    this.noResults.hidden = true;
-+      // Reset visible property count
-+      this.numVisibleProperties = 0;
- 
--    // Reset visible property count
--    this.numVisibleProperties = 0;
-+      // Reset zebra striping.
-+      this._darkStripe = true;
- 
--    // Reset zebra striping.
--    this._darkStripe = true;
-+      let display = this.propertyContainer.style.display;
- 
--    let display = this.propertyContainer.style.display;
--    this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
--      onItem: function(aPropView) {
--        aPropView.refresh();
--      }.bind(this),
--      onDone: function() {
--        this._refreshProcess = null;
--        this.noResults.hidden = this.numVisibleProperties > 0;
--        Services.obs.notifyObservers(null, "StyleInspector-populated", null);
--      }.bind(this)
--    });
--    this._refreshProcess.schedule();
-+      let deferred = promise.defer();
-+      this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
-+        onItem: (aPropView) => {
-+          aPropView.refresh();
-+        },
-+        onCancel: () => {
-+          deferred.reject("refresh cancelled");
-+        },
-+        onDone: () => {
-+          this._refreshProcess = null;
-+          this.noResults.hidden = this.numVisibleProperties > 0;
-+          this.styleInspector.inspector.emit("computed-view-refreshed");
-+          dump("DONE WITH ALL THE UPDATING AND WHATNOT\n");
-+          deferred.resolve(undefined);
-+        }
-+      });
-+      this._refreshProcess.schedule();
-+      return deferred.promise;
-+    }).then(null, (err) => console.error(err));
-   },
- 
-   /**
-    * Called when the user enters a search term.
-    *
-    * @param {Event} aEvent the DOM Event object.
-    */
-   filterChanged: function CssHtmlTree_filterChanged(aEvent)
-@@ -372,17 +400,17 @@ CssHtmlTree.prototype = {
-    * When includeBrowserStyles.checked is false we only display properties that
-    * have matched selectors and have been included by the document or one of the
-    * document's stylesheets. If .checked is false we display all properties
-    * including those that come from UA stylesheets.
-    */
-   refreshSourceFilter: function CssHtmlTree_setSourceFilter()
-   {
-     this._matchedProperties = null;
--    this.cssLogic.sourceFilter = this.includeBrowserStyles ?
-+    this._sourceFilter = this.includeBrowserStyles ?
-                                  CssLogic.FILTER.UA :
-                                  CssLogic.FILTER.ALL;
-   },
- 
-   /**
-    * The CSS as displayed by the UI.
-    */
-   createStyleViews: function CssHtmlTree_createStyleViews()
-@@ -404,31 +432,28 @@ CssHtmlTree.prototype = {
-       } else {
-         CssHtmlTree.propertyNames.push(prop);
-       }
-     }
- 
-     CssHtmlTree.propertyNames.sort();
-     CssHtmlTree.propertyNames.push.apply(CssHtmlTree.propertyNames,
-       mozProps.sort());
-+
-+    this._createPropertyViews();
-   },
- 
-   /**
--   * Get a list of properties that have matched selectors.
-+   * Get a set of properties that have matched selectors.
-    *
--   * @return {object} the object maps property names (keys) to booleans (values)
--   * that tell if the given property has matched selectors or not.
-+   * @return {Set} If a property name is in the set, it has matching selectors.
-    */
-   get matchedProperties()
-   {
--    if (!this._matchedProperties) {
--      this._matchedProperties =
--        this.cssLogic.hasMatchedSelectors(CssHtmlTree.propertyNames);
--    }
--    return this._matchedProperties;
-+    return this._matchedProperties || new Set;
-   },
- 
-   /**
-    * Focus the window on mousedown.
-    *
-    * @param aEvent The event object
-    */
-   focusWindow: function si_focusWindow(aEvent)
-@@ -468,16 +493,19 @@ CssHtmlTree.prototype = {
-     delete this.viewedElement;
- 
-     // Remove event listeners
-     this.includeBrowserStylesCheckbox.removeEventListener("command",
-       this.includeBrowserStylesChanged);
-     this.searchField.removeEventListener("command", this.filterChanged);
- 
-     // Cancel tree construction
-+    if (this._createViewsProcess) {
-+      this._createViewsProcess.cancel();
-+    }
-     if (this._refreshProcess) {
-       this._refreshProcess.cancel();
-     }
- 
-     // Remove context menu
-     let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
-     let menu = outerDoc.querySelector("#computed-view-context-menu");
-     if (menu) {
-@@ -512,21 +540,28 @@ CssHtmlTree.prototype = {
- 
-     // The document in which we display the results (csshtmltree.xul).
-     delete this.styleDocument;
- 
-     // The element that we're inspecting, and the document that it comes from.
-     delete this.propertyViews;
-     delete this.styleWindow;
-     delete this.styleDocument;
--    delete this.cssLogic;
-     delete this.styleInspector;
-   },
- };
- 
-+function PropertyInfo(aTree, aName) {
-+  this.tree = aTree;
-+  this.name = aName;
-+}
-+PropertyInfo.prototype = {
-+  get value() this.tree._computed ? this.tree._computed[this.name].value : ""
-+}
-+
- /**
-  * A container to give easy access to property data from the template engine.
-  *
-  * @constructor
-  * @param {CssHtmlTree} aTree the CssHtmlTree instance we are working with.
-  * @param {string} aName the CSS property name for which this PropertyView
-  * instance will render the rules.
-  */
-@@ -534,16 +569,17 @@ function PropertyView(aTree, aName)
- {
-   this.tree = aTree;
-   this.name = aName;
-   this.getRTLAttr = aTree.getRTLAttr;
- 
-   this.link = "https://developer.mozilla.org/CSS/" + aName;
- 
-   this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
-+  this._propertyInfo = new PropertyInfo(aTree, aName);
- }
- 
- PropertyView.prototype = {
-   // The parent element which contains the open attribute
-   element: null,
- 
-   // Property header node
-   propertyHeader: null,
-@@ -580,25 +616,25 @@ PropertyView.prototype = {
-     return this.propertyInfo.value;
-   },
- 
-   /**
-    * An easy way to access the CssPropertyInfo behind this PropertyView.
-    */
-   get propertyInfo()
-   {
--    return this.tree.cssLogic.getPropertyInfo(this.name);
-+    return this._propertyInfo;
-   },
- 
-   /**
-    * Does the property have any matched selectors?
-    */
-   get hasMatchedSelectors()
-   {
--    return this.name in this.tree.matchedProperties;
-+    return this.tree.matchedProperties.has(this.name);
-   },
- 
-   /**
-    * Should this property be visible?
-    */
-   get visible()
-   {
-     if (!this.tree.includeBrowserStyles && !this.hasMatchedSelectors) {
-@@ -723,44 +759,62 @@ PropertyView.prototype = {
-     this.refreshMatchedSelectors();
-   },
- 
-   /**
-    * Refresh the panel matched rules.
-    */
-   refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors()
-   {
-+    dump("refreshing matched on " + this.name + " (" + this.matchedSelectorsContainer + ")\n");
-     let hasMatchedSelectors = this.hasMatchedSelectors;
-     this.matchedSelectorsContainer.parentNode.hidden = !hasMatchedSelectors;
- 
-     if (hasMatchedSelectors) {
-       this.matchedExpander.classList.add("expandable");
-     } else {
-       this.matchedExpander.classList.remove("expandable");
-     }
- 
-     if (this.matchedExpanded && hasMatchedSelectors) {
--      CssHtmlTree.processTemplate(this.templateMatchedSelectors,
--        this.matchedSelectorsContainer, this);
--      this.matchedExpander.setAttribute("open", "");
-+      return this.tree.nodeStyle.getMatchedSelectors(this.tree.viewedElement, this.name).then(matched => {
-+        dump("got matched!\n");
-+        if (!this.matchedExpanded) {
-+          return;
-+        }
-+
-+        this._matchedSelectorResponse = matched;
-+        CssHtmlTree.processTemplate(this.templateMatchedSelectors,
-+          this.matchedSelectorsContainer, this);
-+        this.matchedExpander.setAttribute("open", "");
-+        dump("property expanded\n");
-+        this.tree.styleInspector.inspector.emit("computed-view-property-expanded");
-+      }).then(null, console.error);
-     } else {
-+      dump("NO MATCHED\n");
-       this.matchedSelectorsContainer.innerHTML = "";
-       this.matchedExpander.removeAttribute("open");
-+      return promise.resolve(undefined);
-     }
-   },
- 
-+  get matchedSelectors()
-+  {
-+    return this._matchedSelectorResponse;
-+  },
-+
-   /**
-    * Provide access to the matched SelectorViews that we are currently
-    * displaying.
-    */
-   get matchedSelectorViews()
-   {
-     if (!this._matchedSelectorViews) {
-       this._matchedSelectorViews = [];
--      this.propertyInfo.matchedSelectors.forEach(
-+      this._matchedSelectorResponse.forEach(
-         function matchedSelectorViews_convert(aSelectorInfo) {
-           this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
-         }, this);
-     }
- 
-     return this._matchedSelectorViews;
-   },
- 
-@@ -797,16 +851,25 @@ PropertyView.prototype = {
-  * @param CssHtmlTree aTree, the owning CssHtmlTree
-  * @param aSelectorInfo
-  */
- function SelectorView(aTree, aSelectorInfo)
- {
-   this.tree = aTree;
-   this.selectorInfo = aSelectorInfo;
-   this._cacheStatusNames();
-+
-+  let rule = this.selectorInfo.rule;
-+  if (rule && rule.parentStyleSheet) {
-+    this.sheet = rule.parentStyleSheet;
-+    this.source = CssLogic.shortSource(this.sheet) + ":" + rule.line;
-+  } else if (this.selectorInfo.sourceElement) {
-+    this.source = CssLogic.l10n("rule.sourceElement");
-+    this.href = "#";
-+  }
- }
- 
- /**
-  * Decode for cssInfo.rule.status
-  * @see SelectorView.prototype._cacheStatusNames
-  * @see CssLogic.STATUS
-  */
- SelectorView.STATUS_NAMES = [
-@@ -854,34 +917,34 @@ SelectorView.prototype = {
-   /**
-    * Get class name for selector depending on status
-    */
-   get statusClass()
-   {
-     return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
-   },
- 
--  /**
--   * A localized Get localized human readable info
--   */
--  text: function SelectorView_text(aElement) {
--    let result = this.selectorInfo.selector.text;
--    if (this.selectorInfo.elementStyle) {
--      let source = this.selectorInfo.sourceElement;
--      let inspector = this.tree.styleInspector.inspector;
-+  get href()
-+  {
-+    if (this._href) {
-+      return this._href;
-+    }
-+    let sheet = this.selectorInfo.rule.parentStyleSheet;
-+    this._href = sheet ? (sheet.href || sheet.nodeHref) : "#";
-+    return this._href;
-+  },
- 
--      if (inspector.selection.node == source) {
--        result = "this";
--      } else {
--        result = CssLogic.getShortName(source);
--      }
--      result += ".style";
--    }
-+  get sourceText()
-+  {
-+    return this.selectorInfo.sourceText;
-+  },
- 
--    return result;
-+  get value()
-+  {
-+    return this.selectorInfo.value;
-   },
- 
-   maybeOpenStyleEditor: function(aEvent)
-   {
-     let keyEvent = Ci.nsIDOMKeyEvent;
-     if (aEvent.keyCode == keyEvent.DOM_VK_RETURN) {
-       this.openStyleEditor();
-     }
-@@ -895,59 +958,42 @@ SelectorView.prototype = {
-    *   We can only view stylesheets contained in document.styleSheets inside the
-    *   style editor.
-    *
-    * @param aEvent The click event
-    */
-   openStyleEditor: function(aEvent)
-   {
-     let inspector = this.tree.styleInspector.inspector;
--    let contentDoc = inspector.selection.document;
--    let cssSheet = this.selectorInfo.selector._cssRule._cssSheet;
--    let line = this.selectorInfo.ruleLine || 0;
--    let contentSheet = false;
--    let styleSheet;
--    let styleSheets;
-+    let rule = this.selectorInfo.rule;
-+    let line = rule.line || 0;
- 
-     // The style editor can only display stylesheets coming from content because
-     // chrome stylesheets are not listed in the editor's stylesheet selector.
-     //
-     // If the stylesheet is a content stylesheet we send it to the style
-     // editor else we display it in the view source window.
-     //
--    // We check if cssSheet exists in case of inline styles (which contain no
--    // sheet)
--    if (cssSheet) {
--      styleSheet = cssSheet.domSheet;
--      styleSheets = contentDoc.styleSheets;
--
--      // Array.prototype.indexOf always returns -1 here so we loop through
--      // the styleSheets array instead.
--      for each (let sheet in styleSheets) {
--        if (sheet == styleSheet) {
--          contentSheet = true;
--          break;
--        }
-+    let href = rule.href;
-+    let sheet = rule.parentStyleSheet;
-+    if (sheet && href && !sheet.isSystem) {
-+      let target = inspector.target;
-+      if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
-+        gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
-+          toolbox.getCurrentPanel().selectStyleSheet(href, line);
-+        });
-       }
-+      return;
-     }
- 
--    if (contentSheet) {
--      let target = inspector.target;
-+    let contentDoc = null;
-+    let rawNode = this.tree.viewedElement.rawNode();
-+    if (rawNode) {
-+      contentDoc = rawNode.ownerDocument;
-+    }
- 
--      if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
--        gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
--          toolbox.getCurrentPanel().selectStyleSheet(styleSheet.href, line);
--        });
--      }
--    } else {
--      let href = styleSheet ? styleSheet.href : "";
--      let viewSourceUtils = inspector.viewSourceUtils;
--
--      if (this.selectorInfo.sourceElement) {
--        href = this.selectorInfo.sourceElement.ownerDocument.location.href;
--      }
--      viewSourceUtils.viewSource(href, null, contentDoc, line);
--    }
--  },
-+    let viewSourceUtils = inspector.viewSourceUtils;
-+    viewSourceUtils.viewSource(href, null, contentDoc, line);
-+  }
- };
- 
- exports.CssHtmlTree = CssHtmlTree;
--exports.PropertyView = PropertyView;
-\ No newline at end of file
-+exports.PropertyView = PropertyView;
 diff --git a/browser/devtools/styleinspector/computedview.xhtml b/browser/devtools/styleinspector/computedview.xhtml
 --- a/browser/devtools/styleinspector/computedview.xhtml
 +++ b/browser/devtools/styleinspector/computedview.xhtml
 @@ -93,22 +93,22 @@
        -->
        <div id="templateMatchedSelectors">
          <loop foreach="selector in ${matchedSelectorViews}">
            <p>
@@ -1033,75 +351,16 @@ diff --git a/browser/devtools/styleinspe
              </span>
            </p>
          </loop>
        </div>
      </div>
  
    </body>
  </html>
-diff --git a/browser/devtools/styleinspector/css-logic.js b/browser/devtools/styleinspector/css-logic.js
---- a/browser/devtools/styleinspector/css-logic.js
-+++ b/browser/devtools/styleinspector/css-logic.js
-@@ -138,16 +138,18 @@ CssLogic.prototype = {
-     this._matchedSelectors = null;
-   },
- 
-   /**
-    * Focus on a new element - remove the style caches.
-    *
-    * @param {nsIDOMElement} aViewedElement the element the user has highlighted
-    * in the Inspector.
-+   *
-+   * @returns a promise that will be resolved when CssLogic is up to date.
-    */
-   highlight: function CssLogic_highlight(aViewedElement)
-   {
-     if (!aViewedElement) {
-       this.viewedElement = null;
-       this.viewedDocument = null;
-       this._computedStyle = null;
-       this.reset();
-@@ -750,17 +752,17 @@ CssLogic.isContentStylesheet = function 
-  *
-  * @param {CSSStyleSheet} aSheet the DOM object for the style sheet.
-  * @return {string} the address of the stylesheet.
-  */
- CssLogic.href = function CssLogic_href(aSheet)
- {
-   let href = aSheet.href;
-   if (!href) {
--    href = aSheet.ownerNode.ownerDocument.location;
-+    href = aSheet.ownerNode.ownerDocument.location.href;
-   }
- 
-   return href;
- };
- 
- /**
-  * Return a shortened version of a style sheet's source.
-  *
-@@ -1128,16 +1130,17 @@ CssSheet.prototype = {
-  * to cache data. If the rule comes from element.style, then provide
-  * an object of the form: {style: element.style}.
-  * @param {Element} [aElement] If the rule comes from element.style, then this
-  * argument must point to the element.
-  * @constructor
-  */
- function CssRule(aCssSheet, aDomRule, aElement)
- {
-+  dump("Creating rule: " + aDomRule + "\n");
-   this._cssSheet = aCssSheet;
-   this._domRule = aDomRule;
- 
-   let parentRule = aDomRule.parentRule;
-   if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
-     this.mediaText = parentRule.media.mediaText;
-   }
- 
 diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js
 --- a/browser/devtools/styleinspector/rule-view.js
 +++ b/browser/devtools/styleinspector/rule-view.js
 @@ -2,19 +2,21 @@
  /* vim: set ts=2 et sw=2 tw=80: */
  /* This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -1172,21 +431,21 @@ diff --git a/browser/devtools/styleinspe
   * @param {object} aStore
   *        The ElementStyle can use this object to store metadata
   *        that might outlast the rule view, particularly the current
   *        set of disabled properties.
   *
   * @constructor
   */
 -function ElementStyle(aElement, aStore)
-+function ElementStyle(aElement, aStore, aNodeStyle)
++function ElementStyle(aElement, aStore, aPageStyle)
  {
    this.element = aElement;
    this.store = aStore || {};
-+  this.nodeStyle = aNodeStyle;
++  this.pageStyle = aPageStyle;
  
    // We don't want to overwrite this.store.userProperties so we only create it
    // if it doesn't already exist.
    if (!("userProperties" in this.store)) {
      this.store.userProperties = new UserProperties();
    }
  
    if (!("disabled" in this.store)) {
@@ -1228,17 +487,17 @@ diff --git a/browser/devtools/styleinspe
 +   * Returns a promise that will be resolved when the elementStyle is
 +   * ready.
     */
    populate: function ElementStyle_populate()
    {
 -    // Store the current list of rules (if any) during the population
 -    // process.  They will be reused if possible.
 -    this._refreshRules = this.rules;
-+    let populated = this.nodeStyle.getApplied(this.element, {
++    let populated = this.pageStyle.getApplied(this.element, {
 +      inherited: true,
 +      matchedSelectors: true
 +    }).then(entries => {
 +      // Make sure the dummy element has been created before continuing...
 +      return this.dummyElementPromise.then(() => {
 +        dump("BUILT MY DUMMY ELEMENT\n");
 +        if (this.populated != populated) {
 +          dump("DON'T CARE ANYMORE\n");
@@ -1759,66 +1018,67 @@ diff --git a/browser/devtools/styleinspe
      let newTextProps = this._getTextProperties();
  
      // Update current properties for each property present on the style.
      // This will mark any touched properties with _visited so we
      // can detect properties that weren't touched (because they were
      // removed from the style).
      // Also keep track of properties that didn't exist in the current set
      // of properties.
-@@ -857,40 +960,45 @@ TextProperty.prototype = {
+@@ -857,24 +960,25 @@ TextProperty.prototype = {
   * property will be available with the user interface.
   *
   * @param {Document} aDoc
   *        The document that will contain the rule view.
   * @param {object} aStore
   *        The CSS rule view can use this object to store metadata
   *        that might outlast the rule view, particularly the current
   *        set of disabled properties.
 - * @param {<iframe>} aOuterIFrame
 - *        The iframe containing the ruleview.
-+ * @param {NodeStyleFront} aNodeStyle
-+ *        The NodeStyleFront for communicating with the remote server.
++ * @param {PageStyleFront} aPageStyle
++ *        The PageStyleFront for communicating with the remote server.
   * @constructor
   */
 -function CssRuleView(aDoc, aStore)
-+function CssRuleView(aDoc, aStore, aNodeStyle)
++function CssRuleView(aDoc, aStore, aPageStyle)
  {
    this.doc = aDoc;
    this.store = aStore;
-+  this.nodeStyle = aNodeStyle;
++  this.pageStyle = aPageStyle;
    this.element = this.doc.createElementNS(HTML_NS, "div");
    this.element.className = "ruleview devtools-monospace";
    this.element.flex = 1;
  
    this._boundCopy = this._onCopy.bind(this);
    this.element.addEventListener("copy", this._boundCopy);
  
-   this._showEmpty();
+   let options = {
+@@ -888,16 +992,20 @@ function CssRuleView(aDoc, aStore)
  }
  
  exports.CssRuleView = CssRuleView;
  
  CssRuleView.prototype = {
    // The element that we're inspecting.
    _viewedElement: null,
  
-+  setNodeStyle: function(aNodeStyle) {
-+    this.nodeStyle = aNodeStyle;
++  setPageStyle: function(aPageStyle) {
++    this.pageStyle = aPageStyle;
 +  },
 +
    /**
     * Return {bool} true if the rule view currently has an input editor visible.
     */
    get isEditing() {
      return this.element.querySelectorAll(".styleinspector-propertyeditor").length > 0;
    },
  
    destroy: function CssRuleView_destroy()
-@@ -908,62 +1016,79 @@ CssRuleView.prototype = {
+@@ -917,62 +1025,79 @@ CssRuleView.prototype = {
    /**
     * Update the highlighted element.
     *
     * @param {nsIDOMElement} aElement
     *        The node whose style rules we'll inspect.
     */
    highlight: function CssRuleView_highlight(aElement)
    {
@@ -1843,17 +1103,17 @@ diff --git a/browser/devtools/styleinspe
      }
  
 -    this._elementStyle = new ElementStyle(aElement, this.store);
 -    this._elementStyle.onChanged = function() {
 -      this._changed();
 -    }.bind(this);
 -
 -    this._createEditors();
-+    this._elementStyle = new ElementStyle(aElement, this.store, this.nodeStyle);
++    this._elementStyle = new ElementStyle(aElement, this.store, this.pageStyle);
 +    dump("Calling _populate\n");
 +    return this._populate().then(() => {
 +      this._elementStyle.onChanged = () => {
 +        this._changed();
 +      };
 +    }).then(null, (err) => console.error);
    },
  
@@ -1904,17 +1164,17 @@ diff --git a/browser/devtools/styleinspe
    },
  
    /**
     * Show the user that the rule view has no node selected.
     */
    _showEmpty: function CssRuleView_showEmpty()
    {
      if (this.doc.getElementById("noResults") > 0) {
-@@ -1007,36 +1132,41 @@ CssRuleView.prototype = {
+@@ -1016,36 +1141,41 @@ CssRuleView.prototype = {
      this.element.dispatchEvent(evt);
    },
  
    /**
     * Creates editor UI for each of the rules in _elementStyle.
     */
    _createEditors: function CssRuleView_createEditors()
    {
@@ -1947,17 +1207,17 @@ diff --git a/browser/devtools/styleinspe
    },
  
    /**
     * Copy selected text from the rule view.
     *
     * @param {Event} aEvent
     *        The event object.
     */
-@@ -1091,39 +1221,41 @@ function RuleEditor(aRuleView, aRule)
+@@ -1100,39 +1230,41 @@ function RuleEditor(aRuleView, aRule)
    this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
  
    this._create();
  }
  
  RuleEditor.prototype = {
    _create: function RuleEditor_create()
    {
@@ -1990,17 +1250,17 @@ diff --git a/browser/devtools/styleinspe
      sourceLabel.setAttribute("tooltiptext", this.rule.title);
      source.appendChild(sourceLabel);
  
      let code = createChild(this.element, "div", {
        class: "ruleview-code"
      });
  
      let header = createChild(code, "div", {});
-@@ -1160,65 +1292,65 @@ RuleEditor.prototype = {
+@@ -1169,65 +1301,65 @@ RuleEditor.prototype = {
      }.bind(this), false);
  
      this.propertyList = createChild(code, "ul", {
        class: "ruleview-propertylist"
      });
  
      this.populate();
  
@@ -2065,20 +1325,20 @@ diff --git a/browser/devtools/styleinspe
      }
  
      for (let prop of this.rule.textProps) {
        if (!prop.editor) {
          new TextPropertyEditor(this, prop);
          this.propertyList.appendChild(prop.editor.element);
        }
      }
-@@ -1323,17 +1455,17 @@ RuleEditor.prototype = {
- function TextPropertyEditor(aRuleEditor, aProperty)
+@@ -1335,17 +1467,17 @@ function TextPropertyEditor(aRuleEditor,
  {
    this.doc = aRuleEditor.doc;
+   this.popup = aRuleEditor.ruleView.popup;
    this.prop = aProperty;
    this.prop.editor = this;
    this.browserWindow = this.doc.defaultView.top;
  
    let sheet = this.prop.rule.sheet;
 -  let href = sheet ? CssLogic.href(sheet) : null;
 +  let href = sheet ? (sheet.href || sheet.nodeHref) : null;
    if (href) {
@@ -2212,17 +1472,17 @@ diff --git a/browser/devtools/styleinspe
  exports.RuleViewTool = RuleViewTool;
  
  RuleViewTool.prototype = {
    onSelect: function RVT_onSelect(aEvent) {
 +    if (!this.isActive()) {
 +      // We'll update when the panel is selected.
 +      return;
 +    }
-+    this.view.setNodeStyle(this.inspector.nodeStyle);
++    this.view.setPageStyle(this.inspector.pageStyle);
 +    dump("HIGHLIGHTING: " + this.inspector.selection.nodeFront + "\n");
 +    console.trace();
 +
      if (!this.inspector.selection.isConnected() ||
          !this.inspector.selection.isElementNode()) {
        this.view.highlight(null);
        return;
      }
@@ -2296,17 +1556,17 @@ diff --git a/browser/devtools/styleinspe
  function ComputedViewTool(aInspector, aWindow, aIFrame)
  {
    this.inspector = aInspector;
    this.window = aWindow;
    this.document = aWindow.document;
    this.outerIFrame = aIFrame;
 -  this.cssLogic = new CssLogic();
 -  this.view = new ComputedView.CssHtmlTree(this);
-+  this.view = new ComputedView.CssHtmlTree(this, aInspector.nodeStyle);
++  this.view = new ComputedView.CssHtmlTree(this, aInspector.pageStyle);
  
    this._onSelect = this.onSelect.bind(this);
    this.inspector.selection.on("detached", this._onSelect);
 -  this.inspector.selection.on("new-node", this._onSelect);
 +  this.inspector.selection.on("new-node-front", this._onSelect);
    if (this.inspector.highlighter) {
      this.inspector.highlighter.on("locked", this._onSelect);
    }
@@ -2328,17 +1588,17 @@ diff --git a/browser/devtools/styleinspe
  ComputedViewTool.prototype = {
    onSelect: function CVT_onSelect(aEvent)
    {
 +    if (!this.isActive()) {
 +      // We'll try again when we're selected.
 +      return;
 +    }
 +
-+    this.view.setNodeStyle(this.inspector.nodeStyle);
++    this.view.setPageStyle(this.inspector.pageStyle);
 +
      if (!this.inspector.selection.isConnected() ||
          !this.inspector.selection.isElementNode()) {
        this.view.highlight(null);
        return;
      }
  
 -    if (!aEvent || aEvent == "new-node") {
@@ -2869,16 +2129,104 @@ diff --git a/browser/devtools/styleinspe
    doc = null;
    gBrowser.removeCurrentTab();
    finish();
  }
  
  function test()
  {
    waitForExplicitFinish();
+diff --git a/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_existing_property.js b/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_existing_property.js
+--- a/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_existing_property.js
++++ b/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_existing_property.js
+@@ -57,17 +57,17 @@ function openRuleView()
+   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+     inspector = toolbox.getCurrentPanel();
+     inspector.sidebar.select("ruleview");
+ 
+     // Highlight a node.
+     let node = content.document.getElementsByTagName("h1")[0];
+     inspector.selection.setNode(node);
+ 
+-    inspector.sidebar.once("ruleview-ready", testCompletion);
++    inspector.once("inspector-updated", testCompletion);
+   });
+ }
+ 
+ function testCompletion()
+ {
+   ruleViewWindow = inspector.sidebar.getWindowForTab("ruleview");
+   let brace = ruleViewWindow.document.querySelector(".ruleview-propertyname");
+ 
+diff --git a/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_new_property.js b/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_new_property.js
+--- a/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_new_property.js
++++ b/browser/devtools/styleinspector/test/browser_bug893965_css_property_completion_new_property.js
+@@ -44,17 +44,17 @@ function openRuleView() {
+   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+     inspector = toolbox.getCurrentPanel();
+     inspector.sidebar.select("ruleview");
+ 
+     // Highlight a node.
+     let node = content.document.getElementsByTagName("h1")[0];
+     inspector.selection.setNode(node);
+ 
+-    inspector.sidebar.once("ruleview-ready", testCompletion);
++    inspector.once("inspector-updated", testCompletion);
+   });
+ }
+ 
+ function testCompletion() {
+   ruleViewWindow = inspector.sidebar.getWindowForTab("ruleview");
+   let brace = ruleViewWindow.document.querySelector(".ruleview-ruleclose");
+ 
+   waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) {
+diff --git a/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_existing_property_value_pair.js b/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_existing_property_value_pair.js
+--- a/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_existing_property_value_pair.js
++++ b/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_existing_property_value_pair.js
+@@ -42,17 +42,17 @@ function openRuleView()
+   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+     inspector = toolbox.getCurrentPanel();
+     inspector.sidebar.select("ruleview");
+ 
+     // Highlight a node.
+     let node = content.document.getElementsByTagName("h1")[0];
+     inspector.selection.setNode(node);
+ 
+-    inspector.sidebar.once("ruleview-ready", testCompletion);
++    inspector.once("inspector-updated", testCompletion);
+   });
+ }
+ 
+ function testCompletion()
+ {
+   ruleViewWindow = inspector.sidebar.getWindowForTab("ruleview");
+   brace = ruleViewWindow.document.querySelector(".ruleview-ruleclose");
+ 
+diff --git a/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_new_property_value_pair.js b/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_new_property_value_pair.js
+--- a/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_new_property_value_pair.js
++++ b/browser/devtools/styleinspector/test/browser_bug894376_css_value_completion_new_property_value_pair.js
+@@ -43,17 +43,17 @@ function openRuleView() {
+   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+     inspector = toolbox.getCurrentPanel();
+     inspector.sidebar.select("ruleview");
+ 
+     // Highlight a node.
+     let node = content.document.getElementsByTagName("h1")[0];
+     inspector.selection.setNode(node);
+ 
+-    inspector.sidebar.once("ruleview-ready", testCompletion);
++    inspector.once("inspector-updated", testCompletion);
+   });
+ }
+ 
+ function testCompletion() {
+   ruleViewWindow = inspector.sidebar.getWindowForTab("ruleview");
+   brace = ruleViewWindow.document.querySelector(".ruleview-ruleclose");
+ 
+   waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) {
 diff --git a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
 --- a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
 +++ b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
 @@ -8,62 +8,57 @@ let doc;
  let computedView;
  
  function createDocument()
  {
@@ -5004,121 +4352,16 @@ diff --git a/browser/devtools/styleinspe
    let placeholder = computedView.noResults;
    let win = computedView.styleWindow;
    let display = win.getComputedStyle(placeholder).display;
  
    is(display, "none", "placeholder is hidden");
  
    finishUp();
  }
-diff --git a/browser/devtools/styleinspector/test/head.js b/browser/devtools/styleinspector/test/head.js
---- a/browser/devtools/styleinspector/test/head.js
-+++ b/browser/devtools/styleinspector/test/head.js
-@@ -1,26 +1,34 @@
- /* vim:set ts=2 sw=2 sts=2 et: */
- /* 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/. */
- 
-+Services.prefs.setBoolPref("devtools.debugger.log", true);
-+SimpleTest.registerCleanupFunction(() => {
-+  Services.prefs.clearUserPref("devtools.debugger.log");
-+});
-+
- let tempScope = {};
-+
- Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
- let ConsoleUtils = tempScope.ConsoleUtils;
- let gDevTools = tempScope.gDevTools;
- 
- Cu.import("resource://gre/modules/devtools/Loader.jsm", tempScope);
- let devtools = tempScope.devtools;
- 
- let TargetFactory = devtools.TargetFactory;
- let {CssHtmlTree} = devtools.require("devtools/styleinspector/computed-view");
- let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
- let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
- 
-+let promise = devtools.require("sdk/core/promise");
-+
- let {
-   editableField,
-   getInplaceEditorForSpan: inplaceEditor
- } = devtools.require("devtools/shared/inplace-editor");
- Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
- let console = tempScope.console;
- 
- let browser, hudId, hud, hudBox, filterBox, outputNode, cs;
-@@ -35,16 +43,38 @@ function addTab(aURL)
- function openInspector(callback)
- {
-   let target = TargetFactory.forTab(gBrowser.selectedTab);
-   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
-     callback(toolbox.getCurrentPanel());
-   });
- }
- 
-+function openRuleView(callback)
-+{
-+  openInspector(inspector => {
-+    inspector.sidebar.once("ruleview-ready", () => {
-+      inspector.sidebar.select("ruleview");
-+      let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
-+      callback(inspector, ruleView);
-+    })
-+  });
-+}
-+
-+function openComputedView(callback)
-+{
-+  openInspector(inspector => {
-+    inspector.sidebar.once("computedview-ready", () => {
-+      inspector.sidebar.select("computedview");
-+      let ruleView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
-+      callback(inspector, ruleView);
-+    })
-+  });
-+}
-+
- function addStyle(aDocument, aString)
- {
-   let node = aDocument.createElement('style');
-   node.setAttribute("type", "text/css");
-   node.textContent = aString;
-   aDocument.getElementsByTagName("head")[0].appendChild(node);
-   return node;
- }
-@@ -115,12 +145,27 @@ function contextMenuClick(element) {
- 
-   evt.initMouseEvent('contextmenu', true, true,
-        element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
-        false, false, false, button, null);
- 
-   element.dispatchEvent(evt);
- }
- 
-+function expectRuleChange(rule) {
-+  return rule._applyingModifications;
-+}
-+
-+function promiseDone(promise) {
-+  promise.then(null, err => {
-+    ok(false, "Promise failed: " + err);
-+    if (err.stack) {
-+      dump(err.stack);
-+    }
-+    SimpleTest.finish();
-+  });
-+}
-+
-+
- registerCleanupFunction(tearDown);
- 
- waitForExplicitFinish();
- 
 diff --git a/toolkit/devtools/Console.jsm b/toolkit/devtools/Console.jsm
 --- a/toolkit/devtools/Console.jsm
 +++ b/toolkit/devtools/Console.jsm
 @@ -72,17 +72,17 @@ function fmt(aStr, aMaxLen, aMinLen, aOp
        return start + "_" + end;
      }
      else {
        return aStr.substring(0, aMaxLen - 1) + "_";
deleted file mode 100644
--- a/styles-actor-src.diff
+++ /dev/null
@@ -1,3 +0,0 @@
-# HG changeset patch
-# Parent 5bf585ae5028b9620dedba884e45099be0af68ce
-
--- a/styles-actor.diff
+++ b/styles-actor.diff
@@ -1,55 +1,923 @@
 # HG changeset patch
-# Parent 96c7688c3ffa882291f439b5fcd1827e1395d3e6
+# User Dave Camp <dcamp@mozilla.com>
+# Date 1374619918 25200
+#      Tue Jul 23 15:51:58 2013 -0700
+# Node ID fa033ae8ee80efe8165f322bcaa437ab03064095
+# Parent 2325e927a53a7cf2d1c0fb9b07166c8f665bf304
+[mq]: styles-actor.diff
+* * *
+[mq]: crap.diff
+
+diff --git a/browser/devtools/styleinspector/computed-view.js b/browser/devtools/styleinspector/computed-view.js
+--- a/browser/devtools/styleinspector/computed-view.js
++++ b/browser/devtools/styleinspector/computed-view.js
+@@ -3,24 +3,28 @@
+ /* 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/. */
+ 
+ const {Cc, Ci, Cu} = require("chrome");
+ 
+ let ToolDefinitions = require("main").Tools;
+ let {CssLogic} = require("devtools/styleinspector/css-logic");
++let {ELEMENT_STYLE} = require("devtools/server/actors/styles");
++let promise = require("sdk/core/promise");
++let {EventEmitter} = require("devtools/shared/event-emitter");
+ 
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/PluralForm.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ Cu.import("resource://gre/modules/devtools/Templater.jsm");
+ 
+ Cu.import("resource:///modules/devtools/gDevTools.jsm");
+ 
++
+ const FILTER_CHANGED_TIMEOUT = 300;
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ 
+ /**
+  * Helper for long-running processes that should yield occasionally to
+  * the mainloop.
+@@ -85,16 +89,17 @@ UpdateProcess.prototype = {
+       this._runBatch();
+       this.schedule();
+     } catch(e) {
+       if (e instanceof StopIteration) {
+         this.onBatch();
+         this.onDone();
+         return;
+       }
++      console.error(e);
+       throw e;
+     }
+   },
+ 
+   _runBatch: function Y_runBatch()
+   {
+     let time = Date.now();
+     while(!this.canceled) {
+@@ -112,22 +117,22 @@ UpdateProcess.prototype = {
+ /**
+  * CssHtmlTree is a panel that manages the display of a table sorted by style.
+  * There should be one instance of CssHtmlTree per style display (of which there
+  * will generally only be one).
+  *
+  * @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
+  * @constructor
+  */
+-function CssHtmlTree(aStyleInspector)
++function CssHtmlTree(aStyleInspector, aPageStyle)
+ {
+   this.styleWindow = aStyleInspector.window;
+   this.styleDocument = aStyleInspector.window.document;
+   this.styleInspector = aStyleInspector;
+-  this.cssLogic = aStyleInspector.cssLogic;
++  this.pageStyle = aPageStyle;
+   this.propertyViews = [];
+ 
+   let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
+     getService(Ci.nsIXULChromeRegistry);
+   this.getRTLAttr = chromeReg.isLocaleRTL("global") ? "rtl" : "ltr";
+ 
+   // Create bound methods.
+   this.siFocusWindow = this.focusWindow.bind(this);
+@@ -139,16 +144,19 @@ function CssHtmlTree(aStyleInspector)
+   // Nodes used in templating
+   this.root = this.styleDocument.getElementById("root");
+   this.templateRoot = this.styleDocument.getElementById("templateRoot");
+   this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
+ 
+   // No results text.
+   this.noResults = this.styleDocument.getElementById("noResults");
+ 
++
++  CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
++
+   // The element that we're inspecting, and the document that it comes from.
+   this.viewedElement = null;
+   this.createStyleViews();
+ }
+ 
+ /**
+  * Memoized lookup of a l10n string from a string bundle.
+  * @param {string} aName The key to lookup.
+@@ -202,18 +210,16 @@ XPCOMUtils.defineLazyGetter(this, "clipb
+   return Cc["@mozilla.org/widget/clipboardhelper;1"].
+     getService(Ci.nsIClipboardHelper);
+ });
+ 
+ CssHtmlTree.prototype = {
+   // Cache the list of properties that match the selected element.
+   _matchedProperties: null,
+ 
+-  htmlComplete: false,
+-
+   // Used for cancelling timeouts in the style filter.
+   _filterChangedTimeout: null,
+ 
+   // The search filter
+   searchField: null,
+ 
+   // Reference to the "Include browser styles" checkbox.
+   includeBrowserStylesCheckbox: null,
+@@ -222,124 +228,147 @@ CssHtmlTree.prototype = {
+   _panelRefreshTimeout: null,
+ 
+   // Toggle for zebra striping
+   _darkStripe: true,
+ 
+   // Number of visible properties
+   numVisibleProperties: 0,
+ 
++  setPageStyle: function(pageStyle) {
++    this.pageStyle = pageStyle;
++  },
++
+   get includeBrowserStyles()
+   {
+     return this.includeBrowserStylesCheckbox.checked;
+   },
+ 
+   /**
+    * Update the highlighted element. The CssHtmlTree panel will show the style
+    * information for the given element.
+    * @param {nsIDOMElement} aElement The highlighted node to get styles for.
+    */
+-  highlight: function CssHtmlTree_highlight(aElement)
+-  {
+-    this.viewedElement = aElement;
+-    this._matchedProperties = null;
+-
++  highlight: function(aElement) {
++    dump("Highlighting\n");
+     if (!aElement) {
+       if (this._refreshProcess) {
+         this._refreshProcess.cancel();
+       }
+-      return;
++      return promise.resolve(undefined)
+     }
+ 
+-    if (this.htmlComplete) {
+-      this.refreshSourceFilter();
+-      this.refreshPanel();
+-    } else {
+-      if (this._refreshProcess) {
+-        this._refreshProcess.cancel();
++    if (aElement === this.viewedElement) {
++      return promise.resolve(undefined);
++    }
++
++    this.viewedElement = aElement;
++
++    this.refreshSourceFilter();
++    return this.refreshPanel();
++  },
++
++  _createPropertyViews: function()
++  {
++    if (this._createViewsPromise) {
++      return this._createViewsPromise;
++    }
++
++    let deferred = promise.defer();
++    this._createViewsPromise = deferred.promise;
++
++    this.refreshSourceFilter();
++    this.numVisibleProperties = 0;
++    let fragment = this.styleDocument.createDocumentFragment();
++
++    this._createViewsProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
++      onItem: (aPropertyName) => {
++        // Per-item callback.
++        let propView = new PropertyView(this, aPropertyName);
++        fragment.appendChild(propView.buildMain());
++        fragment.appendChild(propView.buildSelectorContainer());
++
++        if (propView.visible) {
++          this.numVisibleProperties++;
++        }
++        this.propertyViews.push(propView);
++      },
++      onCancel: () => {
++        deferred.reject("_createPropertyViews cancelled");
++      },
++      onDone: () => {
++        dump("DONE CREATING SELECTOR CONTAINER\n");
++        // Completed callback.
++        this.propertyContainer.appendChild(fragment);
++        this.noResults.hidden = this.numVisibleProperties > 0;
++        deferred.resolve(undefined);
+       }
++    });
+ 
+-      CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
+-
+-      // Refresh source filter ... this must be done after templateRoot has been
+-      // processed.
+-      this.refreshSourceFilter();
+-      this.numVisibleProperties = 0;
+-      let fragment = this.styleDocument.createDocumentFragment();
+-      this._refreshProcess = new UpdateProcess(this.styleWindow, CssHtmlTree.propertyNames, {
+-        onItem: function(aPropertyName) {
+-          // Per-item callback.
+-          let propView = new PropertyView(this, aPropertyName);
+-          fragment.appendChild(propView.buildMain());
+-          fragment.appendChild(propView.buildSelectorContainer());
+-
+-          if (propView.visible) {
+-            this.numVisibleProperties++;
+-          }
+-          propView.refreshMatchedSelectors();
+-          this.propertyViews.push(propView);
+-        }.bind(this),
+-        onDone: function() {
+-          // Completed callback.
+-          this.htmlComplete = true;
+-          this.propertyContainer.appendChild(fragment);
+-          this.noResults.hidden = this.numVisibleProperties > 0;
+-          this._refreshProcess = null;
+-
+-          // If a refresh was scheduled during the building, complete it.
+-          if (this._needsRefresh) {
+-            delete this._needsRefresh;
+-            this.refreshPanel();
+-          } else {
+-            Services.obs.notifyObservers(null, "StyleInspector-populated", null);
+-          }
+-        }.bind(this)});
+-
+-      this._refreshProcess.schedule();
+-    }
++    this._createViewsProcess.schedule();
++    return deferred.promise;
+   },
+ 
+   /**
+    * Refresh the panel content.
+    */
+   refreshPanel: function CssHtmlTree_refreshPanel()
+   {
+-    // If we're still in the process of creating the initial layout,
+-    // leave it alone.
+-    if (!this.htmlComplete) {
++    dump("--------ABOUT TO GET COMPUTED: " + this.viewedElement + "\n");
++    console.trace();
++    return promise.all([
++      this._createPropertyViews(),
++      this.pageStyle.getComputed(this.viewedElement, {
++        filter: this._sourceFilter,
++        onlyMatched: !this.includeBrowserStyles,
++        markMatched: true
++      })
++    ]).then(([createViews, computed]) => {
++      dump("-0-0-=-=-0=-0=-0= GOT COMPUTED?!?!?!?!??!?!?!?!?");
++      this._matchedProperties = new Set;
++      for (let name in computed) {
++        if (computed[name].matched) {
++          this._matchedProperties.add(name);
++        }
++      }
++      this._computed = computed;
++
+       if (this._refreshProcess) {
+-        this._needsRefresh = true;
++        this._refreshProcess.cancel();
+       }
+-      return;
+-    }
+ 
+-    if (this._refreshProcess) {
+-      this._refreshProcess.cancel();
+-    }
++      this.noResults.hidden = true;
+ 
+-    this.noResults.hidden = true;
++      // Reset visible property count
++      this.numVisibleProperties = 0;
+ 
+-    // Reset visible property count
+-    this.numVisibleProperties = 0;
++      // Reset zebra striping.
++      this._darkStripe = true;
+ 
+-    // Reset zebra striping.
+-    this._darkStripe = true;
++      let display = this.propertyContainer.style.display;
+ 
+-    let display = this.propertyContainer.style.display;
+-    this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
+-      onItem: function(aPropView) {
+-        aPropView.refresh();
+-      }.bind(this),
+-      onDone: function() {
+-        this._refreshProcess = null;
+-        this.noResults.hidden = this.numVisibleProperties > 0;
+-        Services.obs.notifyObservers(null, "StyleInspector-populated", null);
+-      }.bind(this)
+-    });
+-    this._refreshProcess.schedule();
++      let deferred = promise.defer();
++      this._refreshProcess = new UpdateProcess(this.styleWindow, this.propertyViews, {
++        onItem: (aPropView) => {
++          aPropView.refresh();
++        },
++        onCancel: () => {
++          deferred.reject("refresh cancelled");
++        },
++        onDone: () => {
++          this._refreshProcess = null;
++          this.noResults.hidden = this.numVisibleProperties > 0;
++          this.styleInspector.inspector.emit("computed-view-refreshed");
++          dump("DONE WITH ALL THE UPDATING AND WHATNOT\n");
++          deferred.resolve(undefined);
++        }
++      });
++      this._refreshProcess.schedule();
++      return deferred.promise;
++    }).then(null, (err) => console.error(err));
+   },
+ 
+   /**
+    * Called when the user enters a search term.
+    *
+    * @param {Event} aEvent the DOM Event object.
+    */
+   filterChanged: function CssHtmlTree_filterChanged(aEvent)
+@@ -372,17 +401,17 @@ CssHtmlTree.prototype = {
+    * When includeBrowserStyles.checked is false we only display properties that
+    * have matched selectors and have been included by the document or one of the
+    * document's stylesheets. If .checked is false we display all properties
+    * including those that come from UA stylesheets.
+    */
+   refreshSourceFilter: function CssHtmlTree_setSourceFilter()
+   {
+     this._matchedProperties = null;
+-    this.cssLogic.sourceFilter = this.includeBrowserStyles ?
++    this._sourceFilter = this.includeBrowserStyles ?
+                                  CssLogic.FILTER.UA :
+                                  CssLogic.FILTER.ALL;
+   },
+ 
+   /**
+    * The CSS as displayed by the UI.
+    */
+   createStyleViews: function CssHtmlTree_createStyleViews()
+@@ -404,31 +433,28 @@ CssHtmlTree.prototype = {
+       } else {
+         CssHtmlTree.propertyNames.push(prop);
+       }
+     }
+ 
+     CssHtmlTree.propertyNames.sort();
+     CssHtmlTree.propertyNames.push.apply(CssHtmlTree.propertyNames,
+       mozProps.sort());
++
++    this._createPropertyViews();
+   },
+ 
+   /**
+-   * Get a list of properties that have matched selectors.
++   * Get a set of properties that have matched selectors.
+    *
+-   * @return {object} the object maps property names (keys) to booleans (values)
+-   * that tell if the given property has matched selectors or not.
++   * @return {Set} If a property name is in the set, it has matching selectors.
+    */
+   get matchedProperties()
+   {
+-    if (!this._matchedProperties) {
+-      this._matchedProperties =
+-        this.cssLogic.hasMatchedSelectors(CssHtmlTree.propertyNames);
+-    }
+-    return this._matchedProperties;
++    return this._matchedProperties || new Set;
+   },
+ 
+   /**
+    * Focus the window on mousedown.
+    *
+    * @param aEvent The event object
+    */
+   focusWindow: function si_focusWindow(aEvent)
+@@ -468,16 +494,19 @@ CssHtmlTree.prototype = {
+     delete this.viewedElement;
+ 
+     // Remove event listeners
+     this.includeBrowserStylesCheckbox.removeEventListener("command",
+       this.includeBrowserStylesChanged);
+     this.searchField.removeEventListener("command", this.filterChanged);
+ 
+     // Cancel tree construction
++    if (this._createViewsProcess) {
++      this._createViewsProcess.cancel();
++    }
+     if (this._refreshProcess) {
+       this._refreshProcess.cancel();
+     }
+ 
+     // Remove context menu
+     let outerDoc = this.styleInspector.outerIFrame.ownerDocument;
+     let menu = outerDoc.querySelector("#computed-view-context-menu");
+     if (menu) {
+@@ -512,21 +541,28 @@ CssHtmlTree.prototype = {
+ 
+     // The document in which we display the results (csshtmltree.xul).
+     delete this.styleDocument;
+ 
+     // The element that we're inspecting, and the document that it comes from.
+     delete this.propertyViews;
+     delete this.styleWindow;
+     delete this.styleDocument;
+-    delete this.cssLogic;
+     delete this.styleInspector;
+   },
+ };
+ 
++function PropertyInfo(aTree, aName) {
++  this.tree = aTree;
++  this.name = aName;
++}
++PropertyInfo.prototype = {
++  get value() this.tree._computed ? this.tree._computed[this.name].value : ""
++}
++
+ /**
+  * A container to give easy access to property data from the template engine.
+  *
+  * @constructor
+  * @param {CssHtmlTree} aTree the CssHtmlTree instance we are working with.
+  * @param {string} aName the CSS property name for which this PropertyView
+  * instance will render the rules.
+  */
+@@ -534,16 +570,17 @@ function PropertyView(aTree, aName)
+ {
+   this.tree = aTree;
+   this.name = aName;
+   this.getRTLAttr = aTree.getRTLAttr;
+ 
+   this.link = "https://developer.mozilla.org/CSS/" + aName;
+ 
+   this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
++  this._propertyInfo = new PropertyInfo(aTree, aName);
+ }
+ 
+ PropertyView.prototype = {
+   // The parent element which contains the open attribute
+   element: null,
+ 
+   // Property header node
+   propertyHeader: null,
+@@ -580,25 +617,25 @@ PropertyView.prototype = {
+     return this.propertyInfo.value;
+   },
+ 
+   /**
+    * An easy way to access the CssPropertyInfo behind this PropertyView.
+    */
+   get propertyInfo()
+   {
+-    return this.tree.cssLogic.getPropertyInfo(this.name);
++    return this._propertyInfo;
+   },
+ 
+   /**
+    * Does the property have any matched selectors?
+    */
+   get hasMatchedSelectors()
+   {
+-    return this.name in this.tree.matchedProperties;
++    return this.tree.matchedProperties.has(this.name);
+   },
+ 
+   /**
+    * Should this property be visible?
+    */
+   get visible()
+   {
+     if (!this.tree.includeBrowserStyles && !this.hasMatchedSelectors) {
+@@ -723,44 +760,62 @@ PropertyView.prototype = {
+     this.refreshMatchedSelectors();
+   },
+ 
+   /**
+    * Refresh the panel matched rules.
+    */
+   refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors()
+   {
++    dump("refreshing matched on " + this.name + " (" + this.matchedSelectorsContainer + ")\n");
+     let hasMatchedSelectors = this.hasMatchedSelectors;
+     this.matchedSelectorsContainer.parentNode.hidden = !hasMatchedSelectors;
+ 
+     if (hasMatchedSelectors) {
+       this.matchedExpander.classList.add("expandable");
+     } else {
+       this.matchedExpander.classList.remove("expandable");
+     }
+ 
+     if (this.matchedExpanded && hasMatchedSelectors) {
+-      CssHtmlTree.processTemplate(this.templateMatchedSelectors,
+-        this.matchedSelectorsContainer, this);
+-      this.matchedExpander.setAttribute("open", "");
++      return this.tree.pageStyle.getMatchedSelectors(this.tree.viewedElement, this.name).then(matched => {
++        dump("got matched!\n");
++        if (!this.matchedExpanded) {
++          return;
++        }
++
++        this._matchedSelectorResponse = matched;
++        CssHtmlTree.processTemplate(this.templateMatchedSelectors,
++          this.matchedSelectorsContainer, this);
++        this.matchedExpander.setAttribute("open", "");
++        dump("property expanded\n");
++        this.tree.styleInspector.inspector.emit("computed-view-property-expanded");
++      }).then(null, console.error);
+     } else {
++      dump("NO MATCHED\n");
+       this.matchedSelectorsContainer.innerHTML = "";
+       this.matchedExpander.removeAttribute("open");
++      return promise.resolve(undefined);
+     }
+   },
+ 
++  get matchedSelectors()
++  {
++    return this._matchedSelectorResponse;
++  },
++
+   /**
+    * Provide access to the matched SelectorViews that we are currently
+    * displaying.
+    */
+   get matchedSelectorViews()
+   {
+     if (!this._matchedSelectorViews) {
+       this._matchedSelectorViews = [];
+-      this.propertyInfo.matchedSelectors.forEach(
++      this._matchedSelectorResponse.forEach(
+         function matchedSelectorViews_convert(aSelectorInfo) {
+           this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
+         }, this);
+     }
+ 
+     return this._matchedSelectorViews;
+   },
+ 
+@@ -797,16 +852,25 @@ PropertyView.prototype = {
+  * @param CssHtmlTree aTree, the owning CssHtmlTree
+  * @param aSelectorInfo
+  */
+ function SelectorView(aTree, aSelectorInfo)
+ {
+   this.tree = aTree;
+   this.selectorInfo = aSelectorInfo;
+   this._cacheStatusNames();
++
++  let rule = this.selectorInfo.rule;
++  if (rule && rule.parentStyleSheet) {
++    this.sheet = rule.parentStyleSheet;
++    this.source = CssLogic.shortSource(this.sheet) + ":" + rule.line;
++  } else {
++    this.source = CssLogic.l10n("rule.sourceElement");
++    this.href = "#";
++  }
+ }
+ 
+ /**
+  * Decode for cssInfo.rule.status
+  * @see SelectorView.prototype._cacheStatusNames
+  * @see CssLogic.STATUS
+  */
+ SelectorView.STATUS_NAMES = [
+@@ -854,34 +918,35 @@ SelectorView.prototype = {
+   /**
+    * Get class name for selector depending on status
+    */
+   get statusClass()
+   {
+     return SelectorView.CLASS_NAMES[this.selectorInfo.status - 1];
+   },
+ 
+-  /**
+-   * A localized Get localized human readable info
+-   */
+-  text: function SelectorView_text(aElement) {
+-    let result = this.selectorInfo.selector.text;
+-    if (this.selectorInfo.elementStyle) {
+-      let source = this.selectorInfo.sourceElement;
+-      let inspector = this.tree.styleInspector.inspector;
++  get href()
++  {
++    if (this._href) {
++      return this._href;
++    }
++    let sheet = this.selectorInfo.rule.parentStyleSheet;
++    this._href = sheet ? sheet.href : "#";
++    return this._href;
++  },
+ 
+-      if (inspector.selection.node == source) {
+-        result = "this";
+-      } else {
+-        result = CssLogic.getShortName(source);
+-      }
+-      result += ".style";
+-    }
++  get sourceText()
++  {
++    return this.selectorInfo.sourceText;
++  },
+ 
+-    return result;
++
++  get value()
++  {
++    return this.selectorInfo.value;
+   },
+ 
+   maybeOpenStyleEditor: function(aEvent)
+   {
+     let keyEvent = Ci.nsIDOMKeyEvent;
+     if (aEvent.keyCode == keyEvent.DOM_VK_RETURN) {
+       this.openStyleEditor();
+     }
+@@ -895,59 +960,44 @@ SelectorView.prototype = {
+    *   We can only view stylesheets contained in document.styleSheets inside the
+    *   style editor.
+    *
+    * @param aEvent The click event
+    */
+   openStyleEditor: function(aEvent)
+   {
+     let inspector = this.tree.styleInspector.inspector;
+-    let contentDoc = inspector.selection.document;
+-    let cssSheet = this.selectorInfo.selector._cssRule._cssSheet;
+-    let line = this.selectorInfo.ruleLine || 0;
+-    let contentSheet = false;
+-    let styleSheet;
+-    let styleSheets;
++    let rule = this.selectorInfo.rule;
++    let line = rule.line || 0;
+ 
+     // The style editor can only display stylesheets coming from content because
+     // chrome stylesheets are not listed in the editor's stylesheet selector.
+     //
+     // If the stylesheet is a content stylesheet we send it to the style
+     // editor else we display it in the view source window.
+     //
+-    // We check if cssSheet exists in case of inline styles (which contain no
+-    // sheet)
+-    if (cssSheet) {
+-      styleSheet = cssSheet.domSheet;
+-      styleSheets = contentDoc.styleSheets;
+ 
+-      // Array.prototype.indexOf always returns -1 here so we loop through
+-      // the styleSheets array instead.
+-      for each (let sheet in styleSheets) {
+-        if (sheet == styleSheet) {
+-          contentSheet = true;
+-          break;
+-        }
++    // XXX: I don't think this is right.
++    let href = rule.href;
++    let sheet = rule.parentStyleSheet;
++    if (sheet && href && !sheet.isSystem) {
++      let target = inspector.target;
++      if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
++        gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
++          toolbox.getCurrentPanel().selectStyleSheet(href, line);
++        });
+       }
++      return;
+     }
+ 
+-    if (contentSheet) {
+-      let target = inspector.target;
++    let contentDoc = null;
++    let rawNode = this.tree.viewedElement.rawNode();
++    if (rawNode) {
++      contentDoc = rawNode.ownerDocument;
++    }
+ 
+-      if (ToolDefinitions.styleEditor.isTargetSupported(target)) {
+-        gDevTools.showToolbox(target, "styleeditor").then(function(toolbox) {
+-          toolbox.getCurrentPanel().selectStyleSheet(styleSheet.href, line);
+-        });
+-      }
+-    } else {
+-      let href = styleSheet ? styleSheet.href : "";
+-      let viewSourceUtils = inspector.viewSourceUtils;
+-
+-      if (this.selectorInfo.sourceElement) {
+-        href = this.selectorInfo.sourceElement.ownerDocument.location.href;
+-      }
+-      viewSourceUtils.viewSource(href, null, contentDoc, line);
+-    }
+-  },
++    let viewSourceUtils = inspector.viewSourceUtils;
++    viewSourceUtils.viewSource(href, null, contentDoc, line);
++  }
+ };
+ 
+ exports.CssHtmlTree = CssHtmlTree;
+-exports.PropertyView = PropertyView;
+\ No newline at end of file
++exports.PropertyView = PropertyView;
+diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js
+--- a/browser/devtools/styleinspector/rule-view.js
++++ b/browser/devtools/styleinspector/rule-view.js
+@@ -1154,17 +1154,17 @@ RuleEditor.prototype = {
+ 
+     this.element.addEventListener("mousedown", function() {
+       this.doc.defaultView.focus();
+ 
+       let editorNodes =
+         this.doc.querySelectorAll(".styleinspector-propertyeditor");
+ 
+       if (editorNodes) {
+-        for (let node of editorNodes) {
++          let node of editorNodes) {
+           if (node.inplaceEditor) {
+             node.inplaceEditor._clear();
+           }
+         }
+       }
+     }.bind(this), false);
+ 
+     this.propertyList = createChild(code, "ul", {
+diff --git a/browser/devtools/styleinspector/test/head.js b/browser/devtools/styleinspector/test/head.js
+--- a/browser/devtools/styleinspector/test/head.js
++++ b/browser/devtools/styleinspector/test/head.js
+@@ -1,26 +1,34 @@
+ /* vim:set ts=2 sw=2 sts=2 et: */
+ /* 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/. */
+ 
++Services.prefs.setBoolPref("devtools.debugger.log", true);
++SimpleTest.registerCleanupFunction(() => {
++  Services.prefs.clearUserPref("devtools.debugger.log");
++});
++
+ let tempScope = {};
++
+ Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
+ let ConsoleUtils = tempScope.ConsoleUtils;
+ let gDevTools = tempScope.gDevTools;
+ 
+ Cu.import("resource://gre/modules/devtools/Loader.jsm", tempScope);
+ let devtools = tempScope.devtools;
+ 
+ let TargetFactory = devtools.TargetFactory;
+ let {CssHtmlTree} = devtools.require("devtools/styleinspector/computed-view");
+ let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
+ let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
+ 
++let promise = devtools.require("sdk/core/promise");
++
+ let {
+   editableField,
+   getInplaceEditorForSpan: inplaceEditor
+ } = devtools.require("devtools/shared/inplace-editor");
+ Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
+ let console = tempScope.console;
+ 
+ let browser, hudId, hud, hudBox, filterBox, outputNode, cs;
+@@ -35,16 +43,38 @@ function addTab(aURL)
+ function openInspector(callback)
+ {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
+     callback(toolbox.getCurrentPanel());
+   });
+ }
+ 
++function openRuleView(callback)
++{
++  openInspector(inspector => {
++    inspector.sidebar.once("ruleview-ready", () => {
++      inspector.sidebar.select("ruleview");
++      let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
++      callback(inspector, ruleView);
++    })
++  });
++}
++
++function openComputedView(callback)
++{
++  openInspector(inspector => {
++    inspector.sidebar.once("computedview-ready", () => {
++      inspector.sidebar.select("computedview");
++      let ruleView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
++      callback(inspector, ruleView);
++    })
++  });
++}
++
+ function addStyle(aDocument, aString)
+ {
+   let node = aDocument.createElement('style');
+   node.setAttribute("type", "text/css");
+   node.textContent = aString;
+   aDocument.getElementsByTagName("head")[0].appendChild(node);
+   return node;
+ }
+@@ -115,12 +145,27 @@ function contextMenuClick(element) {
+ 
+   evt.initMouseEvent('contextmenu', true, true,
+        element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
+        false, false, false, button, null);
+ 
+   element.dispatchEvent(evt);
+ }
+ 
++function expectRuleChange(rule) {
++  return rule._applyingModifications;
++}
++
++function promiseDone(promise) {
++  promise.then(null, err => {
++    ok(false, "Promise failed: " + err);
++    if (err.stack) {
++      dump(err.stack);
++    }
++    SimpleTest.finish();
++  });
++}
++
++
+ registerCleanupFunction(tearDown);
+ 
+ waitForExplicitFinish();
+ 
 diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
 --- a/toolkit/devtools/server/actors/inspector.js
 +++ b/toolkit/devtools/server/actors/inspector.js
-@@ -56,16 +56,18 @@ const protocol = require("devtools/serve
+@@ -55,16 +55,17 @@ const {Cc, Ci, Cu} = require("chrome");
+ const protocol = require("devtools/server/protocol");
  const {Arg, Option, method, RetVal, types} = protocol;
  const {LongStringActor, ShortLongString} = require("devtools/server/actors/string");
  const promise = require("sdk/core/promise");
  const object = require("sdk/util/object");
  const events = require("sdk/event/core");
  const { Unknown } = require("sdk/platform/xpcom");
  const { Class } = require("sdk/core/heritage");
++const {PageStyleActor} = require("devtools/server/actors/styles");
  
-+const {NodeStyleActor} = require("devtools/server/actors/styles");
-+
  const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
  
+ const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
+ 
+ const HELPER_SHEET = "." + HIDDEN_CLASS + " { visibility: hidden !important }";
+ 
  Cu.import("resource://gre/modules/Services.jsm");
+@@ -2075,48 +2076,88 @@ var InspectorActor = protocol.ActorClass
+     let deferred = promise.defer();
+     this._walkerPromise = deferred.promise;
  
- exports.register = function(handle) {
-   handle.addTabActor(InspectorActor, "inspectorActor");
- };
+     let window = this.window;
  
-@@ -2082,33 +2084,71 @@ var InspectorActor = protocol.ActorClass
+     var domReady = () => {
+       let tabActor = this.tabActor;
+       window.removeEventListener("DOMContentLoaded", domReady, true);
+-      deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options));
++      this.walker = WalkerActor(this.conn, window.document, tabActor._tabbrowser, options);
++      deferred.resolve(this.walker);
+     };
+ 
+     if (window.document.readyState === "loading") {
+       window.addEventListener("DOMContentLoaded", domReady, true);
+     } else {
+       domReady();
      }
  
      return this._walkerPromise;
    }, {
      request: {},
      response: {
        walker: RetVal("domwalker")
      }
 +  }),
 +
-+  getNodeStyle: method(function() {
-+    if (this._nodeStylePromise) {
-+      return this._nodeStylePromise;
++  getPageStyle: method(function() {
++    if (this._pageStylePromise) {
++      return this._pageStylePromise;
 +    }
 +
-+    this._nodeStylePromise = this.getWalker().then(walker => {
-+      return NodeStyleActor(walker);
++    this._pageStylePromise = this.getWalker().then(walker => {
++      return PageStyleActor(this);
 +    });
-+    return this._nodeStylePromise;
++    return this._pageStylePromise;
 +  }, {
 +    request: {},
-+    response: { nodeStyle: RetVal("nodestyle") }
++    response: { pageStyle: RetVal("pagestyle") }
    })
  });
  
  /**
   * Client side of the inspector actor, which is used to create
   * inspector-related actors, including the walker.
   */
  var InspectorFront = exports.InspectorFront = protocol.FrontClass(InspectorActor, {
@@ -59,123 +927,186 @@ diff --git a/toolkit/devtools/server/act
  
      // 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.
      client.addActorPool(this);
      this.manage(this);
 -  }
 +  },
 +
-+  getWalker: custom(function() {
++  getWalker: protocol.custom(function() {
++    dump("ABOUT TO GET A WALKER\n");
 +    return this._getWalker().then(walker => {
 +      this.walker = walker;
 +      return walker;
 +    });
 +  }, {
 +    impl: "_getWalker"
 +  }),
 +
-+  getNodeStyle: custom(function() {
-+    this._getNodeStyle().then(nodeStyle => {
++  getPageStyle: protocol.custom(function() {
++    return this._getPageStyle().then(pageStyle => {
 +      // We need a walker to understand node references from the
 +      // node style.
 +      if (this.walker) {
-+        return nodeStyle;
++        return pageStyle;
 +      }
 +      return this.getWalker().then(() => {
-+        return nodeStyle;
++        return pageStyle;
 +      });
-+    }
++    });
 +  }, {
-+    impl: "_getNodeStyle"
++    impl: "_getPageStyle"
 +  })
  });
  
  function documentWalker(node, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) {
    return new DocumentWalker(node, whatToShow, whitespaceTextFilter, false);
  }
  
  // Exported for test purposes.
  exports._documentWalker = documentWalker;
 diff --git a/toolkit/devtools/server/actors/styles.js b/toolkit/devtools/server/actors/styles.js
 new file mode 100644
 --- /dev/null
 +++ b/toolkit/devtools/server/actors/styles.js
-@@ -0,0 +1,553 @@
+@@ -0,0 +1,758 @@
 +/* 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 {Cc, Ci} = require("chrome");
 +const protocol = require("devtools/server/protocol");
 +const {Arg, Option, method, RetVal, types} = protocol;
 +const events = require("sdk/event/core");
 +const object = require("sdk/util/object");
++const { Class } = require("sdk/core/heritage");
 +
-+// XXX: Move css-logic into toolkit.
 +loader.lazyGetter(this, "CssLogic", () => require("devtools/styleinspector/css-logic").CssLogic);
 +loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
 +
++// The PageStyle actor flattens the DOM CSS objects a little bit, merging
++// Rules and their Styles into one actor.  For elements (which have a style
++// but no associated rule) we fake a rule with the following style id.
 +const ELEMENT_STYLE = 100;
 +exports.ELEMENT_STYLE = ELEMENT_STYLE;
 +
 +// Predeclare the domnode actor type for use in requests.
 +types.addActorType("domnode");
 +
++/**
++ * DOM Nodes returned by the style actor will be owned by the DOM walker
++ * for the connection.
++  */
++types.addLifetime("walker", "walker");
++
++/**
++ * When asking for the styles applied to a node, we return a list of
++ * appliedstyle json objects that lists the rules that apply to the node
++ * and which element they were inherited from (if any).
++ */
 +types.addDictType("appliedstyle", {
 +  rule: "domstylerule#actorid",
 +  inherited: "nullable:domnode#actorid"
 +});
 +
-+types.addLifetime("walker", "walker");
-+
 +types.addDictType("matchedselector", {
 +  rule: "domstylerule#actorid",
-+  sourceElement: "nullable:walker:domnode",
 +  selector: "string",
 +  value: "string",
 +  status: "number"
 +});
 +
-+var NodeStyleActor = protocol.ActorClass({
-+  typeName: "nodestyle",
-+  initialize: function(walker) {
++/**
++ * The PageStyle actor lets the client look at the styles on a page, as
++ * they are applied to a given node.
++ */
++var PageStyleActor = protocol.ActorClass({
++  typeName: "pagestyle",
++
++  /**
++   * Create a PageStyleActor.
++   *
++   * @param inspector
++   *    The InspectorActor that owns this PageStyleActor.
++   *
++   * @constructor
++   */
++  initialize: function(inspector) {
 +    protocol.Actor.prototype.initialize.call(this, null);
-+    this.walker = walker;
-+    this._refMap = new Map;
-+    this.cssLogic = new CssLogic();
++    this.inspector = inspector;
++    if (!this.inspector.walker) {
++      throw Error("The inspector's WalkerActor must be created before " +
++                   "creating a PageStyleActor.");
++    }
++    this.walker = inspector.walker;
++    this.cssLogic = new CssLogic;
++
++    // Stores the association of DOM objects -> actors
++    this.refMap = new Map;
 +  },
 +
-+  get conn() this.walker.conn,
++  get conn() this.inspector.conn,
 +
-+  // item can be a nsIDOMCSSRule or an Element.
++  /**
++   * Return or create a StyleRuleActor for the given item.
++   * @param item Either a CSSStyleRule or a DOM element.
++   */
 +  _styleRef: function(item) {
-+    if (this._refMap.has(item)) {
-+      return this._refMap.get(item);
++    if (this.refMap.has(item)) {
++      return this.refMap.get(item);
 +    }
 +    let actor = StyleRuleActor(this, item);
 +    this.manage(actor);
-+    this._refMap.set(item, actor);
++    this.refMap.set(item, actor);
 +
 +    return actor;
 +  },
 +
++  /**
++   * Return or create a StyleSheetActor for the given
++   * nsIDOMCSSStyleSheet
++   */
 +  _sheetRef: function(sheet) {
-+    if (this._refMap.has(sheet)) {
-+      return this._refMap.get(sheet);
++    if (this.refMap.has(sheet)) {
++      return this.refMap.get(sheet);
 +    }
 +    let actor = StyleSheetActor(this, sheet);
 +    this.manage(actor);
-+    this._refMap.set(sheet, actor);
++    this.refMap.set(sheet, actor);
 +
 +    return actor;
 +  },
 +
++  /**
++   * Get the computed style for a node.
++   *
++   * @param NodeActor node
++   * @param object options
++   *   `filter`: A string filter that affects the "matched" handling.
++   *     'user': Include properties from user style sheets.
++   *     'ua': Include properties from user and user-agent sheets.
++   *     Default value is 'ua'
++   *   `markMatched`: true if you want the 'matched' property to be added
++   *     when a computed property has been modified by a style included
++   *     by `filter`.
++   *   `onlyMatched`: true if unmatched properties shouldn't be included.
++   *
++   * @returns a JSON blob with the following form:
++   *   {
++   *     "property-name": {
++   *       value: "property-value",
++   *       priority: "!important" <optional>
++   *       matched: <true if there are matched selectors for this value>
++   *     },
++   *     ...
++   *   }
++   */
 +  getComputed: method(function(node, options) {
 +    let win = node.rawNode.ownerDocument.defaultView;
 +    let ret = Object.create(null);
 +
 +    this.cssLogic.sourceFilter = options.filter || CssLogic.FILTER.UA;
 +    this.cssLogic.highlight(node.rawNode);
 +    let computed = this.cssLogic._computedStyle;
 +
@@ -206,121 +1137,142 @@ new file mode 100644
 +      filter: Option(1, "string"),
 +
 +    },
 +    response: {
 +      computed: RetVal("json")
 +    }
 +  }),
 +
-+  expandSets: function(ruleSet, sheetSet) {
-+    // Sets include new items in their iteration.
-+    for (let rule of ruleSet) {
-+      if (rule.rawRule.parentRule) {
-+        let parent = this._styleRef(rule.rawRule.parentRule);
-+        if (!ruleSet.has(parent)) {
-+          ruleSet.add(parent);
-+        }
-+      }
-+      if (rule.rawRule.parentStyleSheet) {
-+        let parent = this._sheetRef(rule.rawRule.parentStyleSheet);
-+        if (!sheetSet.has(parent)) {
-+          sheetSet.add(parent);
-+        }
-+      }
-+    }
-+
-+    for (let sheet of sheetSet) {
-+      if (sheet.rawSheet.parentStyleSheet) {
-+        let parent = this._sheetRef(sheet.rawSheet.parentStyleSheet);
-+        if (!sheetSet.has(parent)) {
-+          sheetSet.add(parent);
-+        }
-+      }
-+    }
-+  },
-+
-+  getMatchedSelectors: method(function(node, property) {
++  /**
++   * Get a list of selectors that match a given property for a node.
++   *
++   * @param NodeActor node
++   * @param string property
++   * @param object options
++   *   `filter`: A string filter that affects the "matched" handling.
++   *     'user': Include properties from user style sheets.
++   *     'ua': Include properties from user and user-agent sheets.
++   *     Default value is 'ua'
++   *
++   * @returns a JSON object with the following form:
++   *   {
++   *     // An ordered list of rules that apply
++   *     matched: [{
++   *       rule: <rule actorid>,
++   *       sourceText: <string>, // The source of the selector, relative
++   *                             // to the node in question.
++   *       selector: <string>, // the selector ID that matched
++   *       value: <string>, // the value of the property
++   *       status: <int>,
++   *         // The status of the match - high numbers are better placed
++   *         // to provide styling information:
++   *         // 3: Best match, was used.
++   *         // 2: Matched, but was overridden.
++   *         // 1: Rule from a parent matched.
++   *         // 0: Unmatched (never returned in this API)
++   *     }, ...],
++   *
++   *     // The full form of any domrule referenced.
++   *     rules: [ <domrule>, ... ], // The full form of any domrule referenced
++   *
++   *     // The full form of any sheets referenced.
++   *     sheets: [ <domsheet>, ... ]
++   *  }
++   */
++  getMatchedSelectors: method(function(node, property, options) {
++    this.cssLogic.sourceFilter = options.filter || CssLogic.FILTER.UA;
 +    this.cssLogic.highlight(node.rawNode);
 +
 +    let walker = node.parent();
 +
 +    let rules = new Set;
 +    let sheets = new Set;
 +
 +    let matched = [];
 +    let propInfo = this.cssLogic.getPropertyInfo(property);
 +    for (let selectorInfo of propInfo.matchedSelectors) {
 +      let cssRule = selectorInfo.selector._cssRule;
 +      let domRule = cssRule.sourceElement || cssRule._domRule;
 +
 +      let rule = this._styleRef(domRule);
 +      rules.add(rule);
 +
-+      let sourceElement = undefined;
-+      if (selectorInfo.sourceElement) {
-+        sourceElement = walker._ref(selectorInfo.sourceElement);
-+      }
 +      matched.push({
 +        rule: rule,
 +        sourceText: this.getSelectorSource(selectorInfo, node.rawNode),
-+        sourceElement: sourceElement,
 +        selector: selectorInfo.selector.text,
 +        value: selectorInfo.value,
 +        status: selectorInfo.status
 +      });
 +    }
 +
 +    this.expandSets(rules, sheets);
 +
 +    return {
 +      matched: matched,
 +      rules: [...rules],
-+      sheets: [...sheets]
++      sheets: [...sheets],
 +    }
 +  }, {
 +    request: {
 +      node: Arg(0, "domnode"),
-+      properties: Arg(1, "string")
++      properties: Arg(1, "string"),
++      filter: Option(2, "string")
 +    },
 +    response: RetVal(types.addDictType("matchedselectorresponse", {
 +      rules: "array:domstylerule",
 +      sheets: "array:domsheet",
 +      matched: "array:matchedselector"
 +    }))
 +  }),
 +
++  // Get a selector source for a CssSelectorInfor relative to a given
++  // node.
++  // XXX: This could probably be done in the frontend?
 +  getSelectorSource: function(selectorInfo, relativeTo) {
 +    let result = selectorInfo.selector.text;
 +    if (selectorInfo.elementStyle) {
 +      let source = selectorInfo.sourceElement;
 +      if (source === relativeTo) {
 +        result = "this";
 +      } else {
 +        result = CssLogic.getShortName(source);
 +      }
 +      result += ".style"
 +    }
 +    return result;
 +  },
 +
++  /**
++   * Get the set of styles that apply to a given node.
++   * @param NodeActor node
++   * @param string property
++   * @param object options
++   *   `filter`: A string filter that affects the "matched" handling.
++   *     'user': Include properties from user style sheets.
++   *     'ua': Include properties from user and user-agent sheets.
++   *     Default value is 'ua'
++   *   `inherited`: Include styles inherited from parent nodes.
++   *   `matchedSeletors`: Include an array of specific selectors that
++   *     caused this rule to match its node.
++   */
 +  getApplied: method(function(node, options) {
 +    let entries = [];
 +
 +    this.addElementRules(node.rawNode, undefined, options, entries);
 +
 +    if (options.inherited) {
 +      let parent = this.walker.parentNode(node);
 +      while (parent && parent.rawNode.nodeType != Ci.nsIDOMNode.DOCUMENT_NODE) {
 +        this.addElementRules(parent.rawNode, parent, options, entries);
 +        parent = this.walker.parentNode(parent);
 +      }
 +    }
 +
-+
 +    if (options.matchedSelectors) {
 +      for (let entry of entries) {
 +        if (entry.rule.type === ELEMENT_STYLE) {
 +          continue;
 +        }
 +
 +        let domRule = entry.rule.rawRule;
 +        let selectors = CssLogic.getSelectors(domRule);
@@ -344,55 +1296,116 @@ new file mode 100644
 +      entries: entries,
 +      rules: [...rules],
 +      sheets: [...sheets]
 +    }
 +  }, {
 +    request: {
 +      node: Arg(0, "domnode"),
 +      inherited: Option(1, "boolean"),
-+      matchedSelectors: Option(1, "boolean")
++      matchedSelectors: Option(1, "boolean"),
++      filter: Option(1, "string")
 +    },
 +    response: RetVal(types.addDictType("appliedStylesReturn", {
 +      entries: "array:appliedstyle",
 +      rules: "array:domstylerule",
 +      sheets: "array:domsheet"
 +    }))
 +  }),
 +
++  _hasInheritedProps: function(style) {
++    return Array.prototype.some.call(style, prop => {
++      return DOMUtils.isInheritedProperty(prop);
++    });
++  },
++
++  /**
++   * Helper function for getApplied, adds all the rules from a given
++   * element.
++   */
 +  addElementRules: function(element, inherited, options, rules)
 +  {
 +    let elementStyle = this._styleRef(element);
-+    rules.push({
-+      rule: elementStyle,
-+      inherited: inherited,
-+    });
++
++    if (!inherited || this._hasInheritedProps(element.style)) {
++      rules.push({
++        rule: elementStyle,
++        inherited: inherited,
++      });
++    }
 +
 +    // Get the styles that apply to the element.
 +    let domRules = DOMUtils.getCSSStyleRules(element);
 +
 +    // getCSSStyleRules returns ordered from least-specific to
 +    // most-specific.
 +    for (let i = domRules.Count() - 1; i >= 0; i--) {
 +      let domRule = domRules.GetElementAt(i);
 +
 +      let isSystem = !CssLogic.isContentStylesheet(domRule.parentStyleSheet);
 +
++      if (isSystem && options.filter != CssLogic.FILTER.UA) {
++        continue;
++      }
++
++      if (inherited) {
++        // Don't include inherited rules if none of its properties
++        // are inheritable.
++        let hasInherited = Array.prototype.some.call(domRule.style, prop => {
++          return DOMUtils.isInheritedProperty(prop);
++        });
++        if (!hasInherited) {
++          continue;
++        }
++      }
++
 +      let ruleActor = this._styleRef(domRule);
 +      rules.push({
 +        rule: ruleActor,
 +        inherited: inherited,
-+        system: isSystem || undefined
 +      });
 +    }
 +  },
++
++  /**
++   * Expand Sets of rules and sheets to include all parent rules and sheets.
++   */
++  expandSets: function(ruleSet, sheetSet) {
++    // Sets include new items in their iteration
++    for (let rule of ruleSet) {
++      if (rule.rawRule.parentRule) {
++        let parent = this._styleRef(rule.rawRule.parentRule);
++        if (!ruleSet.has(parent)) {
++          ruleSet.add(parent);
++        }
++      }
++      if (rule.rawRule.parentStyleSheet) {
++        let parent = this._sheetRef(rule.rawRule.parentStyleSheet);
++        if (!sheetSet.has(parent)) {
++          sheetSet.add(parent);
++        }
++      }
++    }
++
++    for (let sheet of sheetSet) {
++      if (sheet.rawSheet.parentStyleSheet) {
++        let parent = this._sheetRef(sheet.rawSheet.parentStyleSheet);
++        if (!sheetSet.has(parent)) {
++          sheetSet.add(parent);
++        }
++      }
++    }
++  }
 +});
-+exports.NodeStyleActor = NodeStyleActor;
++exports.PageStyleActor = PageStyleActor;
 +
-+var NodeStyleFront = protocol.FrontClass(NodeStyleActor, {
++/**
++ * Front object for the PageStyleActor
++ */
++var PageStyleFront = protocol.FrontClass(PageStyleActor, {
 +  initialize: function(conn, form, ctx, detail) {
 +    protocol.Front.prototype.initialize.call(this, conn, form, ctx, detail);
 +    this.inspector = this.parent();
 +  },
 +
 +  destroy: function() {
 +    protocol.Front.prototype.destroy.call(this);
 +  },
@@ -413,119 +1426,147 @@ new file mode 100644
 +    return this._getApplied(node, options).then(ret => {
 +      return ret.entries;
 +    });
 +  }, {
 +    impl: "_getApplied"
 +  })
 +});
 +
++/**
++ * Actor representing an nsIDOMCSSStyleSheet.
++ */
 +var StyleSheetActor = protocol.ActorClass({
 +  typeName: "domsheet",
 +
-+  initialize: function(nodeStyle, sheet) {
++  initialize: function(pageStyle, sheet) {
 +    protocol.Front.prototype.initialize.call(this);
-+    this.nodeStyle = nodeStyle;
++    this.pageStyle = pageStyle;
 +    this.rawSheet = sheet;
 +  },
 +
-+  get conn() this.nodeStyle.conn,
++  get conn() this.pageStyle.conn,
 +
 +  form: function(detail) {
 +    if (detail === "actorid") {
 +      return this.actorID;
 +    }
++
 +    return {
 +      actor: this.actorID,
++
++      // href stores the uri of the sheet
 +      href: this.rawSheet.href,
++
++      // nodeHref stores the URI of the document that
++      // included the sheet.
 +      nodeHref: this.rawSheet.ownerNode ? this.rawSheet.ownerNode.ownerDocument.location.href : undefined,
++
 +      system: !CssLogic.isContentStylesheet(this.rawSheet),
 +      disabled: this.rawSheet.disabled ? true : undefined
 +    }
 +  }
 +});
 +
++/**
++ * Front for the StyleSheetActor.
++ */
 +var StyleSheetFront = protocol.FrontClass(StyleSheetActor, {
 +  initialize: function(conn, form, ctx, detail) {
 +    protocol.Front.prototype.initialize.call(this, conn, form, ctx, detail);
 +  },
 +
 +  form: function(form, detail) {
 +    if (detail === "actorid") {
 +      this.actorID = form;
 +      return;
 +    }
 +    this.actorID = form.actorID;
 +    this._form = form;
 +  },
 +
 +  get href() this._form.href,
 +  get nodeHref() this._form.nodeHref,
-+
 +  get disabled() !!this._form.disabled,
-+
 +  get isSystem() this._form.system
-+})
++});
++
++
++// Predeclare the domstylerule actor type
++types.addActorType("domstylerule");
 +
-+types.addActorType("domstylerule");
++/**
++ * An actor that represents a CSS style object on the protocol.
++ *
++ * We slightly flatten the CSSOM for this actor, it represents
++ * both the CSSRule and CSSStyle objects in one actor.  For nodes
++ * (which have a CSSStyle but no CSSRule) we create a StyleRuleActor
++ * with a special rule type (100).
++ */
 +var StyleRuleActor = protocol.ActorClass({
 +  typeName: "domstylerule",
-+  initialize: function(nodeStyle, item) {
++  initialize: function(pageStyle, item) {
 +    protocol.Actor.prototype.initialize.call(this, null);
-+    this.nodeStyle = nodeStyle;
++    this.pageStyle = pageStyle;
++    this.rawStyle = item.style;
++
 +    if (item instanceof (Ci.nsIDOMCSSRule)) {
 +      this.type = item.type;
 +      this.rawRule = item;
 +      if (this.rawRule instanceof Ci.nsIDOMCSSStyleRule && this.rawRule.parentStyleSheet) {
 +        this.line = DOMUtils.getRuleLine(this.rawRule);
 +      }
-+
 +    } else {
++      // Fake a rule
 +      this.type = ELEMENT_STYLE;
 +      this.rawNode = item;
 +      this.rawRule = {
 +        style: item.style,
 +        toString: function() "[element rule " + this.style + "]"
 +      }
 +    }
 +  },
 +
-+  get conn() this.nodeStyle.conn,
-+  get marshallPool() this.nodeStyle,
++  get conn() this.pageStyle.conn,
++
++  // Objects returned by this actor are owned by the PageStyleActor
++  // to which this rule belongs.
++  get marshallPool() this.pageStyle,
 +
 +  toString: function() "[StyleRuleActor for " + this.rawRule + "]",
 +
 +  form: function(detail) {
 +    if (detail === "actorid") {
 +      return this.actorID;
 +    }
++
 +    let form = {
 +      actor: this.actorID,
 +      type: this.type,
 +      line: this.line || undefined,
-+      selectors: this.selectors,
 +    };
 +
 +    if (this.rawRule.parentRule) {
-+      form.parentRule = this.nodeStyle._styleRef(this.rawRule.parentRule).actorID;
++      form.parentRule = this.pageStyle._styleRef(this.rawRule.parentRule).actorID;
 +    }
 +    if (this.rawRule.parentStyleSheet) {
-+      form.parentStyleSheet = this.nodeStyle._sheetRef(this.rawRule.parentStyleSheet).actorID;
-+    }
-+
-+    if (this.type === ELEMENT_STYLE) {
-+      form.href = this.rawNode.ownerDocument.location.href;
++      form.parentStyleSheet = this.pageStyle._sheetRef(this.rawRule.parentStyleSheet).actorID;
 +    }
 +
 +    switch (this.type) {
 +      case Ci.nsIDOMCSSRule.STYLE_RULE:
 +        form.selectors = CssLogic.getSelectors(this.rawRule);
-+        /* fall through */
++        form.cssText = this.rawStyle.cssText || "";
++        break;
 +      case ELEMENT_STYLE:
-+        form.cssText = this.rawRule.style.cssText || "";
++        // Elements don't have a parent stylesheet, and therefore
++        // don't have an associated URI.  Provide a URI for
++        // those.
++        form.href = this.rawNode.ownerDocument.location.href;
++        form.cssText = this.rawStyle.cssText || "";
 +        break;
 +      case Ci.nsIDOMCSSRule.CHARSET_RULE:
 +        form.encoding = this.rawRule.encoding;
 +        break;
 +      case Ci.nsIDOMCSSRule.IMPORT_RULE:
 +        form.href = this.rawRule.href;
 +        break;
 +      case Ci.nsIDOMCSSRule.MEDIA_RULE:
@@ -534,31 +1575,50 @@ new file mode 100644
 +          form.media.push(this.rawRule.media.item(i));
 +        }
 +        break;
 +    }
 +
 +    return form;
 +  },
 +
++  /**
++   * Modify a rule's properties.  Passed an array of modifications:
++   * {
++   *   type: "set",
++   *   name: <string>,
++   *   value: <string>,
++   *   priority: <optional string>
++   * }
++   *  or
++   * {
++   *   type: "remove",
++   *   name: <string>,
++   * }
++   *
++   * @returns the rule with updated properties
++   */
 +  modifyProperties: method(function(modifications) {
 +    for (let mod of modifications) {
 +      if (mod.type === "set") {
-+        this.rawRule.style.setProperty(mod.name, mod.value, mod.priority);
++        this.rawStyle.setProperty(mod.name, mod.value, mod.priority);
 +      } else if (mod.type === "remove") {
-+        this.rawRule.style.removeProperty(mod.name);
++        this.rawStyle.removeProperty(mod.name);
 +      }
 +    }
 +    return this;
 +  }, {
 +    request: { modifications: Arg(0, "array:json") },
 +    response: { rule: RetVal("domstylerule") }
 +  })
 +});
 +
++/**
++ * Front for the StyleRule actor.
++ */
 +var StyleRuleFront = protocol.FrontClass(StyleRuleActor, {
 +  initialize: function(client, form, ctx, detail) {
 +    protocol.Front.prototype.initialize.call(this, client, form, ctx, detail);
 +  },
 +
 +  destroy: function() {
 +    protocol.Front.prototype.destroy.call(this);
 +  },
@@ -570,16 +1630,19 @@ new file mode 100644
 +    }
 +    this.actorID = form.actor;
 +    this._form = form;
 +    if (this._mediaText) {
 +      this._mediaText = null;
 +    }
 +  },
 +
++  /**
++   * Return a new RuleModificationList for this node.
++   */
 +  startModifyingProperties: function() {
 +    return new RuleModificationList(this);
 +  },
 +
 +  get type() this._form.type,
 +  get line() this._form.line || -1,
 +  get cssText() {
 +    return this._form.cssText;
@@ -595,21 +1658,27 @@ new file mode 100644
 +      return null;
 +    }
 +    if (this._mediaText) {
 +      return this._mediaText;
 +    }
 +    this._mediaText = this.media.join(", ");
 +    return this._mediaText;
 +  },
++
 +  get parentRule() {
-+    return this.parent().get(this._form.parentRule);
++    return this.conn.getActor(this._form.parentRule);
 +  },
++
 +  get parentStyleSheet() {
-+    return this.parent().get(this._form.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.href || sheet.nodeHref;
@@ -620,25 +1689,30 @@ new file mode 100644
 +    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) {
 +      return null;
 +    }
-+    return actor.rawRule.style;
++    return actor.rawStyle;
 +  }
 +});
 +
-+function RuleModificationList(rule) {
-+  this.rule = rule;
-+  this.modifications = [];
-+}
-+RuleModificationList.prototype = {
++/**
++ * Convenience API for building a list of attribute modifications
++ * for the `modifyAttributes` request.
++ */
++var RuleModificationList = Class({
++  initialize: function(rule) {
++    this.rule = rule;
++    this.modifications = [];
++  },
++
 +  apply: function() {
 +    return this.rule.modifyProperties(this.modifications);
 +  },
 +  setProperty: function(name, value, priority) {
 +    this.modifications.push({
 +      type: "set",
 +      name: name,
 +      value: value,
@@ -646,22 +1720,270 @@ new file mode 100644
 +    });
 +  },
 +  removeProperty: function(name) {
 +    this.modifications.push({
 +      type: "remove",
 +      name: name
 +    });
 +  }
-+};
++});
++
+diff --git a/toolkit/devtools/server/tests/mochitest/Makefile.in b/toolkit/devtools/server/tests/mochitest/Makefile.in
+--- a/toolkit/devtools/server/tests/mochitest/Makefile.in
++++ b/toolkit/devtools/server/tests/mochitest/Makefile.in
+@@ -9,27 +9,29 @@ srcdir		= @srcdir@
+ VPATH		= @srcdir@
+ relativesrcdir	= @relativesrcdir@
+ 
+ include $(DEPTH)/config/autoconf.mk
+ 
+ MOCHITEST_CHROME_FILES	= \
+ 	inspector-helpers.js \
+ 	inspector-traversal-data.html \
++	inspector-styles-data.html \
+ 	test_inspector-changeattrs.html \
+ 	test_inspector-changevalue.html \
+ 	test_inspector-hide.html \
+ 	test_inspector-insert.html \
+ 	test_inspector-mutations-attr.html \
+ 	test_inspector-mutations-childlist.html \
+ 	test_inspector-mutations-frameload.html \
+ 	test_inspector-mutations-value.html \
+ 	test_inspector-release.html \
+ 	test_inspector-remove.html \
+ 	test_inspector-reload.html \
+ 	test_inspector-retain.html \
+ 	test_inspector-pseudoclass-lock.html \
+ 	test_inspector-traversal.html \
++	test_styles-applied.html \
+ 	test_unsafeDereference.html \
+ 	nonchrome_unsafeDereference.html \
+ 	$(NULL)
+ 
+ include $(topsrcdir)/config/rules.mk
+diff --git a/toolkit/devtools/server/tests/mochitest/inspector-styles-data.html b/toolkit/devtools/server/tests/mochitest/inspector-styles-data.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/server/tests/mochitest/inspector-styles-data.html
+@@ -0,0 +1,30 @@
++<html>
++<script>
++  window.onload = () => {
++    window.opener.postMessage('ready', '*')
++  }
++</script>
++<style>
++  .inheritable-rule {
++    font-size: 15px;
++  }
++  .uninheritable-rule {
++    background-color: #f06;
++  }
++</style>
++<body>
++  <h1>Style Actor Tests</h1>
++  <!-- Inheritance checks -->
++  <div id="inheritable-rule-uninheritable-style" class="inheritable-rule" style="background-color: purple">
++    <div id="inheritable-rule-inheritable-style" class="inheritable-rule" style="color: blue">
++      <div id="uninheritable-rule-uninheritable-style" class="uninheritable-rule" style="background-color: green">
++        <div id="uninheritable-rule-inheritable-style" class="uninheritable-rule" style="color: red">
++          <div id="test-node">
++            Here is the test node.
++          </div>
++        </div>
++      </div>
++    </div>
++  </div>
++</body>
++</html>
+diff --git a/toolkit/devtools/server/tests/mochitest/inspector-traversal-data.html b/toolkit/devtools/server/tests/mochitest/inspector-traversal-data.html
+--- a/toolkit/devtools/server/tests/mochitest/inspector-traversal-data.html
++++ b/toolkit/devtools/server/tests/mochitest/inspector-traversal-data.html
+@@ -9,17 +9,17 @@
+       iframe.setAttribute("id", "childFrame");
+       iframe.onload = function() {
+         window.opener.postMessage('ready', '*')
+       };
+       iframe.src = data;
+       body.appendChild(iframe);
+     }
+   </script>
+-<body>
++<body style="background-color:white">
+   <h1>Inspector Actor Tests</h1>
+   <span id="longstring">longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong</span>
+   <span id="shortstring">short</span>
+   <span id="empty"></span>
+   <div id="longlist" data-test="exists">
+     <div id="a">a</div>
+     <div id="b">b</div>
+     <div id="c">c</div>
+@@ -46,9 +46,9 @@
+     <div id="x">x</div>
+     <div id="y">y</div>
+     <div id="z">z</div>
+   </div>
+   <div id="longlist-sibling">
+     <div id="longlist-sibling-firstchild"></div>
+   </div>
+ </body>
+-</html>
+\ No newline at end of file
++</html>
+diff --git a/toolkit/devtools/server/tests/mochitest/test_styles-applied.html b/toolkit/devtools/server/tests/mochitest/test_styles-applied.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/devtools/server/tests/mochitest/test_styles-applied.html
+@@ -0,0 +1,141 @@
++<!DOCTYPE HTML>
++<html>
++<!--
++https://bugzilla.mozilla.org/show_bug.cgi?id=
++-->
++<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">
++Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
 +
++const promise = devtools.require("sdk/core/promise");
++const inspector = devtools.require("devtools/server/actors/inspector");
++
++window.onload = function() {
++  SimpleTest.waitForExplicitFinish();
++  runNextTest();
++}
++
++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} = devtools.require("devtools/server/actors/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;
++    }).then(runNextTest));
++  });
++});
++
++addTest(function inheritedUserStyles() {
++  let node = node;
++  promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => {
++    return gStyles.getApplied(node, { inherited: true, filter: "user" });
++  }).then(applied => {
++    ok(!applied[0].inherited, "Entry 0 should be uninherited");
++    is(applied[0].rule.type, 100, "Entry 0 should be an element style");
++    is(applied[0].rule.cssText, "", "Entry 0 should be an empty style");
++
++    is(applied[1].inherited.id, "uninheritable-rule-inheritable-style",
++       "Entry 1 should be inherited from the parent");
++    is(applied[1].rule.type, 100, "Entry 1 should be an element style");
++    is(applied[1].rule.cssText, "color: red;", "Entry 1 should have the expected cssText");
++
++    is(applied[2].inherited.id, "inheritable-rule-inheritable-style",
++       "Entry 2 should be inherited from the parent's parent");
++    is(applied[2].rule.type, 100, "Entry 2 should be an element style");
++    is(applied[2].rule.cssText, "color: blue;", "Entry 2 should have the expected cssText");
++
++    is(applied[3].inherited.id, "inheritable-rule-inheritable-style",
++       "Entry 3 should be inherited from the parent's parent");
++    is(applied[3].rule.type, 1, "Entry 3 should be a rule style");
++    is(applied[3].rule.cssText, "font-size: 15px;", "Entry 3 should have the expected cssText");
++    ok(!applied[3].matchedSelectors, "Shouldn't get matchedSelectors with this request.");
++
++    is(applied[4].inherited.id, "inheritable-rule-uninheritable-style",
++       "Entry 4 should be inherited from the parent's parent");
++    is(applied[4].rule.type, 1, "Entry 4 should be an rule style");
++    is(applied[4].rule.cssText, "font-size: 15px;", "Entry 4 should have the expected cssText");
++    ok(!applied[4].matchedSelectors, "Shouldn't get matchedSelectors with this request.");
++
++    is(applied.length, 5, "Should have 5 rules.");
++  }).then(runNextTest));
++});
++
++addTest(function inheritedSystemStyles() {
++  let node = node;
++  promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => {
++    return gStyles.getApplied(node, { inherited: true, filter: "ua" });
++  }).then(applied => {
++    // If our system stylesheets are prone to churn, this might be a fragile
++    // test.  If you're here because of that I apologize, file a bug
++    // and we can find a different way to test.
++
++    ok(!applied[1].inherited, "Entry 1 should not be inherited");
++    ok(!applied[1].rule.parentStyleSheet.system, "Entry 1 should be a system style");
++    is(applied[1].rule.type, 1, "Entry 1 should be a rule style");
++
++    is(applied.length, 7, "Should have 7 rules.");
++  }).then(runNextTest));
++});
++
++addTest(function noInheritedStyles() {
++  let node = node;
++  promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => {
++    return gStyles.getApplied(node, { inherited: false, filter: "user" });
++  }).then(applied => {
++    ok(!applied[0].inherited, "Entry 0 should be uninherited");
++    is(applied[0].rule.type, 100, "Entry 0 should be an element style");
++    is(applied[0].rule.cssText, "", "Entry 0 should be an empty style");
++    is(applied.length, 1, "Should have 1 rule.");
++  }).then(runNextTest));
++});
++
++
++addTest(function matchedSelectors() {
++  let node = node;
++  promiseDone(gWalker.querySelector(gWalker.rootNode, "#test-node").then(node => {
++    return gStyles.getApplied(node, {
++      inherited: true, filter: "user", matchedSelectors: true
++    });
++  }).then(applied => {
++    is(applied[3].matchedSelectors[0], ".inheritable-rule", "Entry 3 should have a matched selector");
++    is(applied[4].matchedSelectors[0], ".inheritable-rule", "Entry 4 should have a matched selector");
++  }).then(runNextTest));
++});
++
++addTest(function cleanup() {
++  delete gStyles;
++  delete gWalker;
++  delete gClient;
++  runNextTest();
++});
++
++  </script>
++</head>
++<body>
++<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
++<a id="inspectorContent" target="_blank" href="inspector-styles-data.html">Test Document</a>
++<p id="display"></p>
++<div id="content" style="display: none">
++
++</div>
++<pre id="test">
++</pre>
++</body>
++</html>
 diff --git a/toolkit/devtools/server/transport.js b/toolkit/devtools/server/transport.js
 --- a/toolkit/devtools/server/transport.js
 +++ b/toolkit/devtools/server/transport.js
-@@ -227,17 +227,21 @@ LocalDebuggerTransport.prototype = {
+@@ -231,17 +231,21 @@ LocalDebuggerTransport.prototype = {
        }
      }
      this._deepFreeze(aPacket);
      let other = this.other;
      if (other) {
        Services.tm.currentThread.dispatch(makeInfallible(function() {
          // Avoid the cost of JSON.stringify() when logging is disabled.
          if (wantLogging) {
@@ -674,8 +1996,89 @@ diff --git a/toolkit/devtools/server/tra
          }
          if (other.hooks) {
            other.hooks.onPacket(aPacket);
          }
        }, "LocalDebuggerTransport instance's this.other.hooks.onPacket"), 0);
      }
    },
  
+diff --git a/toolkit/devtools/styleinspector/css-logic.js b/toolkit/devtools/styleinspector/css-logic.js
+--- a/toolkit/devtools/styleinspector/css-logic.js
++++ b/toolkit/devtools/styleinspector/css-logic.js
+@@ -58,18 +58,19 @@ function CssLogic()
+ }
+ 
+ exports.CssLogic = CssLogic;
+ 
+ /**
+  * Special values for filter, in addition to an href these values can be used
+  */
+ CssLogic.FILTER = {
+-  ALL: "all", // show properties from all user style sheets.
+-  UA: "ua",   // ALL, plus user-agent (i.e. browser) style sheets
++  ALL: "user", // show properties from all user style sheets
++  USER: "user",
++  UA: "ua",    // ALL, plus user-agent (i.e. browser) style sheets
+ };
+ 
+ /**
+  * Known media values. To distinguish "all" stylesheets (above) from "all" media
+  * The full list includes braille, embossed, handheld, print, projection,
+  * speech, tty, and tv, but this is only a hack because these are not defined
+  * in the DOM at all.
+  * @see http://www.w3.org/TR/CSS21/media.html#media-types
+@@ -138,16 +139,18 @@ CssLogic.prototype = {
+     this._matchedSelectors = null;
+   },
+ 
+   /**
+    * Focus on a new element - remove the style caches.
+    *
+    * @param {nsIDOMElement} aViewedElement the element the user has highlighted
+    * in the Inspector.
++   *
++   * @returns a promise that will be resolved when CssLogic is up to date.
+    */
+   highlight: function CssLogic_highlight(aViewedElement)
+   {
+     if (!aViewedElement) {
+       this.viewedElement = null;
+       this.viewedDocument = null;
+       this._computedStyle = null;
+       this.reset();
+@@ -750,17 +753,17 @@ CssLogic.isContentStylesheet = function 
+  *
+  * @param {CSSStyleSheet} aSheet the DOM object for the style sheet.
+  * @return {string} the address of the stylesheet.
+  */
+ CssLogic.href = function CssLogic_href(aSheet)
+ {
+   let href = aSheet.href;
+   if (!href) {
+-    href = aSheet.ownerNode.ownerDocument.location;
++    href = aSheet.ownerNode.ownerDocument.location.href;
+   }
+ 
+   return href;
+ };
+ 
+ /**
+  * Return a shortened version of a style sheet's source.
+  *
+@@ -1128,16 +1131,17 @@ CssSheet.prototype = {
+  * to cache data. If the rule comes from element.style, then provide
+  * an object of the form: {style: element.style}.
+  * @param {Element} [aElement] If the rule comes from element.style, then this
+  * argument must point to the element.
+  * @constructor
+  */
+ function CssRule(aCssSheet, aDomRule, aElement)
+ {
++  dump("Creating rule: " + aDomRule + "\n");
+   this._cssSheet = aCssSheet;
+   this._domRule = aDomRule;
+ 
+   let parentRule = aDomRule.parentRule;
+   if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
+     this.mediaText = parentRule.media.mediaText;
+   }
+ 
deleted file mode 100644
--- a/stylesheet-parent.diff
+++ /dev/null
@@ -1,34 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1374541449 25200
-#      Mon Jul 22 18:04:09 2013 -0700
-# Node ID b6a1d79eedf8d8310150b4cc1d72e0d0b41e4ff3
-# Parent  4e77488128c5517385dd6eed591e3ce426ea87df
-imported patch stylesheet-parent.diff
-
-diff --git a/toolkit/devtools/server/actors/styleeditor.js b/toolkit/devtools/server/actors/styleeditor.js
---- a/toolkit/devtools/server/actors/styleeditor.js
-+++ b/toolkit/devtools/server/actors/styleeditor.js
-@@ -399,22 +399,16 @@ StyleSheetActor.prototype = {
-       actor: this.actorID,  // actorID is set when this actor is added to a pool
-       href: this.styleSheet.href,
-       disabled: this.styleSheet.disabled,
-       title: this.styleSheet.title,
-       styleSheetIndex: this.styleSheetIndex,
-       text: this.text
-     }
- 
--    // get parent actor if this sheet was @imported
--    let parent = this.styleSheet.parentStyleSheet;
--    if (parent) {
--      form.parentActor = this.parentActor._sheets.get(parent);
--    }
--
-     try {
-       form.ruleCount = this.styleSheet.cssRules.length;
-     }
-     catch(e) {
-       // stylesheet had an @import rule that wasn't loaded yet
-     }
- 
-     return form;
deleted file mode 100644
--- a/walker-reload.diff
+++ /dev/null
@@ -1,411 +0,0 @@
-# HG changeset patch
-# User Dave Camp <dcamp@mozilla.com>
-# Date 1374258100 25200
-#      Fri Jul 19 11:21:40 2013 -0700
-# Node ID a852dec819d5f8e6816e51f8118c72739145b102
-# Parent  73f2e30e015428ac7f3ba0b9c61c8024509b40af
-imported patch walker-reload.diff
-
-diff --git a/browser/devtools/inspector/inspector-panel.js b/browser/devtools/inspector/inspector-panel.js
---- a/browser/devtools/inspector/inspector-panel.js
-+++ b/browser/devtools/inspector/inspector-panel.js
-@@ -151,24 +151,27 @@ InspectorPanel.prototype = {
-     this.setupSidebar();
- 
-     return deferred.promise;
-   },
- 
-   /**
-    * Return a promise that will resolve to the default node for selection.
-    */
--  _getDefaultNodeForSelection : function() {
-+  _getDefaultNodeForSelection: function() {
-     if (this._defaultNode) {
-       return this._defaultNode;
-     }
-     let walker = this.walker;
-+
-     // if available set body node as default selected node
-     // else set documentElement
--    return walker.querySelector(this.walker.rootNode, "body").then(front => {
-+    return walker.getRootNode().then(rootNode => {
-+      return walker.querySelector(rootNode, "body");
-+    }).then(front => {
-       if (front) {
-         return front;
-       }
-       return this.walker.documentElement(this.walker.rootNode);
-     }).then(node => {
-       if (walker !== this.walker) {
-         promise.reject(null);
-       }
-@@ -280,43 +283,31 @@ InspectorPanel.prototype = {
-     this.sidebar.show();
-   },
- 
-   /**
-    * Reset the inspector on navigate away.
-    */
-   onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
-     let newWindow = payload._navPayload || payload;
--    this.walker.release().then(null, console.error);
--    this.walker = null;
-     this._defaultNode = null;
-     this.selection.setNodeFront(null);
--    this.selection.setWalker(null);
-     this._destroyMarkup();
-     this.isDirty = false;
- 
--    this.target.inspector.getWalker().then(walker => {
-+    this._getDefaultNodeForSelection().then(defaultNode => {
-       if (this._destroyPromise) {
--        walker.release().then(null, console.error);
-         return;
-       }
-+      this.selection.setNodeFront(defaultNode, "navigateaway");
- 
--      this.walker = walker;
--      this.selection.setWalker(walker);
--      this._getDefaultNodeForSelection().then(defaultNode => {
--        if (this._destroyPromise) {
--          return;
--        }
--        this.selection.setNodeFront(defaultNode, "navigateaway");
--
--        this._initMarkup();
--        this.once("markuploaded", () => {
--          this.markup.expandNode(this.selection.nodeFront);
--          this.setupSearchBox();
--        });
-+      this._initMarkup();
-+      this.once("markuploaded", () => {
-+        this.markup.expandNode(this.selection.nodeFront);
-+        this.setupSearchBox();
-       });
-     });
-   },
- 
-   /**
-    * When a new node is selected.
-    */
-   onNewSelection: function InspectorPanel_onNewSelection() {
-diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
---- a/toolkit/devtools/server/actors/inspector.js
-+++ b/toolkit/devtools/server/actors/inspector.js
-@@ -1576,16 +1576,24 @@ var WalkerActor = protocol.ActorClass({
-         mutation.added = addedActors;
-       }
-       this.queueMutation(mutation);
-     }
-   },
- 
-   onFrameLoad: function(window) {
-     let frame = window.frameElement;
-+    if (!frame && !this.rootDoc) {
-+      this.rootDoc = window.document;
-+      this.rootNode = this.document();
-+      this.queueMutation({
-+        type: "newRoot",
-+        target: this.rootNode.form()
-+      });
-+    }
-     let frameActor = this._refMap.get(frame);
-     if (!frameActor) {
-       return;
-     }
- 
-     this.queueMutation({
-       type: "frameLoad",
-       target: frameActor.actorID,
-@@ -1636,16 +1644,21 @@ var WalkerActor = protocol.ActorClass({
-     }
- 
-     let doc = window.document;
-     let documentActor = this._refMap.get(doc);
-     if (!documentActor) {
-       return;
-     }
- 
-+    if (this.rootDoc === doc) {
-+      this.rootDoc = null;
-+      this.rootNode = null;
-+    }
-+
-     this.queueMutation({
-       type: "documentUnload",
-       target: documentActor.actorID
-     });
- 
-     let walker = documentWalker(doc);
-     let parentNode = walker.parentNode();
-     if (parentNode) {
-@@ -1668,29 +1681,41 @@ var WalkerActor = protocol.ActorClass({
- /**
-  * Client side of the DOM walker.
-  */
- var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
-   // Set to true if cleanup should be requested after every mutation list.
-   autoCleanup: true,
- 
-   initialize: function(client, form) {
-+    this._rootNodeDeferred = promise.defer();
-     protocol.Front.prototype.initialize.call(this, client, form);
-     this._orphaned = new Set();
-     this._retainedOrphans = new Set();
-   },
- 
-   destroy: function() {
-     protocol.Front.prototype.destroy.call(this);
-   },
- 
-   // Update the object given a form representation off the wire.
-   form: function(json) {
--    this.actorID = json.actorID;
-+    this.actorID = json.actor;
-     this.rootNode = types.getType("domnode").read(json.root, this);
-+    this._rootNodeDeferred.resolve(this.rootNode);
-+  },
-+
-+  /**
-+   * 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;
-   },
- 
-   /**
-    * 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
-@@ -1788,18 +1813,29 @@ var WalkerFront = exports.WalkerFront = 
-   /**
-    * Get any unprocessed mutation records and process them.
-    */
-   getMutations: protocol.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 = change.target;
--        let targetFront = this.get(targetID);
-+        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") {
-@@ -1843,16 +1879,21 @@ var WalkerFront = exports.WalkerFront = 
-           // 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.rootNode = null;
-+            this._rootNodeDeferred = promise.defer();
-+          }
-+
-           // 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") {
-@@ -1986,33 +2027,38 @@ var InspectorActor = protocol.ActorClass
-       return tabActor.browser;
-     } else if (tabActor.browser instanceof Ci.nsIDOMElement) {
-       return tabActor.browser.contentWindow;
-     }
-     return null;
-   },
- 
-   getWalker: method(function(options={}) {
-+    if (this._walkerPromise) {
-+      return this._walkerPromise;
-+    }
-+
-     let deferred = promise.defer();
-+    this._walkerPromise = deferred.promise;
- 
-     let window = this.window;
- 
-     var domReady = () => {
-       let tabActor = this.tabActor;
-       window.removeEventListener("DOMContentLoaded", domReady, true);
-       deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options));
-     };
- 
-     if (window.document.readyState === "loading") {
-       window.addEventListener("DOMContentLoaded", domReady, true);
-     } else {
-       domReady();
-     }
- 
--    return deferred.promise;
-+    return this._walkerPromise;
-   }, {
-     request: {},
-     response: {
-       walker: RetVal("domwalker")
-     }
-   })
- });
- 
-diff --git a/toolkit/devtools/server/tests/mochitest/Makefile.in b/toolkit/devtools/server/tests/mochitest/Makefile.in
---- a/toolkit/devtools/server/tests/mochitest/Makefile.in
-+++ b/toolkit/devtools/server/tests/mochitest/Makefile.in
-@@ -18,16 +18,17 @@ MOCHITEST_CHROME_FILES	= \
- 	test_inspector-changevalue.html \
- 	test_inspector-insert.html \
- 	test_inspector-mutations-attr.html \
- 	test_inspector-mutations-childlist.html \
- 	test_inspector-mutations-frameload.html \
- 	test_inspector-mutations-value.html \
- 	test_inspector-release.html \
- 	test_inspector-remove.html \
-+	test_inspector-reload.html \
- 	test_inspector-retain.html \
- 	test_inspector-pseudoclass-lock.html \
- 	test_inspector-traversal.html \
- 	test_unsafeDereference.html \
- 	nonchrome_unsafeDereference.html \
- 	$(NULL)
- 
- include $(topsrcdir)/config/rules.mk
-diff --git a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
---- a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
-+++ b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
-@@ -230,16 +230,20 @@ function isFrameLoad(change) {
- function isUnretained(change) {
-   return change.type === "unretained";
- }
- 
- function isChildList(change) {
-   return change.type === "childList";
- }
- 
-+function isNewRoot(change) {
-+  return change.type === "newRoot";
-+}
-+
- // Make sure an iframe's src attribute changed and then
- // strip that mutation out of the list.
- function assertSrcChange(mutations) {
-   return assertAndStrip(mutations, "Should have had an iframe source change.", isSrcChange);
- }
- 
- // Make sure there's an unload in the mutation list and strip
- // that mutation out of the list
-diff --git a/toolkit/devtools/server/tests/mochitest/test_inspector-reload.html b/toolkit/devtools/server/tests/mochitest/test_inspector-reload.html
-new file mode 100644
---- /dev/null
-+++ b/toolkit/devtools/server/tests/mochitest/test_inspector-reload.html
-@@ -0,0 +1,83 @@
-+<!DOCTYPE HTML>
-+<html>
-+<!--
-+https://bugzilla.mozilla.org/show_bug.cgi?id=
-+-->
-+<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">
-+Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
-+const Ci = Components.interfaces;
-+const promise = devtools.require("sdk/core/promise");
-+const inspector = devtools.require("devtools/server/actors/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} = devtools.require("devtools/server/actors/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");
-+      ok(walker === gWalker, "getWalker() twice should return the same walker.");
-+    }).then(runNextTest));
-+  });
-+});
-+
-+addTest(function testReload() {
-+  let nodeFront;
-+  let oldRootID = gWalker.rootNode.actorID;
-+  // Load a node to populate the tree a bit.
-+  promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => {
-+    gInspectee.defaultView.location.reload();
-+    return waitForMutation(gWalker, isNewRoot);
-+  }).then(() => {
-+    ok(gWalker.rootNode.actorID != oldRootID, "Root node should have changed.");
-+  }).then(() => {
-+    // Make sure we can still access the document
-+    return gWalker.querySelector(gWalker.rootNode, "#a");
-+  }).then(front => {
-+    ok(front.actorID, "Got a new actor ID");
-+  }).then(runNextTest));
-+});
-+
-+addTest(function cleanup() {
-+  delete gWalker;
-+  delete gInspectee;
-+  delete gClient;
-+  runNextTest();
-+});
-+
-+
-+  </script>
-+</head>
-+<body>
-+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
-+<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
-+<p id="display"></p>
-+<div id="content" style="display: none">
-+
-+</div>
-+<pre id="test">
-+</pre>
-+</body>
-+</html>
--- a/warning-fixes.diff
+++ b/warning-fixes.diff
@@ -1,20 +1,20 @@
 # HG changeset patch
 # User Dave Camp <dcamp@mozilla.com>
 # Date 1371477174 25200
 #      Mon Jun 17 06:52:54 2013 -0700
-# Node ID 8bc59ac5b61833921ae44356dc54ffb601925c93
-# Parent  b6a1d79eedf8d8310150b4cc1d72e0d0b41e4ff3
+# Node ID b1234d4c191aa44e83207f07c2be6c9a8f489554
+# Parent  6070992bdde6e39b5e32ebc7e6c43dcf649e4e0b
 imported patch warning-fixes.diff
 
 diff --git a/browser/devtools/shared/inplace-editor.js b/browser/devtools/shared/inplace-editor.js
 --- a/browser/devtools/shared/inplace-editor.js
 +++ b/browser/devtools/shared/inplace-editor.js
-@@ -398,17 +398,17 @@ InplaceEditor.prototype = {
+@@ -413,17 +413,17 @@ InplaceEditor.prototype = {
              --selStart;
            }
          }
        }
        return this._incrementGenericValue(value, increment, selStart, selEnd, info);
      }
  
      if (incrementedValue === null) {
@@ -23,17 +23,17 @@ diff --git a/browser/devtools/shared/inp
      }
  
      let preRawValue = value.substr(0, range.start);
      let postRawValue = value.substr(range.end);
  
      return {
        value: preRawValue + incrementedValue + postRawValue,
        start: range.start + selection[0],
-@@ -435,17 +435,17 @@ InplaceEditor.prototype = {
+@@ -450,17 +450,17 @@ InplaceEditor.prototype = {
      while ((m = reSplitCSS.exec(value)) &&
            (m.index + m[0].length < offset)) {
        value = value.substr(m.index + m[0].length);
        start += m.index + m[0].length;
        offset -= m.index + m[0].length;
      }
  
      if (!m) {
@@ -42,17 +42,17 @@ diff --git a/browser/devtools/shared/inp
      }
  
      let type;
      if (m[1]) {
        type = "url";
      } else if (m[2]) {
        type = "rgb";
      } else if (m[3]) {
-@@ -529,16 +529,17 @@ InplaceEditor.prototype = {
+@@ -544,16 +544,17 @@ InplaceEditor.prototype = {
        if (mid !== null) {
          return {
            value: first + mid + last,
            start: start,
            end: start + mid.length
          };
        }
      }
@@ -60,17 +60,17 @@ diff --git a/browser/devtools/shared/inp
    },
  
    /**
     * Increment the property value for numbers.
     *
     * @param {string} rawValue
     *        Raw value to increment.
     * @param {number} increment
-@@ -587,20 +588,20 @@ InplaceEditor.prototype = {
+@@ -602,20 +603,20 @@ InplaceEditor.prototype = {
     *        Ending index of the property value.
     * @return {object} object with properties 'value' and 'selection'.
     */
    _incHexColor:
    function InplaceEditor_incHexColor(rawValue, increment, offset, offsetEnd)
    {
      // Return early if no part of the rawValue is selected.
      if (offsetEnd > rawValue.length && offset >= rawValue.length) {
@@ -83,17 +83,17 @@ diff --git a/browser/devtools/shared/inp
      }
      // Ignore the leading #.
      rawValue = rawValue.substr(1);
      --offset;
      --offsetEnd;
  
      // Clamp the selection to within the actual value.
      offset = Math.max(offset, 0);
-@@ -612,17 +613,17 @@ InplaceEditor.prototype = {
+@@ -627,17 +628,17 @@ InplaceEditor.prototype = {
        rawValue = rawValue.charAt(0) + rawValue.charAt(0) +
                   rawValue.charAt(1) + rawValue.charAt(1) +
                   rawValue.charAt(2) + rawValue.charAt(2);
        offset *= 2;
        offsetEnd *= 2;
      }
  
      if (rawValue.length !== 6) {
@@ -102,17 +102,17 @@ diff --git a/browser/devtools/shared/inp
      }
  
      // If no selection, increment an adjacent color, preferably one to the left.
      if (offset === offsetEnd) {
        if (offset === 0) {
          offsetEnd = 1;
        } else {
          offset = offsetEnd - 1;
-@@ -644,17 +645,17 @@ InplaceEditor.prototype = {
+@@ -659,17 +660,17 @@ InplaceEditor.prototype = {
      let isUpper = (rawValue.toUpperCase() === rawValue);
  
      for (let pos = offset; pos < offsetEnd; pos += 2) {
        // Increment the part in [pos, pos+2).
        let mid = rawValue.substr(pos, 2);
        let value = parseInt(mid, 16);
  
        if (isNaN(value)) {
@@ -121,17 +121,17 @@ diff --git a/browser/devtools/shared/inp
        }
  
        mid = Math.min(Math.max(value + increment, 0), 255).toString(16);
  
        while (mid.length < 2) {
          mid = "0" + mid;
        }
        if (isUpper) {
-@@ -671,17 +672,17 @@ InplaceEditor.prototype = {
+@@ -686,17 +687,17 @@ InplaceEditor.prototype = {
    },
  
    /**
     * Call the client's done handler and clear out.
     */
    _apply: function InplaceEditor_apply(aEvent)
    {
      if (this._applied) {
@@ -140,58 +140,16 @@ diff --git a/browser/devtools/shared/inp
      }
  
      this._applied = true;
  
      if (this.done) {
        let val = this.input.value.trim();
        return this.done(this.cancelled ? this.initial : val, !this.cancelled);
      }
-diff --git a/browser/devtools/shared/theme-switching.js b/browser/devtools/shared/theme-switching.js
---- a/browser/devtools/shared/theme-switching.js
-+++ b/browser/devtools/shared/theme-switching.js
-@@ -21,34 +21,34 @@
-   function switchTheme(newTheme, oldTheme) {
-     let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
- 
-     if (oldTheme && newTheme != oldTheme) {
-       let oldThemeUrl = Services.io.newURI(
-         DEVTOOLS_SKIN_URL + oldTheme + "-theme.css", null, null);
-       try {
--        winUtils.removeSheet(oldThemeUrl, window.AUTHOR_SHEET);
-+        winUtils.removeSheet(oldThemeUrl, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
-       } catch(ex) {}
-     }
- 
-     let newThemeUrl = Services.io.newURI(
-       DEVTOOLS_SKIN_URL + newTheme + "-theme.css", null, null);
--    winUtils.loadSheet(newThemeUrl, window.AUTHOR_SHEET);
-+    winUtils.loadSheet(newThemeUrl, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
- 
-     // Floating scrollbars à la osx
-     if (Services.appinfo.OS != "Darwin") {
-       let scrollbarsUrl = Services.io.newURI(
-         DEVTOOLS_SKIN_URL + "floating-scrollbars-light.css", null, null);
- 
-       if (newTheme == "dark") {
--        winUtils.loadSheet(scrollbarsUrl, window.AGENT_SHEET);
-+        winUtils.loadSheet(scrollbarsUrl, Ci.nsIDOMWindowUtils.AGENT_SHEET);
-       } else if (oldTheme == "dark") {
-         try {
--          winUtils.removeSheet(scrollbarsUrl, window.AGENT_SHEET);
-+          winUtils.removeSheet(scrollbarsUrl, Ci.nsIDOMWindowUtils.AGENT_SHEET);
-         } catch(ex) {}
-       }
-       forceStyle();
-     }
- 
-     document.documentElement.classList.remove("theme-" + oldTheme);
-     document.documentElement.classList.add("theme-" + newTheme);
-   }
 diff --git a/browser/devtools/shared/undo.js b/browser/devtools/shared/undo.js
 --- a/browser/devtools/shared/undo.js
 +++ b/browser/devtools/shared/undo.js
 @@ -195,12 +195,13 @@ UndoStack.prototype = {
    },
  
    doCommand: function Undo_doCommand(aCommand)
    {
--- a/window-targets.diff
+++ b/window-targets.diff
@@ -1,14 +1,14 @@
 # HG changeset patch
 # User Dave Camp <dcamp@mozilla.com>
 # Date 1372178155 25200
 #      Tue Jun 25 09:35:55 2013 -0700
-# Node ID 4e77488128c5517385dd6eed591e3ce426ea87df
-# Parent ab95d59d66c89d736969eee97cef670fc8b5ec6a
+# Node ID 6070992bdde6e39b5e32ebc7e6c43dcf649e4e0b
+# Parent 03c2f346b15f571ae25b78c586210f09359290c2
 imported patch window-targets.diff
 * * *
 imported patch custom-windows.diff
 
 diff --git a/browser/devtools/framework/target.js b/browser/devtools/framework/target.js
 --- a/browser/devtools/framework/target.js
 +++ b/browser/devtools/framework/target.js
 @@ -77,17 +77,19 @@ exports.TargetFactory = {
@@ -146,17 +146,17 @@ diff --git a/browser/devtools/framework/
        // Since a remote protocol connection will be made, let's start the
        // DebuggerServer here, once and for all tools.
        if (!DebuggerServer.initialized) {
          DebuggerServer.init();
          DebuggerServer.addBrowserActors();
        }
  
        this._client = new DebuggerClient(DebuggerServer.connectPipe());
-@@ -304,16 +327,38 @@ TabTarget.prototype = {
+@@ -305,16 +328,38 @@ TabTarget.prototype = {
      if (this.isLocalTab) {
        this._client.connect((aType, aTraits) => {
          this._client.listTabs(aResponse => {
            this._root = aResponse;
            this._form = aResponse.tabs[aResponse.selected];
            attachTab();
          });
        });
@@ -185,17 +185,17 @@ diff --git a/browser/devtools/framework/
      } else if (!this.chrome) {
        // In the remote debugging case, the protocol connection will have been
        // already initialized in the connection screen code.
        attachTab();
      } else {
        // Remote chrome debugging doesn't need anything at this point.
        this._remote.resolve(null);
      }
-@@ -525,90 +570,8 @@ TabWebProgressListener.prototype = {
+@@ -526,90 +571,8 @@ TabWebProgressListener.prototype = {
        this.target.tab.linkedBrowser.removeProgressListener(this);
      }
      this.target._webProgressListener = null;
      this.target._navRequest = null;
      this.target._navWindow = null;
      this.target = null;
    }
  };
@@ -279,17 +279,17 @@ diff --git a/browser/devtools/framework/
 -
 -  toString: function() {
 -    return 'WindowTarget:' + this.window;
 -  },
 -};
 diff --git a/toolkit/devtools/server/actors/inspector.js b/toolkit/devtools/server/actors/inspector.js
 --- a/toolkit/devtools/server/actors/inspector.js
 +++ b/toolkit/devtools/server/actors/inspector.js
-@@ -681,17 +681,17 @@ let traversalMethod = {
+@@ -699,17 +699,17 @@ let traversalMethod = {
   */
  var ProgressListener = Class({
    extends: Unknown,
    interfaces: ["nsIWebProgressListener", "nsISupportsWeakReference"],
  
    initialize: function(webProgress) {
      Unknown.prototype.initialize.call(this);
      this.webProgress = webProgress;
@@ -298,17 +298,17 @@ diff --git a/toolkit/devtools/server/act
    },
  
    destroy: function() {
      this.webProgress.removeProgressListener(this);
    },
  
    onStateChange: makeInfallible(function stateChange(progress, request, flag, status) {
      let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
-@@ -2036,20 +2036,27 @@ var InspectorActor = protocol.ActorClass
+@@ -2073,20 +2073,27 @@ var InspectorActor = protocol.ActorClass
        return this._walkerPromise;
      }
  
      let deferred = promise.defer();
      this._walkerPromise = deferred.promise;
  
      let window = this.window;
  
@@ -317,26 +317,26 @@ diff --git a/toolkit/devtools/server/act
 +      webProgress = webProgress.QueryInterface(Ci.nsIInterfaceRequestor)
 +                      .getInterface(Ci.nsIDocShell)
 +                      .QueryInterface(Ci.nsIWebProgress)
 +    }
 +
      var domReady = () => {
        let tabActor = this.tabActor;
        window.removeEventListener("DOMContentLoaded", domReady, true);
--      deferred.resolve(WalkerActor(this.conn, window.document, tabActor._tabbrowser, options));
-+      deferred.resolve(WalkerActor(this.conn, window.document, webProgress, options));
+-      this.walker = WalkerActor(this.conn, window.document, tabActor._tabbrowser, options);
++      this.walker = WalkerActor(this.conn, window.document, webProgress, options);
+       deferred.resolve(this.walker);
      };
  
      if (window.document.readyState === "loading") {
        window.addEventListener("DOMContentLoaded", domReady, true);
      } else {
        domReady();
      }
- 
 diff --git a/toolkit/devtools/server/actors/webbrowser.js b/toolkit/devtools/server/actors/webbrowser.js
 --- a/toolkit/devtools/server/actors/webbrowser.js
 +++ b/toolkit/devtools/server/actors/webbrowser.js
 @@ -4,16 +4,22 @@
   * 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";
@@ -463,37 +463,36 @@ diff --git a/toolkit/devtools/server/act
    /**
     * Getter for the tab title.
     * @return string
     *         Tab title.
     */
    get title() {
      let title = this.browser.contentTitle;
      // If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
-@@ -521,38 +547,42 @@ BrowserTabActor.prototype = {
+@@ -521,17 +547,17 @@ BrowserTabActor.prototype = {
    },
  
    /**
     * Getter for the tab URL.
     * @return string
     *         Tab URL.
     */
    get url() {
 -    return this.browser.currentURI.spec;
 +    return this.currentURI;
    },
  
    /**
-    * Getter for the tab content window.
+    * Getter for the tab content window, will be used by child actors to target
+    * the right window.
     * @return nsIDOMWindow
     *         Tab content window.
     */
-   get contentWindow() {
--    return this.browser.contentWindow;
-+    return this.browser.contentWindow ? this.browser.contentWindow : this.browser;
+@@ -546,20 +572,24 @@ BrowserTabActor.prototype = {
    },
  
    grip: function BTA_grip() {
      dbg_assert(!this.exited,
                 "grip() shouldn't be called on exited browser actor.");
      dbg_assert(this.actorID,
                 "tab should have an actorID.");