Bug 1225254 - split css-logic.js into server and shared files; r=pbro
authorTom Tromey <tom@tromey.com>
Fri, 24 Jun 2016 08:26:21 -0600
changeset 343169 b111182d1cd2ea3c0e2bcee1d5d8012211f87709
parent 343168 d6addbcbd85d5e76c0299b6f573f27fd2183bdbb
child 343170 31e5df99112e4e1c9bfb75c8752883310675b11c
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbro
bugs1225254
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1225254 - split css-logic.js into server and shared files; r=pbro MozReview-Commit-ID: BTFVQJcVI5d
.eslintignore
devtools/client/inspector/computed/computed.js
devtools/client/inspector/rules/models/rule.js
devtools/client/inspector/rules/rules.js
devtools/client/inspector/rules/test/doc_frame_script.js
devtools/client/inspector/rules/views/rule-editor.js
devtools/client/inspector/rules/views/text-property-editor.js
devtools/client/inspector/shared/test/doc_frame_script.js
devtools/client/inspector/shared/test/head.js
devtools/client/styleeditor/StyleSheetEditor.jsm
devtools/server/actors/csscoverage.js
devtools/server/actors/highlighters/utils/markup.js
devtools/server/actors/inspector.js
devtools/server/actors/script.js
devtools/server/actors/styleeditor.js
devtools/server/actors/styles.js
devtools/server/actors/stylesheets.js
devtools/server/css-logic.js
devtools/server/moz.build
devtools/server/tests/mochitest/test_css-logic-media-queries.html
devtools/server/tests/mochitest/test_css-logic-specificity.html
devtools/server/tests/mochitest/test_css-logic.html
devtools/server/tests/mochitest/test_styles-matched.html
devtools/shared/inspector/css-logic.js
devtools/shared/tests/unit/test_prettifyCSS.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -104,33 +104,33 @@ devtools/client/sourceeditor/**
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/**
 !devtools/client/webconsole/panel.js
 !devtools/client/webconsole/jsterm.js
 !devtools/client/webconsole/console-commands.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
 devtools/server/**
+!devtools/server/css-logic.js
 !devtools/server/actors/webbrowser.js
 !devtools/server/actors/styles.js
 !devtools/server/actors/string.js
 devtools/shared/*.js
 !devtools/shared/css-lexer.js
 !devtools/shared/defer.js
 !devtools/shared/event-emitter.js
 !devtools/shared/task.js
 devtools/shared/*.jsm
 !devtools/shared/Loader.jsm
 devtools/shared/apps/**
 devtools/shared/client/**
 devtools/shared/discovery/**
 devtools/shared/gcli/**
 !devtools/shared/gcli/templater.js
 devtools/shared/heapsnapshot/**
-devtools/shared/inspector/**
 devtools/shared/layout/**
 devtools/shared/locales/**
 devtools/shared/performance/**
 devtools/shared/qrcode/**
 devtools/shared/security/**
 devtools/shared/shims/**
 devtools/shared/tests/**
 !devtools/shared/tests/unit/test_csslexer.js
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -6,17 +6,17 @@
 
 /* globals StopIteration */
 
 "use strict";
 
 const {Cc, Ci} = require("chrome");
 
 const ToolDefinitions = require("devtools/client/definitions").Tools;
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {OutputParser} = require("devtools/client/shared/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const {createChild} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -3,17 +3,17 @@
 /* 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 promise = require("promise");
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {TextProperty} =
       require("devtools/client/inspector/rules/models/text-property");
 const {promiseWarn} = require("devtools/client/inspector/shared/utils");
 const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "osString", function () {
--- a/devtools/client/inspector/rules/rules.js
+++ b/devtools/client/inspector/rules/rules.js
@@ -9,17 +9,17 @@
 
 const {Cc, Ci} = require("chrome");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const {Task} = require("devtools/shared/task");
 const {Tools} = require("devtools/client/definitions");
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {OutputParser} = require("devtools/client/shared/output-parser");
 const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const {ElementStyle} = require("devtools/client/inspector/rules/models/element-style");
 const {Rule} = require("devtools/client/inspector/rules/models/rule");
 const {RuleEditor} = require("devtools/client/inspector/rules/views/rule-editor");
 const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
 const {gDevTools} = require("devtools/client/framework/devtools");
@@ -963,17 +963,17 @@ CssRuleView.prototype = {
    */
   _showEmpty: function () {
     if (this.styleDocument.getElementById("noResults") > 0) {
       return;
     }
 
     createChild(this.element, "div", {
       id: "noResults",
-      textContent: CssLogic.l10n("rule.empty")
+      textContent: l10n("rule.empty")
     });
   },
 
   /**
    * Clear the rules.
    */
   _clearRules: function () {
     this.element.innerHTML = "";
@@ -1006,28 +1006,28 @@ CssRuleView.prototype = {
 
   /**
    * Text for header that shows above rules for this element
    */
   get selectedElementLabel() {
     if (this._selectedElementLabel) {
       return this._selectedElementLabel;
     }
-    this._selectedElementLabel = CssLogic.l10n("rule.selectedElement");
+    this._selectedElementLabel = l10n("rule.selectedElement");
     return this._selectedElementLabel;
   },
 
   /**
    * Text for header that shows above rules for pseudo elements
    */
   get pseudoElementLabel() {
     if (this._pseudoElementLabel) {
       return this._pseudoElementLabel;
     }
-    this._pseudoElementLabel = CssLogic.l10n("rule.pseudoElement");
+    this._pseudoElementLabel = l10n("rule.pseudoElement");
     return this._pseudoElementLabel;
   },
 
   get showPseudoElements() {
     if (this._showPseudoElements === undefined) {
       this._showPseudoElements =
         Services.prefs.getBoolPref("devtools.inspector.show_pseudo_elements");
     }
--- a/devtools/client/inspector/rules/test/doc_frame_script.js
+++ b/devtools/client/inspector/rules/test/doc_frame_script.js
@@ -14,17 +14,17 @@
 // let response = yield executeInContent(browser, "Test:msgName", data, true);
 // The response message should have the same name "Test:msgName"
 //
 // Some listeners do not send a response message back.
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var {CssLogic} = require("devtools/shared/inspector/css-logic");
+var {isContentStylesheet} = require("devtools/shared/inspector/css-logic");
 var defer = require("devtools/shared/defer");
 
 /**
  * Get a value for a given property name in a css rule in a stylesheet, given
  * their indexes
  * @param {Object} data Expects a data object with the following properties
  * - {Number} styleSheetIndex
  * - {Number} ruleIndex
@@ -63,17 +63,17 @@ addMessageListener("Test:GetStyleSheetsI
   let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
     .getService(Ci.inIDOMUtils);
   let domRules = domUtils.getCSSStyleRules(target);
 
   for (let i = 0, n = domRules.Count(); i < n; i++) {
     let sheet = domRules.GetElementAt(i).parentStyleSheet;
     sheets.push({
       href: sheet.href,
-      isContentSheet: CssLogic.isContentStylesheet(sheet)
+      isContentSheet: isContentStylesheet(sheet)
     });
   }
 
   sendAsyncMessage("Test:GetStyleSheetsInfoForNode", sheets);
 });
 
 /**
  * Get the property value from the computed style for an element.
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -1,17 +1,17 @@
 /* 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 {Ci} = require("chrome");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const {l10n} = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {PREF_ORIG_SOURCES} = require("devtools/client/styleeditor/utils");
 const {Rule} = require("devtools/client/inspector/rules/models/rule");
 const {InplaceEditor, editableField, editableItem} =
       require("devtools/client/shared/inplace-editor");
 const {TextPropertyEditor} =
       require("devtools/client/inspector/rules/views/text-property-editor");
 const {
@@ -154,17 +154,17 @@ RuleEditor.prototype = {
     if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE &&
         this.rule.domRule.selectors) {
       let selector = this.rule.domRule.selectors.join(", ");
 
       let selectorHighlighter = createChild(header, "span", {
         class: "ruleview-selectorhighlighter" +
                (this.ruleView.highlightedSelector === selector ?
                 " highlighted" : ""),
-        title: CssLogic.l10n("rule.selectorHighlighter.tooltip")
+        title: l10n("rule.selectorHighlighter.tooltip")
       });
       selectorHighlighter.addEventListener("click", () => {
         this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector);
       });
     }
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
--- a/devtools/client/inspector/rules/views/text-property-editor.js
+++ b/devtools/client/inspector/rules/views/text-property-editor.js
@@ -1,16 +1,16 @@
 /* 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 {Ci} = require("chrome");
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const {l10n} = require("devtools/shared/inspector/css-logic");
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 const {InplaceEditor, editableField} =
       require("devtools/client/shared/inplace-editor");
 const {
   createChild,
   appendText,
   advanceValidate,
   blurOnMultipleProperties,
@@ -171,25 +171,25 @@ TextPropertyEditor.prototype = {
                        value: parsedValue,
                        priority: this.prop.priority };
 
     appendText(this.valueContainer, ";");
 
     this.warning = createChild(this.container, "div", {
       class: "ruleview-warning",
       hidden: "",
-      title: CssLogic.l10n("rule.warning.title"),
+      title: l10n("rule.warning.title"),
     });
 
     // Filter button that filters for the current property name and is
     // displayed when the property is overridden by another rule.
     this.filterProperty = createChild(this.container, "div", {
       class: "ruleview-overridden-rule-filter",
       hidden: "",
-      title: CssLogic.l10n("rule.filterProperty.title"),
+      title: l10n("rule.filterProperty.title"),
     });
 
     this.filterProperty.addEventListener("click", event => {
       this.ruleEditor.ruleView.setFilterStyles("`" + this.prop.name + "`");
       event.stopPropagation();
     }, false);
 
     // Holds the viewers for the computed properties.
@@ -366,17 +366,17 @@ TextPropertyEditor.prototype = {
         // knows about
         this.ruleView.tooltips.colorPicker.addSwatch(span, {
           onShow: this._onStartEditing,
           onPreview: this._onSwatchPreview,
           onCommit: this._onSwatchCommit,
           onRevert: this._onSwatchRevert
         });
         span.on("unit-change", this._onSwatchCommit);
-        let title = CssLogic.l10n("rule.colorSwatch.tooltip");
+        let title = l10n("rule.colorSwatch.tooltip");
         span.setAttribute("title", title);
       }
     }
 
     // Attach the cubic-bezier tooltip to the bezier swatches
     this._bezierSwatchSpans =
       this.valueSpan.querySelectorAll("." + BEZIER_SWATCH_CLASS);
     if (this.ruleEditor.isEditable) {
@@ -384,44 +384,44 @@ TextPropertyEditor.prototype = {
         // Adding this swatch to the list of swatches our colorpicker
         // knows about
         this.ruleView.tooltips.cubicBezier.addSwatch(span, {
           onShow: this._onStartEditing,
           onPreview: this._onSwatchPreview,
           onCommit: this._onSwatchCommit,
           onRevert: this._onSwatchRevert
         });
-        let title = CssLogic.l10n("rule.bezierSwatch.tooltip");
+        let title = l10n("rule.bezierSwatch.tooltip");
         span.setAttribute("title", title);
       }
     }
 
     // Attach the filter editor tooltip to the filter swatch
     let span = this.valueSpan.querySelector("." + FILTER_SWATCH_CLASS);
     if (this.ruleEditor.isEditable) {
       if (span) {
         parserOptions.filterSwatch = true;
 
         this.ruleView.tooltips.filterEditor.addSwatch(span, {
           onShow: this._onStartEditing,
           onPreview: this._onSwatchPreview,
           onCommit: this._onSwatchCommit,
           onRevert: this._onSwatchRevert
         }, outputParser, parserOptions);
-        let title = CssLogic.l10n("rule.filterSwatch.tooltip");
+        let title = l10n("rule.filterSwatch.tooltip");
         span.setAttribute("title", title);
       }
     }
 
     this.angleSwatchSpans =
       this.valueSpan.querySelectorAll("." + ANGLE_SWATCH_CLASS);
     if (this.ruleEditor.isEditable) {
       for (let angleSpan of this.angleSwatchSpans) {
         angleSpan.on("unit-change", this._onSwatchCommit);
-        let title = CssLogic.l10n("rule.angleSwatch.tooltip");
+        let title = l10n("rule.angleSwatch.tooltip");
         angleSpan.setAttribute("title", title);
       }
     }
 
     // Now that we have updated the property's value, we might have a pending
     // click on the value container. If we do, we have to trigger a click event
     // on the right element.
     if (this._hasPendingClick) {
--- a/devtools/client/inspector/shared/test/doc_frame_script.js
+++ b/devtools/client/inspector/shared/test/doc_frame_script.js
@@ -14,17 +14,17 @@
 // let response = yield executeInContent(browser, "Test:MsgName", data, true);
 // The response message should have the same name "Test:MsgName"
 //
 // Some listeners do not send a response message back.
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var {CssLogic} = require("devtools/shared/inspector/css-logic");
+var {isContentStylesheet} = require("devtools/shared/inspector/css-logic");
 var defer = require("devtools/shared/defer");
 
 /**
  * Get a value for a given property name in a css rule in a stylesheet, given
  * their indexes
  * @param {Object} data Expects a data object with the following properties
  * - {Number} styleSheetIndex
  * - {Number} ruleIndex
@@ -63,17 +63,17 @@ addMessageListener("Test:GetStyleSheetsI
   let domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
     .getService(Ci.inIDOMUtils);
   let domRules = domUtils.getCSSStyleRules(target);
 
   for (let i = 0, n = domRules.Count(); i < n; i++) {
     let sheet = domRules.GetElementAt(i).parentStyleSheet;
     sheets.push({
       href: sheet.href,
-      isContentSheet: CssLogic.isContentStylesheet(sheet)
+      isContentSheet: isContentStylesheet(sheet)
     });
   }
 
   sendAsyncMessage("Test:GetStyleSheetsInfoForNode", sheets);
 });
 
 /**
  * Get the property value from the computed style for an element.
--- a/devtools/client/inspector/shared/test/head.js
+++ b/devtools/client/inspector/shared/test/head.js
@@ -7,17 +7,16 @@
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
 var {CssRuleView} = require("devtools/client/inspector/rules/rules");
-var {CssLogic, CssSelector} = require("devtools/shared/inspector/css-logic");
 var {getInplaceEditorForSpan: inplaceEditor} =
   require("devtools/client/shared/inplace-editor");
 
 const TEST_URL_ROOT =
   "http://example.com/browser/devtools/client/inspector/shared/test/";
 const TEST_URL_ROOT_SSL =
   "https://example.com/browser/devtools/client/inspector/shared/test/";
 const ROOT_TEST_DIR = getRootDirectory(gTestPath);
--- a/devtools/client/styleeditor/StyleSheetEditor.jsm
+++ b/devtools/client/styleeditor/StyleSheetEditor.jsm
@@ -10,17 +10,17 @@ this.EXPORTED_SYMBOLS = ["StyleSheetEdit
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const Editor = require("devtools/client/sourceeditor/editor");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const {shortSource, prettifyCSS} = require("devtools/shared/inspector/css-logic");
 const {console} = require("resource://gre/modules/Console.jsm");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
 const {Task} = require("devtools/shared/task");
 const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
 const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
 const {TextDecoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
 const {
@@ -191,17 +191,17 @@ StyleSheetEditor.prototype = {
 
     if (!this.styleSheet.href) {
       let index = this.styleSheet.styleSheetIndex + 1;
       return getString("inlineStyleSheet", index);
     }
 
     if (!this._friendlyName) {
       let sheetURI = this.styleSheet.href;
-      this._friendlyName = CssLogic.shortSource({ href: sheetURI });
+      this._friendlyName = shortSource({ href: sheetURI });
       try {
         this._friendlyName = decodeURI(this._friendlyName);
       } catch (ex) {
         // Ignore.
       }
     }
     return this._friendlyName;
   },
@@ -257,29 +257,28 @@ StyleSheetEditor.prototype = {
       this._fileModDate = info.lastModificationDate.getTime();
     }, this.markLinkedFileBroken);
 
     this.emit("linked-css-file");
   },
 
   /**
    * A helper function that fetches the source text from the style
-   * sheet.  The text is possibly prettified using
-   * CssLogic.prettifyCSS.  This also sets |this._state.text| to the
-   * new text.
+   * sheet.  The text is possibly prettified using prettifyCSS.  This
+   * also sets |this._state.text| to the new text.
    *
    * @return {Promise} a promise that resolves to the new text
    */
   _getSourceTextAndPrettify: function () {
     return this.styleSheet.getText().then((longStr) => {
       return longStr.string();
     }).then((source) => {
       let ruleCount = this.styleSheet.ruleCount;
       if (!this.styleSheet.isOriginalSource) {
-        source = CssLogic.prettifyCSS(source, ruleCount);
+        source = prettifyCSS(source, ruleCount);
       }
       this._state.text = source;
       return source;
     });
   },
 
   /**
    * Start fetching the full text source for this editor's sheet.
--- a/devtools/server/actors/csscoverage.js
+++ b/devtools/server/actors/csscoverage.js
@@ -13,17 +13,17 @@ const events = require("sdk/event/core")
 const protocol = require("devtools/shared/protocol");
 const { custom } = protocol;
 const { cssUsageSpec } = require("devtools/shared/specs/csscoverage");
 
 loader.lazyGetter(this, "DOMUtils", () => {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 loader.lazyRequireGetter(this, "stylesheets", "devtools/server/actors/stylesheets");
-loader.lazyRequireGetter(this, "CssLogic", "devtools/shared/inspector/css-logic", true);
+loader.lazyRequireGetter(this, "prettifyCSS", "devtools/shared/inspector/css-logic", true);
 
 const CSSRule = Ci.nsIDOMCSSRule;
 
 const MAX_UNUSED_RULES = 10000;
 
 /**
  * Allow: let foo = l10n.lookup("csscoverageFoo");
  */
@@ -392,17 +392,17 @@ var CSSUsageActor = protocol.ActorClassW
 
     // Helper function to create a JSONable data structure representing a rule
     const ruleToRuleReport = function (rule, ruleData) {
       return {
         url: rule.url,
         shortUrl: rule.url.split("/").slice(-1)[0],
         start: { line: rule.line, column: rule.column },
         selectorText: ruleData.selectorText,
-        formattedCssText: CssLogic.prettifyCSS(ruleData.cssText)
+        formattedCssText: prettifyCSS(ruleData.cssText)
       };
     };
 
     // A count of each type of rule for the bar chart
     let summary = { used: 0, unused: 0, preload: 0 };
 
     // Create the set of the unused rules
     let unusedMap = new Map();
--- a/devtools/server/actors/highlighters/utils/markup.js
+++ b/devtools/server/actors/highlighters/utils/markup.js
@@ -7,17 +7,17 @@
 const { Cc, Ci, Cu } = require("chrome");
 const { getCurrentZoom,
   getRootBindingParent } = require("devtools/shared/layout/utils");
 const { on, emit } = require("sdk/event/core");
 
 const lazyContainer = {};
 
 loader.lazyRequireGetter(lazyContainer, "CssLogic",
-  "devtools/shared/inspector/css-logic", true);
+  "devtools/server/css-logic", true);
 exports.getComputedStyle = (node) =>
   lazyContainer.CssLogic.getComputedStyle(node);
 
 exports.getBindingElementAndPseudo = (node) =>
   lazyContainer.CssLogic.getBindingElementAndPseudo(node);
 
 loader.lazyGetter(lazyContainer, "DOMUtils", () =>
   Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -145,17 +145,17 @@ loader.lazyGetter(this, "DOMParser", fun
            .createInstance(Ci.nsIDOMParser);
 });
 
 loader.lazyGetter(this, "eventListenerService", function () {
   return Cc["@mozilla.org/eventlistenerservice;1"]
            .getService(Ci.nsIEventListenerService);
 });
 
-loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic").CssLogic);
+loader.lazyGetter(this, "CssLogic", () => require("devtools/server/css-logic").CssLogic);
 
 /**
  * We only send nodeValue up to a certain size by default.  This stuff
  * controls that size.
  */
 exports.DEFAULT_VALUE_SUMMARY_LENGTH = 50;
 var gValueSummaryLength = exports.DEFAULT_VALUE_SUMMARY_LENGTH;
 
--- a/devtools/server/actors/script.js
+++ b/devtools/server/actors/script.js
@@ -28,17 +28,17 @@ const { threadSpec } = require("devtools
 
 const { defer, resolve, reject, all } = promise;
 
 loader.lazyGetter(this, "Debugger", () => {
   let Debugger = require("Debugger");
   hackDebugger(Debugger);
   return Debugger;
 });
-loader.lazyRequireGetter(this, "CssLogic", "devtools/shared/inspector/css-logic", true);
+loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
 
 /**
  * A BreakpointActorMap is a map from locations to instances of BreakpointActor.
  */
 function BreakpointActorMap() {
   this._size = 0;
--- a/devtools/server/actors/styleeditor.js
+++ b/devtools/server/actors/styleeditor.js
@@ -9,17 +9,17 @@ const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const promise = require("promise");
 const events = require("sdk/event/core");
 const protocol = require("devtools/shared/protocol");
 const {Arg, method, RetVal} = protocol;
 const {fetch} = require("devtools/shared/DevToolsUtils");
 const {oldStyleSheetSpec, styleEditorSpec} = require("devtools/shared/specs/styleeditor");
 
-loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic").CssLogic);
+loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic"));
 
 var TRANSITION_CLASS = "moz-styleeditor-transitioning";
 var TRANSITION_DURATION_MS = 500;
 var TRANSITION_RULE = "\
 :root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\
 transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
 transition-delay: 0ms !important;\
 transition-timing-function: ease-out !important;\
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -14,17 +14,18 @@ const {isCssPropertyKnown} = require("de
 const {Task} = require("devtools/shared/task");
 const events = require("sdk/event/core");
 
 // This will also add the "stylesheet" actor type for protocol.js to recognize
 const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} = require("devtools/server/actors/stylesheets");
 const {pageStyleSpec, styleRuleSpec, ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
-loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic").CssLogic);
+loader.lazyGetter(this, "CssLogic", () => require("devtools/server/css-logic").CssLogic);
+loader.lazyGetter(this, "SharedCssLogic", () => require("devtools/shared/inspector/css-logic"));
 loader.lazyGetter(this, "DOMUtils", () => Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
 
 // When gathering rules to read for pseudo elements, we will skip
 // :before and :after, which are handled as a special case.
 loader.lazyGetter(this, "PSEUDO_ELEMENTS_TO_READ", () => {
   return DOMUtils.getCSSPseudoElementNames().filter(pseudo => {
     return pseudo !== ":before" && pseudo !== ":after";
   });
@@ -197,17 +198,17 @@ var PageStyleActor = protocol.ActorClass
    *       matched: <true if there are matched selectors for this value>
    *     },
    *     ...
    *   }
    */
   getComputed: function (node, options) {
     let ret = Object.create(null);
 
-    this.cssLogic.sourceFilter = options.filter || CssLogic.FILTER.UA;
+    this.cssLogic.sourceFilter = options.filter || SharedCssLogic.FILTER.UA;
     this.cssLogic.highlight(node.rawNode);
     let computed = this.cssLogic.computedStyle || [];
 
     Array.prototype.forEach.call(computed, name => {
       ret[name] = {
         value: computed.getPropertyValue(name),
         priority: computed.getPropertyPriority(name) || undefined
       };
@@ -375,17 +376,17 @@ var PageStyleActor = protocol.ActorClass
    *     // 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: function (node, property, options) {
-    this.cssLogic.sourceFilter = options.filter || CssLogic.FILTER.UA;
+    this.cssLogic.sourceFilter = options.filter || SharedCssLogic.FILTER.UA;
     this.cssLogic.highlight(node.rawNode);
 
     let rules = new Set();
     let sheets = new Set();
 
     let matched = [];
     let propInfo = this.cssLogic.getPropertyInfo(property);
     for (let selectorInfo of propInfo.matchedSelectors) {
@@ -572,19 +573,19 @@ var PageStyleActor = protocol.ActorClass
 
     let rules = [];
 
     // 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);
+      let isSystem = !SharedCssLogic.isContentStylesheet(domRule.parentStyleSheet);
 
-      if (isSystem && options.filter != CssLogic.FILTER.UA) {
+      if (isSystem && options.filter != SharedCssLogic.FILTER.UA) {
         continue;
       }
 
       if (inherited) {
         // Don't include inherited rules if none of its properties
         // are inheritable.
         let hasInherited = [...domRule.style].some(
           prop => DOMUtils.isInheritedProperty(prop)
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -13,17 +13,17 @@ const events = require("sdk/event/core")
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {fetch} = require("devtools/shared/DevToolsUtils");
 const {listenOnce} = require("devtools/shared/async-utils");
 const {originalSourceSpec, mediaRuleSpec, styleSheetSpec,
        styleSheetsSpec} = require("devtools/shared/specs/stylesheets");
 const {SourceMapConsumer} = require("source-map");
 
-loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic").CssLogic);
+loader.lazyGetter(this, "CssLogic", () => require("devtools/shared/inspector/css-logic"));
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 var TRANSITION_CLASS = "moz-styleeditor-transitioning";
 var TRANSITION_DURATION_MS = 500;
 var TRANSITION_BUFFER_MS = 1000;
new file mode 100644
--- /dev/null
+++ b/devtools/server/css-logic.js
@@ -0,0 +1,1544 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+/*
+ * About the objects defined in this file:
+ * - CssLogic contains style information about a view context. It provides
+ *   access to 2 sets of objects: Css[Sheet|Rule|Selector] provide access to
+ *   information that does not change when the selected element changes while
+ *   Css[Property|Selector]Info provide information that is dependent on the
+ *   selected element.
+ *   Its key methods are highlight(), getPropertyInfo() and forEachSheet(), etc
+ *
+ * - CssSheet provides a more useful API to a DOM CSSSheet for our purposes,
+ *   including shortSource and href.
+ * - CssRule a more useful API to a nsIDOMCSSRule including access to the group
+ *   of CssSelectors that the rule provides properties for
+ * - CssSelector A single selector - i.e. not a selector group. In other words
+ *   a CssSelector does not contain ','. This terminology is different from the
+ *   standard DOM API, but more inline with the definition in the spec.
+ *
+ * - CssPropertyInfo contains style information for a single property for the
+ *   highlighted element.
+ * - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
+ *   reference to the selected element.
+ */
+
+"use strict";
+
+const { Cc, Ci, Cu } = require("chrome");
+const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const { getRootBindingParent } = require("devtools/shared/layout/utils");
+const nodeConstants = require("devtools/shared/dom-node-constants");
+const {l10n, isContentStylesheet, shortSource, FILTER, STATUS} = require("devtools/shared/inspector/css-logic");
+
+// This should be ok because none of the functions that use this should be used
+// on the worker thread, where Cu is not available.
+loader.lazyRequireGetter(this, "CSS", "CSS");
+
+loader.lazyRequireGetter(this, "CSSLexer", "devtools/shared/css-lexer");
+
+/**
+ * @param {function} isInherited A function that determines if the CSS property
+ *                   is inherited.
+ */
+function CssLogic(isInherited) {
+  // The cache of examined CSS properties.
+  this._isInherited = isInherited;
+  this._propertyInfos = {};
+}
+
+exports.CssLogic = CssLogic;
+
+CssLogic.prototype = {
+  // Both setup by highlight().
+  viewedElement: null,
+  viewedDocument: null,
+
+  // The cache of the known sheets.
+  _sheets: null,
+
+  // Have the sheets been cached?
+  _sheetsCached: false,
+
+  // The total number of rules, in all stylesheets, after filtering.
+  _ruleCount: 0,
+
+  // The computed styles for the viewedElement.
+  _computedStyle: null,
+
+  // Source filter. Only display properties coming from the given source
+  _sourceFilter: FILTER.USER,
+
+  // Used for tracking unique CssSheet/CssRule/CssSelector objects, in a run of
+  // processMatchedSelectors().
+  _passId: 0,
+
+  // Used for tracking matched CssSelector objects.
+  _matchId: 0,
+
+  _matchedRules: null,
+  _matchedSelectors: null,
+
+  // Cached keyframes rules in all stylesheets
+  _keyframesRules: null,
+
+  /**
+   * Reset various properties
+   */
+  reset: function () {
+    this._propertyInfos = {};
+    this._ruleCount = 0;
+    this._sheetIndex = 0;
+    this._sheets = {};
+    this._sheetsCached = false;
+    this._matchedRules = null;
+    this._matchedSelectors = null;
+    this._keyframesRules = [];
+  },
+
+  /**
+   * Focus on a new element - remove the style caches.
+   *
+   * @param {nsIDOMElement} aViewedElement the element the user has highlighted
+   * in the Inspector.
+   */
+  highlight: function (viewedElement) {
+    if (!viewedElement) {
+      this.viewedElement = null;
+      this.viewedDocument = null;
+      this._computedStyle = null;
+      this.reset();
+      return;
+    }
+
+    if (viewedElement === this.viewedElement) {
+      return;
+    }
+
+    this.viewedElement = viewedElement;
+
+    let doc = this.viewedElement.ownerDocument;
+    if (doc != this.viewedDocument) {
+      // New document: clear/rebuild the cache.
+      this.viewedDocument = doc;
+
+      // Hunt down top level stylesheets, and cache them.
+      this._cacheSheets();
+    } else {
+      // Clear cached data in the CssPropertyInfo objects.
+      this._propertyInfos = {};
+    }
+
+    this._matchedRules = null;
+    this._matchedSelectors = null;
+    this._computedStyle = CssLogic.getComputedStyle(this.viewedElement);
+  },
+
+  /**
+   * Get the values of all the computed CSS properties for the highlighted
+   * element.
+   * @returns {object} The computed CSS properties for a selected element
+   */
+  get computedStyle() {
+    return this._computedStyle;
+  },
+
+  /**
+   * Get the source filter.
+   * @returns {string} The source filter being used.
+   */
+  get sourceFilter() {
+    return this._sourceFilter;
+  },
+
+  /**
+   * Source filter. Only display properties coming from the given source (web
+   * address). Note that in order to avoid information overload we DO NOT show
+   * unmatched system rules.
+   * @see FILTER.*
+   */
+  set sourceFilter(value) {
+    let oldValue = this._sourceFilter;
+    this._sourceFilter = value;
+
+    let ruleCount = 0;
+
+    // Update the CssSheet objects.
+    this.forEachSheet(function (sheet) {
+      sheet._sheetAllowed = -1;
+      if (sheet.contentSheet && sheet.sheetAllowed) {
+        ruleCount += sheet.ruleCount;
+      }
+    }, this);
+
+    this._ruleCount = ruleCount;
+
+    // Full update is needed because the this.processMatchedSelectors() method
+    // skips UA stylesheets if the filter does not allow such sheets.
+    let needFullUpdate = (oldValue == FILTER.UA || value == FILTER.UA);
+
+    if (needFullUpdate) {
+      this._matchedRules = null;
+      this._matchedSelectors = null;
+      this._propertyInfos = {};
+    } else {
+      // Update the CssPropertyInfo objects.
+      for (let property in this._propertyInfos) {
+        this._propertyInfos[property].needRefilter = true;
+      }
+    }
+  },
+
+  /**
+   * Return a CssPropertyInfo data structure for the currently viewed element
+   * and the specified CSS property. If there is no currently viewed element we
+   * return an empty object.
+   *
+   * @param {string} property The CSS property to look for.
+   * @return {CssPropertyInfo} a CssPropertyInfo structure for the given
+   * property.
+   */
+  getPropertyInfo: function (property) {
+    if (!this.viewedElement) {
+      return {};
+    }
+
+    let info = this._propertyInfos[property];
+    if (!info) {
+      info = new CssPropertyInfo(this, property, this._isInherited);
+      this._propertyInfos[property] = info;
+    }
+
+    return info;
+  },
+
+  /**
+   * Cache all the stylesheets in the inspected document
+   * @private
+   */
+  _cacheSheets: function () {
+    this._passId++;
+    this.reset();
+
+    // styleSheets isn't an array, but forEach can work on it anyway
+    Array.prototype.forEach.call(this.viewedDocument.styleSheets,
+        this._cacheSheet, this);
+
+    this._sheetsCached = true;
+  },
+
+  /**
+   * Cache a stylesheet if it falls within the requirements: if it's enabled,
+   * and if the @media is allowed. This method also walks through the stylesheet
+   * cssRules to find @imported rules, to cache the stylesheets of those rules
+   * as well. In addition, the @keyframes rules in the stylesheet are cached.
+   *
+   * @private
+   * @param {CSSStyleSheet} domSheet the CSSStyleSheet object to cache.
+   */
+  _cacheSheet: function (domSheet) {
+    if (domSheet.disabled) {
+      return;
+    }
+
+    // Only work with stylesheets that have their media allowed.
+    if (!this.mediaMatches(domSheet)) {
+      return;
+    }
+
+    // Cache the sheet.
+    let cssSheet = this.getSheet(domSheet, this._sheetIndex++);
+    if (cssSheet._passId != this._passId) {
+      cssSheet._passId = this._passId;
+
+      // Find import and keyframes rules.
+      for (let aDomRule of domSheet.cssRules) {
+        if (aDomRule.type == CSSRule.IMPORT_RULE &&
+            aDomRule.styleSheet &&
+            this.mediaMatches(aDomRule)) {
+          this._cacheSheet(aDomRule.styleSheet);
+        } else if (aDomRule.type == CSSRule.KEYFRAMES_RULE) {
+          this._keyframesRules.push(aDomRule);
+        }
+      }
+    }
+  },
+
+  /**
+   * Retrieve the list of stylesheets in the document.
+   *
+   * @return {array} the list of stylesheets in the document.
+   */
+  get sheets() {
+    if (!this._sheetsCached) {
+      this._cacheSheets();
+    }
+
+    let sheets = [];
+    this.forEachSheet(function (sheet) {
+      if (sheet.contentSheet) {
+        sheets.push(sheet);
+      }
+    }, this);
+
+    return sheets;
+  },
+
+  /**
+   * Retrieve the list of keyframes rules in the document.
+   *
+   * @ return {array} the list of keyframes rules in the document.
+   */
+  get keyframesRules() {
+    if (!this._sheetsCached) {
+      this._cacheSheets();
+    }
+    return this._keyframesRules;
+  },
+
+  /**
+   * Retrieve a CssSheet object for a given a CSSStyleSheet object. If the
+   * stylesheet is already cached, you get the existing CssSheet object,
+   * otherwise the new CSSStyleSheet object is cached.
+   *
+   * @param {CSSStyleSheet} domSheet the CSSStyleSheet object you want.
+   * @param {number} index the index, within the document, of the stylesheet.
+   *
+   * @return {CssSheet} the CssSheet object for the given CSSStyleSheet object.
+   */
+  getSheet: function (domSheet, index) {
+    let cacheId = "";
+
+    if (domSheet.href) {
+      cacheId = domSheet.href;
+    } else if (domSheet.ownerNode && domSheet.ownerNode.ownerDocument) {
+      cacheId = domSheet.ownerNode.ownerDocument.location;
+    }
+
+    let sheet = null;
+    let sheetFound = false;
+
+    if (cacheId in this._sheets) {
+      for (let i = 0, numSheets = this._sheets[cacheId].length;
+           i < numSheets;
+           i++) {
+        sheet = this._sheets[cacheId][i];
+        if (sheet.domSheet === domSheet) {
+          if (index != -1) {
+            sheet.index = index;
+          }
+          sheetFound = true;
+          break;
+        }
+      }
+    }
+
+    if (!sheetFound) {
+      if (!(cacheId in this._sheets)) {
+        this._sheets[cacheId] = [];
+      }
+
+      sheet = new CssSheet(this, domSheet, index);
+      if (sheet.sheetAllowed && sheet.contentSheet) {
+        this._ruleCount += sheet.ruleCount;
+      }
+
+      this._sheets[cacheId].push(sheet);
+    }
+
+    return sheet;
+  },
+
+  /**
+   * Process each cached stylesheet in the document using your callback.
+   *
+   * @param {function} callback the function you want executed for each of the
+   * CssSheet objects cached.
+   * @param {object} scope the scope you want for the callback function. scope
+   * will be the this object when callback executes.
+   */
+  forEachSheet: function (callback, scope) {
+    for (let cacheId in this._sheets) {
+      let sheets = this._sheets[cacheId];
+      for (let i = 0; i < sheets.length; i++) {
+        // We take this as an opportunity to clean dead sheets
+        try {
+          let sheet = sheets[i];
+          // If accessing domSheet raises an exception, then the style
+          // sheet is a dead object.
+          sheet.domSheet;
+          callback.call(scope, sheet, i, sheets);
+        } catch (e) {
+          sheets.splice(i, 1);
+          i--;
+        }
+      }
+    }
+  },
+
+  /**
+
+  /**
+   * Get the number nsIDOMCSSRule objects in the document, counted from all of
+   * the stylesheets. System sheets are excluded. If a filter is active, this
+   * tells only the number of nsIDOMCSSRule objects inside the selected
+   * CSSStyleSheet.
+   *
+   * WARNING: This only provides an estimate of the rule count, and the results
+   * could change at a later date. Todo remove this
+   *
+   * @return {number} the number of nsIDOMCSSRule (all rules).
+   */
+  get ruleCount() {
+    if (!this._sheetsCached) {
+      this._cacheSheets();
+    }
+
+    return this._ruleCount;
+  },
+
+  /**
+   * Process the CssSelector objects that match the highlighted element and its
+   * parent elements. scope.callback() is executed for each CssSelector
+   * object, being passed the CssSelector object and the match status.
+   *
+   * This method also includes all of the element.style properties, for each
+   * highlighted element parent and for the highlighted element itself.
+   *
+   * Note that the matched selectors are cached, such that next time your
+   * callback is invoked for the cached list of CssSelector objects.
+   *
+   * @param {function} callback the function you want to execute for each of
+   * the matched selectors.
+   * @param {object} scope the scope you want for the callback function. scope
+   * will be the this object when callback executes.
+   */
+  processMatchedSelectors: function (callback, scope) {
+    if (this._matchedSelectors) {
+      if (callback) {
+        this._passId++;
+        this._matchedSelectors.forEach(function (value) {
+          callback.call(scope, value[0], value[1]);
+          value[0].cssRule._passId = this._passId;
+        }, this);
+      }
+      return;
+    }
+
+    if (!this._matchedRules) {
+      this._buildMatchedRules();
+    }
+
+    this._matchedSelectors = [];
+    this._passId++;
+
+    for (let i = 0; i < this._matchedRules.length; i++) {
+      let rule = this._matchedRules[i][0];
+      let status = this._matchedRules[i][1];
+
+      rule.selectors.forEach(function (selector) {
+        if (selector._matchId !== this._matchId &&
+           (selector.elementStyle ||
+            this.selectorMatchesElement(rule.domRule,
+                                        selector.selectorIndex))) {
+          selector._matchId = this._matchId;
+          this._matchedSelectors.push([ selector, status ]);
+          if (callback) {
+            callback.call(scope, selector, status);
+          }
+        }
+      }, this);
+
+      rule._passId = this._passId;
+    }
+  },
+
+  /**
+   * Check if the given selector matches the highlighted element or any of its
+   * parents.
+   *
+   * @private
+   * @param {DOMRule} domRule
+   *        The DOM Rule containing the selector.
+   * @param {Number} idx
+   *        The index of the selector within the DOMRule.
+   * @return {boolean}
+   *         true if the given selector matches the highlighted element or any
+   *         of its parents, otherwise false is returned.
+   */
+  selectorMatchesElement: function (domRule, idx) {
+    let element = this.viewedElement;
+    do {
+      if (domUtils.selectorMatchesElement(element, domRule, idx)) {
+        return true;
+      }
+    } while ((element = element.parentNode) &&
+             element.nodeType === nodeConstants.ELEMENT_NODE);
+
+    return false;
+  },
+
+  /**
+   * Check if the highlighted element or it's parents have matched selectors.
+   *
+   * @param {array} aProperties The list of properties you want to check if they
+   * have matched selectors or not.
+   * @return {object} An object that tells for each property if it has matched
+   * selectors or not. Object keys are property names and values are booleans.
+   */
+  hasMatchedSelectors: function (properties) {
+    if (!this._matchedRules) {
+      this._buildMatchedRules();
+    }
+
+    let result = {};
+
+    this._matchedRules.some(function (value) {
+      let rule = value[0];
+      let status = value[1];
+      properties = properties.filter((property) => {
+        // We just need to find if a rule has this property while it matches
+        // the viewedElement (or its parents).
+        if (rule.getPropertyValue(property) &&
+            (status == STATUS.MATCHED ||
+             (status == STATUS.PARENT_MATCH &&
+              this._isInherited(property)))) {
+          result[property] = true;
+          return false;
+        }
+        // Keep the property for the next rule.
+        return true;
+      });
+      return properties.length == 0;
+    }, this);
+
+    return result;
+  },
+
+  /**
+   * Build the array of matched rules for the currently highlighted element.
+   * The array will hold rules that match the viewedElement and its parents.
+   *
+   * @private
+   */
+  _buildMatchedRules: function () {
+    let domRules;
+    let element = this.viewedElement;
+    let filter = this.sourceFilter;
+    let sheetIndex = 0;
+
+    this._matchId++;
+    this._passId++;
+    this._matchedRules = [];
+
+    if (!element) {
+      return;
+    }
+
+    do {
+      let status = this.viewedElement === element ?
+                   STATUS.MATCHED : STATUS.PARENT_MATCH;
+
+      try {
+        // Handle finding rules on pseudo by reading style rules
+        // on the parent node with proper pseudo arg to getCSSStyleRules.
+        let {bindingElement, pseudo} =
+            CssLogic.getBindingElementAndPseudo(element);
+        domRules = domUtils.getCSSStyleRules(bindingElement, pseudo);
+      } catch (ex) {
+        console.log("CL__buildMatchedRules error: " + ex);
+        continue;
+      }
+
+      // getCSSStyleRules can return null with a shadow DOM element.
+      let numDomRules = domRules ? domRules.Count() : 0;
+      for (let i = 0; i < numDomRules; i++) {
+        let domRule = domRules.GetElementAt(i);
+        if (domRule.type !== CSSRule.STYLE_RULE) {
+          continue;
+        }
+
+        let sheet = this.getSheet(domRule.parentStyleSheet, -1);
+        if (sheet._passId !== this._passId) {
+          sheet.index = sheetIndex++;
+          sheet._passId = this._passId;
+        }
+
+        if (filter === FILTER.USER && !sheet.contentSheet) {
+          continue;
+        }
+
+        let rule = sheet.getRule(domRule);
+        if (rule._passId === this._passId) {
+          continue;
+        }
+
+        rule._matchId = this._matchId;
+        rule._passId = this._passId;
+        this._matchedRules.push([rule, status]);
+      }
+
+      // Add element.style information.
+      if (element.style && element.style.length > 0) {
+        let rule = new CssRule(null, { style: element.style }, element);
+        rule._matchId = this._matchId;
+        rule._passId = this._passId;
+        this._matchedRules.push([rule, status]);
+      }
+    } while ((element = element.parentNode) &&
+              element.nodeType === nodeConstants.ELEMENT_NODE);
+  },
+
+  /**
+   * Tells if the given DOM CSS object matches the current view media.
+   *
+   * @param {object} domObject The DOM CSS object to check.
+   * @return {boolean} True if the DOM CSS object matches the current view
+   * media, or false otherwise.
+   */
+  mediaMatches: function (domObject) {
+    let mediaText = domObject.media.mediaText;
+    return !mediaText ||
+      this.viewedDocument.defaultView.matchMedia(mediaText).matches;
+  },
+};
+
+/**
+ * If the element has an id, return '#id'. Otherwise return 'tagname[n]' where
+ * n is the index of this element in its siblings.
+ * <p>A technically more 'correct' output from the no-id case might be:
+ * 'tagname:nth-of-type(n)' however this is unlikely to be more understood
+ * and it is longer.
+ *
+ * @param {nsIDOMElement} element the element for which you want the short name.
+ * @return {string} the string to be displayed for element.
+ */
+CssLogic.getShortName = function (element) {
+  if (!element) {
+    return "null";
+  }
+  if (element.id) {
+    return "#" + element.id;
+  }
+  let priorSiblings = 0;
+  let temp = element;
+  while ((temp = temp.previousElementSibling)) {
+    priorSiblings++;
+  }
+  return element.tagName + "[" + priorSiblings + "]";
+};
+
+/**
+ * Get a string list of selectors for a given DOMRule.
+ *
+ * @param {DOMRule} domRule
+ *        The DOMRule to parse.
+ * @return {Array}
+ *         An array of string selectors.
+ */
+CssLogic.getSelectors = function (domRule) {
+  let selectors = [];
+
+  let len = domUtils.getSelectorCount(domRule);
+  for (let i = 0; i < len; i++) {
+    let text = domUtils.getSelectorText(domRule, i);
+    selectors.push(text);
+  }
+  return selectors;
+};
+
+/**
+ * Given a node, check to see if it is a ::before or ::after element.
+ * If so, return the node that is accessible from within the document
+ * (the parent of the anonymous node), along with which pseudo element
+ * it was.  Otherwise, return the node itself.
+ *
+ * @returns {Object}
+ *            - {DOMNode} node The non-anonymous node
+ *            - {string} pseudo One of ':before', ':after', or null.
+ */
+CssLogic.getBindingElementAndPseudo = function (node) {
+  let bindingElement = node;
+  let pseudo = null;
+  if (node.nodeName == "_moz_generated_content_before") {
+    bindingElement = node.parentNode;
+    pseudo = ":before";
+  } else if (node.nodeName == "_moz_generated_content_after") {
+    bindingElement = node.parentNode;
+    pseudo = ":after";
+  }
+  return {
+    bindingElement: bindingElement,
+    pseudo: pseudo
+  };
+};
+
+/**
+ * Get the computed style on a node.  Automatically handles reading
+ * computed styles on a ::before/::after element by reading on the
+ * parent node with the proper pseudo argument.
+ *
+ * @param {Node}
+ * @returns {CSSStyleDeclaration}
+ */
+CssLogic.getComputedStyle = function (node) {
+  if (!node ||
+      Cu.isDeadWrapper(node) ||
+      node.nodeType !== nodeConstants.ELEMENT_NODE ||
+      !node.ownerDocument ||
+      !node.ownerDocument.defaultView) {
+    return null;
+  }
+
+  let {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(node);
+  return node.ownerDocument.defaultView.getComputedStyle(bindingElement,
+                                                         pseudo);
+};
+
+/**
+ * Get a source for a stylesheet, taking into account embedded stylesheets
+ * for which we need to use document.defaultView.location.href rather than
+ * sheet.href
+ *
+ * @param {CSSStyleSheet} sheet the DOM object for the style sheet.
+ * @return {string} the address of the stylesheet.
+ */
+CssLogic.href = function (sheet) {
+  let href = sheet.href;
+  if (!href) {
+    href = sheet.ownerNode.ownerDocument.location;
+  }
+
+  return href;
+};
+
+/**
+ * Find the position of [element] in [nodeList].
+ * @returns an index of the match, or -1 if there is no match
+ */
+function positionInNodeList(element, nodeList) {
+  for (let i = 0; i < nodeList.length; i++) {
+    if (element === nodeList[i]) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+/**
+ * Find a unique CSS selector for a given element
+ * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
+ * and ele.ownerDocument.querySelectorAll(reply).length === 1
+ */
+CssLogic.findCssSelector = function (ele) {
+  ele = getRootBindingParent(ele);
+  let document = ele.ownerDocument;
+  if (!document || !document.contains(ele)) {
+    throw new Error("findCssSelector received element not inside document");
+  }
+
+  // document.querySelectorAll("#id") returns multiple if elements share an ID
+  if (ele.id &&
+      document.querySelectorAll("#" + CSS.escape(ele.id)).length === 1) {
+    return "#" + CSS.escape(ele.id);
+  }
+
+  // Inherently unique by tag name
+  let tagName = ele.localName;
+  if (tagName === "html") {
+    return "html";
+  }
+  if (tagName === "head") {
+    return "head";
+  }
+  if (tagName === "body") {
+    return "body";
+  }
+
+  // We might be able to find a unique class name
+  let selector, index, matches;
+  if (ele.classList.length > 0) {
+    for (let i = 0; i < ele.classList.length; i++) {
+      // Is this className unique by itself?
+      selector = "." + CSS.escape(ele.classList.item(i));
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+      // Maybe it's unique with a tag name?
+      selector = tagName + selector;
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+      // Maybe it's unique using a tag name and nth-child
+      index = positionInNodeList(ele, ele.parentNode.children) + 1;
+      selector = selector + ":nth-child(" + index + ")";
+      matches = document.querySelectorAll(selector);
+      if (matches.length === 1) {
+        return selector;
+      }
+    }
+  }
+
+  // Not unique enough yet.  As long as it's not a child of the document,
+  // continue recursing up until it is unique enough.
+  if (ele.parentNode !== document) {
+    index = positionInNodeList(ele, ele.parentNode.children) + 1;
+    selector = CssLogic.findCssSelector(ele.parentNode) + " > " +
+      tagName + ":nth-child(" + index + ")";
+  }
+
+  return selector;
+};
+
+/**
+ * A safe way to access cached bits of information about a stylesheet.
+ *
+ * @constructor
+ * @param {CssLogic} cssLogic pointer to the CssLogic instance working with
+ * this CssSheet object.
+ * @param {CSSStyleSheet} domSheet reference to a DOM CSSStyleSheet object.
+ * @param {number} index tells the index/position of the stylesheet within the
+ * main document.
+ */
+function CssSheet(cssLogic, domSheet, index) {
+  this._cssLogic = cssLogic;
+  this.domSheet = domSheet;
+  this.index = this.contentSheet ? index : -100 * index;
+
+  // Cache of the sheets href. Cached by the getter.
+  this._href = null;
+  // Short version of href for use in select boxes etc. Cached by getter.
+  this._shortSource = null;
+
+  // null for uncached.
+  this._sheetAllowed = null;
+
+  // Cached CssRules from the given stylesheet.
+  this._rules = {};
+
+  this._ruleCount = -1;
+}
+
+CssSheet.prototype = {
+  _passId: null,
+  _contentSheet: null,
+
+  /**
+   * Tells if the stylesheet is provided by the browser or not.
+   *
+   * @return {boolean} false if this is a browser-provided stylesheet, or true
+   * otherwise.
+   */
+  get contentSheet() {
+    if (this._contentSheet === null) {
+      this._contentSheet = isContentStylesheet(this.domSheet);
+    }
+    return this._contentSheet;
+  },
+
+  /**
+   * Tells if the stylesheet is disabled or not.
+   * @return {boolean} true if this stylesheet is disabled, or false otherwise.
+   */
+  get disabled() {
+    return this.domSheet.disabled;
+  },
+
+  /**
+   * Get a source for a stylesheet, using CssLogic.href
+   *
+   * @return {string} the address of the stylesheet.
+   */
+  get href() {
+    if (this._href) {
+      return this._href;
+    }
+
+    this._href = CssLogic.href(this.domSheet);
+    return this._href;
+  },
+
+  /**
+   * Create a shorthand version of the href of a stylesheet.
+   *
+   * @return {string} the shorthand source of the stylesheet.
+   */
+  get shortSource() {
+    if (this._shortSource) {
+      return this._shortSource;
+    }
+
+    this._shortSource = shortSource(this.domSheet);
+    return this._shortSource;
+  },
+
+  /**
+   * Tells if the sheet is allowed or not by the current CssLogic.sourceFilter.
+   *
+   * @return {boolean} true if the stylesheet is allowed by the sourceFilter, or
+   * false otherwise.
+   */
+  get sheetAllowed() {
+    if (this._sheetAllowed !== null) {
+      return this._sheetAllowed;
+    }
+
+    this._sheetAllowed = true;
+
+    let filter = this._cssLogic.sourceFilter;
+    if (filter === FILTER.USER && !this.contentSheet) {
+      this._sheetAllowed = false;
+    }
+    if (filter !== FILTER.USER && filter !== FILTER.UA) {
+      this._sheetAllowed = (filter === this.href);
+    }
+
+    return this._sheetAllowed;
+  },
+
+  /**
+   * Retrieve the number of rules in this stylesheet.
+   *
+   * @return {number} the number of nsIDOMCSSRule objects in this stylesheet.
+   */
+  get ruleCount() {
+    return this._ruleCount > -1 ?
+      this._ruleCount :
+      this.domSheet.cssRules.length;
+  },
+
+  /**
+   * Retrieve a CssRule object for the given CSSStyleRule. The CssRule object is
+   * cached, such that subsequent retrievals return the same CssRule object for
+   * the same CSSStyleRule object.
+   *
+   * @param {CSSStyleRule} aDomRule the CSSStyleRule object for which you want a
+   * CssRule object.
+   * @return {CssRule} the cached CssRule object for the given CSSStyleRule
+   * object.
+   */
+  getRule: function (domRule) {
+    let cacheId = domRule.type + domRule.selectorText;
+
+    let rule = null;
+    let ruleFound = false;
+
+    if (cacheId in this._rules) {
+      for (let i = 0, rulesLen = this._rules[cacheId].length;
+           i < rulesLen;
+           i++) {
+        rule = this._rules[cacheId][i];
+        if (rule.domRule === domRule) {
+          ruleFound = true;
+          break;
+        }
+      }
+    }
+
+    if (!ruleFound) {
+      if (!(cacheId in this._rules)) {
+        this._rules[cacheId] = [];
+      }
+
+      rule = new CssRule(this, domRule);
+      this._rules[cacheId].push(rule);
+    }
+
+    return rule;
+  },
+
+  toString: function () {
+    return "CssSheet[" + this.shortSource + "]";
+  }
+};
+
+/**
+ * Information about a single CSSStyleRule.
+ *
+ * @param {CSSSheet|null} cssSheet the CssSheet object of the stylesheet that
+ * holds the CSSStyleRule. If the rule comes from element.style, set this
+ * argument to null.
+ * @param {CSSStyleRule|object} domRule the DOM CSSStyleRule for which you want
+ * to cache data. If the rule comes from element.style, then provide
+ * an object of the form: {style: element.style}.
+ * @param {Element} [element] If the rule comes from element.style, then this
+ * argument must point to the element.
+ * @constructor
+ */
+function CssRule(cssSheet, domRule, element) {
+  this._cssSheet = cssSheet;
+  this.domRule = domRule;
+
+  let parentRule = domRule.parentRule;
+  if (parentRule && parentRule.type == CSSRule.MEDIA_RULE) {
+    this.mediaText = parentRule.media.mediaText;
+  }
+
+  if (this._cssSheet) {
+    // parse domRule.selectorText on call to this.selectors
+    this._selectors = null;
+    this.line = domUtils.getRuleLine(this.domRule);
+    this.source = this._cssSheet.shortSource + ":" + this.line;
+    if (this.mediaText) {
+      this.source += " @media " + this.mediaText;
+    }
+    this.href = this._cssSheet.href;
+    this.contentRule = this._cssSheet.contentSheet;
+  } else if (element) {
+    this._selectors = [ new CssSelector(this, "@element.style", 0) ];
+    this.line = -1;
+    this.source = l10n("rule.sourceElement");
+    this.href = "#";
+    this.contentRule = true;
+    this.sourceElement = element;
+  }
+}
+
+CssRule.prototype = {
+  _passId: null,
+
+  mediaText: "",
+
+  get isMediaRule() {
+    return !!this.mediaText;
+  },
+
+  /**
+   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
+   *
+   * @return {boolean} true if the parent stylesheet is allowed by the current
+   * sourceFilter, or false otherwise.
+   */
+  get sheetAllowed() {
+    return this._cssSheet ? this._cssSheet.sheetAllowed : true;
+  },
+
+  /**
+   * Retrieve the parent stylesheet index/position in the viewed document.
+   *
+   * @return {number} the parent stylesheet index/position in the viewed
+   * document.
+   */
+  get sheetIndex() {
+    return this._cssSheet ? this._cssSheet.index : 0;
+  },
+
+  /**
+   * Retrieve the style property value from the current CSSStyleRule.
+   *
+   * @param {string} property the CSS property name for which you want the
+   * value.
+   * @return {string} the property value.
+   */
+  getPropertyValue: function (property) {
+    return this.domRule.style.getPropertyValue(property);
+  },
+
+  /**
+   * Retrieve the style property priority from the current CSSStyleRule.
+   *
+   * @param {string} property the CSS property name for which you want the
+   * priority.
+   * @return {string} the property priority.
+   */
+  getPropertyPriority: function (property) {
+    return this.domRule.style.getPropertyPriority(property);
+  },
+
+  /**
+   * Retrieve the list of CssSelector objects for each of the parsed selectors
+   * of the current CSSStyleRule.
+   *
+   * @return {array} the array hold the CssSelector objects.
+   */
+  get selectors() {
+    if (this._selectors) {
+      return this._selectors;
+    }
+
+    // Parse the CSSStyleRule.selectorText string.
+    this._selectors = [];
+
+    if (!this.domRule.selectorText) {
+      return this._selectors;
+    }
+
+    let selectors = CssLogic.getSelectors(this.domRule);
+
+    for (let i = 0, len = selectors.length; i < len; i++) {
+      this._selectors.push(new CssSelector(this, selectors[i], i));
+    }
+
+    return this._selectors;
+  },
+
+  toString: function () {
+    return "[CssRule " + this.domRule.selectorText + "]";
+  },
+};
+
+/**
+ * The CSS selector class allows us to document the ranking of various CSS
+ * selectors.
+ *
+ * @constructor
+ * @param {CssRule} cssRule the CssRule instance from where the selector comes.
+ * @param {string} selector The selector that we wish to investigate.
+ * @param {Number} index The index of the selector within it's rule.
+ */
+function CssSelector(cssRule, selector, index) {
+  this.cssRule = cssRule;
+  this.text = selector;
+  this.elementStyle = this.text == "@element.style";
+  this._specificity = null;
+  this.selectorIndex = index;
+}
+
+exports.CssSelector = CssSelector;
+
+CssSelector.prototype = {
+  _matchId: null,
+
+  /**
+   * Retrieve the CssSelector source, which is the source of the CssSheet owning
+   * the selector.
+   *
+   * @return {string} the selector source.
+   */
+  get source() {
+    return this.cssRule.source;
+  },
+
+  /**
+   * Retrieve the CssSelector source element, which is the source of the CssRule
+   * owning the selector. This is only available when the CssSelector comes from
+   * an element.style.
+   *
+   * @return {string} the source element selector.
+   */
+  get sourceElement() {
+    return this.cssRule.sourceElement;
+  },
+
+  /**
+   * Retrieve the address of the CssSelector. This points to the address of the
+   * CssSheet owning this selector.
+   *
+   * @return {string} the address of the CssSelector.
+   */
+  get href() {
+    return this.cssRule.href;
+  },
+
+  /**
+   * Check if the selector comes from a browser-provided stylesheet.
+   *
+   * @return {boolean} true if the selector comes from a content-provided
+   * stylesheet, or false otherwise.
+   */
+  get contentRule() {
+    return this.cssRule.contentRule;
+  },
+
+  /**
+   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
+   *
+   * @return {boolean} true if the parent stylesheet is allowed by the current
+   * sourceFilter, or false otherwise.
+   */
+  get sheetAllowed() {
+    return this.cssRule.sheetAllowed;
+  },
+
+  /**
+   * Retrieve the parent stylesheet index/position in the viewed document.
+   *
+   * @return {number} the parent stylesheet index/position in the viewed
+   * document.
+   */
+  get sheetIndex() {
+    return this.cssRule.sheetIndex;
+  },
+
+  /**
+   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
+   *
+   * @return {number} the line of the parent CSSStyleRule in the parent
+   * stylesheet.
+   */
+  get ruleLine() {
+    return this.cssRule.line;
+  },
+
+  /**
+   * Retrieve specificity information for the current selector.
+   *
+   * @see http://www.w3.org/TR/css3-selectors/#specificity
+   * @see http://www.w3.org/TR/CSS2/selector.html
+   *
+   * @return {Number} The selector's specificity.
+   */
+  get specificity() {
+    if (this.elementStyle) {
+      // We can't ask specificity from DOMUtils as element styles don't provide
+      // CSSStyleRule interface DOMUtils expect. However, specificity of element
+      // style is constant, 1,0,0,0 or 0x01000000, just return the constant
+      // directly. @see http://www.w3.org/TR/CSS2/cascade.html#specificity
+      return 0x01000000;
+    }
+
+    if (this._specificity) {
+      return this._specificity;
+    }
+
+    this._specificity = domUtils.getSpecificity(this.cssRule.domRule,
+                                                this.selectorIndex);
+
+    return this._specificity;
+  },
+
+  toString: function () {
+    return this.text;
+  },
+};
+
+/**
+ * A cache of information about the matched rules, selectors and values attached
+ * to a CSS property, for the highlighted element.
+ *
+ * The heart of the CssPropertyInfo object is the _findMatchedSelectors()
+ * method. This are invoked when the PropertyView tries to access the
+ * .matchedSelectors array.
+ * Results are cached, for later reuse.
+ *
+ * @param {CssLogic} cssLogic Reference to the parent CssLogic instance
+ * @param {string} property The CSS property we are gathering information for
+ * @param {function} isInherited A function that determines if the CSS property
+ *                   is inherited.
+ * @constructor
+ */
+function CssPropertyInfo(cssLogic, property, isInherited) {
+  this._cssLogic = cssLogic;
+  this.property = property;
+  this._value = "";
+  this._isInherited = isInherited;
+
+  // An array holding CssSelectorInfo objects for each of the matched selectors
+  // that are inside a CSS rule. Only rules that hold the this.property are
+  // counted. This includes rules that come from filtered stylesheets (those
+  // that have sheetAllowed = false).
+  this._matchedSelectors = null;
+}
+
+CssPropertyInfo.prototype = {
+  /**
+   * Retrieve the computed style value for the current property, for the
+   * highlighted element.
+   *
+   * @return {string} the computed style value for the current property, for the
+   * highlighted element.
+   */
+  get value() {
+    if (!this._value && this._cssLogic.computedStyle) {
+      try {
+        this._value =
+          this._cssLogic.computedStyle.getPropertyValue(this.property);
+      } catch (ex) {
+        console.log("Error reading computed style for " + this.property);
+        console.log(ex);
+      }
+    }
+    return this._value;
+  },
+
+  /**
+   * Retrieve the array holding CssSelectorInfo objects for each of the matched
+   * selectors, from each of the matched rules. Only selectors coming from
+   * allowed stylesheets are included in the array.
+   *
+   * @return {array} the list of CssSelectorInfo objects of selectors that match
+   * the highlighted element and its parents.
+   */
+  get matchedSelectors() {
+    if (!this._matchedSelectors) {
+      this._findMatchedSelectors();
+    } else if (this.needRefilter) {
+      this._refilterSelectors();
+    }
+
+    return this._matchedSelectors;
+  },
+
+  /**
+   * Find the selectors that match the highlighted element and its parents.
+   * Uses CssLogic.processMatchedSelectors() to find the matched selectors,
+   * passing in a reference to CssPropertyInfo._processMatchedSelector() to
+   * create CssSelectorInfo objects, which we then sort
+   * @private
+   */
+  _findMatchedSelectors: function () {
+    this._matchedSelectors = [];
+    this.needRefilter = false;
+
+    this._cssLogic.processMatchedSelectors(this._processMatchedSelector, this);
+
+    // Sort the selectors by how well they match the given element.
+    this._matchedSelectors.sort(function (selectorInfo1, selectorInfo2) {
+      if (selectorInfo1.status > selectorInfo2.status) {
+        return -1;
+      } else if (selectorInfo2.status > selectorInfo1.status) {
+        return 1;
+      }
+      return selectorInfo1.compareTo(selectorInfo2);
+    });
+
+    // Now we know which of the matches is best, we can mark it BEST_MATCH.
+    if (this._matchedSelectors.length > 0 &&
+        this._matchedSelectors[0].status > STATUS.UNMATCHED) {
+      this._matchedSelectors[0].status = STATUS.BEST;
+    }
+  },
+
+  /**
+   * Process a matched CssSelector object.
+   *
+   * @private
+   * @param {CssSelector} selector the matched CssSelector object.
+   * @param {STATUS} status the CssSelector match status.
+   */
+  _processMatchedSelector: function (selector, status) {
+    let cssRule = selector.cssRule;
+    let value = cssRule.getPropertyValue(this.property);
+    if (value &&
+        (status == STATUS.MATCHED ||
+         (status == STATUS.PARENT_MATCH &&
+          this._isInherited(this.property)))) {
+      let selectorInfo = new CssSelectorInfo(selector, this.property, value,
+          status);
+      this._matchedSelectors.push(selectorInfo);
+    }
+  },
+
+  /**
+   * Refilter the matched selectors array when the CssLogic.sourceFilter
+   * changes. This allows for quick filter changes.
+   * @private
+   */
+  _refilterSelectors: function () {
+    let passId = ++this._cssLogic._passId;
+    let ruleCount = 0;
+
+    let iterator = function (selectorInfo) {
+      let cssRule = selectorInfo.selector.cssRule;
+      if (cssRule._passId != passId) {
+        if (cssRule.sheetAllowed) {
+          ruleCount++;
+        }
+        cssRule._passId = passId;
+      }
+    };
+
+    if (this._matchedSelectors) {
+      this._matchedSelectors.forEach(iterator);
+    }
+
+    this.needRefilter = false;
+  },
+
+  toString: function () {
+    return "CssPropertyInfo[" + this.property + "]";
+  },
+};
+
+/**
+ * A class that holds information about a given CssSelector object.
+ *
+ * Instances of this class are given to CssHtmlTree in the array of matched
+ * selectors. Each such object represents a displayable row in the PropertyView
+ * objects. The information given by this object blends data coming from the
+ * CssSheet, CssRule and from the CssSelector that own this object.
+ *
+ * @param {CssSelector} selector The CssSelector object for which to
+ *        present information.
+ * @param {string} property The property for which information should
+ *        be retrieved.
+ * @param {string} value The property value from the CssRule that owns
+ *        the selector.
+ * @param {STATUS} status The selector match status.
+ * @constructor
+ */
+function CssSelectorInfo(selector, property, value, status) {
+  this.selector = selector;
+  this.property = property;
+  this.status = status;
+  this.value = value;
+  let priority = this.selector.cssRule.getPropertyPriority(this.property);
+  this.important = (priority === "important");
+}
+
+CssSelectorInfo.prototype = {
+  /**
+   * Retrieve the CssSelector source, which is the source of the CssSheet owning
+   * the selector.
+   *
+   * @return {string} the selector source.
+   */
+  get source() {
+    return this.selector.source;
+  },
+
+  /**
+   * Retrieve the CssSelector source element, which is the source of the CssRule
+   * owning the selector. This is only available when the CssSelector comes from
+   * an element.style.
+   *
+   * @return {string} the source element selector.
+   */
+  get sourceElement() {
+    return this.selector.sourceElement;
+  },
+
+  /**
+   * Retrieve the address of the CssSelector. This points to the address of the
+   * CssSheet owning this selector.
+   *
+   * @return {string} the address of the CssSelector.
+   */
+  get href() {
+    return this.selector.href;
+  },
+
+  /**
+   * Check if the CssSelector comes from element.style or not.
+   *
+   * @return {boolean} true if the CssSelector comes from element.style, or
+   * false otherwise.
+   */
+  get elementStyle() {
+    return this.selector.elementStyle;
+  },
+
+  /**
+   * Retrieve specificity information for the current selector.
+   *
+   * @return {object} an object holding specificity information for the current
+   * selector.
+   */
+  get specificity() {
+    return this.selector.specificity;
+  },
+
+  /**
+   * Retrieve the parent stylesheet index/position in the viewed document.
+   *
+   * @return {number} the parent stylesheet index/position in the viewed
+   * document.
+   */
+  get sheetIndex() {
+    return this.selector.sheetIndex;
+  },
+
+  /**
+   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
+   *
+   * @return {boolean} true if the parent stylesheet is allowed by the current
+   * sourceFilter, or false otherwise.
+   */
+  get sheetAllowed() {
+    return this.selector.sheetAllowed;
+  },
+
+  /**
+   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
+   *
+   * @return {number} the line of the parent CSSStyleRule in the parent
+   * stylesheet.
+   */
+  get ruleLine() {
+    return this.selector.ruleLine;
+  },
+
+  /**
+   * Check if the selector comes from a browser-provided stylesheet.
+   *
+   * @return {boolean} true if the selector comes from a browser-provided
+   * stylesheet, or false otherwise.
+   */
+  get contentRule() {
+    return this.selector.contentRule;
+  },
+
+  /**
+   * Compare the current CssSelectorInfo instance to another instance, based on
+   * specificity information.
+   *
+   * @param {CssSelectorInfo} that The instance to compare ourselves against.
+   * @return number -1, 0, 1 depending on how that compares with this.
+   */
+  compareTo: function (that) {
+    if (!this.contentRule && that.contentRule) {
+      return 1;
+    }
+    if (this.contentRule && !that.contentRule) {
+      return -1;
+    }
+
+    if (this.elementStyle && !that.elementStyle) {
+      if (!this.important && that.important) {
+        return 1;
+      }
+      return -1;
+    }
+
+    if (!this.elementStyle && that.elementStyle) {
+      if (this.important && !that.important) {
+        return -1;
+      }
+      return 1;
+    }
+
+    if (this.important && !that.important) {
+      return -1;
+    }
+    if (that.important && !this.important) {
+      return 1;
+    }
+
+    if (this.specificity > that.specificity) {
+      return -1;
+    }
+    if (that.specificity > this.specificity) {
+      return 1;
+    }
+
+    if (this.sheetIndex > that.sheetIndex) {
+      return -1;
+    }
+    if (that.sheetIndex > this.sheetIndex) {
+      return 1;
+    }
+
+    if (this.ruleLine > that.ruleLine) {
+      return -1;
+    }
+    if (that.ruleLine > this.ruleLine) {
+      return 1;
+    }
+
+    return 0;
+  },
+
+  toString: function () {
+    return this.selector + " -> " + this.value;
+  },
+};
+
+DevToolsUtils.defineLazyGetter(this, "domUtils", function () {
+  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+});
--- a/devtools/server/moz.build
+++ b/devtools/server/moz.build
@@ -27,13 +27,14 @@ SOURCES += [
 ]
 
 FINAL_LIBRARY = 'xul'
 
 DevToolsModules(
     'child.js',
     'content-globals.js',
     'content-server.jsm',
+    'css-logic.js',
     'main.js',
     'primitive.js',
     'service-worker-child.js',
     'worker.js'
 )
--- a/devtools/server/tests/mochitest/test_css-logic-media-queries.html
+++ b/devtools/server/tests/mochitest/test_css-logic-media-queries.html
@@ -28,17 +28,17 @@ Test that css-logic handles media-querie
 
   window.onload = function() {
     var { classes: Cc, utils: Cu, interfaces: Ci } = Components;
     const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
       .getService(Ci.inIDOMUtils);
 
     var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
     var Services = require("Services");
-    const {CssLogic} = require("devtools/shared/inspector/css-logic");
+    const {CssLogic} = require("devtools/server/css-logic");
 
     SimpleTest.waitForExplicitFinish();
 
     let div = document.querySelector("div");
     let cssLogic = new CssLogic(DOMUtils.isInheritedProperty);
     cssLogic.highlight(div);
     cssLogic.processMatchedSelectors();
 
--- a/devtools/server/tests/mochitest/test_css-logic-specificity.html
+++ b/devtools/server/tests/mochitest/test_css-logic-specificity.html
@@ -10,17 +10,17 @@ Test that css-logic calculates CSS speci
 </head>
 <body style="background:blue;">
   <script type="application/javascript;version=1.8">
 
   window.onload = function() {
     var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
 
     const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-    const {CssLogic, CssSelector} = require("devtools/shared/inspector/css-logic");
+    const {CssLogic, CssSelector} = require("devtools/server/css-logic");
     const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
                        .getService(Ci.inIDOMUtils);
 
     const TEST_DATA = [
       {text: "*", expected: 0},
       {text: "LI", expected: 1},
       {text: "UL LI", expected: 2},
       {text: "UL OL + LI", expected: 3},
--- a/devtools/server/tests/mochitest/test_css-logic.html
+++ b/devtools/server/tests/mochitest/test_css-logic.html
@@ -6,17 +6,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <head>
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const {CssLogic} = require("devtools/server/css-logic");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 addTest(function findAllCssSelectors() {
   var nodes = document.querySelectorAll('*');
--- a/devtools/server/tests/mochitest/test_styles-matched.html
+++ b/devtools/server/tests/mochitest/test_styles-matched.html
@@ -7,17 +7,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 const inspector = require("devtools/server/actors/inspector");
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const CssLogic = require("devtools/shared/inspector/css-logic");
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 }
 
 var gWalker = null;
 var gStyles = null;
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -35,776 +35,83 @@
  * CssLogic uses the standard DOM API, and the Gecko inIDOMUtils API to access
  * styling information in the page, and present this to the user in a way that
  * helps them understand:
  * - why their expectations may not have been fulfilled
  * - how browsers process CSS
  * @constructor
  */
 
-const { Cc, Ci, Cu } = require("chrome");
+const { Cc, Ci } = require("chrome");
 const Services = require("Services");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const { getRootBindingParent } = require("devtools/shared/layout/utils");
-const nodeConstants = require("devtools/shared/dom-node-constants");
 
 // This should be ok because none of the functions that use this should be used
 // on the worker thread, where Cu is not available.
 loader.lazyRequireGetter(this, "CSS", "CSS");
 
 loader.lazyRequireGetter(this, "CSSLexer", "devtools/shared/css-lexer");
 
 /**
- * @param {function} isInherited A function that determines if the CSS property
- *                   is inherited.
- */
-function CssLogic(isInherited) {
-  // The cache of examined CSS properties.
-  this._isInherited = isInherited;
-  this._propertyInfos = {};
-}
-
-exports.CssLogic = CssLogic;
-
-/**
  * Special values for filter, in addition to an href these values can be used
  */
-CssLogic.FILTER = {
+exports.FILTER = {
   // show properties for all user style sheets.
   USER: "user",
   // USER, plus user-agent (i.e. browser) style sheets
   UA: "ua",
 };
 
 /**
- * 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
- */
-CssLogic.MEDIA = {
-  ALL: "all",
-  SCREEN: "screen",
-};
-
-/**
  * Each rule has a status, the bigger the number, the better placed it is to
  * provide styling information.
  *
  * These statuses are localized inside the styleinspector.properties
  * string bundle.
  * @see csshtmltree.js RuleView._cacheStatusNames()
  */
-CssLogic.STATUS = {
+exports.STATUS = {
   BEST: 3,
   MATCHED: 2,
   PARENT_MATCH: 1,
   UNMATCHED: 0,
   UNKNOWN: -1,
 };
 
-CssLogic.prototype = {
-  // Both setup by highlight().
-  viewedElement: null,
-  viewedDocument: null,
-
-  // The cache of the known sheets.
-  _sheets: null,
-
-  // Have the sheets been cached?
-  _sheetsCached: false,
-
-  // The total number of rules, in all stylesheets, after filtering.
-  _ruleCount: 0,
-
-  // The computed styles for the viewedElement.
-  _computedStyle: null,
-
-  // Source filter. Only display properties coming from the given source
-  _sourceFilter: CssLogic.FILTER.USER,
-
-  // Used for tracking unique CssSheet/CssRule/CssSelector objects, in a run of
-  // processMatchedSelectors().
-  _passId: 0,
-
-  // Used for tracking matched CssSelector objects.
-  _matchId: 0,
-
-  _matchedRules: null,
-  _matchedSelectors: null,
-
-  // Cached keyframes rules in all stylesheets
-  _keyframesRules: null,
-
-  /**
-   * Reset various properties
-   */
-  reset: function () {
-    this._propertyInfos = {};
-    this._ruleCount = 0;
-    this._sheetIndex = 0;
-    this._sheets = {};
-    this._sheetsCached = false;
-    this._matchedRules = null;
-    this._matchedSelectors = null;
-    this._keyframesRules = [];
-  },
-
-  /**
-   * Focus on a new element - remove the style caches.
-   *
-   * @param {nsIDOMElement} aViewedElement the element the user has highlighted
-   * in the Inspector.
-   */
-  highlight: function (viewedElement) {
-    if (!viewedElement) {
-      this.viewedElement = null;
-      this.viewedDocument = null;
-      this._computedStyle = null;
-      this.reset();
-      return;
-    }
-
-    if (viewedElement === this.viewedElement) {
-      return;
-    }
-
-    this.viewedElement = viewedElement;
-
-    let doc = this.viewedElement.ownerDocument;
-    if (doc != this.viewedDocument) {
-      // New document: clear/rebuild the cache.
-      this.viewedDocument = doc;
-
-      // Hunt down top level stylesheets, and cache them.
-      this._cacheSheets();
-    } else {
-      // Clear cached data in the CssPropertyInfo objects.
-      this._propertyInfos = {};
-    }
-
-    this._matchedRules = null;
-    this._matchedSelectors = null;
-    this._computedStyle = CssLogic.getComputedStyle(this.viewedElement);
-  },
-
-  /**
-   * Get the values of all the computed CSS properties for the highlighted
-   * element.
-   * @returns {object} The computed CSS properties for a selected element
-   */
-  get computedStyle() {
-    return this._computedStyle;
-  },
-
-  /**
-   * Get the source filter.
-   * @returns {string} The source filter being used.
-   */
-  get sourceFilter() {
-    return this._sourceFilter;
-  },
-
-  /**
-   * Source filter. Only display properties coming from the given source (web
-   * address). Note that in order to avoid information overload we DO NOT show
-   * unmatched system rules.
-   * @see CssLogic.FILTER.*
-   */
-  set sourceFilter(value) {
-    let oldValue = this._sourceFilter;
-    this._sourceFilter = value;
-
-    let ruleCount = 0;
-
-    // Update the CssSheet objects.
-    this.forEachSheet(function (sheet) {
-      sheet._sheetAllowed = -1;
-      if (sheet.contentSheet && sheet.sheetAllowed) {
-        ruleCount += sheet.ruleCount;
-      }
-    }, this);
-
-    this._ruleCount = ruleCount;
-
-    // Full update is needed because the this.processMatchedSelectors() method
-    // skips UA stylesheets if the filter does not allow such sheets.
-    let needFullUpdate = (oldValue == CssLogic.FILTER.UA ||
-        value == CssLogic.FILTER.UA);
-
-    if (needFullUpdate) {
-      this._matchedRules = null;
-      this._matchedSelectors = null;
-      this._propertyInfos = {};
-    } else {
-      // Update the CssPropertyInfo objects.
-      for (let property in this._propertyInfos) {
-        this._propertyInfos[property].needRefilter = true;
-      }
-    }
-  },
-
-  /**
-   * Return a CssPropertyInfo data structure for the currently viewed element
-   * and the specified CSS property. If there is no currently viewed element we
-   * return an empty object.
-   *
-   * @param {string} property The CSS property to look for.
-   * @return {CssPropertyInfo} a CssPropertyInfo structure for the given
-   * property.
-   */
-  getPropertyInfo: function (property) {
-    if (!this.viewedElement) {
-      return {};
-    }
-
-    let info = this._propertyInfos[property];
-    if (!info) {
-      info = new CssPropertyInfo(this, property, this._isInherited);
-      this._propertyInfos[property] = info;
-    }
-
-    return info;
-  },
-
-  /**
-   * Cache all the stylesheets in the inspected document
-   * @private
-   */
-  _cacheSheets: function () {
-    this._passId++;
-    this.reset();
-
-    // styleSheets isn't an array, but forEach can work on it anyway
-    Array.prototype.forEach.call(this.viewedDocument.styleSheets,
-        this._cacheSheet, this);
-
-    this._sheetsCached = true;
-  },
-
-  /**
-   * Cache a stylesheet if it falls within the requirements: if it's enabled,
-   * and if the @media is allowed. This method also walks through the stylesheet
-   * cssRules to find @imported rules, to cache the stylesheets of those rules
-   * as well. In addition, the @keyframes rules in the stylesheet are cached.
-   *
-   * @private
-   * @param {CSSStyleSheet} domSheet the CSSStyleSheet object to cache.
-   */
-  _cacheSheet: function (domSheet) {
-    if (domSheet.disabled) {
-      return;
-    }
-
-    // Only work with stylesheets that have their media allowed.
-    if (!this.mediaMatches(domSheet)) {
-      return;
-    }
-
-    // Cache the sheet.
-    let cssSheet = this.getSheet(domSheet, this._sheetIndex++);
-    if (cssSheet._passId != this._passId) {
-      cssSheet._passId = this._passId;
-
-      // Find import and keyframes rules.
-      for (let aDomRule of domSheet.cssRules) {
-        if (aDomRule.type == CSSRule.IMPORT_RULE &&
-            aDomRule.styleSheet &&
-            this.mediaMatches(aDomRule)) {
-          this._cacheSheet(aDomRule.styleSheet);
-        } else if (aDomRule.type == CSSRule.KEYFRAMES_RULE) {
-          this._keyframesRules.push(aDomRule);
-        }
-      }
-    }
-  },
-
-  /**
-   * Retrieve the list of stylesheets in the document.
-   *
-   * @return {array} the list of stylesheets in the document.
-   */
-  get sheets() {
-    if (!this._sheetsCached) {
-      this._cacheSheets();
-    }
-
-    let sheets = [];
-    this.forEachSheet(function (sheet) {
-      if (sheet.contentSheet) {
-        sheets.push(sheet);
-      }
-    }, this);
-
-    return sheets;
-  },
-
-  /**
-   * Retrieve the list of keyframes rules in the document.
-   *
-   * @ return {array} the list of keyframes rules in the document.
-   */
-  get keyframesRules() {
-    if (!this._sheetsCached) {
-      this._cacheSheets();
-    }
-    return this._keyframesRules;
-  },
-
-  /**
-   * Retrieve a CssSheet object for a given a CSSStyleSheet object. If the
-   * stylesheet is already cached, you get the existing CssSheet object,
-   * otherwise the new CSSStyleSheet object is cached.
-   *
-   * @param {CSSStyleSheet} domSheet the CSSStyleSheet object you want.
-   * @param {number} index the index, within the document, of the stylesheet.
-   *
-   * @return {CssSheet} the CssSheet object for the given CSSStyleSheet object.
-   */
-  getSheet: function (domSheet, index) {
-    let cacheId = "";
-
-    if (domSheet.href) {
-      cacheId = domSheet.href;
-    } else if (domSheet.ownerNode && domSheet.ownerNode.ownerDocument) {
-      cacheId = domSheet.ownerNode.ownerDocument.location;
-    }
-
-    let sheet = null;
-    let sheetFound = false;
-
-    if (cacheId in this._sheets) {
-      for (let i = 0, numSheets = this._sheets[cacheId].length;
-           i < numSheets;
-           i++) {
-        sheet = this._sheets[cacheId][i];
-        if (sheet.domSheet === domSheet) {
-          if (index != -1) {
-            sheet.index = index;
-          }
-          sheetFound = true;
-          break;
-        }
-      }
-    }
-
-    if (!sheetFound) {
-      if (!(cacheId in this._sheets)) {
-        this._sheets[cacheId] = [];
-      }
-
-      sheet = new CssSheet(this, domSheet, index);
-      if (sheet.sheetAllowed && sheet.contentSheet) {
-        this._ruleCount += sheet.ruleCount;
-      }
-
-      this._sheets[cacheId].push(sheet);
-    }
-
-    return sheet;
-  },
-
-  /**
-   * Process each cached stylesheet in the document using your callback.
-   *
-   * @param {function} callback the function you want executed for each of the
-   * CssSheet objects cached.
-   * @param {object} scope the scope you want for the callback function. scope
-   * will be the this object when callback executes.
-   */
-  forEachSheet: function (callback, scope) {
-    for (let cacheId in this._sheets) {
-      let sheets = this._sheets[cacheId];
-      for (let i = 0; i < sheets.length; i++) {
-        // We take this as an opportunity to clean dead sheets
-        try {
-          let sheet = sheets[i];
-          // If accessing domSheet raises an exception, then the style
-          // sheet is a dead object.
-          sheet.domSheet;
-          callback.call(scope, sheet, i, sheets);
-        } catch (e) {
-          sheets.splice(i, 1);
-          i--;
-        }
-      }
-    }
-  },
-
-  /**
-
-  /**
-   * Get the number nsIDOMCSSRule objects in the document, counted from all of
-   * the stylesheets. System sheets are excluded. If a filter is active, this
-   * tells only the number of nsIDOMCSSRule objects inside the selected
-   * CSSStyleSheet.
-   *
-   * WARNING: This only provides an estimate of the rule count, and the results
-   * could change at a later date. Todo remove this
-   *
-   * @return {number} the number of nsIDOMCSSRule (all rules).
-   */
-  get ruleCount() {
-    if (!this._sheetsCached) {
-      this._cacheSheets();
-    }
-
-    return this._ruleCount;
-  },
-
-  /**
-   * Process the CssSelector objects that match the highlighted element and its
-   * parent elements. scope.callback() is executed for each CssSelector
-   * object, being passed the CssSelector object and the match status.
-   *
-   * This method also includes all of the element.style properties, for each
-   * highlighted element parent and for the highlighted element itself.
-   *
-   * Note that the matched selectors are cached, such that next time your
-   * callback is invoked for the cached list of CssSelector objects.
-   *
-   * @param {function} callback the function you want to execute for each of
-   * the matched selectors.
-   * @param {object} scope the scope you want for the callback function. scope
-   * will be the this object when callback executes.
-   */
-  processMatchedSelectors: function (callback, scope) {
-    if (this._matchedSelectors) {
-      if (callback) {
-        this._passId++;
-        this._matchedSelectors.forEach(function (value) {
-          callback.call(scope, value[0], value[1]);
-          value[0].cssRule._passId = this._passId;
-        }, this);
-      }
-      return;
-    }
-
-    if (!this._matchedRules) {
-      this._buildMatchedRules();
-    }
-
-    this._matchedSelectors = [];
-    this._passId++;
-
-    for (let i = 0; i < this._matchedRules.length; i++) {
-      let rule = this._matchedRules[i][0];
-      let status = this._matchedRules[i][1];
-
-      rule.selectors.forEach(function (selector) {
-        if (selector._matchId !== this._matchId &&
-           (selector.elementStyle ||
-            this.selectorMatchesElement(rule.domRule,
-                                        selector.selectorIndex))) {
-          selector._matchId = this._matchId;
-          this._matchedSelectors.push([ selector, status ]);
-          if (callback) {
-            callback.call(scope, selector, status);
-          }
-        }
-      }, this);
-
-      rule._passId = this._passId;
-    }
-  },
-
-  /**
-   * Check if the given selector matches the highlighted element or any of its
-   * parents.
-   *
-   * @private
-   * @param {DOMRule} domRule
-   *        The DOM Rule containing the selector.
-   * @param {Number} idx
-   *        The index of the selector within the DOMRule.
-   * @return {boolean}
-   *         true if the given selector matches the highlighted element or any
-   *         of its parents, otherwise false is returned.
-   */
-  selectorMatchesElement: function (domRule, idx) {
-    let element = this.viewedElement;
-    do {
-      if (domUtils.selectorMatchesElement(element, domRule, idx)) {
-        return true;
-      }
-    } while ((element = element.parentNode) &&
-             element.nodeType === nodeConstants.ELEMENT_NODE);
-
-    return false;
-  },
-
-  /**
-   * Check if the highlighted element or it's parents have matched selectors.
-   *
-   * @param {array} aProperties The list of properties you want to check if they
-   * have matched selectors or not.
-   * @return {object} An object that tells for each property if it has matched
-   * selectors or not. Object keys are property names and values are booleans.
-   */
-  hasMatchedSelectors: function (properties) {
-    if (!this._matchedRules) {
-      this._buildMatchedRules();
-    }
-
-    let result = {};
-
-    this._matchedRules.some(function (value) {
-      let rule = value[0];
-      let status = value[1];
-      properties = properties.filter((property) => {
-        // We just need to find if a rule has this property while it matches
-        // the viewedElement (or its parents).
-        if (rule.getPropertyValue(property) &&
-            (status == CssLogic.STATUS.MATCHED ||
-             (status == CssLogic.STATUS.PARENT_MATCH &&
-              this._isInherited(property)))) {
-          result[property] = true;
-          return false;
-        }
-        // Keep the property for the next rule.
-        return true;
-      });
-      return properties.length == 0;
-    }, this);
-
-    return result;
-  },
-
-  /**
-   * Build the array of matched rules for the currently highlighted element.
-   * The array will hold rules that match the viewedElement and its parents.
-   *
-   * @private
-   */
-  _buildMatchedRules: function () {
-    let domRules;
-    let element = this.viewedElement;
-    let filter = this.sourceFilter;
-    let sheetIndex = 0;
-
-    this._matchId++;
-    this._passId++;
-    this._matchedRules = [];
-
-    if (!element) {
-      return;
-    }
-
-    do {
-      let status = this.viewedElement === element ?
-                   CssLogic.STATUS.MATCHED : CssLogic.STATUS.PARENT_MATCH;
-
-      try {
-        // Handle finding rules on pseudo by reading style rules
-        // on the parent node with proper pseudo arg to getCSSStyleRules.
-        let {bindingElement, pseudo} =
-            CssLogic.getBindingElementAndPseudo(element);
-        domRules = domUtils.getCSSStyleRules(bindingElement, pseudo);
-      } catch (ex) {
-        console.log("CL__buildMatchedRules error: " + ex);
-        continue;
-      }
-
-      // getCSSStyleRules can return null with a shadow DOM element.
-      let numDomRules = domRules ? domRules.Count() : 0;
-      for (let i = 0; i < numDomRules; i++) {
-        let domRule = domRules.GetElementAt(i);
-        if (domRule.type !== CSSRule.STYLE_RULE) {
-          continue;
-        }
-
-        let sheet = this.getSheet(domRule.parentStyleSheet, -1);
-        if (sheet._passId !== this._passId) {
-          sheet.index = sheetIndex++;
-          sheet._passId = this._passId;
-        }
-
-        if (filter === CssLogic.FILTER.USER && !sheet.contentSheet) {
-          continue;
-        }
-
-        let rule = sheet.getRule(domRule);
-        if (rule._passId === this._passId) {
-          continue;
-        }
-
-        rule._matchId = this._matchId;
-        rule._passId = this._passId;
-        this._matchedRules.push([rule, status]);
-      }
-
-      // Add element.style information.
-      if (element.style && element.style.length > 0) {
-        let rule = new CssRule(null, { style: element.style }, element);
-        rule._matchId = this._matchId;
-        rule._passId = this._passId;
-        this._matchedRules.push([rule, status]);
-      }
-    } while ((element = element.parentNode) &&
-              element.nodeType === nodeConstants.ELEMENT_NODE);
-  },
-
-  /**
-   * Tells if the given DOM CSS object matches the current view media.
-   *
-   * @param {object} domObject The DOM CSS object to check.
-   * @return {boolean} True if the DOM CSS object matches the current view
-   * media, or false otherwise.
-   */
-  mediaMatches: function (domObject) {
-    let mediaText = domObject.media.mediaText;
-    return !mediaText ||
-      this.viewedDocument.defaultView.matchMedia(mediaText).matches;
-  },
-};
-
 /**
- * If the element has an id, return '#id'. Otherwise return 'tagname[n]' where
- * n is the index of this element in its siblings.
- * <p>A technically more 'correct' output from the no-id case might be:
- * 'tagname:nth-of-type(n)' however this is unlikely to be more understood
- * and it is longer.
- *
- * @param {nsIDOMElement} element the element for which you want the short name.
- * @return {string} the string to be displayed for element.
- */
-CssLogic.getShortName = function (element) {
-  if (!element) {
-    return "null";
-  }
-  if (element.id) {
-    return "#" + element.id;
-  }
-  let priorSiblings = 0;
-  let temp = element;
-  while ((temp = temp.previousElementSibling)) {
-    priorSiblings++;
-  }
-  return element.tagName + "[" + priorSiblings + "]";
-};
-
-/**
- * Get a string list of selectors for a given DOMRule.
- *
- * @param {DOMRule} domRule
- *        The DOMRule to parse.
- * @return {Array}
- *         An array of string selectors.
- */
-CssLogic.getSelectors = function (domRule) {
-  let selectors = [];
-
-  let len = domUtils.getSelectorCount(domRule);
-  for (let i = 0; i < len; i++) {
-    let text = domUtils.getSelectorText(domRule, i);
-    selectors.push(text);
-  }
-  return selectors;
-};
-
-/**
- * Given a node, check to see if it is a ::before or ::after element.
- * If so, return the node that is accessible from within the document
- * (the parent of the anonymous node), along with which pseudo element
- * it was.  Otherwise, return the node itself.
- *
- * @returns {Object}
- *            - {DOMNode} node The non-anonymous node
- *            - {string} pseudo One of ':before', ':after', or null.
- */
-CssLogic.getBindingElementAndPseudo = function (node) {
-  let bindingElement = node;
-  let pseudo = null;
-  if (node.nodeName == "_moz_generated_content_before") {
-    bindingElement = node.parentNode;
-    pseudo = ":before";
-  } else if (node.nodeName == "_moz_generated_content_after") {
-    bindingElement = node.parentNode;
-    pseudo = ":after";
-  }
-  return {
-    bindingElement: bindingElement,
-    pseudo: pseudo
-  };
-};
-
-/**
- * Get the computed style on a node.  Automatically handles reading
- * computed styles on a ::before/::after element by reading on the
- * parent node with the proper pseudo argument.
- *
- * @param {Node}
- * @returns {CSSStyleDeclaration}
- */
-CssLogic.getComputedStyle = function (node) {
-  if (!node ||
-      Cu.isDeadWrapper(node) ||
-      node.nodeType !== nodeConstants.ELEMENT_NODE ||
-      !node.ownerDocument ||
-      !node.ownerDocument.defaultView) {
-    return null;
-  }
-
-  let {bindingElement, pseudo} = CssLogic.getBindingElementAndPseudo(node);
-  return node.ownerDocument.defaultView.getComputedStyle(bindingElement,
-                                                         pseudo);
-};
-
-/**
- * Memonized lookup of a l10n string from a string bundle.
+ * Memoized lookup of a l10n string from a string bundle.
  * @param {string} name The key to lookup.
  * @returns A localized version of the given key.
  */
-CssLogic.l10n = function (name) {
-  return CssLogic._strings.GetStringFromName(name);
+exports.l10n = function (name) {
+  return exports._strings.GetStringFromName(name);
 };
 
-DevToolsUtils.defineLazyGetter(CssLogic, "_strings", function () {
-  return Services.strings
-    .createBundle("chrome://devtools-shared/locale/styleinspector.properties");
-});
+exports._strings = Services.strings
+  .createBundle("chrome://devtools-shared/locale/styleinspector.properties");
 
 /**
  * Is the given property sheet a content stylesheet?
  *
  * @param {CSSStyleSheet} sheet a stylesheet
  * @return {boolean} true if the given stylesheet is a content stylesheet,
  * false otherwise.
  */
-CssLogic.isContentStylesheet = function (sheet) {
+exports.isContentStylesheet = function (sheet) {
   return sheet.parsingMode !== "agent";
 };
 
 /**
- * Get a source for a stylesheet, taking into account embedded stylesheets
- * for which we need to use document.defaultView.location.href rather than
- * sheet.href
- *
- * @param {CSSStyleSheet} sheet the DOM object for the style sheet.
- * @return {string} the address of the stylesheet.
- */
-CssLogic.href = function (sheet) {
-  let href = sheet.href;
-  if (!href) {
-    href = sheet.ownerNode.ownerDocument.location;
-  }
-
-  return href;
-};
-
-/**
  * Return a shortened version of a style sheet's source.
  *
  * @param {CSSStyleSheet} sheet the DOM object for the style sheet.
  */
-CssLogic.shortSource = function (sheet) {
+exports.shortSource = function (sheet) {
   // Use a string like "inline" if there is no source href
   if (!sheet || !sheet.href) {
-    return CssLogic.l10n("rule.sourceInline");
+    return exports.l10n("rule.sourceInline");
   }
 
   // We try, in turn, the filename, filePath, query string, whole thing
   let url = {};
   try {
     url = Services.io.newURI(sheet.href, null, null);
     url = url.QueryInterface(Ci.nsIURL);
   } catch (ex) {
@@ -822,109 +129,29 @@ CssLogic.shortSource = function (sheet) 
   if (url.query) {
     return url.query;
   }
 
   let dataUrl = sheet.href.match(/^(data:[^,]*),/);
   return dataUrl ? dataUrl[1] : sheet.href;
 };
 
-/**
- * Find the position of [element] in [nodeList].
- * @returns an index of the match, or -1 if there is no match
- */
-function positionInNodeList(element, nodeList) {
-  for (let i = 0; i < nodeList.length; i++) {
-    if (element === nodeList[i]) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-/**
- * Find a unique CSS selector for a given element
- * @returns a string such that ele.ownerDocument.querySelector(reply) === ele
- * and ele.ownerDocument.querySelectorAll(reply).length === 1
- */
-CssLogic.findCssSelector = function (ele) {
-  ele = getRootBindingParent(ele);
-  let document = ele.ownerDocument;
-  if (!document || !document.contains(ele)) {
-    throw new Error("findCssSelector received element not inside document");
-  }
-
-  // document.querySelectorAll("#id") returns multiple if elements share an ID
-  if (ele.id &&
-      document.querySelectorAll("#" + CSS.escape(ele.id)).length === 1) {
-    return "#" + CSS.escape(ele.id);
-  }
-
-  // Inherently unique by tag name
-  let tagName = ele.localName;
-  if (tagName === "html") {
-    return "html";
-  }
-  if (tagName === "head") {
-    return "head";
-  }
-  if (tagName === "body") {
-    return "body";
-  }
-
-  // We might be able to find a unique class name
-  let selector, index, matches;
-  if (ele.classList.length > 0) {
-    for (let i = 0; i < ele.classList.length; i++) {
-      // Is this className unique by itself?
-      selector = "." + CSS.escape(ele.classList.item(i));
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique with a tag name?
-      selector = tagName + selector;
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-      // Maybe it's unique using a tag name and nth-child
-      index = positionInNodeList(ele, ele.parentNode.children) + 1;
-      selector = selector + ":nth-child(" + index + ")";
-      matches = document.querySelectorAll(selector);
-      if (matches.length === 1) {
-        return selector;
-      }
-    }
-  }
-
-  // Not unique enough yet.  As long as it's not a child of the document,
-  // continue recursing up until it is unique enough.
-  if (ele.parentNode !== document) {
-    index = positionInNodeList(ele, ele.parentNode.children) + 1;
-    selector = CssLogic.findCssSelector(ele.parentNode) + " > " +
-      tagName + ":nth-child(" + index + ")";
-  }
-
-  return selector;
-};
-
 const TAB_CHARS = "\t";
 
 /**
  * Prettify minified CSS text.
  * This prettifies CSS code where there is no indentation in usual places while
  * keeping original indentation as-is elsewhere.
  * @param string text The CSS source to prettify.
  * @return string Prettified CSS source
  */
-CssLogic.prettifyCSS = function (text, ruleCount) {
-  if (CssLogic.LINE_SEPARATOR == null) {
+function prettifyCSS(text, ruleCount) {
+  if (prettifyCSS.LINE_SEPARATOR == null) {
     let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-    CssLogic.LINE_SEPARATOR = (os === "WINNT" ? "\r\n" : "\n");
+    prettifyCSS.LINE_SEPARATOR = (os === "WINNT" ? "\r\n" : "\n");
   }
 
   // remove initial and terminating HTML comments and surrounding whitespace
   text = text.replace(/(?:^\s*<!--[\r\n]*)|(?:\s*-->\s*$)/g, "");
   let originalText = text;
   text = text.trim();
 
   // don't attempt to prettify if there's more than one line per rule.
@@ -1051,17 +278,17 @@ CssLogic.prettifyCSS = function (text, r
     // Append any saved up text to the result, applying indentation.
     if (startIndex !== undefined) {
       if (isCloseBrace && !anyNonWS) {
         // If we saw only whitespace followed by a "}", then we don't
         // need anything here.
       } else {
         result = result + indent + text.substring(startIndex, endIndex);
         if (isCloseBrace) {
-          result += CssLogic.LINE_SEPARATOR;
+          result += prettifyCSS.LINE_SEPARATOR;
         }
       }
     }
 
     if (isCloseBrace) {
       indent = TAB_CHARS.repeat(--indentLevel);
       result = result + indent + "}";
     }
@@ -1086,765 +313,20 @@ CssLogic.prettifyCSS = function (text, r
     // Here we ignore the case where whitespace appears at the end of
     // the text.
     if (pushbackToken && token && token.tokenType === "whitespace" &&
         /\n/g.test(text.substring(token.startOffset, token.endOffset))) {
       return originalText;
     }
 
     // Finally time for that newline.
-    result = result + CssLogic.LINE_SEPARATOR;
+    result = result + prettifyCSS.LINE_SEPARATOR;
 
     // Maybe we hit EOF.
     if (!pushbackToken) {
       break;
     }
   }
 
   return result;
-};
-
-/**
- * A safe way to access cached bits of information about a stylesheet.
- *
- * @constructor
- * @param {CssLogic} cssLogic pointer to the CssLogic instance working with
- * this CssSheet object.
- * @param {CSSStyleSheet} domSheet reference to a DOM CSSStyleSheet object.
- * @param {number} index tells the index/position of the stylesheet within the
- * main document.
- */
-function CssSheet(cssLogic, domSheet, index) {
-  this._cssLogic = cssLogic;
-  this.domSheet = domSheet;
-  this.index = this.contentSheet ? index : -100 * index;
-
-  // Cache of the sheets href. Cached by the getter.
-  this._href = null;
-  // Short version of href for use in select boxes etc. Cached by getter.
-  this._shortSource = null;
-
-  // null for uncached.
-  this._sheetAllowed = null;
-
-  // Cached CssRules from the given stylesheet.
-  this._rules = {};
-
-  this._ruleCount = -1;
-}
-
-CssSheet.prototype = {
-  _passId: null,
-  _contentSheet: null,
-
-  /**
-   * Tells if the stylesheet is provided by the browser or not.
-   *
-   * @return {boolean} false if this is a browser-provided stylesheet, or true
-   * otherwise.
-   */
-  get contentSheet() {
-    if (this._contentSheet === null) {
-      this._contentSheet = CssLogic.isContentStylesheet(this.domSheet);
-    }
-    return this._contentSheet;
-  },
-
-  /**
-   * Tells if the stylesheet is disabled or not.
-   * @return {boolean} true if this stylesheet is disabled, or false otherwise.
-   */
-  get disabled() {
-    return this.domSheet.disabled;
-  },
-
-  /**
-   * Get a source for a stylesheet, using CssLogic.href
-   *
-   * @return {string} the address of the stylesheet.
-   */
-  get href() {
-    if (this._href) {
-      return this._href;
-    }
-
-    this._href = CssLogic.href(this.domSheet);
-    return this._href;
-  },
-
-  /**
-   * Create a shorthand version of the href of a stylesheet.
-   *
-   * @return {string} the shorthand source of the stylesheet.
-   */
-  get shortSource() {
-    if (this._shortSource) {
-      return this._shortSource;
-    }
-
-    this._shortSource = CssLogic.shortSource(this.domSheet);
-    return this._shortSource;
-  },
-
-  /**
-   * Tells if the sheet is allowed or not by the current CssLogic.sourceFilter.
-   *
-   * @return {boolean} true if the stylesheet is allowed by the sourceFilter, or
-   * false otherwise.
-   */
-  get sheetAllowed() {
-    if (this._sheetAllowed !== null) {
-      return this._sheetAllowed;
-    }
-
-    this._sheetAllowed = true;
-
-    let filter = this._cssLogic.sourceFilter;
-    if (filter === CssLogic.FILTER.USER && !this.contentSheet) {
-      this._sheetAllowed = false;
-    }
-    if (filter !== CssLogic.FILTER.USER && filter !== CssLogic.FILTER.UA) {
-      this._sheetAllowed = (filter === this.href);
-    }
-
-    return this._sheetAllowed;
-  },
-
-  /**
-   * Retrieve the number of rules in this stylesheet.
-   *
-   * @return {number} the number of nsIDOMCSSRule objects in this stylesheet.
-   */
-  get ruleCount() {
-    return this._ruleCount > -1 ?
-      this._ruleCount :
-      this.domSheet.cssRules.length;
-  },
-
-  /**
-   * Retrieve a CssRule object for the given CSSStyleRule. The CssRule object is
-   * cached, such that subsequent retrievals return the same CssRule object for
-   * the same CSSStyleRule object.
-   *
-   * @param {CSSStyleRule} aDomRule the CSSStyleRule object for which you want a
-   * CssRule object.
-   * @return {CssRule} the cached CssRule object for the given CSSStyleRule
-   * object.
-   */
-  getRule: function (domRule) {
-    let cacheId = domRule.type + domRule.selectorText;
-
-    let rule = null;
-    let ruleFound = false;
-
-    if (cacheId in this._rules) {
-      for (let i = 0, rulesLen = this._rules[cacheId].length;
-           i < rulesLen;
-           i++) {
-        rule = this._rules[cacheId][i];
-        if (rule.domRule === domRule) {
-          ruleFound = true;
-          break;
-        }
-      }
-    }
-
-    if (!ruleFound) {
-      if (!(cacheId in this._rules)) {
-        this._rules[cacheId] = [];
-      }
-
-      rule = new CssRule(this, domRule);
-      this._rules[cacheId].push(rule);
-    }
-
-    return rule;
-  },
-
-  toString: function () {
-    return "CssSheet[" + this.shortSource + "]";
-  }
-};
-
-/**
- * Information about a single CSSStyleRule.
- *
- * @param {CSSSheet|null} cssSheet the CssSheet object of the stylesheet that
- * holds the CSSStyleRule. If the rule comes from element.style, set this
- * argument to null.
- * @param {CSSStyleRule|object} domRule the DOM CSSStyleRule for which you want
- * to cache data. If the rule comes from element.style, then provide
- * an object of the form: {style: element.style}.
- * @param {Element} [element] If the rule comes from element.style, then this
- * argument must point to the element.
- * @constructor
- */
-function CssRule(cssSheet, domRule, element) {
-  this._cssSheet = cssSheet;
-  this.domRule = domRule;
-
-  let parentRule = domRule.parentRule;
-  if (parentRule && parentRule.type == CSSRule.MEDIA_RULE) {
-    this.mediaText = parentRule.media.mediaText;
-  }
-
-  if (this._cssSheet) {
-    // parse domRule.selectorText on call to this.selectors
-    this._selectors = null;
-    this.line = domUtils.getRuleLine(this.domRule);
-    this.source = this._cssSheet.shortSource + ":" + this.line;
-    if (this.mediaText) {
-      this.source += " @media " + this.mediaText;
-    }
-    this.href = this._cssSheet.href;
-    this.contentRule = this._cssSheet.contentSheet;
-  } else if (element) {
-    this._selectors = [ new CssSelector(this, "@element.style", 0) ];
-    this.line = -1;
-    this.source = CssLogic.l10n("rule.sourceElement");
-    this.href = "#";
-    this.contentRule = true;
-    this.sourceElement = element;
-  }
-}
-
-CssRule.prototype = {
-  _passId: null,
-
-  mediaText: "",
-
-  get isMediaRule() {
-    return !!this.mediaText;
-  },
-
-  /**
-   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
-   *
-   * @return {boolean} true if the parent stylesheet is allowed by the current
-   * sourceFilter, or false otherwise.
-   */
-  get sheetAllowed() {
-    return this._cssSheet ? this._cssSheet.sheetAllowed : true;
-  },
-
-  /**
-   * Retrieve the parent stylesheet index/position in the viewed document.
-   *
-   * @return {number} the parent stylesheet index/position in the viewed
-   * document.
-   */
-  get sheetIndex() {
-    return this._cssSheet ? this._cssSheet.index : 0;
-  },
-
-  /**
-   * Retrieve the style property value from the current CSSStyleRule.
-   *
-   * @param {string} property the CSS property name for which you want the
-   * value.
-   * @return {string} the property value.
-   */
-  getPropertyValue: function (property) {
-    return this.domRule.style.getPropertyValue(property);
-  },
-
-  /**
-   * Retrieve the style property priority from the current CSSStyleRule.
-   *
-   * @param {string} property the CSS property name for which you want the
-   * priority.
-   * @return {string} the property priority.
-   */
-  getPropertyPriority: function (property) {
-    return this.domRule.style.getPropertyPriority(property);
-  },
-
-  /**
-   * Retrieve the list of CssSelector objects for each of the parsed selectors
-   * of the current CSSStyleRule.
-   *
-   * @return {array} the array hold the CssSelector objects.
-   */
-  get selectors() {
-    if (this._selectors) {
-      return this._selectors;
-    }
-
-    // Parse the CSSStyleRule.selectorText string.
-    this._selectors = [];
-
-    if (!this.domRule.selectorText) {
-      return this._selectors;
-    }
-
-    let selectors = CssLogic.getSelectors(this.domRule);
-
-    for (let i = 0, len = selectors.length; i < len; i++) {
-      this._selectors.push(new CssSelector(this, selectors[i], i));
-    }
-
-    return this._selectors;
-  },
-
-  toString: function () {
-    return "[CssRule " + this.domRule.selectorText + "]";
-  },
-};
-
-/**
- * The CSS selector class allows us to document the ranking of various CSS
- * selectors.
- *
- * @constructor
- * @param {CssRule} cssRule the CssRule instance from where the selector comes.
- * @param {string} selector The selector that we wish to investigate.
- * @param {Number} index The index of the selector within it's rule.
- */
-function CssSelector(cssRule, selector, index) {
-  this.cssRule = cssRule;
-  this.text = selector;
-  this.elementStyle = this.text == "@element.style";
-  this._specificity = null;
-  this.selectorIndex = index;
 }
 
-exports.CssSelector = CssSelector;
-
-CssSelector.prototype = {
-  _matchId: null,
-
-  /**
-   * Retrieve the CssSelector source, which is the source of the CssSheet owning
-   * the selector.
-   *
-   * @return {string} the selector source.
-   */
-  get source() {
-    return this.cssRule.source;
-  },
-
-  /**
-   * Retrieve the CssSelector source element, which is the source of the CssRule
-   * owning the selector. This is only available when the CssSelector comes from
-   * an element.style.
-   *
-   * @return {string} the source element selector.
-   */
-  get sourceElement() {
-    return this.cssRule.sourceElement;
-  },
-
-  /**
-   * Retrieve the address of the CssSelector. This points to the address of the
-   * CssSheet owning this selector.
-   *
-   * @return {string} the address of the CssSelector.
-   */
-  get href() {
-    return this.cssRule.href;
-  },
-
-  /**
-   * Check if the selector comes from a browser-provided stylesheet.
-   *
-   * @return {boolean} true if the selector comes from a content-provided
-   * stylesheet, or false otherwise.
-   */
-  get contentRule() {
-    return this.cssRule.contentRule;
-  },
-
-  /**
-   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
-   *
-   * @return {boolean} true if the parent stylesheet is allowed by the current
-   * sourceFilter, or false otherwise.
-   */
-  get sheetAllowed() {
-    return this.cssRule.sheetAllowed;
-  },
-
-  /**
-   * Retrieve the parent stylesheet index/position in the viewed document.
-   *
-   * @return {number} the parent stylesheet index/position in the viewed
-   * document.
-   */
-  get sheetIndex() {
-    return this.cssRule.sheetIndex;
-  },
-
-  /**
-   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
-   *
-   * @return {number} the line of the parent CSSStyleRule in the parent
-   * stylesheet.
-   */
-  get ruleLine() {
-    return this.cssRule.line;
-  },
-
-  /**
-   * Retrieve specificity information for the current selector.
-   *
-   * @see http://www.w3.org/TR/css3-selectors/#specificity
-   * @see http://www.w3.org/TR/CSS2/selector.html
-   *
-   * @return {Number} The selector's specificity.
-   */
-  get specificity() {
-    if (this.elementStyle) {
-      // We can't ask specificity from DOMUtils as element styles don't provide
-      // CSSStyleRule interface DOMUtils expect. However, specificity of element
-      // style is constant, 1,0,0,0 or 0x01000000, just return the constant
-      // directly. @see http://www.w3.org/TR/CSS2/cascade.html#specificity
-      return 0x01000000;
-    }
-
-    if (this._specificity) {
-      return this._specificity;
-    }
-
-    this._specificity = domUtils.getSpecificity(this.cssRule.domRule,
-                                                this.selectorIndex);
-
-    return this._specificity;
-  },
-
-  toString: function () {
-    return this.text;
-  },
-};
-
-/**
- * A cache of information about the matched rules, selectors and values attached
- * to a CSS property, for the highlighted element.
- *
- * The heart of the CssPropertyInfo object is the _findMatchedSelectors()
- * method. This are invoked when the PropertyView tries to access the
- * .matchedSelectors array.
- * Results are cached, for later reuse.
- *
- * @param {CssLogic} cssLogic Reference to the parent CssLogic instance
- * @param {string} property The CSS property we are gathering information for
- * @param {function} isInherited A function that determines if the CSS property
- *                   is inherited.
- * @constructor
- */
-function CssPropertyInfo(cssLogic, property, isInherited) {
-  this._cssLogic = cssLogic;
-  this.property = property;
-  this._value = "";
-  this._isInherited = isInherited;
-
-  // An array holding CssSelectorInfo objects for each of the matched selectors
-  // that are inside a CSS rule. Only rules that hold the this.property are
-  // counted. This includes rules that come from filtered stylesheets (those
-  // that have sheetAllowed = false).
-  this._matchedSelectors = null;
-}
-
-CssPropertyInfo.prototype = {
-  /**
-   * Retrieve the computed style value for the current property, for the
-   * highlighted element.
-   *
-   * @return {string} the computed style value for the current property, for the
-   * highlighted element.
-   */
-  get value() {
-    if (!this._value && this._cssLogic.computedStyle) {
-      try {
-        this._value =
-          this._cssLogic.computedStyle.getPropertyValue(this.property);
-      } catch (ex) {
-        console.log("Error reading computed style for " + this.property);
-        console.log(ex);
-      }
-    }
-    return this._value;
-  },
-
-  /**
-   * Retrieve the array holding CssSelectorInfo objects for each of the matched
-   * selectors, from each of the matched rules. Only selectors coming from
-   * allowed stylesheets are included in the array.
-   *
-   * @return {array} the list of CssSelectorInfo objects of selectors that match
-   * the highlighted element and its parents.
-   */
-  get matchedSelectors() {
-    if (!this._matchedSelectors) {
-      this._findMatchedSelectors();
-    } else if (this.needRefilter) {
-      this._refilterSelectors();
-    }
-
-    return this._matchedSelectors;
-  },
-
-  /**
-   * Find the selectors that match the highlighted element and its parents.
-   * Uses CssLogic.processMatchedSelectors() to find the matched selectors,
-   * passing in a reference to CssPropertyInfo._processMatchedSelector() to
-   * create CssSelectorInfo objects, which we then sort
-   * @private
-   */
-  _findMatchedSelectors: function () {
-    this._matchedSelectors = [];
-    this.needRefilter = false;
-
-    this._cssLogic.processMatchedSelectors(this._processMatchedSelector, this);
-
-    // Sort the selectors by how well they match the given element.
-    this._matchedSelectors.sort(function (selectorInfo1, selectorInfo2) {
-      if (selectorInfo1.status > selectorInfo2.status) {
-        return -1;
-      } else if (selectorInfo2.status > selectorInfo1.status) {
-        return 1;
-      }
-      return selectorInfo1.compareTo(selectorInfo2);
-    });
-
-    // Now we know which of the matches is best, we can mark it BEST_MATCH.
-    if (this._matchedSelectors.length > 0 &&
-        this._matchedSelectors[0].status > CssLogic.STATUS.UNMATCHED) {
-      this._matchedSelectors[0].status = CssLogic.STATUS.BEST;
-    }
-  },
-
-  /**
-   * Process a matched CssSelector object.
-   *
-   * @private
-   * @param {CssSelector} selector the matched CssSelector object.
-   * @param {CssLogic.STATUS} status the CssSelector match status.
-   */
-  _processMatchedSelector: function (selector, status) {
-    let cssRule = selector.cssRule;
-    let value = cssRule.getPropertyValue(this.property);
-    if (value &&
-        (status == CssLogic.STATUS.MATCHED ||
-         (status == CssLogic.STATUS.PARENT_MATCH &&
-          this._isInherited(this.property)))) {
-      let selectorInfo = new CssSelectorInfo(selector, this.property, value,
-          status);
-      this._matchedSelectors.push(selectorInfo);
-    }
-  },
-
-  /**
-   * Refilter the matched selectors array when the CssLogic.sourceFilter
-   * changes. This allows for quick filter changes.
-   * @private
-   */
-  _refilterSelectors: function () {
-    let passId = ++this._cssLogic._passId;
-    let ruleCount = 0;
-
-    let iterator = function (selectorInfo) {
-      let cssRule = selectorInfo.selector.cssRule;
-      if (cssRule._passId != passId) {
-        if (cssRule.sheetAllowed) {
-          ruleCount++;
-        }
-        cssRule._passId = passId;
-      }
-    };
-
-    if (this._matchedSelectors) {
-      this._matchedSelectors.forEach(iterator);
-    }
-
-    this.needRefilter = false;
-  },
-
-  toString: function () {
-    return "CssPropertyInfo[" + this.property + "]";
-  },
-};
-
-/**
- * A class that holds information about a given CssSelector object.
- *
- * Instances of this class are given to CssHtmlTree in the array of matched
- * selectors. Each such object represents a displayable row in the PropertyView
- * objects. The information given by this object blends data coming from the
- * CssSheet, CssRule and from the CssSelector that own this object.
- *
- * @param {CssSelector} selector The CssSelector object for which to
- *        present information.
- * @param {string} property The property for which information should
- *        be retrieved.
- * @param {string} value The property value from the CssRule that owns
- *        the selector.
- * @param {CssLogic.STATUS} status The selector match status.
- * @constructor
- */
-function CssSelectorInfo(selector, property, value, status) {
-  this.selector = selector;
-  this.property = property;
-  this.status = status;
-  this.value = value;
-  let priority = this.selector.cssRule.getPropertyPriority(this.property);
-  this.important = (priority === "important");
-}
-
-CssSelectorInfo.prototype = {
-  /**
-   * Retrieve the CssSelector source, which is the source of the CssSheet owning
-   * the selector.
-   *
-   * @return {string} the selector source.
-   */
-  get source() {
-    return this.selector.source;
-  },
-
-  /**
-   * Retrieve the CssSelector source element, which is the source of the CssRule
-   * owning the selector. This is only available when the CssSelector comes from
-   * an element.style.
-   *
-   * @return {string} the source element selector.
-   */
-  get sourceElement() {
-    return this.selector.sourceElement;
-  },
-
-  /**
-   * Retrieve the address of the CssSelector. This points to the address of the
-   * CssSheet owning this selector.
-   *
-   * @return {string} the address of the CssSelector.
-   */
-  get href() {
-    return this.selector.href;
-  },
-
-  /**
-   * Check if the CssSelector comes from element.style or not.
-   *
-   * @return {boolean} true if the CssSelector comes from element.style, or
-   * false otherwise.
-   */
-  get elementStyle() {
-    return this.selector.elementStyle;
-  },
-
-  /**
-   * Retrieve specificity information for the current selector.
-   *
-   * @return {object} an object holding specificity information for the current
-   * selector.
-   */
-  get specificity() {
-    return this.selector.specificity;
-  },
-
-  /**
-   * Retrieve the parent stylesheet index/position in the viewed document.
-   *
-   * @return {number} the parent stylesheet index/position in the viewed
-   * document.
-   */
-  get sheetIndex() {
-    return this.selector.sheetIndex;
-  },
-
-  /**
-   * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
-   *
-   * @return {boolean} true if the parent stylesheet is allowed by the current
-   * sourceFilter, or false otherwise.
-   */
-  get sheetAllowed() {
-    return this.selector.sheetAllowed;
-  },
-
-  /**
-   * Retrieve the line of the parent CSSStyleRule in the parent CSSStyleSheet.
-   *
-   * @return {number} the line of the parent CSSStyleRule in the parent
-   * stylesheet.
-   */
-  get ruleLine() {
-    return this.selector.ruleLine;
-  },
-
-  /**
-   * Check if the selector comes from a browser-provided stylesheet.
-   *
-   * @return {boolean} true if the selector comes from a browser-provided
-   * stylesheet, or false otherwise.
-   */
-  get contentRule() {
-    return this.selector.contentRule;
-  },
-
-  /**
-   * Compare the current CssSelectorInfo instance to another instance, based on
-   * specificity information.
-   *
-   * @param {CssSelectorInfo} that The instance to compare ourselves against.
-   * @return number -1, 0, 1 depending on how that compares with this.
-   */
-  compareTo: function (that) {
-    if (!this.contentRule && that.contentRule) {
-      return 1;
-    }
-    if (this.contentRule && !that.contentRule) {
-      return -1;
-    }
-
-    if (this.elementStyle && !that.elementStyle) {
-      if (!this.important && that.important) {
-        return 1;
-      }
-      return -1;
-    }
-
-    if (!this.elementStyle && that.elementStyle) {
-      if (this.important && !that.important) {
-        return -1;
-      }
-      return 1;
-    }
-
-    if (this.important && !that.important) {
-      return -1;
-    }
-    if (that.important && !this.important) {
-      return 1;
-    }
-
-    if (this.specificity > that.specificity) {
-      return -1;
-    }
-    if (that.specificity > this.specificity) {
-      return 1;
-    }
-
-    if (this.sheetIndex > that.sheetIndex) {
-      return -1;
-    }
-    if (that.sheetIndex > this.sheetIndex) {
-      return 1;
-    }
-
-    if (this.ruleLine > that.ruleLine) {
-      return -1;
-    }
-    if (that.ruleLine > this.ruleLine) {
-      return 1;
-    }
-
-    return 0;
-  },
-
-  toString: function () {
-    return this.selector + " -> " + this.value;
-  },
-};
-
-DevToolsUtils.defineLazyGetter(this, "domUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
+exports.prettifyCSS = prettifyCSS;
--- a/devtools/shared/tests/unit/test_prettifyCSS.js
+++ b/devtools/shared/tests/unit/test_prettifyCSS.js
@@ -1,16 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test prettifyCSS.
 
 "use strict";
 
-const {CssLogic} = require("devtools/shared/inspector/css-logic");
+const {prettifyCSS} = require("devtools/shared/inspector/css-logic");
 
 const TESTS = [
   { name: "simple test",
     input: "div { font-family:'Arial Black', Arial, sans-serif; }",
     expected: [
       "div {",
       "\tfont-family:'Arial Black', Arial, sans-serif;",
       "}"
@@ -47,22 +47,22 @@ const TESTS = [
       "div {",
       "\tcolor: red;",
       "}"
     ]
   },
 ];
 
 function run_test() {
-  // Note that CssLogic.LINE_SEPARATOR is computed lazily, so we
+  // Note that prettifyCSS.LINE_SEPARATOR is computed lazily, so we
   // ensure it is set.
-  CssLogic.prettifyCSS("");
+  prettifyCSS("");
 
   for (let test of TESTS) {
     do_print(test.name);
 
-    let input = test.input.split("\n").join(CssLogic.LINE_SEPARATOR);
-    let output = CssLogic.prettifyCSS(input);
-    let expected = test.expected.join(CssLogic.LINE_SEPARATOR) +
-        CssLogic.LINE_SEPARATOR;
+    let input = test.input.split("\n").join(prettifyCSS.LINE_SEPARATOR);
+    let output = prettifyCSS(input);
+    let expected = test.expected.join(prettifyCSS.LINE_SEPARATOR) +
+        prettifyCSS.LINE_SEPARATOR;
     equal(output, expected, test.name);
   }
 }