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