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 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 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 "RenderFlowThread.h" 30#include "RenderLayer.h" 31#include "RenderView.h" 32#include "TransformState.h" 33#include <wtf/TemporaryChange.h> 34 35namespace WebCore { 36 37RenderGeometryMap::RenderGeometryMap(MapCoordinatesFlags flags) 38 : m_insertionPosition(notFound) 39 , m_nonUniformStepsCount(0) 40 , m_transformedStepsCount(0) 41 , m_fixedStepsCount(0) 42 , m_mapCoordinatesFlags(flags) 43{ 44} 45 46RenderGeometryMap::~RenderGeometryMap() 47{ 48} 49 50void 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 !ASSERT_DISABLED 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 RenderView (step 0) we want to apply its scroll offset. 68 if (i > 0 && currentStep.m_renderer == container) { 69#if !ASSERT_DISABLED 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 if (!i) { 84 // A null container indicates mapping through the RenderView, so including its transform (the page scale). 85 if (!container && currentStep.m_transform) 86 transformState.applyTransform(*currentStep.m_transform.get()); 87 88 // The root gets special treatment for fixed position 89 if (inFixed) 90 transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height()); 91 } else { 92 TransformState::TransformAccumulation accumulate = currentStep.m_accumulatingTransform ? TransformState::AccumulateTransform : TransformState::FlattenTransform; 93 if (currentStep.m_transform) 94 transformState.applyTransform(*currentStep.m_transform.get(), accumulate); 95 else 96 transformState.move(currentStep.m_offset.width(), currentStep.m_offset.height(), accumulate); 97 } 98 } 99 100 ASSERT(foundContainer); 101 transformState.flatten(); 102} 103 104FloatPoint RenderGeometryMap::mapToContainer(const FloatPoint& p, const RenderLayerModelObject* container) const 105{ 106 FloatPoint result; 107 108 if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) 109 result = p + roundedIntSize(m_accumulatedOffset); 110 else { 111 TransformState transformState(TransformState::ApplyTransformDirection, p); 112 mapToContainer(transformState, container); 113 result = transformState.lastPlanarPoint(); 114 } 115 116#if !ASSERT_DISABLED 117 FloatPoint rendererMappedResult = m_mapping.last().m_renderer->localToAbsolute(p, m_mapCoordinatesFlags); 118 ASSERT(roundedIntPoint(rendererMappedResult) == roundedIntPoint(result)); 119#endif 120 121 return result; 122} 123 124FloatQuad RenderGeometryMap::mapToContainer(const FloatRect& rect, const RenderLayerModelObject* container) const 125{ 126 FloatRect result; 127 128 if (!hasFixedPositionStep() && !hasTransformStep() && !hasNonUniformStep() && (!container || (m_mapping.size() && container == m_mapping[0].m_renderer))) { 129 result = rect; 130 result.move(m_accumulatedOffset); 131 } else { 132 TransformState transformState(TransformState::ApplyTransformDirection, rect.center(), rect); 133 mapToContainer(transformState, container); 134 result = transformState.lastPlanarQuad().boundingBox(); 135 } 136 137#if !ASSERT_DISABLED 138 FloatRect rendererMappedResult = m_mapping.last().m_renderer->localToContainerQuad(rect, container, m_mapCoordinatesFlags).boundingBox(); 139 // Inspector creates renderers with negative width <https://bugs.webkit.org/show_bug.cgi?id=87194>. 140 // Taking FloatQuad bounds avoids spurious assertions because of that. 141 ASSERT(enclosingIntRect(rendererMappedResult) == enclosingIntRect(FloatQuad(result).boundingBox())); 142#endif 143 144 return result; 145} 146 147void RenderGeometryMap::pushMappingsToAncestor(const RenderObject* renderer, const RenderLayerModelObject* ancestorRenderer) 148{ 149 // We need to push mappings in reverse order here, so do insertions rather than appends. 150 TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size()); 151 do { 152 renderer = renderer->pushMappingToContainer(ancestorRenderer, *this); 153 } while (renderer && renderer != ancestorRenderer); 154 155 ASSERT(m_mapping.isEmpty() || m_mapping[0].m_renderer->isRenderView()); 156} 157 158static bool canMapBetweenRenderers(const RenderObject& renderer, const RenderObject& ancestor) 159{ 160 for (const RenderObject* current = &renderer; ; current = current->parent()) { 161 const RenderStyle& style = current->style(); 162 if (style.position() == FixedPosition || style.isFlippedBlocksWritingMode()) 163 return false; 164 165 if (current->hasTransform() || current->isRenderFlowThread()) 166 return false; 167 168 if (current->isSVGRoot()) 169 return false; 170 171 if (current == &ancestor) 172 break; 173 } 174 175 return true; 176} 177 178void 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 if (canConvertInLayerTree) { 187 LayoutSize layerOffset = layer->offsetFromAncestor(ancestorLayer); 188 189 // The RenderView must be pushed first. 190 if (!m_mapping.size()) { 191 ASSERT(ancestorLayer->renderer().isRenderView()); 192 pushMappingsToAncestor(&ancestorLayer->renderer(), 0); 193 } 194 195 TemporaryChange<size_t> positionChange(m_insertionPosition, m_mapping.size()); 196 push(&renderer, layerOffset, /*accumulatingTransform*/ true, /*isNonUniform*/ false, /*isFixedPosition*/ false, /*hasTransform*/ false); 197 return; 198 } 199 const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? &ancestorLayer->renderer() : 0; 200 pushMappingsToAncestor(&renderer, ancestorRenderer); 201} 202 203void RenderGeometryMap::push(const RenderObject* renderer, const LayoutSize& offsetFromContainer, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) 204{ 205 ASSERT(m_insertionPosition != notFound); 206 207 m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform)); 208 209 RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; 210 step.m_offset = offsetFromContainer; 211 212 stepInserted(step); 213} 214 215void RenderGeometryMap::push(const RenderObject* renderer, const TransformationMatrix& t, bool accumulatingTransform, bool isNonUniform, bool isFixedPosition, bool hasTransform) 216{ 217 ASSERT(m_insertionPosition != notFound); 218 219 m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(renderer, accumulatingTransform, isNonUniform, isFixedPosition, hasTransform)); 220 221 RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; 222 if (!t.isIntegerTranslation()) 223 step.m_transform = std::make_unique<TransformationMatrix>(t); 224 else 225 step.m_offset = LayoutSize(t.e(), t.f()); 226 227 stepInserted(step); 228} 229 230void RenderGeometryMap::pushView(const RenderView* view, const LayoutSize& scrollOffset, const TransformationMatrix* t) 231{ 232 ASSERT(m_insertionPosition != notFound); 233 ASSERT(!m_insertionPosition); // The view should always be the first step. 234 235 m_mapping.insert(m_insertionPosition, RenderGeometryMapStep(view, false, false, false, t)); 236 237 RenderGeometryMapStep& step = m_mapping[m_insertionPosition]; 238 step.m_offset = scrollOffset; 239 if (t) 240 step.m_transform = std::make_unique<TransformationMatrix>(*t); 241 242 stepInserted(step); 243} 244 245void RenderGeometryMap::pushRenderFlowThread(const RenderFlowThread* flowThread) 246{ 247 m_mapping.append(RenderGeometryMapStep(flowThread, false, false, false, false)); 248 stepInserted(m_mapping.last()); 249} 250 251void RenderGeometryMap::popMappingsToAncestor(const RenderLayerModelObject* ancestorRenderer) 252{ 253 ASSERT(m_mapping.size()); 254 255 while (m_mapping.size() && m_mapping.last().m_renderer != ancestorRenderer) { 256 stepRemoved(m_mapping.last()); 257 m_mapping.removeLast(); 258 } 259} 260 261void RenderGeometryMap::popMappingsToAncestor(const RenderLayer* ancestorLayer) 262{ 263 const RenderLayerModelObject* ancestorRenderer = ancestorLayer ? &ancestorLayer->renderer() : 0; 264 popMappingsToAncestor(ancestorRenderer); 265} 266 267void RenderGeometryMap::stepInserted(const RenderGeometryMapStep& step) 268{ 269 // RenderView's offset, is only applied when we have fixed-positions. 270 if (!step.m_renderer->isRenderView()) 271 m_accumulatedOffset += step.m_offset; 272 273 if (step.m_isNonUniform) 274 ++m_nonUniformStepsCount; 275 276 if (step.m_transform) 277 ++m_transformedStepsCount; 278 279 if (step.m_isFixedPosition) 280 ++m_fixedStepsCount; 281} 282 283void RenderGeometryMap::stepRemoved(const RenderGeometryMapStep& step) 284{ 285 // RenderView's offset, is only applied when we have fixed-positions. 286 if (!step.m_renderer->isRenderView()) 287 m_accumulatedOffset -= step.m_offset; 288 289 if (step.m_isNonUniform) { 290 ASSERT(m_nonUniformStepsCount); 291 --m_nonUniformStepsCount; 292 } 293 294 if (step.m_transform) { 295 ASSERT(m_transformedStepsCount); 296 --m_transformedStepsCount; 297 } 298 299 if (step.m_isFixedPosition) { 300 ASSERT(m_fixedStepsCount); 301 --m_fixedStepsCount; 302 } 303} 304 305} // namespace WebCore 306