1 /* 2 Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 2004, 2005, 2006, 2007, 2008 Rob Buis <buis (at) 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 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 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 88 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 95 void SVGSVGElement::setContentScriptType(const AtomicString& type) 96 { 97 setAttribute(SVGNames::contentScriptTypeAttr, type); 98 } 99 100 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 107 void SVGSVGElement::setContentStyleType(const AtomicString& type) 108 { 109 setAttribute(SVGNames::contentStyleTypeAttr, type); 110 } 111 112 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 130 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 139 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 148 float SVGSVGElement::pixelUnitToMillimeterX() const 149 { 150 // 2.54 / cssPixelsPerInch gives CM. 151 return (2.54f / cssPixelsPerInch) * 10.0f; 152 } 153 154 float SVGSVGElement::pixelUnitToMillimeterY() const 155 { 156 // 2.54 / cssPixelsPerInch gives CM. 157 return (2.54f / cssPixelsPerInch) * 10.0f; 158 } 159 160 float SVGSVGElement::screenPixelToMillimeterX() const 161 { 162 return pixelUnitToMillimeterX(); 163 } 164 165 float SVGSVGElement::screenPixelToMillimeterY() const 166 { 167 return pixelUnitToMillimeterY(); 168 } 169 170 bool SVGSVGElement::useCurrentView() const 171 { 172 return m_useCurrentView; 173 } 174 175 void SVGSVGElement::setUseCurrentView(bool currentView) 176 { 177 m_useCurrentView = currentView; 178 } 179 180 SVGViewSpec* SVGSVGElement::currentView() const 181 { 182 if (!m_viewSpec) 183 m_viewSpec.set(new SVGViewSpec(this)); 184 185 return m_viewSpec.get(); 186 } 187 188 float SVGSVGElement::currentScale() const 189 { 190 if (document() && parentNode() == document()) 191 return document()->frame() ? document()->frame()->zoomFactor() : 1; 192 return m_scale; 193 } 194 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 208 FloatPoint SVGSVGElement::currentTranslate() const 209 { 210 return m_translation; 211 } 212 213 void SVGSVGElement::setCurrentTranslate(const FloatPoint &translation) 214 { 215 m_translation = translation; 216 if (parentNode() == document() && document()->renderer()) 217 document()->renderer()->repaint(); 218 } 219 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. 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 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 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 343 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */) 344 { 345 // FIXME: Implement me (see bug 11275) 346 return 0; 347 } 348 349 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */) 350 { 351 // FIXME: Implement me (see bug 11275) 352 } 353 354 void SVGSVGElement::unsuspendRedrawAll() 355 { 356 // FIXME: Implement me (see bug 11275) 357 } 358 359 void SVGSVGElement::forceRedraw() 360 { 361 // FIXME: Implement me (see bug 11275) 362 } 363 364 NodeList* SVGSVGElement::getIntersectionList(const FloatRect&, SVGElement*) 365 { 366 // FIXME: Implement me (see bug 11274) 367 return 0; 368 } 369 370 NodeList* SVGSVGElement::getEnclosureList(const FloatRect&, SVGElement*) 371 { 372 // FIXME: Implement me (see bug 11274) 373 return 0; 374 } 375 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 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 392 void SVGSVGElement::deselectAll() 393 { 394 document()->frame()->selection()->clear(); 395 } 396 397 float SVGSVGElement::createSVGNumber() 398 { 399 return 0.0f; 400 } 401 402 SVGLength SVGSVGElement::createSVGLength() 403 { 404 return SVGLength(); 405 } 406 407 SVGAngle SVGSVGElement::createSVGAngle() 408 { 409 return SVGAngle(); 410 } 411 412 FloatPoint SVGSVGElement::createSVGPoint() 413 { 414 return FloatPoint(); 415 } 416 417 AffineTransform SVGSVGElement::createSVGMatrix() 418 { 419 return AffineTransform(); 420 } 421 422 FloatRect SVGSVGElement::createSVGRect() 423 { 424 return FloatRect(); 425 } 426 427 SVGTransform SVGSVGElement::createSVGTransform() 428 { 429 return SVGTransform(); 430 } 431 432 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const AffineTransform& matrix) 433 { 434 return SVGTransform(matrix); 435 } 436 437 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 451 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 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 486 void SVGSVGElement::insertedIntoDocument() 487 { 488 document()->accessSVGExtensions()->addTimeContainer(this); 489 SVGStyledLocatableElement::insertedIntoDocument(); 490 } 491 492 void SVGSVGElement::removedFromDocument() 493 { 494 document()->accessSVGExtensions()->removeTimeContainer(this); 495 SVGStyledLocatableElement::removedFromDocument(); 496 } 497 498 void SVGSVGElement::pauseAnimations() 499 { 500 if (!m_timeContainer->isPaused()) 501 m_timeContainer->pause(); 502 } 503 504 void SVGSVGElement::unpauseAnimations() 505 { 506 if (m_timeContainer->isPaused()) 507 m_timeContainer->resume(); 508 } 509 510 bool SVGSVGElement::animationsPaused() const 511 { 512 return m_timeContainer->isPaused(); 513 } 514 515 float SVGSVGElement::getCurrentTime() const 516 { 517 return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); 518 } 519 520 void SVGSVGElement::setCurrentTime(float /* seconds */) 521 { 522 // FIXME: Implement me, bug 12073 523 } 524 525 bool SVGSVGElement::hasRelativeValues() const 526 { 527 return (x().isRelative() || width().isRelative() || 528 y().isRelative() || height().isRelative()); 529 } 530 531 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 541 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 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 577 void SVGSVGElement::documentWillBecomeInactive() 578 { 579 pauseAnimations(); 580 } 581 582 void SVGSVGElement::documentDidBecomeActive() 583 { 584 unpauseAnimations(); 585 } 586 587 } 588 589 #endif // ENABLE(SVG) 590 591 // vim:ts=4:noet 592