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/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 
     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 
     46 RenderGeometryMap::~RenderGeometryMap()
     47 {
     48 }
     49 
     50 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 
    107 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.
    140 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 
    152 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 
    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 
    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 
    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 
    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 
    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 
    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 
    292 void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer)
    293 {
    294     const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? ancestorLayer->renderer() : 0;
    295     popMappingsToAncestor(ancestorRenderer);
    296 }
    297 
    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 
    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)
    333 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