1/* 2 * Copyright (C) 2007 Apple 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "LayoutState.h" 28 29#include "ColumnInfo.h" 30#include "RenderArena.h" 31#include "RenderFlowThread.h" 32#include "RenderInline.h" 33#include "RenderLayer.h" 34#include "RenderView.h" 35 36namespace WebCore { 37 38LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged, ColumnInfo* columnInfo) 39 : m_columnInfo(columnInfo) 40 , m_lineGrid(0) 41 , m_next(prev) 42#if ENABLE(CSS_SHAPES) 43 , m_shapeInsideInfo(0) 44#endif 45#ifndef NDEBUG 46 , m_renderer(renderer) 47#endif 48{ 49 ASSERT(m_next); 50 51 bool fixed = renderer->isOutOfFlowPositioned() && renderer->style()->position() == FixedPosition; 52 if (fixed) { 53 // FIXME: This doesn't work correctly with transforms. 54 FloatPoint fixedOffset = renderer->view()->localToAbsolute(FloatPoint(), IsFixed); 55 m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset; 56 } else 57 m_paintOffset = prev->m_paintOffset + offset; 58 59 if (renderer->isOutOfFlowPositioned() && !fixed) { 60 if (RenderObject* container = renderer->container()) { 61 if (container->isInFlowPositioned() && container->isRenderInline()) 62 m_paintOffset += toRenderInline(container)->offsetForInFlowPositionedInline(renderer); 63 } 64 } 65 66 m_layoutOffset = m_paintOffset; 67 68 if (renderer->hasPaintOffset() && renderer->hasLayer()) 69 m_paintOffset += renderer->layer()->paintOffset(); 70 71 m_clipped = !fixed && prev->m_clipped; 72 if (m_clipped) 73 m_clipRect = prev->m_clipRect; 74 75 if (renderer->hasOverflowClip()) { 76 LayoutRect clipRect(toPoint(m_paintOffset) + renderer->view()->layoutDelta(), renderer->cachedSizeForOverflowClip()); 77 if (m_clipped) 78 m_clipRect.intersect(clipRect); 79 else { 80 m_clipRect = clipRect; 81 m_clipped = true; 82 } 83 84 m_paintOffset -= renderer->scrolledContentOffset(); 85 } 86 87 // If we establish a new page height, then cache the offset to the top of the first page. 88 // We can compare this later on to figure out what part of the page we're actually on, 89 if (pageLogicalHeight || m_columnInfo || renderer->isRenderFlowThread()) { 90 m_pageLogicalHeight = pageLogicalHeight; 91 bool isFlipped = renderer->style()->isFlippedBlocksWritingMode(); 92 m_pageOffset = LayoutSize(m_layoutOffset.width() + (!isFlipped ? renderer->borderLeft() + renderer->paddingLeft() : renderer->borderRight() + renderer->paddingRight()), 93 m_layoutOffset.height() + (!isFlipped ? renderer->borderTop() + renderer->paddingTop() : renderer->borderBottom() + renderer->paddingBottom())); 94 m_pageLogicalHeightChanged = pageLogicalHeightChanged; 95 } else { 96 // If we don't establish a new page height, then propagate the old page height and offset down. 97 m_pageLogicalHeight = m_next->m_pageLogicalHeight; 98 m_pageLogicalHeightChanged = m_next->m_pageLogicalHeightChanged; 99 m_pageOffset = m_next->m_pageOffset; 100 101 // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and 102 // writing mode roots. 103 if (renderer->isUnsplittableForPagination()) 104 m_pageLogicalHeight = 0; 105 } 106 107 // Propagate line grid information. 108 propagateLineGridInfo(renderer); 109 110 if (!m_columnInfo) 111 m_columnInfo = m_next->m_columnInfo; 112 113#if ENABLE(CSS_SHAPES) 114 if (renderer->isRenderBlock()) { 115 const RenderBlock* renderBlock = toRenderBlock(renderer); 116 m_shapeInsideInfo = renderBlock->shapeInsideInfo(); 117 if (!m_shapeInsideInfo && m_next->m_shapeInsideInfo && renderBlock->allowsShapeInsideInfoSharing()) 118 m_shapeInsideInfo = m_next->m_shapeInsideInfo; 119 } 120#endif 121 122 m_layoutDelta = m_next->m_layoutDelta; 123#if !ASSERT_DISABLED && ENABLE(SATURATED_LAYOUT_ARITHMETIC) 124 m_layoutDeltaXSaturated = m_next->m_layoutDeltaXSaturated; 125 m_layoutDeltaYSaturated = m_next->m_layoutDeltaYSaturated; 126#endif 127 128 m_isPaginated = m_pageLogicalHeight || m_columnInfo || renderer->isRenderFlowThread(); 129 130 if (lineGrid() && renderer->hasColumns() && renderer->style()->hasInlineColumnAxis()) 131 computeLineGridPaginationOrigin(renderer); 132 133 // If we have a new grid to track, then add it to our set. 134 if (renderer->style()->lineGrid() != RenderStyle::initialLineGrid() && renderer->isBlockFlow()) 135 establishLineGrid(toRenderBlock(renderer)); 136 137 // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present. 138} 139 140LayoutState::LayoutState(RenderObject* root) 141 : m_clipped(false) 142 , m_isPaginated(false) 143 , m_pageLogicalHeightChanged(false) 144#if !ASSERT_DISABLED && ENABLE(SATURATED_LAYOUT_ARITHMETIC) 145 , m_layoutDeltaXSaturated(false) 146 , m_layoutDeltaYSaturated(false) 147#endif 148 , m_columnInfo(0) 149 , m_lineGrid(0) 150 , m_next(0) 151#if ENABLE(CSS_SHAPES) 152 , m_shapeInsideInfo(0) 153#endif 154 , m_pageLogicalHeight(0) 155#ifndef NDEBUG 156 , m_renderer(root) 157#endif 158{ 159 RenderObject* container = root->container(); 160 FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms); 161 m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y()); 162 163 if (container->hasOverflowClip()) { 164 m_clipped = true; 165 RenderBox* containerBox = toRenderBox(container); 166 m_clipRect = LayoutRect(toPoint(m_paintOffset), containerBox->cachedSizeForOverflowClip()); 167 m_paintOffset -= containerBox->scrolledContentOffset(); 168 } 169} 170 171#ifndef NDEBUG 172static bool inLayoutStateDestroy; 173#endif 174 175void LayoutState::destroy(RenderArena* renderArena) 176{ 177#ifndef NDEBUG 178 inLayoutStateDestroy = true; 179#endif 180 delete this; 181#ifndef NDEBUG 182 inLayoutStateDestroy = false; 183#endif 184 renderArena->free(*(size_t*)this, this); 185} 186 187void* LayoutState::operator new(size_t sz, RenderArena* renderArena) 188{ 189 return renderArena->allocate(sz); 190} 191 192void LayoutState::operator delete(void* ptr, size_t sz) 193{ 194 ASSERT(inLayoutStateDestroy); 195 *(size_t*)ptr = sz; 196} 197 198void LayoutState::clearPaginationInformation() 199{ 200 m_pageLogicalHeight = m_next->m_pageLogicalHeight; 201 m_pageOffset = m_next->m_pageOffset; 202 m_columnInfo = m_next->m_columnInfo; 203} 204 205LayoutUnit LayoutState::pageLogicalOffset(RenderBox* child, LayoutUnit childLogicalOffset) const 206{ 207 if (child->isHorizontalWritingMode()) 208 return m_layoutOffset.height() + childLogicalOffset - m_pageOffset.height(); 209 return m_layoutOffset.width() + childLogicalOffset - m_pageOffset.width(); 210} 211 212void LayoutState::addForcedColumnBreak(RenderBox* child, LayoutUnit childLogicalOffset) 213{ 214 if (!m_columnInfo || m_columnInfo->columnHeight()) 215 return; 216 m_columnInfo->addForcedBreak(pageLogicalOffset(child, childLogicalOffset)); 217} 218 219void LayoutState::propagateLineGridInfo(RenderBox* renderer) 220{ 221 // Disable line grids for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and 222 // writing mode roots. 223 if (!m_next || renderer->isUnsplittableForPagination()) 224 return; 225 226 m_lineGrid = m_next->m_lineGrid; 227 m_lineGridOffset = m_next->m_lineGridOffset; 228 m_lineGridPaginationOrigin = m_next->m_lineGridPaginationOrigin; 229} 230 231void LayoutState::establishLineGrid(RenderBlock* block) 232{ 233 // First check to see if this grid has been established already. 234 if (m_lineGrid) { 235 if (m_lineGrid->style()->lineGrid() == block->style()->lineGrid()) 236 return; 237 RenderBlock* currentGrid = m_lineGrid; 238 for (LayoutState* currentState = m_next; currentState; currentState = currentState->m_next) { 239 if (currentState->m_lineGrid == currentGrid) 240 continue; 241 currentGrid = currentState->m_lineGrid; 242 if (!currentGrid) 243 break; 244 if (currentGrid->style()->lineGrid() == block->style()->lineGrid()) { 245 m_lineGrid = currentGrid; 246 m_lineGridOffset = currentState->m_lineGridOffset; 247 return; 248 } 249 } 250 } 251 252 // We didn't find an already-established grid with this identifier. Our render object establishes the grid. 253 m_lineGrid = block; 254 m_lineGridOffset = m_layoutOffset; 255} 256 257void LayoutState::computeLineGridPaginationOrigin(RenderBox* renderer) 258{ 259 // We need to cache a line grid pagination origin so that we understand how to reset the line grid 260 // at the top of each column. 261 // Get the current line grid and offset. 262 if (!lineGrid() || lineGrid()->style()->writingMode() != renderer->style()->writingMode()) 263 return; 264 265 // Get the hypothetical line box used to establish the grid. 266 RootInlineBox* lineGridBox = lineGrid()->lineGridBox(); 267 if (!lineGridBox) 268 return; 269 270 bool isHorizontalWritingMode = lineGrid()->isHorizontalWritingMode(); 271 272 LayoutUnit lineGridBlockOffset = isHorizontalWritingMode ? lineGridOffset().height() : lineGridOffset().width(); 273 274 // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple 275 // as established by the line box. 276 // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume 277 // the grid should honor line-box-contain. 278 LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); 279 if (!gridLineHeight) 280 return; 281 282 LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); 283 284 if (isPaginated() && pageLogicalHeight()) { 285 LayoutUnit pageLogicalTop = renderer->isHorizontalWritingMode() ? m_pageOffset.height() : m_pageOffset.width(); 286 if (pageLogicalTop > firstLineTopWithLeading) { 287 // Shift to the next highest line grid multiple past the page logical top. Cache the delta 288 // between this new value and the page logical top as the pagination origin. 289 LayoutUnit remainder = roundToInt(pageLogicalTop - firstLineTopWithLeading) % roundToInt(gridLineHeight); 290 LayoutUnit paginationDelta = gridLineHeight - remainder; 291 if (isHorizontalWritingMode) 292 m_lineGridPaginationOrigin.setHeight(paginationDelta); 293 else 294 m_lineGridPaginationOrigin.setWidth(paginationDelta); 295 } 296 } 297} 298 299} // namespace WebCore 300