Commit 238ddcb1 authored by Mathias Bynens's avatar Mathias Bynens Committed by Chromium LUCI CQ

Support forcing the CSS :target state

This patch extends the existing `CSS.forcePseudoState` CDP command so
that it supports the CSS :target pseudo-class.

DevTools CL: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2581544

Bug: chromium:1156628
Change-Id: I06d3bc2c76d78601fd0abd403f621e74d2e2ae03
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2575668
Commit-Queue: Mathias Bynens <mathias@chromium.org>
Reviewed-by: default avatarSigurd Schneider <sigurds@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835623}
parent 4bb44c6d
......@@ -920,6 +920,10 @@ bool SelectorChecker::CheckPseudoClass(const SelectorCheckingContext& context,
return selector.MatchNth(NthIndexCache::NthLastOfTypeIndex(element));
}
case CSSSelector::kPseudoTarget:
probe::ForcePseudoState(&element, CSSSelector::kPseudoTarget,
&force_pseudo_state);
if (force_pseudo_state)
return true;
return element == element.GetDocument().CssTarget();
case CSSSelector::kPseudoIs:
case CSSSelector::kPseudoWhere:
......
......@@ -349,7 +349,8 @@ enum ForcePseudoClassFlags {
kPseudoActive = 1 << 2,
kPseudoVisited = 1 << 3,
kPseudoFocusWithin = 1 << 4,
kPseudoFocusVisible = 1 << 5
kPseudoFocusVisible = 1 << 5,
kPseudoTarget = 1 << 6,
};
static unsigned ComputePseudoClassMask(
......@@ -359,6 +360,7 @@ static unsigned ComputePseudoClassMask(
DEFINE_STATIC_LOCAL(String, focus, ("focus"));
DEFINE_STATIC_LOCAL(String, focusVisible, ("focus-visible"));
DEFINE_STATIC_LOCAL(String, focusWithin, ("focus-within"));
DEFINE_STATIC_LOCAL(String, target, ("target"));
DEFINE_STATIC_LOCAL(String, visited, ("visited"));
if (!pseudo_class_array || pseudo_class_array->empty())
return kPseudoNone;
......@@ -375,6 +377,8 @@ static unsigned ComputePseudoClassMask(
result |= kPseudoFocusVisible;
else if (pseudo_class == focusWithin)
result |= kPseudoFocusWithin;
else if (pseudo_class == target)
result |= kPseudoTarget;
else if (pseudo_class == visited)
result |= kPseudoVisited;
}
......@@ -960,6 +964,9 @@ void InspectorCSSAgent::ForcePseudoState(Element* element,
case CSSSelector::kPseudoHover:
force = forced_pseudo_state & kPseudoHover;
break;
case CSSSelector::kPseudoTarget:
force = forced_pseudo_state & kPseudoTarget;
break;
case CSSSelector::kPseudoVisited:
force = forced_pseudo_state & kPseudoVisited;
break;
......
......@@ -82,7 +82,7 @@ html: [regular, 4:0-4:4] {
['width':'85%'] @[0:21-0:32]
['background-image':'url(bar.png)'] @[0:33-0:63]
Running: test_forcedState
Running: test_forcedStateHover
=== BODY with forced :hover ===
body: [user-agent] {
['display':'block'] @[undefined-undefined]
......@@ -141,6 +141,33 @@ body:hover: [regular, 13:0-13:10] {
['color':'#CDE'] @[14:2-14:14]
}
Running: test_forcedStateTarget
=== #target with forced :target ===
p: [user-agent] {
['display':'block'] @[undefined-undefined]
['margin-block-start':'1em'] @[undefined-undefined]
['margin-block-end':'1em'] @[undefined-undefined]
['margin-inline-start':'0px'] @[undefined-undefined]
['margin-inline-end':'0px'] @[undefined-undefined]
}
#target:target: [regular, 17:0-17:14] {
['background':'#bada55'] @[18:2-18:22]
['outline':'5px solid lime'] @[19:2-19:26]
['background-image':'initial'] @[undefined-undefined]
['background-position-x':'initial'] @[undefined-undefined]
['background-position-y':'initial'] @[undefined-undefined]
['background-size':'initial'] @[undefined-undefined]
['background-repeat-x':'initial'] @[undefined-undefined]
['background-repeat-y':'initial'] @[undefined-undefined]
['background-attachment':'initial'] @[undefined-undefined]
['background-origin':'initial'] @[undefined-undefined]
['background-clip':'initial'] @[undefined-undefined]
['background-color':'rgb(186, 218, 85)'] @[undefined-undefined]
['outline-color':'lime'] @[undefined-undefined]
['outline-style':'solid'] @[undefined-undefined]
['outline-width':'5px'] @[undefined-undefined]
}
Running: test_textNodeComputedStyles
=== Computed style property count for TextNode ===
......@@ -216,7 +243,4 @@ body.mainpage: [regular, 8:0-8:13] {
['prop1':'val1' non-parsed] @[9:4-9:16]
['prop2':'val2' non-parsed] @[10:4-10:16]
}
body:hover: [regular, 13:0-13:10] {
['color':'#CDE'] @[14:2-14:14]
}
......@@ -25,12 +25,18 @@ body.mainpage {
body:hover {
color: #CDE;
}
#target:target {
background: #bada55;
outline: 5px solid lime;
}
</style>
</head>
<body id="mainBody" class="main1 main2 mainpage" style="font-weight: normal; width: 85%; background-image: url(bar.png)">
<table width="50%" id="thetable">
</table>
<h1 id="toggle">H1</h1>
<p id="target">:target</p>
</body>
`);
......@@ -114,13 +120,34 @@ body:hover {
ElementsTestRunner.selectNodeWithId('mainBody', nodeCallback);
},
async function test_forcedState(next) {
TestRunner.CSSAgent.forcePseudoState(bodyId, ['hover']);
var response = await TestRunner.CSSAgent.invoke_getMatchedStylesForNode({nodeId: bodyId});
async function test_forcedStateHover(next) {
await TestRunner.CSSAgent.forcePseudoState(bodyId, ['hover']);
const response = await TestRunner.CSSAgent.invoke_getMatchedStylesForNode({nodeId: bodyId});
TestRunner.addResult('=== BODY with forced :hover ===');
ElementsTestRunner.dumpRuleMatchesArray(response.matchedCSSRules);
TestRunner.CSSAgent.forcePseudoState(bodyId, ['hover']).then(next);
// Note: the forced :hover state persists for now, but is removed
// as part of the next test.
next();
},
async function test_forcedStateTarget(next) {
await TestRunner.CSSAgent.forcePseudoState(bodyId, ['target']);
ElementsTestRunner.nodeWithId('target', nodeCallback);
async function nodeCallback(node) {
const nodeId = node.id;
await TestRunner.CSSAgent.forcePseudoState(nodeId, ['target']);
const response = await TestRunner.CSSAgent.invoke_getMatchedStylesForNode({nodeId: nodeId});
TestRunner.addResult('=== #target with forced :target ===');
ElementsTestRunner.dumpRuleMatchesArray(response.matchedCSSRules);
// Reset all forced pseudo states for the next tests.
await TestRunner.CSSAgent.forcePseudoState(nodeId, []);
next();
}
},
function test_textNodeComputedStyles(next) {
......
Test CSS.forcePseudoStates method for :target
Color without forced :target: rgb(255, 0, 0)
Color with forced :target: rgb(0, 128, 0)
Didn’t fail after disabling the CSS agent (https://crbug.com/1123526).
(async function(testRunner) {
const {session, dp} = await testRunner.startHTML(`
<style>
#target {
color: red;
}
#target:target {
color: green;
}
</style>
<div id="target">test</div>
`, 'Test CSS.forcePseudoStates method for :target');
await dp.DOM.enable();
await dp.CSS.enable();
const CSSHelper = await testRunner.loadScript('../resources/css-helper.js');
const cssHelper = new CSSHelper(testRunner, dp);
const documentNodeId = await cssHelper.requestDocumentNodeId();
const nodeId = await cssHelper.requestNodeId(documentNodeId, '#target');
async function getTargetColor() {
return await session.evaluate(() => {
return window.getComputedStyle(document.querySelector('#target')).color;
});
}
testRunner.log('Color without forced :target: ' + await getTargetColor());
await dp.CSS.forcePseudoState({
nodeId,
forcedPseudoClasses: ['target'],
});
testRunner.log('Color with forced :target: ' + await getTargetColor());
await dp.CSS.disable();
await dp.DOM.disable();
testRunner.log('Didn’t fail after disabling the CSS agent (https://crbug.com/1123526).');
testRunner.completeTest();
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment