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