Upstream version 5.34.92.0
[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  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #include "core/svg/SVGSVGElement.h"
25
26 #include "HTMLNames.h"
27 #include "SVGNames.h"
28 #include "bindings/v8/ScriptEventListener.h"
29 #include "core/css/CSSHelper.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/ElementTraversal.h"
32 #include "core/dom/NodeTraversal.h"
33 #include "core/dom/StaticNodeList.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/events/EventListener.h"
36 #include "core/events/ThreadLocalEventNames.h"
37 #include "core/frame/Frame.h"
38 #include "core/page/FrameTree.h"
39 #include "core/frame/FrameView.h"
40 #include "core/frame/UseCounter.h"
41 #include "core/rendering/RenderObject.h"
42 #include "core/rendering/RenderPart.h"
43 #include "core/rendering/svg/RenderSVGModelObject.h"
44 #include "core/rendering/svg/RenderSVGResource.h"
45 #include "core/rendering/svg/RenderSVGRoot.h"
46 #include "core/rendering/svg/RenderSVGViewportContainer.h"
47 #include "core/svg/SVGAngle.h"
48 #include "core/svg/SVGElementInstance.h"
49 #include "core/svg/SVGNumberTearOff.h"
50 #include "core/svg/SVGPreserveAspectRatio.h"
51 #include "core/svg/SVGRectTearOff.h"
52 #include "core/svg/SVGTransform.h"
53 #include "core/svg/SVGTransformList.h"
54 #include "core/svg/SVGViewElement.h"
55 #include "core/svg/SVGViewSpec.h"
56 #include "core/svg/animation/SMILTimeContainer.h"
57 #include "platform/FloatConversion.h"
58 #include "platform/LengthFunctions.h"
59 #include "platform/geometry/FloatRect.h"
60 #include "platform/transforms/AffineTransform.h"
61 #include "wtf/StdLibExtras.h"
62
63 namespace WebCore {
64
65 // Animated property definitions
66
67 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement)
68     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement)
69 END_REGISTER_ANIMATED_PROPERTIES
70
71 inline SVGSVGElement::SVGSVGElement(Document& doc)
72     : SVGGraphicsElement(SVGNames::svgTag, doc)
73     , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth)))
74     , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight)))
75     , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth)))
76     , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight)))
77     , m_viewBox(SVGAnimatedRect::create(this, SVGNames::viewBoxAttr))
78     , m_preserveAspectRatio(SVGAnimatedPreserveAspectRatio::create(this, SVGNames::preserveAspectRatioAttr, SVGPreserveAspectRatio::create()))
79     , m_useCurrentView(false)
80     , m_zoomAndPan(SVGZoomAndPanMagnify)
81     , m_timeContainer(SMILTimeContainer::create(this))
82     , m_translation(SVGPoint::create())
83 {
84     ScriptWrappable::init(this);
85
86     m_width->setDefaultValueAsString("100%");
87     m_height->setDefaultValueAsString("100%");
88
89     addToPropertyMap(m_x);
90     addToPropertyMap(m_y);
91     addToPropertyMap(m_width);
92     addToPropertyMap(m_height);
93     addToPropertyMap(m_viewBox);
94     addToPropertyMap(m_preserveAspectRatio);
95     registerAnimatedPropertiesForSVGSVGElement();
96
97     UseCounter::count(doc, UseCounter::SVGSVGElement);
98 }
99
100 PassRefPtr<SVGSVGElement> SVGSVGElement::create(Document& document)
101 {
102     return adoptRef(new SVGSVGElement(document));
103 }
104
105 SVGSVGElement::~SVGSVGElement()
106 {
107     if (m_viewSpec)
108         m_viewSpec->detachContextElement();
109
110     // There are cases where removedFromDocument() is not called.
111     // see ContainerNode::removeAllChildren, called by its destructor.
112     document().accessSVGExtensions()->removeTimeContainer(this);
113
114     ASSERT(inDocument() || !accessDocumentSVGExtensions()->isSVGRootWithRelativeLengthDescendents(this));
115 }
116
117 const AtomicString& SVGSVGElement::contentScriptType() const
118 {
119     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral));
120     const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr);
121     return n.isNull() ? defaultValue : n;
122 }
123
124 void SVGSVGElement::setContentScriptType(const AtomicString& type)
125 {
126     setAttribute(SVGNames::contentScriptTypeAttr, type);
127 }
128
129 const AtomicString& SVGSVGElement::contentStyleType() const
130 {
131     DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral));
132     const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr);
133     return n.isNull() ? defaultValue : n;
134 }
135
136 void SVGSVGElement::setContentStyleType(const AtomicString& type)
137 {
138     setAttribute(SVGNames::contentStyleTypeAttr, type);
139 }
140
141 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
142 {
143     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
144     // As we have no test coverage for this, we're going to disable it completly for now.
145     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
146 }
147
148 float SVGSVGElement::pixelUnitToMillimeterX() const
149 {
150     return 1 / cssPixelsPerMillimeter;
151 }
152
153 float SVGSVGElement::pixelUnitToMillimeterY() const
154 {
155     return 1 / cssPixelsPerMillimeter;
156 }
157
158 float SVGSVGElement::screenPixelToMillimeterX() const
159 {
160     return pixelUnitToMillimeterX();
161 }
162
163 float SVGSVGElement::screenPixelToMillimeterY() const
164 {
165     return pixelUnitToMillimeterY();
166 }
167
168 SVGViewSpec* SVGSVGElement::currentView()
169 {
170     if (!m_viewSpec)
171         m_viewSpec = SVGViewSpec::create(this);
172     return m_viewSpec.get();
173 }
174
175 float SVGSVGElement::currentScale() const
176 {
177     if (!inDocument() || !isOutermostSVGSVGElement())
178         return 1;
179
180     Frame* frame = document().frame();
181     if (!frame)
182         return 1;
183
184     const FrameTree& frameTree = frame->tree();
185
186     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
187     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
188     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
189     return frameTree.parent() ? 1 : frame->pageZoomFactor();
190 }
191
192 void SVGSVGElement::setCurrentScale(float scale)
193 {
194     if (!inDocument() || !isOutermostSVGSVGElement())
195         return;
196
197     Frame* frame = document().frame();
198     if (!frame)
199         return;
200
201     const FrameTree& frameTree = frame->tree();
202
203     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
204     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
205     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
206     if (frameTree.parent())
207         return;
208
209     frame->setPageZoomFactor(scale);
210 }
211
212 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
213 public:
214     static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
215     {
216         return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
217     }
218
219     virtual void commitChange() OVERRIDE
220     {
221         ASSERT(contextElement());
222         toSVGSVGElement(contextElement())->updateCurrentTranslate();
223     }
224
225 private:
226     SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
227         : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
228     {
229     }
230 };
231
232 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
233 {
234     return SVGCurrentTranslateTearOff::create(this);
235 }
236
237 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
238 {
239     m_translation->setValue(point);
240     updateCurrentTranslate();
241 }
242
243 void SVGSVGElement::updateCurrentTranslate()
244 {
245     if (RenderObject* object = renderer())
246         object->setNeedsLayout();
247
248     if (parentNode() == document() && document().renderer())
249         document().renderer()->repaint();
250 }
251
252 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
253 {
254     SVGParsingError parseError = NoError;
255
256     if (!nearestViewportElement()) {
257         bool setListener = true;
258
259         // Only handle events if we're the outermost <svg> element
260         if (name == HTMLNames::onunloadAttr)
261             document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value));
262         else if (name == HTMLNames::onresizeAttr)
263             document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value));
264         else if (name == HTMLNames::onscrollAttr)
265             document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value));
266         else if (name == SVGNames::onzoomAttr)
267             document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value));
268         else
269             setListener = false;
270
271         if (setListener)
272             return;
273     }
274
275     if (name == HTMLNames::onabortAttr)
276         document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value));
277     else if (name == HTMLNames::onerrorAttr)
278         document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value));
279     else if (name == SVGNames::xAttr)
280         m_x->setBaseValueAsString(value, AllowNegativeLengths, parseError);
281     else if (name == SVGNames::yAttr)
282         m_y->setBaseValueAsString(value, AllowNegativeLengths, parseError);
283     else if (name == SVGNames::widthAttr)
284         m_width->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
285     else if (name == SVGNames::heightAttr)
286         m_height->setBaseValueAsString(value, ForbidNegativeLengths, parseError);
287     else if (SVGFitToViewBox::parseAttribute(this, name, value)
288             || SVGZoomAndPan::parseAttribute(this, name, value)) {
289     } else
290         SVGGraphicsElement::parseAttribute(name, value);
291
292     reportAttributeParsingError(parseError, name, value);
293 }
294
295 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
296 {
297     bool updateRelativeLengthsOrViewBox = false;
298     bool widthChanged = attrName == SVGNames::widthAttr;
299     if (widthChanged
300         || attrName == SVGNames::heightAttr
301         || attrName == SVGNames::xAttr
302         || attrName == SVGNames::yAttr) {
303         updateRelativeLengthsOrViewBox = true;
304         updateRelativeLengthsInformation();
305         invalidateRelativeLengthClients();
306
307         // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can
308         // affect the replaced size so we need to mark it for updating.
309         if (widthChanged) {
310             RenderObject* renderObject = renderer();
311             if (renderObject && renderObject->isSVGRoot())
312                 toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc();
313         }
314     }
315
316     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
317         updateRelativeLengthsOrViewBox = true;
318         if (RenderObject* object = renderer())
319             object->setNeedsTransformUpdate();
320     }
321
322     SVGElementInstance::InvalidationGuard invalidationGuard(this);
323
324     if (updateRelativeLengthsOrViewBox
325         || SVGZoomAndPan::isKnownAttribute(attrName)) {
326         if (renderer())
327             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
328         return;
329     }
330
331     SVGGraphicsElement::svgAttributeChanged(attrName);
332 }
333
334 unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */)
335 {
336     // FIXME: Implement me (see bug 11275)
337     return 0;
338 }
339
340 void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */)
341 {
342     // FIXME: Implement me (see bug 11275)
343 }
344
345 void SVGSVGElement::unsuspendRedrawAll()
346 {
347     // FIXME: Implement me (see bug 11275)
348 }
349
350 void SVGSVGElement::forceRedraw()
351 {
352     // FIXME: Implement me (see bug 11275)
353 }
354
355 PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const
356 {
357     Vector<RefPtr<Node> > nodes;
358     Element* element = ElementTraversal::next(*(referenceElement ? referenceElement : this));
359     while (element) {
360         if (element->isSVGElement()) {
361             SVGElement* svgElement = toSVGElement(element);
362             if (collect == CollectIntersectionList) {
363                 if (RenderSVGModelObject::checkIntersection(svgElement->renderer(), rect))
364                     nodes.append(element);
365             } else {
366                 if (RenderSVGModelObject::checkEnclosure(svgElement->renderer(), rect))
367                     nodes.append(element);
368             }
369         }
370
371         element = ElementTraversal::next(*element, referenceElement ? referenceElement : this);
372     }
373     return StaticNodeList::adopt(nodes);
374 }
375
376 PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const
377 {
378     RefPtr<SVGRectTearOff> rect = passRect;
379     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectIntersectionList);
380 }
381
382 PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const
383 {
384     RefPtr<SVGRectTearOff> rect = passRect;
385     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectEnclosureList);
386 }
387
388 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const
389 {
390     if (!element)
391         return false;
392     RefPtr<SVGRectTearOff> rect = passRect;
393     return RenderSVGModelObject::checkIntersection(element->renderer(), rect->target()->value());
394 }
395
396 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const
397 {
398     if (!element)
399         return false;
400     RefPtr<SVGRectTearOff> rect = passRect;
401     return RenderSVGModelObject::checkEnclosure(element->renderer(), rect->target()->value());
402 }
403
404 void SVGSVGElement::deselectAll()
405 {
406     if (Frame* frame = document().frame())
407         frame->selection().clear();
408 }
409
410 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
411 {
412     return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
413 }
414
415 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
416 {
417     return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
418 }
419
420 SVGAngle SVGSVGElement::createSVGAngle()
421 {
422     return SVGAngle();
423 }
424
425 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
426 {
427     return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
428 }
429
430 SVGMatrix SVGSVGElement::createSVGMatrix()
431 {
432     return SVGMatrix();
433 }
434
435 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
436 {
437     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
438 }
439
440 SVGTransform SVGSVGElement::createSVGTransform()
441 {
442     return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX);
443 }
444
445 SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix)
446 {
447     return SVGTransform(static_cast<const AffineTransform&>(matrix));
448 }
449
450 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
451 {
452     AffineTransform viewBoxTransform;
453     if (!hasEmptyViewBox()) {
454         FloatSize size = currentViewportSize();
455         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
456     }
457
458     AffineTransform transform;
459     if (!isOutermostSVGSVGElement()) {
460         SVGLengthContext lengthContext(this);
461         transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
462     } else if (mode == SVGElement::ScreenScope) {
463         if (RenderObject* renderer = this->renderer()) {
464             FloatPoint location;
465             float zoomFactor = 1;
466
467             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
468             // to map an element from SVG viewport coordinates to CSS box coordinates.
469             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
470             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
471             if (renderer->isSVGRoot()) {
472                 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
473                 zoomFactor = 1 / renderer->style()->effectiveZoom();
474             }
475
476             // Translate in our CSS parent coordinate space
477             // FIXME: This doesn't work correctly with CSS transforms.
478             location = renderer->localToAbsolute(location, UseTransforms);
479             location.scale(zoomFactor, zoomFactor);
480
481             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
482             // so we have to subtract it here (original cause of bug #27183)
483             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
484
485             // Respect scroll offset.
486             if (FrameView* view = document().view()) {
487                 LayoutSize scrollOffset = view->scrollOffset();
488                 scrollOffset.scale(zoomFactor);
489                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
490             }
491         }
492     }
493
494     return transform.multiply(viewBoxTransform);
495 }
496
497 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
498 {
499     // FIXME: We should respect display: none on the documentElement svg element
500     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
501     // they should instead depend on the RenderView.
502     // https://bugs.webkit.org/show_bug.cgi?id=103493
503     if (document().documentElement() == this)
504         return true;
505     return Element::rendererIsNeeded(style);
506 }
507
508 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
509 {
510     if (isOutermostSVGSVGElement())
511         return new RenderSVGRoot(this);
512
513     return new RenderSVGViewportContainer(this);
514 }
515
516 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
517 {
518     if (rootParent->inDocument()) {
519         document().accessSVGExtensions()->addTimeContainer(this);
520
521         // Animations are started at the end of document parsing and after firing the load event,
522         // but if we miss that train (deferred programmatic element insertion for example) we need
523         // to initialize the time container here.
524         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
525             timeContainer()->begin();
526     }
527     return SVGGraphicsElement::insertedInto(rootParent);
528 }
529
530 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
531 {
532     if (rootParent->inDocument()) {
533         SVGDocumentExtensions* svgExtensions = document().accessSVGExtensions();
534         svgExtensions->removeTimeContainer(this);
535         svgExtensions->removeSVGRootWithRelativeLengthDescendents(this);
536     }
537
538     SVGGraphicsElement::removedFrom(rootParent);
539 }
540
541 void SVGSVGElement::pauseAnimations()
542 {
543     if (!m_timeContainer->isPaused())
544         m_timeContainer->pause();
545 }
546
547 void SVGSVGElement::unpauseAnimations()
548 {
549     if (m_timeContainer->isPaused())
550         m_timeContainer->resume();
551 }
552
553 bool SVGSVGElement::animationsPaused() const
554 {
555     return m_timeContainer->isPaused();
556 }
557
558 float SVGSVGElement::getCurrentTime() const
559 {
560     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
561 }
562
563 void SVGSVGElement::setCurrentTime(float seconds)
564 {
565     if (std::isnan(seconds))
566         return;
567     seconds = max(seconds, 0.0f);
568     m_timeContainer->setElapsed(seconds);
569 }
570
571 bool SVGSVGElement::selfHasRelativeLengths() const
572 {
573     return m_x->currentValue()->isRelative()
574         || m_y->currentValue()->isRelative()
575         || m_width->currentValue()->isRelative()
576         || m_height->currentValue()->isRelative()
577         || hasAttribute(SVGNames::viewBoxAttr);
578 }
579
580 FloatRect SVGSVGElement::currentViewBoxRect() const
581 {
582     if (m_useCurrentView)
583         return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
584
585     FloatRect useViewBox = m_viewBox->currentValue()->value();
586     if (!useViewBox.isEmpty())
587         return useViewBox;
588     if (!renderer() || !renderer()->isSVGRoot())
589         return FloatRect();
590     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
591         return FloatRect();
592
593     Length intrinsicWidth = this->intrinsicWidth();
594     Length intrinsicHeight = this->intrinsicHeight();
595     if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed())
596         return FloatRect();
597
598     // If no viewBox is specified but non-relative width/height values, then we
599     // should always synthesize a viewBox if we're embedded through a SVGImage.
600     return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)));
601 }
602
603 FloatSize SVGSVGElement::currentViewportSize() const
604 {
605     Length intrinsicWidth = this->intrinsicWidth();
606     Length intrinsicHeight = this->intrinsicHeight();
607     if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed())
608         return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
609
610     if (!renderer())
611         return FloatSize();
612
613     if (renderer()->isSVGRoot()) {
614         LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
615         return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
616     }
617
618     FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
619     return FloatSize(viewportRect.width(), viewportRect.height());
620 }
621
622 bool SVGSVGElement::widthAttributeEstablishesViewport() const
623 {
624     if (!renderer() || renderer()->isSVGViewportContainer())
625         return true;
626
627     // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace
628     // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met:
629     // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or
630     //   the SVG content is embedded inline within a containing document;
631     // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL];
632     // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element)
633     //   or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions,
634     //   the positioning properties establish the viewport's width.
635     RenderSVGRoot* root = toRenderSVGRoot(renderer());
636
637     // SVG embedded through object/embed/iframe.
638     if (root->isEmbeddedThroughFrameContainingSVGDocument())
639         return !root->hasReplacedLogicalWidth() && !document().frame()->ownerRenderer()->hasReplacedLogicalWidth();
640
641     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
642     if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
643         return !root->hasReplacedLogicalWidth();
644
645     return true;
646 }
647
648 bool SVGSVGElement::heightAttributeEstablishesViewport() const
649 {
650     if (!renderer() || renderer()->isSVGViewportContainer())
651         return true;
652
653     // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing
654     // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element
655     // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's
656     // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height.
657     RenderSVGRoot* root = toRenderSVGRoot(renderer());
658
659     // SVG embedded through object/embed/iframe.
660     if (root->isEmbeddedThroughFrameContainingSVGDocument())
661         return !root->hasReplacedLogicalHeight() && !document().frame()->ownerRenderer()->hasReplacedLogicalHeight();
662
663     // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG.
664     if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this)
665         return !root->hasReplacedLogicalHeight();
666
667     return true;
668 }
669
670 Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const
671 {
672     if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
673         if (m_width->currentValue()->unitType() == LengthTypePercentage)
674             return Length(m_width->currentValue()->valueAsPercentage() * 100, Percent);
675
676         SVGLengthContext lengthContext(this);
677         return Length(m_width->currentValue()->value(lengthContext), Fixed);
678     }
679
680     ASSERT(renderer());
681     return renderer()->style()->width();
682 }
683
684 Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const
685 {
686     if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) {
687         if (m_height->currentValue()->unitType() == LengthTypePercentage)
688             return Length(m_height->currentValue()->valueAsPercentage() * 100, Percent);
689
690         SVGLengthContext lengthContext(this);
691         return Length(m_height->currentValue()->value(lengthContext), Fixed);
692     }
693
694     ASSERT(renderer());
695     return renderer()->style()->height();
696 }
697
698 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
699 {
700     if (!m_useCurrentView || !m_viewSpec)
701         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_preserveAspectRatio->currentValue(), viewWidth, viewHeight);
702
703     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
704     const SVGTransformList& transformList = m_viewSpec->transformBaseValue();
705     if (transformList.isEmpty())
706         return ctm;
707
708     AffineTransform transform;
709     if (transformList.concatenate(transform))
710         ctm *= transform;
711
712     return ctm;
713 }
714
715 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
716 {
717     RenderObject* renderer = this->renderer();
718     SVGViewSpec* view = m_viewSpec.get();
719     if (view)
720         view->reset();
721
722     bool hadUseCurrentView = m_useCurrentView;
723     m_useCurrentView = false;
724
725     if (fragmentIdentifier.startsWith("xpointer(")) {
726         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
727         if (renderer && hadUseCurrentView)
728             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
729         return;
730     }
731
732     if (fragmentIdentifier.startsWith("svgView(")) {
733         if (!view)
734             view = currentView(); // Create the SVGViewSpec.
735
736         if (view->parseViewSpec(fragmentIdentifier))
737             m_useCurrentView = true;
738         else
739             view->reset();
740
741         if (renderer && (hadUseCurrentView || m_useCurrentView))
742             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
743         return;
744     }
745
746     // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
747     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
748     // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
749     // attributes on the closest ancestor ‘svg’ element.
750     if (anchorNode && anchorNode->hasTagName(SVGNames::viewTag)) {
751         SVGViewElement* viewElement = toSVGViewElement(anchorNode);
752         if (!viewElement)
753             return;
754
755         if (SVGSVGElement* svg = viewElement->ownerSVGElement()) {
756             svg->inheritViewAttributes(viewElement);
757
758             if (RenderObject* renderer = svg->renderer())
759                 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
760         }
761     }
762
763     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
764     // FIXME: We need to actually "highlight" the viewTarget(s).
765 }
766
767 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
768 {
769     SVGViewSpec* view = currentView();
770     m_useCurrentView = true;
771
772     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
773         view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
774     else
775         view->viewBox()->baseValue()->setValue(m_viewBox->currentValue()->value());
776
777     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
778         view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
779         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
780     } else {
781         view->preserveAspectRatio()->baseValue()->setAlign(m_preserveAspectRatio->currentValue()->align());
782         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(m_preserveAspectRatio->currentValue()->meetOrSlice());
783     }
784
785     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
786         view->setZoomAndPanBaseValue(viewElement->zoomAndPan());
787     else
788         view->setZoomAndPanBaseValue(zoomAndPan());
789 }
790
791 // getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element.
792 // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement
793 Element* SVGSVGElement::getElementById(const AtomicString& id) const
794 {
795     Element* element = treeScope().getElementById(id);
796     if (element && element->isDescendantOf(this))
797         return element;
798
799     // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will
800     // be returned.
801     for (Node* node = firstChild(); node; node = NodeTraversal::next(*node, this)) {
802         if (!node->isElementNode())
803             continue;
804
805         Element* element = toElement(node);
806         if (element->getIdAttribute() == id)
807             return element;
808     }
809     return 0;
810 }
811
812 }