Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(420)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerListView.js

Issue 2698623008: DevTools: extract a list view out of CSSTrackerView (Closed)
Patch Set: review comments + rebase Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 CSSTracker.CSSTrackerView = class extends UI.VBox { 5 CSSTracker.CSSTrackerListView = class extends UI.VBox {
6 constructor() { 6 constructor() {
7 super(true); 7 super(true);
8
9 this.registerRequiredCSS('css_tracker/cssTrackerView.css');
10
11 var toolbarContainer = this.contentElement.createChild('div', 'css-tracker-t oolbar-container');
12 var topToolbar = new UI.Toolbar('css-tracker-toolbar', toolbarContainer);
13
14 this._recordButton =
15 new UI.ToolbarToggle(Common.UIString('Start recording'), 'largeicon-resu me', 'largeicon-pause');
16 this._recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => thi s._toggleRecording(!this._isRecording));
17 topToolbar.appendToolbarItem(this._recordButton);
18
19 var clearButton = new UI.ToolbarButton(Common.UIString('Clear all'), 'largei con-clear');
20 clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._reset.bind (this));
21 topToolbar.appendToolbarItem(clearButton);
22
23 this._cssResultsElement = this.contentElement.createChild('div', 'css-result s');
24 this._progressElement = this._cssResultsElement.createChild('div', 'progress -view');
25 this._treeOutline = new UI.TreeOutlineInShadow(); 8 this._treeOutline = new UI.TreeOutlineInShadow();
26 this._treeOutline.registerRequiredCSS('css_tracker/unusedRulesTree.css'); 9 this._treeOutline.registerRequiredCSS('css_tracker/unusedRulesTree.css');
27 10 this.contentElement.appendChild(this._treeOutline.element);
28 this._statusToolbarElement = this.contentElement.createChild('div', 'css-too lbar-summary');
29 this._statusMessageElement = this._statusToolbarElement.createChild('div', ' css-message');
30
31 this._isRecording = false;
32 }
33
34 _reset() {
35 Workspace.workspace.uiSourceCodes().forEach(
36 uiSourceCode => uiSourceCode.removeDecorationsForType(CSSTracker.CSSTrac kerView.LineDecorator.type));
37
38 this._cssResultsElement.removeChildren();
39 this._progressElement.textContent = '';
40 this._cssResultsElement.appendChild(this._progressElement);
41
42 this._treeOutline.removeChildren();
43 this._statusMessageElement.textContent = '';
44 }
45
46 /**
47 * @param {boolean} enable
48 */
49 _toggleRecording(enable) {
50 if (enable === this._isRecording)
51 return;
52
53 this._isRecording = enable;
54 this._recordButton.setToggled(this._isRecording);
55
56 if (this._isRecording)
57 this._startRecording();
58 else
59 this._stopRecording();
60 }
61
62 _startRecording() {
63 this._reset();
64 var mainTarget = SDK.targetManager.mainTarget();
65 if (!mainTarget)
66 return;
67 var cssModel = mainTarget.model(SDK.CSSModel);
68 if (!cssModel)
69 return;
70 this._recordButton.setTitle(Common.UIString('Stop recording'));
71 cssModel.startRuleUsageTracking();
72
73 this._progressElement.textContent = Common.UIString('Recording...');
74 }
75
76 _stopRecording() {
77 var mainTarget = SDK.targetManager.mainTarget();
78 if (!mainTarget)
79 return;
80
81 this._recordButton.setTitle(Common.UIString('Start recording'));
82 this._progressElement.textContent = Common.UIString('Fetching results...');
83
84 var cssModel = mainTarget.model(SDK.CSSModel);
85 if (!cssModel)
86 return;
87
88 cssModel.ruleListPromise().then(processRuleList.bind(this)).then(updateViews .bind(this));
89
90 /**
91 * @param {!Array<!SDK.CSSModel.RuleUsage>} ruleUsageList
92 * @this {!CSSTracker.CSSTrackerView}
93 * @return {!Promise<!Array<!CSSTracker.StyleSheetUsage>>}
94 */
95 function processRuleList(ruleUsageList) {
96 /** @type {!Map<?SDK.CSSStyleSheetHeader, !CSSTracker.StyleSheetUsage>} */
97 var rulesByStyleSheet = new Map();
98 for (var rule of ruleUsageList) {
99 var styleSheetHeader = cssModel.styleSheetHeaderForId(rule.styleSheetId) ;
100 var entry = rulesByStyleSheet.get(styleSheetHeader);
101 if (!entry) {
102 entry = {styleSheetHeader: styleSheetHeader, rules: []};
103 rulesByStyleSheet.set(styleSheetHeader, entry);
104 }
105 entry.rules.push(rule);
106 }
107 return Promise.all(Array.from(
108 rulesByStyleSheet.values(),
109 entry => this._populateSourceInfo(/** @type {!CSSTracker.StyleSheetUsa ge} */ (entry))));
110 }
111
112 /**
113 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages
114 * @this {!CSSTracker.CSSTrackerView}
115 */
116 function updateViews(styleSheetUsages) {
117 this._updateStats(styleSheetUsages);
118 this._updateGutter(styleSheetUsages);
119 this._updateTree(styleSheetUsages);
120 }
121 }
122
123 /**
124 * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage
125 * @return {!Promise<!CSSTracker.StyleSheetUsage>}
126 */
127 _populateSourceInfo(styleSheetUsage) {
128 if (!styleSheetUsage.styleSheetHeader)
129 return Promise.resolve(styleSheetUsage);
130 var ruleIndex =
131 new Map(styleSheetUsage.rules.map(rule => [`${rule.range.startLine}.${ru le.range.startColumn}`, rule]));
132
133 return new Promise(fulfill => {
134 styleSheetUsage.styleSheetHeader.requestContent().then(
135 content => Common.formatterWorkerPool.parseCSS(content || '', onRules) );
136
137 /**
138 * @param {boolean} isLastChunk
139 * @param {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} rules
140 */
141 function onRules(isLastChunk, rules) {
142 for (var rule of rules) {
143 if (!rule.styleRange)
144 continue;
145 var entry = ruleIndex.get(`${rule.styleRange.startLine}.${rule.styleRa nge.startColumn}`);
146 if (entry)
147 entry.selector = rule.selectorText;
148 }
149 if (isLastChunk)
150 fulfill(styleSheetUsage);
151 }
152 });
153 }
154
155 /**
156 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsage
157 */
158 _updateStats(styleSheetUsage) {
159 var total = 0;
160 var unused = 0;
161 for (var styleSheet of styleSheetUsage) {
162 total += styleSheet.rules.length;
163 unused += styleSheet.rules.reduce((count, rule) => rule.wasUsed ? count : count + 1, 0);
164 }
165 var percentUnused = total ? Math.round(100 * unused / total) : 0;
166 if (unused === 1) {
167 this._statusMessageElement.textContent =
168 Common.UIString('%d CSS rule is not used. (%d%%)', unused, percentUnus ed);
169 } else {
170 this._statusMessageElement.textContent =
171 Common.UIString('%d CSS rules are not used. (%d%%)', unused, percentUn used);
172 }
173 } 11 }
174 12
175 /** 13 /**
176 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages 14 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages
177 */ 15 */
178 _updateGutter(styleSheetUsages) { 16 update(styleSheetUsages) {
179 for (var styleSheet of styleSheetUsages) { 17 this._treeOutline.removeChildren();
180 if (!styleSheet.styleSheetHeader)
181 continue;
182 var url = styleSheet.styleSheetHeader.sourceURL;
183 var uiSourceCode = url && Workspace.workspace.uiSourceCodeForURL(url);
184 if (!uiSourceCode)
185 continue;
186 for (var rule of styleSheet.rules) {
187 var gutterRange = Common.TextRange.fromObject(rule.range);
188 if (gutterRange.startColumn)
189 gutterRange.startColumn--;
190 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe corator.type, rule.wasUsed);
191 }
192 }
193 }
194
195 /**
196 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages
197 */
198 _updateTree(styleSheetUsages) {
199 this._cssResultsElement.removeChildren();
200 this._cssResultsElement.appendChild(this._treeOutline.element);
201 18
202 for (var sheet of styleSheetUsages) { 19 for (var sheet of styleSheetUsages) {
203 var unusedRuleCount = sheet.rules.reduce((count, rule) => rule.wasUsed ? c ount : count + 1, 0); 20 var unusedRuleCount = sheet.rules.reduce((count, rule) => rule.wasUsed ? c ount : count + 1, 0);
204 if (sheet.styleSheetHeader) { 21 if (sheet.styleSheetHeader) {
205 var url = sheet.styleSheetHeader.sourceURL; 22 var url = sheet.styleSheetHeader.sourceURL;
206 if (!url) 23 if (!url)
207 continue; 24 continue;
208 25
209 var styleSheetTreeElement = new CSSTracker.CSSTrackerView.StyleSheetTree Element(url, sheet.rules); 26 var styleSheetTreeElement = new CSSTracker.CSSTrackerListView.StyleSheet TreeElement(url, sheet.rules);
210 this._treeOutline.appendChild(styleSheetTreeElement); 27 this._treeOutline.appendChild(styleSheetTreeElement);
211 continue; 28 continue;
212 } 29 }
213 if (!unusedRuleCount) 30 if (!unusedRuleCount)
214 continue; 31 continue;
215 var removedStyleSheetStats = unusedRuleCount === 1 ? 32 var removedStyleSheetStats = unusedRuleCount === 1 ?
216 Common.UIString('1 unused rule in a removed style sheet.') : 33 Common.UIString('1 unused rule in a removed style sheet.') :
217 Common.UIString('%d unused rules in removed style sheets.', unusedRule Count); 34 Common.UIString('%d unused rules in removed style sheets.', unusedRule Count);
218 35
219 var treeElement = new UI.TreeElement(Common.UIString('Unknown style sheets '), true); 36 var treeElement = new UI.TreeElement(Common.UIString('Unknown style sheets '), true);
220 treeElement.toggleOnClick = true; 37 treeElement.toggleOnClick = true;
221 treeElement.selectable = false; 38 treeElement.selectable = false;
222 39
223 var stats = new UI.TreeElement(removedStyleSheetStats, false); 40 var stats = new UI.TreeElement(removedStyleSheetStats, false);
224 stats.selectable = false; 41 stats.selectable = false;
225 treeElement.appendChild(stats); 42 treeElement.appendChild(stats);
226 this._treeOutline.appendChild(treeElement); 43 this._treeOutline.appendChild(treeElement);
227 } 44 }
228 } 45 }
229 }; 46 };
230 47
231 /** @typedef {{range: !Protocol.CSS.SourceRange, 48 CSSTracker.CSSTrackerListView._rulesShownAtOnce = 20;
232 * selector: (string|undefined),
233 * wasUsed: boolean}}
234 */
235 CSSTracker.RuleUsage;
236 49
237 /** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTra cker.RuleUsage>}} */ 50 CSSTracker.CSSTrackerListView.StyleSheetTreeElement = class extends UI.TreeEleme nt {
238 CSSTracker.StyleSheetUsage;
239
240 CSSTracker.CSSTrackerView._rulesShownAtOnce = 20;
241
242 CSSTracker.CSSTrackerView.StyleSheetTreeElement = class extends UI.TreeElement {
243 /** 51 /**
244 * @param {string} url 52 * @param {string} url
245 * @param {!Array<!CSSTracker.RuleUsage>} ruleList 53 * @param {!Array<!CSSTracker.RuleUsage>} ruleList
246 */ 54 */
247 constructor(url, ruleList) { 55 constructor(url, ruleList) {
248 super('', true); 56 super('', true);
249 57
250 this._uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url); 58 this._uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url);
251 59
252 /** @type {!Array<!CSSTracker.RuleUsage>} */ 60 /** @type {!Array<!CSSTracker.RuleUsage>} */
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
286 rulesCountSpan.textContent = 94 rulesCountSpan.textContent =
287 Common.UIString('(%d unused rules : %d%%)', this._unusedRules.length, this._percentUnused); 95 Common.UIString('(%d unused rules : %d%%)', this._unusedRules.length, this._percentUnused);
288 } 96 }
289 this.title = title; 97 this.title = title;
290 } 98 }
291 99
292 /** 100 /**
293 * @override 101 * @override
294 */ 102 */
295 onpopulate() { 103 onpopulate() {
296 var toIndex = Math.min(this._unusedRules.length, CSSTracker.CSSTrackerView._ rulesShownAtOnce); 104 var toIndex = Math.min(this._unusedRules.length, CSSTracker.CSSTrackerListVi ew._rulesShownAtOnce);
297 this._appendRules(0, toIndex); 105 this._appendRules(0, toIndex);
298 if (toIndex < this._unusedRules.length) 106 if (toIndex < this._unusedRules.length)
299 this._appendShowAllRulesButton(toIndex); 107 this._appendShowAllRulesButton(toIndex);
300 } 108 }
301 109
302 /** 110 /**
303 * @param {number} fromIndex 111 * @param {number} fromIndex
304 * @param {number} toIndex 112 * @param {number} toIndex
305 */ 113 */
306 _appendRules(fromIndex, toIndex) { 114 _appendRules(fromIndex, toIndex) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
350 /** 158 /**
351 * @param {number} startMatchIndex 159 * @param {number} startMatchIndex
352 */ 160 */
353 _showMoreRulesElementSelected(startMatchIndex) { 161 _showMoreRulesElementSelected(startMatchIndex) {
354 if (!this._showAllRulesTreeElement) 162 if (!this._showAllRulesTreeElement)
355 return; 163 return;
356 this.removeChild(this._showAllRulesTreeElement); 164 this.removeChild(this._showAllRulesTreeElement);
357 this._appendRules(startMatchIndex, this._unusedRules.length); 165 this._appendRules(startMatchIndex, this._unusedRules.length);
358 } 166 }
359 }; 167 };
360
361 /**
362 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator}
363 */
364 CSSTracker.CSSTrackerView.LineDecorator = class {
365 /**
366 * @override
367 * @param {!Workspace.UISourceCode} uiSourceCode
368 * @param {!TextEditor.CodeMirrorTextEditor} textEditor
369 */
370 decorate(uiSourceCode, textEditor) {
371 var gutterType = 'CodeMirror-gutter-coverage';
372
373 var decorations = uiSourceCode.decorationsForType(CSSTracker.CSSTrackerView. LineDecorator.type);
374 textEditor.uninstallGutter(gutterType);
375 if (!decorations || !decorations.size)
376 return;
377
378 textEditor.installGutter(gutterType, false);
379
380 for (var decoration of decorations) {
381 for (var line = decoration.range().startLine; line <= decoration.range().e ndLine; ++line) {
382 var element = createElementWithClass('div');
383 if (decoration.data())
384 element.className = 'text-editor-css-rule-used-marker';
385 else
386 element.className = 'text-editor-css-rule-unused-marker';
387
388 textEditor.setGutterDecoration(line, gutterType, element);
389 }
390 }
391 }
392 };
393
394 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage';
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698