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 * 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 61 namespace WebCore { 62 63 // Animated property definitions 64 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x) 65 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y) 66 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width) 67 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height) 68 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) 69 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio) 70 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox) 71 72 BEGIN_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) 81 END_REGISTER_ANIMATED_PROPERTIES 82 83 inline 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 100 PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document) 101 { 102 return adoptRef(new SVGSVGElement(document)); 103 } 104 105 SVGSVGElement::~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 114 const 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 121 void SVGSVGElement::setContentScriptType(const AtomicString& type) 122 { 123 setAttribute(SVGNames::contentScriptTypeAttr, type); 124 } 125 126 const 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 133 void SVGSVGElement::setContentStyleType(const AtomicString& type) 134 { 135 setAttribute(SVGNames::contentStyleTypeAttr, type); 136 } 137 138 SVGRect 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 145 float SVGSVGElement::pixelUnitToMillimeterX() const 146 { 147 return 1 / cssPixelsPerMillimeter; 148 } 149 150 float SVGSVGElement::pixelUnitToMillimeterY() const 151 { 152 return 1 / cssPixelsPerMillimeter; 153 } 154 155 float SVGSVGElement::screenPixelToMillimeterX() const 156 { 157 return pixelUnitToMillimeterX(); 158 } 159 160 float SVGSVGElement::screenPixelToMillimeterY() const 161 { 162 return pixelUnitToMillimeterY(); 163 } 164 165 SVGViewSpec* SVGSVGElement::currentView() 166 { 167 if (!m_viewSpec) 168 m_viewSpec = SVGViewSpec::create(m_weakFactory.createWeakPtr()); 169 return m_viewSpec.get(); 170 } 171 172 float 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 189 void 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 209 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation) 210 { 211 m_translation = translation; 212 updateCurrentTranslate(); 213 } 214 215 void SVGSVGElement::updateCurrentTranslate() 216 { 217 if (RenderObject* object = renderer()) 218 object->setNeedsLayout(); 219 220 if (parentNode() == document() && document().renderer()) 221 document().renderer()->repaint(); 222 } 223 224 void 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 268 void 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 308 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */) 309 { 310 // FIXME: Implement me (see bug 11275) 311 return 0; 312 } 313 314 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */) 315 { 316 // FIXME: Implement me (see bug 11275) 317 } 318 319 void SVGSVGElement::unsuspendRedrawAll() 320 { 321 // FIXME: Implement me (see bug 11275) 322 } 323 324 void SVGSVGElement::forceRedraw() 325 { 326 // FIXME: Implement me (see bug 11275) 327 } 328 329 PassRefPtr<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 350 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const SVGRect& rect, SVGElement* referenceElement) const 351 { 352 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList); 353 } 354 355 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const SVGRect& rect, SVGElement* referenceElement) const 356 { 357 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList); 358 } 359 360 bool SVGSVGElement::checkIntersection(SVGElement* element, const SVGRect& rect) const 361 { 362 if (!element) 363 return false; 364 return RenderSVGModelObject::checkIntersection(element->renderer(), rect); 365 } 366 367 bool SVGSVGElement::checkEnclosure(SVGElement* element, const SVGRect& rect) const 368 { 369 if (!element) 370 return false; 371 return RenderSVGModelObject::checkEnclosure(element->renderer(), rect); 372 } 373 374 void SVGSVGElement::deselectAll() 375 { 376 if (Frame* frame = document().frame()) 377 frame->selection().clear(); 378 } 379 380 float SVGSVGElement::createSVGNumber() 381 { 382 return 0.0f; 383 } 384 385 SVGLength SVGSVGElement::createSVGLength() 386 { 387 return SVGLength(); 388 } 389 390 SVGAngle SVGSVGElement::createSVGAngle() 391 { 392 return SVGAngle(); 393 } 394 395 SVGPoint SVGSVGElement::createSVGPoint() 396 { 397 return SVGPoint(); 398 } 399 400 SVGMatrix SVGSVGElement::createSVGMatrix() 401 { 402 return SVGMatrix(); 403 } 404 405 SVGRect SVGSVGElement::createSVGRect() 406 { 407 return SVGRect(); 408 } 409 410 SVGTransform SVGSVGElement::createSVGTransform() 411 { 412 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX); 413 } 414 415 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix) 416 { 417 return SVGTransform(static_cast<const AffineTransform&>(matrix)); 418 } 419 420 AffineTransform 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 467 bool 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 478 RenderObject* SVGSVGElement::createRenderer(RenderStyle*) 479 { 480 if (isOutermostSVGSVGElement()) 481 return new RenderSVGRoot(this); 482 483 return new RenderSVGViewportContainer(this); 484 } 485 486 Node::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 500 void 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 511 void SVGSVGElement::pauseAnimations() 512 { 513 if (!m_timeContainer->isPaused()) 514 m_timeContainer->pause(); 515 } 516 517 void SVGSVGElement::unpauseAnimations() 518 { 519 if (m_timeContainer->isPaused()) 520 m_timeContainer->resume(); 521 } 522 523 bool SVGSVGElement::animationsPaused() const 524 { 525 return m_timeContainer->isPaused(); 526 } 527 528 float SVGSVGElement::getCurrentTime() const 529 { 530 return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); 531 } 532 533 void 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 541 bool SVGSVGElement::selfHasRelativeLengths() const 542 { 543 return xCurrentValue().isRelative() 544 || yCurrentValue().isRelative() 545 || widthCurrentValue().isRelative() 546 || heightCurrentValue().isRelative() 547 || hasAttribute(SVGNames::viewBoxAttr); 548 } 549 550 SVGRect 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 573 FloatSize 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 592 bool 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 618 bool 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 640 Length 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 654 Length 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 668 AffineTransform 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 685 void 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 737 void 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 760 Element* 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