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#include "RenderSVGResourceContainer.h" 22 23#include "RenderLayer.h" 24#include "RenderSVGRoot.h" 25#include "RenderView.h" 26#include "SVGRenderingContext.h" 27#include "SVGResourcesCache.h" 28#include <wtf/StackStats.h> 29 30namespace WebCore { 31 32static inline SVGDocumentExtensions& svgExtensionsFromElement(SVGElement& element) 33{ 34 // FIXME: accessSVGExtensions() should return a reference. 35 return *element.document().accessSVGExtensions(); 36} 37 38RenderSVGResourceContainer::RenderSVGResourceContainer(SVGElement& element, PassRef<RenderStyle> style) 39 : RenderSVGHiddenContainer(element, WTF::move(style)) 40 , m_id(element.getIdAttribute()) 41 , m_registered(false) 42 , m_isInvalidating(false) 43{ 44} 45 46RenderSVGResourceContainer::~RenderSVGResourceContainer() 47{ 48 if (m_registered) 49 svgExtensionsFromElement(element()).removeResource(m_id); 50} 51 52void RenderSVGResourceContainer::layout() 53{ 54 StackStats::LayoutCheckPoint layoutCheckPoint; 55 // Invalidate all resources if our layout changed. 56 if (everHadLayout() && selfNeedsLayout()) 57 RenderSVGRoot::addResourceForClientInvalidation(this); 58 59 RenderSVGHiddenContainer::layout(); 60} 61 62void RenderSVGResourceContainer::willBeDestroyed() 63{ 64 SVGResourcesCache::resourceDestroyed(*this); 65 RenderSVGHiddenContainer::willBeDestroyed(); 66} 67 68void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 69{ 70 RenderSVGHiddenContainer::styleDidChange(diff, oldStyle); 71 72 if (!m_registered) { 73 m_registered = true; 74 registerResource(); 75 } 76} 77 78void RenderSVGResourceContainer::idChanged() 79{ 80 // Invalidate all our current clients. 81 removeAllClientsFromCache(); 82 83 // Remove old id, that is guaranteed to be present in cache. 84 svgExtensionsFromElement(element()).removeResource(m_id); 85 m_id = element().getIdAttribute(); 86 87 registerResource(); 88} 89 90void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode) 91{ 92 if ((m_clients.isEmpty() && m_clientLayers.isEmpty()) || m_isInvalidating) 93 return; 94 95 m_isInvalidating = true; 96 bool needsLayout = mode == LayoutAndBoundariesInvalidation; 97 bool markForInvalidation = mode != ParentOnlyInvalidation; 98 99 for (auto* client : m_clients) { 100 if (client->isSVGResourceContainer()) { 101 toRenderSVGResourceContainer(*client).removeAllClientsFromCache(markForInvalidation); 102 continue; 103 } 104 105 if (markForInvalidation) 106 markClientForInvalidation(*client, mode); 107 108 RenderSVGResource::markForLayoutAndParentResourceInvalidation(*client, needsLayout); 109 } 110 111 markAllClientLayersForInvalidation(); 112 113 m_isInvalidating = false; 114} 115 116void RenderSVGResourceContainer::markAllClientLayersForInvalidation() 117{ 118#if ENABLE(CSS_FILTERS) 119 for (auto* clientLayer : m_clientLayers) 120 clientLayer->filterNeedsRepaint(); 121#endif 122} 123 124void RenderSVGResourceContainer::markClientForInvalidation(RenderObject& client, InvalidationMode mode) 125{ 126 ASSERT(!m_clients.isEmpty()); 127 128 switch (mode) { 129 case LayoutAndBoundariesInvalidation: 130 case BoundariesInvalidation: 131 client.setNeedsBoundariesUpdate(); 132 break; 133 case RepaintInvalidation: 134 if (!client.documentBeingDestroyed()) 135 client.repaint(); 136 break; 137 case ParentOnlyInvalidation: 138 break; 139 } 140} 141 142void RenderSVGResourceContainer::addClient(RenderElement& client) 143{ 144 m_clients.add(&client); 145} 146 147void RenderSVGResourceContainer::removeClient(RenderElement& client) 148{ 149 removeClientFromCache(client, false); 150 m_clients.remove(&client); 151} 152 153void RenderSVGResourceContainer::addClientRenderLayer(RenderLayer* client) 154{ 155 ASSERT(client); 156 m_clientLayers.add(client); 157} 158 159void RenderSVGResourceContainer::removeClientRenderLayer(RenderLayer* client) 160{ 161 ASSERT(client); 162 m_clientLayers.remove(client); 163} 164 165void RenderSVGResourceContainer::registerResource() 166{ 167 SVGDocumentExtensions& extensions = svgExtensionsFromElement(element()); 168 if (!extensions.isIdOfPendingResource(m_id)) { 169 extensions.addResource(m_id, this); 170 return; 171 } 172 173 std::unique_ptr<SVGDocumentExtensions::PendingElements> clients = extensions.removePendingResource(m_id); 174 175 // Cache us with the new id. 176 extensions.addResource(m_id, this); 177 178 // Update cached resources of pending clients. 179 for (auto* client : *clients) { 180 ASSERT(client->hasPendingResources()); 181 extensions.clearHasPendingResourcesIfPossible(client); 182 auto* renderer = client->renderer(); 183 if (!renderer) 184 continue; 185 SVGResourcesCache::clientStyleChanged(*renderer, StyleDifferenceLayout, renderer->style()); 186 renderer->setNeedsLayout(); 187 } 188} 189 190bool RenderSVGResourceContainer::shouldTransformOnTextPainting(const RenderElement& renderer, AffineTransform& resourceTransform) 191{ 192#if USE(CG) 193 UNUSED_PARAM(renderer); 194 UNUSED_PARAM(resourceTransform); 195 return false; 196#else 197 // This method should only be called for RenderObjects that deal with text rendering. Cmp. RenderObject.h's is*() methods. 198 ASSERT(renderer.isSVGText() || renderer.isSVGTextPath() || renderer.isSVGInline()); 199 200 // In text drawing, the scaling part of the graphics context CTM is removed, compare SVGInlineTextBox::paintTextWithShadows. 201 // So, we use that scaling factor here, too, and then push it down to pattern or gradient space 202 // in order to keep the pattern or gradient correctly scaled. 203 float scalingFactor = SVGRenderingContext::calculateScreenFontSizeScalingFactor(renderer); 204 if (scalingFactor == 1) 205 return false; 206 resourceTransform.scale(scalingFactor); 207 return true; 208#endif 209} 210 211// FIXME: This does not belong here. 212AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform) 213{ 214 if (!object->isSVGShape()) 215 return resourceTransform; 216 217 SVGGraphicsElement* element = toSVGGraphicsElement(object->node()); 218 AffineTransform transform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate); 219 transform *= resourceTransform; 220 return transform; 221} 222 223} 224