/* * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis * Copyright (C) 2007 Apple Inc. 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 * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "core/svg/SVGSVGElement.h" #include "HTMLNames.h" #include "SVGNames.h" #include "bindings/v8/ScriptEventListener.h" #include "core/css/CSSHelper.h" #include "core/dom/Document.h" #include "core/dom/EventListener.h" #include "core/dom/EventNames.h" #include "core/dom/NodeTraversal.h" #include "core/dom/StaticNodeList.h" #include "core/editing/FrameSelection.h" #include "core/page/Frame.h" #include "core/page/FrameTree.h" #include "core/page/FrameView.h" #include "core/platform/FloatConversion.h" #include "core/platform/graphics/FloatRect.h" #include "core/platform/graphics/transforms/AffineTransform.h" #include "core/rendering/RenderObject.h" #include "core/rendering/RenderPart.h" #include "core/rendering/svg/RenderSVGModelObject.h" #include "core/rendering/svg/RenderSVGResource.h" #include "core/rendering/svg/RenderSVGRoot.h" #include "core/rendering/svg/RenderSVGViewportContainer.h" #include "core/svg/SVGAngle.h" #include "core/svg/SVGElementInstance.h" #include "core/svg/SVGFitToViewBox.h" #include "core/svg/SVGPreserveAspectRatio.h" #include "core/svg/SVGTransform.h" #include "core/svg/SVGTransformList.h" #include "core/svg/SVGViewElement.h" #include "core/svg/SVGViewSpec.h" #include "core/svg/animation/SMILTimeContainer.h" #include "wtf/StdLibExtras.h" namespace WebCore { // Animated property definitions DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::xAttr, X, x) DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::yAttr, Y, y) DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::widthAttr, Width, width) DEFINE_ANIMATED_LENGTH(SVGSVGElement, SVGNames::heightAttr, Height, height) DEFINE_ANIMATED_BOOLEAN(SVGSVGElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired) DEFINE_ANIMATED_PRESERVEASPECTRATIO(SVGSVGElement, SVGNames::preserveAspectRatioAttr, PreserveAspectRatio, preserveAspectRatio) DEFINE_ANIMATED_RECT(SVGSVGElement, SVGNames::viewBoxAttr, ViewBox, viewBox) BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGSVGElement) REGISTER_LOCAL_ANIMATED_PROPERTY(x) REGISTER_LOCAL_ANIMATED_PROPERTY(y) REGISTER_LOCAL_ANIMATED_PROPERTY(width) REGISTER_LOCAL_ANIMATED_PROPERTY(height) REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired) REGISTER_LOCAL_ANIMATED_PROPERTY(viewBox) REGISTER_LOCAL_ANIMATED_PROPERTY(preserveAspectRatio) REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGraphicsElement) END_REGISTER_ANIMATED_PROPERTIES inline SVGSVGElement::SVGSVGElement(const QualifiedName& tagName, Document* doc) : SVGGraphicsElement(tagName, doc) , m_x(LengthModeWidth) , m_y(LengthModeHeight) , m_width(LengthModeWidth, "100%") , m_height(LengthModeHeight, "100%") , m_useCurrentView(false) , m_zoomAndPan(SVGZoomAndPanMagnify) , m_timeContainer(SMILTimeContainer::create(this)) { ASSERT(hasTagName(SVGNames::svgTag)); ScriptWrappable::init(this); registerAnimatedPropertiesForSVGSVGElement(); } PassRefPtr SVGSVGElement::create(const QualifiedName& tagName, Document* document) { return adoptRef(new SVGSVGElement(tagName, document)); } SVGSVGElement::~SVGSVGElement() { if (m_viewSpec) m_viewSpec->resetContextElement(); // There are cases where removedFromDocument() is not called. // see ContainerNode::removeAllChildren, called by its destructor. document()->accessSVGExtensions()->removeTimeContainer(this); } 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; } void SVGSVGElement::setContentScriptType(const AtomicString& type) { setAttribute(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; } void SVGSVGElement::setContentStyleType(const AtomicString& type) { setAttribute(SVGNames::contentStyleTypeAttr, type); } 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 SVGRect(); } float SVGSVGElement::pixelUnitToMillimeterX() const { // 2.54 / cssPixelsPerInch gives CM. return (2.54f / cssPixelsPerInch) * 10.0f; } float SVGSVGElement::pixelUnitToMillimeterY() const { // 2.54 / cssPixelsPerInch gives CM. return (2.54f / cssPixelsPerInch) * 10.0f; } float SVGSVGElement::screenPixelToMillimeterX() const { return pixelUnitToMillimeterX(); } float SVGSVGElement::screenPixelToMillimeterY() const { return pixelUnitToMillimeterY(); } SVGViewSpec* SVGSVGElement::currentView() { if (!m_viewSpec) m_viewSpec = SVGViewSpec::create(this); return m_viewSpec.get(); } float SVGSVGElement::currentScale() const { if (!inDocument() || !isOutermostSVGSVGElement()) return 1; Frame* frame = document()->frame(); if (!frame) return 1; FrameTree* frameTree = frame->tree(); ASSERT(frameTree); // 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 frameTree->parent() ? 1 : frame->pageZoomFactor(); } void SVGSVGElement::setCurrentScale(float scale) { if (!inDocument() || !isOutermostSVGSVGElement()) return; Frame* frame = document()->frame(); if (!frame) return; FrameTree* frameTree = frame->tree(); ASSERT(frameTree); // 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 (frameTree->parent()) return; frame->setPageZoomFactor(scale); } void SVGSVGElement::setCurrentTranslate(const FloatPoint& translation) { m_translation = translation; updateCurrentTranslate(); } void SVGSVGElement::updateCurrentTranslate() { if (RenderObject* object = renderer()) object->setNeedsLayout(); if (parentNode() == document() && document()->renderer()) document()->renderer()->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 element if (name == HTMLNames::onunloadAttr) document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), name, value)); else if (name == HTMLNames::onresizeAttr) document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), name, value)); else if (name == HTMLNames::onscrollAttr) document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), name, value)); else if (name == SVGNames::onzoomAttr) document()->setWindowAttributeEventListener(eventNames().zoomEvent, createAttributeEventListener(document()->frame(), name, value)); else setListener = false; if (setListener) return; } if (name == HTMLNames::onabortAttr) document()->setWindowAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(document()->frame(), name, value)); else if (name == HTMLNames::onerrorAttr) document()->setWindowAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(document()->frame(), name, value)); else if (name == SVGNames::xAttr) setXBaseValue(SVGLength::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); reportAttributeParsingError(parseError, 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) { 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(); } SVGElementInstance::InvalidationGuard invalidationGuard(this); if (updateRelativeLengthsOrViewBox || SVGLangSpace::isKnownAttribute(attrName) || SVGExternalResourcesRequired::isKnownAttribute(attrName) || SVGZoomAndPan::isKnownAttribute(attrName)) { if (renderer()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer()); return; } SVGGraphicsElement::svgAttributeChanged(attrName); } unsigned SVGSVGElement::suspendRedraw(unsigned /* maxWaitMilliseconds */) { // FIXME: Implement me (see bug 11275) return 0; } void SVGSVGElement::unsuspendRedraw(unsigned /* suspendHandleId */) { // 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 SVGSVGElement::collectIntersectionOrEnclosureList(const SVGRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const { Vector > nodes; Element* element = ElementTraversal::next(referenceElement ? referenceElement : this); while (element) { if (element->isSVGElement()) { SVGElement* svgElement = toSVGElement(element); if (collect == CollectIntersectionList) { if (checkIntersection(svgElement, rect)) nodes.append(element); } else { if (checkEnclosure(svgElement, rect)) nodes.append(element); } } element = ElementTraversal::next(element, referenceElement ? referenceElement : this); } return StaticNodeList::adopt(nodes); } PassRefPtr SVGSVGElement::getIntersectionList(const SVGRect& rect, SVGElement* referenceElement) const { return collectIntersectionOrEnclosureList(rect, referenceElement, CollectIntersectionList); } PassRefPtr SVGSVGElement::getEnclosureList(const SVGRect& rect, SVGElement* referenceElement) const { return collectIntersectionOrEnclosureList(rect, referenceElement, CollectEnclosureList); } bool SVGSVGElement::checkIntersection(SVGElement* element, const SVGRect& rect) const { if (!element) return false; return RenderSVGModelObject::checkIntersection(element->renderer(), rect); } bool SVGSVGElement::checkEnclosure(SVGElement* element, const SVGRect& rect) const { if (!element) return false; return RenderSVGModelObject::checkEnclosure(element->renderer(), rect); } void SVGSVGElement::deselectAll() { if (Frame* frame = document()->frame()) frame->selection()->clear(); } float SVGSVGElement::createSVGNumber() { return 0.0f; } SVGLength SVGSVGElement::createSVGLength() { return SVGLength(); } SVGAngle SVGSVGElement::createSVGAngle() { return SVGAngle(); } SVGPoint SVGSVGElement::createSVGPoint() { return SVGPoint(); } SVGMatrix SVGSVGElement::createSVGMatrix() { return SVGMatrix(); } SVGRect SVGSVGElement::createSVGRect() { return SVGRect(); } SVGTransform SVGSVGElement::createSVGTransform() { return SVGTransform(SVGTransform::SVG_TRANSFORM_MATRIX); } SVGTransform SVGSVGElement::createSVGTransformFromMatrix(const SVGMatrix& matrix) { return SVGTransform(static_cast(matrix)); } AffineTransform SVGSVGElement::localCoordinateSpaceTransform(SVGLocatable::CTMScope mode) const { AffineTransform viewBoxTransform; if (!hasEmptyViewBox()) { FloatSize size = currentViewportSize(); viewBoxTransform = viewBoxToViewTransform(size.width(), size.height()); } AffineTransform transform; if (!isOutermostSVGSVGElement()) { SVGLengthContext lengthContext(this); transform.translate(xCurrentValue().value(lengthContext), yCurrentValue().value(lengthContext)); } else if (mode == SVGLocatable::ScreenScope) { if (RenderObject* renderer = this->renderer()) { FloatPoint location; float zoomFactor = 1; // At the SVG/HTML boundary (aka RenderSVGRoot), we apply the localToBorderBoxTransform // 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); 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); // 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) transform.translate(location.x() - viewBoxTransform.e(), location.y() - viewBoxTransform.f()); // Respect scroll offset. if (FrameView* view = document()->view()) { LayoutSize scrollOffset = view->scrollOffset(); scrollOffset.scale(zoomFactor); transform.translate(-scrollOffset.width(), -scrollOffset.height()); } } } return transform.multiply(viewBoxTransform); } bool SVGSVGElement::rendererIsNeeded(const NodeRenderingContext& context) { // 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. // https://bugs.webkit.org/show_bug.cgi?id=103493 if (document()->documentElement() == this) return true; return Element::rendererIsNeeded(context); } RenderObject* SVGSVGElement::createRenderer(RenderStyle*) { if (isOutermostSVGSVGElement()) return new RenderSVGRoot(this); return new RenderSVGViewportContainer(this); } Node::InsertionNotificationRequest SVGSVGElement::insertedInto(ContainerNode* rootParent) { if (rootParent->inDocument()) { 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(); } return SVGGraphicsElement::insertedInto(rootParent); } void SVGSVGElement::removedFrom(ContainerNode* rootParent) { if (rootParent->inDocument()) document()->accessSVGExtensions()->removeTimeContainer(this); SVGGraphicsElement::removedFrom(rootParent); } void SVGSVGElement::pauseAnimations() { if (!m_timeContainer->isPaused()) m_timeContainer->pause(); } void SVGSVGElement::unpauseAnimations() { if (m_timeContainer->isPaused()) m_timeContainer->resume(); } bool SVGSVGElement::animationsPaused() const { return m_timeContainer->isPaused(); } float SVGSVGElement::getCurrentTime() const { return narrowPrecisionToFloat(m_timeContainer->elapsed().value()); } void SVGSVGElement::setCurrentTime(float seconds) { if (std::isnan(seconds)) return; seconds = max(seconds, 0.0f); m_timeContainer->setElapsed(seconds); } bool SVGSVGElement::selfHasRelativeLengths() const { return xCurrentValue().isRelative() || yCurrentValue().isRelative() || widthCurrentValue().isRelative() || heightCurrentValue().isRelative() || hasAttribute(SVGNames::viewBoxAttr); } SVGRect SVGSVGElement::currentViewBoxRect() const { if (m_useCurrentView) return m_viewSpec ? m_viewSpec->viewBoxCurrentValue() : SVGRect(); FloatRect useViewBox = viewBoxCurrentValue(); if (!useViewBox.isEmpty()) return useViewBox; if (!renderer() || !renderer()->isSVGRoot()) return SVGRect(); if (!toRenderSVGRoot(renderer())->isEmbeddedThroughSVGImage()) return SVGRect(); Length intrinsicWidth = this->intrinsicWidth(); Length intrinsicHeight = this->intrinsicHeight(); if (!intrinsicWidth.isFixed() || !intrinsicHeight.isFixed()) return SVGRect(); // 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 SVGRect(FloatPoint(), FloatSize(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(); if (renderer()->isSVGRoot()) { LayoutRect contentBoxRect = toRenderSVGRoot(renderer())->contentBoxRect(); return FloatSize(contentBoxRect.width() / renderer()->style()->effectiveZoom(), contentBoxRect.height() / renderer()->style()->effectiveZoom()); } 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()); // SVG embedded through object/embed/iframe. if (root->isEmbeddedThroughFrameContainingSVGDocument()) return !root->hasReplacedLogicalWidth() && !document()->frame()->ownerRenderer()->hasReplacedLogicalWidth(); // SVG embedded via SVGImage (background-image/border-image/etc) / Inline SVG. if (root->isEmbeddedThroughSVGImage() || document()->documentElement() != this) return !root->hasReplacedLogicalWidth(); return true; } bool SVGSVGElement::heightAttributeEstablishesViewport() 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; } Length SVGSVGElement::intrinsicWidth(ConsiderCSSMode mode) const { if (widthAttributeEstablishesViewport() || mode == IgnoreCSSProperties) { if (widthCurrentValue().unitType() == LengthTypePercentage) return Length(widthCurrentValue().valueAsPercentage() * 100, Percent); SVGLengthContext lengthContext(this); return Length(widthCurrentValue().value(lengthContext), Fixed); } ASSERT(renderer()); return renderer()->style()->width(); } Length SVGSVGElement::intrinsicHeight(ConsiderCSSMode mode) const { if (heightAttributeEstablishesViewport() || mode == IgnoreCSSProperties) { if (heightCurrentValue().unitType() == LengthTypePercentage) return Length(heightCurrentValue().valueAsPercentage() * 100, Percent); SVGLengthContext lengthContext(this); return Length(heightCurrentValue().value(lengthContext), Fixed); } ASSERT(renderer()); return renderer()->style()->height(); } AffineTransform SVGSVGElement::viewBoxToViewTransform(float viewWidth, float viewHeight) const { if (!m_useCurrentView || !m_viewSpec) return SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), preserveAspectRatioCurrentValue(), viewWidth, viewHeight); AffineTransform ctm = SVGFitToViewBox::viewBoxToViewTransform(currentViewBoxRect(), m_viewSpec->preserveAspectRatioCurrentValue(), viewWidth, viewHeight); const SVGTransformList& transformList = m_viewSpec->transformBaseValue(); if (transformList.isEmpty()) return ctm; AffineTransform transform; if (transformList.concatenate(transform)) ctm *= transform; return ctm; } void SVGSVGElement::setupInitialView(const String& fragmentIdentifier, Element* anchorNode) { RenderObject* renderer = this->renderer(); SVGViewSpec* view = m_viewSpec.get(); if (view) view->reset(); bool hadUseCurrentView = m_useCurrentView; m_useCurrentView = false; if (fragmentIdentifier.startsWith("xpointer(")) { // FIXME: XPointer references are ignored (https://bugs.webkit.org/show_bug.cgi?id=17491) if (renderer && hadUseCurrentView) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); return; } if (fragmentIdentifier.startsWith("svgView(")) { if (!view) view = currentView(); // 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 && anchorNode->hasTagName(SVGNames::viewTag)) { if (SVGViewElement* viewElement = anchorNode->hasTagName(SVGNames::viewTag) ? static_cast(anchorNode) : 0) { SVGElement* element = SVGLocatable::nearestViewportElement(viewElement); if (element->hasTagName(SVGNames::svgTag)) { SVGSVGElement* svg = toSVGSVGElement(element); svg->inheritViewAttributes(viewElement); if (RenderObject* renderer = svg->renderer()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } } return; } // FIXME: We need to decide which to focus on, and zoom to it. // FIXME: We need to actually "highlight" the viewTarget(s). } void SVGSVGElement::inheritViewAttributes(SVGViewElement* viewElement) { SVGViewSpec* view = currentView(); m_useCurrentView = true; if (viewElement->hasAttribute(SVGNames::viewBoxAttr)) view->setViewBoxBaseValue(viewElement->viewBoxCurrentValue()); else view->setViewBoxBaseValue(viewBoxCurrentValue()); if (viewElement->hasAttribute(SVGNames::preserveAspectRatioAttr)) view->setPreserveAspectRatioBaseValue(viewElement->preserveAspectRatioBaseValue()); else view->setPreserveAspectRatioBaseValue(preserveAspectRatioBaseValue()); if (viewElement->hasAttribute(SVGNames::zoomAndPanAttr)) view->setZoomAndPanBaseValue(viewElement->zoomAndPan()); else view->setZoomAndPanBaseValue(zoomAndPan()); } // getElementById on SVGSVGElement is restricted to only the child subtree defined by the element. // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement Element* SVGSVGElement::getElementById(const AtomicString& id) const { Element* element = treeScope()->getElementById(id); if (element && element->isDescendantOf(this)) return element; // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will // be returned. for (Node* node = firstChild(); node; node = NodeTraversal::next(node, this)) { if (!node->isElementNode()) continue; Element* element = toElement(node); if (element->getIdAttribute() == id) return element; } return 0; } }