1 /*
2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2007, 2008 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 #if ENABLE(SVG)
24 #include "SVGStyledElement.h"
25
26 #include "Attr.h"
27 #include "CSSParser.h"
28 #include "CSSStyleSelector.h"
29 #include "CString.h"
30 #include "Document.h"
31 #include "HTMLNames.h"
32 #include "MappedAttribute.h"
33 #include "PlatformString.h"
34 #include "RenderObject.h"
35 #include "SVGElement.h"
36 #include "SVGElementInstance.h"
37 #include "SVGElementRareData.h"
38 #include "SVGNames.h"
39 #include "SVGRenderStyle.h"
40 #include "SVGResourceClipper.h"
41 #include "SVGResourceFilter.h"
42 #include "SVGResourceMasker.h"
43 #include "SVGSVGElement.h"
44 #include <wtf/Assertions.h>
45
46 namespace WebCore {
47
48 using namespace SVGNames;
49
mapAttributeToCSSProperty(HashMap<AtomicStringImpl *,int> * propertyNameToIdMap,const QualifiedName & attrName)50 void mapAttributeToCSSProperty(HashMap<AtomicStringImpl*, int>* propertyNameToIdMap, const QualifiedName& attrName)
51 {
52 int propertyId = cssPropertyID(attrName.localName());
53 ASSERT(propertyId > 0);
54 propertyNameToIdMap->set(attrName.localName().impl(), propertyId);
55 }
56
SVGStyledElement(const QualifiedName & tagName,Document * doc)57 SVGStyledElement::SVGStyledElement(const QualifiedName& tagName, Document* doc)
58 : SVGElement(tagName, doc)
59 {
60 }
61
~SVGStyledElement()62 SVGStyledElement::~SVGStyledElement()
63 {
64 SVGResource::removeClient(this);
65 }
66
rendererIsNeeded(RenderStyle * style)67 bool SVGStyledElement::rendererIsNeeded(RenderStyle* style)
68 {
69 // http://www.w3.org/TR/SVG/extend.html#PrivateData
70 // Prevent anything other than SVG renderers from appearing in our render tree
71 // Spec: SVG allows inclusion of elements from foreign namespaces anywhere
72 // with the SVG content. In general, the SVG user agent will include the unknown
73 // elements in the DOM but will otherwise ignore unknown elements.
74 if (!parentNode() || parentNode()->isSVGElement())
75 return StyledElement::rendererIsNeeded(style);
76
77 return false;
78 }
79
cssPropertyIdForSVGAttributeName(const QualifiedName & attrName)80 int SVGStyledElement::cssPropertyIdForSVGAttributeName(const QualifiedName& attrName)
81 {
82 if (!attrName.namespaceURI().isNull())
83 return 0;
84
85 static HashMap<AtomicStringImpl*, int>* propertyNameToIdMap = 0;
86 if (!propertyNameToIdMap) {
87 propertyNameToIdMap = new HashMap<AtomicStringImpl*, int>;
88 // This is a list of all base CSS and SVG CSS properties which are exposed as SVG XML attributes
89 mapAttributeToCSSProperty(propertyNameToIdMap, alignment_baselineAttr);
90 mapAttributeToCSSProperty(propertyNameToIdMap, baseline_shiftAttr);
91 mapAttributeToCSSProperty(propertyNameToIdMap, clipAttr);
92 mapAttributeToCSSProperty(propertyNameToIdMap, clip_pathAttr);
93 mapAttributeToCSSProperty(propertyNameToIdMap, clip_ruleAttr);
94 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::colorAttr);
95 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolationAttr);
96 mapAttributeToCSSProperty(propertyNameToIdMap, color_interpolation_filtersAttr);
97 mapAttributeToCSSProperty(propertyNameToIdMap, color_profileAttr);
98 mapAttributeToCSSProperty(propertyNameToIdMap, color_renderingAttr);
99 mapAttributeToCSSProperty(propertyNameToIdMap, cursorAttr);
100 mapAttributeToCSSProperty(propertyNameToIdMap, SVGNames::directionAttr);
101 mapAttributeToCSSProperty(propertyNameToIdMap, displayAttr);
102 mapAttributeToCSSProperty(propertyNameToIdMap, dominant_baselineAttr);
103 mapAttributeToCSSProperty(propertyNameToIdMap, enable_backgroundAttr);
104 mapAttributeToCSSProperty(propertyNameToIdMap, fillAttr);
105 mapAttributeToCSSProperty(propertyNameToIdMap, fill_opacityAttr);
106 mapAttributeToCSSProperty(propertyNameToIdMap, fill_ruleAttr);
107 mapAttributeToCSSProperty(propertyNameToIdMap, filterAttr);
108 mapAttributeToCSSProperty(propertyNameToIdMap, flood_colorAttr);
109 mapAttributeToCSSProperty(propertyNameToIdMap, flood_opacityAttr);
110 mapAttributeToCSSProperty(propertyNameToIdMap, font_familyAttr);
111 mapAttributeToCSSProperty(propertyNameToIdMap, font_sizeAttr);
112 mapAttributeToCSSProperty(propertyNameToIdMap, font_stretchAttr);
113 mapAttributeToCSSProperty(propertyNameToIdMap, font_styleAttr);
114 mapAttributeToCSSProperty(propertyNameToIdMap, font_variantAttr);
115 mapAttributeToCSSProperty(propertyNameToIdMap, font_weightAttr);
116 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_horizontalAttr);
117 mapAttributeToCSSProperty(propertyNameToIdMap, glyph_orientation_verticalAttr);
118 mapAttributeToCSSProperty(propertyNameToIdMap, image_renderingAttr);
119 mapAttributeToCSSProperty(propertyNameToIdMap, kerningAttr);
120 mapAttributeToCSSProperty(propertyNameToIdMap, letter_spacingAttr);
121 mapAttributeToCSSProperty(propertyNameToIdMap, lighting_colorAttr);
122 mapAttributeToCSSProperty(propertyNameToIdMap, marker_endAttr);
123 mapAttributeToCSSProperty(propertyNameToIdMap, marker_midAttr);
124 mapAttributeToCSSProperty(propertyNameToIdMap, marker_startAttr);
125 mapAttributeToCSSProperty(propertyNameToIdMap, maskAttr);
126 mapAttributeToCSSProperty(propertyNameToIdMap, opacityAttr);
127 mapAttributeToCSSProperty(propertyNameToIdMap, overflowAttr);
128 mapAttributeToCSSProperty(propertyNameToIdMap, pointer_eventsAttr);
129 mapAttributeToCSSProperty(propertyNameToIdMap, shape_renderingAttr);
130 mapAttributeToCSSProperty(propertyNameToIdMap, stop_colorAttr);
131 mapAttributeToCSSProperty(propertyNameToIdMap, stop_opacityAttr);
132 mapAttributeToCSSProperty(propertyNameToIdMap, strokeAttr);
133 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dasharrayAttr);
134 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_dashoffsetAttr);
135 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linecapAttr);
136 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_linejoinAttr);
137 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_miterlimitAttr);
138 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_opacityAttr);
139 mapAttributeToCSSProperty(propertyNameToIdMap, stroke_widthAttr);
140 mapAttributeToCSSProperty(propertyNameToIdMap, text_anchorAttr);
141 mapAttributeToCSSProperty(propertyNameToIdMap, text_decorationAttr);
142 mapAttributeToCSSProperty(propertyNameToIdMap, text_renderingAttr);
143 mapAttributeToCSSProperty(propertyNameToIdMap, unicode_bidiAttr);
144 mapAttributeToCSSProperty(propertyNameToIdMap, visibilityAttr);
145 mapAttributeToCSSProperty(propertyNameToIdMap, word_spacingAttr);
146 mapAttributeToCSSProperty(propertyNameToIdMap, writing_modeAttr);
147 }
148
149 return propertyNameToIdMap->get(attrName.localName().impl());
150 }
151
mapToEntry(const QualifiedName & attrName,MappedAttributeEntry & result) const152 bool SVGStyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
153 {
154 if (SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName) > 0) {
155 result = eSVG;
156 return false;
157 }
158 return SVGElement::mapToEntry(attrName, result);
159 }
160
parseMappedAttribute(MappedAttribute * attr)161 void SVGStyledElement::parseMappedAttribute(MappedAttribute* attr)
162 {
163 const QualifiedName& attrName = attr->name();
164 // NOTE: Any subclass which overrides parseMappedAttribute for a property handled by
165 // cssPropertyIdForSVGAttributeName will also have to override mapToEntry to disable the default eSVG mapping
166 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName);
167 if (propId > 0) {
168 addCSSProperty(attr, propId, attr->value());
169 setNeedsStyleRecalc();
170 return;
171 }
172
173 // SVG animation has currently requires special storage of values so we set
174 // the className here. svgAttributeChanged actually causes the resulting
175 // style updates (instead of StyledElement::parseMappedAttribute). We don't
176 // tell StyledElement about the change to avoid parsing the class list twice
177 if (attrName.matches(HTMLNames::classAttr))
178 setClassNameBaseValue(attr->value());
179 else
180 // id is handled by StyledElement which SVGElement inherits from
181 SVGElement::parseMappedAttribute(attr);
182 }
183
isKnownAttribute(const QualifiedName & attrName)184 bool SVGStyledElement::isKnownAttribute(const QualifiedName& attrName)
185 {
186 // Recognize all style related SVG CSS properties
187 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(attrName);
188 if (propId > 0)
189 return true;
190
191 return (attrName == idAttributeName() || attrName == HTMLNames::styleAttr);
192 }
193
svgAttributeChanged(const QualifiedName & attrName)194 void SVGStyledElement::svgAttributeChanged(const QualifiedName& attrName)
195 {
196 SVGElement::svgAttributeChanged(attrName);
197
198 if (attrName.matches(HTMLNames::classAttr))
199 classAttributeChanged(className());
200
201 // If we're the child of a resource element, be sure to invalidate it.
202 invalidateResourcesInAncestorChain();
203
204 // If the element is using resources, invalidate them.
205 invalidateResources();
206
207 // Invalidate all SVGElementInstances associated with us
208 SVGElementInstance::invalidateAllInstancesOfElement(this);
209 }
210
synchronizeProperty(const QualifiedName & attrName)211 void SVGStyledElement::synchronizeProperty(const QualifiedName& attrName)
212 {
213 SVGElement::synchronizeProperty(attrName);
214
215 if (attrName == anyQName() || attrName.matches(HTMLNames::classAttr))
216 synchronizeClassName();
217 }
218
invalidateResources()219 void SVGStyledElement::invalidateResources()
220 {
221 RenderObject* object = renderer();
222 if (!object)
223 return;
224
225 const SVGRenderStyle* svgStyle = object->style()->svgStyle();
226 Document* document = this->document();
227
228 if (document->parsing())
229 return;
230
231 #if ENABLE(FILTERS)
232 SVGResourceFilter* filter = getFilterById(document, svgStyle->filter(), object);
233 if (filter)
234 filter->invalidate();
235 #endif
236
237 SVGResourceMasker* masker = getMaskerById(document, svgStyle->maskElement(), object);
238 if (masker)
239 masker->invalidate();
240
241 SVGResourceClipper* clipper = getClipperById(document, svgStyle->clipPath(), object);
242 if (clipper)
243 clipper->invalidate();
244 }
245
invalidateResourcesInAncestorChain() const246 void SVGStyledElement::invalidateResourcesInAncestorChain() const
247 {
248 Node* node = parentNode();
249 while (node) {
250 if (!node->isSVGElement())
251 break;
252
253 SVGElement* element = static_cast<SVGElement*>(node);
254 if (SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(element->isStyled() ? element : 0))
255 styledElement->invalidateCanvasResources();
256
257 node = node->parentNode();
258 }
259 }
260
invalidateCanvasResources()261 void SVGStyledElement::invalidateCanvasResources()
262 {
263 if (SVGResource* resource = canvasResource(renderer()))
264 resource->invalidate();
265 }
266
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)267 void SVGStyledElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
268 {
269 SVGElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
270
271 // Invalidate all SVGElementInstances associated with us
272 SVGElementInstance::invalidateAllInstancesOfElement(this);
273 }
274
resolveStyle(RenderStyle * parentStyle)275 PassRefPtr<RenderStyle> SVGStyledElement::resolveStyle(RenderStyle* parentStyle)
276 {
277 if (renderer())
278 return renderer()->style();
279 return document()->styleSelector()->styleForElement(this, parentStyle);
280 }
281
getPresentationAttribute(const String & name)282 PassRefPtr<CSSValue> SVGStyledElement::getPresentationAttribute(const String& name)
283 {
284 if (!mappedAttributes())
285 return 0;
286
287 QualifiedName attributeName(nullAtom, name, nullAtom);
288 Attribute* attr = mappedAttributes()->getAttributeItem(attributeName);
289 if (!attr || !attr->isMappedAttribute() || !attr->style())
290 return 0;
291
292 MappedAttribute* cssSVGAttr = static_cast<MappedAttribute*>(attr);
293 // This function returns a pointer to a CSSValue which can be mutated from JavaScript.
294 // If the associated MappedAttribute uses the same CSSMappedAttributeDeclaration
295 // as StyledElement's mappedAttributeDecls cache, create a new CSSMappedAttributeDeclaration
296 // before returning so that any modifications to the CSSValue will not affect other attributes.
297 MappedAttributeEntry entry;
298 mapToEntry(attributeName, entry);
299 if (getMappedAttributeDecl(entry, cssSVGAttr) == cssSVGAttr->decl()) {
300 cssSVGAttr->setDecl(0);
301 int propId = SVGStyledElement::cssPropertyIdForSVGAttributeName(cssSVGAttr->name());
302 addCSSProperty(cssSVGAttr, propId, cssSVGAttr->value());
303 }
304 return cssSVGAttr->style()->getPropertyCSSValue(name);
305 }
306
detach()307 void SVGStyledElement::detach()
308 {
309 SVGResource::removeClient(this);
310 SVGElement::detach();
311 }
312
instanceUpdatesBlocked() const313 bool SVGStyledElement::instanceUpdatesBlocked() const
314 {
315 return hasRareSVGData() && rareSVGData()->instanceUpdatesBlocked();
316 }
317
setInstanceUpdatesBlocked(bool value)318 void SVGStyledElement::setInstanceUpdatesBlocked(bool value)
319 {
320 if (hasRareSVGData())
321 rareSVGData()->setInstanceUpdatesBlocked(value);
322 }
323
324 }
325
326 #endif // ENABLE(SVG)
327