Changeset 147426 in webkit


Ignore:
Timestamp:
Apr 2, 2013 6:48:53 AM (11 years ago)
Author:
abucur@adobe.com
Message:

[CSS Regions] Nested auto-height regions don't layout correctly
https://bugs.webkit.org/show_bug.cgi?id=111969

Reviewed by David Hyatt.

Source/WebCore:

The patch fixes the auto-height regions processing model to work with nested named flows. Currently
this use case doesn't work correctly because the order in which the computed height value is propagated
to the regions is incorrect. For example, in the case of two flows (f1 and f2) and two auto-height
regions (r1 and r2) with r2 a member of f1:

  1. the normal layout phase starts
  2. r1 is laid out with height = 0
  3. f1 is laid out including r2 with height = 0; overrideLogicalHeight for r1 is computed
  4. f2 is laid out; overrideLogicalHeight for r2 is computed
  5. the constrained layout phase starts
  6. r1 is laid out using the overrideLogicalHeight computed in the normal phase
  7. f1 is laid out including r2 with its overrideLogicalHeight
  8. f2 is laid out

The problem appears at step 6 because the overrideLogicalheight computed during step 3 assumes r2
has a height of 0. The patch changes the algorithm to update the auto-height regions in the reverse
order of their flow threads dependecies. Here is a high level overview of the new algorithm,
considering the named flows are sorted in the order of their dependencies:

  1. The flows are laid out from the outer flow to the inner flow. This successfully computes the outer

non-auto-height regions size so the inner flows have the necessary information to correctly fragment
the content.

  1. The flows are laid out from the inner flow to the outer flow. After an inner flow is laid out it

goes into the constrained layout phase and marks the auto-height regions they need layout. This
means the outer flows will relayout if they depend on regions with auto-height regions belonging to
inner flows. This step will correctly compute the overrideLogicalHeights for the auto-height regions.
It's possible for non-auto-height regions to relayout if they depend on auto-height regions. This
will invalidate the inner flow threads and mark them as needing layout.

  1. The last step is to do one last layout if there are pahtological dependencies between non-auto-height

regions and auto-height regions as detected in the previous step.

The patch also removes the layout phase flag from RenderView and moves it to the flow threads. This
happens because a flow needs to update its auto-height regions overrideLogicalHeight while updating
the logical height of the regions belonging to the inner flows that are laid out by the flow (i.e. the
outer flow is in the normal phase while the inner flows are in the constrained layout phase).

There's also a new flag on the RenderFlowThread that is set when the flow needs the constrained layout
phase. This could have been placed on the flow thread controller but I think it will be useful when doing
content balancing for the new multi-column implementation.

Another change is the moment we clear the overrideLogicalHeight value on empty auto-height regions because
we don't have to relayout a flow thread once it's in the constrained phase. We need to have the correct
flow thread portions correctly computed after the artificial forced break is applied at the end of the content.

Tests: fast/regions/autoheight-mixed-nested-complex-regions.html

fast/regions/autoheight-mixed-nested-regions.html
fast/regions/autoheight-mixed-parallel-regions.html
fast/regions/autoheight-nested-regions.html

  • rendering/FlowThreadController.cpp:

(WebCore::FlowThreadController::FlowThreadController):
(WebCore::FlowThreadController::layoutRenderNamedFlowThreads):
(WebCore):
(WebCore::FlowThreadController::registerNamedFlowContentNode):
(WebCore::FlowThreadController::unregisterNamedFlowContentNode):
(WebCore::FlowThreadController::updateFlowThreadsChainIfNecessary):
(WebCore::FlowThreadController::updateFlowThreadsNeedingLayout):
(WebCore::FlowThreadController::updateFlowThreadsNeedingTwoStepLayout):
(WebCore::FlowThreadController::resetFlowThreadsWithAutoHeightRegions):
(WebCore::FlowThreadController::updateFlowThreadsIntoConstrainedPhase):

  • rendering/FlowThreadController.h:

(FlowThreadController):

  • rendering/RenderBlock.cpp:

(WebCore::RenderBlock::layoutBlock):

  • rendering/RenderFlowThread.cpp:

(WebCore::RenderFlowThread::RenderFlowThread):
(WebCore::RenderFlowThread::validateRegions):
(WebCore::RenderFlowThread::layout):
(WebCore::RenderFlowThread::regionAtBlockOffset):
(WebCore::RenderFlowThread::applyBreakAfterContent): Apply an artificial break at the end of the content.
This way we can detect when the content ends and clear the overrideLogicalHeight on the empty auto-height regions.
(WebCore::RenderFlowThread::computeOverflowStateForRegions):
(WebCore):
(WebCore::RenderFlowThread::initializeRegionsOverrideLogicalContentHeight):
(WebCore::RenderFlowThread::markAutoLogicalHeightRegionsForLayout):
(WebCore::RenderFlowThread::updateRegionsFlowThreadPortionRect):
(WebCore::RenderFlowThread::addForcedRegionBreak):

  • rendering/RenderFlowThread.h:
  • rendering/RenderRegion.cpp:

(WebCore::RenderRegion::pageLogicalWidth):
(WebCore::RenderRegion::pageLogicalHeight):
(WebCore::RenderRegion::maxPageLogicalHeight):
(WebCore::RenderRegion::logicalHeightOfAllFlowThreadContent):
(WebCore::RenderRegion::layoutBlock):
(WebCore::RenderRegion::updateLogicalHeight):

  • rendering/RenderView.cpp:

