diff options
Diffstat (limited to 'Source/WebCore/svg/SVGSVGElement.cpp')
-rw-r--r-- | Source/WebCore/svg/SVGSVGElement.cpp | 592 |
1 files changed, 260 insertions, 332 deletions
diff --git a/Source/WebCore/svg/SVGSVGElement.cpp b/Source/WebCore/svg/SVGSVGElement.cpp index 5a9fe0ce5..13738b754 100644 --- a/Source/WebCore/svg/SVGSVGElement.cpp +++ b/Source/WebCore/svg/SVGSVGElement.cpp @@ -1,7 +1,8 @@ /* * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@kde.org> - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007-2017 Apple Inc. All rights reserved. + * Copyright (C) 2014 Adobe Systems Incorporated. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,44 +21,30 @@ */ #include "config.h" - -#if ENABLE(SVG) #include "SVGSVGElement.h" -#include "AffineTransform.h" -#include "Attribute.h" #include "CSSHelper.h" -#include "Document.h" +#include "DOMWrapperWorld.h" #include "ElementIterator.h" -#include "EventListener.h" #include "EventNames.h" -#include "FloatConversion.h" -#include "FloatRect.h" -#include "Frame.h" #include "FrameSelection.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "HTMLNames.h" -#include "RenderObject.h" +#include "MainFrame.h" #include "RenderSVGResource.h" -#include "RenderSVGModelObject.h" #include "RenderSVGRoot.h" #include "RenderSVGViewportContainer.h" #include "RenderView.h" -#include "RenderWidget.h" #include "SMILTimeContainer.h" #include "SVGAngle.h" -#include "SVGElementInstance.h" -#include "SVGFitToViewBox.h" -#include "SVGNames.h" -#include "SVGPreserveAspectRatio.h" +#include "SVGLength.h" +#include "SVGMatrix.h" +#include "SVGNumber.h" +#include "SVGPoint.h" +#include "SVGRect.h" +#include "SVGStaticPropertyTearOff.h" #include "SVGTransform.h" -#include "SVGTransformList.h" #include "SVGViewElement.h" #include "SVGViewSpec.h" -#include "SVGZoomEvent.h" #include "StaticNodeList.h" -#include <wtf/StdLibExtras.h> namespace WebCore { @@ -85,81 +72,80 @@ inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document& docu : SVGGraphicsElement(tagName, document) , m_x(LengthModeWidth) , m_y(LengthModeHeight) - , m_width(LengthModeWidth, "100%") - , m_height(LengthModeHeight, "100%") - , m_useCurrentView(false) - , m_zoomAndPan(SVGZoomAndPanMagnify) + , m_width(LengthModeWidth, ASCIILiteral("100%")) + , m_height(LengthModeHeight, ASCIILiteral("100%")) , m_timeContainer(SMILTimeContainer::create(this)) { ASSERT(hasTagName(SVGNames::svgTag)); registerAnimatedPropertiesForSVGSVGElement(); - document.registerForPageCacheSuspensionCallbacks(this); + document.registerForDocumentSuspensionCallbacks(this); +} + +Ref<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document& document) +{ + return adoptRef(*new SVGSVGElement(tagName, document)); } -PassRefPtr<SVGSVGElement> SVGSVGElement::create(const QualifiedName& tagName, Document& document) +Ref<SVGSVGElement> SVGSVGElement::create(Document& document) { - return adoptRef(new SVGSVGElement(tagName, document)); + return create(SVGNames::svgTag, document); } SVGSVGElement::~SVGSVGElement() { if (m_viewSpec) m_viewSpec->resetContextElement(); - document().unregisterForPageCacheSuspensionCallbacks(this); - // There are cases where removedFromDocument() is not called. - // see ContainerNode::removeAllChildren, called by its destructor. - document().accessSVGExtensions()->removeTimeContainer(this); + document().unregisterForDocumentSuspensionCallbacks(this); + document().accessSVGExtensions().removeTimeContainer(this); } -void SVGSVGElement::didMoveToNewDocument(Document* oldDocument) +void SVGSVGElement::didMoveToNewDocument(Document& oldDocument) { - if (oldDocument) - oldDocument->unregisterForPageCacheSuspensionCallbacks(this); - document().registerForPageCacheSuspensionCallbacks(this); + oldDocument.unregisterForDocumentSuspensionCallbacks(this); + document().registerForDocumentSuspensionCallbacks(this); SVGGraphicsElement::didMoveToNewDocument(oldDocument); } const AtomicString& SVGSVGElement::contentScriptType() const { - DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/ecmascript", AtomicString::ConstructFromLiteral)); - const AtomicString& n = fastGetAttribute(SVGNames::contentScriptTypeAttr); - return n.isNull() ? defaultValue : n; + static NeverDestroyed<AtomicString> defaultScriptType { "text/ecmascript" }; + const AtomicString& type = attributeWithoutSynchronization(SVGNames::contentScriptTypeAttr); + return type.isNull() ? defaultScriptType.get() : type; } void SVGSVGElement::setContentScriptType(const AtomicString& type) { - setAttribute(SVGNames::contentScriptTypeAttr, type); + setAttributeWithoutSynchronization(SVGNames::contentScriptTypeAttr, type); } const AtomicString& SVGSVGElement::contentStyleType() const { - DEFINE_STATIC_LOCAL(const AtomicString, defaultValue, ("text/css", AtomicString::ConstructFromLiteral)); - const AtomicString& n = fastGetAttribute(SVGNames::contentStyleTypeAttr); - return n.isNull() ? defaultValue : n; + static NeverDestroyed<AtomicString> defaultStyleType { "text/css" }; + const AtomicString& type = attributeWithoutSynchronization(SVGNames::contentStyleTypeAttr); + return type.isNull() ? defaultStyleType.get() : type; } void SVGSVGElement::setContentStyleType(const AtomicString& type) { - setAttribute(SVGNames::contentStyleTypeAttr, type); + setAttributeWithoutSynchronization(SVGNames::contentStyleTypeAttr, type); } -FloatRect SVGSVGElement::viewport() const +Ref<SVGRect> SVGSVGElement::viewport() const { - // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here. - // As we have no test coverage for this, we're going to disable it completly for now. - return FloatRect(); + // FIXME: Not implemented. + return SVGRect::create(); } float SVGSVGElement::pixelUnitToMillimeterX() const { - // 2.54 / cssPixelsPerInch gives CM. - return (2.54f / cssPixelsPerInch) * 10.0f; + // There are 25.4 millimeters in an inch. + return 25.4f / cssPixelsPerInch; } float SVGSVGElement::pixelUnitToMillimeterY() const { - // 2.54 / cssPixelsPerInch gives CM. - return (2.54f / cssPixelsPerInch) * 10.0f; + // There are 25.4 millimeters in an inch. + return 25.4f / cssPixelsPerInch; } float SVGSVGElement::screenPixelToMillimeterX() const @@ -172,49 +158,47 @@ float SVGSVGElement::screenPixelToMillimeterY() const return pixelUnitToMillimeterY(); } -SVGViewSpec* SVGSVGElement::currentView() +SVGViewSpec& SVGSVGElement::currentView() { if (!m_viewSpec) - m_viewSpec = SVGViewSpec::create(this); - return m_viewSpec.get(); + m_viewSpec = SVGViewSpec::create(*this); + return *m_viewSpec; } -float SVGSVGElement::currentScale() const +Frame* SVGSVGElement::frameForCurrentScale() const { - if (!inDocument() || !isOutermostSVGSVGElement()) - return 1; - + // The behavior of currentScale() is undefined when we're dealing with non-standalone SVG documents. + // If the document is embedded, the scaling is handled by the host renderer. + if (!isConnected() || !isOutermostSVGSVGElement()) + return nullptr; Frame* frame = document().frame(); - if (!frame) - return 1; + return frame && frame->isMainFrame() ? frame : nullptr; +} - // The behaviour of currentScale() is undefined, when we're dealing with non-standalone SVG documents. - // If the svg is embedded, the scaling is handled by the host renderer, so when asking from inside - // the SVG document, a scale value of 1 seems reasonable, as it doesn't know anything about the parent scale. - return frame->tree().parent() ? 1 : frame->pageZoomFactor(); +float SVGSVGElement::currentScale() const +{ + // When asking from inside an embedded SVG document, a scale value of 1 seems reasonable, as it doesn't + // know anything about the parent scale. + Frame* frame = frameForCurrentScale(); + return frame ? frame->pageZoomFactor() : 1; } void SVGSVGElement::setCurrentScale(float scale) { - if (!inDocument() || !isOutermostSVGSVGElement()) - return; - - Frame* frame = document().frame(); - if (!frame) - return; - - // The behaviour of setCurrentScale() is undefined, when we're dealing with non-standalone SVG documents. - // We choose the ignore this call, it's pretty useless to support calling setCurrentScale() from within - // an embedded SVG document, for the same reasons as in currentScale() - needs resolution by SVG WG. - if (frame->tree().parent()) - return; + if (Frame* frame = frameForCurrentScale()) + frame->setPageZoomFactor(scale); +} - frame->setPageZoomFactor(scale); +Ref<SVGPoint> SVGSVGElement::currentTranslate() +{ + return SVGStaticPropertyTearOff<SVGSVGElement, SVGPoint>::create(*this, m_currentTranslate, &SVGSVGElement::updateCurrentTranslate); } void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation) { - m_translation = translation; + if (m_currentTranslate == translation) + return; + m_currentTranslate = translation; updateCurrentTranslate(); } @@ -222,83 +206,92 @@ void SVGSVGElement::updateCurrentTranslate() { if (RenderObject* object = renderer()) object->setNeedsLayout(); - if (parentNode() == &document() && document().renderView()) document().renderView()->repaint(); } void SVGSVGElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { - SVGParsingError parseError = NoError; - if (!nearestViewportElement()) { - bool setListener = true; - - // Only handle events if we're the outermost <svg> element - if (name == HTMLNames::onunloadAttr) - document().setWindowAttributeEventListener(eventNames().unloadEvent, name, value); - else if (name == HTMLNames::onresizeAttr) - document().setWindowAttributeEventListener(eventNames().resizeEvent, name, value); - else if (name == HTMLNames::onscrollAttr) - document().setWindowAttributeEventListener(eventNames().scrollEvent, name, value); - else if (name == SVGNames::onzoomAttr) - document().setWindowAttributeEventListener(eventNames().zoomEvent, name, value); - else - setListener = false; - - if (setListener) + // For these events, the outermost <svg> element works like a <body> element does, + // setting certain event handlers directly on the window object. + if (name == HTMLNames::onunloadAttr) { + document().setWindowAttributeEventListener(eventNames().unloadEvent, name, value, mainThreadNormalWorld()); + return; + } + if (name == HTMLNames::onresizeAttr) { + document().setWindowAttributeEventListener(eventNames().resizeEvent, name, value, mainThreadNormalWorld()); + return; + } + if (name == HTMLNames::onscrollAttr) { + document().setWindowAttributeEventListener(eventNames().scrollEvent, name, value, mainThreadNormalWorld()); return; + } + if (name == SVGNames::onzoomAttr) { + document().setWindowAttributeEventListener(eventNames().zoomEvent, name, value, mainThreadNormalWorld()); + return; + } + } + + // For these events, any <svg> element works like a <body> element does, + // setting certain event handlers directly on the window object. + // FIXME: Why different from the events above that work only on the outermost <svg> element? + if (name == HTMLNames::onabortAttr) { + document().setWindowAttributeEventListener(eventNames().abortEvent, name, value, mainThreadNormalWorld()); + return; + } + if (name == HTMLNames::onerrorAttr) { + document().setWindowAttributeEventListener(eventNames().errorEvent, name, value, mainThreadNormalWorld()); + return; } - if (name == HTMLNames::onabortAttr) - document().setWindowAttributeEventListener(eventNames().abortEvent, name, value); - else if (name == HTMLNames::onerrorAttr) - document().setWindowAttributeEventListener(eventNames().errorEvent, name, value); - else if (name == SVGNames::xAttr) - setXBaseValue(SVGLength::construct(LengthModeWidth, value, parseError)); + SVGParsingError parseError = NoError; + + if (name == SVGNames::xAttr) + setXBaseValue(SVGLengthValue::construct(LengthModeWidth, value, parseError)); else if (name == SVGNames::yAttr) - setYBaseValue(SVGLength::construct(LengthModeHeight, value, parseError)); - else if (name == SVGNames::widthAttr) - setWidthBaseValue(SVGLength::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths)); - else if (name == SVGNames::heightAttr) - setHeightBaseValue(SVGLength::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths)); - else if (SVGLangSpace::parseAttribute(name, value) - || SVGExternalResourcesRequired::parseAttribute(name, value) - || SVGFitToViewBox::parseAttribute(this, name, value) - || SVGZoomAndPan::parseAttribute(this, name, value)) { - } else - SVGGraphicsElement::parseAttribute(name, value); + setYBaseValue(SVGLengthValue::construct(LengthModeHeight, value, parseError)); + else if (name == SVGNames::widthAttr) { + auto length = SVGLengthValue::construct(LengthModeWidth, value, parseError, ForbidNegativeLengths); + if (parseError != NoError || value.isEmpty()) { + // FIXME: This is definitely the correct behavior for a missing/removed attribute. + // Not sure it's correct for the empty string or for something that can't be parsed. + length = SVGLengthValue(LengthModeWidth, ASCIILiteral("100%")); + } + setWidthBaseValue(length); + } else if (name == SVGNames::heightAttr) { + auto length = SVGLengthValue::construct(LengthModeHeight, value, parseError, ForbidNegativeLengths); + if (parseError != NoError || value.isEmpty()) { + // FIXME: This is definitely the correct behavior for a removed attribute. + // Not sure it's correct for the empty string or for something that can't be parsed. + length = SVGLengthValue(LengthModeHeight, ASCIILiteral("100%")); + } + setHeightBaseValue(length); + } reportAttributeParsingError(parseError, name, value); + + SVGExternalResourcesRequired::parseAttribute(name, value); + SVGFitToViewBox::parseAttribute(this, name, value); + SVGZoomAndPan::parseAttribute(*this, name, value); + SVGGraphicsElement::parseAttribute(name, value); } void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) { bool updateRelativeLengthsOrViewBox = false; - bool widthChanged = attrName == SVGNames::widthAttr; - if (widthChanged - || attrName == SVGNames::heightAttr - || attrName == SVGNames::xAttr - || attrName == SVGNames::yAttr) { + if (attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr) { + invalidateSVGPresentationAttributeStyle(); updateRelativeLengthsOrViewBox = true; - updateRelativeLengthsInformation(); - - // At the SVG/HTML boundary (aka RenderSVGRoot), the width attribute can - // affect the replaced size so we need to mark it for updating. - if (widthChanged) { - RenderObject* renderObject = renderer(); - if (renderObject && renderObject->isSVGRoot()) - toRenderSVGRoot(renderObject)->setNeedsLayoutAndPrefWidthsRecalc(); - } } if (SVGFitToViewBox::isKnownAttribute(attrName)) { updateRelativeLengthsOrViewBox = true; - if (RenderObject* object = renderer()) - object->setNeedsTransformUpdate(); + if (auto* renderer = this->renderer()) + renderer->setNeedsTransformUpdate(); } - SVGElementInstance::InvalidationGuard invalidationGuard(this); + InstanceInvalidationGuard guard(*this); if (updateRelativeLengthsOrViewBox || SVGLangSpace::isKnownAttribute(attrName) @@ -312,66 +305,52 @@ void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) SVGGraphicsElement::svgAttributeChanged(attrName); } -unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */) +unsigned SVGSVGElement::suspendRedraw(unsigned) { - // FIXME: Implement me (see bug 11275) return 0; } -void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */) +void SVGSVGElement::unsuspendRedraw(unsigned) { - // FIXME: Implement me (see bug 11275) } void SVGSVGElement::unsuspendRedrawAll() { - // FIXME: Implement me (see bug 11275) } void SVGSVGElement::forceRedraw() { - // FIXME: Implement me (see bug 11275) } -PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const +Ref<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(SVGRect& rect, SVGElement* referenceElement, bool (*checkFunction)(const SVGElement*, SVGRect&)) { Vector<Ref<Element>> elements; - - for (auto& svgElement : descendantsOfType<SVGElement>(*(referenceElement ? referenceElement : this))) { - if (collect == CollectIntersectionList) { - if (RenderSVGModelObject::checkIntersection(svgElement.renderer(), rect)) - elements.append(const_cast<SVGElement&>(svgElement)); - } else { - if (RenderSVGModelObject::checkEnclosure(svgElement.renderer(), rect)) - elements.append(const_cast<SVGElement&>(svgElement)); - } + for (auto& element : descendantsOfType<SVGElement>(referenceElement ? *referenceElement : *this)) { + if (checkFunction(&element, rect)) + elements.append(element); } - - return StaticElementList::adopt(elements); + return StaticElementList::create(WTFMove(elements)); } -PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(const FloatRect& rect, SVGElement* referenceElement) const +Ref<NodeList> SVGSVGElement::getIntersectionList(SVGRect& rect, SVGElement* referenceElement) { - return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList); + document().updateLayoutIgnorePendingStylesheets(); + return collectIntersectionOrEnclosureList(rect, referenceElement, checkIntersection); } -PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(const FloatRect& rect, SVGElement* referenceElement) const +Ref<NodeList> SVGSVGElement::getEnclosureList(SVGRect& rect, SVGElement* referenceElement) { - return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList); + return collectIntersectionOrEnclosureList(rect, referenceElement, checkEnclosure); } -bool SVGSVGElement::checkIntersection(const SVGElement* element, const FloatRect& rect) const +bool SVGSVGElement::checkIntersection(const SVGElement* element, SVGRect& rect) { - if (!element) - return false; - return RenderSVGModelObject::checkIntersection(element->renderer(), rect); + return element && RenderSVGModelObject::checkIntersection(element->renderer(), rect.propertyReference()); } -bool SVGSVGElement::checkEnclosure(const SVGElement* element, const FloatRect& rect) const +bool SVGSVGElement::checkEnclosure(const SVGElement* element, SVGRect& rect) { - if (!element) - return false; - return RenderSVGModelObject::checkEnclosure(element->renderer(), rect); + return element && RenderSVGModelObject::checkEnclosure(element->renderer(), rect.propertyReference()); } void SVGSVGElement::deselectAll() @@ -380,44 +359,44 @@ void SVGSVGElement::deselectAll() frame->selection().clear(); } -float SVGSVGElement::createSVGNumber() +Ref<SVGNumber> SVGSVGElement::createSVGNumber() { - return 0.0f; + return SVGNumber::create(); } -SVGLength SVGSVGElement::createSVGLength() +Ref<SVGLength> SVGSVGElement::createSVGLength() { - return SVGLength(); + return SVGLength::create(); } -SVGAngle SVGSVGElement::createSVGAngle() +Ref<SVGAngle> SVGSVGElement::createSVGAngle() { - return SVGAngle(); + return SVGAngle::create(); } -SVGPoint SVGSVGElement::createSVGPoint() +Ref<SVGPoint> SVGSVGElement::createSVGPoint() { - return SVGPoint(); + return SVGPoint::create(); } -SVGMatrix SVGSVGElement::createSVGMatrix() +Ref<SVGMatrix> SVGSVGElement::createSVGMatrix() { - return SVGMatrix(); + return SVGMatrix::create(); } -FloatRect SVGSVGElement::createSVGRect() +Ref<SVGRect> SVGSVGElement::createSVGRect() { - return FloatRect(); + return SVGRect::create(); } -SVGTransform SVGSVGElement::createSVGTransform() +Ref<SVGTransform> SVGSVGElement::createSVGTransform() { - return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX); + return SVGTransform::create(SVGTransformValue { SVGTransformValue::SVG_TRANSFORM_MATRIX }); } -SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix) +Ref<SVGTransform> SVGSVGElement::createSVGTransformFromMatrix(SVGMatrix& matrix) { - return SVGTransform(static_cast<const AffineTransform&>(matrix)); + return SVGTransform::create(SVGTransformValue { matrix.propertyReference() }); } AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const @@ -433,7 +412,7 @@ AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMSc SVGLengthContext lengthContext(this); transform.translate(x().value(lengthContext), y().value(lengthContext)); } else if (mode == SVGLocatable::ScreenScope) { - if (RenderObject* renderer = this->renderer()) { + if (auto* renderer = this->renderer()) { FloatPoint location; float zoomFactor = 1; @@ -441,15 +420,15 @@ AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMSc // to map an element from SVG viewport coordinates to CSS box coordinates. // RenderSVGRoot's localToAbsolute method expects CSS box coordinates. // We also need to adjust for the zoom level factored into CSS coordinates (bug #96361). - if (renderer->isSVGRoot()) { - location = toRenderSVGRoot(renderer)->localToBorderBoxTransform().mapPoint(location); + if (is<RenderSVGRoot>(*renderer)) { + location = downcast<RenderSVGRoot>(*renderer).localToBorderBoxTransform().mapPoint(location); zoomFactor = 1 / renderer->style().effectiveZoom(); } // Translate in our CSS parent coordinate space // FIXME: This doesn't work correctly with CSS transforms. location = renderer->localToAbsolute(location, UseTransforms); - location.scale(zoomFactor, zoomFactor); + location.scale(zoomFactor); // Be careful here! localToBorderBoxTransform() included the x/y offset coming from the viewBoxToViewTransform(), // so we have to subtract it here (original cause of bug #27183) @@ -457,9 +436,9 @@ AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMSc // Respect scroll offset. if (FrameView* view = document().view()) { - LayoutSize scrollOffset = view->scrollOffset(); - scrollOffset.scale(zoomFactor); - transform.translate(-scrollOffset.width(), -scrollOffset.height()); + LayoutPoint scrollPosition = view->scrollPosition(); + scrollPosition.scale(zoomFactor); + transform.translate(-scrollPosition.x(), -scrollPosition.y()); } } } @@ -469,6 +448,8 @@ AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMSc bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style) { + if (!isValid()) + return false; // FIXME: We should respect display: none on the documentElement svg element // but many things in FrameView and SVGImage depend on the RenderSVGRoot when // they should instead depend on the RenderView. @@ -478,32 +459,31 @@ bool SVGSVGElement::rendererIsNeeded(const RenderStyle& style) return StyledElement::rendererIsNeeded(style); } -RenderPtr<RenderElement> SVGSVGElement::createElementRenderer(PassRef<RenderStyle> style) +RenderPtr<RenderElement> SVGSVGElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { if (isOutermostSVGSVGElement()) - return createRenderer<RenderSVGRoot>(*this, std::move(style)); - - return createRenderer<RenderSVGViewportContainer>(*this, std::move(style)); + return createRenderer<RenderSVGRoot>(*this, WTFMove(style)); + return createRenderer<RenderSVGViewportContainer>(*this, WTFMove(style)); } Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode& rootParent) { - if (rootParent.inDocument()) { - document().accessSVGExtensions()->addTimeContainer(this); + if (rootParent.isConnected()) { + document().accessSVGExtensions().addTimeContainer(this); // Animations are started at the end of document parsing and after firing the load event, // but if we miss that train (deferred programmatic element insertion for example) we need // to initialize the time container here. - if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !timeContainer()->isStarted()) - timeContainer()->begin(); + if (!document().parsing() && !document().processingLoadEvent() && document().loadEventFinished() && !m_timeContainer->isStarted()) + m_timeContainer->begin(); } return SVGGraphicsElement::insertedInto(rootParent); } void SVGSVGElement::removedFrom(ContainerNode& rootParent) { - if (rootParent.inDocument()) - document().accessSVGExtensions()->removeTimeContainer(this); + if (rootParent.isConnected()) + document().accessSVGExtensions().removeTimeContainer(this); SVGGraphicsElement::removedFrom(rootParent); } @@ -531,10 +511,9 @@ float SVGSVGElement::getCurrentTime() const void SVGSVGElement::setCurrentTime(float seconds) { - if (std::isnan(seconds)) + if (!std::isfinite(seconds)) return; - seconds = std::max(seconds, 0.0f); - m_timeContainer->setElapsed(seconds); + m_timeContainer->setElapsed(std::max(seconds, 0.0f)); } bool SVGSVGElement::selfHasRelativeLengths() const @@ -551,117 +530,72 @@ FloatRect SVGSVGElement::currentViewBoxRect() const if (m_useCurrentView) return m_viewSpec ? m_viewSpec->viewBox() : FloatRect(); - FloatRect useViewBox = viewBox(); - if (!useViewBox.isEmpty()) - return useViewBox; - if (!renderer() || !renderer()->isSVGRoot()) - return FloatRect(); - if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage()) - return FloatRect(); + FloatRect viewBox = this->viewBox(); + if (!viewBox.isEmpty()) + return viewBox; + + if (!is<RenderSVGRoot>(renderer())) + return { }; + if (!downcast<RenderSVGRoot>(*renderer()).isEmbeddedThroughSVGImage()) + return { }; Length intrinsicWidth = this->intrinsicWidth(); Length intrinsicHeight = this->intrinsicHeight(); if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed()) - return FloatRect(); + return { }; // If no viewBox is specified but non-relative width/height values, then we // should always synthesize a viewBox if we're embedded through a SVGImage. - return FloatRect(FloatPoint(), FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0))); + return { 0, 0, floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0) }; } FloatSize SVGSVGElement::currentViewportSize() const { - Length intrinsicWidth = this->intrinsicWidth(); - Length intrinsicHeight = this->intrinsicHeight(); - if (intrinsicWidth.isFixed() && intrinsicHeight.isFixed()) - return FloatSize(floatValueForLength(intrinsicWidth, 0), floatValueForLength(intrinsicHeight, 0)); - - if (!renderer()) - return FloatSize(); + FloatSize viewportSize; - if (renderer()->isSVGRoot()) { - LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect(); - return FloatSize(contentBoxRect.width() / renderer()->style().effectiveZoom(), contentBoxRect.height() / renderer()->style().effectiveZoom()); + if (renderer()) { + if (is<RenderSVGRoot>(*renderer())) { + auto& root = downcast<RenderSVGRoot>(*renderer()); + viewportSize = root.contentBoxRect().size() / root.style().effectiveZoom(); + } else + viewportSize = downcast<RenderSVGViewportContainer>(*renderer()).viewport().size(); } - FloatRect viewportRect = toRenderSVGViewportContainer(renderer())->viewport(); - return FloatSize(viewportRect.width(), viewportRect.height()); -} - -bool SVGSVGElement::widthAttributeEstablishesViewport() const -{ - if (!renderer() || renderer()->isSVGViewportContainer()) - return true; - - // Spec: http://www.w3.org/TR/SVG/coords.html#ViewportSpace - // The ‘width’ attribute on the outermost svg element establishes the viewport's width, unless the following conditions are met: - // - the SVG content is a separately stored resource that is embedded by reference (such as the ‘object’ element in XHTML [XHTML]), or - // the SVG content is embedded inline within a containing document; - // - and the referencing element or containing document is styled using CSS [CSS2] or XSL [XSL]; - // - and there are CSS-compatible positioning properties ([CSS2], section 9.3) specified on the referencing element (e.g., the ‘object’ element) - // or on the containing document's outermost svg element that are sufficient to establish the width of the viewport. Under these conditions, - // the positioning properties establish the viewport's width. - RenderSVGRoot* root = toRenderSVGRoot(renderer()); + if (!viewportSize.isEmpty()) + return viewportSize; - // SVG embedded through object/embed/iframe. - if (root->isEmbeddedThroughFrameContainingSVGDocument()) - return !root->hasReplacedLogicalWidth() && !document().frame()->ownerRenderer()->hasReplacedLogicalWidth(); + if (!(hasIntrinsicWidth() && hasIntrinsicHeight())) + return { }; - // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. - if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this) - return !root->hasReplacedLogicalWidth(); - - return true; + return FloatSize(floatValueForLength(intrinsicWidth(), 0), floatValueForLength(intrinsicHeight(), 0)); } -bool SVGSVGElement::heightAttributeEstablishesViewport() const +bool SVGSVGElement::hasIntrinsicWidth() const { - if (!renderer() || renderer()->isSVGViewportContainer()) - return true; - - // Spec: http://www.w3.org/TR/SVG/coords.html#IntrinsicSizing - // Similarly, if there are positioning properties specified on the referencing element or on the outermost svg element - // that are sufficient to establish the height of the viewport, then these positioning properties establish the viewport's - // height; otherwise, the ‘height’ attribute on the outermost svg element establishes the viewport's height. - RenderSVGRoot* root = toRenderSVGRoot(renderer()); - - // SVG embedded through object/embed/iframe. - if (root->isEmbeddedThroughFrameContainingSVGDocument()) - return !root->hasReplacedLogicalHeight() && !document().frame()->ownerRenderer()->hasReplacedLogicalHeight(); - - // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. - if (root->isEmbeddedThroughSVGImage() || document().documentElement() != this) - return !root->hasReplacedLogicalHeight(); - - return true; + return width().unitType() != LengthTypePercentage; } -Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const +bool SVGSVGElement::hasIntrinsicHeight() const { - if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) { - if (width().unitType() == LengthTypePercentage) - return Length(width().valueAsPercentage() * 100, Percent); + return height().unitType() != LengthTypePercentage; +} - SVGLengthContext lengthContext(this); - return Length(width().value(lengthContext), Fixed); - } +Length SVGSVGElement::intrinsicWidth() const +{ + if (width().unitType() == LengthTypePercentage) + return Length(0, Fixed); - ASSERT(renderer()); - return renderer()->style().width(); + SVGLengthContext lengthContext(this); + return Length(width().value(lengthContext), Fixed); } -Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const +Length SVGSVGElement::intrinsicHeight() const { - if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) { - if (height().unitType() == LengthTypePercentage) - return Length(height().valueAsPercentage() * 100, Percent); + if (height().unitType() == LengthTypePercentage) + return Length(0, Fixed); - SVGLengthContext lengthContext(this); - return Length(height().value(lengthContext), Fixed); - } - - ASSERT(renderer()); - return renderer()->style().height(); + SVGLengthContext lengthContext(this); + return Length(height().value(lengthContext), Fixed); } AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const @@ -669,19 +603,12 @@ AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float vie if (!m_useCurrentView || !m_viewSpec) return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatio(), viewWidth, viewHeight); - AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio(), viewWidth, viewHeight); - const SVGTransformList& transformList = m_viewSpec->transformBaseValue(); - if (transformList.isEmpty()) - return ctm; - - AffineTransform transform; - if (transformList.concatenate(transform)) - ctm *= transform; - - return ctm; + AffineTransform transform = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatio(), viewWidth, viewHeight); + m_viewSpec->transformBaseValue().concatenate(transform); + return transform; } -void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode) +void SVGSVGElement::scrollToAnchor(const String& fragmentIdentifier, Element* anchorNode) { auto renderer = this->renderer(); SVGViewSpec* view = m_viewSpec.get(); @@ -700,32 +627,28 @@ void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* if (fragmentIdentifier.startsWith("svgView(")) { if (!view) - view = currentView(); // Create the SVGViewSpec. - + view = ¤tView(); // Create the SVGViewSpec. if (view->parseViewSpec(fragmentIdentifier)) m_useCurrentView = true; else view->reset(); - if (renderer && (hadUseCurrentView || m_useCurrentView)) RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); return; } - // Spec: If the SVG fragment identifier addresses a ‘view’ element within an SVG document (e.g., MyDrawing.svg#MyView - // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor ‘svg’ element is displayed in the viewport. - // Any view specification attributes included on the given ‘view’ element override the corresponding view specification - // attributes on the closest ancestor ‘svg’ element. - if (anchorNode && isSVGViewElement(anchorNode)) { - if (SVGViewElement* viewElement = toSVGViewElement(anchorNode)) { - SVGElement* element = SVGLocatable::nearestViewportElement(viewElement); - if (element->hasTagName(SVGNames::svgTag)) { - SVGSVGElement* svg = static_cast<SVGSVGElement*>(element); - svg->inheritViewAttributes(viewElement); - - if (RenderElement* renderer = svg->renderer()) - RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); - } + // Spec: If the SVG fragment identifier addresses a "view" element within an SVG document (e.g., MyDrawing.svg#MyView + // or MyDrawing.svg#xpointer(id('MyView'))) then the closest ancestor "svg" element is displayed in the viewport. + // Any view specification attributes included on the given "view" element override the corresponding view specification + // attributes on the closest ancestor "svg" element. + if (is<SVGViewElement>(anchorNode)) { + auto& viewElement = downcast<SVGViewElement>(*anchorNode); + auto* viewportElement = SVGLocatable::nearestViewportElement(&viewElement); + if (is<SVGSVGElement>(viewportElement)) { + auto& element = downcast<SVGSVGElement>(*viewportElement); + element.inheritViewAttributes(viewElement); + if (auto* renderer = element.renderer()) + RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer); } return; } @@ -734,33 +657,33 @@ void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* // FIXME: We need to actually "highlight" the viewTarget(s). } -void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement) +void SVGSVGElement::inheritViewAttributes(const SVGViewElement& viewElement) { - SVGViewSpec* view = currentView(); + SVGViewSpec& view = currentView(); m_useCurrentView = true; - if (viewElement->hasAttribute(SVGNames::viewBoxAttr)) - view->setViewBoxBaseValue(viewElement->viewBox()); + if (viewElement.hasAttribute(SVGNames::viewBoxAttr)) + view.setViewBoxBaseValue(viewElement.viewBox()); else - view->setViewBoxBaseValue(viewBox()); + view.setViewBoxBaseValue(viewBox()); - if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) - view->setPreserveAspectRatioBaseValue(viewElement->preserveAspectRatioBaseValue()); + if (viewElement.hasAttribute(SVGNames::preserveAspectRatioAttr)) + view.setPreserveAspectRatioBaseValue(viewElement.preserveAspectRatioBaseValue()); else - view->setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue()); + view.setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue()); - if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr)) - view->setZoomAndPanBaseValue(viewElement->zoomAndPan()); + if (viewElement.hasAttribute(SVGNames::zoomAndPanAttr)) + view.setZoomAndPanBaseValue(viewElement.zoomAndPan()); else - view->setZoomAndPanBaseValue(zoomAndPan()); + view.setZoomAndPanBaseValue(zoomAndPan()); } -void SVGSVGElement::documentWillSuspendForPageCache() +void SVGSVGElement::prepareForDocumentSuspension() { pauseAnimations(); } -void SVGSVGElement::documentDidResumeFromPageCache() +void SVGSVGElement::resumeFromDocumentSuspension() { unpauseAnimations(); } @@ -769,19 +692,24 @@ void SVGSVGElement::documentDidResumeFromPageCache() // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement Element* SVGSVGElement::getElementById(const AtomicString& id) { + if (id.isNull()) + return nullptr; + Element* element = treeScope().getElementById(id); - if (element && element->isDescendantOf(this)) + if (element && element->isDescendantOf(*this)) return element; - - // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will - // be returned. - for (auto& element : descendantsOfType<Element>(*this)) { - if (element.getIdAttribute() == id) - return &element; + if (treeScope().containsMultipleElementsWithId(id)) { + for (auto* element : *treeScope().getAllElementsById(id)) { + if (element->isDescendantOf(*this)) + return element; + } } - return 0; + return nullptr; } +bool SVGSVGElement::isValid() const +{ + return SVGTests::isValid(); } -#endif // ENABLE(SVG) +} |