viewBox on nested SVG causes wrong content size for relative values
[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         || attrName == SVGNames::viewBoxAttr) {
308         if (renderer())
309             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
310         return;
311     }
312
313     SVGStyledElement::svgAttributeChanged(attrName);
314 }
315
316 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
317 {
318     // FIXME: Implement me (see bug 11275)
319     return 0;
320 }
321
322 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
323 {
324     // FIXME: Implement me (see bug 11275)
325 }
326
327 void SVGSVGElement::unsuspendRedrawAll()
328 {
329     // FIXME: Implement me (see bug 11275)
330 }
331
332 void SVGSVGElement::forceRedraw()
333 {
334     // FIXME: Implement me (see bug 11275)
335 }
336
337 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
338 {
339     Vector<RefPtr<Node> > nodes;
340     Node* node = traverseNextNode(referenceElement ? referenceElement : this);
341     while (node) {
342         if (node->isSVGElement()) { 
343             if (collect == CollectIntersectionList) {
344                 if (checkIntersection(static_cast<SVGElement*>(node), rect))
345                     nodes.append(node);
346             } else {
347                 if (checkEnclosure(static_cast<SVGElement*>(node), rect))
348                     nodes.append(node);
349             }
350         }
351
352         node = node->traverseNextNode(referenceElement ? referenceElement : this);
353     }
354     return StaticNodeList::adopt(nodes);
355 }
356
357 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const
358 {
359     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
360 }
361
362 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const
363 {
364     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
365 }
366
367 bool SVGSVGElement::checkIntersection(SVGElement* element, const FloatRect& rect) const
368 {
369     return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
370 }
371
372 bool SVGSVGElement::checkEnclosure(SVGElement* element, const FloatRect& rect) const
373 {
374     return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
375 }
376
377 void SVGSVGElement::deselectAll()
378 {
379     if (Frame* frame = document()->frame())
380         frame->selection()->clear();
381 }
382
383 float SVGSVGElement::createSVGNumber()
384 {
385     return 0.0f;
386 }
387
388 SVGLength SVGSVGElement::createSVGLength()
389 {
390     return SVGLength();
391 }
392
393 SVGAngle SVGSVGElement::createSVGAngle()
394 {
395     return SVGAngle();
396 }
397
398 FloatPoint SVGSVGElement::createSVGPoint()
399 {
400     return FloatPoint();
401 }
402
403 SVGMatrix SVGSVGElement::createSVGMatrix()
404 {
405     return SVGMatrix();
406 }
407
408 FloatRect SVGSVGElement::createSVGRect()
409 {
410     return FloatRect();
411 }
412
413 SVGTransform SVGSVGElement::createSVGTransform()
414 {
415     return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
416 }
417
418 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
419 {
420     return SVGTransform(static_cast<const AffineTransform&>(matrix));
421 }
422
423 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
424 {
425     AffineTransform viewBoxTransform;
426     if (hasAttribute(SVGNames::viewBoxAttr)) {
427         FloatSize size = currentViewportSize();
428         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
429     }
430
431     AffineTransform transform;
432     if (!isOutermostSVGSVGElement()) {
433         SVGLengthContext lengthContext(this);
434         transform.translate(x().value(lengthContext), y().value(lengthContext));
435     } else if (mode == SVGLocatable::ScreenScope) {
436         if (RenderObject* renderer = this->renderer()) {
437             // Translate in our CSS parent coordinate space
438             // FIXME: This doesn't work correctly with CSS transforms.
439             FloatPoint location = renderer->localToAbsolute(FloatPoint(), false, true);
440
441             // Be careful here! localToAbsolute() includes the x/y offset coming from the viewBoxToViewTransform(), because
442             // RenderSVGRoot::localToBorderBoxTransform() (called through mapLocalToContainer(), called from localToAbsolute())
443             // also takes the viewBoxToViewTransform() into account, so we have to subtract it here (original cause of bug #27183)
444             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
445
446             // Respect scroll offset.
447             if (FrameView* view = document()->view()) {
448                 LayoutSize scrollOffset = view->scrollOffset();
449                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
450             }
451         }
452     }
453
454     return transform.multiply(viewBoxTransform);
455 }
456
457 RenderObject* SVGSVGElement::createRenderer(RenderArena* arena, RenderStyle*)
458 {
459     if (isOutermostSVGSVGElement())
460         return new (arena) RenderSVGRoot(this);
461
462     return new (arena) RenderSVGViewportContainer(this);
463 }
464
465 void SVGSVGElement::insertedIntoDocument()
466 {
467     document()->accessSVGExtensions()->addTimeContainer(this);
468     SVGStyledLocatableElement::insertedIntoDocument();
469 }
470
471 void SVGSVGElement::removedFromDocument()
472 {
473     document()->accessSVGExtensions()->removeTimeContainer(this);
474     SVGStyledLocatableElement::removedFromDocument();
475 }
476
477 void SVGSVGElement::pauseAnimations()
478 {
479     if (!m_timeContainer->isPaused())
480         m_timeContainer->pause();
481 }
482
483 void SVGSVGElement::unpauseAnimations()
484 {
485     if (m_timeContainer->isPaused())
486         m_timeContainer->resume();
487 }
488
489 bool SVGSVGElement::animationsPaused() const
490 {
491     return m_timeContainer->isPaused();
492 }
493
494 float SVGSVGElement::getCurrentTime() const
495 {
496     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
497 }
498
499 void SVGSVGElement::setCurrentTime(float seconds)
500 {
501     if (isnan(seconds))
502         return;
503     seconds = max(seconds, 0.0f);
504     m_timeContainer->setElapsed(seconds);
505 }
506
507 bool SVGSVGElement::selfHasRelativeLengths() const
508 {
509     return x().isRelative()
510         || y().isRelative()
511         || width().isRelative()
512         || height().isRelative()
513         || hasAttribute(SVGNames::viewBoxAttr);
514 }
515
516 FloatRect SVGSVGElement::currentViewBoxRect() const
517 {
518     if (useCurrentView()) {
519         if (SVGViewSpec* view = currentView()) // what if we should use it but it is not set?
520             return view->viewBox();
521         return FloatRect();
522     }
523
524     FloatRect useViewBox = viewBox();
525     if (!useViewBox.isEmpty())
526         return useViewBox;
527     if (!renderer() || !renderer()->isSVGRoot())
528         return FloatRect();
529     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
530         return FloatRect();
531
532     Length intrinsicWidth = this->intrinsicWidth();
533     Length intrinsicHeight = this->intrinsicHeight();
534     if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
535         return FloatRect();
536
537     // If no viewBox is specified but non-relative width/height values, then we
538     // should always synthesize a viewBox if we're embedded through a SVGImage.    
539     return FloatRect(FloatPoint(), FloatSize(intrinsicWidth.calcFloatValue(0), intrinsicHeight.calcFloatValue(0)));
540 }
541
542 FloatSize SVGSVGElement::currentViewportSize() const
543 {
544     Length intrinsicWidth = this->intrinsicWidth();
545     Length intrinsicHeight = this->intrinsicHeight();
546     if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
547         return FloatSize(intrinsicWidth.calcFloatValue(0), intrinsicHeight.calcFloatValue(0));
548
549     if (!renderer())
550         return FloatSize();
551
552     if (renderer()->isSVGRoot()) {
553         LayoutRect frameRect = toRenderSVGRoot(renderer())->frameRect();
554         return FloatSize(frameRect.width() / renderer()->style()->effectiveZoom(), frameRect.height() / renderer()->style()->effectiveZoom());
555     }
556
557     FloatRect frameRect = toRenderSVGViewportContainer(renderer())->viewport();
558     return FloatSize(frameRect.width() / renderer()->style()->effectiveZoom(), frameRect.height() / renderer()->style()->effectiveZoom());
559 }
560
561 bool SVGSVGElement::widthAttributeEstablishesViewport() const
562 {
563     if (!renderer() || renderer()->isSVGViewportContainer())
564         return true;
565
566     // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
567     // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
568     // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or
569     //   the SVG content is embedded inline within a containing document;
570     // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
571     // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element)
572     //   or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
573     //   the positioning properties establish the viewport's width.
574     RenderSVGRoot* root = toRenderSVGRoot(renderer());
575
576     // SVG embedded through object/embed/iframe.
577     if (root->isEmbeddedThroughFrameContainingSVGDocument())
578         return !root->hasReplacedLogicalWidth() && !document()->frame()->ownerRenderer()->hasReplacedLogicalWidth();
579
580     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
581     if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this)
582         return !root->hasReplacedLogicalWidth();
583
584     return true;
585 }
586
587 bool SVGSVGElement::heightAttributeEstablishesViewport() const
588 {
589     if (!renderer() || renderer()->isSVGViewportContainer())
590         return true;
591
592     // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
593     // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
594     // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
595     // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height.
596     RenderSVGRoot* root = toRenderSVGRoot(renderer());
597
598     // SVG embedded through object/embed/iframe.
599     if (root->isEmbeddedThroughFrameContainingSVGDocument())
600         return !root->hasReplacedLogicalHeight() && !document()->frame()->ownerRenderer()->hasReplacedLogicalHeight();
601
602     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
603     if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this)
604         return !root->hasReplacedLogicalHeight();
605
606     return true;
607 }
608
609 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
610 {
611     if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
612         if (width().unitType() == LengthTypePercentage)
613             return Length(width().valueAsPercentage() * 100, Percent);
614
615         SVGLengthContext lengthContext(this);
616         return Length(width().value(lengthContext), Fixed);
617     }
618
619     ASSERT(renderer());
620     return renderer()->style()->width();
621 }
622
623 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
624 {
625     if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
626         if (height().unitType() == LengthTypePercentage)
627             return Length(height().valueAsPercentage() * 100, Percent);
628
629         SVGLengthContext lengthContext(this);
630         return Length(height().value(lengthContext), Fixed);
631     }
632
633     ASSERT(renderer());
634     return renderer()->style()->height();
635 }
636
637 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
638 {
639     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight);
640     if (useCurrentView() && currentView()) {
641         AffineTransform transform;
642         if (currentView()->transform().concatenate(transform))
643             ctm *= transform;
644     }
645
646     return ctm;
647 }
648
649 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
650 {
651     bool hadUseCurrentView = m_useCurrentView;
652     if (fragmentIdentifier.startsWith("xpointer(")) {
653         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
654         setUseCurrentView(false);
655     } else if (fragmentIdentifier.startsWith("svgView(")) {
656         if (currentView()->parseViewSpec(fragmentIdentifier))
657             setUseCurrentView(true);
658     } else if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
659         if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast<SVGViewElement*>(anchorNode) : 0) {
660             SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
661             if (element->hasTagName(SVGNames::svgTag)) {
662                 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
663                 svg->inheritViewAttributes(viewElement);
664                 setUseCurrentView(true);
665             }
666         }
667     }
668
669     if (!hadUseCurrentView) {
670         if (!m_useCurrentView)
671             return;
672     } else if (!m_useCurrentView)
673         currentView()->setTransform(emptyString());
674
675     // Force a layout, otherwise RenderSVGRoots localToBorderBoxTransform won't be rebuild.
676     if (RenderObject* object = renderer())
677         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
678
679     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
680     // FIXME: We need to actually "highlight" the viewTarget(s).
681 }
682
683 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
684 {
685     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
686         currentView()->setViewBoxBaseValue(viewElement->viewBox());
687     else
688         currentView()->setViewBoxBaseValue(viewBox());
689
690     SVGPreserveAspectRatio aspectRatio;
691     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
692         aspectRatio = viewElement->preserveAspectRatioBaseValue();
693     else
694         aspectRatio = preserveAspectRatioBaseValue();
695     currentView()->setPreserveAspectRatioBaseValue(aspectRatio);
696
697     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
698         currentView()->setZoomAndPan(viewElement->zoomAndPan());
699     
700     if (RenderObject* object = renderer())
701         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
702 }
703     
704 void SVGSVGElement::documentWillSuspendForPageCache()
705 {
706     pauseAnimations();
707 }
708
709 void SVGSVGElement::documentDidResumeFromPageCache()
710 {
711     unpauseAnimations();
712 }
713
714 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
715 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
716 Element* SVGSVGElement::getElementById(const AtomicString& id) const
717 {
718     Element* element = treeScope()->getElementById(id);
719     if (element && element->isDescendantOf(this))
720         return element;
721
722     // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
723     // be returned.
724     for (Node* node = traverseNextNode(this); node; node = node->traverseNextNode(this)) {
725         if (!node->isElementNode())
726             continue;
727
728         Element* element = static_cast<Element*>(node);
729         if (element->hasID() && element->getIdAttribute() == id)
730             return element;
731     }
732     return 0;
733 }
734
735 }
736
737 #endif // ENABLE(SVG)