1/*
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2007 Rob Buis <buis@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22
23#if ENABLE(SVG)
24#include "SVGScriptElement.h"
25
26#include "Attribute.h"
27#include "Document.h"
28#include "Event.h"
29#include "EventNames.h"
30#include "SVGNames.h"
31
32namespace WebCore {
33
34// Animated property definitions
35DEFINE_ANIMATED_STRING(SVGScriptElement, XLinkNames::hrefAttr, Href, href)
36DEFINE_ANIMATED_BOOLEAN(SVGScriptElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
37
38inline SVGScriptElement::SVGScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted)
39    : SVGElement(tagName, document)
40    , ScriptElement(this, wasInsertedByParser, alreadyStarted)
41{
42}
43
44PassRefPtr<SVGScriptElement> SVGScriptElement::create(const QualifiedName& tagName, Document* document, bool insertedByParser)
45{
46    return adoptRef(new SVGScriptElement(tagName, document, insertedByParser, false));
47}
48
49void SVGScriptElement::parseMappedAttribute(Attribute* attr)
50{
51    const QualifiedName& attrName = attr->name();
52
53    if (attrName == SVGNames::typeAttr)
54        setType(attr->value());
55    else {
56        if (SVGURIReference::parseMappedAttribute(attr))
57            return;
58        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
59            return;
60
61        SVGElement::parseMappedAttribute(attr);
62    }
63}
64
65void SVGScriptElement::svgAttributeChanged(const QualifiedName& attrName)
66{
67    SVGElement::svgAttributeChanged(attrName);
68
69    if (SVGURIReference::isKnownAttribute(attrName))
70        handleSourceAttribute(href());
71    else if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) {
72        // Handle dynamic updates of the 'externalResourcesRequired' attribute. Only possible case: changing from 'true' to 'false'
73        // causes an immediate dispatch of the SVGLoad event. If the attribute value was 'false' before inserting the script element
74        // in the document, the SVGLoad event has already been dispatched.
75        if (!externalResourcesRequiredBaseValue() && !haveFiredLoadEvent() && !isParserInserted()) {
76            setHaveFiredLoadEvent(true);
77            ASSERT(haveLoadedRequiredResources());
78
79            sendSVGLoadEventIfPossible();
80        }
81    }
82}
83
84void SVGScriptElement::synchronizeProperty(const QualifiedName& attrName)
85{
86    SVGElement::synchronizeProperty(attrName);
87
88    if (attrName == anyQName()) {
89        synchronizeExternalResourcesRequired();
90        synchronizeHref();
91        return;
92    }
93
94    if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
95        synchronizeExternalResourcesRequired();
96    else if (SVGURIReference::isKnownAttribute(attrName))
97        synchronizeHref();
98}
99
100AttributeToPropertyTypeMap& SVGScriptElement::attributeToPropertyTypeMap()
101{
102    DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
103    return s_attributeToPropertyTypeMap;
104}
105
106void SVGScriptElement::fillAttributeToPropertyTypeMap()
107{
108    attributeToPropertyTypeMap().set(XLinkNames::hrefAttr, AnimatedString);
109}
110
111void SVGScriptElement::insertedIntoDocument()
112{
113    SVGElement::insertedIntoDocument();
114    ScriptElement::insertedIntoDocument();
115
116    if (isParserInserted())
117        return;
118
119    // Eventually send SVGLoad event now for the dynamically inserted script element
120    if (!externalResourcesRequiredBaseValue()) {
121        setHaveFiredLoadEvent(true);
122        sendSVGLoadEventIfPossible();
123    }
124}
125
126void SVGScriptElement::removedFromDocument()
127{
128    SVGElement::removedFromDocument();
129    ScriptElement::removedFromDocument();
130}
131
132void SVGScriptElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
133{
134    ScriptElement::childrenChanged();
135    SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
136}
137
138bool SVGScriptElement::isURLAttribute(Attribute* attr) const
139{
140    return attr->name() == sourceAttributeValue();
141}
142
143void SVGScriptElement::finishParsingChildren()
144{
145    SVGElement::finishParsingChildren();
146
147    // A SVGLoad event has been fired by SVGElement::finishParsingChildren.
148    if (!externalResourcesRequiredBaseValue())
149        setHaveFiredLoadEvent(true);
150}
151
152String SVGScriptElement::type() const
153{
154    return m_type;
155}
156
157void SVGScriptElement::setType(const String& type)
158{
159    m_type = type;
160}
161
162void SVGScriptElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
163{
164    SVGElement::addSubresourceAttributeURLs(urls);
165
166    addSubresourceURL(urls, document()->completeURL(href()));
167}
168
169bool SVGScriptElement::haveLoadedRequiredResources()
170{
171    return !externalResourcesRequiredBaseValue() || haveFiredLoadEvent();
172}
173
174String SVGScriptElement::sourceAttributeValue() const
175{
176    return href();
177}
178
179String SVGScriptElement::charsetAttributeValue() const
180{
181    return String();
182}
183
184String SVGScriptElement::typeAttributeValue() const
185{
186    return type();
187}
188
189String SVGScriptElement::languageAttributeValue() const
190{
191    return String();
192}
193
194String SVGScriptElement::forAttributeValue() const
195{
196    return String();
197}
198
199String SVGScriptElement::eventAttributeValue() const
200{
201    return String();
202}
203
204bool SVGScriptElement::asyncAttributeValue() const
205{
206    return false;
207}
208
209bool SVGScriptElement::deferAttributeValue() const
210{
211    return false;
212}
213
214bool SVGScriptElement::hasSourceAttribute() const
215{
216    return hasAttribute(XLinkNames::hrefAttr);
217}
218
219void SVGScriptElement::dispatchLoadEvent()
220{
221    bool externalResourcesRequired = externalResourcesRequiredBaseValue();
222
223    if (isParserInserted())
224        ASSERT(externalResourcesRequired != haveFiredLoadEvent());
225    else if (haveFiredLoadEvent()) {
226        // If we've already fired an load event and externalResourcesRequired is set to 'true'
227        // externalResourcesRequired has been modified while loading the <script>. Don't dispatch twice.
228        if (externalResourcesRequired)
229            return;
230    }
231
232    // HTML and SVG differ completly in the 'onload' event handling of <script> elements.
233    // HTML fires the 'load' event after it sucessfully loaded a remote resource, otherwhise an error event.
234    // SVG fires the SVGLoad event immediately after parsing the <script> element, if externalResourcesRequired
235    // is set to 'false', otherwhise it dispatches the 'SVGLoad' event just after loading the remote resource.
236    if (externalResourcesRequired) {
237        ASSERT(!haveFiredLoadEvent());
238
239        // Dispatch SVGLoad event
240        setHaveFiredLoadEvent(true);
241        ASSERT(haveLoadedRequiredResources());
242
243        sendSVGLoadEventIfPossible();
244    }
245}
246
247void SVGScriptElement::dispatchErrorEvent()
248{
249    dispatchEvent(Event::create(eventNames().errorEvent, true, false));
250}
251
252PassRefPtr<Element> SVGScriptElement::cloneElementWithoutAttributesAndChildren() const
253{
254    return adoptRef(new SVGScriptElement(tagQName(), document(), false, alreadyStarted()));
255}
256
257}
258
259#endif // ENABLE(SVG)
260