• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 
SVGDocumentExtensions(Document * document)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 
~SVGDocumentExtensions()48 SVGDocumentExtensions::~SVGDocumentExtensions()
49 {
50 }
51 
addTimeContainer(SVGSVGElement * element)52 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element)
53 {
54     m_timeContainers.add(element);
55 }
56 
removeTimeContainer(SVGSVGElement * element)57 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element)
58 {
59     m_timeContainers.remove(element);
60 }
61 
addResource(const AtomicString & id,RenderSVGResourceContainer * resource)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 
removeResource(const AtomicString & id)73 void SVGDocumentExtensions::removeResource(const AtomicString& id)
74 {
75     if (id.isEmpty())
76         return;
77 
78     m_resources.remove(id);
79 }
80 
resourceById(const AtomicString & id) const81 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const
82 {
83     if (id.isEmpty())
84         return 0;
85 
86     return m_resources.get(id);
87 }
88 
serviceOnAnimationFrame(Document & document,double monotonicAnimationStartTime)89 void SVGDocumentExtensions::serviceOnAnimationFrame(Document& document, double monotonicAnimationStartTime)
90 {
91     if (!document.svgExtensions())
92         return;
93     document.accessSVGExtensions().serviceAnimations(monotonicAnimationStartTime);
94 }
95 
serviceAnimations(double monotonicAnimationStartTime)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 
startAnimations()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 
pauseAnimations()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 
dispatchSVGLoadEventToOutermostSVGElements()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 
reportMessage(Document * document,MessageLevel level,const String & message)145 static void reportMessage(Document* document, MessageLevel level, const String& message)
146 {
147     if (document->frame())
148         document->addConsoleMessage(RenderingMessageSource, level, message);
149 }
150 
reportWarning(const String & message)151 void SVGDocumentExtensions::reportWarning(const String& message)
152 {
153     reportMessage(m_document, WarningMessageLevel, "Warning: " + message);
154 }
155 
reportError(const String & message)156 void SVGDocumentExtensions::reportError(const String& message)
157 {
158     reportMessage(m_document, ErrorMessageLevel, "Error: " + message);
159 }
160 
addPendingResource(const AtomicString & id,Element * element)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 
hasPendingResource(const AtomicString & id) const177 bool SVGDocumentExtensions::hasPendingResource(const AtomicString& id) const
178 {
179     if (id.isEmpty())
180         return false;
181 
182     return m_pendingResources.contains(id);
183 }
184 
isElementPendingResources(Element * element) const185 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 
isElementPendingResource(Element * element,const AtomicString & id) const203 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 
clearHasPendingResourcesIfPossible(Element * element)213 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element)
214 {
215     if (!isElementPendingResources(element))
216         element->clearHasPendingResources();
217 }
218 
removeElementFromPendingResources(Element * element)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 
removePendingResource(const AtomicString & id)266 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id)
267 {
268     ASSERT(m_pendingResources.contains(id));
269     return m_pendingResources.take(id);
270 }
271 
removePendingResourceForRemoval(const AtomicString & id)272 PassOwnPtr<SVGDocumentExtensions::SVGPendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id)
273 {
274     ASSERT(m_pendingResourcesForRemoval.contains(id));
275     return m_pendingResourcesForRemoval.take(id);
276 }
277 
markPendingResourcesForRemoval(const AtomicString & id)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 
removeElementFromPendingResourcesForRemoval(const AtomicString & id)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 
setOfElementsReferencingTarget(SVGElement * referencedElement) const310 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 
addElementReferencingTarget(SVGElement * referencingElement,SVGElement * referencedElement)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 
removeAllTargetReferencesForElement(SVGElement * referencingElement)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 
rebuildAllElementReferencesForTarget(SVGElement * referencedElement)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 
removeAllElementReferencesForTarget(SVGElement * referencedElement)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 
addSVGRootWithRelativeLengthDescendents(SVGSVGElement * svgRoot)388 void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot)
389 {
390     ASSERT(!m_inRelativeLengthSVGRootsInvalidation);
391     m_relativeLengthSVGRoots.add(svgRoot);
392 }
393 
removeSVGRootWithRelativeLengthDescendents(SVGSVGElement * svgRoot)394 void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot)
395 {
396     ASSERT(!m_inRelativeLengthSVGRootsInvalidation);
397     m_relativeLengthSVGRoots.remove(svgRoot);
398 }
399 
isSVGRootWithRelativeLengthDescendents(SVGSVGElement * svgRoot) const400 bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement* svgRoot) const
401 {
402     return m_relativeLengthSVGRoots.contains(svgRoot);
403 }
404 
invalidateSVGRootsWithRelativeLengthDescendents(SubtreeLayoutScope * scope)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)
registerSVGFontFaceElement(SVGFontFaceElement * element)418 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element)
419 {
420     m_svgFontFaceElements.add(element);
421 }
422 
unregisterSVGFontFaceElement(SVGFontFaceElement * element)423 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element)
424 {
425     ASSERT(m_svgFontFaceElements.contains(element));
426     m_svgFontFaceElements.remove(element);
427 }
428 
registerPendingSVGFontFaceElementsForRemoval(PassRefPtrWillBeRawPtr<SVGFontFaceElement> font)429 void SVGDocumentExtensions::registerPendingSVGFontFaceElementsForRemoval(PassRefPtrWillBeRawPtr<SVGFontFaceElement> font)
430 {
431     m_pendingSVGFontFaceElementsForRemoval.add(font);
432 }
433 
removePendingSVGFontFaceElementsForRemoval()434 void SVGDocumentExtensions::removePendingSVGFontFaceElementsForRemoval()
435 {
436     m_pendingSVGFontFaceElementsForRemoval.clear();
437 }
438 
439 #endif
440 
zoomAndPanEnabled() const441 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 
startPan(const FloatPoint & start)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 
updatePan(const FloatPoint & pos) const461 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 
rootElement(const Document & document)467 SVGSVGElement* SVGDocumentExtensions::rootElement(const Document& document)
468 {
469     Element* elem = document.documentElement();
470     return isSVGSVGElement(elem) ? toSVGSVGElement(elem) : 0;
471 }
472 
rootElement() const473 SVGSVGElement* SVGDocumentExtensions::rootElement() const
474 {
475     ASSERT(m_document);
476     return rootElement(*m_document);
477 }
478 
trace(Visitor * visitor)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