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