Skip to content

Commit

Permalink
Implement the :toggle() pseudo-class.
Browse files Browse the repository at this point in the history
This implements
https://tabatkins.github.io/css-toggle/#checked-pseudoclass

(Support for toggles is behind the CSSToggles flag, which is currently
off.)

Bug: 1250716
Change-Id: I2d3226c5a023e2bf36dd4d990d57933f932983ce
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3780614
Commit-Queue: David Baron <dbaron@chromium.org>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1030624}
  • Loading branch information
dbaron authored and Chromium LUCI CQ committed Aug 2, 2022
1 parent e3b5359 commit 867c33b
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 1 deletion.
33 changes: 33 additions & 0 deletions third_party/blink/renderer/core/css/css_selector.cc
Expand Up @@ -323,6 +323,7 @@ PseudoId CSSSelector::GetPseudoId(PseudoType type) {
case kPseudoRightPage:
case kPseudoInRange:
case kPseudoOutOfRange:
case kPseudoToggle:
case kPseudoWebKitCustomElement:
case kPseudoBlinkInternalElement:
case kPseudoCue:
Expand Down Expand Up @@ -505,6 +506,7 @@ const static NameToPseudoStruct kPseudoTypeWithArgumentsMap[] = {
CSSSelector::kPseudoPageTransitionOutgoingImage},
{"part", CSSSelector::kPseudoPart},
{"slotted", CSSSelector::kPseudoSlotted},
{"toggle", CSSSelector::kPseudoToggle},
{"where", CSSSelector::kPseudoWhere},
};

Expand Down Expand Up @@ -586,6 +588,11 @@ CSSSelector::PseudoType CSSSelector::NameToPseudoType(const AtomicString& name,
return CSSSelector::kPseudoUnknown;
}

if (match->type == CSSSelector::kPseudoToggle &&
!RuntimeEnabledFeatures::CSSTogglesEnabled()) {
return CSSSelector::kPseudoUnknown;
}

return static_cast<CSSSelector::PseudoType>(match->type);
}

