• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/rendering/RenderGeometryMap.h"
28 
29 #include "core/frame/LocalFrame.h"
30 #include "core/rendering/RenderLayer.h"
31 #include "core/rendering/RenderView.h"
32 #include "platform/geometry/TransformState.h"
33 #include "wtf/TemporaryChange.h"
34 
35 namespace blink {
36 
RenderGeometryMap(MapCoordinatesFlags flags)37 RenderGeometryMap::RenderGeometryMap(MapCoordinatesFlags flags)
38     : m_insertionPosition(kNotFound)
39     , m_nonUniformStepsCount(0)
40     , m_transformedStepsCount(0)
41     , m_fixedStepsCount(0)
42     , m_mapCoordinatesFlags(flags)
43 {
44 }
45 
~RenderGeometryMap()46 RenderGeometryMap::~RenderGeometryMap()
47 {
48 }
49 
mapToContainer(TransformState & transformState,const RenderLayerModelObject * container) const50 void RenderGeometryMap::mapToContainer(TransformState& transformState, const RenderLayerModelObject* container) const
51 {
52     // If the mapping includes something like columns, we have to go via renderers.
53     if (hasNonUniformStep()) {
54         m_mapping.last().m_renderer->mapLocalToContainer(container, transformState, ApplyContainerFlip | m_mapCoordinatesFlags);
55         transformState.flatten();
56         return;
57     }
58 
59     bool inFixed = false;
60 #if ENABLE(ASSERT)
61     bool foundContainer = !container || (m_mapping.size() && m_mapping[0].m_renderer == container);
62 #endif
63 
64     for (int i = m_mapping.size() - 1; i >= 0; --i) {
65         const RenderGeometryMapStep& currentStep = m_mapping[i];
66 
67         // If container is the root RenderView (step 0) we want to apply its fixed position offset.
68         if (i > 0 && currentStep.m_renderer == container) {
69 #if ENABLE(ASSERT)
70             foundContainer = true;
71 #endif
72             break;
73         }
74 
75         // If this box has a transform, it acts as a fixed position container
76         // for fixed descendants, which prevents the propagation of 'fixed'
77         // unless the layer itself is also fixed position.
78         if (i && currentStep.m_hasTransform && !currentStep.m_isFixedPosition)
79             inFixed = false;
80         else if (currentStep.m_isFixedPosition)
81             inFixed = true;
82 
83         ASSERT(!i == isTopmostRenderView(currentStep.m_renderer));
84 
85         if (!i) {
86             // A null container indicates mapping through the root 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         if (inFixed && !currentStep.m_offsetForFixedPosition.isZero()) {
98             ASSERT(currentStep.m_renderer->isRenderView());
99             transformState.move(currentStep.m_offsetForFixedPosition);
100         }
101     }
102 
103     ASSERT(foundContainer);
104     transformState.flatten();
105 }
106 
mapToContainer(const FloatPoint & p,const RenderLayerModelObject * container) const107 FloatPoint RenderGeometryMap::mapToContainer(const FloatPoint& p, const RenderLayerModelObject* container) const
108 {
109     FloatPoint result;
110 
111     if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer)))
112         result = p + m_accumulatedOffset;
113     else {
114         TransformState transformState(TransformState::ApplyTransformDirection, p);
115         mapToContainer(transformState, container);
116         result = transformState.lastPlanarPoint();
117     }
118 
119 #if ENABLE(ASSERT)
120     if (m_mapping.size() > 0) {
121         const RenderObject* lastRenderer = m_mapping.last().m_renderer;
122         const RenderLayer* layer = lastRenderer->enclosingLayer();
123 
124         // Bounds for invisible layers are intentionally not calculated, and are
125         // therefore not necessarily expected to be correct here. This is ok,
126         // because they will be recomputed if the layer becomes visible.
127         if (!layer || !layer->subtreeIsInvisible()) {
128             FloatPoint rendererMappedResult = lastRenderer->localToContainerPoint(p, container, m_mapCoordinatesFlags);
129 
130             ASSERT(roundedIntPoint(rendererMappedResult) == roundedIntPoint(result));
131         }
132     }
133 #endif
134 
135     return result;
136 }
137 
138 #ifndef NDEBUG
139 // Handy function to call from gdb while debugging mismatched point/rect errors.
dumpSteps() const140 void RenderGeometryMap::dumpSteps() const
141 {
142     fprintf(stderr, "RenderGeometryMap::dumpSteps accumulatedOffset=%d,%d\n", m_accumulatedOffset.width().toInt(), m_accumulatedOffset.height().toInt());
143     for (int i = m_mapping.size() - 1; i >= 0; --i) {
144         fprintf(stderr, " [%d] %s: offset=%d,%d", i, m_mapping[i].m_renderer->debugName().ascii().data(), m_mapping[i].m_offset.width().toInt(), m_mapping[i].m_offset.height().toInt());
145         if (m_mapping[i].m_hasTransform)
146             fprintf(stderr, " hasTransform");
147         fprintf(stderr, "\n");
148     }
149 }
150 #endif
151 
mapToContainer(const FloatRect & rect,const RenderLayerModelObject * container) const152 FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, const RenderLayerModelObject* container) const
153 {
154     FloatRect result;
155 
156     if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) {
157         result = rect;
158         result.move(m_accumulatedOffset);
159     } else {
160         TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect);
161         mapToContainer(transformState, container);
162         result = transformState.lastPlanarQuad().boundingBox();
163     }
164 
165 #if ENABLE(ASSERT)
166     if (m_mapping.size() > 0) {
167         const RenderObject* lastRenderer = m_mapping.last().m_renderer;
168         const RenderLayer* layer = lastRenderer->enclosingLayer();
169 
170         // Bounds for invisible layers are intentionally not calculated, and are
171         // therefore not necessarily expected to be correct here. This is ok,
172         // because they will be recomputed if the layer becomes visible.
173         if (!layer->subtreeIsInvisible() && lastRenderer->style()->visibility() == VISIBLE) {
174             FloatRect rendererMappedResult = lastRenderer->localToContainerQuad(rect, container, m_mapCoordinatesFlags).boundingBox();
175 
176             // Inspector creates renderers with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>.
177             // Taking FloatQuad bounds avoids spurious assertions because of that.
178             ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox()));
179         }
180     }
181 #endif
182 
183     return result;
184 }
185 
pushMappingsToAncestor(const RenderObject * renderer,const RenderLayerModelObject * ancestorRenderer)186 void RenderGeometryMap::pushMappingsToAncestor(const RenderObject* renderer, const RenderLayerModelObject* ancestorRenderer)
187 {
188     // We need to push mappings in reverse order here, so do insertions rather than appends.
189     TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size());
190     do {
191         renderer = renderer->pushMappingToContainer(ancestorRenderer, *this);
192     } while (renderer && renderer != ancestorRenderer);
193 
194     ASSERT(m_mapping.isEmpty() || isTopmostRenderView(m_mapping[0].m_renderer));
195 }
196 
canMapBetweenRenderers(const RenderObject * renderer,const RenderObject * ancestor)197 static bool canMapBetweenRenderers(const RenderObject* renderer, const RenderObject* ancestor)
198 {
199     for (const RenderObject* current = renderer; ; current = current->parent()) {
200         const RenderStyle* style = current->style();
201         if (style->position() == FixedPosition || style->isFlippedBlocksWritingMode())
202             return false;
203 
204         if (current->hasColumns() || current->hasTransform() || current->isRenderFlowThread() || current->isSVGRoot())
205             return false;
206 
207         if (current == ancestor)
208             break;
209     }
210 
211     return true;
212 }
213 
pushMappingsToAncestor(const RenderLayer * layer,const RenderLayer * ancestorLayer)214 void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer)
215 {
216     const RenderObject* renderer = layer->renderer();
217 
218     bool crossDocument = ancestorLayer && layer->renderer()->frame() != ancestorLayer->renderer()->frame();
219     ASSERT(!crossDocument || m_mapCoordinatesFlags & TraverseDocumentBoundaries);
220 
221     // We have to visit all the renderers to detect flipped blocks. This might defeat the gains
222     // from mapping via layers.
223     bool canConvertInLayerTree = (ancestorLayer && !crossDocument) ? canMapBetweenRenderers(layer->renderer(), ancestorLayer->renderer()) : false;
224 
225 //    fprintf(stderr, "RenderGeometryMap::pushMappingsToAncestor from layer %p to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, canConvertInLayerTree);
226 
227     if (canConvertInLayerTree) {
228         LayoutPoint layerOffset;
229         layer->convertToLayerCoords(ancestorLayer, layerOffset);
230 
231         // The RenderView must be pushed first.
232         if (!m_mapping.size()) {
233             ASSERT(ancestorLayer->renderer()->isRenderView());
234             pushMappingsToAncestor(ancestorLayer->renderer(), 0);
235         }
236 
237         TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size());
238         bool accumulatingTransform = layer->renderer()->style()->preserves3D() || ancestorLayer->renderer()->style()->preserves3D();
239         push(renderer, toLayoutSize(layerOffset), accumulatingTransform, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false);
240         return;
241     }
242     const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
243     pushMappingsToAncestor(renderer, ancestorRenderer);
244 }
245 
push(const RenderObject * renderer,const LayoutSize & offsetFromContainer,bool accumulatingTransform,bool isNonUniform,bool isFixedPosition,bool hasTransform,LayoutSize offsetForFixedPosition)246 void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform, LayoutSize offsetForFixedPosition)
247 {
248 //    fprintf(stderr, "RenderGeometryMap::push %p %d,%d isNonUniform=%d\n", renderer, offsetFromContainer.width().toInt(), offsetFromContainer.height().toInt(), isNonUniform);
249 
250     ASSERT(m_insertionPosition != kNotFound);
251     ASSERT(!renderer->isRenderView() || !m_insertionPosition || m_mapCoordinatesFlags & TraverseDocumentBoundaries);
252     ASSERT(offsetForFixedPosition.isZero() || renderer->isRenderView());
253 
254     m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform));
255 
256     RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
257     step.m_offset = offsetFromContainer;
258     step.m_offsetForFixedPosition = offsetForFixedPosition;
259 
260     stepInserted(step);
261 }
262 
push(const RenderObject * renderer,const TransformationMatrix & t,bool accumulatingTransform,bool isNonUniform,bool isFixedPosition,bool hasTransform,LayoutSize offsetForFixedPosition)263 void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform, LayoutSize offsetForFixedPosition)
264 {
265     ASSERT(m_insertionPosition != kNotFound);
266     ASSERT(!renderer->isRenderView() || !m_insertionPosition || m_mapCoordinatesFlags & TraverseDocumentBoundaries);
267     ASSERT(offsetForFixedPosition.isZero() || renderer->isRenderView());
268 
269     m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform));
270 
271     RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
272     step.m_offsetForFixedPosition = offsetForFixedPosition;
273 
274     if (!t.isIntegerTranslation())
275         step.m_transform = adoptPtr(new TransformationMatrix(t));
276     else
277         step.m_offset = LayoutSize(t.e(), t.f());
278 
279     stepInserted(step);
280 }
281 
popMappingsToAncestor(const RenderLayerModelObject * ancestorRenderer)282 void RenderGeometryMap::popMappingsToAncestor(const RenderLayerModelObject* ancestorRenderer)
283 {
284     ASSERT(m_mapping.size());
285 
286     while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) {
287         stepRemoved(m_mapping.last());
288         m_mapping.removeLast();
289     }
290 }
291 
popMappingsToAncestor(const RenderLayer * ancestorLayer)292 void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer)
293 {
294     const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
295     popMappingsToAncestor(ancestorRenderer);
296 }
297 
stepInserted(const RenderGeometryMapStep & step)298 void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step)
299 {
300     m_accumulatedOffset += step.m_offset;
301 
302     if (step.m_isNonUniform)
303         ++m_nonUniformStepsCount;
304 
305     if (step.m_transform)
306         ++m_transformedStepsCount;
307 
308     if (step.m_isFixedPosition)
309         ++m_fixedStepsCount;
310 }
311 
stepRemoved(const RenderGeometryMapStep & step)312 void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step)
313 {
314     m_accumulatedOffset -= step.m_offset;
315 
316     if (step.m_isNonUniform) {
317         ASSERT(m_nonUniformStepsCount);
318         --m_nonUniformStepsCount;
319     }
320 
321     if (step.m_transform) {
322         ASSERT(m_transformedStepsCount);
323         --m_transformedStepsCount;
324     }
325 
326     if (step.m_isFixedPosition) {
327         ASSERT(m_fixedStepsCount);
328         --m_fixedStepsCount;
329     }
330 }
331 
332 #if ENABLE(ASSERT)
isTopmostRenderView(const RenderObject * renderer) const333 bool RenderGeometryMap::isTopmostRenderView(const RenderObject* renderer) const
334 {
335     if (!renderer->isRenderView())
336         return false;
337 
338     // If we're not working with multiple RenderViews, then any view is considered
339     // "topmost" (to preserve original behavior).
340     if (!(m_mapCoordinatesFlags & TraverseDocumentBoundaries))
341         return true;
342 
343     return renderer->frame()->isMainFrame();
344 }
345 #endif
346 
347 } // namespace blink
348