| 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 |