Chromium Code Reviews| 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 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 74 var mainTarget = SDK.targetManager.mainTarget(); | 74 var mainTarget = SDK.targetManager.mainTarget(); |
| 75 if (!mainTarget) | 75 if (!mainTarget) |
| 76 return; | 76 return; |
| 77 | 77 |
| 78 this._recordButton.setTitle(Common.UIString('Start recording')); | 78 this._recordButton.setTitle(Common.UIString('Start recording')); |
| 79 this._progressElement.textContent = Common.UIString('Fetching results...'); | 79 this._progressElement.textContent = Common.UIString('Fetching results...'); |
| 80 | 80 |
| 81 var cssModel = SDK.CSSModel.fromTarget(mainTarget); | 81 var cssModel = SDK.CSSModel.fromTarget(mainTarget); |
| 82 if (!cssModel) | 82 if (!cssModel) |
| 83 return; | 83 return; |
| 84 cssModel.ruleListPromise().then(ruleListReceived.bind(this, cssModel)); | 84 |
| 85 cssModel.ruleListPromise() | |
|
alph
2017/02/17 19:08:55
nit: wanna be a cool guy and start using async/awa
caseq
2017/02/17 22:22:17
No.
| |
| 86 .then(processRuleList.bind(this)) | |
| 87 .then(updateViews.bind(this)); | |
| 85 | 88 |
| 86 /** | 89 /** |
| 87 * @param {!SDK.CSSModel} cssModel | 90 * @param {!Array<!SDK.CSSModel.RuleUsage>} ruleUsageList |
| 88 * @param {!Array<!CSSTracker.RuleUsage>} ruleUsageList | 91 * @this {!CSSTracker.CSSTrackerView} |
| 92 * @return {!Promise<!Array<!CSSTracker.StyleSheetUsage>>} | |
| 93 */ | |
| 94 function processRuleList(ruleUsageList) { | |
| 95 /** @type {!Map<?SDK.CSSStyleSheetHeader, !CSSTracker.StyleSheetUsage>} */ | |
| 96 var rulesByStyleSheet = new Map(); | |
| 97 for (var rule of ruleUsageList) { | |
| 98 var styleSheetHeader = cssModel.styleSheetHeaderForId(rule.styleSheetId) ; | |
|
alph
2017/02/17 19:08:55
nit: || null;
caseq
2017/02/17 22:22:17
It's already so in the function being called.
| |
| 99 var entry = rulesByStyleSheet.get(styleSheetHeader); | |
| 100 if (!entry) { | |
| 101 entry = {styleSheetHeader: styleSheetHeader, rules: []}; | |
| 102 rulesByStyleSheet.set(styleSheetHeader, entry); | |
| 103 } | |
| 104 entry.rules.push(rule); | |
| 105 } | |
| 106 var styleSheetUsages = Array.from(rulesByStyleSheet.values()); | |
| 107 return Promise.all(styleSheetUsages.map(entry => this._populateSourceInfo( entry))).then(() => styleSheetUsages); | |
|
alph
2017/02/17 19:08:55
nit: if you make _populateSourceInfo return entry,
caseq
2017/02/17 22:22:17
done.
| |
| 108 } | |
| 109 | |
| 110 /** | |
| 111 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages | |
| 89 * @this {!CSSTracker.CSSTrackerView} | 112 * @this {!CSSTracker.CSSTrackerView} |
| 90 */ | 113 */ |
| 91 function ruleListReceived(cssModel, ruleUsageList) { | 114 function updateViews(styleSheetUsages) { |
| 92 var unusedRulesCount = 0; | 115 this._updateStats(styleSheetUsages); |
| 93 for (var rule of ruleUsageList) { | 116 this._updateGutter(styleSheetUsages); |
| 94 if (!rule.wasUsed) | 117 this._updateTree(styleSheetUsages); |
| 95 unusedRulesCount++; | 118 } |
| 119 } | |
| 96 | 120 |
| 97 var url = this._urlForStyleSheetId(cssModel, rule.styleSheetId); | 121 /** |
| 98 if (!url) | 122 * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage |
|
alph
2017/02/17 19:08:55
@return
caseq
2017/02/17 22:22:17
done.
| |
| 99 continue; | 123 */ |
| 100 var uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url); | 124 _populateSourceInfo(styleSheetUsage) { |
| 101 if (!uiSourceCode) | 125 if (!styleSheetUsage.styleSheetHeader) |
| 102 continue; | 126 return Promise.resolve(); |
| 127 var ruleIndex = new Map(styleSheetUsage.rules.map( | |
| 128 rule => [`${rule.range.startLine}.${rule.range.startColumn}`, rule])); | |
| 103 | 129 |
| 130 return new Promise(fulfill => { | |
| 131 styleSheetUsage.styleSheetHeader.requestContent() | |
| 132 .then(content => Common.formatterWorkerPool.parseCSS(content || '', on Rules)); | |
| 133 | |
| 134 /** | |
| 135 * @param {boolean} isLastChunk | |
| 136 * @param {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} rules | |
| 137 */ | |
| 138 function onRules(isLastChunk, rules) { | |
| 139 for (var rule of rules) { | |
| 140 if (!rule.styleRange) | |
| 141 continue; | |
| 142 var entry = ruleIndex.get(`${rule.styleRange.startLine}.${rule.styleRa nge.startColumn}`); | |
| 143 if (entry) | |
| 144 entry.selector = rule.selectorText; | |
| 145 } | |
| 146 if (isLastChunk) | |
| 147 fulfill(); | |
| 148 } | |
| 149 }); | |
| 150 } | |
| 151 | |
| 152 /** | |
| 153 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsage | |
| 154 */ | |
| 155 _updateStats(styleSheetUsage) { | |
| 156 var total = 0; | |
| 157 var unused = 0; | |
| 158 for (var styleSheet of styleSheetUsage) { | |
| 159 total += styleSheet.length; | |
| 160 unused += styleSheet.rules.reduce((count, rule) => rule.wasUsed ? count : count + 1); | |
|
alph
2017/02/17 19:08:55
you need an initial value
caseq
2017/02/17 22:22:17
thanks, fixed!
| |
| 161 } | |
| 162 var percentUnused = Math.round(100 * unused / total); | |
|
alph
2017/02/17 19:08:55
can total be 0?
| |
| 163 if (unused === 1) { | |
| 164 this._statusMessageElement.textContent = | |
| 165 Common.UIString('%d CSS rule is not used. (%d%%)', unused, percentUnus ed); | |
| 166 } else { | |
| 167 this._statusMessageElement.textContent = | |
| 168 Common.UIString('%d CSS rules are not used. (%d%%)', unused, percentUn used); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages | |
| 174 */ | |
| 175 _updateGutter(styleSheetUsages) { | |
| 176 for (var styleSheet of styleSheetUsages) { | |
| 177 if (!styleSheet.styleSheetHeader) | |
| 178 continue; | |
| 179 var url = styleSheet.styleSheetHeader.sourceURL; | |
| 180 var uiSourceCode = url && Workspace.workspace.uiSourceCodeForURL(url); | |
| 181 if (!uiSourceCode) | |
| 182 continue; | |
| 183 for (var rule of styleSheet.rules) { | |
| 104 var gutterRange = Common.TextRange.fromObject(rule.range); | 184 var gutterRange = Common.TextRange.fromObject(rule.range); |
| 105 if (gutterRange.startColumn) | 185 if (gutterRange.startColumn) |
| 106 gutterRange.startColumn--; | 186 gutterRange.startColumn--; |
| 107 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe corator.type, rule.wasUsed); | 187 uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDe corator.type, rule.wasUsed); |
| 108 } | 188 } |
| 109 var percentUnused = Math.round(100 * unusedRulesCount / ruleUsageList.leng th); | |
| 110 if (unusedRulesCount === 1) { | |
| 111 this._statusMessageElement.textContent = | |
| 112 Common.UIString('%d CSS rule is not used. (%d%%)', unusedRulesCount, percentUnused); | |
| 113 } else { | |
| 114 this._statusMessageElement.textContent = | |
| 115 Common.UIString('%d CSS rules are not used. (%d%%)', unusedRulesCoun t, percentUnused); | |
| 116 } | |
| 117 | |
| 118 this._renderRuleUsage(cssModel, ruleUsageList); | |
| 119 } | 189 } |
| 120 } | 190 } |
| 121 | 191 |
| 122 /** | 192 /** |
| 123 * @param {!Array<!SDK.CSSStyleSheetHeader>} styleSheetHeaders | 193 * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages |
| 124 * @return {!Promise<!Array<!CSSTracker.ParsedStyleSheet>>} | |
| 125 */ | 194 */ |
| 126 _parseStyleSheets(styleSheetHeaders) { | 195 _updateTree(styleSheetUsages) { |
| 127 var promises = styleSheetHeaders.map(header => parseStyleSheet(header)); | |
| 128 return Promise.all(promises); | |
| 129 | |
| 130 /** | |
| 131 * @param {!SDK.CSSStyleSheetHeader} styleSheet | |
| 132 * @return {!Promise<!CSSTracker.ParsedStyleSheet>} | |
| 133 */ | |
| 134 function parseStyleSheet(styleSheet) { | |
| 135 return new Promise(fulfill => { | |
| 136 /** @type {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} */ | |
| 137 var allRules = []; | |
| 138 styleSheet.requestContent().then(content => Common.formatterWorkerPool.p arseCSS(content || '', onRules)); | |
| 139 | |
| 140 /** | |
| 141 * @param {boolean} isLastChunk | |
| 142 * @param {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} rules | |
| 143 */ | |
| 144 function onRules(isLastChunk, rules) { | |
| 145 allRules.pushAll(rules); | |
| 146 if (isLastChunk) | |
| 147 fulfill({sourceURL: styleSheet.sourceURL, rules: allRules}); | |
| 148 } | |
| 149 }); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 /** | |
| 154 * @param {!SDK.CSSModel} cssModel | |
| 155 * @param {!Array<!CSSTracker.RuleUsage>} ruleList | |
| 156 */ | |
| 157 _renderRuleUsage(cssModel, ruleList) { | |
| 158 var headers = cssModel.allStyleSheets(); | |
| 159 if (!headers.length) | |
| 160 return; | |
| 161 | |
| 162 this._parseStyleSheets(headers).then(this._onGotStyleSheets.bind(this, cssMo del, ruleList)); | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * @param {!SDK.CSSModel} cssModel | |
| 167 * @param {!Array<!CSSTracker.RuleUsage>} ruleList | |
| 168 * @param {!Array<!CSSTracker.ParsedStyleSheet>} styleSheets | |
| 169 * @this {CSSTracker.CSSTrackerView} | |
| 170 */ | |
| 171 _onGotStyleSheets(cssModel, ruleList, styleSheets) { | |
| 172 /** @type {!Map<string, string>} */ | |
| 173 var rangeToSelector = new Map(); | |
| 174 | |
| 175 for (var styleSheet of styleSheets) { | |
| 176 for (var rule of styleSheet.rules) { | |
| 177 if (!rule.styleRange) | |
| 178 continue; | |
| 179 | |
| 180 rangeToSelector.set( | |
| 181 rule.styleRange.startLine + ',' + rule.styleRange.startColumn + ',' + styleSheet.sourceURL, | |
| 182 rule.selectorText); | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 var unattributedRulesCount = 0; | |
| 187 | |
| 188 for (var rule of ruleList) { | |
| 189 var url = this._urlForStyleSheetId(cssModel, rule.styleSheetId); | |
| 190 if (url === '') | |
| 191 continue; | |
| 192 var range = rule.range.startLine + ',' + rule.range.startColumn + ',' + ur l; | |
| 193 rule.selector = rangeToSelector.get(range); | |
| 194 if (!rule.selector && !rule.wasUsed) | |
| 195 ++unattributedRulesCount; | |
| 196 } | |
| 197 ruleList = ruleList.filter(rule => rule.selector); | |
| 198 | |
| 199 this._cssResultsElement.removeChildren(); | 196 this._cssResultsElement.removeChildren(); |
| 200 | |
| 201 if (unattributedRulesCount) { | |
| 202 if (unattributedRulesCount === 1) { | |
| 203 var removedStyleSheetStats = Common.UIString('1 unused rule in a removed style sheet.'); | |
| 204 } else { | |
| 205 var removedStyleSheetStats = | |
| 206 Common.UIString('%d unused rules in removed style sheets.', unattrib utedRulesCount); | |
| 207 } | |
| 208 var treeElement = new UI.TreeElement(Common.UIString('Unknown style sheets '), true); | |
| 209 treeElement.toggleOnClick = true; | |
| 210 treeElement.selectable = false; | |
| 211 | |
| 212 var stats = new UI.TreeElement(removedStyleSheetStats, false); | |
| 213 stats.selectable = false; | |
| 214 treeElement.appendChild(stats); | |
| 215 this._treeOutline.appendChild(treeElement); | |
| 216 } | |
| 217 | |
| 218 if (!ruleList.length) | |
| 219 return; | |
| 220 | |
| 221 this._cssResultsElement.appendChild(this._treeOutline.element); | 197 this._cssResultsElement.appendChild(this._treeOutline.element); |
| 222 | 198 |
| 223 var startPosition = 0; | 199 for (var sheet of styleSheetUsages) { |
| 224 for (var i = 1; i < ruleList.length; ++i) { | 200 var unusedRuleCount = sheet.rules.reduce((count, rule) => rule.wasUsed ? c ount : count + 1, 0); |
| 225 if (ruleList[i].styleSheetId === ruleList[i - 1].styleSheetId) | 201 if (!sheet.styleSheetHeader) { |
|
alph
2017/02/17 19:08:55
can you plz reverse the condition
| |
| 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) | |
| 226 continue; | 222 continue; |
| 227 | 223 |
| 228 var url = this._urlForStyleSheetId(cssModel, ruleList[startPosition].style SheetId); | |
| 229 var styleSheetTreeElement = | 224 var styleSheetTreeElement = |
| 230 new CSSTracker.CSSTrackerView.StyleSheetTreeElement(url, ruleList.slic e(startPosition, i)); | 225 new CSSTracker.CSSTrackerView.StyleSheetTreeElement(url, sheet.rules); |
| 231 this._treeOutline.appendChild(styleSheetTreeElement); | 226 this._treeOutline.appendChild(styleSheetTreeElement); |
| 232 | |
| 233 startPosition = i; | |
| 234 } | 227 } |
| 235 var url = this._urlForStyleSheetId(cssModel, ruleList[startPosition].styleSh eetId); | |
| 236 var styleSheetTreeElement = new CSSTracker.CSSTrackerView.StyleSheetTreeElem ent(url, ruleList.slice(startPosition)); | |
| 237 this._treeOutline.appendChild(styleSheetTreeElement); | |
| 238 } | |
| 239 | |
| 240 /** | |
| 241 * @param {string} styleSheetId | |
| 242 * @param {!SDK.CSSModel} cssModel | |
| 243 * @return {string} | |
| 244 */ | |
| 245 _urlForStyleSheetId(cssModel, styleSheetId) { | |
| 246 var styleSheetHeader = cssModel.styleSheetHeaderForId(styleSheetId); | |
| 247 if (!styleSheetHeader) | |
| 248 return ''; | |
| 249 return styleSheetHeader.sourceURL; | |
| 250 } | 228 } |
| 251 }; | 229 }; |
| 252 | 230 |
| 253 /** @typedef {{range: !Protocol.CSS.SourceRange, | 231 /** @typedef {{range: !Protocol.CSS.SourceRange, |
| 254 * selector: (string|undefined), | 232 * selector: (string|undefined), |
| 255 * styleSheetId: !Protocol.CSS.StyleSheetId, | |
| 256 * wasUsed: boolean}} | 233 * wasUsed: boolean}} |
| 257 */ | 234 */ |
| 258 CSSTracker.RuleUsage; | 235 CSSTracker.RuleUsage; |
| 259 | 236 |
| 260 /** @typedef {{sourceURL: string, rules: !Array<!Common.FormatterWorkerPool.CSSS tyleRule>}} */ | 237 /** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTra cker.RuleUsage>}} */ |
| 261 CSSTracker.ParsedStyleSheet; | 238 CSSTracker.StyleSheetUsage; |
| 262 | 239 |
| 263 CSSTracker.CSSTrackerView._rulesShownAtOnce = 20; | 240 CSSTracker.CSSTrackerView._rulesShownAtOnce = 20; |
| 264 | 241 |
| 265 CSSTracker.CSSTrackerView.StyleSheetTreeElement = class extends UI.TreeElement { | 242 CSSTracker.CSSTrackerView.StyleSheetTreeElement = class extends UI.TreeElement { |
| 266 /** | 243 /** |
| 267 * @param {string} url | 244 * @param {string} url |
| 268 * @param {!Array<!CSSTracker.RuleUsage>} ruleList | 245 * @param {!Array<!CSSTracker.RuleUsage>} ruleList |
| 269 */ | 246 */ |
| 270 constructor(url, ruleList) { | 247 constructor(url, ruleList) { |
| 271 super('', true); | 248 super('', true); |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 408 else | 385 else |
| 409 element.className = 'text-editor-css-rule-unused-marker'; | 386 element.className = 'text-editor-css-rule-unused-marker'; |
| 410 | 387 |
| 411 textEditor.setGutterDecoration(line, gutterType, element); | 388 textEditor.setGutterDecoration(line, gutterType, element); |
| 412 } | 389 } |
| 413 } | 390 } |
| 414 } | 391 } |
| 415 }; | 392 }; |
| 416 | 393 |
| 417 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage'; | 394 CSSTracker.CSSTrackerView.LineDecorator.type = 'coverage'; |
| OLD | NEW |