1 /* 2 * Copyright (C) 2006 Apple Inc. All rights reserved. 3 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 4 * Copyright (C) 2007 Rob Buis <buis (at) 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 34 namespace WebCore { 35 36 SVGDocumentExtensions::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 45 SVGDocumentExtensions::~SVGDocumentExtensions() 46 { 47 } 48 49 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element) 50 { 51 m_timeContainers.add(element); 52 } 53 54 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element) 55 { 56 m_timeContainers.remove(element); 57 } 58 59 void 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 70 void SVGDocumentExtensions::removeResource(const AtomicString& id) 71 { 72 if (id.isEmpty()) 73 return; 74 75 m_resources.remove(id); 76 } 77 78 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const 79 { 80 if (id.isEmpty()) 81 return 0; 82 83 return m_resources.get(id); 84 } 85 86 void 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 99 void 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 106 void 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 113 void 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 127 static void reportMessage(Document* document, MessageLevel level, const String& message) 128 { 129 if (document->frame()) 130 document->addConsoleMessage(RenderingMessageSource, level, message); 131 } 132 133 void SVGDocumentExtensions::reportWarning(const String& message) 134 { 135 reportMessage(m_document, WarningMessageLevel, "Warning: " + message); 136 } 137 138 void SVGDocumentExtensions::reportError(const String& message) 139 { 140 reportMessage(m_document, ErrorMessageLevel, "Error: " + message); 141 } 142 143 void 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 159 bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const 160 { 161 if (id.isEmpty()) 162 return false; 163 164 return m_pendingResources.contains(id); 165 } 166 167 bool 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 185 bool 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 195 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element) 196 { 197 if (!isElementPendingResources(element)) 198 element->clearHasPendingResources(); 199 } 200 201 void 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 248 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id) 249 { 250 ASSERT(m_pendingResources.contains(id)); 251 return m_pendingResources.take(id); 252 } 253 254 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id) 255 { 256 ASSERT(m_pendingResourcesForRemoval.contains(id)); 257 return m_pendingResourcesForRemoval.take(id); 258 } 259 260 void 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 272 Element* 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 292 HashSet<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 301 void 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 316 void 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 338 void 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 363 void 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 374 void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 375 { 376 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 377 m_relativeLengthSVGRoots.add(svgRoot); 378 } 379 380 void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 381 { 382 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 383 m_relativeLengthSVGRoots.remove(svgRoot); 384 } 385 386 bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) const 387 { 388 return m_relativeLengthSVGRoots.contains(svgRoot); 389 } 390 391 void 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) 404 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element) 405 { 406 m_svgFontFaceElements.add(element); 407 } 408 409 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element) 410 { 411 ASSERT(m_svgFontFaceElements.contains(element)); 412 m_svgFontFaceElements.remove(element); 413 } 414 #endif 415 416 } 417