Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js b/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js |
| index 38318b0f0469f6a8c30c606a7a8f4e1aa53630c6..354ed14157a3bd2316f65531dada472244e3c6b3 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/css_tracker/CSSTrackerView.js |
| @@ -84,130 +84,138 @@ CSSTracker.CSSTrackerView = class extends UI.VBox { |
| var cssModel = mainTarget.model(SDK.CSSModel); |
|
dgozman
2017/02/18 06:21:09
While you are working on this, could you make it w
|
| if (!cssModel) |
| return; |
| - cssModel.ruleListPromise().then(ruleListReceived.bind(this, cssModel)); |
| + |
| + cssModel.ruleListPromise().then(processRuleList.bind(this)).then(updateViews.bind(this)); |
| /** |
| - * @param {!SDK.CSSModel} cssModel |
| - * @param {!Array<!CSSTracker.RuleUsage>} ruleUsageList |
| + * @param {!Array<!SDK.CSSModel.RuleUsage>} ruleUsageList |
| * @this {!CSSTracker.CSSTrackerView} |
| + * @return {!Promise<!Array<!CSSTracker.StyleSheetUsage>>} |
| */ |
| - function ruleListReceived(cssModel, ruleUsageList) { |
| - var unusedRulesCount = 0; |
| + function processRuleList(ruleUsageList) { |
| + /** @type {!Map<?SDK.CSSStyleSheetHeader, !CSSTracker.StyleSheetUsage>} */ |
| + var rulesByStyleSheet = new Map(); |
| for (var rule of ruleUsageList) { |
| - if (!rule.wasUsed) |
| - unusedRulesCount++; |
| - |
| - var url = this._urlForStyleSheetId(cssModel, rule.styleSheetId); |
| - if (!url) |
| - continue; |
| - var uiSourceCode = Workspace.workspace.uiSourceCodeForURL(url); |
| - if (!uiSourceCode) |
| - continue; |
| - |
| - var gutterRange = Common.TextRange.fromObject(rule.range); |
| - if (gutterRange.startColumn) |
| - gutterRange.startColumn--; |
| - uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDecorator.type, rule.wasUsed); |
| - } |
| - var percentUnused = Math.round(100 * unusedRulesCount / ruleUsageList.length); |
| - if (unusedRulesCount === 1) { |
| - this._statusMessageElement.textContent = |
| - Common.UIString('%d CSS rule is not used. (%d%%)', unusedRulesCount, percentUnused); |
| - } else { |
| - this._statusMessageElement.textContent = |
| - Common.UIString('%d CSS rules are not used. (%d%%)', unusedRulesCount, percentUnused); |
| + var styleSheetHeader = cssModel.styleSheetHeaderForId(rule.styleSheetId); |
| + var entry = rulesByStyleSheet.get(styleSheetHeader); |
| + if (!entry) { |
| + entry = {styleSheetHeader: styleSheetHeader, rules: []}; |
| + rulesByStyleSheet.set(styleSheetHeader, entry); |
| + } |
| + entry.rules.push(rule); |
| } |
| + return Promise.all(Array.from( |
| + rulesByStyleSheet.values(), |
| + entry => this._populateSourceInfo(/** @type {!CSSTracker.StyleSheetUsage} */ (entry)))); |
| + } |
| - this._renderRuleUsage(cssModel, ruleUsageList); |
| + /** |
| + * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages |
| + * @this {!CSSTracker.CSSTrackerView} |
| + */ |
| + function updateViews(styleSheetUsages) { |
| + this._updateStats(styleSheetUsages); |
| + this._updateGutter(styleSheetUsages); |
| + this._updateTree(styleSheetUsages); |
| } |
| } |
| /** |
| - * @param {!Array<!SDK.CSSStyleSheetHeader>} styleSheetHeaders |
| - * @return {!Promise<!Array<!CSSTracker.ParsedStyleSheet>>} |
| + * @param {!CSSTracker.StyleSheetUsage} styleSheetUsage |
| + * @return {!Promise<!CSSTracker.StyleSheetUsage>} |
| */ |
| - _parseStyleSheets(styleSheetHeaders) { |
| - var promises = styleSheetHeaders.map(header => parseStyleSheet(header)); |
| - return Promise.all(promises); |
| - |
| - /** |
| - * @param {!SDK.CSSStyleSheetHeader} styleSheet |
| - * @return {!Promise<!CSSTracker.ParsedStyleSheet>} |
| - */ |
| - function parseStyleSheet(styleSheet) { |
| - return new Promise(fulfill => { |
| - /** @type {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} */ |
| - var allRules = []; |
| - styleSheet.requestContent().then(content => Common.formatterWorkerPool.parseCSS(content || '', onRules)); |
| - |
| - /** |
| - * @param {boolean} isLastChunk |
| - * @param {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} rules |
| - */ |
| - function onRules(isLastChunk, rules) { |
| - allRules.pushAll(rules); |
| - if (isLastChunk) |
| - fulfill({sourceURL: styleSheet.sourceURL, rules: allRules}); |
| + _populateSourceInfo(styleSheetUsage) { |
| + if (!styleSheetUsage.styleSheetHeader) |
| + return Promise.resolve(styleSheetUsage); |
| + var ruleIndex = |
| + new Map(styleSheetUsage.rules.map(rule => [`${rule.range.startLine}.${rule.range.startColumn}`, rule])); |
| + |
| + return new Promise(fulfill => { |
| + styleSheetUsage.styleSheetHeader.requestContent().then( |
| + content => Common.formatterWorkerPool.parseCSS(content || '', onRules)); |
| + |
| + /** |
| + * @param {boolean} isLastChunk |
| + * @param {!Array<!Common.FormatterWorkerPool.CSSStyleRule>} rules |
| + */ |
| + function onRules(isLastChunk, rules) { |
| + for (var rule of rules) { |
| + if (!rule.styleRange) |
| + continue; |
| + var entry = ruleIndex.get(`${rule.styleRange.startLine}.${rule.styleRange.startColumn}`); |
| + if (entry) |
| + entry.selector = rule.selectorText; |
| } |
| - }); |
| - } |
| + if (isLastChunk) |
| + fulfill(styleSheetUsage); |
| + } |
| + }); |
| } |
| /** |
| - * @param {!SDK.CSSModel} cssModel |
| - * @param {!Array<!CSSTracker.RuleUsage>} ruleList |
| + * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsage |
| */ |
| - _renderRuleUsage(cssModel, ruleList) { |
| - var headers = cssModel.allStyleSheets(); |
| - if (!headers.length) |
| - return; |
| - |
| - this._parseStyleSheets(headers).then(this._onGotStyleSheets.bind(this, cssModel, ruleList)); |
| + _updateStats(styleSheetUsage) { |
| + var total = 0; |
| + var unused = 0; |
| + for (var styleSheet of styleSheetUsage) { |
| + total += styleSheet.rules.length; |
| + unused += styleSheet.rules.reduce((count, rule) => rule.wasUsed ? count : count + 1, 0); |
| + } |
| + var percentUnused = total ? Math.round(100 * unused / total) : 0; |
| + if (unused === 1) { |
| + this._statusMessageElement.textContent = |
| + Common.UIString('%d CSS rule is not used. (%d%%)', unused, percentUnused); |
| + } else { |
| + this._statusMessageElement.textContent = |
| + Common.UIString('%d CSS rules are not used. (%d%%)', unused, percentUnused); |
| + } |
| } |
| /** |
| - * @param {!SDK.CSSModel} cssModel |
| - * @param {!Array<!CSSTracker.RuleUsage>} ruleList |
| - * @param {!Array<!CSSTracker.ParsedStyleSheet>} styleSheets |
| - * @this {CSSTracker.CSSTrackerView} |
| + * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages |
| */ |
| - _onGotStyleSheets(cssModel, ruleList, styleSheets) { |
| - /** @type {!Map<string, string>} */ |
| - var rangeToSelector = new Map(); |
| - |
| - for (var styleSheet of styleSheets) { |
| + _updateGutter(styleSheetUsages) { |
| + for (var styleSheet of styleSheetUsages) { |
| + if (!styleSheet.styleSheetHeader) |
| + continue; |
| + var url = styleSheet.styleSheetHeader.sourceURL; |
| + var uiSourceCode = url && Workspace.workspace.uiSourceCodeForURL(url); |
| + if (!uiSourceCode) |
| + continue; |
| for (var rule of styleSheet.rules) { |
| - if (!rule.styleRange) |
| - continue; |
| - |
| - rangeToSelector.set( |
| - rule.styleRange.startLine + ',' + rule.styleRange.startColumn + ',' + styleSheet.sourceURL, |
| - rule.selectorText); |
| + var gutterRange = Common.TextRange.fromObject(rule.range); |
| + if (gutterRange.startColumn) |
| + gutterRange.startColumn--; |
| + uiSourceCode.addDecoration(gutterRange, CSSTracker.CSSTrackerView.LineDecorator.type, rule.wasUsed); |
| } |
| } |
| + } |
| - var unattributedRulesCount = 0; |
| - |
| - for (var rule of ruleList) { |
| - var url = this._urlForStyleSheetId(cssModel, rule.styleSheetId); |
| - if (url === '') |
| - continue; |
| - var range = rule.range.startLine + ',' + rule.range.startColumn + ',' + url; |
| - rule.selector = rangeToSelector.get(range); |
| - if (!rule.selector && !rule.wasUsed) |
| - ++unattributedRulesCount; |
| - } |
| - ruleList = ruleList.filter(rule => rule.selector); |
| - |
| + /** |
| + * @param {!Array<!CSSTracker.StyleSheetUsage>} styleSheetUsages |
| + */ |
| + _updateTree(styleSheetUsages) { |
| this._cssResultsElement.removeChildren(); |
| + this._cssResultsElement.appendChild(this._treeOutline.element); |
| + |
| + for (var sheet of styleSheetUsages) { |
| + var unusedRuleCount = sheet.rules.reduce((count, rule) => rule.wasUsed ? count : count + 1, 0); |
| + if (sheet.styleSheetHeader) { |
| + var url = sheet.styleSheetHeader.sourceURL; |
| + if (!url) |
| + continue; |
| - if (unattributedRulesCount) { |
| - if (unattributedRulesCount === 1) { |
| - var removedStyleSheetStats = Common.UIString('1 unused rule in a removed style sheet.'); |
| - } else { |
| - var removedStyleSheetStats = |
| - Common.UIString('%d unused rules in removed style sheets.', unattributedRulesCount); |
| + var styleSheetTreeElement = new CSSTracker.CSSTrackerView.StyleSheetTreeElement(url, sheet.rules); |
| + this._treeOutline.appendChild(styleSheetTreeElement); |
| + continue; |
| } |
| + if (!unusedRuleCount) |
| + continue; |
| + var removedStyleSheetStats = unusedRuleCount === 1 ? |
| + Common.UIString('1 unused rule in a removed style sheet.') : |
| + Common.UIString('%d unused rules in removed style sheets.', unusedRuleCount); |
| + |
| var treeElement = new UI.TreeElement(Common.UIString('Unknown style sheets'), true); |
| treeElement.toggleOnClick = true; |
| treeElement.selectable = false; |
| @@ -217,51 +225,17 @@ CSSTracker.CSSTrackerView = class extends UI.VBox { |
| treeElement.appendChild(stats); |
| this._treeOutline.appendChild(treeElement); |
| } |
| - |
| - if (!ruleList.length) |
| - return; |
| - |
| - this._cssResultsElement.appendChild(this._treeOutline.element); |
| - |
| - var startPosition = 0; |
| - for (var i = 1; i < ruleList.length; ++i) { |
| - if (ruleList[i].styleSheetId === ruleList[i - 1].styleSheetId) |
| - continue; |
| - |
| - var url = this._urlForStyleSheetId(cssModel, ruleList[startPosition].styleSheetId); |
| - var styleSheetTreeElement = |
| - new CSSTracker.CSSTrackerView.StyleSheetTreeElement(url, ruleList.slice(startPosition, i)); |
| - this._treeOutline.appendChild(styleSheetTreeElement); |
| - |
| - startPosition = i; |
| - } |
| - var url = this._urlForStyleSheetId(cssModel, ruleList[startPosition].styleSheetId); |
| - var styleSheetTreeElement = new CSSTracker.CSSTrackerView.StyleSheetTreeElement(url, ruleList.slice(startPosition)); |
| - this._treeOutline.appendChild(styleSheetTreeElement); |
| - } |
| - |
| - /** |
| - * @param {string} styleSheetId |
| - * @param {!SDK.CSSModel} cssModel |
| - * @return {string} |
| - */ |
| - _urlForStyleSheetId(cssModel, styleSheetId) { |
| - var styleSheetHeader = cssModel.styleSheetHeaderForId(styleSheetId); |
| - if (!styleSheetHeader) |
| - return ''; |
| - return styleSheetHeader.sourceURL; |
| } |
| }; |
| /** @typedef {{range: !Protocol.CSS.SourceRange, |
| * selector: (string|undefined), |
| - * styleSheetId: !Protocol.CSS.StyleSheetId, |
| * wasUsed: boolean}} |
| */ |
| CSSTracker.RuleUsage; |
| -/** @typedef {{sourceURL: string, rules: !Array<!Common.FormatterWorkerPool.CSSStyleRule>}} */ |
| -CSSTracker.ParsedStyleSheet; |
| +/** @typedef {{styleSheetHeader: ?SDK.CSSStyleSheetHeader, rules: !Array<!CSSTracker.RuleUsage>}} */ |
| +CSSTracker.StyleSheetUsage; |
| CSSTracker.CSSTrackerView._rulesShownAtOnce = 20; |