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