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 "core/rendering/LayoutState.h" 28 29 #include "core/platform/Partitions.h" 30 #include "core/rendering/ColumnInfo.h" 31 #include "core/rendering/RenderInline.h" 32 #include "core/rendering/RenderLayer.h" 33 #include "core/rendering/RenderView.h" 34 35 namespace WebCore { 36 37 LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged, ColumnInfo* columnInfo) 38 : m_columnInfo(columnInfo) 39 , m_lineGrid(0) 40 , m_next(prev) 41 , m_shapeInsideInfo(0) 42 #ifndef NDEBUG 43 , m_renderer(renderer) 44 #endif 45 { 46 ASSERT(m_next); 47 48 bool fixed = renderer->isOutOfFlowPositioned() && renderer->style()->position() == FixedPosition; 49 if (fixed) { 50 // FIXME: This doesn't work correctly with transforms. 51 FloatPoint fixedOffset = renderer->view()->localToAbsolute(FloatPoint(), IsFixed); 52 m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset; 53 } else 54 m_paintOffset = prev->m_paintOffset + offset; 55 56 if (renderer->isOutOfFlowPositioned() && !fixed) { 57 if (RenderObject* container = renderer->container()) { 58 if (container->isInFlowPositioned() && container->isRenderInline()) 59 m_paintOffset += toRenderInline(container)->offsetForInFlowPositionedInline(renderer); 60 } 61 } 62 63 m_layoutOffset = m_paintOffset; 64 65 if (renderer->isInFlowPositioned() && renderer->hasLayer()) 66 m_paintOffset += renderer->layer()->offsetForInFlowPosition(); 67 68 m_clipped = !fixed && prev->m_clipped; 69 if (m_clipped) 70 m_clipRect = prev->m_clipRect; 71 72 if (renderer->hasOverflowClip()) { 73 LayoutRect clipRect(toPoint(m_paintOffset) + renderer->view()->layoutDelta(), renderer->cachedSizeForOverflowClip()); 74 if (m_clipped) 75 m_clipRect.intersect(clipRect); 76 else { 77 m_clipRect = clipRect; 78 m_clipped = true; 79 } 80 81 m_paintOffset -= renderer->scrolledContentOffset(); 82 } 83 84 // If we establish a new page height, then cache the offset to the top of the first page. 85 // We can compare this later on to figure out what part of the page we're actually on, 86 if (pageLogicalHeight || m_columnInfo || renderer->isRenderFlowThread()) { 87 m_pageLogicalHeight = pageLogicalHeight; 88 bool isFlipped = renderer->style()->isFlippedBlocksWritingMode(); 89 m_pageOffset = LayoutSize(m_layoutOffset.width() + (!isFlipped ? renderer->borderLeft() + renderer->paddingLeft() : renderer->borderRight() + renderer->paddingRight()), 90 m_layoutOffset.height() + (!isFlipped ? renderer->borderTop() + renderer->paddingTop() : renderer->borderBottom() + renderer->paddingBottom())); 91 m_pageLogicalHeightChanged = pageLogicalHeightChanged; 92 } else { 93 // If we don't establish a new page height, then propagate the old page height and offset down. 94 m_pageLogicalHeight = m_next->m_pageLogicalHeight; 95 m_pageLogicalHeightChanged = m_next->m_pageLogicalHeightChanged; 96 m_pageOffset = m_next->m_pageOffset; 97 98 // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and 99 // writing mode roots. 100 if (renderer->isUnsplittableForPagination()) 101 m_pageLogicalHeight = 0; 102 } 103 104 // Propagate line grid information. 105 propagateLineGridInfo(renderer); 106 107 if (!m_columnInfo) 108 m_columnInfo = m_next->m_columnInfo; 109 110 if (renderer->isRenderBlock()) { 111 const RenderBlock* renderBlock = toRenderBlock(renderer); 112 m_shapeInsideInfo = renderBlock->shapeInsideInfo(); 113 if (!m_shapeInsideInfo && m_next->m_shapeInsideInfo && renderBlock->allowsShapeInsideInfoSharing()) 114 m_shapeInsideInfo = m_next->m_shapeInsideInfo; 115 } 116 117 m_layoutDelta = m_next->m_layoutDelta; 118 #if !ASSERT_DISABLED 119 m_layoutDeltaXSaturated = m_next->m_layoutDeltaXSaturated; 120 m_layoutDeltaYSaturated = m_next->m_layoutDeltaYSaturated; 121 #endif 122 123 m_isPaginated = m_pageLogicalHeight || m_columnInfo || renderer->isRenderFlowThread(); 124 125 if (lineGrid() && renderer->hasColumns() && renderer->style()->hasInlineColumnAxis()) 126 computeLineGridPaginationOrigin(renderer); 127 128 // If we have a new grid to track, then add it to our set. 129 if (renderer->style()->lineGrid() != RenderStyle::initialLineGrid() && renderer->isBlockFlow()) 130 establishLineGrid(toRenderBlock(renderer)); 131 132 // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present. 133 } 134 135 LayoutState::LayoutState(RenderObject* root) 136 : m_clipped(false) 137 , m_isPaginated(false) 138 , m_pageLogicalHeightChanged(false) 139 #if !ASSERT_DISABLED 140 , m_layoutDeltaXSaturated(false) 141 , m_layoutDeltaYSaturated(false) 142 #endif 143 , m_columnInfo(0) 144 , m_lineGrid(0) 145 , m_next(0) 146 , m_shapeInsideInfo(0) 147 , m_pageLogicalHeight(0) 148 #ifndef NDEBUG 149 , m_renderer(root) 150 #endif 151 { 152 RenderObject* container = root->container(); 153 FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms); 154 m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y()); 155 156 if (container->hasOverflowClip()) { 157 m_clipped = true; 158 RenderBox* containerBox = toRenderBox(container); 159 m_clipRect = LayoutRect(toPoint(m_paintOffset), containerBox->cachedSizeForOverflowClip()); 160 m_paintOffset -= containerBox->scrolledContentOffset(); 161 } 162 } 163 164 void* LayoutState::operator new(size_t sz) 165 { 166 return partitionAlloc(Partitions::getRenderingPartition(), sz); 167 } 168 169 void LayoutState::operator delete(void* ptr) 170 { 171 partitionFree(ptr); 172 } 173 174 void LayoutState::clearPaginationInformation() 175 { 176 m_pageLogicalHeight = m_next->m_pageLogicalHeight; 177 m_pageOffset = m_next->m_pageOffset; 178 m_columnInfo = m_next->m_columnInfo; 179 } 180 181 LayoutUnit LayoutState::pageLogicalOffset(RenderBox* child, LayoutUnit childLogicalOffset) const 182 { 183 if (child->isHorizontalWritingMode()) 184 return m_layoutOffset.height() + childLogicalOffset - m_pageOffset.height(); 185 return m_layoutOffset.width() + childLogicalOffset - m_pageOffset.width(); 186 } 187 188 void LayoutState::addForcedColumnBreak(RenderBox* child, LayoutUnit childLogicalOffset) 189 { 190 if (!m_columnInfo || m_columnInfo->columnHeight()) 191 return; 192 m_columnInfo->addForcedBreak(pageLogicalOffset(child, childLogicalOffset)); 193 } 194 195 void LayoutState::propagateLineGridInfo(RenderBox* renderer) 196 { 197 // Disable line grids for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and 198 // writing mode roots. 199 if (!m_next || renderer->isUnsplittableForPagination()) 200 return; 201 202 m_lineGrid = m_next->m_lineGrid; 203 m_lineGridOffset = m_next->m_lineGridOffset; 204 m_lineGridPaginationOrigin = m_next->m_lineGridPaginationOrigin; 205 } 206 207 void LayoutState::establishLineGrid(RenderBlock* block) 208 { 209 // First check to see if this grid has been established already. 210 if (m_lineGrid) { 211 if (m_lineGrid->style()->lineGrid() == block->style()->lineGrid()) 212 return; 213 RenderBlock* currentGrid = m_lineGrid; 214 for (LayoutState* currentState = m_next; currentState; currentState = currentState->m_next) { 215 if (currentState->m_lineGrid == currentGrid) 216 continue; 217 currentGrid = currentState->m_lineGrid; 218 if (!currentGrid) 219 break; 220 if (currentGrid->style()->lineGrid() == block->style()->lineGrid()) { 221 m_lineGrid = currentGrid; 222 m_lineGridOffset = currentState->m_lineGridOffset; 223 return; 224 } 225 } 226 } 227 228 // We didn't find an already-established grid with this identifier. Our render object establishes the grid. 229 m_lineGrid = block; 230 m_lineGridOffset = m_layoutOffset; 231 } 232 233 void LayoutState::computeLineGridPaginationOrigin(RenderBox* renderer) 234 { 235 // We need to cache a line grid pagination origin so that we understand how to reset the line grid 236 // at the top of each column. 237 // Get the current line grid and offset. 238 if (!lineGrid() || lineGrid()->style()->writingMode() != renderer->style()->writingMode()) 239 return; 240 241 // Get the hypothetical line box used to establish the grid. 242 RootInlineBox* lineGridBox = lineGrid()->lineGridBox(); 243 if (!lineGridBox) 244 return; 245 246 bool isHorizontalWritingMode = lineGrid()->isHorizontalWritingMode(); 247 248 LayoutUnit lineGridBlockOffset = isHorizontalWritingMode ? lineGridOffset().height() : lineGridOffset().width(); 249 250 // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple 251 // as established by the line box. 252 // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume 253 // the grid should honor line-box-contain. 254 LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); 255 if (!gridLineHeight) 256 return; 257 258 LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); 259 260 if (isPaginated() && pageLogicalHeight()) { 261 LayoutUnit pageLogicalTop = renderer->isHorizontalWritingMode() ? m_pageOffset.height() : m_pageOffset.width(); 262 if (pageLogicalTop > firstLineTopWithLeading) { 263 // Shift to the next highest line grid multiple past the page logical top. Cache the delta 264 // between this new value and the page logical top as the pagination origin. 265 LayoutUnit remainder = roundToInt(pageLogicalTop - firstLineTopWithLeading) % roundToInt(gridLineHeight); 266 LayoutUnit paginationDelta = gridLineHeight - remainder; 267 if (isHorizontalWritingMode) 268 m_lineGridPaginationOrigin.setHeight(paginationDelta); 269 else 270 m_lineGridPaginationOrigin.setWidth(paginationDelta); 271 } 272 } 273 } 274 275 } // namespace WebCore 276