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