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 |