Skip to content

Commit 0649b63

Browse files
tcaptan-crChromium LUCI CQ
authored and
Chromium LUCI CQ
committed
Add ::check pseudo element
This pseudo element is added to allow <option> elements inside <select> elements to have a standardized and customizable checkmark icon. Spec definition: ``` Option checkmark icon: the '::check' pseudo-element The '::check' pseudo-element is only generated when the originating element is a option element which has an ancestor select element with base appearance. '::check' is a tree-abiding pseudo-element. It accepts all properties. It inherits from its originating element. '::check' generates a box as if it was an child of its originating element, preceding any boxes generated by the '::before' pseudo-element, with content as specified by 'content'. ``` Defined in: w3c/csswg-drafts#10986 Bug: 369319576 Change-Id: Ie5058d66f90644f9732bc619a3f587b6203d1cf9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5925018 Reviewed-by: Joey Arhar <[email protected]> Reviewed-by: Philip Rogers <[email protected]> Commit-Queue: Traian Captan <[email protected]> Reviewed-by: Kevin Babbitt <[email protected]> Cr-Commit-Position: refs/heads/main@{#1371648}
1 parent 1ddcdd0 commit 0649b63

31 files changed

+189
-33
lines changed

third_party/blink/public/devtools_protocol/browser_protocol.pdl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,7 @@ domain DOM
26772677
enum
26782678
first-line
26792679
first-letter
2680+
check
26802681
before
26812682
after
26822683
marker

