95d99eab2c2fbb076dff574af228070f814b136a
[WebKit-https.git] / Source / WebCore / inspector / front-end / CSSStyleModel.js
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.Object}
34  * @param {WebInspector.Workspace} workspace
35  */
36 WebInspector.CSSStyleModel = function(workspace)
37 {
38     this._workspace = workspace;
39     this._pendingCommandsMajorState = [];
40     /** @type {Array.<WebInspector.CSSStyleModel.LiveLocation>} */
41     this._locations = [];
42     this._sourceMappings = {};
43     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoRequested, this._undoRedoRequested, this);
44     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.UndoRedoCompleted, this._undoRedoCompleted, this);
45     this._resourceBinding = new WebInspector.CSSStyleModelResourceBinding();
46     WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameCreatedOrNavigated, this._mainFrameCreatedOrNavigated, this);
47     this._namedFlowCollections = {};
48     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._resetNamedFlowCollections, this);
49     InspectorBackend.registerCSSDispatcher(new WebInspector.CSSDispatcher(this));
50     CSSAgent.enable();
51 }
52
53 /**
54  * @param {Array.<CSSAgent.CSSRule>} ruleArray
55  */
56 WebInspector.CSSStyleModel.parseRuleArrayPayload = function(ruleArray)
57 {
58     var result = [];
59     for (var i = 0; i < ruleArray.length; ++i)
60         result.push(WebInspector.CSSRule.parsePayload(ruleArray[i]));
61     return result;
62 }
63     
64 /**
65  * @param {Array.<CSSAgent.RuleMatch>} matchArray
66  */
67 WebInspector.CSSStyleModel.parseRuleMatchArrayPayload = function(matchArray)
68 {
69     var result = [];
70     for (var i = 0; i < matchArray.length; ++i)
71         result.push(WebInspector.CSSRule.parsePayload(matchArray[i].rule, matchArray[i].matchingSelectors));
72     return result;
73 }
74
75 WebInspector.CSSStyleModel.Events = {
76     StyleSheetChanged: "StyleSheetChanged",
77     MediaQueryResultChanged: "MediaQueryResultChanged",
78     NamedFlowCreated: "NamedFlowCreated",
79     NamedFlowRemoved: "NamedFlowRemoved",
80     RegionLayoutUpdated: "RegionLayoutUpdated"
81 }
82
83 WebInspector.CSSStyleModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"];
84
85 WebInspector.CSSStyleModel.prototype = {
86     /**
87      * @param {DOMAgent.NodeId} nodeId
88      * @param {boolean} needPseudo
89      * @param {boolean} needInherited
90      * @param {function(?*)} userCallback
91      */
92     getMatchedStylesAsync: function(nodeId, needPseudo, needInherited, userCallback)
93     {
94         /**
95          * @param {function(?*)} userCallback
96          * @param {?Protocol.Error} error
97          * @param {Array.<CSSAgent.RuleMatch>=} matchedPayload
98          * @param {Array.<CSSAgent.PseudoIdMatches>=} pseudoPayload
99          * @param {Array.<CSSAgent.InheritedStyleEntry>=} inheritedPayload
100          */
101         function callback(userCallback, error, matchedPayload, pseudoPayload, inheritedPayload)
102         {
103             if (error) {
104                 if (userCallback)
105                     userCallback(null);
106                 return;
107             }
108
109             var result = {};
110             if (matchedPayload)
111                 result.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(matchedPayload);
112
113             if (pseudoPayload) {
114                 result.pseudoElements = [];
115                 for (var i = 0; i < pseudoPayload.length; ++i) {
116                     var entryPayload = pseudoPayload[i];
117                     result.pseudoElements.push({ pseudoId: entryPayload.pseudoId, rules: WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matches) });
118                 }
119             }
120
121             if (inheritedPayload) {
122                 result.inherited = [];
123                 for (var i = 0; i < inheritedPayload.length; ++i) {
124                     var entryPayload = inheritedPayload[i];
125                     var entry = {};
126                     if (entryPayload.inlineStyle)
127                         entry.inlineStyle = WebInspector.CSSStyleDeclaration.parsePayload(entryPayload.inlineStyle);
128                     if (entryPayload.matchedCSSRules)
129                         entry.matchedCSSRules = WebInspector.CSSStyleModel.parseRuleMatchArrayPayload(entryPayload.matchedCSSRules);
130                     result.inherited.push(entry);
131                 }
132             }
133
134             if (userCallback)
135                 userCallback(result);
136         }
137
138         CSSAgent.getMatchedStylesForNode(nodeId, needPseudo, needInherited, callback.bind(null, userCallback));
139     },
140
141     /**
142      * @param {DOMAgent.NodeId} nodeId
143      * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback
144      */
145     getComputedStyleAsync: function(nodeId, userCallback)
146     {
147         /**
148          * @param {function(?WebInspector.CSSStyleDeclaration)} userCallback
149          */
150         function callback(userCallback, error, computedPayload)
151         {
152             if (error || !computedPayload)
153                 userCallback(null);
154             else
155                 userCallback(WebInspector.CSSStyleDeclaration.parseComputedStylePayload(computedPayload));
156         }
157
158         CSSAgent.getComputedStyleForNode(nodeId, callback.bind(null, userCallback));
159     },
160
161     /**
162      * @param {DOMAgent.NodeId} nodeId
163      * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback
164      */
165     getInlineStylesAsync: function(nodeId, userCallback)
166     {
167         /**
168          * @param {function(?WebInspector.CSSStyleDeclaration, ?WebInspector.CSSStyleDeclaration)} userCallback
169          * @param {?Protocol.Error} error
170          * @param {?CSSAgent.CSSStyle=} inlinePayload
171          * @param {?CSSAgent.CSSStyle=} attributesStylePayload
172          */
173         function callback(userCallback, error, inlinePayload, attributesStylePayload)
174         {
175             if (error || !inlinePayload)
176                 userCallback(null, null);
177             else
178                 userCallback(WebInspector.CSSStyleDeclaration.parsePayload(inlinePayload), attributesStylePayload ? WebInspector.CSSStyleDeclaration.parsePayload(attributesStylePayload) : null);
179         }
180
181         CSSAgent.getInlineStylesForNode(nodeId, callback.bind(null, userCallback));
182     },
183
184     /**
185      * @param {DOMAgent.NodeId} nodeId
186      * @param {?Array.<string>|undefined} forcedPseudoClasses
187      * @param {function()=} userCallback
188      */
189     forcePseudoState: function(nodeId, forcedPseudoClasses, userCallback)
190     {
191         CSSAgent.forcePseudoState(nodeId, forcedPseudoClasses || [], userCallback);
192     },
193
194     /**
195      * @param {DOMAgent.NodeId} documentNodeId
196      * @param {function(?WebInspector.NamedFlowCollection)} userCallback
197      */
198     getNamedFlowCollectionAsync: function(documentNodeId, userCallback)
199     {
200         var namedFlowCollection = this._namedFlowCollections[documentNodeId];
201         if (namedFlowCollection) {
202             userCallback(namedFlowCollection);
203             return;
204         }
205
206         /**
207          * @param {function(?WebInspector.NamedFlowCollection)} userCallback
208          * @param {?Protocol.Error} error
209          * @param {?Array.<CSSAgent.NamedFlow>} namedFlowPayload
210          */
211         function callback(userCallback, error, namedFlowPayload)
212         {
213             if (error || !namedFlowPayload)
214                 userCallback(null);
215             else {
216                 var namedFlowCollection = new WebInspector.NamedFlowCollection(namedFlowPayload);
217                 this._namedFlowCollections[documentNodeId] = namedFlowCollection;
218                 userCallback(namedFlowCollection);
219             }
220         }
221
222         CSSAgent.getNamedFlowCollection(documentNodeId, callback.bind(this, userCallback));
223     },
224
225     /**
226      * @param {DOMAgent.NodeId} documentNodeId
227      * @param {string} flowName
228      * @param {function(?WebInspector.NamedFlow)} userCallback
229      */
230     getFlowByNameAsync: function(documentNodeId, flowName, userCallback)
231     {
232         var namedFlowCollection = this._namedFlowCollections[documentNodeId];
233         if (namedFlowCollection) {
234             userCallback(namedFlowCollection.flowByName(flowName));
235             return;
236         }
237
238         /**
239          * @param {function(?WebInspector.NamedFlow)} userCallback
240          * @param {?WebInspector.NamedFlowCollection} namedFlowCollection
241          */
242         function callback(userCallback, namedFlowCollection)
243         {
244             if (!namedFlowCollection)
245                 userCallback(null);
246             else
247                 userCallback(namedFlowCollection.flowByName(flowName));
248         }
249
250         this.getNamedFlowCollectionAsync(documentNodeId, callback.bind(this, userCallback));
251     },
252
253     /**
254      * @param {CSSAgent.CSSRuleId} ruleId
255      * @param {DOMAgent.NodeId} nodeId
256      * @param {string} newSelector
257      * @param {function(WebInspector.CSSRule, boolean)} successCallback
258      * @param {function()} failureCallback
259      */
260     setRuleSelector: function(ruleId, nodeId, newSelector, successCallback, failureCallback)
261     {
262         /**
263          * @param {DOMAgent.NodeId} nodeId
264          * @param {function(WebInspector.CSSRule, boolean)} successCallback
265          * @param {CSSAgent.CSSRule} rulePayload
266          * @param {?Array.<DOMAgent.NodeId>} selectedNodeIds
267          */
268         function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
269         {
270             if (!selectedNodeIds)
271                 return;
272             var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
273             var rule = WebInspector.CSSRule.parsePayload(rulePayload);
274             successCallback(rule, doesAffectSelectedNode);
275         }
276
277         /**
278          * @param {DOMAgent.NodeId} nodeId
279          * @param {function(WebInspector.CSSRule, boolean)} successCallback
280          * @param {function()} failureCallback
281          * @param {?Protocol.Error} error
282          * @param {string} newSelector
283          * @param {?CSSAgent.CSSRule} rulePayload
284          */
285         function callback(nodeId, successCallback, failureCallback, newSelector, error, rulePayload)
286         {
287             this._pendingCommandsMajorState.pop();
288             if (error)
289                 failureCallback();
290             else {
291                 WebInspector.domAgent.markUndoableState();
292                 var ownerDocumentId = this._ownerDocumentId(nodeId);
293                 if (ownerDocumentId)
294                     WebInspector.domAgent.querySelectorAll(ownerDocumentId, newSelector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
295                 else
296                     failureCallback();
297             }
298         }
299
300         this._pendingCommandsMajorState.push(true);
301         CSSAgent.setRuleSelector(ruleId, newSelector, callback.bind(this, nodeId, successCallback, failureCallback, newSelector));
302     },
303
304     /**
305      * @param {DOMAgent.NodeId} nodeId
306      * @param {string} selector
307      * @param {function(WebInspector.CSSRule, boolean)} successCallback
308      * @param {function()} failureCallback
309      */
310     addRule: function(nodeId, selector, successCallback, failureCallback)
311     {
312         /**
313          * @param {DOMAgent.NodeId} nodeId
314          * @param {function(WebInspector.CSSRule, boolean)} successCallback
315          * @param {CSSAgent.CSSRule} rulePayload
316          * @param {?Array.<DOMAgent.NodeId>} selectedNodeIds
317          */
318         function checkAffectsCallback(nodeId, successCallback, rulePayload, selectedNodeIds)
319         {
320             if (!selectedNodeIds)
321                 return;
322
323             var doesAffectSelectedNode = (selectedNodeIds.indexOf(nodeId) >= 0);
324             var rule = WebInspector.CSSRule.parsePayload(rulePayload);
325             successCallback(rule, doesAffectSelectedNode);
326         }
327
328         /**
329          * @param {function(WebInspector.CSSRule, boolean)} successCallback
330          * @param {function()} failureCallback
331          * @param {string} selector
332          * @param {?Protocol.Error} error
333          * @param {?CSSAgent.CSSRule} rulePayload
334          */
335         function callback(successCallback, failureCallback, selector, error, rulePayload)
336         {
337             this._pendingCommandsMajorState.pop();
338             if (error) {
339                 // Invalid syntax for a selector
340                 failureCallback();
341             } else {
342                 WebInspector.domAgent.markUndoableState();
343                 var ownerDocumentId = this._ownerDocumentId(nodeId);
344                 if (ownerDocumentId)
345                     WebInspector.domAgent.querySelectorAll(ownerDocumentId, selector, checkAffectsCallback.bind(this, nodeId, successCallback, rulePayload));
346                 else
347                     failureCallback();
348             }
349         }
350
351         this._pendingCommandsMajorState.push(true);
352         CSSAgent.addRule(nodeId, selector, callback.bind(this, successCallback, failureCallback, selector));
353     },
354
355     mediaQueryResultChanged: function()
356     {
357         this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged);
358     },
359
360     /**
361      * @param {DOMAgent.NodeId} nodeId
362      */
363     _ownerDocumentId: function(nodeId)
364     {
365         var node = WebInspector.domAgent.nodeForId(nodeId);
366         if (!node)
367             return null;
368         return node.ownerDocument ? node.ownerDocument.id : null;
369     },
370
371     /**
372      * @param {CSSAgent.StyleSheetId} styleSheetId
373      */
374     _fireStyleSheetChanged: function(styleSheetId)
375     {
376         if (!this._pendingCommandsMajorState.length)
377             return;
378
379         var majorChange = this._pendingCommandsMajorState[this._pendingCommandsMajorState.length - 1];
380
381         if (!majorChange || !styleSheetId || !this.hasEventListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged))
382             return;
383
384         this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, majorChange: majorChange });
385     },
386
387     /**
388      * @param {CSSAgent.NamedFlow} namedFlowPayload
389      */
390     _namedFlowCreated: function(namedFlowPayload)
391     {
392         var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
393         var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
394
395         if (!namedFlowCollection)
396             return;
397
398         namedFlowCollection._appendNamedFlow(namedFlow);
399         this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowCreated, namedFlow);
400     },
401
402     /**
403      * @param {DOMAgent.NodeId} documentNodeId
404      * @param {string} flowName
405      */
406     _namedFlowRemoved: function(documentNodeId, flowName)
407     {
408         var namedFlowCollection = this._namedFlowCollections[documentNodeId];
409
410         if (!namedFlowCollection)
411             return;
412
413         namedFlowCollection._removeNamedFlow(flowName);
414         this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, { documentNodeId: documentNodeId, flowName: flowName });
415     },
416
417     /**
418      * @param {CSSAgent.NamedFlow} namedFlowPayload
419      */
420     _regionLayoutUpdated: function(namedFlowPayload)
421     {
422         var namedFlow = WebInspector.NamedFlow.parsePayload(namedFlowPayload);
423         var namedFlowCollection = this._namedFlowCollections[namedFlow.documentNodeId];
424
425         if (!namedFlowCollection)
426             return;
427
428         namedFlowCollection._appendNamedFlow(namedFlow);
429         this.dispatchEventToListeners(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, namedFlow);
430     },
431
432     /**
433      * @param {CSSAgent.StyleSheetId} styleSheetId
434      * @param {string} newText
435      * @param {boolean} majorChange
436      * @param {function(?string)} userCallback
437      */
438     setStyleSheetText: function(styleSheetId, newText, majorChange, userCallback)
439     {
440         function callback(error)
441         {
442             this._pendingCommandsMajorState.pop();
443             if (!error && majorChange)
444                 WebInspector.domAgent.markUndoableState();
445             
446             if (!error && userCallback)
447                 userCallback(error);
448         }
449         this._pendingCommandsMajorState.push(majorChange);
450         CSSAgent.setStyleSheetText(styleSheetId, newText, callback.bind(this));
451     },
452
453     _undoRedoRequested: function()
454     {
455         this._pendingCommandsMajorState.push(true);
456     },
457
458     _undoRedoCompleted: function()
459     {
460         this._pendingCommandsMajorState.pop();
461     },
462
463     /**
464      * @param {WebInspector.CSSRule} rule
465      * @param {function(?WebInspector.Resource)} callback
466      */
467     getViaInspectorResourceForRule: function(rule, callback)
468     {
469         if (!rule.id) {
470             callback(null);
471             return;
472         }
473         this._resourceBinding._requestViaInspectorResource(rule.id.styleSheetId, callback);
474     },
475
476     /**
477      * @return {WebInspector.CSSStyleModelResourceBinding}
478      */
479     resourceBinding: function()
480     {
481         return this._resourceBinding;
482     },
483
484     /**
485      * @param {WebInspector.Event} event
486      */
487     _mainFrameCreatedOrNavigated: function(event)
488     {
489         this._resetSourceMappings();
490         this._resourceBinding._reset();
491     },
492
493     /**
494      * @param {string} url
495      * @param {WebInspector.SourceMapping} sourceMapping
496      */
497     setSourceMapping: function(url, sourceMapping)
498     {
499         if (sourceMapping)
500             this._sourceMappings[url] = sourceMapping;
501         else
502             delete this._sourceMappings[url];
503         this._updateLocations();
504     },
505
506     _resetSourceMappings: function()
507     {
508         this._sourceMappings = {};
509     },
510
511     _resetNamedFlowCollections: function()
512     {
513         this._namedFlowCollections = {};
514     },
515
516     _updateLocations: function()
517     {
518         for (var i = 0; i < this._locations.length; ++i)
519             this._locations[i].update();
520     },
521
522     /**
523      * @param {WebInspector.CSSRule} cssRule
524      * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
525      * @return {?WebInspector.LiveLocation}
526      */
527     createLiveLocation: function(cssRule, updateDelegate)
528     {
529         if (!cssRule._rawLocation)
530             return null;
531         var location = new WebInspector.CSSStyleModel.LiveLocation(cssRule._rawLocation, updateDelegate);
532         if (!location.uiLocation())
533             return null;
534         this._locations.push(location);
535         location.update();
536         return location;
537     },
538
539     /**
540      * @param {WebInspector.CSSLocation} rawLocation
541      * @return {?WebInspector.UILocation}
542      */
543     rawLocationToUILocation: function(rawLocation)
544     {
545         var sourceMapping = this._sourceMappings[rawLocation.url];
546         if (sourceMapping) {
547             var uiLocation = sourceMapping.rawLocationToUILocation(rawLocation);
548             if (uiLocation)
549                 return uiLocation;
550         }
551         var uiSourceCode = this._workspace.uiSourceCodeForURL(rawLocation.url);
552         if (!uiSourceCode)
553             return null;
554         return new WebInspector.UILocation(uiSourceCode, rawLocation.lineNumber, rawLocation.columnNumber);
555     },
556
557     __proto__: WebInspector.Object.prototype
558 }
559
560 /**
561  * @constructor
562  * @extends {WebInspector.LiveLocation}
563  * @param {WebInspector.CSSLocation} rawLocation
564  * @param {function(WebInspector.UILocation):(boolean|undefined)} updateDelegate
565  */
566 WebInspector.CSSStyleModel.LiveLocation = function(rawLocation, updateDelegate)
567 {
568     WebInspector.LiveLocation.call(this, rawLocation, updateDelegate);
569 }
570
571 WebInspector.CSSStyleModel.LiveLocation.prototype = {
572     /**
573      * @return {WebInspector.UILocation}
574      */
575     uiLocation: function()
576     {
577         var cssLocation = /** @type WebInspector.CSSLocation */ (this.rawLocation());
578         return WebInspector.cssModel.rawLocationToUILocation(cssLocation);
579     },
580
581     dispose: function()
582     {
583         WebInspector.LiveLocation.prototype.dispose.call(this);
584         var locations = WebInspector.cssModel._locations;
585         if (locations)
586             locations.remove(this);
587     },
588
589     __proto__: WebInspector.LiveLocation.prototype
590 }
591
592 /**
593  * @constructor
594  * @implements {WebInspector.RawLocation}
595  * @param {string} url
596  * @param {number} lineNumber
597  * @param {number=} columnNumber
598  */
599 WebInspector.CSSLocation = function(url, lineNumber, columnNumber)
600 {
601     this.url = url;
602     this.lineNumber = lineNumber;
603     this.columnNumber = columnNumber || 0;
604 }
605
606 /**
607  * @constructor
608  * @param {CSSAgent.CSSStyle} payload
609  */
610 WebInspector.CSSStyleDeclaration = function(payload)
611 {
612     this.id = payload.styleId;
613     this.width = payload.width;
614     this.height = payload.height;
615     this.range = payload.range;
616     this._shorthandValues = WebInspector.CSSStyleDeclaration.buildShorthandValueMap(payload.shorthandEntries);
617     this._livePropertyMap = {}; // LIVE properties (source-based or style-based) : { name -> CSSProperty }
618     this._allProperties = []; // ALL properties: [ CSSProperty ]
619     this.__disabledProperties = {}; // DISABLED properties: { index -> CSSProperty }
620     var payloadPropertyCount = payload.cssProperties.length;
621
622     var propertyIndex = 0;
623     for (var i = 0; i < payloadPropertyCount; ++i) {
624         var property = WebInspector.CSSProperty.parsePayload(this, i, payload.cssProperties[i]);
625         this._allProperties.push(property);
626         if (property.disabled)
627             this.__disabledProperties[i] = property;
628         if (!property.active && !property.styleBased)
629             continue;
630         var name = property.name;
631         this[propertyIndex] = name;
632         this._livePropertyMap[name] = property;
633         ++propertyIndex;
634     }
635     this.length = propertyIndex;
636     if ("cssText" in payload)
637         this.cssText = payload.cssText;
638 }
639
640 /**
641  * @param {Array.<CSSAgent.ShorthandEntry>} shorthandEntries
642  * @return {Object}
643  */
644 WebInspector.CSSStyleDeclaration.buildShorthandValueMap = function(shorthandEntries)
645 {
646     var result = {};
647     for (var i = 0; i < shorthandEntries.length; ++i)
648         result[shorthandEntries[i].name] = shorthandEntries[i].value;
649     return result;
650 }
651
652 /**
653  * @param {CSSAgent.CSSStyle} payload
654  * @return {WebInspector.CSSStyleDeclaration}
655  */
656 WebInspector.CSSStyleDeclaration.parsePayload = function(payload)
657 {
658     return new WebInspector.CSSStyleDeclaration(payload);
659 }
660
661 /**
662  * @param {Array.<CSSAgent.CSSComputedStyleProperty>} payload
663  * @return {WebInspector.CSSStyleDeclaration}
664  */
665 WebInspector.CSSStyleDeclaration.parseComputedStylePayload = function(payload)
666 {
667     var newPayload = /** @type {CSSAgent.CSSStyle} */ ({ cssProperties: [], shorthandEntries: [], width: "", height: "" });
668     if (payload)
669         newPayload.cssProperties = payload;
670
671     return new WebInspector.CSSStyleDeclaration(newPayload);
672 }
673
674 WebInspector.CSSStyleDeclaration.prototype = {
675     get allProperties()
676     {
677         return this._allProperties;
678     },
679
680     /**
681      * @param {string} name
682      * @return {WebInspector.CSSProperty|undefined}
683      */
684     getLiveProperty: function(name)
685     {
686         return this._livePropertyMap[name];
687     },
688
689     /**
690      * @param {string} name
691      * @return {string}
692      */
693     getPropertyValue: function(name)
694     {
695         var property = this._livePropertyMap[name];
696         return property ? property.value : "";
697     },
698
699     /**
700      * @param {string} name
701      * @return {string}
702      */
703     getPropertyPriority: function(name)
704     {
705         var property = this._livePropertyMap[name];
706         return property ? property.priority : "";
707     },
708
709     /**
710      * @param {string} name
711      * @return {boolean}
712      */
713     isPropertyImplicit: function(name)
714     {
715         var property = this._livePropertyMap[name];
716         return property ? property.implicit : "";
717     },
718
719     /**
720      * @param {string} name
721      * @return {Array.<WebInspector.CSSProperty>}
722      */
723     longhandProperties: function(name)
724     {
725         var longhands = WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(name);
726         var result = [];
727         for (var i = 0; longhands && i < longhands.length; ++i) {
728             var property = this._livePropertyMap[longhands[i]];
729             if (property)
730                 result.push(property);
731         }
732         return result;
733     },
734
735     /**
736      * @param {string} shorthandProperty
737      * @return {string}
738      */
739     shorthandValue: function(shorthandProperty)
740     {
741         return this._shorthandValues[shorthandProperty];
742     },
743
744     /**
745      * @param {number} index
746      * @return {?WebInspector.CSSProperty}
747      */
748     propertyAt: function(index)
749     {
750         return (index < this.allProperties.length) ? this.allProperties[index] : null;
751     },
752
753     /**
754      * @return {number}
755      */
756     pastLastSourcePropertyIndex: function()
757     {
758         for (var i = this.allProperties.length - 1; i >= 0; --i) {
759             var property = this.allProperties[i];
760             if (property.active || property.disabled)
761                 return i + 1;
762         }
763         return 0;
764     },
765
766     /**
767      * @param {number=} index
768      */
769     newBlankProperty: function(index)
770     {
771         index = (typeof index === "undefined") ? this.pastLastSourcePropertyIndex() : index;
772         return new WebInspector.CSSProperty(this, index, "", "", "", "active", true, false, "");
773     },
774
775     /**
776      * @param {number} index
777      * @param {string} name
778      * @param {string} value
779      * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
780      */
781     insertPropertyAt: function(index, name, value, userCallback)
782     {
783         /**
784          * @param {?string} error
785          * @param {CSSAgent.CSSStyle} payload
786          */
787         function callback(error, payload)
788         {
789             WebInspector.cssModel._pendingCommandsMajorState.pop();
790             if (!userCallback)
791                 return;
792
793             if (error) {
794                 console.error(error);
795                 userCallback(null);
796             } else {
797                 userCallback(WebInspector.CSSStyleDeclaration.parsePayload(payload));
798             }
799         }
800
801         if (!this.id)
802             throw "No style id";
803
804         WebInspector.cssModel._pendingCommandsMajorState.push(true);
805         CSSAgent.setPropertyText(this.id, index, name + ": " + value + ";", false, callback.bind(this));
806     },
807
808     /**
809      * @param {string} name
810      * @param {string} value
811      * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
812      */
813     appendProperty: function(name, value, userCallback)
814     {
815         this.insertPropertyAt(this.allProperties.length, name, value, userCallback);
816     }
817 }
818
819 /**
820  * @constructor
821  * @param {CSSAgent.CSSRule} payload
822  * @param {Array.<number>=} matchingSelectors
823  */
824 WebInspector.CSSRule = function(payload, matchingSelectors)
825 {
826     this.id = payload.ruleId;
827     if (matchingSelectors)
828         this.matchingSelectors = matchingSelectors;
829     this.selectors = payload.selectorList.selectors;
830     this.selectorText = this.selectors.join(", ");
831     this.selectorRange = payload.selectorList.range;
832     this.sourceLine = payload.sourceLine;
833     this.sourceURL = payload.sourceURL;
834     if (payload.sourceURL)
835         this._rawLocation = new WebInspector.CSSLocation(payload.sourceURL, payload.sourceLine);
836     this.origin = payload.origin;
837     this.style = WebInspector.CSSStyleDeclaration.parsePayload(payload.style);
838     this.style.parentRule = this;
839     if (payload.media)
840         this.media = WebInspector.CSSMedia.parseMediaArrayPayload(payload.media);
841 }
842
843 /**
844  * @param {CSSAgent.CSSRule} payload
845  * @param {Array.<number>=} matchingIndices
846  * @return {WebInspector.CSSRule}
847  */
848 WebInspector.CSSRule.parsePayload = function(payload, matchingIndices)
849 {
850     return new WebInspector.CSSRule(payload, matchingIndices);
851 }
852
853 WebInspector.CSSRule.prototype = {
854     get isUserAgent()
855     {
856         return this.origin === "user-agent";
857     },
858
859     get isUser()
860     {
861         return this.origin === "user";
862     },
863
864     get isViaInspector()
865     {
866         return this.origin === "inspector";
867     },
868
869     get isRegular()
870     {
871         return this.origin === "regular";
872     }
873 }
874
875 /**
876  * @constructor
877  * @param {?WebInspector.CSSStyleDeclaration} ownerStyle
878  * @param {number} index
879  * @param {string} name
880  * @param {string} value
881  * @param {?string} priority
882  * @param {string} status
883  * @param {boolean} parsedOk
884  * @param {boolean} implicit
885  * @param {?string=} text
886  * @param {CSSAgent.SourceRange=} range
887  */
888 WebInspector.CSSProperty = function(ownerStyle, index, name, value, priority, status, parsedOk, implicit, text, range)
889 {
890     this.ownerStyle = ownerStyle;
891     this.index = index;
892     this.name = name;
893     this.value = value;
894     this.priority = priority;
895     this.status = status;
896     this.parsedOk = parsedOk;
897     this.implicit = implicit;
898     this.text = text;
899     this.range = range;
900 }
901
902 /**
903  * @param {?WebInspector.CSSStyleDeclaration} ownerStyle
904  * @param {number} index
905  * @param {CSSAgent.CSSProperty} payload
906  * @return {WebInspector.CSSProperty}
907  */
908 WebInspector.CSSProperty.parsePayload = function(ownerStyle, index, payload)
909 {
910     // The following default field values are used in the payload:
911     // priority: ""
912     // parsedOk: true
913     // implicit: false
914     // status: "style"
915     var result = new WebInspector.CSSProperty(
916         ownerStyle, index, payload.name, payload.value, payload.priority || "", payload.status || "style", ("parsedOk" in payload) ? !!payload.parsedOk : true, !!payload.implicit, payload.text, payload.range);
917     return result;
918 }
919
920 WebInspector.CSSProperty.prototype = {
921     get propertyText()
922     {
923         if (this.text !== undefined)
924             return this.text;
925
926         if (this.name === "")
927             return "";
928         return this.name + ": " + this.value + (this.priority ? " !" + this.priority : "") + ";";
929     },
930
931     get isLive()
932     {
933         return this.active || this.styleBased;
934     },
935
936     get active()
937     {
938         return this.status === "active";
939     },
940
941     get styleBased()
942     {
943         return this.status === "style";
944     },
945
946     get inactive()
947     {
948         return this.status === "inactive";
949     },
950
951     get disabled()
952     {
953         return this.status === "disabled";
954     },
955
956     /**
957      * Replaces "propertyName: propertyValue [!important];" in the stylesheet by an arbitrary propertyText.
958      *
959      * @param {string} propertyText
960      * @param {boolean} majorChange
961      * @param {boolean} overwrite
962      * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
963      */
964     setText: function(propertyText, majorChange, overwrite, userCallback)
965     {
966         /**
967          * @param {?WebInspector.CSSStyleDeclaration} style
968          */
969         function enabledCallback(style)
970         {
971             if (userCallback)
972                 userCallback(style);
973         }
974
975         /**
976          * @param {?string} error
977          * @param {?CSSAgent.CSSStyle} stylePayload
978          */
979         function callback(error, stylePayload)
980         {
981             WebInspector.cssModel._pendingCommandsMajorState.pop();
982             if (!error) {
983                 if (majorChange)
984                     WebInspector.domAgent.markUndoableState();
985                 this.text = propertyText;
986                 var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
987                 var newProperty = style.allProperties[this.index];
988
989                 if (newProperty && this.disabled && !propertyText.match(/^\s*$/)) {
990                     newProperty.setDisabled(false, enabledCallback);
991                     return;
992                 }
993
994                 if (userCallback)
995                     userCallback(style);
996             } else {
997                 if (userCallback)
998                     userCallback(null);
999             }
1000         }
1001
1002         if (!this.ownerStyle)
1003             throw "No ownerStyle for property";
1004
1005         if (!this.ownerStyle.id)
1006             throw "No owner style id";
1007
1008         // An index past all the properties adds a new property to the style.
1009         WebInspector.cssModel._pendingCommandsMajorState.push(majorChange);
1010         CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, overwrite, callback.bind(this));
1011     },
1012
1013     /**
1014      * @param {string} newValue
1015      * @param {boolean} majorChange
1016      * @param {boolean} overwrite
1017      * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
1018      */
1019     setValue: function(newValue, majorChange, overwrite, userCallback)
1020     {
1021         var text = this.name + ": " + newValue + (this.priority ? " !" + this.priority : "") + ";"
1022         this.setText(text, majorChange, overwrite, userCallback);
1023     },
1024
1025     /**
1026      * @param {boolean} disabled
1027      * @param {function(?WebInspector.CSSStyleDeclaration)=} userCallback
1028      */
1029     setDisabled: function(disabled, userCallback)
1030     {
1031         if (!this.ownerStyle && userCallback)
1032             userCallback(null);
1033         if (disabled === this.disabled && userCallback)
1034             userCallback(this.ownerStyle);
1035
1036         /**
1037          * @param {?string} error
1038          * @param {CSSAgent.CSSStyle} stylePayload
1039          */
1040         function callback(error, stylePayload)
1041         {
1042             WebInspector.cssModel._pendingCommandsMajorState.pop();
1043             if (error) {
1044                 if (userCallback)
1045                     userCallback(null);
1046                 return;
1047             }
1048             WebInspector.domAgent.markUndoableState();
1049             if (userCallback) {
1050                 var style = WebInspector.CSSStyleDeclaration.parsePayload(stylePayload);
1051                 userCallback(style);
1052             }
1053         }
1054
1055         if (!this.ownerStyle.id)
1056             throw "No owner style id";
1057
1058         WebInspector.cssModel._pendingCommandsMajorState.push(false);
1059         CSSAgent.toggleProperty(this.ownerStyle.id, this.index, disabled, callback.bind(this));
1060     }
1061 }
1062
1063 /**
1064  * @constructor
1065  * @param {CSSAgent.CSSMedia} payload
1066  */
1067 WebInspector.CSSMedia = function(payload)
1068 {
1069     this.text = payload.text;
1070     this.source = payload.source;
1071     this.sourceURL = payload.sourceURL || "";
1072     this.sourceLine = typeof payload.sourceLine === "undefined" || this.source === "linkedSheet" ? -1 : payload.sourceLine;
1073 }
1074
1075 WebInspector.CSSMedia.Source = {
1076     LINKED_SHEET: "linkedSheet",
1077     INLINE_SHEET: "inlineSheet",
1078     MEDIA_RULE: "mediaRule",
1079     IMPORT_RULE: "importRule"
1080 };
1081
1082 /**
1083  * @param {CSSAgent.CSSMedia} payload
1084  * @return {WebInspector.CSSMedia}
1085  */
1086 WebInspector.CSSMedia.parsePayload = function(payload)
1087 {
1088     return new WebInspector.CSSMedia(payload);
1089 }
1090
1091 /**
1092  * @param {Array.<CSSAgent.CSSMedia>} payload
1093  * @return {Array.<WebInspector.CSSMedia>}
1094  */
1095 WebInspector.CSSMedia.parseMediaArrayPayload = function(payload)
1096 {
1097     var result = [];
1098     for (var i = 0; i < payload.length; ++i)
1099         result.push(WebInspector.CSSMedia.parsePayload(payload[i]));
1100     return result;
1101 }
1102
1103 /**
1104  * @constructor
1105  * @param {CSSAgent.CSSStyleSheetBody} payload
1106  */
1107 WebInspector.CSSStyleSheet = function(payload)
1108 {
1109     this.id = payload.styleSheetId;
1110     this.rules = [];
1111     this.styles = {};
1112     for (var i = 0; i < payload.rules.length; ++i) {
1113         var rule = WebInspector.CSSRule.parsePayload(payload.rules[i]);
1114         this.rules.push(rule);
1115         if (rule.style)
1116             this.styles[rule.style.id] = rule.style;
1117     }
1118     if ("text" in payload)
1119         this._text = payload.text;
1120 }
1121
1122 /**
1123  * @param {CSSAgent.StyleSheetId} styleSheetId
1124  * @param {function(?WebInspector.CSSStyleSheet)} userCallback
1125  */
1126 WebInspector.CSSStyleSheet.createForId = function(styleSheetId, userCallback)
1127 {
1128     /**
1129      * @param {?string} error
1130      * @param {CSSAgent.CSSStyleSheetBody} styleSheetPayload
1131      */
1132     function callback(error, styleSheetPayload)
1133     {
1134         if (error)
1135             userCallback(null);
1136         else
1137             userCallback(new WebInspector.CSSStyleSheet(styleSheetPayload));
1138     }
1139     CSSAgent.getStyleSheet(styleSheetId, callback.bind(this));
1140 }
1141
1142 WebInspector.CSSStyleSheet.prototype = {
1143     /**
1144      * @return {string|undefined}
1145      */
1146     getText: function()
1147     {
1148         return this._text;
1149     },
1150
1151     /**
1152      * @param {string} newText
1153      * @param {boolean} majorChange
1154      * @param {function(?string)=} userCallback
1155      */
1156     setText: function(newText, majorChange, userCallback)
1157     {
1158         /**
1159          * @param {?string} error
1160          */
1161         function callback(error)
1162         {
1163             if (!error)
1164                 WebInspector.domAgent.markUndoableState();
1165
1166             WebInspector.cssModel._pendingCommandsMajorState.pop();
1167             if (userCallback)
1168                 userCallback(error);
1169         }
1170
1171         WebInspector.cssModel._pendingCommandsMajorState.push(majorChange);
1172         CSSAgent.setStyleSheetText(this.id, newText, callback.bind(this));
1173     }
1174 }
1175
1176 /**
1177  * @constructor
1178  */
1179 WebInspector.CSSStyleModelResourceBinding = function()
1180 {
1181     this._reset();
1182 }
1183
1184 WebInspector.CSSStyleModelResourceBinding.prototype = {
1185     /**
1186      * @param {WebInspector.Resource} resource
1187      * @param {function(?CSSAgent.StyleSheetId)} callback
1188      */
1189     requestStyleSheetIdForResource: function(resource, callback)
1190     {
1191         function innerCallback()
1192         {
1193             callback(this._styleSheetIdForResource(resource));
1194         }
1195         
1196         if (this._styleSheetIdForResource(resource))
1197             innerCallback.call(this);
1198         else
1199             this._loadStyleSheetHeaders(innerCallback.bind(this));
1200     },
1201
1202     /**
1203      * @param {CSSAgent.StyleSheetId} styleSheetId
1204      * @param {function(?string)} callback
1205      */
1206     requestResourceURLForStyleSheetId: function(styleSheetId, callback)
1207     {
1208         function innerCallback()
1209         {
1210             var header = this._styleSheetIdToHeader[styleSheetId];
1211             if (!header) {
1212                 callback(null);
1213                 return;
1214             }
1215
1216             var frame = WebInspector.resourceTreeModel.frameForId(header.frameId);
1217             if (!frame) {
1218                 callback(null);
1219                 return;
1220             }
1221
1222             var styleSheetURL = header.origin === "inspector" ? this._viaInspectorResourceURL(header.sourceURL) : header.sourceURL;
1223             callback(styleSheetURL);
1224         }
1225         
1226         if (this._styleSheetIdToHeader[styleSheetId])
1227             innerCallback.call(this);
1228         else
1229             this._loadStyleSheetHeaders(innerCallback.bind(this));
1230     },
1231
1232     /**
1233      * @param {WebInspector.Resource} resource
1234      * @return {CSSAgent.StyleSheetId}
1235      */
1236     _styleSheetIdForResource: function(resource)
1237     {
1238         return this._frameAndURLToStyleSheetId[resource.frameId + ":" + resource.url];
1239     },
1240
1241     /**
1242      * @param {function(?string)} callback
1243      */
1244     _loadStyleSheetHeaders: function(callback)
1245     {
1246         /**
1247          * @param {?string} error
1248          * @param {Array.<CSSAgent.CSSStyleSheetHeader>} infos
1249          */
1250         function didGetAllStyleSheets(error, infos)
1251         {
1252             if (error) {
1253                 callback(error);
1254                 return;
1255             }
1256
1257             for (var i = 0; i < infos.length; ++i) {
1258                 var info = infos[i];
1259                 if (info.origin === "inspector") {
1260                     this._getOrCreateInspectorResource(info);
1261                     continue;
1262                 }
1263                 this._frameAndURLToStyleSheetId[info.frameId + ":" + info.sourceURL] = info.styleSheetId;
1264                 this._styleSheetIdToHeader[info.styleSheetId] = info;
1265             }
1266             callback(null);
1267         }
1268         CSSAgent.getAllStyleSheets(didGetAllStyleSheets.bind(this));
1269     },
1270
1271     /**
1272      * @param {CSSAgent.StyleSheetId} styleSheetId
1273      * @param {function(?WebInspector.Resource)} callback
1274      */
1275     _requestViaInspectorResource: function(styleSheetId, callback)
1276     {
1277         var header = this._styleSheetIdToHeader[styleSheetId];
1278         if (header) {
1279             callback(this._getOrCreateInspectorResource(header));
1280             return;
1281         }
1282
1283         function headersLoaded()
1284         {
1285             var header = this._styleSheetIdToHeader[styleSheetId];
1286             if (header)
1287                 callback(this._getOrCreateInspectorResource(header));
1288             else
1289                 callback(null);
1290         }
1291         this._loadStyleSheetHeaders(headersLoaded.bind(this));
1292     },
1293
1294     /**
1295      * @param {CSSAgent.CSSStyleSheetHeader} header
1296      * @return {?WebInspector.Resource}
1297      */
1298     _getOrCreateInspectorResource: function(header)
1299     {
1300         var frame = WebInspector.resourceTreeModel.frameForId(header.frameId);
1301         if (!frame)
1302             return null;
1303
1304         var viaInspectorURL = this._viaInspectorResourceURL(header.sourceURL);    
1305         var inspectorResource = frame.resourceForURL(viaInspectorURL);
1306         if (inspectorResource)
1307             return inspectorResource;
1308
1309         var resource = frame.resourceForURL(header.sourceURL);
1310         if (!resource)
1311             return null;
1312
1313         this._frameAndURLToStyleSheetId[header.frameId + ":" + viaInspectorURL] = header.styleSheetId;
1314         this._styleSheetIdToHeader[header.styleSheetId] = header;
1315         inspectorResource = new WebInspector.Resource(null, viaInspectorURL, resource.documentURL, resource.frameId, resource.loaderId, WebInspector.resourceTypes.Stylesheet, "text/css", true);
1316         /**
1317          * @param {function(?string, boolean, string)} callback
1318          */
1319         function overrideRequestContent(callback)
1320         {
1321             function callbackWrapper(error, content)
1322             {
1323                 callback(error ? "" : content, false, "text/css");
1324             }
1325             CSSAgent.getStyleSheetText(header.styleSheetId, callbackWrapper);
1326         }
1327         inspectorResource.requestContent = overrideRequestContent;
1328         frame.addResource(inspectorResource);
1329         return inspectorResource;
1330     },
1331
1332     /**
1333      * @param {string} documentURL
1334      * @return {string}
1335      */
1336     _viaInspectorResourceURL: function(documentURL)
1337     {
1338         var parsedURL = new WebInspector.ParsedURL(documentURL);
1339         var fakeURL = "inspector://" + parsedURL.host + parsedURL.folderPathComponents;
1340         if (!fakeURL.endsWith("/"))
1341             fakeURL += "/";
1342         fakeURL += "inspector-stylesheet";
1343         return fakeURL;
1344     },
1345
1346     _reset: function()
1347     {
1348         // Main frame navigation - clear history.
1349         this._frameAndURLToStyleSheetId = {};
1350         this._styleSheetIdToHeader = {};
1351     }
1352 }
1353
1354 /**
1355  * @constructor
1356  * @implements {CSSAgent.Dispatcher}
1357  * @param {WebInspector.CSSStyleModel} cssModel
1358  */
1359 WebInspector.CSSDispatcher = function(cssModel)
1360 {
1361     this._cssModel = cssModel;
1362 }
1363
1364 WebInspector.CSSDispatcher.prototype = {
1365     mediaQueryResultChanged: function()
1366     {
1367         this._cssModel.mediaQueryResultChanged();
1368     },
1369
1370     /**
1371      * @param {CSSAgent.StyleSheetId} styleSheetId
1372      */
1373     styleSheetChanged: function(styleSheetId)
1374     {
1375         this._cssModel._fireStyleSheetChanged(styleSheetId);
1376     },
1377
1378     /**
1379      * @param {CSSAgent.NamedFlow} namedFlowPayload
1380      */
1381     namedFlowCreated: function(namedFlowPayload)
1382     {
1383         this._cssModel._namedFlowCreated(namedFlowPayload);
1384     },
1385
1386     /**
1387      * @param {DOMAgent.NodeId} documentNodeId
1388      * @param {string} flowName
1389      */
1390     namedFlowRemoved: function(documentNodeId, flowName)
1391     {
1392         this._cssModel._namedFlowRemoved(documentNodeId, flowName);
1393     },
1394
1395     /**
1396      * @param {CSSAgent.NamedFlow} namedFlowPayload
1397      */
1398     regionLayoutUpdated: function(namedFlowPayload)
1399     {
1400         this._cssModel._regionLayoutUpdated(namedFlowPayload);
1401     }
1402 }
1403
1404 /**
1405  * @constructor
1406  * @param {CSSAgent.NamedFlow} payload
1407  */
1408 WebInspector.NamedFlow = function(payload)
1409 {
1410     this.documentNodeId = payload.documentNodeId;
1411     this.name = payload.name;
1412     this.overset = payload.overset;
1413     this.content = payload.content;
1414     this.regions = payload.regions;
1415 }
1416
1417 /**
1418  * @param {CSSAgent.NamedFlow} payload
1419  * @return {WebInspector.NamedFlow}
1420  */
1421 WebInspector.NamedFlow.parsePayload = function(payload)
1422 {
1423     return new WebInspector.NamedFlow(payload);
1424 }
1425
1426 /**
1427  * @constructor
1428  * @param {Array.<CSSAgent.NamedFlow>} payload
1429  */
1430 WebInspector.NamedFlowCollection = function(payload)
1431 {
1432     /** @type {Object.<string, WebInspector.NamedFlow>} */
1433     this.namedFlowMap = {};
1434
1435     for (var i = 0; i < payload.length; ++i) {
1436         var namedFlow = WebInspector.NamedFlow.parsePayload(payload[i]);
1437         this.namedFlowMap[namedFlow.name] = namedFlow;
1438     }
1439 }
1440
1441 WebInspector.NamedFlowCollection.prototype = {
1442     /**
1443      * @param {WebInspector.NamedFlow} namedFlow
1444      */
1445     _appendNamedFlow: function(namedFlow)
1446     {
1447         this.namedFlowMap[namedFlow.name] = namedFlow;
1448     },
1449
1450     /**
1451      * @param {string} flowName
1452      */
1453     _removeNamedFlow: function(flowName)
1454     {
1455         delete this.namedFlowMap[flowName];
1456     },
1457
1458     /**
1459      * @param {string} flowName
1460      * @return {WebInspector.NamedFlow}
1461      */
1462     flowByName: function(flowName)
1463     {
1464         var namedFlow = this.namedFlowMap[flowName];
1465
1466         if (!namedFlow)
1467             return null;
1468         return namedFlow;
1469     }
1470 }
1471 /**
1472  * @type {WebInspector.CSSStyleModel}
1473  */
1474 WebInspector.cssModel = null;