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 "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 39namespace blink { 40 41SVGDocumentExtensions::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 50SVGDocumentExtensions::~SVGDocumentExtensions() 51{ 52} 53 54void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element) 55{ 56 m_timeContainers.add(element); 57} 58 59void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element) 60{ 61 m_timeContainers.remove(element); 62} 63 64void 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 75void SVGDocumentExtensions::removeResource(const AtomicString& id) 76{ 77 if (id.isEmpty()) 78 return; 79 80 m_resources.remove(id); 81} 82 83RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const 84{ 85 if (id.isEmpty()) 86 return 0; 87 88 return m_resources.get(id); 89} 90 91void SVGDocumentExtensions::serviceOnAnimationFrame(Document& document, double monotonicAnimationStartTime) 92{ 93 if (!document.svgExtensions()) 94 return; 95 document.accessSVGExtensions().serviceAnimations(monotonicAnimationStartTime); 96} 97 98void 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 107void 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 123void 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 130void 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 147static void reportMessage(Document* document, MessageLevel level, const String& message) 148{ 149 if (document->frame()) 150 document->addConsoleMessage(ConsoleMessage::create(RenderingMessageSource, level, message)); 151} 152 153void SVGDocumentExtensions::reportWarning(const String& message) 154{ 155 reportMessage(m_document, WarningMessageLevel, "Warning: " + message); 156} 157 158void SVGDocumentExtensions::reportError(const String& message) 159{ 160 reportMessage(m_document, ErrorMessageLevel, "Error: " + message); 161} 162 163void 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 179bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const 180{ 181 if (id.isEmpty()) 182 return false; 183 184 return m_pendingResources.contains(id); 185} 186 187bool 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 205bool 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 215void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element) 216{ 217 if (!isElementPendingResources(element)) 218 element->clearHasPendingResources(); 219} 220 221void 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 268PassOwnPtrWillBeRawPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id) 269{ 270 ASSERT(m_pendingResources.contains(id)); 271 return m_pendingResources.take(id); 272} 273 274PassOwnPtrWillBeRawPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id) 275{ 276 ASSERT(m_pendingResourcesForRemoval.contains(id)); 277 return m_pendingResourcesForRemoval.take(id); 278} 279 280void 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 292Element* 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 312void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 313{ 314 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 315 m_relativeLengthSVGRoots.add(svgRoot); 316} 317 318void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) 319{ 320 ASSERT(!m_inRelativeLengthSVGRootsInvalidation); 321 m_relativeLengthSVGRoots.remove(svgRoot); 322} 323 324bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) const 325{ 326 return m_relativeLengthSVGRoots.contains(svgRoot); 327} 328 329void 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) 342void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element) 343{ 344 m_svgFontFaceElements.add(element); 345} 346 347void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element) 348{ 349 ASSERT(m_svgFontFaceElements.contains(element)); 350 m_svgFontFaceElements.remove(element); 351} 352 353void SVGDocumentExtensions::registerPendingSVGFontFaceElementsForRemoval(PassRefPtrWillBeRawPtr<SVGFontFaceElement> font) 354{ 355 m_pendingSVGFontFaceElementsForRemoval.add(font); 356} 357 358void SVGDocumentExtensions::removePendingSVGFontFaceElementsForRemoval() 359{ 360 m_pendingSVGFontFaceElementsForRemoval.clear(); 361} 362 363#endif 364 365bool 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 379void 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 385void 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 391SVGSVGElement* SVGDocumentExtensions::rootElement(const Document& document) 392{ 393 Element* elem = document.documentElement(); 394 return isSVGSVGElement(elem) ? toSVGSVGElement(elem) : 0; 395} 396 397SVGSVGElement* SVGDocumentExtensions::rootElement() const 398{ 399 ASSERT(m_document); 400 return rootElement(*m_document); 401} 402 403void 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