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