third_party/blink/renderer/core/css/counters_attachment_context.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ std::optional<std::pair<unsigned, int>> DetermineCounterTypeAndValue(
4141
const ComputedStyle& style = layout_object.StyleRef();
4242
switch (style.StyleType()) {
4343
case kPseudoIdNone:
44+
case kPseudoIdCheck:
4445
case kPseudoIdBefore:
4546
case kPseudoIdAfter:
4647
case kPseudoIdMarker:

third_party/blink/renderer/core/css/element_rule_collector.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1213,7 +1213,8 @@ void ElementRuleCollector::DidMatchRule(
12131213
if (dynamic_pseudo > kLastTrackedPublicPseudoId) {
12141214
return;
12151215
}
1216-
if ((dynamic_pseudo == kPseudoIdBefore ||
1216+
if ((dynamic_pseudo == kPseudoIdCheck ||
1217+
dynamic_pseudo == kPseudoIdBefore ||
12171218
dynamic_pseudo == kPseudoIdAfter) &&
12181219
!rule_data->Rule()->Properties().HasProperty(CSSPropertyID::kContent)) {
12191220
return;

third_party/blink/renderer/core/css/invalidation/rule_invalidation_data_visitor.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ bool SupportsInvalidation(CSSSelector::PseudoType type) {
8686
case CSSSelector::kPseudoIndeterminate:
8787
case CSSSelector::kPseudoTarget:
8888
case CSSSelector::kPseudoCurrent:
89+
case CSSSelector::kPseudoCheck:
8990
case CSSSelector::kPseudoBefore:
9091
case CSSSelector::kPseudoAfter:
9192
case CSSSelector::kPseudoMarker:

third_party/blink/renderer/core/css/style_engine.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,12 @@ namespace {
17311731

17321732
bool HasAttributeDependentGeneratedContent(const Element& element) {
17331733
DCHECK(!RuntimeEnabledFeatures::CSSAdvancedAttrFunctionEnabled());
1734+
if (PseudoElement* check = element.GetPseudoElement(kPseudoIdCheck)) {
1735+
const ComputedStyle* style = check->GetComputedStyle();
1736+
if (style && style->HasAttrFunction()) {
1737+
return true;
1738+
}
1739+
}
17341740
if (PseudoElement* before = element.GetPseudoElement(kPseudoIdBefore)) {
17351741
const ComputedStyle* style = before->GetComputedStyle();
17361742
if (style && style->HasAttrFunction()) {

third_party/blink/renderer/core/dom/document.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,7 @@ static void AssertNodeClean(const Node& node) {
21942194

21952195
static void AssertLayoutTreeUpdatedForPseudoElements(const Element& element) {
21962196
WTF::Vector<PseudoId> pseudo_ids = {kPseudoIdFirstLetter,
2197+
kPseudoIdCheck,
21972198
kPseudoIdBefore,
21982199
kPseudoIdAfter,
21992200
kPseudoIdMarker,

third_party/blink/renderer/core/dom/element.cc

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3829,6 +3829,13 @@ void Element::RecalcStyle(const StyleRecalcChange change,
38293829
UpdatePseudoElement(kPseudoIdMarker, child_change, child_recalc_context);
38303830
UpdatePseudoElement(kPseudoIdScrollMarker, child_change,
38313831
child_recalc_context);
3832+
3833+
if (RuntimeEnabledFeatures::CustomizableSelectEnabled()) {
3834+
if (DynamicTo<HTMLOptionElement>(this)) {
3835+
UpdatePseudoElement(kPseudoIdCheck, child_change, child_recalc_context);
3836+
}
3837+
}
3838+
38323839
UpdatePseudoElement(kPseudoIdBefore, child_change, child_recalc_context);
38333840
}
38343841

@@ -4374,6 +4381,7 @@ void Element::RebuildLayoutTree(WhitespaceAttacher& whitespace_attacher) {
43744381
} else {
43754382
RebuildChildrenLayoutTrees(*child_attacher);
43764383
}
4384+
RebuildPseudoElementLayoutTree(kPseudoIdCheck, *child_attacher);
43774385
RebuildPseudoElementLayoutTree(kPseudoIdBefore, *child_attacher);
43784386
RebuildPseudoElementLayoutTree(kPseudoIdMarker, *child_attacher);
43794387
RebuildPseudoElementLayoutTree(kPseudoIdScrollMarkerGroupBefore,
@@ -7594,6 +7602,7 @@ void Element::SetShadowPseudoId(const AtomicString& id) {
75947602
DCHECK(type == CSSSelector::kPseudoWebKitCustomElement ||
75957603
type == CSSSelector::kPseudoBlinkInternalElement ||
75967604
type == CSSSelector::kPseudoDetailsContent ||
7605+
type == CSSSelector::kPseudoCheck ||
75977606
id == shadow_element_names::kPickerSelect)
75987607
<< "type: " << type << ", id: " << id;
75997608
}
@@ -8406,10 +8415,11 @@ const ComputedStyle* Element::StyleForPseudoElement(
84068415
const StyleRequest& request) {
84078416
GetDocument().GetStyleEngine().UpdateViewportSize();
84088417

8409-
const bool is_before_or_after = request.pseudo_id == kPseudoIdBefore ||
8410-
request.pseudo_id == kPseudoIdAfter;
8418+
const bool is_before_or_after_like = request.pseudo_id == kPseudoIdCheck ||
8419+
request.pseudo_id == kPseudoIdBefore ||
8420+
request.pseudo_id == kPseudoIdAfter;
84118421

8412-
if (is_before_or_after) {
8422+
if (is_before_or_after_like) {
84138423
DCHECK(request.parent_override);
84148424
DCHECK(request.layout_parent_override);
84158425

@@ -10537,6 +10547,7 @@ Element* Element::ImplicitAnchorElement() const {
1053710547
}
1053810548
if (const PseudoElement* pseudo_element = DynamicTo<PseudoElement>(this)) {
1053910549
switch (pseudo_element->GetPseudoId()) {
10550+
case kPseudoIdCheck:
1054010551
case kPseudoIdBefore:
1054110552
case kPseudoIdAfter:
1054210553
case kPseudoIdBackdrop:

third_party/blink/renderer/core/dom/element.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,6 +1762,7 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {
17621762
void AttachPrecedingPseudoElements(AttachContext& context) {
17631763
AttachPseudoElement(kPseudoIdScrollPrevButton, context);
17641764
AttachPseudoElement(kPseudoIdMarker, context);
1765+
AttachPseudoElement(kPseudoIdCheck, context);
17651766
AttachPseudoElement(kPseudoIdBefore, context);
17661767
}
17671768

@@ -1777,6 +1778,7 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {
17771778
DetachPseudoElement(kPseudoIdScrollPrevButton, performing_reattach);
17781779
DetachPseudoElement(kPseudoIdScrollMarkerGroupBefore, performing_reattach);
17791780
DetachPseudoElement(kPseudoIdMarker, performing_reattach);
1781+
DetachPseudoElement(kPseudoIdCheck, performing_reattach);
17801782
DetachPseudoElement(kPseudoIdBefore, performing_reattach);
17811783
}
17821784

third_party/blink/renderer/core/dom/element_test.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,4 +1239,37 @@ TEST_F(ElementTest, ColumnPseudoElements) {
12391239
EXPECT_EQ(element->GetColumnPseudoElements()->size(), 0u);
12401240
}
12411241

1242+
TEST_F(ElementTest, TheCheckPseudoElement) {
1243+
GetDocument().body()->setInnerHTML(R"HTML(
1244+
<style>
1245+
#a-div::check {
1246+
content: "*";
1247+
}
1248+
1249+
#target::check {
1250+
content: "*";
1251+
}
1252+
</style>
1253+
1254+
<div id="a-div"></div>
1255+
1256+
<select id="target">
1257+
<option id="target-option" value="the only option"></option>
1258+
</select
1259+
)HTML");
1260+
1261+
// GetPseudoElement() relies on style recalc.
1262+
GetDocument().UpdateStyleAndLayoutTree();
1263+
1264+
Element* div = GetElementById("a-div");
1265+
EXPECT_EQ(nullptr, div->GetPseudoElement(kPseudoIdCheck));
1266+
1267+
Element* target = GetElementById("target");
1268+
EXPECT_EQ(nullptr, target->GetPseudoElement(kPseudoIdCheck));
1269+
1270+
// The `::check` pseudo element should only be created for option elements.
1271+
Element* target_option = GetElementById("target-option");
1272+
EXPECT_NE(nullptr, target_option->GetPseudoElement(kPseudoIdCheck));
1273+
}
1274+
12421275
} // namespace blink

third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ Node* LayoutTreeBuilderTraversal::NextSibling(const Node& node) {
112112
}
113113
[[fallthrough]];
114114
case kPseudoIdScrollMarker:
115+
if (Node* next = parent_element->GetPseudoElement(kPseudoIdCheck)) {
116+
return next;
117+
}
118+
[[fallthrough]];
119+
case kPseudoIdCheck:
115120
if (Node* next = parent_element->GetPseudoElement(kPseudoIdBefore))
116121
return next;
117122
[[fallthrough]];
@@ -214,6 +219,11 @@ Node* LayoutTreeBuilderTraversal::PreviousSibling(const Node& node) {
214219
return previous;
215220
[[fallthrough]];
216221
case kPseudoIdBefore:
222+
if (Node* previous = parent_element->GetPseudoElement(kPseudoIdCheck)) {
223+
return previous;
224+
}
225+
[[fallthrough]];
226+
case kPseudoIdCheck:
217227
if (Node* previous =
218228
parent_element->GetPseudoElement(kPseudoIdScrollMarker)) {
219229
return previous;
@@ -262,6 +272,9 @@ Node* LayoutTreeBuilderTraversal::LastChild(const Node& node) {
262272
return last;
263273
if (Node* last = current_element->GetPseudoElement(kPseudoIdBefore))
264274
return last;
275+
if (Node* last = current_element->GetPseudoElement(kPseudoIdCheck)) {
276+
return last;
277+
}
265278
if (Node* last = current_element->GetPseudoElement(kPseudoIdScrollMarker)) {
266279
return last;
267280
}
@@ -306,6 +319,9 @@ Node* LayoutTreeBuilderTraversal::FirstChild(const Node& node) {
306319
if (Node* first = current_element->GetPseudoElement(kPseudoIdScrollMarker)) {
307320
return first;
308321
}
322+
if (Node* first = current_element->GetPseudoElement(kPseudoIdCheck)) {
323+
return first;
324+
}
309325
if (Node* first = current_element->GetPseudoElement(kPseudoIdBefore))
310326
return first;
311327
if (Node* first = FlatTreeTraversal::FirstChild(node))

third_party/blink/renderer/core/dom/node.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ Node* Node::PseudoAwarePreviousSibling() const {
422422
return previous;
423423
[[fallthrough]];
424424
case kPseudoIdBefore:
425+
if (Node* previous = parent->GetPseudoElement(kPseudoIdCheck)) {
426+
return previous;
427+
}
428+
[[fallthrough]];
429+
case kPseudoIdCheck:
425430
if (Node* previous = parent->GetPseudoElement(kPseudoIdScrollMarker)) {
426431
return previous;
427432
}
@@ -501,6 +506,11 @@ Node* Node::PseudoAwareNextSibling() const {
501506
}
502507
[[fallthrough]];
503508
case kPseudoIdScrollMarker:
509+
if (Node* next = parent->GetPseudoElement(kPseudoIdCheck)) {
510+
return next;
511+
}
512+
[[fallthrough]];
513+
case kPseudoIdCheck:
504514
if (Node* next = parent->GetPseudoElement(kPseudoIdBefore))
505515
return next;
506516
[[fallthrough]];
@@ -601,6 +611,9 @@ Node* Node::PseudoAwareFirstChild() const {
601611
current_element->GetPseudoElement(kPseudoIdScrollMarker)) {
602612
return first;
603613
}
614+
if (Node* first = current_element->GetPseudoElement(kPseudoIdCheck)) {
615+
return first;
616+
}
604617
if (Node* first = current_element->GetPseudoElement(kPseudoIdBefore))
605618
return first;
606619
if (Node* first = current_element->firstChild())
@@ -669,6 +682,9 @@ Node* Node::PseudoAwareLastChild() const {
669682
return last;
670683
if (Node* last = current_element->GetPseudoElement(kPseudoIdBefore))
671684
return last;
685+
if (Node* last = current_element->GetPseudoElement(kPseudoIdCheck)) {
686+
return last;
687+
}
672688
if (Node* last = current_element->GetPseudoElement(kPseudoIdScrollMarker)) {
673689
return last;
674690
}
@@ -2670,6 +2686,10 @@ static void AppendMarkedTree(const String& base_indent,
26702686
AppendMarkedTree(indent_string, pseudo, marked_node1, marked_label1,
26712687
marked_node2, marked_label2, builder);
26722688
}
2689+
if (Element* pseudo = element->GetPseudoElement(kPseudoIdCheck)) {
2690+
AppendMarkedTree(indent_string, pseudo, marked_node1, marked_label1,
2691+
marked_node2, marked_label2, builder);
2692+
}
26732693
if (Element* pseudo = element->GetPseudoElement(kPseudoIdBefore))
26742694
AppendMarkedTree(indent_string, pseudo, marked_node1, marked_label1,
26752695
marked_node2, marked_label2, builder);

third_party/blink/renderer/core/dom/node.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ class CORE_EXPORT Node : public EventTarget {
350350
DISABLE_CFI_PERF bool IsBeforePseudoElement() const {
351351
return GetPseudoId() == kPseudoIdBefore;
352352
}
353+
DISABLE_CFI_PERF bool IsCheckPseudoElement() const {
354+
return GetPseudoId() == kPseudoIdCheck;
355+
}
353356
DISABLE_CFI_PERF bool IsAfterPseudoElement() const {
354357
return GetPseudoId() == kPseudoIdAfter;
355358
}

third_party/blink/renderer/core/dom/pseudo_element.cc

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.h"
3939
#include "third_party/blink/renderer/core/frame/web_feature.h"
4040
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
41+
#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
4142
#include "third_party/blink/renderer/core/input_type_names.h"
4243
#include "third_party/blink/renderer/core/layout/generated_children.h"
4344
#include "third_party/blink/renderer/core/layout/layout_counter.h"
@@ -79,6 +80,16 @@ PseudoId ResolvePseudoIdAlias(PseudoId pseudo_id) {
7980
PseudoElement* PseudoElement::Create(Element* parent,
8081
PseudoId pseudo_id,
8182
const AtomicString& view_transition_name) {
83+
if (pseudo_id == kPseudoIdCheck) {
84+
CHECK(RuntimeEnabledFeatures::CustomizableSelectEnabled());
85+
86+
if (!IsA<HTMLOptionElement>(parent)) {
87+
// The `::check` pseudo element should only be created for option
88+
// elements.
89+
return nullptr;
90+
}
91+
}
92+
8293
if (pseudo_id == kPseudoIdFirstLetter) {
8394
return MakeGarbageCollected<FirstLetterPseudoElement>(parent);
8495
} else if (IsTransitionPseudoElement(pseudo_id)) {
@@ -97,8 +108,8 @@ PseudoElement* PseudoElement::Create(Element* parent,
97108
return MakeGarbageCollected<ScrollButtonPseudoElement>(parent, pseudo_id);
98109
}
99110
DCHECK(pseudo_id == kPseudoIdAfter || pseudo_id == kPseudoIdBefore ||
100-
pseudo_id == kPseudoIdBackdrop || pseudo_id == kPseudoIdMarker ||
101-
pseudo_id == kPseudoIdColumn);
111+
pseudo_id == kPseudoIdCheck || pseudo_id == kPseudoIdBackdrop ||
112+
pseudo_id == kPseudoIdMarker || pseudo_id == kPseudoIdColumn);
102113
return MakeGarbageCollected<PseudoElement>(parent, pseudo_id,
103114
view_transition_name);
104115
}
@@ -113,6 +124,10 @@ const QualifiedName& PseudoElementTagName(PseudoId pseudo_id) {
113124
DEFINE_STATIC_LOCAL(QualifiedName, before, (AtomicString("::before")));
114125
return before;
115126
}
127+
case kPseudoIdCheck: {
128+
DEFINE_STATIC_LOCAL(QualifiedName, check, (AtomicString("::check")));
129+
return check;
130+
}
116131
case kPseudoIdBackdrop: {
117132
DEFINE_STATIC_LOCAL(QualifiedName, backdrop,
118133
(AtomicString("::backdrop")));
@@ -369,6 +384,7 @@ void PseudoElement::AttachLayoutTree(AttachContext& context) {
369384
return;
370385
}
371386
break;
387+
case kPseudoIdCheck:
372388
case kPseudoIdBefore:
373389
case kPseudoIdAfter:
374390
break;
@@ -421,6 +437,7 @@ void PseudoElement::AttachLayoutTree(AttachContext& context) {
421437
bool PseudoElement::CanGenerateContent() const {
422438
switch (GetPseudoIdForStyling()) {
423439
case kPseudoIdMarker:
440+
case kPseudoIdCheck:
424441
case kPseudoIdBefore:
425442
case kPseudoIdAfter:
426443
case kPseudoIdScrollMarker:
@@ -440,6 +457,7 @@ bool PseudoElement::LayoutObjectIsNeeded(const DisplayStyle& style) const {
440457

441458
bool PseudoElement::CanGeneratePseudoElement(PseudoId pseudo_id) const {
442459
switch (GetPseudoId()) {
460+
case kPseudoIdCheck:
443461
case kPseudoIdBefore:
444462
case kPseudoIdAfter:
445463
if (pseudo_id != kPseudoIdMarker)
@@ -503,6 +521,7 @@ bool PseudoElementLayoutObjectIsNeeded(PseudoId pseudo_id,
503521
case kPseudoIdViewTransitionNew:
504522
case kPseudoIdViewTransitionOld:
505523
return true;
524+
case kPseudoIdCheck:
506525
case kPseudoIdBefore:
507526
case kPseudoIdAfter:
508527
return !pseudo_style.ContentPreventsBoxGeneration();

0 commit comments

Comments
 (0)