Assert that updateStyle and updateLayout are only called when it's safe to dispatch...
[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-2017 Apple Inc. All rights reserved.
5  * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include "config.h"
24 #include "SVGSVGElement.h"
25
26 #include "CSSHelper.h"
27 #include "DOMWrapperWorld.h"
28 #include "ElementIterator.h"
29 #include "EventNames.h"
30 #include "FrameSelection.h"
31 #include "MainFrame.h"
32 #include "RenderSVGResource.h"
33 #include "RenderSVGRoot.h"
34 #include "RenderSVGViewportContainer.h"
35 #include "RenderView.h"
36 #include "SMILTimeContainer.h"
37 #include "SVGAngle.h"
38 #include "SVGDocumentExtensions.h"
39 #include "SVGLength.h"
40 #include "SVGMatrix.h"
41 #include "SVGNumber.h"
42 #include "SVGPoint.h"
43 #include "SVGRect.h"
44 #include "SVGStaticPropertyTearOff.h"
45 #include "SVGTransform.h"
46 #include "SVGViewElement.h"
47 #include "SVGViewSpec.h"
48 #include "StaticNodeList.h"
49
50 namespace WebCore {
51
52 // Animated property definitions
53 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x)
54 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y)
55 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width)
56 DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height)
57 DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
58 DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio)
59 DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox)
60
61 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
62     REGISTER_LOCAL_ANIMATED_PROPERTY(x)
63     REGISTER_LOCAL_ANIMATED_PROPERTY(y)
64     REGISTER_LOCAL_ANIMATED_PROPERTY(width)
65     REGISTER_LOCAL_ANIMATED_PROPERTY(height)
66     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
67     REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox)
68     REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio)
69     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
70 END_REGISTER_ANIMATED_PROPERTIES
71
72 inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document& document)
73     : SVGGraphicsElement(tagName, document)
74     , m_x(LengthModeWidth)
75     , m_y(LengthModeHeight)
76     , m_width(LengthModeWidth, ASCIILiteral("100%"))
77     , m_height(LengthModeHeight, ASCIILiteral("100%"))
78     , m_timeContainer(SMILTimeContainer::create(*this))
79 {
80     ASSERT(hasTagName(SVGNames::svgTag));
81     registerAnimatedPropertiesForSVGSVGElement();
82     document.registerForDocumentSuspensionCallbacks(this);
83 }
84
85 Ref<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document& document)
86 {
87     return adoptRef(*new SVGSVGElement(tagName, document));
88 }
89
90 Ref<SVGSVGElement> SVGSVGElement::create(Document& document)
91 {
92     return create(SVGNames::svgTag, document);
93 }
94
95 SVGSVGElement::~SVGSVGElement()
96 {
97     if (m_viewSpec)
98         m_viewSpec->resetContextElement();
99     document().unregisterForDocumentSuspensionCallbacks(this);
100     document().accessSVGExtensions().removeTimeContainer(this);
101 }
102
103 void SVGSVGElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
104 {
105     oldDocument.unregisterForDocumentSuspensionCallbacks(this);
106     document().registerForDocumentSuspensionCallbacks(this);
107     SVGGraphicsElement::didMoveToNewDocument(oldDocument, newDocument);
108 }
109
110 const AtomicString& SVGSVGElement::contentScriptType() const
111 {
112     static NeverDestroyed<AtomicString> defaultScriptType { "text/ecmascript" };
113     const AtomicString& type = attributeWithoutSynchronization(SVGNames::contentScriptTypeAttr);
114     return type.isNull() ? defaultScriptType.get() : type;
115 }
116
117 void SVGSVGElement::setContentScriptType(const AtomicString& type)
118 {
119     setAttributeWithoutSynchronization(SVGNames::contentScriptTypeAttr, type);
120 }
121
122 const AtomicString& SVGSVGElement::contentStyleType() const
123 {
124     static NeverDestroyed<AtomicString> defaultStyleType { "text/css" };
125     const AtomicString& type = attributeWithoutSynchronization(SVGNames::contentStyleTypeAttr);
126     return type.isNull() ? defaultStyleType.get() : type;
127 }
128
129 void SVGSVGElement::setContentStyleType(const AtomicString& type)
130 {
131     setAttributeWithoutSynchronization(SVGNames::contentStyleTypeAttr, type);
132 }
133
134 Ref<SVGRect> SVGSVGElement::viewport() const
135 {
136     // FIXME: Not implemented.
137     return SVGRect::create();
138 }
139
140 float SVGSVGElement::pixelUnitToMillimeterX() const
141 {
142     // There are 25.4 millimeters in an inch.
143     return 25.4f / cssPixelsPerInch;
144 }
145
146 float SVGSVGElement::pixelUnitToMillimeterY() const
147 {
148     // There are 25.4 millimeters in an inch.
149     return 25.4f / cssPixelsPerInch;
150 }
151
152 float SVGSVGElement::screenPixelToMillimeterX() const
153 {
154     return pixelUnitToMillimeterX();
155 }
156
157 float SVGSVGElement::screenPixelToMillimeterY() const
158 {
159     return pixelUnitToMillimeterY();
160 }
161
162 SVGViewSpec& SVGSVGElement::currentView()
163 {
164     if (!m_viewSpec)
165         m_viewSpec = SVGViewSpec::create(*this);
166     return *m_viewSpec;
167 }
168
169 Frame* SVGSVGElement::frameForCurrentScale() const
170 {
171     // The behavior of currentScale() is undefined when we're dealing with non-standalone SVG documents.
172     // If the document is embedded, the scaling is handled by the host renderer.
173     if (!isConnected() || !isOutermostSVGSVGElement())
174         return nullptr;
175     Frame* frame = document().frame();
176     return frame && frame->isMainFrame() ? frame : nullptr;
177 }
178
179 float SVGSVGElement::currentScale() const
180 {
181     // When asking from inside an embedded SVG document, a scale value of 1 seems reasonable, as it doesn't
182     // know anything about the parent scale.
183     Frame* frame = frameForCurrentScale();
184     return frame ? frame->pageZoomFactor() : 1;
185 }
186
187 void SVGSVGElement::setCurrentScale(float scale)
188 {
189     if (Frame* frame = frameForCurrentScale())
190         frame->setPageZoomFactor(scale);
191 }
192
193 Ref<SVGPoint> SVGSVGElement::currentTranslate()
194 {
195     return SVGStaticPropertyTearOff<SVGSVGElement, SVGPoint>::create(*this, m_currentTranslate, &SVGSVGElement::updateCurrentTranslate);
196 }
197
198 void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation)
199 {
200     if (m_currentTranslate == translation)
201         return;
202     m_currentTranslate = translation;
203     updateCurrentTranslate();
204 }
205
206 void SVGSVGElement::updateCurrentTranslate()
207 {
208     if (RenderObject* object = renderer())
209         object->setNeedsLayout();
210     if (parentNode() == &document() && document().renderView())
211         document().renderView()->repaint();
212 }
213
214 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
215 {
216     if (!nearestViewportElement()) {
217         // For these events, the outermost <svg> element works like a <body> element does,
218         // setting certain event handlers directly on the window object.
219         if (name == HTMLNames::onunloadAttr) {
220             document().setWindowAttributeEventListener(eventNames().unloadEvent, name, value, mainThreadNormalWorld());
221             return;
222         }
223         if (name == HTMLNames::onresizeAttr) {
224             document().setWindowAttributeEventListener(eventNames().resizeEvent, name, value, mainThreadNormalWorld());
225             return;
226         }
227         if (name == HTMLNames::onscrollAttr) {
228             document().setWindowAttributeEventListener(eventNames().scrollEvent, name, value, mainThreadNormalWorld());
229             return;
230         }
231         if (name == SVGNames::onzoomAttr) {
232             document().setWindowAttributeEventListener(eventNames().zoomEvent, name, value, mainThreadNormalWorld());
233             return;
234         }
235     }
236
237     // For these events, any <svg> element works like a <body> element does,
238     // setting certain event handlers directly on the window object.
239     // FIXME: Why different from the events above that work only on the outermost <svg> element?
240     if (name == HTMLNames::onabortAttr) {
241         document().setWindowAttributeEventListener(eventNames().abortEvent, name, value, mainThreadNormalWorld());
242         return;
243     }
244     if (name == HTMLNames::onerrorAttr) {
245         document().setWindowAttributeEventListener(eventNames().errorEvent, name, value, mainThreadNormalWorld());
246         return;
247     }
248
249     SVGParsingError parseError = NoError;
250
251     if (name == SVGNames::xAttr)
252         setXBaseValue(SVGLengthValue::construct(LengthModeWidth, value, parseError));
253     else if (name == SVGNames::yAttr)
254         setYBaseValue(SVGLengthValue::construct(LengthModeHeight, value, parseError));
255     else if (name == SVGNames::widthAttr) {
256         auto length = SVGLengthValue::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths);
257         if (parseError != NoError || value.isEmpty()) {
258             // FIXME: This is definitely the correct behavior for a missing/removed attribute.
259             // Not sure it's correct for the empty string or for something that can't be parsed.
260             length = SVGLengthValue(LengthModeWidth, ASCIILiteral("100%"));
261         }
262         setWidthBaseValue(length);
263     } else if (name == SVGNames::heightAttr) {
264         auto length = SVGLengthValue::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths);
265         if (parseError != NoError || value.isEmpty()) {
266             // FIXME: This is definitely the correct behavior for a removed attribute.
267             // Not sure it's correct for the empty string or for something that can't be parsed.
268             length = SVGLengthValue(LengthModeHeight, ASCIILiteral("100%"));
269         }
270         setHeightBaseValue(length);
271     }
272
273     reportAttributeParsingError(parseError, name, value);
274
275     SVGExternalResourcesRequired::parseAttribute(name, value);
276     SVGFitToViewBox::parseAttribute(this, name, value);
277     SVGZoomAndPan::parseAttribute(*this, name, value);
278     SVGGraphicsElement::parseAttribute(name, value);
279 }
280
281 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
282 {
283     bool updateRelativeLengthsOrViewBox = false;
284     if (attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr) {
285         invalidateSVGPresentationAttributeStyle();
286         updateRelativeLengthsOrViewBox = true;
287     }
288
289     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
290         updateRelativeLengthsOrViewBox = true; 
291         if (auto* renderer = this->renderer())
292             renderer->setNeedsTransformUpdate();
293     }
294
295     InstanceInvalidationGuard guard(*this);
296
297     if (updateRelativeLengthsOrViewBox
298         || SVGLangSpace::isKnownAttribute(attrName)
299         || SVGExternalResourcesRequired::isKnownAttribute(attrName)
300         || SVGZoomAndPan::isKnownAttribute(attrName)) {
301         if (auto renderer = this->renderer())
302             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
303         return;
304     }
305
306     SVGGraphicsElement::svgAttributeChanged(attrName);
307 }
308
309 unsigned SVGSVGElement::suspendRedraw(unsigned)
310 {
311     return 0;
312 }
313
314 void SVGSVGElement::unsuspendRedraw(unsigned)
315 {
316 }
317
318 void SVGSVGElement::unsuspendRedrawAll()
319 {
320 }
321
322 void SVGSVGElement::forceRedraw()
323 {
324 }
325
326 Ref<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(SVGRect& rect, SVGElement* referenceElement, bool (*checkFunction)(SVGElement&, SVGRect&))
327 {
328     Vector<Ref<Element>> elements;
329     for (auto& element : descendantsOfType<SVGElement>(referenceElement ? *referenceElement : *this)) {
330         if (checkFunction(element, rect))
331             elements.append(element);
332     }
333     return StaticElementList::create(WTFMove(elements));
334 }
335
336 static bool checkIntersectionWithoutUpdatingLayout(SVGElement& element, SVGRect& rect)
337 {
338     return RenderSVGModelObject::checkIntersection(element.renderer(), rect.propertyReference());
339 }
340     
341 static bool checkEnclosureWithoutUpdatingLayout(SVGElement& element, SVGRect& rect)
342 {
343     return RenderSVGModelObject::checkEnclosure(element.renderer(), rect.propertyReference());
344 }
345
346 Ref<NodeList> SVGSVGElement::getIntersectionList(SVGRect& rect, SVGElement* referenceElement)
347 {
348     document().updateLayoutIgnorePendingStylesheets();
349     return collectIntersectionOrEnclosureList(rect, referenceElement, checkIntersectionWithoutUpdatingLayout);
350 }
351
352 Ref<NodeList> SVGSVGElement::getEnclosureList(SVGRect& rect, SVGElement* referenceElement)
353 {
354     document().updateLayoutIgnorePendingStylesheets();
355     return collectIntersectionOrEnclosureList(rect, referenceElement, checkEnclosureWithoutUpdatingLayout);
356 }
357
358 bool SVGSVGElement::checkIntersection(RefPtr<SVGElement>&& element, SVGRect& rect)
359 {
360     if (!element)
361         return false;
362     element->document().updateLayoutIgnorePendingStylesheets();
363     return checkIntersectionWithoutUpdatingLayout(*element, rect);
364 }
365
366 bool SVGSVGElement::checkEnclosure(RefPtr<SVGElement>&& element, SVGRect& rect)
367 {
368     if (!element)
369         return false;
370     element->document().updateLayoutIgnorePendingStylesheets();
371     return checkEnclosureWithoutUpdatingLayout(*element, rect);
372 }
373
374 void SVGSVGElement::deselectAll()
375 {
376     if (Frame* frame = document().frame())
377         frame->selection().clear();
378 }
379
380 Ref<SVGNumber> SVGSVGElement::createSVGNumber()
381 {
382     return SVGNumber::create();
383 }
384
385 Ref<SVGLength> SVGSVGElement::createSVGLength()
386 {
387     return SVGLength::create();
388 }
389
390 Ref<SVGAngle> SVGSVGElement::createSVGAngle()
391 {
392     return SVGAngle::create();
393 }
394
395 Ref<SVGPoint> SVGSVGElement::createSVGPoint()
396 {
397     return SVGPoint::create();
398 }
399
400 Ref<SVGMatrix> SVGSVGElement::createSVGMatrix()
401 {
402     return SVGMatrix::create();
403 }
404
405 Ref<SVGRect> SVGSVGElement::createSVGRect()
406 {
407     return SVGRect::create();
408 }
409
410 Ref<SVGTransform> SVGSVGElement::createSVGTransform()
411 {
412     return SVGTransform::create(SVGTransformValue { SVGTransformValue::SVG_TRANSFORM_MATRIX });
413 }
414
415 Ref<SVGTransform> SVGSVGElement::createSVGTransformFromMatrix(SVGMatrix& matrix)
416 {
417     return SVGTransform::create(SVGTransformValue { matrix.propertyReference() });
418 }
419
420 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const
421 {
422     AffineTransform viewBoxTransform;
423     if (!hasEmptyViewBox()) {
424         FloatSize size = currentViewportSize();
425         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
426     }
427
428     AffineTransform transform;
429     if (!isOutermostSVGSVGElement()) {
430         SVGLengthContext lengthContext(this);
431         transform.translate(x().value(lengthContext), y().value(lengthContext));
432     } else if (mode == SVGLocatable::ScreenScope) {
433         if (auto* renderer = this->renderer()) {
434             FloatPoint location;
435             float zoomFactor = 1;
436
437             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform 
438             // to map an element from SVG viewport coordinates to CSS box coordinates.
439             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
440             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
441             if (is<RenderSVGRoot>(*renderer)) {
442                 location = downcast<RenderSVGRoot>(*renderer).localToBorderBoxTransform().mapPoint(location);
443                 zoomFactor = 1 / renderer->style().effectiveZoom();
444             }
445
446             // Translate in our CSS parent coordinate space
447             // FIXME: This doesn't work correctly with CSS transforms.
448             location = renderer->localToAbsolute(location, UseTransforms);
449             location.scale(zoomFactor);
450
451             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
452             // so we have to subtract it here (original cause of bug #27183)
453             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
454
455             // Respect scroll offset.
456             if (FrameView* view = document().view()) {
457                 LayoutPoint scrollPosition = view->scrollPosition();
458                 scrollPosition.scale(zoomFactor);
459                 transform.translate(-scrollPosition);
460             }
461         }
462     }
463
464     return transform.multiply(viewBoxTransform);
465 }
466
467 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
468 {
469     if (!isValid())
470         return false;
471     // FIXME: We should respect display: none on the documentElement svg element
472     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
473     // they should instead depend on the RenderView.
474     // https://bugs.webkit.org/show_bug.cgi?id=103493
475     if (document().documentElement() == this)
476         return true;
477     return StyledElement::rendererIsNeeded(style);
478 }
479
480 RenderPtr<RenderElement> SVGSVGElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
481 {
482     if (isOutermostSVGSVGElement())
483         return createRenderer<RenderSVGRoot>(*this, WTFMove(style));
484     return createRenderer<RenderSVGViewportContainer>(*this, WTFMove(style));
485 }
486
487 Node::InsertedIntoAncestorResult SVGSVGElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
488 {
489     if (insertionType.connectedToDocument) {
490         document().accessSVGExtensions().addTimeContainer(this);
491         if (!document().accessSVGExtensions().areAnimationsPaused())
492             unpauseAnimations();
493
494         // Animations are started at the end of document parsing and after firing the load event,
495         // but if we miss that train (deferred programmatic element insertion for example) we need
496         // to initialize the time container here.
497         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !m_timeContainer->isStarted())
498             m_timeContainer->begin();
499     }
500     return SVGGraphicsElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
501 }
502
503 void SVGSVGElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
504 {
505     if (removalType.disconnectedFromDocument) {
506         document().accessSVGExtensions().removeTimeContainer(this);
507         pauseAnimations();
508     }
509     SVGGraphicsElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
510 }
511
512 void SVGSVGElement::pauseAnimations()
513 {
514     if (!m_timeContainer->isPaused())
515         m_timeContainer->pause();
516 }
517
518 void SVGSVGElement::unpauseAnimations()
519 {
520     if (m_timeContainer->isPaused())
521         m_timeContainer->resume();
522 }
523
524 bool SVGSVGElement::animationsPaused() const
525 {
526     return m_timeContainer->isPaused();
527 }
528
529 bool SVGSVGElement::hasActiveAnimation() const
530 {
531     return m_timeContainer->isActive();
532 }
533
534 float SVGSVGElement::getCurrentTime() const
535 {
536     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
537 }
538
539 void SVGSVGElement::setCurrentTime(float seconds)
540 {
541     if (!std::isfinite(seconds))
542         return;
543     m_timeContainer->setElapsed(std::max(seconds, 0.0f));
544 }
545
546 bool SVGSVGElement::selfHasRelativeLengths() const
547 {
548     return x().isRelative()
549         || y().isRelative()
550         || width().isRelative()
551         || height().isRelative()
552         || hasAttribute(SVGNames::viewBoxAttr);
553 }
554
555 FloatRect SVGSVGElement::currentViewBoxRect() const
556 {
557     if (m_useCurrentView)
558         return m_viewSpec ? m_viewSpec->viewBox() : FloatRect();
559
560     FloatRect viewBox = this->viewBox();
561     if (!viewBox.isEmpty())
562         return viewBox;
563
564     if (!is<RenderSVGRoot>(renderer()))
565         return { };
566     if (!downcast<RenderSVGRoot>(*renderer()).isEmbeddedThroughSVGImage())
567         return { };
568
569     Length intrinsicWidth = this->intrinsicWidth();
570     Length intrinsicHeight = this->intrinsicHeight();
571     if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
572         return { };
573
574     // If no viewBox is specified but non-relative width/height values, then we
575     // should always synthesize a viewBox if we're embedded through a SVGImage.    
576     return { 0, 0, floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0) };
577 }
578
579 FloatSize SVGSVGElement::currentViewportSize() const
580 {
581     FloatSize viewportSize;
582
583     if (renderer()) {
584         if (is<RenderSVGRoot>(*renderer())) {
585             auto& root = downcast<RenderSVGRoot>(*renderer());
586             viewportSize = root.contentBoxRect().size() / root.style().effectiveZoom();
587         } else
588             viewportSize = downcast<RenderSVGViewportContainer>(*renderer()).viewport().size();
589     }
590
591     if (!viewportSize.isEmpty())
592         return viewportSize;
593
594     if (!(hasIntrinsicWidth() && hasIntrinsicHeight()))
595         return { };
596
597     return FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0));
598 }
599
600 bool SVGSVGElement::hasIntrinsicWidth() const
601 {
602     return width().unitType() != LengthTypePercentage;
603 }
604
605 bool SVGSVGElement::hasIntrinsicHeight() const
606 {
607     return height().unitType() != LengthTypePercentage;
608 }
609
610 Length SVGSVGElement::intrinsicWidth() const
611 {
612     if (width().unitType() == LengthTypePercentage)
613         return Length(0, Fixed);
614
615     SVGLengthContext lengthContext(this);
616     return Length(width().value(lengthContext), Fixed);
617 }
618
619 Length SVGSVGElement::intrinsicHeight() const
620 {
621     if (height().unitType() == LengthTypePercentage)
622         return Length(0, Fixed);
623
624     SVGLengthContext lengthContext(this);
625     return Length(height().value(lengthContext), Fixed);
626 }
627
628 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
629 {
630     if (!m_useCurrentView || !m_viewSpec)
631         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight);
632
633     AffineTransform transform = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio(), viewWidth, viewHeight);
634     m_viewSpec->transformBaseValue().concatenate(transform);
635     return transform;
636 }
637
638 void SVGSVGElement::scrollToAnchor(const String& fragmentIdentifier, Element* anchorNode)
639 {
640     auto renderer = this->renderer();
641     SVGViewSpec* view = m_viewSpec.get();
642     if (view)
643         view->reset();
644
645     bool hadUseCurrentView = m_useCurrentView;
646     m_useCurrentView = false;
647
648     if (fragmentIdentifier.startsWith("xpointer(")) {
649         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
650         if (renderer && hadUseCurrentView)
651             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
652         return;
653     }
654
655     if (fragmentIdentifier.startsWith("svgView(")) {
656         if (!view)
657             view = &currentView(); // Create the SVGViewSpec.
658         if (view->parseViewSpec(fragmentIdentifier))
659             m_useCurrentView = true;
660         else
661             view->reset();
662         if (renderer && (hadUseCurrentView || m_useCurrentView))
663             RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
664         return;
665     }
666
667     // Spec: If the SVG fragment identifier addresses a "view" element within an SVG document (e.g., MyDrawing.svg#MyView
668     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor "svg" element is displayed in the viewport.
669     // Any view specification attributes included on the given "view" element override the corresponding view specification
670     // attributes on the closest ancestor "svg" element.
671     if (is<SVGViewElement>(anchorNode)) {
672         auto& viewElement = downcast<SVGViewElement>(*anchorNode);
673         auto* viewportElement = SVGLocatable::nearestViewportElement(&viewElement);
674         if (is<SVGSVGElement>(viewportElement)) {
675             auto& element = downcast<SVGSVGElement>(*viewportElement);
676             element.inheritViewAttributes(viewElement);
677             if (auto* renderer = element.renderer())
678                 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
679         }
680         return;
681     }
682
683     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
684     // FIXME: We need to actually "highlight" the viewTarget(s).
685 }
686
687 void SVGSVGElement::inheritViewAttributes(const SVGViewElement& viewElement)
688 {
689     SVGViewSpec& view = currentView();
690     m_useCurrentView = true;
691
692     if (viewElement.hasAttribute(SVGNames::viewBoxAttr))
693         view.setViewBoxBaseValue(viewElement.viewBox());
694     else
695         view.setViewBoxBaseValue(viewBox());
696
697     if (viewElement.hasAttribute(SVGNames::preserveAspectRatioAttr))
698         view.setPreserveAspectRatioBaseValue(viewElement.preserveAspectRatioBaseValue());
699     else
700         view.setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue());
701
702     if (viewElement.hasAttribute(SVGNames::zoomAndPanAttr))
703         view.setZoomAndPanBaseValue(viewElement.zoomAndPan());
704     else
705         view.setZoomAndPanBaseValue(zoomAndPan());
706 }
707
708 void SVGSVGElement::prepareForDocumentSuspension()
709 {
710     pauseAnimations();
711 }
712
713 void SVGSVGElement::resumeFromDocumentSuspension()
714 {
715     unpauseAnimations();
716 }
717
718 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
719 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
720 Element* SVGSVGElement::getElementById(const AtomicString& id)
721 {
722     if (id.isNull())
723         return nullptr;
724
725     Element* element = treeScope().getElementById(id);
726     if (element && element->isDescendantOf(*this))
727         return element;
728     if (treeScope().containsMultipleElementsWithId(id)) {
729         for (auto* element : *treeScope().getAllElementsById(id)) {
730             if (element->isDescendantOf(*this))
731                 return element;
732         }
733     }
734     return nullptr;
735 }
736
737 bool SVGSVGElement::isValid() const
738 {
739     return SVGTests::isValid();
740 }
741
742 }