mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
315 lines
8.9 KiB
JavaScript
315 lines
8.9 KiB
JavaScript
/*
|
|
* Software License Agreement (BSD License)
|
|
*
|
|
* Copyright (c) 2007, Parakey Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use of this software in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Parakey Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior
|
|
* written permission of Parakey Inc.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Creator:
|
|
* Joe Hewitt
|
|
* Contributors
|
|
* John J. Barton (IBM Almaden)
|
|
* Jan Odvarko (Mozilla Corp.)
|
|
* Max Stepanov (Aptana Inc.)
|
|
* Rob Campbell (Mozilla Corp.)
|
|
* Hans Hillen (Paciello Group, Mozilla)
|
|
* Curtis Bartley (Mozilla Corp.)
|
|
* Mike Collins (IBM Almaden)
|
|
* Kevin Decker
|
|
* Mike Ratcliffe (Comartis AG)
|
|
* Hernan Rodríguez Colmeiro
|
|
* Austin Andrews
|
|
* Christoph Dorn
|
|
* Steven Roussey (AppCenter Inc, Network54)
|
|
*/
|
|
|
|
var EXPORTED_SYMBOLS = ["style"];
|
|
|
|
const Cc = Components.classes;
|
|
const Ci = Components.interfaces;
|
|
const Cu = Components.utils;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
var style = {
|
|
|
|
/**
|
|
* initialize domUtils
|
|
*/
|
|
initialize: function CSS_initialize()
|
|
{
|
|
this.domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].
|
|
getService(Ci["inIDOMUtils"]);
|
|
},
|
|
|
|
/**
|
|
* Is the given property sheet a system (user agent) stylesheet?
|
|
*
|
|
* @param aSheet
|
|
* a stylesheet
|
|
*/
|
|
isSystemStyleSheet: function CSS_isSystemStyleSheet(aSheet)
|
|
{
|
|
if (!aSheet)
|
|
return true;
|
|
|
|
let url = aSheet.href;
|
|
|
|
if (!url)
|
|
return false;
|
|
if (url.length == 0)
|
|
return true;
|
|
if (url[0] == 'h')
|
|
return false;
|
|
if (url.substr(0, 9) == "resource:")
|
|
return true;
|
|
if (url.substr(0, 7) == "chrome:")
|
|
return true;
|
|
if (url == "XPCSafeJSObjectWrapper.cpp")
|
|
return true;
|
|
if (url.substr(0, 6) == "about:")
|
|
return true;
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Parse properties from a given style object.
|
|
* Borrowed from Firebug's css.js.
|
|
*
|
|
* @param aStyle
|
|
* a style object
|
|
*/
|
|
parseCSSProperties: function CSS_parseCSSProps(aStyle)
|
|
{
|
|
let properties = [];
|
|
let lines = aStyle.cssText.match(/(?:[^;\(]*(?:\([^\)]*?\))?[^;\(]*)*;?/g);
|
|
let propRE = /\s*([^:\s]*)\s*:\s*(.*?)\s*(! important)?;?$/;
|
|
let line, i = 0;
|
|
while(line = lines[i++]) {
|
|
let match = propRE.exec(line);
|
|
if (!match)
|
|
continue;
|
|
let name = match[1];
|
|
let value = match[2];
|
|
let important = !!match[3]; // true if match[3] is non-empty
|
|
properties.unshift({name: name, value: value, important: important});
|
|
}
|
|
|
|
return properties;
|
|
},
|
|
|
|
/**
|
|
* Mark properties overridden further up the hierarchy.
|
|
*
|
|
* @param aProps
|
|
* Array of properties.
|
|
* @param aUsedProps
|
|
* Object of arrays keyed by property name.
|
|
* @param aInherit
|
|
* Boolean of whether or not we are in inherited mode.
|
|
*/
|
|
markOverriddenProperties: function CSS_markOverriddenProperties(aProps, aUsedProps, aInherit)
|
|
{
|
|
for (let i = 0; i < aProps.length; ++i) {
|
|
let prop = aProps[i];
|
|
if (aUsedProps.hasOwnProperty(prop.name)) {
|
|
// all previous occurrences of this property
|
|
let deadProps = aUsedProps[prop.name];
|
|
for (let j = 0; j < deadProps.length; ++j) {
|
|
let deadProp = deadProps[j];
|
|
if (!deadProp.disabled && !deadProp.wasInherited &&
|
|
deadProp.important && !prop.important) {
|
|
prop.overridden = true; // new occurrence overridden
|
|
} else if (!prop.disabled) {
|
|
deadProp.overridden = true; // previous occurrences overridden
|
|
} else {
|
|
aUsedProps[prop.name] = [];
|
|
}
|
|
|
|
prop.wasInherited = aInherit ? true : false;
|
|
// all occurrences of a property seen so far, by name
|
|
aUsedProps[prop.name].push(prop);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sort given properties in lexical order by name.
|
|
*
|
|
* @param properties
|
|
* An array of properties.
|
|
* @returns sorted array.
|
|
*/
|
|
sortProperties: function CSS_sortProperties(properties)
|
|
{
|
|
properties.sort(function(a, b)
|
|
{
|
|
if (a.name < b.name) {
|
|
return -1;
|
|
}
|
|
if (a.name > b.name) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get properties for a given element and push them to the rules array.
|
|
*
|
|
* @param aNode
|
|
* a DOM node
|
|
* @param rules
|
|
* An array of rules to add properties to.
|
|
* @param usedProps
|
|
* Object of arrays keyed by property name.
|
|
* @param inherit
|
|
* boolean determining whether or not we're in inherit mode
|
|
*/
|
|
getStyleProperties: function CSS_getStyleProperties(aNode, aRules, aUsedProps, aInherit)
|
|
{
|
|
let properties = this.parseCSSProperties(aNode.style, aInherit);
|
|
|
|
this.sortProperties(properties);
|
|
this.markOverriddenProperties(properties, aUsedProps, aInherit);
|
|
|
|
if (properties.length) {
|
|
aRules.push({rule: aNode, selector: "element.style",
|
|
properties: properties, inherited: aInherit});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get properties for a given rule.
|
|
*
|
|
* @param aRule
|
|
* A Rule from a stylesheet.
|
|
*/
|
|
getRuleProperties: function CSS_getRuleProperties(aRule)
|
|
{
|
|
let style = aRule.style;
|
|
return this.parseCSSProperties(style);
|
|
},
|
|
|
|
/**
|
|
* Recursively get rules for an element's parents and add them to the
|
|
* sections array.
|
|
*
|
|
* @param aNode
|
|
* an element in a DOM tree.
|
|
* @param sections
|
|
* an array of sections
|
|
* @param usedProps
|
|
* Object of arrays keyed by property name.
|
|
*/
|
|
getInheritedRules: function CSS_getInheritedRules(aNode, aSections, aUsedProps)
|
|
{
|
|
let parent = aNode.parentNode;
|
|
if (parent && parent.nodeType == 1) {
|
|
this.getInheritedRules(parent, aSections, aUsedProps);
|
|
|
|
let rules = [];
|
|
this.getElementRules(parent, rules, aUsedProps, true);
|
|
|
|
if (rules.length) {
|
|
aSections.unshift({element: parent, rules: rules});
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get the CSS style rules for a given node in the DOM and append them to the
|
|
* rules array.
|
|
*
|
|
* @param aNode
|
|
* an element in the DOM tree.
|
|
* @param aRules
|
|
* an array of rules.
|
|
* @param aUsedProps
|
|
* Object of arrays keyed by property name.
|
|
* @param aInherit
|
|
* boolean indicating whether we are in an inherited mode or not
|
|
*/
|
|
getElementRules: function CSS_getElementRules(aNode, aRules, aUsedProps, aInherit)
|
|
{
|
|
let inspectedRules;
|
|
|
|
try {
|
|
inspectedRules = this.domUtils.getCSSStyleRules(aNode);
|
|
} catch (ex) {
|
|
Services.console.logStringMessage(ex);
|
|
}
|
|
|
|
if (!inspectedRules)
|
|
return;
|
|
|
|
for (let i = 0; i < inspectedRules.Count(); ++i) {
|
|
let rule = inspectedRules.GetElementAt(i);
|
|
let href = rule.parentStyleSheet.href;
|
|
|
|
if (!href) {
|
|
// Null href means inline style.
|
|
href = aNode.ownerDocument.location.href;
|
|
}
|
|
|
|
let isSystemSheet = this.isSystemStyleSheet(rule.parentStyleSheet);
|
|
|
|
if (isSystemSheet)
|
|
continue;
|
|
|
|
let properties = this.getRuleProperties(rule, aInherit);
|
|
if (aInherit && !properties.length)
|
|
continue;
|
|
|
|
let line = this.domUtils.getRuleLine(rule);
|
|
let ruleId = rule.selectorText + " " + href + " (" + line + ")";
|
|
|
|
let sourceLink = "view-source:" + href + "#" + line;
|
|
|
|
this.markOverriddenProperties(properties, aUsedProps, aInherit);
|
|
|
|
aRules.unshift(
|
|
{rule: rule,
|
|
id: ruleId,
|
|
selector: rule.selectorText,
|
|
properties: properties,
|
|
inherited: aInherit,
|
|
sourceLink: sourceLink,
|
|
isSystemSheet: isSystemSheet});
|
|
}
|
|
|
|
if (aNode.style) {
|
|
this.getStyleProperties(aNode, aRules, aUsedProps, aInherit);
|
|
}
|
|
},
|
|
};
|