1/*
2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2007 Rob Buis <buis@kde.org>
4 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
5 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24
25#if ENABLE(SVG)
26#include "RenderSVGResource.h"
27
28#include "Frame.h"
29#include "FrameView.h"
30#include "RenderSVGResourceClipper.h"
31#include "RenderSVGResourceContainer.h"
32#include "RenderSVGResourceFilter.h"
33#include "RenderSVGResourceMasker.h"
34#include "RenderSVGResourceSolidColor.h"
35#include "SVGResources.h"
36#include "SVGResourcesCache.h"
37#include "SVGURIReference.h"
38
39namespace WebCore {
40
41static inline bool inheritColorFromParentStyleIfNeeded(RenderObject* object, bool applyToFill, Color& color)
42{
43    if (color.isValid())
44        return true;
45    if (!object->parent() || !object->parent()->style())
46        return false;
47    const SVGRenderStyle* parentSVGStyle = object->parent()->style()->svgStyle();
48    color = applyToFill ? parentSVGStyle->fillPaintColor() : parentSVGStyle->strokePaintColor();
49    return true;
50}
51
52static inline RenderSVGResource* requestPaintingResource(RenderSVGResourceMode mode, RenderObject* object, const RenderStyle* style, Color& fallbackColor)
53{
54    ASSERT(object);
55    ASSERT(style);
56
57    // If we have no style at all, ignore it.
58    const SVGRenderStyle* svgStyle = style->svgStyle();
59    if (!svgStyle)
60        return 0;
61
62    bool isRenderingMask = false;
63    if (object->frame() && object->frame()->view())
64        isRenderingMask = object->frame()->view()->paintBehavior() & PaintBehaviorRenderingSVGMask;
65
66    // If we have no fill/stroke, return 0.
67    if (mode == ApplyToFillMode) {
68        // When rendering the mask for a RenderSVGResourceClipper, always use the initial fill paint server, and ignore stroke.
69        if (isRenderingMask) {
70            RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource();
71            colorResource->setColor(SVGRenderStyle::initialFillPaintColor());
72            return colorResource;
73        }
74
75        if (!svgStyle->hasFill())
76            return 0;
77    } else {
78        if (!svgStyle->hasStroke() || isRenderingMask)
79            return 0;
80    }
81
82    bool applyToFill = mode == ApplyToFillMode;
83    SVGPaint::SVGPaintType paintType = applyToFill ? svgStyle->fillPaintType() : svgStyle->strokePaintType();
84    if (paintType == SVGPaint::SVG_PAINTTYPE_NONE)
85        return 0;
86
87    Color color;
88    switch (paintType) {
89    case SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR:
90    case SVGPaint::SVG_PAINTTYPE_RGBCOLOR:
91    case SVGPaint::SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR:
92    case SVGPaint::SVG_PAINTTYPE_URI_CURRENTCOLOR:
93    case SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR:
94    case SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR:
95        color = applyToFill ? svgStyle->fillPaintColor() : svgStyle->strokePaintColor();
96    default:
97        break;
98    }
99
100    if (style->insideLink() == InsideVisitedLink) {
101        // FIXME: This code doesn't support the uri component of the visited link paint, https://bugs.webkit.org/show_bug.cgi?id=70006
102        SVGPaint::SVGPaintType visitedPaintType = applyToFill ? svgStyle->visitedLinkFillPaintType() : svgStyle->visitedLinkStrokePaintType();
103
104        // For SVG_PAINTTYPE_CURRENTCOLOR, 'color' already contains the 'visitedColor'.
105        if (visitedPaintType < SVGPaint::SVG_PAINTTYPE_URI_NONE && visitedPaintType != SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) {
106            const Color& visitedColor = applyToFill ? svgStyle->visitedLinkFillPaintColor() : svgStyle->visitedLinkStrokePaintColor();
107            if (visitedColor.isValid())
108                color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha());
109        }
110    }
111
112    // If the primary resource is just a color, return immediately.
113    RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource();
114    if (paintType < SVGPaint::SVG_PAINTTYPE_URI_NONE) {
115        if (!inheritColorFromParentStyleIfNeeded(object, applyToFill, color))
116            return 0;
117
118        colorResource->setColor(color);
119        return colorResource;
120    }
121
122    // If no resources are associated with the given renderer, return the color resource.
123    SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
124    if (!resources) {
125        if (paintType == SVGPaint::SVG_PAINTTYPE_URI_NONE || !inheritColorFromParentStyleIfNeeded(object, applyToFill, color))
126            return 0;
127
128        colorResource->setColor(color);
129        return colorResource;
130    }
131
132    // If the requested resource is not available, return the color resource.
133    RenderSVGResource* uriResource = mode == ApplyToFillMode ? resources->fill() : resources->stroke();
134    if (!uriResource) {
135        if (!inheritColorFromParentStyleIfNeeded(object, applyToFill, color))
136            return 0;
137
138        colorResource->setColor(color);
139        return colorResource;
140    }
141
142    // The paint server resource exists, though it may be invalid (pattern with width/height=0). Pass the fallback color to our caller
143    // so it can use the solid color painting resource, if applyResource() on the URI resource failed.
144    fallbackColor = color;
145    return uriResource;
146}
147
148RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderObject* object, const RenderStyle* style, Color& fallbackColor)
149{
150    return requestPaintingResource(ApplyToFillMode, object, style, fallbackColor);
151}
152
153RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderObject* object, const RenderStyle* style, Color& fallbackColor)
154{
155    return requestPaintingResource(ApplyToStrokeMode, object, style, fallbackColor);
156}
157
158RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource()
159{
160    static RenderSVGResourceSolidColor* s_sharedSolidPaintingResource = 0;
161    if (!s_sharedSolidPaintingResource)
162        s_sharedSolidPaintingResource = new RenderSVGResourceSolidColor;
163    return s_sharedSolidPaintingResource;
164}
165
166static inline void removeFromCacheAndInvalidateDependencies(RenderObject* object, bool needsLayout)
167{
168    ASSERT(object);
169    if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object)) {
170#if ENABLE(FILTERS)
171        if (RenderSVGResourceFilter* filter = resources->filter())
172            filter->removeClientFromCache(object);
173#endif
174        if (RenderSVGResourceMasker* masker = resources->masker())
175            masker->removeClientFromCache(object);
176
177        if (RenderSVGResourceClipper* clipper = resources->clipper())
178            clipper->removeClientFromCache(object);
179    }
180
181    if (!object->node() || !object->node()->isSVGElement())
182        return;
183    HashSet<SVGElement*>* dependencies = object->document()->accessSVGExtensions()->setOfElementsReferencingTarget(toSVGElement(object->node()));
184    if (!dependencies)
185        return;
186    HashSet<SVGElement*>::iterator end = dependencies->end();
187    for (HashSet<SVGElement*>::iterator it = dependencies->begin(); it != end; ++it) {
188        if (RenderObject* renderer = (*it)->renderer())
189            RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, needsLayout);
190    }
191}
192
193void RenderSVGResource::markForLayoutAndParentResourceInvalidation(RenderObject* object, bool needsLayout)
194{
195    ASSERT(object);
196    ASSERT(object->document());
197    ASSERT(object->node());
198
199    if (needsLayout)
200        object->setNeedsLayout(true);
201
202    removeFromCacheAndInvalidateDependencies(object, needsLayout);
203
204    // Invalidate resources in ancestor chain, if needed.
205    RenderObject* current = object->parent();
206    while (current) {
207        removeFromCacheAndInvalidateDependencies(current, needsLayout);
208
209        if (current->isSVGResourceContainer()) {
210            // This will process the rest of the ancestors.
211            current->toRenderSVGResourceContainer()->removeAllClientsFromCache();
212            break;
213        }
214
215        current = current->parent();
216    }
217}
218
219}
220
221#endif
222
223