Add WTF::move()
[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 #include "SVGSVGElement.h"
24
25 #include "AffineTransform.h"
26 #include "Attribute.h"
27 #include "CSSHelper.h"
28 #include "Document.h"
29 #include "ElementIterator.h"
30 #include "EventListener.h"
31 #include "EventNames.h"
32 #include "FloatConversion.h"
33 #include "FloatRect.h"
34 #include "Frame.h"
35 #include "FrameSelection.h"
36 #include "FrameTree.h"
37 #include "FrameView.h"
38 #include "HTMLNames.h"
39 #include "RenderObject.h"
40 #include "RenderSVGResource.h"
41 #include "RenderSVGModelObject.h"
42 #include "RenderSVGRoot.h"
43 #include "RenderSVGViewportContainer.h"
44 #include "RenderView.h"
45 #include "RenderWidget.h"
46 #include "SMILTimeContainer.h"
47 #include "SVGAngle.h"
48 #include "SVGElementInstance.h"
49 #include "SVGFitToViewBox.h"
50 #include "SVGNames.h"
51 #include "SVGPreserveAspectRatio.h"
52 #include "SVGTransform.h"
53 #include "SVGTransformList.h"
54 #include "SVGViewElement.h"
55 #include "SVGViewSpec.h"
56 #include "SVGZoomEvent.h"
57 #include "StaticNodeList.h"
58 #include <wtf/StdLibExtras.h>
59
60 namespace WebCore {
61
62 // Animated property definitions
63 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
64 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
65 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
66 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
67 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
68 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
69 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
70
71 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
72     REGISTER_LOCAL_ANIMATED_PROPERTY(x)
73     REGISTER_LOCAL_ANIMATED_PROPERTY(y)
74     REGISTER_LOCAL_ANIMATED_PROPERTY(width)
75     REGISTER_LOCAL_ANIMATED_PROPERTY(height)
76     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
77     REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
78     REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
79     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
80 END_REGISTER_ANIMATED_PROPERTIES
81
82 inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document& document)
83     : SVGGraphicsElement(tagName, document)
84     , m_x(LengthModeWidth)
85     , m_y(LengthModeHeight)
86     , m_width(LengthModeWidth, "100%")
87     , m_height(LengthModeHeight, "100%") 
88     , m_useCurrentView(false)
89     , m_zoomAndPan(SVGZoomAndPanMagnify)
90     , m_timeContainer(SMILTimeContainer::create(this))
91 {
92     ASSERT(hasTagName(SVGNames::svgTag));
93     registerAnimatedPropertiesForSVGSVGElement();
94     document.registerForPageCacheSuspensionCallbacks(this);
95 }
96
97 PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document& document)
98 {
99     return adoptRef(new SVGSVGElement(tagName, document));
100 }
101
102 SVGSVGElement::~SVGSVGElement()
103 {
104     if (m_viewSpec)
105         m_viewSpec->resetContextElement();
106     document().unregisterForPageCacheSuspensionCallbacks(this);
107     // There are cases where removedFromDocument() is not called.
108     // see ContainerNode::removeAllChildren, called by its destructor.
109     document().accessSVGExtensions()->removeTimeContainer(this);
110 }
111
112 void SVGSVGElement::didMoveToNewDocument(Document* oldDocument)
113 {
114     if (oldDocument)
115         oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
116     document().registerForPageCacheSuspensionCallbacks(this);
117     SVGGraphicsElement::didMoveToNewDocument(oldDocument);
118 }
119
120 const AtomicString& SVGSVGElement::contentScriptType() const
121 {
122     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral));
123     const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
124     return n.isNull() ? defaultValue : n;
125 }
126
127 void SVGSVGElement::setContentScriptType(const AtomicString& type)
128 {
129     setAttribute(SVGNames::contentScriptTypeAttr, type);
130 }
131
132 const AtomicString& SVGSVGElement::contentStyleType() const
133 {
134     DEPRECATED_DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral));
135     const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
136     return n.isNull() ? defaultValue : n;
137 }
138
139 void SVGSVGElement::setContentStyleType(const AtomicString& type)
140 {
141     setAttribute(SVGNames::contentStyleTypeAttr, type);
142 }
143
144 FloatRect SVGSVGElement::viewport() const
145 {
146     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
147     // As we have no test coverage for this, we're going to disable it completly for now.
148     return FloatRect();
149 }
150
151 float SVGSVGElement::pixelUnitToMillimeterX() const
152 {
153     // 2.54 / cssPixelsPerInch gives CM.
154     return (2.54f / cssPixelsPerInch) * 10.0f;
155 }
156
157 float SVGSVGElement::pixelUnitToMillimeterY() const
158 {
159     // 2.54 / cssPixelsPerInch gives CM.
160     return (2.54f / cssPixelsPerInch) * 10.0f;
161 }
162
163 float SVGSVGElement::screenPixelToMillimeterX() const
164 {
165     return pixelUnitToMillimeterX();
166 }
167
168 float SVGSVGElement::screenPixelToMillimeterY() const
169 {
170     return pixelUnitToMillimeterY();
171 }
172
173 SVGViewSpec* SVGSVGElement::currentView()
174 {
175     if (!m_viewSpec)
176         m_viewSpec = SVGViewSpec::create(this);
177     return m_viewSpec.get();
178 }
179
180 float SVGSVGElement::currentScale() const
181 {
182     if (!inDocument() || !isOutermostSVGSVGElement())
183         return 1;
184
185     Frame* frame = document().frame();
186     if (!frame)
187         return 1;
188
189     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
190     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
191     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
192     return frame->tree().parent() ? 1 : frame->pageZoomFactor();
193 }
194
195 void SVGSVGElement::setCurrentScale(float scale)
196 {
197     if (!inDocument() || !isOutermostSVGSVGElement())
198         return;
199
200     Frame* frame = document().frame();
201     if (!frame)
202         return;
203
204     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
205     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
206     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
207     if (frame->tree().parent())
208         return;
209
210     frame->setPageZoomFactor(scale);
211 }
212
213 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
214 {
215     m_translation = translation;
216     updateCurrentTranslate();
217 }
218
219 void SVGSVGElement::updateCurrentTranslate()
220 {
221     if (RenderObject* object = renderer())
222         object->setNeedsLayout();
223
224     if (parentNode() == &document() && document().renderView())
225         document().renderView()->repaint();
226 }
227
228 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
229 {
230     SVGParsingError parseError = NoError;
231
232     if (!nearestViewportElement()) {
233         bool setListener = true;
234
235         // Only handle events if we're the outermost <svg> element
236         if (name == HTMLNames::onunloadAttr)
237             document().setWindowAttributeEventListener(eventNames().unloadEvent, name, value);
238         else if (name == HTMLNames::onresizeAttr)
239             document().setWindowAttributeEventListener(eventNames().resizeEvent, name, value);
240         else if (name == HTMLNames::onscrollAttr)
241             document().setWindowAttributeEventListener(eventNames().scrollEvent, name, value);
242         else if (name == SVGNames::onzoomAttr)
243             document().setWindowAttributeEventListener(eventNames().zoomEvent, name, value);
244         else
245             setListener = false;
246  
247         if (setListener)
248             return;
249     }
250
251     if (name == HTMLNames::onabortAttr)
252         document().setWindowAttributeEventListener(eventNames().abortEvent, name, value);
253     else if (name == HTMLNames::onerrorAttr)
254         document().setWindowAttributeEventListener(eventNames().errorEvent, name, value);
255     else if (name == SVGNames::xAttr)
256         setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
257     else if (name == SVGNames::yAttr)
258         setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
259     else if (name == SVGNames::widthAttr)
260         setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths));
261     else if (name == SVGNames::heightAttr)
262         setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths));
263     else if (SVGLangSpace::parseAttribute(name, value)
264                || SVGExternalResourcesRequired::parseAttribute(name, value)
265                || SVGFitToViewBox::parseAttribute(this, name, value)
266                || SVGZoomAndPan::parseAttribute(this, name, value)) {
267     } else
268         SVGGraphicsElement::parseAttribute(name, value);
269
270     reportAttributeParsingError(parseError, name, value);
271 }
272
273 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const
274 {
275     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr))
276         return true;
277     return SVGGraphicsElement::isPresentationAttribute(name);
278 }
279
280 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
281 {
282     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) {
283         if (name == SVGNames::widthAttr)
284             addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value);
285         else if (name == SVGNames::heightAttr)
286             addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value);
287     } else
288         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
289 }
290
291 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
292 {
293     bool updateRelativeLengthsOrViewBox = false;
294     bool widthChanged = attrName == SVGNames::widthAttr;
295     bool heightChanged = attrName == SVGNames::heightAttr;
296     if (widthChanged || heightChanged
297         || attrName == SVGNames::xAttr
298         || attrName == SVGNames::yAttr) {
299         updateRelativeLengthsOrViewBox = true;
300         updateRelativeLengthsInformation();
301
302         // At the SVG/HTML boundary (aka RenderSVGRoot), the width and
303         // height attributes can affect the replaced size so we need
304         // to mark it for updating.
305         if (widthChanged || heightChanged) {
306             // FIXME: This is a hack to synchronize changes from SVG DOM earlier in the
307             // run. We need these changes in the style calculation process earlier than
308             // usual.
309             synchronizeAllAttributes();
310             RenderObject* renderObject = renderer();
311             if (renderObject && renderObject->isSVGRoot()) {
312                 invalidateSVGPresentationAttributeStyle();
313                 setNeedsStyleRecalc();
314             }
315         }
316     }
317
318     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
319         updateRelativeLengthsOrViewBox = true; 
320         if (RenderObject* object = renderer())
321             object->setNeedsTransformUpdate();
322     }
323
324     SVGElementInstance::InvalidationGuard invalidationGuard(this);
325
326     if (updateRelativeLengthsOrViewBox
327         || SVGLangSpace::isKnownAttribute(attrName)
328         || SVGExternalResourcesRequired::isKnownAttribute(attrName)
329         || SVGZoomAndPan::isKnownAttribute(attrName)) {
330         if (auto renderer = this->renderer())
331             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
332         return;
333     }
334
335     SVGGraphicsElement::svgAttributeChanged(attrName);
336 }
337
338 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
339 {
340     // FIXME: Implement me (see bug 11275)
341     return 0;
342 }
343
344 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
345 {
346     // FIXME: Implement me (see bug 11275)
347 }
348
349 void SVGSVGElement::unsuspendRedrawAll()
350 {
351     // FIXME: Implement me (see bug 11275)
352 }
353
354 void SVGSVGElement::forceRedraw()
355 {
356     // FIXME: Implement me (see bug 11275)
357 }
358
359 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
360 {
361     Vector<Ref<Element>> elements;
362
363     for (auto& svgElement : descendantsOfType<SVGElement>(*(referenceElement ? referenceElement : this))) {
364         if (collect == CollectIntersectionList) {
365             if (RenderSVGModelObject::checkIntersection(svgElement.renderer(), rect))
366                 elements.append(const_cast<SVGElement&>(svgElement));
367         } else {
368             if (RenderSVGModelObject::checkEnclosure(svgElement.renderer(), rect))
369                 elements.append(const_cast<SVGElement&>(svgElement));
370         }
371     }
372
373     return StaticElementList::adopt(elements);
374 }
375
376 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const
377 {
378     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList);
379 }
380
381 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const
382 {
383     return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList);
384 }
385
386 bool SVGSVGElement::checkIntersection(const SVGElement* element, const FloatRect& rect) const
387 {
388     if (!element)
389         return false;
390     return RenderSVGModelObject::checkIntersection(element->renderer(), rect);
391 }
392
393 bool SVGSVGElement::checkEnclosure(const SVGElement* element, const FloatRect& rect) const
394 {
395     if (!element)
396         return false;
397     return RenderSVGModelObject::checkEnclosure(element->renderer(), rect);
398 }
399
400 void SVGSVGElement::deselectAll()
401 {
402     if (Frame* frame = document().frame())
403         frame->selection().clear();
404 }
405
406 float SVGSVGElement::createSVGNumber()
407 {
408     return 0.0f;
409 }
410
411 SVGLength SVGSVGElement::createSVGLength()
412 {
413     return SVGLength();
414 }
415
416 SVGAngle SVGSVGElement::createSVGAngle()
417 {
418     return SVGAngle();
419 }
420
421 SVGPoint SVGSVGElement::createSVGPoint()
422 {
423     return SVGPoint();
424 }
425
426 SVGMatrix SVGSVGElement::createSVGMatrix()
427 {
428     return SVGMatrix();
429 }
430
431 FloatRect SVGSVGElement::createSVGRect()
432 {
433     return FloatRect();
434 }
435
436 SVGTransform SVGSVGElement::createSVGTransform()
437 {
438     return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
439 }
440
441 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
442 {
443     return SVGTransform(static_cast<const AffineTransform&>(matrix));
444 }
445
446 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
447 {
448     AffineTransform viewBoxTransform;
449     if (!hasEmptyViewBox()) {
450         FloatSize size = currentViewportSize();
451         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
452     }
453
454     AffineTransform transform;
455     if (!isOutermostSVGSVGElement()) {
456         SVGLengthContext lengthContext(this);
457         transform.translate(x().value(lengthContext), y().value(lengthContext));
458     } else if (mode == SVGLocatable::ScreenScope) {
459         if (RenderObject* renderer = this->renderer()) {
460             FloatPoint location;
461             float zoomFactor = 1;
462
463             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 
464             // to map an element from SVG viewport coordinates to CSS box coordinates.
465             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
466             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
467             if (renderer->isSVGRoot()) {
468                 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
469                 zoomFactor = 1 / renderer->style().effectiveZoom();
470             }
471
472             // Translate in our CSS parent coordinate space
473             // FIXME: This doesn't work correctly with CSS transforms.
474             location = renderer->localToAbsolute(location, UseTransforms);
475             location.scale(zoomFactor, zoomFactor);
476
477             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
478             // so we have to subtract it here (original cause of bug #27183)
479             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
480
481             // Respect scroll offset.
482             if (FrameView* view = document().view()) {
483                 LayoutSize scrollOffset = view->scrollOffset();
484                 scrollOffset.scale(zoomFactor);
485                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
486             }
487         }
488     }
489
490     return transform.multiply(viewBoxTransform);
491 }
492
493 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
494 {
495     // FIXME: We should respect display: none on the documentElement svg element
496     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
497     // they should instead depend on the RenderView.
498     // https://bugs.webkit.org/show_bug.cgi?id=103493
499     if (document().documentElement() == this)
500         return true;
501     return StyledElement::rendererIsNeeded(style);
502 }
503
504 RenderPtr<RenderElement> SVGSVGElement::createElementRenderer(PassRef<RenderStyle> style)
505 {
506     if (isOutermostSVGSVGElement())
507         return createRenderer<RenderSVGRoot>(*this, WTF::move(style));
508
509     return createRenderer<RenderSVGViewportContainer>(*this, WTF::move(style));
510 }
511
512 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode& rootParent)
513 {
514     if (rootParent.inDocument()) {
515         document().accessSVGExtensions()->addTimeContainer(this);
516
517         // Animations are started at the end of document parsing and after firing the load event,
518         // but if we miss that train (deferred programmatic element insertion for example) we need
519         // to initialize the time container here.
520         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
521             timeContainer()->begin();
522     }
523     return SVGGraphicsElement::insertedInto(rootParent);
524 }
525
526 void SVGSVGElement::removedFrom(ContainerNode& rootParent)
527 {
528     if (rootParent.inDocument())
529         document().accessSVGExtensions()->removeTimeContainer(this);
530     SVGGraphicsElement::removedFrom(rootParent);
531 }
532
533 void SVGSVGElement::pauseAnimations()
534 {
535     if (!m_timeContainer->isPaused())
536         m_timeContainer->pause();
537 }
538
539 void SVGSVGElement::unpauseAnimations()
540 {
541     if (m_timeContainer->isPaused())
542         m_timeContainer->resume();
543 }
544
545 bool SVGSVGElement::animationsPaused() const
546 {
547     return m_timeContainer->isPaused();
548 }
549
550 float SVGSVGElement::getCurrentTime() const
551 {
552     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
553 }
554
555 void SVGSVGElement::setCurrentTime(float seconds)
556 {
557     if (std::isnan(seconds))
558         return;
559     seconds = std::max(seconds, 0.0f);
560     m_timeContainer->setElapsed(seconds);
561 }
562
563 bool SVGSVGElement::selfHasRelativeLengths() const
564 {
565     return x().isRelative()
566         || y().isRelative()
567         || width().isRelative()
568         || height().isRelative()
569         || hasAttribute(SVGNames::viewBoxAttr);
570 }
571
572 FloatRect SVGSVGElement::currentViewBoxRect() const
573 {
574     if (m_useCurrentView)
575         return m_viewSpec ? m_viewSpec->viewBox() : FloatRect();
576
577     FloatRect useViewBox = viewBox();
578     if (!useViewBox.isEmpty())
579         return useViewBox;
580     if (!renderer() || !renderer()->isSVGRoot())
581         return FloatRect();
582     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
583         return FloatRect();
584
585     Length intrinsicWidth = this->intrinsicWidth();
586     Length intrinsicHeight = this->intrinsicHeight();
587     if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
588         return FloatRect();
589
590     // If no viewBox is specified but non-relative width/height values, then we
591     // should always synthesize a viewBox if we're embedded through a SVGImage.    
592     return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)));
593 }
594
595 FloatSize SVGSVGElement::currentViewportSize() const
596 {
597     if (hasIntrinsicWidth() && hasIntrinsicHeight()) {
598         Length intrinsicWidth = this->intrinsicWidth();
599         Length intrinsicHeight = this->intrinsicHeight();
600         return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
601     }
602
603     if (!renderer())
604         return FloatSize();
605
606     if (renderer()->isSVGRoot()) {
607         LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
608         return FloatSize(contentBoxRect.width() / renderer()->style().effectiveZoom(), contentBoxRect.height() / renderer()->style().effectiveZoom());
609     }
610
611     FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
612     return FloatSize(viewportRect.width(), viewportRect.height());
613 }
614
615 bool SVGSVGElement::hasIntrinsicWidth() const
616 {
617     return width().unitType() != LengthTypePercentage;
618 }
619
620 bool SVGSVGElement::hasIntrinsicHeight() const
621 {
622     return height().unitType() != LengthTypePercentage;
623 }
624
625 Length SVGSVGElement::intrinsicWidth() const
626 {
627     if (width().unitType() == LengthTypePercentage)
628         return Length(0, Fixed);
629
630     SVGLengthContext lengthContext(this);
631     return Length(width().value(lengthContext), Fixed);
632 }
633
634 Length SVGSVGElement::intrinsicHeight() const
635 {
636     if (height().unitType() == LengthTypePercentage)
637         return Length(0, Fixed);
638
639     SVGLengthContext lengthContext(this);
640     return Length(height().value(lengthContext), Fixed);
641 }
642
643 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
644 {
645     if (!m_useCurrentView || !m_viewSpec)
646         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight);
647
648     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio(), viewWidth, viewHeight);
649     const SVGTransformList& transformList = m_viewSpec->transformBaseValue();
650     if (transformList.isEmpty())
651         return ctm;
652
653     AffineTransform transform;
654     if (transformList.concatenate(transform))
655         ctm *= transform;
656
657     return ctm;
658 }
659
660 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
661 {
662     auto renderer = this->renderer();
663     SVGViewSpec* view = m_viewSpec.get();
664     if (view)
665         view->reset();
666
667     bool hadUseCurrentView = m_useCurrentView;
668     m_useCurrentView = false;
669
670     if (fragmentIdentifier.startsWith("xpointer(")) {
671         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
672         if (renderer && hadUseCurrentView)
673             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
674         return;
675     }
676
677     if (fragmentIdentifier.startsWith("svgView(")) {
678         if (!view)
679             view = currentView(); // Create the SVGViewSpec.
680
681         if (view->parseViewSpec(fragmentIdentifier))
682             m_useCurrentView = true;
683         else
684             view->reset();
685
686         if (renderer && (hadUseCurrentView || m_useCurrentView))
687             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
688         return;
689     }
690
691     // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
692     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
693     // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
694     // attributes on the closest ancestor ‘svg’ element.
695     if (anchorNode && isSVGViewElement(anchorNode)) {
696         if (SVGViewElement* viewElement = toSVGViewElement(anchorNode)) {
697             SVGElement* element = SVGLocatable::nearestViewportElement(viewElement);
698             if (element->hasTagName(SVGNames::svgTag)) {
699                 SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
700                 svg->inheritViewAttributes(viewElement);
701
702                 if (RenderElement* renderer = svg->renderer())
703                     RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
704             }
705         }
706         return;
707     }
708
709     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
710     // FIXME: We need to actually "highlight" the viewTarget(s).
711 }
712
713 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
714 {
715     SVGViewSpec* view = currentView();
716     m_useCurrentView = true;
717
718     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
719         view->setViewBoxBaseValue(viewElement->viewBox());
720     else
721         view->setViewBoxBaseValue(viewBox());
722
723     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr))
724         view->setPreserveAspectRatioBaseValue(viewElement->preserveAspectRatioBaseValue());
725     else
726         view->setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue());
727
728     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
729         view->setZoomAndPanBaseValue(viewElement->zoomAndPan());
730     else
731         view->setZoomAndPanBaseValue(zoomAndPan());
732 }
733
734 void SVGSVGElement::documentWillSuspendForPageCache()
735 {
736     pauseAnimations();
737 }
738
739 void SVGSVGElement::documentDidResumeFromPageCache()
740 {
741     unpauseAnimations();
742 }
743
744 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
745 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
746 Element* SVGSVGElement::getElementById(const String& id)
747 {
748     Element* element = treeScope().getElementById(id);
749     if (element && element->isDescendantOf(this))
750         return element;
751
752     // FIXME: This should use treeScope().getAllElementsById.
753     // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
754     // be returned.
755     for (auto& element : descendantsOfType<Element>(*this)) {
756         if (element.getIdAttribute() == id)
757             return &element;
758     }
759     return 0;
760 }
761
762 }