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"
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/svg/SVGFilterElement.h"
30 #include "core/svg/SVGGradientElement.h"
31 #include "core/svg/SVGPaint.h"
32 #include "core/svg/SVGPatternElement.h"
33 #include "core/svg/SVGURIReference.h"
41 SVGResources::SVGResources()
46 static HashSet<AtomicString>& clipperFilterMaskerTags()
48 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
49 if (s_tagList.isEmpty()) {
50 // "container elements": http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
51 // "graphics elements" : http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement
52 s_tagList.add(SVGNames::aTag.localName());
53 s_tagList.add(SVGNames::circleTag.localName());
54 s_tagList.add(SVGNames::ellipseTag.localName());
55 s_tagList.add(SVGNames::glyphTag.localName());
56 s_tagList.add(SVGNames::gTag.localName());
57 s_tagList.add(SVGNames::imageTag.localName());
58 s_tagList.add(SVGNames::lineTag.localName());
59 s_tagList.add(SVGNames::markerTag.localName());
60 s_tagList.add(SVGNames::maskTag.localName());
61 s_tagList.add(SVGNames::missing_glyphTag.localName());
62 s_tagList.add(SVGNames::pathTag.localName());
63 s_tagList.add(SVGNames::polygonTag.localName());
64 s_tagList.add(SVGNames::polylineTag.localName());
65 s_tagList.add(SVGNames::rectTag.localName());
66 s_tagList.add(SVGNames::svgTag.localName());
67 s_tagList.add(SVGNames::textTag.localName());
68 s_tagList.add(SVGNames::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(SVGNames::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(SVGNames::altGlyphTag.localName());
79 s_tagList.add(SVGNames::textPathTag.localName());
80 s_tagList.add(SVGNames::tspanTag.localName());
82 // Not listed in the definitions is the foreignObject element, but clip-path
83 // is a supported attribute.
84 s_tagList.add(SVGNames::foreignObjectTag.localName());
86 // Elements that we ignore, as it doesn't make any sense.
87 // defs, pattern, switch (FIXME: Mail SVG WG about these)
88 // symbol (is converted to a svg element, when referenced by use, we can safely ignore it.)
94 static HashSet<AtomicString>& markerTags()
96 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
97 if (s_tagList.isEmpty()) {
98 s_tagList.add(SVGNames::lineTag.localName());
99 s_tagList.add(SVGNames::pathTag.localName());
100 s_tagList.add(SVGNames::polygonTag.localName());
101 s_tagList.add(SVGNames::polylineTag.localName());
107 static HashSet<AtomicString>& fillAndStrokeTags()
109 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
110 if (s_tagList.isEmpty()) {
111 s_tagList.add(SVGNames::altGlyphTag.localName());
112 s_tagList.add(SVGNames::circleTag.localName());
113 s_tagList.add(SVGNames::ellipseTag.localName());
114 s_tagList.add(SVGNames::lineTag.localName());
115 s_tagList.add(SVGNames::pathTag.localName());
116 s_tagList.add(SVGNames::polygonTag.localName());
117 s_tagList.add(SVGNames::polylineTag.localName());
118 s_tagList.add(SVGNames::rectTag.localName());
119 s_tagList.add(SVGNames::textTag.localName());
120 s_tagList.add(SVGNames::textPathTag.localName());
121 s_tagList.add(SVGNames::tspanTag.localName());
127 static HashSet<AtomicString>& chainableResourceTags()
129 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
130 if (s_tagList.isEmpty()) {
131 s_tagList.add(SVGNames::linearGradientTag.localName());
132 s_tagList.add(SVGNames::filterTag.localName());
133 s_tagList.add(SVGNames::patternTag.localName());
134 s_tagList.add(SVGNames::radialGradientTag.localName());
140 static inline String targetReferenceFromResource(SVGElement* element)
143 if (element->hasTagName(SVGNames::patternTag))
144 target = toSVGPatternElement(element)->hrefCurrentValue();
145 else if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag))
146 target = toSVGGradientElement(element)->hrefCurrentValue();
147 else if (element->hasTagName(SVGNames::filterTag))
148 target = toSVGFilterElement(element)->hrefCurrentValue();
150 ASSERT_NOT_REACHED();
152 return SVGURIReference::fragmentIdentifierFromIRIString(target, element->document());
155 static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(Document& document, const SVGPaint::SVGPaintType& paintType, const String& paintUri, AtomicString& id, bool& hasPendingResource)
157 if (paintType != SVGPaint::SVG_PAINTTYPE_URI && paintType != SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR)
160 id = SVGURIReference::fragmentIdentifierFromIRIString(paintUri, document);
161 RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(document, id);
163 hasPendingResource = true;
167 RenderSVGResourceType resourceType = container->resourceType();
168 if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType)
174 static inline void registerPendingResource(SVGDocumentExtensions* extensions, const AtomicString& id, SVGElement* element)
177 extensions->addPendingResource(id, element);
180 bool SVGResources::buildResources(const RenderObject* object, const SVGRenderStyle* style)
185 Node* node = object->node();
187 ASSERT_WITH_SECURITY_IMPLICATION(node->isSVGElement());
189 SVGElement* element = toSVGElement(node);
193 Document& document = object->document();
195 SVGDocumentExtensions* extensions = document.accessSVGExtensions();
198 const AtomicString& tagName = element->localName();
199 if (tagName.isNull())
202 bool foundResources = false;
203 if (clipperFilterMaskerTags().contains(tagName)) {
204 if (style->hasClipper()) {
205 AtomicString id(style->clipperResource());
206 if (setClipper(getRenderSVGResourceById<RenderSVGResourceClipper>(document, id)))
207 foundResources = true;
209 registerPendingResource(extensions, id, element);
212 if (style->hasFilter()) {
213 AtomicString id(style->filterResource());
214 if (setFilter(getRenderSVGResourceById<RenderSVGResourceFilter>(document, id)))
215 foundResources = true;
217 registerPendingResource(extensions, id, element);
220 if (style->hasMasker()) {
221 AtomicString id(style->maskerResource());
222 if (setMasker(getRenderSVGResourceById<RenderSVGResourceMasker>(document, id)))
223 foundResources = true;
225 registerPendingResource(extensions, id, element);
229 if (markerTags().contains(tagName) && style->hasMarkers()) {
230 AtomicString markerStartId(style->markerStartResource());
231 if (setMarkerStart(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerStartId)))
232 foundResources = true;
234 registerPendingResource(extensions, markerStartId, element);
236 AtomicString markerMidId(style->markerMidResource());
237 if (setMarkerMid(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerMidId)))
238 foundResources = true;
240 registerPendingResource(extensions, markerMidId, element);
242 AtomicString markerEndId(style->markerEndResource());
243 if (setMarkerEnd(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerEndId)))
244 foundResources = true;
246 registerPendingResource(extensions, markerEndId, element);
249 if (fillAndStrokeTags().contains(tagName)) {
250 if (style->hasFill()) {
251 bool hasPendingResource = false;
253 if (setFill(paintingResourceFromSVGPaint(document, style->fillPaintType(), style->fillPaintUri(), id, hasPendingResource)))
254 foundResources = true;
255 else if (hasPendingResource)
256 registerPendingResource(extensions, id, element);
259 if (style->hasStroke()) {
260 bool hasPendingResource = false;
262 if (setStroke(paintingResourceFromSVGPaint(document, style->strokePaintType(), style->strokePaintUri(), id, hasPendingResource)))
263 foundResources = true;
264 else if (hasPendingResource)
265 registerPendingResource(extensions, id, element);
269 if (chainableResourceTags().contains(tagName)) {
270 AtomicString id(targetReferenceFromResource(element));
271 if (setLinkedResource(getRenderSVGResourceContainerById(document, id)))
272 foundResources = true;
274 registerPendingResource(extensions, id, element);
277 return foundResources;
280 void SVGResources::layoutIfNeeded()
282 if (m_clipperFilterMaskerData) {
283 if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
284 clipper->layoutIfNeeded();
285 if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
286 masker->layoutIfNeeded();
287 if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
288 filter->layoutIfNeeded();
292 if (RenderSVGResourceMarker* marker = m_markerData->markerStart)
293 marker->layoutIfNeeded();
294 if (RenderSVGResourceMarker* marker = m_markerData->markerMid)
295 marker->layoutIfNeeded();
296 if (RenderSVGResourceMarker* marker = m_markerData->markerEnd)
297 marker->layoutIfNeeded();
300 if (m_fillStrokeData) {
301 if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
302 fill->layoutIfNeeded();
303 if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
304 stroke->layoutIfNeeded();
307 if (m_linkedResource)
308 m_linkedResource->layoutIfNeeded();
311 void SVGResources::removeClientFromCache(RenderObject* object, bool markForInvalidation) const
313 if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
316 if (m_linkedResource) {
317 ASSERT(!m_clipperFilterMaskerData);
318 ASSERT(!m_markerData);
319 ASSERT(!m_fillStrokeData);
320 m_linkedResource->removeClientFromCache(object, markForInvalidation);
324 if (m_clipperFilterMaskerData) {
325 if (m_clipperFilterMaskerData->clipper)
326 m_clipperFilterMaskerData->clipper->removeClientFromCache(object, markForInvalidation);
327 if (m_clipperFilterMaskerData->filter)
328 m_clipperFilterMaskerData->filter->removeClientFromCache(object, markForInvalidation);
329 if (m_clipperFilterMaskerData->masker)
330 m_clipperFilterMaskerData->masker->removeClientFromCache(object, markForInvalidation);
334 if (m_markerData->markerStart)
335 m_markerData->markerStart->removeClientFromCache(object, markForInvalidation);
336 if (m_markerData->markerMid)
337 m_markerData->markerMid->removeClientFromCache(object, markForInvalidation);
338 if (m_markerData->markerEnd)
339 m_markerData->markerEnd->removeClientFromCache(object, markForInvalidation);
342 if (m_fillStrokeData) {
343 if (m_fillStrokeData->fill)
344 m_fillStrokeData->fill->removeClientFromCache(object, markForInvalidation);
345 if (m_fillStrokeData->stroke)
346 m_fillStrokeData->stroke->removeClientFromCache(object, markForInvalidation);
350 void SVGResources::resourceDestroyed(RenderSVGResourceContainer* resource)
353 if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
356 if (m_linkedResource == resource) {
357 ASSERT(!m_clipperFilterMaskerData);
358 ASSERT(!m_markerData);
359 ASSERT(!m_fillStrokeData);
360 m_linkedResource->removeAllClientsFromCache();
361 m_linkedResource = 0;
365 switch (resource->resourceType()) {
366 case MaskerResourceType:
367 if (!m_clipperFilterMaskerData)
369 if (m_clipperFilterMaskerData->masker == resource) {
370 m_clipperFilterMaskerData->masker->removeAllClientsFromCache();
371 m_clipperFilterMaskerData->masker = 0;
374 case MarkerResourceType:
377 if (m_markerData->markerStart == resource) {
378 m_markerData->markerStart->removeAllClientsFromCache();
379 m_markerData->markerStart = 0;
381 if (m_markerData->markerMid == resource) {
382 m_markerData->markerMid->removeAllClientsFromCache();
383 m_markerData->markerMid = 0;
385 if (m_markerData->markerEnd == resource) {
386 m_markerData->markerEnd->removeAllClientsFromCache();
387 m_markerData->markerEnd = 0;
390 case PatternResourceType:
391 case LinearGradientResourceType:
392 case RadialGradientResourceType:
393 if (!m_fillStrokeData)
395 if (m_fillStrokeData->fill == resource) {
396 m_fillStrokeData->fill->removeAllClientsFromCache();
397 m_fillStrokeData->fill = 0;
399 if (m_fillStrokeData->stroke == resource) {
400 m_fillStrokeData->stroke->removeAllClientsFromCache();
401 m_fillStrokeData->stroke = 0;
404 case FilterResourceType:
405 if (!m_clipperFilterMaskerData)
407 if (m_clipperFilterMaskerData->filter == resource) {
408 m_clipperFilterMaskerData->filter->removeAllClientsFromCache();
409 m_clipperFilterMaskerData->filter = 0;
412 case ClipperResourceType:
413 if (!m_clipperFilterMaskerData)
415 if (m_clipperFilterMaskerData->clipper == resource) {
416 m_clipperFilterMaskerData->clipper->removeAllClientsFromCache();
417 m_clipperFilterMaskerData->clipper = 0;
420 case SolidColorResourceType:
421 ASSERT_NOT_REACHED();
425 void SVGResources::buildSetOfResources(HashSet<RenderSVGResourceContainer*>& set)
427 if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
430 if (m_linkedResource) {
431 ASSERT(!m_clipperFilterMaskerData);
432 ASSERT(!m_markerData);
433 ASSERT(!m_fillStrokeData);
434 set.add(m_linkedResource);
438 if (m_clipperFilterMaskerData) {
439 if (m_clipperFilterMaskerData->clipper)
440 set.add(m_clipperFilterMaskerData->clipper);
441 if (m_clipperFilterMaskerData->filter)
442 set.add(m_clipperFilterMaskerData->filter);
443 if (m_clipperFilterMaskerData->masker)
444 set.add(m_clipperFilterMaskerData->masker);
448 if (m_markerData->markerStart)
449 set.add(m_markerData->markerStart);
450 if (m_markerData->markerMid)
451 set.add(m_markerData->markerMid);
452 if (m_markerData->markerEnd)
453 set.add(m_markerData->markerEnd);
456 if (m_fillStrokeData) {
457 if (m_fillStrokeData->fill)
458 set.add(m_fillStrokeData->fill);
459 if (m_fillStrokeData->stroke)
460 set.add(m_fillStrokeData->stroke);
464 bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
469 ASSERT(clipper->resourceType() == ClipperResourceType);
471 if (!m_clipperFilterMaskerData)
472 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
474 m_clipperFilterMaskerData->clipper = clipper;
478 void SVGResources::resetClipper()
480 ASSERT(m_clipperFilterMaskerData);
481 ASSERT(m_clipperFilterMaskerData->clipper);
482 m_clipperFilterMaskerData->clipper = 0;
485 bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
490 ASSERT(filter->resourceType() == FilterResourceType);
492 if (!m_clipperFilterMaskerData)
493 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
495 m_clipperFilterMaskerData->filter = filter;
499 void SVGResources::resetFilter()
501 ASSERT(m_clipperFilterMaskerData);
502 ASSERT(m_clipperFilterMaskerData->filter);
503 m_clipperFilterMaskerData->filter = 0;
506 bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
511 ASSERT(markerStart->resourceType() == MarkerResourceType);
514 m_markerData = MarkerData::create();
516 m_markerData->markerStart = markerStart;
520 void SVGResources::resetMarkerStart()
522 ASSERT(m_markerData);
523 ASSERT(m_markerData->markerStart);
524 m_markerData->markerStart = 0;
527 bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
532 ASSERT(markerMid->resourceType() == MarkerResourceType);
535 m_markerData = MarkerData::create();
537 m_markerData->markerMid = markerMid;
541 void SVGResources::resetMarkerMid()
543 ASSERT(m_markerData);
544 ASSERT(m_markerData->markerMid);
545 m_markerData->markerMid = 0;
548 bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
553 ASSERT(markerEnd->resourceType() == MarkerResourceType);
556 m_markerData = MarkerData::create();
558 m_markerData->markerEnd = markerEnd;
562 void SVGResources::resetMarkerEnd()
564 ASSERT(m_markerData);
565 ASSERT(m_markerData->markerEnd);
566 m_markerData->markerEnd = 0;
569 bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
574 ASSERT(masker->resourceType() == MaskerResourceType);
576 if (!m_clipperFilterMaskerData)
577 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
579 m_clipperFilterMaskerData->masker = masker;
583 void SVGResources::resetMasker()
585 ASSERT(m_clipperFilterMaskerData);
586 ASSERT(m_clipperFilterMaskerData->masker);
587 m_clipperFilterMaskerData->masker = 0;
590 bool SVGResources::setFill(RenderSVGResourceContainer* fill)
595 ASSERT(fill->resourceType() == PatternResourceType
596 || fill->resourceType() == LinearGradientResourceType
597 || fill->resourceType() == RadialGradientResourceType);
599 if (!m_fillStrokeData)
600 m_fillStrokeData = FillStrokeData::create();
602 m_fillStrokeData->fill = fill;
606 void SVGResources::resetFill()
608 ASSERT(m_fillStrokeData);
609 ASSERT(m_fillStrokeData->fill);
610 m_fillStrokeData->fill = 0;
613 bool SVGResources::setStroke(RenderSVGResourceContainer* stroke)
618 ASSERT(stroke->resourceType() == PatternResourceType
619 || stroke->resourceType() == LinearGradientResourceType
620 || stroke->resourceType() == RadialGradientResourceType);
622 if (!m_fillStrokeData)
623 m_fillStrokeData = FillStrokeData::create();
625 m_fillStrokeData->stroke = stroke;
629 void SVGResources::resetStroke()
631 ASSERT(m_fillStrokeData);
632 ASSERT(m_fillStrokeData->stroke);
633 m_fillStrokeData->stroke = 0;
636 bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
641 m_linkedResource = linkedResource;
645 void SVGResources::resetLinkedResource()
647 ASSERT(m_linkedResource);
648 m_linkedResource = 0;
652 void SVGResources::dump(const RenderObject* object)
655 ASSERT(object->node());
657 fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
658 fprintf(stderr, " | DOM Tree:\n");
659 object->node()->showTreeForThis();
661 fprintf(stderr, "\n | List of resources:\n");
662 if (m_clipperFilterMaskerData) {
663 if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
664 fprintf(stderr, " |-> Clipper : %p (node=%p)\n", clipper, clipper->element());
665 if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
666 fprintf(stderr, " |-> Filter : %p (node=%p)\n", filter, filter->element());
667 if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
668 fprintf(stderr, " |-> Masker : %p (node=%p)\n", masker, masker->element());
672 if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
673 fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, markerStart->element());
674 if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
675 fprintf(stderr, " |-> MarkerMid : %p (node=%p)\n", markerMid, markerMid->element());
676 if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
677 fprintf(stderr, " |-> MarkerEnd : %p (node=%p)\n", markerEnd, markerEnd->element());
680 if (m_fillStrokeData) {
681 if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
682 fprintf(stderr, " |-> Fill : %p (node=%p)\n", fill, fill->element());
683 if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
684 fprintf(stderr, " |-> Stroke : %p (node=%p)\n", stroke, stroke->element());
687 if (m_linkedResource)
688 fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, m_linkedResource->element());