Home | History | Annotate | Download | only in rendering
      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/platform/graphics/transforms/TransformState.h"
     30 #include "core/rendering/RenderLayer.h"
     31 #include "core/rendering/RenderView.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(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() || current->isSVGRoot())
    169             return false;
    170 
    171         if (current == ancestor)
    172             break;
    173     }
    174 
    175     return true;
    176 }
    177 
    178 void RenderGeometryMap::pushMappingsToAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer)
    179 {
    180     const RenderObject* renderer = layer->renderer();
    181 
    182     // We have to visit all the renderers to detect flipped blocks. This might defeat the gains
    183     // from mapping via layers.
    184     bool canConvertInLayerTree = ancestorLayer ? canMapBetweenRenderers(layer->renderer(), ancestorLayer->renderer()) : false;
    185 
    186 //    fprintf(stderr, "RenderGeometryMap::pushMappingsToAncestor from layer %p to layer %p, canConvertInLayerTree=%d\n", layer, ancestorLayer, canConvertInLayerTree);
    187 
    188     if (canConvertInLayerTree) {
    189         LayoutPoint layerOffset;
    190         layer->convertToLayerCoords(ancestorLayer, layerOffset);
    191 
    192         // The RenderView must be pushed first.
    193         if (!m_mapping.size()) {
    194             ASSERT(ancestorLayer->renderer()->isRenderView());
    195             pushMappingsToAncestor(ancestorLayer->renderer(), 0);
    196         }
    197 
    198         TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size());
    199         push(renderer, toLayoutSize(layerOffset), /*accumulatingTransform*/ true, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false);
    200         return;
    201     }
    202     const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
    203     pushMappingsToAncestor(renderer, ancestorRenderer);
    204 }
    205 
    206 void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform)
    207 {
    208 //    fprintf(stderr, "RenderGeometryMap::push %p %d,%d isNonUniform=%d\n", renderer, offsetFromContainer.width().toInt(), offsetFromContainer.height().toInt(), isNonUniform);
    209 
    210     ASSERT(m_insertionPosition != notFound);
    211 
    212     m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform));
    213 
    214     RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
    215     step.m_offset = offsetFromContainer;
    216 
    217     stepInserted(step);
    218 }
    219 
    220 void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform)
    221 {
    222     ASSERT(m_insertionPosition != notFound);
    223 
    224     m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform));
    225 
    226     RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
    227     if (!t.isIntegerTranslation())
    228         step.m_transform = adoptPtr(new TransformationMatrix(t));
    229     else
    230         step.m_offset = LayoutSize(t.e(), t.f());
    231 
    232     stepInserted(step);
    233 }
    234 
    235 void RenderGeometryMap::pushView(const RenderView* view, const LayoutSize& scrollOffset, const TransformationMatrix* t)
    236 {
    237     ASSERT(m_insertionPosition != notFound);
    238     ASSERT(!m_insertionPosition); // The view should always be the first step.
    239 
    240     m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(view, false, false, false, t));
    241 
    242     RenderGeometryMapStep& step = m_mapping[m_insertionPosition];
    243     step.m_offset = scrollOffset;
    244     if (t)
    245         step.m_transform = adoptPtr(new TransformationMatrix(*t));
    246 
    247     stepInserted(step);
    248 }
    249 
    250 void RenderGeometryMap::popMappingsToAncestor(const RenderLayerModelObject* ancestorRenderer)
    251 {
    252     ASSERT(m_mapping.size());
    253 
    254     while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) {
    255         stepRemoved(m_mapping.last());
    256         m_mapping.removeLast();
    257     }
    258 }
    259 
    260 void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer)
    261 {
    262     const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
    263     popMappingsToAncestor(ancestorRenderer);
    264 }
    265 
    266 void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step)
    267 {
    268     // RenderView's offset, is only applied when we have fixed-positions.
    269     if (!step.m_renderer->isRenderView())
    270         m_accumulatedOffset += step.m_offset;
    271 
    272     if (step.m_isNonUniform)
    273         ++m_nonUniformStepsCount;
    274 
    275     if (step.m_transform)
    276         ++m_transformedStepsCount;
    277 
    278     if (step.m_isFixedPosition)
    279         ++m_fixedStepsCount;
    280 }
    281 
    282 void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step)
    283 {
    284     // RenderView's offset, is only applied when we have fixed-positions.
    285     if (!step.m_renderer->isRenderView())
    286         m_accumulatedOffset -= step.m_offset;
    287 
    288     if (step.m_isNonUniform) {
    289         ASSERT(m_nonUniformStepsCount);
    290         --m_nonUniformStepsCount;
    291     }
    292 
    293     if (step.m_transform) {
    294         ASSERT(m_transformedStepsCount);
    295         --m_transformedStepsCount;
    296     }
    297 
    298     if (step.m_isFixedPosition) {
    299         ASSERT(m_fixedStepsCount);
    300         --m_fixedStepsCount;
    301     }
    302 }
    303 
    304 } // namespace WebCore
    305