2011-05-21 Nikolas Zimmermann <nzimmermann@rim.com>
[WebKit-https.git] / Source / WebCore / svg / SVGScriptElement.cpp
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 "HTMLNames.h"
31 #include "SVGElementInstance.h"
32 #include "SVGNames.h"
33 #include "ScriptEventListener.h"
34
35 namespace WebCore {
36
37 // Animated property definitions
38 DEFINE_ANIMATED_STRING(SVGScriptElement, XLinkNames::hrefAttr, Href, href)
39 DEFINE_ANIMATED_BOOLEAN(SVGScriptElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
40
41 inline SVGScriptElement::SVGScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted)
42     : SVGElement(tagName, document)
43     , ScriptElement(this, wasInsertedByParser, alreadyStarted)
44 {
45     ASSERT(hasTagName(SVGNames::scriptTag));
46 }
47
48 PassRefPtr<SVGScriptElement> SVGScriptElement::create(const QualifiedName& tagName, Document* document, bool insertedByParser)
49 {
50     return adoptRef(new SVGScriptElement(tagName, document, insertedByParser, false));
51 }
52
53 bool SVGScriptElement::isSupportedAttribute(const QualifiedName& attrName)
54 {
55     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
56     if (supportedAttributes.isEmpty()) {
57         SVGURIReference::addSupportedAttributes(supportedAttributes);
58         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
59         supportedAttributes.add(SVGNames::typeAttr);
60         supportedAttributes.add(HTMLNames::onerrorAttr);
61     }
62     return supportedAttributes.contains(attrName);
63 }
64
65 void SVGScriptElement::parseMappedAttribute(Attribute* attr)
66 {
67     if (!isSupportedAttribute(attr->name())) {
68         SVGElement::parseMappedAttribute(attr);
69         return;
70     }
71
72     if (attr->name() == SVGNames::typeAttr) {
73         setType(attr->value());
74         return;
75     }
76
77     if (attr->name() == HTMLNames::onerrorAttr) {
78         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
79         return;
80     }
81
82     if (SVGURIReference::parseMappedAttribute(attr))
83         return;
84     if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
85         return;
86
87     ASSERT_NOT_REACHED();
88 }
89
90 void SVGScriptElement::svgAttributeChanged(const QualifiedName& attrName)
91 {
92     if (!isSupportedAttribute(attrName)) {
93         SVGElement::svgAttributeChanged(attrName);
94         return;
95     }
96
97     SVGElementInstance::InvalidationGuard invalidationGuard(this);
98
99     if (attrName == SVGNames::typeAttr || attrName == HTMLNames::onerrorAttr)
100         return;
101
102     if (SVGURIReference::isKnownAttribute(attrName)) {
103         handleSourceAttribute(href());
104         return;
105     }
106
107     if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) {
108         // Handle dynamic updates of the 'externalResourcesRequired' attribute. Only possible case: changing from 'true' to 'false'
109         // causes an immediate dispatch of the SVGLoad event. If the attribute value was 'false' before inserting the script element
110         // in the document, the SVGLoad event has already been dispatched.
111         if (!externalResourcesRequiredBaseValue() && !haveFiredLoadEvent() && !isParserInserted()) {
112             setHaveFiredLoadEvent(true);
113             ASSERT(haveLoadedRequiredResources());
114
115             sendSVGLoadEventIfPossible();
116         }
117         return;
118     }
119
120     ASSERT_NOT_REACHED();
121 }
122
123 void SVGScriptElement::synchronizeProperty(const QualifiedName& attrName)
124 {
125     if (attrName == anyQName()) {
126         synchronizeExternalResourcesRequired();
127         synchronizeHref();
128         SVGElement::synchronizeProperty(attrName);
129         return;
130     }
131
132     if (!isSupportedAttribute(attrName)) {
133         SVGElement::synchronizeProperty(attrName);
134         return;
135     }   
136
137     if (SVGExternalResourcesRequired::isKnownAttribute(attrName)) {
138         synchronizeExternalResourcesRequired();
139         return;
140     }
141
142     if (SVGURIReference::isKnownAttribute(attrName)) {
143         synchronizeHref();
144         return;
145     }
146
147     ASSERT_NOT_REACHED();
148 }
149
150 AttributeToPropertyTypeMap& SVGScriptElement::attributeToPropertyTypeMap()
151 {
152     DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
153     return s_attributeToPropertyTypeMap;
154 }
155
156 void SVGScriptElement::fillAttributeToPropertyTypeMap()
157 {
158     attributeToPropertyTypeMap().set(XLinkNames::hrefAttr, AnimatedString);
159 }
160
161 void SVGScriptElement::insertedIntoDocument()
162 {
163     SVGElement::insertedIntoDocument();
164     ScriptElement::insertedIntoDocument();
165
166     if (isParserInserted())
167         return;
168
169     // Eventually send SVGLoad event now for the dynamically inserted script element
170     if (!externalResourcesRequiredBaseValue()) {
171         setHaveFiredLoadEvent(true);
172         sendSVGLoadEventIfPossible();
173     }
174 }
175
176 void SVGScriptElement::removedFromDocument()
177 {
178     SVGElement::removedFromDocument();
179     ScriptElement::removedFromDocument();
180 }
181
182 void SVGScriptElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
183 {
184     ScriptElement::childrenChanged();
185     SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
186 }
187
188 bool SVGScriptElement::isURLAttribute(Attribute* attr) const
189 {
190     return attr->name() == sourceAttributeValue();
191 }
192
193 void SVGScriptElement::finishParsingChildren()
194 {
195     SVGElement::finishParsingChildren();
196
197     // A SVGLoad event has been fired by SVGElement::finishParsingChildren.
198     if (!externalResourcesRequiredBaseValue())
199         setHaveFiredLoadEvent(true);
200 }
201
202 String SVGScriptElement::type() const
203 {
204     return m_type;
205 }
206
207 void SVGScriptElement::setType(const String& type)
208 {
209     m_type = type;
210 }
211
212 void SVGScriptElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
213 {
214     SVGElement::addSubresourceAttributeURLs(urls);
215
216     addSubresourceURL(urls, document()->completeURL(href()));
217 }
218
219 bool SVGScriptElement::haveLoadedRequiredResources()
220 {
221     return !externalResourcesRequiredBaseValue() || haveFiredLoadEvent();
222 }
223
224 String SVGScriptElement::sourceAttributeValue() const
225 {
226     return href();
227 }
228
229 String SVGScriptElement::charsetAttributeValue() const
230 {
231     return String();
232 }
233
234 String SVGScriptElement::typeAttributeValue() const
235 {
236     return type();
237 }
238
239 String SVGScriptElement::languageAttributeValue() const
240 {
241     return String();
242 }
243
244 String SVGScriptElement::forAttributeValue() const
245 {
246     return String();
247 }
248
249 String SVGScriptElement::eventAttributeValue() const
250 {
251     return String();
252 }
253
254 bool SVGScriptElement::asyncAttributeValue() const
255 {
256     return false;
257 }
258
259 bool SVGScriptElement::deferAttributeValue() const
260 {
261     return false;
262 }
263
264 bool SVGScriptElement::hasSourceAttribute() const
265 {
266     return hasAttribute(XLinkNames::hrefAttr);
267 }
268
269 void SVGScriptElement::dispatchLoadEvent()
270 {
271     bool externalResourcesRequired = externalResourcesRequiredBaseValue();
272
273     if (isParserInserted())
274         ASSERT(externalResourcesRequired != haveFiredLoadEvent());
275     else if (haveFiredLoadEvent()) {
276         // If we've already fired an load event and externalResourcesRequired is set to 'true'
277         // externalResourcesRequired has been modified while loading the <script>. Don't dispatch twice.
278         if (externalResourcesRequired)
279             return;
280     }
281
282     // HTML and SVG differ completly in the 'onload' event handling of <script> elements.
283     // HTML fires the 'load' event after it sucessfully loaded a remote resource, otherwhise an error event.
284     // SVG fires the SVGLoad event immediately after parsing the <script> element, if externalResourcesRequired
285     // is set to 'false', otherwhise it dispatches the 'SVGLoad' event just after loading the remote resource.
286     if (externalResourcesRequired) {
287         ASSERT(!haveFiredLoadEvent());
288
289         // Dispatch SVGLoad event
290         setHaveFiredLoadEvent(true);
291         ASSERT(haveLoadedRequiredResources());
292
293         sendSVGLoadEventIfPossible();
294     }
295 }
296
297 PassRefPtr<Element> SVGScriptElement::cloneElementWithoutAttributesAndChildren() const
298 {
299     return adoptRef(new SVGScriptElement(tagQName(), document(), false, alreadyStarted()));
300 }
301
302 }
303
304 #endif // ENABLE(SVG)