1 /* 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 3 * 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. 8 * 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. 13 * 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. 18 */ 19 20 #include "config.h" 21 22 #include "core/rendering/svg/RenderSVGResourceContainer.h" 23 24 #include "core/rendering/RenderLayer.h" 25 #include "core/rendering/RenderView.h" 26 #include "core/rendering/svg/SVGRenderingContext.h" 27 #include "core/rendering/svg/SVGResourcesCache.h" 28 #include "core/svg/SVGGraphicsElement.h" 29 30 #include "wtf/TemporaryChange.h" 31 32 namespace WebCore { 33 34 static inline SVGDocumentExtensions& svgExtensionsFromElement(SVGElement* element) 35 { 36 ASSERT(element); 37 return element->document().accessSVGExtensions(); 38 } 39 40 RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement* node) 41 : RenderSVGHiddenContainer(node) 42 , m_isInLayout(false) 43 , m_id(node->getIdAttribute()) 44 , m_invalidationMask(0) 45 , m_registered(false) 46 , m_isInvalidating(false) 47 { 48 } 49 50 RenderSVGResourceContainer::~RenderSVGResourceContainer() 51 { 52 if (m_registered) 53 svgExtensionsFromElement(element()).removeResource(m_id); 54 } 55 56 void RenderSVGResourceContainer::layout() 57 { 58 // FIXME: Investigate a way to detect and break resource layout dependency cycles early. 59 // Then we can remove this method altogether, and fall back onto RenderSVGHiddenContainer::layout(). 60 ASSERT(needsLayout()); 61 if (m_isInLayout) 62 return; 63 64 TemporaryChange<bool> inLayoutChange(m_isInLayout, true); 65 66 RenderSVGHiddenContainer::layout(); 67 68 clearInvalidationMask(); 69 } 70 71 void RenderSVGResourceContainer::willBeDestroyed() 72 { 73 SVGResourcesCache::resourceDestroyed(this); 74 RenderSVGHiddenContainer::willBeDestroyed(); 75 } 76 77 void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 78 { 79 RenderSVGHiddenContainer::styleDidChange(diff, oldStyle); 80 81 if (!m_registered) { 82 m_registered = true; 83 registerResource(); 84 } 85 } 86 87 void RenderSVGResourceContainer::idChanged() 88 { 89 // Invalidate all our current clients. 90 removeAllClientsFromCache(); 91 92 // Remove old id, that is guaranteed to be present in cache. 93 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element()); 94 extensions.removeResource(m_id); 95 m_id = element()->getIdAttribute(); 96 97 registerResource(); 98 } 99 100 void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode) 101 { 102 if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating) 103 return; 104 105 if (m_invalidationMask & mode) 106 return; 107 108 m_invalidationMask |= mode; 109 m_isInvalidating = true; 110 bool needsLayout = mode == LayoutAndBoundariesInvalidation; 111 bool markForInvalidation = mode != ParentOnlyInvalidation; 112 113 HashSet<RenderObject*>::iterator end = m_clients.end(); 114 for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) { 115 RenderObject* client = *it; 116 if (client->isSVGResourceContainer()) { 117 toRenderSVGResourceContainer(client)->removeAllClientsFromCache(markForInvalidation); 118 continue; 119 } 120 121 if (markForInvalidation) 122 markClientForInvalidation(client, mode); 123 124 RenderSVGResource::markForLayoutAndParentResourceInvalidation(client, needsLayout); 125 } 126 127 markAllClientLayersForInvalidation(); 128 129 m_isInvalidating = false; 130 } 131 132 void RenderSVGResourceContainer::markAllClientLayersForInvalidation() 133 { 134 HashSet<RenderLayer*>::iterator layerEnd = m_clientLayers.end(); 135 for (HashSet<RenderLayer*>::iterator it = m_clientLayers.begin(); it != layerEnd; ++it) 136 (*it)->filterNeedsRepaint(); 137 } 138 139 void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode) 140 { 141 ASSERT(client); 142 ASSERT(!m_clients.isEmpty()); 143 144 switch (mode) { 145 case LayoutAndBoundariesInvalidation: 146 case BoundariesInvalidation: 147 client->setNeedsBoundariesUpdate(); 148 break; 149 case RepaintInvalidation: 150 if (client->view()) { 151 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && frameView()->isInPerformLayout()) 152 client->setShouldDoFullPaintInvalidationAfterLayout(true); 153 else 154 client->paintInvalidationForWholeRenderer(); 155 } 156 break; 157 case ParentOnlyInvalidation: 158 break; 159 } 160 } 161 162 void RenderSVGResourceContainer::addClient(RenderObject* client) 163 { 164 ASSERT(client); 165 m_clients.add(client); 166 clearInvalidationMask(); 167 } 168 169 void RenderSVGResourceContainer::removeClient(RenderObject* client) 170 { 171 ASSERT(client); 172 removeClientFromCache(client, false); 173 m_clients.remove(client); 174 } 175 176 void RenderSVGResourceContainer::addClientRenderLayer(Node* node) 177 { 178 ASSERT(node); 179 if (!node->renderer() || !node->renderer()->hasLayer()) 180 return; 181 m_clientLayers.add(toRenderLayerModelObject(node->renderer())->layer()); 182 clearInvalidationMask(); 183 } 184 185 void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client) 186 { 187 ASSERT(client); 188 m_clientLayers.add(client); 189 clearInvalidationMask(); 190 } 191 192 void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client) 193 { 194 ASSERT(client); 195 m_clientLayers.remove(client); 196 } 197 198 void RenderSVGResourceContainer::invalidateCacheAndMarkForLayout(SubtreeLayoutScope* layoutScope) 199 { 200 if (selfNeedsLayout()) 201 return; 202 203 setNeedsLayoutAndFullPaintInvalidation(MarkContainingBlockChain, layoutScope); 204 205 if (everHadLayout()) 206 removeAllClientsFromCache(); 207 } 208 209 void RenderSVGResourceContainer::registerResource() 210 { 211 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element()); 212 if (!extensions.hasPendingResource(m_id)) { 213 extensions.addResource(m_id, this); 214 return; 215 } 216 217 OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions.removePendingResource(m_id)); 218 219 // Cache us with the new id. 220 extensions.addResource(m_id, this); 221 222 // Update cached resources of pending clients. 223 const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end(); 224 for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) { 225 ASSERT((*it)->hasPendingResources()); 226 extensions.clearHasPendingResourcesIfPossible(*it); 227 RenderObject* renderer = (*it)->renderer(); 228 if (!renderer) 229 continue; 230 231 StyleDifference diff; 232 diff.setNeedsFullLayout(); 233 SVGResourcesCache::clientStyleChanged(renderer, diff, renderer->style()); 234 renderer->setNeedsLayoutAndFullPaintInvalidation(); 235 } 236 } 237 238 static bool shouldTransformOnTextPainting(RenderObject* object, AffineTransform& resourceTransform) 239 { 240 ASSERT(object); 241 242 // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods. 243 ASSERT(object->isSVGText() || object->isSVGTextPath() || object->isSVGInline()); 244 245 // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows. 246 // So, we use that scaling factor here, too, and then push it down to pattern or gradient space 247 // in order to keep the pattern or gradient correctly scaled. 248 float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(object); 249 if (scalingFactor == 1) 250 return false; 251 resourceTransform.scale(scalingFactor); 252 return true; 253 } 254 255 AffineTransform RenderSVGResourceContainer::computeResourceSpaceTransform(RenderObject* object, const AffineTransform& baseTransform, const SVGRenderStyle* svgStyle, unsigned short resourceMode) 256 { 257 AffineTransform computedSpaceTransform = baseTransform; 258 if (resourceMode & ApplyToTextMode) { 259 // Depending on the font scaling factor, we may need to apply an 260 // additional transform (scale-factor) the paintserver, since text 261 // painting removes the scale factor from the context. (See 262 // SVGInlineTextBox::paintTextWithShadows.) 263 AffineTransform additionalTextTransformation; 264 if (shouldTransformOnTextPainting(object, additionalTextTransformation)) 265 computedSpaceTransform = additionalTextTransformation * computedSpaceTransform; 266 } 267 if (resourceMode & ApplyToStrokeMode) { 268 // Non-scaling stroke needs to reset the transform back to the host transform. 269 if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE) 270 computedSpaceTransform = transformOnNonScalingStroke(object, computedSpaceTransform); 271 } 272 return computedSpaceTransform; 273 } 274 275 // FIXME: This does not belong here. 276 AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform) 277 { 278 if (!object->isSVGShape()) 279 return resourceTransform; 280 281 SVGGraphicsElement* element = toSVGGraphicsElement(object->node()); 282 AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate); 283 transform *= resourceTransform; 284 return transform; 285 } 286 287 } 288