a75890f3180468d1524bee854359d0356eb5222d
[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  * 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
36 namespace WebCore {
37
38 WTF_MAKE_ISO_ALLOCATED_IMPL(SVGGraphicsElement);
39
40 SVGGraphicsElement::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
51 SVGGraphicsElement::~SVGGraphicsElement() = default;
52
53 Ref<SVGMatrix> SVGGraphicsElement::getCTMForBindings()
54 {
55     return SVGMatrix::create(getCTM());
56 }
57
58 AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
59 {
60     return SVGLocatable::computeCTM(this, SVGLocatable::NearestViewportScope, styleUpdateStrategy);
61 }
62
63 Ref<SVGMatrix> SVGGraphicsElement::getScreenCTMForBindings()
64 {
65     return SVGMatrix::create(getScreenCTM());
66 }
67
68 AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
69 {
70     return SVGLocatable::computeCTM(this, SVGLocatable::ScreenScope, styleUpdateStrategy);
71 }
72
73 AffineTransform 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
119 AffineTransform* SVGGraphicsElement::supplementalTransform()
120 {
121     if (!m_supplementalTransform)
122         m_supplementalTransform = std::make_unique<AffineTransform>();
123     return m_supplementalTransform.get();
124 }
125
126 void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomString& 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
137 void 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
154 SVGElement* SVGGraphicsElement::nearestViewportElement() const
155 {
156     return SVGTransformable::nearestViewportElement(this);
157 }
158
159 SVGElement* SVGGraphicsElement::farthestViewportElement() const
160 {
161     return SVGTransformable::farthestViewportElement(this);
162 }
163
164 Ref<SVGRect> SVGGraphicsElement::getBBoxForBindings()
165 {
166     return SVGRect::create(getBBox());
167 }
168
169 FloatRect SVGGraphicsElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
170 {
171     return SVGTransformable::getBBox(this, styleUpdateStrategy);
172 }
173
174 RenderPtr<RenderElement> SVGGraphicsElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
175 {
176     return createRenderer<RenderSVGPath>(*this, WTFMove(style));
177 }
178
179 Path 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 }