590eec9a32f7b05739433e24bd8e96e313ff275e
[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 "Document.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "FloatConversion.h"
34 #include "FloatRect.h"
35 #include "Frame.h"
36 #include "FrameSelection.h"
37 #include "FrameTree.h"
38 #include "FrameView.h"
39 #include "HTMLNames.h"
40 #include "RenderPart.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     // As we have no test coverage for this, we're going to disable it completly for now.
145     return FloatRect();
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 = adoptPtr(new SVGViewSpec(const_cast<SVGSVGElement*>(this)));
184     return m_viewSpec.get();
185 }
186
187 float SVGSVGElement::currentScale() const
188 {
189     if (!inDocument() || !isOutermostSVGSVGElement())
190         return 1;
191
192     Frame* frame = document()->frame();
193     if (!frame)
194         return 1;
195
196     FrameTree* frameTree = frame->tree();
197     ASSERT(frameTree);
198
199     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
200     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
201     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
202     return frameTree->parent() ? 1 : frame->pageZoomFactor();
203 }
204
205 void SVGSVGElement::setCurrentScale(float scale)
206 {
207     if (!inDocument() || !isOutermostSVGSVGElement())
208         return;
209
210     Frame* frame = document()->frame();
211     if (!frame)
212         return;
213
214     FrameTree* frameTree = frame->tree();
215     ASSERT(frameTree);
216
217     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
218     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
219     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
220     if (frameTree->parent())
221         return;
222
223     frame->setPageZoomFactor(scale);
224 }
225
226 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
227 {
228     m_translation = translation;
229     updateCurrentTranslate();
230 }
231
232 void SVGSVGElement::updateCurrentTranslate()
233 {
234     if (RenderObject* object = renderer())
235         object->setNeedsLayout(true);
236
237     if (parentNode() == document() && document()->renderer())
238         document()->renderer()->repaint();
239 }
240
241 void SVGSVGElement::parseAttribute(Attribute* attr)
242 {
243     SVGParsingError parseError = NoError;
244
245     if (!nearestViewportElement()) {
246         bool setListener = true;
247
248         // Only handle events if we're the outermost <svg> element
249         if (attr->name() == HTMLNames::onunloadAttr)
250             document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
251         else if (attr->name() == HTMLNames::onresizeAttr)
252             document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
253         else if (attr->name() == HTMLNames::onscrollAttr)
254             document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
255         else if (attr->name() == SVGNames::onzoomAttr)
256             document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), attr));
257         else
258             setListener = false;
259  
260         if (setListener)
261             return;
262     }
263
264     if (attr->name() == HTMLNames::onabortAttr)
265         document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), attr));
266     else if (attr->name() == HTMLNames::onerrorAttr)
267         document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), attr));
268     else if (attr->name() == SVGNames::xAttr)
269         setXBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError));
270     else if (attr->name() == SVGNames::yAttr)
271         setYBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError));
272     else if (attr->name() == SVGNames::widthAttr)
273         setWidthBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError, ForbidNegativeLengths));
274     else if (attr->name() == SVGNames::heightAttr)
275         setHeightBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError, ForbidNegativeLengths));
276     else if (SVGTests::parseAttribute(attr)
277                || SVGLangSpace::parseAttribute(attr)
278                || SVGExternalResourcesRequired::parseAttribute(attr)
279                || SVGFitToViewBox::parseAttribute(document(), attr)
280                || SVGZoomAndPan::parseAttribute(attr)) {
281     } else
282         SVGStyledLocatableElement::parseAttribute(attr);
283
284     reportAttributeParsingError(parseError, attr);
285 }
286
287 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
288
289     bool updateRelativeLengths = false;
290     if (attrName == SVGNames::widthAttr
291         || attrName == SVGNames::heightAttr
292         || attrName == SVGNames::xAttr
293         || attrName == SVGNames::yAttr
294         || SVGFitToViewBox::isKnownAttribute(attrName)) {
295         updateRelativeLengths = true;
296         updateRelativeLengthsInformation();
297     }
298
299     SVGElementInstance::InvalidationGuard invalidationGuard(this);
300     if (SVGTests::handleAttributeChange(this, attrName))
301         return;
302
303     if (updateRelativeLengths
304         || SVGLangSpace::isKnownAttribute(attrName)
305         || SVGExternalResourcesRequired::isKnownAttribute(attrName)
306         || SVGZoomAndPan::isKnownAttribute(attrName)) {
307         if (renderer())
308             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
309         return;
310     }
311
312     SVGStyledElement::svgAttributeChanged(attrName);
313 }
314
315 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
316 {
317     // FIXME: Implement me (see bug 11275)
318     return 0;
319 }
320
321 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
322 {
323     // FIXME: Implement me (see bug 11275)
324 }
325
326 void SVGSVGElement::unsuspendRedrawAll()
327 {
328     // FIXME: Implement me (see bug 11275)
329 }
330
331 void SVGSVGElement::forceRedraw()
332 {
333     // FIXME: Implement me (see bug 11275)
334 }
335
336 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
337 {
338     Vector<RefPtr<Node> > nodes;
339     Node* node = traverseNextNode(referenceElement ? referenceElement : this);
340     while (node) {
341         if (node->isSVGElement()) { 
342             if (collect == CollectIntersectionList) {
343                 if (checkIntersection(static_cast<SVGElement*>(node), rect))
344                     nodes.append(node);
345             } else {
346                 if (checkEnclosure(static_cast<SVGElement*>(node), rect))
347                     nodes.append(node);
348             }
349         }
350
351         node = node->traverseNextNode(referenceElement ? referenceElement : this);
352     }
353     return StaticNodeList::adopt(nodes);
354 }
355
356 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const
357 {
358     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
359 }
360
361 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const
362 {
363     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
364 }
365
366 bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect) const
367 {
368     return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
369 }
370
371 bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect) const
372 {
373     return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
374 }
375
376 void SVGSVGElement::deselectAll()
377 {
378     if (Frame* frame = document()->frame())
379         frame->selection()->clear();
380 }
381
382 float SVGSVGElement::createSVGNumber()
383 {
384     return 0.0f;
385 }
386
387 SVGLength SVGSVGElement::createSVGLength()
388 {
389     return SVGLength();
390 }
391
392 SVGAngle SVGSVGElement::createSVGAngle()
393 {
394     return SVGAngle();
395 }
396
397 FloatPoint SVGSVGElement::createSVGPoint()
398 {
399     return FloatPoint();
400 }
401
402 SVGMatrix SVGSVGElement::createSVGMatrix()
403 {
404     return SVGMatrix();
405 }
406
407 FloatRect SVGSVGElement::createSVGRect()
408 {
409     return FloatRect();
410 }
411
412 SVGTransform SVGSVGElement::createSVGTransform()
413 {
414     return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
415 }
416
417 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
418 {
419     return SVGTransform(static_cast<const AffineTransform&>(matrix));
420 }
421
422 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
423 {
424     AffineTransform viewBoxTransform;
425     if (hasAttribute(SVGNames::viewBoxAttr)) {
426         FloatSize size = currentViewportSize();
427         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
428     }
429
430     AffineTransform transform;
431     if (!isOutermostSVGSVGElement()) {
432         SVGLengthContext lengthContext(this);
433         transform.translate(x().value(lengthContext), y().value(lengthContext));
434     } else if (mode == SVGLocatable::ScreenScope) {
435         if (RenderObject* renderer = this->renderer()) {
436             // Translate in our CSS parent coordinate space
437             // FIXME: This doesn't work correctly with CSS transforms.
438             FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true);
439
440             // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because
441             // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute())
442             // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183)
443             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
444
445             // Respect scroll offset.
446             if (FrameView* view = document()->view()) {
447                 LayoutSize scrollOffset = view->scrollOffset();
448                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
449             }
450         }
451     }
452
453     return transform.multiply(viewBoxTransform);
454 }
455
456 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
457 {
458     if (isOutermostSVGSVGElement())
459         return new (arena) RenderSVGRoot(this);
460
461     return new (arena) RenderSVGViewportContainer(this);
462 }
463
464 void SVGSVGElement::insertedIntoDocument()
465 {
466     document()->accessSVGExtensions()->addTimeContainer(this);
467     SVGStyledLocatableElement::insertedIntoDocument();
468 }
469
470 void SVGSVGElement::removedFromDocument()
471 {
472     document()->accessSVGExtensions()->removeTimeContainer(this);
473     SVGStyledLocatableElement::removedFromDocument();
474 }
475
476 void SVGSVGElement::pauseAnimations()
477 {
478     if (!m_timeContainer->isPaused())
479         m_timeContainer->pause();
480 }
481
482 void SVGSVGElement::unpauseAnimations()
483 {
484     if (m_timeContainer->isPaused())
485         m_timeContainer->resume();
486 }
487
488 bool SVGSVGElement::animationsPaused() const
489 {
490     return m_timeContainer->isPaused();
491 }
492
493 float SVGSVGElement::getCurrentTime() const
494 {
495     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
496 }
497
498 void SVGSVGElement::setCurrentTime(float seconds)
499 {
500     if (isnan(seconds))
501         return;
502     seconds = max(seconds, 0.0f);
503     m_timeContainer->setElapsed(seconds);
504 }
505
506 bool SVGSVGElement::selfHasRelativeLengths() const
507 {
508     return x().isRelative()
509         || y().isRelative()
510         || width().isRelative()
511         || height().isRelative()
512         || hasAttribute(SVGNames::viewBoxAttr);
513 }
514
515 FloatRect SVGSVGElement::currentViewBoxRect() const
516 {
517     if (useCurrentView()) {
518         if (SVGViewSpec* view = currentView()) // what if we should use it but it is not set?
519             return view->viewBox();
520         return FloatRect();
521     }
522
523     FloatRect useViewBox = viewBox();
524     if (!useViewBox.isEmpty())
525         return useViewBox;
526     if (!renderer() || !renderer()->isSVGRoot())
527         return FloatRect();
528     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
529         return FloatRect();
530
531     Length intrinsicWidth = this->intrinsicWidth();
532     Length intrinsicHeight = this->intrinsicHeight();
533     if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
534         return FloatRect();
535
536     // If no viewBox is specified but non-relative width/height values, then we
537     // should always synthesize a viewBox if we're embedded through a SVGImage.    
538     return FloatRect(FloatPoint(), FloatSize(intrinsicWidth.calcFloatValue(0), intrinsicHeight.calcFloatValue(0)));
539 }
540
541 FloatSize SVGSVGElement::currentViewportSize() const
542 {
543     Length intrinsicWidth = this->intrinsicWidth();
544     Length intrinsicHeight = this->intrinsicHeight();
545     if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
546         return FloatSize(intrinsicWidth.calcFloatValue(0), intrinsicHeight.calcFloatValue(0));
547
548     if (!renderer())
549         return FloatSize();
550
551     if (renderer()->isSVGRoot()) {
552         LayoutRect frameRect = toRenderSVGRoot(renderer())->frameRect();
553         return FloatSize(frameRect.width() / renderer()->style()->effectiveZoom(), frameRect.height() / renderer()->style()->effectiveZoom());
554     }
555
556     FloatRect frameRect = toRenderSVGViewportContainer(renderer())->viewport();
557     return FloatSize(frameRect.width() / renderer()->style()->effectiveZoom(), frameRect.height() / renderer()->style()->effectiveZoom());
558 }
559
560 bool SVGSVGElement::widthAttributeEstablishesViewport() const
561 {
562     if (!renderer() || renderer()->isSVGViewportContainer())
563         return true;
564
565     // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
566     // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
567     // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or
568     //   the SVG content is embedded inline within a containing document;
569     // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
570     // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element)
571     //   or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
572     //   the positioning properties establish the viewport's width.
573     RenderSVGRoot* root = toRenderSVGRoot(renderer());
574
575     // SVG embedded through object/embed/iframe.
576     if (root->isEmbeddedThroughFrameContainingSVGDocument())
577         return !root->hasReplacedLogicalWidth() && !document()->frame()->ownerRenderer()->hasReplacedLogicalWidth();
578
579     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
580     if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this)
581         return !root->hasReplacedLogicalWidth();
582
583     return true;
584 }
585
586 bool SVGSVGElement::heightAttributeEstablishesViewport() const
587 {
588     if (!renderer() || renderer()->isSVGViewportContainer())
589         return true;
590
591     // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
592     // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
593     // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
594     // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height.
595     RenderSVGRoot* root = toRenderSVGRoot(renderer());
596
597     // SVG embedded through object/embed/iframe.
598     if (root->isEmbeddedThroughFrameContainingSVGDocument())
599         return !root->hasReplacedLogicalHeight() && !document()->frame()->ownerRenderer()->hasReplacedLogicalHeight();
600
601     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
602     if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this)
603         return !root->hasReplacedLogicalHeight();
604
605     return true;
606 }
607
608 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
609 {
610     if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
611         if (width().unitType() == LengthTypePercentage)
612             return Length(width().valueAsPercentage() * 100, Percent);
613
614         SVGLengthContext lengthContext(this);
615         return Length(width().value(lengthContext), Fixed);
616     }
617
618     ASSERT(renderer());
619     return renderer()->style()->width();
620 }
621
622 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
623 {
624     if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
625         if (height().unitType() == LengthTypePercentage)
626             return Length(height().valueAsPercentage() * 100, Percent);
627
628         SVGLengthContext lengthContext(this);
629         return Length(height().value(lengthContext), Fixed);
630     }
631
632     ASSERT(renderer());
633     return renderer()->style()->height();
634 }
635
636 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
637 {
638     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight);
639     if (useCurrentView() && currentView()) {
640         AffineTransform transform;
641         if (currentView()->transform().concatenate(transform))
642             ctm *= transform;
643     }
644
645     return ctm;
646 }
647
648 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
649 {
650     bool hadUseCurrentView = m_useCurrentView;
651     if (fragmentIdentifier.startsWith("xpointer(")) {
652         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
653         setUseCurrentView(false);
654     } else if (fragmentIdentifier.startsWith("svgView(")) {
655         if (currentView()->parseViewSpec(fragmentIdentifier))
656             setUseCurrentView(true);
657     } else if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
658         if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) {
659             SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
660             if (element->hasTagName(SVGNames::svgTag)) {
661                 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
662                 svg->inheritViewAttributes(viewElement);
663                 setUseCurrentView(true);
664             }
665         }
666     }
667
668     if (!hadUseCurrentView) {
669         if (!m_useCurrentView)
670             return;
671     } else if (!m_useCurrentView)
672         currentView()->setTransform(emptyString());
673
674     // Force a layout, otherwise RenderSVGRoots localToBorderBoxTransform won't be rebuild.
675     if (RenderObject* object = renderer())
676         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
677
678     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
679     // FIXME: We need to actually "highlight" the viewTarget(s).
680 }
681
682 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
683 {
684     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
685         currentView()->setViewBoxBaseValue(viewElement->viewBox());
686     else
687         currentView()->setViewBoxBaseValue(viewBox());
688
689     SVGPreserveAspectRatio aspectRatio;
690     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
691         aspectRatio = viewElement->preserveAspectRatioBaseValue();
692     else
693         aspectRatio = preserveAspectRatioBaseValue();
694     currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
695
696     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
697         currentView()->setZoomAndPan(viewElement->zoomAndPan());
698     
699     if (RenderObject* object = renderer())
700         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
701 }
702     
703 void SVGSVGElement::documentWillSuspendForPageCache()
704 {
705     pauseAnimations();
706 }
707
708 void SVGSVGElement::documentDidResumeFromPageCache()
709 {
710     unpauseAnimations();
711 }
712
713 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
714 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
715 Element* SVGSVGElement::getElementById(const AtomicString& id) const
716 {
717     Element* element = treeScope()->getElementById(id);
718     if (element && element->isDescendantOf(this))
719         return element;
720
721     // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
722     // be returned.
723     for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) {
724         if (!node->isElementNode())
725             continue;
726
727         Element* element = static_cast<Element*>(node);
728         if (element->hasID() && element->getIdAttribute() == id)
729             return element;
730     }
731     return 0;
732 }
733
734 }
735
736 #endif // ENABLE(SVG)