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