Web Inspector: REGRESSION(r225569): CSSStyleDetailsSidebarPanel no longer exists
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / SpreadsheetRulesStyleDetailsPanel.js
1 /*
2  * Copyright (C) 2017 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WI.SpreadsheetRulesStyleDetailsPanel = class SpreadsheetRulesStyleDetailsPanel extends WI.StyleDetailsPanel
27 {
28     constructor(delegate)
29     {
30         const className = "rules";
31         const identifier = "rules";
32         const label = WI.UIString("Styles \u2014 Rules");
33         super(delegate, className, identifier, label);
34
35         this.element.classList.add("spreadsheet-style-panel");
36
37         this._headerMap = new Map;
38         this._sections = [];
39         this._newRuleSelector = null;
40         this._ruleMediaAndInherticanceList = [];
41         this._propertyToSelectAndHighlight = null;
42         this._filterText = null;
43
44         this._emptyFilterResultsElement = WI.createMessageTextView(WI.UIString("No Results Found"));
45     }
46
47     // Public
48
49     refresh(significantChange)
50     {
51         // We only need to do a rebuild on significant changes. Other changes are handled
52         // by the sections and text editors themselves.
53         if (!significantChange) {
54             super.refresh(significantChange);
55             return;
56         }
57
58         this.removeAllSubviews();
59
60         let previousStyle = null;
61         this._headerMap.clear();
62         this._sections = [];
63
64         let uniqueOrderedStyles = (orderedStyles) => {
65             let uniqueStyles = [];
66
67             for (let style of orderedStyles) {
68                 let rule = style.ownerRule;
69                 if (!rule) {
70                     uniqueStyles.push(style);
71                     continue;
72                 }
73
74                 let found = false;
75                 for (let existingStyle of uniqueStyles) {
76                     if (rule.isEqualTo(existingStyle.ownerRule)) {
77                         found = true;
78                         break;
79                     }
80                 }
81                 if (!found)
82                     uniqueStyles.push(style);
83             }
84
85             return uniqueStyles;
86         };
87
88         let createHeader = (text, node) => {
89             let header = this.element.appendChild(document.createElement("h2"));
90             header.classList.add("section-header");
91             header.append(text);
92             header.append(" ", WI.linkifyNodeReference(node, {
93                 maxLength: 100,
94                 excludeRevealElement: true,
95             }));
96
97             this._headerMap.set(node, header);
98         };
99
100         let createSection = (style) => {
101             let section = style[WI.SpreadsheetRulesStyleDetailsPanel.RuleSection];
102             if (!section) {
103                 section = new WI.SpreadsheetCSSStyleDeclarationSection(this, style);
104                 style[WI.SpreadsheetRulesStyleDetailsPanel.RuleSection] = section;
105             }
106
107             section.addEventListener(WI.SpreadsheetCSSStyleDeclarationSection.Event.FilterApplied, this._handleSectionFilterApplied, this);
108
109             if (this._newRuleSelector === style.selectorText && !style.hasProperties())
110                 section.startEditingRuleSelector();
111
112             this.addSubview(section);
113             section.needsLayout();
114             this._sections.push(section);
115
116             previousStyle = style;
117         };
118
119         for (let style of uniqueOrderedStyles(this.nodeStyles.orderedStyles)) {
120             if (style.inherited && (!previousStyle || previousStyle.node !== style.node))
121                 createHeader(WI.UIString("Inherited From"), style.node);
122
123             createSection(style);
124         }
125
126         let pseudoElements = Array.from(this.nodeStyles.node.pseudoElements().values());
127         Promise.all(pseudoElements.map((pseudoElement) => WI.cssStyleManager.stylesForNode(pseudoElement).refreshIfNeeded()))
128         .then((pseudoNodeStyles) => {
129             for (let pseudoNodeStyle of pseudoNodeStyles) {
130                 createHeader(WI.UIString("Pseudo Element"), pseudoNodeStyle.node);
131
132                 for (let style of uniqueOrderedStyles(pseudoNodeStyle.orderedStyles))
133                     createSection(style);
134             }
135         });
136
137         this._newRuleSelector = null;
138
139         this.element.append(this._emptyFilterResultsElement);
140
141         if (this._filterText)
142             this.applyFilter(this._filterText);
143
144         super.refresh(significantChange);
145     }
146
147     scrollToSectionAndHighlightProperty(property)
148     {
149         if (!this._visible) {
150             this._propertyToSelectAndHighlight = property;
151             return;
152         }
153
154         for (let section of this._sections) {
155             if (section.highlightProperty(property))
156                 return;
157         }
158     }
159
160     nodeStylesRefreshed(event)
161     {
162         super.nodeStylesRefreshed(event);
163
164         if (this._propertyToSelectAndHighlight) {
165             this.scrollToSectionAndHighlightProperty(this._propertyToSelectAndHighlight);
166             this._propertyToSelectAndHighlight = null;
167         }
168     }
169
170     newRuleButtonClicked()
171     {
172         if (this.nodeStyles.node.isInUserAgentShadowTree())
173             return;
174
175         this._addNewRule();
176     }
177
178     newRuleButtonContextMenu(event)
179     {
180         if (this.nodeStyles.node.isInUserAgentShadowTree())
181             return;
182
183         let styleSheets = WI.cssStyleManager.styleSheets.filter(styleSheet => styleSheet.hasInfo() && !styleSheet.isInlineStyleTag() && !styleSheet.isInlineStyleAttributeStyleSheet());
184         if (!styleSheets.length)
185             return;
186
187         let contextMenu = WI.ContextMenu.createFromEvent(event);
188
189         const handler = null;
190         const disabled = true;
191         contextMenu.appendItem(WI.UIString("Available Style Sheets"), handler, disabled);
192
193         let [inspectorStyleSheets, regularStyleSheets] = styleSheets.partition(styleSheet => styleSheet.isInspectorStyleSheet());
194         console.assert(inspectorStyleSheets.length <= 1, "There should never be more than one inspector style sheet");
195
196         contextMenu.appendItem(WI.UIString("Inspector Style Sheet"), () => {
197             this._addNewRule(inspectorStyleSheets.length ? inspectorStyleSheets[0].id : null);
198         });
199
200         for (let styleSheet of regularStyleSheets) {
201             contextMenu.appendItem(styleSheet.displayName, () => {
202                 this._addNewRule(styleSheet.id);
203             });
204         }
205     }
206
207     cssStyleDeclarationSectionStartEditingNextRule(currentSection)
208     {
209         let currentIndex = this._sections.indexOf(currentSection);
210         let index = currentIndex < this._sections.length - 1 ? currentIndex + 1 : 0;
211         this._sections[index].startEditingRuleSelector();
212     }
213
214     cssStyleDeclarationSectionStartEditingPreviousRule(currentSection)
215     {
216         let index = this._sections.indexOf(currentSection);
217         console.assert(index > -1);
218
219         while (true) {
220             index--;
221             if (index < 0)
222                 break;
223
224             let section = this._sections[index];
225             if (section.editable) {
226                 section._propertiesEditor.startEditingLastProperty();
227                 break;
228             }
229         }
230     }
231
232     applyFilter(filterText)
233     {
234         this._filterText = filterText;
235
236         if (!this.didInitialLayout)
237             return;
238
239         if (this._filterText)
240             this.element.classList.add("filter-non-matching");
241
242         for (let header of this._headerMap.values())
243             header.classList.remove(WI.GeneralStyleDetailsSidebarPanel.NoFilterMatchInSectionClassName);
244
245         for (let section of this._sections)
246             section.applyFilter(this._filterText);
247     }
248
249     // Protected
250
251     filterDidChange(filterBar)
252     {
253         super.filterDidChange(filterBar);
254
255         this.applyFilter(filterBar.filters.text);
256     }
257
258     // Private
259
260     _handleSectionFilterApplied(event)
261     {
262         if (event.data.matches)
263             this.element.classList.remove("filter-non-matching");
264         else {
265             let header = this._headerMap.get(event.target.style.node);
266             if (header)
267                 header.classList.add(WI.GeneralStyleDetailsSidebarPanel.NoFilterMatchInSectionClassName);
268         }
269     }
270
271     // Private
272
273     _addNewRule(stylesheetId)
274     {
275         const justSelector = true;
276         this._newRuleSelector = this.nodeStyles.node.appropriateSelectorFor(justSelector);
277
278         const text = "";
279         this.nodeStyles.addRule(this._newRuleSelector, text, stylesheetId);
280     }
281 };
282
283 WI.SpreadsheetRulesStyleDetailsPanel.RuleSection = Symbol("rule-section");