1/* 2 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@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 62namespace blink { 63 64inline 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 86DEFINE_NODE_FACTORY(SVGSVGElement) 87 88SVGSVGElement::~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 104PassRefPtr<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 111float SVGSVGElement::pixelUnitToMillimeterX() const 112{ 113 return 1 / cssPixelsPerMillimeter; 114} 115 116float SVGSVGElement::pixelUnitToMillimeterY() const 117{ 118 return 1 / cssPixelsPerMillimeter; 119} 120 121float SVGSVGElement::screenPixelToMillimeterX() const 122{ 123 return pixelUnitToMillimeterX(); 124} 125 126float SVGSVGElement::screenPixelToMillimeterY() const 127{ 128 return pixelUnitToMillimeterY(); 129} 130 131SVGViewSpec* SVGSVGElement::currentView() 132{ 133 if (!m_viewSpec) 134 m_viewSpec = SVGViewSpec::create(this); 135 return m_viewSpec.get(); 136} 137 138float 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 155void 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 175class SVGCurrentTranslateTearOff : public SVGPointTearOff { 176public: 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 188private: 189 SVGCurrentTranslateTearOff(SVGSVGElement* contextElement) 190 : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal) 191 { 192 } 193}; 194 195PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript() 196{ 197 return SVGCurrentTranslateTearOff::create(this); 198} 199 200void SVGSVGElement::setCurrentTranslate(const FloatPoint& point) 201{ 202 m_translation->setValue(point); 203 updateCurrentTranslate(); 204} 205 206void SVGSVGElement::updateCurrentTranslate() 207{ 208 if (RenderObject* object = renderer()) 209 object->setNeedsLayoutAndFullPaintInvalidation(); 210} 211 212void 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 256bool 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 263void 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 280void 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()). 327static 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. 338static bool isIntersectionOrEnclosureTarget(RenderObject* renderer) 339{ 340 return renderer->isSVGShape() 341 || renderer->isSVGText() 342 || renderer->isSVGImage() 343 || isSVGUseElement(*renderer->node()); 344} 345 346bool 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 376PassRefPtrWillBeRawPtr<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 401PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const 402{ 403 document().updateLayoutIgnorePendingStylesheets(); 404 405 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection); 406} 407 408PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const 409{ 410 document().updateLayoutIgnorePendingStylesheets(); 411 412 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure); 413} 414 415bool 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 423bool 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 431void SVGSVGElement::deselectAll() 432{ 433 if (LocalFrame* frame = document().frame()) 434 frame->selection().clear(); 435} 436 437PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber() 438{ 439 return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal); 440} 441 442PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength() 443{ 444 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal); 445} 446 447PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle() 448{ 449 return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal); 450} 451 452PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint() 453{ 454 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal); 455} 456 457PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix() 458{ 459 return SVGMatrixTearOff::create(AffineTransform()); 460} 461 462PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect() 463{ 464 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal); 465} 466 467PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform() 468{ 469 return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal); 470} 471 472PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix) 473{ 474 return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal); 475} 476 477AffineTransform 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 524bool 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 535RenderObject* SVGSVGElement::createRenderer(RenderStyle*) 536{ 537 if (isOutermostSVGSVGElement()) 538 return new RenderSVGRoot(this); 539 540 return new RenderSVGViewportContainer(this); 541} 542 543Node::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 561void 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 572void SVGSVGElement::pauseAnimations() 573{ 574 if (!m_timeContainer->isPaused()) 575 m_timeContainer->pause(); 576} 577 578void SVGSVGElement::unpauseAnimations() 579{ 580 if (m_timeContainer->isPaused()) 581 m_timeContainer->resume(); 582} 583 584bool SVGSVGElement::animationsPaused() const 585{ 586 return m_timeContainer->isPaused(); 587} 588 589float SVGSVGElement::getCurrentTime() const 590{ 591 return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); 592} 593 594void 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 602bool 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 611FloatRect 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 629FloatSize 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 643bool SVGSVGElement::hasIntrinsicWidth() const 644{ 645 return width()->currentValue()->unitType() != LengthTypePercentage; 646} 647 648bool SVGSVGElement::hasIntrinsicHeight() const 649{ 650 return height()->currentValue()->unitType() != LengthTypePercentage; 651} 652 653Length 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 662Length 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 671AffineTransform 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 688void 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 738void 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 762void 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 775void 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