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

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: Review comments addressed 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 } 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
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';
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