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