• 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 "AffineTransform.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 "MappedAttribute.h"
38 #include "RenderSVGRoot.h"
39 #include "RenderSVGViewportContainer.h"
40 #include "SMILTimeContainer.h"
41 #include "SVGAngle.h"
42 #include "SVGLength.h"
43 #include "SVGNames.h"
44 #include "SVGPreserveAspectRatio.h"
45 #include "SVGTransform.h"
46 #include "SVGTransformList.h"
47 #include "SVGViewElement.h"
48 #include "SVGViewSpec.h"
49 #include "SVGZoomEvent.h"
50 #include "ScriptEventListener.h"
51 #include "SelectionController.h"
52 #include <wtf/StdLibExtras.h>
53 
54 namespace WebCore {
55 
56 using namespace HTMLNames;
57 using namespace SVGNames;
58 
SVGSVGElement(const QualifiedName & tagName,Document * doc)59 SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
60     : SVGStyledLocatableElement(tagName, doc)
61     , SVGTests()
62     , SVGLangSpace()
63     , SVGExternalResourcesRequired()
64     , SVGFitToViewBox()
65     , SVGZoomAndPan()
66     , m_x(LengthModeWidth)
67     , m_y(LengthModeHeight)
68     , m_width(LengthModeWidth, "100%")
69     , m_height(LengthModeHeight, "100%")
70     , m_useCurrentView(false)
71     , m_timeContainer(SMILTimeContainer::create(this))
72     , m_scale(1)
73     , m_viewSpec(0)
74     , m_containerSize(300, 150)
75     , m_hasSetContainerSize(false)
76 {
77     doc->registerForDocumentActivationCallbacks(this);
78 }
79 
~SVGSVGElement()80 SVGSVGElement::~SVGSVGElement()
81 {
82     document()->unregisterForDocumentActivationCallbacks(this);
83     // There are cases where removedFromDocument() is not called.
84     // see ContainerNode::removeAllChildren, called by its destructor.
85     document()->accessSVGExtensions()->removeTimeContainer(this);
86 }
87 
contentScriptType() const88 const AtomicString& SVGSVGElement::contentScriptType() const
89 {
90     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
91     const AtomicString& n = getAttribute(contentScriptTypeAttr);
92     return n.isNull() ? defaultValue : n;
93 }
94 
setContentScriptType(const AtomicString & type)95 void SVGSVGElement::setContentScriptType(const AtomicString& type)
96 {
97     setAttribute(SVGNames::contentScriptTypeAttr, type);
98 }
99 
contentStyleType() const100 const AtomicString& SVGSVGElement::contentStyleType() const
101 {
102     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
103     const AtomicString& n = getAttribute(contentStyleTypeAttr);
104     return n.isNull() ? defaultValue : n;
105 }
106 
setContentStyleType(const AtomicString & type)107 void SVGSVGElement::setContentStyleType(const AtomicString& type)
108 {
109     setAttribute(SVGNames::contentStyleTypeAttr, type);
110 }
111 
viewport() const112 FloatRect SVGSVGElement::viewport() const
113 {
114     double _x = 0.0;
115     double _y = 0.0;
116     if (!isOutermostSVG()) {
117         _x = x().value(this);
118         _y = y().value(this);
119     }
120     float w = width().value(this);
121     float h = height().value(this);
122     AffineTransform viewBox = viewBoxToViewTransform(w, h);
123     double wDouble = w;
124     double hDouble = h;
125     viewBox.map(_x, _y, _x, _y);
126     viewBox.map(w, h, wDouble, hDouble);
127     return FloatRect::narrowPrecision(_x, _y, wDouble, hDouble);
128 }
129 
relativeWidthValue() const130 int SVGSVGElement::relativeWidthValue() const
131 {
132     SVGLength w = width();
133     if (w.unitType() != LengthTypePercentage)
134         return 0;
135 
136     return static_cast<int>(w.valueAsPercentage() * m_containerSize.width());
137 }
138 
relativeHeightValue() const139 int SVGSVGElement::relativeHeightValue() const
140 {
141     SVGLength h = height();
142     if (h.unitType() != LengthTypePercentage)
143         return 0;
144 
145     return static_cast<int>(h.valueAsPercentage() * m_containerSize.height());
146 }
147 
pixelUnitToMillimeterX() const148 float SVGSVGElement::pixelUnitToMillimeterX() const
149 {
150     // 2.54 / cssPixelsPerInch gives CM.
151     return (2.54f / cssPixelsPerInch) * 10.0f;
152 }
153 
pixelUnitToMillimeterY() const154 float SVGSVGElement::pixelUnitToMillimeterY() const
155 {
156     // 2.54 / cssPixelsPerInch gives CM.
157     return (2.54f / cssPixelsPerInch) * 10.0f;
158 }
159 
screenPixelToMillimeterX() const160 float SVGSVGElement::screenPixelToMillimeterX() const
161 {
162     return pixelUnitToMillimeterX();
163 }
164 
screenPixelToMillimeterY() const165 float SVGSVGElement::screenPixelToMillimeterY() const
166 {
167     return pixelUnitToMillimeterY();
168 }
169 
useCurrentView() const170 bool SVGSVGElement::useCurrentView() const
171 {
172     return m_useCurrentView;
173 }
174 
setUseCurrentView(bool currentView)175 void SVGSVGElement::setUseCurrentView(bool currentView)
176 {
177     m_useCurrentView = currentView;
178 }
179 
currentView() const180 SVGViewSpec* SVGSVGElement::currentView() const
181 {
182     if (!m_viewSpec)
183         m_viewSpec.set(new SVGViewSpec(this));
184 
185     return m_viewSpec.get();
186 }
187 
currentScale() const188 float SVGSVGElement::currentScale() const
189 {
190     if (document() && parentNode() == document())
191         return document()->frame() ? document()->frame()->zoomFactor() : 1;
192     return m_scale;
193 }
194 
setCurrentScale(float scale)195 void SVGSVGElement::setCurrentScale(float scale)
196 {
197     if (document() && parentNode() == document()) {
198         if (document()->frame())
199             document()->frame()->setZoomFactor(scale, true);
200         return;
201     }
202 
203     m_scale = scale;
204     if (renderer())
205         renderer()->setNeedsLayout(true);
206 }
207 
currentTranslate() const208 FloatPoint SVGSVGElement::currentTranslate() const
209 {
210     return m_translation;
211 }
212 
setCurrentTranslate(const FloatPoint & translation)213 void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation)
214 {
215     m_translation = translation;
216     if (parentNode() == document() && document()->renderer())
217         document()->renderer()->repaint();
218 }
219 
parseMappedAttribute(MappedAttribute * attr)220 void SVGSVGElement::parseMappedAttribute(MappedAttribute* attr)
221 {
222     if (!nearestViewportElement()) {
223         bool setListener = true;
224 
225         // Only handle events if we're the outermost <svg> element
226         if (attr->name() == onunloadAttr)
227             document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
228         else if (attr->name() == onresizeAttr)
229             document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
230         else if (attr->name() == onscrollAttr)
231             document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
232         else if (attr->name() == SVGNames::onzoomAttr)
233             document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
234         else
235             setListener = false;
236 
237         if (setListener)
238             return;
239     }
240 
241     if (attr->name() == onabortAttr)
242         document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
243     else if (attr->name() == onerrorAttr)
244         document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
245     else if (attr->name() == SVGNames::xAttr)
246         setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
247     else if (attr->name() == SVGNames::yAttr)
248         setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
249     else if (attr->name() == SVGNames::widthAttr) {
250         setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
251         addCSSProperty(attr, CSSPropertyWidth, attr->value());
252         if (widthBaseValue().value(this) < 0.0)
253             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <width> is not allowed");
254     } else if (attr->name() == SVGNames::heightAttr) {
255         setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
256         addCSSProperty(attr, CSSPropertyHeight, attr->value());
257         if (heightBaseValue().value(this) < 0.0)
258             document()->accessSVGExtensions()->reportError("A negative value for svg attribute <height> is not allowed");
259     } else {
260         if (SVGTests::parseMappedAttribute(attr))
261             return;
262         if (SVGLangSpace::parseMappedAttribute(attr))
263             return;
264         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
265             return;
266         if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
267             return;
268         if (SVGZoomAndPan::parseMappedAttribute(attr))
269             return;
270 
271         SVGStyledLocatableElement::parseMappedAttribute(attr);
272     }
273 }
274 
275 // This hack will not handle the case where we're setting a width/height
276 // on a root <svg> via svg.width.baseValue = when it has none.
updateCSSForAttribute(SVGSVGElement * element,const QualifiedName & attrName,CSSPropertyID property,const SVGLength & value)277 static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value)
278 {
279     Attribute* attribute = element->attributes(false)->getAttributeItem(attrName);
280     if (!attribute || !attribute->isMappedAttribute())
281         return;
282     element->addCSSProperty(static_cast<MappedAttribute*>(attribute), property, value.valueAsString());
283 }
284 
svgAttributeChanged(const QualifiedName & attrName)285 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
286 {
287     SVGStyledElement::svgAttributeChanged(attrName);
288 
289     // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called
290     // when svg.width.baseValue = 100 is evaluated.
291     // Thus the CSS length value for width is not updated, and width() calcWidth()
292     // calculations on RenderSVGRoot will be wrong.
293     // https://bugs.webkit.org/show_bug.cgi?id=25387
294     if (attrName == SVGNames::widthAttr)
295         updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue());
296     else if (attrName == SVGNames::heightAttr)
297         updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue());
298 
299     if (!renderer())
300         return;
301 
302     if (attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
303         SVGTests::isKnownAttribute(attrName) ||
304         SVGLangSpace::isKnownAttribute(attrName) ||
305         SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
306         SVGFitToViewBox::isKnownAttribute(attrName) ||
307         SVGZoomAndPan::isKnownAttribute(attrName) ||
308         SVGStyledLocatableElement::isKnownAttribute(attrName))
309         renderer()->setNeedsLayout(true);
310 }
311 
synchronizeProperty(const QualifiedName & attrName)312 void SVGSVGElement::synchronizeProperty(const QualifiedName& attrName)
313 {
314     SVGStyledElement::synchronizeProperty(attrName);
315 
316     if (attrName == anyQName()) {
317         synchronizeX();
318         synchronizeY();
319         synchronizeWidth();
320         synchronizeHeight();
321         synchronizeExternalResourcesRequired();
322         synchronizeViewBox();
323         synchronizePreserveAspectRatio();
324         return;
325     }
326 
327     if (attrName == SVGNames::xAttr)
328         synchronizeX();
329     else if (attrName == SVGNames::yAttr)
330         synchronizeY();
331     else if (attrName == SVGNames::widthAttr)
332         synchronizeWidth();
333     else if (attrName == SVGNames::heightAttr)
334         synchronizeHeight();
335     else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
336         synchronizeExternalResourcesRequired();
337     else if (SVGFitToViewBox::isKnownAttribute(attrName)) {
338         synchronizeViewBox();
339         synchronizePreserveAspectRatio();
340     }
341 }
342 
suspendRedraw(unsigned)343 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
344 {
345     // FIXME: Implement me (see bug 11275)
346     return 0;
347 }
348 
unsuspendRedraw(unsigned)349 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
350 {
351     // FIXME: Implement me (see bug 11275)
352 }
353 
unsuspendRedrawAll()354 void SVGSVGElement::unsuspendRedrawAll()
355 {
356     // FIXME: Implement me (see bug 11275)
357 }
358 
forceRedraw()359 void SVGSVGElement::forceRedraw()
360 {
361     // FIXME: Implement me (see bug 11275)
362 }
363 
getIntersectionList(const FloatRect &,SVGElement *)364 NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*)
365 {
366     // FIXME: Implement me (see bug 11274)
367     return 0;
368 }
369 
getEnclosureList(const FloatRect &,SVGElement *)370 NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*)
371 {
372     // FIXME: Implement me (see bug 11274)
373     return 0;
374 }
375 
checkIntersection(SVGElement *,const FloatRect & rect)376 bool SVGSVGElement::checkIntersection(SVGElement*, const FloatRect& rect)
377 {
378     // TODO : take into account pointer-events?
379     // FIXME: Why is element ignored??
380     // FIXME: Implement me (see bug 11274)
381     return rect.intersects(getBBox());
382 }
383 
checkEnclosure(SVGElement *,const FloatRect & rect)384 bool SVGSVGElement::checkEnclosure(SVGElement*, const FloatRect& rect)
385 {
386     // TODO : take into account pointer-events?
387     // FIXME: Why is element ignored??
388     // FIXME: Implement me (see bug 11274)
389     return rect.contains(getBBox());
390 }
391 
deselectAll()392 void SVGSVGElement::deselectAll()
393 {
394     document()->frame()->selection()->clear();
395 }
396 
createSVGNumber()397 float SVGSVGElement::createSVGNumber()
398 {
399     return 0.0f;
400 }
401 
createSVGLength()402 SVGLength SVGSVGElement::createSVGLength()
403 {
404     return SVGLength();
405 }
406 
createSVGAngle()407 SVGAngle SVGSVGElement::createSVGAngle()
408 {
409     return SVGAngle();
410 }
411 
createSVGPoint()412 FloatPoint SVGSVGElement::createSVGPoint()
413 {
414     return FloatPoint();
415 }
416 
createSVGMatrix()417 AffineTransform SVGSVGElement::createSVGMatrix()
418 {
419     return AffineTransform();
420 }
421 
createSVGRect()422 FloatRect SVGSVGElement::createSVGRect()
423 {
424     return FloatRect();
425 }
426 
createSVGTransform()427 SVGTransform SVGSVGElement::createSVGTransform()
428 {
429     return SVGTransform();
430 }
431 
createSVGTransformFromMatrix(const AffineTransform & matrix)432 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const AffineTransform& matrix)
433 {
434     return SVGTransform(matrix);
435 }
436 
getCTM() const437 AffineTransform SVGSVGElement::getCTM() const
438 {
439     AffineTransform mat;
440     if (!isOutermostSVG())
441         mat.translate(x().value(this), y().value(this));
442 
443     if (attributes()->getAttributeItem(SVGNames::viewBoxAttr)) {
444         AffineTransform viewBox = viewBoxToViewTransform(width().value(this), height().value(this));
445         mat = viewBox * mat;
446     }
447 
448     return mat;
449 }
450 
getScreenCTM() const451 AffineTransform SVGSVGElement::getScreenCTM() const
452 {
453     document()->updateLayoutIgnorePendingStylesheets();
454     FloatPoint rootLocation;
455 
456     if (RenderObject* renderer = this->renderer()) {
457         if (isOutermostSVG()) {
458             // FIXME: This doesn't work correctly with CSS transforms.
459             FloatPoint point;
460             if (renderer->parent())
461                 point = renderer->localToAbsolute(point, false, true);
462             rootLocation.move(point.x(), point.y());
463         } else
464             rootLocation.move(x().value(this), y().value(this));
465     }
466 
467     AffineTransform mat = SVGStyledLocatableElement::getScreenCTM();
468     mat.translate(rootLocation.x(), rootLocation.y());
469 
470     if (attributes()->getAttributeItem(SVGNames::viewBoxAttr)) {
471         AffineTransform viewBox = viewBoxToViewTransform(width().value(this), height().value(this));
472         mat = viewBox * mat;
473     }
474 
475     return mat;
476 }
477 
createRenderer(RenderArena * arena,RenderStyle *)478 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
479 {
480     if (isOutermostSVG())
481         return new (arena) RenderSVGRoot(this);
482     else
483         return new (arena) RenderSVGViewportContainer(this);
484 }
485 
insertedIntoDocument()486 void SVGSVGElement::insertedIntoDocument()
487 {
488     document()->accessSVGExtensions()->addTimeContainer(this);
489     SVGStyledLocatableElement::insertedIntoDocument();
490 }
491 
removedFromDocument()492 void SVGSVGElement::removedFromDocument()
493 {
494     document()->accessSVGExtensions()->removeTimeContainer(this);
495     SVGStyledLocatableElement::removedFromDocument();
496 }
497 
pauseAnimations()498 void SVGSVGElement::pauseAnimations()
499 {
500     if (!m_timeContainer->isPaused())
501         m_timeContainer->pause();
502 }
503 
unpauseAnimations()504 void SVGSVGElement::unpauseAnimations()
505 {
506     if (m_timeContainer->isPaused())
507         m_timeContainer->resume();
508 }
509 
animationsPaused() const510 bool SVGSVGElement::animationsPaused() const
511 {
512     return m_timeContainer->isPaused();
513 }
514 
getCurrentTime() const515 float SVGSVGElement::getCurrentTime() const
516 {
517     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
518 }
519 
setCurrentTime(float)520 void SVGSVGElement::setCurrentTime(float /* seconds */)
521 {
522     // FIXME: Implement me, bug 12073
523 }
524 
hasRelativeValues() const525 bool SVGSVGElement::hasRelativeValues() const
526 {
527     return (x().isRelative() || width().isRelative() ||
528             y().isRelative() || height().isRelative());
529 }
530 
isOutermostSVG() const531 bool SVGSVGElement::isOutermostSVG() const
532 {
533     // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
534     if (!parentNode())
535         return true;
536 
537     // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
538     return !parentNode()->isSVGElement();
539 }
540 
viewBoxToViewTransform(float viewWidth,float viewHeight) const541 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
542 {
543     FloatRect viewBoxRect;
544     if (useCurrentView()) {
545         if (currentView()) // what if we should use it but it is not set?
546             viewBoxRect = currentView()->viewBox();
547     } else
548         viewBoxRect = viewBox();
549 
550     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(viewBoxRect, preserveAspectRatio(), viewWidth, viewHeight);
551     if (useCurrentView() && currentView())
552         return currentView()->transform()->concatenate().matrix() * ctm;
553 
554     return ctm;
555 }
556 
inheritViewAttributes(SVGViewElement * viewElement)557 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
558 {
559     setUseCurrentView(true);
560     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
561         currentView()->setViewBox(viewElement->viewBox());
562     else
563         currentView()->setViewBox(viewBox());
564 
565     SVGPreserveAspectRatio aspectRatio;
566     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
567         aspectRatio = viewElement->preserveAspectRatioBaseValue();
568     else
569         aspectRatio = preserveAspectRatioBaseValue();
570     currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
571 
572     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
573         currentView()->setZoomAndPan(viewElement->zoomAndPan());
574     renderer()->setNeedsLayout(true);
575 }
576 
documentWillBecomeInactive()577 void SVGSVGElement::documentWillBecomeInactive()
578 {
579     pauseAnimations();
580 }
581 
documentDidBecomeActive()582 void SVGSVGElement::documentDidBecomeActive()
583 {
584     unpauseAnimations();
585 }
586 
587 }
588 
589 #endif // ENABLE(SVG)
590 
591 // vim:ts=4:noet
592