1 /* 2 * Copyright (C) 2006 Apple Computer, Inc. 3 * Copyright (C) 2006 Alexander Kellett <lypanov (at) kde.org> 4 * Copyright (C) 2006 Oliver Hunt <ojh16 (at) student.canterbury.ac.nz> 5 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 6 * Copyright (C) 2008 Rob Buis <buis (at) kde.org> 7 * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org> 8 * Copyright (C) Research In Motion Limited 2010-2012. All rights reserved. 9 * Copyright (C) 2012 Google Inc. 10 * 11 * This library is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU Library General Public 13 * License as published by the Free Software Foundation; either 14 * version 2 of the License, or (at your option) any later version. 15 * 16 * This library is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * Library General Public License for more details. 20 * 21 * You should have received a copy of the GNU Library General Public License 22 * along with this library; see the file COPYING.LIB. If not, write to 23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 * Boston, MA 02110-1301, USA. 25 */ 26 27 #include "config.h" 28 29 #include "core/rendering/svg/RenderSVGText.h" 30 31 #include "core/platform/FloatConversion.h" 32 #include "core/platform/graphics/FloatQuad.h" 33 #include "core/platform/graphics/FontCache.h" 34 #include "core/platform/graphics/GraphicsContextStateSaver.h" 35 #include "core/platform/graphics/SimpleFontData.h" 36 #include "core/platform/graphics/transforms/TransformState.h" 37 #include "core/rendering/HitTestRequest.h" 38 #include "core/rendering/HitTestResult.h" 39 #include "core/rendering/LayoutRepainter.h" 40 #include "core/rendering/PointerEventsHitRules.h" 41 #include "core/rendering/svg/RenderSVGInlineText.h" 42 #include "core/rendering/svg/RenderSVGResource.h" 43 #include "core/rendering/svg/RenderSVGRoot.h" 44 #include "core/rendering/svg/SVGRenderSupport.h" 45 #include "core/rendering/svg/SVGResourcesCache.h" 46 #include "core/rendering/svg/SVGRootInlineBox.h" 47 #include "core/rendering/svg/SVGTextLayoutAttributesBuilder.h" 48 #include "core/rendering/svg/SVGTextRunRenderingContext.h" 49 #include "core/svg/SVGLengthList.h" 50 #include "core/svg/SVGTextElement.h" 51 #include "core/svg/SVGTransformList.h" 52 #include "core/svg/SVGURIReference.h" 53 54 namespace WebCore { 55 56 RenderSVGText::RenderSVGText(SVGTextElement* node) 57 : RenderSVGBlock(node) 58 , m_needsReordering(false) 59 , m_needsPositioningValuesUpdate(false) 60 , m_needsTransformUpdate(true) 61 , m_needsTextMetricsUpdate(false) 62 { 63 } 64 65 RenderSVGText::~RenderSVGText() 66 { 67 ASSERT(m_layoutAttributes.isEmpty()); 68 } 69 70 bool RenderSVGText::isChildAllowed(RenderObject* child, RenderStyle*) const 71 { 72 return child->isInline() && !SVGRenderSupport::isEmptySVGInlineText(child); 73 } 74 75 RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(RenderObject* start) 76 { 77 ASSERT(start); 78 while (start && !start->isSVGText()) 79 start = start->parent(); 80 if (!start || !start->isSVGText()) 81 return 0; 82 return toRenderSVGText(start); 83 } 84 85 const RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(const RenderObject* start) 86 { 87 ASSERT(start); 88 while (start && !start->isSVGText()) 89 start = start->parent(); 90 if (!start || !start->isSVGText()) 91 return 0; 92 return toRenderSVGText(start); 93 } 94 95 LayoutRect RenderSVGText::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const 96 { 97 return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer); 98 } 99 100 void RenderSVGText::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const 101 { 102 FloatRect repaintRect = rect; 103 computeFloatRectForRepaint(repaintContainer, repaintRect, fixed); 104 rect = enclosingLayoutRect(repaintRect); 105 } 106 107 void RenderSVGText::computeFloatRectForRepaint(const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed) const 108 { 109 SVGRenderSupport::computeFloatRectForRepaint(this, repaintContainer, repaintRect, fixed); 110 } 111 112 void RenderSVGText::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags, bool* wasFixed) const 113 { 114 SVGRenderSupport::mapLocalToContainer(this, repaintContainer, transformState, wasFixed); 115 } 116 117 const RenderObject* RenderSVGText::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const 118 { 119 return SVGRenderSupport::pushMappingToContainer(this, ancestorToStopAt, geometryMap); 120 } 121 122 static inline void collectLayoutAttributes(RenderObject* text, Vector<SVGTextLayoutAttributes*>& attributes) 123 { 124 for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) { 125 if (descendant->isSVGInlineText()) 126 attributes.append(toRenderSVGInlineText(descendant)->layoutAttributes()); 127 } 128 } 129 130 static inline bool findPreviousAndNextAttributes(RenderObject* start, RenderSVGInlineText* locateElement, bool& stopAfterNext, SVGTextLayoutAttributes*& previous, SVGTextLayoutAttributes*& next) 131 { 132 ASSERT(start); 133 ASSERT(locateElement); 134 // FIXME: Make this iterative. 135 for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { 136 if (child->isSVGInlineText()) { 137 RenderSVGInlineText* text = toRenderSVGInlineText(child); 138 if (locateElement != text) { 139 if (stopAfterNext) { 140 next = text->layoutAttributes(); 141 return true; 142 } 143 144 previous = text->layoutAttributes(); 145 continue; 146 } 147 148 stopAfterNext = true; 149 continue; 150 } 151 152 if (!child->isSVGInline()) 153 continue; 154 155 if (findPreviousAndNextAttributes(child, locateElement, stopAfterNext, previous, next)) 156 return true; 157 } 158 159 return false; 160 } 161 162 inline bool RenderSVGText::shouldHandleSubtreeMutations() const 163 { 164 if (beingDestroyed() || !everHadLayout()) { 165 ASSERT(m_layoutAttributes.isEmpty()); 166 ASSERT(!m_layoutAttributesBuilder.numberOfTextPositioningElements()); 167 return false; 168 } 169 return true; 170 } 171 172 void RenderSVGText::subtreeChildWasAdded(RenderObject* child) 173 { 174 ASSERT(child); 175 if (!shouldHandleSubtreeMutations() || documentBeingDestroyed()) 176 return; 177 178 // Always protect the cache before clearing text positioning elements when the cache will subsequently be rebuilt. 179 FontCachePurgePreventer fontCachePurgePreventer; 180 181 // The positioning elements cache doesn't include the new 'child' yet. Clear the 182 // cache, as the next buildLayoutAttributesForTextRenderer() call rebuilds it. 183 m_layoutAttributesBuilder.clearTextPositioningElements(); 184 185 if (!child->isSVGInlineText() && !child->isSVGInline()) 186 return; 187 188 // Detect changes in layout attributes and only measure those text parts that have changed! 189 Vector<SVGTextLayoutAttributes*> newLayoutAttributes; 190 collectLayoutAttributes(this, newLayoutAttributes); 191 if (newLayoutAttributes.isEmpty()) { 192 ASSERT(m_layoutAttributes.isEmpty()); 193 return; 194 } 195 196 // Compare m_layoutAttributes with newLayoutAttributes to figure out which attribute got added. 197 size_t size = newLayoutAttributes.size(); 198 SVGTextLayoutAttributes* attributes = 0; 199 for (size_t i = 0; i < size; ++i) { 200 attributes = newLayoutAttributes[i]; 201 if (m_layoutAttributes.find(attributes) == notFound) { 202 // Every time this is invoked, there's only a single new entry in the newLayoutAttributes list, compared to the old in m_layoutAttributes. 203 bool stopAfterNext = false; 204 SVGTextLayoutAttributes* previous = 0; 205 SVGTextLayoutAttributes* next = 0; 206 ASSERT_UNUSED(child, attributes->context() == child); 207 findPreviousAndNextAttributes(this, attributes->context(), stopAfterNext, previous, next); 208 209 if (previous) 210 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(previous->context()); 211 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(attributes->context()); 212 if (next) 213 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(next->context()); 214 break; 215 } 216 } 217 218 #ifndef NDEBUG 219 // Verify that m_layoutAttributes only differs by a maximum of one entry. 220 for (size_t i = 0; i < size; ++i) 221 ASSERT(m_layoutAttributes.find(newLayoutAttributes[i]) != notFound || newLayoutAttributes[i] == attributes); 222 #endif 223 224 m_layoutAttributes = newLayoutAttributes; 225 } 226 227 static inline void checkLayoutAttributesConsistency(RenderSVGText* text, Vector<SVGTextLayoutAttributes*>& expectedLayoutAttributes) 228 { 229 #ifndef NDEBUG 230 Vector<SVGTextLayoutAttributes*> newLayoutAttributes; 231 collectLayoutAttributes(text, newLayoutAttributes); 232 ASSERT(newLayoutAttributes == expectedLayoutAttributes); 233 #else 234 UNUSED_PARAM(text); 235 UNUSED_PARAM(expectedLayoutAttributes); 236 #endif 237 } 238 239 void RenderSVGText::willBeDestroyed() 240 { 241 m_layoutAttributes.clear(); 242 m_layoutAttributesBuilder.clearTextPositioningElements(); 243 244 RenderSVGBlock::willBeDestroyed(); 245 } 246 247 void RenderSVGText::subtreeChildWillBeRemoved(RenderObject* child, Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes) 248 { 249 ASSERT(child); 250 if (!shouldHandleSubtreeMutations()) 251 return; 252 253 checkLayoutAttributesConsistency(this, m_layoutAttributes); 254 255 // The positioning elements cache depends on the size of each text renderer in the 256 // subtree. If this changes, clear the cache. It's going to be rebuilt below. 257 m_layoutAttributesBuilder.clearTextPositioningElements(); 258 if (m_layoutAttributes.isEmpty() || !child->isSVGInlineText()) 259 return; 260 261 // This logic requires that the 'text' child is still inserted in the tree. 262 RenderSVGInlineText* text = toRenderSVGInlineText(child); 263 bool stopAfterNext = false; 264 SVGTextLayoutAttributes* previous = 0; 265 SVGTextLayoutAttributes* next = 0; 266 if (!documentBeingDestroyed()) 267 findPreviousAndNextAttributes(this, text, stopAfterNext, previous, next); 268 269 if (previous) 270 affectedAttributes.append(previous); 271 if (next) 272 affectedAttributes.append(next); 273 274 size_t position = m_layoutAttributes.find(text->layoutAttributes()); 275 ASSERT(position != notFound); 276 m_layoutAttributes.remove(position); 277 } 278 279 void RenderSVGText::subtreeChildWasRemoved(const Vector<SVGTextLayoutAttributes*, 2>& affectedAttributes) 280 { 281 if (!shouldHandleSubtreeMutations() || documentBeingDestroyed()) { 282 ASSERT(affectedAttributes.isEmpty()); 283 return; 284 } 285 286 // This is called immediately after subtreeChildWillBeDestroyed, once the RenderSVGInlineText::willBeDestroyed() method 287 // passes on to the base class, which removes us from the render tree. At this point we can update the layout attributes. 288 unsigned size = affectedAttributes.size(); 289 for (unsigned i = 0; i < size; ++i) 290 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(affectedAttributes[i]->context()); 291 } 292 293 void RenderSVGText::subtreeStyleDidChange(RenderSVGInlineText* text) 294 { 295 ASSERT(text); 296 if (!shouldHandleSubtreeMutations() || documentBeingDestroyed()) 297 return; 298 299 checkLayoutAttributesConsistency(this, m_layoutAttributes); 300 301 // Only update the metrics cache, but not the text positioning element cache 302 // nor the layout attributes cached in the leaf #text renderers. 303 FontCachePurgePreventer fontCachePurgePreventer; 304 for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) { 305 if (descendant->isSVGInlineText()) 306 m_layoutAttributesBuilder.rebuildMetricsForTextRenderer(toRenderSVGInlineText(descendant)); 307 } 308 } 309 310 void RenderSVGText::subtreeTextDidChange(RenderSVGInlineText* text) 311 { 312 ASSERT(text); 313 ASSERT(!beingDestroyed()); 314 if (!everHadLayout()) { 315 ASSERT(m_layoutAttributes.isEmpty()); 316 ASSERT(!m_layoutAttributesBuilder.numberOfTextPositioningElements()); 317 return; 318 } 319 320 // Always protect the cache before clearing text positioning elements when the cache will subsequently be rebuilt. 321 FontCachePurgePreventer fontCachePurgePreventer; 322 323 // The positioning elements cache depends on the size of each text renderer in the 324 // subtree. If this changes, clear the cache. It's going to be rebuilt below. 325 m_layoutAttributesBuilder.clearTextPositioningElements(); 326 327 checkLayoutAttributesConsistency(this, m_layoutAttributes); 328 for (RenderObject* descendant = text; descendant; descendant = descendant->nextInPreOrder(text)) { 329 if (descendant->isSVGInlineText()) 330 m_layoutAttributesBuilder.buildLayoutAttributesForTextRenderer(toRenderSVGInlineText(descendant)); 331 } 332 } 333 334 static inline void updateFontInAllDescendants(RenderObject* start, SVGTextLayoutAttributesBuilder* builder = 0) 335 { 336 for (RenderObject* descendant = start; descendant; descendant = descendant->nextInPreOrder(start)) { 337 if (!descendant->isSVGInlineText()) 338 continue; 339 RenderSVGInlineText* text = toRenderSVGInlineText(descendant); 340 text->updateScaledFont(); 341 if (builder) 342 builder->rebuildMetricsForTextRenderer(text); 343 } 344 } 345 346 void RenderSVGText::layout() 347 { 348 StackStats::LayoutCheckPoint layoutCheckPoint; 349 ASSERT(needsLayout()); 350 LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this)); 351 352 bool updateCachedBoundariesInParents = false; 353 if (m_needsTransformUpdate) { 354 SVGTextElement* text = static_cast<SVGTextElement*>(node()); 355 m_localTransform = text->animatedLocalTransform(); 356 m_needsTransformUpdate = false; 357 updateCachedBoundariesInParents = true; 358 } 359 360 if (!everHadLayout()) { 361 // When laying out initially, collect all layout attributes, build the character data map, 362 // and propogate resulting SVGLayoutAttributes to all RenderSVGInlineText children in the subtree. 363 ASSERT(m_layoutAttributes.isEmpty()); 364 collectLayoutAttributes(this, m_layoutAttributes); 365 updateFontInAllDescendants(this); 366 m_layoutAttributesBuilder.buildLayoutAttributesForForSubtree(this); 367 368 m_needsReordering = true; 369 m_needsTextMetricsUpdate = false; 370 m_needsPositioningValuesUpdate = false; 371 updateCachedBoundariesInParents = true; 372 } else if (m_needsPositioningValuesUpdate) { 373 // When the x/y/dx/dy/rotate lists change, recompute the layout attributes, and eventually 374 // update the on-screen font objects as well in all descendants. 375 if (m_needsTextMetricsUpdate) { 376 updateFontInAllDescendants(this); 377 m_needsTextMetricsUpdate = false; 378 } 379 380 m_layoutAttributesBuilder.buildLayoutAttributesForForSubtree(this); 381 m_needsReordering = true; 382 m_needsPositioningValuesUpdate = false; 383 updateCachedBoundariesInParents = true; 384 } else if (m_needsTextMetricsUpdate || SVGRenderSupport::findTreeRootObject(this)->isLayoutSizeChanged()) { 385 // If the root layout size changed (eg. window size changes) or the transform to the root 386 // context has changed then recompute the on-screen font size. 387 updateFontInAllDescendants(this, &m_layoutAttributesBuilder); 388 389 ASSERT(!m_needsReordering); 390 ASSERT(!m_needsPositioningValuesUpdate); 391 m_needsTextMetricsUpdate = false; 392 updateCachedBoundariesInParents = true; 393 } 394 395 checkLayoutAttributesConsistency(this, m_layoutAttributes); 396 397 // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text. 398 // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions. 399 ASSERT(!isInline()); 400 ASSERT(!simplifiedLayout()); 401 ASSERT(!scrollsOverflow()); 402 ASSERT(!hasControlClip()); 403 ASSERT(!hasColumns()); 404 ASSERT(!positionedObjects()); 405 ASSERT(!m_overflow); 406 ASSERT(!isAnonymousBlock()); 407 408 if (!firstChild()) 409 setChildrenInline(true); 410 411 // FIXME: We need to find a way to only layout the child boxes, if needed. 412 FloatRect oldBoundaries = objectBoundingBox(); 413 ASSERT(childrenInline()); 414 forceLayoutInlineChildren(); 415 416 if (m_needsReordering) 417 m_needsReordering = false; 418 419 if (!updateCachedBoundariesInParents) 420 updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox(); 421 422 // Invalidate all resources of this client if our layout changed. 423 if (everHadLayout() && selfNeedsLayout()) 424 SVGResourcesCache::clientLayoutChanged(this); 425 426 // If our bounds changed, notify the parents. 427 if (updateCachedBoundariesInParents) 428 RenderSVGBlock::setNeedsBoundariesUpdate(); 429 430 repainter.repaintAfterLayout(); 431 clearNeedsLayout(); 432 } 433 434 RootInlineBox* RenderSVGText::createRootInlineBox() 435 { 436 RootInlineBox* box = new SVGRootInlineBox(this); 437 box->setHasVirtualLogicalHeight(); 438 return box; 439 } 440 441 bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) 442 { 443 PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents()); 444 bool isVisible = (style()->visibility() == VISIBLE); 445 if (isVisible || !hitRules.requireVisible) { 446 if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke)) 447 || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) { 448 FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent); 449 450 if (!SVGRenderSupport::pointInClippingArea(this, localPoint)) 451 return false; 452 453 HitTestLocation hitTestLocation(LayoutPoint(flooredIntPoint(localPoint))); 454 return RenderBlock::nodeAtPoint(request, result, hitTestLocation, LayoutPoint(), hitTestAction); 455 } 456 } 457 458 return false; 459 } 460 461 bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation&, const LayoutPoint&, HitTestAction) 462 { 463 ASSERT_NOT_REACHED(); 464 return false; 465 } 466 467 PositionWithAffinity RenderSVGText::positionForPoint(const LayoutPoint& pointInContents) 468 { 469 RootInlineBox* rootBox = firstRootBox(); 470 if (!rootBox) 471 return createPositionWithAffinity(0, DOWNSTREAM); 472 473 ASSERT_WITH_SECURITY_IMPLICATION(rootBox->isSVGRootInlineBox()); 474 ASSERT(!rootBox->nextRootBox()); 475 ASSERT(childrenInline()); 476 477 InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents); 478 if (!closestBox) 479 return createPositionWithAffinity(0, DOWNSTREAM); 480 481 return closestBox->renderer()->positionForPoint(LayoutPoint(pointInContents.x(), closestBox->y())); 482 } 483 484 void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const 485 { 486 quads.append(localToAbsoluteQuad(strokeBoundingBox(), 0 /* mode */, wasFixed)); 487 } 488 489 void RenderSVGText::paint(PaintInfo& paintInfo, const LayoutPoint&) 490 { 491 if (paintInfo.context->paintingDisabled()) 492 return; 493 494 if (paintInfo.phase != PaintPhaseForeground 495 && paintInfo.phase != PaintPhaseSelfOutline 496 && paintInfo.phase != PaintPhaseSelection) 497 return; 498 499 PaintInfo blockInfo(paintInfo); 500 GraphicsContextStateSaver stateSaver(*blockInfo.context); 501 blockInfo.applyTransform(localToParentTransform()); 502 RenderBlock::paint(blockInfo, LayoutPoint()); 503 } 504 505 FloatRect RenderSVGText::strokeBoundingBox() const 506 { 507 FloatRect strokeBoundaries = objectBoundingBox(); 508 const SVGRenderStyle* svgStyle = style()->svgStyle(); 509 if (!svgStyle->hasStroke()) 510 return strokeBoundaries; 511 512 ASSERT(node()); 513 ASSERT(node()->isSVGElement()); 514 SVGLengthContext lengthContext(toSVGElement(node())); 515 strokeBoundaries.inflate(svgStyle->strokeWidth().value(lengthContext)); 516 return strokeBoundaries; 517 } 518 519 FloatRect RenderSVGText::repaintRectInLocalCoordinates() const 520 { 521 FloatRect repaintRect = strokeBoundingBox(); 522 SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect); 523 524 if (const ShadowData* textShadow = style()->textShadow()) 525 textShadow->adjustRectForShadow(repaintRect); 526 527 return repaintRect; 528 } 529 530 void RenderSVGText::addChild(RenderObject* child, RenderObject* beforeChild) 531 { 532 RenderSVGBlock::addChild(child, beforeChild); 533 534 SVGResourcesCache::clientWasAddedToTree(child, child->style()); 535 subtreeChildWasAdded(child); 536 } 537 538 void RenderSVGText::removeChild(RenderObject* child) 539 { 540 SVGResourcesCache::clientWillBeRemovedFromTree(child); 541 542 Vector<SVGTextLayoutAttributes*, 2> affectedAttributes; 543 FontCachePurgePreventer fontCachePurgePreventer; 544 subtreeChildWillBeRemoved(child, affectedAttributes); 545 RenderSVGBlock::removeChild(child); 546 subtreeChildWasRemoved(affectedAttributes); 547 } 548 549 // Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style 550 // in a SVG text element context. 551 RenderBlock* RenderSVGText::firstLineBlock() const 552 { 553 return 0; 554 } 555 556 // Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style 557 // in a SVG text element context. 558 void RenderSVGText::updateFirstLetter() 559 { 560 } 561 562 } 563