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.
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.
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.
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.
25 #include "SVGSVGElement.h"
27 #include "AffineTransform.h"
28 #include "Attribute.h"
29 #include "CSSHelper.h"
30 #include "CSSPropertyNames.h"
32 #include "EventListener.h"
33 #include "EventNames.h"
34 #include "FloatConversion.h"
35 #include "FloatRect.h"
37 #include "FrameSelection.h"
38 #include "FrameTree.h"
39 #include "FrameView.h"
40 #include "HTMLNames.h"
41 #include "RenderSVGResource.h"
42 #include "RenderSVGModelObject.h"
43 #include "RenderSVGRoot.h"
44 #include "RenderSVGViewportContainer.h"
45 #include "SMILTimeContainer.h"
47 #include "SVGElementInstance.h"
49 #include "SVGPreserveAspectRatio.h"
50 #include "SVGTransform.h"
51 #include "SVGTransformList.h"
52 #include "SVGViewElement.h"
53 #include "SVGViewSpec.h"
54 #include "SVGZoomEvent.h"
55 #include "ScriptEventListener.h"
56 #include "StaticNodeList.h"
57 #include <wtf/StdLibExtras.h>
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)
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(SVGStyledLocatableElement)
79 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
80 END_REGISTER_ANIMATED_PROPERTIES
82 inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
83 : SVGStyledLocatableElement(tagName, doc)
84 , m_x(LengthModeWidth)
85 , m_y(LengthModeHeight)
86 , m_width(LengthModeWidth, "100%")
87 , m_height(LengthModeHeight, "100%")
88 , m_useCurrentView(false)
89 , m_timeContainer(SMILTimeContainer::create(this))
90 , m_containerSize(300, 150)
91 , m_hasSetContainerSize(false)
93 ASSERT(hasTagName(SVGNames::svgTag));
94 registerAnimatedPropertiesForSVGSVGElement();
95 doc->registerForDocumentActivationCallbacks(this);
98 PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document* document)
100 return adoptRef(new SVGSVGElement(tagName, document));
103 SVGSVGElement::~SVGSVGElement()
105 document()->unregisterForDocumentActivationCallbacks(this);
106 // There are cases where removedFromDocument() is not called.
107 // see ContainerNode::removeAllChildren, called by its destructor.
108 document()->accessSVGExtensions()->removeTimeContainer(this);
111 void SVGSVGElement::willMoveToNewOwnerDocument()
113 document()->unregisterForDocumentActivationCallbacks(this);
114 SVGStyledLocatableElement::willMoveToNewOwnerDocument();
117 void SVGSVGElement::didMoveToNewOwnerDocument()
119 document()->registerForDocumentActivationCallbacks(this);
120 SVGStyledLocatableElement::didMoveToNewOwnerDocument();
123 const AtomicString& SVGSVGElement::contentScriptType() const
125 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
126 const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
127 return n.isNull() ? defaultValue : n;
130 void SVGSVGElement::setContentScriptType(const AtomicString& type)
132 setAttribute(SVGNames::contentScriptTypeAttr, type);
135 const AtomicString& SVGSVGElement::contentStyleType() const
137 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
138 const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
139 return n.isNull() ? defaultValue : n;
142 void SVGSVGElement::setContentStyleType(const AtomicString& type)
144 setAttribute(SVGNames::contentStyleTypeAttr, type);
147 FloatRect SVGSVGElement::viewport() const
149 FloatRect viewRectangle;
150 if (!isOutermostSVG())
151 viewRectangle.setLocation(FloatPoint(x().value(this), y().value(this)));
153 viewRectangle.setSize(FloatSize(width().value(this), height().value(this)));
154 return viewBoxToViewTransform(viewRectangle.width(), viewRectangle.height()).mapRect(viewRectangle);
157 int SVGSVGElement::relativeWidthValue() const
159 SVGLength w = width();
160 if (w.unitType() != LengthTypePercentage)
163 return static_cast<int>(w.valueAsPercentage() * m_containerSize.width());
166 int SVGSVGElement::relativeHeightValue() const
168 SVGLength h = height();
169 if (h.unitType() != LengthTypePercentage)
172 return static_cast<int>(h.valueAsPercentage() * m_containerSize.height());
175 float SVGSVGElement::pixelUnitToMillimeterX() const
177 // 2.54 / cssPixelsPerInch gives CM.
178 return (2.54f / cssPixelsPerInch) * 10.0f;
181 float SVGSVGElement::pixelUnitToMillimeterY() const
183 // 2.54 / cssPixelsPerInch gives CM.
184 return (2.54f / cssPixelsPerInch) * 10.0f;
187 float SVGSVGElement::screenPixelToMillimeterX() const
189 return pixelUnitToMillimeterX();
192 float SVGSVGElement::screenPixelToMillimeterY() const
194 return pixelUnitToMillimeterY();
197 bool SVGSVGElement::useCurrentView() const
199 return m_useCurrentView;
202 void SVGSVGElement::setUseCurrentView(bool currentView)
204 m_useCurrentView = currentView;
207 SVGViewSpec* SVGSVGElement::currentView() const
210 m_viewSpec = adoptPtr(new SVGViewSpec(const_cast<SVGSVGElement*>(this)));
211 return m_viewSpec.get();
214 float SVGSVGElement::currentScale() const
216 if (!inDocument() || !isOutermostSVG())
219 Frame* frame = document()->frame();
223 FrameTree* frameTree = frame->tree();
226 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
227 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
228 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
229 return frameTree->parent() ? 1 : frame->pageZoomFactor();
232 void SVGSVGElement::setCurrentScale(float scale)
234 if (!inDocument() || !isOutermostSVG())
237 Frame* frame = document()->frame();
241 FrameTree* frameTree = frame->tree();
244 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
245 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
246 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
247 if (frameTree->parent())
250 frame->setPageZoomFactor(scale);
253 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
255 m_translation = translation;
256 updateCurrentTranslate();
259 void SVGSVGElement::updateCurrentTranslate()
261 if (RenderObject* object = renderer())
262 object->setNeedsLayout(true);
264 if (parentNode() == document() && document()->renderer())
265 document()->renderer()->repaint();
268 void SVGSVGElement::parseMappedAttribute(Attribute* attr)
270 SVGParsingError parseError = NoError;
272 if (!nearestViewportElement()) {
273 bool setListener = true;
275 // Only handle events if we're the outermost <svg> element
276 if (attr->name() == HTMLNames::onunloadAttr)
277 document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
278 else if (attr->name() == HTMLNames::onresizeAttr)
279 document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
280 else if (attr->name() == HTMLNames::onscrollAttr)
281 document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
282 else if (attr->name() == SVGNames::onzoomAttr)
283 document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
291 if (attr->name() == HTMLNames::onabortAttr)
292 document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
293 else if (attr->name() == HTMLNames::onerrorAttr)
294 document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
295 else if (attr->name() == SVGNames::xAttr)
296 setXBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError));
297 else if (attr->name() == SVGNames::yAttr)
298 setYBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError));
299 else if (attr->name() == SVGNames::widthAttr) {
300 setWidthBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError, ForbidNegativeLengths));
301 addCSSProperty(attr, CSSPropertyWidth, attr->value());
302 } else if (attr->name() == SVGNames::heightAttr) {
303 setHeightBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError, ForbidNegativeLengths));
304 addCSSProperty(attr, CSSPropertyHeight, attr->value());
305 } else if (SVGTests::parseMappedAttribute(attr)
306 || SVGLangSpace::parseMappedAttribute(attr)
307 || SVGExternalResourcesRequired::parseMappedAttribute(attr)
308 || SVGFitToViewBox::parseMappedAttribute(document(), attr)
309 || SVGZoomAndPan::parseMappedAttribute(attr)) {
311 SVGStyledLocatableElement::parseMappedAttribute(attr);
313 reportAttributeParsingError(parseError, attr);
316 // This hack will not handle the case where we're setting a width/height
317 // on a root <svg> via svg.width.baseValue = when it has none.
318 static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value)
320 Attribute* attribute = element->attributes(false)->getAttributeItem(attrName);
321 if (!attribute || !attribute->isMappedAttribute())
323 element->addCSSProperty(attribute, property, value.valueAsString());
326 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
328 // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called
329 // when svg.width.baseValue = 100 is evaluated.
330 // Thus the CSS length value for width is not updated, and width() computeLogicalWidth()
331 // calculations on RenderSVGRoot will be wrong.
332 // https://bugs.webkit.org/show_bug.cgi?id=25387
333 bool updateRelativeLengths = false;
334 if (attrName == SVGNames::widthAttr) {
335 updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue());
336 updateRelativeLengths = true;
337 } else if (attrName == SVGNames::heightAttr) {
338 updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue());
339 updateRelativeLengths = true;
342 if (updateRelativeLengths
343 || attrName == SVGNames::xAttr
344 || attrName == SVGNames::yAttr
345 || SVGFitToViewBox::isKnownAttribute(attrName)) {
346 updateRelativeLengths = true;
347 updateRelativeLengthsInformation();
350 SVGElementInstance::InvalidationGuard invalidationGuard(this);
351 if (SVGTests::handleAttributeChange(this, attrName))
354 if (updateRelativeLengths
355 || SVGLangSpace::isKnownAttribute(attrName)
356 || SVGExternalResourcesRequired::isKnownAttribute(attrName)
357 || SVGZoomAndPan::isKnownAttribute(attrName)) {
359 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
363 SVGStyledElement::svgAttributeChanged(attrName);
366 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
368 // FIXME: Implement me (see bug 11275)
372 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
374 // FIXME: Implement me (see bug 11275)
377 void SVGSVGElement::unsuspendRedrawAll()
379 // FIXME: Implement me (see bug 11275)
382 void SVGSVGElement::forceRedraw()
384 // FIXME: Implement me (see bug 11275)
387 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
389 Vector<RefPtr<Node> > nodes;
390 Node* node = traverseNextNode(referenceElement ? referenceElement : this);
392 if (node->isSVGElement()) {
393 if (collect == CollectIntersectionList) {
394 if (checkIntersection(static_cast<SVGElement*>(node), rect))
397 if (checkEnclosure(static_cast<SVGElement*>(node), rect))
402 node = node->traverseNextNode(referenceElement ? referenceElement : this);
404 return StaticNodeList::adopt(nodes);
407 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const
409 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
412 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const
414 return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
417 bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect) const
419 return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
422 bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect) const
424 return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
427 void SVGSVGElement::deselectAll()
429 if (Frame* frame = document()->frame())
430 frame->selection()->clear();
433 float SVGSVGElement::createSVGNumber()
438 SVGLength SVGSVGElement::createSVGLength()
443 SVGAngle SVGSVGElement::createSVGAngle()
448 FloatPoint SVGSVGElement::createSVGPoint()
453 SVGMatrix SVGSVGElement::createSVGMatrix()
458 FloatRect SVGSVGElement::createSVGRect()
463 SVGTransform SVGSVGElement::createSVGTransform()
465 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
468 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
470 return SVGTransform(static_cast<const AffineTransform&>(matrix));
473 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
475 AffineTransform viewBoxTransform;
476 if (attributes()->getAttributeItem(SVGNames::viewBoxAttr))
477 viewBoxTransform = viewBoxToViewTransform(width().value(this), height().value(this));
479 AffineTransform transform;
480 if (!isOutermostSVG())
481 transform.translate(x().value(this), y().value(this));
482 else if (mode == SVGLocatable::ScreenScope) {
483 if (RenderObject* renderer = this->renderer()) {
484 // Translate in our CSS parent coordinate space
485 // FIXME: This doesn't work correctly with CSS transforms.
486 FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true);
488 // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because
489 // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute())
490 // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183)
491 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
493 // Respect scroll offset.
494 if (FrameView* view = document()->view()) {
495 LayoutSize scrollOffset = view->scrollOffset();
496 transform.translate(-scrollOffset.width(), -scrollOffset.height());
501 return transform.multiply(viewBoxTransform);
504 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
506 if (isOutermostSVG())
507 return new (arena) RenderSVGRoot(this);
509 return new (arena) RenderSVGViewportContainer(this);
512 void SVGSVGElement::insertedIntoDocument()
514 document()->accessSVGExtensions()->addTimeContainer(this);
515 SVGStyledLocatableElement::insertedIntoDocument();
518 void SVGSVGElement::removedFromDocument()
520 document()->accessSVGExtensions()->removeTimeContainer(this);
521 SVGStyledLocatableElement::removedFromDocument();
524 void SVGSVGElement::pauseAnimations()
526 if (!m_timeContainer->isPaused())
527 m_timeContainer->pause();
530 void SVGSVGElement::unpauseAnimations()
532 if (m_timeContainer->isPaused())
533 m_timeContainer->resume();
536 bool SVGSVGElement::animationsPaused() const
538 return m_timeContainer->isPaused();
541 float SVGSVGElement::getCurrentTime() const
543 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
546 void SVGSVGElement::setCurrentTime(float /* seconds */)
548 // FIXME: Implement me, bug 12073
551 bool SVGSVGElement::selfHasRelativeLengths() const
553 return x().isRelative()
555 || width().isRelative()
556 || height().isRelative()
557 || hasAttribute(SVGNames::viewBoxAttr);
560 bool SVGSVGElement::isOutermostSVG() const
562 // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
566 // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element.
567 if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
570 // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
571 return !parentNode()->isSVGElement();
574 FloatRect SVGSVGElement::currentViewBoxRect() const
576 if (useCurrentView()) {
577 if (SVGViewSpec* view = currentView()) // what if we should use it but it is not set?
578 return view->viewBox();
585 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
587 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight);
588 if (useCurrentView() && currentView()) {
589 AffineTransform transform;
590 if (currentView()->transform().concatenate(transform))
597 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
599 bool hadUseCurrentView = m_useCurrentView;
600 setUseCurrentView(false);
601 if (fragmentIdentifier.startsWith("xpointer(")) {
602 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
605 if (fragmentIdentifier.startsWith("svgView(")) {
606 if (!currentView()->parseViewSpec(fragmentIdentifier))
608 setUseCurrentView(true);
611 if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
612 SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0;
614 SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
615 if (element->hasTagName(SVGNames::svgTag)) {
616 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
617 svg->inheritViewAttributes(viewElement);
618 setUseCurrentView(true);
623 if (hadUseCurrentView) {
624 currentView()->setTransform(emptyString());
625 if (RenderObject* object = renderer())
626 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
628 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
629 // FIXME: We need to actually "highlight" the viewTarget(s).
632 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
634 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
635 currentView()->setViewBoxBaseValue(viewElement->viewBox());
637 currentView()->setViewBoxBaseValue(viewBox());
639 SVGPreserveAspectRatio aspectRatio;
640 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
641 aspectRatio = viewElement->preserveAspectRatioBaseValue();
643 aspectRatio = preserveAspectRatioBaseValue();
644 currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
646 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
647 currentView()->setZoomAndPan(viewElement->zoomAndPan());
649 if (RenderObject* object = renderer())
650 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
653 void SVGSVGElement::documentWillBecomeInactive()
658 void SVGSVGElement::documentDidBecomeActive()
663 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
664 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
665 Element* SVGSVGElement::getElementById(const AtomicString& id) const
667 Element* element = treeScope()->getElementById(id);
668 if (element && element->isDescendantOf(this))
671 // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
673 for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) {
674 if (!node->isElementNode())
677 Element* element = static_cast<Element*>(node);
678 if (element->hasID() && element->getIdAttribute() == id)
686 #endif // ENABLE(SVG)