--- 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);
}
}