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 24 #if ENABLE(SVG) 25 #include "SVGDocumentExtensions.h" 26 27 #include "Console.h" 28 #include "DOMWindow.h" 29 #include "Document.h" 30 #include "EventListener.h" 31 #include "Frame.h" 32 #include "FrameLoader.h" 33 #include "Page.h" 34 #include "SMILTimeContainer.h" 35 #include "SVGElement.h" 36 #include "SVGSMILElement.h" 37 #include "SVGSVGElement.h" 38 #include "ScriptController.h" 39 #include "ScriptableDocumentParser.h" 40 #include <wtf/text/AtomicString.h> 41 42 namespace WebCore { 43 44 SVGDocumentExtensions::SVGDocumentExtensions(Document* document) 45 : m_document(document) 46 , m_resourcesCache(adoptPtr(new SVGResourcesCache)) 47 { 48 } 49 50 SVGDocumentExtensions::~SVGDocumentExtensions() 51 { 52 deleteAllValues(m_animatedElements); 53 deleteAllValues(m_pendingResources); 54 } 55 56 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element) 57 { 58 m_timeContainers.add(element); 59 } 60 61 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element) 62 { 63 m_timeContainers.remove(element); 64 } 65 66 void SVGDocumentExtensions::addResource(const AtomicString& id, RenderSVGResourceContainer* resource) 67 { 68 ASSERT(resource); 69 70 if (id.isEmpty()) 71 return; 72 73 // Replaces resource if already present, to handle potential id changes 74 m_resources.set(id, resource); 75 } 76 77 void SVGDocumentExtensions::removeResource(const AtomicString& id) 78 { 79 if (id.isEmpty() || !m_resources.contains(id)) 80 return; 81 82 m_resources.remove(id); 83 } 84 85 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const 86 { 87 if (id.isEmpty()) 88 return 0; 89 90 return m_resources.get(id); 91 } 92 93 void SVGDocumentExtensions::startAnimations() 94 { 95 // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer 96 // starting animations for a document will do this "latching" 97 #if ENABLE(SVG_ANIMATION) 98 // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us. 99 // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704 100 Vector<RefPtr<SVGSVGElement> > timeContainers; 101 timeContainers.appendRange(m_timeContainers.begin(), m_timeContainers.end()); 102 Vector<RefPtr<SVGSVGElement> >::iterator end = timeContainers.end(); 103 for (Vector<RefPtr<SVGSVGElement> >::iterator itr = timeContainers.begin(); itr != end; ++itr) 104 (*itr)->timeContainer()->begin(); 105 #endif 106 } 107 108 void SVGDocumentExtensions::pauseAnimations() 109 { 110 HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end(); 111 for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 112 (*itr)->pauseAnimations(); 113 } 114 115 void SVGDocumentExtensions::unpauseAnimations() 116 { 117 HashSet<SVGSVGElement*>::iterator end = m_timeContainers.end(); 118 for (HashSet<SVGSVGElement*>::iterator itr = m_timeContainers.begin(); itr != end; ++itr) 119 (*itr)->unpauseAnimations(); 120 } 121 122 bool SVGDocumentExtensions::sampleAnimationAtTime(const String& elementId, SVGSMILElement* element, double time) 123 { 124 #if !ENABLE(SVG_ANIMATION) 125 UNUSED_PARAM(elementId); 126 UNUSED_PARAM(element); 127 UNUSED_PARAM(time); 128 return false; 129 #else 130 ASSERT(element); 131 SMILTimeContainer* container = element->timeContainer(); 132 if (!container || container->isPaused()) 133 return false; 134 135 container->sampleAnimationAtTime(elementId, time); 136 return true; 137 #endif 138 } 139 140 void SVGDocumentExtensions::addAnimationElementToTarget(SVGSMILElement* animationElement, SVGElement* targetElement) 141 { 142 ASSERT(targetElement); 143 ASSERT(animationElement); 144 145 if (HashSet<SVGSMILElement*>* animationElementsForTarget = m_animatedElements.get(targetElement)) { 146 animationElementsForTarget->add(animationElement); 147 return; 148 } 149 150 HashSet<SVGSMILElement*>* animationElementsForTarget = new HashSet<SVGSMILElement*>; 151 animationElementsForTarget->add(animationElement); 152 m_animatedElements.set(targetElement, animationElementsForTarget); 153 } 154 155 void SVGDocumentExtensions::removeAnimationElementFromTarget(SVGSMILElement* animationElement, SVGElement* targetElement) 156 { 157 ASSERT(targetElement); 158 ASSERT(animationElement); 159 160 HashMap<SVGElement*, HashSet<SVGSMILElement*>* >::iterator it = m_animatedElements.find(targetElement); 161 ASSERT(it != m_animatedElements.end()); 162 163 HashSet<SVGSMILElement*>* animationElementsForTarget = it->second; 164 ASSERT(!animationElementsForTarget->isEmpty()); 165 166 animationElementsForTarget->remove(animationElement); 167 if (animationElementsForTarget->isEmpty()) { 168 m_animatedElements.remove(it); 169 delete animationElementsForTarget; 170 } 171 } 172 173 void SVGDocumentExtensions::removeAllAnimationElementsFromTarget(SVGElement* targetElement) 174 { 175 ASSERT(targetElement); 176 HashSet<SVGSMILElement*>* animationElementsForTarget = m_animatedElements.take(targetElement); 177 if (!animationElementsForTarget) 178 return; 179 #if ENABLE(SVG_ANIMATION) 180 HashSet<SVGSMILElement*>::iterator it = animationElementsForTarget->begin(); 181 HashSet<SVGSMILElement*>::iterator end = animationElementsForTarget->end(); 182 for (; it != end; ++it) 183 (*it)->resetTargetElement(); 184 delete animationElementsForTarget; 185 #else 186 ASSERT_NOT_REACHED(); 187 #endif 188 } 189 190 // FIXME: Callers should probably use ScriptController::eventHandlerLineNumber() 191 static int parserLineNumber(Document* document) 192 { 193 ScriptableDocumentParser* parser = document->scriptableDocumentParser(); 194 if (!parser) 195 return 1; 196 return parser->lineNumber(); 197 } 198 199 static void reportMessage(Document* document, MessageLevel level, const String& message) 200 { 201 if (Frame* frame = document->frame()) 202 frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, level, message, parserLineNumber(document), String()); 203 } 204 205 void SVGDocumentExtensions::reportWarning(const String& message) 206 { 207 reportMessage(m_document, WarningMessageLevel, "Warning: " + message); 208 } 209 210 void SVGDocumentExtensions::reportError(const String& message) 211 { 212 reportMessage(m_document, ErrorMessageLevel, "Error: " + message); 213 } 214 215 void SVGDocumentExtensions::addPendingResource(const AtomicString& id, PassRefPtr<SVGStyledElement> obj) 216 { 217 ASSERT(obj); 218 219 if (id.isEmpty()) 220 return; 221 222 if (m_pendingResources.contains(id)) 223 m_pendingResources.get(id)->add(obj); 224 else { 225 SVGPendingElements* set = new SVGPendingElements; 226 set->add(obj); 227 228 m_pendingResources.add(id, set); 229 } 230 } 231 232 bool SVGDocumentExtensions::isPendingResource(const AtomicString& id) const 233 { 234 if (id.isEmpty()) 235 return false; 236 237 return m_pendingResources.contains(id); 238 } 239 240 PassOwnPtr<HashSet<RefPtr<SVGStyledElement> > > SVGDocumentExtensions::removePendingResource(const AtomicString& id) 241 { 242 ASSERT(m_pendingResources.contains(id)); 243 244 OwnPtr<SVGPendingElements> set(m_pendingResources.get(id)); 245 m_pendingResources.remove(id); 246 return set.release(); 247 } 248 249 } 250 251 #endif 252