03554fdb02c8b05890d204dbf65cdb2dc2251612
[WebKit-https.git] / Source / WebCore / svg / SVGSVGElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@kde.org>
4  * Copyright (C) 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 "Attribute.h"
29 #include "CSSHelper.h"
30 #include "CSSPropertyNames.h"
31 #include "Document.h"
32 #include "EventListener.h"
33 #include "EventNames.h"
34 #include "FloatConversion.h"
35 #include "FloatRect.h"
36 #include "Frame.h"
37 #include "FrameSelection.h"
38 #include "FrameTree.h"
39 #include "FrameView.h"
40 #include "HTMLNames.h"
41 #include "RenderSVGResource.h"
42 #include "RenderSVGModelObject.h"
43 #include "RenderSVGRoot.h"
44 #include "RenderSVGViewportContainer.h"
45 #include "SMILTimeContainer.h"
46 #include "SVGAngle.h"
47 #include "SVGElementInstance.h"
48 #include "SVGNames.h"
49 #include "SVGPreserveAspectRatio.h"
50 #include "SVGTransform.h"
51 #include "SVGTransformList.h"
52 #include "SVGViewElement.h"
53 #include "SVGViewSpec.h"
54 #include "SVGZoomEvent.h"
55 #include "ScriptEventListener.h"
56 #include "StaticNodeList.h"
57 #include <wtf/StdLibExtras.h>
58
59 namespace WebCore {
60
61 // Animated property definitions
62 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
63 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
64 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
65 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
66 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
67 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
68 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
69
70 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
71     REGISTER_LOCAL_ANIMATED_PROPERTY(x)
72     REGISTER_LOCAL_ANIMATED_PROPERTY(y)
73     REGISTER_LOCAL_ANIMATED_PROPERTY(width)
74     REGISTER_LOCAL_ANIMATED_PROPERTY(height)
75     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
76     REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
77     REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
78     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledLocatableElement)
79     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
80 END_REGISTER_ANIMATED_PROPERTIES
81
82 inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc)
83     : SVGStyledLocatableElement(tagName, doc)
84     , m_x(LengthModeWidth)
85     , m_y(LengthModeHeight)
86     , m_width(LengthModeWidth, "100%")
87     , m_height(LengthModeHeight, "100%") 
88     , m_useCurrentView(false)
89     , m_timeContainer(SMILTimeContainer::create(this))
90 {
91     ASSERT(hasTagName(SVGNames::svgTag));
92     registerAnimatedPropertiesForSVGSVGElement();
93     doc->registerForPageCacheSuspensionCallbacks(this);
94 }
95
96 PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document* document)
97 {
98     return adoptRef(new SVGSVGElement(tagName, document));
99 }
100
101 SVGSVGElement::~SVGSVGElement()
102 {
103     document()->unregisterForPageCacheSuspensionCallbacks(this);
104     // There are cases where removedFromDocument() is not called.
105     // see ContainerNode::removeAllChildren, called by its destructor.
106     document()->accessSVGExtensions()->removeTimeContainer(this);
107 }
108
109 void SVGSVGElement::didMoveToNewDocument(Document* oldDocument)
110 {
111     if (oldDocument)
112         oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
113     document()->registerForPageCacheSuspensionCallbacks(this);
114     SVGStyledLocatableElement::didMoveToNewDocument(oldDocument);
115 }
116
117 const AtomicString& SVGSVGElement::contentScriptType() const
118 {
119     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript"));
120     const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
121     return n.isNull() ? defaultValue : n;
122 }
123
124 void SVGSVGElement::setContentScriptType(const AtomicString& type)
125 {
126     setAttribute(SVGNames::contentScriptTypeAttr, type);
127 }
128
129 const AtomicString& SVGSVGElement::contentStyleType() const
130 {
131     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css"));
132     const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
133     return n.isNull() ? defaultValue : n;
134 }
135
136 void SVGSVGElement::setContentStyleType(const AtomicString& type)
137 {
138     setAttribute(SVGNames::contentStyleTypeAttr, type);
139 }
140
141 FloatRect SVGSVGElement::viewport() const
142 {
143     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
144     SVGLengthContext lengthContext(this);
145     FloatRect viewRectangle;
146     if (!isOutermostSVG())
147         viewRectangle.setLocation(FloatPoint(x().value(lengthContext), y().value(lengthContext)));
148
149     viewRectangle.setSize(FloatSize(width().value(lengthContext), height().value(lengthContext)));    
150     return viewBoxToViewTransform(viewRectangle.width(), viewRectangle.height()).mapRect(viewRectangle);
151 }
152
153 float SVGSVGElement::pixelUnitToMillimeterX() const
154 {
155     // 2.54 / cssPixelsPerInch gives CM.
156     return (2.54f / cssPixelsPerInch) * 10.0f;
157 }
158
159 float SVGSVGElement::pixelUnitToMillimeterY() const
160 {
161     // 2.54 / cssPixelsPerInch gives CM.
162     return (2.54f / cssPixelsPerInch) * 10.0f;
163 }
164
165 float SVGSVGElement::screenPixelToMillimeterX() const
166 {
167     return pixelUnitToMillimeterX();
168 }
169
170 float SVGSVGElement::screenPixelToMillimeterY() const
171 {
172     return pixelUnitToMillimeterY();
173 }
174
175 bool SVGSVGElement::useCurrentView() const
176 {
177     return m_useCurrentView;
178 }
179
180 void SVGSVGElement::setUseCurrentView(bool currentView)
181 {
182     m_useCurrentView = currentView;
183 }
184
185 SVGViewSpec* SVGSVGElement::currentView() const
186 {
187     if (!m_viewSpec)
188         m_viewSpec = adoptPtr(new SVGViewSpec(const_cast<SVGSVGElement*>(this)));
189     return m_viewSpec.get();
190 }
191
192 float SVGSVGElement::currentScale() const
193 {
194     if (!inDocument() || !isOutermostSVG())
195         return 1;
196
197     Frame* frame = document()->frame();
198     if (!frame)
199         return 1;
200
201     FrameTree* frameTree = frame->tree();
202     ASSERT(frameTree);
203
204     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
205     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
206     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
207     return frameTree->parent() ? 1 : frame->pageZoomFactor();
208 }
209
210 void SVGSVGElement::setCurrentScale(float scale)
211 {
212     if (!inDocument() || !isOutermostSVG())
213         return;
214
215     Frame* frame = document()->frame();
216     if (!frame)
217         return;
218
219     FrameTree* frameTree = frame->tree();
220     ASSERT(frameTree);
221
222     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
223     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
224     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
225     if (frameTree->parent())
226         return;
227
228     frame->setPageZoomFactor(scale);
229 }
230
231 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
232 {
233     m_translation = translation;
234     updateCurrentTranslate();
235 }
236
237 void SVGSVGElement::updateCurrentTranslate()
238 {
239     if (RenderObject* object = renderer())
240         object->setNeedsLayout(true);
241
242     if (parentNode() == document() && document()->renderer())
243         document()->renderer()->repaint();
244 }
245
246 void SVGSVGElement::parseMappedAttribute(Attribute* attr)
247 {
248     SVGParsingError parseError = NoError;
249
250     if (!nearestViewportElement()) {
251         bool setListener = true;
252
253         // Only handle events if we're the outermost <svg> element
254         if (attr->name() == HTMLNames::onunloadAttr)
255             document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
256         else if (attr->name() == HTMLNames::onresizeAttr)
257             document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
258         else if (attr->name() == HTMLNames::onscrollAttr)
259             document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
260         else if (attr->name() == SVGNames::onzoomAttr)
261             document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
262         else
263             setListener = false;
264  
265         if (setListener)
266             return;
267     }
268
269     if (attr->name() == HTMLNames::onabortAttr)
270         document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
271     else if (attr->name() == HTMLNames::onerrorAttr)
272         document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
273     else if (attr->name() == SVGNames::xAttr)
274         setXBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError));
275     else if (attr->name() == SVGNames::yAttr)
276         setYBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError));
277     else if (attr->name() == SVGNames::widthAttr) {
278         setWidthBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError, ForbidNegativeLengths));
279         addCSSProperty(attr, CSSPropertyWidth, attr->value());
280     } else if (attr->name() == SVGNames::heightAttr) {
281         setHeightBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError, ForbidNegativeLengths));
282         addCSSProperty(attr, CSSPropertyHeight, attr->value());
283     } else if (SVGTests::parseMappedAttribute(attr)
284                || SVGLangSpace::parseMappedAttribute(attr)
285                || SVGExternalResourcesRequired::parseMappedAttribute(attr)
286                || SVGFitToViewBox::parseMappedAttribute(document(), attr)
287                || SVGZoomAndPan::parseMappedAttribute(attr)) {
288     } else
289         SVGStyledLocatableElement::parseMappedAttribute(attr);
290
291     reportAttributeParsingError(parseError, attr);
292 }
293
294 // This hack will not handle the case where we're setting a width/height
295 // on a root <svg> via svg.width.baseValue = when it has none.
296 static void updateCSSForAttribute(SVGSVGElement* element, const QualifiedName& attrName, CSSPropertyID property, const SVGLength& value)
297 {
298     Attribute* attribute = element->attributes(false)->getAttributeItem(attrName);
299     if (!attribute || !attribute->isMappedAttribute())
300         return;
301     element->addCSSProperty(attribute, property, value.valueAsString());
302 }
303
304 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
305
306     // FIXME: Ugly, ugly hack to around that parseMappedAttribute is not called
307     // when svg.width.baseValue = 100 is evaluated.
308     // Thus the CSS length value for width is not updated, and width() computeLogicalWidth()
309     // calculations on RenderSVGRoot will be wrong.
310     // https://bugs.webkit.org/show_bug.cgi?id=25387
311     bool updateRelativeLengths = false;
312     if (attrName == SVGNames::widthAttr) {
313         updateCSSForAttribute(this, attrName, CSSPropertyWidth, widthBaseValue());
314         updateRelativeLengths = true;
315     } else if (attrName == SVGNames::heightAttr) {
316         updateCSSForAttribute(this, attrName, CSSPropertyHeight, heightBaseValue());
317         updateRelativeLengths = true;
318     }
319
320     if (updateRelativeLengths
321         || attrName == SVGNames::xAttr
322         || attrName == SVGNames::yAttr
323         || SVGFitToViewBox::isKnownAttribute(attrName)) {
324         updateRelativeLengths = true;
325         updateRelativeLengthsInformation();
326     }
327
328     SVGElementInstance::InvalidationGuard invalidationGuard(this);
329     if (SVGTests::handleAttributeChange(this, attrName))
330         return;
331
332     if (updateRelativeLengths
333         || SVGLangSpace::isKnownAttribute(attrName)
334         || SVGExternalResourcesRequired::isKnownAttribute(attrName)
335         || SVGZoomAndPan::isKnownAttribute(attrName)) {
336         if (renderer())
337             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
338         return;
339     }
340
341     SVGStyledElement::svgAttributeChanged(attrName);
342 }
343
344 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
345 {
346     // FIXME: Implement me (see bug 11275)
347     return 0;
348 }
349
350 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
351 {
352     // FIXME: Implement me (see bug 11275)
353 }
354
355 void SVGSVGElement::unsuspendRedrawAll()
356 {
357     // FIXME: Implement me (see bug 11275)
358 }
359
360 void SVGSVGElement::forceRedraw()
361 {
362     // FIXME: Implement me (see bug 11275)
363 }
364
365 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
366 {
367     Vector<RefPtr<Node> > nodes;
368     Node* node = traverseNextNode(referenceElement ? referenceElement : this);
369     while (node) {
370         if (node->isSVGElement()) { 
371             if (collect == CollectIntersectionList) {
372                 if (checkIntersection(static_cast<SVGElement*>(node), rect))
373                     nodes.append(node);
374             } else {
375                 if (checkEnclosure(static_cast<SVGElement*>(node), rect))
376                     nodes.append(node);
377             }
378         }
379
380         node = node->traverseNextNode(referenceElement ? referenceElement : this);
381     }
382     return StaticNodeList::adopt(nodes);
383 }
384
385 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const
386 {
387     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
388 }
389
390 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const
391 {
392     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
393 }
394
395 bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect) const
396 {
397     return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
398 }
399
400 bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect) const
401 {
402     return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
403 }
404
405 void SVGSVGElement::deselectAll()
406 {
407     if (Frame* frame = document()->frame())
408         frame->selection()->clear();
409 }
410
411 float SVGSVGElement::createSVGNumber()
412 {
413     return 0.0f;
414 }
415
416 SVGLength SVGSVGElement::createSVGLength()
417 {
418     return SVGLength();
419 }
420
421 SVGAngle SVGSVGElement::createSVGAngle()
422 {
423     return SVGAngle();
424 }
425
426 FloatPoint SVGSVGElement::createSVGPoint()
427 {
428     return FloatPoint();
429 }
430
431 SVGMatrix SVGSVGElement::createSVGMatrix()
432 {
433     return SVGMatrix();
434 }
435
436 FloatRect SVGSVGElement::createSVGRect()
437 {
438     return FloatRect();
439 }
440
441 SVGTransform SVGSVGElement::createSVGTransform()
442 {
443     return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
444 }
445
446 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
447 {
448     return SVGTransform(static_cast<const AffineTransform&>(matrix));
449 }
450
451 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
452 {
453     // This method resolves length manually, w/o involving the render tree. This is desired, as getCTM()/getScreenCTM()/.. have to work without a renderer.
454     SVGLengthContext lengthContext(this);
455
456     AffineTransform viewBoxTransform;
457     if (attributes()->getAttributeItem(SVGNames::viewBoxAttr))
458         viewBoxTransform = viewBoxToViewTransform(width().value(lengthContext), height().value(lengthContext));
459
460     AffineTransform transform;
461     if (!isOutermostSVG())
462         transform.translate(x().value(lengthContext), y().value(lengthContext));
463     else if (mode == SVGLocatable::ScreenScope) {
464         if (RenderObject* renderer = this->renderer()) {
465             // Translate in our CSS parent coordinate space
466             // FIXME: This doesn't work correctly with CSS transforms.
467             FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true);
468
469             // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because
470             // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute())
471             // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183)
472             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
473
474             // Respect scroll offset.
475             if (FrameView* view = document()->view()) {
476                 LayoutSize scrollOffset = view->scrollOffset();
477                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
478             }
479         }
480     }
481
482     return transform.multiply(viewBoxTransform);
483 }
484
485 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
486 {
487     if (isOutermostSVG())
488         return new (arena) RenderSVGRoot(this);
489
490     return new (arena) RenderSVGViewportContainer(this);
491 }
492
493 void SVGSVGElement::insertedIntoDocument()
494 {
495     document()->accessSVGExtensions()->addTimeContainer(this);
496     SVGStyledLocatableElement::insertedIntoDocument();
497 }
498
499 void SVGSVGElement::removedFromDocument()
500 {
501     document()->accessSVGExtensions()->removeTimeContainer(this);
502     SVGStyledLocatableElement::removedFromDocument();
503 }
504
505 void SVGSVGElement::pauseAnimations()
506 {
507     if (!m_timeContainer->isPaused())
508         m_timeContainer->pause();
509 }
510
511 void SVGSVGElement::unpauseAnimations()
512 {
513     if (m_timeContainer->isPaused())
514         m_timeContainer->resume();
515 }
516
517 bool SVGSVGElement::animationsPaused() const
518 {
519     return m_timeContainer->isPaused();
520 }
521
522 float SVGSVGElement::getCurrentTime() const
523 {
524     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
525 }
526
527 void SVGSVGElement::setCurrentTime(float seconds)
528 {
529     if (isnan(seconds))
530         return;
531     seconds = max(seconds, 0.0f);
532     m_timeContainer->setElapsed(seconds);
533 }
534
535 bool SVGSVGElement::selfHasRelativeLengths() const
536 {
537     return x().isRelative()
538         || y().isRelative()
539         || width().isRelative()
540         || height().isRelative()
541         || hasAttribute(SVGNames::viewBoxAttr);
542 }
543
544 bool SVGSVGElement::isOutermostSVG() const
545 {
546     // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc.
547     if (!parentNode())
548         return true;
549
550     // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element.
551     if (parentNode()->hasTagName(SVGNames::foreignObjectTag))
552         return true;
553
554     // This is true whenever this is the outermost SVG, even if there are HTML elements outside it
555     return !parentNode()->isSVGElement();
556 }
557
558 FloatRect SVGSVGElement::currentViewBoxRect(CalculateViewBoxMode mode) const
559 {
560     // This method resolves length manually, w/o involving the render tree. This is desired, as getCTM()/getScreenCTM()/.. have to work without a renderer.
561     SVGLengthContext lengthContext(this);
562
563     // FIXME: The interaction of 'currentView' and embedding SVGs in other documents, is untested and unspecified.
564     if (useCurrentView()) {
565         if (SVGViewSpec* view = currentView()) // what if we should use it but it is not set?
566             return view->viewBox();
567         return FloatRect();
568     }
569
570     bool isEmbeddedThroughSVGImage = renderer() && renderer()->isSVGRoot() ? toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage() : false;
571     bool hasFixedSize = width().unitType() != LengthTypePercentage && height().unitType() != LengthTypePercentage;
572
573     FloatRect useViewBox = viewBox();
574     if (useViewBox.isEmpty()) {
575         // If no viewBox is specified but non-relative width/height values, then we
576         // should always synthesize a viewBox if we're embedded through a SVGImage.
577         if (hasFixedSize && isEmbeddedThroughSVGImage)
578             return FloatRect(0, 0, width().value(lengthContext), height().value(lengthContext));
579         return FloatRect();
580     }
581
582     // If a viewBox is specified and non-relative width/height values, then the host document only
583     // uses the width/height values to figure out the intrinsic size when embedding us, whereas the
584     // embedded document sees specified viewBox only.
585     if (hasFixedSize && mode == CalculateViewBoxInHostDocument)
586         return FloatRect(0, 0, width().value(lengthContext), height().value(lengthContext));
587
588     return useViewBox;
589 }
590
591 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
592 {
593     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight);
594     if (useCurrentView() && currentView()) {
595         AffineTransform transform;
596         if (currentView()->transform().concatenate(transform))
597             ctm *= transform;
598     }
599
600     return ctm;
601 }
602
603 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
604 {
605     bool hadUseCurrentView = m_useCurrentView;
606     if (fragmentIdentifier.startsWith("xpointer(")) {
607         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
608         setUseCurrentView(false);
609     } else if (fragmentIdentifier.startsWith("svgView(")) {
610         if (currentView()->parseViewSpec(fragmentIdentifier))
611             setUseCurrentView(true);
612     } else if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
613         if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) {
614             SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
615             if (element->hasTagName(SVGNames::svgTag)) {
616                 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
617                 svg->inheritViewAttributes(viewElement);
618                 setUseCurrentView(true);
619             }
620         }
621     }
622
623     if (!hadUseCurrentView) {
624         if (!m_useCurrentView)
625             return;
626     } else if (!m_useCurrentView)
627         currentView()->setTransform(emptyString());
628
629     // Force a layout, otherwise RenderSVGRoots localToBorderBoxTransform won't be rebuild.
630     if (RenderObject* object = renderer())
631         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
632
633     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
634     // FIXME: We need to actually "highlight" the viewTarget(s).
635 }
636
637 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
638 {
639     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
640         currentView()->setViewBoxBaseValue(viewElement->viewBox());
641     else
642         currentView()->setViewBoxBaseValue(viewBox());
643
644     SVGPreserveAspectRatio aspectRatio;
645     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
646         aspectRatio = viewElement->preserveAspectRatioBaseValue();
647     else
648         aspectRatio = preserveAspectRatioBaseValue();
649     currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
650
651     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
652         currentView()->setZoomAndPan(viewElement->zoomAndPan());
653     
654     if (RenderObject* object = renderer())
655         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
656 }
657     
658 void SVGSVGElement::documentWillSuspendForPageCache()
659 {
660     pauseAnimations();
661 }
662
663 void SVGSVGElement::documentDidResumeFromPageCache()
664 {
665     unpauseAnimations();
666 }
667
668 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
669 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
670 Element* SVGSVGElement::getElementById(const AtomicString& id) const
671 {
672     Element* element = treeScope()->getElementById(id);
673     if (element && element->isDescendantOf(this))
674         return element;
675
676     // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
677     // be returned.
678     for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) {
679         if (!node->isElementNode())
680             continue;
681
682         Element* element = static_cast<Element*>(node);
683         if (element->hasID() && element->getIdAttribute() == id)
684             return element;
685     }
686     return 0;
687 }
688
689 }
690
691 #endif // ENABLE(SVG)