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 bool SVGResources::supportsMarkers(const SVGElement& element)
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());
104 return s_tagList.contains(element.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 AtomicString targetReferenceFromResource(SVGElement& element)
143 if (isSVGPatternElement(element))
144 target = toSVGPatternElement(element).href()->currentValue()->value();
145 else if (isSVGGradientElement(element))
146 target = toSVGGradientElement(element).href()->currentValue()->value();
147 else if (isSVGFilterElement(element))
148 target = toSVGFilterElement(element).href()->currentValue()->value();
150 ASSERT_NOT_REACHED();
152 return SVGURIReference::fragmentIdentifierFromIRIString(target, element.treeScope());
155 static inline bool svgPaintTypeHasURL(SVGPaint::SVGPaintType paintType)
158 case SVGPaint::SVG_PAINTTYPE_URI_NONE:
159 case SVGPaint::SVG_PAINTTYPE_URI_CURRENTCOLOR:
160 case SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR:
161 case SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR:
162 case SVGPaint::SVG_PAINTTYPE_URI:
170 static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(TreeScope& treeScope, const SVGPaint::SVGPaintType& paintType, const String& paintUri, AtomicString& id, bool& hasPendingResource)
172 if (!svgPaintTypeHasURL(paintType))
175 id = SVGURIReference::fragmentIdentifierFromIRIString(paintUri, treeScope);
176 RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(treeScope, id);
178 hasPendingResource = true;
182 RenderSVGResourceType resourceType = container->resourceType();
183 if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType)
189 static inline void registerPendingResource(SVGDocumentExtensions& extensions, const AtomicString& id, SVGElement* element)
192 extensions.addPendingResource(id, element);
195 bool SVGResources::hasResourceData() const
197 return !m_clipperFilterMaskerData
200 && !m_linkedResource;
203 static inline SVGResources* ensureResources(OwnPtr<SVGResources>& resources)
206 resources = adoptPtr(new SVGResources);
208 return resources.get();
211 PassOwnPtr<SVGResources> SVGResources::buildResources(const RenderObject* object, const SVGRenderStyle* style)
216 Node* node = object->node();
218 ASSERT_WITH_SECURITY_IMPLICATION(node->isSVGElement());
220 SVGElement* element = toSVGElement(node);
224 TreeScope& treeScope = element->treeScope();
226 SVGDocumentExtensions& extensions = object->document().accessSVGExtensions();
228 const AtomicString& tagName = element->localName();
229 if (tagName.isNull())
232 OwnPtr<SVGResources> resources;
233 if (clipperFilterMaskerTags().contains(tagName)) {
234 if (style->hasClipper()) {
235 AtomicString id = style->clipperResource();
236 if (!ensureResources(resources)->setClipper(getRenderSVGResourceById<RenderSVGResourceClipper>(treeScope, id)))
237 registerPendingResource(extensions, id, element);
240 if (style->hasFilter()) {
241 AtomicString id = style->filterResource();
242 if (!ensureResources(resources)->setFilter(getRenderSVGResourceById<RenderSVGResourceFilter>(treeScope, id)))
243 registerPendingResource(extensions, id, element);
246 if (style->hasMasker()) {
247 AtomicString id = style->maskerResource();
248 if (!ensureResources(resources)->setMasker(getRenderSVGResourceById<RenderSVGResourceMasker>(treeScope, id)))
249 registerPendingResource(extensions, id, element);
253 if (style->hasMarkers() && supportsMarkers(*element)) {
254 const AtomicString& markerStartId = style->markerStartResource();
255 if (!ensureResources(resources)->setMarkerStart(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerStartId)))
256 registerPendingResource(extensions, markerStartId, element);
258 const AtomicString& markerMidId = style->markerMidResource();
259 if (!ensureResources(resources)->setMarkerMid(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerMidId)))
260 registerPendingResource(extensions, markerMidId, element);
262 const AtomicString& markerEndId = style->markerEndResource();
263 if (!ensureResources(resources)->setMarkerEnd(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, style->markerEndResource())))
264 registerPendingResource(extensions, markerEndId, element);
267 if (fillAndStrokeTags().contains(tagName)) {
268 if (style->hasFill()) {
269 bool hasPendingResource = false;
271 RenderSVGResourceContainer* resource = paintingResourceFromSVGPaint(treeScope, style->fillPaintType(), style->fillPaintUri(), id, hasPendingResource);
272 if (!ensureResources(resources)->setFill(resource) && hasPendingResource) {
273 registerPendingResource(extensions, id, element);
277 if (style->hasStroke()) {
278 bool hasPendingResource = false;
280 RenderSVGResourceContainer* resource = paintingResourceFromSVGPaint(treeScope, style->strokePaintType(), style->strokePaintUri(), id, hasPendingResource);
281 if (!ensureResources(resources)->setStroke(resource) && hasPendingResource) {
282 registerPendingResource(extensions, id, element);
287 if (chainableResourceTags().contains(tagName)) {
288 AtomicString id = targetReferenceFromResource(*element);
289 if (!ensureResources(resources)->setLinkedResource(getRenderSVGResourceContainerById(treeScope, id)))
290 registerPendingResource(extensions, id, element);
293 return (!resources || resources->hasResourceData()) ? nullptr : resources.release();
296 void SVGResources::layoutIfNeeded()
298 if (m_clipperFilterMaskerData) {
299 if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
300 clipper->layoutIfNeeded();
301 if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
302 masker->layoutIfNeeded();
303 if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
304 filter->layoutIfNeeded();
308 if (RenderSVGResourceMarker* marker = m_markerData->markerStart)
309 marker->layoutIfNeeded();
310 if (RenderSVGResourceMarker* marker = m_markerData->markerMid)
311 marker->layoutIfNeeded();
312 if (RenderSVGResourceMarker* marker = m_markerData->markerEnd)
313 marker->layoutIfNeeded();
316 if (m_fillStrokeData) {
317 if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
318 fill->layoutIfNeeded();
319 if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
320 stroke->layoutIfNeeded();
323 if (m_linkedResource)
324 m_linkedResource->layoutIfNeeded();
327 void SVGResources::removeClientFromCache(RenderObject* object, bool markForInvalidation) const
329 if (hasResourceData())
332 if (m_linkedResource) {
333 ASSERT(!m_clipperFilterMaskerData);
334 ASSERT(!m_markerData);
335 ASSERT(!m_fillStrokeData);
336 m_linkedResource->removeClientFromCache(object, markForInvalidation);
340 if (m_clipperFilterMaskerData) {
341 if (m_clipperFilterMaskerData->clipper)
342 m_clipperFilterMaskerData->clipper->removeClientFromCache(object, markForInvalidation);
343 if (m_clipperFilterMaskerData->filter)
344 m_clipperFilterMaskerData->filter->removeClientFromCache(object, markForInvalidation);
345 if (m_clipperFilterMaskerData->masker)
346 m_clipperFilterMaskerData->masker->removeClientFromCache(object, markForInvalidation);
350 if (m_markerData->markerStart)
351 m_markerData->markerStart->removeClientFromCache(object, markForInvalidation);
352 if (m_markerData->markerMid)
353 m_markerData->markerMid->removeClientFromCache(object, markForInvalidation);
354 if (m_markerData->markerEnd)
355 m_markerData->markerEnd->removeClientFromCache(object, markForInvalidation);
358 if (m_fillStrokeData) {
359 if (m_fillStrokeData->fill)
360 m_fillStrokeData->fill->removeClientFromCache(object, markForInvalidation);
361 if (m_fillStrokeData->stroke)
362 m_fillStrokeData->stroke->removeClientFromCache(object, markForInvalidation);
366 void SVGResources::resourceDestroyed(RenderSVGResourceContainer* resource)
369 if (hasResourceData())
372 if (m_linkedResource == resource) {
373 ASSERT(!m_clipperFilterMaskerData);
374 ASSERT(!m_markerData);
375 ASSERT(!m_fillStrokeData);
376 m_linkedResource->removeAllClientsFromCache();
377 m_linkedResource = 0;
381 switch (resource->resourceType()) {
382 case MaskerResourceType:
383 if (!m_clipperFilterMaskerData)
385 if (m_clipperFilterMaskerData->masker == resource) {
386 m_clipperFilterMaskerData->masker->removeAllClientsFromCache();
387 m_clipperFilterMaskerData->masker = 0;
390 case MarkerResourceType:
393 if (m_markerData->markerStart == resource) {
394 m_markerData->markerStart->removeAllClientsFromCache();
395 m_markerData->markerStart = 0;
397 if (m_markerData->markerMid == resource) {
398 m_markerData->markerMid->removeAllClientsFromCache();
399 m_markerData->markerMid = 0;
401 if (m_markerData->markerEnd == resource) {
402 m_markerData->markerEnd->removeAllClientsFromCache();
403 m_markerData->markerEnd = 0;
406 case PatternResourceType:
407 case LinearGradientResourceType:
408 case RadialGradientResourceType:
409 if (!m_fillStrokeData)
411 if (m_fillStrokeData->fill == resource) {
412 m_fillStrokeData->fill->removeAllClientsFromCache();
413 m_fillStrokeData->fill = 0;
415 if (m_fillStrokeData->stroke == resource) {
416 m_fillStrokeData->stroke->removeAllClientsFromCache();
417 m_fillStrokeData->stroke = 0;
420 case FilterResourceType:
421 if (!m_clipperFilterMaskerData)
423 if (m_clipperFilterMaskerData->filter == resource) {
424 m_clipperFilterMaskerData->filter->removeAllClientsFromCache();
425 m_clipperFilterMaskerData->filter = 0;
428 case ClipperResourceType:
429 if (!m_clipperFilterMaskerData)
431 if (m_clipperFilterMaskerData->clipper == resource) {
432 m_clipperFilterMaskerData->clipper->removeAllClientsFromCache();
433 m_clipperFilterMaskerData->clipper = 0;
436 case SolidColorResourceType:
437 ASSERT_NOT_REACHED();
441 void SVGResources::buildSetOfResources(HashSet<RenderSVGResourceContainer*>& set)
443 if (hasResourceData())
446 if (m_linkedResource) {
447 ASSERT(!m_clipperFilterMaskerData);
448 ASSERT(!m_markerData);
449 ASSERT(!m_fillStrokeData);
450 set.add(m_linkedResource);
454 if (m_clipperFilterMaskerData) {
455 if (m_clipperFilterMaskerData->clipper)
456 set.add(m_clipperFilterMaskerData->clipper);
457 if (m_clipperFilterMaskerData->filter)
458 set.add(m_clipperFilterMaskerData->filter);
459 if (m_clipperFilterMaskerData->masker)
460 set.add(m_clipperFilterMaskerData->masker);
464 if (m_markerData->markerStart)
465 set.add(m_markerData->markerStart);
466 if (m_markerData->markerMid)
467 set.add(m_markerData->markerMid);
468 if (m_markerData->markerEnd)
469 set.add(m_markerData->markerEnd);
472 if (m_fillStrokeData) {
473 if (m_fillStrokeData->fill)
474 set.add(m_fillStrokeData->fill);
475 if (m_fillStrokeData->stroke)
476 set.add(m_fillStrokeData->stroke);
480 bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
485 ASSERT(clipper->resourceType() == ClipperResourceType);
487 if (!m_clipperFilterMaskerData)
488 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
490 m_clipperFilterMaskerData->clipper = clipper;
494 void SVGResources::resetClipper()
496 ASSERT(m_clipperFilterMaskerData);
497 ASSERT(m_clipperFilterMaskerData->clipper);
498 m_clipperFilterMaskerData->clipper = 0;
501 bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
506 ASSERT(filter->resourceType() == FilterResourceType);
508 if (!m_clipperFilterMaskerData)
509 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
511 m_clipperFilterMaskerData->filter = filter;
515 void SVGResources::resetFilter()
517 ASSERT(m_clipperFilterMaskerData);
518 ASSERT(m_clipperFilterMaskerData->filter);
519 m_clipperFilterMaskerData->filter = 0;
522 bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
527 ASSERT(markerStart->resourceType() == MarkerResourceType);
530 m_markerData = MarkerData::create();
532 m_markerData->markerStart = markerStart;
536 void SVGResources::resetMarkerStart()
538 ASSERT(m_markerData);
539 ASSERT(m_markerData->markerStart);
540 m_markerData->markerStart = 0;
543 bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
548 ASSERT(markerMid->resourceType() == MarkerResourceType);
551 m_markerData = MarkerData::create();
553 m_markerData->markerMid = markerMid;
557 void SVGResources::resetMarkerMid()
559 ASSERT(m_markerData);
560 ASSERT(m_markerData->markerMid);
561 m_markerData->markerMid = 0;
564 bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
569 ASSERT(markerEnd->resourceType() == MarkerResourceType);
572 m_markerData = MarkerData::create();
574 m_markerData->markerEnd = markerEnd;
578 void SVGResources::resetMarkerEnd()
580 ASSERT(m_markerData);
581 ASSERT(m_markerData->markerEnd);
582 m_markerData->markerEnd = 0;
585 bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
590 ASSERT(masker->resourceType() == MaskerResourceType);
592 if (!m_clipperFilterMaskerData)
593 m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
595 m_clipperFilterMaskerData->masker = masker;
599 void SVGResources::resetMasker()
601 ASSERT(m_clipperFilterMaskerData);
602 ASSERT(m_clipperFilterMaskerData->masker);
603 m_clipperFilterMaskerData->masker = 0;
606 bool SVGResources::setFill(RenderSVGResourceContainer* fill)
611 ASSERT(fill->resourceType() == PatternResourceType
612 || fill->resourceType() == LinearGradientResourceType
613 || fill->resourceType() == RadialGradientResourceType);
615 if (!m_fillStrokeData)
616 m_fillStrokeData = FillStrokeData::create();
618 m_fillStrokeData->fill = fill;
622 void SVGResources::resetFill()
624 ASSERT(m_fillStrokeData);
625 ASSERT(m_fillStrokeData->fill);
626 m_fillStrokeData->fill = 0;
629 bool SVGResources::setStroke(RenderSVGResourceContainer* stroke)
634 ASSERT(stroke->resourceType() == PatternResourceType
635 || stroke->resourceType() == LinearGradientResourceType
636 || stroke->resourceType() == RadialGradientResourceType);
638 if (!m_fillStrokeData)
639 m_fillStrokeData = FillStrokeData::create();
641 m_fillStrokeData->stroke = stroke;
645 void SVGResources::resetStroke()
647 ASSERT(m_fillStrokeData);
648 ASSERT(m_fillStrokeData->stroke);
649 m_fillStrokeData->stroke = 0;
652 bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
657 m_linkedResource = linkedResource;
661 void SVGResources::resetLinkedResource()
663 ASSERT(m_linkedResource);
664 m_linkedResource = 0;
668 void SVGResources::dump(const RenderObject* object)
671 ASSERT(object->node());
673 fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
674 fprintf(stderr, " | DOM Tree:\n");
675 object->node()->showTreeForThis();
677 fprintf(stderr, "\n | List of resources:\n");
678 if (m_clipperFilterMaskerData) {
679 if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
680 fprintf(stderr, " |-> Clipper : %p (node=%p)\n", clipper, clipper->element());
681 if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
682 fprintf(stderr, " |-> Filter : %p (node=%p)\n", filter, filter->element());
683 if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
684 fprintf(stderr, " |-> Masker : %p (node=%p)\n", masker, masker->element());
688 if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
689 fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, markerStart->element());
690 if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
691 fprintf(stderr, " |-> MarkerMid : %p (node=%p)\n", markerMid, markerMid->element());
692 if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
693 fprintf(stderr, " |-> MarkerEnd : %p (node=%p)\n", markerEnd, markerEnd->element());
696 if (m_fillStrokeData) {
697 if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
698 fprintf(stderr, " |-> Fill : %p (node=%p)\n", fill, fill->element());
699 if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
700 fprintf(stderr, " |-> Stroke : %p (node=%p)\n", stroke, stroke->element());
703 if (m_linkedResource)
704 fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, m_linkedResource->element());