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