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.
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.
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.
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.
25 #include "core/svg/SVGSVGElement.h"
27 #include "bindings/core/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/svg/RenderSVGModelObject.h"
42 #include "core/rendering/svg/RenderSVGRoot.h"
43 #include "core/rendering/svg/RenderSVGViewportContainer.h"
44 #include "core/svg/SVGAngleTearOff.h"
45 #include "core/svg/SVGNumberTearOff.h"
46 #include "core/svg/SVGPreserveAspectRatio.h"
47 #include "core/svg/SVGRectTearOff.h"
48 #include "core/svg/SVGTransform.h"
49 #include "core/svg/SVGTransformList.h"
50 #include "core/svg/SVGTransformTearOff.h"
51 #include "core/svg/SVGViewElement.h"
52 #include "core/svg/SVGViewSpec.h"
53 #include "core/svg/animation/SMILTimeContainer.h"
54 #include "platform/FloatConversion.h"
55 #include "platform/LengthFunctions.h"
56 #include "platform/geometry/FloatRect.h"
57 #include "platform/transforms/AffineTransform.h"
58 #include "wtf/StdLibExtras.h"
62 inline SVGSVGElement::SVGSVGElement(Document& doc)
63 : SVGGraphicsElement(SVGNames::svgTag, doc)
64 , SVGFitToViewBox(this)
65 , m_x(SVGAnimatedLength::create(this, SVGNames::xAttr, SVGLength::create(LengthModeWidth), AllowNegativeLengths))
66 , m_y(SVGAnimatedLength::create(this, SVGNames::yAttr, SVGLength::create(LengthModeHeight), AllowNegativeLengths))
67 , m_width(SVGAnimatedLength::create(this, SVGNames::widthAttr, SVGLength::create(LengthModeWidth), ForbidNegativeLengths))
68 , m_height(SVGAnimatedLength::create(this, SVGNames::heightAttr, SVGLength::create(LengthModeHeight), ForbidNegativeLengths))
69 , m_useCurrentView(false)
70 , m_timeContainer(SMILTimeContainer::create(*this))
71 , m_translation(SVGPoint::create())
73 m_width->setDefaultValueAsString("100%");
74 m_height->setDefaultValueAsString("100%");
76 addToPropertyMap(m_x);
77 addToPropertyMap(m_y);
78 addToPropertyMap(m_width);
79 addToPropertyMap(m_height);
81 UseCounter::count(doc, UseCounter::SVGSVGElement);
84 DEFINE_NODE_FACTORY(SVGSVGElement)
86 SVGSVGElement::~SVGSVGElement()
90 m_viewSpec->detachContextElement();
92 // There are cases where removedFromDocument() is not called.
93 // see ContainerNode::removeAllChildren, called by its destructor.
94 // With Oilpan, either removedFrom is called or the document
95 // is dead as well and there is no reason to clear the extensions.
96 document().accessSVGExtensions().removeTimeContainer(this);
98 ASSERT(inDocument() || !accessDocumentSVGExtensions().isSVGRootWithRelativeLengthDescendents(this));
102 PassRefPtr<SVGRectTearOff> SVGSVGElement::viewport() const
104 // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here.
105 // As we have no test coverage for this, we're going to disable it completly for now.
106 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
109 float SVGSVGElement::pixelUnitToMillimeterX() const
111 return 1 / cssPixelsPerMillimeter;
114 float SVGSVGElement::pixelUnitToMillimeterY() const
116 return 1 / cssPixelsPerMillimeter;
119 float SVGSVGElement::screenPixelToMillimeterX() const
121 return pixelUnitToMillimeterX();
124 float SVGSVGElement::screenPixelToMillimeterY() const
126 return pixelUnitToMillimeterY();
129 SVGViewSpec* SVGSVGElement::currentView()
132 m_viewSpec = SVGViewSpec::create(this);
133 return m_viewSpec.get();
136 float SVGSVGElement::currentScale() const
138 if (!inDocument() || !isOutermostSVGSVGElement())
141 LocalFrame* frame = document().frame();
145 const FrameTree& frameTree = frame->tree();
147 // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents.
148 // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside
149 // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale.
150 return frameTree.parent() ? 1 : frame->pageZoomFactor();
153 void SVGSVGElement::setCurrentScale(float scale)
155 if (!inDocument() || !isOutermostSVGSVGElement())
158 LocalFrame* frame = document().frame();
162 const FrameTree& frameTree = frame->tree();
164 // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents.
165 // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within
166 // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG.
167 if (frameTree.parent())
170 frame->setPageZoomFactor(scale);
173 class SVGCurrentTranslateTearOff : public SVGPointTearOff {
175 static PassRefPtr<SVGCurrentTranslateTearOff> create(SVGSVGElement* contextElement)
177 return adoptRef(new SVGCurrentTranslateTearOff(contextElement));
180 virtual void commitChange() override
182 ASSERT(contextElement());
183 toSVGSVGElement(contextElement())->updateCurrentTranslate();
187 SVGCurrentTranslateTearOff(SVGSVGElement* contextElement)
188 : SVGPointTearOff(contextElement->m_translation, contextElement, PropertyIsNotAnimVal)
193 PassRefPtr<SVGPointTearOff> SVGSVGElement::currentTranslateFromJavascript()
195 return SVGCurrentTranslateTearOff::create(this);
198 void SVGSVGElement::setCurrentTranslate(const FloatPoint& point)
200 m_translation->setValue(point);
201 updateCurrentTranslate();
204 void SVGSVGElement::updateCurrentTranslate()
206 if (RenderObject* object = renderer())
207 object->setNeedsLayoutAndFullPaintInvalidation();
210 void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
212 if (!nearestViewportElement()) {
213 bool setListener = true;
215 // Only handle events if we're the outermost <svg> element
216 if (name == HTMLNames::onunloadAttr)
217 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
218 else if (name == HTMLNames::onresizeAttr)
219 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
220 else if (name == HTMLNames::onscrollAttr)
221 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
222 else if (name == SVGNames::onzoomAttr)
223 document().setWindowAttributeEventListener(EventTypeNames::zoom, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
231 if (name == HTMLNames::onabortAttr) {
232 document().setWindowAttributeEventListener(EventTypeNames::abort, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
233 } else if (name == HTMLNames::onerrorAttr) {
234 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName()));
235 } else if (SVGZoomAndPan::parseAttribute(name, value)) {
237 parseAttributeNew(name, value);
241 bool SVGSVGElement::isPresentationAttribute(const QualifiedName& name) const
243 if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr))
245 return SVGGraphicsElement::isPresentationAttribute(name);
248 void SVGSVGElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
250 if (isOutermostSVGSVGElement() && (name == SVGNames::widthAttr || name == SVGNames::heightAttr)) {
251 RefPtr<SVGLength> length = SVGLength::create(LengthModeOther);
252 TrackExceptionState exceptionState;
253 length->setValueAsString(value, exceptionState);
254 if (!exceptionState.hadException()) {
255 if (name == SVGNames::widthAttr)
256 addPropertyToPresentationAttributeStyle(style, CSSPropertyWidth, value);
257 else if (name == SVGNames::heightAttr)
258 addPropertyToPresentationAttributeStyle(style, CSSPropertyHeight, value);
261 SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
265 void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName)
267 bool updateRelativeLengthsOrViewBox = false;
268 bool widthChanged = attrName == SVGNames::widthAttr;
269 bool heightChanged = attrName == SVGNames::heightAttr;
270 if (widthChanged || heightChanged
271 || attrName == SVGNames::xAttr
272 || attrName == SVGNames::yAttr) {
273 updateRelativeLengthsOrViewBox = true;
274 updateRelativeLengthsInformation();
275 invalidateRelativeLengthClients();
277 // At the SVG/HTML boundary (aka RenderSVGRoot), the width and
278 // height attributes can affect the replaced size so we need
279 // to mark it for updating.
281 // FIXME: For width/height animated as XML attributes on SVG
282 // roots, there is an attribute synchronization missing. See
283 // http://crbug.com/364807
284 if (widthChanged || heightChanged) {
285 RenderObject* renderObject = renderer();
286 if (renderObject && renderObject->isSVGRoot()) {
287 invalidateSVGPresentationAttributeStyle();
288 setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::SVGContainerSizeChange));
293 if (SVGFitToViewBox::isKnownAttribute(attrName)) {
294 updateRelativeLengthsOrViewBox = true;
295 if (RenderObject* object = renderer())
296 object->setNeedsTransformUpdate();
299 SVGElement::InvalidationGuard invalidationGuard(this);
301 if (updateRelativeLengthsOrViewBox
302 || SVGZoomAndPan::isKnownAttribute(attrName)) {
304 markForLayoutAndParentResourceInvalidation(renderer());
308 SVGGraphicsElement::svgAttributeChanged(attrName);
311 // FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()).
312 static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2)
314 if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0)
317 return r1.x() < r2.maxX() && r2.x() < r1.maxX()
318 && r1.y() < r2.maxY() && r2.y() < r1.maxY();
321 // One of the element types that can cause graphics to be drawn onto the target canvas.
322 // Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use.
323 static bool isIntersectionOrEnclosureTarget(RenderObject* renderer)
325 return renderer->isSVGShape()
326 || renderer->isSVGText()
327 || renderer->isSVGImage()
328 || isSVGUseElement(*renderer->node());
331 bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect,
332 CheckIntersectionOrEnclosure mode) const
334 RenderObject* renderer = element.renderer();
335 ASSERT(!renderer || renderer->style());
336 if (!renderer || renderer->style()->pointerEvents() == PE_NONE)
339 if (!isIntersectionOrEnclosureTarget(renderer))
342 AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this);
343 FloatRect mappedRepaintRect = ctm.mapRect(renderer->paintInvalidationRectInLocalCoordinates());
347 case CheckIntersection:
348 result = intersectsAllowingEmpty(rect, mappedRepaintRect);
351 result = rect.contains(mappedRepaintRect);
354 ASSERT_NOT_REACHED();
361 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect,
362 SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const
364 WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
366 const SVGElement* root = this;
367 if (referenceElement) {
368 // Only the common subtree needs to be traversed.
369 if (contains(referenceElement)) {
370 root = referenceElement;
371 } else if (!isDescendantOf(referenceElement)) {
372 // No common subtree.
373 return StaticNodeList::adopt(nodes);
377 for (SVGGraphicsElement& element : Traversal<SVGGraphicsElement>::descendantsOf(*root)) {
378 if (checkIntersectionOrEnclosure(element, rect, mode))
379 nodes.append(&element);
382 return StaticNodeList::adopt(nodes);
385 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
387 document().updateLayoutIgnorePendingStylesheets();
389 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection);
392 PassRefPtrWillBeRawPtr<StaticNodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const
394 document().updateLayoutIgnorePendingStylesheets();
396 return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure);
399 bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
402 document().updateLayoutIgnorePendingStylesheets();
404 return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection);
407 bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const
410 document().updateLayoutIgnorePendingStylesheets();
412 return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure);
415 void SVGSVGElement::deselectAll()
417 if (LocalFrame* frame = document().frame())
418 frame->selection().clear();
421 PassRefPtr<SVGNumberTearOff> SVGSVGElement::createSVGNumber()
423 return SVGNumberTearOff::create(SVGNumber::create(0.0f), 0, PropertyIsNotAnimVal);
426 PassRefPtr<SVGLengthTearOff> SVGSVGElement::createSVGLength()
428 return SVGLengthTearOff::create(SVGLength::create(), 0, PropertyIsNotAnimVal);
431 PassRefPtr<SVGAngleTearOff> SVGSVGElement::createSVGAngle()
433 return SVGAngleTearOff::create(SVGAngle::create(), 0, PropertyIsNotAnimVal);
436 PassRefPtr<SVGPointTearOff> SVGSVGElement::createSVGPoint()
438 return SVGPointTearOff::create(SVGPoint::create(), 0, PropertyIsNotAnimVal);
441 PassRefPtr<SVGMatrixTearOff> SVGSVGElement::createSVGMatrix()
443 return SVGMatrixTearOff::create(AffineTransform());
446 PassRefPtr<SVGRectTearOff> SVGSVGElement::createSVGRect()
448 return SVGRectTearOff::create(SVGRect::create(), 0, PropertyIsNotAnimVal);
451 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransform()
453 return SVGTransformTearOff::create(SVGTransform::create(SVG_TRANSFORM_MATRIX), 0, PropertyIsNotAnimVal);
456 PassRefPtr<SVGTransformTearOff> SVGSVGElement::createSVGTransformFromMatrix(PassRefPtr<SVGMatrixTearOff> matrix)
458 return SVGTransformTearOff::create(SVGTransform::create(matrix->value()), 0, PropertyIsNotAnimVal);
461 AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGElement::CTMScope mode) const
463 AffineTransform viewBoxTransform;
464 if (!hasEmptyViewBox()) {
465 FloatSize size = currentViewportSize();
466 viewBoxTransform = viewBoxToViewTransform(size.width(), size.height());
469 AffineTransform transform;
470 if (!isOutermostSVGSVGElement()) {
471 SVGLengthContext lengthContext(this);
472 transform.translate(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext));
473 } else if (mode == SVGElement::ScreenScope) {
474 if (RenderObject* renderer = this->renderer()) {
476 float zoomFactor = 1;
478 // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform
479 // to map an element from SVG viewport coordinates to CSS box coordinates.
480 // RenderSVGRoot's localToAbsolute method expects CSS box coordinates.
481 // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361).
482 if (renderer->isSVGRoot()) {
483 location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location);
484 zoomFactor = 1 / renderer->style()->effectiveZoom();
487 // Translate in our CSS parent coordinate space
488 // FIXME: This doesn't work correctly with CSS transforms.
489 location = renderer->localToAbsolute(location, UseTransforms);
490 location.scale(zoomFactor, zoomFactor);
492 // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(),
493 // so we have to subtract it here (original cause of bug #27183)
494 transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f());
496 // Respect scroll offset.
497 if (FrameView* view = document().view()) {
498 LayoutSize scrollOffset = view->scrollOffset();
499 scrollOffset.scale(zoomFactor);
500 transform.translate(-scrollOffset.width(), -scrollOffset.height());
505 return transform.multiply(viewBoxTransform);
508 bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style)
510 // FIXME: We should respect display: none on the documentElement svg element
511 // but many things in FrameView and SVGImage depend on the RenderSVGRoot when
512 // they should instead depend on the RenderView.
513 // https://bugs.webkit.org/show_bug.cgi?id=103493
514 if (document().documentElement() == this)
516 return Element::rendererIsNeeded(style);
519 RenderObject* SVGSVGElement::createRenderer(RenderStyle*)
521 if (isOutermostSVGSVGElement())
522 return new RenderSVGRoot(this);
524 return new RenderSVGViewportContainer(this);
527 Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent)
529 if (rootParent->inDocument()) {
530 UseCounter::count(document(), UseCounter::SVGSVGElementInDocument);
531 if (rootParent->document().isXMLDocument())
532 UseCounter::count(document(), UseCounter::SVGSVGElementInXMLDocument);
534 document().accessSVGExtensions().addTimeContainer(this);
536 // Animations are started at the end of document parsing and after firing the load event,
537 // but if we miss that train (deferred programmatic element insertion for example) we need
538 // to initialize the time container here.
539 if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted())
540 timeContainer()->begin();
542 return SVGGraphicsElement::insertedInto(rootParent);
545 void SVGSVGElement::removedFrom(ContainerNode* rootParent)
547 if (rootParent->inDocument()) {
548 SVGDocumentExtensions& svgExtensions = document().accessSVGExtensions();
549 svgExtensions.removeTimeContainer(this);
550 svgExtensions.removeSVGRootWithRelativeLengthDescendents(this);
553 SVGGraphicsElement::removedFrom(rootParent);
556 void SVGSVGElement::pauseAnimations()
558 if (!m_timeContainer->isPaused())
559 m_timeContainer->pause();
562 void SVGSVGElement::unpauseAnimations()
564 if (m_timeContainer->isPaused())
565 m_timeContainer->resume();
568 bool SVGSVGElement::animationsPaused() const
570 return m_timeContainer->isPaused();
573 float SVGSVGElement::getCurrentTime() const
575 return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
578 void SVGSVGElement::setCurrentTime(float seconds)
580 if (std::isnan(seconds))
582 seconds = max(seconds, 0.0f);
583 m_timeContainer->setElapsed(seconds);
586 bool SVGSVGElement::selfHasRelativeLengths() const
588 return m_x->currentValue()->isRelative()
589 || m_y->currentValue()->isRelative()
590 || m_width->currentValue()->isRelative()
591 || m_height->currentValue()->isRelative()
592 || hasAttribute(SVGNames::viewBoxAttr);
595 FloatRect SVGSVGElement::currentViewBoxRect() const
597 if (m_useCurrentView)
598 return m_viewSpec ? m_viewSpec->viewBox()->currentValue()->value() : FloatRect();
600 FloatRect useViewBox = viewBox()->currentValue()->value();
601 if (!useViewBox.isEmpty())
603 if (!renderer() || !renderer()->isSVGRoot())
605 if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage())
608 // If no viewBox is specified but non-relative width/height values, then we
609 // should always synthesize a viewBox if we're embedded through a SVGImage.
610 return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)));
613 FloatSize SVGSVGElement::currentViewportSize() const
618 if (renderer()->isSVGRoot()) {
619 LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect();
620 return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom());
623 FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport();
624 return FloatSize(viewportRect.width(), viewportRect.height());
627 bool SVGSVGElement::hasIntrinsicWidth() const
629 return width()->currentValue()->unitType() != LengthTypePercentage;
632 bool SVGSVGElement::hasIntrinsicHeight() const
634 return height()->currentValue()->unitType() != LengthTypePercentage;
637 Length SVGSVGElement::intrinsicWidth() const
639 if (width()->currentValue()->unitType() == LengthTypePercentage)
640 return Length(0, Fixed);
642 SVGLengthContext lengthContext(this);
643 return Length(width()->currentValue()->value(lengthContext), Fixed);
646 Length SVGSVGElement::intrinsicHeight() const
648 if (height()->currentValue()->unitType() == LengthTypePercentage)
649 return Length(0, Fixed);
651 SVGLengthContext lengthContext(this);
652 return Length(height()->currentValue()->value(lengthContext), Fixed);
655 AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const
657 if (!m_useCurrentView || !m_viewSpec)
658 return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
660 AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio()->currentValue(), viewWidth, viewHeight);
661 RefPtr<SVGTransformList> transformList = m_viewSpec->transform();
662 if (transformList->isEmpty())
665 AffineTransform transform;
666 if (transformList->concatenate(transform))
672 void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode)
674 RenderObject* renderer = this->renderer();
675 SVGViewSpec* view = m_viewSpec.get();
679 bool hadUseCurrentView = m_useCurrentView;
680 m_useCurrentView = false;
682 if (fragmentIdentifier.startsWith("xpointer(")) {
683 // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491)
684 if (renderer && hadUseCurrentView)
685 markForLayoutAndParentResourceInvalidation(renderer);
689 if (fragmentIdentifier.startsWith("svgView(")) {
691 view = currentView(); // Create the SVGViewSpec.
693 if (view->parseViewSpec(fragmentIdentifier))
694 m_useCurrentView = true;
698 if (renderer && (hadUseCurrentView || m_useCurrentView))
699 markForLayoutAndParentResourceInvalidation(renderer);
703 // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView
704 // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport.
705 // Any view specification attributes included on the given ‘view’ element override the corresponding view specification
706 // attributes on the closest ancestor ‘svg’ element.
707 if (isSVGViewElement(anchorNode)) {
708 SVGViewElement& viewElement = toSVGViewElement(*anchorNode);
710 if (SVGSVGElement* svg = viewElement.ownerSVGElement()) {
711 svg->inheritViewAttributes(&viewElement);
713 if (RenderObject* renderer = svg->renderer())
714 markForLayoutAndParentResourceInvalidation(renderer);
718 // FIXME: We need to decide which <svg> to focus on, and zoom to it.
719 // FIXME: We need to actually "highlight" the viewTarget(s).
722 void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement)
724 SVGViewSpec* view = currentView();
725 m_useCurrentView = true;
727 if (viewElement->hasAttribute(SVGNames::viewBoxAttr))
728 view->viewBox()->baseValue()->setValue(viewElement->viewBox()->currentValue()->value());
730 view->viewBox()->baseValue()->setValue(viewBox()->currentValue()->value());
732 if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) {
733 view->preserveAspectRatio()->baseValue()->setAlign(viewElement->preserveAspectRatio()->currentValue()->align());
734 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(viewElement->preserveAspectRatio()->currentValue()->meetOrSlice());
736 view->preserveAspectRatio()->baseValue()->setAlign(preserveAspectRatio()->currentValue()->align());
737 view->preserveAspectRatio()->baseValue()->setMeetOrSlice(preserveAspectRatio()->currentValue()->meetOrSlice());
740 if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr))
741 view->setZoomAndPan(viewElement->zoomAndPan());
743 view->setZoomAndPan(zoomAndPan());
746 void SVGSVGElement::finishParsingChildren()
748 SVGGraphicsElement::finishParsingChildren();
750 // The outermost SVGSVGElement SVGLoad event is fired through Document::dispatchWindowLoadEvent.
751 if (isOutermostSVGSVGElement())
754 // finishParsingChildren() is called when the close tag is reached for an element (e.g. </svg>)
755 // we send SVGLoad events here if we can, otherwise they'll be sent when any required loads finish
756 sendSVGLoadEventIfPossible();
759 void SVGSVGElement::trace(Visitor* visitor)
761 visitor->trace(m_timeContainer);
762 visitor->trace(m_viewSpec);
763 SVGGraphicsElement::trace(visitor);