Changeset 223514 in webkit


Ignore:
Timestamp:
Oct 17, 2017 3:18:19 AM (7 years ago)
Author:
Antti Koivisto
Message:

Text nodes with display:contents parent should render as if they were wrapped in an unstyled <span>
https://bugs.webkit.org/show_bug.cgi?id=178332

Reviewed by Ryosuke Niwa.

Source/WebCore:

According to https://github.com/w3c/csswg-drafts/issues/1118

<div style="display:contents;color:green">text</div>

must result in green text even though div doesn't generate a box.

This patch implements the behavior by wrapping text renderers with display:contents parent element
in an anonymous inline box that receives its style by inheriting from the parent element.

  • dom/Document.cpp:

(WebCore::Document::updateTextRenderer):

  • rendering/RenderElement.cpp:

(WebCore::RenderElement::computeFirstLineStyle const):

Synthesize the first line style in display:contents parent case.

  • rendering/RenderObject.cpp:

(WebCore::findDestroyRootIncludingAnonymous):

Factor into a function.

(WebCore::RenderObject::removeFromParentAndDestroyCleaningUpAnonymousWrappers):

Get rid of the anonymous wrapper if it exists.

  • rendering/RenderText.cpp:

(WebCore::inlineWrapperForDisplayContentsMap):
(WebCore::RenderText::RenderText):
(WebCore::RenderText::willBeDestroyed):
(WebCore::RenderText::inlineWrapperForDisplayContents):
(WebCore::RenderText::setInlineWrapperForDisplayContents):

Add a weak member (implemented as a rare data map) for holding the wrapper pointer.

(WebCore::RenderText::findByDisplayContentsInlineWrapperCandidate):

Helper to get the text renderer for a wrapper.

  • rendering/RenderText.h:
  • style/RenderTreeUpdater.cpp:

(WebCore::createTextRenderer):
(WebCore::RenderTreeUpdater::updateTextRenderer):

Create the wrapper if needed.

  • style/StyleTreeResolver.cpp:

(WebCore::Style::TreeResolver::resolveComposedTree):

Compute the wrapper style by inheriting from the display:contents parent.

  • style/StyleUpdate.h:

(WebCore::Style::TextUpdate::TextUpdate):

LayoutTests:

