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