[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 <tvanderlippe@chromium.org>
Commit-Queue: Paul Lewis <aerotwist@chromium.org>
This commit is contained in:
Paul Lewis
2019-10-23 09:55:00 +00:00
committed by Commit Bot
parent 3c77a59376
commit de0224ccdd
8 changed files with 385 additions and 235 deletions
@@ -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`
<div class="vbox overview-completed-view">
@@ -110,47 +168,49 @@ CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar {
</li>
<li>
<div class="label">${ls`Type selectors`}</div>
<div class="value">${this._formatter.format(elementStyleStats.type.size)}</div>
<div class="value">${this._formatter.format(globalStyleStats.stats.type)}</div>
</li>
<li>
<div class="label">${ls`ID selectors`}</div>
<div class="value">${this._formatter.format(elementStyleStats.id.size)}</div>
<div class="value">${this._formatter.format(globalStyleStats.stats.id)}</div>
</li>
<li>
<div class="label">${ls`Class selectors`}</div>
<div class="value">${this._formatter.format(elementStyleStats.class.size)}</div>
<div class="value">${this._formatter.format(globalStyleStats.stats.class)}</div>
</li>
<li>
<div class="label">${ls`Universal selectors`}</div>
<div class="value">${this._formatter.format(elementStyleStats.universal.size)}</div>
<div class="value">${this._formatter.format(globalStyleStats.stats.universal)}</div>
</li>
<li>
<div class="label">${ls`Attribute selectors`}</div>
<div class="value">${this._formatter.format(elementStyleStats.attribute.size)}</div>
<div class="value">${this._formatter.format(globalStyleStats.stats.attribute)}</div>
</li>
<li>
<div class="label">${ls`Non-simple selectors`}</div>
<div class="value">${this._formatter.format(elementStyleStats.nonSimple.size)}</div>
<div class="value">${this._formatter.format(globalStyleStats.stats.nonSimple)}</div>
</li>
</ul>
</div>
<div $="colors" class="results-section colors">
<h1>${ls`Colors`}</h1>
<h2>${ls`Unique background colors: ${nonTransparentBackgroundColors.length}`}</h2>
<h2>${ls`Unique background colors: ${sortedBackgroundColors.length}`}</h2>
<ul>
${nonTransparentBackgroundColors.map(this._colorsToFragment)}
${sortedBackgroundColors.map(this._colorsToFragment.bind(this, 'background'))}
</ul>
<h2>${ls`Unique text colors: ${nonTransparentTextColors.length}`}</h2>
<h2>${ls`Unique text colors: ${sortedTextColors.length}`}</h2>
<ul>
${nonTransparentTextColors.map(this._colorsToFragment)}
${sortedTextColors.map(this._colorsToFragment.bind(this, 'text'))}
</ul>
</div>
<!-- TODO: Fonts -->
<div $="media-queries" class="results-section media-queries">
<h1>${ls`Media queries`}</h1>
${this._mediaQueryGrid.element}
${mediaQueries.length > 0 ? this._mediaQueryGrid.element : `There are no media queries.`}
</div>
</div>`;
@@ -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`<li>
<div class="block" $="color"></div>
<div class="block-title">${colorFormatted}</div>
<button data-color="${color}" data-section="${section}" class="block" $="color"></button>
<div class="block-title">${color}</div>
</li>`;
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<string,*>} 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);
}
};
@@ -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'),
+138 -67
View File
@@ -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);
+48 -73
View File
@@ -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);
}
@@ -25,20 +25,10 @@ CssOverview.CSSOverviewProcessingView = class extends UI.Widget {
<div class="vbox overview-processing-view">
<h1>Processing page</h1>
<div>${cancelButton}</div>
<h2 $="processed"></h2>
</div>
`;
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}.`;
}
};
@@ -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 {
@@ -12,18 +12,15 @@
<message name="IDS_DEVTOOLS_1f1621cad66c8f661842bf9392d8559b" desc="Label for the summary in the CSS Overview report">
Overview summary
</message>
<message name="IDS_DEVTOOLS_231afe47f3f37d3808096b36c28b4ded" desc="Label for the column in the element list in the CSS Overview report">
Element
</message>
<message name="IDS_DEVTOOLS_23717568804f63f75e7596dcced3119e" desc="Label for the number of universal selectors in the CSS Overview report">
Universal selectors
</message>
<message name="IDS_DEVTOOLS_396ea220fc4d34d57a5a505de918bdde" desc="Singular of 'document element' in the processing update message">
document element
</message>
<message name="IDS_DEVTOOLS_4b3e6e4359cfb882a3b8b551f38a92c7" desc="Label for unique text colors in the CSS Overview Panel">
Unique text colors: <ph name="TEXTCOLORS_LENGTH">$1s<ex>32</ex></ph>
</message>
<message name="IDS_DEVTOOLS_52798275481d47b10f5b8e31310b155e" desc="Label for the update message in the processing stage of CSS Overview">
Processed <ph name="THIS__FORMATTER_FORMAT_HANDLED_">$1s<ex>23</ex></ph> of <ph name="THIS__FORMATTER_FORMAT_TOTAL_">$2s<ex>1,223</ex></ph> <ph name="ELEMENTSTOTAL">$3s<ex>document elements</ex></ph>.
</message>
<message name="IDS_DEVTOOLS_5d50889672f6f860d14f502de3de1957" desc="Title of colors subsection in the CSS Overview Panel">
Colors
</message>
@@ -42,9 +39,6 @@
<message name="IDS_DEVTOOLS_95d366a44bd7bad09d9916e05b512cc6" desc="Label for the number of External stylesheets in the CSS Overview report">
External stylesheets
</message>
<message name="IDS_DEVTOOLS_a9427e421abd9fd7ba45212c25ba55a5" desc="Plural of 'document element' in the processing update message">
document elements
</message>
<message name="IDS_DEVTOOLS_aff1062318f6d07520a0feba60f3f253" desc="Label for the number of type selectors in the CSS Overview report">
Type selectors
</message>
+34 -33
View File
@@ -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"
]
}