Support transform-box to switch sizing box in SVG
[WebKit-https.git] / Source / WebCore / svg / SVGGraphicsElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "SVGGraphicsElement.h"
23
24 #include "RenderSVGPath.h"
25 #include "RenderSVGResource.h"
26 #include "SVGMatrix.h"
27 #include "SVGNames.h"
28 #include "SVGPathData.h"
29 #include "SVGRect.h"
30 #include "SVGSVGElement.h"
31 #include "SVGStringList.h"
32 #include <wtf/NeverDestroyed.h>
33
34 namespace WebCore {
35
36 // Animated property definitions
37 DEFINE_ANIMATED_TRANSFORM_LIST(SVGGraphicsElement, SVGNames::transformAttr, Transform, transform)
38
39 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGraphicsElement)
40     REGISTER_LOCAL_ANIMATED_PROPERTY(transform)
41     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
42     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
43 END_REGISTER_ANIMATED_PROPERTIES
44
45 SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document)
46     : SVGElement(tagName, document)
47     , m_shouldIsolateBlending(false)
48 {
49     registerAnimatedPropertiesForSVGGraphicsElement();
50 }
51
52 SVGGraphicsElement::~SVGGraphicsElement()
53 {
54 }
55
56 Ref<SVGMatrix> SVGGraphicsElement::getCTMForBindings()
57 {
58     return SVGMatrix::create(getCTM());
59 }
60
61 AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
62 {
63     return SVGLocatable::computeCTM(this, SVGLocatable::NearestViewportScope, styleUpdateStrategy);
64 }
65
66 Ref<SVGMatrix> SVGGraphicsElement::getScreenCTMForBindings()
67 {
68     return SVGMatrix::create(getScreenCTM());
69 }
70
71 AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
72 {
73     return SVGLocatable::computeCTM(this, SVGLocatable::ScreenScope, styleUpdateStrategy);
74 }
75
76 AffineTransform SVGGraphicsElement::animatedLocalTransform() const
77 {
78     AffineTransform matrix;
79     auto* style = renderer() ? &renderer()->style() : nullptr;
80
81     // If CSS property was set, use that, otherwise fallback to attribute (if set).
82     if (style && style->hasTransform()) {
83         
84         FloatRect boundingBox;
85         switch (style->transformBox()) {
86         case TransformBox::FillBox:
87             boundingBox = renderer()->objectBoundingBox();
88             break;
89         case TransformBox::BorderBox:
90             // For SVG elements without an associated CSS layout box, the used value for border-box is view-box.
91         case TransformBox::ViewBox:
92             if (auto *viewportElement = nearestViewportElement()) {
93                 if (is<SVGSVGElement>(*viewportElement))
94                     boundingBox = downcast<SVGSVGElement>(*viewportElement).viewBox();
95             }
96             break;
97         }
98         
99         // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
100         // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
101         TransformationMatrix transform;
102         style->applyTransform(transform, boundingBox);
103
104         // Flatten any 3D transform.
105         matrix = transform.toAffineTransform();
106         // CSS bakes the zoom factor into lengths, including translation components.
107         // In order to align CSS & SVG transforms, we need to invert this operation.
108         float zoom = style->effectiveZoom();
109         if (zoom != 1) {
110             matrix.setE(matrix.e() / zoom);
111             matrix.setF(matrix.f() / zoom);
112         }
113
114     } else
115         transform().concatenate(matrix);
116
117     if (m_supplementalTransform)
118         return *m_supplementalTransform * matrix;
119     return matrix;
120 }
121
122 AffineTransform* SVGGraphicsElement::supplementalTransform()
123 {
124     if (!m_supplementalTransform)
125         m_supplementalTransform = std::make_unique<AffineTransform>();
126     return m_supplementalTransform.get();
127 }
128
129 bool SVGGraphicsElement::isSupportedAttribute(const QualifiedName& attrName)
130 {
131     static NeverDestroyed<HashSet<QualifiedName>> supportedAttributes;
132     if (supportedAttributes.get().isEmpty()) {
133         SVGTests::addSupportedAttributes(supportedAttributes);
134         supportedAttributes.get().add(SVGNames::transformAttr);
135     }
136     return supportedAttributes.get().contains<SVGAttributeHashTranslator>(attrName);
137 }
138
139 void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
140 {
141     if (name == SVGNames::transformAttr) {
142         SVGTransformListValues newList;
143         newList.parse(value);
144         detachAnimatedTransformListWrappers(newList.size());
145         setTransformBaseValue(newList);
146         return;
147     }
148
149     SVGElement::parseAttribute(name, value);
150     SVGTests::parseAttribute(name, value);
151 }
152
153 void SVGGraphicsElement::svgAttributeChanged(const QualifiedName& attrName)
154 {
155     if (!isSupportedAttribute(attrName)) {
156         SVGElement::svgAttributeChanged(attrName);
157         return;
158     }
159
160     InstanceInvalidationGuard guard(*this);
161
162     if (SVGTests::handleAttributeChange(this, attrName))
163         return;
164
165     auto renderer = this->renderer();
166     if (!renderer)
167         return;
168
169     if (attrName == SVGNames::transformAttr) {
170         renderer->setNeedsTransformUpdate();
171         RenderSVGResource::markForLayoutAndParentResourceInvalidation(*renderer);
172         return;
173     }
174
175     ASSERT_NOT_REACHED();
176 }
177
178 SVGElement* SVGGraphicsElement::nearestViewportElement() const
179 {
180     return SVGTransformable::nearestViewportElement(this);
181 }
182
183 SVGElement* SVGGraphicsElement::farthestViewportElement() const
184 {
185     return SVGTransformable::farthestViewportElement(this);
186 }
187
188 Ref<SVGRect> SVGGraphicsElement::getBBoxForBindings()
189 {
190     return SVGRect::create(getBBox());
191 }
192
193 FloatRect SVGGraphicsElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
194 {
195     return SVGTransformable::getBBox(this, styleUpdateStrategy);
196 }
197
198 RenderPtr<RenderElement> SVGGraphicsElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
199 {
200     return createRenderer<RenderSVGPath>(*this, WTFMove(style));
201 }
202
203 void SVGGraphicsElement::toClipPath(Path& path)
204 {
205     updatePathFromGraphicsElement(this, path);
206     // FIXME: How do we know the element has done a layout?
207     path.transform(animatedLocalTransform());
208 }
209
210 Ref<SVGStringList> SVGGraphicsElement::requiredFeatures()
211 {
212     return SVGTests::requiredFeatures(*this);
213 }
214
215 Ref<SVGStringList> SVGGraphicsElement::requiredExtensions()
216
217     return SVGTests::requiredExtensions(*this);
218 }
219
220 Ref<SVGStringList> SVGGraphicsElement::systemLanguage()
221 {
222     return SVGTests::systemLanguage(*this);
223 }
224
225 }