Commit 7cfed512 authored by Kent Tamura's avatar Kent Tamura Committed by Chromium LUCI CQ

Custom state: Update the pseudo class syntax

This CL updates the custom state pseudo class syntax from :state(foo)
to :--foo.
See https://github.com/WICG/custom-state-pseudo-class/issues/6.

This feature is not shipped yet.

Bug: 1012098
Change-Id: I71bd92113d89b073cdeba3b6ab78d2c7f0e091dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2636075Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845429}
parent a1725c66
......@@ -471,7 +471,6 @@ const static NameToPseudoStruct kPseudoTypeWithArgumentsMap[] = {
{"nth-of-type", CSSSelector::kPseudoNthOfType},
{"part", CSSSelector::kPseudoPart},
{"slotted", CSSSelector::kPseudoSlotted},
{"state", CSSSelector::kPseudoState},
{"where", CSSSelector::kPseudoWhere},
};
......@@ -517,11 +516,6 @@ static CSSSelector::PseudoType NameToPseudoType(const AtomicString& name,
!RuntimeEnabledFeatures::CSSPictureInPictureEnabled())
return CSSSelector::kPseudoUnknown;
if (match->type == CSSSelector::kPseudoState &&
!RuntimeEnabledFeatures::CustomStatePseudoClassEnabled()) {
return CSSSelector::kPseudoUnknown;
}
if (match->type == CSSSelector::kPseudoTargetText &&
!RuntimeEnabledFeatures::CSSTargetTextPseudoElementEnabled()) {
return CSSSelector::kPseudoUnknown;
......@@ -580,6 +574,9 @@ CSSSelector::PseudoType CSSSelector::ParsePseudoType(const AtomicString& name,
return kPseudoWebKitCustomElement;
if (name.StartsWith("-internal-"))
return kPseudoBlinkInternalElement;
if (RuntimeEnabledFeatures::CustomStatePseudoClassEnabled() &&
name.StartsWith("--"))
return kPseudoState;
return kPseudoUnknown;
}
......@@ -610,8 +607,10 @@ void CSSSelector::UpdatePseudoType(const AtomicString& value,
bool has_arguments,
CSSParserMode mode) {
DCHECK(match_ == kPseudoClass || match_ == kPseudoElement);
SetValue(value);
SetPseudoType(ParsePseudoType(value, has_arguments));
AtomicString lower_value = value.LowerASCII();
PseudoType pseudo_type = ParsePseudoType(lower_value, has_arguments);
SetPseudoType(pseudo_type);
SetValue(pseudo_type == kPseudoState ? value : lower_value);
switch (GetPseudoType()) {
case kPseudoAfter:
......@@ -813,8 +812,10 @@ const CSSSelector* CSSSelector::SerializeCompound(
SerializeIdentifier(simple_selector->SerializingValue(), builder);
} else if (simple_selector->match_ == kPseudoClass ||
simple_selector->match_ == kPagePseudoClass) {
builder.Append(':');
builder.Append(simple_selector->SerializingValue());
if (simple_selector->GetPseudoType() != kPseudoState) {
builder.Append(':');
builder.Append(simple_selector->SerializingValue());
}
switch (simple_selector->GetPseudoType()) {
case kPseudoNthChild:
......@@ -847,7 +848,6 @@ const CSSSelector* CSSSelector::SerializeCompound(
}
case kPseudoDir:
case kPseudoLang:
case kPseudoState:
builder.Append('(');
SerializeIdentifier(simple_selector->Argument(), builder);
builder.Append(')');
......@@ -855,6 +855,10 @@ const CSSSelector* CSSSelector::SerializeCompound(
case kPseudoNot:
DCHECK(simple_selector->SelectorList());
break;
case kPseudoState:
builder.Append(':');
SerializeIdentifier(simple_selector->SerializingValue(), builder);
break;
case kPseudoHost:
case kPseudoHostContext:
case kPseudoAny:
......
......@@ -628,7 +628,7 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::ConsumePseudo(
selector->SetMatch(colons == 1 ? CSSSelector::kPseudoClass
: CSSSelector::kPseudoElement);
AtomicString value = token.Value().ToAtomicString().LowerASCII();
AtomicString value = token.Value().ToAtomicString();
bool has_arguments = token.GetType() == kFunctionToken;
selector->UpdatePseudoType(value, *context_, has_arguments, context_->Mode());
......@@ -734,8 +734,7 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::ConsumePseudo(
selector->SetSelectorList(std::move(selector_list));
return selector;
}
case CSSSelector::kPseudoDir:
case CSSSelector::kPseudoState: {
case CSSSelector::kPseudoDir: {
const CSSParserToken& ident = block.ConsumeIncludingWhitespace();
if (ident.GetType() != kIdentToken || !block.AtEnd())
return nullptr;
......
......@@ -403,7 +403,6 @@ INSTANTIATE_TEST_SUITE_P(InvalidPseudoIsArguments,
SelectorParseTest,
testing::ValuesIn(invalid_pseudo_is_argments_data));
static const SelectorTestCase is_where_nesting_data[] = {
// clang-format off
// These pseudos only accept compound selectors:
......@@ -419,7 +418,7 @@ static const SelectorTestCase is_where_nesting_data[] = {
{"::cue(:is(.a .b))", "::cue(:is())"},
{"::cue(:is(.a + .b))", "::cue(:is())"},
{"::cue(:is(.a, .b + .c))", "::cue(:is(.a))"},
// Only user-action pseudos + :state() are allowed after kPseudoPart:
// Only user-action pseudos + :--state are allowed after kPseudoPart:
{"::part(foo):is(.a)", "::part(foo):is()"},
{"::part(foo):is(.a:hover)", "::part(foo):is()"},
{"::part(foo):is(:hover.a)", "::part(foo):is()"},
......@@ -430,7 +429,7 @@ static const SelectorTestCase is_where_nesting_data[] = {
{"::part(foo):is(:hover, :where(.a))",
"::part(foo):is(:hover, :where())"},
{"::part(foo):is(:hover, .a)", "::part(foo):is(:hover)"},
{"::part(foo):is(:state(bar), .a)", "::part(foo):is(:state(bar))"},
{"::part(foo):is(:--bar, .a)", "::part(foo):is(:--bar)"},
{"::part(foo):is(:enabled)", "::part(foo):is()"},
// Only scrollbar pseudos after kPseudoScrollbar:
{"::-webkit-scrollbar:is(:focus)", "::-webkit-scrollbar:is()"},
......@@ -469,7 +468,7 @@ static const SelectorTestCase is_where_nesting_data[] = {
{"::part(foo):is(:is(:hover))"},
{"::part(foo):is(:focus, :hover)"},
{"::part(foo):is(:focus, :is(:hover))"},
{"::part(foo):is(:focus, :state(bar))"},
{"::part(foo):is(:focus, :--bar)"},
{"::-webkit-scrollbar:is(:enabled)"},
{"::selection:is(:window-inactive)"},
{"::-webkit-input-placeholder:is(:hover)"},
......
......@@ -1027,7 +1027,7 @@ bool SelectorChecker::CheckPseudoClass(const SelectorCheckingContext& context,
return !element.GetDocument().GetPage()->GetFocusController().IsActive();
case CSSSelector::kPseudoState: {
return element.DidAttachInternals() &&
element.EnsureElementInternals().HasState(selector.Argument());
element.EnsureElementInternals().HasState(selector.Value());
}
case CSSSelector::kPseudoHorizontal:
case CSSSelector::kPseudoVertical:
......
......@@ -5,13 +5,13 @@
#state-and-part::part(inner) {
opacity: 0;
}
#state-and-part::part(inner):state(innerFoo) {
#state-and-part::part(inner):--innerFoo {
opacity: 0.5;
}
#state-and-part:state(outerFoo)::part(inner) {
#state-and-part:--outerFoo::part(inner) {
opacity: 0.25;
}
:state( \(escaped\ state ) {}
:--\(escaped\ state {}
</style>
<body>
<script>
......@@ -37,7 +37,7 @@ class ContainerElement extends HTMLElement {
:host {
border-style: solid;
}
:host(:state(dotted)) {
:host(:--dotted) {
border-style: dotted;
}
</style>
......@@ -54,36 +54,38 @@ class ContainerElement extends HTMLElement {
customElements.define('container-element', ContainerElement);
test(() => {
assert_throws_dom('SyntaxError', () => { document.querySelector(':state'); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':state('); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':state()'); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':state(=)'); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':state(name=value)'); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':state( foo bar)'); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':state(16px)'); });
}, ':state() parsing failures');
document.querySelector(':--');
document.querySelector(':--16px');
}, ':--foo parsing passes');
test(() => {
assert_throws_dom('SyntaxError', () => { document.querySelector(':--('); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':--)'); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':--='); });
assert_throws_dom('SyntaxError', () => { document.querySelector(':--name=value'); });
}, ':--foo parsing failures');
test(() => {
assert_equals(document.styleSheets[0].cssRules[1].cssText,
'#state-and-part::part(inner):state(innerFoo) { opacity: 0.5; }');
'#state-and-part::part(inner):--innerFoo { opacity: 0.5; }');
assert_equals(document.styleSheets[0].cssRules[3].selectorText,
':state(\\(escaped\\ state)');
}, ':state() serialization');
':--\\(escaped\\ state');
}, ':--foo serialization');
test(() => {
let element = new TestElement();
let states = element.i.states;
assert_false(element.matches(':state(foo)'));
assert_true(element.matches(':not(:state(foo))'));
states.add('foo');
assert_true(element.matches(':state(foo)'));
assert_true(element.matches(':is(:state(foo))'));
assert_false(element.matches(':--foo'));
assert_true(element.matches(':not(:--foo)'));
states.add('--foo');
assert_true(element.matches(':--foo'));
assert_true(element.matches(':is(:--foo)'));
element.classList.add('c1', 'c2');
assert_true(element.matches('.c1:state(foo)'));
assert_true(element.matches(':state(foo).c1'));
assert_true(element.matches('.c2:state(foo).c1'));
}, ':state() in simple cases');
assert_true(element.matches('.c1:--foo'));
assert_true(element.matches(':--foo.c1'));
assert_true(element.matches('.c2:--foo.c1'));
}, ':--foo in simple cases');
test(() => {
let element = new TestElement();
......@@ -92,10 +94,10 @@ test(() => {
element.focus();
let states = element.i.states;
states.value = 'foo';
assert_true(element.matches(':focus:state(foo)'));
assert_true(element.matches(':state(foo):focus'));
}, ':state() and other pseudo classes');
states.value = '--foo';
assert_true(element.matches(':focus:--foo'));
assert_true(element.matches(':--foo:focus'));
}, ':--foo and other pseudo classes');
test(() => {
let outer = new ContainerElement();
......@@ -104,26 +106,26 @@ test(() => {
let inner = outer.innerElement;
let innerStates = inner.i.states;
innerStates.add('innerFoo');
innerStates.add('--innerFoo');
assert_equals(getComputedStyle(inner).opacity, '0.5',
'::part() followed by :state()');
innerStates.replace('innerFoo', 'innerfoo');
'::part() followed by :--foo');
innerStates.replace('--innerFoo', '--innerfoo');
assert_equals(getComputedStyle(inner).opacity, '0',
':state() matching should be case-sensitive');
innerStates.remove('innerfoo');
':--foo matching should be case-sensitive');
innerStates.remove('--innerfoo');
outer.i.states.add('outerFoo');
outer.i.states.add('--outerFoo');
assert_equals(getComputedStyle(inner).opacity, '0.25',
':state() followed by ::part()');
}, ':state() and ::part()');
':--foo followed by ::part()');
}, ':--foo and ::part()');
test(() => {
let outer = new ContainerElement();
document.body.appendChild(outer);
assert_equals(getComputedStyle(outer).borderStyle, 'solid');
outer.i.states.toggle('dotted');
outer.i.states.toggle('--dotted');
assert_equals(getComputedStyle(outer).borderStyle, 'dotted');
}, ':state() and :host()');
}, ':--foo and :host()');
</script>
</body>
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