Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(552)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js

Issue 2705433003: DevTools: decouple model logic from presentation within CSSTrackerView (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source.html ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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';
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/inspector/css_tracker/highlight-in-source.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698