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