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