1 | /* |
2 | * Copyright (C) 2012 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 "RenderGeometryMap.h" |
28 | |
29 | #include "RenderFragmentedFlow.h" |
30 | #include "RenderLayer.h" |
31 | #include "RenderView.h" |
32 | #include "TransformState.h" |
33 | #include <wtf/SetForScope.h> |
34 | |
35 | namespace WebCore { |
36 | |
37 | RenderGeometryMap::RenderGeometryMap(MapCoordinatesFlags flags) |
38 | : m_insertionPosition(notFound) |
39 | , m_nonUniformStepsCount(0) |
40 | , m_transformedStepsCount(0) |
41 | , m_fixedStepsCount(0) |
42 | , m_mapCoordinatesFlags(flags) |
43 | { |
44 | } |
45 | |
46 | RenderGeometryMap::~RenderGeometryMap() = default; |
47 | |
48 | void RenderGeometryMap::mapToContainer(TransformState& transformState, const RenderLayerModelObject* container) const |
49 | { |
50 | // If the mapping includes something like columns, we have to go via renderers. |
51 | if (hasNonUniformStep()) { |
52 | m_mapping.last().m_renderer->mapLocalToContainer(container, transformState, ApplyContainerFlip | m_mapCoordinatesFlags); |
53 | transformState.flatten(); |
54 | return; |
55 | } |
56 | |
57 | bool inFixed = false; |
58 | #if !ASSERT_DISABLED |
59 | bool foundContainer = !container || (m_mapping.size() && m_mapping[0].m_renderer == container); |
60 | #endif |
61 | |
62 | for (int i = m_mapping.size() - 1; i >= 0; --i) { |
63 | const RenderGeometryMapStep& currentStep = m_mapping[i]; |
64 | |
65 | // If container is the RenderView (step 0) we want to apply its scroll offset. |
66 | if (i > 0 && currentStep.m_renderer == container) { |
67 | #if !ASSERT_DISABLED |
68 | foundContainer = true; |
69 | #endif |
70 | break; |
71 | } |
72 | |
73 | // If this box has a transform, it acts as a fixed position container |
74 | // for fixed descendants, which prevents the propagation of 'fixed' |
75 | // unless the layer itself is also fixed position. |
76 | if (i && currentStep.m_hasTransform && !currentStep.m_isFixedPosition) |
77 | inFixed = false; |
78 | else if (currentStep.m_isFixedPosition) |
79 | inFixed = true; |
80 | |
81 | if (!i) { |
82 | // The root gets special treatment for fixed position |
83 | if (inFixed) |
84 | transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height()); |
85 | |
86 | // A null container indicates mapping through the RenderView, so including its transform (the page scale). |
87 | if (!container && currentStep.m_transform) |
88 | transformState.applyTransform(*currentStep.m_transform.get()); |
89 | } else { |
90 | TransformState::TransformAccumulation accumulate = currentStep.m_accumulatingTransform ? TransformState::AccumulateTransform : TransformState::FlattenTransform; |
91 | if (currentStep.m_transform) |
92 | transformState.applyTransform(*currentStep.m_transform.get(), accumulate); |
93 | else |
94 | transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height(), accumulate); |
95 | } |
96 | } |
97 | |
98 | ASSERT(foundContainer); |
99 | transformState.flatten(); |
100 | } |
101 | |
102 | FloatPoint RenderGeometryMap::mapToContainer(const FloatPoint& p, const RenderLayerModelObject* container) const |
103 | { |
104 | FloatPoint result; |
105 | #if !ASSERT_DISABLED |
106 | FloatPoint rendererMappedResult = m_mapping.last().m_renderer->localToAbsolute(p, m_mapCoordinatesFlags); |
107 | #endif |
108 | |
109 | if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) { |
110 | result = p + roundedIntSize(m_accumulatedOffset); |
111 | // Should convert to a LayoutPoint because of the uniqueness of LayoutUnit::round |
112 | ASSERT(roundedIntPoint(LayoutPoint(rendererMappedResult)) == result); |
113 | } else { |
114 | TransformState transformState(TransformState::ApplyTransformDirection, p); |
115 | mapToContainer(transformState, container); |
116 | result = transformState.lastPlanarPoint(); |
117 | ASSERT(areEssentiallyEqual(rendererMappedResult, result)); |
118 | } |
119 | |
120 | return result; |
121 | } |
122 | |
123 | FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, const RenderLayerModelObject* container) const |
124 | { |
125 | FloatQuad result; |
126 | |
127 | if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) { |
128 | result = rect; |
129 | result.move(m_accumulatedOffset); |
130 | } else { |
131 | TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); |
132 | mapToContainer(transformState, container); |
133 | result = transformState.lastPlanarQuad(); |
134 | } |
135 | |
136 | return result; |
137 | } |
138 | |
139 | void RenderGeometryMap::pushMappingsToAncestor(const RenderObject* renderer, const RenderLayerModelObject* ancestorRenderer) |
140 | { |
141 | // We need to push mappings in reverse order here, so do insertions rather than appends. |
142 | SetForScope<size_t> positionChange(m_insertionPosition, m_mapping.size()); |
143 | do { |
144 | renderer = renderer->pushMappingToContainer(ancestorRenderer, *this); |
145 | } while (renderer && renderer != ancestorRenderer); |
146 | |
147 | ASSERT(m_mapping.isEmpty() || m_mapping[0].m_renderer->isRenderView()); |
148 | } |
149 | |
150 | static bool canMapBetweenRenderersViaLayers(const RenderLayerModelObject& renderer, const RenderLayerModelObject& ancestor) |
151 | { |
152 | for (const RenderElement* current = &renderer; ; current = current->parent()) { |
153 | const RenderStyle& style = current->style(); |
154 | if (current->isFixedPositioned() || style.isFlippedBlocksWritingMode()) |
155 | return false; |
156 | |
157 | if (current->hasTransformRelatedProperty() && (current->style().hasTransform() || current->style().hasPerspective())) |
158 | return false; |
159 | |
160 | if (current->isRenderFragmentedFlow()) |
161 | return false; |
162 | |
163 | if (current->isSVGRoot()) |
164 | return false; |
165 | |
166 | if (current == &ancestor) |
167 | break; |
168 | } |
169 | |
170 | return true; |
171 | } |
172 | |
173 | void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, bool respectTransforms) |
174 | { |
175 | MapCoordinatesFlags newFlags = respectTransforms ? m_mapCoordinatesFlags : m_mapCoordinatesFlags & ~UseTransforms; |
176 | SetForScope<MapCoordinatesFlags> flagsChange(m_mapCoordinatesFlags, newFlags); |
177 | |
178 | const RenderLayerModelObject& renderer = layer->renderer(); |
179 | |
180 | // We have to visit all the renderers to detect flipped blocks. This might defeat the gains |
181 | // from mapping via layers. |
182 | bool canConvertInLayerTree = ancestorLayer ? canMapBetweenRenderersViaLayers(layer->renderer(), ancestorLayer->renderer()) : false; |
183 | |
184 | if (canConvertInLayerTree) { |
185 | LayoutSize layerOffset = layer->offsetFromAncestor(ancestorLayer); |
186 | |
187 | // The RenderView must be pushed first. |
188 | if (!m_mapping.size()) { |
189 | ASSERT(ancestorLayer->renderer().isRenderView()); |
190 | pushMappingsToAncestor(&ancestorLayer->renderer(), nullptr); |
191 | } |
192 | |
193 | SetForScope<size_t> positionChange(m_insertionPosition, m_mapping.size()); |
194 | push(&renderer, layerOffset, /*accumulatingTransform*/ true, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false); |
195 | return; |
196 | } |
197 | const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? &ancestorLayer->renderer() : nullptr; |
198 | pushMappingsToAncestor(&renderer, ancestorRenderer); |
199 | } |
200 | |
201 | void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) |
202 | { |
203 | ASSERT(m_insertionPosition != notFound); |
204 | |
205 | m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform)); |
206 | |
207 | RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; |
208 | step.m_offset = offsetFromContainer; |
209 | |
210 | stepInserted(step); |
211 | } |
212 | |
213 | void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) |
214 | { |
215 | ASSERT(m_insertionPosition != notFound); |
216 | |
217 | m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform)); |
218 | |
219 | RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; |
220 | if (!t.isIntegerTranslation()) |
221 | step.m_transform = std::make_unique<TransformationMatrix>(t); |
222 | else |
223 | step.m_offset = LayoutSize(t.e(), t.f()); |
224 | |
225 | stepInserted(step); |
226 | } |
227 | |
228 | void RenderGeometryMap::pushView(const RenderView* view, const LayoutSize& scrollOffset, const TransformationMatrix* t) |
229 | { |
230 | ASSERT(m_insertionPosition != notFound); |
231 | ASSERT(!m_insertionPosition); // The view should always be the first step. |
232 | |
233 | m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(view, false, false, false, t)); |
234 | |
235 | RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; |
236 | step.m_offset = scrollOffset; |
237 | if (t) |
238 | step.m_transform = std::make_unique<TransformationMatrix>(*t); |
239 | |
240 | stepInserted(step); |
241 | } |
242 | |
243 | void RenderGeometryMap::pushRenderFragmentedFlow(const RenderFragmentedFlow* fragmentedFlow) |
244 | { |
245 | m_mapping.append(RenderGeometryMapStep(fragmentedFlow, false, false, false, false)); |
246 | stepInserted(m_mapping.last()); |
247 | } |
248 | |
249 | void RenderGeometryMap::popMappingsToAncestor(const RenderLayerModelObject* ancestorRenderer) |
250 | { |
251 | ASSERT(m_mapping.size()); |
252 | |
253 | while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) { |
254 | stepRemoved(m_mapping.last()); |
255 | m_mapping.removeLast(); |
256 | } |
257 | } |
258 | |
259 | void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer) |
260 | { |
261 | const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? &ancestorLayer->renderer() : 0; |
262 | popMappingsToAncestor(ancestorRenderer); |
263 | } |
264 | |
265 | void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step) |
266 | { |
267 | // RenderView's offset, is only applied when we have fixed-positions. |
268 | if (!step.m_renderer->isRenderView()) |
269 | m_accumulatedOffset += step.m_offset; |
270 | |
271 | if (step.m_isNonUniform) |
272 | ++m_nonUniformStepsCount; |
273 | |
274 | if (step.m_transform) |
275 | ++m_transformedStepsCount; |
276 | |
277 | if (step.m_isFixedPosition) |
278 | ++m_fixedStepsCount; |
279 | } |
280 | |
281 | void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step) |
282 | { |
283 | // RenderView's offset, is only applied when we have fixed-positions. |
284 | if (!step.m_renderer->isRenderView()) |
285 | m_accumulatedOffset -= step.m_offset; |
286 | |
287 | if (step.m_isNonUniform) { |
288 | ASSERT(m_nonUniformStepsCount); |
289 | --m_nonUniformStepsCount; |
290 | } |
291 | |
292 | if (step.m_transform) { |
293 | ASSERT(m_transformedStepsCount); |
294 | --m_transformedStepsCount; |
295 | } |
296 | |
297 | if (step.m_isFixedPosition) { |
298 | ASSERT(m_fixedStepsCount); |
299 | --m_fixedStepsCount; |
300 | } |
301 | } |
302 | |
303 | } // namespace WebCore |
304 | |