1/*
2 * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2018-2019 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "SVGPathElement.h"
24
25#include "RenderSVGPath.h"
26#include "RenderSVGResource.h"
27#include "SVGDocumentExtensions.h"
28#include "SVGMPathElement.h"
29#include "SVGNames.h"
30#include "SVGPathUtilities.h"
31#include "SVGPoint.h"
32#include <wtf/IsoMallocInlines.h>
33
34namespace WebCore {
35
36WTF_MAKE_ISO_ALLOCATED_IMPL(SVGPathElement);
37
38inline SVGPathElement::SVGPathElement(const QualifiedName& tagName, Document& document)
39 : SVGGeometryElement(tagName, document)
40 , SVGExternalResourcesRequired(this)
41{
42 ASSERT(hasTagName(SVGNames::pathTag));
43
44 static std::once_flag onceFlag;
45 std::call_once(onceFlag, [] {
46 PropertyRegistry::registerProperty<SVGNames::dAttr, &SVGPathElement::m_pathSegList>();
47 });
48}
49
50Ref<SVGPathElement> SVGPathElement::create(const QualifiedName& tagName, Document& document)
51{
52 return adoptRef(*new SVGPathElement(tagName, document));
53}
54
55void SVGPathElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
56{
57 if (name == SVGNames::dAttr) {
58 if (!m_pathSegList->baseVal()->parse(value))
59 document().accessSVGExtensions().reportError("Problem parsing d=\"" + value + "\"");
60 return;
61 }
62
63 SVGGeometryElement::parseAttribute(name, value);
64 SVGExternalResourcesRequired::parseAttribute(name, value);
65}
66
67void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
68{
69 if (attrName == SVGNames::dAttr) {
70 InstanceInvalidationGuard guard(*this);
71 invalidateMPathDependencies();
72
73 if (auto* renderer = downcast<RenderSVGPath>(this->renderer())) {
74 renderer->setNeedsShapeUpdate();
75 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
76 }
77
78 return;
79 }
80
81 SVGGeometryElement::svgAttributeChanged(attrName);
82 SVGExternalResourcesRequired::svgAttributeChanged(attrName);
83}
84
85void SVGPathElement::invalidateMPathDependencies()
86{
87 // <mpath> can only reference <path> but this dependency is not handled in
88 // markForLayoutAndParentResourceInvalidation so we update any mpath dependencies manually.
89 if (HashSet<SVGElement*>* dependencies = document().accessSVGExtensions().setOfElementsReferencingTarget(*this)) {
90 for (auto* element : *dependencies) {
91 if (is<SVGMPathElement>(*element))
92 downcast<SVGMPathElement>(*element).targetPathChanged();
93 }
94 }
95}
96
97Node::InsertedIntoAncestorResult SVGPathElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
98{
99 SVGGeometryElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
100 invalidateMPathDependencies();
101 return InsertedIntoAncestorResult::Done;
102}
103
104void SVGPathElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
105{
106 SVGGeometryElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
107 invalidateMPathDependencies();
108}
109
110float SVGPathElement::getTotalLength() const
111{
112 float totalLength = 0;
113 getTotalLengthOfSVGPathByteStream(pathByteStream(), totalLength);
114 return totalLength;
115}
116
117Ref<SVGPoint> SVGPathElement::getPointAtLength(float length) const
118{
119 FloatPoint point;
120 getPointAtLengthOfSVGPathByteStream(pathByteStream(), length, point);
121 return SVGPoint::create(point);
122}
123
124unsigned SVGPathElement::getPathSegAtLength(float length) const
125{
126 unsigned pathSeg = 0;
127 getSVGPathSegAtLengthFromSVGPathByteStream(pathByteStream(), length, pathSeg);
128 return pathSeg;
129}
130
131FloatRect SVGPathElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
132{
133 if (styleUpdateStrategy == AllowStyleUpdate)
134 document().updateLayoutIgnorePendingStylesheets();
135
136 RenderSVGPath* renderer = downcast<RenderSVGPath>(this->renderer());
137
138 // FIXME: Eventually we should support getBBox for detached elements.
139 // FIXME: If the path is null it means we're calling getBBox() before laying out this element,
140 // which is an error.
141 if (!renderer || !renderer->hasPath())
142 return { };
143
144 return renderer->path().boundingRect();
145}
146
147RenderPtr<RenderElement> SVGPathElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
148{
149 return createRenderer<RenderSVGPath>(*this, WTFMove(style));
150}
151
152}
153