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