• 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  * Copyright (C) 2014 Google, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #include "config.h"
24 
25 #include "core/svg/SVGSVGElement.h"
26 
27 #include "bindings/v8/ScriptEventListener.h"
28 #include "core/HTMLNames.h"
29 #include "core/SVGNames.h"
30 #include "core/css/CSSHelper.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/ElementTraversal.h"
33 #include "core/dom/StaticNodeList.h"
34 #include "core/editing/FrameSelection.h"
35 #include "core/events/EventListener.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/page/FrameTree.h"
38 #include "core/frame/FrameView.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/rendering/RenderObject.h"
41 #include "core/rendering/RenderPart.h"
42 #include "core/rendering/svg/RenderSVGModelObject.h"
43 #include "core/rendering/svg/RenderSVGResource.h"
44 #include "core/rendering/svg/RenderSVGRoot.h"
45 #include "core/rendering/svg/RenderSVGViewportContainer.h"
46 #include "core/svg/SVGAngleTearOff.h"
47 #include "core/svg/SVGNumberTearOff.h"
48 #include "core/svg/SVGPreserveAspectRatio.h"
49 #include "core/svg/SVGRectTearOff.h"
50 #include "core/svg/SVGTransform.h"
51 #include "core/svg/SVGTransformList.h"
52 #include "core/svg/SVGTransformTearOff.h"
53 #include "core/svg/SVGViewElement.h"
54 #include "core/svg/SVGViewSpec.h"
55 #include "core/svg/animation/SMILTimeContainer.h"
56 #include "platform/FloatConversion.h"
57 #include "platform/LengthFunctions.h"
58 #include "platform/geometry/FloatRect.h"
59 #include "platform/transforms/AffineTransform.h"
60 #include "wtf/StdLibExtras.h"
61 
62 namespace WebCore {
63 
SVGSVGElement(Document & doc)64 inline SVGSVGElement::SVGSVGElement(Document& doc)
65     : SVGGraphicsElement(SVGNames::svgTag, doc)
66     , SVGFitToViewBox(this)
67     , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
68     , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
69     , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
70     , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
71     , m_useCurrentView(false)
72     , m_timeContainer(SMILTimeContainer::create(*this))
73     , m_translation(SVGPoint::create())
74 {
75     ScriptWrappable::init(this);
76 
77     m_width->setDefaultValueAsString("100%");
78     m_height->setDefaultValueAsString("100%");
79 
80     addToPropertyMap(m_x);
81     addToPropertyMap(m_y);
82     addToPropertyMap(m_width);
83     addToPropertyMap(m_height);
84 
85     UseCounter::count(doc, UseCounter::SVGSVGElement);
86 }
87 
DEFINE_NODE_FACTORY(SVGSVGElement)88 DEFINE_NODE_FACTORY(SVGSVGElement)
89 
90 SVGSVGElement::~SVGSVGElement()
91 {
92 #if !ENABLE(OILPAN)
93     if (m_viewSpec)
94         m_viewSpec->detachContextElement();
95 
96     // There are cases where removedFromDocument() is not called.
97     // see ContainerNode::removeAllChildren, called by its destructor.
98     // With Oilpan, either removedFrom is called or the document
99     // is dead as well and there is no reason to clear the extensions.
100     document().accessSVGExtensions().removeTimeContainer(this);
101 
102     ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this));
103 #endif
104 }
105 
viewport() const106 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
107 {
108     // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
109     // As we have no test coverage for this, we're going to disable it completly for now.
110     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
111 }
112 
pixelUnitToMillimeterX() const113 float SVGSVGElement::pixelUnitToMillimeterX() const
114 {
115     return 1 / cssPixelsPerMillimeter;
116 }
117 
pixelUnitToMillimeterY() const118 float SVGSVGElement::pixelUnitToMillimeterY() const
119 {
120     return 1 / cssPixelsPerMillimeter;
121 }
122 
screenPixelToMillimeterX() const123 float SVGSVGElement::screenPixelToMillimeterX() const
124 {
125     return pixelUnitToMillimeterX();
126 }
127 
screenPixelToMillimeterY() const128 float SVGSVGElement::screenPixelToMillimeterY() const
129 {
130     return pixelUnitToMillimeterY();
131 }
132 
currentView()133 SVGViewSpec* SVGSVGElement::currentView()
134 {
135     if (!m_viewSpec)
136         m_viewSpec = SVGViewSpec::create(this);
137     return m_viewSpec.get();
138 }
139 
currentScale() const140 float SVGSVGElement::currentScale() const
141 {
142     if (!inDocument() || !isOutermostSVGSVGElement())
143         return 1;
144 
145     LocalFrame* frame = document().frame();
146     if (!frame)
147         return 1;
148 
149     const FrameTree& frameTree = frame->tree();
150 
151     // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
152     // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
153     // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
154     return frameTree.parent() ? 1 : frame->pageZoomFactor();
155 }
156 
setCurrentScale(float scale)157 void SVGSVGElement::setCurrentScale(float scale)
158 {
159     if (!inDocument() || !isOutermostSVGSVGElement())
160         return;
161 
162     LocalFrame* frame = document().frame();
163     if (!frame)
164         return;
165 
166     const FrameTree& frameTree = frame->tree();
167 
168     // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
169     // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
170     // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
171     if (frameTree.parent())
172         return;
173 
174     frame->setPageZoomFactor(scale);
175 }
176 
177 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
178 public:
create(SVGSVGElement * contextElement)179     static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
180     {
181         return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
182     }
183 
commitChange()184     virtual void commitChange() OVERRIDE
185     {
186         ASSERT(contextElement());
187         toSVGSVGElement(contextElement())->updateCurrentTranslate();
188     }
189 
190 private:
SVGCurrentTranslateTearOff(SVGSVGElement * contextElement)191     SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
192         : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
193     {
194     }
195 };
196 
currentTranslateFromJavascript()197 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
198 {
199     return SVGCurrentTranslateTearOff::create(this);
200 }
201 
setCurrentTranslate(const FloatPoint & point)202 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
203 {
204     m_translation->setValue(point);
205     updateCurrentTranslate();
206 }
207 
updateCurrentTranslate()208 void SVGSVGElement::updateCurrentTranslate()
209 {
210     if (RenderObject* object = renderer())
211         object->setNeedsLayoutAndFullPaintInvalidation();
212 }
213 
parseAttribute(const QualifiedName & name,const AtomicString & value)214 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
215 {
216     SVGParsingError parseError = NoError;
217 
218     if (!nearestViewportElement()) {
219         bool setListener = true;
220 
221         // Only handle events if we're the outermost <svg> element
222         if (name == HTMLNames::onunloadAttr)
223             document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
224         else if (name == HTMLNames::onresizeAttr)
225             document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
226         else if (name == HTMLNames::onscrollAttr)
227             document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
228         else if (name == SVGNames::onzoomAttr)
229             document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
230         else
231             setListener = false;
232 
233         if (setListener)
234             return;
235     }
236 
237     if (name == HTMLNames::onabortAttr) {
238         document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
239     } else if (name == HTMLNames::onerrorAttr) {
240         document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
241     } else if (name == SVGNames::xAttr) {
242         m_x->setBaseValueAsString(value, parseError);
243     } else if (name == SVGNames::yAttr) {
244         m_y->setBaseValueAsString(value, parseError);
245     } else if (name == SVGNames::widthAttr) {
246         m_width->setBaseValueAsString(value, parseError);
247     } else if (name == SVGNames::heightAttr) {
248         m_height->setBaseValueAsString(value, parseError);
249     } else if (SVGFitToViewBox::parseAttribute(name, value, document(), parseError)) {
250     } else if (SVGZoomAndPan::parseAttribute(name, value)) {
251     } else {
252         SVGGraphicsElement::parseAttribute(name, value);
253     }
254 
255     reportAttributeParsingError(parseError, name, value);
256 }
257 
isPresentationAttribute(const QualifiedName & name) const258 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const
259 {
260     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr))
261         return true;
262     return SVGGraphicsElement::isPresentationAttribute(name);
263 }
264 
collectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableStylePropertySet * style)265 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
266 {
267     if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) {
268         RefPtr<SVGLength> length = SVGLength::create(LengthModeOther);
269         TrackExceptionState exceptionState;
270         length->setValueAsString(value, exceptionState);
271         if (!exceptionState.hadException()) {
272             if (name == SVGNames::widthAttr)
273                 addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value);
274             else if (name == SVGNames::heightAttr)
275                 addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value);
276         }
277     } else {
278         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
279     }
280 }
281 
svgAttributeChanged(const QualifiedName & attrName)282 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
283 {
284     bool updateRelativeLengthsOrViewBox = false;
285     bool widthChanged = attrName == SVGNames::widthAttr;
286     bool heightChanged = attrName == SVGNames::heightAttr;
287     if (widthChanged || heightChanged
288         || attrName == SVGNames::xAttr
289         || attrName == SVGNames::yAttr) {
290         updateRelativeLengthsOrViewBox = true;
291         updateRelativeLengthsInformation();
292         invalidateRelativeLengthClients();
293 
294         // At the SVG/HTML boundary (aka RenderSVGRoot), the width and
295         // height attributes can affect the replaced size so we need
296         // to mark it for updating.
297         //
298         // FIXME: For width/height animated as XML attributes on SVG
299         // roots, there is an attribute synchronization missing. See
300         // http://crbug.com/364807
301         if (widthChanged || heightChanged) {
302             RenderObject* renderObject = renderer();
303             if (renderObject && renderObject->isSVGRoot()) {
304                 invalidateSVGPresentationAttributeStyle();
305                 setNeedsStyleRecalc(LocalStyleChange);
306             }
307         }
308     }
309 
310     if (SVGFitToViewBox::isKnownAttribute(attrName)) {
311         updateRelativeLengthsOrViewBox = true;
312         if (RenderObject* object = renderer())
313             object->setNeedsTransformUpdate();
314     }
315 
316     SVGElement::InvalidationGuard invalidationGuard(this);
317 
318     if (updateRelativeLengthsOrViewBox
319         || SVGZoomAndPan::isKnownAttribute(attrName)) {
320         if (renderer())
321             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer());
322         return;
323     }
324 
325     SVGGraphicsElement::svgAttributeChanged(attrName);
326 }
327 
328 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()).
intersectsAllowingEmpty(const FloatRect & r1,const FloatRect & r2)329 static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2)
330 {
331     if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0)
332         return false;
333 
334     return r1.x() < r2.maxX() && r2.x() < r1.maxX()
335         && r1.y() < r2.maxY() && r2.y() < r1.maxY();
336 }
337 
338 // One of the element types that can cause graphics to be drawn onto the target canvas.
339 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use.
isIntersectionOrEnclosureTarget(RenderObject * renderer)340 static bool isIntersectionOrEnclosureTarget(RenderObject* renderer)
341 {
342     return renderer->isSVGShape()
343         || renderer->isSVGText()
344         || renderer->isSVGImage()
345         || isSVGUseElement(*renderer->node());
346 }
347 
checkIntersectionOrEnclosure(const SVGElement & element,const FloatRect & rect,CheckIntersectionOrEnclosure mode) const348 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect,
349     CheckIntersectionOrEnclosure mode) const
350 {
351     RenderObject* renderer = element.renderer();
352     ASSERT(!renderer || renderer->style());
353     if (!renderer || renderer->style()->pointerEvents() == PE_NONE)
354         return false;
355 
356     if (!isIntersectionOrEnclosureTarget(renderer))
357         return false;
358 
359     AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this);
360     FloatRect mappedRepaintRect = ctm.mapRect(renderer->paintInvalidationRectInLocalCoordinates());
361 
362     bool result = false;
363     switch (mode) {
364     case CheckIntersection:
365         result = intersectsAllowingEmpty(rect, mappedRepaintRect);
366         break;
367     case CheckEnclosure:
368         result = rect.contains(mappedRepaintRect);
369         break;
370     default:
371         ASSERT_NOT_REACHED();
372         break;
373     }
374 
375     return result;
376 }
377 
collectIntersectionOrEnclosureList(const FloatRect & rect,SVGElement * referenceElement,CheckIntersectionOrEnclosure mode) const378 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect,
379     SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const
380 {
381     WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
382 
383     const SVGElement* root = this;
384     if (referenceElement) {
385         // Only the common subtree needs to be traversed.
386         if (contains(referenceElement)) {
387             root = referenceElement;
388         } else if (!isDescendantOf(referenceElement)) {
389             // No common subtree.
390             return StaticNodeList::adopt(nodes);
391         }
392     }
393 
394     for (SVGGraphicsElement* element = Traversal<SVGGraphicsElement>::firstWithin(*root); element;
395         element = Traversal<SVGGraphicsElement>::next(*element, root)) {
396         if (checkIntersectionOrEnclosure(*element, rect, mode))
397             nodes.append(element);
398     }
399 
400     return StaticNodeList::adopt(nodes);
401 }
402 
getIntersectionList(PassRefPtr<SVGRectTearOff> rect,SVGElement * referenceElement) const403 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
404 {
405     document().updateLayoutIgnorePendingStylesheets();
406 
407     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection);
408 }
409 
getEnclosureList(PassRefPtr<SVGRectTearOff> rect,SVGElement * referenceElement) const410 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
411 {
412     document().updateLayoutIgnorePendingStylesheets();
413 
414     return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure);
415 }
416 
checkIntersection(SVGElement * element,PassRefPtr<SVGRectTearOff> rect) const417 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
418 {
419     ASSERT(element);
420     document().updateLayoutIgnorePendingStylesheets();
421 
422     return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection);
423 }
424 
checkEnclosure(SVGElement * element,PassRefPtr<SVGRectTearOff> rect) const425 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
426 {
427     ASSERT(element);
428     document().updateLayoutIgnorePendingStylesheets();
429 
430     return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure);
431 }
432 
deselectAll()433 void SVGSVGElement::deselectAll()
434 {
435     if (LocalFrame* frame = document().frame())
436         frame->selection().clear();
437 }
438 
createSVGNumber()439 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
440 {
441     return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
442 }
443 
createSVGLength()444 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
445 {
446     return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
447 }
448 
createSVGAngle()449 PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle()
450 {
451     return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal);
452 }
453 
createSVGPoint()454 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
455 {
456     return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
457 }
458 
createSVGMatrix()459 PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix()
460 {
461     return SVGMatrixTearOff::create(AffineTransform());
462 }
463 
createSVGRect()464 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
465 {
466     return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
467 }
468 
createSVGTransform()469 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform()
470 {
471     return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal);
472 }
473 
createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)474 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)
475 {
476     return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal);
477 }
478 
localCoordinateSpaceTransform(SVGElement::CTMScope mode) const479 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
480 {
481     AffineTransform viewBoxTransform;
482     if (!hasEmptyViewBox()) {
483         FloatSize size = currentViewportSize();
484         viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
485     }
486 
487     AffineTransform transform;
488     if (!isOutermostSVGSVGElement()) {
489         SVGLengthContext lengthContext(this);
490         transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
491     } else if (mode == SVGElement::ScreenScope) {
492         if (RenderObject* renderer = this->renderer()) {
493             FloatPoint location;
494             float zoomFactor = 1;
495 
496             // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
497             // to map an element from SVG viewport coordinates to CSS box coordinates.
498             // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
499             // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
500             if (renderer->isSVGRoot()) {
501                 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
502                 zoomFactor = 1 / renderer->style()->effectiveZoom();
503             }
504 
505             // Translate in our CSS parent coordinate space
506             // FIXME: This doesn't work correctly with CSS transforms.
507             location = renderer->localToAbsolute(location, UseTransforms);
508             location.scale(zoomFactor, zoomFactor);
509 
510             // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
511             // so we have to subtract it here (original cause of bug #27183)
512             transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
513 
514             // Respect scroll offset.
515             if (FrameView* view = document().view()) {
516                 LayoutSize scrollOffset = view->scrollOffset();
517                 scrollOffset.scale(zoomFactor);
518                 transform.translate(-scrollOffset.width(), -scrollOffset.height());
519             }
520         }
521     }
522 
523     return transform.multiply(viewBoxTransform);
524 }
525 
rendererIsNeeded(const RenderStyle & style)526 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
527 {
528     // FIXME: We should respect display: none on the documentElement svg element
529     // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
530     // they should instead depend on the RenderView.
531     // https://bugs.webkit.org/show_bug.cgi?id=103493
532     if (document().documentElement() == this)
533         return true;
534     return Element::rendererIsNeeded(style);
535 }
536 
createRenderer(RenderStyle *)537 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
538 {
539     if (isOutermostSVGSVGElement())
540         return new RenderSVGRoot(this);
541 
542     return new RenderSVGViewportContainer(this);
543 }
544 
insertedInto(ContainerNode * rootParent)545 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
546 {
547     if (rootParent->inDocument()) {
548         UseCounter::count(document(), UseCounter::SVGSVGElementInDocument);
549         if (rootParent->document().isXMLDocument())
550             UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument);
551 
552         document().accessSVGExtensions().addTimeContainer(this);
553 
554         // Animations are started at the end of document parsing and after firing the load event,
555         // but if we miss that train (deferred programmatic element insertion for example) we need
556         // to initialize the time container here.
557         if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
558             timeContainer()->begin();
559     }
560     return SVGGraphicsElement::insertedInto(rootParent);
561 }
562 
removedFrom(ContainerNode * rootParent)563 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
564 {
565     if (rootParent->inDocument()) {
566         SVGDocumentExtensions& svgExtensions = document().accessSVGExtensions();
567         svgExtensions.removeTimeContainer(this);
568         svgExtensions.removeSVGRootWithRelativeLengthDescendents(this);
569     }
570 
571     SVGGraphicsElement::removedFrom(rootParent);
572 }
573 
pauseAnimations()574 void SVGSVGElement::pauseAnimations()
575 {
576     if (!m_timeContainer->isPaused())
577         m_timeContainer->pause();
578 }
579 
unpauseAnimations()580 void SVGSVGElement::unpauseAnimations()
581 {
582     if (m_timeContainer->isPaused())
583         m_timeContainer->resume();
584 }
585 
animationsPaused() const586 bool SVGSVGElement::animationsPaused() const
587 {
588     return m_timeContainer->isPaused();
589 }
590 
getCurrentTime() const591 float SVGSVGElement::getCurrentTime() const
592 {
593     return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
594 }
595 
setCurrentTime(float seconds)596 void SVGSVGElement::setCurrentTime(float seconds)
597 {
598     if (std::isnan(seconds))
599         return;
600     seconds = max(seconds, 0.0f);
601     m_timeContainer->setElapsed(seconds);
602 }
603 
selfHasRelativeLengths() const604 bool SVGSVGElement::selfHasRelativeLengths() const
605 {
606     return m_x->currentValue()->isRelative()
607         || m_y->currentValue()->isRelative()
608         || m_width->currentValue()->isRelative()
609         || m_height->currentValue()->isRelative()
610         || hasAttribute(SVGNames::viewBoxAttr);
611 }
612 
currentViewBoxRect() const613 FloatRect SVGSVGElement::currentViewBoxRect() const
614 {
615     if (m_useCurrentView)
616         return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
617 
618     FloatRect useViewBox = viewBox()->currentValue()->value();
619     if (!useViewBox.isEmpty())
620         return useViewBox;
621     if (!renderer() || !renderer()->isSVGRoot())
622         return FloatRect();
623     if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
624         return FloatRect();
625 
626     // If no viewBox is specified but non-relative width/height values, then we
627     // should always synthesize a viewBox if we're embedded through a SVGImage.
628     return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)));
629 }
630 
currentViewportSize() const631 FloatSize SVGSVGElement::currentViewportSize() const
632 {
633     if (hasIntrinsicWidth() && hasIntrinsicHeight()) {
634         Length intrinsicWidth = this->intrinsicWidth();
635         Length intrinsicHeight = this->intrinsicHeight();
636         return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0));
637     }
638 
639     if (!renderer())
640         return FloatSize();
641 
642     if (renderer()->isSVGRoot()) {
643         LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
644         return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
645     }
646 
647     FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
648     return FloatSize(viewportRect.width(), viewportRect.height());
649 }
650 
hasIntrinsicWidth() const651 bool SVGSVGElement::hasIntrinsicWidth() const
652 {
653     return width()->currentValue()->unitType() != LengthTypePercentage;
654 }
655 
hasIntrinsicHeight() const656 bool SVGSVGElement::hasIntrinsicHeight() const
657 {
658     return height()->currentValue()->unitType() != LengthTypePercentage;
659 }
660 
intrinsicWidth() const661 Length SVGSVGElement::intrinsicWidth() const
662 {
663     if (width()->currentValue()->unitType() == LengthTypePercentage)
664         return Length(0, Fixed);
665 
666     SVGLengthContext lengthContext(this);
667     return Length(width()->currentValue()->value(lengthContext), Fixed);
668 }
669 
intrinsicHeight() const670 Length SVGSVGElement::intrinsicHeight() const
671 {
672     if (height()->currentValue()->unitType() == LengthTypePercentage)
673         return Length(0, Fixed);
674 
675     SVGLengthContext lengthContext(this);
676     return Length(height()->currentValue()->value(lengthContext), Fixed);
677 }
678 
viewBoxToViewTransform(float viewWidth,float viewHeight) const679 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
680 {
681     if (!m_useCurrentView || !m_viewSpec)
682         return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
683 
684     AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
685     RefPtr<SVGTransformList> transformList = m_viewSpec->transform();
686     if (transformList->isEmpty())
687         return ctm;
688 
689     AffineTransform transform;
690     if (transformList->concatenate(transform))
691         ctm *= transform;
692 
693     return ctm;
694 }
695 
setupInitialView(const String & fragmentIdentifier,Element * anchorNode)696 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
697 {
698     RenderObject* renderer = this->renderer();
699     SVGViewSpec* view = m_viewSpec.get();
700     if (view)
701         view->reset();
702 
703     bool hadUseCurrentView = m_useCurrentView;
704     m_useCurrentView = false;
705 
706     if (fragmentIdentifier.startsWith("xpointer(")) {
707         // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
708         if (renderer && hadUseCurrentView)
709             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
710         return;
711     }
712 
713     if (fragmentIdentifier.startsWith("svgView(")) {
714         if (!view)
715             view = currentView(); // Create the SVGViewSpec.
716 
717         if (view->parseViewSpec(fragmentIdentifier))
718             m_useCurrentView = true;
719         else
720             view->reset();
721 
722         if (renderer && (hadUseCurrentView || m_useCurrentView))
723             RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
724         return;
725     }
726 
727     // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
728     // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
729     // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
730     // attributes on the closest ancestor ‘svg’ element.
731     if (isSVGViewElement(anchorNode)) {
732         SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
733 
734         if (SVGSVGElement* svg = viewElement.ownerSVGElement()) {
735             svg->inheritViewAttributes(&viewElement);
736 
737             if (RenderObject* renderer = svg->renderer())
738                 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
739         }
740     }
741 
742     // FIXME: We need to decide which <svg> to focus on, and zoom to it.
743     // FIXME: We need to actually "highlight" the viewTarget(s).
744 }
745 
inheritViewAttributes(SVGViewElement * viewElement)746 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
747 {
748     SVGViewSpec* view = currentView();
749     m_useCurrentView = true;
750 
751     if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
752         view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
753     else
754         view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value());
755 
756     if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
757         view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
758         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
759     } else {
760         view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align());
761         view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice());
762     }
763 
764     if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
765         view->setZoomAndPan(viewElement->zoomAndPan());
766     else
767         view->setZoomAndPan(zoomAndPan());
768 }
769 
finishParsingChildren()770 void SVGSVGElement::finishParsingChildren()
771 {
772     SVGGraphicsElement::finishParsingChildren();
773 
774     // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
775     if (isOutermostSVGSVGElement())
776         return;
777 
778     // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
779     // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
780     sendSVGLoadEventIfPossible();
781 }
782 
trace(Visitor * visitor)783 void SVGSVGElement::trace(Visitor* visitor)
784 {
785     visitor->trace(m_timeContainer);
786     visitor->trace(m_viewSpec);
787     SVGGraphicsElement::trace(visitor);
788 }
789 
790 }
791