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