1 /* 2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2007 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2008 Dirk Schulze <krit (at) 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 #include "core/rendering/svg/RenderSVGResource.h" 26 27 #include "core/page/FrameView.h" 28 #include "core/rendering/svg/RenderSVGResourceClipper.h" 29 #include "core/rendering/svg/RenderSVGResourceFilter.h" 30 #include "core/rendering/svg/RenderSVGResourceMasker.h" 31 #include "core/rendering/svg/RenderSVGResourceSolidColor.h" 32 #include "core/rendering/svg/SVGResources.h" 33 #include "core/rendering/svg/SVGResourcesCache.h" 34 35 namespace WebCore { 36 37 static inline bool inheritColorFromParentStyleIfNeeded(RenderObject* object, bool applyToFill, StyleColor& color) 38 { 39 if (color.isValid()) 40 return true; 41 if (!object->parent() || !object->parent()->style()) 42 return false; 43 const SVGRenderStyle* parentSVGStyle = object->parent()->style()->svgStyle(); 44 color = applyToFill ? parentSVGStyle->fillPaintColor() : parentSVGStyle->strokePaintColor(); 45 return true; 46 } 47 48 static inline RenderSVGResource* requestPaintingResource(RenderSVGResourceMode mode, RenderObject* object, const RenderStyle* style, StyleColor& fallbackColor) 49 { 50 ASSERT(object); 51 ASSERT(style); 52 53 // If we have no style at all, ignore it. 54 const SVGRenderStyle* svgStyle = style->svgStyle(); 55 if (!svgStyle) 56 return 0; 57 58 bool isRenderingMask = false; 59 if (object->frame() && object->frame()->view()) 60 isRenderingMask = object->frame()->view()->paintBehavior() & PaintBehaviorRenderingSVGMask; 61 62 // If we have no fill/stroke, return 0. 63 if (mode == ApplyToFillMode) { 64 // When rendering the mask for a RenderSVGResourceClipper, always use the initial fill paint server, and ignore stroke. 65 if (isRenderingMask) { 66 RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource(); 67 colorResource->setColor(SVGRenderStyle::initialFillPaintColor()); 68 return colorResource; 69 } 70 71 if (!svgStyle->hasFill()) 72 return 0; 73 } else { 74 if (!svgStyle->hasStroke() || isRenderingMask) 75 return 0; 76 } 77 78 bool applyToFill = mode == ApplyToFillMode; 79 SVGPaint::SVGPaintType paintType = applyToFill ? svgStyle->fillPaintType() : svgStyle->strokePaintType(); 80 if (paintType == SVGPaint::SVG_PAINTTYPE_NONE) 81 return 0; 82 83 StyleColor color; 84 switch (paintType) { 85 case SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR: 86 case SVGPaint::SVG_PAINTTYPE_RGBCOLOR: 87 case SVGPaint::SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR: 88 case SVGPaint::SVG_PAINTTYPE_URI_CURRENTCOLOR: 89 case SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR: 90 case SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR: 91 color = applyToFill ? svgStyle->fillPaintColor() : svgStyle->strokePaintColor(); 92 default: 93 break; 94 } 95 96 if (style->insideLink() == InsideVisitedLink) { 97 // FIXME: This code doesn't support the uri component of the visited link paint, https://bugs.webkit.org/show_bug.cgi?id=70006 98 SVGPaint::SVGPaintType visitedPaintType = applyToFill ? svgStyle->visitedLinkFillPaintType() : svgStyle->visitedLinkStrokePaintType(); 99 100 // For SVG_PAINTTYPE_CURRENTCOLOR, 'color' already contains the 'visitedColor'. 101 if (visitedPaintType < SVGPaint::SVG_PAINTTYPE_URI_NONE && visitedPaintType != SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) { 102 const Color& visitedColor = applyToFill ? svgStyle->visitedLinkFillPaintColor() : svgStyle->visitedLinkStrokePaintColor(); 103 color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha()); 104 } 105 } 106 107 // If the primary resource is just a color, return immediately. 108 RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource(); 109 if (paintType < SVGPaint::SVG_PAINTTYPE_URI_NONE) { 110 if (!inheritColorFromParentStyleIfNeeded(object, applyToFill, color)) 111 return 0; 112 113 colorResource->setColor(color.color()); 114 return colorResource; 115 } 116 117 // If no resources are associated with the given renderer, return the color resource. 118 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 119 if (!resources) { 120 if (paintType == SVGPaint::SVG_PAINTTYPE_URI_NONE || !inheritColorFromParentStyleIfNeeded(object, applyToFill, color)) 121 return 0; 122 123 colorResource->setColor(color.color()); 124 return colorResource; 125 } 126 127 // If the requested resource is not available, return the color resource. 128 RenderSVGResource* uriResource = mode == ApplyToFillMode ? resources->fill() : resources->stroke(); 129 if (!uriResource) { 130 if (!inheritColorFromParentStyleIfNeeded(object, applyToFill, color)) 131 return 0; 132 133 colorResource->setColor(color.color()); 134 return colorResource; 135 } 136 137 // The paint server resource exists, though it may be invalid (pattern with width/height=0). Pass the fallback color to our caller 138 // so it can use the solid color painting resource, if applyResource() on the URI resource failed. 139 fallbackColor = color; 140 return uriResource; 141 } 142 143 RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderObject* object, const RenderStyle* style, StyleColor& fallbackColor) 144 { 145 return requestPaintingResource(ApplyToFillMode, object, style, fallbackColor); 146 } 147 148 RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderObject* object, const RenderStyle* style, StyleColor& fallbackColor) 149 { 150 return requestPaintingResource(ApplyToStrokeMode, object, style, fallbackColor); 151 } 152 153 RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource() 154 { 155 static RenderSVGResourceSolidColor* s_sharedSolidPaintingResource = 0; 156 if (!s_sharedSolidPaintingResource) 157 s_sharedSolidPaintingResource = new RenderSVGResourceSolidColor; 158 return s_sharedSolidPaintingResource; 159 } 160 161 static inline void removeFromCacheAndInvalidateDependencies(RenderObject* object, bool needsLayout) 162 { 163 ASSERT(object); 164 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object)) { 165 if (RenderSVGResourceFilter* filter = resources->filter()) 166 filter->removeClientFromCache(object); 167 168 if (RenderSVGResourceMasker* masker = resources->masker()) 169 masker->removeClientFromCache(object); 170 171 if (RenderSVGResourceClipper* clipper = resources->clipper()) 172 clipper->removeClientFromCache(object); 173 } 174 175 if (!object->node() || !object->node()->isSVGElement()) 176 return; 177 HashSet<SVGElement*>* dependencies = object->document()->accessSVGExtensions()->setOfElementsReferencingTarget(toSVGElement(object->node())); 178 if (!dependencies) 179 return; 180 HashSet<SVGElement*>::iterator end = dependencies->end(); 181 for (HashSet<SVGElement*>::iterator it = dependencies->begin(); it != end; ++it) { 182 if (RenderObject* renderer = (*it)->renderer()) 183 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, needsLayout); 184 } 185 } 186 187 void RenderSVGResource::markForLayoutAndParentResourceInvalidation(RenderObject* object, bool needsLayout) 188 { 189 ASSERT(object); 190 ASSERT(object->document()); 191 ASSERT(object->node()); 192 193 if (needsLayout) 194 object->setNeedsLayout(); 195 196 removeFromCacheAndInvalidateDependencies(object, needsLayout); 197 198 // Invalidate resources in ancestor chain, if needed. 199 RenderObject* current = object->parent(); 200 while (current) { 201 removeFromCacheAndInvalidateDependencies(current, needsLayout); 202 203 if (current->isSVGResourceContainer()) { 204 // This will process the rest of the ancestors. 205 current->toRenderSVGResourceContainer()->removeAllClientsFromCache(); 206 break; 207 } 208 209 current = current->parent(); 210 } 211 } 212 213 } 214