Fix styling of th elements when explicitly specifiying text-align:inherit
https://bugs.webkit.org/show_bug.cgi?id=138577

Patch by Ryan Reno <rreno@apple.com> on 2022-06-16
Reviewed by Tim Nguyen.

<th> elements were being incorrectly centered when specifying
text-align: inherit. This fixes that bug by adding a new internal CSS
value for use in the UA stylesheet. This also removes a non-inherited
flag that was meant to be used for detecting this special case but ultimately didn't
work due to conflicts with the `all` property.

* Source/WebCore/css/CSSValueKeywords.in:
* Source/WebCore/css/html.css:
(th):
* Source/WebCore/css/parser/CSSParserFastPaths.cpp:
(WebCore::CSSParserFastPaths::isValidKeywordPropertyAndValue):
* Source/WebCore/css/parser/CSSParserIdioms.cpp:
(WebCore::isValueAllowedInMode):
* Source/WebCore/rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::RenderStyle):
* Source/WebCore/rendering/style/RenderStyle.h:
(WebCore::RenderStyle::NonInheritedFlags::operator== const):
(WebCore::RenderStyle::hasExplicitlySetTextAlign const): Deleted.
(WebCore::RenderStyle::setHasExplicitlySetTextAlign): Deleted.
* Source/WebCore/style/StyleAdjuster.cpp:
(WebCore::Style::Adjuster::adjust const):
* Source/WebCore/style/StyleBuilderConverter.h:
(WebCore::Style::BuilderConverter::convertTextAlign):
* Source/WebCore/css/CSSProperties.json:
* Source/WebCore/style/StyleBuilderCustom.h:
(WebCore::Style::BuilderCustom::applyInitialTextAlign): Deleted.
(WebCore::Style::BuilderCustom::applyValueTextAlign): Deleted.
* LayoutTests/fast/css/internal-th-center-ua-only-expected.txt: Added.
* LayoutTests/fast/css/internal-th-center-ua-only.html: Added.
* LayoutTests/fast/table/center-th-when-parent-has-initial-text-align-expected.html:
* LayoutTests/fast/table/center-th-when-parent-has-initial-text-align.html:

Canonical link: https://commits.webkit.org/251630@main

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@295625 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/fast/css/internal-th-center-ua-only-expected.txt b/LayoutTests/fast/css/internal-th-center-ua-only-expected.txt
new file mode 100644
index 0000000..abdebbc
--- /dev/null
+++ b/LayoutTests/fast/css/internal-th-center-ua-only-expected.txt
@@ -0,0 +1,4 @@
+
+PASS "text-align" property does not support value "-internal-th-center".
+PASS "text-align" property cannot be set to "-internal-th-center" by the author stylesheet.
+
diff --git a/LayoutTests/fast/css/internal-th-center-ua-only.html b/LayoutTests/fast/css/internal-th-center-ua-only.html
new file mode 100644
index 0000000..5b2e549
--- /dev/null
+++ b/LayoutTests/fast/css/internal-th-center-ua-only.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<style>
+th {
+    text-align: right;
+    text-align: -internal-th-center;
+}
+</style>
+<table>
+  <tr>
+    <th id="header"></th>
+  </tr>
+</table>
+
+<script>
+  test(function() {
+    assert_false(CSS.supports('text-align', '-internal-th-center'));
+  }, '"text-align" property does not support value "-internal-th-center".');
+
+  test(function() {
+    assert_equals(getComputedStyle(header).textAlign, 'right');
+  }, '"text-align" property cannot be set to "-internal-th-center" by the author stylesheet.');
+</script>
diff --git a/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align-expected.html b/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align-expected.html
index 6f388a7..51c4dc4 100644
--- a/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align-expected.html
+++ b/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align-expected.html
@@ -4,7 +4,7 @@
 <title>This tests that th is text-align centered when parent has initial value.</title>
 <style>
     table { 
-    width: 500px; border: 1px solid green; 
+    width: 500px; border: 1px solid green;
 }
 </style>
 <table><tbody>
@@ -12,11 +12,13 @@
     <tr><th style="text-align: left">th initial</th></tr>
     <tr><th style="text-align: left">th start</th></tr>
     <tr><th style="text-align: center">th center</th></tr>
+    <tr><th style="text-align: left">th initial</th></tr>
     <tr><th style="text-align: right">th end</th></tr>
     <tr><th style="text-align: center">tr initial</th></tr>
     <tr><th style="text-align: center">tr start</th></tr>
     <tr><th style="text-align: center">tr center</th></tr>
     <tr><th style="text-align: right">tr end</th></tr>
