Skip to content

Commit 8b519b7

Browse files
Eric LeeseDevtools-frontend LUCI CQ
authored andcommitted
Support attribute functions in computed values.
Add support for attr() Bug: 393087009 Change-Id: I473e2f5f7272174fffc5f27c4a9dacb67e33643d Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6722637 Commit-Queue: Eric Leese <[email protected]> Reviewed-by: Philip Pfaffe <[email protected]>
1 parent b88894b commit 8b519b7

File tree

8 files changed

+614
-103
lines changed

8 files changed

+614
-103
lines changed

front_end/core/sdk/CSSMatchedStyles.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import * as Protocol from '../../generated/protocol.js';
6+
import {renderElementIntoDOM} from '../../testing/DOMHelpers.js';
67
import {createTarget} from '../../testing/EnvironmentHelpers.js';
78
import {describeWithMockConnection, setMockConnectionResponseHandler} from '../../testing/MockConnection.js';
89
import {getMatchedStyles, ruleMatch} from '../../testing/StyleHelpers.js';
@@ -696,6 +697,91 @@ describe('CSSMatchedStyles', () => {
696697
assert.strictEqual(matchedStyles.resolveProperty('color', styles[0])?.value, 'y');
697698
assert.isNull(matchedStyles.resolveProperty('background-color', styles[0]));
698699
});
700+
701+
it('evaluates variables with attr() calls in the same way as blink', async () => {
702+
const attributes = [
703+
{name: 'data-test-nonexistent', value: 'attr(data-nonexistent)'},
704+
{name: 'data-test-nonexistent-raw-string', value: 'attr(data-nonexistent raw-string)'},
705+
{name: 'data-test-empty', value: ''},
706+
{name: 'data-test-self-loop', value: 'attr(data-test-self-loop)'},
707+
{name: 'data-test-loop-1', value: 'attr(data-test-loop-2 type(<length>), 1px)'},
708+
{name: 'data-test-loop-2', value: 'var(--test-loop-1, 2px)'},
709+
{name: 'data-test-loop-indirect-2', value: 'attr(data-test-loop-1 type(<length>), 6px)'},
710+
{name: 'data-test-number', value: '70'},
711+
{name: 'data-test-length', value: 'attr(data-test-number in)'},
712+
{name: 'data-test-bad-unit', value: 'attr(data-test-number parsecs, 2px)'},
713+
{name: 'data-test-bad-type', value: 'attr(data-test-number type(<nomber>), 2)'},
714+
{name: 'data-cant-parse', value: 'attr('},
715+
];
716+
const variables = [
717+
{name: '--test-missing', value: 'attr(data-nonexistent)'},
718+
{name: '--test-missing-fallback', value: 'attr(data-nonexistent, 2px)'},
719+
{name: '--test-missing-var-indirect', value: 'var(--test-missing, 2px)'},
720+
{name: '--test-missing-attr-indirect-raw', value: 'attr(data-test-nonexistent, 2px)'},
721+
{name: '--test-missing-attr-indirect', value: 'attr(data-test-nonexistent type(*), 2px)'},
722+
{name: '--test-missing-raw-string', value: 'attr(data-nonexistent raw-string)'},
723+
{name: '--test-missing-raw-string-fallback', value: 'attr(data-nonexistent raw-string, 2px)'},
724+
{name: '--test-missing-raw-string-var-indirect', value: 'var(--test-missing-raw-string, 2px)'},
725+
{name: '--test-missing-raw-string-attr-indirect', value: 'attr(data-test-nonexistent-raw-string, 2px)'},
726+
{name: '--test-empty-any', value: 'attr(data-test-empty type(*))'},
727+
{name: '--test-empty-number', value: 'attr(data-test-empty type(<number>))'},
728+
{name: '--test-empty-any-fallback', value: 'attr(data-test-empty type(*), 2px)'},
729+
{name: '--test-empty-number-fallback', value: 'attr(data-test-empty type(<number>), 2px)'},
730+
{name: '--test-self-loop', value: 'attr(data-test-self-loop type(*))'},
731+
{name: '--test-self-loop-raw', value: 'attr(data-test-self-loop)'},
732+
{name: '--test-self-loop-fallback', value: 'attr(data-test-self-loop type(*), 2px)'},
733+
{name: '--test-self-loop-fallback-2', value: 'var(--test-self-loop-fallback, 4px)'},
734+
{name: '--test-self-loop-fallback-self', value: 'attr(data-test-self-loop type(*), attr(data-test-self-loop))'},
735+
{name: '--test-loop-1', value: 'var(--test-loop-2, 3px)'},
736+
{name: '--test-loop-2', value: 'attr(data-test-loop-1 type(<length>), 4px)'},
737+
{name: '--test-loop-indirect-1', value: 'attr(data-test-loop-1 type(<length>), 5px)'},
738+
{name: '--test-loop-indirect-2', value: 'attr(data-test-loop-indirect-2 type(<length>), 7px)'},
739+
{name: '--test-number', value: 'attr(data-test-number type(<number>))'},
740+
{name: '--test-number-to-length', value: 'attr(data-test-number in)'},
741+
{
742+
name: '--test-number-as-length',
743+
value: 'attr(data-test-number type(<length>), var(--test-self-loop-fallback-2))'
744+
},
745+
{name: '--test-length', value: 'attr(data-test-length type(<length>))'},
746+
// TODO(b/393087009): Re-enable once fallback is fixed.
747+
// {name: '--test-bad-unit-indirect', value: 'attr(data-test-bad-unit type(*), 4px)'},
748+
// {name: '--test-bad-type-indirect', value: 'attr(data-test-bad-type type(*), 4)'},
749+
// {name: '--test-cant-parse', value: 'attr(data-cant-parse type(*), red)'},
750+
];
751+
// Create an element
752+
const element = document.createElement('div');
753+
for (const {name, value} of attributes) {
754+
element.setAttribute(name, value);
755+
}
756+
for (const {name, value} of variables) {
757+
element.style.setProperty(name, value);
758+
}
759+
760+
renderElementIntoDOM(element);
761+
const computedProperties = element.computedStyleMap();
762+
763+
const node = sinon.createStubInstance(SDK.DOMModel.DOMNode);
764+
node.id = 1 as Protocol.DOM.NodeId;
765+
node.nodeType.returns(Node.ELEMENT_NODE);
766+
// Create a sinon stub to return the requested attribute
767+
node.getAttribute.callsFake((name: string) => attributes.find(attr => attr.name === name)?.value);
768+
769+
const matchedStyles = await getMatchedStyles({
770+
matchedPayload: [ruleMatch('div', variables)],
771+
node,
772+
});
773+
774+
for (const {name} of variables) {
775+
const frontendComputedValue =
776+
matchedStyles.computeCSSVariable(matchedStyles.nodeStyles()[0], name)?.value ?? null;
777+
const backendComputedValue = computedProperties.get(name) ?? null;
778+
if (backendComputedValue === null) {
779+
assert.isNull(frontendComputedValue, `evaluating variable ${name}`);
780+
} else {
781+
assert.strictEqual(frontendComputedValue, backendComputedValue.toString(), `evaluating variable ${name}`);
782+
}
783+
}
784+
});
699785
});
700786

701787
describeWithMockConnection('NodeCascade', () => {

0 commit comments

Comments
 (0)