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/text/AtomicString.h" 32 33 namespace WebCore { 34 35 SVGDocumentExtensions::SVGDocumentExtensions(Document* document) 36 : m_document(document) 37 , m_resourcesCache(adoptPtr(new SVGResourcesCache)) 38 { 39 } 40 41 SVGDocumentExtensions::~SVGDocumentExtensions() 42 { 43 } 44 45 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element) 46 { 47 m_timeContainers.add(element); 48 } 49 50 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element) 51 { 52 m_timeContainers.remove(element); 53 } 54 55 void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource) 56 { 57 ASSERT(resource); 58 59 if (id.isEmpty()) 60 return; 61 62 // Replaces resource if already present, to handle potential id changes 63 m_resources.set(id, resource); 64 } 65 66 void SVGDocumentExtensions::removeResource(const AtomicString& id) 67 { 68 if (id.isEmpty() || !m_resources.contains(id)) 69 return; 70 71 m_resources.remove(id); 72 } 73 74 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const 75 { 76 if (id.isEmpty()) 77 return 0; 78 79 return m_resources.get(id); 80 } 81 82 void SVGDocumentExtensions::startAnimations() 83 { 84 // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer 85 // starting animations for a document will do this "latching" 86 // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us. 87 // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704 88 Vector<RefPtr<SVGSVGElement> > timeContainers; 89 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 90 Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end(); 91 for (Vector<RefPtr<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) 92 (*itr)->timeContainer()->begin(); 93 } 94 95 void SVGDocumentExtensions::pauseAnimations() 96 { 97 HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end(); 98 for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 99 (*itr)->pauseAnimations(); 100 } 101 102 void SVGDocumentExtensions::unpauseAnimations() 103 { 104 HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end(); 105 for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 106 (*itr)->unpauseAnimations(); 107 } 108 109 void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements() 110 { 111 Vector<RefPtr<SVGSVGElement> > timeContainers; 112 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 113 114 Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end(); 115 for (Vector<RefPtr<SVGSVGElement> >::iterator it = timeContainers.begin(); it != end; ++it) { 116 SVGSVGElement* outerSVG = (*it).get(); 117 if (!outerSVG->isOutermostSVGSVGElement()) 118 continue; 119 outerSVG->sendSVGLoadEventIfPossible(); 120 } 121 } 122 123 static void reportMessage(Document* document, MessageLevel level, const String& message) 124 { 125 if (document->frame()) 126 document->addConsoleMessage(RenderingMessageSource, level, message); 127 } 128 129 void SVGDocumentExtensions::reportWarning(const String& message) 130 { 131 reportMessage(m_document, WarningMessageLevel, "Warning: " + message); 132 } 133 134 void SVGDocumentExtensions::reportError(const String& message) 135 { 136 reportMessage(m_document, ErrorMessageLevel, "Error: " + message); 137 } 138 139 void SVGDocumentExtensions::addPendingResource(const AtomicString& id, Element* element) 140 { 141 ASSERT(element); 142 ASSERT(element->inDocument()); 143 144 if (id.isEmpty()) 145 return; 146 147 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::AddResult result = m_pendingResources.add(id, nullptr); 148 if (result.isNewEntry) 149 result.iterator->value = adoptPtr(new SVGPendingElements); 150 result.iterator->value->add(element); 151 152 element->setHasPendingResources(); 153 } 154 155 bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const 156 { 157 if (id.isEmpty()) 158 return false; 159 160 return m_pendingResources.contains(id); 161 } 162 163 bool SVGDocumentExtensions::isElementPendingResources(Element* element) const 164 { 165 // This algorithm takes time proportional to the number of pending resources and need not. 166 // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently. 167 168 ASSERT(element); 169 170 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator end = m_pendingResources.end(); 171 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator it = m_pendingResources.begin(); it != end; ++it) { 172 SVGPendingElements* elements = it->value.get(); 173 ASSERT(elements); 174 175 if (elements->contains(element)) 176 return true; 177 } 178 return false; 179 } 180 181 bool SVGDocumentExtensions::isElementPendingResource(Element* element, const AtomicString& id) const 182 { 183 ASSERT(element); 184 185 if (!hasPendingResource(id)) 186 return false; 187 188 return m_pendingResources.get(id)->contains(element); 189 } 190 191 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element) 192 { 193 if (!isElementPendingResources(element)) 194 element->clearHasPendingResources(); 195 } 196 197 void SVGDocumentExtensions::removeElementFromPendingResources(Element* element) 198 { 199 ASSERT(element); 200 201 // Remove the element from pending resources. 202 if (!m_pendingResources.isEmpty() && element->hasPendingResources()) { 203 Vector<AtomicString> toBeRemoved; 204 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResources.end(); 205 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResources.begin(); it != end; ++it) { 206 SVGPendingElements* elements = it->value.get(); 207 ASSERT(elements); 208 ASSERT(!elements->isEmpty()); 209 210 elements->remove(element); 211 if (elements->isEmpty()) 212 toBeRemoved.append(it->key); 213 } 214 215 clearHasPendingResourcesIfPossible(element); 216 217 // We use the removePendingResource function here because it deals with set lifetime correctly. 218 Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end(); 219 for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 220 removePendingResource(*it); 221 } 222 223 // Remove the element from pending resources that were scheduled for removal. 224 if (!m_pendingResourcesForRemoval.isEmpty()) { 225 Vector<AtomicString> toBeRemoved; 226 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResourcesForRemoval.end(); 227 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResourcesForRemoval.begin(); it != end; ++it) { 228 SVGPendingElements* elements = it->value.get(); 229 ASSERT(elements); 230 ASSERT(!elements->isEmpty()); 231 232 elements->remove(element); 233 if (elements->isEmpty()) 234 toBeRemoved.append(it->key); 235 } 236 237 // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly. 238 Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end(); 239 for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 240 removePendingResourceForRemoval(*it); 241 } 242 } 243 244 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id) 245 { 246 ASSERT(m_pendingResources.contains(id)); 247 return m_pendingResources.take(id); 248 } 249 250 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id) 251 { 252 ASSERT(m_pendingResourcesForRemoval.contains(id)); 253 return m_pendingResourcesForRemoval.take(id); 254 } 255 256 void SVGDocumentExtensions::markPendingResourcesForRemoval(const AtomicString& id) 257 { 258 if (id.isEmpty()) 259 return; 260 261 ASSERT(!m_pendingResourcesForRemoval.contains(id)); 262 263 OwnPtr<SVGPendingElements> existing = m_pendingResources.take(id); 264 if (existing && !existing->isEmpty()) 265 m_pendingResourcesForRemoval.add(id, existing.release()); 266 } 267 268 Element* SVGDocumentExtensions::removeElementFromPendingResourcesForRemoval(const AtomicString& id) 269 { 270 if (id.isEmpty()) 271 return 0; 272 273 SVGPendingElements* resourceSet = m_pendingResourcesForRemoval.get(id); 274 if (!resourceSet || resourceSet->isEmpty()) 275 return 0; 276 277 SVGPendingElements::iterator firstElement = resourceSet->begin(); 278 Element* element = *firstElement; 279 280 resourceSet->remove(firstElement); 281 282 if (resourceSet->isEmpty()) 283 removePendingResourceForRemoval(id); 284 285 return element; 286 } 287 288 HashSet<SVGElement*>* SVGDocumentExtensions::setOfElementsReferencingTarget(SVGElement* referencedElement) const 289 { 290 ASSERT(referencedElement); 291 const HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::const_iterator it = m_elementDependencies.find(referencedElement); 292 if (it == m_elementDependencies.end()) 293 return 0; 294 return it->value.get(); 295 } 296 297 void SVGDocumentExtensions::addElementReferencingTarget(SVGElement* referencingElement, SVGElement* referencedElement) 298 { 299 ASSERT(referencingElement); 300 ASSERT(referencedElement); 301 302 if (HashSet<SVGElement*>* elements = m_elementDependencies.get(referencedElement)) { 303 elements->add(referencingElement); 304 return; 305 } 306 307 OwnPtr<HashSet<SVGElement*> > elements = adoptPtr(new HashSet<SVGElement*>); 308 elements->add(referencingElement); 309 m_elementDependencies.set(referencedElement, elements.release()); 310 } 311 312 void SVGDocumentExtensions::removeAllTargetReferencesForElement(SVGElement* referencingElement) 313 { 314 Vector<SVGElement*> toBeRemoved; 315 316 HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator end = m_elementDependencies.end(); 317 for (HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.begin(); it != end; ++it) { 318 SVGElement* referencedElement = it->key; 319 HashSet<SVGElement*>* referencingElements = it->value.get(); 320 HashSet<SVGElement*>::iterator setIt = referencingElements->find(referencingElement); 321 if (setIt == referencingElements->end()) 322 continue; 323 324 referencingElements->remove(setIt); 325 if (referencingElements->isEmpty()) 326 toBeRemoved.append(referencedElement); 327 } 328 329 Vector<SVGElement*>::iterator vectorEnd = toBeRemoved.end(); 330 for (Vector<SVGElement*>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 331 m_elementDependencies.remove(*it); 332 } 333 334 void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement) 335 { 336 ASSERT(referencedElement); 337 HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.find(referencedElement); 338 if (it == m_elementDependencies.end()) 339 return; 340 ASSERT(it->key == referencedElement); 341 Vector<SVGElement*> toBeNotified; 342 343 HashSet<SVGElement*>* referencingElements = it->value.get(); 344 HashSet<SVGElement*>::iterator setEnd = referencingElements->end(); 345 for (HashSet<SVGElement*>::iterator setIt = referencingElements->begin(); setIt != setEnd; ++setIt) 346 toBeNotified.append(*setIt); 347 348 // Force rebuilding the referencingElement so it knows about this change. 349 Vector<SVGElement*>::iterator vectorEnd = toBeNotified.end(); 350 for (Vector<SVGElement*>::iterator vectorIt = toBeNotified.begin(); vectorIt != vectorEnd; ++vectorIt) { 351 // Before rebuilding referencingElement ensure it was not removed from under us. 352 if (HashSet<SVGElement*>* referencingElements = setOfElementsReferencingTarget(referencedElement)) { 353 if (referencingElements->contains(*vectorIt)) 354 (*vectorIt)->svgAttributeChanged(XLinkNames::hrefAttr); 355 } 356 } 357 } 358 359 void SVGDocumentExtensions::removeAllElementReferencesForTarget(SVGElement* referencedElement) 360 { 361 ASSERT(referencedElement); 362 HashMap<SVGElement*, OwnPtr<HashSet<SVGElement*> > >::iterator it = m_elementDependencies.find(referencedElement); 363 if (it == m_elementDependencies.end()) 364 return; 365 ASSERT(it->key == referencedElement); 366 367 m_elementDependencies.remove(it); 368 } 369 370 #if ENABLE(SVG_FONTS) 371 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element) 372 { 373 m_svgFontFaceElements.add(element); 374 } 375 376 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element) 377 { 378 ASSERT(m_svgFontFaceElements.contains(element)); 379 m_svgFontFaceElements.remove(element); 380 } 381 #endif 382 383 } 384