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