+    <tr><th style="text-align: right">tr end</th></tr>
     <tr><th style="text-align: left">tr initial, th initial</th></tr>
     <tr><th style="text-align: left">tr initial, th start</th></tr>
     <tr><th style="text-align: left">tr start, th start</th></tr>
diff --git a/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align.html b/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align.html
index 1c91d44..280a513 100644
--- a/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align.html
+++ b/LayoutTests/fast/table/center-th-when-parent-has-initial-text-align.html
@@ -4,7 +4,7 @@
 <title>This tests that th is text-align centered when parent has initial value.</title>
 <style>
     table { 
-    width: 500px; border: 1px solid green; 
+    width: 500px; border: 1px solid green;
 }
 </style>
 <table><tbody>
@@ -12,11 +12,13 @@
     <tr><th style="text-align: initial">th initial</th></tr>
     <tr><th style="text-align: start">th start</th></tr>
     <tr><th style="text-align: center">th center</th></tr>
+    <tr><th style="text-align: inherit">th initial</th></tr>
     <tr><th style="text-align: end">th end</th></tr>
     <tr style="text-align: initial"><th>tr initial</th></tr>
     <tr style="text-align: start"><th>tr start</th></tr>
     <tr style="text-align: center"><th>tr center</th></tr>
     <tr style="text-align: end"><th>tr end</th></tr>
+    <tr style="text-align: end"><th style="text-align: inherit">tr end</th></tr>
     <tr style="text-align: initial"><th style="text-align: initial">tr initial, th initial</th></tr>
     <tr style="text-align: initial"><th style="text-align: start">tr initial, th start</th></tr>
     <tr style="text-align: start"><th style="text-align: start">tr start, th start</th></tr>
