1/* 2 * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> 3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> 7 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25#include "config.h" 26#include "SVGRenderSupport.h" 27 28#include "NodeRenderStyle.h" 29#include "RenderElement.h" 30#include "RenderGeometryMap.h" 31#include "RenderIterator.h" 32#include "RenderLayer.h" 33#include "RenderSVGResourceClipper.h" 34#include "RenderSVGResourceFilter.h" 35#include "RenderSVGResourceMarker.h" 36#include "RenderSVGResourceMasker.h" 37#include "RenderSVGRoot.h" 38#include "RenderSVGText.h" 39#include "RenderSVGViewportContainer.h" 40#include "SVGResources.h" 41#include "SVGResourcesCache.h" 42#include "TransformState.h" 43 44namespace WebCore { 45 46FloatRect SVGRenderSupport::repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(const RenderElement& renderer) 47{ 48 // FIXME: Add support for RenderSVGBlock. 49 50 if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer()) 51 return toRenderSVGModelObject(renderer).repaintRectInLocalCoordinatesExcludingSVGShadow(); 52 53 return renderer.repaintRectInLocalCoordinates(); 54} 55 56LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer) 57{ 58 // Return early for any cases where we don't actually paint 59 if (renderer.style().visibility() != VISIBLE && !renderer.enclosingLayer()->hasVisibleContent()) 60 return LayoutRect(); 61 62 // Pass our local paint rect to computeRectForRepaint() which will 63 // map to parent coords and recurse up the parent chain. 64 FloatRect repaintRect = repaintRectForRendererInLocalCoordinatesExcludingSVGShadow(renderer); 65 const SVGRenderStyle& svgStyle = renderer.style().svgStyle(); 66 if (const ShadowData* shadow = svgStyle.shadow()) 67 shadow->adjustRectForShadow(repaintRect); 68 renderer.computeFloatRectForRepaint(repaintContainer, repaintRect); 69 return enclosingLayoutRect(repaintRect); 70} 71 72void SVGRenderSupport::computeFloatRectForRepaint(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) 73{ 74 const SVGRenderStyle& svgStyle = renderer.style().svgStyle(); 75 if (const ShadowData* shadow = svgStyle.shadow()) 76 shadow->adjustRectForShadow(repaintRect); 77 repaintRect.inflate(renderer.style().outlineWidth()); 78 79 // Translate to coords in our parent renderer, and then call computeFloatRectForRepaint() on our parent. 80 repaintRect = renderer.localToParentTransform().mapRect(repaintRect); 81 renderer.parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed); 82} 83 84void SVGRenderSupport::mapLocalToContainer(const RenderElement& renderer, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed) 85{ 86 ASSERT(renderer.parent()); 87 auto& parent = *renderer.parent(); 88 89 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 90 // to map an element from SVG viewport coordinates to CSS box coordinates. 91 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. 92 if (parent.isSVGRoot()) 93 transformState.applyTransform(toRenderSVGRoot(parent).localToBorderBoxTransform()); 94 95 transformState.applyTransform(renderer.localToParentTransform()); 96 97 MapCoordinatesFlags mode = UseTransforms; 98 parent.mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); 99} 100 101const RenderElement* SVGRenderSupport::pushMappingToContainer(const RenderElement& renderer, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) 102{ 103 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != &renderer); 104 105 ASSERT(renderer.parent()); 106 auto& parent = *renderer.parent(); 107 108 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 109 // to map an element from SVG viewport coordinates to CSS box coordinates. 110 // RenderSVGRoot's mapLocalToContainer method expects CSS box coordinates. 111 if (parent.isSVGRoot()) { 112 TransformationMatrix matrix(renderer.localToParentTransform()); 113 matrix.multiply(toRenderSVGRoot(parent).localToBorderBoxTransform()); 114 geometryMap.push(&renderer, matrix); 115 } else 116 geometryMap.push(&renderer, renderer.localToParentTransform()); 117 118 return &parent; 119} 120 121bool SVGRenderSupport::checkForSVGRepaintDuringLayout(const RenderElement& renderer) 122{ 123 if (!renderer.checkForRepaintDuringLayout()) 124 return false; 125 // When a parent container is transformed in SVG, all children will be painted automatically 126 // so we are able to skip redundant repaint checks. 127 auto parent = renderer.parent(); 128 return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate()); 129} 130 131// Update a bounding box taking into account the validity of the other bounding box. 132static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox) 133{ 134 bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true; 135 if (!otherValid) 136 return; 137 138 if (!objectBoundingBoxValid) { 139 objectBoundingBox = otherBoundingBox; 140 objectBoundingBoxValid = true; 141 return; 142 } 143 144 objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox); 145} 146 147void SVGRenderSupport::computeContainerBoundingBoxes(const RenderElement& container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox) 148{ 149 objectBoundingBox = FloatRect(); 150 objectBoundingBoxValid = false; 151 strokeBoundingBox = FloatRect(); 152 153 // When computing the strokeBoundingBox, we use the repaintRects of the container's children so that the container's stroke includes 154 // the resources applied to the children (such as clips and filters). This allows filters applied to containers to correctly bound 155 // the children, and also improves inlining of SVG content, as the stroke bound is used in that situation also. 156 for (RenderObject* current = container.firstChild(); current; current = current->nextSibling()) { 157 if (current->isSVGHiddenContainer()) 158 continue; 159 160 // Don't include elements in the union that do not render. 161 if (current->isSVGShape() && toRenderSVGShape(current)->isRenderingDisabled()) 162 continue; 163 164 const AffineTransform& transform = current->localToParentTransform(); 165 if (transform.isIdentity()) { 166 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, current->objectBoundingBox()); 167 strokeBoundingBox.unite(current->repaintRectInLocalCoordinates()); 168 } else { 169 updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current, transform.mapRect(current->objectBoundingBox())); 170 strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates())); 171 } 172 } 173 174 repaintBoundingBox = strokeBoundingBox; 175} 176 177bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo) 178{ 179 if (localTransform.isIdentity()) 180 return localRepaintRect.intersects(paintInfo.rect); 181 182 return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect); 183} 184 185const RenderSVGRoot& SVGRenderSupport::findTreeRootObject(const RenderElement& start) 186{ 187 return *lineageOfType<RenderSVGRoot>(start).first(); 188} 189 190static inline void invalidateResourcesOfChildren(RenderElement& renderer) 191{ 192 ASSERT(!renderer.needsLayout()); 193 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer)) 194 resources->removeClientFromCache(renderer, false); 195 196 for (auto& child : childrenOfType<RenderElement>(renderer)) 197 invalidateResourcesOfChildren(child); 198} 199 200static inline bool layoutSizeOfNearestViewportChanged(const RenderElement& renderer) 201{ 202 const RenderElement* start = &renderer; 203 while (start && !start->isSVGRoot() && !start->isSVGViewportContainer()) 204 start = start->parent(); 205 206 ASSERT(start); 207 ASSERT(start->isSVGRoot() || start->isSVGViewportContainer()); 208 if (start->isSVGViewportContainer()) 209 return toRenderSVGViewportContainer(start)->isLayoutSizeChanged(); 210 211 return toRenderSVGRoot(start)->isLayoutSizeChanged(); 212} 213 214bool SVGRenderSupport::transformToRootChanged(RenderElement* ancestor) 215{ 216 while (ancestor && !ancestor->isSVGRoot()) { 217 if (ancestor->isSVGTransformableContainer()) 218 return toRenderSVGContainer(ancestor)->didTransformToRootUpdate(); 219 if (ancestor->isSVGViewportContainer()) 220 return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate(); 221 ancestor = ancestor->parent(); 222 } 223 224 return false; 225} 226 227void SVGRenderSupport::layoutChildren(RenderElement& start, bool selfNeedsLayout) 228{ 229 bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start); 230 bool transformChanged = transformToRootChanged(&start); 231 bool hasSVGShadow = rendererHasSVGShadow(start); 232 bool needsBoundariesUpdate = start.needsBoundariesUpdate(); 233 HashSet<RenderElement*> elementsThatDidNotReceiveLayout; 234 235 for (RenderObject* child = start.firstChild(); child; child = child->nextSibling()) { 236 bool needsLayout = selfNeedsLayout; 237 bool childEverHadLayout = child->everHadLayout(); 238 239 if (needsBoundariesUpdate && hasSVGShadow) { 240 // If we have a shadow, our shadow is baked into our children's cached boundaries, 241 // so they need to update. 242 child->setNeedsBoundariesUpdate(); 243 needsLayout = true; 244 } 245 246 if (transformChanged) { 247 // If the transform changed we need to update the text metrics (note: this also happens for layoutSizeChanged=true). 248 if (child->isSVGText()) 249 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 250 needsLayout = true; 251 } 252 253 if (layoutSizeChanged) { 254 // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths 255 if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) { 256 if (element->hasRelativeLengths()) { 257 // When the layout size changed and when using relative values tell the RenderSVGShape to update its shape object 258 if (child->isSVGShape()) 259 toRenderSVGShape(child)->setNeedsShapeUpdate(); 260 else if (child->isSVGText()) { 261 toRenderSVGText(child)->setNeedsTextMetricsUpdate(); 262 toRenderSVGText(child)->setNeedsPositioningValuesUpdate(); 263 } 264 265 needsLayout = true; 266 } 267 } 268 } 269 270 if (needsLayout) 271 child->setNeedsLayout(MarkOnlyThis); 272 273 if (child->needsLayout()) { 274 toRenderElement(child)->layout(); 275 // Renderers are responsible for repainting themselves when changing, except 276 // for the initial paint to avoid potential double-painting caused by non-sensical "old" bounds. 277 // We could handle this in the individual objects, but for now it's easier to have 278 // parent containers call repaint(). (RenderBlock::layout* has similar logic.) 279 if (!childEverHadLayout) 280 child->repaint(); 281 } else if (layoutSizeChanged && child->isRenderElement()) 282 elementsThatDidNotReceiveLayout.add(toRenderElement(child)); 283 284 ASSERT(!child->needsLayout()); 285 } 286 287 if (!layoutSizeChanged) { 288 ASSERT(elementsThatDidNotReceiveLayout.isEmpty()); 289 return; 290 } 291 292 // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path. 293 for (auto* element : elementsThatDidNotReceiveLayout) 294 invalidateResourcesOfChildren(*element); 295} 296 297bool SVGRenderSupport::isOverflowHidden(const RenderElement& renderer) 298{ 299 // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size. 300 ASSERT(!renderer.isRoot()); 301 302 return renderer.style().overflowX() == OHIDDEN || renderer.style().overflowX() == OSCROLL; 303} 304 305bool SVGRenderSupport::rendererHasSVGShadow(const RenderObject& renderer) 306{ 307 // FIXME: Add support for RenderSVGBlock. 308 309 if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer()) 310 return toRenderSVGModelObject(renderer).hasSVGShadow(); 311 312 if (renderer.isSVGRoot()) 313 return toRenderSVGRoot(renderer).hasSVGShadow(); 314 315 return false; 316} 317 318void SVGRenderSupport::setRendererHasSVGShadow(RenderObject& renderer, bool hasShadow) 319{ 320 // FIXME: Add support for RenderSVGBlock. 321 322 if (renderer.isSVGShape() || renderer.isSVGImage() || renderer.isSVGContainer()) { 323 toRenderSVGModelObject(renderer).setHasSVGShadow(hasShadow); 324 return; 325 } 326 327 if (renderer.isSVGRoot()) 328 toRenderSVGRoot(renderer).setHasSVGShadow(hasShadow); 329} 330 331void SVGRenderSupport::intersectRepaintRectWithShadows(const RenderElement& renderer, FloatRect& repaintRect) 332{ 333 // Since -webkit-svg-shadow enables shadow drawing for its children, but its children 334 // don't inherit the shadow in their SVGRenderStyle, we need to search our parents for 335 // shadows in order to correctly compute our repaint rect. 336 337 auto currentObject = &renderer; 338 339 AffineTransform localToRootTransform; 340 341 while (currentObject && rendererHasSVGShadow(*currentObject)) { 342 const RenderStyle& style = currentObject->style(); 343 const SVGRenderStyle& svgStyle = style.svgStyle(); 344 if (const ShadowData* shadow = svgStyle.shadow()) 345 shadow->adjustRectForShadow(repaintRect); 346 347 repaintRect = currentObject->localToParentTransform().mapRect(repaintRect); 348 localToRootTransform *= currentObject->localToParentTransform(); 349 350 currentObject = currentObject->parent(); 351 }; 352 353 if (localToRootTransform.isIdentity()) 354 return; 355 356 AffineTransform rootToLocalTransform = localToRootTransform.inverse(); 357 repaintRect = rootToLocalTransform.mapRect(repaintRect); 358} 359 360void SVGRenderSupport::intersectRepaintRectWithResources(const RenderElement& renderer, FloatRect& repaintRect) 361{ 362 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 363 if (!resources) 364 return; 365 366#if ENABLE(FILTERS) 367 if (RenderSVGResourceFilter* filter = resources->filter()) 368 repaintRect = filter->resourceBoundingBox(renderer); 369#endif 370 371 if (RenderSVGResourceClipper* clipper = resources->clipper()) 372 repaintRect.intersect(clipper->resourceBoundingBox(renderer)); 373 374 if (RenderSVGResourceMasker* masker = resources->masker()) 375 repaintRect.intersect(masker->resourceBoundingBox(renderer)); 376} 377 378bool SVGRenderSupport::filtersForceContainerLayout(const RenderElement& renderer) 379{ 380 // If any of this container's children need to be laid out, and a filter is applied 381 // to the container, we need to repaint the entire container. 382 if (!renderer.normalChildNeedsLayout()) 383 return false; 384 385 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 386 if (!resources || !resources->filter()) 387 return false; 388 389 return true; 390} 391 392bool SVGRenderSupport::pointInClippingArea(const RenderElement& renderer, const FloatPoint& point) 393{ 394 // We just take clippers into account to determine if a point is on the node. The Specification may 395 // change later and we also need to check maskers. 396 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); 397 if (!resources) 398 return true; 399 400 if (RenderSVGResourceClipper* clipper = resources->clipper()) 401 return clipper->hitTestClipContent(renderer.objectBoundingBox(), point); 402 403 return true; 404} 405 406void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle& style, const RenderElement& renderer) 407{ 408 ASSERT(context); 409 ASSERT(renderer.element()); 410 ASSERT(renderer.element()->isSVGElement()); 411 412 const SVGRenderStyle& svgStyle = style.svgStyle(); 413 414 SVGLengthContext lengthContext(toSVGElement(renderer.element())); 415 context->setStrokeThickness(svgStyle.strokeWidth().value(lengthContext)); 416 context->setLineCap(svgStyle.capStyle()); 417 context->setLineJoin(svgStyle.joinStyle()); 418 if (svgStyle.joinStyle() == MiterJoin) 419 context->setMiterLimit(svgStyle.strokeMiterLimit()); 420 421 const Vector<SVGLength>& dashes = svgStyle.strokeDashArray(); 422 if (dashes.isEmpty()) 423 context->setStrokeStyle(SolidStroke); 424 else { 425 DashArray dashArray; 426 dashArray.reserveInitialCapacity(dashes.size()); 427 for (auto& dash : dashes) 428 dashArray.uncheckedAppend(dash.value(lengthContext)); 429 430 context->setLineDash(dashArray, svgStyle.strokeDashOffset().value(lengthContext)); 431 } 432} 433 434void SVGRenderSupport::childAdded(RenderElement& parent, RenderObject& child) 435{ 436 SVGRenderSupport::setRendererHasSVGShadow(child, SVGRenderSupport::rendererHasSVGShadow(parent) || SVGRenderSupport::rendererHasSVGShadow(child)); 437} 438 439void SVGRenderSupport::styleChanged(RenderElement& renderer, const RenderStyle* oldStyle) 440{ 441 auto parent = renderer.parent(); 442 SVGRenderSupport::setRendererHasSVGShadow(renderer, (parent && SVGRenderSupport::rendererHasSVGShadow(*parent)) || renderer.style().svgStyle().shadow()); 443 444#if ENABLE(CSS_COMPOSITING) 445 if (renderer.element() && renderer.element()->isSVGElement() && (!oldStyle || renderer.style().hasBlendMode() != oldStyle->hasBlendMode())) 446 SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(renderer); 447#else 448 UNUSED_PARAM(oldStyle); 449#endif 450} 451 452#if ENABLE(CSS_COMPOSITING) 453bool SVGRenderSupport::isolatesBlending(const RenderStyle& style) 454{ 455 return style.svgStyle().isolatesBlending() || style.hasBlendMode() || style.opacity() < 1.0f; 456} 457 458void SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(const RenderElement& renderer) 459{ 460 ASSERT(renderer.element()); 461 ASSERT(renderer.element()->isSVGElement()); 462 463 bool maskedAncestorShouldIsolateBlending = renderer.style().hasBlendMode(); 464 for (auto* ancestor = renderer.element()->parentElement(); ancestor && ancestor->isSVGElement(); ancestor = ancestor->parentElement()) { 465 if (!toSVGElement(ancestor)->isSVGGraphicsElement() || !isolatesBlending(*ancestor->computedStyle())) 466 continue; 467 468 if (ancestor->computedStyle()->svgStyle().hasMasker()) 469 toSVGGraphicsElement(ancestor)->setShouldIsolateBlending(maskedAncestorShouldIsolateBlending); 470 471 return; 472 } 473} 474#endif 475} 476