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