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/frame/FrameView.h" 28 #include "core/frame/LocalFrame.h" 29 #include "core/rendering/svg/RenderSVGResourceClipper.h" 30 #include "core/rendering/svg/RenderSVGResourceFilter.h" 31 #include "core/rendering/svg/RenderSVGResourceMasker.h" 32 #include "core/rendering/svg/RenderSVGResourceSolidColor.h" 33 #include "core/rendering/svg/SVGResources.h" 34 #include "core/rendering/svg/SVGResourcesCache.h" 35 36 namespace WebCore { 37 38 static inline bool inheritColorFromParentStyle(RenderObject* object, bool applyToFill, Color& color) 39 { 40 if (!object->parent() || !object->parent()->style()) 41 return false; 42 const SVGRenderStyle* parentSVGStyle = object->parent()->style()->svgStyle(); 43 SVGPaint::SVGPaintType paintType = applyToFill ? parentSVGStyle->fillPaintType() : parentSVGStyle->strokePaintType(); 44 if (paintType != SVGPaint::SVG_PAINTTYPE_RGBCOLOR && paintType != SVGPaint::SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR) 45 return false; 46 color = applyToFill ? parentSVGStyle->fillPaintColor() : parentSVGStyle->strokePaintColor(); 47 return true; 48 } 49 50 static inline RenderSVGResource* requestPaintingResource(RenderSVGResourceMode mode, RenderObject* object, const RenderStyle* style, bool& hasFallback) 51 { 52 ASSERT(object); 53 ASSERT(style); 54 55 hasFallback = false; 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 ASSERT(paintType != SVGPaint::SVG_PAINTTYPE_NONE); 85 86 Color color; 87 bool hasColor = false; 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 hasColor = true; 97 default: 98 break; 99 } 100 101 if (style->insideLink() == InsideVisitedLink) { 102 // FIXME: This code doesn't support the uri component of the visited link paint, https://bugs.webkit.org/show_bug.cgi?id=70006 103 SVGPaint::SVGPaintType visitedPaintType = applyToFill ? svgStyle->visitedLinkFillPaintType() : svgStyle->visitedLinkStrokePaintType(); 104 105 // For SVG_PAINTTYPE_CURRENTCOLOR, 'color' already contains the 'visitedColor'. 106 if (visitedPaintType < SVGPaint::SVG_PAINTTYPE_URI_NONE && visitedPaintType != SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) { 107 const Color& visitedColor = applyToFill ? svgStyle->visitedLinkFillPaintColor() : svgStyle->visitedLinkStrokePaintColor(); 108 color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha()); 109 hasColor = true; 110 } 111 } 112 113 // If the primary resource is just a color, return immediately. 114 RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource(); 115 if (paintType < SVGPaint::SVG_PAINTTYPE_URI_NONE) { 116 if (!hasColor && !inheritColorFromParentStyle(object, applyToFill, color)) 117 return 0; 118 119 colorResource->setColor(color); 120 return colorResource; 121 } 122 123 // If no resources are associated with the given renderer, return the color resource. 124 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); 125 if (!resources) { 126 if (paintType == SVGPaint::SVG_PAINTTYPE_URI_NONE || (!hasColor && !inheritColorFromParentStyle(object, applyToFill, color))) 127 return 0; 128 129 colorResource->setColor(color); 130 return colorResource; 131 } 132 133 // If the requested resource is not available, return the color resource. 134 RenderSVGResource* uriResource = mode == ApplyToFillMode ? resources->fill() : resources->stroke(); 135 if (!uriResource) { 136 if (!hasColor && !inheritColorFromParentStyle(object, applyToFill, color)) 137 return 0; 138 139 colorResource->setColor(color); 140 return colorResource; 141 } 142 143 // The paint server resource exists, though it may be invalid (pattern with width/height=0). Pass the fallback color to our caller 144 // via sharedSolidPaintingResource so it can use the solid color painting resource, if applyResource() on the URI resource failed. 145 if (hasColor) { 146 colorResource->setColor(color); 147 hasFallback = true; 148 } 149 return uriResource; 150 } 151 152 RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderObject* object, const RenderStyle* style, bool& hasFallback) 153 { 154 return requestPaintingResource(ApplyToFillMode, object, style, hasFallback); 155 } 156 157 RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderObject* object, const RenderStyle* style, bool& hasFallback) 158 { 159 return requestPaintingResource(ApplyToStrokeMode, object, style, hasFallback); 160 } 161 162 RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource() 163 { 164 static RenderSVGResourceSolidColor* s_sharedSolidPaintingResource = 0; 165 if (!s_sharedSolidPaintingResource) 166 s_sharedSolidPaintingResource = new RenderSVGResourceSolidColor; 167 return s_sharedSolidPaintingResource; 168 } 169 170 static inline void removeFromCacheAndInvalidateDependencies(RenderObject* object, bool needsLayout) 171 { 172 ASSERT(object); 173 if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object)) { 174 if (RenderSVGResourceFilter* filter = resources->filter()) 175 filter->removeClientFromCache(object); 176 177 if (RenderSVGResourceMasker* masker = resources->masker()) 178 masker->removeClientFromCache(object); 179 180 if (RenderSVGResourceClipper* clipper = resources->clipper()) 181 clipper->removeClientFromCache(object); 182 } 183 184 if (!object->node() || !object->node()->isSVGElement()) 185 return; 186 SVGElementSet* dependencies = object->document().accessSVGExtensions().setOfElementsReferencingTarget(toSVGElement(object->node())); 187 if (!dependencies) 188 return; 189 190 // We allow cycles in SVGDocumentExtensions reference sets in order to avoid expensive 191 // reference graph adjustments on changes, so we need to break possible cycles here. 192 // This strong reference is safe, as it is guaranteed that this set will be emptied 193 // at the end of recursion. 194 typedef WillBeHeapHashSet<RawPtrWillBeMember<SVGElement> > SVGElementSet; 195 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<SVGElementSet>, invalidatingDependencies, (adoptPtrWillBeNoop(new SVGElementSet))); 196 197 SVGElementSet::iterator end = dependencies->end(); 198 for (SVGElementSet::iterator it = dependencies->begin(); it != end; ++it) { 199 if (RenderObject* renderer = (*it)->renderer()) { 200 if (UNLIKELY(!invalidatingDependencies->add(*it).isNewEntry)) { 201 // Reference cycle: we are in process of invalidating this dependant. 202 continue; 203 } 204 205 RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, needsLayout); 206 invalidatingDependencies->remove(*it); 207 } 208 } 209 } 210 211 void RenderSVGResource::markForLayoutAndParentResourceInvalidation(RenderObject* object, bool needsLayout) 212 { 213 ASSERT(object); 214 ASSERT(object->node()); 215 216 if (needsLayout && !object->documentBeingDestroyed()) 217 object->setNeedsLayoutAndFullPaintInvalidation(); 218 219 removeFromCacheAndInvalidateDependencies(object, needsLayout); 220 221 // Invalidate resources in ancestor chain, if needed. 222 RenderObject* current = object->parent(); 223 while (current) { 224 removeFromCacheAndInvalidateDependencies(current, needsLayout); 225 226 if (current->isSVGResourceContainer()) { 227 // This will process the rest of the ancestors. 228 toRenderSVGResourceContainer(current)->removeAllClientsFromCache(); 229 break; 230 } 231 232 current = current->parent(); 233 } 234 } 235 236 } 237