⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 294291 in webkit


Ignore:
Timestamp:
May 16, 2022, 10:59:55 PM (4 years ago)
Author:
Antti Koivisto
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.
Location:
trunk
Files:
1 added
11 deleted
14 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r294283 r294291  
     12022-05-16  Antti Koivisto  <antti@apple.com>
     2
     3        Resolve ::first-letter eagerly
     4        https://bugs.webkit.org/show_bug.cgi?id=239844
     5
     6        Reviewed by Alan Bujtas.
     7
     8        * fast/css-generated-content/first-letter-in-nested-before-expected.html: Added.
     9        * fast/css-generated-content/first-letter-in-nested-before-expected.png: Removed.
     10        * fast/css-generated-content/first-letter-in-nested-before-expected.txt: Removed.
     11        * 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.
     12        * fast/css-generated-content/first-letter-in-nested-before-table-expected.png: Removed.
     13        * fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
     14        * fast/css-generated-content/first-letter-in-nested-before-table.html:
     15        * fast/css-generated-content/first-letter-in-nested-before.html:
     16
     17        Make reftest and update the behavior. ::first-letter won't affect ::before/after with display:table,
     18        matching Firefox behavior (and spec logic).
     19
     20        * platform/glib/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
     21        * platform/gtk/fast/css-generated-content/first-letter-in-nested-before-expected.png: Removed.
     22        * platform/gtk/fast/css-generated-content/first-letter-in-nested-before-table-expected.png: Removed.
     23        * platform/ios/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
     24        * platform/mac/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
     25        * platform/win/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
     26        * platform/wincairo/fast/css-generated-content/first-letter-in-nested-before-table-expected.txt: Removed.
     27
    1282022-05-16  Karl Rackler  <rackler@apple.com>
    229
  • trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table-expected.html

    r294290 r294291  
    11<!DOCTYPE html>
    2 <!-- No text in black, B and F should be blue. -->
    32<html>
    43<body style="font-family: ahem; font-size: 50px; -webkit-font-smoothing: none;">
     
    1211    content: "EF";
    1312}
    14 .test1:first-letter { color: green; }
    15 .test2:first-letter { color: green; }
    1613</style>
    1714<div class="test1">C</div>
    18 <div class="test2">D</div>
     15<div class="test2"><span style="color:green">D</span></div>
    1916<div class="test2"></div>
    2017<script>
  • trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before-table.html

    r97124 r294291  
    11<!DOCTYPE html>
    2 <!-- No text in black, B and F should be blue. -->
    32<html>
    43<body style="font-family: ahem; font-size: 50px; -webkit-font-smoothing: none;">
  • trunk/LayoutTests/fast/css-generated-content/first-letter-in-nested-before.html

    r96427 r294291  
    11<style>
    22    #target { color: green; margin-top: -100px; }
    3     #target:before { display: table; content: "A"; }
     3    #target:before { display: block; content: "A"; }
    44    #target:first-letter { font-size: 200%; }
    55</style>
  • trunk/Source/WebCore/ChangeLog

    r294290 r294291  
     12022-05-16  Antti Koivisto  <antti@apple.com>
     2
     3        Resolve ::first-letter eagerly
     4        https://bugs.webkit.org/show_bug.cgi?id=239844
     5
     6        Reviewed by Alan Bujtas.
     7
     8        ::first-letter pseudo-element should be resolved eagerly during style resolution, similar to ::first-line.
     9        This will clean up the architecture. It also makes ::first-letter animatable.
     10
     11        * rendering/RenderTextFragment.cpp:
     12        (WebCore::RenderTextFragment::styleDidChange): Deleted.
     13        * rendering/RenderTextFragment.h:
     14        * rendering/style/RenderStyle.cpp:
     15        (WebCore::RenderStyle::removeCachedPseudoStyle): Deleted.
     16
     17        We always create a new style. There is no need to removed pseudo element styles.
     18
     19        * rendering/style/RenderStyle.h:
     20        * rendering/updating/RenderTreeBuilderFirstLetter.cpp:
     21        (WebCore::styleForFirstLetter):
     22
     23        Pull first-letter style from the parent style instead of resolving it in the middle of render tree building.
     24
     25        (WebCore::RenderTreeBuilder::FirstLetter::updateAfterDescendants):
     26        (WebCore::RenderTreeBuilder::FirstLetter::updateStyle):
     27
     28        Delete the renderer if we no longer have a style.
     29
     30        (WebCore::RenderTreeBuilder::FirstLetter::createRenderers):
     31        * rendering/updating/RenderTreeBuilderFirstLetter.h:
     32        * style/StyleTreeResolver.cpp:
     33        (WebCore::Style::TreeResolver::resolveElement):
     34
     35        Resove first-letter.
     36
     37        (WebCore::Style::TreeResolver::resolvePseudoElement):
     38        (WebCore::Style::TreeResolver::resolveInheritedPseudoElement):
     39
     40        Factor into a function.
     41
     42        (WebCore::Style::isChildInBlockFormattingContext):
     43        (WebCore::Style::TreeResolver::resolveAncestorFirstLinePseudoElement):
     44        (WebCore::Style::TreeResolver::resolveAncestorFirstLetterPseudoElement):
     45
     46        Resolving first-letter is similar to first-line except it is always a leaf and so won't be inherited from.
     47
     48        (WebCore::Style::TreeResolver::makeResolutionContextForPseudoElement):
     49        (WebCore::Style::TreeResolver::makeResolutionContextForInheritedFirstLine):
     50        * style/StyleTreeResolver.h:
     51
    1522022-05-16  Zan Dobersek  <zdobersek@igalia.com>
    253
  • trunk/Source/WebCore/rendering/RenderTextFragment.cpp

    r232178 r294291  
    7070}
    7171
    72 void RenderTextFragment::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    73 {
    74     RenderText::styleDidChange(diff, oldStyle);
    75 
    76     if (RenderBlock* block = blockForAccompanyingFirstLetter())
    77         block->mutableStyle().removeCachedPseudoStyle(PseudoId::FirstLetter);
    78 }
    79 
    8072void RenderTextFragment::setText(const String& newText, bool force)
    8173{
  • trunk/Source/WebCore/rendering/RenderTextFragment.h

    r284093 r294291  
    6161private:
    6262    bool isTextFragment() const override { return true; }
    63     void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override;
    6463
    6564    UChar previousCharacter() const override;
  • trunk/Source/WebCore/rendering/TextAutoSizing.cpp

    r272805 r294291  
    4646{
    4747    auto newStyle = RenderStyle::clone(currentStyle);
     48
     49    // FIXME: This should probably handle at least ::first-line too.
     50    if (auto* firstLetterStyle = currentStyle.getCachedPseudoStyle(PseudoId::FirstLetter))
     51        newStyle.addCachedPseudoStyle(makeUnique<RenderStyle>(RenderStyle::clone(*firstLetterStyle)));
     52
    4853    if (currentStyle.lastChildState())
    4954        newStyle.setLastChildState();
     
    171176        if (!block)
    172177            continue;
     178
     179        RenderObject* firstLetterRenderer;
     180        RenderElement* dummy;
     181        block->getFirstLetter(firstLetterRenderer, dummy);
     182        if (firstLetterRenderer && firstLetterRenderer->parent() && firstLetterRenderer->parent()->parent()) {
     183            auto& parentStyle = firstLetterRenderer->parent()->parent()->style();
     184            auto* firstLetterStyle = parentStyle.getCachedPseudoStyle(PseudoId::FirstLetter);
     185            if (!firstLetterStyle)
     186                continue;
     187            auto fontDescription = firstLetterStyle->fontDescription();
     188            fontDescription.setComputedSize(averageSize * fontDescription.specifiedSize() / parentStyle.fontDescription().specifiedSize());
     189            firstLetterStyle->setFontDescription(FontCascadeDescription { fontDescription });
     190            firstLetterStyle->fontCascade().update(&node->document().fontSelector());
     191        }
     192
    173193        builder.updateAfterDescendants(*block);
    174194    }
  • trunk/Source/WebCore/rendering/style/RenderStyle.cpp

    r294195 r294291  
    116116{
    117117    auto newStyle = RenderStyle(style, Clone);
    118 
    119     if (!style.m_cachedPseudoStyles)
    120         return newStyle;
    121 
    122     for (auto& pseudoElementStyle : *style.m_cachedPseudoStyles) {
    123         auto clone = makeUnique<RenderStyle>(cloneIncludingPseudoElements(*pseudoElementStyle));
    124         newStyle.addCachedPseudoStyle(WTFMove(clone));
    125     }
     118    newStyle.copyPseudoElementsFrom(style);
    126119    return newStyle;
    127120}
     
    393386}
    394387
     388void RenderStyle::copyPseudoElementsFrom(const RenderStyle& other)
     389{
     390    if (!other.m_cachedPseudoStyles)
     391        return;
     392
     393    for (auto& pseudoElementStyle : *other.m_cachedPseudoStyles)
     394        addCachedPseudoStyle(makeUnique<RenderStyle>(cloneIncludingPseudoElements(*pseudoElementStyle)));
     395}
     396
    395397bool RenderStyle::operator==(const RenderStyle& other) const
    396398{
     
    449451
    450452    return result;
    451 }
    452 
    453 void RenderStyle::removeCachedPseudoStyle(PseudoId pid)
    454 {
    455     if (!m_cachedPseudoStyles)
    456         return;
    457     for (size_t i = 0; i < m_cachedPseudoStyles->size(); ++i) {
    458         RenderStyle* pseudoStyle = m_cachedPseudoStyles->at(i).get();
    459         if (pseudoStyle->styleType() == pid) {
    460             m_cachedPseudoStyles->remove(i);
    461             return;
    462         }
    463     }
    464453}
    465454
  • trunk/Source/WebCore/rendering/style/RenderStyle.h

    r294195 r294291  
    170170    void copyNonInheritedFrom(const RenderStyle&);
    171171    void copyContentFrom(const RenderStyle&);
     172    void copyPseudoElementsFrom(const RenderStyle&);
    172173
    173174    ContentPosition resolvedJustifyContentPosition(const StyleContentAlignmentData& normalValueBehavior) const;
     
    187188    RenderStyle* getCachedPseudoStyle(PseudoId) const;
    188189    RenderStyle* addCachedPseudoStyle(std::unique_ptr<RenderStyle>);
    189     void removeCachedPseudoStyle(PseudoId);
    190190
    191191    const PseudoStyleCache* cachedPseudoStyles() const { return m_cachedPseudoStyles.get(); }
  • trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.cpp

    r290205 r294291  
    3939namespace WebCore {
    4040
    41 static RenderStyle styleForFirstLetter(const RenderBlock& firstLetterBlock, const RenderObject& firstLetterContainer)
    42 {
    43     auto* containerFirstLetterStyle = firstLetterBlock.getCachedPseudoStyle(PseudoId::FirstLetter, &firstLetterContainer.firstLineStyle());
    44     // FIXME: first-letter style needs to be computed eagerly.
    45     auto firstLetterStyle = RenderStyle::clone(containerFirstLetterStyle ? *containerFirstLetterStyle : firstLetterContainer.firstLineStyle());
     41static std::optional<RenderStyle> styleForFirstLetter(const RenderElement& firstLetterContainer)
     42{
     43    auto& styleContainer = firstLetterContainer.isAnonymous() && firstLetterContainer.parent() ? *firstLetterContainer.parent() : firstLetterContainer;
     44    auto style = styleContainer.style().getCachedPseudoStyle(PseudoId::FirstLetter);
     45    if (!style)
     46        return { };
     47
     48    auto firstLetterStyle = RenderStyle::clone(*style);
    4649
    4750    // If we have an initial letter drop that is >= 1, then we need to force floating to be on.
     
    151154        return;
    152155
    153     createRenderers(block, downcast<RenderText>(*firstLetterRenderer));
     156    createRenderers(downcast<RenderText>(*firstLetterRenderer));
    154157}
    155158
     
    165168    RenderElement* firstLetter = currentChild.parent();
    166169    ASSERT(firstLetter->isFirstLetter());
    167 
    168     RenderElement* firstLetterContainer = firstLetter->parent();
    169     auto pseudoStyle = styleForFirstLetter(firstLetterBlock, *firstLetterContainer);
     170    if (!firstLetter || !firstLetter->parent())
     171        return;
     172
     173    auto& firstLetterContainer = *firstLetter->parent();
     174
     175    auto pseudoStyle = styleForFirstLetter(firstLetterContainer);
     176    if (!pseudoStyle) {
     177        m_builder.destroy(*firstLetter, CanCollapseAnonymousBlock::No);
     178        return;
     179    }
     180
    170181    ASSERT(firstLetter->isFloating() || firstLetter->isInline());
    171182
    172     if (Style::determineChange(firstLetter->style(), pseudoStyle) == Style::Change::Renderer) {
     183    if (Style::determineChange(firstLetter->style(), *pseudoStyle) == Style::Change::Renderer) {
    173184        // The first-letter renderer needs to be replaced. Create a new renderer of the right type.
    174185        RenderPtr<RenderBoxModelObject> newFirstLetter;
    175         if (pseudoStyle.display() == DisplayType::Inline)
    176             newFirstLetter = createRenderer<RenderInline>(firstLetterBlock.document(), WTFMove(pseudoStyle));
     186        if (pseudoStyle->display() == DisplayType::Inline)
     187            newFirstLetter = createRenderer<RenderInline>(firstLetterBlock.document(), WTFMove(*pseudoStyle));
    177188        else
    178             newFirstLetter = createRenderer<RenderBlockFlow>(firstLetterBlock.document(), WTFMove(pseudoStyle));
     189            newFirstLetter = createRenderer<RenderBlockFlow>(firstLetterBlock.document(), WTFMove(*pseudoStyle));
    179190        newFirstLetter->initializeStyle();
    180191        newFirstLetter->setIsFirstLetter();
     
    196207        WeakPtr nextSibling = firstLetter->nextSibling();
    197208        m_builder.destroy(*firstLetter);
    198         m_builder.attach(*firstLetterContainer, WTFMove(newFirstLetter), nextSibling.get());
    199         return;
    200     }
    201 
    202     firstLetter->setStyle(WTFMove(pseudoStyle));
    203 }
    204 
    205 void RenderTreeBuilder::FirstLetter::createRenderers(RenderBlock& firstLetterBlock, RenderText& currentTextChild)
     209        m_builder.attach(firstLetterContainer, WTFMove(newFirstLetter), nextSibling.get());
     210        return;
     211    }
     212
     213    firstLetter->setStyle(WTFMove(*pseudoStyle));
     214}
     215
     216void RenderTreeBuilder::FirstLetter::createRenderers(RenderText& currentTextChild)
    206217{
    207218    RenderElement* textContentParent = currentTextChild.parent();
     
    211222    else
    212223        firstLetterContainer = textContentParent;
    213     auto pseudoStyle = styleForFirstLetter(firstLetterBlock, *firstLetterContainer);
     224    if (!firstLetterContainer)
     225        return;
     226   
     227    auto pseudoStyle = styleForFirstLetter(*firstLetterContainer);
     228    if (!pseudoStyle)
     229        return;
     230
    214231    RenderPtr<RenderBoxModelObject> newFirstLetter;
    215     if (pseudoStyle.display() == DisplayType::Inline)
    216         newFirstLetter = createRenderer<RenderInline>(firstLetterBlock.document(), WTFMove(pseudoStyle));
     232    if (pseudoStyle->display() == DisplayType::Inline)
     233        newFirstLetter = createRenderer<RenderInline>(currentTextChild.document(), WTFMove(*pseudoStyle));
    217234    else
    218         newFirstLetter = createRenderer<RenderBlockFlow>(firstLetterBlock.document(), WTFMove(pseudoStyle));
     235        newFirstLetter = createRenderer<RenderBlockFlow>(currentTextChild.document(), WTFMove(*pseudoStyle));
    219236    newFirstLetter->initializeStyle();
    220237    newFirstLetter->setIsFirstLetter();
     
    264281            textNode->setRenderer(newRemainingText.get());
    265282        } else
    266             newRemainingText = createRenderer<RenderTextFragment>(firstLetterBlock.document(), oldText, length, oldText.length() - length);
     283            newRemainingText = createRenderer<RenderTextFragment>(m_builder.m_view.document(), oldText, length, oldText.length() - length);
    267284
    268285        RenderTextFragment& remainingText = *newRemainingText;
     
    278295
    279296        // Construct text fragment for the first letter.
    280         auto letter = createRenderer<RenderTextFragment>(firstLetterBlock.document(), oldText, 0, length);
     297        auto letter = createRenderer<RenderTextFragment>(m_builder.m_view.document(), oldText, 0, length);
    281298        m_builder.attach(firstLetter, WTFMove(letter));
    282299    }
  • trunk/Source/WebCore/rendering/updating/RenderTreeBuilderFirstLetter.h

    r253290 r294291  
    4343private:
    4444    void updateStyle(RenderBlock& firstLetterBlock, RenderObject& currentChild);
    45     void createRenderers(RenderBlock& firstLetterBlock, RenderText& currentTextChild);
     45    void createRenderers(RenderText& currentTextChild);
    4646
    4747    RenderTreeBuilder& m_builder;
  • trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp

    r290867 r294291  
    145145        contentsStyle->inheritFrom(*updateStyle);
    146146        contentsStyle->copyContentFrom(*updateStyle);
     147        contentsStyle->copyPseudoElementsFrom(*updateStyle);
    147148
    148149        Style::ElementUpdate contentsUpdate { WTFMove(contentsStyle), styleChange, elementUpdate.recompositeLayer };
  • trunk/Source/WebCore/style/StyleTreeResolver.cpp

    r293956 r294291  
    274274    if (resolveAndAddPseudoElementStyle(PseudoId::FirstLine) != Change::None)
    275275        descendantsToResolve = DescendantsToResolve::All;
     276    if (resolveAndAddPseudoElementStyle(PseudoId::FirstLetter) != Change::None)
     277        descendantsToResolve = DescendantsToResolve::All;
    276278
    277279    resolveAndAddPseudoElementStyle(PseudoId::Marker);
     
    293295}
    294296
    295 inline bool supportsFirstLinePseudoElement(const RenderStyle& style)
     297inline bool supportsFirstLineAndLetterPseudoElement(const RenderStyle& style)
    296298{
    297299    auto display = style.display();
     
    312314    if (pseudoId == PseudoId::FirstLine && !scope().resolver->usesFirstLineRules())
    313315        return { };
     316    if (pseudoId == PseudoId::FirstLetter && !scope().resolver->usesFirstLetterRules())
     317        return { };
    314318    if (elementUpdate.style->display() == DisplayType::None)
    315319        return { };
    316320
    317     if (!elementUpdate.style->hasPseudoStyle(pseudoId)) {
    318         if (pseudoId == PseudoId::FirstLine) {
    319             auto firstLineStyle = resolveInheritedFirstLinePseudoElement(element, elementUpdate);
    320             if (!firstLineStyle)
    321                 return { };
    322 
    323             auto* oldStyle = element.renderOrDisplayContentsStyle(PseudoId::FirstLine);
    324             auto change = oldStyle ? determineChange(*oldStyle, *firstLineStyle) : Change::Renderer;
    325             return ElementUpdate { WTFMove(firstLineStyle), change };
    326         }
    327         return { };
    328     }
    329 
    330     if (pseudoId == PseudoId::FirstLine && !supportsFirstLinePseudoElement(*elementUpdate.style))
    331         return { };
    332 
    333     auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate);
     321    if (!elementUpdate.style->hasPseudoStyle(pseudoId))
     322        return resolveAncestorPseudoElement(element, pseudoId, elementUpdate);
     323
     324    if ((pseudoId == PseudoId::FirstLine || pseudoId == PseudoId::FirstLetter) && !supportsFirstLineAndLetterPseudoElement(*elementUpdate.style))
     325        return { };
     326
     327    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, pseudoId);
    334328
    335329    auto pseudoStyle = scope().resolver->pseudoStyleForElement(element, { pseudoId }, resolutionContext);
     
    338332
    339333    // FIXME: This test shouldn't be needed.
    340     bool hasAnimations = pseudoStyle->hasAnimationsOrTransitions() || element.hasKeyframeEffects(pseudoId);
    341     if (pseudoId != PseudoId::FirstLine && !pseudoElementRendererIsNeeded(pseudoStyle.get()) && !hasAnimations)
     334    bool alwaysNeedsPseudoElement = pseudoStyle->hasAnimationsOrTransitions()
     335        || element.hasKeyframeEffects(pseudoId)
     336        || pseudoId == PseudoId::FirstLine
     337        || pseudoId == PseudoId::FirstLetter;
     338    if (!alwaysNeedsPseudoElement && !pseudoElementRendererIsNeeded(pseudoStyle.get()))
    342339        return { };
    343340
     
    345342
    346343    if (pseudoId == PseudoId::Before || pseudoId == PseudoId::After) {
    347         // ::first-line can inherit to ::before/::after
    348         auto firstLineContext = makeResolutionContextForInheritedFirstLine(elementUpdate, *elementUpdate.style);
    349         if (firstLineContext) {
    350             auto firstLineStyle = scope().resolver->pseudoStyleForElement(element, { pseudoId }, *firstLineContext);
    351             firstLineStyle->setStyleType(PseudoId::FirstLine);
    352             animatedUpdate.style->addCachedPseudoStyle(WTFMove(firstLineStyle));
     344        if (scope().resolver->usesFirstLineRules()) {
     345            // ::first-line can inherit to ::before/::after
     346            if (auto firstLineContext = makeResolutionContextForInheritedFirstLine(elementUpdate, *elementUpdate.style)) {
     347                auto firstLineStyle = scope().resolver->pseudoStyleForElement(element, { pseudoId }, *firstLineContext);
     348                firstLineStyle->setStyleType(PseudoId::FirstLine);
     349                animatedUpdate.style->addCachedPseudoStyle(WTFMove(firstLineStyle));
     350            }
     351        }
     352        if (scope().resolver->usesFirstLetterRules()) {
     353            auto beforeAfterContext = makeResolutionContextForPseudoElement(animatedUpdate, PseudoId::FirstLetter);
     354            if (auto firstLetterStyle = resolveAncestorFirstLetterPseudoElement(element, elementUpdate, beforeAfterContext))
     355                animatedUpdate.style->addCachedPseudoStyle(WTFMove(firstLetterStyle));
    353356        }
    354357    }
     
    357360}
    358361
    359 std::unique_ptr<RenderStyle> TreeResolver::resolveInheritedFirstLinePseudoElement(Element& element, const ElementUpdate& elementUpdate)
     362std::optional<ElementUpdate> TreeResolver::resolveAncestorPseudoElement(Element& element, PseudoId pseudoId, const ElementUpdate& elementUpdate)
     363{
     364    ASSERT(!elementUpdate.style->hasPseudoStyle(pseudoId));
     365
     366    auto pseudoElementStyle = [&]() -> std::unique_ptr<RenderStyle> {
     367        // ::first-line and ::first-letter defined on an ancestor element may need to be resolved for the current element.
     368        if (pseudoId == PseudoId::FirstLine)
     369            return resolveAncestorFirstLinePseudoElement(element, elementUpdate);
     370        if (pseudoId == PseudoId::FirstLetter) {
     371            auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, PseudoId::FirstLetter);
     372            return resolveAncestorFirstLetterPseudoElement(element, elementUpdate, resolutionContext);
     373        }
     374        return nullptr;
     375    }();
     376
     377    if (!pseudoElementStyle)
     378        return { };
     379
     380    auto* oldStyle = element.renderOrDisplayContentsStyle(pseudoId);
     381    auto change = oldStyle ? determineChange(*oldStyle, *pseudoElementStyle) : Change::Renderer;
     382    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, pseudoId);
     383
     384    return createAnimatedElementUpdate(WTFMove(pseudoElementStyle), { element, pseudoId }, change, resolutionContext);
     385}
     386
     387static bool isChildInBlockFormattingContext(const RenderStyle& style)
     388{
     389    // FIXME: Incomplete. There should be shared code with layout for this.
     390    if (style.display() != DisplayType::Block && style.display() != DisplayType::ListItem)
     391        return false;
     392    if (style.hasOutOfFlowPosition())
     393        return false;
     394    if (style.floating() != Float::None)
     395        return false;
     396    if (style.overflowX() != Overflow::Visible || style.overflowY() != Overflow::Visible)
     397        return false;
     398    return true;
     399};
     400
     401std::unique_ptr<RenderStyle> TreeResolver::resolveAncestorFirstLinePseudoElement(Element& element, const ElementUpdate& elementUpdate)
    360402{
    361403    if (elementUpdate.style->display() == DisplayType::Inline) {
     
    375417    }
    376418
    377     auto isChildInBlockFormattingContext = [](const RenderStyle& style) {
    378         // FIXME: Incomplete. There should be shared code with layout for this.
    379         if (style.display() != DisplayType::Block)
    380             return false;
    381         if (style.hasOutOfFlowPosition())
    382             return false;
    383         if (style.floating() != Float::None)
    384             return false;
    385         if (style.overflowX() != Overflow::Visible || style.overflowY() != Overflow::Visible)
    386             return false;
    387         return true;
    388     };
    389 
    390     auto firstLineElementForBlock = [&]() -> Element* {
     419    auto findFirstLineElementForBlock = [&]() -> Element* {
    391420        if (!isChildInBlockFormattingContext(*elementUpdate.style))
    392421            return nullptr;
    393422
    394423        // ::first-line is only propagated to the first block.
    395         if (parent().resolvedFirstBoxGeneratingChild)
     424        if (parent().resolvedFirstLineAndLetterChild)
    396425            return nullptr;
    397426
     
    399428            if (parent.style.display() == DisplayType::Contents)
    400429                continue;
    401             if (!supportsFirstLinePseudoElement(parent.style))
     430            if (!supportsFirstLineAndLetterPseudoElement(parent.style))
    402431                return nullptr;
    403432            if (parent.style.hasPseudoStyle(PseudoId::FirstLine))
     
    409438    };
    410439
    411     auto firstLineElement = firstLineElementForBlock();
     440    auto firstLineElement = findFirstLineElementForBlock();
    412441    if (!firstLineElement)
    413442        return { };
    414443
    415     auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate);
     444    auto resolutionContext = makeResolutionContextForPseudoElement(elementUpdate, PseudoId::FirstLine);
    416445    // Can't use the cached state since the element being resolved is not the current one.
    417446    resolutionContext.selectorMatchingState = nullptr;
    418447
    419448    return scope().resolver->pseudoStyleForElement(*firstLineElement, { PseudoId::FirstLine }, resolutionContext);
     449}
     450
     451std::unique_ptr<RenderStyle> TreeResolver::resolveAncestorFirstLetterPseudoElement(Element& element, const ElementUpdate& elementUpdate, ResolutionContext& resolutionContext)
     452{
     453    auto findFirstLetterElement = [&]() -> Element* {
     454        if (elementUpdate.style->hasPseudoStyle(PseudoId::FirstLetter) && supportsFirstLineAndLetterPseudoElement(*elementUpdate.style))
     455            return &element;
     456
     457        // ::first-letter is only propagated to the first box.
     458        if (parent().resolvedFirstLineAndLetterChild)
     459            return nullptr;
     460
     461        bool skipInlines = elementUpdate.style->display() == DisplayType::Inline;
     462        if (!skipInlines && !isChildInBlockFormattingContext(*elementUpdate.style))
     463            return nullptr;
     464
     465        for (auto& parent : makeReversedRange(m_parentStack)) {
     466            if (parent.style.display() == DisplayType::Contents)
     467                continue;
     468            if (skipInlines && parent.style.display() == DisplayType::Inline)
     469                continue;
     470            skipInlines = false;
     471
     472            if (!supportsFirstLineAndLetterPseudoElement(parent.style))
     473                return nullptr;
     474            if (parent.style.hasPseudoStyle(PseudoId::FirstLetter))
     475                return parent.element;
     476            if (!isChildInBlockFormattingContext(parent.style))
     477                return nullptr;
     478        }
     479        return nullptr;
     480    };
     481
     482    auto firstLetterElement = findFirstLetterElement();
     483    if (!firstLetterElement)
     484        return { };
     485
     486    // Can't use the cached state since the element being resolved is not the current one.
     487    resolutionContext.selectorMatchingState = nullptr;
     488
     489    return scope().resolver->pseudoStyleForElement(*firstLetterElement, { PseudoId::FirstLetter }, resolutionContext);
    420490}
    421491
     
    430500}
    431501
    432 ResolutionContext TreeResolver::makeResolutionContextForPseudoElement(const ElementUpdate& elementUpdate)
    433 {
     502ResolutionContext TreeResolver::makeResolutionContextForPseudoElement(const ElementUpdate& elementUpdate, PseudoId pseudoId)
     503{
     504    auto parentStyle = [&] {
     505        if (pseudoId == PseudoId::FirstLetter) {
     506            if (auto* firstLineStyle = elementUpdate.style->getCachedPseudoStyle(PseudoId::FirstLine))
     507                return firstLineStyle;
     508        }
     509        return elementUpdate.style.get();
     510    };
     511
    434512    return {
    435         elementUpdate.style.get(),
     513        parentStyle(),
    436514        parentBoxStyleForPseudoElement(elementUpdate),
    437515        m_documentElementStyle.get(),
     
    442520std::optional<ResolutionContext> TreeResolver::makeResolutionContextForInheritedFirstLine(const ElementUpdate& elementUpdate, const RenderStyle& inheritStyle)
    443521{
    444     if (!scope().resolver->usesFirstLineRules())
    445         return { };
    446 
    447522    auto parentFirstLineStyle = inheritStyle.getCachedPseudoStyle(PseudoId::FirstLine);
    448523    if (!parentFirstLineStyle)
     
    706781
    707782            if (!text.data().isAllSpecialCharacters<isHTMLSpace>())
    708                 parent.resolvedFirstBoxGeneratingChild = true;
     783                parent.resolvedFirstLineAndLetterChild = true;
    709784
    710785            text.setHasValidStyle();
     
    763838            m_didSeePendingStylesheet = hasLoadingStylesheet(m_document.styleScope(), element, !shouldIterateChildren);
    764839
    765         if (style && generatesBox(*style))
    766             parent.resolvedFirstBoxGeneratingChild = true;
     840        if (!parent.resolvedFirstLineAndLetterChild && style && generatesBox(*style) && supportsFirstLineAndLetterPseudoElement(*style))
     841            parent.resolvedFirstLineAndLetterChild = true;
    767842
    768843        if (!shouldIterateChildren) {
  • trunk/Source/WebCore/style/StyleTreeResolver.h

    r293956 r294291  
    7272    static ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, const Styleable&, Change, const ResolutionContext&);
    7373    std::optional<ElementUpdate> resolvePseudoElement(Element&, PseudoId, const ElementUpdate&);
    74     std::unique_ptr<RenderStyle> resolveInheritedFirstLinePseudoElement(Element&, const ElementUpdate&);
     74    std::optional<ElementUpdate> resolveAncestorPseudoElement(Element&, PseudoId, const ElementUpdate&);
     75    std::unique_ptr<RenderStyle> resolveAncestorFirstLinePseudoElement(Element&, const ElementUpdate&);
     76    std::unique_ptr<RenderStyle> resolveAncestorFirstLetterPseudoElement(Element&, const ElementUpdate&, ResolutionContext&);
    7577
    7678    struct Scope : RefCounted<Scope> {
     
    9395        DescendantsToResolve descendantsToResolve { DescendantsToResolve::None };
    9496        bool didPushScope { false };
    95         bool resolvedFirstBoxGeneratingChild { false };
     97        bool resolvedFirstLineAndLetterChild { false };
    9698
    9799        Parent(Document&);
     
    115117
    116118    ResolutionContext makeResolutionContext();
    117     ResolutionContext makeResolutionContextForPseudoElement(const ElementUpdate&);
     119    ResolutionContext makeResolutionContextForPseudoElement(const ElementUpdate&, PseudoId);
    118120    std::optional<ResolutionContext> makeResolutionContextForInheritedFirstLine(const ElementUpdate&, const RenderStyle& inheritStyle);
    119121    const Parent* boxGeneratingParent() const;
Note: See TracChangeset for help on using the changeset viewer.