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