5e8b995ce07fbed46a4906ba93290fd1f973955c
[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 "LayoutContext.h"
30 #include "RenderFragmentedFlow.h"
31 #include "RenderInline.h"
32 #include "RenderLayer.h"
33 #include "RenderMultiColumnFlow.h"
34 #include "RenderView.h"
35
36 namespace WebCore {
37
38 LayoutState::LayoutState(RenderElement& renderer)
39     : m_clipped(false)
40     , m_isPaginated(false)
41     , m_pageLogicalHeightChanged(false)
42 #if !ASSERT_DISABLED
43     , m_layoutDeltaXSaturated(false)
44     , m_layoutDeltaYSaturated(false)
45 #endif
46 #ifndef NDEBUG
47     , m_renderer(&renderer)
48 #endif
49 {
50     if (RenderElement* container = renderer.container()) {
51         FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), UseTransforms);
52         m_paintOffset = LayoutSize(absContentPoint.x(), absContentPoint.y());
53
54         if (container->hasOverflowClip()) {
55             m_clipped = true;
56             auto& containerBox = downcast<RenderBox>(*container);
57             m_clipRect = LayoutRect(toLayoutPoint(m_paintOffset), containerBox.cachedSizeForOverflowClip());
58             m_paintOffset -= toLayoutSize(containerBox.scrollPosition());
59         }
60     }
61 }
62
63 LayoutState::LayoutState(std::unique_ptr<LayoutState> ancestor, RenderBox& renderer, const LayoutSize& offset, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged)
64     : m_ancestor(WTFMove(ancestor))
65     , m_clipped(false)
66     , m_isPaginated(false)
67     , m_pageLogicalHeightChanged(false)
68 #if !ASSERT_DISABLED
69     , m_layoutDeltaXSaturated(false)
70     , m_layoutDeltaYSaturated(false)
71 #endif
72 #ifndef NDEBUG
73     , m_renderer(&renderer)
74 #endif
75 {
76     if (m_ancestor) {
77         computeOffsets(renderer, offset);
78         computeClipRect(renderer);
79     }
80     computePaginationInformation(renderer, pageLogicalHeight, pageLogicalHeightChanged);
81 }
82
83 void LayoutState::computeOffsets(RenderBox& renderer, LayoutSize offset)
84 {
85     ASSERT(m_ancestor);
86
87     bool fixed = renderer.isFixedPositioned();
88     if (fixed) {
89         FloatPoint fixedOffset = renderer.view().localToAbsolute(FloatPoint(), IsFixed);
90         m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y()) + offset;
91     } else
92         m_paintOffset = m_ancestor->m_paintOffset + offset;
93
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);
98         }
99     }
100
101     m_layoutOffset = m_paintOffset;
102
103     if (renderer.isInFlowPositioned() && renderer.hasLayer())
104         m_paintOffset += renderer.layer()->offsetForInFlowPosition();
105
106     if (renderer.hasOverflowClip())
107         m_paintOffset -= toLayoutSize(renderer.scrollPosition());
108
109     m_layoutDelta = m_ancestor->m_layoutDelta;
110 #if !ASSERT_DISABLED
111     m_layoutDeltaXSaturated = m_ancestor->m_layoutDeltaXSaturated;
112     m_layoutDeltaYSaturated = m_ancestor->m_layoutDeltaYSaturated;
113 #endif
114 }
115
116 void LayoutState::computeClipRect(RenderBox& renderer)
117 {
118     ASSERT(m_ancestor);
119
120     m_clipped = !renderer.isFixedPositioned() && m_ancestor->m_clipped;
121     if (m_clipped)
122         m_clipRect = m_ancestor->m_clipRect;
123     if (!renderer.hasOverflowClip())
124         return;
125
126     LayoutRect clipRect(toLayoutPoint(m_paintOffset) + renderer.view().frameView().layoutContext().layoutDelta(), renderer.cachedSizeForOverflowClip());
127     if (m_clipped)
128         m_clipRect.intersect(clipRect);
129     else
130         m_clipRect = clipRect;
131     m_clipped = true;
132     // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present.
133 }
134
135 void LayoutState::computePaginationInformation(RenderBox& renderer, LayoutUnit pageLogicalHeight, bool pageLogicalHeightChanged)
136 {
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;
150
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;
155         } else
156             m_isPaginated = m_pageLogicalHeight || renderer.enclosingFragmentedFlow();
157     }
158
159     // Propagate line grid information.
160     propagateLineGridInfo(renderer);
161
162     if (lineGrid() && (lineGrid()->style().writingMode() == renderer.style().writingMode()) && is<RenderMultiColumnFlow>(renderer))
163         downcast<RenderMultiColumnFlow>(renderer).computeLineGridPaginationOrigin(*this);
164
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));
168 }
169
170 void LayoutState::clearPaginationInformation()
171 {
172     m_pageLogicalHeight = m_ancestor->m_pageLogicalHeight;
173     m_pageOffset = m_ancestor->m_pageOffset;
174 }
175
176 LayoutUnit LayoutState::pageLogicalOffset(RenderBox* child, LayoutUnit childLogicalOffset) const
177 {
178     if (child->isHorizontalWritingMode())
179         return m_layoutOffset.height() + childLogicalOffset - m_pageOffset.height();
180     return m_layoutOffset.width() + childLogicalOffset - m_pageOffset.width();
181 }
182
183 void LayoutState::propagateLineGridInfo(RenderBox& renderer)
184 {
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())
188         return;
189
190     m_lineGrid = m_ancestor->m_lineGrid;
191     m_lineGridOffset = m_ancestor->m_lineGridOffset;
192     m_lineGridPaginationOrigin = m_ancestor->m_lineGridPaginationOrigin;
193 }
194
195 void LayoutState::establishLineGrid(RenderBlockFlow& renderer)
196 {
197     // First check to see if this grid has been established already.
198     if (m_lineGrid) {
199         if (m_lineGrid->style().lineGrid() == renderer.style().lineGrid())
200             return;
201         RenderBlockFlow* currentGrid = m_lineGrid;
202         for (LayoutState* currentState = m_ancestor.get(); currentState; currentState = currentState->m_ancestor.get()) {
203             if (currentState->m_lineGrid == currentGrid)
204                 continue;
205             currentGrid = currentState->m_lineGrid;
206             if (!currentGrid)
207                 break;
208             if (currentGrid->style().lineGrid() == renderer.style().lineGrid()) {
209                 m_lineGrid = currentGrid;
210                 m_lineGridOffset = currentState->m_lineGridOffset;
211                 return;
212             }
213         }
214     }
215     
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;
219 }
220
221 void LayoutState::addLayoutDelta(LayoutSize delta)
222 {
223     m_layoutDelta += delta;
224 #if !ASSERT_DISABLED
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();
227 #endif
228 }
229
230 #if !ASSERT_DISABLED
231 bool LayoutState::layoutDeltaMatches(LayoutSize delta)
232 {
233     return (delta.width() == m_layoutDelta.width() || m_layoutDeltaXSaturated) && (delta.height() == m_layoutDelta.height() || m_layoutDeltaYSaturated);
234 }
235 #endif
236
237 LayoutStateMaintainer::LayoutStateMaintainer(RenderBox& root, LayoutSize offset, bool disablePaintOffsetCache, LayoutUnit pageHeight, bool pageHeightChanged)
238     : m_layoutContext(root.view().frameView().layoutContext())
239     , m_paintOffsetCacheIsDisabled(disablePaintOffsetCache)
240 {
241     push(root, offset, pageHeight, pageHeightChanged);
242 }
243
244 LayoutStateMaintainer::LayoutStateMaintainer(LayoutContext& layoutContext)
245     : m_layoutContext(layoutContext)
246 {
247 }
248
249 LayoutStateMaintainer::~LayoutStateMaintainer()
250 {
251     // FIXME: Remove conditional push/pop.
252     if (m_didCallPush && !m_didCallPop)
253         pop();
254     ASSERT(!m_didCallPush || m_didCallPush == m_didCallPop);
255 }
256
257 void LayoutStateMaintainer::push(RenderBox& root, LayoutSize offset, LayoutUnit pageHeight, bool pageHeightChanged)
258 {
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)
264         return;
265     if (m_paintOffsetCacheIsDisabled)
266         m_layoutContext.disablePaintOffsetCache();
267 }
268
269 void LayoutStateMaintainer::pop()
270 {
271     ASSERT(!m_didCallPop);
272     m_didCallPop = true;
273     if (!m_didCallPush)
274         return;
275     if (!m_didPushLayoutState)
276         return;
277     m_layoutContext.popLayoutState();
278     if (m_paintOffsetCacheIsDisabled)
279         m_layoutContext.enablePaintOffsetCache();
280 }
281
282 LayoutStateDisabler::LayoutStateDisabler(LayoutContext& layoutContext)
283     : m_layoutContext(layoutContext)
284 {
285     m_layoutContext.disablePaintOffsetCache();
286 }
287
288 LayoutStateDisabler::~LayoutStateDisabler()
289 {
290     m_layoutContext.enablePaintOffsetCache();
291 }
292
293 static bool shouldDisablePaintOffsetCacheForSubtree(RenderElement& subtreeLayoutRoot)
294 {
295     for (auto* renderer = &subtreeLayoutRoot; renderer; renderer = renderer->container()) {
296         if (renderer->hasTransform() || renderer->hasReflection())
297             return true;
298     }
299     return false;
300 }
301
302 SubtreeLayoutStateMaintainer::SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot)
303     : m_subtreeLayoutRoot(subtreeLayoutRoot)
304 {
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;
311         }
312     }
313 }
314
315 SubtreeLayoutStateMaintainer::~SubtreeLayoutStateMaintainer()
316 {
317     if (m_subtreeLayoutRoot) {
318         auto& layoutContext = m_subtreeLayoutRoot->view().frameView().layoutContext();
319         layoutContext.popLayoutState(*m_subtreeLayoutRoot);
320         if (m_didDisablePaintOffsetCache)
321             layoutContext.enablePaintOffsetCache();
322     }
323 }
324
325 PaginatedLayoutStateMaintainer::PaginatedLayoutStateMaintainer(RenderBlockFlow& flow)
326     : m_flow(flow)
327     , m_pushed(flow.view().frameView().layoutContext().pushLayoutStateForPaginationIfNeeded(flow))
328 {
329 }
330
331 PaginatedLayoutStateMaintainer::~PaginatedLayoutStateMaintainer()
332 {
333     if (m_pushed)
334         m_flow.view().frameView().layoutContext().popLayoutState(m_flow);
335 }
336
337 } // namespace WebCore
338