2 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "RenderGeometryMap.h"
29 #include "RenderLayer.h"
30 #include "RenderView.h"
31 #include "TransformState.h"
32 #include <wtf/TemporaryChange.h>
36 RenderGeometryMap::RenderGeometryMap()
37 : m_insertionPosition(notFound)
38 , m_nonUniformStepsCount(0)
39 , m_transformedStepsCount(0)
40 , m_fixedStepsCount(0)
44 RenderGeometryMap::~RenderGeometryMap()
48 FloatPoint RenderGeometryMap::absolutePoint(const FloatPoint& p) const
52 if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep())
53 result = p + m_accumulatedOffset;
55 TransformState transformState(TransformState::ApplyTransformDirection, p);
56 mapToAbsolute(transformState);
57 result = transformState.lastPlanarPoint();
61 FloatPoint rendererMappedResult = m_mapping.last().m_renderer->localToAbsolute(p, false, true);
62 ASSERT(rendererMappedResult == result);
68 FloatRect RenderGeometryMap::absoluteRect(const FloatRect& rect) const
72 if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep()) {
74 result.move(m_accumulatedOffset);
76 TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect);
77 mapToAbsolute(transformState);
78 result = transformState.lastPlanarQuad().boundingBox();
82 FloatRect rendererMappedResult = m_mapping.last().m_renderer->localToAbsoluteQuad(rect).boundingBox();
83 // Inspector creates renderers with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>.
84 // Taking FloatQuad bounds avoids spurious assertions because of that.
85 ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox()));
91 void RenderGeometryMap::mapToAbsolute(TransformState& transformState) const
93 // If the mapping includes something like columns, we have to go via renderers.
94 if (hasNonUniformStep()) {
96 m_mapping.last().m_renderer->mapLocalToContainer(0, fixed, true, transformState, RenderObject::ApplyContainerFlip);
100 bool inFixed = false;
102 for (int i = m_mapping.size() - 1; i >= 0; --i) {
103 const RenderGeometryMapStep& currentStep = m_mapping[i];
105 // If this box has a transform, it acts as a fixed position container
106 // for fixed descendants, which prevents the propagation of 'fixed'
107 // unless the layer itself is also fixed position.
108 if (currentStep.m_hasTransform && !currentStep.m_isFixedPosition)
110 else if (currentStep.m_isFixedPosition)
114 if (currentStep.m_transform)
115 transformState.applyTransform(*currentStep.m_transform.get());
117 // The root gets special treatment for fixed position
119 transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height());
121 TransformState::TransformAccumulation accumulate = currentStep.m_accumulatingTransform ? TransformState::AccumulateTransform : TransformState::FlattenTransform;
122 if (currentStep.m_transform)
123 transformState.applyTransform(*currentStep.m_transform.get(), accumulate);
125 transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height(), accumulate);
129 transformState.flatten();
132 void RenderGeometryMap::pushMappingsToAncestor(const RenderObject* renderer, const RenderBoxModelObject* ancestorRenderer)
134 // We need to push mappings in reverse order here, so do insertions rather than appends.
135 TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size());
137 renderer = renderer->pushMappingToContainer(ancestorRenderer, *this);
138 } while (renderer && renderer != ancestorRenderer);
140 ASSERT(m_mapping.isEmpty() || m_mapping[0].m_renderer->isRenderView());
143 static bool canMapViaLayer(const RenderLayer* layer)
145 RenderStyle* style = layer->renderer()->style();
146 if (style->position() == FixedPosition || style->isFlippedBlocksWritingMode())
149 if (layer->renderer()->hasColumns() || layer->renderer()->hasTransform())
153 if (layer->renderer()->isSVGRoot())
160 void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer)
162 const RenderObject* renderer = layer->renderer();
164 // The simple case can be handled fast in the layer tree.
165 bool canConvertInLayerTree = ancestorLayer ? canMapViaLayer(ancestorLayer) : false;
166 for (const RenderLayer* current = layer; current != ancestorLayer && canConvertInLayerTree; current = current->parent())
167 canConvertInLayerTree = canMapViaLayer(current);
169 if (canConvertInLayerTree) {
170 TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size());
171 LayoutPoint layerOffset;
172 layer->convertToLayerCoords(ancestorLayer, layerOffset);
173 push(renderer, toLayoutSize(layerOffset), /*accumulatingTransform*/ true, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false);
176 const RenderBoxModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
177 pushMappingsToAncestor(renderer, ancestorRenderer);
180 void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform)
182 ASSERT(m_insertionPosition != notFound);
184 m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform));
186 RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
187 step.m_offset = offsetFromContainer;
192 void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform)
194 ASSERT(m_insertionPosition != notFound);
196 m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform));
198 RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
199 if (!t.isIntegerTranslation())
200 step.m_transform = adoptPtr(new TransformationMatrix(t));
202 step.m_offset = LayoutSize(t.e(), t.f());
207 void RenderGeometryMap::pushView(const RenderView* view, const LayoutSize& scrollOffset, const TransformationMatrix* t)
209 ASSERT(m_insertionPosition != notFound);
210 ASSERT(!m_insertionPosition); // The view should always be the first step.
212 m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(view, false, false, false, t));
214 RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
215 step.m_offset = scrollOffset;
217 step.m_transform = adoptPtr(new TransformationMatrix(*t));
222 void RenderGeometryMap::popMappingsToAncestor(const RenderBoxModelObject* ancestorRenderer)
224 ASSERT(m_mapping.size());
226 while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) {
227 stepRemoved(m_mapping.last());
228 m_mapping.removeLast();
232 void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer)
234 const RenderBoxModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
235 popMappingsToAncestor(ancestorRenderer);
238 void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step)
240 // RenderView's offset, is only applied when we have fixed-positions.
241 if (!step.m_renderer->isRenderView())
242 m_accumulatedOffset += step.m_offset;
244 if (step.m_isNonUniform)
245 ++m_nonUniformStepsCount;
247 if (step.m_transform)
248 ++m_transformedStepsCount;
250 if (step.m_isFixedPosition)
254 void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step)
256 // RenderView's offset, is only applied when we have fixed-positions.
257 if (!step.m_renderer->isRenderView())
258 m_accumulatedOffset -= step.m_offset;
260 if (step.m_isNonUniform) {
261 ASSERT(m_nonUniformStepsCount);
262 --m_nonUniformStepsCount;
265 if (step.m_transform) {
266 ASSERT(m_transformedStepsCount);
267 --m_transformedStepsCount;
270 if (step.m_isFixedPosition) {
271 ASSERT(m_fixedStepsCount);
276 } // namespace WebCore