JavaScriptCore:
[WebKit-https.git] / WebCore / ksvg2 / svg / SVGSVGElement.cpp
1 /*
2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4                   2007 Apple Inc.  All rights reserved.
5
6     This file is part of the KDE project
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., 59 Temple Place - Suite 330,
21     Boston, MA 02111-1307, USA.
22 */
23
24 #include "config.h"
25
26 #if ENABLE(SVG)
27 #include "SVGSVGElement.h"
28
29 #include "AffineTransform.h"
30 #include "CSSPropertyNames.h"
31 #include "Document.h"
32 #include "EventListener.h"
33 #include "EventNames.h"
34 #include "Frame.h"
35 #include "HTMLNames.h"
36 #include "RenderSVGContainer.h"
37 #include "SVGAngle.h"
38 #include "SVGLength.h"
39 #include "SVGNames.h"
40 #include "SVGPreserveAspectRatio.h"
41 #include "SVGTransform.h"
42 #include "SVGZoomEvent.h"
43 #include "SelectionController.h"
44 #include "TextStream.h"
45 #include "TimeScheduler.h"
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50 using namespace EventNames;
51 using namespace SVGNames;
52
53 SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
54     : SVGStyledLocatableElement(tagName, doc)
55     , SVGTests()
56     , SVGLangSpace()
57     , SVGExternalResourcesRequired()
58     , SVGFitToViewBox()
59     , SVGZoomAndPan()
60     , m_x(this, LengthModeWidth)
61     , m_y(this, LengthModeHeight)
62     , m_width(this, LengthModeWidth)
63     , m_height(this, LengthModeHeight)
64     , m_useCurrentView(false)
65     , m_timeScheduler(new TimeScheduler(doc))
66 {
67     setWidthBaseValue(SVGLength(this, LengthModeWidth, "100%"));
68     setHeightBaseValue(SVGLength(this, LengthModeHeight, "100%"));
69 }
70
71 SVGSVGElement::~SVGSVGElement()
72 {
73     delete m_timeScheduler;
74     m_timeScheduler = 0;
75
76     // There are cases where removedFromDocument() is not called.
77     // see ContainerNode::removeAllChildren, called by it's destructor.
78     document()->accessSVGExtensions()->removeTimeContainer(this);
79 }
80
81 ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, X, x, SVGNames::xAttr.localName(), m_x)
82 ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Y, y, SVGNames::yAttr.localName(), m_y)
83 ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr.localName(), m_width)
84 ANIMATED_PROPERTY_DEFINITIONS(SVGSVGElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr.localName(), m_height)
85
86 const AtomicString& SVGSVGElement::contentScriptType() const
87 {
88     static const AtomicString defaultValue("text/ecmascript");
89     const AtomicString& n = getAttribute(contentScriptTypeAttr);
90     return n.isNull() ? defaultValue : n;
91 }
92
93 void SVGSVGElement::setContentScriptType(const AtomicString& type)
94 {
95     setAttribute(SVGNames::contentScriptTypeAttr, type);
96 }
97
98 const AtomicString& SVGSVGElement::contentStyleType() const
99 {
100     static const AtomicString defaultValue("text/css");
101     const AtomicString& n = getAttribute(contentStyleTypeAttr);
102     return n.isNull() ? defaultValue : n;
103 }
104
105 void SVGSVGElement::setContentStyleType(const AtomicString& type)
106 {
107     setAttribute(SVGNames::contentStyleTypeAttr, type);
108 }
109
110 FloatRect SVGSVGElement::viewport() const
111 {
112     double _x = 0.0;
113     double _y = 0.0;
114     if (renderer() && renderer()->parent() &&
115        !renderer()->parent()->isSVGContainer()) {
116         _x = x().value();
117         _y = y().value();
118     }
119     double w = width().value();
120     double h = height().value();
121     AffineTransform viewBox = viewBoxToViewTransform(w, h);
122     viewBox.map(_x, _y, &_x, &_y);
123     viewBox.map(w, h, &w, &h);
124     return FloatRect(_x, _y, w, h);
125 }
126
127 float SVGSVGElement::pixelUnitToMillimeterX() const
128 {
129     // FIXME: Implement me (see bug 11273)
130     return .28f;
131 }
132
133 float SVGSVGElement::pixelUnitToMillimeterY() const
134 {
135     // FIXME: Implement me (see bug 11273)
136     return .28f;
137 }
138
139 float SVGSVGElement::screenPixelToMillimeterX() const
140 {
141     return pixelUnitToMillimeterX();
142 }
143
144 float SVGSVGElement::screenPixelToMillimeterY() const
145 {
146     return pixelUnitToMillimeterY();
147 }
148
149 bool SVGSVGElement::useCurrentView() const
150 {
151     return m_useCurrentView;
152 }
153
154 void SVGSVGElement::setUseCurrentView(bool currentView)
155 {
156     m_useCurrentView = currentView;
157 }
158
159 float SVGSVGElement::currentScale() const
160 {
161     if (document() && document()->frame())
162         return document()->frame()->zoomFactor() / 100.0f;
163     return 1.0f;
164 }
165
166 void SVGSVGElement::setCurrentScale(float scale)
167 {
168     if (document() && document()->frame())
169         document()->frame()->setZoomFactor(static_cast<int>(scale / 100.0f));
170 }
171
172 FloatPoint SVGSVGElement::currentTranslate() const
173 {
174     return m_translation;
175 }
176
177 void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation)
178 {
179     m_translation = translation;
180 }
181
182 void SVGSVGElement::addSVGWindowEventListener(const AtomicString& eventType, const Attribute* attr)
183 {
184     // FIXME: None of these should be window events long term.
185     // Once we propertly support SVGLoad, etc.
186     RefPtr<EventListener> listener = document()->accessSVGExtensions()->
187         createSVGEventListener(attr->localName().domString(), attr->value(), this);
188     document()->setHTMLWindowEventListener(eventType, listener.release());
189 }
190
191 void SVGSVGElement::parseMappedAttribute(MappedAttribute* attr)
192 {
193     if (!nearestViewportElement()) {
194         // Only handle events if we're the outermost <svg> element
195         if (attr->name() == onunloadAttr)
196             addSVGWindowEventListener(unloadEvent, attr);
197         else if (attr->name() == onabortAttr)
198             addSVGWindowEventListener(abortEvent, attr);
199         else if (attr->name() == onerrorAttr)
200             addSVGWindowEventListener(errorEvent, attr);
201         else if (attr->name() == onresizeAttr)
202             addSVGWindowEventListener(resizeEvent, attr);
203         else if (attr->name() == onscrollAttr)
204             addSVGWindowEventListener(scrollEvent, attr);
205         else if (attr->name() == SVGNames::onzoomAttr)
206             addSVGWindowEventListener(zoomEvent, attr);
207     }
208     if (attr->name() == SVGNames::xAttr)
209         setXBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
210     else if (attr->name() == SVGNames::yAttr)
211         setYBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
212     else if (attr->name() == SVGNames::widthAttr) {
213         setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
214         addCSSProperty(attr, CSS_PROP_WIDTH, attr->value());
215         if (width().value() < 0.0)
216             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
217     } else if (attr->name() == SVGNames::heightAttr) {
218         setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
219         addCSSProperty(attr, CSS_PROP_HEIGHT, attr->value());
220         if (height().value() < 0.0)
221             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
222     } else {
223         if (SVGTests::parseMappedAttribute(attr))
224             return;
225         if (SVGLangSpace::parseMappedAttribute(attr))
226             return;
227         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
228             return;
229         if (SVGFitToViewBox::parseMappedAttribute(attr) && renderer()) {
230             static_cast<RenderSVGContainer*>(renderer())->setViewBox(viewBox());
231             return;
232         }
233         if (SVGZoomAndPan::parseMappedAttribute(attr))
234             return;
235
236         SVGStyledLocatableElement::parseMappedAttribute(attr);
237     }
238 }
239
240 unsigned long SVGSVGElement::suspendRedraw(unsigned long /* max_wait_milliseconds */)
241 {
242     // FIXME: Implement me (see bug 11275)
243     return 0;
244 }
245
246 void SVGSVGElement::unsuspendRedraw(unsigned long /* suspend_handle_id */, ExceptionCode& ec)
247 {
248     // if suspend_handle_id is not found, throw exception
249     // FIXME: Implement me (see bug 11275)
250 }
251
252 void SVGSVGElement::unsuspendRedrawAll()
253 {
254     // FIXME: Implement me (see bug 11275)
255 }
256
257 void SVGSVGElement::forceRedraw()
258 {
259     // FIXME: Implement me (see bug 11275)
260 }
261
262 NodeList* SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement*)
263 {
264     // FIXME: Implement me (see bug 11274)
265     return 0;
266 }
267
268 NodeList* SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement*)
269 {
270     // FIXME: Implement me (see bug 11274)
271     return 0;
272 }
273
274 bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect)
275 {
276     // TODO : take into account pointer-events?
277     // FIXME: Why is element ignored??
278     // FIXME: Implement me (see bug 11274)
279     return rect.intersects(getBBox());
280 }
281
282 bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect)
283 {
284     // TODO : take into account pointer-events?
285     // FIXME: Why is element ignored??
286     // FIXME: Implement me (see bug 11274)
287     return rect.contains(getBBox());
288 }
289
290 void SVGSVGElement::deselectAll()
291 {
292     document()->frame()->selectionController()->clear();
293 }
294
295 double SVGSVGElement::createSVGNumber()
296 {
297     return 0.0;
298 }
299
300 SVGLength SVGSVGElement::createSVGLength()
301 {
302     return SVGLength();
303 }
304
305 SVGAngle* SVGSVGElement::createSVGAngle()
306 {
307     return new SVGAngle(0);
308 }
309
310 FloatPoint SVGSVGElement::createSVGPoint()
311 {
312     return FloatPoint();
313 }
314
315 AffineTransform SVGSVGElement::createSVGMatrix()
316 {
317     return AffineTransform();
318 }
319
320 FloatRect SVGSVGElement::createSVGRect()
321 {
322     return FloatRect();
323 }
324
325 SVGTransform SVGSVGElement::createSVGTransform()
326 {
327     return SVGTransform();
328 }
329
330 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const AffineTransform& matrix)
331 {
332     return SVGTransform(matrix);
333 }
334
335 AffineTransform SVGSVGElement::getCTM() const
336 {
337     AffineTransform mat;
338     if (renderer() && renderer()->parent() &&
339        !renderer()->parent()->isSVGContainer())
340         mat.translate(x().value(), y().value());
341
342     if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) {
343         AffineTransform viewBox = viewBoxToViewTransform(width().value(), height().value());
344         mat = viewBox * mat;
345     }
346
347     return mat;
348 }
349
350 AffineTransform SVGSVGElement::getScreenCTM() const
351 {
352     // FIXME: This assumes that any <svg> element not immediately descending from another SVGElement 
353     // has *no* svg ancestors
354     document()->updateLayoutIgnorePendingStylesheets();
355     float rootX = 0.0f;
356     float rootY = 0.0f;
357     
358     if (RenderObject* renderer = this->renderer()) {
359         renderer = renderer->parent();
360         if (renderer && !(renderer->element() && renderer->element()->isSVGElement())) {
361             int tx = 0;
362             int ty = 0;
363             renderer->absolutePosition(tx, ty, true);
364             rootX += tx;
365             rootY += ty;
366         } else {
367             rootX += x().value();
368             rootY += y().value();
369         }
370     }
371     
372     AffineTransform mat = SVGStyledLocatableElement::getScreenCTM();
373     mat.translate(rootX, rootY);
374
375     if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) {
376         AffineTransform viewBox = viewBoxToViewTransform(width().value(), height().value());
377         mat = viewBox * mat;
378     }
379
380     return mat;
381 }
382
383 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
384 {
385     RenderSVGContainer* rootContainer = new (arena) RenderSVGContainer(this);
386
387     // FIXME: All this setup should be done after attributesChanged, not here.
388     rootContainer->setViewBox(viewBox());
389     rootContainer->setAlign(KCAlign(preserveAspectRatio()->align() - 1));
390     rootContainer->setSlice(preserveAspectRatio()->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE);
391     
392     return rootContainer;
393 }
394
395 void SVGSVGElement::insertedIntoDocument()
396 {
397     document()->accessSVGExtensions()->addTimeContainer(this);
398     SVGStyledLocatableElement::insertedIntoDocument();
399 }
400
401 void SVGSVGElement::removedFromDocument()
402 {
403     document()->accessSVGExtensions()->removeTimeContainer(this);
404     SVGStyledLocatableElement::removedFromDocument();
405 }
406
407 void SVGSVGElement::pauseAnimations()
408 {
409     if (!m_timeScheduler->animationsPaused())
410         m_timeScheduler->toggleAnimations();
411 }
412
413 void SVGSVGElement::unpauseAnimations()
414 {
415     if (m_timeScheduler->animationsPaused())
416         m_timeScheduler->toggleAnimations();
417 }
418
419 bool SVGSVGElement::animationsPaused() const
420 {
421     return m_timeScheduler->animationsPaused();
422 }
423
424 float SVGSVGElement::getCurrentTime() const
425 {
426     return m_timeScheduler->elapsed();
427 }
428
429 void SVGSVGElement::setCurrentTime(float /* seconds */)
430 {
431     // FIXME: Implement me, bug 12073
432 }
433
434 bool SVGSVGElement::hasRelativeValues() const
435 {
436     return (x().isRelative() || width().isRelative() ||
437             y().isRelative() || height().isRelative());
438 }
439
440 void SVGSVGElement::attributeChanged(Attribute* attr, bool preserveDecls)
441 {
442     if (attr->name() == SVGNames::xAttr ||
443         attr->name() == SVGNames::yAttr ||
444         attr->name() == SVGNames::widthAttr ||
445         attr->name() == SVGNames::heightAttr)
446         if (renderer())
447             renderer()->setNeedsLayout(true);
448
449     SVGStyledElement::attributeChanged(attr, preserveDecls);
450 }
451
452 }
453
454 #endif // ENABLE(SVG)
455
456 // vim:ts=4:noet