1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 * @param {!WebInspector.SectionCascade} cascade
8 * @param {?WebInspector.CSSRule} rule
9 * @param {!WebInspector.CSSStyleDeclaration} style
10 * @param {string} customSelectorText
11 * @param {?WebInspector.DOMNode=} inheritedFromNode
13 WebInspector.StylesSectionModel = function(cascade, rule, style, customSelectorText, inheritedFromNode)
15 this._cascade = cascade;
18 this._customSelectorText = customSelectorText;
19 this._editable = !!(this._style && this._style.styleSheetId);
20 this._inheritedFromNode = inheritedFromNode || null;
23 WebInspector.StylesSectionModel.prototype = {
25 * @return {!WebInspector.SectionCascade}
35 hasMatchingSelectors: function()
37 return this.rule() ? this.rule().matchingSelectors.length > 0 && this.mediaMatches() : true;
43 mediaMatches: function()
45 var media = this.media();
46 for (var i = 0; media && i < media.length; ++i) {
47 if (!media[i].active())
58 return !!this._inheritedFromNode;
62 * @return {?WebInspector.DOMNode}
64 parentNode: function()
66 return this._inheritedFromNode;
72 selectorText: function()
74 if (this._customSelectorText)
75 return this._customSelectorText;
76 return this.rule() ? this.rule().selectorText() : "";
84 return this._editable;
88 * @param {boolean} editable
90 setEditable: function(editable)
92 this._editable = editable;
96 * @return {!WebInspector.CSSStyleDeclaration}
104 * @return {?WebInspector.CSSRule}
112 * @return {?Array.<!WebInspector.CSSMedia>}
116 return this.rule() ? this.rule().media : null;
120 * @param {!WebInspector.CSSRule} rule
122 updateRule: function(rule)
125 this._style = rule.style;
126 this._cascade._resetUsedProperties();
129 resetCachedData: function()
131 this._cascade._resetUsedProperties();
135 * @param {string} propertyName
138 isPropertyInCascade: function(propertyName)
140 if (!this.hasMatchingSelectors())
142 if (this.inherited() && !WebInspector.CSSMetadata.isPropertyInherited(propertyName))
148 * @param {string} propertyName
151 isPropertyOverloaded: function(propertyName)
153 if (!this.isPropertyInCascade(propertyName))
155 var usedProperties = this._cascade._usedPropertiesForModel(this);
156 var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(propertyName);
157 return !usedProperties.has(canonicalName);
164 WebInspector.SectionCascade = function()
167 this._resetUsedProperties();
170 WebInspector.SectionCascade.prototype = {
172 * @return {!Array.<!WebInspector.StylesSectionModel>}
174 sectionModels: function()
180 * @param {!WebInspector.CSSRule} rule
181 * @param {?WebInspector.DOMNode=} inheritedFromNode
182 * @return {!WebInspector.StylesSectionModel}
184 appendModelFromRule: function(rule, inheritedFromNode)
186 return this._insertModel(new WebInspector.StylesSectionModel(this, rule, rule.style, "", inheritedFromNode));
190 * @param {!WebInspector.CSSRule} rule
191 * @param {!WebInspector.StylesSectionModel} insertAfterStyleRule
192 * @return {!WebInspector.StylesSectionModel}
194 insertModelFromRule: function(rule, insertAfterStyleRule)
196 return this._insertModel(new WebInspector.StylesSectionModel(this, rule, rule.style, "", null), insertAfterStyleRule);
200 * @param {!WebInspector.CSSStyleDeclaration} style
201 * @param {string} selectorText
202 * @param {?WebInspector.DOMNode=} inheritedFromNode
203 * @return {!WebInspector.StylesSectionModel}
205 appendModelFromStyle: function(style, selectorText, inheritedFromNode)
207 return this._insertModel(new WebInspector.StylesSectionModel(this, null, style, selectorText, inheritedFromNode));
211 * @return {!Set.<string>}
213 allUsedProperties: function()
215 this._recomputeUsedPropertiesIfNeeded();
216 return this._allUsedProperties;
220 * @param {!WebInspector.StylesSectionModel} model
221 * @param {!WebInspector.StylesSectionModel=} insertAfter
222 * @return {!WebInspector.StylesSectionModel}
224 _insertModel: function(model, insertAfter)
227 var index = this._models.indexOf(insertAfter);
228 console.assert(index !== -1, "The insertAfter anchor could not be found in cascade");
229 this._models.splice(index + 1, 0, model);
231 this._models.push(model);
233 this._resetUsedProperties();
237 _recomputeUsedPropertiesIfNeeded: function()
239 if (this._usedPropertiesPerModel.size > 0)
241 var usedProperties = WebInspector.SectionCascade._computeUsedProperties(this._models, this._allUsedProperties);
242 for (var i = 0; i < usedProperties.length; ++i)
243 this._usedPropertiesPerModel.set(this._models[i], usedProperties[i]);
246 _resetUsedProperties: function()
248 /** @type {!Set.<string>} */
249 this._allUsedProperties = new Set();
250 /** @type {!Map.<!WebInspector.StylesSectionModel, !Set.<string>>} */
251 this._usedPropertiesPerModel = new Map();
255 * @param {!WebInspector.StylesSectionModel} model
256 * @return {!Set.<string>}
258 _usedPropertiesForModel: function(model)
260 this._recomputeUsedPropertiesIfNeeded();
261 return /**@type {!Set.<string>}*/ (this._usedPropertiesPerModel.get(model));
266 * @param {!Array.<!WebInspector.StylesSectionModel>} styleRules
267 * @param {!Set.<string>} allUsedProperties
268 * @return {!Array.<!Set.<string>>}
270 WebInspector.SectionCascade._computeUsedProperties = function(styleRules, allUsedProperties)
272 /** @type {!Set.<string>} */
273 var foundImportantProperties = new Set();
274 /** @type {!Map.<string, !Set.<string>>} */
275 var propertyToEffectiveRule = new Map();
276 /** @type {!Map.<string, !WebInspector.DOMNode>} */
277 var inheritedPropertyToNode = new Map();
278 var stylesUsedProperties = [];
279 for (var i = 0; i < styleRules.length; ++i) {
280 var styleRule = styleRules[i];
281 /** @type {!Set.<string>} */
282 var styleRuleUsedProperties = new Set();
283 stylesUsedProperties.push(styleRuleUsedProperties);
284 if (!styleRule.hasMatchingSelectors())
287 var style = styleRule.style();
288 var allProperties = style.allProperties;
289 for (var j = 0; j < allProperties.length; ++j) {
290 var property = allProperties[j];
291 if (!property.activeInStyle())
294 // Do not pick non-inherited properties from inherited styles.
295 if (styleRule.inherited() && !WebInspector.CSSMetadata.isPropertyInherited(property.name))
298 var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(property.name);
299 if (foundImportantProperties.has(canonicalName))
302 if (!property.important && allUsedProperties.has(canonicalName))
305 var isKnownProperty = propertyToEffectiveRule.has(canonicalName);
306 var parentNode = styleRule.parentNode();
307 if (!isKnownProperty && parentNode && !inheritedPropertyToNode.has(canonicalName))
308 inheritedPropertyToNode.set(canonicalName, parentNode);
310 if (property.important) {
311 if (styleRule.inherited() && isKnownProperty && styleRule.parentNode() !== inheritedPropertyToNode.get(canonicalName))
314 foundImportantProperties.add(canonicalName);
316 propertyToEffectiveRule.get(canonicalName).delete(canonicalName);
319 styleRuleUsedProperties.add(canonicalName);
320 allUsedProperties.add(canonicalName);
321 propertyToEffectiveRule.set(canonicalName, styleRuleUsedProperties);
324 // If every longhand of the shorthand is not active, then the shorthand is not active too.
325 for (var property of style.leadingProperties()) {
326 var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(property.name);
327 if (!styleRuleUsedProperties.has(canonicalName))
329 var longhands = style.longhandProperties(property.name);
330 if (!longhands.length)
333 for (var longhand of longhands) {
334 var longhandCanonicalName = WebInspector.CSSMetadata.canonicalPropertyName(longhand.name);
335 notUsed = notUsed && !styleRuleUsedProperties.has(longhandCanonicalName);
339 styleRuleUsedProperties.delete(canonicalName);
340 allUsedProperties.delete(canonicalName);
343 return stylesUsedProperties;