1/* 2 * Copyright (C) 2006 Apple Inc. All rights reserved. 3 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2007 Rob Buis <buis@kde.org> 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23#include "core/svg/SVGDocumentExtensions.h" 24 25#include "XLinkNames.h" 26#include "core/dom/Document.h" 27#include "core/rendering/svg/SVGResourcesCache.h" 28#include "core/svg/SVGElement.h" 29#include "core/svg/SVGSVGElement.h" 30#include "core/svg/animation/SMILTimeContainer.h" 31#include "wtf/TemporaryChange.h" 32#include "wtf/text/AtomicString.h" 33 34namespace WebCore { 35 36SVGDocumentExtensions::SVGDocumentExtensions(Document* document) 37 : m_document(document) 38 , m_resourcesCache(adoptPtr(new SVGResourcesCache)) 39#if !ASSERT_DISABLED 40 , m_inRelativeLengthSVGRootsInvalidation(false) 41#endif 42{ 43} 44 45SVGDocumentExtensions::~SVGDocumentExtensions() 46{ 47} 48 49void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element) 50{ 51 m_timeContainers.add(element); 52} 53 54void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element) 55{ 56 m_timeContainers.remove(element); 57} 58 59void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource) 60{ 61 ASSERT(resource); 62 63 if (id.isEmpty()) 64 return; 65 66 // Replaces resource if already present, to handle potential id changes 67 m_resources.set(id, resource); 68} 69 70void SVGDocumentExtensions::removeResource(const AtomicString& id) 71{ 72 if (id.isEmpty()) 73 return; 74 75 m_resources.remove(id); 76} 77 78RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const 79{ 80 if (id.isEmpty()) 81 return 0; 82 83 return m_resources.get(id); 84} 85 86void SVGDocumentExtensions::startAnimations() 87{ 88 // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer 89 // starting animations for a document will do this "latching" 90 // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us. 91 // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704 92 Vector<RefPtr<SVGSVGElement> > timeContainers; 93 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 94 Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end(); 95 for (Vector<RefPtr<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) 96 (*itr)->timeContainer()->begin(); 97} 98 99void SVGDocumentExtensions::pauseAnimations() 100{ 101 HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end(); 102 for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 103 (*itr)->pauseAnimations(); 104} 105 106void SVGDocumentExtensions::unpauseAnimations() 107{ 108 HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end(); 109 for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 110 (*itr)->unpauseAnimations(); 111} 112 113void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements() 114{ 115 Vector<RefPtr<SVGSVGElement> > timeContainers; 116 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 117 118 Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end(); 119 for (Vector<RefPtr<SVGSVGElement> >::iterator it = timeContainers.begin(); it != end; ++it) { 120 SVGSVGElement* outerSVG = (*it).get(); 121 if (!outerSVG->isOutermostSVGSVGElement()) 122 continue; 123 outerSVG->sendSVGLoadEventIfPossible(); 124 } 125} 126 127static void reportMessage(Document* document, MessageLevel level, const String& message) 128{ 129 if (document->frame()) 130 document->addConsoleMessage(RenderingMessageSource, level, message); 131} 132 133void SVGDocumentExtensions::reportWarning(const String& message) 134{ 135 reportMessage(m_document, WarningMessageLevel, "Warning: " + message); 136} 137 138void SVGDocumentExtensions::reportError(const String& message) 139{ 140 reportMessage(m_document, ErrorMessageLevel, "Error: " + message); 141} 142 143void SVGDocumentExtensions::addPendingResource(const AtomicString& id, Element* element) 144{ 145 ASSERT(element); 146 ASSERT(element->inDocument()); 147 148 if (id.isEmpty()) 149 return; 150 151 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::AddResult result = m_pendingResources.add(id, nullptr); 152 if (result.isNewEntry) 153 result.iterator->value = adoptPtr(new SVGPendingElements); 154 result.iterator->value->add(element); 155 156 element->setHasPendingResources(); 157} 158 159bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const 160{ 161 if (id.isEmpty()) 162 return false; 163 164 return m_pendingResources.contains(id); 165} 166 167bool SVGDocumentExtensions::isElementPendingResources(Element* element) const 168{ 169 // This algorithm takes time proportional to the number of pending resources and need not. 170 // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently. 171 172 ASSERT(element); 173 174 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator end = m_pendingResources.end(); 175 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator it = m_pendingResources.begin(); it != end; ++it) { 176 SVGPendingElements* elements = it->value.get(); 177 ASSERT(elements); 178 179 if (elements->contains(element)) 180 return true; 181 } 182 return false; 183} 184 185bool SVGDocumentExtensions::isElementPendingResource(Element* element, const AtomicString& id) const 186{ 187 ASSERT(element); 188 189 if (!hasPendingResource(id)) 190 return false; 191 192 return m_pendingResources.get(id)->contains(element); 193} 194 195void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element) 196{ 197 if (!isElementPendingResources(element)) 198 element->clearHasPendingResources(); 199} 200 201void SVGDocumentExtensions::removeElementFromPendingResources(Element* element) 202{ 203 ASSERT(element); 204 205 // Remove the element from pending resources. 206 if (!m_pendingResources.isEmpty() && element->hasPendingResources()) { 207 Vector<AtomicString> toBeRemoved; 208 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResources.end(); 209 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResources.begin(); it != end; ++it) { 210 SVGPendingElements* elements = it->value.get(); 211 ASSERT(elements); 212 ASSERT(!elements->isEmpty()); 213 214 elements->remove(element); 215 if (elements->isEmpty()) 216 toBeRemoved.append(it->key); 217 } 218 219 clearHasPendingResourcesIfPossible(element); 220 221 // We use the removePendingResource function here because it deals with set lifetime correctly. 222 Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end(); 223 for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 224 removePendingResource(*it); 225 } 226 227 // Remove the element from pending resources that were scheduled for removal. 228 if (!m_pendingResourcesForRemoval.isEmpty()) { 229 Vector<AtomicString> toBeRemoved; 230 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResourcesForRemoval.end(); 231 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResourcesForRemoval.begin(); it != end; ++it) { 232 SVGPendingElements* elements = it->value.get(); 233 ASSERT(elements); 234 ASSERT(!elements->isEmpty()); 235 236 elements->remove(element); 237 if (elements->isEmpty()) 238 toBeRemoved.append(it->key); 239 } 240 241 // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly. 242 Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end(); 243 for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 244 removePendingResourceForRemoval(*it); 245 } 246} 247 248PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id) 249{ 250 ASSERT(m_pendingResources.contains(id)); 251 return m_pendingResources.take(id); 252} 253 254PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id) 255{ 256 ASSERT(m_pendingResourcesForRemoval.contains(id)); 257 return m_pendingResourcesForRemoval.take(id); 258} 259 260void SVGDocumentExtensions::markPendingResourcesForRemoval(const AtomicString& id) 261{ 262 if (id.isEmpty()) 263 return; 264 265 ASSERT(!m_pendingResourcesForRemoval.contains(id)); 266 267 OwnPtr<SVGPendingElements> existing = m_pendingResources.take(id); 268 if (existing && !existing->isEmpty()) 269 m_pendingResourcesForRemoval.add(id, existing.release()); 270} 271 272Element* SVGDocumentExtensions::removeElementFromPendingResourcesForRemoval(const AtomicString& id) 273{ 274 if (id.isEmpty()) 275 return 0; 276 277 SVGPendingElements* resourceSet = m_pendingResourcesForRemoval.get(id); 278 if (!resourceSet || resourceSet->isEmpty()) 279 return 0; 280 281 SVGPendingElements::iterator firstElement = resourceSet->begin(); 282 Element* element = *firstElement; 283 284 resourceSet->remove(firstElement); 285 286 if (resourceSet->isEmpty()) 287 removePendingResourceForRemoval(id); 288 289 return element; 290} 291 292HashSet<SVGElement*>* SVGDocumentExtensions::setOfElementsReferencingTarget(SVGElement* referencedElement) const 293{ 294 ASSERT(referencedElement); 295 const HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::const_iterator it = m_elementDependencies.find(referencedElement); 296 if (it == m_elementDependencies.end()) 297 return 0; 298 return it->value.get(); 299} 300 301void SVGDocumentExtensions::addElementReferencingTarget(SVGElement* referencingElement, SVGElement* referencedElement) 302{ 303 ASSERT(referencingElement); 304 ASSERT(referencedElement); 305 306 if (HashSet<SVGElement*>* elements = m_elementDependencies.get(referencedElement)) { 307 elements->add(referencingElement); 308 return; 309 } 310 311 OwnPtr<HashSet<SVGElement*> > elements = adoptPtr(new HashSet<SVGElement*>); 312 elements->add(referencingElement); 313 m_elementDependencies.set(referencedElement, elements.release()); 314} 315 316void SVGDocumentExtensions::removeAllTargetReferencesForElement(SVGElement* referencingElement) 317{ 318 Vector<SVGElement*> toBeRemoved; 319 320 HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator end = m_elementDependencies.end(); 321 for (HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.begin(); it != end; ++it) { 322 SVGElement* referencedElement = it->key; 323 HashSet<SVGElement*>* referencingElements = it->value.get(); 324 HashSet<SVGElement*>::iterator setIt = referencingElements->find(referencingElement); 325 if (setIt == referencingElements->end()) 326 continue; 327 328 referencingElements->remove(setIt); 329 if (referencingElements->isEmpty()) 330 toBeRemoved.append(referencedElement); 331 } 332 333 Vector<SVGElement*>::iterator vectorEnd = toBeRemoved.end(); 334 for (Vector<SVGElement*>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 335 m_elementDependencies.remove(*it); 336} 337 338void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement) 339{ 340 ASSERT(referencedElement); 341 HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.find(referencedElement); 342 if (it == m_elementDependencies.end()) 343 return; 344 ASSERT(it->key == referencedElement); 345 Vector<SVGElement*> toBeNotified; 346 347 HashSet<SVGElement*>* referencingElements = it->value.get(); 348 HashSet<SVGElement*>::iterator setEnd = referencingElements->end(); 349 for (HashSet<SVGElement*>::iterator setIt = referencingElements->begin(); setIt != setEnd; ++setIt) 350 toBeNotified.append(*setIt); 351 352 // Force rebuilding the referencingElement so it knows about this change. 353 Vector<SVGElement*>::iterator vectorEnd = toBeNotified.end(); 354 for (Vector<SVGElement*>::iterator vectorIt = toBeNotified.begin(); vectorIt != vectorEnd; ++vectorIt) { 355 // Before rebuilding referencingElement ensure it was not removed from under us. 356 if (HashSet<SVGElement*>* referencingElements = setOfElementsReferencingTarget(referencedElement)) { 357 if (referencingElements->contains(*vectorIt)) 358 (*vectorIt)->svgAttributeChanged(XLinkNames::hrefAttr); 359 } 360 } 361} 362 363void SVGDocumentExtensions::removeAllElementReferencesForTarget(SVGElement* referencedElement) 364{ 365 ASSERT(referencedElement); 366 HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.find(referencedElement); 367 if (it == m_elementDependencies.end()) 368 return; 369 ASSERT(it->key == referencedElement); 370 371 m_elementDependencies.remove(it); 372} 373 374void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 375{ 376 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 377 m_relativeLengthSVGRoots.add(svgRoot); 378} 379 380void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 381{ 382 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 383 m_relativeLengthSVGRoots.remove(svgRoot); 384} 385 386bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) const 387{ 388 return m_relativeLengthSVGRoots.contains(svgRoot); 389} 390 391void SVGDocumentExtensions::invalidateSVGRootsWithRelativeLengthDescendents(SubtreeLayoutScope* scope) 392{ 393 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 394#if !ASSERT_DISABLED 395 TemporaryChange<bool> inRelativeLengthSVGRootsChange(m_inRelativeLengthSVGRootsInvalidation, true); 396#endif 397 398 HashSet<SVGSVGElement*>::iterator end = m_relativeLengthSVGRoots.end(); 399 for (HashSet<SVGSVGElement*>::iterator it = m_relativeLengthSVGRoots.begin(); it != end; ++it) 400 (*it)->invalidateRelativeLengthClients(scope); 401} 402 403#if ENABLE(SVG_FONTS) 404void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element) 405{ 406 m_svgFontFaceElements.add(element); 407} 408 409void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element) 410{ 411 ASSERT(m_svgFontFaceElements.contains(element)); 412 m_svgFontFaceElements.remove(element); 413} 414#endif 415 416} 417