2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 #include "core/rendering/svg/SVGResources.h"
23 #include "core/SVGNames.h"
24 #include "core/rendering/style/SVGRenderStyle.h"
25 #include "core/rendering/svg/RenderSVGResourceClipper.h"
26 #include "core/rendering/svg/RenderSVGResourceFilter.h"
27 #include "core/rendering/svg/RenderSVGResourceMarker.h"
28 #include "core/rendering/svg/RenderSVGResourceMasker.h"
29 #include "core/rendering/svg/RenderSVGResourcePaintServer.h"
30 #include "core/svg/SVGFilterElement.h"
31 #include "core/svg/SVGGradientElement.h"
32 #include "core/svg/SVGPatternElement.h"
33 #include "core/svg/SVGURIReference.h"
41 using namespace SVGNames;
43 SVGResources::SVGResources()
48 static HashSet<AtomicString>& clipperFilterMaskerTags()
50 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
51 if (s_tagList.isEmpty()) {
52 // "container elements": http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
53 // "graphics elements" : http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement
54 s_tagList.add(aTag.localName());
55 s_tagList.add(circleTag.localName());
56 s_tagList.add(ellipseTag.localName());
57 s_tagList.add(gTag.localName());
58 s_tagList.add(imageTag.localName());
59 s_tagList.add(lineTag.localName());
60 s_tagList.add(markerTag.localName());
61 s_tagList.add(maskTag.localName());
62 s_tagList.add(pathTag.localName());
63 s_tagList.add(polygonTag.localName());
64 s_tagList.add(polylineTag.localName());
65 s_tagList.add(rectTag.localName());
66 s_tagList.add(svgTag.localName());
67 s_tagList.add(textTag.localName());
68 s_tagList.add(useTag.localName());
70 // Not listed in the definitions is the clipPath element, the SVG spec says though:
71 // The "clipPath" element or any of its children can specify property "clip-path".
72 // So we have to add clipPathTag here, otherwhise clip-path on clipPath will fail.
73 // (Already mailed SVG WG, waiting for a solution)
74 s_tagList.add(clipPathTag.localName());
76 // Not listed in the definitions are the text content elements, though filter/clipper/masker on tspan/text/.. is allowed.
77 // (Already mailed SVG WG, waiting for a solution)
78 s_tagList.add(textPathTag.localName());
79 s_tagList.add(tspanTag.localName());
81 // Not listed in the definitions is the foreignObject element, but clip-path
82 // is a supported attribute.
83 s_tagList.add(foreignObjectTag.localName());
85 // Elements that we ignore, as it doesn't make any sense.
86 // defs, pattern, switch (FIXME: Mail SVG WG about these)
87 // symbol (is converted to a svg element, when referenced by use, we can safely ignore it.)
93 bool SVGResources::supportsMarkers(const SVGElement& element)
95 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
96 if (s_tagList.isEmpty()) {
97 s_tagList.add(lineTag.localName());
98 s_tagList.add(pathTag.localName());
99 s_tagList.add(polygonTag.localName());
100 s_tagList.add(polylineTag.localName());
103 return s_tagList.contains(element.localName());
106 static HashSet<AtomicString>& fillAndStrokeTags()
108 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
109 if (s_tagList.isEmpty()) {
110 s_tagList.add(circleTag.localName());
111 s_tagList.add(ellipseTag.localName());
112 s_tagList.add(lineTag.localName());
113 s_tagList.add(pathTag.localName());
114 s_tagList.add(polygonTag.localName());
115 s_tagList.add(polylineTag.localName());
116 s_tagList.add(rectTag.localName());
117 s_tagList.add(textTag.localName());
118 s_tagList.add(textPathTag.localName());
119 s_tagList.add(tspanTag.localName());
125 static HashSet<AtomicString>& chainableResourceTags()
127 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
128 if (s_tagList.isEmpty()) {
129 s_tagList.add(linearGradientTag.localName());
130 s_tagList.add(filterTag.localName());
131 s_tagList.add(patternTag.localName());
132 s_tagList.add(radialGradientTag.localName());
138 static inline AtomicString targetReferenceFromResource(SVGElement& element)
141 if (isSVGPatternElement(element))
142 target = toSVGPatternElement(element).href()->currentValue()->value();
143 else if (isSVGGradientElement(element))
144 target = toSVGGradientElement(element).href()->currentValue()->value();
145 else if (isSVGFilterElement(element))
146 target = toSVGFilterElement(element).href()->currentValue()->value();
148 ASSERT_NOT_REACHED();
150 return SVGURIReference::fragmentIdentifierFromIRIString(target, element.treeScope());
153 static inline bool svgPaintTypeHasURL(SVGPaintType paintType)
156 case SVG_PAINTTYPE_URI_NONE:
157 case SVG_PAINTTYPE_URI_CURRENTCOLOR:
158 case SVG_PAINTTYPE_URI_RGBCOLOR:
159 case SVG_PAINTTYPE_URI:
167 static inline RenderSVGResourcePaintServer* paintingResourceFromSVGPaint(TreeScope& treeScope, const SVGPaintType& paintType, const String& paintUri, AtomicString& id, bool& hasPendingResource)
169 if (!svgPaintTypeHasURL(paintType))
172 id = SVGURIReference::fragmentIdentifierFromIRIString(paintUri, treeScope);
173 RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(treeScope, id);
175 hasPendingResource = true;
179 if (!container->isSVGPaintServer())
182 return toRenderSVGResourcePaintServer(container);
185 static inline void registerPendingResource(SVGDocumentExtensions& extensions, const AtomicString& id, SVGElement* element)
188 extensions.addPendingResource(id, element);
191 bool SVGResources::hasResourceData() const
193 return m_clipperFilterMaskerData
199 static inline SVGResources* ensureResources(OwnPtr<SVGResources>& resources)
202 resources = adoptPtr(new SVGResources);
204 return resources.get();
207 PassOwnPtr<SVGResources> SVGResources::buildResources(const RenderObject* object, const SVGRenderStyle& style)
211 Node* node = object->node();
213 ASSERT_WITH_SECURITY_IMPLICATION(node->isSVGElement());
215 SVGElement* element = toSVGElement(node);
219 TreeScope& treeScope = element->treeScope();
221 SVGDocumentExtensions& extensions = object->document().accessSVGExtensions();
223 const AtomicString& tagName = element->localName();
224 if (tagName.isNull())
227 OwnPtr<SVGResources> resources;
228 if (clipperFilterMaskerTags().contains(tagName)) {
229 if (style.hasClipper()) {
230 AtomicString id = style.clipperResource();
231 if (!ensureResources(resources)->setClipper(getRenderSVGResourceById<RenderSVGResourceClipper>(treeScope, id)))
232 registerPendingResource(extensions, id, element);
235 if (style.hasFilter()) {
236 AtomicString id = style.filterResource();
237 if (!ensureResources(resources)->setFilter(getRenderSVGResourceById<RenderSVGResourceFilter>(treeScope, id)))
238 registerPendingResource(extensions, id, element);
241 if (style.hasMasker()) {
242 AtomicString id = style.maskerResource();
243 if (!ensureResources(resources)->setMasker(getRenderSVGResourceById<RenderSVGResourceMasker>(treeScope, id)))
244 registerPendingResource(extensions, id, element);
248 if (style.hasMarkers() && supportsMarkers(*element)) {
249 const AtomicString& markerStartId = style.markerStartResource();
250 if (!ensureResources(resources)->setMarkerStart(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerStartId)))
251 registerPendingResource(extensions, markerStartId, element);
253 const AtomicString& markerMidId = style.markerMidResource();
254 if (!ensureResources(resources)->setMarkerMid(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerMidId)))
255 registerPendingResource(extensions, markerMidId, element);
257 const AtomicString& markerEndId = style.markerEndResource();
258 if (!ensureResources(resources)->setMarkerEnd(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, style.markerEndResource())))
259 registerPendingResource(extensions, markerEndId, element);
262 if (fillAndStrokeTags().contains(tagName)) {
263 if (style.hasFill()) {
264 bool hasPendingResource = false;
266 RenderSVGResourcePaintServer* resource = paintingResourceFromSVGPaint(treeScope, style.fillPaintType(), style.fillPaintUri(), id, hasPendingResource);
267 if (!ensureResources(resources)->setFill(resource) && hasPendingResource) {
268 registerPendingResource(extensions, id, element);
272 if (style.hasStroke()) {
273 bool hasPendingResource = false;
275 RenderSVGResourcePaintServer* resource = paintingResourceFromSVGPaint(treeScope, style.strokePaintType(), style.strokePaintUri(), id, hasPendingResource);
276 if (!ensureResources(resources)->setStroke(resource) && hasPendingResource) {
277 registerPendingResource(extensions, id, element);
282 if (chainableResourceTags().contains(tagName)) {
283 AtomicString id = targetReferenceFromResource(*element);
284 if (!ensureResources(resources)->setLinkedResource(getRenderSVGResourceContainerById(treeScope, id)))
285 registerPendingResource(extensions, id, element);
288 return (!resources || !resources->hasResourceData()) ? nullptr : resources.release();
291 void SVGResources::layoutIfNeeded()
293 if (m_clipperFilterMaskerData) {
294 if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
295 clipper->layoutIfNeeded();
296 if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
297 masker->layoutIfNeeded();
298 if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
299 filter->layoutIfNeeded();
303 if (RenderSVGResourceMarker* marker = m_markerData->markerStart)
304 marker->layoutIfNeeded();
305 if (RenderSVGResourceMarker* marker = m_markerData->markerMid)
306 marker->layoutIfNeeded();
307 if (RenderSVGResourceMarker* marker = m_markerData->markerEnd)
308 marker->layoutIfNeeded();
311 if (m_fillStrokeData) {
312 if (RenderSVGResourcePaintServer* fill = m_fillStrokeData->fill)
313 fill->layoutIfNeeded();
314 if (RenderSVGResourcePaintServer* stroke = m_fillStrokeData->stroke)
315 stroke->layoutIfNeeded();
318 if (m_linkedResource)
319 m_linkedResource->layoutIfNeeded();
322 void SVGResources::removeClientFromCache(RenderObject* object, bool markForInvalidation) const
324 if (!hasResourceData())
327 if (m_linkedResource) {
328 ASSERT(!m_clipperFilterMaskerData);
329 ASSERT(!m_markerData);
330 ASSERT(!m_fillStrokeData);
331 m_linkedResource->removeClientFromCache(object, markForInvalidation);
335 if (m_clipperFilterMaskerData) {
336 if (m_clipperFilterMaskerData->clipper)
337 m_clipperFilterMaskerData->clipper->removeClientFromCache(object, markForInvalidation);
338 if (m_clipperFilterMaskerData->filter)
339 m_clipperFilterMaskerData->filter->removeClientFromCache(object, markForInvalidation);
340 if (m_clipperFilterMaskerData->masker)
341 m_clipperFilterMaskerData->masker->removeClientFromCache(object, markForInvalidation);
345 if (m_markerData->markerStart)
346 m_markerData->markerStart->removeClientFromCache(object, markForInvalidation);
347 if (m_markerData->markerMid)
348 m_markerData->markerMid->removeClientFromCache(object, markForInvalidation);
349 if (m_markerData->markerEnd)
350 m_markerData->markerEnd->removeClientFromCache(object, markForInvalidation);
353 if (m_fillStrokeData) {
354 if (m_fillStrokeData->fill)
355 m_fillStrokeData->fill->removeClientFromCache(object, markForInvalidation);
356 if (m_fillStrokeData->stroke)
357 m_fillStrokeData->stroke->removeClientFromCache(object, markForInvalidation);
361 void SVGResources::resourceDestroyed(RenderSVGResourceContainer* resource)
364 if (!hasResourceData())
367 if (m_linkedResource == resource) {
368 ASSERT(!m_clipperFilterMaskerData);
369 ASSERT(!m_markerData);
370 ASSERT(!m_fillStrokeData);
371 m_linkedResource->removeAllClientsFromCache();
372 m_linkedResource = 0;
376 switch (resource->resourceType()) {
377 case MaskerResourceType:
378 if (!m_clipperFilterMaskerData)
380 if (m_clipperFilterMaskerData->masker == resource) {
381 m_clipperFilterMaskerData->masker->removeAllClientsFromCache();
382 m_clipperFilterMaskerData->masker = 0;
385 case MarkerResourceType:
388 if (m_markerData->markerStart == resource) {
389 m_markerData->markerStart->removeAllClientsFromCache();
390 m_markerData->markerStart = 0;
392 if (m_markerData->markerMid == resource) {
393 m_markerData->markerMid->removeAllClientsFromCache();
394 m_markerData->markerMid = 0;
396 if (m_markerData->markerEnd == resource) {
397 m_markerData->markerEnd->removeAllClientsFromCache();
398 m_markerData->markerEnd = 0;
401 case PatternResourceType:
402 case LinearGradientResourceType:
403 case RadialGradientResourceType:
404 if (!m_fillStrokeData)
406 if (m_fillStrokeData->fill == resource) {
407 m_fillStrokeData->fill->removeAllClientsFromCache();
408 m_fillStrokeData->fill = 0;
410 if (m_fillStrokeData->stroke == resource) {
411 m_fillStrokeData->stroke->removeAllClientsFromCache();
412 m_fillStrokeData->stroke = 0;
415 case FilterResourceType:
416 if (!m_clipperFilterMaskerData)
418 if (m_clipperFilterMaskerData->filter == resource) {
419 m_clipperFilterMaskerData->filter->removeAllClientsFromCache();
420 m_clipperFilterMaskerData->filter = 0;
423 case ClipperResourceType:
424 if (!m_clipperFilterMaskerData)
426 if (m_clipperFilterMaskerData->clipper == resource) {
427 m_clipperFilterMaskerData->clipper->removeAllClientsFromCache();
428 m_clipperFilterMaskerData->clipper = 0;
432 ASSERT_NOT_REACHED();
436 void SVGResources::buildSetOfResources(HashSet<RenderSVGResourceContainer*>& set)
438 if (!hasResourceData())
441 if (m_linkedResource) {
442 ASSERT(!m_clipperFilterMaskerData);
443 ASSERT(!m_markerData);
444 ASSERT(!m_fillStrokeData);
445 set.add(m_linkedResource);
449 if (m_clipperFilterMaskerData) {
450 if (m_clipperFilterMaskerData->clipper)
451 set.add(m_clipperFilterMaskerData->clipper);
452 if (m_clipperFilterMaskerData->filter)
453 set.add(m_clipperFilterMaskerData->filter);
454 if (m_clipperFilterMaskerData->masker)
455 set.add(m_clipperFilterMaskerData->masker);
459 if (m_markerData->markerStart)
460 set.add(m_markerData->markerStart);
461 if (m_markerData->markerMid)
462 set.add(m_markerData->markerMid);
463 if (m_markerData->markerEnd)
464 set.add(m_markerData->markerEnd);
467 if (m_fillStrokeData) {
468 if (m_fillStrokeData->fill)
469 set.add(m_fillStrokeData->fill);
470 if (m_fillStrokeData->stroke)
471 set.add(m_fillStrokeData->stroke);
475 bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
480 ASSERT(clipper->resourceType() == ClipperResourceType);
482 if (!m_clipperFilterMaskerData)
483 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
485 m_clipperFilterMaskerData->clipper = clipper;
489 void SVGResources::resetClipper()
491 ASSERT(m_clipperFilterMaskerData);
492 ASSERT(m_clipperFilterMaskerData->clipper);
493 m_clipperFilterMaskerData->clipper = 0;
496 bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
501 ASSERT(filter->resourceType() == FilterResourceType);
503 if (!m_clipperFilterMaskerData)
504 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
506 m_clipperFilterMaskerData->filter = filter;
510 void SVGResources::resetFilter()
512 ASSERT(m_clipperFilterMaskerData);
513 ASSERT(m_clipperFilterMaskerData->filter);
514 m_clipperFilterMaskerData->filter = 0;
517 bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
522 ASSERT(markerStart->resourceType() == MarkerResourceType);
525 m_markerData = MarkerData::create();
527 m_markerData->markerStart = markerStart;
531 void SVGResources::resetMarkerStart()
533 ASSERT(m_markerData);
534 ASSERT(m_markerData->markerStart);
535 m_markerData->markerStart = 0;
538 bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
543 ASSERT(markerMid->resourceType() == MarkerResourceType);
546 m_markerData = MarkerData::create();
548 m_markerData->markerMid = markerMid;
552 void SVGResources::resetMarkerMid()
554 ASSERT(m_markerData);
555 ASSERT(m_markerData->markerMid);
556 m_markerData->markerMid = 0;
559 bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
564 ASSERT(markerEnd->resourceType() == MarkerResourceType);
567 m_markerData = MarkerData::create();
569 m_markerData->markerEnd = markerEnd;
573 void SVGResources::resetMarkerEnd()
575 ASSERT(m_markerData);
576 ASSERT(m_markerData->markerEnd);
577 m_markerData->markerEnd = 0;
580 bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
585 ASSERT(masker->resourceType() == MaskerResourceType);
587 if (!m_clipperFilterMaskerData)
588 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
590 m_clipperFilterMaskerData->masker = masker;
594 void SVGResources::resetMasker()
596 ASSERT(m_clipperFilterMaskerData);
597 ASSERT(m_clipperFilterMaskerData->masker);
598 m_clipperFilterMaskerData->masker = 0;
601 bool SVGResources::setFill(RenderSVGResourcePaintServer* fill)
606 if (!m_fillStrokeData)
607 m_fillStrokeData = FillStrokeData::create();
609 m_fillStrokeData->fill = fill;
613 void SVGResources::resetFill()
615 ASSERT(m_fillStrokeData);
616 ASSERT(m_fillStrokeData->fill);
617 m_fillStrokeData->fill = 0;
620 bool SVGResources::setStroke(RenderSVGResourcePaintServer* stroke)
625 if (!m_fillStrokeData)
626 m_fillStrokeData = FillStrokeData::create();
628 m_fillStrokeData->stroke = stroke;
632 void SVGResources::resetStroke()
634 ASSERT(m_fillStrokeData);
635 ASSERT(m_fillStrokeData->stroke);
636 m_fillStrokeData->stroke = 0;
639 bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
644 m_linkedResource = linkedResource;
648 void SVGResources::resetLinkedResource()
650 ASSERT(m_linkedResource);
651 m_linkedResource = 0;
655 void SVGResources::dump(const RenderObject* object)
658 ASSERT(object->node());
660 fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
661 fprintf(stderr, " | DOM Tree:\n");
662 object->node()->showTreeForThis();
664 fprintf(stderr, "\n | List of resources:\n");
665 if (m_clipperFilterMaskerData) {
666 if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
667 fprintf(stderr, " |-> Clipper : %p (node=%p)\n", clipper, clipper->element());
668 if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
669 fprintf(stderr, " |-> Filter : %p (node=%p)\n", filter, filter->element());
670 if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
671 fprintf(stderr, " |-> Masker : %p (node=%p)\n", masker, masker->element());
675 if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
676 fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, markerStart->element());
677 if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
678 fprintf(stderr, " |-> MarkerMid : %p (node=%p)\n", markerMid, markerMid->element());
679 if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
680 fprintf(stderr, " |-> MarkerEnd : %p (node=%p)\n", markerEnd, markerEnd->element());
683 if (m_fillStrokeData) {
684 if (RenderSVGResourcePaintServer* fill = m_fillStrokeData->fill)
685 fprintf(stderr, " |-> Fill : %p (node=%p)\n", fill, fill->element());
686 if (RenderSVGResourcePaintServer* stroke = m_fillStrokeData->stroke)
687 fprintf(stderr, " |-> Stroke : %p (node=%p)\n", stroke, stroke->element());
690 if (m_linkedResource)
691 fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, m_linkedResource->element());