[css-pseudo] Support 'content: none' for LayoutNG ::marker
Unlike ::before and ::after, in ::marker 'content: normal' and 'none'
should behave differently: the former should display it normally, the
latter should prevent it from generating a box.
Before this patch, 'normal' and 'none' were both stored as nullptr, so
they were indistinguishable. This patch makes 'none' be stored as a
NoneContentData instead.
However, 'none' keeps behaving as 'normal' for anything different than
::marker. In getComputedStyle, 'none' keeps resolving to 'normal' for
anything different than ::marker, ::before and ::after, and 'normal'
keeps resolving to 'none' for ::before and ::after.
Inheritance is not affected either since the 'content' property can't
be inherited in Chromium.
BUG=457718
TEST=external/wpt/css/css-pseudo/marker-computed-content.html
TEST=external/wpt/css/css-pseudo/marker-computed-size.html
TEST=external/wpt/css/css-pseudo/marker-content-019.html
marker-content-019.html fails in legacy.
Change-Id: Ib71ef18dfb7f7c98e76888bffb408a17fa458235
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2002517
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Oriol Brufau <obrufau@igalia.com>
Cr-Commit-Position: refs/heads/master@{#732549}
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 131abc6..68eb20e 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1844,6 +1844,11 @@
CSSValue* ComputedStyleUtils::ValueForContentData(const ComputedStyle& style,
bool allow_visited_style) {
+ if (style.ContentPreventsBoxGeneration())
+ return CSSIdentifierValue::Create(CSSValueID::kNone);
+ if (style.ContentBehavesAsNormal())
+ return CSSIdentifierValue::Create(CSSValueID::kNormal);
+
CSSValueList* outer_list = CSSValueList::CreateSlashSeparated();
CSSValueList* list = CSSValueList::CreateSpaceSeparated();
@@ -1888,12 +1893,7 @@
NOTREACHED();
}
}
- if (!list->length()) {
- PseudoId pseudoId = style.StyleType();
- if (pseudoId == kPseudoIdBefore || pseudoId == kPseudoIdAfter)
- return CSSIdentifierValue::Create(CSSValueID::kNone);
- return CSSIdentifierValue::Create(CSSValueID::kNormal);
- }
+ DCHECK(list->length());
outer_list->Append(*list);
if (alt_text)
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index efc1972..99509a5 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -1968,7 +1968,10 @@
if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
DCHECK(identifier_value->GetValueID() == CSSValueID::kNormal ||
identifier_value->GetValueID() == CSSValueID::kNone);
- state.Style()->SetContent(nullptr);
+ if (identifier_value->GetValueID() == CSSValueID::kNone)
+ state.Style()->SetContent(MakeGarbageCollected<NoneContentData>());
+ else
+ state.Style()->SetContent(nullptr);
return;
}
const CSSValueList& outer_list = To<CSSValueList>(value);
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index 8dcd7c6..43bb6f3d 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -184,7 +184,7 @@
ToLayoutNGListItem(layout_object->Parent())
->UpdateMarkerContentIfNeeded();
}
- if (!style.GetContentData())
+ if (style.ContentBehavesAsNormal())
return;
break;
}
@@ -195,7 +195,8 @@
return;
}
- DCHECK(style.GetContentData());
+ DCHECK(!style.ContentBehavesAsNormal());
+ DCHECK(!style.ContentPreventsBoxGeneration());
for (const ContentData* content = style.GetContentData(); content;
content = content->Next()) {
LegacyLayout legacy = context.force_legacy_layout ? LegacyLayout::kForce
@@ -246,10 +247,10 @@
return true;
case kPseudoIdBefore:
case kPseudoIdAfter:
- return pseudo_style->GetContentData();
+ return !pseudo_style->ContentPreventsBoxGeneration();
case kPseudoIdMarker: {
- if (pseudo_style->GetContentData())
- return true;
+ if (!pseudo_style->ContentBehavesAsNormal())
+ return !pseudo_style->ContentPreventsBoxGeneration();
const ComputedStyle* parent_style =
originating_element->GetComputedStyle();
return parent_style &&
diff --git a/third_party/blink/renderer/core/html/html_br_element.cc b/third_party/blink/renderer/core/html/html_br_element.cc
index d81dd2d7..53c7f60 100644
--- a/third_party/blink/renderer/core/html/html_br_element.cc
+++ b/third_party/blink/renderer/core/html/html_br_element.cc
@@ -62,9 +62,9 @@
LayoutObject* HTMLBRElement::CreateLayoutObject(const ComputedStyle& style,
LegacyLayout legacy) {
- if (style.HasContent())
- return LayoutObject::CreateObject(this, style, legacy);
- return new LayoutBR(this);
+ if (style.ContentBehavesAsNormal())
+ return new LayoutBR(this);
+ return LayoutObject::CreateObject(this, style, legacy);
}
} // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_frame_set_element.cc b/third_party/blink/renderer/core/html/html_frame_set_element.cc
index 9d4cb87f..a851271 100644
--- a/third_party/blink/renderer/core/html/html_frame_set_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_set_element.cc
@@ -221,9 +221,9 @@
LayoutObject* HTMLFrameSetElement::CreateLayoutObject(
const ComputedStyle& style,
LegacyLayout legacy) {
- if (style.HasContent())
- return LayoutObject::CreateObject(this, style, legacy);
- return new LayoutFrameSet(this);
+ if (style.ContentBehavesAsNormal())
+ return new LayoutFrameSet(this);
+ return LayoutObject::CreateObject(this, style, legacy);
}
void HTMLFrameSetElement::AttachLayoutTree(AttachContext& context) {
diff --git a/third_party/blink/renderer/core/layout/layout_list_marker.cc b/third_party/blink/renderer/core/layout/layout_list_marker.cc
index 7ca78af..edb53c65 100644
--- a/third_party/blink/renderer/core/layout/layout_list_marker.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_marker.cc
@@ -312,7 +312,7 @@
std::pair<LayoutUnit, LayoutUnit> LayoutListMarker::InlineMarginsForInside(
const ComputedStyle& style,
bool is_image) {
- if (style.GetContentData())
+ if (!style.ContentBehavesAsNormal())
return {};
if (is_image)
return {LayoutUnit(), LayoutUnit(kCMarkerPaddingPx)};
@@ -332,7 +332,7 @@
LayoutUnit marker_inline_size) {
LayoutUnit margin_start;
LayoutUnit margin_end;
- if (style.GetContentData()) {
+ if (!style.ContentBehavesAsNormal()) {
margin_start = -marker_inline_size;
} else if (is_image) {
margin_start = -marker_inline_size - kCMarkerPaddingPx;
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 622955da..80866b0 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -2532,7 +2532,7 @@
// Don't propagate style from markers with 'content: normal' because it's not
// needed and it would be slow.
- if (pseudo_id == kPseudoIdMarker && !StyleRef().GetContentData())
+ if (pseudo_id == kPseudoIdMarker && StyleRef().ContentBehavesAsNormal())
return;
// Propagate style from pseudo elements to generated content. We skip children
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h b/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h
index 826079b..43f353d 100644
--- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h
+++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_inside_list_marker.h
@@ -20,7 +20,7 @@
#if DCHECK_IS_ON()
void AddChild(LayoutObject* new_child, LayoutObject* before_child) override {
// List markers with 'content: normal' should have at most one child.
- DCHECK(StyleRef().GetContentData() || !FirstChild());
+ DCHECK(!StyleRef().ContentBehavesAsNormal() || !FirstChild());
LayoutInline::AddChild(new_child, before_child);
}
#endif
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
index db7b5f4b..baa2e08 100644
--- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
@@ -274,7 +274,7 @@
LayoutObject* child = marker->SlowFirstChild();
// There should be at most one child.
DCHECK(!child || !child->SlowFirstChild());
- if (marker->StyleRef().GetContentData()) {
+ if (!marker->StyleRef().ContentBehavesAsNormal()) {
marker_type_ = kStatic;
is_marker_text_updated_ = true;
} else if (IsMarkerImage()) {
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 6b0eecd..dbfaa89 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2137,7 +2137,25 @@
bool HasIsolation() const { return Isolation() != EIsolation::kAuto; }
// Content utility functions.
- bool HasContent() const { return GetContentData(); }
+ bool ContentBehavesAsNormal() const {
+ switch (StyleType()) {
+ case kPseudoIdMarker:
+ return !GetContentData();
+ default:
+ return !GetContentData() || GetContentData()->IsNone();
+ }
+ }
+ bool ContentPreventsBoxGeneration() const {
+ switch (StyleType()) {
+ case kPseudoIdBefore:
+ case kPseudoIdAfter:
+ return ContentBehavesAsNormal();
+ case kPseudoIdMarker:
+ return GetContentData() && GetContentData()->IsNone();
+ default:
+ return false;
+ }
+ }
// Cursor utility functions.
CursorList* Cursors() const { return CursorDataInternal().Get(); }
diff --git a/third_party/blink/renderer/core/style/content_data.cc b/third_party/blink/renderer/core/style/content_data.cc
index 549b833cc..41679038 100644
--- a/third_party/blink/renderer/core/style/content_data.cc
+++ b/third_party/blink/renderer/core/style/content_data.cc
@@ -110,4 +110,12 @@
return layout_object;
}
+LayoutObject* NoneContentData::CreateLayoutObject(
+ PseudoElement& pseudo,
+ const ComputedStyle& pseudo_style,
+ LegacyLayout) const {
+ NOTREACHED();
+ return nullptr;
+}
+
} // namespace blink
diff --git a/third_party/blink/renderer/core/style/content_data.h b/third_party/blink/renderer/core/style/content_data.h
index 60ec585..ea892c0 100644
--- a/third_party/blink/renderer/core/style/content_data.h
+++ b/third_party/blink/renderer/core/style/content_data.h
@@ -50,6 +50,7 @@
virtual bool IsQuote() const { return false; }
virtual bool IsText() const { return false; }
virtual bool IsAltText() const { return false; }
+ virtual bool IsNone() const { return false; }
virtual LayoutObject* CreateLayoutObject(PseudoElement&,
const ComputedStyle&,
@@ -58,7 +59,11 @@
virtual ContentData* Clone() const;
ContentData* Next() const { return next_.Get(); }
- void SetNext(ContentData* next) { next_ = next; }
+ void SetNext(ContentData* next) {
+ DCHECK(!IsNone());
+ DCHECK(!next || !next->IsNone());
+ next_ = next;
+ }
virtual bool Equals(const ContentData&) const = 0;
@@ -258,6 +263,30 @@
}
};
+class NoneContentData final : public ContentData {
+ friend class ContentData;
+
+ public:
+ explicit NoneContentData() {}
+
+ bool IsNone() const override { return true; }
+ LayoutObject* CreateLayoutObject(PseudoElement&,
+ const ComputedStyle&,
+ LegacyLayout) const override;
+
+ bool Equals(const ContentData& data) const override { return IsNone(); }
+
+ private:
+ ContentData* CloneInternal() const override {
+ return MakeGarbageCollected<NoneContentData>();
+ }
+};
+
+template <>
+struct DowncastTraits<NoneContentData> {
+ static bool AllowFrom(const ContentData& content) { return content.IsNone(); }
+};
+
inline bool operator==(const ContentData& a, const ContentData& b) {
const ContentData* ptr_a = &a;
const ContentData* ptr_b = &b;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index d980f6c..2e4703c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -307,7 +307,7 @@
base::Optional<String> AXNodeObject::GetCSSAltText(Node* node) {
if (!node || !node->GetComputedStyle() ||
- !node->GetComputedStyle()->GetContentData()) {
+ node->GetComputedStyle()->ContentBehavesAsNormal()) {
return base::nullopt;
}
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 2415961c..49d9414 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -82,6 +82,7 @@
crbug.com/457718 external/wpt/css/css-pseudo/marker-content-016.html [ Failure ]
crbug.com/457718 external/wpt/css/css-pseudo/marker-content-017.html [ Failure ]
crbug.com/457718 external/wpt/css/css-pseudo/marker-content-018.html [ Failure ]
+crbug.com/457718 external/wpt/css/css-pseudo/marker-content-019.html [ Failure ]
crbug.com/1012289 external/wpt/css/css-pseudo/marker-unicode-bidi-default.html [ Failure ]
crbug.com/1012289 external/wpt/css/css-pseudo/marker-unicode-bidi-normal.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-computed-content.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-computed-content.html
new file mode 100644
index 0000000..b2df337
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-computed-content.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Pseudo-Elements Test: Computed size of ::marker</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#marker-pseudo">
+<link rel="help" href="https://drafts.csswg.org/css-content/#content-property">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<meta name="assert" content="This test checks that 'content' resolves correctly in ::marker." />
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+.no-list > li {
+ display: block;
+}
+.normal::marker {
+ content: normal;
+}
+.string::marker {
+ content: "string";
+}
+.image::marker {
+ content: url("about:invalid");
+}
+.none::marker {
+ content: none;
+}
+</style>
+<div id="log"></div>
+<ol class="list">
+ <li class="default">item</li>
+ <li class="normal">item</li>
+ <li class="string">item</li>
+ <li class="image">item</li>
+ <li class="none">item</li>
+</ol>
+<ol class="no-list">
+ <li class="default">item</li>
+ <li class="normal">item</li>
+ <li class="string">item</li>
+ <li class="image">item</li>
+ <li class="none">item</li>
+</ol>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const expectations = {
+ default: 'normal',
+ normal: 'normal',
+ string: '"string"',
+ image: 'url("about:invalid")',
+ none: 'none',
+};
+for (const target of document.querySelectorAll('.list > li')) {
+ const {content} = getComputedStyle(target, '::marker');
+ test(() => {
+ debugger;
+ assert_equals(content, expectations[target.className]);
+ }, `Computed 'content' for list-item ::marker, variant ${target.className}`);
+}
+for (const target of document.querySelectorAll('.no-list > li')) {
+ const {content} = getComputedStyle(target, '::marker');
+ test(() => {
+ assert_equals(content, expectations[target.className]);
+ }, `Computed 'content' for non-list-item ::marker, variant ${target.className}`);
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-computed-size-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-computed-size-expected.txt
deleted file mode 100644
index 6ddc9e6..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-computed-size-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-PASS Decimal ::marker
-PASS Decimal ::marker with custom value
-PASS String ::marker
-PASS ::marker with no box due to 'list-style'
-PASS ::marker with custom string contents
-PASS ::marker with custom image contents
-PASS ::marker with custom string and image contents
-FAIL ::marker with no box due to 'content' assert_equals: width expected "auto" but got "5px"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-019-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-019-ref.html
new file mode 100644
index 0000000..4b96577
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-019-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<style>
+li {
+ display: block;
+}
+</style>
+<ol>
+ <li>inside symbol</li>
+ <li>inside decimal</li>
+ <li>inside string</li>
+ <li>inside image</li>
+</ol>
+<ol>
+ <li>outside symbol</li>
+ <li>outside decimal</li>
+ <li>outside string</li>
+ <li>outside image</li>
+</ol>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-019.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-019.html
new file mode 100644
index 0000000..4921ca7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/marker-content-019.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Test: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="match" href="marker-content-019-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#marker-pseudo">
+<link rel="help" href="https://drafts.csswg.org/css-content/#content-property">
+<meta name="assert" content="Checks that the 'content: none' hides the ::marker.">
+<style>
+::marker {
+ content: none
+}
+.inside {
+ list-style-position: inside;
+}
+.symbol {
+ list-style-type: disc;
+}
+.decimal {
+ list-style-type: decimal;
+}
+.string {
+ list-style-type: "string";
+}
+.image {
+ list-style-image: url("/images/green-100x50.png");
+}
+</style>
+<ol class="inside">
+ <li class="symbol">inside symbol</li>
+ <li class="decimal">inside decimal</li>
+ <li class="string">inside string</li>
+ <li class="image">inside image</li>
+</ol>
+<ol class="outside">
+ <li class="symbol">outside symbol</li>
+ <li class="decimal">outside decimal</li>
+ <li class="string">outside string</li>
+ <li class="image">outside image</li>
+</ol>