Bug 1225254 - split css-logic.js into server and shared files; r?pbro draft
authorTom Tromey <tom@tromey.com>
Fri, 24 Jun 2016 08:26:21 -0600
changeset 382474 6ea3ef30659f8e09fc3495f8606c33b733bd53c7
parent 382473 a263ae844dd266bbb79c487dcfc885954c4925fe
child 524199 deb2201bb3817a615ed535e40275e27ee00ea5e9
push id21728
push userbmo:ttromey@mozilla.com
push dateWed, 29 Jun 2016 18:24:58 +0000
reviewerspbro
bugs1225254
milestone50.0a1
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);
   }
 }