• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3                   2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
4                   2007 Apple Inc.  All rights reserved.
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Library General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10 
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Library General Public License for more details.
15 
16     You should have received a copy of the GNU Library General Public License
17     along with this library; see the file COPYING.LIB.  If not, write to
18     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19     Boston, MA 02110-1301, USA.
20 */
21 
22 #include "config.h"
23 
24 #if ENABLE(SVG)
25 #include "SVGSVGElement.h"
26 
27 #include "TransformationMatrix.h"
28 #include "CSSHelper.h"
29 #include "CSSPropertyNames.h"
30 #include "Document.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "FloatConversion.h"
34 #include "FloatRect.h"
35 #include "Frame.h"
36 #include "HTMLNames.h"
37 #include "RenderSVGViewportContainer.h"
38 #include "RenderSVGRoot.h"
39 #include "SVGAngle.h"
40 #include "SVGLength.h"
41 #include "SVGNames.h"
42 #include "SVGPreserveAspectRatio.h"
43 #include "SVGTransform.h"
44 #include "SVGTransformList.h"
45 #include "SVGViewElement.h"
46 #include "SVGViewSpec.h"
47 #include "SVGZoomEvent.h"
48 #include "SelectionController.h"
49 #include "SMILTimeContainer.h"
50 #include <wtf/StdLibExtras.h>
51 
52 namespace WebCore {
53 
54 using namespace HTMLNames;
55 using namespace SVGNames;
56 
SVGSVGElement(const QualifiedName & tagName,Document * doc)57 SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
58     : SVGStyledLocatableElement(tagName, doc)
59     , SVGTests()
60     , SVGLangSpace()
61     , SVGExternalResourcesRequired()
62     , SVGFitToViewBox()
63     , SVGZoomAndPan()
64     , m_x(this, SVGNames::xAttr, LengthModeWidth)
65     , m_y(this, SVGNames::yAttr, LengthModeHeight)
66     , m_width(this, SVGNames::widthAttr, LengthModeWidth, "100%")
67     , m_height(this, SVGNames::heightAttr, LengthModeHeight, "100%")
68     , m_useCurrentView(false)
69     , m_timeContainer(SMILTimeContainer::create(this))
70     , m_viewSpec(0)
71     , m_containerSize(300, 150)
72     , m_hasSetContainerSize(false)
73 {
74     doc->registerForDocumentActivationCallbacks(this);
75 }
76 
~SVGSVGElement()77 SVGSVGElement::~SVGSVGElement()
78 {
79     document()->unregisterForDocumentActivationCallbacks(this);
80     // There are cases where removedFromDocument() is not called.
81     // see ContainerNode::removeAllChildren, called by its destructor.
82     document()->accessSVGExtensions()->removeTimeContainer(this);
83 
84     // Call detach() here because if we wait until ~Node() calls it, we crash during
85     // RenderSVGViewportContainer destruction, as the renderer assumes that the element
86     // is still fully constructed. See <https://bugs.webkit.org/show_bug.cgi?id=21293>.
87     if (renderer())
88         detach();
89 }
90 
contentScriptType() const91 const AtomicString& SVGSVGElement::contentScriptType() const
92 {
93     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
94     const AtomicString& n = getAttribute(contentScriptTypeAttr);
95     return n.isNull() ? defaultValue : n;
96 }
97 
setContentScriptType(const AtomicString & type)98 void SVGSVGElement::setContentScriptType(const AtomicString& type)
99 {
100     setAttribute(SVGNames::contentScriptTypeAttr, type);
101 }
102 
contentStyleType() const103 const AtomicString& SVGSVGElement::contentStyleType() const
104 {
105     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
106     const AtomicString& n = getAttribute(contentStyleTypeAttr);
107     return n.isNull() ? defaultValue : n;
108 }
109 
setContentStyleType(const AtomicString & type)110 void SVGSVGElement::setContentStyleType(const AtomicString& type)
111 {
112     setAttribute(SVGNames::contentStyleTypeAttr, type);
113 }
114 
viewport() const115 FloatRect SVGSVGElement::viewport() const
116 {
117     double _x = 0.0;
118     double _y = 0.0;
119     if (!isOutermostSVG()) {
120         _x = x().value(this);
121         _y = y().value(this);
122     }
123     float w = width().value(this);
124     float h = height().value(this);
125     TransformationMatrix viewBox = viewBoxToViewTransform(w, h);
126     double wDouble = w;
127     double hDouble = h;
128     viewBox.map(_x, _y, &_x, &_y);
129     viewBox.map(w, h, &wDouble, &hDouble);
130     return FloatRect::narrowPrecision(_x, _y, wDouble, hDouble);
131 }
132 
relativeWidthValue() const133 int SVGSVGElement::relativeWidthValue() const
134 {
135     SVGLength w = width();
136     if (w.unitType() != LengthTypePercentage)
137         return 0;
138 
139     return static_cast<int>(w.valueAsPercentage() * m_containerSize.width());
140 }
141 
relativeHeightValue() const142 int SVGSVGElement::relativeHeightValue() const
143 {
144     SVGLength h = height();
145     if (h.unitType() != LengthTypePercentage)
146         return 0;
147 
148     return static_cast<int>(h.valueAsPercentage() * m_containerSize.height());
149 }
150 
pixelUnitToMillimeterX() const151 float SVGSVGElement::pixelUnitToMillimeterX() const
152 {
153     // 2.54 / cssPixelsPerInch gives CM.
154     return (2.54f / cssPixelsPerInch) * 10.0f;
155 }
156 
pixelUnitToMillimeterY() const157 float SVGSVGElement::pixelUnitToMillimeterY() const
158 {
159     // 2.54 / cssPixelsPerInch gives CM.
160     return (2.54f / cssPixelsPerInch) * 10.0f;
161 }
162 
screenPixelToMillimeterX() const163 float SVGSVGElement::screenPixelToMillimeterX() const
164 {
165     return pixelUnitToMillimeterX();
166 }
167 
screenPixelToMillimeterY() const168 float SVGSVGElement::screenPixelToMillimeterY() const
169 {
170     return pixelUnitToMillimeterY();
171 }
172 
useCurrentView() const173 bool SVGSVGElement::useCurrentView() const
174 {
175     return m_useCurrentView;
176 }
177 
setUseCurrentView(bool currentView)178 void SVGSVGElement::setUseCurrentView(bool currentView)
179 {
180     m_useCurrentView = currentView;
181 }
182 
currentView() const183 SVGViewSpec* SVGSVGElement::currentView() const
184 {
185     if (!m_viewSpec)
186         m_viewSpec.set(new SVGViewSpec(this));
187 
188     return m_viewSpec.get();
189 }
190 
currentScale() const191 float SVGSVGElement::currentScale() const
192 {
193     if (document() && document()->frame())
194         return document()->frame()->zoomFactor();
195     return 1.0f;
196 }
197 
setCurrentScale(float scale)198 void SVGSVGElement::setCurrentScale(float scale)
199 {
200     if (document() && document()->frame())
201         document()->frame()->setZoomFactor(scale, true);
202 }
203 
currentTranslate() const204 FloatPoint SVGSVGElement::currentTranslate() const
205 {
206     return m_translation;
207 }
208 
setCurrentTranslate(const FloatPoint & translation)209 void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation)
210 {
211     m_translation = translation;
212     if (parentNode() == document() && document()->renderer())
213         document()->renderer()->repaint();
214 }
215 
parseMappedAttribute(MappedAttribute * attr)216 void SVGSVGElement::parseMappedAttribute(MappedAttribute* attr)
217 {
218     if (!nearestViewportElement()) {
219         bool setListener = true;
220 
221         // Only handle events if we're the outermost <svg> element
222         if (attr->name() == onunloadAttr)
223             document()->setWindowInlineEventListenerForTypeAndAttribute(eventNames().unloadEvent, attr);
224         else if (attr->name() == onresizeAttr)
225             document()->setWindowInlineEventListenerForTypeAndAttribute(eventNames().resizeEvent, attr);
226         else if (attr->name() == onscrollAttr)
227             document()->setWindowInlineEventListenerForTypeAndAttribute(eventNames().scrollEvent, attr);
228         else if (attr->name() == SVGNames::onzoomAttr)
229             document()->setWindowInlineEventListenerForTypeAndAttribute(eventNames().zoomEvent, attr);
230         else
231             setListener = false;
232 
233         if (setListener)
234             return;
235     }
236 
237     if (attr->name() == onabortAttr)
238         document()->setWindowInlineEventListenerForTypeAndAttribute(eventNames().abortEvent, attr);
239     else if (attr->name() == onerrorAttr)
240         document()->setWindowInlineEventListenerForTypeAndAttribute(eventNames().errorEvent, attr);
241     else if (attr->name() == SVGNames::xAttr)
242         setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
243     else if (attr->name() == SVGNames::yAttr)
244         setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
245     else if (attr->name() == SVGNames::widthAttr) {
246         setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
247         addCSSProperty(attr, CSSPropertyWidth, attr->value());
248         if (widthBaseValue().value(this) < 0.0)
249             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
250     } else if (attr->name() == SVGNames::heightAttr) {
251         setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
252         addCSSProperty(attr, CSSPropertyHeight, attr->value());
253         if (heightBaseValue().value(this) < 0.0)
254             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
255     } else {
256         if (SVGTests::parseMappedAttribute(attr))
257             return;
258         if (SVGLangSpace::parseMappedAttribute(attr))
259             return;
260         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
261             return;
262         if (SVGFitToViewBox::parseMappedAttribute(attr))
263             return;
264         if (SVGZoomAndPan::parseMappedAttribute(attr))
265             return;
266 
267         SVGStyledLocatableElement::parseMappedAttribute(attr);
268     }
269 }
270 
svgAttributeChanged(const QualifiedName & attrName)271 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
272 {
273     SVGStyledElement::svgAttributeChanged(attrName);
274 
275     if (!renderer())
276         return;
277 
278     if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
279         attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
280         SVGTests::isKnownAttribute(attrName) ||
281         SVGLangSpace::isKnownAttribute(attrName) ||
282         SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
283         SVGFitToViewBox::isKnownAttribute(attrName) ||
284         SVGZoomAndPan::isKnownAttribute(attrName) ||
285         SVGStyledLocatableElement::isKnownAttribute(attrName))
286         renderer()->setNeedsLayout(true);
287 }
288 
suspendRedraw(unsigned long)289 unsigned long SVGSVGElement::suspendRedraw(unsigned long /* max_wait_milliseconds */)
290 {
291     // FIXME: Implement me (see bug 11275)
292     return 0;
293 }
294 
unsuspendRedraw(unsigned long,ExceptionCode &)295 void SVGSVGElement::unsuspendRedraw(unsigned long /* suspend_handle_id */, ExceptionCode&)
296 {
297     // if suspend_handle_id is not found, throw exception
298     // FIXME: Implement me (see bug 11275)
299 }
300 
unsuspendRedrawAll()301 void SVGSVGElement::unsuspendRedrawAll()
302 {
303     // FIXME: Implement me (see bug 11275)
304 }
305 
forceRedraw()306 void SVGSVGElement::forceRedraw()
307 {
308     // FIXME: Implement me (see bug 11275)
309 }
310 
getIntersectionList(const FloatRect &,SVGElement *)311 NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*)
312 {
313     // FIXME: Implement me (see bug 11274)
314     return 0;
315 }
316 
getEnclosureList(const FloatRect &,SVGElement *)317 NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*)
318 {
319     // FIXME: Implement me (see bug 11274)
320     return 0;
321 }
322 
checkIntersection(SVGElement *,const FloatRect & rect)323 bool SVGSVGElement::checkIntersection(SVGElement*, const FloatRect& rect)
324 {
325     // TODO : take into account pointer-events?
326     // FIXME: Why is element ignored??
327     // FIXME: Implement me (see bug 11274)
328     return rect.intersects(getBBox());
329 }
330 
checkEnclosure(SVGElement *,const FloatRect & rect)331 bool SVGSVGElement::checkEnclosure(SVGElement*, const FloatRect& rect)
332 {
333     // TODO : take into account pointer-events?
334     // FIXME: Why is element ignored??
335     // FIXME: Implement me (see bug 11274)
336     return rect.contains(getBBox());
337 }
338 
deselectAll()339 void SVGSVGElement::deselectAll()
340 {
341     document()->frame()->selection()->clear();
342 }
343 
createSVGNumber()344 float SVGSVGElement::createSVGNumber()
345 {
346     return 0.0f;
347 }
348 
createSVGLength()349 SVGLength SVGSVGElement::createSVGLength()
350 {
351     return SVGLength();
352 }
353 
createSVGAngle()354 PassRefPtr<SVGAngle> SVGSVGElement::createSVGAngle()
355 {
356     return SVGAngle::create();
357 }
358 
createSVGPoint()359 FloatPoint SVGSVGElement::createSVGPoint()
360 {
361     return FloatPoint();
362 }
363 
createSVGMatrix()364 TransformationMatrix SVGSVGElement::createSVGMatrix()
365 {
366     return TransformationMatrix();
367 }
368 
createSVGRect()369 FloatRect SVGSVGElement::createSVGRect()
370 {
371     return FloatRect();
372 }
373 
createSVGTransform()374 SVGTransform SVGSVGElement::createSVGTransform()
375 {
376     return SVGTransform();
377 }
378 
createSVGTransformFromMatrix(const TransformationMatrix & matrix)379 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const TransformationMatrix& matrix)
380 {
381     return SVGTransform(matrix);
382 }
383 
getCTM() const384 TransformationMatrix SVGSVGElement::getCTM() const
385 {
386     TransformationMatrix mat;
387     if (!isOutermostSVG())
388         mat.translate(x().value(this), y().value(this));
389 
390     if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) {
391         TransformationMatrix viewBox = viewBoxToViewTransform(width().value(this), height().value(this));
392         mat = viewBox * mat;
393     }
394 
395     return mat;
396 }
397 
getScreenCTM() const398 TransformationMatrix SVGSVGElement::getScreenCTM() const
399 {
400     document()->updateLayoutIgnorePendingStylesheets();
401     FloatPoint rootLocation;
402 
403     if (RenderObject* renderer = this->renderer()) {
404         if (isOutermostSVG()) {
405             // FIXME: This doesn't work correctly with CSS transforms.
406             FloatPoint point;
407             if (renderer->parent())
408                 point = renderer->localToAbsolute(point, true);
409             rootLocation.move(point.x(), point.y());
410         } else
411             rootLocation.move(x().value(this), y().value(this));
412     }
413 
414     TransformationMatrix mat = SVGStyledLocatableElement::getScreenCTM();
415     mat.translate(rootLocation.x(), rootLocation.y());
416 
417     if (attributes()->getNamedItem(SVGNames::viewBoxAttr)) {
418         TransformationMatrix viewBox = viewBoxToViewTransform(width().value(this), height().value(this));
419         mat = viewBox * mat;
420     }
421 
422     return mat;
423 }
424 
createRenderer(RenderArena * arena,RenderStyle *)425 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
426 {
427     if (isOutermostSVG())
428         return new (arena) RenderSVGRoot(this);
429     else
430         return new (arena) RenderSVGViewportContainer(this);
431 }
432 
insertedIntoDocument()433 void SVGSVGElement::insertedIntoDocument()
434 {
435     document()->accessSVGExtensions()->addTimeContainer(this);
436     SVGStyledLocatableElement::insertedIntoDocument();
437 }
438 
removedFromDocument()439 void SVGSVGElement::removedFromDocument()
440 {
441     document()->accessSVGExtensions()->removeTimeContainer(this);
442     SVGStyledLocatableElement::removedFromDocument();
443 }
444 
pauseAnimations()445 void SVGSVGElement::pauseAnimations()
446 {
447     if (!m_timeContainer->isPaused())
448         m_timeContainer->pause();
449 }
450 
unpauseAnimations()451 void SVGSVGElement::unpauseAnimations()
452 {
453     if (m_timeContainer->isPaused())
454         m_timeContainer->resume();
455 }
456 
animationsPaused() const457 bool SVGSVGElement::animationsPaused() const
458 {
459     return m_timeContainer->isPaused();
460 }
461 
getCurrentTime() const462 float SVGSVGElement::getCurrentTime() const
463 {
464     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
465 }
466 
setCurrentTime(float)467 void SVGSVGElement::setCurrentTime(float /* seconds */)
468 {
469     // FIXME: Implement me, bug 12073
470 }
471 
hasRelativeValues() const472 bool SVGSVGElement::hasRelativeValues() const
473 {
474     return (x().isRelative() || width().isRelative() ||
475             y().isRelative() || height().isRelative());
476 }
477 
isOutermostSVG() const478 bool SVGSVGElement::isOutermostSVG() const
479 {
480     // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
481     return !parentNode()->isSVGElement();
482 }
483 
viewBoxToViewTransform(float viewWidth,float viewHeight) const484 TransformationMatrix SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
485 {
486     FloatRect viewBoxRect;
487     if (useCurrentView()) {
488         if (currentView()) // what if we should use it but it is not set?
489             viewBoxRect = currentView()->viewBox();
490     } else
491         viewBoxRect = viewBox();
492     if (!viewBoxRect.width() || !viewBoxRect.height())
493         return TransformationMatrix();
494 
495     TransformationMatrix ctm = preserveAspectRatio()->getCTM(viewBoxRect.x(),
496             viewBoxRect.y(), viewBoxRect.width(), viewBoxRect.height(),
497             0, 0, viewWidth, viewHeight);
498 
499     if (useCurrentView() && currentView())
500         return currentView()->transform()->concatenate().matrix() * ctm;
501 
502     return ctm;
503 }
504 
inheritViewAttributes(SVGViewElement * viewElement)505 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
506 {
507     setUseCurrentView(true);
508     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
509         currentView()->setViewBox(viewElement->viewBox());
510     else
511         currentView()->setViewBox(viewBox());
512     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
513         currentView()->preserveAspectRatio()->setAlign(viewElement->preserveAspectRatio()->align());
514         currentView()->preserveAspectRatio()->setMeetOrSlice(viewElement->preserveAspectRatio()->meetOrSlice());
515     } else {
516         currentView()->preserveAspectRatio()->setAlign(preserveAspectRatio()->align());
517         currentView()->preserveAspectRatio()->setMeetOrSlice(preserveAspectRatio()->meetOrSlice());
518     }
519     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
520         currentView()->setZoomAndPan(viewElement->zoomAndPan());
521     renderer()->setNeedsLayout(true);
522 }
523 
documentWillBecomeInactive()524 void SVGSVGElement::documentWillBecomeInactive()
525 {
526     pauseAnimations();
527 }
528 
documentDidBecomeActive()529 void SVGSVGElement::documentDidBecomeActive()
530 {
531     unpauseAnimations();
532 }
533 
534 }
535 
536 #endif // ENABLE(SVG)
537 
538 // vim:ts=4:noet
539