Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / devtools / front_end / elements / StylesSectionModel.js
blobdc9e52434a8f9796deecd432da7d70ec21903bcf
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.
5 /**
6  * @constructor
7  * @param {!WebInspector.SectionCascade} cascade
8  * @param {?WebInspector.CSSRule} rule
9  * @param {!WebInspector.CSSStyleDeclaration} style
10  * @param {string} customSelectorText
11  * @param {?WebInspector.DOMNode=} inheritedFromNode
12  */
13 WebInspector.StylesSectionModel = function(cascade, rule, style, customSelectorText, inheritedFromNode)
15     this._cascade = cascade;
16     this._rule = rule;
17     this._style = style;
18     this._customSelectorText = customSelectorText;
19     this._editable = !!(this._style && this._style.styleSheetId);
20     this._inheritedFromNode = inheritedFromNode || null;
23 WebInspector.StylesSectionModel.prototype = {
24     /**
25      * @return {!WebInspector.SectionCascade}
26      */
27     cascade: function()
28     {
29         return this._cascade;
30     },
32     /**
33      * @return {boolean}
34      */
35     hasMatchingSelectors: function()
36     {
37         return this.rule() ? this.rule().matchingSelectors.length > 0 && this.mediaMatches() : true;
38     },
40     /**
41      * @return {boolean}
42      */
43     mediaMatches: function()
44     {
45         var media = this.media();
46         for (var i = 0; media && i < media.length; ++i) {
47             if (!media[i].active())
48                 return false;
49         }
50         return true;
51     },
53     /**
54      * @return {boolean}
55      */
56     inherited: function()
57     {
58         return !!this._inheritedFromNode;
59     },
61     /**
62      * @return {?WebInspector.DOMNode}
63      */
64     parentNode: function()
65     {
66         return this._inheritedFromNode;
67     },
69     /**
70      * @return {string}
71      */
72     selectorText: function()
73     {
74         if (this._customSelectorText)
75             return this._customSelectorText;
76         return this.rule() ? this.rule().selectorText() : "";
77     },
79     /**
80      * @return {boolean}
81      */
82     editable: function()
83     {
84         return this._editable;
85     },
87     /**
88      * @param {boolean} editable
89      */
90     setEditable: function(editable)
91     {
92         this._editable = editable;
93     },
95     /**
96      * @return {!WebInspector.CSSStyleDeclaration}
97      */
98     style: function()
99     {
100         return this._style;
101     },
103     /**
104      * @return {?WebInspector.CSSRule}
105      */
106     rule: function()
107     {
108         return this._rule;
109     },
111     /**
112      * @return {?Array.<!WebInspector.CSSMedia>}
113      */
114     media: function()
115     {
116         return this.rule() ? this.rule().media : null;
117     },
119     /**
120      * @param {!WebInspector.CSSRule} rule
121      */
122     updateRule: function(rule)
123     {
124         this._rule = rule;
125         this._style = rule.style;
126         this._cascade._resetUsedProperties();
127     },
129     resetCachedData: function()
130     {
131         this._cascade._resetUsedProperties();
132     },
134     /**
135      * @param {string} propertyName
136      * @return {boolean}
137      */
138     isPropertyInCascade: function(propertyName)
139     {
140         if (!this.hasMatchingSelectors())
141             return false;
142         if (this.inherited() && !WebInspector.CSSMetadata.isPropertyInherited(propertyName))
143             return false;
144         return true;
145     },
147     /**
148      * @param {string} propertyName
149      * @return {boolean}
150      */
151     isPropertyOverloaded: function(propertyName)
152     {
153         if (!this.isPropertyInCascade(propertyName))
154             return false;
155         var usedProperties = this._cascade._usedPropertiesForModel(this);
156         var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(propertyName);
157         return !usedProperties.has(canonicalName);
158     }
162  * @constructor
163  */
164 WebInspector.SectionCascade = function()
166     this._models = [];
167     this._resetUsedProperties();
170 WebInspector.SectionCascade.prototype = {
171     /**
172      * @return {!Array.<!WebInspector.StylesSectionModel>}
173      */
174     sectionModels: function()
175     {
176         return this._models;
177     },
179     /**
180      * @param {!WebInspector.CSSRule} rule
181      * @param {?WebInspector.DOMNode=} inheritedFromNode
182      * @return {!WebInspector.StylesSectionModel}
183      */
184     appendModelFromRule: function(rule, inheritedFromNode)
185     {
186         return this._insertModel(new WebInspector.StylesSectionModel(this, rule, rule.style, "", inheritedFromNode));
187     },
189     /**
190      * @param {!WebInspector.CSSRule} rule
191      * @param {!WebInspector.StylesSectionModel} insertAfterStyleRule
192      * @return {!WebInspector.StylesSectionModel}
193      */
194     insertModelFromRule: function(rule, insertAfterStyleRule)
195     {
196         return this._insertModel(new WebInspector.StylesSectionModel(this, rule, rule.style, "", null), insertAfterStyleRule);
197     },
199     /**
200      * @param {!WebInspector.CSSStyleDeclaration} style
201      * @param {string} selectorText
202      * @param {?WebInspector.DOMNode=} inheritedFromNode
203      * @return {!WebInspector.StylesSectionModel}
204      */
205     appendModelFromStyle: function(style, selectorText, inheritedFromNode)
206     {
207         return this._insertModel(new WebInspector.StylesSectionModel(this, null, style, selectorText, inheritedFromNode));
208     },
210     /**
211      * @return {!Set.<string>}
212      */
213     allUsedProperties: function()
214     {
215         this._recomputeUsedPropertiesIfNeeded();
216         return this._allUsedProperties;
217     },
219     /**
220      * @param {!WebInspector.StylesSectionModel} model
221      * @param {!WebInspector.StylesSectionModel=} insertAfter
222      * @return {!WebInspector.StylesSectionModel}
223      */
224     _insertModel: function(model, insertAfter)
225     {
226         if (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);
230         } else {
231             this._models.push(model);
232         }
233         this._resetUsedProperties();
234         return model;
235     },
237     _recomputeUsedPropertiesIfNeeded: function()
238     {
239         if (this._usedPropertiesPerModel.size > 0)
240             return;
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]);
244     },
246     _resetUsedProperties: function()
247     {
248         /** @type {!Set.<string>} */
249         this._allUsedProperties = new Set();
250         /** @type {!Map.<!WebInspector.StylesSectionModel, !Set.<string>>} */
251         this._usedPropertiesPerModel = new Map();
252     },
254     /**
255      * @param {!WebInspector.StylesSectionModel} model
256      * @return {!Set.<string>}
257      */
258     _usedPropertiesForModel: function(model)
259     {
260         this._recomputeUsedPropertiesIfNeeded();
261         return /**@type {!Set.<string>}*/ (this._usedPropertiesPerModel.get(model));
262     }
266  * @param {!Array.<!WebInspector.StylesSectionModel>} styleRules
267  * @param {!Set.<string>} allUsedProperties
268  * @return {!Array.<!Set.<string>>}
269  */
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())
285             continue;
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())
292                 continue;
294             // Do not pick non-inherited properties from inherited styles.
295             if (styleRule.inherited() && !WebInspector.CSSMetadata.isPropertyInherited(property.name))
296                 continue;
298             var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(property.name);
299             if (foundImportantProperties.has(canonicalName))
300                 continue;
302             if (!property.important && allUsedProperties.has(canonicalName))
303                 continue;
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))
312                     continue;
314                 foundImportantProperties.add(canonicalName);
315                 if (isKnownProperty)
316                     propertyToEffectiveRule.get(canonicalName).delete(canonicalName);
317             }
319             styleRuleUsedProperties.add(canonicalName);
320             allUsedProperties.add(canonicalName);
321             propertyToEffectiveRule.set(canonicalName, styleRuleUsedProperties);
322         }
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))
328                 continue;
329             var longhands = style.longhandProperties(property.name);
330             if (!longhands.length)
331                 continue;
332             var notUsed = true;
333             for (var longhand of longhands) {
334                 var longhandCanonicalName = WebInspector.CSSMetadata.canonicalPropertyName(longhand.name);
335                 notUsed = notUsed && !styleRuleUsedProperties.has(longhandCanonicalName);
336             }
337             if (!notUsed)
338                 continue;
339             styleRuleUsedProperties.delete(canonicalName);
340             allUsedProperties.delete(canonicalName);
341         }
342     }
343     return stylesUsedProperties;