0470a3bab43ea5462b141e49a7b65f6fd91f8b5c
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 Google, Inc.
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
25 #include "core/svg/SVGSVGElement.h"
26
27 #include "bindings/core/v8/ScriptEventListener.h"
28 #include "core/HTMLNames.h"
29 #include "core/SVGNames.h"
30 #include "core/css/CSSHelper.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/ElementTraversal.h"
33 #include "core/dom/StaticNodeList.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/events/EventListener.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/page/FrameTree.h"
38 #include "core/frame/FrameView.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/rendering/RenderObject.h"
41 #include "core/rendering/svg/RenderSVGModelObject.h"
42 #include "core/rendering/svg/RenderSVGRoot.h"
43 #include "core/rendering/svg/RenderSVGViewportContainer.h"
44 #include "core/svg/SVGAngleTearOff.h"
45 #include "core/svg/SVGNumberTearOff.h"
46 #include "core/svg/SVGPreserveAspectRatio.h"
47 #include "core/svg/SVGRectTearOff.h"
48 #include "core/svg/SVGTransform.h"
49 #include "core/svg/SVGTransformList.h"
50 #include "core/svg/SVGTransformTearOff.h"
51 #include "core/svg/SVGViewElement.h"
52 #include "core/svg/SVGViewSpec.h"
53 #include "core/svg/animation/SMILTimeContainer.h"
54 #include "platform/FloatConversion.h"
55 #include "platform/LengthFunctions.h"
56 #include "platform/geometry/FloatRect.h"
57 #include "platform/transforms/AffineTransform.h"
58 #include "wtf/StdLibExtras.h"
59
60 namespace blink {
61
62 inline SVGSVGElement::SVGSVGElement(Document& doc)
63     : SVGGraphicsElement(SVGNames::svgTag, doc)
64     , SVGFitToViewBox(this)
65     , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
66     , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
67     , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
68     , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
69     , m_useCurrentView(false)
70     , m_timeContainer(SMILTimeContainer::create(*this))
71     , m_translation(SVGPoint::create())
72 {
73     m_width->setDefaultValueAsString("100%");
74     m_height->setDefaultValueAsString("100%");
75
76     addToPropertyMap(m_x);
77     addToPropertyMap(m_y);
78     addToPropertyMap(m_width);
79     addToPropertyMap(m_height);
80
81     UseCounter::count(doc, UseCounter::SVGSVGElement);
82 }
83
84 DEFINE_NODE_FACTORY(SVGSVGElement)
85
86 SVGSVGElement::~SVGSVGElement()
87 {
88 #if !ENABLE(OILPAN)
89     if (m_viewSpec)
90         m_viewSpec->detachContextElement();
91
92     // There are cases where removedFromDocument() is not called.
93     // see ContainerNode::removeAllChildren, called by its destructor.
94     // With Oilpan, either removedFrom is called or the document
95     // is dead as well and there is no reason to clear the extensions.
96     document().accessSVGExtensions().removeTimeContainer(this);
97
98     ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this));
99 #endif
100 }
101
102 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
103 {
104     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
105     // As we have no test coverage for this, we're going to disable it completly for now.
106     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
107 }
108
109 float SVGSVGElement::pixelUnitToMillimeterX() const
110 {
111     return 1 / cssPixelsPerMillimeter;
112 }
113
114 float SVGSVGElement::pixelUnitToMillimeterY() const
115 {
116     return 1 / cssPixelsPerMillimeter;
117 }
118
119 float SVGSVGElement::screenPixelToMillimeterX() const
120 {
121     return pixelUnitToMillimeterX();
122 }
123
124 float SVGSVGElement::screenPixelToMillimeterY() const
125 {
126     return pixelUnitToMillimeterY();
127 }
128
129 SVGViewSpec* SVGSVGElement::currentView()
130 {
131     if (!m_viewSpec)
132         m_viewSpec = SVGViewSpec::create(this);
133     return m_viewSpec.get();
134 }
135
136 float SVGSVGElement::currentScale() const
137 {
138     if (!inDocument() || !isOutermostSVGSVGElement())
139         return 1;
140
141     LocalFrame* frame = document().frame();
142     if (!frame)
143         return 1;
144
145     const FrameTree& frameTree = frame->tree();
146
147     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
148     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
149     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
150     return frameTree.parent() ? 1 : frame->pageZoomFactor();
151 }
152
153 void SVGSVGElement::setCurrentScale(float scale)
154 {
155     if (!inDocument() || !isOutermostSVGSVGElement())
156         return;
157
158     LocalFrame* frame = document().frame();
159     if (!frame)
160         return;
161
162     const FrameTree& frameTree = frame->tree();
163
164     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
165     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
166     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
167     if (frameTree.parent())
168         return;
169
170     frame->setPageZoomFactor(scale);
171 }
172
173 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
174 public:
175     static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
176     {
177         return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
178     }
179
180     virtual void commitChange() override
181     {
182         ASSERT(contextElement());
183         toSVGSVGElement(contextElement())->updateCurrentTranslate();
184     }
185
186 private:
187     SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
188         : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
189     {
190     }
191 };
192
193 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
194 {
195     return SVGCurrentTranslateTearOff::create(this);
196 }
197
198 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
199 {
200     m_translation->setValue(point);
201     updateCurrentTranslate();
202 }
203
204 void SVGSVGElement::updateCurrentTranslate()
205 {
206     if (RenderObject* object = renderer())
207         object->setNeedsLayoutAndFullPaintInvalidation();
208 }
209
210 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
211 {
212     if (!nearestViewportElement()) {
213         bool setListener = true;
214
215         // Only handle events if we're the outermost <svg> element
216         if (name == HTMLNames::onunloadAttr)
217             document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
218         else if (name == HTMLNames::onresizeAttr)
219             document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
220         else if (name == HTMLNames::onscrollAttr)
221             document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
222         else if (name == SVGNames::onzoomAttr)
223             document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
224         else
225             setListener = false;
226
227         if (setListener)
228             return;
229     }
230
231     if (name == HTMLNames::onabortAttr) {
232         document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
233     } else if (name == HTMLNames::onerrorAttr) {
234         document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
235     } else if (SVGZoomAndPan::parseAttribute(name, value)) {
236     } else {
237         parseAttributeNew(name, value);
238     }
239 }
240
241 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const
242 {
243     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr))
244         return true;
245     return SVGGraphicsElement::isPresentationAttribute(name);
246 }
247
248 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
249 {
250     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) {
251         RefPtr<SVGLength> length = SVGLength::create(LengthModeOther);
252         TrackExceptionState exceptionState;
253         length->setValueAsString(value, exceptionState);
254         if (!exceptionState.hadException()) {
255             if (name == SVGNames::widthAttr)
256                 addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value);
257             else if (name == SVGNames::heightAttr)
258                 addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value);
259         }
260     } else {
261         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
262     }
263 }
264
265 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
266 {
267     bool updateRelativeLengthsOrViewBox = false;
268     bool widthChanged = attrName == SVGNames::widthAttr;
269     bool heightChanged = attrName == SVGNames::heightAttr;
270     if (widthChanged || heightChanged
271         || attrName == SVGNames::xAttr
272         || attrName == SVGNames::yAttr) {
273         updateRelativeLengthsOrViewBox = true;
274         updateRelativeLengthsInformation();
275         invalidateRelativeLengthClients();
276
277         // At the SVG/HTML boundary (aka RenderSVGRoot), the width and
278         // height attributes can affect the replaced size so we need
279         // to mark it for updating.
280         //
281         // FIXME: For width/height animated as XML attributes on SVG
282         // roots, there is an attribute synchronization missing. See
283         // http://crbug.com/364807
284         if (widthChanged || heightChanged) {
285             RenderObject* renderObject = renderer();
286             if (renderObject && renderObject->isSVGRoot()) {
287                 invalidateSVGPresentationAttributeStyle();
288                 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SVGContainerSizeChange));
289             }
290         }
291     }
292
293     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
294         updateRelativeLengthsOrViewBox = true;
295         if (RenderObject* object = renderer())
296             object->setNeedsTransformUpdate();
297     }
298
299     SVGElement::InvalidationGuard invalidationGuard(this);
300
301     if (updateRelativeLengthsOrViewBox
302         || SVGZoomAndPan::isKnownAttribute(attrName)) {
303         if (renderer())
304             markForLayoutAndParentResourceInvalidation(renderer());
305         return;
306     }
307
308     SVGGraphicsElement::svgAttributeChanged(attrName);
309 }
310
311 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()).
312 static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2)
313 {
314     if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0)
315         return false;
316
317     return r1.x() < r2.maxX() && r2.x() < r1.maxX()
318         && r1.y() < r2.maxY() && r2.y() < r1.maxY();
319 }
320
321 // One of the element types that can cause graphics to be drawn onto the target canvas.
322 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use.
323 static bool isIntersectionOrEnclosureTarget(RenderObject* renderer)
324 {
325     return renderer->isSVGShape()
326         || renderer->isSVGText()
327         || renderer->isSVGImage()
328         || isSVGUseElement(*renderer->node());
329 }
330
331 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect,
332     CheckIntersectionOrEnclosure mode) const
333 {
334     RenderObject* renderer = element.renderer();
335     ASSERT(!renderer || renderer->style());
336     if (!renderer || renderer->style()->pointerEvents() == PE_NONE)
337         return false;
338
339     if (!isIntersectionOrEnclosureTarget(renderer))
340         return false;
341
342     AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this);
343     FloatRect mappedRepaintRect = ctm.mapRect(renderer->paintInvalidationRectInLocalCoordinates());
344
345     bool result = false;
346     switch (mode) {
347     case CheckIntersection:
348         result = intersectsAllowingEmpty(rect, mappedRepaintRect);
349         break;
350     case CheckEnclosure:
351         result = rect.contains(mappedRepaintRect);
352         break;
353     default:
354         ASSERT_NOT_REACHED();
355         break;
356     }
357
358     return result;
359 }
360
361 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect,
362     SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const
363 {
364     WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
365
366     const SVGElement* root = this;
367     if (referenceElement) {
368         // Only the common subtree needs to be traversed.
369         if (contains(referenceElement)) {
370             root = referenceElement;
371         } else if (!isDescendantOf(referenceElement)) {
372             // No common subtree.
373             return StaticNodeList::adopt(nodes);
374         }
375     }
376
377     for (SVGGraphicsElement& element : Traversal<SVGGraphicsElement>::descendantsOf(*root)) {
378         if (checkIntersectionOrEnclosure(element, rect, mode))
379             nodes.append(&element);
380     }
381
382     return StaticNodeList::adopt(nodes);
383 }
384
385 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
386 {
387     document().updateLayoutIgnorePendingStylesheets();
388
389     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection);
390 }
391
392 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
393 {
394     document().updateLayoutIgnorePendingStylesheets();
395
396     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure);
397 }
398
399 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
400 {
401     ASSERT(element);
402     document().updateLayoutIgnorePendingStylesheets();
403
404     return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection);
405 }
406
407 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
408 {
409     ASSERT(element);
410     document().updateLayoutIgnorePendingStylesheets();
411
412     return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure);
413 }
414
415 void SVGSVGElement::deselectAll()
416 {
417     if (LocalFrame* frame = document().frame())
418         frame->selection().clear();
419 }
420
421 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
422 {
423     return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
424 }
425
426 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
427 {
428     return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
429 }
430
431 PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle()
432 {
433     return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal);
434 }
435
436 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
437 {
438     return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
439 }
440
441 PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix()
442 {
443     return SVGMatrixTearOff::create(AffineTransform());
444 }
445
446 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
447 {
448     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
449 }
450
451 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform()
452 {
453     return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal);
454 }
455
456 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)
457 {
458     return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal);
459 }
460
461 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
462 {
463     AffineTransform viewBoxTransform;
464     if (!hasEmptyViewBox()) {
465         FloatSize size = currentViewportSize();
466         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
467     }
468
469     AffineTransform transform;
470     if (!isOutermostSVGSVGElement()) {
471         SVGLengthContext lengthContext(this);
472         transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
473     } else if (mode == SVGElement::ScreenScope) {
474         if (RenderObject* renderer = this->renderer()) {
475             FloatPoint location;
476             float zoomFactor = 1;
477
478             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
479             // to map an element from SVG viewport coordinates to CSS box coordinates.
480             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
481             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
482             if (renderer->isSVGRoot()) {
483                 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
484                 zoomFactor = 1 / renderer->style()->effectiveZoom();
485             }
486
487             // Translate in our CSS parent coordinate space
488             // FIXME: This doesn't work correctly with CSS transforms.
489             location = renderer->localToAbsolute(location, UseTransforms);
490             location.scale(zoomFactor, zoomFactor);
491
492             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
493             // so we have to subtract it here (original cause of bug #27183)
494             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
495
496             // Respect scroll offset.
497             if (FrameView* view = document().view()) {
498                 LayoutSize scrollOffset = view->scrollOffset();
499                 scrollOffset.scale(zoomFactor);
500                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
501             }
502         }
503     }
504
505     return transform.multiply(viewBoxTransform);
506 }
507
508 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
509 {
510     // FIXME: We should respect display: none on the documentElement svg element
511     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
512     // they should instead depend on the RenderView.
513     // https://bugs.webkit.org/show_bug.cgi?id=103493
514     if (document().documentElement() == this)
515         return true;
516     return Element::rendererIsNeeded(style);
517 }
518
519 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
520 {
521     if (isOutermostSVGSVGElement())
522         return new RenderSVGRoot(this);
523
524     return new RenderSVGViewportContainer(this);
525 }
526
527 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
528 {
529     if (rootParent->inDocument()) {
530         UseCounter::count(document(), UseCounter::SVGSVGElementInDocument);
531         if (rootParent->document().isXMLDocument())
532             UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument);
533
534         document().accessSVGExtensions().addTimeContainer(this);
535
536         // Animations are started at the end of document parsing and after firing the load event,
537         // but if we miss that train (deferred programmatic element insertion for example) we need
538         // to initialize the time container here.
539         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
540             timeContainer()->begin();
541     }
542     return SVGGraphicsElement::insertedInto(rootParent);
543 }
544
545 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
546 {
547     if (rootParent->inDocument()) {
548         SVGDocumentExtensions& svgExtensions = document().accessSVGExtensions();
549         svgExtensions.removeTimeContainer(this);
550         svgExtensions.removeSVGRootWithRelativeLengthDescendents(this);
551     }
552
553     SVGGraphicsElement::removedFrom(rootParent);
554 }
555
556 void SVGSVGElement::pauseAnimations()
557 {
558     if (!m_timeContainer->isPaused())
559         m_timeContainer->pause();
560 }
561
562 void SVGSVGElement::unpauseAnimations()
563 {
564     if (m_timeContainer->isPaused())
565         m_timeContainer->resume();
566 }
567
568 bool SVGSVGElement::animationsPaused() const
569 {
570     return m_timeContainer->isPaused();
571 }
572
573 float SVGSVGElement::getCurrentTime() const
574 {
575     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
576 }
577
578 void SVGSVGElement::setCurrentTime(float seconds)
579 {
580     if (std::isnan(seconds))
581         return;
582     seconds = max(seconds, 0.0f);
583     m_timeContainer->setElapsed(seconds);
584 }
585
586 bool SVGSVGElement::selfHasRelativeLengths() const
587 {
588     return m_x->currentValue()->isRelative()
589         || m_y->currentValue()->isRelative()
590         || m_width->currentValue()->isRelative()
591         || m_height->currentValue()->isRelative()
592         || hasAttribute(SVGNames::viewBoxAttr);
593 }
594
595 FloatRect SVGSVGElement::currentViewBoxRect() const
596 {
597     if (m_useCurrentView)
598         return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
599
600     FloatRect useViewBox = viewBox()->currentValue()->value();
601     if (!useViewBox.isEmpty())
602         return useViewBox;
603     if (!renderer() || !renderer()->isSVGRoot())
604         return FloatRect();
605     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
606         return FloatRect();
607
608     // If no viewBox is specified but non-relative width/height values, then we
609     // should always synthesize a viewBox if we're embedded through a SVGImage.
610     return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)));
611 }
612
613 FloatSize SVGSVGElement::currentViewportSize() const
614 {
615     if (!renderer())
616         return FloatSize();
617
618     if (renderer()->isSVGRoot()) {
619         LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
620         return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
621     }
622
623     FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
624     return FloatSize(viewportRect.width(), viewportRect.height());
625 }
626
627 bool SVGSVGElement::hasIntrinsicWidth() const
628 {
629     return width()->currentValue()->unitType() != LengthTypePercentage;
630 }
631
632 bool SVGSVGElement::hasIntrinsicHeight() const
633 {
634     return height()->currentValue()->unitType() != LengthTypePercentage;
635 }
636
637 Length SVGSVGElement::intrinsicWidth() const
638 {
639     if (width()->currentValue()->unitType() == LengthTypePercentage)
640         return Length(0, Fixed);
641
642     SVGLengthContext lengthContext(this);
643     return Length(width()->currentValue()->value(lengthContext), Fixed);
644 }
645
646 Length SVGSVGElement::intrinsicHeight() const
647 {
648     if (height()->currentValue()->unitType() == LengthTypePercentage)
649         return Length(0, Fixed);
650
651     SVGLengthContext lengthContext(this);
652     return Length(height()->currentValue()->value(lengthContext), Fixed);
653 }
654
655 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
656 {
657     if (!m_useCurrentView || !m_viewSpec)
658         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
659
660     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
661     RefPtr<SVGTransformList> transformList = m_viewSpec->transform();
662     if (transformList->isEmpty())
663         return ctm;
664
665     AffineTransform transform;
666     if (transformList->concatenate(transform))
667         ctm *= transform;
668
669     return ctm;
670 }
671
672 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
673 {
674     RenderObject* renderer = this->renderer();
675     SVGViewSpec* view = m_viewSpec.get();
676     if (view)
677         view->reset();
678
679     bool hadUseCurrentView = m_useCurrentView;
680     m_useCurrentView = false;
681
682     if (fragmentIdentifier.startsWith("xpointer(")) {
683         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
684         if (renderer && hadUseCurrentView)
685             markForLayoutAndParentResourceInvalidation(renderer);
686         return;
687     }
688
689     if (fragmentIdentifier.startsWith("svgView(")) {
690         if (!view)
691             view = currentView(); // Create the SVGViewSpec.
692
693         if (view->parseViewSpec(fragmentIdentifier))
694             m_useCurrentView = true;
695         else
696             view->reset();
697
698         if (renderer && (hadUseCurrentView || m_useCurrentView))
699             markForLayoutAndParentResourceInvalidation(renderer);
700         return;
701     }
702
703     // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
704     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
705     // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
706     // attributes on the closest ancestor ‘svg’ element.
707     if (isSVGViewElement(anchorNode)) {
708         SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
709
710         if (SVGSVGElement* svg = viewElement.ownerSVGElement()) {
711             svg->inheritViewAttributes(&viewElement);
712
713             if (RenderObject* renderer = svg->renderer())
714                 markForLayoutAndParentResourceInvalidation(renderer);
715         }
716     }
717
718     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
719     // FIXME: We need to actually "highlight" the viewTarget(s).
720 }
721
722 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
723 {
724     SVGViewSpec* view = currentView();
725     m_useCurrentView = true;
726
727     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
728         view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
729     else
730         view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value());
731
732     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
733         view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
734         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
735     } else {
736         view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align());
737         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice());
738     }
739
740     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
741         view->setZoomAndPan(viewElement->zoomAndPan());
742     else
743         view->setZoomAndPan(zoomAndPan());
744 }
745
746 void SVGSVGElement::finishParsingChildren()
747 {
748     SVGGraphicsElement::finishParsingChildren();
749
750     // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
751     if (isOutermostSVGSVGElement())
752         return;
753
754     // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
755     // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
756     sendSVGLoadEventIfPossible();
757 }
758
759 void SVGSVGElement::trace(Visitor* visitor)
760 {
761     visitor->trace(m_timeContainer);
762     visitor->trace(m_viewSpec);
763     SVGGraphicsElement::trace(visitor);
764 }
765
766 } // namespace blink