Changeset 192275 in webkit


Ignore:
Timestamp:
Nov 10, 2015 3:18:52 PM (8 years ago)
Author:
Alan Bujtas
Message:

Continuations with anonymous wrappers inside misplaces child renderers.
https://bugs.webkit.org/show_bug.cgi?id=150908

When a child is appended to an inline container and the beforeChild is not a direct child, but
it is inside a generated subtree, we need to special case the inline split to form continuation.

RenderInline::splitInlines() assumes that beforeChild is always a direct child of the current
inline container. However when beforeChild type requires wrapper content (such as table cells), the DOM and the
render tree get out of sync. In such cases, we need to ensure that both the beforeChild and its siblings end up
in the correct generated block.

Reviewed by Darin Adler and David Hyatt.

Source/WebCore:

Test: fast/inline/continuation-with-anon-wrappers.html

  • rendering/RenderInline.cpp:

(WebCore::RenderInline::splitInlines):
(WebCore::RenderInline::addChildToContinuation):

LayoutTests:

  • fast/inline/continuation-with-anon-wrappers-expected.txt: Added.
  • fast/inline/continuation-with-anon-wrappers.html: Added.
Location:
trunk
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r192270 r192275  
     12015-11-10  Zalan Bujtas  <zalan@apple.com>
     2
     3        Continuations with anonymous wrappers inside misplaces child renderers.
     4        https://bugs.webkit.org/show_bug.cgi?id=150908
     5
     6        When a child is appended to an inline container and the beforeChild is not a direct child, but
     7        it is inside a generated subtree, we need to special case the inline split to form continuation.
     8
     9        RenderInline::splitInlines() assumes that beforeChild is always a direct child of the current
     10        inline container. However when beforeChild type requires wrapper content (such as table cells), the DOM and the
     11        render tree get out of sync. In such cases, we need to ensure that both the beforeChild and its siblings end up
     12        in the correct generated block.
     13
     14        Reviewed by Darin Adler and David Hyatt.
     15
     16        * fast/inline/continuation-with-anon-wrappers-expected.txt: Added.
     17        * fast/inline/continuation-with-anon-wrappers.html: Added.
     18
    1192015-11-10  Geoffrey Garen  <ggaren@apple.com>
    220
  • trunk/Source/WebCore/ChangeLog

    r192270 r192275  
     12015-11-10  Zalan Bujtas  <zalan@apple.com>
     2
     3        Continuations with anonymous wrappers inside misplaces child renderers.
     4        https://bugs.webkit.org/show_bug.cgi?id=150908
     5
     6        When a child is appended to an inline container and the beforeChild is not a direct child, but
     7        it is inside a generated subtree, we need to special case the inline split to form continuation.
     8
     9        RenderInline::splitInlines() assumes that beforeChild is always a direct child of the current
     10        inline container. However when beforeChild type requires wrapper content (such as table cells), the DOM and the
     11        render tree get out of sync. In such cases, we need to ensure that both the beforeChild and its siblings end up
     12        in the correct generated block.
     13
     14        Reviewed by Darin Adler and David Hyatt.
     15
     16        Test: fast/inline/continuation-with-anon-wrappers.html
     17
     18        * rendering/RenderInline.cpp:
     19        (WebCore::RenderInline::splitInlines):
     20        (WebCore::RenderInline::addChildToContinuation):
     21
    1222015-11-10  Geoffrey Garen  <ggaren@apple.com>
    223
  • trunk/Source/WebCore/rendering/RenderInline.cpp

    r191011 r192275  
    413413    // Create a clone of this inline.
    414414    RenderPtr<RenderInline> cloneInline = clone();
    415     cloneInline->setContinuation(oldCont);
    416 
    417415#if ENABLE(FULLSCREEN_API)
    418416    // If we're splitting the inline containing the fullscreened element,
     
    425423        beforeChild = document().fullScreenRenderer();
    426424#endif
    427 
    428425    // Now take all of the children from beforeChild to the end and remove
    429426    // them from |this| and place them in the clone.
    430     RenderObject* renderer = beforeChild;
    431     while (renderer) {
    432         RenderObject* tmp = renderer;
    433         renderer = tmp->nextSibling();
    434         removeChildInternal(*tmp, NotifyChildren);
    435         cloneInline->addChildIgnoringContinuation(tmp);
    436         tmp->setNeedsLayoutAndPrefWidthsRecalc();
    437     }
    438 
     427    for (RenderObject* rendererToMove = beforeChild; rendererToMove;) {
     428        RenderObject* nextSibling = rendererToMove->nextSibling();
     429        // When anonymous wrapper is present, we might need to move the whole subtree instead.
     430        if (rendererToMove->parent() != this) {
     431            auto* anonymousParent = rendererToMove->parent();
     432            while (anonymousParent && anonymousParent->parent() != this) {
     433                ASSERT(anonymousParent->isAnonymous());
     434                anonymousParent = anonymousParent->parent();
     435            }
     436            if (!anonymousParent) {
     437                ASSERT_NOT_REACHED();
     438                break;
     439            }
     440            // If beforeChild is the first child in the subtree, we could just move the whole subtree.
     441            if (!rendererToMove->previousSibling()) {
     442                // Reparent the whole anonymous wrapper tree.
     443                rendererToMove = anonymousParent;
     444                // Skip to the next sibling that is not in this subtree.
     445                nextSibling = anonymousParent->nextSibling();
     446            } else if (!rendererToMove->nextSibling()) {
     447                // This is the last renderer in the subtree. We need to jump out of the wrapper subtree, so that
     448                // the siblings are getting reparented too.
     449                nextSibling = anonymousParent->nextSibling();
     450            }
     451            // Otherwise just move the renderer to the inline clone. Should the renderer need an anon
     452            // wrapper, the addChild() will generate one for it.
     453            // FIXME: When the anonymous wrapper has multiple children, we end up traversing up to the topmost wrapper
     454            // every time, which is a bit wasteful.
     455        }
     456        rendererToMove->parent()->removeChildInternal(*rendererToMove, NotifyChildren);
     457        cloneInline->addChildIgnoringContinuation(rendererToMove);
     458        rendererToMove->setNeedsLayoutAndPrefWidthsRecalc();
     459        rendererToMove = nextSibling;
     460    }
     461    cloneInline->setContinuation(oldCont);
    439462    // Hook |clone| up as the continuation of the middle block.
    440463    middleBlock->setContinuation(cloneInline.get());
     
    469492            // Now we need to take all of the children starting from the first child
    470493            // *after* currentChild and append them all to the clone.
    471             renderer = currentChild->nextSibling();
    472             while (renderer) {
    473                 RenderObject* tmp = renderer;
    474                 renderer = tmp->nextSibling();
    475                 currentInline.removeChildInternal(*tmp, NotifyChildren);
    476                 cloneInline->addChildIgnoringContinuation(tmp);
    477                 tmp->setNeedsLayoutAndPrefWidthsRecalc();
     494            for (auto* current = currentChild->nextSibling(); current;) {
     495                auto* next = current->nextSibling();
     496                currentInline.removeChildInternal(*current, NotifyChildren);
     497                cloneInline->addChildIgnoringContinuation(current);
     498                current->setNeedsLayoutAndPrefWidthsRecalc();
     499                current = next;
    478500            }
    479501        }
     
    493515    // Now take all the children after currentChild and remove them from the fromBlock
    494516    // and put them in the toBlock.
    495     renderer = currentChild->nextSibling();
    496     while (renderer) {
    497         RenderObject* tmp = renderer;
    498         renderer = tmp->nextSibling();
    499         fromBlock->removeChildInternal(*tmp, NotifyChildren);
    500         toBlock->insertChildInternal(tmp, nullptr, NotifyChildren);
     517    for (auto* current = currentChild->nextSibling(); current;) {
     518        auto* next = current->nextSibling();
     519        fromBlock->removeChildInternal(*current, NotifyChildren);
     520        toBlock->insertChildInternal(current, nullptr, NotifyChildren);
     521        current = next;
    501522    }
    502523}
     
    570591{
    571592    RenderBoxModelObject* flow = continuationBefore(beforeChild);
    572     ASSERT(!beforeChild || is<RenderBlock>(*beforeChild->parent()) || is<RenderInline>(*beforeChild->parent()));
    573     RenderBoxModelObject* beforeChildParent = nullptr;
    574     if (beforeChild)
    575         beforeChildParent = downcast<RenderBoxModelObject>(beforeChild->parent());
    576     else {
    577         if (RenderBoxModelObject* continuation = nextContinuation(flow))
    578             beforeChildParent = continuation;
    579         else
    580             beforeChildParent = flow;
     593    // It may or may not be the direct parent of the beforeChild.
     594    RenderBoxModelObject* beforeChildAncestor = nullptr;
     595    // In case of anonymous wrappers, the parent of the beforeChild is mostly irrelevant. What we need is
     596    // the topmost wrapper.
     597    if (beforeChild && !is<RenderBlock>(beforeChild->parent()) && beforeChild->parent()->isAnonymous()) {
     598        RenderElement* anonymousParent = beforeChild->parent();
     599        while (anonymousParent && anonymousParent->parent() && anonymousParent->parent()->isAnonymous())
     600            anonymousParent = anonymousParent->parent();
     601        ASSERT(anonymousParent && anonymousParent->parent());
     602        beforeChildAncestor = downcast<RenderBoxModelObject>(anonymousParent->parent());
     603    } else {
     604        ASSERT(!beforeChild || is<RenderBlock>(*beforeChild->parent()) || is<RenderInline>(*beforeChild->parent()));
     605        if (beforeChild)
     606            beforeChildAncestor = downcast<RenderBoxModelObject>(beforeChild->parent());
     607        else {
     608            if (RenderBoxModelObject* continuation = nextContinuation(flow))
     609                beforeChildAncestor = continuation;
     610            else
     611                beforeChildAncestor = flow;
     612        }
    581613    }
    582614
    583615    if (newChild->isFloatingOrOutOfFlowPositioned())
    584         return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
    585 
     616        return beforeChildAncestor->addChildIgnoringContinuation(newChild, beforeChild);
     617
     618    if (flow == beforeChildAncestor)
     619        return flow->addChildIgnoringContinuation(newChild, beforeChild);
    586620    // A continuation always consists of two potential candidates: an inline or an anonymous
    587621    // block box holding block children.
    588622    bool childInline = newChildIsInline(*newChild, *this);
    589     bool bcpInline = beforeChildParent->isInline();
    590     bool flowInline = flow->isInline();
    591 
    592     if (flow == beforeChildParent)
    593         return flow->addChildIgnoringContinuation(newChild, beforeChild);
    594     else {
    595         // The goal here is to match up if we can, so that we can coalesce and create the
    596         // minimal # of continuations needed for the inline.
    597         if (childInline == bcpInline)
    598             return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
    599         else if (flowInline == childInline)
    600             return flow->addChildIgnoringContinuation(newChild); // Just treat like an append.
    601         else
    602             return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild);
    603     }
     623    // The goal here is to match up if we can, so that we can coalesce and create the
     624    // minimal # of continuations needed for the inline.
     625    if (childInline == beforeChildAncestor->isInline())
     626        return beforeChildAncestor->addChildIgnoringContinuation(newChild, beforeChild);
     627    if (flow->isInline() == childInline)
     628        return flow->addChildIgnoringContinuation(newChild); // Just treat like an append.
     629    return beforeChildAncestor->addChildIgnoringContinuation(newChild, beforeChild);
    604630}
    605631
Note: See TracChangeset for help on using the changeset viewer.