[LayoutState cleanup] LayoutContext should own the stack of LayoutState objects
[WebKit-https.git] / Source / WebCore / rendering / LayoutState.cpp
1 /*
2  * Copyright (C) 2007, 2013 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 "RenderFragmentedFlow.h"
30 #include "RenderInline.h"
31 #include "RenderLayer.h"
32 #include "RenderMultiColumnFlow.h"
33 #include "RenderView.h"
34
35 namespace WebCore {
36
37 LayoutState::LayoutState(RenderElement& renderer)
38     : m_clipped(false)
39     , m_isPaginated(false)
40     , m_pageLogicalHeightChanged(false)
41 #if !ASSERT_DISABLED
42     , m_layoutDeltaXSaturated(false)
43     , m_layoutDeltaYSaturated(false)
44 #endif
45 #ifndef NDEBUG
46     , m_renderer(&renderer)
47 #endif
48 {
49     if (RenderElement* container = renderer.container()) {
50         FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms);
51         m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y());
52
53         if (container->hasOverflowClip()) {
54             m_clipped = true;
55             auto& containerBox = downcast<RenderBox>(*container);
56             m_clipRect = LayoutRect(toLayoutPoint(m_paintOffset), containerBox.cachedSizeForOverflowClip());
57             m_paintOffset -= toLayoutSize(containerBox.scrollPosition());
58         }
59     }
60 }
61
62 LayoutState::LayoutState(const LayoutContext::LayoutStateStack& layoutStateStack, RenderBox& renderer, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged)
63     : m_clipped(false)
64     , m_isPaginated(false)
65     , m_pageLogicalHeightChanged(false)
66 #if !ASSERT_DISABLED
67     , m_layoutDeltaXSaturated(false)
68     , m_layoutDeltaYSaturated(false)
69 #endif
70 #ifndef NDEBUG
71     , m_renderer(&renderer)
72 #endif
73 {
74     if (!layoutStateStack.isEmpty()) {
75         auto& ancestor = *layoutStateStack.last().get();
76         computeOffsets(ancestor, renderer, offset);
77         computeClipRect(ancestor, renderer);
78     }
79     computePaginationInformation(layoutStateStack, renderer, pageLogicalHeight, pageLogicalHeightChanged);
80 }
81
82 void LayoutState::computeOffsets(const LayoutState& ancestor, RenderBox& renderer, LayoutSize offset)
83 {
84     bool fixed = renderer.isFixedPositioned();
85     if (fixed) {
86         FloatPoint fixedOffset = renderer.view().localToAbsolute(FloatPoint(), IsFixed);
87         m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset;
88     } else
89         m_paintOffset = ancestor.paintOffset() + offset;
90
91     if (renderer.isOutOfFlowPositioned() && !fixed) {
92         if (auto* container = renderer.container()) {
93             if (container->isInFlowPositioned() && is<RenderInline>(*container))
94                 m_paintOffset += downcast<RenderInline>(*container).offsetForInFlowPositionedInline(&renderer);
95         }
96     }
97
98     m_layoutOffset = m_paintOffset;
99
100     if (renderer.isInFlowPositioned() && renderer.hasLayer())
101         m_paintOffset += renderer.layer()->offsetForInFlowPosition();
102
103     if (renderer.hasOverflowClip())
104         m_paintOffset -= toLayoutSize(renderer.scrollPosition());
105
106     m_layoutDelta = ancestor.layoutDelta();
107 #if !ASSERT_DISABLED
108     m_layoutDeltaXSaturated = ancestor.m_layoutDeltaXSaturated;
109     m_layoutDeltaYSaturated = ancestor.m_layoutDeltaYSaturated;
110 #endif
111 }
112
113 void LayoutState::computeClipRect(const LayoutState& ancestor, RenderBox& renderer)
114 {
115     m_clipped = !renderer.isFixedPositioned() && ancestor.isClipped();
116     if (m_clipped)
117         m_clipRect = ancestor.clipRect();
118     if (!renderer.hasOverflowClip())
119         return;
120
121     LayoutRect clipRect(toLayoutPoint(m_paintOffset) + renderer.view().frameView().layoutContext().layoutDelta(), renderer.cachedSizeForOverflowClip());
122     if (m_clipped)
123         m_clipRect.intersect(clipRect);
124     else
125         m_clipRect = clipRect;
126     m_clipped = true;
127     // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present.
128 }
129
130 void LayoutState::computePaginationInformation(const LayoutContext::LayoutStateStack& layoutStateStack, RenderBox& renderer, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged)
131 {
132     auto* ancestor = layoutStateStack.isEmpty() ? nullptr : layoutStateStack.last().get();
133     // If we establish a new page height, then cache the offset to the top of the first page.
134     // We can compare this later on to figure out what part of the page we're actually on.
135     if (pageLogicalHeight || renderer.isRenderFragmentedFlow()) {
136         m_pageLogicalHeight = pageLogicalHeight;
137         bool isFlipped = renderer.style().isFlippedBlocksWritingMode();
138         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()));
139         m_pageLogicalHeightChanged = pageLogicalHeightChanged;
140         m_isPaginated = true;
141     } else if (ancestor) {
142         // If we don't establish a new page height, then propagate the old page height and offset down.
143         m_pageLogicalHeight = ancestor->pageLogicalHeight();
144         m_pageLogicalHeightChanged = ancestor->pageLogicalHeightChanged();
145         m_pageOffset = ancestor->pageOffset();
146
147         // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and writing mode roots.
148         if (renderer.isUnsplittableForPagination()) {
149             m_pageLogicalHeight = 0;
150             m_isPaginated = false;
151         } else
152             m_isPaginated = m_pageLogicalHeight || renderer.enclosingFragmentedFlow();
153     }
154
155     // Propagate line grid information.
156     if (ancestor)
157         propagateLineGridInfo(*ancestor, renderer);
158
159     if (lineGrid() && (lineGrid()->style().writingMode() == renderer.style().writingMode()) && is<RenderMultiColumnFlow>(renderer))
160         downcast<RenderMultiColumnFlow>(renderer).computeLineGridPaginationOrigin(*this);
161
162     // If we have a new grid to track, then add it to our set.
163     if (renderer.style().lineGrid() != RenderStyle::initialLineGrid() && is<RenderBlockFlow>(renderer))
164         establishLineGrid(layoutStateStack, downcast<RenderBlockFlow>(renderer));
165 }
166
167 LayoutUnit LayoutState::pageLogicalOffset(RenderBox* child, LayoutUnit childLogicalOffset) const
168 {
169     if (child->isHorizontalWritingMode())
170         return m_layoutOffset.height() + childLogicalOffset - m_pageOffset.height();
171     return m_layoutOffset.width() + childLogicalOffset - m_pageOffset.width();
172 }
173
174 void LayoutState::propagateLineGridInfo(const LayoutState& ancestor, RenderBox& renderer)
175 {
176     // Disable line grids for objects we don't support. For now this includes overflow:scroll/auto, inline blocks and
177     // writing mode roots.
178     if (renderer.isUnsplittableForPagination())
179         return;
180
181     m_lineGrid = ancestor.lineGrid();
182     m_lineGridOffset = ancestor.lineGridOffset();
183     m_lineGridPaginationOrigin = ancestor.lineGridPaginationOrigin();
184 }
185
186 void LayoutState::establishLineGrid(const LayoutContext::LayoutStateStack& layoutStateStack, RenderBlockFlow& renderer)
187 {
188     // FIXME: webkit.org/b/179440 This logic should be part of the LayoutContext.
189     // First check to see if this grid has been established already.
190     if (m_lineGrid) {
191         if (m_lineGrid->style().lineGrid() == renderer.style().lineGrid())
192             return;
193         RenderBlockFlow* currentGrid = m_lineGrid;
194         for (int i = layoutStateStack.size() - 1; i <= 0; --i) {
195             auto& currentState = *layoutStateStack[i].get();
196             if (currentState.m_lineGrid == currentGrid)
197                 continue;
198             currentGrid = currentState.m_lineGrid;
199             if (!currentGrid)
200                 break;
201             if (currentGrid->style().lineGrid() == renderer.style().lineGrid()) {
202                 m_lineGrid = currentGrid;
203                 m_lineGridOffset = currentState.m_lineGridOffset;
204                 return;
205             }
206         }
207     }
208     
209     // We didn't find an already-established grid with this identifier. Our render object establishes the grid.
210     m_lineGrid = &renderer;
211     m_lineGridOffset = m_layoutOffset;
212 }
213
214 void LayoutState::addLayoutDelta(LayoutSize delta)
215 {
216     m_layoutDelta += delta;
217 #if !ASSERT_DISABLED
218     m_layoutDeltaXSaturated |= m_layoutDelta.width() == LayoutUnit::max() || m_layoutDelta.width() == LayoutUnit::min();
219     m_layoutDeltaYSaturated |= m_layoutDelta.height() == LayoutUnit::max() || m_layoutDelta.height() == LayoutUnit::min();
220 #endif
221 }
222
223 #if !ASSERT_DISABLED
224 bool LayoutState::layoutDeltaMatches(LayoutSize delta)
225 {
226     return (delta.width() == m_layoutDelta.width() || m_layoutDeltaXSaturated) && (delta.height() == m_layoutDelta.height() || m_layoutDeltaYSaturated);
227 }
228 #endif
229
230 LayoutStateMaintainer::LayoutStateMaintainer(RenderBox& root, LayoutSize offset, bool disablePaintOffsetCache, LayoutUnit pageHeight, bool pageHeightChanged)
231     : m_layoutContext(root.view().frameView().layoutContext())
232     , m_paintOffsetCacheIsDisabled(disablePaintOffsetCache)
233 {
234     push(root, offset, pageHeight, pageHeightChanged);
235 }
236
237 LayoutStateMaintainer::LayoutStateMaintainer(LayoutContext& layoutContext)
238     : m_layoutContext(layoutContext)
239 {
240 }
241
242 LayoutStateMaintainer::~LayoutStateMaintainer()
243 {
244     // FIXME: Remove conditional push/pop.
245     if (m_didCallPush && !m_didCallPop)
246         pop();
247     ASSERT(!m_didCallPush || m_didCallPush == m_didCallPop);
248 }
249
250 void LayoutStateMaintainer::push(RenderBox& root, LayoutSize offset, LayoutUnit pageHeight, bool pageHeightChanged)
251 {
252     ASSERT(!m_didCallPush);
253     m_didCallPush = true;
254     // We push state even if disabled, because we still need to store layoutDelta
255     m_didPushLayoutState = m_layoutContext.pushLayoutState(root, offset, pageHeight, pageHeightChanged);
256     if (!m_didPushLayoutState)
257         return;
258     if (m_paintOffsetCacheIsDisabled)
259         m_layoutContext.disablePaintOffsetCache();
260 }
261
262 void LayoutStateMaintainer::pop()
263 {
264     ASSERT(!m_didCallPop);
265     m_didCallPop = true;
266     if (!m_didCallPush)
267         return;
268     if (!m_didPushLayoutState)
269         return;
270     m_layoutContext.popLayoutState();
271     if (m_paintOffsetCacheIsDisabled)
272         m_layoutContext.enablePaintOffsetCache();
273 }
274
275 LayoutStateDisabler::LayoutStateDisabler(LayoutContext& layoutContext)
276     : m_layoutContext(layoutContext)
277 {
278     m_layoutContext.disablePaintOffsetCache();
279 }
280
281 LayoutStateDisabler::~LayoutStateDisabler()
282 {
283     m_layoutContext.enablePaintOffsetCache();
284 }
285
286 static bool shouldDisablePaintOffsetCacheForSubtree(RenderElement& subtreeLayoutRoot)
287 {
288     for (auto* renderer = &subtreeLayoutRoot; renderer; renderer = renderer->container()) {
289         if (renderer->hasTransform() || renderer->hasReflection())
290             return true;
291     }
292     return false;
293 }
294
295 SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
296     : m_subtreeLayoutRoot(subtreeLayoutRoot)
297 {
298     if (m_subtreeLayoutRoot) {
299         auto& layoutContext = m_subtreeLayoutRoot->view().frameView().layoutContext();
300         layoutContext.pushLayoutState(*m_subtreeLayoutRoot);
301         if (shouldDisablePaintOffsetCacheForSubtree(*m_subtreeLayoutRoot)) {
302             layoutContext.disablePaintOffsetCache();
303             m_didDisablePaintOffsetCache = true;
304         }
305     }
306 }
307
308 SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer()
309 {
310     if (m_subtreeLayoutRoot) {
311         auto& layoutContext = m_subtreeLayoutRoot->view().frameView().layoutContext();
312         layoutContext.popLayoutState(*m_subtreeLayoutRoot);
313         if (m_didDisablePaintOffsetCache)
314             layoutContext.enablePaintOffsetCache();
315     }
316 }
317
318 PaginatedLayoutStateMaintainer::PaginatedLayoutStateMaintainer(RenderBlockFlow& flow)
319     : m_flow(flow)
320     , m_pushed(flow.view().frameView().layoutContext().pushLayoutStateForPaginationIfNeeded(flow))
321 {
322 }
323
324 PaginatedLayoutStateMaintainer::~PaginatedLayoutStateMaintainer()
325 {
326     if (m_pushed)
327         m_flow.view().frameView().layoutContext().popLayoutState(m_flow);
328 }
329
330 } // namespace WebCore
331