diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderGeometryMap.cpp')
| -rw-r--r-- | Source/WebCore/rendering/RenderGeometryMap.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderGeometryMap.cpp b/Source/WebCore/rendering/RenderGeometryMap.cpp new file mode 100644 index 000000000..65f189bb1 --- /dev/null +++ b/Source/WebCore/rendering/RenderGeometryMap.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderGeometryMap.h" + +#include "RenderView.h" +#include "TransformState.h" + +namespace WebCore { + + +// Stores data about how to map from one renderer to its container. +class RenderGeometryMapStep { + WTF_MAKE_NONCOPYABLE(RenderGeometryMapStep); +public: + RenderGeometryMapStep(const RenderObject* renderer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) + : m_renderer(renderer) + , m_accumulatingTransform(accumulatingTransform) + , m_isNonUniform(isNonUniform) + , m_isFixedPosition(isFixedPosition) + , m_hasTransform(hasTransform) + { + } + + FloatPoint mapPoint(const FloatPoint& p) const + { + if (!m_transform) + return p + m_offset; + + return m_transform->mapPoint(p); + } + + FloatQuad mapQuad(const FloatQuad& quad) const + { + if (!m_transform) { + FloatQuad q = quad; + q.move(m_offset); + return q; + } + + return m_transform->mapQuad(quad); + } + + const RenderObject* m_renderer; + LayoutSize m_offset; + OwnPtr<TransformationMatrix> m_transform; // Includes offset if non-null. + bool m_accumulatingTransform; + bool m_isNonUniform; // Mapping depends on the input point, e.g. because of CSS columns. + bool m_isFixedPosition; + bool m_hasTransform; +}; + + +RenderGeometryMap::RenderGeometryMap() + : m_insertionPosition(notFound) + , m_nonUniformStepsCount(0) + , m_transformedStepsCount(0) + , m_fixedStepsCount(0) +{ +} + +RenderGeometryMap::~RenderGeometryMap() +{ +} + +FloatPoint RenderGeometryMap::absolutePoint(const FloatPoint& p) const +{ + FloatPoint result; + + if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep()) + result = p + m_accumulatedOffset; + else { + TransformState transformState(TransformState::ApplyTransformDirection, p); + mapToAbsolute(transformState); + result = transformState.lastPlanarPoint(); + } + +#if !ASSERT_DISABLED + FloatPoint rendererMappedResult = m_mapping.last()->m_renderer->localToAbsolute(p, false, true); + ASSERT(rendererMappedResult == result); +#endif + + return result; +} + +FloatRect RenderGeometryMap::absoluteRect(const FloatRect& rect) const +{ + FloatRect result; + + if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep()) { + result = rect; + result.move(m_accumulatedOffset); + } else { + TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); + mapToAbsolute(transformState); + result = transformState.lastPlanarQuad().boundingBox(); + } + +#if !ASSERT_DISABLED + FloatRect rendererMappedResult = m_mapping.last()->m_renderer->localToAbsoluteQuad(rect).boundingBox(); + // Inspector creates renderers with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>. + // Taking FloatQuad bounds avoids spurious assertions because of that. + ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox())); +#endif + + return result; +} + +void RenderGeometryMap::mapToAbsolute(TransformState& transformState) const +{ + // If the mapping includes something like columns, we have to go via renderers. + if (hasNonUniformStep()) { + bool fixed = false; + m_mapping.last()->m_renderer->mapLocalToContainer(0, fixed, true, transformState, RenderObject::ApplyContainerFlip); + return; + } + + bool inFixed = false; + + for (int i = m_mapping.size() - 1; i >= 0; --i) { + const RenderGeometryMapStep* currStep = m_mapping[i].get(); + + if (currStep->m_hasTransform) { + // If this box has a transform, it acts as a fixed position container for fixed descendants, + // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position. + inFixed &= currStep->m_isFixedPosition; + } else + inFixed |= currStep->m_isFixedPosition; + + if (!i) { + if (currStep->m_transform) + transformState.applyTransform(*currStep->m_transform.get()); + + // The root gets special treatment for fixed position + if (inFixed) + transformState.move(currStep->m_offset.width(), currStep->m_offset.height()); + } else { + TransformState::TransformAccumulation accumulate = currStep->m_accumulatingTransform ? TransformState::AccumulateTransform : TransformState::FlattenTransform; + if (currStep->m_transform) + transformState.applyTransform(*currStep->m_transform.get(), accumulate); + else + transformState.move(currStep->m_offset.width(), currStep->m_offset.height(), accumulate); + } + } + + transformState.flatten(); +} + +void RenderGeometryMap::pushMappingsToAncestor(const RenderObject* renderer, const RenderBoxModelObject* ancestor) +{ + const RenderObject* currRenderer = renderer; + + // We need to push mappings in reverse order here, so do insertions rather than appends. + m_insertionPosition = m_mapping.size(); + + do { + currRenderer = currRenderer->pushMappingToContainer(ancestor, *this); + } while (currRenderer && currRenderer != ancestor); + + m_insertionPosition = notFound; +} + +void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) +{ + ASSERT(m_insertionPosition != notFound); + + OwnPtr<RenderGeometryMapStep> step = adoptPtr(new RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform)); + step->m_offset = offsetFromContainer; + + stepInserted(*step.get()); + m_mapping.insert(m_insertionPosition, step.release()); +} + +void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) +{ + ASSERT(m_insertionPosition != notFound); + + OwnPtr<RenderGeometryMapStep> step = adoptPtr(new RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform)); + step->m_transform = adoptPtr(new TransformationMatrix(t)); + + stepInserted(*step.get()); + m_mapping.insert(m_insertionPosition, step.release()); +} + +void RenderGeometryMap::pushView(const RenderView* view, const LayoutSize& scrollOffset, const TransformationMatrix* t) +{ + ASSERT(m_insertionPosition != notFound); + + OwnPtr<RenderGeometryMapStep> step = adoptPtr(new RenderGeometryMapStep(view, false, false, false, t)); + step->m_offset = scrollOffset; + if (t) + step->m_transform = adoptPtr(new TransformationMatrix(*t)); + + ASSERT(!m_mapping.size()); // The view should always be the first thing pushed. + stepInserted(*step.get()); + m_mapping.insert(m_insertionPosition, step.release()); +} + +void RenderGeometryMap::popMappingsToAncestor(const RenderBoxModelObject* ancestor) +{ + ASSERT(m_mapping.size()); + + while (m_mapping.size() && m_mapping.last()->m_renderer != ancestor) { + stepRemoved(*m_mapping.last().get()); + m_mapping.removeLast(); + } +} + +void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step) +{ + // Offset on the first step is the RenderView's offset, which is only applied when we have fixed-position.s + if (m_mapping.size()) + m_accumulatedOffset += step.m_offset; + + if (step.m_isNonUniform) + ++m_nonUniformStepsCount; + + if (step.m_transform) + ++m_transformedStepsCount; + + if (step.m_isFixedPosition) + ++m_fixedStepsCount; +} + +void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step) +{ + // Offset on the first step is the RenderView's offset, which is only applied when we have fixed-position.s + if (m_mapping.size() > 1) + m_accumulatedOffset -= step.m_offset; + + if (step.m_isNonUniform) { + ASSERT(m_nonUniformStepsCount); + --m_nonUniformStepsCount; + } + + if (step.m_transform) { + ASSERT(m_transformedStepsCount); + --m_transformedStepsCount; + } + + if (step.m_isFixedPosition) { + ASSERT(m_fixedStepsCount); + --m_fixedStepsCount; + } +} + +} // namespace WebCore |
