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