diff --git a/Source/WebCore/css/CSSProperties.json b/Source/WebCore/css/CSSProperties.json
index ddcc146..0a99924 100644
--- a/Source/WebCore/css/CSSProperties.json
+++ b/Source/WebCore/css/CSSProperties.json
@@ -4587,8 +4587,7 @@
                 }
             ],
             "codegen-properties": {
-                "converter": "TextAlign",
-                "custom": "Initial|Value"
+                "converter": "TextAlign"
             },
             "specification": {
                 "category": "css-22",
diff --git a/Source/WebCore/css/CSSValueKeywords.in b/Source/WebCore/css/CSSValueKeywords.in
index 0569181..742e530 100644
--- a/Source/WebCore/css/CSSValueKeywords.in
+++ b/Source/WebCore/css/CSSValueKeywords.in
@@ -345,6 +345,7 @@
 -webkit-center
 match-parent
 -webkit-match-parent
+-internal-th-center
 //start
 //end
 //
diff --git a/Source/WebCore/css/html.css b/Source/WebCore/css/html.css
index d0b8593..6518bfc 100644
--- a/Source/WebCore/css/html.css
+++ b/Source/WebCore/css/html.css
@@ -269,6 +269,7 @@
 
 th {
     font-weight: bold;
+    text-align: -internal-th-center;
 }
 
 caption {
diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.cpp b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
index 2d6bab2..adb5e3c 100644
--- a/Source/WebCore/css/parser/CSSParserFastPaths.cpp
+++ b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
@@ -715,7 +715,7 @@
     case CSSPropertyTableLayout: // auto | fixed
         return valueID == CSSValueAuto || valueID == CSSValueFixed;
     case CSSPropertyTextAlign:
-        return (valueID >= CSSValueWebkitAuto && valueID <= CSSValueWebkitMatchParent) || valueID == CSSValueStart || valueID == CSSValueEnd;
+        return (valueID >= CSSValueWebkitAuto && valueID <= CSSValueInternalThCenter) || valueID == CSSValueStart || valueID == CSSValueEnd;
     case CSSPropertyTextAlignLast:
         // auto | start | end | left | right | center | justify | match-parent
         return (valueID >= CSSValueLeft && valueID <= CSSValueJustify) || valueID == CSSValueStart || valueID == CSSValueEnd || valueID == CSSValueAuto || valueID == CSSValueMatchParent;
diff --git a/Source/WebCore/css/parser/CSSParserIdioms.cpp b/Source/WebCore/css/parser/CSSParserIdioms.cpp
index 6544f26..4a1fcba 100644
--- a/Source/WebCore/css/parser/CSSParserIdioms.cpp
+++ b/Source/WebCore/css/parser/CSSParserIdioms.cpp
@@ -40,6 +40,8 @@
         return isUASheetBehavior(mode);
     case CSSValueWebkitFocusRingColor:
         return isUASheetBehavior(mode) || isQuirksModeBehavior(mode);
+    case CSSValueInternalThCenter:
+        return isUASheetBehavior(mode);
     default:
         return true;
     }
diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp
index bcb1b93..e397e9c 100644
--- a/Source/WebCore/rendering/style/RenderStyle.cpp
+++ b/Source/WebCore/rendering/style/RenderStyle.cpp
@@ -197,7 +197,6 @@
     m_nonInheritedFlags.hasExplicitlySetBorderTopRightRadius = false;
     m_nonInheritedFlags.hasExplicitlySetDirection = false;
     m_nonInheritedFlags.hasExplicitlySetWritingMode = false;
-    m_nonInheritedFlags.hasExplicitlySetTextAlign = false;
     m_nonInheritedFlags.usesViewportUnits = false;
     m_nonInheritedFlags.usesContainerUnits = false;
     m_nonInheritedFlags.hasExplicitlyInheritedProperties = false;
diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h
index 7ef353b..06b17e5 100644
--- a/Source/WebCore/rendering/style/RenderStyle.h
+++ b/Source/WebCore/rendering/style/RenderStyle.h
@@ -1570,9 +1570,6 @@
     bool hasExplicitlySetWritingMode() const { return m_nonInheritedFlags.hasExplicitlySetWritingMode; }
     void setHasExplicitlySetWritingMode(bool v) { m_nonInheritedFlags.hasExplicitlySetWritingMode = v; }
 
-    bool hasExplicitlySetTextAlign() const { return m_nonInheritedFlags.hasExplicitlySetTextAlign; }
-    void setHasExplicitlySetTextAlign(bool v) { m_nonInheritedFlags.hasExplicitlySetTextAlign = v; }
-
     // A unique style is one that has matches something that makes it impossible to share.
     bool unique() const { return m_nonInheritedFlags.isUnique; }
     void setUnique() { m_nonInheritedFlags.isUnique = true; }
@@ -1990,7 +1987,6 @@
         unsigned hasExplicitlySetBorderTopRightRadius : 1;
         unsigned hasExplicitlySetDirection : 1;
         unsigned hasExplicitlySetWritingMode : 1;
-        unsigned hasExplicitlySetTextAlign : 1;
 #if ENABLE(DARK_MODE_CSS)
         unsigned hasExplicitlySetColorScheme : 1;
 #endif
@@ -2132,7 +2128,6 @@
         && hasExplicitlySetBorderTopRightRadius == other.hasExplicitlySetBorderTopRightRadius
         && hasExplicitlySetDirection == other.hasExplicitlySetDirection
         && hasExplicitlySetWritingMode == other.hasExplicitlySetWritingMode
-        && hasExplicitlySetTextAlign == other.hasExplicitlySetTextAlign
 #if ENABLE(DARK_MODE_CSS)
         && hasExplicitlySetColorScheme == other.hasExplicitlySetColorScheme
 #endif
diff --git a/Source/WebCore/style/StyleAdjuster.cpp b/Source/WebCore/style/StyleAdjuster.cpp
index 55478f0..589e9b8 100644
--- a/Source/WebCore/style/StyleAdjuster.cpp
+++ b/Source/WebCore/style/StyleAdjuster.cpp
@@ -315,13 +315,6 @@
                 style.setFloating(Float::None);
             }
 
-            // User agents are expected to have a rule in their user agent stylesheet that matches th elements that have a parent
-            // node whose computed value for the 'text-align' property is its initial value, whose declaration block consists of
-            // just a single declaration that sets the 'text-align' property to the value 'center'.
-            // https://html.spec.whatwg.org/multipage/rendering.html#rendering
-            if (m_element->hasTagName(thTag) && !style.hasExplicitlySetTextAlign() && m_parentStyle.textAlign() == RenderStyle::initialTextAlign())
-                style.setTextAlign(TextAlignMode::Center);
-
             if (m_element->hasTagName(legendTag))
                 style.setEffectiveDisplay(DisplayType::Block);
         }
