OLD | NEW |
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2016 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.CSSTrackerView = class extends UI.VBox { |
6 constructor() { | 6 constructor() { |
7 super(true); | 7 super(true); |
8 | 8 |
9 this.registerRequiredCSS('css_tracker/cssTrackerView.css'); | 9 this.registerRequiredCSS('css_tracker/cssTrackerView.css'); |
10 | 10 |
11 var toolbarContainer = this.contentElement.createChild('div', 'css-tracker-t
oolbar-container'); | 11 var toolbarContainer = this.contentElement.createChild('div', 'css-tracker-t
oolbar-container'); |
12 var topToolbar = new UI.Toolbar('css-tracker-toolbar', toolbarContainer); | 12 var topToolbar = new UI.Toolbar('css-tracker-toolbar', toolbarContainer); |
13 | 13 |
14 this._recordButton = | 14 this._recordButton = |
15 new UI.ToolbarToggle(Common.UIString('Start recording'), 'largeicon-resu
me', 'largeicon-pause'); | 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)); | 16 this._recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => thi
s._toggleRecording(!this._isRecording)); |
17 topToolbar.appendToolbarItem(this._recordButton); | 17 topToolbar.appendToolbarItem(this._recordButton); |
18 | 18 |
19 var clearButton = new UI.ToolbarButton(Common.UIString('Clear all'), 'largei
con-clear'); | 19 var clearButton = new UI.ToolbarButton(Common.UIString('Clear all'), 'largei
con-clear'); |
20 clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._reset.bind
(this)); | 20 clearButton.addEventListener(UI.ToolbarButton.Events.Click, this._reset.bind
(this)); |
21 topToolbar.appendToolbarItem(clearButton); | 21 topToolbar.appendToolbarItem(clearButton); |
22 | 22 |
23 this._cssResultsElement = this.contentElement.createChild('div', 'css-result
s'); | 23 this._cssResultsElement = this.contentElement.createChild('div', 'css-result
s'); |
24 this._progressElement = this._cssResultsElement.createChild('div', 'progress
-view'); | 24 this._progressElement = this._cssResultsElement.createChild('div', 'progress
-view'); |
25 this._treeOutline = new UI.TreeOutlineInShadow(); | 25 this._listView = new CSSTracker.CSSTrackerListView(); |
26 this._treeOutline.registerRequiredCSS('css_tracker/unusedRulesTree.css'); | |
27 | 26 |
28 this._statusToolbarElement = this.contentElement.createChild('div', 'css-too
lbar-summary'); | 27 this._statusToolbarElement = this.contentElement.createChild('div', 'css-too
lbar-summary'); |
29 this._statusMessageElement = this._statusToolbarElement.createChild('div', '
css-message'); | 28 this._statusMessageElement = this._statusToolbarElement.createChild('div', '
css-message'); |
30 | 29 |
31 this._isRecording = false; | 30 this._isRecording = false; |
32 } | 31 } |
33 | 32 |
34 _reset() { | 33 _reset() { |
35 Workspace.workspace.uiSourceCodes().forEach( | 34 Workspace.workspace.uiSourceCodes().forEach( |
36 uiSourceCode => uiSourceCode.removeDecorationsForType(CSSTracker.CSSTrac
kerView.LineDecorator.type)); | 35 uiSourceCode => uiSourceCode.removeDecorationsForType(CSSTracker.CSSTrac
kerView.LineDecorator.type)); |
37 | 36 |
| 37 this._listView.detach(); |
38 this._cssResultsElement.removeChildren(); | 38 this._cssResultsElement.removeChildren(); |
39 this._progressElement.textContent = ''; | 39 this._progressElement.textContent = ''; |
40 this._cssResultsElement.appendChild(this._progressElement); | 40 this._cssResultsElement.appendChild(this._progressElement); |
41 | 41 |
42 this._treeOutline.removeChildren(); | |
43 this._statusMessageElement.textContent = ''; | 42 this._statusMessageElement.textContent = ''; |
44 } | 43 } |
45 | 44 |
46 /** | 45 /** |
47 * @param {boolean} enable | 46 * @param {boolean} enable |
48 */ | 47 */ |
49 _toggleRecording(enable) { | 48 _toggleRecording(enable) { |
50 if (enable === this._isRecording) | 49 if (enable === this._isRecording) |
51 return; | 50 return; |
52 | 51 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 entry => this._populateSourceInfo(/** @type {!CSSTracker.StyleSheetUsa
ge} */ (entry)))); | 108 entry => this._populateSourceInfo(/** @type {!CSSTracker.StyleSheetUsa
ge} */ (entry)))); |
110 } | 109 } |
111 | 110 |
112 /** | 111 /** |
113 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages | 112 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages |
114 * @this {!CSSTracker.CSSTrackerView} | 113 * @this {!CSSTracker.CSSTrackerView} |
115 */ | 114 */ |
116 function updateViews(styleSheetUsages) { | 115 function updateViews(styleSheetUsages) { |
117 this._updateStats(styleSheetUsages); | 116 this._updateStats(styleSheetUsages); |
118 this._updateGutter(styleSheetUsages); | 117 this._updateGutter(styleSheetUsages); |
119 this._updateTree(styleSheetUsages); | 118 this._cssResultsElement.removeChildren(); |
| 119 this._listView.update(styleSheetUsages); |
| 120 this._listView.show(this._cssResultsElement); |
120 } | 121 } |
121 } | 122 } |
122 | 123 |
123 /** | 124 /** |
124 * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage | 125 * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage |
125 * @return {!Promise<!CSSTracker.StyleSheetUsage>} | 126 * @return {!Promise<!CSSTracker.StyleSheetUsage>} |
126 */ | 127 */ |
127 _populateSourceInfo(styleSheetUsage) { | 128 _populateSourceInfo(styleSheetUsage) { |
128 if (!styleSheetUsage.styleSheetHeader) | 129 if (!styleSheetUsage.styleSheetHeader) |
129 return Promise.resolve(styleSheetUsage); | 130 return Promise.resolve(styleSheetUsage); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
184 if (!uiSourceCode) | 185 if (!uiSourceCode) |
185 continue; | 186 continue; |
186 for (var rule of styleSheet.rules) { | 187 for (var rule of styleSheet.rules) { |
187 var gutterRange = Common.TextRange.fromObject(rule.range); | 188 var gutterRange = Common.TextRange.fromObject(rule.range); |
188 if (gutterRange.startColumn) | 189 if (gutterRange.startColumn) |
189 gutterRange.startColumn--; | 190 gutterRange.startColumn--; |
190 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe
corator.type, rule.wasUsed); | 191 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe
corator.type, rule.wasUsed); |
191 } | 192 } |
192 } | 193 } |
193 } | 194 } |
194 | |
195 /** | |
196 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages | |
197 */ | |
198 _updateTree(styleSheetUsages) { | |
199 this._cssResultsElement.removeChildren(); | |
200 this._cssResultsElement.appendChild(this._treeOutline.element); | |
201 | |
202 for (var sheet of styleSheetUsages) { | |
203 var unusedRuleCount = sheet.rules.reduce((count, rule) => rule.wasUsed ? c
ount : count + 1, 0); | |
204 if (sheet.styleSheetHeader) { | |
205 var url = sheet.styleSheetHeader.sourceURL; | |
206 if (!url) | |
207 continue; | |
208 | |
209 var styleSheetTreeElement = new CSSTracker.CSSTrackerView.StyleSheetTree
Element(url, sheet.rules); | |
210 this._treeOutline.appendChild(styleSheetTreeElement); | |
211 continue; | |
212 } | |
213 if (!unusedRuleCount) | |
214 continue; | |
215 var removedStyleSheetStats = unusedRuleCount === 1 ? | |
216 Common.UIString('1 unused rule in a removed style sheet.') : | |
217 Common.UIString('%d unused rules in removed style sheets.', unusedRule
Count); | |
218 | |
219 var treeElement = new UI.TreeElement(Common.UIString('Unknown style sheets
'), true); | |
220 treeElement.toggleOnClick = true; | |
221 treeElement.selectable = false; | |
222 | |
223 var stats = new UI.TreeElement(removedStyleSheetStats, false); | |
224 stats.selectable = false; | |
225 treeElement.appendChild(stats); | |
226 this._treeOutline.appendChild(treeElement); | |
227 } | |
228 } | |
229 }; | 195 }; |
230 | 196 |
231 /** @typedef {{range: !Protocol.CSS.SourceRange, | 197 /** @typedef {{range: !Protocol.CSS.SourceRange, |
232 * selector: (string|undefined), | 198 * selector: (string|undefined), |
233 * wasUsed: boolean}} | 199 * wasUsed: boolean}} |
234 */ | 200 */ |
235 CSSTracker.RuleUsage; | 201 CSSTracker.RuleUsage; |
236 | 202 |
237 /** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTra
cker.RuleUsage>}} */ | 203 /** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTra
cker.RuleUsage>}} */ |
238 CSSTracker.StyleSheetUsage; | 204 CSSTracker.StyleSheetUsage; |
239 | 205 |
240 CSSTracker.CSSTrackerView._rulesShownAtOnce = 20; | |
241 | |
242 CSSTracker.CSSTrackerView.StyleSheetTreeElement = class extends UI.TreeElement { | |
243 /** | |
244 * @param {string} url | |
245 * @param {!Array<!CSSTracker.RuleUsage>} ruleList | |
246 */ | |
247 constructor(url, ruleList) { | |
248 super('', true); | |
249 | |
250 this._uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url); | |
251 | |
252 /** @type {!Array<!CSSTracker.RuleUsage>} */ | |
253 this._unusedRules = ruleList.filter(rule => !rule.wasUsed); | |
254 | |
255 var lastLineNumber = 0; | |
256 for (var i = this._unusedRules.length - 1; i >= 0; --i) { | |
257 if (this._unusedRules[i].range) { | |
258 lastLineNumber = this._unusedRules[i].range.startLine; | |
259 break; | |
260 } | |
261 } | |
262 this._numberOfSpaces = lastLineNumber.toString().length + 1; | |
263 | |
264 this._percentUnused = Math.round(100 * this._unusedRules.length / ruleList.l
ength); | |
265 | |
266 this.toggleOnClick = true; | |
267 this.selectable = false; | |
268 | |
269 /** @type {?UI.TreeElement} */ | |
270 this._showAllRulesTreeElement = null; | |
271 | |
272 var title = createElementWithClass('div', 'rule-result'); | |
273 var titleText; | |
274 if (this._uiSourceCode) | |
275 titleText = this._uiSourceCode.fullDisplayName(); | |
276 else | |
277 titleText = Common.UIString('Style Sheet was removed'); | |
278 title.createChild('span', 'rule-result-file-name').textContent = titleText; | |
279 | |
280 var rulesCountSpan = title.createChild('span', 'rule-result-matches-count'); | |
281 | |
282 if (this._unusedRules.length === 1) { | |
283 rulesCountSpan.textContent = | |
284 Common.UIString('(%d unused rule : %d%%)', this._unusedRules.length, t
his._percentUnused); | |
285 } else { | |
286 rulesCountSpan.textContent = | |
287 Common.UIString('(%d unused rules : %d%%)', this._unusedRules.length,
this._percentUnused); | |
288 } | |
289 this.title = title; | |
290 } | |
291 | |
292 /** | |
293 * @override | |
294 */ | |
295 onpopulate() { | |
296 var toIndex = Math.min(this._unusedRules.length, CSSTracker.CSSTrackerView._
rulesShownAtOnce); | |
297 this._appendRules(0, toIndex); | |
298 if (toIndex < this._unusedRules.length) | |
299 this._appendShowAllRulesButton(toIndex); | |
300 } | |
301 | |
302 /** | |
303 * @param {number} fromIndex | |
304 * @param {number} toIndex | |
305 */ | |
306 _appendRules(fromIndex, toIndex) { | |
307 for (var i = fromIndex; i < toIndex; ++i) { | |
308 if (!this._uiSourceCode) { | |
309 var rule = this._unusedRules[i]; | |
310 var contentSpan = createElementWithClass('span', 'rule-match-content'); | |
311 contentSpan.textContent = rule.selector; | |
312 ruleElement.listItemElement.appendChild(contentSpan); | |
313 continue; | |
314 } | |
315 | |
316 var rule = this._unusedRules[i]; | |
317 var lineNumber = rule.range.startLine; | |
318 var columnNumber = rule.range.startColumn; | |
319 | |
320 var anchor = Components.Linkifier.linkifyRevealable(this._uiSourceCode.uiL
ocation(lineNumber, columnNumber), ''); | |
321 | |
322 var lineNumberSpan = createElement('span'); | |
323 lineNumberSpan.classList.add('rule-match-line-number'); | |
324 lineNumberSpan.textContent = numberToStringWithSpacesPadding(lineNumber +
1, this._numberOfSpaces); | |
325 anchor.appendChild(lineNumberSpan); | |
326 | |
327 var contentSpan = anchor.createChild('span', 'rule-match-content'); | |
328 contentSpan.textContent = rule.selector; | |
329 | |
330 var ruleElement = new UI.TreeElement(); | |
331 ruleElement.selectable = true; | |
332 this.appendChild(ruleElement); | |
333 ruleElement.listItemElement.className = 'rule-match source-code'; | |
334 ruleElement.listItemElement.appendChild(anchor); | |
335 } | |
336 } | |
337 | |
338 /** | |
339 * @param {number} startMatchIndex | |
340 */ | |
341 _appendShowAllRulesButton(startMatchIndex) { | |
342 var rulesLeftCount = this._unusedRules.length - startMatchIndex; | |
343 var button = UI.createTextButton('', this._showMoreRulesElementSelected.bind
(this, startMatchIndex)); | |
344 button.textContent = Common.UIString('Show all rules (%d more).', rulesLeftC
ount); | |
345 this._showAllRulesTreeElement = new UI.TreeElement(button); | |
346 this._showAllRulesTreeElement.selectable = false; | |
347 this.appendChild(this._showAllRulesTreeElement); | |
348 } | |
349 | |
350 /** | |
351 * @param {number} startMatchIndex | |
352 */ | |
353 _showMoreRulesElementSelected(startMatchIndex) { | |
354 if (!this._showAllRulesTreeElement) | |
355 return; | |
356 this.removeChild(this._showAllRulesTreeElement); | |
357 this._appendRules(startMatchIndex, this._unusedRules.length); | |
358 } | |
359 }; | |
360 | |
361 /** | 206 /** |
362 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator} | 207 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator} |
363 */ | 208 */ |
364 CSSTracker.CSSTrackerView.LineDecorator = class { | 209 CSSTracker.CSSTrackerView.LineDecorator = class { |
365 /** | 210 /** |
366 * @override | 211 * @override |
367 * @param {!Workspace.UISourceCode} uiSourceCode | 212 * @param {!Workspace.UISourceCode} uiSourceCode |
368 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | 213 * @param {!TextEditor.CodeMirrorTextEditor} textEditor |
369 */ | 214 */ |
370 decorate(uiSourceCode, textEditor) { | 215 decorate(uiSourceCode, textEditor) { |
(...skipping 14 matching lines...) Expand all Loading... |
385 else | 230 else |
386 element.className = 'text-editor-css-rule-unused-marker'; | 231 element.className = 'text-editor-css-rule-unused-marker'; |
387 | 232 |
388 textEditor.setGutterDecoration(line, gutterType, element); | 233 textEditor.setGutterDecoration(line, gutterType, element); |
389 } | 234 } |
390 } | 235 } |
391 } | 236 } |
392 }; | 237 }; |
393 | 238 |
394 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage'; | 239 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage'; |
OLD | NEW |