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