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