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 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 return Promise.all(styleSheetUsages.map(entry => this._populateSourceInfo(
entry))).then(() => styleSheetUsages); | 106 return Promise.all(styleSheetUsages.map(entry => this._populateSourceInfo(
entry))).then(() => styleSheetUsages); |
108 } | 107 } |
109 | 108 |
110 /** | 109 /** |
111 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages | 110 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages |
112 * @this {!CSSTracker.CSSTrackerView} | 111 * @this {!CSSTracker.CSSTrackerView} |
113 */ | 112 */ |
114 function updateViews(styleSheetUsages) { | 113 function updateViews(styleSheetUsages) { |
115 this._updateStats(styleSheetUsages); | 114 this._updateStats(styleSheetUsages); |
116 this._updateGutter(styleSheetUsages); | 115 this._updateGutter(styleSheetUsages); |
117 this._updateTree(styleSheetUsages); | 116 this._cssResultsElement.removeChildren(); |
| 117 this._listView.update(styleSheetUsages); |
| 118 this._listView.show(this._cssResultsElement); |
118 } | 119 } |
119 } | 120 } |
120 | 121 |
121 /** | 122 /** |
122 * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage | 123 * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage |
123 */ | 124 */ |
124 _populateSourceInfo(styleSheetUsage) { | 125 _populateSourceInfo(styleSheetUsage) { |
125 if (!styleSheetUsage.styleSheetHeader) | 126 if (!styleSheetUsage.styleSheetHeader) |
126 return Promise.resolve(); | 127 return Promise.resolve(); |
127 var ruleIndex = new Map(styleSheetUsage.rules.map( | 128 var ruleIndex = new Map(styleSheetUsage.rules.map( |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 continue; | 183 continue; |
183 for (var rule of styleSheet.rules) { | 184 for (var rule of styleSheet.rules) { |
184 var gutterRange = Common.TextRange.fromObject(rule.range); | 185 var gutterRange = Common.TextRange.fromObject(rule.range); |
185 if (gutterRange.startColumn) | 186 if (gutterRange.startColumn) |
186 gutterRange.startColumn--; | 187 gutterRange.startColumn--; |
187 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe
corator.type, rule.wasUsed); | 188 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe
corator.type, rule.wasUsed); |
188 } | 189 } |
189 } | 190 } |
190 } | 191 } |
191 | 192 |
192 /** | |
193 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages | |
194 */ | |
195 _updateTree(styleSheetUsages) { | |
196 this._cssResultsElement.removeChildren(); | |
197 this._cssResultsElement.appendChild(this._treeOutline.element); | |
198 | |
199 for (var sheet of styleSheetUsages) { | |
200 var unusedRuleCount = sheet.rules.reduce((count, rule) => rule.wasUsed ? c
ount : count + 1, 0); | |
201 if (!sheet.styleSheetHeader) { | |
202 if (!unusedRuleCount) | |
203 continue; | |
204 if (unusedRuleCount === 1) { | |
205 var removedStyleSheetStats = Common.UIString('1 unused rule in a remov
ed style sheet.'); | |
206 } else { | |
207 var removedStyleSheetStats = | |
208 Common.UIString('%d unused rules in removed style sheets.', unused
RuleCount); | |
209 } | |
210 var treeElement = new UI.TreeElement(Common.UIString('Unknown style shee
ts'), true); | |
211 treeElement.toggleOnClick = true; | |
212 treeElement.selectable = false; | |
213 | |
214 var stats = new UI.TreeElement(removedStyleSheetStats, false); | |
215 stats.selectable = false; | |
216 treeElement.appendChild(stats); | |
217 this._treeOutline.appendChild(treeElement); | |
218 continue; | |
219 } | |
220 var url = sheet.styleSheetHeader.sourceURL; | |
221 if (!url) | |
222 continue; | |
223 | |
224 var styleSheetTreeElement = | |
225 new CSSTracker.CSSTrackerView.StyleSheetTreeElement(url, sheet.rules); | |
226 this._treeOutline.appendChild(styleSheetTreeElement); | |
227 } | |
228 } | |
229 }; | 193 }; |
230 | 194 |
231 /** @typedef {{range: !Protocol.CSS.SourceRange, | 195 /** @typedef {{range: !Protocol.CSS.SourceRange, |
232 * selector: (string|undefined), | 196 * selector: (string|undefined), |
233 * wasUsed: boolean}} | 197 * wasUsed: boolean}} |
234 */ | 198 */ |
235 CSSTracker.RuleUsage; | 199 CSSTracker.RuleUsage; |
236 | 200 |
237 /** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTra
cker.RuleUsage>}} */ | 201 /** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTra
cker.RuleUsage>}} */ |
238 CSSTracker.StyleSheetUsage; | 202 CSSTracker.StyleSheetUsage; |
239 | 203 |
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 /** | 204 /** |
362 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator} | 205 * @implements {SourceFrame.UISourceCodeFrame.LineDecorator} |
363 */ | 206 */ |
364 CSSTracker.CSSTrackerView.LineDecorator = class { | 207 CSSTracker.CSSTrackerView.LineDecorator = class { |
365 /** | 208 /** |
366 * @override | 209 * @override |
367 * @param {!Workspace.UISourceCode} uiSourceCode | 210 * @param {!Workspace.UISourceCode} uiSourceCode |
368 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | 211 * @param {!TextEditor.CodeMirrorTextEditor} textEditor |
369 */ | 212 */ |
370 decorate(uiSourceCode, textEditor) { | 213 decorate(uiSourceCode, textEditor) { |
(...skipping 14 matching lines...) Expand all Loading... |
385 else | 228 else |
386 element.className = 'text-editor-css-rule-unused-marker'; | 229 element.className = 'text-editor-css-rule-unused-marker'; |
387 | 230 |
388 textEditor.setGutterDecoration(line, gutterType, element); | 231 textEditor.setGutterDecoration(line, gutterType, element); |
389 } | 232 } |
390 } | 233 } |
391 } | 234 } |
392 }; | 235 }; |
393 | 236 |
394 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage'; | 237 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage'; |
OLD | NEW |