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