Location:
trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r223505 r223514  
     12017-10-17  Antti Koivisto  <antti@apple.com>
     2
     3        Text nodes with display:contents parent should render as if they were wrapped in an unstyled <span>
     4        https://bugs.webkit.org/show_bug.cgi?id=178332
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        * TestExpectations: 10 more display:contents tests pass.
     9
    1102017-10-17  Alicia Boya García  <aboya@igalia.com>
    211
  • trunk/LayoutTests/TestExpectations

    r223440 r223514  
    12651265### START OF display: contents failures
    12661266
    1267 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-list-001.html [ ImageOnlyFailure ]
    1268 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-table-002-inline.html [ ImageOnlyFailure ]
    1269 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-inline-flex-001.html [ ImageOnlyFailure ]
    12701267webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-before-after-first-letter-001.html [ ImageOnlyFailure ]
    12711268webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-before-after-001.html [ ImageOnlyFailure ]
    12721269webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-table-002-none.html [ ImageOnlyFailure ]
    12731270webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-flex-003.html [ ImageOnlyFailure ]
    1274 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-table-001.html [ ImageOnlyFailure ]
    12751271webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-before-after-002.html [ ImageOnlyFailure ]
    1276 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-table-002.html [ ImageOnlyFailure ]
    1277 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-inline-flex-001-none.html [ ImageOnlyFailure ]
    12781272webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-table-001-inline.html [ ImageOnlyFailure ]
    12791273webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-flex-002-none.html [ ImageOnlyFailure ]
     
    12811275webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-before-after-001.html [ ImageOnlyFailure ]
    12821276webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-flow-root-001.html [ ImageOnlyFailure ]
    1283 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-inline-flex-001-inline.html [ ImageOnlyFailure ]
    12841277webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-flex-003-none.html [ ImageOnlyFailure ]
    12851278webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-table-001-none.html [ ImageOnlyFailure ]
    1286 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-state-change-001.html [ ImageOnlyFailure ]
    12871279webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-flex-002-inline.html [ ImageOnlyFailure ]
    12881280webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-flex-003-inline.html [ ImageOnlyFailure ]
    1289 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-list-001-inline.html [ ImageOnlyFailure ]
    1290 webkit.org/b/157477 imported/w3c/web-platform-tests/css/css-display-3/display-contents-dynamic-list-001-none.html [ ImageOnlyFailure ]
    12911281
    12921282### END OF display: contents failures
  • trunk/Source/WebCore/ChangeLog

    r223505 r223514  
     12017-10-17  Antti Koivisto  <antti@apple.com>
     2
     3        Text nodes with display:contents parent should render as if they were wrapped in an unstyled <span>
     4        https://bugs.webkit.org/show_bug.cgi?id=178332
     5
     6        Reviewed by Ryosuke Niwa.
     7
     8        According to https://github.com/w3c/csswg-drafts/issues/1118
     9
     10            <div style="display:contents;color:green">text</div>
     11
     12        must result in green text even though div doesn't generate a box.
     13
     14        This patch implements the behavior by wrapping text renderers with display:contents parent element
     15        in an anonymous inline box that receives its style by inheriting from the parent element.
     16
     17        * dom/Document.cpp:
     18        (WebCore::Document::updateTextRenderer):
     19        * rendering/RenderElement.cpp:
     20        (WebCore::RenderElement::computeFirstLineStyle const):
     21
     22            Synthesize the first line style in display:contents parent case.
     23
     24        * rendering/RenderObject.cpp:
     25        (WebCore::findDestroyRootIncludingAnonymous):
     26
     27            Factor into a function.
     28
     29        (WebCore::RenderObject::removeFromParentAndDestroyCleaningUpAnonymousWrappers):
     30
     31            Get rid of the anonymous wrapper if it exists.
     32
     33        * rendering/RenderText.cpp:
     34        (WebCore::inlineWrapperForDisplayContentsMap):
     35        (WebCore::RenderText::RenderText):
     36        (WebCore::RenderText::willBeDestroyed):
     37        (WebCore::RenderText::inlineWrapperForDisplayContents):
     38        (WebCore::RenderText::setInlineWrapperForDisplayContents):
     39
     40            Add a weak member (implemented as a rare data map) for holding the wrapper pointer.
     41
     42        (WebCore::RenderText::findByDisplayContentsInlineWrapperCandidate):
     43
     44            Helper to get the text renderer for a wrapper.
     45
     46        * rendering/RenderText.h:
     47        * style/RenderTreeUpdater.cpp:
     48        (WebCore::createTextRenderer):
     49        (WebCore::RenderTreeUpdater::updateTextRenderer):
     50
     51            Create the wrapper if needed.
     52
     53        * style/StyleTreeResolver.cpp:
     54        (WebCore::Style::TreeResolver::resolveComposedTree):
     55
     56            Compute the wrapper style by inheriting from the display:contents parent.
     57
     58        * style/StyleUpdate.h:
     59        (WebCore::Style::TextUpdate::TextUpdate):
     60
    1612017-10-17  Alicia Boya García  <aboya@igalia.com>
    262
  • trunk/Source/WebCore/dom/Document.cpp

    r223476 r223514  
    18721872
    18731873    auto textUpdate = std::make_unique<Style::Update>(*this);
    1874     textUpdate->addText(text, { offsetOfReplacedText, lengthOfReplacedText });
     1874    textUpdate->addText(text, { offsetOfReplacedText, lengthOfReplacedText, std::nullopt });
    18751875
    18761876    RenderTreeUpdater renderTreeUpdater(*this);
  • trunk/Source/WebCore/rendering/RenderElement.cpp

    r223194 r223514  
    218218    }
    219219
    220     if (rendererForFirstLineStyle.isAnonymous() || !rendererForFirstLineStyle.isRenderInline())
     220    if (!rendererForFirstLineStyle.isRenderInline())
    221221        return nullptr;
    222222
     
    224224    if (&parentStyle == &rendererForFirstLineStyle.parent()->style())
    225225        return nullptr;
     226
     227    if (rendererForFirstLineStyle.isAnonymous()) {
     228        auto* textRendererWithDisplayContentsParent = RenderText::findByDisplayContentsInlineWrapperCandidate(rendererForFirstLineStyle);
     229        if (!textRendererWithDisplayContentsParent)
     230            return nullptr;
     231        auto* composedTreeParentElement = textRendererWithDisplayContentsParent->textNode()->parentElementInComposedTree();
     232        if (!composedTreeParentElement)
     233            return nullptr;
     234
     235        auto style = composedTreeParentElement->styleResolver().styleForElement(*composedTreeParentElement, &parentStyle).renderStyle;
     236        ASSERT(style->display() == CONTENTS);
     237
     238        // We act as if there was an unstyled <span> around the text node. Only styling happens via inheritance.
     239        auto firstLineStyle = RenderStyle::createPtr();
     240        firstLineStyle->inheritFrom(*style);
     241        return firstLineStyle;
     242    }
     243
    226244    return rendererForFirstLineStyle.element()->styleResolver().styleForElement(*element(), &parentStyle).renderStyle;
    227245}
  • trunk/Source/WebCore/rendering/RenderObject.cpp

    r223139 r223514  
    14561456}
    14571457
    1458 void RenderObject::removeFromParentAndDestroyCleaningUpAnonymousWrappers()
    1459 {
    1460     // If the tree is destroyed, there is no need for a clean-up phase.
    1461     if (renderTreeBeingDestroyed()) {
    1462         removeFromParentAndDestroy();
    1463         return;
    1464     }
    1465 
    1466     auto* destroyRoot = this;
     1458static RenderObject& findDestroyRootIncludingAnonymous(RenderObject& renderer)
     1459{
     1460    auto* inlineWrapperForDisplayContents = is<RenderText>(renderer) ? downcast<RenderText>(renderer).inlineWrapperForDisplayContents() : nullptr;
     1461
     1462    auto* destroyRoot = inlineWrapperForDisplayContents ? inlineWrapperForDisplayContents : &renderer;
    14671463    auto* destroyRootParent = destroyRoot->parent();
    14681464    while (destroyRootParent && destroyRootParent->isAnonymous()) {
     
    14761472        destroyRootParent = destroyRootParent->parent();
    14771473    }
    1478 
    1479     if (is<RenderTableRow>(*destroyRoot))
    1480         downcast<RenderTableRow>(*destroyRoot).collapseAndDestroyAnonymousSiblingRows();
    1481 
    1482     destroyRoot->removeFromParentAndDestroy();
     1474    return *destroyRoot;
     1475}
     1476
     1477void RenderObject::removeFromParentAndDestroyCleaningUpAnonymousWrappers()
     1478{
     1479    // If the tree is destroyed, there is no need for a clean-up phase.
     1480    if (renderTreeBeingDestroyed()) {
     1481        removeFromParentAndDestroy();
     1482        return;
     1483    }
     1484
     1485    auto& destroyRoot = findDestroyRootIncludingAnonymous(*this);
     1486
     1487    if (is<RenderTableRow>(destroyRoot))
     1488        downcast<RenderTableRow>(destroyRoot).collapseAndDestroyAnonymousSiblingRows();
     1489
     1490    destroyRoot.removeFromParentAndDestroy();
    14831491    // WARNING: |this| is deleted here.
    14841492}
  • trunk/Source/WebCore/rendering/RenderText.cpp

    r223476 r223514  
    133133}
    134134
     135static HashMap<const RenderText*, WeakPtr<RenderInline>>& inlineWrapperForDisplayContentsMap()
     136{
     137    static NeverDestroyed<HashMap<const RenderText*, WeakPtr<RenderInline>>> map;
     138    return map;
     139}
     140
    135141void makeCapitalized(String* string, UChar previous)
    136142{
     
    184190    , m_useBackslashAsYenSymbol(false)
    185191    , m_originalTextDiffersFromRendered(false)
     192    , m_hasInlineWrapperForDisplayContents(false)
    186193#if ENABLE(TEXT_AUTOSIZING)
    187194    , m_candidateComputedTextSize(0)
     
    292299    if (m_originalTextDiffersFromRendered)
    293300        originalTextMap().remove(this);
     301
     302    setInlineWrapperForDisplayContents(nullptr);
    294303
    295304    RenderObject::willBeDestroyed();
     
    17381747}
    17391748
     1749RenderInline* RenderText::inlineWrapperForDisplayContents()
     1750{
     1751    ASSERT(m_hasInlineWrapperForDisplayContents == inlineWrapperForDisplayContentsMap().contains(this));
     1752
     1753    if (!m_hasInlineWrapperForDisplayContents)
     1754        return nullptr;
     1755    return inlineWrapperForDisplayContentsMap().get(this).get();
     1756}
     1757
     1758void RenderText::setInlineWrapperForDisplayContents(RenderInline* wrapper)
     1759{
     1760    ASSERT(m_hasInlineWrapperForDisplayContents == inlineWrapperForDisplayContentsMap().contains(this));
     1761
     1762    if (!wrapper) {
     1763        if (!m_hasInlineWrapperForDisplayContents)
     1764            return;
     1765        inlineWrapperForDisplayContentsMap().remove(this);
     1766        m_hasInlineWrapperForDisplayContents = false;
     1767        return;
     1768    }
     1769    inlineWrapperForDisplayContentsMap().add(this, makeWeakPtr(wrapper));
     1770    m_hasInlineWrapperForDisplayContents = true;
     1771}
     1772
     1773RenderText* RenderText::findByDisplayContentsInlineWrapperCandidate(RenderElement& renderer)
     1774{
     1775    auto* firstChild = renderer.firstChild();
     1776    if (!is<RenderText>(firstChild))
     1777        return nullptr;
     1778    auto& textRenderer = downcast<RenderText>(*firstChild);
     1779    if (textRenderer.inlineWrapperForDisplayContents() != &renderer)
     1780        return nullptr;
     1781    ASSERT(textRenderer.textNode());
     1782    ASSERT(renderer.firstChild() == renderer.lastChild());
     1783    return &textRenderer;
     1784
     1785}
     1786
    17401787} // namespace WebCore
  • trunk/Source/WebCore/rendering/RenderText.h

    r222556 r223514  
    180180    Vector<std::pair<unsigned, unsigned>> draggedContentRangesBetweenOffsets(unsigned startOffset, unsigned endOffset) const;
    181181
     182    RenderInline* inlineWrapperForDisplayContents();
     183    void setInlineWrapperForDisplayContents(RenderInline*);
     184
     185    static RenderText* findByDisplayContentsInlineWrapperCandidate(RenderElement&);
     186
    182187protected:
    183188    virtual void computePreferredLogicalWidths(float leadWidth);
     
    234239    unsigned m_useBackslashAsYenSymbol : 1;
    235240    unsigned m_originalTextDiffersFromRendered : 1;
     241    unsigned m_hasInlineWrapperForDisplayContents : 1;
    236242    unsigned m_canUseSimplifiedTextMeasuring : 1;
    237243
  • trunk/Source/WebCore/style/RenderTreeUpdater.cpp

    r223500 r223514  
    3939#include "RenderDescendantIterator.h"
    4040#include "RenderFullScreen.h"
     41#include "RenderInline.h"
    4142#include "RenderListItem.h"
    4243#include "RenderTreeUpdaterFirstLetter.h"
     
    429430}
    430431
    431 static void createTextRenderer(Text& textNode, RenderTreePosition& renderTreePosition)
     432static void createTextRenderer(Text& textNode, RenderTreePosition& renderTreePosition, const Style::TextUpdate* textUpdate)
    432433{
    433434    ASSERT(!textNode.renderer());
    434435
    435     auto newRenderer = textNode.createTextRenderer(renderTreePosition.parent().style());
    436     ASSERT(newRenderer);
     436    auto textRenderer = textNode.createTextRenderer(renderTreePosition.parent().style());
    437437
    438438    renderTreePosition.computeNextSibling(textNode);
    439439
    440     if (!renderTreePosition.canInsert(*newRenderer))
    441         return;
    442 
    443     textNode.setRenderer(newRenderer.get());
    444     renderTreePosition.insert(WTFMove(newRenderer));
     440    if (!renderTreePosition.canInsert(*textRenderer))
     441        return;
     442
     443    textNode.setRenderer(textRenderer.get());
     444
     445    if (textUpdate && textUpdate->inheritedDisplayContentsStyle && *textUpdate->inheritedDisplayContentsStyle) {
     446        // Wrap text renderer into anonymous inline so we can give it a style.
     447        // This is to support "<div style='display:contents;color:green'>text</div>" type cases
     448        auto newDisplayContentsAnonymousWrapper = createRenderer<RenderInline>(textNode.document(), RenderStyle::clone(**textUpdate->inheritedDisplayContentsStyle));
     449        newDisplayContentsAnonymousWrapper->initializeStyle();
     450        auto& displayContentsAnonymousWrapper = *newDisplayContentsAnonymousWrapper;
     451        renderTreePosition.insert(WTFMove(newDisplayContentsAnonymousWrapper));
     452
     453        textRenderer->setInlineWrapperForDisplayContents(&displayContentsAnonymousWrapper);
     454        displayContentsAnonymousWrapper.addChild(WTFMove(textRenderer));
     455        return;
     456    }
     457
     458    renderTreePosition.insert(WTFMove(textRenderer));
    445459}
    446460
     
    449463    auto* existingRenderer = text.renderer();
    450464    bool needsRenderer = textRendererIsNeeded(text, renderTreePosition());
     465
     466    if (existingRenderer && textUpdate && textUpdate->inheritedDisplayContentsStyle) {
     467        if (existingRenderer->inlineWrapperForDisplayContents() || *textUpdate->inheritedDisplayContentsStyle) {
     468            // FIXME: We could update without teardown.
     469            tearDownRenderer(text);
     470            existingRenderer = nullptr;
     471        }
     472    }
     473
    451474    if (existingRenderer) {
    452475        if (needsRenderer) {
     
    461484    if (!needsRenderer)
    462485        return;
    463     createTextRenderer(text, renderTreePosition());
     486    createTextRenderer(text, renderTreePosition(), textUpdate);
    464487    invalidateWhitespaceOnlyTextSiblingsAfterAttachIfNeeded(text);
    465488}
  • trunk/Source/WebCore/style/StyleTreeResolver.cpp

    r223500 r223514  
    373373}
    374374
     375static std::unique_ptr<RenderStyle> createInheritedDisplayContentsStyleIfNeeded(const RenderStyle& parentElementStyle, const RenderStyle* parentBoxStyle)
     376{
     377    if (parentElementStyle.display() != CONTENTS)
     378        return nullptr;
     379    if (parentBoxStyle && !parentBoxStyle->inheritedNotEqual(&parentElementStyle))
     380        return nullptr;
     381    // Compute style for imaginary unstyled <span> around the text node.
     382    auto style = RenderStyle::createPtr();
     383    style->inheritFrom(parentElementStyle);
     384    return style;
     385}
     386
    375387void TreeResolver::resolveComposedTree()
    376388{
     
    397409        if (is<Text>(node)) {
    398410            auto& text = downcast<Text>(node);
    399             if (text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach)
    400                 m_update->addText(text, parent.element, { });
     411           
     412            if ((text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach) || parent.style.display() == CONTENTS) {
     413                TextUpdate textUpdate;
     414                textUpdate.inheritedDisplayContentsStyle = createInheritedDisplayContentsStyleIfNeeded(parent.style, parentBoxStyle());
     415
     416                m_update->addText(text, parent.element, WTFMove(textUpdate));
     417            }
    401418
    402419            text.setHasValidStyle();
  • trunk/Source/WebCore/style/StyleUpdate.h

    r223500 r223514  
    7676#if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
    7777    TextUpdate() = default;
    78     TextUpdate(unsigned offset, unsigned length)
     78    TextUpdate(unsigned offset, unsigned length, std::optional<std::unique_ptr<RenderStyle>> inheritedDisplayContentsStyle)
    7979        : offset { offset }
    8080        , length { length }
     81        , inheritedDisplayContentsStyle { WTFMove(inheritedDisplayContentsStyle) }
    8182    {
    8283    }
     
    8586    unsigned offset { 0 };
    8687    unsigned length { std::numeric_limits<unsigned>::max() };
     88    std::optional<std::unique_ptr<RenderStyle>> inheritedDisplayContentsStyle;
    8789};
    8890
Note: See TracChangeset for help on using the changeset viewer.