Changeset 167714 in webkit


Ignore:
Timestamp:
Apr 23, 2014 11:05:33 AM (10 years ago)
Author:
hyatt@apple.com
Message:

[New Multicolumn] Nested columns not working at all.
https://bugs.webkit.org/show_bug.cgi?id=131805

Reviewed by Dean Jackson.

Add support for nested pagination contexts, allowing for an arbitrary level
of nesting of multicolumn layouts. There were a number of things that had to
be patched in order for this to work.

  • rendering/RenderBlock.cpp:

(WebCore::RenderBlock::regionAtBlockOffset):
Make sure RenderMultiColumnFlowThreads just return null for regions at any
block offset. Individual region sets will be created as you cross ancestor
regions eventually, so this is just getting in the way.

  • rendering/RenderLayer.cpp:

(WebCore::RenderLayer::enclosingPaginationLayerInSubtree):
Add a new helper method for obtaining an enclosingPaginationLayer when
constrained by some root. This function ensures you don't accidentally
cross your subtree root when looking for enclosing pagination layers.

(WebCore::RenderLayer::collectFragments):
Patch collectFragments to know how to recur to collect ancestor fragments
in order to apply nested splitting as you cross pagination boundaries.

(WebCore::RenderLayer::updatePaintingInfoForFragments):
(WebCore::RenderLayer::calculateClipRects):

  • rendering/RenderLayer.h:

(WebCore::LayerFragment::LayerFragment):
(WebCore::LayerFragment::setRects):
(WebCore::LayerFragment::moveBy):
(WebCore::LayerFragment::intersect):
Improve the LayerFragment so that it caches transformed bounding boxes as
well. This is needed to fix intersectsDamageRect so that it doesn't grab
the wrong bounding box when checking inline layers that are paginated.

  • rendering/RenderMultiColumnFlowThread.cpp:

(WebCore::RenderMultiColumnFlowThread::flowThreadDescendantInserted):
Ignore inserted flow threads inside an ancestor flow thread, since we only
care about what the sets do.

  • rendering/RenderObject.cpp:

(WebCore::RenderObject::insertedIntoTree):
Make sure that nested flow thread layers return themselves when a child
is inserted directly under them.

