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