(WebCore::RenderView::RenderView):
(WebCore):
(WebCore::RenderView::layoutContentInAutoLogicalHeightRegions):
(WebCore::RenderView::layout):

  • rendering/RenderView.h:

(WebCore):
(RenderView):

LayoutTests:

These tests cover various combinations of dependencies between flows and regions with
or without auto-height.

  • fast/regions/autoheight-mixed-nested-complex-regions-expected.txt: Added.
  • fast/regions/autoheight-mixed-nested-complex-regions.html: Added.
  • fast/regions/autoheight-mixed-nested-regions-expected.txt: Added.
  • fast/regions/autoheight-mixed-nested-regions.html: Added.
  • fast/regions/autoheight-mixed-parallel-regions-expected.txt: Added.
  • fast/regions/autoheight-mixed-parallel-regions.html: Added.
  • fast/regions/autoheight-nested-regions-expected.txt: Added.
  • fast/regions/autoheight-nested-regions.html: Added.
Location:
trunk
Files:
8 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r147424 r147426  
     12013-04-02  Andrei Bucur  <abucur@adobe.com>
     2
     3        [CSS Regions] Nested auto-height regions don't layout correctly
     4        https://bugs.webkit.org/show_bug.cgi?id=111969
     5
     6        Reviewed by David Hyatt.
     7
     8        These tests cover various combinations of dependencies between flows and regions with
     9        or without auto-height.
     10
     11        * fast/regions/autoheight-mixed-nested-complex-regions-expected.txt: Added.
     12        * fast/regions/autoheight-mixed-nested-complex-regions.html: Added.
     13        * fast/regions/autoheight-mixed-nested-regions-expected.txt: Added.
     14        * fast/regions/autoheight-mixed-nested-regions.html: Added.
     15        * fast/regions/autoheight-mixed-parallel-regions-expected.txt: Added.
     16        * fast/regions/autoheight-mixed-parallel-regions.html: Added.
     17        * fast/regions/autoheight-nested-regions-expected.txt: Added.
     18        * fast/regions/autoheight-nested-regions.html: Added.
     19
    1202013-04-02  Dmitry Zvorygin  <zvorygin@chromium.org>
    221
  • trunk/Source/WebCore/ChangeLog

    r147425 r147426  
     12013-04-02  Andrei Bucur  <abucur@adobe.com>
     2
     3        [CSS Regions] Nested auto-height regions don't layout correctly
     4        https://bugs.webkit.org/show_bug.cgi?id=111969
     5
     6        Reviewed by David Hyatt.
     7
     8        The patch fixes the auto-height regions processing model to work with nested named flows. Currently
     9        this use case doesn't work correctly because the order in which the computed height value is propagated
     10        to the regions is incorrect. For example, in the case of two flows (f1 and f2) and two auto-height
     11        regions (r1 and r2) with r2 a member of f1:
     12        1. the normal layout phase starts
     13        2. r1 is laid out with height = 0
     14        3. f1 is laid out including r2 with height = 0; overrideLogicalHeight for r1 is computed
     15        4. f2 is laid out; overrideLogicalHeight for r2 is computed
     16        5. the constrained layout phase starts
     17        6. r1 is laid out using the overrideLogicalHeight computed in the normal phase
     18        7. f1 is laid out including r2 with its overrideLogicalHeight
     19        8. f2 is laid out
     20
     21        The problem appears at step 6 because the overrideLogicalheight computed during step 3 assumes r2
     22        has a height of 0. The patch changes the algorithm to update the auto-height regions in the reverse
     23        order of their flow threads dependecies. Here is a high level overview of the new algorithm,
     24        considering the named flows are sorted in the order of their dependencies:
     25        1. The flows are laid out from the outer flow to the inner flow. This successfully computes the outer
     26        non-auto-height regions size so the inner flows have the necessary information to correctly fragment
     27        the content.
     28        2. The flows are laid out from the inner flow to the outer flow. After an inner flow is laid out it
     29        goes into the constrained layout phase and marks the auto-height regions they need layout. This
     30        means the outer flows will relayout if they depend on regions with auto-height regions belonging to
     31        inner flows. This step will correctly compute the overrideLogicalHeights for the auto-height regions.
     32        It's possible for non-auto-height regions to relayout if they depend on auto-height regions. This
     33        will invalidate the inner flow threads and mark them as needing layout.
     34        3. The last step is to do one last layout if there are pahtological dependencies between non-auto-height
     35        regions and auto-height regions as detected in the previous step.
     36
     37        The patch also removes the layout phase flag from RenderView and moves it to the flow threads. This
     38        happens because a flow needs to update its auto-height regions overrideLogicalHeight while updating
     39        the logical height of the regions belonging to the inner flows that are laid out by the flow (i.e. the
     40        outer flow is in the normal phase while the inner flows are in the constrained layout phase).
     41
     42        There's also a new flag on the RenderFlowThread that is set when the flow needs the constrained layout
     43        phase. This could have been placed on the flow thread controller but I think it will be useful when doing
     44        content balancing for the new multi-column implementation.
     45
     46        Another change is the moment we clear the overrideLogicalHeight value on empty auto-height regions because
     47        we don't have to relayout a flow thread once it's in the constrained phase. We need to have the correct
     48        flow thread portions correctly computed after the artificial forced break is applied at the end of the content.
     49
     50        Tests: fast/regions/autoheight-mixed-nested-complex-regions.html
     51               fast/regions/autoheight-mixed-nested-regions.html
     52               fast/regions/autoheight-mixed-parallel-regions.html
     53               fast/regions/autoheight-nested-regions.html
     54
     55        * rendering/FlowThreadController.cpp:
     56        (WebCore::FlowThreadController::FlowThreadController):
     57        (WebCore::FlowThreadController::layoutRenderNamedFlowThreads):
     58        (WebCore):
     59        (WebCore::FlowThreadController::registerNamedFlowContentNode):
     60        (WebCore::FlowThreadController::unregisterNamedFlowContentNode):
     61        (WebCore::FlowThreadController::updateFlowThreadsChainIfNecessary):
     62        (WebCore::FlowThreadController::updateFlowThreadsNeedingLayout):
     63        (WebCore::FlowThreadController::updateFlowThreadsNeedingTwoStepLayout):
     64        (WebCore::FlowThreadController::resetFlowThreadsWithAutoHeightRegions):
     65        (WebCore::FlowThreadController::updateFlowThreadsIntoConstrainedPhase):
     66        * rendering/FlowThreadController.h:
     67        (FlowThreadController):
     68        * rendering/RenderBlock.cpp:
     69        (WebCore::RenderBlock::layoutBlock):
     70        * rendering/RenderFlowThread.cpp:
     71        (WebCore::RenderFlowThread::RenderFlowThread):
     72        (WebCore::RenderFlowThread::validateRegions):
     73        (WebCore::RenderFlowThread::layout):
     74        (WebCore::RenderFlowThread::regionAtBlockOffset):
     75        (WebCore::RenderFlowThread::applyBreakAfterContent): Apply an artificial break at the end of the content.
     76        This way we can detect when the content ends and clear the overrideLogicalHeight on the empty auto-height regions.
     77        (WebCore::RenderFlowThread::computeOverflowStateForRegions):
     78        (WebCore):
     79        (WebCore::RenderFlowThread::initializeRegionsOverrideLogicalContentHeight):
     80        (WebCore::RenderFlowThread::markAutoLogicalHeightRegionsForLayout):
     81        (WebCore::RenderFlowThread::updateRegionsFlowThreadPortionRect):
     82        (WebCore::RenderFlowThread::addForcedRegionBreak):
     83        * rendering/RenderFlowThread.h:
     84        * rendering/RenderRegion.cpp:
     85        (WebCore::RenderRegion::pageLogicalWidth):
     86        (WebCore::RenderRegion::pageLogicalHeight):
     87        (WebCore::RenderRegion::maxPageLogicalHeight):
     88        (WebCore::RenderRegion::logicalHeightOfAllFlowThreadContent):
     89        (WebCore::RenderRegion::layoutBlock):
     90        (WebCore::RenderRegion::updateLogicalHeight):
     91        * rendering/RenderView.cpp:
     92        (WebCore::RenderView::RenderView):
     93        (WebCore):
     94        (WebCore::RenderView::layoutContentInAutoLogicalHeightRegions):
     95        (WebCore::RenderView::layout):
     96        * rendering/RenderView.h:
     97        (WebCore):
     98        (RenderView):
     99
    11002013-04-02  Andrey Kosyakov  <caseq@chromium.org>
    2101
  • trunk/Source/WebCore/rendering/FlowThreadController.cpp

    r147414 r147426  
    5050    , m_currentRenderFlowThread(0)
    5151    , m_isRenderNamedFlowThreadOrderDirty(false)
    52     , m_needsTwoPassLayoutForAutoHeightRegions(false)
    5352    , m_flowThreadsWithAutoLogicalHeightRegions(0)
    5453{
     
    9897
    9998void FlowThreadController::layoutRenderNamedFlowThreads()
     99{
     100    updateFlowThreadsChainIfNecessary();
     101
     102    for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) {
     103        RenderNamedFlowThread* flowRenderer = *iter;
     104        flowRenderer->layoutIfNeeded();
     105    }
     106}
     107
     108void FlowThreadController::registerNamedFlowContentNode(Node* contentNode, RenderNamedFlowThread* namedFlow)
     109{
     110    ASSERT(contentNode && contentNode->isElementNode());
     111    ASSERT(namedFlow);
     112    ASSERT(!m_mapNamedFlowContentNodes.contains(contentNode));
     113    ASSERT(!namedFlow->hasContentNode(contentNode));
     114    m_mapNamedFlowContentNodes.add(contentNode, namedFlow);
     115    namedFlow->registerNamedFlowContentNode(contentNode);
     116}
     117
     118void FlowThreadController::unregisterNamedFlowContentNode(Node* contentNode)
     119{
     120    ASSERT(contentNode && contentNode->isElementNode());
     121    HashMap<Node*, RenderNamedFlowThread*>::iterator it = m_mapNamedFlowContentNodes.find(contentNode);
     122    ASSERT(it != m_mapNamedFlowContentNodes.end());
     123    ASSERT(it->value);
     124    ASSERT(it->value->hasContentNode(contentNode));
     125    it->value->unregisterNamedFlowContentNode(contentNode);
     126    m_mapNamedFlowContentNodes.remove(contentNode);
     127}
     128
     129void FlowThreadController::updateFlowThreadsChainIfNecessary()
    100130{
    101131    ASSERT(m_renderNamedFlowThreadList);
     
    132162        setIsRenderNamedFlowThreadOrderDirty(false);
    133163    }
    134 
    135     for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) {
    136         RenderNamedFlowThread* flowRenderer = *iter;
     164}
     165
     166bool FlowThreadController::updateFlowThreadsNeedingLayout()
     167{
     168    bool needsTwoPassLayout = false;
     169
     170    for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) {
     171        RenderNamedFlowThread* flowRenderer = *iter;
     172        ASSERT(!flowRenderer->needsTwoPhasesLayout());
     173        flowRenderer->setInConstrainedLayoutPhase(false);
     174        if (flowRenderer->needsLayout() && flowRenderer->hasAutoLogicalHeightRegions())
     175            needsTwoPassLayout = true;
     176    }
     177
     178    if (needsTwoPassLayout)
     179        resetFlowThreadsWithAutoHeightRegions();
     180
     181    return needsTwoPassLayout;
     182}
     183
     184bool FlowThreadController::updateFlowThreadsNeedingTwoStepLayout()
     185{
     186    bool needsTwoPassLayout = false;
     187
     188    for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) {
     189        RenderNamedFlowThread* flowRenderer = *iter;
     190        if (flowRenderer->needsTwoPhasesLayout()) {
     191            needsTwoPassLayout = true;
     192            break;
     193        }
     194    }
     195
     196    if (needsTwoPassLayout)
     197        resetFlowThreadsWithAutoHeightRegions();
     198
     199    return needsTwoPassLayout;
     200}
     201
     202void FlowThreadController::resetFlowThreadsWithAutoHeightRegions()
     203{
     204    for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter) {
     205        RenderNamedFlowThread* flowRenderer = *iter;
     206        if (flowRenderer->hasAutoLogicalHeightRegions()) {
     207            flowRenderer->markAutoLogicalHeightRegionsForLayout();
     208            flowRenderer->invalidateRegions();
     209        }
     210    }
     211}
     212
     213void FlowThreadController::updateFlowThreadsIntoConstrainedPhase()
     214{
     215    // Walk the flow chain in reverse order to update the auto-height regions and compute correct sizes for the containing regions. Only after this we can
     216    // set the flow in the constrained layout phase.
     217    for (RenderNamedFlowThreadList::reverse_iterator iter = m_renderNamedFlowThreadList->rbegin(); iter != m_renderNamedFlowThreadList->rend(); ++iter) {
     218        RenderNamedFlowThread* flowRenderer = *iter;
     219        ASSERT(!flowRenderer->hasRegions() || flowRenderer->hasValidRegionInfo());
    137220        flowRenderer->layoutIfNeeded();
    138     }
    139 }
    140 
    141 void FlowThreadController::registerNamedFlowContentNode(Node* contentNode, RenderNamedFlowThread* namedFlow)
    142 {
    143     ASSERT(contentNode && contentNode->isElementNode());
    144     ASSERT(namedFlow);
    145     ASSERT(!m_mapNamedFlowContentNodes.contains(contentNode));
    146     ASSERT(!namedFlow->hasContentNode(contentNode));
    147     m_mapNamedFlowContentNodes.add(contentNode, namedFlow);
    148     namedFlow->registerNamedFlowContentNode(contentNode);
    149 }
    150 
    151 void FlowThreadController::unregisterNamedFlowContentNode(Node* contentNode)
    152 {
    153     ASSERT(contentNode && contentNode->isElementNode());
    154     HashMap<Node*, RenderNamedFlowThread*>::iterator it = m_mapNamedFlowContentNodes.find(contentNode);
    155     ASSERT(it != m_mapNamedFlowContentNodes.end());
    156     ASSERT(it->value);
    157     ASSERT(it->value->hasContentNode(contentNode));
    158     it->value->unregisterNamedFlowContentNode(contentNode);
    159     m_mapNamedFlowContentNodes.remove(contentNode);
     221        if (flowRenderer->hasAutoLogicalHeightRegions()) {
     222            ASSERT(flowRenderer->needsTwoPhasesLayout());
     223            flowRenderer->markAutoLogicalHeightRegionsForLayout();
     224        }
     225        flowRenderer->setInConstrainedLayoutPhase(true);
     226        flowRenderer->clearNeedsTwoPhasesLayout();
     227    }
    160228}
    161229
     
    175243#endif
    176244
    177 bool FlowThreadController::hasRenderNamedFlowThreadsNeedingLayout() const
    178 {
    179     ASSERT(m_view->normalLayoutPhase());
    180     for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter)
    181         if ((*iter)->needsLayout())
    182             return true;
    183     return false;
    184 }
    185 
    186 void FlowThreadController::resetRegionsOverrideLogicalContentHeight()
    187 {
    188     ASSERT(m_view->normalLayoutPhase());
    189     ASSERT(hasFlowThreadsWithAutoLogicalHeightRegions());
    190 
    191     for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter)
    192         (*iter)->resetRegionsOverrideLogicalContentHeight();
    193 }
    194 
    195 void FlowThreadController::markAutoLogicalHeightRegionsForLayout()
    196 {
    197     ASSERT(m_view->constrainedFlowThreadsLayoutPhase());
    198     ASSERT(hasFlowThreadsWithAutoLogicalHeightRegions());
    199 
    200     for (RenderNamedFlowThreadList::iterator iter = m_renderNamedFlowThreadList->begin(); iter != m_renderNamedFlowThreadList->end(); ++iter)
    201         (*iter)->markAutoLogicalHeightRegionsForLayout();
    202 }
    203 
    204245} // namespace WebCore
  • trunk/Source/WebCore/rendering/FlowThreadController.h

    r140948 r147426  
    7272    void decrementFlowThreadsWithAutoLogicalHeightRegions() { ASSERT(m_flowThreadsWithAutoLogicalHeightRegions > 0); --m_flowThreadsWithAutoLogicalHeightRegions; }
    7373
    74     bool hasRenderNamedFlowThreadsNeedingLayout() const;
     74    bool updateFlowThreadsNeedingLayout();
     75    bool updateFlowThreadsNeedingTwoStepLayout();
     76    void updateFlowThreadsIntoConstrainedPhase();
    7577
    7678#ifndef NDEBUG
     
    7880#endif
    7981
    80     void resetRegionsOverrideLogicalContentHeight();
    81     void markAutoLogicalHeightRegionsForLayout();
    82 
    83     bool needsTwoPassLayoutForAutoHeightRegions() const { return m_needsTwoPassLayoutForAutoHeightRegions; }
    84     void setNeedsTwoPassLayoutForAutoHeightRegions(bool needsTwoPassLayout) { m_needsTwoPassLayoutForAutoHeightRegions = needsTwoPassLayout; }
    85 
    8682protected:
    8783    FlowThreadController(RenderView*);
     84    void updateFlowThreadsChainIfNecessary();
     85    void resetFlowThreadsWithAutoHeightRegions();
    8886
    8987private:
     
    9189    RenderFlowThread* m_currentRenderFlowThread;
    9290    bool m_isRenderNamedFlowThreadOrderDirty;
    93     bool m_needsTwoPassLayoutForAutoHeightRegions;
    9491    unsigned m_flowThreadsWithAutoLogicalHeightRegions;
    9592    OwnPtr<RenderNamedFlowThreadList> m_renderNamedFlowThreadList;
  • trunk/Source/WebCore/rendering/RenderBlock.cpp

    r147250 r147426  
    15901590    if (relayoutForPagination(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher))
    15911591        return;
    1592  
     1592
    15931593    // Calculate our new height.
    15941594    LayoutUnit oldHeight = logicalHeight();
    15951595    LayoutUnit oldClientAfterEdge = clientLogicalBottom();
     1596
     1597    // Before updating the final size of the flow thread make sure a forced break is applied after the content.
     1598    // This ensures the size information is correctly computed for the last auto-height region receiving content.
     1599    if (isRenderFlowThread())
     1600        toRenderFlowThread(this)->applyBreakAfterContent(oldClientAfterEdge);
     1601
    15961602    updateLogicalHeight();
    15971603    LayoutUnit newHeight = logicalHeight();
  • trunk/Source/WebCore/rendering/RenderFlowThread.cpp

    r147414 r147426  
    5656    , m_dispatchRegionLayoutUpdateEvent(false)
    5757    , m_pageLogicalSizeChanged(false)
     58    , m_inConstrainedLayoutPhase(false)
     59    , m_needsTwoPhasesLayout(false)
    5860{
    5961    setFlowThreadState(InsideOutOfFlowThread);
     
    162164                // Also, if we have auto-height regions we can't assume m_regionsHaveUniformLogicalHeight to be true in the first phase
    163165                // because the auto-height regions don't have their height computed yet.
    164                 if (view()->normalLayoutPhase() && region->hasAutoLogicalHeight()) {
     166                if (!inConstrainedLayoutPhase() && region->hasAutoLogicalHeight()) {
    165167                    region->setOverrideLogicalContentHeight(region->maxPageLogicalHeight());
    166168                    m_regionsHaveUniformLogicalHeight = false;
     
    193195
    194196    m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();
     197
     198    // In case this is the second pass of the normal phase we need to update the auto-height regions to their initial value.
     199    // If the region chain was invalidated this will happen anyway.
     200    if (!m_regionsInvalidated && !inConstrainedLayoutPhase())
     201        initializeRegionsOverrideLogicalContentHeight();
     202
    195203    validateRegions();
     204
     205    // This is the first phase of the layout and because we have auto-height regions we'll need a second
     206    // pass to update the flow with the computed auto-height regions.
     207    m_needsTwoPhasesLayout = !inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions();
    196208
    197209    CurrentRenderFlowThreadMaintainer currentFlowThreadSetter(this);
     
    380392            lastValidRegion = region;
    381393
    382         if (region->hasOverrideHeight() && view()->normalLayoutPhase()) {
     394        if (region->hasOverrideHeight() && !inConstrainedLayoutPhase()) {
    383395            accumulatedLogicalHeight += region->overrideLogicalContentHeight();
    384396            if (offset < accumulatedLogicalHeight)
     
    646658}
    647659
    648 void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
    649 {
    650     LayoutUnit height = oldClientAfterEdge;
    651 
    652     LayoutUnit offsetBreakAdjustment = 0;
     660void RenderFlowThread::applyBreakAfterContent(LayoutUnit clientHeight)
     661{
    653662    // Simulate a region break at height. If it points inside an auto logical height region,
    654663    // then it may determine the region override logical content height.
    655     addForcedRegionBreak(height, this, false, &offsetBreakAdjustment);
    656 
    657     // During the normal layout phase of the flow thread all the auto-height regions have the overrideLogicalContentHeight set to max height.
    658     // We need to clear the overrideLogicalContentHeight for all the regions that didn't receive any content, starting with firstEmptyRegion.
    659     RenderRegion* firstEmptyRegion = 0;
    660     if (view()->normalLayoutPhase())
    661         firstEmptyRegion = regionAtBlockOffset(height + offsetBreakAdjustment);
     664    addForcedRegionBreak(clientHeight, this, false);
     665}
     666
     667void RenderFlowThread::computeOverflowStateForRegions(LayoutUnit oldClientAfterEdge)
     668{
     669    LayoutUnit height = oldClientAfterEdge;
    662670
    663671    // FIXME: the visual overflow of middle region (if it is the last one to contain any content in a render flow thread)
     
    670678        height = isHorizontalWritingMode() ? visualOverflowRect().maxY() : visualOverflowRect().maxX();
    671679
    672     bool inEmptyRegionsSection = false;
    673680    RenderRegion* lastReg = lastRegion();
    674681    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
     
    690697            || state == RenderRegion::RegionOverset)
    691698            setDispatchRegionLayoutUpdateEvent(true);
    692 
    693         if (region == firstEmptyRegion)
    694             inEmptyRegionsSection = true;
    695 
    696         // Clear the overrideLogicalContentHeight value for autoheight regions that didn't receive any content.
    697         if (inEmptyRegionsSection && region->hasAutoLogicalHeight())
    698             region->clearOverrideLogicalContentHeight();
    699699    }
    700700
     
    792792#endif
    793793
    794 void RenderFlowThread::resetRegionsOverrideLogicalContentHeight()
    795 {
    796     ASSERT(view()->layoutState());
    797     ASSERT(view()->normalLayoutPhase());
    798 
    799     if (!hasAutoLogicalHeightRegions())
    800         return;
    801 
    802     for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    803         RenderRegion* region = *iter;
    804         if (!region->hasAutoLogicalHeight())
    805             continue;
    806 
    807         region->clearOverrideLogicalContentHeight();
    808         // FIXME: We need to find a way to avoid marking all the regions ancestors for layout
    809         // as we are already inside layout.
    810         region->setNeedsLayout(true);
    811     }
    812     // Make sure we don't skip any region breaks when we do the layout again.
    813     // Using m_regionsInvalidated to force all the RenderFlowThread children do the layout again.
    814     invalidateRegions();
    815 }
    816 
    817794// During the normal layout phase of the named flow the regions are initialized with a height equal to their max-height.
    818795// This way unforced breaks are automatically placed when a region is full and the content height/position correctly estimated.
     
    820797void RenderFlowThread::initializeRegionsOverrideLogicalContentHeight(RenderRegion* startRegion)
    821798{
    822     ASSERT(view()->normalLayoutPhase());
     799    ASSERT(!inConstrainedLayoutPhase());
    823800    if (!hasAutoLogicalHeightRegions())
    824801        return;
     
    834811void RenderFlowThread::markAutoLogicalHeightRegionsForLayout()
    835812{
    836     ASSERT(view()->layoutState());
    837     ASSERT(view()->constrainedFlowThreadsLayoutPhase());
    838 
    839     if (!hasAutoLogicalHeightRegions())
    840         return;
     813    ASSERT(hasAutoLogicalHeightRegions());
    841814
    842815    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
     
    849822        region->setNeedsLayout(true);
    850823    }
    851 
    852     invalidateRegions();
    853 }
    854 
    855 void RenderFlowThread::updateRegionsFlowThreadPortionRect()
    856 {
     824}
     825
     826void RenderFlowThread::updateRegionsFlowThreadPortionRect(const RenderRegion* lastRegionWithContent)
     827{
     828    ASSERT(!lastRegionWithContent || (!inConstrainedLayoutPhase() && hasAutoLogicalHeightRegions()));
    857829    LayoutUnit logicalHeight = 0;
     830    bool emptyRegionsSegment = false;
    858831    for (RenderRegionList::iterator iter = m_regionList.begin(); iter != m_regionList.end(); ++iter) {
    859832        RenderRegion* region = *iter;
     833
     834        // If we find an empty auto-height region, clear the overrideLogicalContentHeight value.
     835        if (emptyRegionsSegment && region->hasAutoLogicalHeight())
     836            region->clearOverrideLogicalContentHeight();
    860837
    861838        LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
     
    866843        region->setFlowThreadPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());
    867844        logicalHeight += regionLogicalHeight;
    868     }
     845
     846        // Once we find the last region with content the next regions are considered empty.
     847        if (lastRegionWithContent == region)
     848            emptyRegionsSegment = true;
     849    }
     850
     851    ASSERT(!lastRegionWithContent || emptyRegionsSegment);
    869852}
    870853
     
    878861    // and we use the content breaks to determine the overrideContentLogicalHeight for
    879862    // auto logical height regions.
    880     if (view()->constrainedFlowThreadsLayoutPhase())
     863    if (inConstrainedLayoutPhase())
    881864        return false;
    882865
     
    903886        return false;
    904887
     888    bool lastBreakAfterContent = breakChild == this;
    905889    bool overrideLogicalContentHeightComputed = false;
    906890
     
    931915
    932916    // If the break was found inside an auto-height region its size changed so we need to recompute the flow thread portion rectangles.
    933     if (overrideLogicalContentHeightComputed)
     917    // Also, if this is the last break after the content we need to clear the overrideLogicalContentHeight value on the last empty regions.
     918    if (hasAutoLogicalHeightRegions() && lastBreakAfterContent)
     919        updateRegionsFlowThreadPortionRect(region);
     920    else if (overrideLogicalContentHeightComputed)
    934921        updateRegionsFlowThreadPortionRect();
    935922
  • trunk/Source/WebCore/rendering/RenderFlowThread.h

    r147414 r147426  
    137137    bool objectInFlowRegion(const RenderObject*, const RenderRegion*) const;
    138138
    139     void resetRegionsOverrideLogicalContentHeight();
    140139    void markAutoLogicalHeightRegionsForLayout();
    141140
    142141    bool addForcedRegionBreak(LayoutUnit, RenderObject* breakChild, bool isBefore, LayoutUnit* offsetBreakAdjustment = 0);
     142    void applyBreakAfterContent(LayoutUnit);
    143143
    144144    bool pageLogicalSizeChanged() const { return m_pageLogicalSizeChanged; }
     
    155155    LayoutRect fragmentsBoundingBox(const LayoutRect& layerBoundingBox);
    156156
     157    void setInConstrainedLayoutPhase(bool value) { m_inConstrainedLayoutPhase = value; }
     158    bool inConstrainedLayoutPhase() const { return m_inConstrainedLayoutPhase; }
     159
     160    bool needsTwoPhasesLayout() const { return m_needsTwoPhasesLayout; }
     161    void clearNeedsTwoPhasesLayout() { m_needsTwoPhasesLayout = false; }
     162
    157163protected:
    158164    virtual const char* renderName() const = 0;
     
    162168    virtual LayoutUnit initialLogicalWidth() const { return 0; };
    163169
    164     void updateRegionsFlowThreadPortionRect();
     170    void updateRegionsFlowThreadPortionRect(const RenderRegion* = 0);
    165171    bool shouldRepaint(const LayoutRect&) const;
    166172    bool regionInRange(const RenderRegion* targetRegion, const RenderRegion* startRegion, const RenderRegion* endRegion) const;
     
    223229    bool m_dispatchRegionLayoutUpdateEvent : 1;
    224230    bool m_pageLogicalSizeChanged : 1;
     231    bool m_inConstrainedLayoutPhase : 1;
     232    bool m_needsTwoPhasesLayout : 1;
    225233};
    226234
  • trunk/Source/WebCore/rendering/RenderRegion.cpp

    r147411 r147426  
    6060LayoutUnit RenderRegion::pageLogicalWidth() const
    6161{
     62    ASSERT(m_flowThread);
    6263    return m_flowThread->isHorizontalWritingMode() ? contentWidth() : contentHeight();
    6364}
     
    6566LayoutUnit RenderRegion::pageLogicalHeight() const
    6667{
    67     if (hasOverrideHeight() && view()->normalLayoutPhase()) {
     68    ASSERT(m_flowThread);
     69    if (hasOverrideHeight() && !m_flowThread->inConstrainedLayoutPhase()) {
    6870        ASSERT(hasAutoLogicalHeight());
    6971        return overrideLogicalContentHeight();
     
    7678LayoutUnit RenderRegion::maxPageLogicalHeight() const
    7779{
    78     ASSERT(hasAutoLogicalHeight() && view()->normalLayoutPhase());
     80    ASSERT(m_flowThread);
     81    ASSERT(hasAutoLogicalHeight() && !m_flowThread->inConstrainedLayoutPhase());
    7982    return style()->logicalMaxHeight().isUndefined() ? LayoutUnit::max() / 2 : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
    8083}
     
    8285LayoutUnit RenderRegion::logicalHeightOfAllFlowThreadContent() const
    8386{
    84     if (hasOverrideHeight() && view()->normalLayoutPhase()) {
     87    ASSERT(m_flowThread);
     88    if (hasOverrideHeight() && !m_flowThread->inConstrainedLayoutPhase()) {
    8589        ASSERT(hasAutoLogicalHeight());
    8690        return overrideLogicalContentHeight();
     
    272276            oldRegionRect = oldRegionRect.transposedRect();
    273277
    274         if (view()->checkTwoPassLayoutForAutoHeightRegions() && hasAutoLogicalHeight())
    275             view()->flowThreadController()->setNeedsTwoPassLayoutForAutoHeightRegions(true);
    276 
    277         if (!isRenderRegionSet() && (oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight())) {
     278        if (hasAutoLogicalHeight() && !m_flowThread->inConstrainedLayoutPhase()) {
    278279            m_flowThread->invalidateRegions();
    279             if (view()->checkTwoPassLayoutForAutoHeightRegions())
    280                 view()->flowThreadController()->setNeedsTwoPassLayoutForAutoHeightRegions(true);
     280            clearOverrideLogicalContentHeight();
     281            return;
    281282        }
     283
     284        if (!isRenderRegionSet() && (oldRegionRect.width() != pageLogicalWidth() || oldRegionRect.height() != pageLogicalHeight()))
     285            // This can happen even if we are in the inConstrainedLayoutPhase and it will trigger a pathological layout of the flow thread.
     286            m_flowThread->invalidateRegions();
    282287    }
    283288
     
    634639    // content height only if the view is in the layout phase
    635640    // in which all the auto logical height regions have their override logical height set.
    636     if (view()->normalLayoutPhase())
     641    if (!m_flowThread->inConstrainedLayoutPhase())
    637642        return;
    638643
  • trunk/Source/WebCore/rendering/RenderView.cpp

    r145126 r147426  
    6969    , m_renderQuoteHead(0)
    7070    , m_renderCounterCount(0)
    71     , m_layoutPhase(RenderViewNormalLayout)
    7271{
    7372    // init RenderObject attributes
     
    207206}
    208207
    209 // The algorithm to layout the flow thread content in auto height regions has to make sure
    210 // that when a two-pass layout is needed, the auto height regions always start the first
    211 // pass without a computed override logical content height (from previous layouts).
    212 // This way, the layout algorithm gives the same result in all situations.
    213 // If the flow thread content does not need layout, the decision of whether we need a full
    214 // two pass layout cannot be made up-front. Therefore, we do a first layout, and if an auto
    215 // height region needs layout or a non-auto height region changes its box dimensions,
    216 // we need to perform a full two pass layout.
     208// The algorithm below assumes this is a full layout. In case there are previously computed values for regions, supplemental steps are taken
     209// to ensure the results are the same as those obtained from a full layout (i.e. the auto-height regions from all the flows are marked as needing
     210// layout).
     211// 1. The flows are laid out from the outer flow to the inner flow. This successfully computes the outer non-auto-height regions size so the
     212// inner flows have the necessary information to correctly fragment the content.
     213// 2. The flows are laid out from the inner flow to the outer flow. After an inner flow is laid out it goes into the constrained layout phase
     214// and marks the auto-height regions they need layout. This means the outer flows will relayout if they depend on regions with auto-height regions
     215// belonging to inner flows. This step will correctly compute the overrideLogicalHeights for the auto-height regions. It's possible for non-auto-height
     216// regions to relayout if they depend on auto-height regions. This will invalidate the inner flow threads and mark them as needing layout.
     217// 3. The last step is to do one last layout if there are pathological dependencies between non-auto-height regions and auto-height regions
     218// as detected in the previous step.
    217219void RenderView::layoutContentInAutoLogicalHeightRegions(const LayoutState& state)
    218220{
    219     ASSERT(!flowThreadController()->needsTwoPassLayoutForAutoHeightRegions());
    220 
    221     if (!flowThreadController()->hasRenderNamedFlowThreadsNeedingLayout()) {
     221    // We need to invalidate all the flows with auto-height regions if one such flow needs layout.
     222    // If none is found we do a layout a check back again afterwards.
     223    if (!flowThreadController()->updateFlowThreadsNeedingLayout()) {
     224        // Do a first layout of the content. In some cases more layouts are not needed (e.g. only flows with non-auto-height regions have changed).
    222225        layoutContent(state);
    223         if (!flowThreadController()->needsTwoPassLayoutForAutoHeightRegions())
     226
     227        // If we find no named flow needing a two step layout after the first layout, exit early.
     228        // Otherwise, initiate the two step layout algorithm and recompute all the flows.
     229        if (!flowThreadController()->updateFlowThreadsNeedingTwoStepLayout())
    224230            return;
    225231    }
    226232
    227     // Start a full two phase layout for regions with auto logical height.
    228     flowThreadController()->resetRegionsOverrideLogicalContentHeight();
     233    // Layout to recompute all the named flows with auto-height regions.
    229234    layoutContent(state);
    230235
    231     m_layoutPhase = ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions;
    232     flowThreadController()->markAutoLogicalHeightRegionsForLayout();
    233     layoutContent(state);
    234     flowThreadController()->setNeedsTwoPassLayoutForAutoHeightRegions(false);
     236    // Propagate the computed auto-height values upwards.
     237    // Non-auto-height regions may invalidate the flow thread because they depended on auto-height regions, but that's ok.
     238    flowThreadController()->updateFlowThreadsIntoConstrainedPhase();
     239
     240    // Do one last layout that should update the auto-height regions found in the main flow
     241    // and solve pathological dependencies between regions (e.g. a non-auto-height region depending
     242    // on an auto-height one).
     243    if (needsLayout())
     244        layoutContent(state);
    235245}
    236246
     
    270280    m_layoutState = &state;
    271281
    272     m_layoutPhase = RenderViewNormalLayout;
    273282    if (checkTwoPassLayoutForAutoHeightRegions())
    274283        layoutContentInAutoLogicalHeightRegions(state);
  • trunk/Source/WebCore/rendering/RenderView.h

    r146642 r147426  
    3232
    3333class FlowThreadController;
     34class RenderQuote;
    3435class RenderWidget;
    35 class RenderQuote;
    3636
    3737#if USE(ACCELERATED_COMPOSITING)
     
    208208    FlowThreadController* flowThreadController();
    209209
    210     enum RenderViewLayoutPhase { RenderViewNormalLayout, ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions };
    211     bool normalLayoutPhase() const { return m_layoutPhase == RenderViewNormalLayout; }
    212     bool constrainedFlowThreadsLayoutPhase() const { return m_layoutPhase == ConstrainedFlowThreadsLayoutInAutoLogicalHeightRegions; }
    213 
    214210    void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
    215211
     
    340336    RenderQuote* m_renderQuoteHead;
    341337    unsigned m_renderCounterCount;
    342     RenderViewLayoutPhase m_layoutPhase;
    343338};
    344339
Note: See TracChangeset for help on using the changeset viewer.