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 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23 24#if ENABLE(SVG) 25#include "SVGSVGElement.h" 26 27#include "AffineTransform.h" 28#include "Attribute.h" 29#include "CSSHelper.h" 30#include "Document.h" 31#include "EventListener.h" 32#include "EventNames.h" 33#include "FloatConversion.h" 34#include "FloatRect.h" 35#include "Frame.h" 36#include "FrameSelection.h" 37#include "FrameTree.h" 38#include "FrameView.h" 39#include "HTMLNames.h" 40#include "NodeTraversal.h" 41#include "RenderObject.h" 42#include "RenderPart.h" 43#include "RenderSVGResource.h" 44#include "RenderSVGModelObject.h" 45#include "RenderSVGRoot.h" 46#include "RenderSVGViewportContainer.h" 47#include "SMILTimeContainer.h" 48#include "SVGAngle.h" 49#include "SVGElementInstance.h" 50#include "SVGFitToViewBox.h" 51#include "SVGNames.h" 52#include "SVGPreserveAspectRatio.h" 53#include "SVGTransform.h" 54#include "SVGTransformList.h" 55#include "SVGViewElement.h" 56#include "SVGViewSpec.h" 57#include "SVGZoomEvent.h" 58#include "ScriptEventListener.h" 59#include "StaticNodeList.h" 60#include <wtf/StdLibExtras.h> 61 62namespace WebCore { 63 64// Animated property definitions 65DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x) 66DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y) 67DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width) 68DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height) 69DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 70DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio) 71DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox) 72 73BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement) 74 REGISTER_LOCAL_ANIMATED_PROPERTY(x) 75 REGISTER_LOCAL_ANIMATED_PROPERTY(y) 76 REGISTER_LOCAL_ANIMATED_PROPERTY(width) 77 REGISTER_LOCAL_ANIMATED_PROPERTY(height) 78 REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) 79 REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox) 80 REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio) 81 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledTransformableElement) 82 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests) 83END_REGISTER_ANIMATED_PROPERTIES 84 85inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc) 86 : SVGStyledTransformableElement(tagName, doc) 87 , m_x(LengthModeWidth) 88 , m_y(LengthModeHeight) 89 , m_width(LengthModeWidth, "100%") 90 , m_height(LengthModeHeight, "100%") 91 , m_useCurrentView(false) 92 , m_zoomAndPan(SVGZoomAndPanMagnify) 93 , m_timeContainer(SMILTimeContainer::create(this)) 94{ 95 ASSERT(hasTagName(SVGNames::svgTag)); 96 registerAnimatedPropertiesForSVGSVGElement(); 97 doc->registerForPageCacheSuspensionCallbacks(this); 98} 99 100PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document* document) 101{ 102 return adoptRef(new SVGSVGElement(tagName, document)); 103} 104 105SVGSVGElement::~SVGSVGElement() 106{ 107 if (m_viewSpec) 108 m_viewSpec->resetContextElement(); 109 document()->unregisterForPageCacheSuspensionCallbacks(this); 110 // There are cases where removedFromDocument() is not called. 111 // see ContainerNode::removeAllChildren, called by its destructor. 112 document()->accessSVGExtensions()->removeTimeContainer(this); 113} 114 115void SVGSVGElement::didMoveToNewDocument(Document* oldDocument) 116{ 117 if (oldDocument) 118 oldDocument->unregisterForPageCacheSuspensionCallbacks(this); 119 document()->registerForPageCacheSuspensionCallbacks(this); 120 SVGStyledTransformableElement::didMoveToNewDocument(oldDocument); 121} 122 123const AtomicString& SVGSVGElement::contentScriptType() const 124{ 125 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral)); 126 const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr); 127 return n.isNull() ? defaultValue : n; 128} 129 130void SVGSVGElement::setContentScriptType(const AtomicString& type) 131{ 132 setAttribute(SVGNames::contentScriptTypeAttr, type); 133} 134 135const AtomicString& SVGSVGElement::contentStyleType() const 136{ 137 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral)); 138 const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr); 139 return n.isNull() ? defaultValue : n; 140} 141 142void SVGSVGElement::setContentStyleType(const AtomicString& type) 143{ 144 setAttribute(SVGNames::contentStyleTypeAttr, type); 145} 146 147FloatRect SVGSVGElement::viewport() const 148{ 149 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here. 150 // As we have no test coverage for this, we're going to disable it completly for now. 151 return FloatRect(); 152} 153 154float SVGSVGElement::pixelUnitToMillimeterX() const 155{ 156 // 2.54 / cssPixelsPerInch gives CM. 157 return (2.54f / cssPixelsPerInch) * 10.0f; 158} 159 160float SVGSVGElement::pixelUnitToMillimeterY() const 161{ 162 // 2.54 / cssPixelsPerInch gives CM. 163 return (2.54f / cssPixelsPerInch) * 10.0f; 164} 165 166float SVGSVGElement::screenPixelToMillimeterX() const 167{ 168 return pixelUnitToMillimeterX(); 169} 170 171float SVGSVGElement::screenPixelToMillimeterY() const 172{ 173 return pixelUnitToMillimeterY(); 174} 175 176SVGViewSpec* SVGSVGElement::currentView() 177{ 178 if (!m_viewSpec) 179 m_viewSpec = SVGViewSpec::create(this); 180 return m_viewSpec.get(); 181} 182 183float SVGSVGElement::currentScale() const 184{ 185 if (!inDocument() || !isOutermostSVGSVGElement()) 186 return 1; 187 188 Frame* frame = document()->frame(); 189 if (!frame) 190 return 1; 191 192 FrameTree* frameTree = frame->tree(); 193 ASSERT(frameTree); 194 195 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents. 196 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside 197 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale. 198 return frameTree->parent() ? 1 : frame->pageZoomFactor(); 199} 200 201void SVGSVGElement::setCurrentScale(float scale) 202{ 203 if (!inDocument() || !isOutermostSVGSVGElement()) 204 return; 205 206 Frame* frame = document()->frame(); 207 if (!frame) 208 return; 209 210 FrameTree* frameTree = frame->tree(); 211 ASSERT(frameTree); 212 213 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents. 214 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within 215 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG. 216 if (frameTree->parent()) 217 return; 218 219 frame->setPageZoomFactor(scale); 220} 221 222void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation) 223{ 224 m_translation = translation; 225 updateCurrentTranslate(); 226} 227 228void SVGSVGElement::updateCurrentTranslate() 229{ 230 if (RenderObject* object = renderer()) 231 object->setNeedsLayout(true); 232 233 if (parentNode() == document() && document()->renderer()) 234 document()->renderer()->repaint(); 235} 236 237void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 238{ 239 SVGParsingError parseError = NoError; 240 241 if (!nearestViewportElement()) { 242 bool setListener = true; 243 244 // Only handle events if we're the outermost <svg> element 245 if (name == HTMLNames::onunloadAttr) 246 document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), name, value)); 247 else if (name == HTMLNames::onresizeAttr) 248 document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), name, value)); 249 else if (name == HTMLNames::onscrollAttr) 250 document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), name, value)); 251 else if (name == SVGNames::onzoomAttr) 252 document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), name, value)); 253 else 254 setListener = false; 255 256 if (setListener) 257 return; 258 } 259 260 if (name == HTMLNames::onabortAttr) 261 document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), name, value)); 262 else if (name == HTMLNames::onerrorAttr) 263 document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), name, value)); 264 else if (name == SVGNames::xAttr) 265 setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError)); 266 else if (name == SVGNames::yAttr) 267 setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError)); 268 else if (name == SVGNames::widthAttr) 269 setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths)); 270 else if (name == SVGNames::heightAttr) 271 setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths)); 272 else if (SVGTests::parseAttribute(name, value) 273 || SVGLangSpace::parseAttribute(name, value) 274 || SVGExternalResourcesRequired::parseAttribute(name, value) 275 || SVGFitToViewBox::parseAttribute(this, name, value) 276 || SVGZoomAndPan::parseAttribute(this, name, value)) { 277 } else 278 SVGStyledTransformableElement::parseAttribute(name, value); 279 280 reportAttributeParsingError(parseError, name, value); 281} 282 283void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) 284{ 285 bool updateRelativeLengthsOrViewBox = false; 286 bool widthChanged = attrName == SVGNames::widthAttr; 287 if (widthChanged 288 || attrName == SVGNames::heightAttr 289 || attrName == SVGNames::xAttr 290 || attrName == SVGNames::yAttr) { 291 updateRelativeLengthsOrViewBox = true; 292 updateRelativeLengthsInformation(); 293 294 // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can 295 // affect the replaced size so we need to mark it for updating. 296 if (widthChanged) { 297 RenderObject* renderObject = renderer(); 298 if (renderObject && renderObject->isSVGRoot()) 299 toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc(); 300 } 301 } 302 303 if (SVGFitToViewBox::isKnownAttribute(attrName)) { 304 updateRelativeLengthsOrViewBox = true; 305 if (RenderObject* object = renderer()) 306 object->setNeedsTransformUpdate(); 307 } 308 309 SVGElementInstance::InvalidationGuard invalidationGuard(this); 310 if (SVGTests::handleAttributeChange(this, attrName)) 311 return; 312 313 if (updateRelativeLengthsOrViewBox 314 || SVGLangSpace::isKnownAttribute(attrName) 315 || SVGExternalResourcesRequired::isKnownAttribute(attrName) 316 || SVGZoomAndPan::isKnownAttribute(attrName)) { 317 if (renderer()) 318 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer()); 319 return; 320 } 321 322 SVGStyledElement::svgAttributeChanged(attrName); 323} 324 325unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */) 326{ 327 // FIXME: Implement me (see bug 11275) 328 return 0; 329} 330 331void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */) 332{ 333 // FIXME: Implement me (see bug 11275) 334} 335 336void SVGSVGElement::unsuspendRedrawAll() 337{ 338 // FIXME: Implement me (see bug 11275) 339} 340 341void SVGSVGElement::forceRedraw() 342{ 343 // FIXME: Implement me (see bug 11275) 344} 345 346PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const 347{ 348 Vector<RefPtr<Node> > nodes; 349 Element* element = ElementTraversal::next(referenceElement ? referenceElement : this); 350 while (element) { 351 if (element->isSVGElement()) { 352 SVGElement* svgElement = toSVGElement(element); 353 if (collect == CollectIntersectionList) { 354 if (checkIntersection(svgElement, rect)) 355 nodes.append(element); 356 } else { 357 if (checkEnclosure(svgElement, rect)) 358 nodes.append(element); 359 } 360 } 361 362 element = ElementTraversal::next(element, referenceElement ? referenceElement : this); 363 } 364 return StaticNodeList::adopt(nodes); 365} 366 367PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const 368{ 369 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList); 370} 371 372PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const 373{ 374 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList); 375} 376 377bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect) const 378{ 379 if (!element) 380 return false; 381 return RenderSVGModelObject::checkIntersection(element->renderer(), rect); 382} 383 384bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect) const 385{ 386 if (!element) 387 return false; 388 return RenderSVGModelObject::checkEnclosure(element->renderer(), rect); 389} 390 391void SVGSVGElement::deselectAll() 392{ 393 if (Frame* frame = document()->frame()) 394 frame->selection()->clear(); 395} 396 397float SVGSVGElement::createSVGNumber() 398{ 399 return 0.0f; 400} 401 402SVGLength SVGSVGElement::createSVGLength() 403{ 404 return SVGLength(); 405} 406 407SVGAngle SVGSVGElement::createSVGAngle() 408{ 409 return SVGAngle(); 410} 411 412FloatPoint SVGSVGElement::createSVGPoint() 413{ 414 return FloatPoint(); 415} 416 417SVGMatrix SVGSVGElement::createSVGMatrix() 418{ 419 return SVGMatrix(); 420} 421 422FloatRect SVGSVGElement::createSVGRect() 423{ 424 return FloatRect(); 425} 426 427SVGTransform SVGSVGElement::createSVGTransform() 428{ 429 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX); 430} 431 432SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix) 433{ 434 return SVGTransform(static_cast<const AffineTransform&>(matrix)); 435} 436 437AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const 438{ 439 AffineTransform viewBoxTransform; 440 if (!hasEmptyViewBox()) { 441 FloatSize size = currentViewportSize(); 442 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height()); 443 } 444 445 AffineTransform transform; 446 if (!isOutermostSVGSVGElement()) { 447 SVGLengthContext lengthContext(this); 448 transform.translate(x().value(lengthContext), y().value(lengthContext)); 449 } else if (mode == SVGLocatable::ScreenScope) { 450 if (RenderObject* renderer = this->renderer()) { 451 FloatPoint location; 452 float zoomFactor = 1; 453 454 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 455 // to map an element from SVG viewport coordinates to CSS box coordinates. 456 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates. 457 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361). 458 if (renderer->isSVGRoot()) { 459 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location); 460 zoomFactor = 1 / renderer->style()->effectiveZoom(); 461 } 462 463 // Translate in our CSS parent coordinate space 464 // FIXME: This doesn't work correctly with CSS transforms. 465 location = renderer->localToAbsolute(location, UseTransforms); 466 location.scale(zoomFactor, zoomFactor); 467 468 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(), 469 // so we have to subtract it here (original cause of bug #27183) 470 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f()); 471 472 // Respect scroll offset. 473 if (FrameView* view = document()->view()) { 474 LayoutSize scrollOffset = view->scrollOffset(); 475 scrollOffset.scale(zoomFactor); 476 transform.translate(-scrollOffset.width(), -scrollOffset.height()); 477 } 478 } 479 } 480 481 return transform.multiply(viewBoxTransform); 482} 483 484bool SVGSVGElement::rendererIsNeeded(const NodeRenderingContext& context) 485{ 486 // FIXME: We should respect display: none on the documentElement svg element 487 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when 488 // they should instead depend on the RenderView. 489 // https://bugs.webkit.org/show_bug.cgi?id=103493 490 if (document()->documentElement() == this) 491 return true; 492 return StyledElement::rendererIsNeeded(context); 493} 494 495RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*) 496{ 497 if (isOutermostSVGSVGElement()) 498 return new (arena) RenderSVGRoot(this); 499 500 return new (arena) RenderSVGViewportContainer(this); 501} 502 503Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent) 504{ 505 if (rootParent->inDocument()) { 506 document()->accessSVGExtensions()->addTimeContainer(this); 507 508 // Animations are started at the end of document parsing and after firing the load event, 509 // but if we miss that train (deferred programmatic element insertion for example) we need 510 // to initialize the time container here. 511 if (!document()->parsing() && !document()->processingLoadEvent() && document()->loadEventFinished() && !timeContainer()->isStarted()) 512 timeContainer()->begin(); 513 } 514 return SVGStyledTransformableElement::insertedInto(rootParent); 515} 516 517void SVGSVGElement::removedFrom(ContainerNode* rootParent) 518{ 519 if (rootParent->inDocument()) 520 document()->accessSVGExtensions()->removeTimeContainer(this); 521 SVGStyledTransformableElement::removedFrom(rootParent); 522} 523 524void SVGSVGElement::pauseAnimations() 525{ 526 if (!m_timeContainer->isPaused()) 527 m_timeContainer->pause(); 528} 529 530void SVGSVGElement::unpauseAnimations() 531{ 532 if (m_timeContainer->isPaused()) 533 m_timeContainer->resume(); 534} 535 536bool SVGSVGElement::animationsPaused() const 537{ 538 return m_timeContainer->isPaused(); 539} 540 541float SVGSVGElement::getCurrentTime() const 542{ 543 return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); 544} 545 546void SVGSVGElement::setCurrentTime(float seconds) 547{ 548 if (std::isnan(seconds)) 549 return; 550 seconds = max(seconds, 0.0f); 551 m_timeContainer->setElapsed(seconds); 552} 553 554bool SVGSVGElement::selfHasRelativeLengths() const 555{ 556 return x().isRelative() 557 || y().isRelative() 558 || width().isRelative() 559 || height().isRelative() 560 || hasAttribute(SVGNames::viewBoxAttr); 561} 562 563FloatRect SVGSVGElement::currentViewBoxRect() const 564{ 565 if (m_useCurrentView) 566 return m_viewSpec ? m_viewSpec->viewBox() : FloatRect(); 567 568 FloatRect useViewBox = viewBox(); 569 if (!useViewBox.isEmpty()) 570 return useViewBox; 571 if (!renderer() || !renderer()->isSVGRoot()) 572 return FloatRect(); 573 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage()) 574 return FloatRect(); 575 576 Length intrinsicWidth = this->intrinsicWidth(); 577 Length intrinsicHeight = this->intrinsicHeight(); 578 if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed()) 579 return FloatRect(); 580 581 // If no viewBox is specified but non-relative width/height values, then we 582 // should always synthesize a viewBox if we're embedded through a SVGImage. 583 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0))); 584} 585 586FloatSize SVGSVGElement::currentViewportSize() const 587{ 588 Length intrinsicWidth = this->intrinsicWidth(); 589 Length intrinsicHeight = this->intrinsicHeight(); 590 if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed()) 591 return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)); 592 593 if (!renderer()) 594 return FloatSize(); 595 596 if (renderer()->isSVGRoot()) { 597 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect(); 598 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom()); 599 } 600 601 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport(); 602 return FloatSize(viewportRect.width(), viewportRect.height()); 603} 604 605bool SVGSVGElement::widthAttributeEstablishesViewport() const 606{ 607 if (!renderer() || renderer()->isSVGViewportContainer()) 608 return true; 609 610 // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace 611 // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met: 612 // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or 613 // the SVG content is embedded inline within a containing document; 614 // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL]; 615 // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element) 616 // or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions, 617 // the positioning properties establish the viewport's width. 618 RenderSVGRoot* root = toRenderSVGRoot(renderer()); 619 620 // SVG embedded through object/embed/iframe. 621 if (root->isEmbeddedThroughFrameContainingSVGDocument()) 622 return !root->hasReplacedLogicalWidth() && !document()->frame()->ownerRenderer()->hasReplacedLogicalWidth(); 623 624 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. 625 if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this) 626 return !root->hasReplacedLogicalWidth(); 627 628 return true; 629} 630 631bool SVGSVGElement::heightAttributeEstablishesViewport() const 632{ 633 if (!renderer() || renderer()->isSVGViewportContainer()) 634 return true; 635 636 // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing 637 // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element 638 // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's 639 // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height. 640 RenderSVGRoot* root = toRenderSVGRoot(renderer()); 641 642 // SVG embedded through object/embed/iframe. 643 if (root->isEmbeddedThroughFrameContainingSVGDocument()) 644 return !root->hasReplacedLogicalHeight() && !document()->frame()->ownerRenderer()->hasReplacedLogicalHeight(); 645 646 // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. 647 if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this) 648 return !root->hasReplacedLogicalHeight(); 649 650 return true; 651} 652 653Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const 654{ 655 if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) { 656 if (width().unitType() == LengthTypePercentage) 657 return Length(width().valueAsPercentage() * 100, Percent); 658 659 SVGLengthContext lengthContext(this); 660 return Length(width().value(lengthContext), Fixed); 661 } 662 663 ASSERT(renderer()); 664 return renderer()->style()->width(); 665} 666 667Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const 668{ 669 if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) { 670 if (height().unitType() == LengthTypePercentage) 671 return Length(height().valueAsPercentage() * 100, Percent); 672 673 SVGLengthContext lengthContext(this); 674 return Length(height().value(lengthContext), Fixed); 675 } 676 677 ASSERT(renderer()); 678 return renderer()->style()->height(); 679} 680 681AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const 682{ 683 if (!m_useCurrentView || !m_viewSpec) 684 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight); 685 686 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio(), viewWidth, viewHeight); 687 const SVGTransformList& transformList = m_viewSpec->transformBaseValue(); 688 if (transformList.isEmpty()) 689 return ctm; 690 691 AffineTransform transform; 692 if (transformList.concatenate(transform)) 693 ctm *= transform; 694 695 return ctm; 696} 697 698void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode) 699{ 700 RenderObject* renderer = this->renderer(); 701 SVGViewSpec* view = m_viewSpec.get(); 702 if (view) 703 view->reset(); 704 705 bool hadUseCurrentView = m_useCurrentView; 706 m_useCurrentView = false; 707 708 if (fragmentIdentifier.startsWith("xpointer(")) { 709 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491) 710 if (renderer && hadUseCurrentView) 711 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 712 return; 713 } 714 715 if (fragmentIdentifier.startsWith("svgView(")) { 716 if (!view) 717 view = currentView(); // Create the SVGViewSpec. 718 719 if (view->parseViewSpec(fragmentIdentifier)) 720 m_useCurrentView = true; 721 else 722 view->reset(); 723 724 if (renderer && (hadUseCurrentView || m_useCurrentView)) 725 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 726 return; 727 } 728 729 // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView 730 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport. 731 // Any view specification attributes included on the given ‘view’ element override the corresponding view specification 732 // attributes on the closest ancestor ‘svg’ element. 733 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) { 734 if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) { 735 SVGElement* element = SVGLocatable::nearestViewportElement(viewElement); 736 if (element->hasTagName(SVGNames::svgTag)) { 737 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element); 738 svg->inheritViewAttributes(viewElement); 739 740 if (RenderObject* renderer = svg->renderer()) 741 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); 742 } 743 } 744 return; 745 } 746 747 // FIXME: We need to decide which <svg> to focus on, and zoom to it. 748 // FIXME: We need to actually "highlight" the viewTarget(s). 749} 750 751void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement) 752{ 753 SVGViewSpec* view = currentView(); 754 m_useCurrentView = true; 755 756 if (viewElement->hasAttribute(SVGNames::viewBoxAttr)) 757 view->setViewBoxBaseValue(viewElement->viewBox()); 758 else 759 view->setViewBoxBaseValue(viewBox()); 760 761 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) 762 view->setPreserveAspectRatioBaseValue(viewElement->preserveAspectRatioBaseValue()); 763 else 764 view->setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue()); 765 766 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr)) 767 view->setZoomAndPanBaseValue(viewElement->zoomAndPan()); 768 else 769 view->setZoomAndPanBaseValue(zoomAndPan()); 770} 771 772void SVGSVGElement::documentWillSuspendForPageCache() 773{ 774 pauseAnimations(); 775} 776 777void SVGSVGElement::documentDidResumeFromPageCache() 778{ 779 unpauseAnimations(); 780} 781 782// getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element. 783// See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement 784Element* SVGSVGElement::getElementById(const AtomicString& id) const 785{ 786 Element* element = treeScope()->getElementById(id); 787 if (element && element->isDescendantOf(this)) 788 return element; 789 790 // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will 791 // be returned. 792 for (Node* node = firstChild(); node; node = NodeTraversal::next(node, this)) { 793 if (!node->isElementNode()) 794 continue; 795 796 Element* element = toElement(node); 797 if (element->getIdAttribute() == id) 798 return element; 799 } 800 return 0; 801} 802 803} 804 805#endif // ENABLE(SVG) 806