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 "core/XLinkNames.h" 26 #include "core/dom/Document.h" 27 #include "core/rendering/RenderView.h" 28 #include "core/rendering/svg/SVGResourcesCache.h" 29 #include "core/svg/SVGFontFaceElement.h" 30 #include "core/svg/SVGSVGElement.h" 31 #include "core/svg/SVGViewSpec.h" 32 #include "core/svg/SVGZoomAndPan.h" 33 #include "core/svg/animation/SMILTimeContainer.h" 34 #include "wtf/TemporaryChange.h" 35 #include "wtf/text/AtomicString.h" 36 37 namespace WebCore { 38 39 SVGDocumentExtensions::SVGDocumentExtensions(Document* document) 40 : m_document(document) 41 , m_resourcesCache(adoptPtr(new SVGResourcesCache)) 42 #if ASSERT_ENABLED 43 , m_inRelativeLengthSVGRootsInvalidation(false) 44 #endif 45 { 46 } 47 48 SVGDocumentExtensions::~SVGDocumentExtensions() 49 { 50 } 51 52 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element) 53 { 54 m_timeContainers.add(element); 55 } 56 57 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element) 58 { 59 m_timeContainers.remove(element); 60 } 61 62 void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource) 63 { 64 ASSERT(resource); 65 66 if (id.isEmpty()) 67 return; 68 69 // Replaces resource if already present, to handle potential id changes 70 m_resources.set(id, resource); 71 } 72 73 void SVGDocumentExtensions::removeResource(const AtomicString& id) 74 { 75 if (id.isEmpty()) 76 return; 77 78 m_resources.remove(id); 79 } 80 81 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const 82 { 83 if (id.isEmpty()) 84 return 0; 85 86 return m_resources.get(id); 87 } 88 89 void SVGDocumentExtensions::serviceOnAnimationFrame(Document& document, double monotonicAnimationStartTime) 90 { 91 if (!document.svgExtensions()) 92 return; 93 document.accessSVGExtensions().serviceAnimations(monotonicAnimationStartTime); 94 } 95 96 void SVGDocumentExtensions::serviceAnimations(double monotonicAnimationStartTime) 97 { 98 WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers; 99 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 100 WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end(); 101 for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) 102 (*itr)->timeContainer()->serviceAnimations(monotonicAnimationStartTime); 103 } 104 105 void SVGDocumentExtensions::startAnimations() 106 { 107 // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer 108 // starting animations for a document will do this "latching" 109 // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us. 110 // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704 111 WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers; 112 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 113 WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end(); 114 for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) { 115 SMILTimeContainer* timeContainer = (*itr)->timeContainer(); 116 if (!timeContainer->isStarted()) 117 timeContainer->begin(); 118 } 119 } 120 121 void SVGDocumentExtensions::pauseAnimations() 122 { 123 WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator end = m_timeContainers.end(); 124 for (WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 125 (*itr)->pauseAnimations(); 126 } 127 128 void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements() 129 { 130 WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> > timeContainers; 131 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 132 133 WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator end = timeContainers.end(); 134 for (WillBeHeapVector<RefPtrWillBeMember<SVGSVGElement> >::iterator it = timeContainers.begin(); it != end; ++it) { 135 SVGSVGElement* outerSVG = it->get(); 136 if (!outerSVG->isOutermostSVGSVGElement()) 137 continue; 138 139 // don't dispatch the load event document is not wellformed (for XML/standalone svg) 140 if (outerSVG->document().wellFormed() || !outerSVG->document().isSVGDocument()) 141 outerSVG->sendSVGLoadEventIfPossible(); 142 } 143 } 144 145 static void reportMessage(Document* document, MessageLevel level, const String& message) 146 { 147 if (document->frame()) 148 document->addConsoleMessage(RenderingMessageSource, level, message); 149 } 150 151 void SVGDocumentExtensions::reportWarning(const String& message) 152 { 153 reportMessage(m_document, WarningMessageLevel, "Warning: " + message); 154 } 155 156 void SVGDocumentExtensions::reportError(const String& message) 157 { 158 reportMessage(m_document, ErrorMessageLevel, "Error: " + message); 159 } 160 161 void SVGDocumentExtensions::addPendingResource(const AtomicString& id, Element* element) 162 { 163 ASSERT(element); 164 ASSERT(element->inDocument()); 165 166 if (id.isEmpty()) 167 return; 168 169 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::AddResult result = m_pendingResources.add(id, nullptr); 170 if (result.isNewEntry) 171 result.storedValue->value = adoptPtr(new SVGPendingElements); 172 result.storedValue->value->add(element); 173 174 element->setHasPendingResources(); 175 } 176 177 bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const 178 { 179 if (id.isEmpty()) 180 return false; 181 182 return m_pendingResources.contains(id); 183 } 184 185 bool SVGDocumentExtensions::isElementPendingResources(Element* element) const 186 { 187 // This algorithm takes time proportional to the number of pending resources and need not. 188 // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently. 189 190 ASSERT(element); 191 192 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator end = m_pendingResources.end(); 193 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::const_iterator it = m_pendingResources.begin(); it != end; ++it) { 194 SVGPendingElements* elements = it->value.get(); 195 ASSERT(elements); 196 197 if (elements->contains(element)) 198 return true; 199 } 200 return false; 201 } 202 203 bool SVGDocumentExtensions::isElementPendingResource(Element* element, const AtomicString& id) const 204 { 205 ASSERT(element); 206 207 if (!hasPendingResource(id)) 208 return false; 209 210 return m_pendingResources.get(id)->contains(element); 211 } 212 213 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element) 214 { 215 if (!isElementPendingResources(element)) 216 element->clearHasPendingResources(); 217 } 218 219 void SVGDocumentExtensions::removeElementFromPendingResources(Element* element) 220 { 221 ASSERT(element); 222 223 // Remove the element from pending resources. 224 if (!m_pendingResources.isEmpty() && element->hasPendingResources()) { 225 Vector<AtomicString> toBeRemoved; 226 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResources.end(); 227 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResources.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 clearHasPendingResourcesIfPossible(element); 238 239 // We use the removePendingResource function here because it deals with set lifetime correctly. 240 Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end(); 241 for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 242 removePendingResource(*it); 243 } 244 245 // Remove the element from pending resources that were scheduled for removal. 246 if (!m_pendingResourcesForRemoval.isEmpty()) { 247 Vector<AtomicString> toBeRemoved; 248 HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator end = m_pendingResourcesForRemoval.end(); 249 for (HashMap<AtomicString, OwnPtr<SVGPendingElements> >::iterator it = m_pendingResourcesForRemoval.begin(); it != end; ++it) { 250 SVGPendingElements* elements = it->value.get(); 251 ASSERT(elements); 252 ASSERT(!elements->isEmpty()); 253 254 elements->remove(element); 255 if (elements->isEmpty()) 256 toBeRemoved.append(it->key); 257 } 258 259 // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly. 260 Vector<AtomicString>::iterator vectorEnd = toBeRemoved.end(); 261 for (Vector<AtomicString>::iterator it = toBeRemoved.begin(); it != vectorEnd; ++it) 262 removePendingResourceForRemoval(*it); 263 } 264 } 265 266 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id) 267 { 268 ASSERT(m_pendingResources.contains(id)); 269 return m_pendingResources.take(id); 270 } 271 272 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id) 273 { 274 ASSERT(m_pendingResourcesForRemoval.contains(id)); 275 return m_pendingResourcesForRemoval.take(id); 276 } 277 278 void SVGDocumentExtensions::markPendingResourcesForRemoval(const AtomicString& id) 279 { 280 if (id.isEmpty()) 281 return; 282 283 ASSERT(!m_pendingResourcesForRemoval.contains(id)); 284 285 OwnPtr<SVGPendingElements> existing = m_pendingResources.take(id); 286 if (existing && !existing->isEmpty()) 287 m_pendingResourcesForRemoval.add(id, existing.release()); 288 } 289 290 Element* SVGDocumentExtensions::removeElementFromPendingResourcesForRemoval(const AtomicString& id) 291 { 292 if (id.isEmpty()) 293 return 0; 294 295 SVGPendingElements* resourceSet = m_pendingResourcesForRemoval.get(id); 296 if (!resourceSet || resourceSet->isEmpty()) 297 return 0; 298 299 SVGPendingElements::iterator firstElement = resourceSet->begin(); 300 Element* element = *firstElement; 301 302 resourceSet->remove(firstElement); 303 304 if (resourceSet->isEmpty()) 305 removePendingResourceForRemoval(id); 306 307 return element; 308 } 309 310 SVGElementSet* SVGDocumentExtensions::setOfElementsReferencingTarget(SVGElement* referencedElement) const 311 { 312 ASSERT(referencedElement); 313 const ElementDependenciesMap::const_iterator it = m_elementDependencies.find(referencedElement); 314 if (it == m_elementDependencies.end()) 315 return 0; 316 return it->value.get(); 317 } 318 319 void SVGDocumentExtensions::addElementReferencingTarget(SVGElement* referencingElement, SVGElement* referencedElement) 320 { 321 ASSERT(referencingElement); 322 ASSERT(referencedElement); 323 324 if (SVGElementSet* elements = m_elementDependencies.get(referencedElement)) { 325 elements->add(referencingElement); 326 return; 327 } 328 329 OwnPtrWillBeRawPtr<SVGElementSet> elements = adoptPtrWillBeNoop(new SVGElementSet); 330 elements->add(referencingElement); 331 m_elementDependencies.set(referencedElement, elements.release()); 332 } 333 334 void SVGDocumentExtensions::removeAllTargetReferencesForElement(SVGElement* referencingElement) 335 { 336 WillBeHeapVector<RawPtrWillBeMember<SVGElement> > toBeRemoved; 337 338 ElementDependenciesMap::iterator end = m_elementDependencies.end(); 339 for (ElementDependenciesMap::iterator it = m_elementDependencies.begin(); it != end; ++it) { 340 SVGElement* referencedElement = it->key; 341 SVGElementSet* referencingElements = it->value.get(); 342 SVGElementSet::iterator setIt = referencingElements->find(referencingElement); 343 if (setIt == referencingElements->end()) 344 continue; 345 346 referencingElements->remove(setIt); 347 if (referencingElements->isEmpty()) 348 toBeRemoved.append(referencedElement); 349 } 350 351 m_elementDependencies.removeAll(toBeRemoved); 352 } 353 354 void SVGDocumentExtensions::rebuildAllElementReferencesForTarget(SVGElement* referencedElement) 355 { 356 ASSERT(referencedElement); 357 ElementDependenciesMap::iterator it = m_elementDependencies.find(referencedElement); 358 if (it == m_elementDependencies.end()) 359 return; 360 ASSERT(it->key == referencedElement); 361 362 WillBeHeapVector<RawPtrWillBeMember<SVGElement> > toBeNotified; 363 SVGElementSet* referencingElements = it->value.get(); 364 copyToVector(*referencingElements, toBeNotified); 365 366 // Force rebuilding the referencingElement so it knows about this change. 367 WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator vectorEnd = toBeNotified.end(); 368 for (WillBeHeapVector<RawPtrWillBeMember<SVGElement> >::iterator vectorIt = toBeNotified.begin(); vectorIt != vectorEnd; ++vectorIt) { 369 // Before rebuilding referencingElement ensure it was not removed from under us. 370 if (SVGElementSet* referencingElements = setOfElementsReferencingTarget(referencedElement)) { 371 if (referencingElements->contains(*vectorIt)) 372 (*vectorIt)->svgAttributeChanged(XLinkNames::hrefAttr); 373 } 374 } 375 } 376 377 void SVGDocumentExtensions::removeAllElementReferencesForTarget(SVGElement* referencedElement) 378 { 379 ASSERT(referencedElement); 380 ElementDependenciesMap::iterator it = m_elementDependencies.find(referencedElement); 381 if (it == m_elementDependencies.end()) 382 return; 383 ASSERT(it->key == referencedElement); 384 385 m_elementDependencies.remove(it); 386 } 387 388 void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 389 { 390 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 391 m_relativeLengthSVGRoots.add(svgRoot); 392 } 393 394 void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 395 { 396 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 397 m_relativeLengthSVGRoots.remove(svgRoot); 398 } 399 400 bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) const 401 { 402 return m_relativeLengthSVGRoots.contains(svgRoot); 403 } 404 405 void SVGDocumentExtensions::invalidateSVGRootsWithRelativeLengthDescendents(SubtreeLayoutScope* scope) 406 { 407 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 408 #if ASSERT_ENABLED 409 TemporaryChange<bool> inRelativeLengthSVGRootsChange(m_inRelativeLengthSVGRootsInvalidation, true); 410 #endif 411 412 WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator end = m_relativeLengthSVGRoots.end(); 413 for (WillBeHeapHashSet<RawPtrWillBeMember<SVGSVGElement> >::iterator it = m_relativeLengthSVGRoots.begin(); it != end; ++it) 414 (*it)->invalidateRelativeLengthClients(scope); 415 } 416 417 #if ENABLE(SVG_FONTS) 418 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element) 419 { 420 m_svgFontFaceElements.add(element); 421 } 422 423 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element) 424 { 425 ASSERT(m_svgFontFaceElements.contains(element)); 426 m_svgFontFaceElements.remove(element); 427 } 428 429 void SVGDocumentExtensions::registerPendingSVGFontFaceElementsForRemoval(PassRefPtrWillBeRawPtr<SVGFontFaceElement> font) 430 { 431 m_pendingSVGFontFaceElementsForRemoval.add(font); 432 } 433 434 void SVGDocumentExtensions::removePendingSVGFontFaceElementsForRemoval() 435 { 436 m_pendingSVGFontFaceElementsForRemoval.clear(); 437 } 438 439 #endif 440 441 bool SVGDocumentExtensions::zoomAndPanEnabled() const 442 { 443 if (SVGSVGElement* svg = rootElement(*m_document)) { 444 if (svg->useCurrentView()) { 445 if (svg->currentView()) 446 return svg->currentView()->zoomAndPan() == SVGZoomAndPanMagnify; 447 } else { 448 return svg->zoomAndPan() == SVGZoomAndPanMagnify; 449 } 450 } 451 452 return false; 453 } 454 455 void SVGDocumentExtensions::startPan(const FloatPoint& start) 456 { 457 if (SVGSVGElement* svg = rootElement(*m_document)) 458 m_translate = FloatPoint(start.x() - svg->currentTranslate().x(), start.y() - svg->currentTranslate().y()); 459 } 460 461 void SVGDocumentExtensions::updatePan(const FloatPoint& pos) const 462 { 463 if (SVGSVGElement* svg = rootElement(*m_document)) 464 svg->setCurrentTranslate(FloatPoint(pos.x() - m_translate.x(), pos.y() - m_translate.y())); 465 } 466 467 SVGSVGElement* SVGDocumentExtensions::rootElement(const Document& document) 468 { 469 Element* elem = document.documentElement(); 470 return isSVGSVGElement(elem) ? toSVGSVGElement(elem) : 0; 471 } 472 473 SVGSVGElement* SVGDocumentExtensions::rootElement() const 474 { 475 ASSERT(m_document); 476 return rootElement(*m_document); 477 } 478 479 void SVGDocumentExtensions::trace(Visitor* visitor) 480 { 481 visitor->trace(m_document); 482 visitor->trace(m_timeContainers); 483 #if ENABLE(SVG_FONTS) 484 visitor->trace(m_svgFontFaceElements); 485 visitor->trace(m_pendingSVGFontFaceElementsForRemoval); 486 #endif 487 visitor->trace(m_elementDependencies); 488 visitor->trace(m_relativeLengthSVGRoots); 489 } 490 491 } 492