1/*
2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4 * Copyright (C) 2018 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 "SVGGraphicsElement.h"
24
25#include "RenderSVGPath.h"
26#include "RenderSVGResource.h"
27#include "SVGMatrix.h"
28#include "SVGNames.h"
29#include "SVGPathData.h"
30#include "SVGRect.h"
31#include "SVGSVGElement.h"
32#include "SVGStringList.h"
33#include <wtf/IsoMallocInlines.h>
34#include <wtf/NeverDestroyed.h>
35
36namespace WebCore {
37
38WTF_MAKE_ISO_ALLOCATED_IMPL(SVGGraphicsElement);
39
40SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document)
41 : SVGElement(tagName, document)
42 , SVGTests(this)
43 , m_shouldIsolateBlending(false)
44{
45 static std::once_flag onceFlag;
46 std::call_once(onceFlag, [] {
47 PropertyRegistry::registerProperty<SVGNames::transformAttr, &SVGGraphicsElement::m_transform>();
48 });
49}
50
51SVGGraphicsElement::~SVGGraphicsElement() = default;
52
53Ref<SVGMatrix> SVGGraphicsElement::getCTMForBindings()
54{
55 return SVGMatrix::create(getCTM());
56}
57
58AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
59{
60 return SVGLocatable::computeCTM(this, SVGLocatable::NearestViewportScope, styleUpdateStrategy);
61}
62
63Ref<SVGMatrix> SVGGraphicsElement::getScreenCTMForBindings()
64{
65 return SVGMatrix::create(getScreenCTM());
66}
67
68AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
69{
70 return SVGLocatable::computeCTM(this, SVGLocatable::ScreenScope, styleUpdateStrategy);
71}
72
73AffineTransform SVGGraphicsElement::animatedLocalTransform() const
74{
75 AffineTransform matrix;
76 auto* style = renderer() ? &renderer()->style() : nullptr;
77
78 // If CSS property was set, use that, otherwise fallback to attribute (if set).
79 if (style && style->hasTransform()) {
80
81 FloatRect boundingBox;
82 switch (style->transformBox()) {
83 case TransformBox::FillBox:
84 boundingBox = renderer()->objectBoundingBox();
85 break;
86 case TransformBox::BorderBox:
87 // For SVG elements without an associated CSS layout box, the used value for border-box is view-box.
88 case TransformBox::ViewBox: {
89 FloatSize viewportSize;
90 SVGLengthContext(this).determineViewport(viewportSize);
91 boundingBox.setSize(viewportSize);
92 break;
93 }
94 }
95
96 // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
97 // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
98 TransformationMatrix transform;
99 style->applyTransform(transform, boundingBox);
100
101 // Flatten any 3D transform.
102 matrix = transform.toAffineTransform();
103 // CSS bakes the zoom factor into lengths, including translation components.
104 // In order to align CSS & SVG transforms, we need to invert this operation.
105 float zoom = style->effectiveZoom();
106 if (zoom != 1) {
107 matrix.setE(matrix.e() / zoom);
108 matrix.setF(matrix.f() / zoom);
109 }
110
111 } else
112 matrix = transform().concatenate();
113
114 if (m_supplementalTransform)
115 return *m_supplementalTransform * matrix;
116 return matrix;
117}
118
119AffineTransform* SVGGraphicsElement::supplementalTransform()
120{
121 if (!m_supplementalTransform)
122 m_supplementalTransform = std::make_unique<AffineTransform>();
123 return m_supplementalTransform.get();
124}
125
126void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
127{
128 if (name == SVGNames::transformAttr) {
129 m_transform->baseVal()->parse(value);
130 return;
131 }
132
133 SVGElement::parseAttribute(name, value);
134 SVGTests::parseAttribute(name, value);
135}
136
137void SVGGraphicsElement::svgAttributeChanged(const QualifiedName& attrName)
138{
139 if (attrName == SVGNames::transformAttr) {
140 InstanceInvalidationGuard guard(*this);
141
142 if (auto renderer = this->renderer()) {
143 renderer->setNeedsTransformUpdate();
144 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
145 }
146
147 return;
148 }
149
150 SVGElement::svgAttributeChanged(attrName);
151 SVGTests::svgAttributeChanged(attrName);
152}
153
154SVGElement* SVGGraphicsElement::nearestViewportElement() const
155{
156 return SVGTransformable::nearestViewportElement(this);
157}
158
159SVGElement* SVGGraphicsElement::farthestViewportElement() const
160{
161 return SVGTransformable::farthestViewportElement(this);
162}
163
164Ref<SVGRect> SVGGraphicsElement::getBBoxForBindings()
165{
166 return SVGRect::create(getBBox());
167}
168
169FloatRect SVGGraphicsElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
170{
171 return SVGTransformable::getBBox(this, styleUpdateStrategy);
172}
173
174RenderPtr<RenderElement> SVGGraphicsElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
175{
176 return createRenderer<RenderSVGPath>(*this, WTFMove(style));
177}
178
179Path SVGGraphicsElement::toClipPath()
180{
181 Path path = pathFromGraphicsElement(this);
182 // FIXME: How do we know the element has done a layout?
183 path.transform(animatedLocalTransform());
184 return path;
185}
186
187}
188