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