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