gecko/browser/base/content/stylePanel.jsm

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