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"
36 #include "FrameView.h"
37 #include "HTMLNames.h"
38 #include "RenderSVGResource.h"
39 #include "RenderSVGRoot.h"
40 #include "RenderSVGViewportContainer.h"
41 #include "SMILTimeContainer.h"
44 #include "SVGPreserveAspectRatio.h"
45 #include "SVGTransform.h"
46 #include "SVGTransformList.h"
47 #include "SVGViewElement.h"
48 #include "SVGViewSpec.h"
49 #include "SVGZoomEvent.h"
50 #include "ScriptEventListener.h"
51 #include "SelectionController.h"
52 #include <wtf/StdLibExtras.h>
56 // Animated property definitions
57 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
58 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
59 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
60 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
61 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
62 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
63 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
65 inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
66 : SVGStyledLocatableElement(tagName, doc)
67 , m_x(LengthModeWidth)
68 , m_y(LengthModeHeight)
69 , m_width(LengthModeWidth, "100%")
70 , m_height(LengthModeHeight, "100%")
71 , m_useCurrentView(false)
72 , m_timeContainer(SMILTimeContainer::create(this))
75 , m_containerSize(300, 150)
76 , m_hasSetContainerSize(false)
78 doc->registerForDocumentActivationCallbacks(this);
81 PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document* document)
83 return adoptRef(new SVGSVGElement(tagName, document));
86 SVGSVGElement::~SVGSVGElement()
88 document()->unregisterForDocumentActivationCallbacks(this);
89 // There are cases where removedFromDocument() is not called.
90 // see ContainerNode::removeAllChildren, called by its destructor.
91 document()->accessSVGExtensions()->removeTimeContainer(this);
94 void SVGSVGElement::willMoveToNewOwnerDocument()
96 document()->unregisterForDocumentActivationCallbacks(this);
97 SVGStyledLocatableElement::willMoveToNewOwnerDocument();
100 void SVGSVGElement::didMoveToNewOwnerDocument()
102 document()->registerForDocumentActivationCallbacks(this);
103 SVGStyledLocatableElement::didMoveToNewOwnerDocument();
106 const AtomicString& SVGSVGElement::contentScriptType() const
108 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
109 const AtomicString& n = getAttribute(SVGNames::contentScriptTypeAttr);
110 return n.isNull() ? defaultValue : n;
113 void SVGSVGElement::setContentScriptType(const AtomicString& type)
115 setAttribute(SVGNames::contentScriptTypeAttr, type);
118 const AtomicString& SVGSVGElement::contentStyleType() const
120 DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
121 const AtomicString& n = getAttribute(SVGNames::contentStyleTypeAttr);
122 return n.isNull() ? defaultValue : n;
125 void SVGSVGElement::setContentStyleType(const AtomicString& type)
127 setAttribute(SVGNames::contentStyleTypeAttr, type);
130 FloatRect SVGSVGElement::viewport() const
134 if (!isOutermostSVG()) {
135 x = this->x().value(this);
136 y = this->y().value(this);
138 float w = width().value(this);
139 float h = height().value(this);
140 AffineTransform viewBox = viewBoxToViewTransform(w, h);
143 viewBox.map(x, y, x, y);
144 viewBox.map(w, h, wDouble, hDouble);
145 return FloatRect::narrowPrecision(x, y, wDouble, hDouble);
148 int SVGSVGElement::relativeWidthValue() const
150 SVGLength w = width();
151 if (w.unitType() != LengthTypePercentage)
154 return static_cast<int>(w.valueAsPercentage() * m_containerSize.width());
157 int SVGSVGElement::relativeHeightValue() const
159 SVGLength h = height();
160 if (h.unitType() != LengthTypePercentage)
163 return static_cast<int>(h.valueAsPercentage() * m_containerSize.height());
166 float SVGSVGElement::pixelUnitToMillimeterX() const
168 // 2.54 / cssPixelsPerInch gives CM.
169 return (2.54f / cssPixelsPerInch) * 10.0f;
172 float SVGSVGElement::pixelUnitToMillimeterY() const
174 // 2.54 / cssPixelsPerInch gives CM.
175 return (2.54f / cssPixelsPerInch) * 10.0f;
178 float SVGSVGElement::screenPixelToMillimeterX() const
180 return pixelUnitToMillimeterX();
183 float SVGSVGElement::screenPixelToMillimeterY() const
185 return pixelUnitToMillimeterY();
188 bool SVGSVGElement::useCurrentView() const
190 return m_useCurrentView;
193 void SVGSVGElement::setUseCurrentView(bool currentView)
195 m_useCurrentView = currentView;
198 SVGViewSpec* SVGSVGElement::currentView() const
201 m_viewSpec = adoptPtr(new SVGViewSpec(const_cast<SVGSVGElement*>(this)));
202 return m_viewSpec.get();
205 float SVGSVGElement::currentScale() const
207 // Only the page zoom factor is relevant for SVG
208 if (Frame* frame = document()->frame())
209 return frame->pageZoomFactor();
213 void SVGSVGElement::setCurrentScale(float scale)
215 if (Frame* frame = document()->frame()) {
216 // Calling setCurrentScale() on the outermost <svg> element in a standalone SVG document
217 // is allowed to change the page zoom factor, influencing the document size, scrollbars etc.
218 if (parentNode() == document())
219 frame->setPageZoomFactor(scale);
224 if (RenderObject* object = renderer())
225 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
228 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
230 m_translation = translation;
231 updateCurrentTranslate();
234 void SVGSVGElement::updateCurrentTranslate()
236 if (RenderObject* object = renderer())
237 object->setNeedsLayout(true);
239 if (parentNode() == document() && document()->renderer())
240 document()->renderer()->repaint();
243 void SVGSVGElement::parseMappedAttribute(Attribute* attr)
245 if (!nearestViewportElement()) {
246 bool setListener = true;
248 // Only handle events if we're the outermost <svg> element
249 if (attr->name() == HTMLNames::onunloadAttr)
250 document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
251 else if (attr->name() == HTMLNames::onresizeAttr)
252 document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
253 else if (attr->name() == HTMLNames::onscrollAttr)
254 document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
255 else if (attr->name() == SVGNames::onzoomAttr)
256 document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
264 if (attr->name() == HTMLNames::onabortAttr)
265 document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
266 else if (attr->name() == HTMLNames::onerrorAttr)
267 document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
268 else if (attr->name() == SVGNames::xAttr)
269 setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
270 else if (attr->name() == SVGNames::yAttr)
271 setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
272 else if (attr->name() == SVGNames::widthAttr) {
273 setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
274 addCSSProperty(attr, CSSPropertyWidth, attr->value());
275 if (widthBaseValue().value(this) < 0.0)
276 document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
277 } else if (attr->name() == SVGNames::heightAttr) {
278 setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
279 addCSSProperty(attr, CSSPropertyHeight, attr->value());
280 if (heightBaseValue().value(this) < 0.0)
281 document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
283 if (SVGTests::parseMappedAttribute(attr))
285 if (SVGLangSpace::parseMappedAttribute(attr))
287 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
289 if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
291 if (SVGZoomAndPan::parseMappedAttribute(attr))
294 SVGStyledLocatableElement::parseMappedAttribute(attr);
298 // This hack will not handle the case where we're setting a width/height
299 // on a root <svg> via svg.width.baseValue = when it has none.
300 static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value)
302 Attribute* attribute = element->attributes(false)->getAttributeItem(attrName);
303 if (!attribute || !attribute->isMappedAttribute())
305 element->addCSSProperty(attribute, property, value.valueAsString());
308 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
310 SVGStyledElement::svgAttributeChanged(attrName);
312 // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called
313 // when svg.width.baseValue = 100 is evaluated.
314 // Thus the CSS length value for width is not updated, and width() computeLogicalWidth()
315 // calculations on RenderSVGRoot will be wrong.
316 // https://bugs.webkit.org/show_bug.cgi?id=25387
317 bool updateRelativeLengths = false;
318 if (attrName == SVGNames::widthAttr) {
319 updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue());
320 updateRelativeLengths = true;
321 } else if (attrName == SVGNames::heightAttr) {
322 updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue());
323 updateRelativeLengths = true;
326 if (updateRelativeLengths
327 || attrName == SVGNames::xAttr
328 || attrName == SVGNames::yAttr
329 || SVGFitToViewBox::isKnownAttribute(attrName)) {
330 updateRelativeLengths = true;
331 updateRelativeLengthsInformation();
334 if (SVGTests::handleAttributeChange(this, attrName))
340 if (updateRelativeLengths
341 || SVGLangSpace::isKnownAttribute(attrName)
342 || SVGExternalResourcesRequired::isKnownAttribute(attrName)
343 || SVGZoomAndPan::isKnownAttribute(attrName)
344 || SVGStyledLocatableElement::isKnownAttribute(attrName))
345 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
348 void SVGSVGElement::synchronizeProperty(const QualifiedName& attrName)
350 SVGStyledElement::synchronizeProperty(attrName);
352 if (attrName == anyQName()) {
357 synchronizeExternalResourcesRequired();
358 synchronizeViewBox();
359 synchronizePreserveAspectRatio();
360 SVGTests::synchronizeProperties(this, attrName);
364 if (attrName == SVGNames::xAttr)
366 else if (attrName == SVGNames::yAttr)
368 else if (attrName == SVGNames::widthAttr)
370 else if (attrName == SVGNames::heightAttr)
372 else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
373 synchronizeExternalResourcesRequired();
374 else if (attrName == SVGNames::viewBoxAttr)
375 synchronizeViewBox();
376 else if (attrName == SVGNames::preserveAspectRatioAttr)
377 synchronizePreserveAspectRatio();
378 else if (SVGTests::isKnownAttribute(attrName))
379 SVGTests::synchronizeProperties(this, attrName);
382 AttributeToPropertyTypeMap& SVGSVGElement::attributeToPropertyTypeMap()
384 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
385 return s_attributeToPropertyTypeMap;
388 void SVGSVGElement::fillAttributeToPropertyTypeMap()
390 AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap();
391 attributeToPropertyTypeMap.set(SVGNames::xAttr, AnimatedLength);
392 attributeToPropertyTypeMap.set(SVGNames::yAttr, AnimatedLength);
393 attributeToPropertyTypeMap.set(SVGNames::widthAttr, AnimatedLength);
394 attributeToPropertyTypeMap.set(SVGNames::heightAttr, AnimatedLength);
395 attributeToPropertyTypeMap.set(SVGNames::viewBoxAttr, AnimatedRect);
396 attributeToPropertyTypeMap.set(SVGNames::preserveAspectRatioAttr, AnimatedPreserveAspectRatio);
399 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
401 // FIXME: Implement me (see bug 11275)
405 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
407 // FIXME: Implement me (see bug 11275)
410 void SVGSVGElement::unsuspendRedrawAll()
412 // FIXME: Implement me (see bug 11275)
415 void SVGSVGElement::forceRedraw()
417 // FIXME: Implement me (see bug 11275)
420 NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*)
422 // FIXME: Implement me (see bug 11274)
426 NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*)
428 // FIXME: Implement me (see bug 11274)
432 bool SVGSVGElement::checkIntersection(SVGElement*, const FloatRect& rect)
434 // TODO : take into account pointer-events?
435 // FIXME: Why is element ignored??
436 // FIXME: Implement me (see bug 11274)
437 return rect.intersects(getBBox());
440 bool SVGSVGElement::checkEnclosure(SVGElement*, const FloatRect& rect)
442 // TODO : take into account pointer-events?
443 // FIXME: Why is element ignored??
444 // FIXME: Implement me (see bug 11274)
445 return rect.contains(getBBox());
448 void SVGSVGElement::deselectAll()
450 if (Frame* frame = document()->frame())
451 frame->selection()->clear();
454 float SVGSVGElement::createSVGNumber()
459 SVGLength SVGSVGElement::createSVGLength()
464 SVGAngle SVGSVGElement::createSVGAngle()
469 FloatPoint SVGSVGElement::createSVGPoint()
474 SVGMatrix SVGSVGElement::createSVGMatrix()
479 FloatRect SVGSVGElement::createSVGRect()
484 SVGTransform SVGSVGElement::createSVGTransform()
486 return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
489 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
491 return SVGTransform(static_cast<const AffineTransform&>(matrix));
494 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
496 AffineTransform viewBoxTransform;
497 if (attributes()->getAttributeItem(SVGNames::viewBoxAttr))
498 viewBoxTransform = viewBoxToViewTransform(width().value(this), height().value(this));
500 AffineTransform transform;
501 if (!isOutermostSVG())
502 transform.translate(x().value(this), y().value(this));
503 else if (mode == SVGLocatable::ScreenScope) {
504 if (RenderObject* renderer = this->renderer()) {
505 // Translate in our CSS parent coordinate space
506 // FIXME: This doesn't work correctly with CSS transforms.
507 FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true);
509 // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because
510 // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute())
511 // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183)
512 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
514 // Respect scroll offset.
515 if (FrameView* view = document()->view()) {
516 IntSize scrollOffset = view->scrollOffset();
517 transform.translate(-scrollOffset.width(), -scrollOffset.height());
522 return transform.multiply(viewBoxTransform);
525 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
527 if (isOutermostSVG())
528 return new (arena) RenderSVGRoot(this);
530 return new (arena) RenderSVGViewportContainer(this);
533 void SVGSVGElement::insertedIntoDocument()
535 document()->accessSVGExtensions()->addTimeContainer(this);
536 SVGStyledLocatableElement::insertedIntoDocument();
539 void SVGSVGElement::removedFromDocument()
541 document()->accessSVGExtensions()->removeTimeContainer(this);
542 SVGStyledLocatableElement::removedFromDocument();
545 void SVGSVGElement::pauseAnimations()
547 if (!m_timeContainer->isPaused())
548 m_timeContainer->pause();
551 void SVGSVGElement::unpauseAnimations()
553 if (m_timeContainer->isPaused())
554 m_timeContainer->resume();
557 bool SVGSVGElement::animationsPaused() const
559 return m_timeContainer->isPaused();
562 float SVGSVGElement::getCurrentTime() const
564 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
567 void SVGSVGElement::setCurrentTime(float /* seconds */)
569 // FIXME: Implement me, bug 12073
572 bool SVGSVGElement::selfHasRelativeLengths() const
574 return x().isRelative()
576 || width().isRelative()
577 || height().isRelative()
578 || hasAttribute(SVGNames::viewBoxAttr);
581 bool SVGSVGElement::isOutermostSVG() const
583 // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
587 #if ENABLE(SVG_FOREIGN_OBJECT)
588 // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element.
589 if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
593 // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
594 return !parentNode()->isSVGElement();
597 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
599 FloatRect viewBoxRect;
600 if (useCurrentView()) {
601 if (currentView()) // what if we should use it but it is not set?
602 viewBoxRect = currentView()->viewBox();
604 viewBoxRect = viewBox();
606 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(viewBoxRect, preserveAspectRatio(), viewWidth, viewHeight);
608 if (useCurrentView() && currentView()) {
609 AffineTransform transform;
610 if (currentView()->transform().concatenate(transform))
617 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
619 setUseCurrentView(true);
620 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
621 currentView()->setViewBoxBaseValue(viewElement->viewBox());
623 currentView()->setViewBoxBaseValue(viewBox());
625 SVGPreserveAspectRatio aspectRatio;
626 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
627 aspectRatio = viewElement->preserveAspectRatioBaseValue();
629 aspectRatio = preserveAspectRatioBaseValue();
630 currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
632 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
633 currentView()->setZoomAndPan(viewElement->zoomAndPan());
635 if (RenderObject* object = renderer())
636 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
639 void SVGSVGElement::documentWillBecomeInactive()
644 void SVGSVGElement::documentDidBecomeActive()
649 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
650 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
651 Element* SVGSVGElement::getElementById(const AtomicString& id) const
653 Element* element = document()->getElementById(id);
654 if (element && element->isDescendantOf(this))
657 // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
659 for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) {
660 if (!node->isElementNode())
663 Element* element = static_cast<Element*>(node);
664 if (element->hasID() && element->getIdAttribute() == id)
672 #endif // ENABLE(SVG)