/* Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann 2004, 2005, 2006, 2007 Rob Buis 2007 Apple Inc. All rights reserved. This file is part of the KDE project This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #if ENABLE(SVG) #include "SVGSVGElement.h" #include "AffineTransform.h" #include "CSSPropertyNames.h" #include "Document.h" #include "EventListener.h" #include "EventNames.h" #include "FloatConversion.h" #include "FloatRect.h" #include "Frame.h" #include "HTMLNames.h" #include "RenderSVGContainer.h" #include "SVGAngle.h" #include "SVGLength.h" #include "SVGNames.h" #include "SVGPreserveAspectRatio.h" #include "SVGTransform.h" #include "SVGZoomEvent.h" #include "SelectionController.h" #include "TextStream.h" #include "TimeScheduler.h" namespace WebCore { using namespace HTMLNames; using namespace EventNames; using namespace SVGNames; SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc) : SVGStyledLocatableElement(tagName, doc) , SVGTests() , SVGLangSpace() , SVGExternalResourcesRequired() , SVGFitToViewBox() , SVGZoomAndPan() , m_x(this, LengthModeWidth) , m_y(this, LengthModeHeight) , m_width(this, LengthModeWidth) , m_height(this, LengthModeHeight) , m_useCurrentView(false) , m_timeScheduler(new TimeScheduler(doc)) { setWidthBaseValue(SVGLength(this, LengthModeWidth, "100%")); setHeightBaseValue(SVGLength(this, LengthModeHeight, "100%")); } SVGSVGElement::~SVGSVGElement() { delete m_timeScheduler; m_timeScheduler = 0; // There are cases where removedFromDocument() is not called. // see ContainerNode::removeAllChildren, called by it's destructor. document()->accessSVGExtensions()->removeTimeContainer(this); } ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, X, x, SVGNames::xAttr.localName(), m_x) ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Y, y, SVGNames::yAttr.localName(), m_y) ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr.localName(), m_width) ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr.localName(), m_height) const AtomicString& SVGSVGElement::contentScriptType() const { static const AtomicString defaultValue("text/ecmascript"); const AtomicString& n = getAttribute(contentScriptTypeAttr); return n.isNull() ? defaultValue : n; } void SVGSVGElement::setContentScriptType(const AtomicString& type) { setAttribute(SVGNames::contentScriptTypeAttr, type); } const AtomicString& SVGSVGElement::contentStyleType() const { static const AtomicString defaultValue("text/css"); const AtomicString& n = getAttribute(contentStyleTypeAttr); return n.isNull() ? defaultValue : n; } void SVGSVGElement::setContentStyleType(const AtomicString& type) { setAttribute(SVGNames::contentStyleTypeAttr, type); } FloatRect SVGSVGElement::viewport() const { double _x = 0.0; double _y = 0.0; if (renderer() && renderer()->parent() && !renderer()->parent()->isSVGContainer()) { _x = x().value(); _y = y().value(); } float w = width().value(); float h = height().value(); AffineTransform viewBox = viewBoxToViewTransform(w, h); double wDouble = w; double hDouble = h; viewBox.map(_x, _y, &_x, &_y); viewBox.map(w, h, &wDouble, &hDouble); return FloatRect::narrowPrecision(_x, _y, wDouble, hDouble); } float SVGSVGElement::pixelUnitToMillimeterX() const { // FIXME: Implement me (see bug 11273) return .28f; } float SVGSVGElement::pixelUnitToMillimeterY() const { // FIXME: Implement me (see bug 11273) return .28f; } float SVGSVGElement::screenPixelToMillimeterX() const { return pixelUnitToMillimeterX(); } float SVGSVGElement::screenPixelToMillimeterY() const { return pixelUnitToMillimeterY(); } bool SVGSVGElement::useCurrentView() const { return m_useCurrentView; } void SVGSVGElement::setUseCurrentView(bool currentView) { m_useCurrentView = currentView; } float SVGSVGElement::currentScale() const { if (document() && document()->frame()) return document()->frame()->zoomFactor() / 100.0f; return 1.0f; } void SVGSVGElement::setCurrentScale(float scale) { if (document() && document()->frame()) document()->frame()->setZoomFactor(static_cast(scale / 100.0f)); } FloatPoint SVGSVGElement::currentTranslate() const { return m_translation; } void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation) { m_translation = translation; } void SVGSVGElement::addSVGWindowEventListener(const AtomicString& eventType, const Attribute* attr) { // FIXME: None of these should be window events long term. // Once we propertly support SVGLoad, etc. RefPtr listener = document()->accessSVGExtensions()-> createSVGEventListener(attr->localName().domString(), attr->value(), this); document()->setHTMLWindowEventListener(eventType, listener.release()); } void SVGSVGElement::parseMappedAttribute(MappedAttribute* attr) { if (!nearestViewportElement()) { // Only handle events if we're the outermost element if (attr->name() == onunloadAttr) addSVGWindowEventListener(unloadEvent, attr); else if (attr->name() == onabortAttr) addSVGWindowEventListener(abortEvent, attr); else if (attr->name() == onerrorAttr) addSVGWindowEventListener(errorEvent, attr); else if (attr->name() == onresizeAttr) addSVGWindowEventListener(resizeEvent, attr); else if (attr->name() == onscrollAttr) addSVGWindowEventListener(scrollEvent, attr); else if (attr->name() == SVGNames::onzoomAttr) addSVGWindowEventListener(zoomEvent, attr); } if (attr->name() == SVGNames::xAttr) setXBaseValue(SVGLength(this, LengthModeWidth, attr->value())); else if (attr->name() == SVGNames::yAttr) setYBaseValue(SVGLength(this, LengthModeHeight, attr->value())); else if (attr->name() == SVGNames::widthAttr) { setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value())); addCSSProperty(attr, CSS_PROP_WIDTH, attr->value()); if (width().value() < 0.0) document()->accessSVGExtensions()->reportError("A negative value for svg attribute is not allowed"); } else if (attr->name() == SVGNames::heightAttr) { setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value())); addCSSProperty(attr, CSS_PROP_HEIGHT, attr->value()); if (height().value() < 0.0) document()->accessSVGExtensions()->reportError("A negative value for svg attribute is not allowed"); } else { if (SVGTests::parseMappedAttribute(attr)) return; if (SVGLangSpace::parseMappedAttribute(attr)) return; if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) return; if (SVGFitToViewBox::parseMappedAttribute(attr) && renderer()) { static_cast(renderer())->setViewBox(viewBox()); return; } if (SVGZoomAndPan::parseMappedAttribute(attr)) return; SVGStyledLocatableElement::parseMappedAttribute(attr); } } unsigned long SVGSVGElement::suspendRedraw(unsigned long /* max_wait_milliseconds */) { // FIXME: Implement me (see bug 11275) return 0; } void SVGSVGElement::unsuspendRedraw(unsigned long /* suspend_handle_id */, ExceptionCode& ec) { // if suspend_handle_id is not found, throw exception // FIXME: Implement me (see bug 11275) } void SVGSVGElement::unsuspendRedrawAll() { // FIXME: Implement me (see bug 11275) } void SVGSVGElement::forceRedraw() { // FIXME: Implement me (see bug 11275) } NodeList* SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement*) { // FIXME: Implement me (see bug 11274) return 0; } NodeList* SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement*) { // FIXME: Implement me (see bug 11274) return 0; } bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect) { // TODO : take into account pointer-events? // FIXME: Why is element ignored?? // FIXME: Implement me (see bug 11274) return rect.intersects(getBBox()); } bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect) { // TODO : take into account pointer-events? // FIXME: Why is element ignored?? // FIXME: Implement me (see bug 11274) return rect.contains(getBBox()); } void SVGSVGElement::deselectAll() { document()->frame()->selectionController()->clear(); } double SVGSVGElement::createSVGNumber() { return 0.0; } SVGLength SVGSVGElement::createSVGLength() { return SVGLength(); } SVGAngle* SVGSVGElement::createSVGAngle() { return new SVGAngle(0); } FloatPoint SVGSVGElement::createSVGPoint() { return FloatPoint(); } AffineTransform SVGSVGElement::createSVGMatrix() { return AffineTransform(); } FloatRect SVGSVGElement::createSVGRect() { return FloatRect(); } SVGTransform SVGSVGElement::createSVGTransform() { return SVGTransform(); } SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const AffineTransform& matrix) { return SVGTransform(matrix); } AffineTransform SVGSVGElement::getCTM() const { AffineTransform mat; if (renderer() && renderer()->parent() && !renderer()->parent()->isSVGContainer()) mat.translate(x().value(), y().value()); if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) { AffineTransform viewBox = viewBoxToViewTransform(width().value(), height().value()); mat = viewBox * mat; } return mat; } AffineTransform SVGSVGElement::getScreenCTM() const { // FIXME: This assumes that any element not immediately descending from another SVGElement // has *no* svg ancestors document()->updateLayoutIgnorePendingStylesheets(); float rootX = 0.0f; float rootY = 0.0f; if (RenderObject* renderer = this->renderer()) { renderer = renderer->parent(); if (renderer && !(renderer->element() && renderer->element()->isSVGElement())) { int tx = 0; int ty = 0; renderer->absolutePosition(tx, ty, true); rootX += tx; rootY += ty; } else { rootX += x().value(); rootY += y().value(); } } AffineTransform mat = SVGStyledLocatableElement::getScreenCTM(); mat.translate(rootX, rootY); if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) { AffineTransform viewBox = viewBoxToViewTransform(width().value(), height().value()); mat = viewBox * mat; } return mat; } RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*) { RenderSVGContainer* rootContainer = new (arena) RenderSVGContainer(this); // FIXME: All this setup should be done after attributesChanged, not here. rootContainer->setViewBox(viewBox()); rootContainer->setAlign(KCAlign(preserveAspectRatio()->align() - 1)); rootContainer->setSlice(preserveAspectRatio()->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE); return rootContainer; } void SVGSVGElement::insertedIntoDocument() { document()->accessSVGExtensions()->addTimeContainer(this); SVGStyledLocatableElement::insertedIntoDocument(); } void SVGSVGElement::removedFromDocument() { document()->accessSVGExtensions()->removeTimeContainer(this); SVGStyledLocatableElement::removedFromDocument(); } void SVGSVGElement::pauseAnimations() { if (!m_timeScheduler->animationsPaused()) m_timeScheduler->toggleAnimations(); } void SVGSVGElement::unpauseAnimations() { if (m_timeScheduler->animationsPaused()) m_timeScheduler->toggleAnimations(); } bool SVGSVGElement::animationsPaused() const { return m_timeScheduler->animationsPaused(); } float SVGSVGElement::getCurrentTime() const { return narrowPrecisionToFloat(m_timeScheduler->elapsed()); } void SVGSVGElement::setCurrentTime(float /* seconds */) { // FIXME: Implement me, bug 12073 } bool SVGSVGElement::hasRelativeValues() const { return (x().isRelative() || width().isRelative() || y().isRelative() || height().isRelative()); } void SVGSVGElement::attributeChanged(Attribute* attr, bool preserveDecls) { if (attr->name() == SVGNames::xAttr || attr->name() == SVGNames::yAttr || attr->name() == SVGNames::widthAttr || attr->name() == SVGNames::heightAttr) if (renderer()) renderer()->setNeedsLayout(true); SVGStyledElement::attributeChanged(attr, preserveDecls); } } #endif // ENABLE(SVG) // vim:ts=4:noet