diff --git a/Source/WebCore/style/StyleBuilderConverter.h b/Source/WebCore/style/StyleBuilderConverter.h
index b0abcd9..ecdf339 100644
--- a/Source/WebCore/style/StyleBuilderConverter.h
+++ b/Source/WebCore/style/StyleBuilderConverter.h
@@ -610,23 +610,37 @@
 
 inline TextAlignMode BuilderConverter::convertTextAlign(BuilderState& builderState, const CSSValue& value)
 {
-    auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
+    const auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
     ASSERT(primitiveValue.isValueID());
 
-    if (primitiveValue.valueID() != CSSValueWebkitMatchParent && primitiveValue.valueID() != CSSValueMatchParent)
-        return primitiveValue;
+    const auto& parentStyle = builderState.parentStyle();
 
-    auto* element = builderState.element();
-    if (element && element == builderState.document().documentElement())
-        return TextAlignMode::Start;
+    // User agents are expected to have a rule in their user agent stylesheet that matches th elements that have a parent
+    // node whose computed value for the 'text-align' property is its initial value, whose declaration block consists of
+    // just a single declaration that sets the 'text-align' property to the value 'center'.
+    // https://html.spec.whatwg.org/multipage/rendering.html#rendering
+    if (primitiveValue.valueID() == CSSValueInternalThCenter) {
+        if (parentStyle.textAlign() == RenderStyle::initialTextAlign())
+            return TextAlignMode::Center;
+        return parentStyle.textAlign();
+    }
 
-    auto& parentStyle = builderState.parentStyle();
-    if (parentStyle.textAlign() == TextAlignMode::Start)
-        return parentStyle.isLeftToRightDirection() ? TextAlignMode::Left : TextAlignMode::Right;
-    if (parentStyle.textAlign() == TextAlignMode::End)
-        return parentStyle.isLeftToRightDirection() ? TextAlignMode::Right : TextAlignMode::Left;
-    return parentStyle.textAlign();
+    if (primitiveValue.valueID() == CSSValueWebkitMatchParent || primitiveValue.valueID() == CSSValueMatchParent) {
+        const auto* element = builderState.element();
+
+        if (element && element == builderState.document().documentElement())
+            return TextAlignMode::Start;
+        if (parentStyle.textAlign() == TextAlignMode::Start)
+            return parentStyle.isLeftToRightDirection() ? TextAlignMode::Left : TextAlignMode::Right;
+        if (parentStyle.textAlign() == TextAlignMode::End)
+            return parentStyle.isLeftToRightDirection() ? TextAlignMode::Right : TextAlignMode::Left;
+
+        return parentStyle.textAlign();
+    }
+
+    return primitiveValue;
 }
+
 inline TextAlignLast BuilderConverter::convertTextAlignLast(BuilderState& builderState, const CSSValue& value)
 {
     auto& primitiveValue = downcast<CSSPrimitiveValue>(value);
diff --git a/Source/WebCore/style/StyleBuilderCustom.h b/Source/WebCore/style/StyleBuilderCustom.h
index b61b81e..a0fc700 100644
--- a/Source/WebCore/style/StyleBuilderCustom.h
+++ b/Source/WebCore/style/StyleBuilderCustom.h
@@ -136,10 +136,6 @@
     static void applyInheritVerticalAlign(BuilderState&);
     static void applyValueVerticalAlign(BuilderState&, CSSValue&);
 
-    // Custom handling of initial + value only.
-    static void applyInitialTextAlign(BuilderState&);
-    static void applyValueTextAlign(BuilderState&, CSSValue&);
-
     // Custom handling of value setting only.
     static void applyValueBaselineShift(BuilderState&, CSSValue&);
     static void applyValueDirection(BuilderState&, CSSValue&);
@@ -192,18 +188,6 @@
     builderState.style().setHasExplicitlySetDirection(true);
 }
 
-inline void BuilderCustom::applyInitialTextAlign(BuilderState& builderState)
-{
-    builderState.style().setTextAlign(RenderStyle::initialTextAlign());
-    builderState.style().setHasExplicitlySetTextAlign(true);
-}
-
-inline void BuilderCustom::applyValueTextAlign(BuilderState& builderState, CSSValue& value)
-{
-    builderState.style().setTextAlign(BuilderConverter::convertTextAlign(builderState, value));
-    builderState.style().setHasExplicitlySetTextAlign(true);
-}
-
 inline void BuilderCustom::resetEffectiveZoom(BuilderState& builderState)
 {
     // Reset the zoom in effect. This allows the setZoom method to accurately compute a new zoom in effect.