Title: [294291] trunk
Revision
294291
Author
an...@apple.com
Date
2022-05-16 22:59:55 -0700 (Mon, 16 May 2022)

Log Message

Resolve ::first-letter eagerly
https://bugs.webkit.org/show_bug.cgi?id=239844

Reviewed by Alan Bujtas.

Source/WebCore:

::first-letter pseudo-element should be resolved eagerly during style resolution, similar to ::first-line.
This will clean up the architecture. It also makes ::first-letter animatable.

* rendering/RenderTextFragment.cpp:
(WebCore::RenderTextFragment::styleDidChange): Deleted.
* rendering/RenderTextFragment.h:
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::removeCachedPseudoStyle): Deleted.

We always create a new style. There is no need to removed pseudo element styles.

* rendering/style/RenderStyle.h:
* rendering/updating/RenderTreeBuilderFirstLetter.cpp:
(WebCore::styleForFirstLetter):

Pull first-letter style from the parent style instead of resolving it in the middle of render tree building.

(WebCore::RenderTreeBuilder::FirstLetter::updateAfterDescendants):
(WebCore::RenderTreeBuilder::FirstLetter::updateStyle):

Delete the renderer if we no longer have a style.

(WebCore::RenderTreeBuilder::FirstLetter::createRenderers):
* rendering/updating/RenderTreeBuilderFirstLetter.h:
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::resolveElement):

Resove first-letter.

(WebCore::Style::TreeResolver::resolvePseudoElement):
(WebCore::Style::TreeResolver::resolveInheritedPseudoElement):

Factor into a function.

(WebCore::Style::isChildInBlockFormattingContext):
(WebCore::Style::TreeResolver::resolveAncestorFirstLinePseudoElement):
(WebCore::Style::TreeResolver::resolveAncestorFirstLetterPseudoElement):

Resolving first-letter is similar to first-line except it is always a leaf and so won't be inherited from.

(WebCore::Style::TreeResolver::makeResolutionContextForPseudoElement):
(WebCore::Style::TreeResolver::makeResolutionContextForInheritedFirstLine):
* style/StyleTreeResolver.h:

LayoutTests:

* fast/css-generated-content/first-letter-in-nested-before-expected.html: Added.
* fast/css-generated-content/first-letter-in-nested-before-expected.png: Removed.
* fast/css-generated-content/first-letter-in-nested-before-expected.txt: Removed.
* fast/css-generated-content/first-letter-in-nested-before-table-expected.html: Copied from LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html.
* fast/css-generated-content/first-letter-in-nested-before-table-expected.png: Removed.
* fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
* fast/css-generated-content/first-letter-in-nested-before-table.html:
* fast/css-generated-content/first-letter-in-nested-before.html:

Make reftest and update the behavior. ::first-letter won't affect ::before/after with display:table,
matching Firefox behavior (and spec logic).

* platform/glib/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
* platform/gtk/fast/css-generated-content/first-letter-in-nested-before-expected.png: Removed.
* platform/gtk/fast/css-generated-content/first-letter-in-nested-before-table-expected.png: Removed.
* platform/ios/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
* platform/mac/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
* platform/win/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
* platform/wincairo/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (294290 => 294291)


--- trunk/LayoutTests/ChangeLog	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/ChangeLog	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,3 +1,30 @@
+2022-05-16  Antti Koivisto  <an...@apple.com>
+
+        Resolve ::first-letter eagerly
+        https://bugs.webkit.org/show_bug.cgi?id=239844
+
+        Reviewed by Alan Bujtas.
+
+        * fast/css-generated-content/first-letter-in-nested-before-expected.html: Added.
+        * fast/css-generated-content/first-letter-in-nested-before-expected.png: Removed.
+        * fast/css-generated-content/first-letter-in-nested-before-expected.txt: Removed.
+        * fast/css-generated-content/first-letter-in-nested-before-table-expected.html: Copied from LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html.
+        * fast/css-generated-content/first-letter-in-nested-before-table-expected.png: Removed.
+        * fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
+        * fast/css-generated-content/first-letter-in-nested-before-table.html:
+        * fast/css-generated-content/first-letter-in-nested-before.html:
+
+        Make reftest and update the behavior. ::first-letter won't affect ::before/after with display:table,
+        matching Firefox behavior (and spec logic).
+
+        * platform/glib/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
+        * platform/gtk/fast/css-generated-content/first-letter-in-nested-before-expected.png: Removed.
+        * platform/gtk/fast/css-generated-content/first-letter-in-nested-before-table-expected.png: Removed.
+        * platform/ios/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
+        * platform/mac/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
+        * platform/win/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
+        * platform/wincairo/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
+
 2022-05-16  Karl Rackler  <rack...@apple.com>
 
         REGRESSION (r294215): [ iOS ] Nine fast tests are a consistent image failure

Added: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.html (0 => 294291)


--- trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.html	2022-05-17 05:59:55 UTC (rev 294291)
@@ -0,0 +1,3 @@
+<body style="font-family: ahem; -webkit-font-smoothing: none;">
+    <div style="font-size: 100px; color: green;">A</div>
+</body>

Deleted: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.png


(Binary files differ)

Deleted: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.txt (294290 => 294291)


--- trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,17 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  RenderBlock {HTML} at (0,0) size 800x600
-    RenderBody {BODY} at (8,8) size 784x584
-      RenderBlock {DIV} at (0,0) size 784x100 [color=#FF0000]
-        RenderText {#text} at (0,0) size 100x100
-          text run at (0,0) width 100: "A"
-      RenderBlock {DIV} at (0,0) size 784x100 [color=#008000]
-        RenderTable at (0,0) size 100x100
-          RenderTableSection (anonymous) at (0,0) size 100x100
-            RenderTableRow (anonymous) at (0,0) size 100x100
-              RenderTableCell (anonymous) at (0,0) size 100x100 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 100x100
-                  RenderText at (0,0) size 100x100
-                    text run at (0,0) width 100: "A"
-                RenderText at (0,0) size 0x0

Copied: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.html (from rev 294290, trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html) (0 => 294291)


--- trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.html	                        (rev 0)
+++ trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.html	2022-05-17 05:59:55 UTC (rev 294291)
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<body style="font-family: ahem; font-size: 50px; -webkit-font-smoothing: none;">
+<style>
+.test1:before {
+    display: table;
+    content: "AB";
+}
+.test2:after {
+    display: table;
+    content: "EF";
+}
+</style>
+<div class="test1">C</div>
+<div class="test2"><span style="color:green">D</span></div>
+<div class="test2"></div>
+<script>
+document.body.offsetTop;
+document.body.style.color = "blue";
+</script>
+</html>

Deleted: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.png


(Binary files differ)

Deleted: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt (294290 => 294291)


--- trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,40 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x250 [color=#0000FF]
-      RenderBlock {DIV} at (0,0) size 784x100
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "A"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "B"
-        RenderBlock (anonymous) at (0,50) size 784x50
-          RenderText {#text} at (0,0) size 50x50
-            text run at (0,0) width 50: "C"
-      RenderBlock {DIV} at (0,100) size 784x100
-        RenderBlock (anonymous) at (0,0) size 784x50
-          RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-            RenderText {#text} at (0,0) size 50x50
-              text run at (0,0) width 50: "D"
-          RenderText {#text} at (0,0) size 0x0
-        RenderTable at (0,50) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderText at (0,0) size 100x50
-                  text run at (0,0) width 100: "EF"
-      RenderBlock {DIV} at (0,200) size 784x50
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "E"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "F"

Modified: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html (294290 => 294291)


--- trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,5 +1,4 @@
 <!DOCTYPE html>
-<!-- No text in black, B and F should be blue. -->
 <html>
 <body style="font-family: ahem; font-size: 50px; -webkit-font-smoothing: none;">
 <style>
@@ -21,4 +20,4 @@
 document.body.offsetTop;
 document.body.style.color = "blue";
 </script>
-</html>
\ No newline at end of file
+</html>

Modified: trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before.html (294290 => 294291)


--- trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before.html	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before.html	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,6 +1,6 @@
 <style>
     #target { color: green; margin-top: -100px; }
-    #target:before { display: table; content: "A"; }
+    #target:before { display: block; content: "A"; }
     #target:first-letter { font-size: 200%; }
 </style>
 <body style="font-family: ahem; -webkit-font-smoothing: none;">

Deleted: trunk/LayoutTests/platform/glib/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt (294290 => 294291)


--- trunk/LayoutTests/platform/glib/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/platform/glib/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,40 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x250 [color=#0000FF]
-      RenderBlock {DIV} at (0,0) size 784x100
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "A"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "B"
-        RenderBlock (anonymous) at (0,50) size 784x50
-          RenderText {#text} at (0,0) size 50x50
-            text run at (0,0) width 50: "C"
-      RenderBlock {DIV} at (0,100) size 784x100
-        RenderBlock (anonymous) at (0,0) size 784x50
-          RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-            RenderText at (0,0) size 50x50
-              text run at (0,0) width 50: "D"
-          RenderText {#text} at (0,0) size 0x0
-        RenderTable at (0,50) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderText at (0,0) size 100x50
-                  text run at (0,0) width 100: "EF"
-      RenderBlock {DIV} at (0,200) size 784x50
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "E"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "F"

Deleted: trunk/LayoutTests/platform/gtk/fast/css-generated-content/first-letter-in-nested-before-expected.png


(Binary files differ)

Deleted: trunk/LayoutTests/platform/gtk/fast/css-generated-content/first-letter-in-nested-before-table-expected.png


(Binary files differ)

Deleted: trunk/LayoutTests/platform/ios/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt (294290 => 294291)


--- trunk/LayoutTests/platform/ios/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/platform/ios/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,40 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x250 [color=#0000FF]
-      RenderBlock {DIV} at (0,0) size 784x100
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "A"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "B"
-        RenderBlock (anonymous) at (0,50) size 784x50
-          RenderText {#text} at (0,0) size 50x50
-            text run at (0,0) width 50: "C"
-      RenderBlock {DIV} at (0,100) size 784x100
-        RenderBlock (anonymous) at (0,0) size 784x50
-          RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-            RenderText at (0,0) size 50x50
-              text run at (0,0) width 50: "D"
-          RenderText {#text} at (0,0) size 0x0
-        RenderTable at (0,50) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderText at (0,0) size 100x50
-                  text run at (0,0) width 100: "EF"
-      RenderBlock {DIV} at (0,200) size 784x50
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "E"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "F"

Deleted: trunk/LayoutTests/platform/mac/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt (294290 => 294291)


--- trunk/LayoutTests/platform/mac/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/platform/mac/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,40 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x250 [color=#0000FF]
-      RenderBlock {DIV} at (0,0) size 784x100
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "A"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "B"
-        RenderBlock (anonymous) at (0,50) size 784x50
-          RenderText {#text} at (0,0) size 50x50
-            text run at (0,0) width 50: "C"
-      RenderBlock {DIV} at (0,100) size 784x100
-        RenderBlock (anonymous) at (0,0) size 784x50
-          RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-            RenderText at (0,0) size 50x50
-              text run at (0,0) width 50: "D"
-          RenderText {#text} at (0,0) size 0x0
-        RenderTable at (0,50) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderText at (0,0) size 100x50
-                  text run at (0,0) width 100: "EF"
-      RenderBlock {DIV} at (0,200) size 784x50
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "E"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "F"

Deleted: trunk/LayoutTests/platform/win/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt (294290 => 294291)


--- trunk/LayoutTests/platform/win/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/platform/win/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,40 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x250 [color=#0000FF]
-      RenderBlock {DIV} at (0,0) size 784x100
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "A"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "B"
-        RenderBlock (anonymous) at (0,50) size 784x50
-          RenderText {#text} at (0,0) size 50x50
-            text run at (0,0) width 50: "C"
-      RenderBlock {DIV} at (0,100) size 784x100
-        RenderBlock (anonymous) at (0,0) size 784x50
-          RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-            RenderText at (0,0) size 50x50
-              text run at (0,0) width 50: "D"
-          RenderText {#text} at (0,0) size 0x0
-        RenderTable at (0,50) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderText at (0,0) size 100x50
-                  text run at (0,0) width 100: "EF"
-      RenderBlock {DIV} at (0,200) size 784x50
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "E"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "F"

Deleted: trunk/LayoutTests/platform/wincairo/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt (294290 => 294291)


--- trunk/LayoutTests/platform/wincairo/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/LayoutTests/platform/wincairo/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,40 +0,0 @@
-layer at (0,0) size 800x600
-  RenderView at (0,0) size 800x600
-layer at (0,0) size 800x266
-  RenderBlock {HTML} at (0,0) size 800x266
-    RenderBody {BODY} at (8,8) size 784x250 [color=#0000FF]
-      RenderBlock {DIV} at (0,0) size 784x100
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "A"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "B"
-        RenderBlock (anonymous) at (0,50) size 784x50
-          RenderText {#text} at (0,0) size 50x50
-            text run at (0,0) width 50: "C"
-      RenderBlock {DIV} at (0,100) size 784x100
-        RenderBlock (anonymous) at (0,0) size 784x50
-          RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-            RenderText at (0,0) size 50x50
-              text run at (0,0) width 50: "D"
-          RenderText {#text} at (0,0) size 0x0
-        RenderTable at (0,50) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderText at (0,0) size 100x50
-                  text run at (0,0) width 100: "EF"
-      RenderBlock {DIV} at (0,200) size 784x50
-        RenderTable at (0,0) size 100x50
-          RenderTableSection (anonymous) at (0,0) size 100x50
-            RenderTableRow (anonymous) at (0,0) size 100x50
-              RenderTableCell (anonymous) at (0,0) size 100x50 [r=0 c=0 rs=1 cs=1]
-                RenderInline (generated) at (0,0) size 50x50 [color=#008000]
-                  RenderText at (0,0) size 50x50
-                    text run at (0,0) width 50: "E"
-                RenderText at (50,0) size 50x50
-                  text run at (50,0) width 50: "F"

Modified: trunk/Source/WebCore/ChangeLog (294290 => 294291)


--- trunk/Source/WebCore/ChangeLog	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/ChangeLog	2022-05-17 05:59:55 UTC (rev 294291)
@@ -1,3 +1,54 @@
+2022-05-16  Antti Koivisto  <an...@apple.com>
+
+        Resolve ::first-letter eagerly
+        https://bugs.webkit.org/show_bug.cgi?id=239844
+
+        Reviewed by Alan Bujtas.
+
+        ::first-letter pseudo-element should be resolved eagerly during style resolution, similar to ::first-line.
+        This will clean up the architecture. It also makes ::first-letter animatable.
+
+        * rendering/RenderTextFragment.cpp:
+        (WebCore::RenderTextFragment::styleDidChange): Deleted.
+        * rendering/RenderTextFragment.h:
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::removeCachedPseudoStyle): Deleted.
+
+        We always create a new style. There is no need to removed pseudo element styles.
+
+        * rendering/style/RenderStyle.h:
+        * rendering/updating/RenderTreeBuilderFirstLetter.cpp:
+        (WebCore::styleForFirstLetter):
+
+        Pull first-letter style from the parent style instead of resolving it in the middle of render tree building.
+
+        (WebCore::RenderTreeBuilder::FirstLetter::updateAfterDescendants):
+        (WebCore::RenderTreeBuilder::FirstLetter::updateStyle):
+
+        Delete the renderer if we no longer have a style.
+
+        (WebCore::RenderTreeBuilder::FirstLetter::createRenderers):
+        * rendering/updating/RenderTreeBuilderFirstLetter.h:
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::resolveElement):
+
+        Resove first-letter.
+
+        (WebCore::Style::TreeResolver::resolvePseudoElement):
+        (WebCore::Style::TreeResolver::resolveInheritedPseudoElement):
+
+        Factor into a function.
+
+        (WebCore::Style::isChildInBlockFormattingContext):
+        (WebCore::Style::TreeResolver::resolveAncestorFirstLinePseudoElement):
+        (WebCore::Style::TreeResolver::resolveAncestorFirstLetterPseudoElement):
+
+        Resolving first-letter is similar to first-line except it is always a leaf and so won't be inherited from.
+
+        (WebCore::Style::TreeResolver::makeResolutionContextForPseudoElement):
+        (WebCore::Style::TreeResolver::makeResolutionContextForInheritedFirstLine):
+        * style/StyleTreeResolver.h:
+
 2022-05-16  Zan Dobersek  <zdober...@igalia.com>
 
         [GTK][WPE] Current-context enforcement in ANGLE is expensive

Modified: trunk/Source/WebCore/rendering/RenderTextFragment.cpp (294290 => 294291)


--- trunk/Source/WebCore/rendering/RenderTextFragment.cpp	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/RenderTextFragment.cpp	2022-05-17 05:59:55 UTC (rev 294291)
@@ -69,14 +69,6 @@
     return textNode() && textNode()->hasEditableStyle();
 }
 
-void RenderTextFragment::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
-{
-    RenderText::styleDidChange(diff, oldStyle);
-
-    if (RenderBlock* block = blockForAccompanyingFirstLetter())
-        block->mutableStyle().removeCachedPseudoStyle(PseudoId::FirstLetter);
-}
-
 void RenderTextFragment::setText(const String& newText, bool force)
 {
     RenderText::setText(newText, force);

Modified: trunk/Source/WebCore/rendering/RenderTextFragment.h (294290 => 294291)


--- trunk/Source/WebCore/rendering/RenderTextFragment.h	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/RenderTextFragment.h	2022-05-17 05:59:55 UTC (rev 294291)
@@ -60,7 +60,6 @@
     
 private:
     bool isTextFragment() const override { return true; }
-    void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override;
 
     UChar previousCharacter() const override;
 

Modified: trunk/Source/WebCore/rendering/TextAutoSizing.cpp (294290 => 294291)


--- trunk/Source/WebCore/rendering/TextAutoSizing.cpp	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/TextAutoSizing.cpp	2022-05-17 05:59:55 UTC (rev 294291)
@@ -45,6 +45,11 @@
 static RenderStyle cloneRenderStyleWithState(const RenderStyle& currentStyle)
 {
     auto newStyle = RenderStyle::clone(currentStyle);
+
+    // FIXME: This should probably handle at least ::first-line too.
+    if (auto* firstLetterStyle = currentStyle.getCachedPseudoStyle(PseudoId::FirstLetter))
+        newStyle.addCachedPseudoStyle(makeUnique<RenderStyle>(RenderStyle::clone(*firstLetterStyle)));
+
     if (currentStyle.lastChildState())
         newStyle.setLastChildState();
     if (currentStyle.firstChildState())
@@ -170,6 +175,21 @@
         auto* block = downcast<RenderTextFragment>(textRenderer).blockForAccompanyingFirstLetter();
         if (!block)
             continue;
+
+        RenderObject* firstLetterRenderer;
+        RenderElement* dummy;
+        block->getFirstLetter(firstLetterRenderer, dummy);
+        if (firstLetterRenderer && firstLetterRenderer->parent() && firstLetterRenderer->parent()->parent()) {
+            auto& parentStyle = firstLetterRenderer->parent()->parent()->style();
+            auto* firstLetterStyle = parentStyle.getCachedPseudoStyle(PseudoId::FirstLetter);
+            if (!firstLetterStyle)
+                continue;
+            auto fontDescription = firstLetterStyle->fontDescription();
+            fontDescription.setComputedSize(averageSize * fontDescription.specifiedSize() / parentStyle.fontDescription().specifiedSize());
+            firstLetterStyle->setFontDescription(FontCascadeDescription { fontDescription });
+            firstLetterStyle->fontCascade().update(&node->document().fontSelector());
+        }
+
         builder.updateAfterDescendants(*block);
     }
 

Modified: trunk/Source/WebCore/rendering/style/RenderStyle.cpp (294290 => 294291)


--- trunk/Source/WebCore/rendering/style/RenderStyle.cpp	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/style/RenderStyle.cpp	2022-05-17 05:59:55 UTC (rev 294291)
@@ -115,14 +115,7 @@
 RenderStyle RenderStyle::cloneIncludingPseudoElements(const RenderStyle& style)
 {
     auto newStyle = RenderStyle(style, Clone);
-
-    if (!style.m_cachedPseudoStyles)
-        return newStyle;
-
-    for (auto& pseudoElementStyle : *style.m_cachedPseudoStyles) {
-        auto clone = makeUnique<RenderStyle>(cloneIncludingPseudoElements(*pseudoElementStyle));
-        newStyle.addCachedPseudoStyle(WTFMove(clone));
-    }
+    newStyle.copyPseudoElementsFrom(style);
     return newStyle;
 }
 
@@ -392,6 +385,15 @@
     m_rareNonInheritedData.access().content = other.m_rareNonInheritedData->content->clone();
 }
 
+void RenderStyle::copyPseudoElementsFrom(const RenderStyle& other)
+{
+    if (!other.m_cachedPseudoStyles)
+        return;
+
+    for (auto& pseudoElementStyle : *other.m_cachedPseudoStyles)
+        addCachedPseudoStyle(makeUnique<RenderStyle>(cloneIncludingPseudoElements(*pseudoElementStyle)));
+}
+
 bool RenderStyle::operator==(const RenderStyle& other) const
 {
     // compare everything except the pseudoStyle pointer
@@ -450,19 +452,6 @@
     return result;
 }
 
-void RenderStyle::removeCachedPseudoStyle(PseudoId pid)
-{
-    if (!m_cachedPseudoStyles)
-        return;
-    for (size_t i = 0; i < m_cachedPseudoStyles->size(); ++i) {
-        RenderStyle* pseudoStyle = m_cachedPseudoStyles->at(i).get();
-        if (pseudoStyle->styleType() == pid) {
-            m_cachedPseudoStyles->remove(i);
-            return;
-        }
-    }
-}
-
 bool RenderStyle::inheritedEqual(const RenderStyle& other) const
 {
     return m_inheritedFlags == other.m_inheritedFlags

Modified: trunk/Source/WebCore/rendering/style/RenderStyle.h (294290 => 294291)


--- trunk/Source/WebCore/rendering/style/RenderStyle.h	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/style/RenderStyle.h	2022-05-17 05:59:55 UTC (rev 294291)
@@ -169,6 +169,7 @@
     void fastPathInheritFrom(const RenderStyle&);
     void copyNonInheritedFrom(const RenderStyle&);
     void copyContentFrom(const RenderStyle&);
+    void copyPseudoElementsFrom(const RenderStyle&);
 
     ContentPosition resolvedJustifyContentPosition(const StyleContentAlignmentData& normalValueBehavior) const;
     ContentDistribution resolvedJustifyContentDistribution(const StyleContentAlignmentData& normalValueBehavior) const;
@@ -186,7 +187,6 @@
 
     RenderStyle* getCachedPseudoStyle(PseudoId) const;
     RenderStyle* addCachedPseudoStyle(std::unique_ptr<RenderStyle>);
-    void removeCachedPseudoStyle(PseudoId);
 
     const PseudoStyleCache* cachedPseudoStyles() const { return m_cachedPseudoStyles.get(); }
 

Modified: trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.cpp (294290 => 294291)


--- trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.cpp	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.cpp	2022-05-17 05:59:55 UTC (rev 294291)
@@ -38,12 +38,15 @@
 
 namespace WebCore {
 
-static RenderStyle styleForFirstLetter(const RenderBlock& firstLetterBlock, const RenderObject& firstLetterContainer)
+static std::optional<RenderStyle> styleForFirstLetter(const RenderElement& firstLetterContainer)
 {
-    auto* containerFirstLetterStyle = firstLetterBlock.getCachedPseudoStyle(PseudoId::FirstLetter, &firstLetterContainer.firstLineStyle());
-    // FIXME: first-letter style needs to be computed eagerly.
-    auto firstLetterStyle = RenderStyle::clone(containerFirstLetterStyle ? *containerFirstLetterStyle : firstLetterContainer.firstLineStyle());
+    auto& styleContainer = firstLetterContainer.isAnonymous() && firstLetterContainer.parent() ? *firstLetterContainer.parent() : firstLetterContainer;
+    auto style = styleContainer.style().getCachedPseudoStyle(PseudoId::FirstLetter);
+    if (!style)
+        return { };
 
+    auto firstLetterStyle = RenderStyle::clone(*style);
+
     // If we have an initial letter drop that is >= 1, then we need to force floating to be on.
     if (firstLetterStyle.initialLetterDrop() >= 1 && !firstLetterStyle.isFloating())
         firstLetterStyle.setFloating(firstLetterStyle.isLeftToRightDirection() ? Float::Left : Float::Right);
@@ -150,7 +153,7 @@
     if (!is<RenderText>(firstLetterRenderer))
         return;
 
-    createRenderers(block, downcast<RenderText>(*firstLetterRenderer));
+    createRenderers(downcast<RenderText>(*firstLetterRenderer));
 }
 
 void RenderTreeBuilder::FirstLetter::cleanupOnDestroy(RenderTextFragment& textFragment)
@@ -164,18 +167,26 @@
 {
     RenderElement* firstLetter = currentChild.parent();
     ASSERT(firstLetter->isFirstLetter());
+    if (!firstLetter || !firstLetter->parent())
+        return;
 
-    RenderElement* firstLetterContainer = firstLetter->parent();
-    auto pseudoStyle = styleForFirstLetter(firstLetterBlock, *firstLetterContainer);
+    auto& firstLetterContainer = *firstLetter->parent();
+
+    auto pseudoStyle = styleForFirstLetter(firstLetterContainer);
+    if (!pseudoStyle) {
+        m_builder.destroy(*firstLetter, CanCollapseAnonymousBlock::No);
+        return;
+    }
+
     ASSERT(firstLetter->isFloating() || firstLetter->isInline());
 
-    if (Style::determineChange(firstLetter->style(), pseudoStyle) == Style::Change::Renderer) {
+    if (Style::determineChange(firstLetter->style(), *pseudoStyle) == Style::Change::Renderer) {
         // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
         RenderPtr<RenderBoxModelObject> newFirstLetter;
-        if (pseudoStyle.display() == DisplayType::Inline)
-            newFirstLetter = createRenderer<RenderInline>(firstLetterBlock.document(), WTFMove(pseudoStyle));
+        if (pseudoStyle->display() == DisplayType::Inline)
+            newFirstLetter = createRenderer<RenderInline>(firstLetterBlock.document(), WTFMove(*pseudoStyle));
         else
-            newFirstLetter = createRenderer<RenderBlockFlow>(firstLetterBlock.document(), WTFMove(pseudoStyle));
+            newFirstLetter = createRenderer<RenderBlockFlow>(firstLetterBlock.document(), WTFMove(*pseudoStyle));
         newFirstLetter->initializeStyle();
         newFirstLetter->setIsFirstLetter();
 
@@ -195,14 +206,14 @@
         }
         WeakPtr nextSibling = firstLetter->nextSibling();
         m_builder.destroy(*firstLetter);
-        m_builder.attach(*firstLetterContainer, WTFMove(newFirstLetter), nextSibling.get());
+        m_builder.attach(firstLetterContainer, WTFMove(newFirstLetter), nextSibling.get());
         return;
     }
 
-    firstLetter->setStyle(WTFMove(pseudoStyle));
+    firstLetter->setStyle(WTFMove(*pseudoStyle));
 }
 
-void RenderTreeBuilder::FirstLetter::createRenderers(RenderBlock& firstLetterBlock, RenderText& currentTextChild)
+void RenderTreeBuilder::FirstLetter::createRenderers(RenderText& currentTextChild)
 {
     RenderElement* textContentParent = currentTextChild.parent();
     RenderElement* firstLetterContainer = nullptr;
@@ -210,12 +221,18 @@
         firstLetterContainer = wrapperInlineForDisplayContents->parent();
     else
         firstLetterContainer = textContentParent;
-    auto pseudoStyle = styleForFirstLetter(firstLetterBlock, *firstLetterContainer);
+    if (!firstLetterContainer)
+        return;
+    
+    auto pseudoStyle = styleForFirstLetter(*firstLetterContainer);
+    if (!pseudoStyle)
+        return;
+
     RenderPtr<RenderBoxModelObject> newFirstLetter;
-    if (pseudoStyle.display() == DisplayType::Inline)
-        newFirstLetter = createRenderer<RenderInline>(firstLetterBlock.document(), WTFMove(pseudoStyle));
+    if (pseudoStyle->display() == DisplayType::Inline)
+        newFirstLetter = createRenderer<RenderInline>(currentTextChild.document(), WTFMove(*pseudoStyle));
     else
-        newFirstLetter = createRenderer<RenderBlockFlow>(firstLetterBlock.document(), WTFMove(pseudoStyle));
+        newFirstLetter = createRenderer<RenderBlockFlow>(currentTextChild.document(), WTFMove(*pseudoStyle));
     newFirstLetter->initializeStyle();
     newFirstLetter->setIsFirstLetter();
 
@@ -263,7 +280,7 @@
             newRemainingText = createRenderer<RenderTextFragment>(*textNode, oldText, length, oldText.length() - length);
             textNode->setRenderer(newRemainingText.get());
         } else
-            newRemainingText = createRenderer<RenderTextFragment>(firstLetterBlock.document(), oldText, length, oldText.length() - length);
+            newRemainingText = createRenderer<RenderTextFragment>(m_builder.m_view.document(), oldText, length, oldText.length() - length);
 
         RenderTextFragment& remainingText = *newRemainingText;
         ASSERT_UNUSED(hasInlineWrapperForDisplayContents, hasInlineWrapperForDisplayContents == inlineWrapperForDisplayContents.get());
@@ -277,7 +294,7 @@
         m_builder.attach(*firstLetterContainer, WTFMove(newFirstLetter), &remainingText);
 
         // Construct text fragment for the first letter.
-        auto letter = createRenderer<RenderTextFragment>(firstLetterBlock.document(), oldText, 0, length);
+        auto letter = createRenderer<RenderTextFragment>(m_builder.m_view.document(), oldText, 0, length);
         m_builder.attach(firstLetter, WTFMove(letter));
     }
 }

Modified: trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.h (294290 => 294291)


--- trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.h	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.h	2022-05-17 05:59:55 UTC (rev 294291)
@@ -42,7 +42,7 @@
 
 private:
     void updateStyle(RenderBlock& firstLetterBlock, RenderObject& currentChild);
-    void createRenderers(RenderBlock& firstLetterBlock, RenderText& currentTextChild);
+    void createRenderers(RenderText& currentTextChild);
 
     RenderTreeBuilder& m_builder;
 };

Modified: trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp (294290 => 294291)


--- trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp	2022-05-17 05:59:55 UTC (rev 294291)
@@ -144,6 +144,7 @@
         contentsStyle->setStyleType(pseudoId);
         contentsStyle->inheritFrom(*updateStyle);
         contentsStyle->copyContentFrom(*updateStyle);
+        contentsStyle->copyPseudoElementsFrom(*updateStyle);
 
         Style::ElementUpdate contentsUpdate { WTFMove(contentsStyle), styleChange, elementUpdate.recompositeLayer };
         m_updater.updateElementRenderer(*pseudoElement, WTFMove(contentsUpdate));

Modified: trunk/Source/WebCore/style/StyleTreeResolver.cpp (294290 => 294291)


--- trunk/Source/WebCore/style/StyleTreeResolver.cpp	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/style/StyleTreeResolver.cpp	2022-05-17 05:59:55 UTC (rev 294291)
@@ -273,6 +273,8 @@
     
     if (resolveAndAddPseudoElementStyle(PseudoId::FirstLine) != Change::None)
         descendantsToResolve = DescendantsToResolve::All;
+    if (resolveAndAddPseudoElementStyle(PseudoId::FirstLetter) != Change::None)
+        descendantsToResolve = DescendantsToResolve::All;
 
     resolveAndAddPseudoElementStyle(PseudoId::Marker);
     resolveAndAddPseudoElementStyle(PseudoId::Before);
@@ -292,7 +294,7 @@
     return { WTFMove(update), descendantsToResolve };
 }
 
-inline bool supportsFirstLinePseudoElement(const RenderStyle& style)
+inline bool supportsFirstLineAndLetterPseudoElement(const RenderStyle& style)
 {
     auto display = style.display();
     return display == DisplayType::Block
@@ -311,53 +313,93 @@
         return { };
     if (pseudoId == PseudoId::FirstLine && !scope().resolver->usesFirstLineRules())
         return { };
+    if (pseudoId == PseudoId::FirstLetter && !scope().resolver->usesFirstLetterRules())
+        return { };
     if (elementUpdate.style->display() == DisplayType::None)
         return { };
 
-    if (!elementUpdate.style->hasPseudoStyle(pseudoId)) {
-        if (pseudoId == PseudoId::FirstLine) {
-            auto firstLineStyle = resolveInheritedFirstLinePseudoElement(element, elementUpdate);
-            if (!firstLineStyle)
-                return { };
+    if (!elementUpdate.style->hasPseudoStyle(pseudoId))
+        return resolveAncestorPseudoElement(element, pseudoId, elementUpdate);
 
-            auto* oldStyle = element.renderOrDisplayContentsStyle(PseudoId::FirstLine);
-            auto change = oldStyle ? determineChange(*oldStyle, *firstLineStyle) : Change::Renderer;
-            return ElementUpdate { WTFMove(firstLineStyle), change };
-        }
+    if ((pseudoId == PseudoId::FirstLine || pseudoId == PseudoId::FirstLetter) && !supportsFirstLineAndLetterPseudoElement(*elementUpdate.style))
         return { };
-    }
 
-    if (pseudoId == PseudoId::FirstLine && !supportsFirstLinePseudoElement(*elementUpdate.style))
-        return { };
+    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, pseudoId);
 
-    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate);
-
     auto pseudoStyle = scope().resolver->pseudoStyleForElement(element, { pseudoId }, resolutionContext);
     if (!pseudoStyle)
         return { };
 
     // FIXME: This test shouldn't be needed.
-    bool hasAnimations = pseudoStyle->hasAnimationsOrTransitions() || element.hasKeyframeEffects(pseudoId);
-    if (pseudoId != PseudoId::FirstLine && !pseudoElementRendererIsNeeded(pseudoStyle.get()) && !hasAnimations)
+    bool alwaysNeedsPseudoElement = pseudoStyle->hasAnimationsOrTransitions()
+        || element.hasKeyframeEffects(pseudoId)
+        || pseudoId == PseudoId::FirstLine
+        || pseudoId == PseudoId::FirstLetter;
+    if (!alwaysNeedsPseudoElement && !pseudoElementRendererIsNeeded(pseudoStyle.get()))
         return { };
 
     auto animatedUpdate = createAnimatedElementUpdate(WTFMove(pseudoStyle), { element, pseudoId }, elementUpdate.change, resolutionContext);
 
     if (pseudoId == PseudoId::Before || pseudoId == PseudoId::After) {
-        // ::first-line can inherit to ::before/::after
-        auto firstLineContext = makeResolutionContextForInheritedFirstLine(elementUpdate, *elementUpdate.style);
-        if (firstLineContext) {
-            auto firstLineStyle = scope().resolver->pseudoStyleForElement(element, { pseudoId }, *firstLineContext);
-            firstLineStyle->setStyleType(PseudoId::FirstLine);
-            animatedUpdate.style->addCachedPseudoStyle(WTFMove(firstLineStyle));
+        if (scope().resolver->usesFirstLineRules()) {
+            // ::first-line can inherit to ::before/::after
+            if (auto firstLineContext = makeResolutionContextForInheritedFirstLine(elementUpdate, *elementUpdate.style)) {
+                auto firstLineStyle = scope().resolver->pseudoStyleForElement(element, { pseudoId }, *firstLineContext);
+                firstLineStyle->setStyleType(PseudoId::FirstLine);
+                animatedUpdate.style->addCachedPseudoStyle(WTFMove(firstLineStyle));
+            }
         }
+        if (scope().resolver->usesFirstLetterRules()) {
+            auto beforeAfterContext = makeResolutionContextForPseudoElement(animatedUpdate, PseudoId::FirstLetter);
+            if (auto firstLetterStyle = resolveAncestorFirstLetterPseudoElement(element, elementUpdate, beforeAfterContext))
+                animatedUpdate.style->addCachedPseudoStyle(WTFMove(firstLetterStyle));
+        }
     }
 
     return animatedUpdate;
 }
 
-std::unique_ptr<RenderStyle> TreeResolver::resolveInheritedFirstLinePseudoElement(Element& element, const ElementUpdate& elementUpdate)
+std::optional<ElementUpdate> TreeResolver::resolveAncestorPseudoElement(Element& element, PseudoId pseudoId, const ElementUpdate& elementUpdate)
 {
+    ASSERT(!elementUpdate.style->hasPseudoStyle(pseudoId));
+
+    auto pseudoElementStyle = [&]() -> std::unique_ptr<RenderStyle> {
+        // ::first-line and ::first-letter defined on an ancestor element may need to be resolved for the current element.
+        if (pseudoId == PseudoId::FirstLine)
+            return resolveAncestorFirstLinePseudoElement(element, elementUpdate);
+        if (pseudoId == PseudoId::FirstLetter) {
+            auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, PseudoId::FirstLetter);
+            return resolveAncestorFirstLetterPseudoElement(element, elementUpdate, resolutionContext);
+        }
+        return nullptr;
+    }();
+
+    if (!pseudoElementStyle)
+        return { };
+
+    auto* oldStyle = element.renderOrDisplayContentsStyle(pseudoId);
+    auto change = oldStyle ? determineChange(*oldStyle, *pseudoElementStyle) : Change::Renderer;
+    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, pseudoId);
+
+    return createAnimatedElementUpdate(WTFMove(pseudoElementStyle), { element, pseudoId }, change, resolutionContext);
+}
+
+static bool isChildInBlockFormattingContext(const RenderStyle& style)
+{
+    // FIXME: Incomplete. There should be shared code with layout for this.
+    if (style.display() != DisplayType::Block && style.display() != DisplayType::ListItem)
+        return false;
+    if (style.hasOutOfFlowPosition())
+        return false;
+    if (style.floating() != Float::None)
+        return false;
+    if (style.overflowX() != Overflow::Visible || style.overflowY() != Overflow::Visible)
+        return false;
+    return true;
+};
+
+std::unique_ptr<RenderStyle> TreeResolver::resolveAncestorFirstLinePseudoElement(Element& element, const ElementUpdate& elementUpdate)
+{
     if (elementUpdate.style->display() == DisplayType::Inline) {
         auto* parent = boxGeneratingParent();
         if (!parent)
@@ -374,31 +416,18 @@
         return firstLineStyle;
     }
 
-    auto isChildInBlockFormattingContext = [](const RenderStyle& style) {
-        // FIXME: Incomplete. There should be shared code with layout for this.
-        if (style.display() != DisplayType::Block)
-            return false;
-        if (style.hasOutOfFlowPosition())
-            return false;
-        if (style.floating() != Float::None)
-            return false;
-        if (style.overflowX() != Overflow::Visible || style.overflowY() != Overflow::Visible)
-            return false;
-        return true;
-    };
-
-    auto firstLineElementForBlock = [&]() -> Element* {
+    auto findFirstLineElementForBlock = [&]() -> Element* {
         if (!isChildInBlockFormattingContext(*elementUpdate.style))
             return nullptr;
 
         // ::first-line is only propagated to the first block.
-        if (parent().resolvedFirstBoxGeneratingChild)
+        if (parent().resolvedFirstLineAndLetterChild)
             return nullptr;
 
         for (auto& parent : makeReversedRange(m_parentStack)) {
             if (parent.style.display() == DisplayType::Contents)
                 continue;
-            if (!supportsFirstLinePseudoElement(parent.style))
+            if (!supportsFirstLineAndLetterPseudoElement(parent.style))
                 return nullptr;
             if (parent.style.hasPseudoStyle(PseudoId::FirstLine))
                 return parent.element;
@@ -408,11 +437,11 @@
         return nullptr;
     };
 
-    auto firstLineElement = firstLineElementForBlock();
+    auto firstLineElement = findFirstLineElementForBlock();
     if (!firstLineElement)
         return { };
 
-    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate);
+    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, PseudoId::FirstLine);
     // Can't use the cached state since the element being resolved is not the current one.
     resolutionContext.selectorMatchingState = nullptr;
 
@@ -419,6 +448,47 @@
     return scope().resolver->pseudoStyleForElement(*firstLineElement, { PseudoId::FirstLine }, resolutionContext);
 }
 
+std::unique_ptr<RenderStyle> TreeResolver::resolveAncestorFirstLetterPseudoElement(Element& element, const ElementUpdate& elementUpdate, ResolutionContext& resolutionContext)
+{
+    auto findFirstLetterElement = [&]() -> Element* {
+        if (elementUpdate.style->hasPseudoStyle(PseudoId::FirstLetter) && supportsFirstLineAndLetterPseudoElement(*elementUpdate.style))
+            return &element;
+
+        // ::first-letter is only propagated to the first box.
+        if (parent().resolvedFirstLineAndLetterChild)
+            return nullptr;
+
+        bool skipInlines = elementUpdate.style->display() == DisplayType::Inline;
+        if (!skipInlines && !isChildInBlockFormattingContext(*elementUpdate.style))
+            return nullptr;
+
+        for (auto& parent : makeReversedRange(m_parentStack)) {
+            if (parent.style.display() == DisplayType::Contents)
+                continue;
+            if (skipInlines && parent.style.display() == DisplayType::Inline)
+                continue;
+            skipInlines = false;
+
+            if (!supportsFirstLineAndLetterPseudoElement(parent.style))
+                return nullptr;
+            if (parent.style.hasPseudoStyle(PseudoId::FirstLetter))
+                return parent.element;
+            if (!isChildInBlockFormattingContext(parent.style))
+                return nullptr;
+        }
+        return nullptr;
+    };
+
+    auto firstLetterElement = findFirstLetterElement();
+    if (!firstLetterElement)
+        return { };
+
+    // Can't use the cached state since the element being resolved is not the current one.
+    resolutionContext.selectorMatchingState = nullptr;
+
+    return scope().resolver->pseudoStyleForElement(*firstLetterElement, { PseudoId::FirstLetter }, resolutionContext);
+}
+
 ResolutionContext TreeResolver::makeResolutionContext()
 {
     return {
@@ -429,10 +499,18 @@
     };
 }
 
-ResolutionContext TreeResolver::makeResolutionContextForPseudoElement(const ElementUpdate& elementUpdate)
+ResolutionContext TreeResolver::makeResolutionContextForPseudoElement(const ElementUpdate& elementUpdate, PseudoId pseudoId)
 {
+    auto parentStyle = [&] {
+        if (pseudoId == PseudoId::FirstLetter) {
+            if (auto* firstLineStyle = elementUpdate.style->getCachedPseudoStyle(PseudoId::FirstLine))
+                return firstLineStyle;
+        }
+        return elementUpdate.style.get();
+    };
+
     return {
-        elementUpdate.style.get(),
+        parentStyle(),
         parentBoxStyleForPseudoElement(elementUpdate),
         m_documentElementStyle.get(),
         &scope().selectorMatchingState
@@ -441,9 +519,6 @@
 
 std::optional<ResolutionContext> TreeResolver::makeResolutionContextForInheritedFirstLine(const ElementUpdate& elementUpdate, const RenderStyle& inheritStyle)
 {
-    if (!scope().resolver->usesFirstLineRules())
-        return { };
-
     auto parentFirstLineStyle = inheritStyle.getCachedPseudoStyle(PseudoId::FirstLine);
     if (!parentFirstLineStyle)
         return { };
@@ -705,7 +780,7 @@
             }
 
             if (!text.data().isAllSpecialCharacters<isHTMLSpace>())
-                parent.resolvedFirstBoxGeneratingChild = true;
+                parent.resolvedFirstLineAndLetterChild = true;
 
             text.setHasValidStyle();
             it.traverseNextSkippingChildren();
@@ -762,8 +837,8 @@
         if (!m_didSeePendingStylesheet)
             m_didSeePendingStylesheet = hasLoadingStylesheet(m_document.styleScope(), element, !shouldIterateChildren);
 
-        if (style && generatesBox(*style))
-            parent.resolvedFirstBoxGeneratingChild = true;
+        if (!parent.resolvedFirstLineAndLetterChild && style && generatesBox(*style) && supportsFirstLineAndLetterPseudoElement(*style))
+            parent.resolvedFirstLineAndLetterChild = true;
 
         if (!shouldIterateChildren) {
             it.traverseNextSkippingChildren();

Modified: trunk/Source/WebCore/style/StyleTreeResolver.h (294290 => 294291)


--- trunk/Source/WebCore/style/StyleTreeResolver.h	2022-05-17 05:52:04 UTC (rev 294290)
+++ trunk/Source/WebCore/style/StyleTreeResolver.h	2022-05-17 05:59:55 UTC (rev 294291)
@@ -71,7 +71,9 @@
 
     static ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, const Styleable&, Change, const ResolutionContext&);
     std::optional<ElementUpdate> resolvePseudoElement(Element&, PseudoId, const ElementUpdate&);
-    std::unique_ptr<RenderStyle> resolveInheritedFirstLinePseudoElement(Element&, const ElementUpdate&);
+    std::optional<ElementUpdate> resolveAncestorPseudoElement(Element&, PseudoId, const ElementUpdate&);
+    std::unique_ptr<RenderStyle> resolveAncestorFirstLinePseudoElement(Element&, const ElementUpdate&);
+    std::unique_ptr<RenderStyle> resolveAncestorFirstLetterPseudoElement(Element&, const ElementUpdate&, ResolutionContext&);
 
     struct Scope : RefCounted<Scope> {
         WTF_MAKE_STRUCT_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(TreeResolverScope);
@@ -92,7 +94,7 @@
         Change change { Change::None };
         DescendantsToResolve descendantsToResolve { DescendantsToResolve::None };
         bool didPushScope { false };
-        bool resolvedFirstBoxGeneratingChild { false };
+        bool resolvedFirstLineAndLetterChild { false };
 
         Parent(Document&);
         Parent(Element&, const RenderStyle&, Change, DescendantsToResolve);
@@ -114,7 +116,7 @@
     static void resetDescendantStyleRelations(Element&, DescendantsToResolve);
 
     ResolutionContext makeResolutionContext();
-    ResolutionContext makeResolutionContextForPseudoElement(const ElementUpdate&);
+    ResolutionContext makeResolutionContextForPseudoElement(const ElementUpdate&, PseudoId);
     std::optional<ResolutionContext> makeResolutionContextForInheritedFirstLine(const ElementUpdate&, const RenderStyle& inheritStyle);
     const Parent* boxGeneratingParent() const;
     const RenderStyle* parentBoxStyle() const;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to