SVGElement.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2008 Rob Buis <buis@kde.org>
4 * Copyright (C) 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2008 Alp Toker <alp@atoker.com>
6 * Copyright (C) 2009 Cameron McCormack <cam@mcc.id.au>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24#include "config.h"
25
26#if ENABLE(SVG)
27#include "SVGElement.h"
28
29#include "Attribute.h"
30#include "CSSCursorImageValue.h"
31#include "DOMImplementation.h"
32#include "Document.h"
33#include "Event.h"
34#include "EventListener.h"
35#include "EventNames.h"
36#include "FrameView.h"
37#include "HTMLNames.h"
38#include "RegisteredEventListener.h"
39#include "RenderObject.h"
40#include "SVGCursorElement.h"
41#include "SVGDocumentExtensions.h"
42#include "SVGElementInstance.h"
43#include "SVGElementRareData.h"
44#include "SVGNames.h"
45#include "SVGSVGElement.h"
46#include "SVGStyledLocatableElement.h"
47#include "SVGTextElement.h"
48#include "SVGURIReference.h"
49#include "SVGUseElement.h"
50#include "ScriptEventListener.h"
51#include "XMLNames.h"
52
53namespace WebCore {
54
55using namespace HTMLNames;
56
57SVGElement::SVGElement(const QualifiedName& tagName, Document* document)
58    : StyledElement(tagName, document, CreateSVGElement)
59{
60}
61
62PassRefPtr<SVGElement> SVGElement::create(const QualifiedName& tagName, Document* document)
63{
64    return adoptRef(new SVGElement(tagName, document));
65}
66
67SVGElement::~SVGElement()
68{
69    if (!hasRareSVGData())
70        ASSERT(!SVGElementRareData::rareDataMap().contains(this));
71    else {
72        SVGElementRareData::SVGElementRareDataMap& rareDataMap = SVGElementRareData::rareDataMap();
73        SVGElementRareData::SVGElementRareDataMap::iterator it = rareDataMap.find(this);
74        ASSERT(it != rareDataMap.end());
75
76        SVGElementRareData* rareData = it->second;
77        if (SVGCursorElement* cursorElement = rareData->cursorElement())
78            cursorElement->removeClient(this);
79        if (CSSCursorImageValue* cursorImageValue = rareData->cursorImageValue())
80            cursorImageValue->removeReferencedElement(this);
81
82        delete rareData;
83        rareDataMap.remove(it);
84    }
85    document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
86}
87
88SVGElementRareData* SVGElement::rareSVGData() const
89{
90    ASSERT(hasRareSVGData());
91    return SVGElementRareData::rareDataFromMap(this);
92}
93
94SVGElementRareData* SVGElement::ensureRareSVGData()
95{
96    if (hasRareSVGData())
97        return rareSVGData();
98
99    ASSERT(!SVGElementRareData::rareDataMap().contains(this));
100    SVGElementRareData* data = new SVGElementRareData;
101    SVGElementRareData::rareDataMap().set(this, data);
102    setHasRareSVGData();
103    return data;
104}
105
106bool SVGElement::isSupported(StringImpl* feature, StringImpl* version) const
107{
108    return DOMImplementation::hasFeature(feature, version);
109}
110
111String SVGElement::xmlbase() const
112{
113    return getAttribute(XMLNames::baseAttr);
114}
115
116void SVGElement::setXmlbase(const String& value, ExceptionCode&)
117{
118    setAttribute(XMLNames::baseAttr, value);
119}
120
121void SVGElement::removedFromDocument()
122{
123    document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
124    StyledElement::removedFromDocument();
125}
126
127SVGSVGElement* SVGElement::ownerSVGElement() const
128{
129    ContainerNode* n = parentOrHostNode();
130    while (n) {
131        if (n->hasTagName(SVGNames::svgTag))
132            return static_cast<SVGSVGElement*>(n);
133
134        n = n->parentOrHostNode();
135    }
136
137    return 0;
138}
139
140SVGElement* SVGElement::viewportElement() const
141{
142    // This function needs shadow tree support - as RenderSVGContainer uses this function
143    // to determine the "overflow" property. <use> on <symbol> wouldn't work otherwhise.
144    ContainerNode* n = parentOrHostNode();
145    while (n) {
146        if (n->hasTagName(SVGNames::svgTag) || n->hasTagName(SVGNames::imageTag) || n->hasTagName(SVGNames::symbolTag))
147            return static_cast<SVGElement*>(n);
148
149        n = n->parentOrHostNode();
150    }
151
152    return 0;
153}
154
155SVGDocumentExtensions* SVGElement::accessDocumentSVGExtensions() const
156{
157    // This function is provided for use by SVGAnimatedProperty to avoid
158    // global inclusion of Document.h in SVG code.
159    return document() ? document()->accessSVGExtensions() : 0;
160}
161
162void SVGElement::mapInstanceToElement(SVGElementInstance* instance)
163{
164    ASSERT(instance);
165
166    HashSet<SVGElementInstance*>& instances = ensureRareSVGData()->elementInstances();
167    ASSERT(!instances.contains(instance));
168
169    instances.add(instance);
170}
171
172void SVGElement::removeInstanceMapping(SVGElementInstance* instance)
173{
174    ASSERT(instance);
175    ASSERT(hasRareSVGData());
176
177    HashSet<SVGElementInstance*>& instances = rareSVGData()->elementInstances();
178    ASSERT(instances.contains(instance));
179
180    instances.remove(instance);
181}
182
183const HashSet<SVGElementInstance*>& SVGElement::instancesForElement() const
184{
185    if (!hasRareSVGData()) {
186        DEFINE_STATIC_LOCAL(HashSet<SVGElementInstance*>, emptyInstances, ());
187        return emptyInstances;
188    }
189    return rareSVGData()->elementInstances();
190}
191
192bool SVGElement::boundingBox(FloatRect& rect, SVGLocatable::StyleUpdateStrategy styleUpdateStrategy) const
193{
194    if (isStyledLocatable()) {
195        rect = static_cast<const SVGStyledLocatableElement*>(this)->getBBox(styleUpdateStrategy);
196        return true;
197    }
198    if (hasTagName(SVGNames::textTag)) {
199        rect = static_cast<const SVGTextElement*>(this)->getBBox(styleUpdateStrategy);
200        return true;
201    }
202    return false;
203}
204
205void SVGElement::setCursorElement(SVGCursorElement* cursorElement)
206{
207    SVGElementRareData* rareData = ensureRareSVGData();
208    if (SVGCursorElement* oldCursorElement = rareData->cursorElement()) {
209        if (cursorElement == oldCursorElement)
210            return;
211        oldCursorElement->removeReferencedElement(this);
212    }
213    rareData->setCursorElement(cursorElement);
214}
215
216void SVGElement::cursorElementRemoved()
217{
218    ASSERT(hasRareSVGData());
219    rareSVGData()->setCursorElement(0);
220}
221
222void SVGElement::setCursorImageValue(CSSCursorImageValue* cursorImageValue)
223{
224    SVGElementRareData* rareData = ensureRareSVGData();
225    if (CSSCursorImageValue* oldCursorImageValue = rareData->cursorImageValue()) {
226        if (cursorImageValue == oldCursorImageValue)
227            return;
228        oldCursorImageValue->removeReferencedElement(this);
229    }
230    rareData->setCursorImageValue(cursorImageValue);
231}
232
233void SVGElement::cursorImageValueRemoved()
234{
235    ASSERT(hasRareSVGData());
236    rareSVGData()->setCursorImageValue(0);
237}
238
239void SVGElement::parseMappedAttribute(Attribute* attr)
240{
241    // standard events
242    if (attr->name() == onloadAttr)
243        setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
244    else if (attr->name() == onclickAttr)
245        setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr));
246    else if (attr->name() == onmousedownAttr)
247        setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr));
248    else if (attr->name() == onmousemoveAttr)
249        setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr));
250    else if (attr->name() == onmouseoutAttr)
251        setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr));
252    else if (attr->name() == onmouseoverAttr)
253        setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr));
254    else if (attr->name() == onmouseupAttr)
255        setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr));
256    else if (attr->name() == SVGNames::onfocusinAttr)
257        setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, attr));
258    else if (attr->name() == SVGNames::onfocusoutAttr)
259        setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, attr));
260    else if (attr->name() == SVGNames::onactivateAttr)
261        setAttributeEventListener(eventNames().DOMActivateEvent, createAttributeEventListener(this, attr));
262    else
263        StyledElement::parseMappedAttribute(attr);
264}
265
266AttributeToPropertyTypeMap& SVGElement::attributeToPropertyTypeMap()
267{
268    DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
269    return s_attributeToPropertyTypeMap;
270}
271
272AnimatedAttributeType SVGElement::animatedPropertyTypeForAttribute(const QualifiedName& attrName)
273{
274    AttributeToPropertyTypeMap& animatedAttributeMap = attributeToPropertyTypeMap();
275    if (animatedAttributeMap.isEmpty())
276        fillAttributeToPropertyTypeMap();
277    if (animatedAttributeMap.contains(attrName))
278        return animatedAttributeMap.get(attrName);
279    if (isStyled())
280        return static_cast<SVGStyledElement*>(this)->animatedPropertyTypeForCSSProperty(attrName);
281
282    return AnimatedUnknown;
283}
284
285bool SVGElement::haveLoadedRequiredResources()
286{
287    Node* child = firstChild();
288    while (child) {
289        if (child->isSVGElement() && !static_cast<SVGElement*>(child)->haveLoadedRequiredResources())
290            return false;
291        child = child->nextSibling();
292    }
293    return true;
294}
295
296static bool hasLoadListener(Node* node)
297{
298    if (node->hasEventListeners(eventNames().loadEvent))
299        return true;
300
301    for (node = node->parentNode(); node && node->isElementNode(); node = node->parentNode()) {
302        const EventListenerVector& entry = node->getEventListeners(eventNames().loadEvent);
303        for (size_t i = 0; i < entry.size(); ++i) {
304            if (entry[i].useCapture)
305                return true;
306        }
307    }
308
309    return false;
310}
311
312void SVGElement::sendSVGLoadEventIfPossible(bool sendParentLoadEvents)
313{
314    RefPtr<SVGElement> currentTarget = this;
315    while (currentTarget && currentTarget->haveLoadedRequiredResources()) {
316        RefPtr<Node> parent;
317        if (sendParentLoadEvents)
318            parent = currentTarget->parentNode(); // save the next parent to dispatch too incase dispatching the event changes the tree
319        if (hasLoadListener(currentTarget.get()))
320            currentTarget->dispatchEvent(Event::create(eventNames().loadEvent, false, false));
321        currentTarget = (parent && parent->isSVGElement()) ? static_pointer_cast<SVGElement>(parent) : RefPtr<SVGElement>();
322    }
323}
324
325void SVGElement::finishParsingChildren()
326{
327    StyledElement::finishParsingChildren();
328
329    // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
330    // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
331    sendSVGLoadEventIfPossible();
332}
333
334bool SVGElement::childShouldCreateRenderer(Node* child) const
335{
336    if (child->isSVGElement())
337        return static_cast<SVGElement*>(child)->isValid();
338    return false;
339}
340
341void SVGElement::insertedIntoDocument()
342{
343    StyledElement::insertedIntoDocument();
344
345    if (!needsPendingResourceHandling())
346        return;
347
348    SVGDocumentExtensions* extensions = document()->accessSVGExtensions();
349    String resourceId = getIdAttribute();
350    if (!extensions->isPendingResource(resourceId))
351        return;
352
353    OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(resourceId));
354    if (clients->isEmpty())
355        return;
356
357    const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
358    for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it)
359        (*it)->buildPendingResource();
360}
361
362void SVGElement::attributeChanged(Attribute* attr, bool preserveDecls)
363{
364    ASSERT(attr);
365    if (!attr)
366        return;
367
368    StyledElement::attributeChanged(attr, preserveDecls);
369
370    // When an animated SVG property changes through SVG DOM, svgAttributeChanged() is called, not attributeChanged().
371    // Next time someone tries to access the XML attributes, the synchronization code starts. During that synchronization
372    // SVGAnimatedPropertySynchronizer may call NamedNodeMap::removeAttribute(), which in turn calls attributeChanged().
373    // At this point we're not allowed to call svgAttributeChanged() again - it may lead to extra work being done, or crashes
374    // see bug https://bugs.webkit.org/show_bug.cgi?id=40994.
375    if (isSynchronizingSVGAttributes())
376        return;
377
378    if (isIdAttributeName(attr->name()))
379        document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
380
381    // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods),
382    // so we don't want changes to the style attribute to result in extra work here.
383    if (attr->name() != HTMLNames::styleAttr)
384        svgAttributeChanged(attr->name());
385}
386
387void SVGElement::updateAnimatedSVGAttribute(const QualifiedName& name) const
388{
389    if (isSynchronizingSVGAttributes() || areSVGAttributesValid())
390        return;
391
392    setIsSynchronizingSVGAttributes();
393
394    const_cast<SVGElement*>(this)->synchronizeProperty(name);
395    if (name == anyQName())
396        setAreSVGAttributesValid();
397
398    clearIsSynchronizingSVGAttributes();
399}
400
401}
402
403#endif // ENABLE(SVG)
404