Skip to content

Commit a0c4c4a

Browse files
pfaffeDevtools-frontend LUCI CQ
authored andcommitted
[css value tracing] evaluate percentages in longhands
This adds support for evaluating <percentage> units in css value tracing, but only for longhands. For shorthands, we need additional reasoning about which longhand the unit pertains to in order to understand whether it's relative to a width or a height. Bug: 401213719 Change-Id: I823ef9d52bb12ab40e7bcabce770733ad36a51b7 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6376104 Reviewed-by: Eric Leese <[email protected]> Auto-Submit: Philip Pfaffe <[email protected]> Commit-Queue: Eric Leese <[email protected]>
1 parent 0832829 commit a0c4c4a

File tree

5 files changed

+72
-31
lines changed

5 files changed

+72
-31
lines changed

front_end/core/sdk/CSSModel.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import * as Root from '../root/root.js';
4343
import {CSSFontFace} from './CSSFontFace.js';
4444
import {CSSMatchedStyles} from './CSSMatchedStyles.js';
4545
import {CSSMedia} from './CSSMedia.js';
46+
import {cssMetadata} from './CSSMetadata.js';
4647
import {CSSStyleRule} from './CSSRule.js';
4748
import {CSSStyleDeclaration, Type} from './CSSStyleDeclaration.js';
4849
import {CSSStyleSheetHeader} from './CSSStyleSheetHeader.js';
@@ -132,9 +133,16 @@ export class CSSModel extends SDKModel<EventTypes> {
132133
return this.#colorScheme;
133134
}
134135

