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