1 /* 2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2007 Apple Inc. All rights reserved. 5 * Copyright (C) 2014 Google, Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 */ 22 23 #include "config.h" 24 25 #include "core/svg/SVGSVGElement.h" 26 27 #include "bindings/core/v8/ScriptEventListener.h" 28 #include "core/HTMLNames.h" 29 #include "core/SVGNames.h" 30 #include "core/css/CSSHelper.h" 31 #include "core/dom/Document.h" 32 #include "core/dom/ElementTraversal.h" 33 #include "core/dom/StaticNodeList.h" 34 #include "core/editing/FrameSelection.h" 35 #include "core/events/EventListener.h" 36 #include "core/frame/LocalFrame.h" 37 #include "core/page/FrameTree.h" 38 #include "core/frame/FrameView.h" 39 #include "core/frame/UseCounter.h" 40 #include "core/rendering/RenderObject.h" 41 #include "core/rendering/RenderPart.h" 42 #include "core/rendering/svg/RenderSVGModelObject.h" 43 #include "core/rendering/svg/RenderSVGResource.h" 44 #include "core/rendering/svg/RenderSVGRoot.h" 45 #include "core/rendering/svg/RenderSVGViewportContainer.h" 46 #include "core/svg/SVGAngleTearOff.h" 47 #include "core/svg/SVGNumberTearOff.h" 48 #include "core/svg/SVGPreserveAspectRatio.h" 49 #include "core/svg/SVGRectTearOff.h" 50 #include "core/svg/SVGTransform.h" 51 #include "core/svg/SVGTransformList.h" 52 #include "core/svg/SVGTransformTearOff.h" 53 #include "core/svg/SVGViewElement.h" 54 #include "core/svg/SVGViewSpec.h" 55 #include "core/svg/animation/SMILTimeContainer.h" 56 #include "platform/FloatConversion.h" 57 #include "platform/LengthFunctions.h" 58 #include "platform/geometry/FloatRect.h" 59 #include "platform/transforms/AffineTransform.h" 60 #include "wtf/StdLibExtras.h" 61 62 namespace blink { 63 64 inline SVGSVGElement::SVGSVGElement(Document& doc) 65 : SVGGraphicsElement(SVGNames::svgTag, doc) 66 , SVGFitToViewBox(this) 67 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths)) 68 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths)) 69 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths)) 70 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths)) 71 , m_useCurrentView(false) 72 , m_timeContainer(SMILTimeContainer::create(*this)) 73 , m_translation(SVGPoint::create()) 74 { 75 m_width->setDefaultValueAsString("100%"); 76 m_height->setDefaultValueAsString("100%"); 77 78 addToPropertyMap(m_x); 79 addToPropertyMap(m_y); 80 addToPropertyMap(m_width); 81 addToPropertyMap(m_height); 82 83 UseCounter::count(doc, UseCounter::SVGSVGElement); 84 } 85 86 DEFINE_NODE_FACTORY(SVGSVGElement) 87 88 SVGSVGElement::~SVGSVGElement() 89 { 90 #if !ENABLE(OILPAN) 91 if (m_viewSpec) 92 m_viewSpec->detachContextElement(); 93 94 // There are cases where removedFromDocument() is not called. 95 // see ContainerNode::removeAllChildren, called by its destructor. 96 // With Oilpan, either removedFrom is called or the document 97 // is dead as well and there is no reason to clear the extensions. 98 document().accessSVGExtensions().removeTimeContainer(this); 99 100 ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this)); 101 #endif 102 } 103 104 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const 105 { 106 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here. 107 // As we have no test coverage for this, we're going to disable it completly for now. 108 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal); 109 } 110 111 float SVGSVGElement::pixelUnitToMillimeterX() const 112 { 113 return 1 / cssPixelsPerMillimeter; 114 } 115 116 float SVGSVGElement::pixelUnitToMillimeterY() const 117 { 118 return 1 / cssPixelsPerMillimeter; 119 } 120 121 float SVGSVGElement::screenPixelToMillimeterX() const 122 { 123 return pixelUnitToMillimeterX(); 124 } 125 126 float SVGSVGElement::screenPixelToMillimeterY() const 127 { 128 return pixelUnitToMillimeterY(); 129 } 130 131 SVGViewSpec* SVGSVGElement::currentView() 132 { 133 if (!m_viewSpec) 134 m_viewSpec = SVGViewSpec::create(this); 135 return m_viewSpec.get(); 136 } 137 138 float SVGSVGElement::currentScale() const 139 { 140 if (!inDocument() || !isOutermostSVGSVGElement()) 141 return 1; 142 143 LocalFrame* frame = document().frame(); 144 if (!frame) 145 return 1; 146 147 const FrameTree& frameTree = frame->tree(); 148 149 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents. 150 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside 151 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale. 152 return frameTree.parent() ? 1 : frame->pageZoomFactor(); 153 } 154 155 void SVGSVGElement::setCurrentScale(float scale) 156 { 157 if (!inDocument() || !isOutermostSVGSVGElement()) 158 return; 159 160 LocalFrame* frame = document().frame(); 161 if (!frame) 162 return; 163 164 const FrameTree& frameTree = frame->tree(); 165 166 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents. 167 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within 168 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG. 169 if (frameTree.parent()) 170 return; 171 172 frame->setPageZoomFactor(scale); 173 } 174 175 class SVGCurrentTranslateTearOff : public SVGPointTearOff { 176 public: 177 static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement) 178 { 179 return adoptRef(new SVGCurrentTranslateTearOff(contextElement)); 180 } 181 182 virtual void commitChange() OVERRIDE 183 { 184 ASSERT(contextElement()); 185 toSVGSVGElement(contextElement())->updateCurrentTranslate(); 186 } 187 188 private: 189 SVGCurrentTranslateTearOff(SVGSVGElement* contextElement) 190 : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal) 191 { 192 } 193 }; 194 195 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript() 196 { 197 return SVGCurrentTranslateTearOff::create(this); 198 } 199 200 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point) 201 { 202 m_translation->setValue(point); 203 updateCurrentTranslate(); 204 } 205 206 void SVGSVGElement::updateCurrentTranslate() 207 { 208 if (RenderObject* object = renderer()) 209 object->setNeedsLayoutAndFullPaintInvalidation(); 210 } 211 212 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 213 { 214 SVGParsingError parseError = NoError; 215 216 if (!nearestViewportElement()) { 217 bool setListener = true; 218 219 // Only handle events if we're the outermost <svg> element 220 if (name == HTMLNames::onunloadAttr) 221 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 222 else if (name == HTMLNames::onresizeAttr) 223 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 224 else if (name == HTMLNames::onscrollAttr) 225 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 226 else if (name == SVGNames::onzoomAttr) 227 document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 228 else 229 setListener = false; 230 231 if (setListener) 232 return; 233 } 234 235 if (name == HTMLNames::onabortAttr) { 236 document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 237 } else if (name == HTMLNames::onerrorAttr) { 238 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 239 } else if (name == SVGNames::xAttr) { 240 m_x->setBaseValueAsString(value, parseError); 241 } else if (name == SVGNames::yAttr) { 242 m_y->setBaseValueAsString(value, parseError); 243 } else if (name == SVGNames::widthAttr) { 244 m_width->setBaseValueAsString(value, parseError); 245 } else if (name == SVGNames::heightAttr) { 246 m_height->setBaseValueAsString(value, parseError); 247 } else if (SVGFitToViewBox::parseAttribute(name, value, document(), parseError)) { 248 } else if (SVGZoomAndPan::parseAttribute(name, value)) { 249 } else { 250 SVGGraphicsElement::parseAttribute(name, value); 251 } 252 253 reportAttributeParsingError(parseError, name, value); 254 } 255 256 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const 257 { 258 if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) 259 return true; 260 return SVGGraphicsElement::isPresentationAttribute(name); 261 } 262 263 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 264 { 265 if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) { 266 RefPtr<SVGLength> length = SVGLength::create(LengthModeOther); 267 TrackExceptionState exceptionState; 268 length->setValueAsString(value, exceptionState); 269 if (!exceptionState.hadException()) { 270 if (name == SVGNames::widthAttr) 271 addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value); 272 else if (name == SVGNames::heightAttr) 273 addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value); 274 } 275 } else { 276 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style); 277 } 278 } 279 280 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) 281 { 282 bool updateRelativeLengthsOrViewBox = false; 283 bool widthChanged = attrName == SVGNames::widthAttr; 284 bool heightChanged = attrName == SVGNames::heightAttr; 285 if (widthChanged || heightChanged 286 || attrName == SVGNames::xAttr 287 || attrName == SVGNames::yAttr) { 288 updateRelativeLengthsOrViewBox = true; 289 updateRelativeLengthsInformation(); 290 invalidateRelativeLengthClients(); 291 292 // At the SVG/HTML boundary (aka RenderSVGRoot), the width and 293 // height attributes can affect the replaced size so we need 294 // to mark it for updating. 295 // 296 // FIXME: For width/height animated as XML attributes on SVG 297 // roots, there is an attribute synchronization missing. See 298 // http://crbug.com/364807 299 if (widthChanged || heightChanged) { 300 RenderObject* renderObject = renderer(); 301 if (renderObject && renderObject->isSVGRoot()) { 302 invalidateSVGPresentationAttributeStyle(); 303 setNeedsStyleRecalc(LocalStyleChange); 304 } 305 } 306 } 307 308 if (SVGFitToViewBox::isKnownAttribute(attrName)) { 309 updateRelativeLengthsOrViewBox = true; 310 if (RenderObject* object = renderer()) 311 object->setNeedsTransformUpdate(); 312 } 313 314 SVGElement::InvalidationGuard invalidationGuard(this); 315 316 if (updateRelativeLengthsOrViewBox 317 || SVGZoomAndPan::isKnownAttribute(attrName)) { 318 if (renderer()) 319 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer()); 320 return; 321 } 322 323 SVGGraphicsElement::svgAttributeChanged(attrName); 324 } 325 326 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()). 327 static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2) 328 { 329 if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0) 330 return false; 331 332 return r1.x() < r2.maxX() && r2.x() < r1.maxX() 333 && r1.y() < r2.maxY() && r2.y() < r1.maxY(); 334 } 335 336 // One of the element types that can cause graphics to be drawn onto the target canvas. 337 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use. 338 static bool isIntersectionOrEnclosureTarget(RenderObject* renderer) 339 { 340 return renderer->isSVGShape() 341 || renderer->isSVGText() 342 || renderer->isSVGImage() 343 || isSVGUseElement(*renderer->node()); 344 } 345 346 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect, 347 CheckIntersectionOrEnclosure mode) const 348 { 349 RenderObject* renderer = element.renderer(); 350 ASSERT(!renderer || renderer->style()); 351 if (!renderer || renderer->style()->pointerEvents() == PE_NONE) 352 return false; 353 354 if (!isIntersectionOrEnclosureTarget(renderer)) 355 return false; 356 357 AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this); 358 FloatRect mappedRepaintRect = ctm.mapRect(renderer->paintInvalidationRectInLocalCoordinates()); 359 360 bool result = false; 361 switch (mode) { 362 case CheckIntersection: 363 result = intersectsAllowingEmpty(rect, mappedRepaintRect); 364 break; 365 case CheckEnclosure: 366 result = rect.contains(mappedRepaintRect); 367 break; 368 default: 369 ASSERT_NOT_REACHED(); 370 break; 371 } 372 373 return result; 374 } 375 376 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, 377 SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const 378 { 379 WillBeHeapVector<RefPtrWillBeMember<Node> > nodes; 380 381 const SVGElement* root = this; 382 if (referenceElement) { 383 // Only the common subtree needs to be traversed. 384 if (contains(referenceElement)) { 385 root = referenceElement; 386 } else if (!isDescendantOf(referenceElement)) { 387 // No common subtree. 388 return StaticNodeList::adopt(nodes); 389 } 390 } 391 392 for (SVGGraphicsElement* element = Traversal<SVGGraphicsElement>::firstWithin(*root); element; 393 element = Traversal<SVGGraphicsElement>::next(*element, root)) { 394 if (checkIntersectionOrEnclosure(*element, rect, mode)) 395 nodes.append(element); 396 } 397 398 return StaticNodeList::adopt(nodes); 399 } 400 401 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const 402 { 403 document().updateLayoutIgnorePendingStylesheets(); 404 405 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection); 406 } 407 408 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const 409 { 410 document().updateLayoutIgnorePendingStylesheets(); 411 412 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure); 413 } 414 415 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const 416 { 417 ASSERT(element); 418 document().updateLayoutIgnorePendingStylesheets(); 419 420 return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection); 421 } 422 423 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const 424 { 425 ASSERT(element); 426 document().updateLayoutIgnorePendingStylesheets(); 427 428 return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure); 429 } 430 431 void SVGSVGElement::deselectAll() 432 { 433 if (LocalFrame* frame = document().frame()) 434 frame->selection().clear(); 435 } 436 437 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber() 438 { 439 return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal); 440 } 441 442 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength() 443 { 444 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal); 445 } 446 447 PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle() 448 { 449 return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal); 450 } 451 452 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint() 453 { 454 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal); 455 } 456 457 PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix() 458 { 459 return SVGMatrixTearOff::create(AffineTransform()); 460 } 461 462 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect() 463 { 464 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal); 465 } 466 467 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform() 468 { 469 return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal); 470 } 471 472 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix) 473 { 474 return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal); 475 } 476 477 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const 478 { 479 AffineTransform viewBoxTransform; 480 if (!hasEmptyViewBox()) { 481 FloatSize size = currentViewportSize(); 482 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height()); 483 } 484 485 AffineTransform transform; 486 if (!isOutermostSVGSVGElement()) { 487 SVGLengthContext lengthContext(this); 488 transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext)); 489 } else if (mode == SVGElement::ScreenScope) { 490 if (RenderObject* renderer = this->renderer()) { 491 FloatPoint location; 492 float zoomFactor = 1; 493 494 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 495 // to map an element from SVG viewport coordinates to CSS box coordinates. 496 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates. 497 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361). 498 if (renderer->isSVGRoot()) { 499 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location); 500 zoomFactor = 1 / renderer->style()->effectiveZoom(); 501 } 502 503 // Translate in our CSS parent coordinate space 504 // FIXME: This doesn't work correctly with CSS transforms. 505 location = renderer->localToAbsolute(location, UseTransforms); 506 location.scale(zoomFactor, zoomFactor); 507 508 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(), 509 // so we have to subtract it here (original cause of bug #27183) 510 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f()); 511 512 // Respect scroll offset. 513 if (FrameView* view = document().view()) { 514 LayoutSize scrollOffset = view->scrollOffset(); 515 scrollOffset.scale(zoomFactor); 516 transform.translate(-scrollOffset.width(), -scrollOffset.height()); 517 } 518 } 519 } 520 521 return transform.multiply(viewBoxTransform); 522 } 523 524 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style) 525 { 526 // FIXME: We should respect display: none on the documentElement svg element 527 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when 528 // they should instead depend on the RenderView. 529 // https://bugs.webkit.org/show_bug.cgi?id=103493 530 if (document().documentElement() == this) 531 return true; 532 return Element::rendererIsNeeded(style); 533 } 534 535 RenderObject* SVGSVGElement::createRenderer(RenderStyle*) 536 { 537 if (isOutermostSVGSVGElement()) 538 return new RenderSVGRoot(this); 539 540 return new RenderSVGViewportContainer(this); 541 } 542 543 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent) 544 { 545 if (rootParent->inDocument()) { 546 UseCounter::count(document(), UseCounter::SVGSVGElementInDocument); 547 if (rootParent->document().isXMLDocument()) 548 UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument); 549 550 document().accessSVGExtensions().addTimeContainer(this); 551 552 // Animations are started at the end of document parsing and after firing the load event, 553 // but if we miss that train (deferred programmatic element insertion for example) we need 554 // to initialize the time container here. 555 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted()) 556 timeContainer()->begin(); 557 } 558 return SVGGraphicsElement::insertedInto(rootParent); 559 } 560 561 void SVGSVGElement::removedFrom(ContainerNode* rootParent) 562 { 563 if (rootParent->inDocument()) { 564 SVGDocumentExtensions& svgExtensions = document().accessSVGExtensions(); 565 svgExtensions.removeTimeContainer(this); 566 svgExtensions.removeSVGRootWithRelativeLengthDescendents(this); 567 } 568 569 SVGGraphicsElement::removedFrom(rootParent); 570 } 571 572 void SVGSVGElement::pauseAnimations() 573 { 574 if (!m_timeContainer->isPaused()) 575 m_timeContainer->pause(); 576 } 577 578 void SVGSVGElement::unpauseAnimations() 579 { 580 if (m_timeContainer->isPaused()) 581 m_timeContainer->resume(); 582 } 583 584 bool SVGSVGElement::animationsPaused() const 585 { 586 return m_timeContainer->isPaused(); 587 } 588 589 float SVGSVGElement::getCurrentTime() const 590 { 591 return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); 592 } 593 594 void SVGSVGElement::setCurrentTime(float seconds) 595 { 596 if (std::isnan(seconds)) 597 return; 598 seconds = max(seconds, 0.0f); 599 m_timeContainer->setElapsed(seconds); 600 } 601 602 bool SVGSVGElement::selfHasRelativeLengths() const 603 { 604 return m_x->currentValue()->isRelative() 605 || m_y->currentValue()->isRelative() 606 || m_width->currentValue()->isRelative() 607 || m_height->currentValue()->isRelative() 608 || hasAttribute(SVGNames::viewBoxAttr); 609 } 610 611 FloatRect SVGSVGElement::currentViewBoxRect() const 612 { 613 if (m_useCurrentView) 614 return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect(); 615 616 FloatRect useViewBox = viewBox()->currentValue()->value(); 617 if (!useViewBox.isEmpty()) 618 return useViewBox; 619 if (!renderer() || !renderer()->isSVGRoot()) 620 return FloatRect(); 621 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage()) 622 return FloatRect(); 623 624 // If no viewBox is specified but non-relative width/height values, then we 625 // should always synthesize a viewBox if we're embedded through a SVGImage. 626 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0))); 627 } 628 629 FloatSize SVGSVGElement::currentViewportSize() const 630 { 631 if (!renderer()) 632 return FloatSize(); 633 634 if (renderer()->isSVGRoot()) { 635 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect(); 636 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom()); 637 } 638 639 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport(); 640 return FloatSize(viewportRect.width(), viewportRect.height()); 641 } 642 643 bool SVGSVGElement::hasIntrinsicWidth() const 644 { 645 return width()->currentValue()->unitType() != LengthTypePercentage; 646 } 647 648 bool SVGSVGElement::hasIntrinsicHeight() const 649 { 650 return height()->currentValue()->unitType() != LengthTypePercentage; 651 } 652 653 Length SVGSVGElement::intrinsicWidth() const 654 { 655 if (width()->currentValue()->unitType() == LengthTypePercentage) 656 return Length(0, Fixed); 657 658 SVGLengthContext lengthContext(this); 659 return Length(width()->currentValue()->value(lengthContext), Fixed); 660 } 661 662 Length SVGSVGElement::intrinsicHeight() const 663 { 664 if (height()->currentValue()->unitType() == LengthTypePercentage) 665 return Length(0, Fixed); 666 667 SVGLengthContext lengthContext(this); 668 return Length(height()->currentValue()->value(lengthContext), Fixed); 669 } 670 671 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const 672 { 673 if (!m_useCurrentView || !m_viewSpec) 674 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight); 675 676 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight); 677 RefPtr<SVGTransformList> transformList = m_viewSpec->transform(); 678 if (transformList->isEmpty()) 679 return ctm; 680 681 AffineTransform transform; 682 if (transformList->concatenate(transform)) 683 ctm *= transform; 684 685 return ctm; 686 } 687 688 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode) 689 { 690 RenderObject* renderer = this->renderer(); 691 SVGViewSpec* view = m_viewSpec.get(); 692 if (view) 693 view->reset(); 694 695 bool hadUseCurrentView = m_useCurrentView; 696 m_useCurrentView = false; 697 698 if (fragmentIdentifier.startsWith("xpointer(")) { 699 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491) 700 if (renderer && hadUseCurrentView) 701 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 702 return; 703 } 704 705 if (fragmentIdentifier.startsWith("svgView(")) { 706 if (!view) 707 view = currentView(); // Create the SVGViewSpec. 708 709 if (view->parseViewSpec(fragmentIdentifier)) 710 m_useCurrentView = true; 711 else 712 view->reset(); 713 714 if (renderer && (hadUseCurrentView || m_useCurrentView)) 715 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 716 return; 717 } 718 719 // Spec: If the SVG fragment identifier addresses a view element within an SVG document (e.g., MyDrawing.svg#MyView 720 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor svg element is displayed in the viewport. 721 // Any view specification attributes included on the given view element override the corresponding view specification 722 // attributes on the closest ancestor svg element. 723 if (isSVGViewElement(anchorNode)) { 724 SVGViewElement& viewElement = toSVGViewElement(*anchorNode); 725 726 if (SVGSVGElement* svg = viewElement.ownerSVGElement()) { 727 svg->inheritViewAttributes(&viewElement); 728 729 if (RenderObject* renderer = svg->renderer()) 730 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 731 } 732 } 733 734 // FIXME: We need to decide which <svg> to focus on, and zoom to it. 735 // FIXME: We need to actually "highlight" the viewTarget(s). 736 } 737 738 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement) 739 { 740 SVGViewSpec* view = currentView(); 741 m_useCurrentView = true; 742 743 if (viewElement->hasAttribute(SVGNames::viewBoxAttr)) 744 view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value()); 745 else 746 view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value()); 747 748 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) { 749 view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align()); 750 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice()); 751 } else { 752 view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align()); 753 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice()); 754 } 755 756 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr)) 757 view->setZoomAndPan(viewElement->zoomAndPan()); 758 else 759 view->setZoomAndPan(zoomAndPan()); 760 } 761 762 void SVGSVGElement::finishParsingChildren() 763 { 764 SVGGraphicsElement::finishParsingChildren(); 765 766 // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent. 767 if (isOutermostSVGSVGElement()) 768 return; 769 770 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>) 771 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish 772 sendSVGLoadEventIfPossible(); 773 } 774 775 void SVGSVGElement::trace(Visitor* visitor) 776 { 777 visitor->trace(m_timeContainer); 778 visitor->trace(m_viewSpec); 779 SVGGraphicsElement::trace(visitor); 780 } 781 782 } // namespace blink 783