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