Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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
23 #include "core/svg/SVGGraphicsElement.h"
24
25 #include "SVGNames.h"
26 #include "core/rendering/svg/RenderSVGPath.h"
27 #include "core/rendering/svg/RenderSVGResource.h"
28 #include "core/rendering/svg/SVGPathData.h"
29 #include "core/svg/SVGElementInstance.h"
30 #include "platform/transforms/AffineTransform.h"
31
32 namespace WebCore {
33
34 // Animated property definitions
35 DEFINE_ANIMATED_TRANSFORM_LIST(SVGGraphicsElement, SVGNames::transformAttr, Transform, transform)
36
37 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGraphicsElement)
38     REGISTER_LOCAL_ANIMATED_PROPERTY(transform)
39     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
40 END_REGISTER_ANIMATED_PROPERTIES
41
42 SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document, ConstructionType constructionType)
43     : SVGElement(tagName, document, constructionType)
44     , SVGTests(this)
45 {
46     registerAnimatedPropertiesForSVGGraphicsElement();
47 }
48
49 SVGGraphicsElement::~SVGGraphicsElement()
50 {
51 }
52
53 AffineTransform SVGGraphicsElement::getTransformToElement(SVGElement* target, ExceptionState& exceptionState)
54 {
55     AffineTransform ctm = getCTM(AllowStyleUpdate);
56
57     if (target && target->isSVGGraphicsElement()) {
58         AffineTransform targetCTM = toSVGGraphicsElement(target)->getCTM(AllowStyleUpdate);
59         if (!targetCTM.isInvertible()) {
60             exceptionState.throwDOMException(InvalidStateError, "The target transformation is not invertable.");
61             return ctm;
62         }
63         ctm = targetCTM.inverse() * ctm;
64     }
65
66     return ctm;
67 }
68
69 static AffineTransform computeCTM(SVGGraphicsElement* element, SVGElement::CTMScope mode, SVGGraphicsElement::StyleUpdateStrategy styleUpdateStrategy)
70 {
71     ASSERT(element);
72     if (styleUpdateStrategy == SVGGraphicsElement::AllowStyleUpdate)
73         element->document().updateLayoutIgnorePendingStylesheets();
74
75     AffineTransform ctm;
76
77     SVGElement* stopAtElement = mode == SVGGraphicsElement::NearestViewportScope ? element->nearestViewportElement() : 0;
78     for (Element* currentElement = element; currentElement; currentElement = currentElement->parentOrShadowHostElement()) {
79         if (!currentElement->isSVGElement())
80             break;
81
82         ctm = toSVGElement(currentElement)->localCoordinateSpaceTransform(mode).multiply(ctm);
83
84         // For getCTM() computation, stop at the nearest viewport element
85         if (currentElement == stopAtElement)
86             break;
87     }
88
89     return ctm;
90 }
91
92 AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
93 {
94     return computeCTM(this, NearestViewportScope, styleUpdateStrategy);
95 }
96
97 AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
98 {
99     return computeCTM(this, ScreenScope, styleUpdateStrategy);
100 }
101
102 AffineTransform SVGGraphicsElement::animatedLocalTransform() const
103 {
104     AffineTransform matrix;
105     RenderStyle* style = renderer() ? renderer()->style() : 0;
106
107     // If CSS property was set, use that, otherwise fallback to attribute (if set).
108     if (style && style->hasTransform()) {
109         // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
110         // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
111         TransformationMatrix transform;
112         style->applyTransform(transform, renderer()->objectBoundingBox());
113
114         // Flatten any 3D transform.
115         matrix = transform.toAffineTransform();
116
117         // CSS bakes the zoom factor into lengths, including translation components.
118         // In order to align CSS & SVG transforms, we need to invert this operation.
119         float zoom = style->effectiveZoom();
120         if (zoom != 1) {
121             matrix.setE(matrix.e() / zoom);
122             matrix.setF(matrix.f() / zoom);
123         }
124     } else {
125         transformCurrentValue().concatenate(matrix);
126     }
127
128     if (m_supplementalTransform)
129         return *m_supplementalTransform * matrix;
130     return matrix;
131 }
132
133 AffineTransform* SVGGraphicsElement::supplementalTransform()
134 {
135     if (!m_supplementalTransform)
136         m_supplementalTransform = adoptPtr(new AffineTransform);
137     return m_supplementalTransform.get();
138 }
139
140 bool SVGGraphicsElement::isSupportedAttribute(const QualifiedName& attrName)
141 {
142     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
143     if (supportedAttributes.isEmpty()) {
144         SVGTests::addSupportedAttributes(supportedAttributes);
145         supportedAttributes.add(SVGNames::transformAttr);
146     }
147     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
148 }
149
150 void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
151 {
152     if (!isSupportedAttribute(name)) {
153         SVGElement::parseAttribute(name, value);
154         return;
155     }
156
157     if (name == SVGNames::transformAttr) {
158         SVGTransformList newList;
159         newList.parse(value);
160         detachAnimatedTransformListWrappers(newList.size());
161         setTransformBaseValue(newList);
162         return;
163     } else if (SVGTests::parseAttribute(name, value)) {
164         return;
165     }
166
167     ASSERT_NOT_REACHED();
168 }
169
170 void SVGGraphicsElement::svgAttributeChanged(const QualifiedName& attrName)
171 {
172     if (!isSupportedAttribute(attrName)) {
173         SVGElement::svgAttributeChanged(attrName);
174         return;
175     }
176
177     SVGElementInstance::InvalidationGuard invalidationGuard(this);
178
179     // Reattach so the isValid() check will be run again during renderer creation.
180     if (SVGTests::isKnownAttribute(attrName)) {
181         lazyReattachIfAttached();
182         return;
183     }
184
185     RenderObject* object = renderer();
186     if (!object)
187         return;
188
189     if (attrName == SVGNames::transformAttr) {
190         object->setNeedsTransformUpdate();
191         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
192         return;
193     }
194
195     ASSERT_NOT_REACHED();
196 }
197
198 static bool isViewportElement(Node* node)
199 {
200     return (node->hasTagName(SVGNames::svgTag)
201         || node->hasTagName(SVGNames::symbolTag)
202         || node->hasTagName(SVGNames::foreignObjectTag)
203         || node->hasTagName(SVGNames::imageTag));
204 }
205
206 SVGElement* SVGGraphicsElement::nearestViewportElement() const
207 {
208     for (Element* current = parentOrShadowHostElement(); current; current = current->parentOrShadowHostElement()) {
209         if (isViewportElement(current))
210             return toSVGElement(current);
211     }
212
213     return 0;
214 }
215
216 SVGElement* SVGGraphicsElement::farthestViewportElement() const
217 {
218     SVGElement* farthest = 0;
219     for (Element* current = parentOrShadowHostElement(); current; current = current->parentOrShadowHostElement()) {
220         if (isViewportElement(current))
221             farthest = toSVGElement(current);
222     }
223     return farthest;
224 }
225
226 FloatRect SVGGraphicsElement::getBBox()
227 {
228     document().updateLayoutIgnorePendingStylesheets();
229
230     // FIXME: Eventually we should support getBBox for detached elements.
231     if (!renderer())
232         return FloatRect();
233
234     return renderer()->objectBoundingBox();
235 }
236
237 PassRefPtr<SVGRectTearOff> SVGGraphicsElement::getBBoxFromJavascript()
238 {
239     return SVGRectTearOff::create(SVGRect::create(getBBox()), 0, PropertyIsNotAnimVal);
240 }
241
242 RenderObject* SVGGraphicsElement::createRenderer(RenderStyle*)
243 {
244     // By default, any subclass is expected to do path-based drawing
245     return new RenderSVGPath(this);
246 }
247
248 void SVGGraphicsElement::toClipPath(Path& path)
249 {
250     updatePathFromGraphicsElement(this, path);
251     // FIXME: How do we know the element has done a layout?
252     path.transform(animatedLocalTransform());
253 }
254
255 }