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