Expand Down Expand Up @@ -774,6 +781,7 @@ void CSSSelector::UpdatePseudoType(const AtomicString& value,
case kPseudoStart:
case kPseudoState:
case kPseudoTarget:
case kPseudoToggle:
case kPseudoTopLayer:
case kPseudoUnknown:
case kPseudoValid:
Expand Down Expand Up @@ -873,6 +881,15 @@ const CSSSelector* CSSSelector::SerializeCompound(
SerializeIdentifier(simple_selector->Argument(), builder);
builder.Append(')');
break;
case kPseudoToggle:
builder.Append('(');
SerializeIdentifier(simple_selector->Argument(), builder);
if (const ToggleRoot::State* value = simple_selector->ToggleValue()) {
builder.Append(" ");
builder.Append(value->ToString());
}
builder.Append(')');
break;
case kPseudoHas:
case kPseudoNot:
DCHECK(simple_selector->SelectorList());
Expand Down Expand Up @@ -1051,6 +1068,13 @@ void CSSSelector::SetSelectorList(
data_.rare_data_->selector_list_ = std::move(selector_list);
}

void CSSSelector::SetToggle(const AtomicString& name,
std::unique_ptr<ToggleRoot::State>&& value) {
CreateRareData();
data_.rare_data_->argument_ = name;
data_.rare_data_->toggle_value_ = std::move(value);
}

void CSSSelector::SetContainsPseudoInsideHasPseudoClass() {
CreateRareData();
data_.rare_data_->bits_.has_.contains_pseudo_ = true;
Expand Down Expand Up @@ -1109,6 +1133,15 @@ static bool ValidateSubSelector(const CSSSelector* selector) {
case CSSSelector::kPseudoIsHtml:
case CSSSelector::kPseudoListBox:
case CSSSelector::kPseudoHostHasAppearance:
case CSSSelector::kPseudoToggle:
// TODO(https://crbug.com/1346456): Many pseudos should probably be
// added to this list. The default: case below should also be removed
// so that those adding new pseudos know they need to choose one path or
// the other here.
//
// However, it's not clear why a pseudo should be in one list or the
// other. It's also entirely possible that this entire switch() should
// be removed and all cases should return true.
return true;
default:
return false;
Expand Down
10 changes: 9 additions & 1 deletion third_party/blink/renderer/core/css/css_selector.h
Expand Up @@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/css/parser/css_parser_mode.h"
#include "third_party/blink/renderer/core/dom/qualified_name.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/style/toggle_root.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"

namespace blink {
Expand Down Expand Up @@ -264,6 +265,7 @@ class CORE_EXPORT CSSSelector {
kPseudoInRange,
kPseudoOutOfRange,
kPseudoXrOverlay,
kPseudoToggle,
// Pseudo elements in UA ShadowRoots. Available in any stylesheets.
kPseudoWebKitCustomElement,
// Pseudo elements in UA ShadowRoots. Available only in UA stylesheets.
Expand Down Expand Up @@ -356,6 +358,9 @@ class CORE_EXPORT CSSSelector {
const Vector<AtomicString>* PartNames() const {
return has_rare_data_ ? data_.rare_data_->part_names_.get() : nullptr;
}
const ToggleRoot::State* ToggleValue() const {
return has_rare_data_ ? data_.rare_data_->toggle_value_.get() : nullptr;
}
bool ContainsPseudoInsideHasPseudoClass() const {
return has_rare_data_ ? data_.rare_data_->bits_.has_.contains_pseudo_
: false;
Expand All @@ -377,6 +382,8 @@ class CORE_EXPORT CSSSelector {
void SetArgument(const AtomicString&);
void SetSelectorList(std::unique_ptr<CSSSelectorList>);
void SetPartNames(std::unique_ptr<Vector<AtomicString>>);
void SetToggle(const AtomicString& name,
std::unique_ptr<ToggleRoot::State>&& value);
void SetContainsPseudoInsideHasPseudoClass();
void SetContainsComplexLogicalCombinationsInsideHasPseudoClass();

Expand Down Expand Up @@ -500,11 +507,12 @@ class CORE_EXPORT CSSSelector {
} has_;
} bits_;
QualifiedName attribute_; // used for attribute selector
AtomicString argument_; // Used for :contains, :lang, :nth-*
AtomicString argument_; // Used for :contains, :lang, :nth-*, :toggle()
std::unique_ptr<CSSSelectorList>
selector_list_; // Used for :-webkit-any and :not
std::unique_ptr<Vector<AtomicString>>
part_names_; // Used for ::part() selectors.
std::unique_ptr<ToggleRoot::State> toggle_value_; // used for :toggle()

private:
RareData(const AtomicString& value);
Expand Down
Expand Up @@ -72,6 +72,10 @@ class CORE_EXPORT CSSParserSelector {
selector_->SetRelation(value);
}
void SetForPage() { selector_->SetForPage(); }
void SetToggle(const AtomicString& name,
std::unique_ptr<ToggleRoot::State>&& value) {
selector_->SetToggle(name, std::move(value));
}

void UpdatePseudoType(const AtomicString& value,
const CSSParserContext& context,
Expand Down
35 changes: 35 additions & 0 deletions third_party/blink/renderer/core/css/parser/css_selector_parser.cc
Expand Up @@ -12,6 +12,7 @@
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_observer.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/dom/pseudo_element.h"
#include "third_party/blink/renderer/core/frame/deprecation/deprecation.h"
Expand Down Expand Up @@ -1051,6 +1052,40 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::ConsumePseudo(
selector->SetArgument(ident.Value().ToAtomicString());
return selector;
}
case CSSSelector::kPseudoToggle: {
using State = ToggleRoot::State;

const CSSParserToken& name = block.ConsumeIncludingWhitespace();
if (name.GetType() != kIdentToken ||
!css_parsing_utils::IsCustomIdent(name.Id())) {
return nullptr;
}
std::unique_ptr<State> value;
if (!block.AtEnd()) {
const CSSParserToken& value_token = block.ConsumeIncludingWhitespace();
switch (value_token.GetType()) {
case kIdentToken:
if (!css_parsing_utils::IsCustomIdent(value_token.Id()))
return nullptr;
value =
std::make_unique<State>(value_token.Value().ToAtomicString());
break;
case kNumberToken:
if (value_token.GetNumericValueType() != kIntegerValueType ||
value_token.NumericValue() < 0) {
return nullptr;
}
value = std::make_unique<State>(value_token.NumericValue());
break;
default:
return nullptr;
}
}
if (!block.AtEnd())
return nullptr;
selector->SetToggle(name.Value().ToAtomicString(), std::move(value));
return selector;
}
default:
break;
}
Expand Down
2 changes: 2 additions & 0 deletions third_party/blink/renderer/core/css/rule_feature_set.cc
Expand Up @@ -194,6 +194,7 @@ bool SupportsInvalidation(CSSSelector::PseudoType type) {
case CSSSelector::kPseudoPageTransitionImageWrapper:
case CSSSelector::kPseudoPageTransitionIncomingImage:
case CSSSelector::kPseudoPageTransitionOutgoingImage:
case CSSSelector::kPseudoToggle:
return true;
case CSSSelector::kPseudoUnknown:
case CSSSelector::kPseudoLeftPage:
Expand Down Expand Up @@ -661,6 +662,7 @@ InvalidationSet* RuleFeatureSet::InvalidationSetForSimpleSelector(
case CSSSelector::kPseudoMultiSelectFocus:
case CSSSelector::kPseudoModal:
case CSSSelector::kPseudoSelectorFragmentAnchor:
case CSSSelector::kPseudoToggle:
return &EnsurePseudoInvalidationSet(selector.GetPseudoType(), type,
position);
case CSSSelector::kPseudoFirstOfType:
Expand Down
24 changes: 24 additions & 0 deletions third_party/blink/renderer/core/css/selector_checker.cc
Expand Up @@ -45,6 +45,7 @@
#include "third_party/blink/renderer/core/dom/popup_data.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/dom/toggle.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/picture_in_picture_controller.h"
Expand Down Expand Up @@ -1562,6 +1563,29 @@ bool SelectorChecker::CheckPseudoClass(const SelectorCheckingContext& context,
case CSSSelector::kPseudoRelativeAnchor:
DCHECK(context.relative_anchor_element);
return context.relative_anchor_element == &element;
case CSSSelector::kPseudoToggle: {
using State = ToggleRoot::State;

const AtomicString& name = selector.Argument();
const State* value = selector.ToggleValue();

auto [toggle, toggle_element] = element.FindToggleInScope(name);
DCHECK_EQ(!toggle, !toggle_element);
// An element matches :toggle() if the element is in scope for a toggle
// with the name given by <custom-ident>, and ...
if (!toggle)
return false;

if (value) {
// ... either the toggle’s value matches the provided <toggle-value>,
// ...
return toggle->ValueMatches(*value);
} else {
// ... or the <toggle-value> is omitted and the toggle is in any
// active value.
return !toggle->ValueMatches(State(0));
}
}
case CSSSelector::kPseudoUnknown:
default:
NOTREACHED();
Expand Down
Expand Up @@ -365,6 +365,7 @@ const char* PseudoTypeToString(CSSSelector::PseudoType pseudo_type) {
DEFINE_STRING_MAPPING(PseudoPlaying)
DEFINE_STRING_MAPPING(PseudoInRange)
DEFINE_STRING_MAPPING(PseudoOutOfRange)
DEFINE_STRING_MAPPING(PseudoToggle)
DEFINE_STRING_MAPPING(PseudoWebKitCustomElement)
DEFINE_STRING_MAPPING(PseudoBlinkInternalElement)
DEFINE_STRING_MAPPING(PseudoCue)
Expand Down
1 change: 1 addition & 0 deletions third_party/blink/renderer/core/style/build.gni
Expand Up @@ -110,6 +110,7 @@ blink_core_sources_style = [
"text_size_adjust.h",
"toggle_group.h",
"toggle_group_list.h",
"toggle_root.cc",
"toggle_root.h",
"toggle_root_list.h",
"toggle_trigger.h",
Expand Down
18 changes: 18 additions & 0 deletions third_party/blink/renderer/core/style/toggle_root.cc
@@ -0,0 +1,18 @@
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/core/style/toggle_root.h"

namespace blink {

String ToggleRoot::State::ToString() const {
switch (GetType()) {
case Type::Integer:
return String::Number(AsInteger());
case Type::Name:
return AsName().GetString();
}
}

} // namespace blink
3 changes: 3 additions & 0 deletions third_party/blink/renderer/core/style/toggle_root.h
Expand Up @@ -9,6 +9,7 @@
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/renderer/core/style/toggle_group.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"

namespace blink {
Expand Down Expand Up @@ -82,6 +83,8 @@ class ToggleRoot {
return absl::get<std::size_t(Type::Name)>(value_);
}

String ToString() const;

private:
absl::variant<IntegerType, NameType> value_;
};
Expand Down
1 change: 1 addition & 0 deletions third_party/blink/web_tests/TestExpectations
Expand Up @@ -5047,6 +5047,7 @@ crbug.com/1008483 virtual/backface-visibility-interop/paint/invalidation/multico
crbug.com/1250716 external/wpt/css/css-toggle/toggle-creation.tentative.html [ Failure ]
crbug.com/1250716 external/wpt/css/css-toggle/toggle-activation.tentative.html [ Failure ]
crbug.com/1250716 external/wpt/css/css-toggle/toggle-activation-with-groups.tentative.html [ Failure ]
crbug.com/1250716 external/wpt/css/css-toggle/toggle-pseudo-class.tentative.html [ Failure ]

# Swiftshader issue.
crbug.com/1048149 external/wpt/html/browsers/the-window-object/open-close/open-features-non-integer-innerwidth.html [ Crash Timeout ]
Expand Down

0 comments on commit 867c33b

Please sign in to comment.