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