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