Location:
trunk/Source/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r167713 r167714  
     12014-04-22  David Hyatt  <hyatt@apple.com>
     2
     3        [New Multicolumn] Nested columns not working at all.
     4        https://bugs.webkit.org/show_bug.cgi?id=131805
     5
     6        Reviewed by Dean Jackson.
     7
     8        Add support for nested pagination contexts, allowing for an arbitrary level
     9        of nesting of multicolumn layouts. There were a number of things that had to
     10        be patched in order for this to work.
     11
     12        * rendering/RenderBlock.cpp:
     13        (WebCore::RenderBlock::regionAtBlockOffset):
     14        Make sure RenderMultiColumnFlowThreads just return null for regions at any
     15        block offset. Individual region sets will be created as you cross ancestor
     16        regions eventually, so this is just getting in the way.
     17
     18        * rendering/RenderLayer.cpp:
     19        (WebCore::RenderLayer::enclosingPaginationLayerInSubtree):
     20        Add a new helper method for obtaining an enclosingPaginationLayer when
     21        constrained by some root. This function ensures you don't accidentally
     22        cross your subtree root when looking for enclosing pagination layers.
     23
     24        (WebCore::RenderLayer::collectFragments):
     25        Patch collectFragments to know how to recur to collect ancestor fragments
     26        in order to apply nested splitting as you cross pagination boundaries.
     27
     28        (WebCore::RenderLayer::updatePaintingInfoForFragments):
     29        (WebCore::RenderLayer::calculateClipRects):
     30        * rendering/RenderLayer.h:
     31        (WebCore::LayerFragment::LayerFragment):
     32        (WebCore::LayerFragment::setRects):
     33        (WebCore::LayerFragment::moveBy):
     34        (WebCore::LayerFragment::intersect):
     35        Improve the LayerFragment so that it caches transformed bounding boxes as
     36        well. This is needed to fix intersectsDamageRect so that it doesn't grab
     37        the wrong bounding box when checking inline layers that are paginated.
     38
     39        * rendering/RenderMultiColumnFlowThread.cpp:
     40        (WebCore::RenderMultiColumnFlowThread::flowThreadDescendantInserted):
     41        Ignore inserted flow threads inside an ancestor flow thread, since we only
     42        care about what the sets do.
     43       
     44        * rendering/RenderObject.cpp:
     45        (WebCore::RenderObject::insertedIntoTree):
     46        Make sure that nested flow thread layers return themselves when a child
     47        is inserted directly under them.
     48
    1492014-04-22  Myles C. Maxfield  <mmaxfield@apple.com>
    250
  • trunk/Source/WebCore/rendering/RenderBlock.cpp

    r167706 r167714  
    48294829RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const
    48304830{
     4831    if (isInFlowRenderFlowThread())
     4832        return 0;
     4833
    48314834    RenderFlowThread* flowThread = flowThreadContainingBlock();
    48324835    if (!flowThread || !flowThread->hasValidRegionInfo())
  • trunk/Source/WebCore/rendering/RenderLayer.cpp

    r167562 r167714  
    42414241}
    42424242
     4243RenderLayer* RenderLayer::enclosingPaginationLayerInSubtree(const RenderLayer* rootLayer) const
     4244{
     4245    // If we don't have an enclosing layer, or if the root layer is the same as the enclosing layer,
     4246    // then just return the enclosing pagination layer (it will be 0 in the former case and the rootLayer in the latter case).
     4247    if (!m_enclosingPaginationLayer || rootLayer == m_enclosingPaginationLayer)
     4248        return m_enclosingPaginationLayer;
     4249   
     4250    // Walk up the layer tree and see which layer we hit first. If it's the root, then the enclosing pagination
     4251    // layer isn't in our subtree and we return 0. If we hit the enclosing pagination layer first, then
     4252    // we can return it.
     4253    for (const RenderLayer* layer = this; layer; layer = layer->parent()) {
     4254        if (layer == rootLayer)
     4255            return 0;
     4256        if (layer == m_enclosingPaginationLayer)
     4257            return m_enclosingPaginationLayer;
     4258    }
     4259   
     4260    // This should never be reached, since an enclosing layer should always either be the rootLayer or be
     4261    // our enclosing pagination layer.
     4262    ASSERT_NOT_REACHED();
     4263    return 0;
     4264}
     4265
    42434266void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, RenderRegion* region, const LayoutRect& dirtyRect,
    42444267    ClipRectsType clipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutPoint* offsetFromRoot,
    4245     const LayoutRect* layerBoundingBox)
    4246 {
    4247     if (!enclosingPaginationLayer() || hasTransform()) {
     4268    const LayoutRect* layerBoundingBox, ShouldApplyRootOffsetToFragments applyRootOffsetToFragments)
     4269{
     4270    RenderLayer* paginationLayer = enclosingPaginationLayerInSubtree(rootLayer);
     4271    if (!paginationLayer || hasTransform()) {
    42484272        // For unpaginated layers, there is only one fragment.
    42494273        LayerFragment fragment;
     
    42564280    // Compute our offset within the enclosing pagination layer.
    42574281    LayoutPoint offsetWithinPaginatedLayer;
    4258     convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginatedLayer);
     4282    convertToLayerCoords(paginationLayer, offsetWithinPaginatedLayer);
    42594283   
    42604284    // Calculate clip rects relative to the enclosingPaginationLayer. The purpose of this call is to determine our bounds clipped to intermediate
    42614285    // layers between us and the pagination context. It's important to minimize the number of fragments we need to create and this helps with that.
    4262     ClipRectsContext paginationClipRectsContext(enclosingPaginationLayer(), region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
     4286    ClipRectsContext paginationClipRectsContext(paginationLayer, region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
    42634287    LayoutRect layerBoundsInFlowThread;
    42644288    ClipRect backgroundRectInFlowThread;
     
    42694293   
    42704294    // Take our bounding box within the flow thread and clip it.
    4271     LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : boundingBox(enclosingPaginationLayer(), 0, &offsetWithinPaginatedLayer);
     4295    LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : boundingBox(paginationLayer, 0, &offsetWithinPaginatedLayer);
    42724296    layerBoundingBoxInFlowThread.intersect(backgroundRectInFlowThread.rect());
    4273 
     4297   
     4298    RenderFlowThread& enclosingFlowThread = toRenderFlowThread(paginationLayer->renderer());
     4299    RenderLayer* parentPaginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(rootLayer);
     4300    LayerFragments ancestorFragments;
     4301    if (parentPaginationLayer) {
     4302        // Compute a bounding box accounting for fragments.
     4303        LayoutRect layerFragmentBoundingBoxInParentPaginationLayer = enclosingFlowThread.fragmentsBoundingBox(layerBoundingBoxInFlowThread);
     4304       
     4305        // Convert to be in the ancestor pagination context's coordinate space.
     4306        LayoutPoint offsetWithinParentPaginatedLayer;
     4307        paginationLayer->convertToLayerCoords(parentPaginationLayer, offsetWithinParentPaginatedLayer);
     4308        layerFragmentBoundingBoxInParentPaginationLayer.moveBy(offsetWithinParentPaginatedLayer);
     4309       
     4310        // Now collect ancestor fragments.
     4311        parentPaginationLayer->collectFragments(ancestorFragments, rootLayer, region, dirtyRect, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip, nullptr, &layerFragmentBoundingBoxInParentPaginationLayer, ApplyRootOffsetToFragments);
     4312       
     4313        if (ancestorFragments.isEmpty())
     4314            return;
     4315       
     4316        for (auto& ancestorFragment : ancestorFragments) {
     4317            // Shift the dirty rect into flow thread coordinates.
     4318            LayoutRect dirtyRectInFlowThread(dirtyRect);
     4319            dirtyRectInFlowThread.moveBy(-offsetWithinParentPaginatedLayer + -ancestorFragment.paginationOffset);
     4320           
     4321            size_t oldSize = fragments.size();
     4322           
     4323            // Tell the flow thread to collect the fragments. We pass enough information to create a minimal number of fragments based off the pages/columns
     4324            // that intersect the actual dirtyRect as well as the pages/columns that intersect our layer's bounding box.
     4325            enclosingFlowThread.collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread);
     4326           
     4327            size_t newSize = fragments.size();
     4328           
     4329            if (oldSize == newSize)
     4330                continue;
     4331
     4332            for (size_t i = oldSize; i < newSize; ++i) {
     4333                LayerFragment& fragment = fragments.at(i);
     4334               
     4335                // Set our four rects with all clipping applied that was internal to the flow thread.
     4336                fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, outlineRectInFlowThread, &layerBoundingBoxInFlowThread);
     4337               
     4338                // Shift to the root-relative physical position used when painting the flow thread in this fragment.
     4339                fragment.moveBy(ancestorFragment.paginationOffset + fragment.paginationOffset + offsetWithinParentPaginatedLayer);
     4340
     4341                // Intersect the fragment with our ancestor's background clip so that e.g., columns in an overflow:hidden block are
     4342                // properly clipped by the overflow.
     4343                fragment.intersect(ancestorFragment.paginationClip);
     4344               
     4345                // Now intersect with our pagination clip. This will typically mean we're just intersecting the dirty rect with the column
     4346                // clip, so the column clip ends up being all we apply.
     4347                fragment.intersect(fragment.paginationClip);
     4348               
     4349                if (applyRootOffsetToFragments == ApplyRootOffsetToFragments)
     4350                    fragment.paginationOffset = fragment.paginationOffset + offsetWithinParentPaginatedLayer;
     4351            }
     4352        }
     4353       
     4354        return;
     4355    }
     4356   
    42744357    // Shift the dirty rect into flow thread coordinates.
    42754358    LayoutPoint offsetOfPaginationLayerFromRoot;
     
    42804363    // Tell the flow thread to collect the fragments. We pass enough information to create a minimal number of fragments based off the pages/columns
    42814364    // that intersect the actual dirtyRect as well as the pages/columns that intersect our layer's bounding box.
    4282     RenderFlowThread& enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer());
    42834365    enclosingFlowThread.collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread);
    42844366   
     
    42884370    // Get the parent clip rects of the pagination layer, since we need to intersect with that when painting column contents.
    42894371    ClipRect ancestorClipRect = dirtyRect;
    4290     if (enclosingPaginationLayer()->parent()) {
     4372    if (paginationLayer->parent()) {
    42914373        ClipRectsContext clipRectsContext(rootLayer, region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
    4292         ancestorClipRect = enclosingPaginationLayer()->backgroundClipRect(clipRectsContext);
     4374        ancestorClipRect = paginationLayer->backgroundClipRect(clipRectsContext);
    42934375        ancestorClipRect.intersect(dirtyRect);
    42944376    }
     
    42984380       
    42994381        // Set our four rects with all clipping applied that was internal to the flow thread.
    4300         fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, outlineRectInFlowThread);
     4382        fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, outlineRectInFlowThread, &layerBoundingBoxInFlowThread);
    43014383       
    43024384        // Shift to the root-relative physical position used when painting the flow thread in this fragment.
     
    43104392        // clip, so the column clip ends up being all we apply.
    43114393        fragment.intersect(fragment.paginationClip);
     4394       
     4395        if (applyRootOffsetToFragments == ApplyRootOffsetToFragments)
     4396            fragment.paginationOffset = fragment.paginationOffset + offsetOfPaginationLayerFromRoot;
    43124397    }
    43134398}
     
    43224407        if (this != localPaintingInfo.rootLayer || !(localPaintFlags & PaintLayerPaintingOverflowContents)) {
    43234408            LayoutPoint newOffsetFromRoot = *offsetFromRoot + fragment.paginationOffset;
    4324             fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, &newOffsetFromRoot, localPaintingInfo.renderNamedFlowFragment);
     4409            fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, &newOffsetFromRoot, localPaintingInfo.renderNamedFlowFragment, fragment.hasBoundingBox ? &fragment.boundingBox : 0);
    43254410        }
    43264411    }
     
    56515736}
    56525737
    5653 bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot, RenderRegion* region) const
     5738bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot, RenderRegion* region, const LayoutRect* cachedBoundingBox) const
    56545739{
    56555740    // Always examine the canvas and the root.
     
    56795764            return true;
    56805765    }
    5681 
     5766   
    56825767    // Otherwise we need to compute the bounding box of this single layer and see if it intersects
    5683     // the damage rect.
     5768    // the damage rect. It's possible the fragment computed the bounding box already, in which case we
     5769    // can use the cached value.
     5770    if (cachedBoundingBox)
     5771        return cachedBoundingBox->intersects(damageRect);
     5772   
    56845773    return boundingBox(rootLayer, 0, offsetFromRoot).intersects(damageRect);
    56855774}
     
    57355824    else
    57365825        renderer().containingBlock()->flipForWritingMode(result);
    5737 
    5738     if (enclosingPaginationLayer() && (flags & UseFragmentBoxes)) {
     5826   
     5827    const RenderLayer* paginationLayer = (flags & UseFragmentBoxes) ? enclosingPaginationLayerInSubtree(ancestorLayer) : 0;
     5828    const RenderLayer* childLayer = this;
     5829    bool isPaginated = paginationLayer;
     5830   
     5831    while (paginationLayer) {
    57395832        // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to
    57405833        // get our true bounding box.
    57415834        LayoutPoint offsetWithinPaginationLayer;
    5742         convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginationLayer);       
     5835        childLayer->convertToLayerCoords(paginationLayer, offsetWithinPaginationLayer);
    57435836        result.moveBy(offsetWithinPaginationLayer);
    57445837
    5745         RenderFlowThread& enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer());
     5838        RenderFlowThread& enclosingFlowThread = toRenderFlowThread(paginationLayer->renderer());
    57465839        result = enclosingFlowThread.fragmentsBoundingBox(result);
    57475840       
     5841        childLayer = paginationLayer;
     5842        paginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(ancestorLayer);
     5843    }
     5844
     5845    if (isPaginated) {
    57485846        LayoutPoint delta;
    5749         if (offsetFromRoot)
    5750             delta = *offsetFromRoot;
    5751         else
    5752             enclosingPaginationLayer()->convertToLayerCoords(ancestorLayer, delta);
     5847        childLayer->convertToLayerCoords(ancestorLayer, delta);
    57535848        result.moveBy(delta);
    57545849        return result;
    57555850    }
    5756 
     5851   
    57575852    LayoutPoint delta;
    57585853    if (offsetFromRoot)
  • trunk/Source/WebCore/rendering/RenderLayer.h

    r167424 r167714  
    237237};
    238238
     239enum ShouldApplyRootOffsetToFragments {
     240    ApplyRootOffsetToFragments,
     241    IgnoreRootOffsetForFragments
     242};
     243
    239244struct ClipRectsCache {
    240245    WTF_MAKE_FAST_ALLOCATED;
     
    274279    LayerFragment()
    275280        : shouldPaintContent(false)
     281        , hasBoundingBox(false)
    276282    { }
    277283
    278     void setRects(const LayoutRect& bounds, const ClipRect& background, const ClipRect& foreground, const ClipRect& outline)
     284    void setRects(const LayoutRect& bounds, const ClipRect& background, const ClipRect& foreground, const ClipRect& outline, const LayoutRect* bbox)
    279285    {
    280286        layerBounds = bounds;
     
    282288        foregroundRect = foreground;
    283289        outlineRect = outline;
     290        if (bbox) {
     291            boundingBox = *bbox;
     292            hasBoundingBox = true;
     293        }
    284294    }
    285295   
     
    291301        outlineRect.moveBy(offset);
    292302        paginationClip.moveBy(offset);
     303        boundingBox.moveBy(offset);
    293304    }
    294305   
     
    298309        foregroundRect.intersect(rect);
    299310        outlineRect.intersect(rect);
     311        boundingBox.intersect(rect);
    300312    }
    301313   
    302314    bool shouldPaintContent;
     315    bool hasBoundingBox;
    303316    LayoutRect layerBounds;
    304317    ClipRect backgroundRect;
    305318    ClipRect foregroundRect;
    306319    ClipRect outlineRect;
     320    LayoutRect boundingBox;
    307321   
    308322    // Unique to paginated fragments. The physical translation to apply to shift the layer when painting/hit-testing.
     
    697711
    698712    // Pass offsetFromRoot if known.
    699     bool intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot = 0, RenderRegion* = 0) const;
     713    bool intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot = 0, RenderRegion* = 0, const LayoutRect* cachedBoundingBox = 0) const;
    700714
    701715    enum CalculateLayerBoundsFlag {
     
    933947
    934948    IntSize clampScrollOffset(const IntSize&) const;
     949
     950    RenderLayer* enclosingPaginationLayerInSubtree(const RenderLayer* rootLayer) const;
    935951
    936952    void setNextSibling(RenderLayer* next) { m_next = next; }
     
    9851001    void collectFragments(LayerFragments&, const RenderLayer* rootLayer, RenderRegion*, const LayoutRect& dirtyRect,
    9861002        ClipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize,
    987         ShouldRespectOverflowClip = RespectOverflowClip, const LayoutPoint* offsetFromRoot = 0, const LayoutRect* layerBoundingBox = 0);
     1003        ShouldRespectOverflowClip = RespectOverflowClip, const LayoutPoint* offsetFromRoot = 0, const LayoutRect* layerBoundingBox = 0, ShouldApplyRootOffsetToFragments = IgnoreRootOffsetForFragments);
    9881004    void updatePaintingInfoForFragments(LayerFragments&, const LayerPaintingInfo&, PaintLayerFlags, bool shouldPaintContent, const LayoutPoint* offsetFromRoot);
    9891005    void paintBackgroundForFragments(const LayerFragments&, GraphicsContext*, GraphicsContext* transparencyLayerContext,
  • trunk/Source/WebCore/rendering/RenderMultiColumnFlowThread.cpp

    r167404 r167714  
    267267void RenderMultiColumnFlowThread::flowThreadDescendantInserted(RenderObject* descendant)
    268268{
    269     if (m_beingEvacuated)
     269    if (m_beingEvacuated || descendant->isInFlowRenderFlowThread())
    270270        return;
    271271    RenderObject* subtreeRoot = descendant;
  • trunk/Source/WebCore/rendering/RenderObject.cpp

    r167705 r167714  
    19251925    if (!isFloating() && parent()->childrenInline())
    19261926        parent()->dirtyLinesFromChangedChild(this);
    1927 
    1928     if (RenderFlowThread* flowThread = parent()->flowThreadContainingBlock())
     1927   
     1928    if (parent()->isRenderFlowThread())
     1929        toRenderFlowThread(parent())->flowThreadDescendantInserted(this);
     1930    else if (RenderFlowThread* flowThread = parent()->flowThreadContainingBlock())
    19291931        flowThread->flowThreadDescendantInserted(this);
    19301932}
Note: See TracChangeset for help on using the changeset viewer.