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