Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/rendering/RenderLazyBlock.h"
     33 
     34 #include "core/rendering/LayoutRepainter.h"
     35 #include "core/rendering/RenderLayer.h"
     36 #include "core/rendering/RenderView.h"
     37 
     38 namespace WebCore {
     39 
     40 RenderLazyBlock::RenderLazyBlock(Element* element)
     41     : RenderBlock(element)
     42     , m_next(0)
     43     , m_previous(0)
     44     , m_firstVisibleChildBox(0)
     45     , m_lastVisibleChildBox(0)
     46     , m_attached(false)
     47     , m_isNestedLayout(false)
     48 {
     49     setChildrenInline(false); // All of our children must be block-level.
     50 }
     51 
     52 RenderLazyBlock::~RenderLazyBlock()
     53 {
     54     ASSERT(!m_attached);
     55 }
     56 
     57 void RenderLazyBlock::willBeDestroyed()
     58 {
     59     detachLazyBlock();
     60     RenderBlock::willBeDestroyed();
     61 }
     62 
     63 void RenderLazyBlock::willBeRemovedFromTree()
     64 {
     65     RenderBlock::willBeRemovedFromTree();
     66     detachLazyBlock();
     67 }
     68 
     69 bool RenderLazyBlock::isNested() const
     70 {
     71     for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
     72         if (ancestor->isRenderLazyBlock())
     73             return true;
     74     }
     75     return false;
     76 }
     77 
     78 // FIXME: This method and detachLazyBlock are essentially identical to
     79 // RenderQuote::attachQuote and detachQuote. We should just have a
     80 // RenderTreeOrderedList that does this stuff internally.
     81 void RenderLazyBlock::attachLazyBlock()
     82 {
     83     ASSERT(view());
     84     ASSERT(!m_attached);
     85     ASSERT(!m_next && !m_previous);
     86     ASSERT(isRooted());
     87 
     88     if (!view()->firstLazyBlock()) {
     89         view()->setFirstLazyBlock(this);
     90         m_attached = true;
     91         return;
     92     }
     93 
     94     for (RenderObject* predecessor = previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
     95         if (!predecessor->isRenderLazyBlock() || !toRenderLazyBlock(predecessor)->isAttached())
     96             continue;
     97         m_previous = toRenderLazyBlock(predecessor);
     98         m_next = m_previous->m_next;
     99         m_previous->m_next = this;
    100         if (m_next)
    101             m_next->m_previous = this;
    102         break;
    103     }
    104 
    105     if (!m_previous) {
    106         m_next = view()->firstLazyBlock();
    107         view()->setFirstLazyBlock(this);
    108         if (m_next)
    109             m_next->m_previous = this;
    110     }
    111     m_attached = true;
    112 
    113     ASSERT(!m_next || m_next->m_attached);
    114     ASSERT(!m_next || m_next->m_previous == this);
    115     ASSERT(!m_previous || m_previous->m_attached);
    116     ASSERT(!m_previous || m_previous->m_next == this);
    117 }
    118 
    119 void RenderLazyBlock::detachLazyBlock()
    120 {
    121     ASSERT(!m_next || m_next->m_attached);
    122     ASSERT(!m_previous || m_previous->m_attached);
    123     if (!m_attached)
    124         return;
    125     if (m_previous)
    126         m_previous->m_next = m_next;
    127     else if (view())
    128         view()->setFirstLazyBlock(m_next);
    129     if (m_next)
    130         m_next->m_previous = m_previous;
    131     m_attached = false;
    132     m_next = 0;
    133     m_previous = 0;
    134 }
    135 
    136 void RenderLazyBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    137 {
    138     for (RenderBox* child = m_firstVisibleChildBox; child && child != m_lastVisibleChildBox; child = child->nextSiblingBox())
    139         paintChild(child, paintInfo, paintOffset);
    140 }
    141 
    142 void RenderLazyBlock::layoutChildren(bool relayoutChildren)
    143 {
    144     LayoutUnit afterEdge = borderAfter() + paddingAfter() + scrollbarLogicalHeight();
    145     LayoutUnit height = borderBefore() + paddingBefore();
    146     LayoutRect intersectRect = m_intersectRect;
    147 
    148     // FIXME: If we already have m_firstVisibleChildBox we should start there
    149     // and stop when we have enough to fill the viewport. This catches the most
    150     // common cases of scrolling upward or downward.
    151 
    152     m_firstVisibleChildBox = 0;
    153     m_lastVisibleChildBox = 0;
    154 
    155     // FIXME: This should approximate the height so we don't actually need to walk
    156     // every child and can optimistically layout children until we fill the
    157     // the expandedViewportRect.
    158 
    159     setLogicalHeight(height);
    160     int childCount = 0;
    161     LayoutUnit heightOfChildren = 0;
    162     for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
    163         ++childCount;
    164         updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, child);
    165 
    166         if (relayoutChildren)
    167             child->setNeedsLayout(MarkOnlyThis);
    168 
    169         if (child->style()->logicalHeight().isSpecified()) {
    170             LogicalExtentComputedValues computedValues;
    171             child->computeLogicalHeight(-1, height, computedValues);
    172             child->setLogicalHeight(computedValues.m_extent);
    173             heightOfChildren += computedValues.m_extent;
    174         } else {
    175             // FIXME: Enable guessing about height so we don't need to do layout
    176             // on every non fixed height child.
    177             setLogicalHeight(height);
    178             setLogicalTopForChild(child, height);
    179             if (heightOfChildren && child->needsLayout())
    180                 child->setLogicalHeight(heightOfChildren / childCount);
    181             else
    182                 child->layoutIfNeeded();
    183             heightOfChildren += child->logicalHeight();
    184         }
    185 
    186         intersectRect.setHeight(child->logicalHeight());
    187 
    188         if (m_expandedViewportRect.intersects(enclosingIntRect(intersectRect))) {
    189             if (!m_firstVisibleChildBox)
    190                 m_firstVisibleChildBox = child;
    191             m_lastVisibleChildBox = child->nextSiblingBox();
    192             setLogicalHeight(height);
    193             setLogicalTopForChild(child, height);
    194             child->layoutIfNeeded();
    195             // FIXME: Track how far off our estimated height is from the actual height,
    196             // and adjust the scrollbars accordingly.
    197         }
    198 
    199         intersectRect.setY(intersectRect.y() + child->logicalHeight());
    200         height += child->logicalHeight();
    201     }
    202 
    203     setLogicalHeight(height + afterEdge);
    204 
    205     updateLogicalHeight();
    206 }
    207 
    208 void RenderLazyBlock::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight)
    209 {
    210     ASSERT(needsLayout());
    211 
    212     if (!m_attached)
    213         attachLazyBlock();
    214 
    215     // FIXME: We should adjust the style to disallow columns too.
    216     ASSERT(!hasColumns());
    217 
    218     LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
    219     LayoutStateMaintainer statePusher(view(), this, locationOffset(), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode());
    220 
    221     // FIXME: The compositor can instead give us a list of rects it thinks
    222     // are important.
    223     IntRect expandedViewportRect = view()->frameView()->visibleContentRect(ScrollableArea::IncludeScrollbars);
    224     expandedViewportRect.move(-4000, -4000);
    225     expandedViewportRect.expand(8000, 8000);
    226 
    227     // FIXME: We probably want a RenderGeometryMap instead since we need to handle
    228     // rotation of the RenderLazyBlock and other transforms.
    229     Vector<FloatQuad> quads;
    230     enclosingBoxModelObject()->absoluteQuads(quads);
    231     LayoutRect intersectRect = quads[0].enclosingBoundingBox();
    232     if (hasOverflowClip())
    233         intersectRect.move(-scrolledContentOffset());
    234 
    235     // Avoid doing work inside the nested layout if we know everything is correct already.
    236     if (!m_isNestedLayout || m_intersectRect != intersectRect || m_expandedViewportRect != expandedViewportRect) {
    237         m_intersectRect = intersectRect;
    238         m_expandedViewportRect = expandedViewportRect;
    239         layoutChildren(relayoutChildren || updateLogicalWidthAndColumnWidth());
    240     }
    241 
    242     statePusher.pop();
    243     updateLayerTransform();
    244     updateScrollInfoAfterLayout();
    245     repainter.repaintAfterLayout();
    246 
    247     m_isNestedLayout = false;
    248     clearNeedsLayout();
    249 }
    250 
    251 } // namespace WebCore
    252