From de0224ccdd985a0fa11eda17d1521286074a61b7 Mon Sep 17 00:00:00 2001 From: Paul Lewis Date: Tue, 22 Oct 2019 17:23:15 +0100 Subject: [PATCH] [CSS Overview] Uses snapshot; adds element highlight Change-Id: If58dd20c6f4b3c8f730b39c48c14826e402018a0 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/1873759 Reviewed-by: Tim van der Lippe Commit-Queue: Paul Lewis --- .../css_overview/CSSOverviewCompletedView.js | 194 +++++++++++++---- .../css_overview/CSSOverviewController.js | 2 + front_end/css_overview/CSSOverviewModel.js | 205 ++++++++++++------ front_end/css_overview/CSSOverviewPanel.js | 121 ++++------- .../css_overview/CSSOverviewProcessingView.js | 10 - .../css_overview/cssOverviewCompletedView.css | 9 +- .../css_overview/css_overview_strings.grdp | 12 +- front_end/css_overview/module.json | 67 +++--- 8 files changed, 385 insertions(+), 235 deletions(-) diff --git a/front_end/css_overview/CSSOverviewCompletedView.js b/front_end/css_overview/CSSOverviewCompletedView.js index 4665b54e18..ab21c79c2a 100644 --- a/front_end/css_overview/CSSOverviewCompletedView.js +++ b/front_end/css_overview/CSSOverviewCompletedView.js @@ -12,21 +12,36 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar { this._controller = controller; this._formatter = new Intl.NumberFormat('en-US'); - this._mainContainer = new UI.VBox(); + + this._mainContainer = new UI.SplitWidget(true, true); + this._resultsContainer = new UI.VBox(); + this._elementContainer = new UI.VBox(); + + // Dupe the styles into the main container because of the shadow root will prevent outer styles. + this._mainContainer.registerRequiredCSS('css_overview/cssOverviewCompletedView.css'); + + this._mainContainer.setMainWidget(this._resultsContainer); + this._mainContainer.setSidebarWidget(this._elementContainer); + this._mainContainer.setVertical(false); + this._mainContainer.setSecondIsSidebar(true); + this._mainContainer.setSidebarMinimized(true); this._sideBar = new CssOverview.CSSOverviewSidebarPanel(); this.splitWidget().setSidebarWidget(this._sideBar); this.splitWidget().setMainWidget(this._mainContainer); this._cssModel = target.model(SDK.CSSModel); + this._domModel = target.model(SDK.DOMModel); + this._domAgent = target.domAgent(); this._linkifier = new Components.Linkifier(/* maxLinkLength */ 20, /* useLinkDecorator */ true); + this._relatedNodesMap = new Map(); - this._columns = [ + this._mediaQueryColumns = [ {id: 'text', title: ls`Text`, visible: true, sortable: true, weight: 60}, {id: 'sourceURL', title: ls`Source`, visible: true, sortable: true, weight: 40} ]; - this._mediaQueryGrid = new DataGrid.SortableDataGrid(this._columns); + this._mediaQueryGrid = new DataGrid.SortableDataGrid(this._mediaQueryColumns); this._mediaQueryGrid.element.classList.add('media-query-grid'); this._mediaQueryGrid.setStriped(true); this._mediaQueryGrid.addEventListener( @@ -37,10 +52,20 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar { this._sideBar.addItem(ls`Media queries`, 'media-queries'); this._sideBar.select('summary'); + this._elementGridColumns = [{id: 'nodeId', title: ls`Element`, visible: true, sortable: true, weight: 100}]; + + this._elementGrid = new DataGrid.SortableDataGrid(this._elementGridColumns); + this._elementGrid.element.classList.add('element-grid'); + this._elementGrid.element.addEventListener('mouseover', this._onMouseOver.bind(this)); + this._elementGrid.setStriped(true); + this._sideBar.addEventListener(CssOverview.SidebarEvents.ItemSelected, this._sideBarItemSelected, this); this._sideBar.addEventListener(CssOverview.SidebarEvents.Reset, this._sideBarReset, this); this._controller.addEventListener(CssOverview.Events.Reset, this._reset, this); - this._render({}); + this._controller.addEventListener(CssOverview.Events.PopulateNodes, this._populateNodes, this); + this._resultsContainer.element.addEventListener('click', this._onClick.bind(this)); + + this._data = null; } _sortMediaQueryDataGrid() { @@ -67,20 +92,53 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar { } _reset() { - this._mainContainer.element.removeChildren(); + this._resultsContainer.element.removeChildren(); this._mediaQueryGrid.rootNode().removeChildren(); + this._elementGrid.rootNode().removeChildren(); + this._relatedNodesMap = new Map(); + this._mainContainer.setSidebarMinimized(true); } - _render(data) { - if (!(data && ('textColors' in data) && ('backgroundColors' in data))) { + _onClick(evt) { + const color = evt.target.dataset.color; + const section = evt.target.dataset.section; + if (!color) { return; } - const {elementStyleStats, elementCount, backgroundColors, textColors, globalStyleStats, mediaQueries} = data; + const colorNodes = + section === 'background' ? this._data.backgroundColors.get(color) : this._data.textColors.get(color); + if (!colorNodes) { + return; + } + + evt.consume(); + this._controller.dispatchEventToListeners(CssOverview.Events.PopulateNodes, {color, colorNodes}); + } + + _onMouseOver(evt) { + // Traverse the event path on the grid to find the nearest element with a backend node ID attached. Use + // that for the highlighting. + const node = evt.path.find(el => el.dataset && el.dataset.backendNodeId); + if (!node) { + return; + } + + const backendNodeId = Number(node.dataset.backendNodeId); + this._controller.dispatchEventToListeners(CssOverview.Events.RequestNodeHighlight, backendNodeId); + } + + _render(data) { + if (!data || !('backgroundColors' in data) || !('textColors' in data)) { + return; + } + + this._data = data; + const {elementCount, backgroundColors, textColors, globalStyleStats, mediaQueries} = this._data; // Convert rgb values from the computed styles to either undefined or HEX(A) strings. - const nonTransparentBackgroundColors = this._getNonTransparentColorStrings(backgroundColors); - const nonTransparentTextColors = this._getNonTransparentColorStrings(textColors); + const sortedBackgroundColors = this._sortColorsByLuminance(backgroundColors); + const sortedTextColors = this._sortColorsByLuminance(textColors); this._fragment = UI.Fragment.build`
@@ -110,47 +168,49 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar {
  • ${ls`Type selectors`}
    -
    ${this._formatter.format(elementStyleStats.type.size)}
    +
    ${this._formatter.format(globalStyleStats.stats.type)}
  • ${ls`ID selectors`}
    -
    ${this._formatter.format(elementStyleStats.id.size)}
    +
    ${this._formatter.format(globalStyleStats.stats.id)}
  • ${ls`Class selectors`}
    -
    ${this._formatter.format(elementStyleStats.class.size)}
    +
    ${this._formatter.format(globalStyleStats.stats.class)}
  • ${ls`Universal selectors`}
    -
    ${this._formatter.format(elementStyleStats.universal.size)}
    +
    ${this._formatter.format(globalStyleStats.stats.universal)}
  • ${ls`Attribute selectors`}
    -
    ${this._formatter.format(elementStyleStats.attribute.size)}
    +
    ${this._formatter.format(globalStyleStats.stats.attribute)}
  • ${ls`Non-simple selectors`}
    -
    ${this._formatter.format(elementStyleStats.nonSimple.size)}
    +
    ${this._formatter.format(globalStyleStats.stats.nonSimple)}
  • ${ls`Colors`}

    -

    ${ls`Unique background colors: ${nonTransparentBackgroundColors.length}`}

    +

    ${ls`Unique background colors: ${sortedBackgroundColors.length}`}

      - ${nonTransparentBackgroundColors.map(this._colorsToFragment)} + ${sortedBackgroundColors.map(this._colorsToFragment.bind(this, 'background'))}
    -

    ${ls`Unique text colors: ${nonTransparentTextColors.length}`}

    +

    ${ls`Unique text colors: ${sortedTextColors.length}`}

      - ${nonTransparentTextColors.map(this._colorsToFragment)} + ${sortedTextColors.map(this._colorsToFragment.bind(this, 'text'))}
    + +

    ${ls`Media queries`}

    - ${this._mediaQueryGrid.element} + ${mediaQueries.length > 0 ? this._mediaQueryGrid.element : `There are no media queries.`}
    `; @@ -162,23 +222,47 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar { this._mediaQueryGrid.insertChild(mediaQueryNode); } - this._mainContainer.element.appendChild(this._fragment.element()); + this._resultsContainer.element.appendChild(this._fragment.element()); + this._elementContainer.element.appendChild(this._elementGrid.element); + this._mediaQueryGrid.renderInline(); this._mediaQueryGrid.wasShown(); } - _colorsToFragment(color) { - const colorFormatted = - color.hasAlpha() ? color.asString(Common.Color.Format.HEXA) : color.asString(Common.Color.Format.HEX); + async _populateNodes(evt) { + this._elementGrid.rootNode().removeChildren(); + + const {color, colorNodes} = evt.data; + let relatedNodesMap = this._relatedNodesMap.get(color); + if (!relatedNodesMap) { + // This process can't be repeated so we need to now capture the Nodes and hold them in case we need to revisit + // the same set again. + relatedNodesMap = await this._domModel.pushNodesByBackendIdsToFrontend(colorNodes); + this._relatedNodesMap.set(color, relatedNodesMap); + } + + for (const domNode of relatedNodesMap.values()) { + const colorNode = new CssOverview.CSSOverviewCompletedView.ColorNode(this._elementGrid, domNode, this._linkifier); + colorNode.selectable = false; + this._elementGrid.insertChild(colorNode); + } + + this._mainContainer.setSidebarMinimized(false); + this._elementGrid.renderInline(); + this._elementGrid.wasShown(); + } + + _colorsToFragment(section, color) { const blockFragment = UI.Fragment.build`
  • -
    -
    ${colorFormatted}
    + +
    ${color}
  • `; const block = blockFragment.$('color'); - block.style.backgroundColor = colorFormatted; + block.style.backgroundColor = color; - let [h, s, l] = color.hsla(); + const borderColor = Common.Color.parse(color); + let [h, s, l] = borderColor.hsla(); h = Math.round(h * 360); s = Math.round(s * 100); l = Math.round(l * 100); @@ -192,18 +276,10 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar { return blockFragment; } - _getNonTransparentColorStrings(srcColors) { - const colors = []; - for (const colorText of Array.from(srcColors)) { - const color = Common.Color.parse(colorText); - if (color.rgba()[3] === 0) { - continue; - } - - colors.push(color); - } - - return colors.sort((colorA, colorB) => { + _sortColorsByLuminance(srcColors) { + return Array.from(srcColors.keys()).sort((colA, colB) => { + const colorA = Common.Color.parse(colA); + const colorB = Common.Color.parse(colB); return Common.Color.luminance(colorB.rgba()) - Common.Color.luminance(colorA.rgba()); }); } @@ -242,7 +318,7 @@ CssOverview.CSSOverviewCompletedView.MediaQueryNode = class extends DataGrid.Sor if (link.textContent !== '') { cell.appendChild(link); } else { - cell.textContent = `${this.data.sourceURL} (not available)`; + cell.textContent = `(unable to link)`; } return cell; } @@ -258,3 +334,37 @@ CssOverview.CSSOverviewCompletedView.MediaQueryNode = class extends DataGrid.Sor return linkifier.linkifyCSSLocation(matchingSelectorLocation); } }; + +CssOverview.CSSOverviewCompletedView.ColorNode = class extends DataGrid.SortableDataGridNode { + /** + * @param {!DataGrid.SortableDataGrid} dataGrid + * @param {!Object} colorData + * @param {!Components.Linkifier} linkifier + */ + constructor(dataGrid, colorData, linkifier) { + super(dataGrid, colorData.hasChildren); + + this.data = colorData; + this._linkifier = linkifier; + } + + /** + * @override + * @param {string} columnId + * @return {!Element} + */ + createCell(columnId) { + if (columnId === 'nodeId') { + const cell = this.createTD(columnId); + cell.textContent = '...'; + Common.Linkifier.linkify(this.data).then(link => { + cell.textContent = ''; + link.dataset.backendNodeId = this.data.backendNodeId(); + cell.appendChild(link); + }); + return cell; + } + + return super.createCell(columnId); + } +}; diff --git a/front_end/css_overview/CSSOverviewController.js b/front_end/css_overview/CSSOverviewController.js index 3ff6f293b2..eaaf529495 100644 --- a/front_end/css_overview/CSSOverviewController.js +++ b/front_end/css_overview/CSSOverviewController.js @@ -19,6 +19,8 @@ CssOverview.OverviewController = class extends Common.Object { CssOverview.Events = { RequestOverviewStart: Symbol('RequestOverviewStart'), + RequestNodeHighlight: Symbol('RequestNodeHighlight'), + PopulateNodes: Symbol('PopulateNodes'), RequestOverviewCancel: Symbol('RequestOverviewCancel'), OverviewCompleted: Symbol('OverviewCompleted'), Reset: Symbol('Reset'), diff --git a/front_end/css_overview/CSSOverviewModel.js b/front_end/css_overview/CSSOverviewModel.js index 7881c0b365..d4a5480326 100644 --- a/front_end/css_overview/CSSOverviewModel.js +++ b/front_end/css_overview/CSSOverviewModel.js @@ -15,12 +15,87 @@ CssOverview.CSSOverviewModel = class extends SDK.SDKModel { this._runtimeAgent = target.runtimeAgent(); this._cssAgent = target.cssAgent(); this._domAgent = target.domAgent(); + this._domSnapshotAgent = target.domsnapshotAgent(); + this._overlayAgent = target.overlayAgent(); } getFlattenedDocument() { return this._domAgent.getFlattenedDocument(-1, true); } + highlightNode(node) { + const highlightConfig = {contentColor: Common.Color.PageHighlight.Content.toProtocolRGBA(), showInfo: true}; + + this._overlayAgent.invoke_hideHighlight({}); + this._overlayAgent.invoke_highlightNode({backendNodeId: node, highlightConfig}); + } + + async getNodeStyleStats() { + const backgroundColors = new Map(); + const textColors = new Map(); + const fontSizes = new Map(); + const fontWeights = new Map(); + const snapshotConfig = {computedStyles: ['background-color', 'color', 'font-size', 'font-weight']}; + + const storeColor = (id, nodeId, target) => { + if (id === -1) { + return; + } + + // Parse the color, discard transparent ones. + const colorText = strings[id]; + const color = Common.Color.parse(colorText); + if (color.rgba()[3] === 0) { + return; + } + + // Format the color and use as the key. + const colorFormatted = + color.hasAlpha() ? color.asString(Common.Color.Format.HEXA) : color.asString(Common.Color.Format.HEX); + + // Get the existing set of nodes with the color, or create a new set. + const colorValues = target.get(colorFormatted) || new Set(); + colorValues.add(nodeId); + + // Store. + target.set(colorFormatted, colorValues); + }; + + const {documents, strings} = await this._domSnapshotAgent.invoke_captureSnapshot(snapshotConfig); + for (const {nodes, layout} of documents) { + for (let idx = 0; idx < layout.styles.length; idx++) { + const styles = layout.styles[idx]; + const nodeIdx = layout.nodeIndex[idx]; + const nodeId = nodes.backendNodeId[nodeIdx]; + + // Skip text nodes. + const nodeType = nodes.nodeType[idx]; + if (nodeType === Node.TEXT_NODE) { + continue; + } + + const [backgroundColorIdx, textColorIdx, fontSizeIdx, fontWeightIdx] = styles; + + storeColor(backgroundColorIdx, nodeId, backgroundColors); + storeColor(textColorIdx, nodeId, textColors); + + if (fontSizeIdx !== -1) { + const fontSize = strings[fontSizeIdx]; + const fontSizeInstances = (fontSizes.get(fontSize) || 0) + 1; + fontSizes.set(fontSize, fontSizeInstances); + } + + if (fontWeightIdx !== -1) { + const fontWeight = strings[fontWeightIdx]; + const fontWeightInstances = (fontWeights.get(fontWeight) || 0) + 1; + fontWeights.set(fontWeight, fontWeightInstances); + } + } + } + + return {backgroundColors, textColors, fontSizes, fontWeights}; + } + getComputedStyleForNode(nodeId) { return this._cssAgent.getComputedStyleForNode(nodeId); } @@ -38,16 +113,65 @@ CssOverview.CSSOverviewModel = class extends SDK.SDKModel { let styleRules = 0; let inlineStyles = 0; let externalSheets = 0; - for (const { rules, href } of document.styleSheets) { - if (href) { + const stats = { + // Simple. + type: new Set(), + class: new Set(), + id: new Set(), + universal: new Set(), + attribute: new Set(), + + // Non-simple. + nonSimple: new Set() + }; + + for (const styleSheet of document.styleSheets) { + if (styleSheet.href) { externalSheets++; } else { inlineStyles++; } + // Attempting to grab rules can trigger a DOMException. + // Try it and if it fails skip to the next stylesheet. + let rules; + try { + rules = styleSheet.rules; + } catch (err) { + continue; + } + for (const rule of rules) { if ('selectorText' in rule) { styleRules++; + + // Each group that was used. + for (const selectorGroup of rule.selectorText.split(',')) { + // Each selector in the group. + for (const selector of selectorGroup.split(\/[\\t\\n\\f\\r ]+\/g)) { + if (selector.startsWith('.')) { + // Class. + stats.class.add(selector); + } else if (selector.startsWith('#')) { + // Id. + stats.id.add(selector); + } else if (selector.startsWith('*')) { + // Universal. + stats.universal.add(selector); + } else if (selector.startsWith('[')) { + // Attribute. + stats.attribute.add(selector); + } else { + // Type or non-simple selector. + const specialChars = \/[#\.:\\[\\]|\\+>~]\/; + if (specialChars.test(selector)) { + stats.nonSimple.add(selector); + } else { + stats.type.add(selector); + } + } + } + } } } } @@ -55,7 +179,18 @@ CssOverview.CSSOverviewModel = class extends SDK.SDKModel { return { styleRules, inlineStyles, - externalSheets + externalSheets, + stats: { + // Simple. + type: stats.type.size, + class: stats.class.size, + id: stats.id.size, + universal: stats.universal.size, + attribute: stats.attribute.size, + + // Non-simple. + nonSimple: stats.nonSimple.size + } } })()`; const {result} = await this._runtimeAgent.invoke_evaluate({expression, returnByValue: true}); @@ -67,70 +202,6 @@ CssOverview.CSSOverviewModel = class extends SDK.SDKModel { return result.value; } - - async getStylesStatsForNode(nodeId) { - const stats = { - // Simple. - type: new Set(), - class: new Set(), - id: new Set(), - universal: new Set(), - attribute: new Set(), - - // Non-simple. - nonSimple: new Set() - }; - - const matches = await this._cssAgent.invoke_getMatchedStylesForNode({nodeId}); - if (!matches || !matches.matchedCSSRules || !matches.matchedCSSRules.length) { - return; - } - - matches.matchedCSSRules.forEach(cssRule => { - const {matchingSelectors} = cssRule; - const {origin, selectorList} = cssRule.rule; - const isExternalSheet = origin === 'regular'; - if (!isExternalSheet || !selectorList) { - return; - } - - - const selectors = matchingSelectors.map(idx => selectorList.selectors[idx]); - - // Each group of selectors, e.g. foo.baz, foo .bar, foo { ... } - for (const {text} of selectors) { - // Each group that was used. - for (const selectorGroup of text.split(',')) { - // Each selector in the group. - for (const selector of selectorGroup.split(/[\t\n\f\r ]+/g)) { - if (selector.startsWith('.')) { - // Class. - stats.class.add(selector); - } else if (selector.startsWith('#')) { - // Id. - stats.id.add(selector); - } else if (selector.startsWith('*')) { - // Universal. - stats.universal.add(selector); - } else if (selector.startsWith('[')) { - // Attribute. - stats.attribute.add(selector); - } else { - // Type or non-simple selector. - const specialChars = /[#\.:\[\]|\+>~]/; - if (specialChars.test(selector)) { - stats.nonSimple.add(selector); - } else { - stats.type.add(selector); - } - } - } - } - } - }); - - return stats; - } }; SDK.SDKModel.register(CssOverview.CSSOverviewModel, SDK.Target.Capability.DOM, false); diff --git a/front_end/css_overview/CSSOverviewPanel.js b/front_end/css_overview/CSSOverviewPanel.js index e69d43bc93..a4b6f6699a 100644 --- a/front_end/css_overview/CSSOverviewPanel.js +++ b/front_end/css_overview/CSSOverviewPanel.js @@ -23,32 +23,42 @@ CssOverview.CSSOverviewPanel = class extends UI.Panel { this._controller.addEventListener(CssOverview.Events.RequestOverviewCancel, this._cancelOverview, this); this._controller.addEventListener(CssOverview.Events.OverviewCompleted, this._overviewCompleted, this); this._controller.addEventListener(CssOverview.Events.Reset, this._reset, this); + this._controller.addEventListener(CssOverview.Events.RequestNodeHighlight, this._requestNodeHighlight, this); this._reset(); } _reset() { - this._backgroundColors = new Set(); - this._textColors = new Set(); + this._backgroundColors = new Map(); + this._textColors = new Map(); this._fontSizes = new Map(); + this._fontWeights = new Map(); this._mediaQueries = []; this._elementCount = 0; - this._elementStyleStats = { - // Simple. - type: new Set(), - class: new Set(), - id: new Set(), - universal: new Set(), - attribute: new Set(), - - // Non-simple. - nonSimple: new Set() - }; this._cancelled = false; - this._globalStyleStats = {styleRules: 0, inlineStyles: 0, externalSheets: 0}; + this._globalStyleStats = { + styleRules: 0, + inlineStyles: 0, + externalSheets: 0, + stats: { + // Simple. + type: 0, + class: 0, + id: 0, + universal: 0, + attribute: 0, + + // Non-simple. + nonSimple: 0 + } + }; this._renderInitialView(); } + _requestNodeHighlight(evt) { + this._model.highlightNode(evt.data); + } + _renderInitialView() { this._processingView.hideWidget(); this._completedView.hideWidget(); @@ -56,12 +66,11 @@ CssOverview.CSSOverviewPanel = class extends UI.Panel { this._startView.show(this.contentElement); } - _renderOverviewStartedView(elementsHandled = 0, total = 0) { + _renderOverviewStartedView() { this._startView.hideWidget(); this._completedView.hideWidget(); this._processingView.show(this.contentElement); - this._processingView.setElementsHandled(elementsHandled, total); } _renderOverviewCompletedView() { @@ -73,8 +82,8 @@ CssOverview.CSSOverviewPanel = class extends UI.Panel { backgroundColors: this._backgroundColors, textColors: this._textColors, globalStyleStats: this._globalStyleStats, - elementStyleStats: this._elementStyleStats, fontSizes: this._fontSizes, + fontWeights: this._fontWeights, elementCount: this._elementCount, mediaQueries: this._mediaQueries }); @@ -83,74 +92,40 @@ CssOverview.CSSOverviewPanel = class extends UI.Panel { async _startOverview() { this._renderOverviewStartedView(); - const document = await this._model.getFlattenedDocument(); - if (this._cancelled) { - this._reset(); - return; + const [nodes, globalStyleStats, {backgroundColors, textColors, fontSizes, fontWeights}, mediaQueries] = + await Promise.all([ + this._model.getFlattenedDocument(), this._model.getGlobalStylesheetStats(), this._model.getNodeStyleStats(), + this._model.getMediaQueries() + ]); + + if (nodes) { + this._elementCount = nodes.length; } - // 1. Get the global style stats. - const globalStyleStats = await this._model.getGlobalStylesheetStats(); if (globalStyleStats) { this._globalStyleStats = globalStyleStats; } - const mediaQueries = await this._model.getMediaQueries(); if (mediaQueries) { this._mediaQueries = mediaQueries; } - // 2. Get the total element count. - this._elementCount = document.length; - - // 3. Process every element in the doc. - for (let idx = 0; idx < document.length; idx++) { - if (this._cancelled) { - this._reset(); - return; - } - - const node = document[idx]; - const [computedStyles, styleStats] = await Promise.all( - [this._model.getComputedStyleForNode(node.nodeId), this._model.getStylesStatsForNode(node.nodeId)]); - - // 3a. Capture any colors from the computed styles. - if (computedStyles) { - const backgroundColor = this._getStyleValue(computedStyles, 'background-color'); - if (backgroundColor) { - this._backgroundColors.add(backgroundColor); - } - - if (node.nodeType === Node.TEXT_NODE) { - const textColor = this._getStyleValue(computedStyles, 'color'); - this._textColors.add(textColor); - - const fontSize = this._getStyleValue(computedStyles, 'font-size'); - if (!this._fontSizes.has(fontSize)) { - this._fontSizes.set(fontSize, 0); - } - - this._fontSizes.set(fontSize, this._fontSizes.get(fontSize) + 1); - } - } - - // 3b. Tally the selector stats. - if (styleStats) { - for (const section of Object.keys(this._elementStyleStats)) { - if (!styleStats[section]) { - continue; - } - - for (const value of styleStats[section]) { - this._elementStyleStats[section].add(value); - } - } - } - - this._renderOverviewStartedView(idx + 1, document.length); + if (backgroundColors) { + this._backgroundColors = backgroundColors; + } + + if (textColors) { + this._textColors = textColors; + } + + if (fontSizes) { + this._fontSizes = fontSizes; + } + + if (fontWeights) { + this._fontWeights = fontWeights; } - // 4. Finish. this._controller.dispatchEventToListeners(CssOverview.Events.OverviewCompleted); } diff --git a/front_end/css_overview/CSSOverviewProcessingView.js b/front_end/css_overview/CSSOverviewProcessingView.js index 169c363bcd..92820be0ef 100644 --- a/front_end/css_overview/CSSOverviewProcessingView.js +++ b/front_end/css_overview/CSSOverviewProcessingView.js @@ -25,20 +25,10 @@ CssOverview.CSSOverviewProcessingView = class extends UI.Widget {

    Processing page

    ${cancelButton}
    - -

    `; this.contentElement.appendChild(this.fragment.element()); this.contentElement.style.overflow = 'auto'; } - - setElementsHandled(handled = 0, total = 0) { - // TODO(aerotwist): We might want to switch this to using Intl.PluralRules in the future - // @see https://v8.dev/features/intl-pluralrules - const elementsTotal = total > 0 ? ls`document elements` : ls`document element`; - this.fragment.$('processed').textContent = - ls`Processed ${this._formatter.format(handled)} of ${this._formatter.format(total)} ${elementsTotal}.`; - } }; diff --git a/front_end/css_overview/cssOverviewCompletedView.css b/front_end/css_overview/cssOverviewCompletedView.css index 5967edfed0..52636c5be2 100644 --- a/front_end/css_overview/cssOverviewCompletedView.css +++ b/front_end/css_overview/cssOverviewCompletedView.css @@ -54,7 +54,14 @@ .media-query-grid .data-container { top: 0; - max-height: 400px; + max-height: 350px; +} + +.element-grid { + flex: 1; + border-left: none; + border-right: none; + overflow: auto; } .block { diff --git a/front_end/css_overview/css_overview_strings.grdp b/front_end/css_overview/css_overview_strings.grdp index aa7d406222..d59a339305 100644 --- a/front_end/css_overview/css_overview_strings.grdp +++ b/front_end/css_overview/css_overview_strings.grdp @@ -12,18 +12,15 @@ Overview summary + + Element + Universal selectors - - document element - Unique text colors: $1s32 - - Processed $1s23 of $2s1,223 $3sdocument elements. - Colors @@ -42,9 +39,6 @@ External stylesheets - - document elements - Type selectors diff --git a/front_end/css_overview/module.json b/front_end/css_overview/module.json index 7245192e07..bc9317d58f 100644 --- a/front_end/css_overview/module.json +++ b/front_end/css_overview/module.json @@ -1,35 +1,36 @@ { - "extensions": [ - { - "type": "view", - "location": "panel", - "id": "cssoverview", - "title": "CSS Overview", - "className": "CssOverview.CSSOverviewPanel", - "order": 95, - "experiment": "cssOverview" - } - ], - "dependencies": [ - "elements", - "ui", - "sdk", - "data_grid" - ], - "scripts": [ - "CSSOverviewController.js", - "CSSOverviewModel.js", - "CSSOverviewStartView.js", - "CSSOverviewProcessingView.js", - "CSSOverviewCompletedView.js", - "CSSOverviewSidebarPanel.js", - "CSSOverviewPanel.js" + "extensions": [ + { + "type": "view", + "location": "panel", + "id": "cssoverview", + "title": "CSS Overview", + "className": "CssOverview.CSSOverviewPanel", + "order": 95, + "experiment": "cssOverview" + } ], - "resources": [ - "cssOverview.css", - "cssOverviewStartView.css", - "cssOverviewProcessingView.css", - "cssOverviewCompletedView.css", - "cssOverviewSidebarPanel.css" - ] -} + "dependencies": [ + "elements", + "ui", + "sdk", + "data_grid", + "extensions" + ], + "scripts": [ + "CSSOverviewController.js", + "CSSOverviewModel.js", + "CSSOverviewStartView.js", + "CSSOverviewProcessingView.js", + "CSSOverviewCompletedView.js", + "CSSOverviewSidebarPanel.js", + "CSSOverviewPanel.js" + ], + "resources": [ + "cssOverview.css", + "cssOverviewStartView.css", + "cssOverviewProcessingView.css", + "cssOverviewCompletedView.css", + "cssOverviewSidebarPanel.css" + ] + }