Web Inspector: Display Named Flows in the Tabbed Pane of the "CSS Named Flows" Drawer
[WebKit-https.git] / Source / WebCore / inspector / front-end / CSSNamedFlowView.js
1 /*
2  * Copyright (C) 2012 Adobe Systems Incorporated. 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
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 /**
31  * @constructor
32  * @extends {WebInspector.View}
33  * @param {WebInspector.NamedFlow} flow
34  */
35 WebInspector.CSSNamedFlowView = function(flow)
36 {
37     WebInspector.View.call(this);
38     this.element.addStyleClass("css-named-flow");
39     this.element.addStyleClass("outline-disclosure");
40
41     this._treeOutline = new TreeOutline(this.element.createChild("ol"), true);
42
43     this._contentTreeItem = new TreeElement(WebInspector.UIString("content"), null, true);
44     this._treeOutline.appendChild(this._contentTreeItem);
45
46     this._regionsTreeItem = new TreeElement(WebInspector.UIString("region chain"), null, true);
47     this._regionsTreeItem.expand();
48     this._treeOutline.appendChild(this._regionsTreeItem);
49
50     this._flow = flow;
51
52     var content = flow.content;
53     for (var i = 0; i < content.length; ++i)
54         this._insertContentNode(content[i]);
55
56     var regions = flow.regions;
57     for (var i = 0; i < regions.length; ++i)
58         this._insertRegion(regions[i]);
59 }
60
61 WebInspector.CSSNamedFlowView.OversetTypeMessageMap = {
62     empty: "empty",
63     fit: "fit",
64     overset: "overset"
65 }
66
67 WebInspector.CSSNamedFlowView.prototype = {
68     /**
69      * @param {WebInspector.DOMNode=} rootDOMNode
70      * @return {?WebInspector.ElementsTreeOutline}
71      */
72     _createFlowTreeOutline: function(rootDOMNode)
73     {
74         if (!rootDOMNode)
75             return null;
76
77         var treeOutline = new WebInspector.ElementsTreeOutline(false, false, true);
78         treeOutline.element.addStyleClass("named-flow-element");
79         treeOutline.setVisible(true);
80         treeOutline.rootDOMNode = rootDOMNode;
81         treeOutline.wireToDomAgent();
82         WebInspector.domAgent.removeEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, treeOutline._elementsTreeUpdater._documentUpdated, treeOutline._elementsTreeUpdater);
83
84         return treeOutline;        
85     },
86
87     /**
88      * @param {DOMAgent.NodeId} contentNodeId
89      * @param {number=} index
90      */
91     _insertContentNode: function(contentNodeId, index)
92     {
93         var treeOutline = this._createFlowTreeOutline(WebInspector.domAgent.nodeForId(contentNodeId));
94         var treeItem = new TreeElement(treeOutline.element, treeOutline);
95
96         if (index === undefined) {
97             this._contentTreeItem.appendChild(treeItem);
98             return;
99         }
100
101         this._contentTreeItem.insertChild(treeItem, index);
102     },
103
104     /**
105      * @param {CSSAgent.Region} region
106      * @param {number=} index
107      */
108     _insertRegion: function(region, index)
109     {
110         var treeOutline = this._createFlowTreeOutline(WebInspector.domAgent.nodeForId(region.nodeId));
111         treeOutline.element.addStyleClass("region-" + region.regionOverset);
112
113         var treeItem = new TreeElement(treeOutline.element, treeOutline);
114         var oversetText = WebInspector.UIString(WebInspector.CSSNamedFlowView.OversetTypeMessageMap[region.regionOverset]);
115         treeItem.tooltip = WebInspector.UIString("Region is %s.", oversetText);
116
117         if (index === undefined) {
118             this._regionsTreeItem.appendChild(treeItem);
119             return;
120         }
121
122         this._regionsTreeItem.insertChild(treeItem, index);
123     },
124
125     get flow()
126     {
127         return this._flow;
128     },
129
130     set flow(newFlow)
131     {
132         this._update(newFlow);
133     },
134
135     /**
136      * @param {TreeElement} regionTreeItem
137      * @param {string} newRegionOverset
138      * @param {string} oldRegionOverset
139      */
140     _updateRegionOverset: function(regionTreeItem, newRegionOverset, oldRegionOverset)
141     {
142         var element = regionTreeItem.representedObject.element;
143         element.removeStyleClass("region-" + oldRegionOverset);
144         element.addStyleClass("region-" + newRegionOverset);
145
146         var oversetText = WebInspector.UIString(WebInspector.CSSNamedFlowView.OversetTypeMessageMap[newRegionOverset]);
147         regionTreeItem.tooltip = WebInspector.UIString("Region is %s." , oversetText);
148     },
149
150     /**
151      * @param {Array.<DOMAgent.NodeId>} oldContent
152      * @param {Array.<DOMAgent.NodeId>} newContent
153      */
154     _mergeContentNodes: function(oldContent, newContent)
155     {
156         var nodeIdSet = {};
157         for (var i = 0; i < newContent.length; ++i)
158             nodeIdSet[newContent[i]] = true;
159
160         var oldContentIndex = 0;
161         var newContentIndex = 0;
162         var contentTreeChildIndex = 0;
163
164         while(oldContentIndex < oldContent.length || newContentIndex < newContent.length) {
165             if (oldContentIndex === oldContent.length) {
166                 this._insertContentNode(newContent[newContentIndex]);
167                 ++newContentIndex;
168                 continue;
169             }
170
171             if (newContentIndex === newContent.length) {
172                 this._contentTreeItem.removeChildAtIndex(contentTreeChildIndex);
173                 ++oldContentIndex;
174                 continue;
175             }
176
177             if (oldContent[oldContentIndex] === newContent[newContentIndex]) {
178                 ++oldContentIndex;
179                 ++newContentIndex;
180                 ++contentTreeChildIndex;
181                 continue;
182             }
183
184             if (nodeIdSet[oldContent[oldContentIndex]]) {
185                 this._insertContentNode(newContent[newContentIndex], contentTreeChildIndex);
186                 ++newContentIndex;
187                 ++contentTreeChildIndex;
188                 continue;
189             }
190
191             this._contentTreeItem.removeChildAtIndex(contentTreeChildIndex);
192             ++oldContentIndex;
193         }
194     },
195
196     /**
197      * @param {Array.<CSSAgent.Region>} oldRegions
198      * @param {Array.<CSSAgent.Region>} newRegions
199      */
200     _mergeRegions: function(oldRegions, newRegions)
201     {
202         var nodeIdSet = {};
203         for (var i = 0; i < newRegions.length; ++i)
204             nodeIdSet[newRegions[i].nodeId] = true;
205
206         var oldRegionsIndex = 0;
207         var newRegionsIndex = 0;
208         var regionsTreeChildIndex = 0;
209
210         while(oldRegionsIndex < oldRegions.length || newRegionsIndex < newRegions.length) {
211             if (oldRegionsIndex === oldRegions.length) {
212                 this._insertRegion(newRegions[newRegionsIndex]);
213                 ++newRegionsIndex;
214                 continue;
215             }
216
217             if (newRegionsIndex === newRegions.length) {
218                 this._regionsTreeItem.removeChildAtIndex(regionsTreeChildIndex);
219                 ++oldRegionsIndex;
220                 continue;
221             }
222
223             if (oldRegions[oldRegionsIndex].nodeId === newRegions[newRegionsIndex].nodeId) {
224                 if (oldRegions[oldRegionsIndex].regionOverset !== newRegions[newRegionsIndex].regionOverset)
225                     this._updateRegionOverset(this._regionsTreeItem.children[regionsTreeChildIndex], newRegions[newRegionsIndex].regionOverset, oldRegions[oldRegionsIndex].regionOverset);
226                 ++oldRegionsIndex;
227                 ++newRegionsIndex;
228                 ++regionsTreeChildIndex;
229                 continue;
230             }
231
232             if (nodeIdSet[oldRegions[oldRegionsIndex].nodeId]) {
233                 this._insertRegion(newRegions[newRegionsIndex], regionsTreeChildIndex);
234                 ++newRegionsIndex;
235                 ++regionsTreeChildIndex;
236                 continue;
237             }
238
239             this._regionsTreeItem.removeChildAtIndex(regionsTreeChildIndex);
240             ++oldRegionsIndex;
241         }
242     },
243
244     /**
245      * @param {WebInspector.NamedFlow} newFlow
246      */
247     _update: function(newFlow)
248     {
249         this._mergeContentNodes(this._flow.content, newFlow.content);
250         this._mergeRegions(this._flow.regions, newFlow.regions);
251
252         this._flow = newFlow;
253     }
254 }
255
256 WebInspector.CSSNamedFlowView.prototype.__proto__ = WebInspector.View.prototype;