135-
async resolveValues(nodeId: Protocol.DOM.NodeId, ...values: string[]): Promise<string[]|null> {
136-
const response = await this.agent.invoke_resolveValues({values, nodeId});
137-
return response.getError() ? null : response.results;
136+
async resolveValues(propertyName: string|undefined, nodeId: Protocol.DOM.NodeId, ...values: string[]):
137+
Promise<string[]|null> {
138+
if (propertyName && cssMetadata().getLonghands(propertyName)?.length) {
139+
return null;
140+
}
141+
const response = await this.agent.invoke_resolveValues({values, nodeId, propertyName});
142+
if (response.getError()) {
143+
return null;
144+
}
145+
return response.results;
138146
}
139147

140148
headersForSourceURL(sourceURL: Platform.DevToolsPath.UrlString): CSSStyleSheetHeader[] {

front_end/core/sdk/CSSPropertyParserMatchers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -684,10 +684,10 @@ export class LengthMatch implements Match {
684684
export class LengthMatcher extends matcherBase(LengthMatch) {
685685
// clang-format on
686686
static readonly LENGTH_UNITS = new Set([
687-
'em', 'ex', 'ch', 'cap', 'ic', 'lh', 'rem', 'rex', 'rch', 'rlh', 'ric', 'rcap', 'pt',
688-
'pc', 'in', 'cm', 'mm', 'Q', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax', 'dvw', 'dvh',
689-
'dvi', 'dvb', 'dvmin', 'dvmax', 'svw', 'svh', 'svi', 'svb', 'svmin', 'svmax', 'lvw', 'lvh', 'lvi',
690-
'lvb', 'lvmin', 'lvmax', 'cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax', 'cqem', 'cqlh', 'cqex', 'cqch',
687+
'em', 'ex', 'ch', 'cap', 'ic', 'lh', 'rem', 'rex', 'rch', 'rlh', 'ric', 'rcap', 'pt', 'pc',
688+
'in', 'cm', 'mm', 'Q', 'vw', 'vh', 'vi', 'vb', 'vmin', 'vmax', 'dvw', 'dvh', 'dvi', 'dvb',
689+
'dvmin', 'dvmax', 'svw', 'svh', 'svi', 'svb', 'svmin', 'svmax', 'lvw', 'lvh', 'lvi', 'lvb', 'lvmin', 'lvmax',
690+
'cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax', 'cqem', 'cqlh', 'cqex', 'cqch', '%'
691691
]);
692692
override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): LengthMatch|null {
693693
if (node.name !== 'NumberLiteral') {

front_end/panels/elements/CSSValueTraceView.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ async function showTrace(
7373
view.showTrace(
7474
property, null, matchedStyles, new Map(),
7575
Elements.StylePropertyTreeElement.getPropertyRenderers(
76-
property.ownerStyle, treeElement.parentPane(), matchedStyles, treeElement,
76+
property.name, property.ownerStyle, treeElement.parentPane(), matchedStyles, treeElement,
7777
treeElement.getComputedStyles() ?? new Map()));
7878
return await viewFunction.nextInput;
7979
}

front_end/panels/elements/StylePropertyTreeElement.test.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ describeWithMockConnection('StylePropertyTreeElement', () => {
286286
const {valueElement} = Elements.PropertyRenderer.Renderer.renderValueElement(
287287
property, matchedResult,
288288
Elements.StylePropertyTreeElement.getPropertyRenderers(
289-
matchedStyles.nodeStyles()[0], stylesSidebarPane, matchedStyles, null, new Map()),
289+
property.name, matchedStyles.nodeStyles()[0], stylesSidebarPane, matchedStyles, null, new Map()),
290290
context);
291291

292292
const colorSwatch = valueElement.querySelector('devtools-color-swatch');
@@ -1655,7 +1655,7 @@ describeWithMockConnection('StylePropertyTreeElement', () => {
16551655
const addPopoverPromise = Promise.withResolvers<void>();
16561656
sinon.stub(Elements.StylePropertyTreeElement.LengthRenderer.prototype, 'popOverAttachedForTest')
16571657
.callsFake(() => addPopoverPromise.resolve());
1658-
const stylePropertyTreeElement = getTreeElement('margin', '5px 2em');
1658+
const stylePropertyTreeElement = getTreeElement('property', '5px 2em');
16591659
setMockConnectionResponseHandler('CSS.getComputedStyleForNode', () => ({computedStyle: {}}));
16601660

16611661
await stylePropertyTreeElement.onpopulate();
@@ -1664,6 +1664,17 @@ describeWithMockConnection('StylePropertyTreeElement', () => {
16641664
const popover = stylePropertyTreeElement.valueElement?.querySelector('devtools-tooltip');
16651665
assert.strictEqual(popover?.innerText, '15px');
16661666
});
1667+
1668+
it('passes the property name to evaluations', async () => {
1669+
const cssModel = stylesSidebarPane.cssModel();
1670+
assert.exists(cssModel);
1671+
const resolveValuesStub = sinon.stub(cssModel, 'resolveValues').resolves([]);
1672+
const stylePropertyTreeElement = getTreeElement('left', '2%');
1673+
stylePropertyTreeElement.updateTitle();
1674+
1675+
assert.isTrue(resolveValuesStub.calledOnce);
1676+
assert.strictEqual(resolveValuesStub.args[0][0], 'left');
1677+
});
16671678
});
16681679

16691680
describe('MathFunctionRenderer', () => {
@@ -1715,13 +1726,29 @@ describeWithMockConnection('StylePropertyTreeElement', () => {
17151726
view.showTrace(
17161727
property, null, matchedStyles, new Map(),
17171728
Elements.StylePropertyTreeElement.getPropertyRenderers(
1718-
property.ownerStyle, stylesSidebarPane, matchedStyles, null, new Map()));
1729+
property.name, property.ownerStyle, stylesSidebarPane, matchedStyles, null, new Map()));
17191730

17201731
assert.isTrue(evaluationSpy.calledOnce);
17211732
const originalText = evaluationSpy.args[0][0].textContent;
17221733
await evaluationSpy.returnValues[0];
17231734
assert.strictEqual(originalText, evaluationSpy.args[0][0].textContent);
17241735
});
1736+
1737+
it('shows the original text during tracing when evaluation fails', async () => {
1738+
const cssModel = stylesSidebarPane.cssModel();
1739+
assert.exists(cssModel);
1740+
const resolveValuesStub = sinon.stub(cssModel, 'resolveValues').resolves([]);
1741+
const property = addProperty('width', 'calc(1 + 1)');
1742+
1743+
const view = new Elements.CSSValueTraceView.CSSValueTraceView(undefined, () => {});
1744+
view.showTrace(
1745+
property, null, matchedStyles, new Map(),
1746+
Elements.StylePropertyTreeElement.getPropertyRenderers(
1747+
property.name, property.ownerStyle, stylesSidebarPane, matchedStyles, null, new Map()));
1748+
1749+
assert.isTrue(resolveValuesStub.calledOnce);
1750+
assert.strictEqual(resolveValuesStub.args[0][0], 'width');
1751+
});
17251752
});
17261753

17271754
describe('AutoBaseRenderer', () => {

front_end/panels/elements/StylePropertyTreeElement.ts

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ function getTracingTooltip(
256256
?.getWidget()
257257
?.showTrace(
258258
property, text, matchedStyles, computedStyles,
259-
getPropertyRenderers(
259+
getPropertyRenderers(property.name,
260260
property.ownerStyle, stylesPane, matchedStyles, null,
261261
computedStyles));
262262
}
@@ -308,7 +308,8 @@ export class VariableRenderer extends rendererBase(SDK.CSSPropertyParserMatchers
308308
const {nodes, cssControls} = Renderer.renderValueNodes(
309309
{name: declaration.name, value: declaration.value ?? ''},
310310
substitution.cachedParsedValue(declaration.declaration, this.#matchedStyles, this.#computedStyles),
311-
getPropertyRenderers(declaration.style, this.#stylesPane, this.#matchedStyles, null, this.#computedStyles),
311+
getPropertyRenderers(
312+
declaration.name, declaration.style, this.#stylesPane, this.#matchedStyles, null, this.#computedStyles),
312313
substitution);
313314
cssControls.forEach((value, key) => value.forEach(control => context.addControl(key, control)));
314315
return nodes;
@@ -697,7 +698,7 @@ export class ColorMixRenderer extends rendererBase(SDK.CSSPropertyParserMatchers
697698
context.addControl('color', swatch);
698699
const nodeId = this.#pane.node()?.id;
699700
if (nodeId !== undefined) {
700-
void this.#pane.cssModel()?.resolveValues(nodeId, colorMixText).then(results => {
701+
void this.#pane.cssModel()?.resolveValues(undefined, nodeId, colorMixText).then(results => {
701702
if (results) {
702703
const color = Common.Color.parse(results[0]);
703704
if (color) {
@@ -1291,11 +1292,13 @@ export class GridTemplateRenderer extends rendererBase(SDK.CSSPropertyParserMatc
12911292

12921293
// clang-format off
12931294
export class LengthRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.LengthMatch) {
1294-
readonly #stylesPane: StylesSidebarPane;
12951295
// clang-format on
1296-
constructor(stylesPane: StylesSidebarPane) {
1296+
readonly #stylesPane: StylesSidebarPane;
1297+
readonly #propertyName: string;
1298+
constructor(stylesPane: StylesSidebarPane, propertyName: string) {
12971299
super();
12981300
this.#stylesPane = stylesPane;
1301+
this.#propertyName = propertyName;
12991302
}
13001303

13011304
override render(match: SDK.CSSPropertyParserMatchers.LengthMatch, context: RenderingContext): Node[] {
@@ -1318,7 +1321,7 @@ export class LengthRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.L
13181321
return;
13191322
}
13201323

1321-
const pixelValue = await this.#stylesPane.cssModel()?.resolveValues(nodeId, value);
1324+
const pixelValue = await this.#stylesPane.cssModel()?.resolveValues(this.#propertyName, nodeId, value);
13221325

13231326
if (pixelValue) {
13241327
valueElement.textContent = pixelValue[0];
@@ -1331,7 +1334,7 @@ export class LengthRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.L
13311334
return;
13321335
}
13331336

1334-
const pixelValue = await this.#stylesPane.cssModel()?.resolveValues(nodeId, value);
1337+
const pixelValue = await this.#stylesPane.cssModel()?.resolveValues(this.#propertyName, nodeId, value);
13351338
if (!pixelValue) {
13361339
return;
13371340
}
@@ -1351,17 +1354,19 @@ export class LengthRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.L
13511354

13521355
// clang-format off
13531356
export class MathFunctionRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.MathFunctionMatch) {
1354-
readonly #stylesPane: StylesSidebarPane;
1355-
#matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles;
1356-
#computedStyles: Map<string, string>;
13571357
// clang-format on
1358+
readonly #stylesPane: StylesSidebarPane;
1359+
#matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles;
1360+
#computedStyles: Map<string, string>;
1361+
#propertyName: string;
13581362
constructor(
13591363
stylesPane: StylesSidebarPane, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
1360-
computedStyles: Map<string, string>) {
1364+
computedStyles: Map<string, string>, propertyName: string) {
13611365
super();
13621366
this.#matchedStyles = matchedStyles;
13631367
this.#computedStyles = computedStyles;
13641368
this.#stylesPane = stylesPane;
1369+
this.#propertyName = propertyName;
13651370
}
13661371

13671372
override render(match: SDK.CSSPropertyParserMatchers.MathFunctionMatch, context: RenderingContext): Node[] {
@@ -1397,7 +1402,7 @@ export class MathFunctionRenderer extends rendererBase(SDK.CSSPropertyParserMatc
13971402
if (nodeId === undefined) {
13981403
return;
13991404
}
1400-
const evaled = await this.#stylesPane.cssModel()?.resolveValues(nodeId, value);
1405+
const evaled = await this.#stylesPane.cssModel()?.resolveValues(this.#propertyName, nodeId, value);
14011406
if (!evaled?.[0] || evaled[0] === value) {
14021407
return;
14031408
}
@@ -1413,7 +1418,7 @@ export class MathFunctionRenderer extends rendererBase(SDK.CSSPropertyParserMatc
14131418
// and compare the function result to the values of all its arguments. Evaluating the arguments eliminates nested
14141419
// function calls and normalizes all units to px.
14151420
values.unshift(functionText);
1416-
const evaledArgs = await this.#stylesPane.cssModel()?.resolveValues(nodeId, ...values);
1421+
const evaledArgs = await this.#stylesPane.cssModel()?.resolveValues(this.#propertyName, nodeId, ...values);
14171422
if (!evaledArgs) {
14181423
return;
14191424
}
@@ -1555,7 +1560,7 @@ export class PositionTryRenderer extends rendererBase(SDK.CSSPropertyParserMatch
15551560
}
15561561

15571562
export function getPropertyRenderers(
1558-
style: SDK.CSSStyleDeclaration.CSSStyleDeclaration, stylesPane: StylesSidebarPane,
1563+
propertyName: string, style: SDK.CSSStyleDeclaration.CSSStyleDeclaration, stylesPane: StylesSidebarPane,
15591564
matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, treeElement: StylePropertyTreeElement|null,
15601565
computedStyles: Map<string, string>): Array<MatchRenderer<SDK.CSSPropertyParser.Match>> {
15611566
return [
@@ -1576,8 +1581,8 @@ export function getPropertyRenderers(
15761581
new PositionAnchorRenderer(stylesPane),
15771582
new FlexGridRenderer(stylesPane, treeElement),
15781583
new PositionTryRenderer(matchedStyles),
1579-
new LengthRenderer(stylesPane),
1580-
new MathFunctionRenderer(stylesPane, matchedStyles, computedStyles),
1584+
new LengthRenderer(stylesPane, propertyName),
1585+
new MathFunctionRenderer(stylesPane, matchedStyles, computedStyles, propertyName),
15811586
new AutoBaseRenderer(computedStyles),
15821587
new BinOpRenderer(),
15831588
];
@@ -1999,10 +2004,11 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
19992004
this.expandElement.setAttribute('jslog', `${VisualLogging.expand().track({click: true})}`);
20002005
}
20012006

2002-
const renderers = this.property.parsedOk ? getPropertyRenderers(
2003-
this.style, this.parentPaneInternal, this.matchedStylesInternal,
2004-
this, this.getComputedStyles() ?? new Map()) :
2005-
[];
2007+
const renderers = this.property.parsedOk ?
2008+
getPropertyRenderers(
2009+
this.name, this.style, this.parentPaneInternal, this.matchedStylesInternal, this,
2010+
this.getComputedStyles() ?? new Map()) :
2011+
[];
20062012

20072013
if (Root.Runtime.experiments.isEnabled('font-editor') && this.property.parsedOk) {
20082014
renderers.push(new FontRenderer(this));

0 commit comments

Comments
 (0)