2 * Copyright (C) 2007, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "LayoutState.h"
29 #include "LayoutContext.h"
30 #include "RenderFragmentedFlow.h"
31 #include "RenderInline.h"
32 #include "RenderLayer.h"
33 #include "RenderMultiColumnFlow.h"
34 #include "RenderView.h"
38 LayoutState::LayoutState(RenderElement& renderer)
40 , m_isPaginated(false)
41 , m_pageLogicalHeightChanged(false)
43 , m_layoutDeltaXSaturated(false)
44 , m_layoutDeltaYSaturated(false)
47 , m_renderer(&renderer)
50 if (RenderElement* container = renderer.container()) {
51 FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms);
52 m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y());
54 if (container->hasOverflowClip()) {
56 auto& containerBox = downcast<RenderBox>(*container);
57 m_clipRect = LayoutRect(toLayoutPoint(m_paintOffset), containerBox.cachedSizeForOverflowClip());
58 m_paintOffset -= toLayoutSize(containerBox.scrollPosition());
63 LayoutState::LayoutState(std::unique_ptr<LayoutState> ancestor, RenderBox& renderer, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged)
64 : m_ancestor(WTFMove(ancestor))
66 , m_isPaginated(false)
67 , m_pageLogicalHeightChanged(false)
69 , m_layoutDeltaXSaturated(false)
70 , m_layoutDeltaYSaturated(false)
73 , m_renderer(&renderer)
77 computeOffsets(renderer, offset);
78 computeClipRect(renderer);
80 computePaginationInformation(renderer, pageLogicalHeight, pageLogicalHeightChanged);
83 void LayoutState::computeOffsets(RenderBox& renderer, LayoutSize offset)
87 bool fixed = renderer.isFixedPositioned();
89 FloatPoint fixedOffset = renderer.view().localToAbsolute(FloatPoint(), IsFixed);
90 m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset;
92 m_paintOffset = m_ancestor->m_paintOffset + offset;
94 if (renderer.isOutOfFlowPositioned() && !fixed) {
95 if (auto* container = renderer.container()) {
96 if (container->isInFlowPositioned() && is<RenderInline>(*container))
97 m_paintOffset += downcast<RenderInline>(*container).offsetForInFlowPositionedInline(&renderer);
101 m_layoutOffset = m_paintOffset;
103 if (renderer.isInFlowPositioned() && renderer.hasLayer())
104 m_paintOffset += renderer.layer()->offsetForInFlowPosition();
106 if (renderer.hasOverflowClip())
107 m_paintOffset -= toLayoutSize(renderer.scrollPosition());
109 m_layoutDelta = m_ancestor->m_layoutDelta;
111 m_layoutDeltaXSaturated = m_ancestor->m_layoutDeltaXSaturated;
112 m_layoutDeltaYSaturated = m_ancestor->m_layoutDeltaYSaturated;
116 void LayoutState::computeClipRect(RenderBox& renderer)
120 m_clipped = !renderer.isFixedPositioned() && m_ancestor->m_clipped;
122 m_clipRect = m_ancestor->m_clipRect;
123 if (!renderer.hasOverflowClip())
126 LayoutRect clipRect(toLayoutPoint(m_paintOffset) + renderer.view().frameView().layoutContext().layoutDelta(), renderer.cachedSizeForOverflowClip());
128 m_clipRect.intersect(clipRect);
130 m_clipRect = clipRect;
132 // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present.
135 void LayoutState::computePaginationInformation(RenderBox& renderer, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged)
137 // If we establish a new page height, then cache the offset to the top of the first page.
138 // We can compare this later on to figure out what part of the page we're actually on,
139 if (pageLogicalHeight || renderer.isRenderFragmentedFlow()) {
140 m_pageLogicalHeight = pageLogicalHeight;
141 bool isFlipped = renderer.style().isFlippedBlocksWritingMode();
142 m_pageOffset = LayoutSize(m_layoutOffset.width() + (!isFlipped ? renderer.borderLeft() + renderer.paddingLeft() : renderer.borderRight() + renderer.paddingRight()), m_layoutOffset.height() + (!isFlipped ? renderer.borderTop() + renderer.paddingTop() : renderer.borderBottom() + renderer.paddingBottom()));
143 m_pageLogicalHeightChanged = pageLogicalHeightChanged;
144 m_isPaginated = true;
145 } else if (m_ancestor) {
146 // If we don't establish a new page height, then propagate the old page height and offset down.
147 m_pageLogicalHeight = m_ancestor->m_pageLogicalHeight;
148 m_pageLogicalHeightChanged = m_ancestor->m_pageLogicalHeightChanged;
149 m_pageOffset = m_ancestor->m_pageOffset;
151 // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and writing mode roots.
152 if (renderer.isUnsplittableForPagination()) {
153 m_pageLogicalHeight = 0;
154 m_isPaginated = false;
156 m_isPaginated = m_pageLogicalHeight || renderer.enclosingFragmentedFlow();
159 // Propagate line grid information.
160 propagateLineGridInfo(renderer);
162 if (lineGrid() && (lineGrid()->style().writingMode() == renderer.style().writingMode()) && is<RenderMultiColumnFlow>(renderer))
163 downcast<RenderMultiColumnFlow>(renderer).computeLineGridPaginationOrigin(*this);
165 // If we have a new grid to track, then add it to our set.
166 if (renderer.style().lineGrid() != RenderStyle::initialLineGrid() && is<RenderBlockFlow>(renderer))
167 establishLineGrid(downcast<RenderBlockFlow>(renderer));
170 void LayoutState::clearPaginationInformation()
172 m_pageLogicalHeight = m_ancestor->m_pageLogicalHeight;
173 m_pageOffset = m_ancestor->m_pageOffset;
176 LayoutUnit LayoutState::pageLogicalOffset(RenderBox* child, LayoutUnit childLogicalOffset) const
178 if (child->isHorizontalWritingMode())
179 return m_layoutOffset.height() + childLogicalOffset - m_pageOffset.height();
180 return m_layoutOffset.width() + childLogicalOffset - m_pageOffset.width();
183 void LayoutState::propagateLineGridInfo(RenderBox& renderer)
185 // Disable line grids for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and
186 // writing mode roots.
187 if (!m_ancestor || renderer.isUnsplittableForPagination())
190 m_lineGrid = m_ancestor->m_lineGrid;
191 m_lineGridOffset = m_ancestor->m_lineGridOffset;
192 m_lineGridPaginationOrigin = m_ancestor->m_lineGridPaginationOrigin;
195 void LayoutState::establishLineGrid(RenderBlockFlow& renderer)
197 // First check to see if this grid has been established already.
199 if (m_lineGrid->style().lineGrid() == renderer.style().lineGrid())
201 RenderBlockFlow* currentGrid = m_lineGrid;
202 for (LayoutState* currentState = m_ancestor.get(); currentState; currentState = currentState->m_ancestor.get()) {
203 if (currentState->m_lineGrid == currentGrid)
205 currentGrid = currentState->m_lineGrid;
208 if (currentGrid->style().lineGrid() == renderer.style().lineGrid()) {
209 m_lineGrid = currentGrid;
210 m_lineGridOffset = currentState->m_lineGridOffset;
216 // We didn't find an already-established grid with this identifier. Our render object establishes the grid.
217 m_lineGrid = &renderer;
218 m_lineGridOffset = m_layoutOffset;
221 void LayoutState::addLayoutDelta(LayoutSize delta)
223 m_layoutDelta += delta;
225 m_layoutDeltaXSaturated |= m_layoutDelta.width() == LayoutUnit::max() || m_layoutDelta.width() == LayoutUnit::min();
226 m_layoutDeltaYSaturated |= m_layoutDelta.height() == LayoutUnit::max() || m_layoutDelta.height() == LayoutUnit::min();
231 bool LayoutState::layoutDeltaMatches(LayoutSize delta)
233 return (delta.width() == m_layoutDelta.width() || m_layoutDeltaXSaturated) && (delta.height() == m_layoutDelta.height() || m_layoutDeltaYSaturated);
237 LayoutStateMaintainer::LayoutStateMaintainer(RenderBox& root, LayoutSize offset, bool disablePaintOffsetCache, LayoutUnit pageHeight, bool pageHeightChanged)
238 : m_layoutContext(root.view().frameView().layoutContext())
239 , m_paintOffsetCacheIsDisabled(disablePaintOffsetCache)
241 push(root, offset, pageHeight, pageHeightChanged);
244 LayoutStateMaintainer::LayoutStateMaintainer(LayoutContext& layoutContext)
245 : m_layoutContext(layoutContext)
249 LayoutStateMaintainer::~LayoutStateMaintainer()
251 // FIXME: Remove conditional push/pop.
252 if (m_didCallPush && !m_didCallPop)
254 ASSERT(!m_didCallPush || m_didCallPush == m_didCallPop);
257 void LayoutStateMaintainer::push(RenderBox& root, LayoutSize offset, LayoutUnit pageHeight, bool pageHeightChanged)
259 ASSERT(!m_didCallPush);
260 m_didCallPush = true;
261 // We push state even if disabled, because we still need to store layoutDelta
262 m_didPushLayoutState = m_layoutContext.pushLayoutState(root, offset, pageHeight, pageHeightChanged);
263 if (!m_didPushLayoutState)
265 if (m_paintOffsetCacheIsDisabled)
266 m_layoutContext.disablePaintOffsetCache();
269 void LayoutStateMaintainer::pop()
271 ASSERT(!m_didCallPop);
275 if (!m_didPushLayoutState)
277 m_layoutContext.popLayoutState();
278 if (m_paintOffsetCacheIsDisabled)
279 m_layoutContext.enablePaintOffsetCache();
282 LayoutStateDisabler::LayoutStateDisabler(LayoutContext& layoutContext)
283 : m_layoutContext(layoutContext)
285 m_layoutContext.disablePaintOffsetCache();
288 LayoutStateDisabler::~LayoutStateDisabler()
290 m_layoutContext.enablePaintOffsetCache();
293 static bool shouldDisablePaintOffsetCacheForSubtree(RenderElement& subtreeLayoutRoot)
295 for (auto* renderer = &subtreeLayoutRoot; renderer; renderer = renderer->container()) {
296 if (renderer->hasTransform() || renderer->hasReflection())
302 SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
303 : m_subtreeLayoutRoot(subtreeLayoutRoot)
305 if (m_subtreeLayoutRoot) {
306 auto& layoutContext = m_subtreeLayoutRoot->view().frameView().layoutContext();
307 layoutContext.pushLayoutState(*m_subtreeLayoutRoot);
308 if (shouldDisablePaintOffsetCacheForSubtree(*m_subtreeLayoutRoot)) {
309 layoutContext.disablePaintOffsetCache();
310 m_didDisablePaintOffsetCache = true;
315 SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer()
317 if (m_subtreeLayoutRoot) {
318 auto& layoutContext = m_subtreeLayoutRoot->view().frameView().layoutContext();
319 layoutContext.popLayoutState(*m_subtreeLayoutRoot);
320 if (m_didDisablePaintOffsetCache)
321 layoutContext.enablePaintOffsetCache();
325 PaginatedLayoutStateMaintainer::PaginatedLayoutStateMaintainer(RenderBlockFlow& flow)
327 , m_pushed(flow.view().frameView().layoutContext().pushLayoutStateForPaginationIfNeeded(flow))
331 PaginatedLayoutStateMaintainer::~PaginatedLayoutStateMaintainer()
334 m_flow.view().frameView().layoutContext().popLayoutState(m_flow);
337 } // namespace WebCore