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>
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.
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.
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.
23 #include "core/svg/SVGDocumentExtensions.h"
25 #include "core/dom/Document.h"
26 #include "core/inspector/ConsoleMessage.h"
27 #include "core/layout/svg/SVGResourcesCache.h"
28 #include "core/svg/SVGSVGElement.h"
29 #include "core/svg/animation/SMILTimeContainer.h"
30 #include "wtf/TemporaryChange.h"
31 #include "wtf/text/AtomicString.h"
35 SVGDocumentExtensions::SVGDocumentExtensions(Document
* document
)
36 : m_document(document
)
38 , m_inRelativeLengthSVGRootsInvalidation(false)
43 SVGDocumentExtensions::~SVGDocumentExtensions()
47 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement
* element
)
49 m_timeContainers
.add(element
);
52 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement
* element
)
54 m_timeContainers
.remove(element
);
57 void SVGDocumentExtensions::addResource(const AtomicString
& id
, LayoutSVGResourceContainer
* resource
)
64 // Replaces resource if already present, to handle potential id changes
65 m_resources
.set(id
, resource
);
68 void SVGDocumentExtensions::removeResource(const AtomicString
& id
)
73 m_resources
.remove(id
);
76 LayoutSVGResourceContainer
* SVGDocumentExtensions::resourceById(const AtomicString
& id
) const
81 return m_resources
.get(id
);
84 void SVGDocumentExtensions::serviceOnAnimationFrame(Document
& document
, double monotonicAnimationStartTime
)
86 if (!document
.svgExtensions() || !RuntimeEnabledFeatures::smilEnabled())
88 document
.accessSVGExtensions().serviceAnimations(monotonicAnimationStartTime
);
91 void SVGDocumentExtensions::serviceAnimations(double monotonicAnimationStartTime
)
93 WillBeHeapVector
<RefPtrWillBeMember
<SVGSVGElement
>> timeContainers
;
94 copyToVector(m_timeContainers
, timeContainers
);
95 for (const auto& container
: timeContainers
)
96 container
->timeContainer()->serviceAnimations(monotonicAnimationStartTime
);
99 void SVGDocumentExtensions::startAnimations()
101 // FIXME: Eventually every "Time Container" will need a way to latch on to some global timer
102 // starting animations for a document will do this "latching"
103 // FIXME: We hold a ref pointers to prevent a shadow tree from getting removed out from underneath us.
104 // In the future we should refactor the use-element to avoid this. See https://webkit.org/b/53704
105 WillBeHeapVector
<RefPtrWillBeMember
<SVGSVGElement
>> timeContainers
;
106 copyToVector(m_timeContainers
, timeContainers
);
107 for (const auto& container
: timeContainers
) {
108 SMILTimeContainer
* timeContainer
= container
->timeContainer();
109 if (!timeContainer
->isStarted())
110 timeContainer
->begin();
114 void SVGDocumentExtensions::pauseAnimations()
116 for (SVGSVGElement
* element
: m_timeContainers
)
117 element
->pauseAnimations();
120 void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements()
122 WillBeHeapVector
<RefPtrWillBeMember
<SVGSVGElement
>> timeContainers
;
123 copyToVector(m_timeContainers
, timeContainers
);
124 for (const auto& container
: timeContainers
) {
125 SVGSVGElement
* outerSVG
= container
.get();
126 if (!outerSVG
->isOutermostSVGSVGElement())
129 // don't dispatch the load event document is not wellformed (for XML/standalone svg)
130 if (outerSVG
->document().wellFormed() || !outerSVG
->document().isSVGDocument())
131 outerSVG
->sendSVGLoadEventIfPossible();
135 static void reportMessage(Document
* document
, MessageLevel level
, const String
& message
)
137 if (document
->frame())
138 document
->addConsoleMessage(ConsoleMessage::create(RenderingMessageSource
, level
, message
));
141 void SVGDocumentExtensions::reportWarning(const String
& message
)
143 reportMessage(m_document
, WarningMessageLevel
, "Warning: " + message
);
146 void SVGDocumentExtensions::reportError(const String
& message
)
148 reportMessage(m_document
, ErrorMessageLevel
, "Error: " + message
);
151 void SVGDocumentExtensions::addPendingResource(const AtomicString
& id
, Element
* element
)
154 ASSERT(element
->inDocument());
159 WillBeHeapHashMap
<AtomicString
, OwnPtrWillBeMember
<SVGPendingElements
>>::AddResult result
= m_pendingResources
.add(id
, nullptr);
160 if (result
.isNewEntry
)
161 result
.storedValue
->value
= adoptPtrWillBeNoop(new SVGPendingElements
);
162 result
.storedValue
->value
->add(element
);
164 element
->setHasPendingResources();
167 bool SVGDocumentExtensions::hasPendingResource(const AtomicString
& id
) const
172 return m_pendingResources
.contains(id
);
175 bool SVGDocumentExtensions::isElementPendingResources(Element
* element
) const
177 // This algorithm takes time proportional to the number of pending resources and need not.
178 // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently.
182 for (const auto& entry
: m_pendingResources
) {
183 SVGPendingElements
* elements
= entry
.value
.get();
186 if (elements
->contains(element
))
192 bool SVGDocumentExtensions::isElementPendingResource(Element
* element
, const AtomicString
& id
) const
196 if (!hasPendingResource(id
))
199 return m_pendingResources
.get(id
)->contains(element
);
202 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element
* element
)
204 if (!isElementPendingResources(element
))
205 element
->clearHasPendingResources();
208 void SVGDocumentExtensions::removeElementFromPendingResources(Element
* element
)
212 // Remove the element from pending resources.
213 if (!m_pendingResources
.isEmpty() && element
->hasPendingResources()) {
214 Vector
<AtomicString
> toBeRemoved
;
215 for (const auto& entry
: m_pendingResources
) {
216 SVGPendingElements
* elements
= entry
.value
.get();
218 ASSERT(!elements
->isEmpty());
220 elements
->remove(element
);
221 if (elements
->isEmpty())
222 toBeRemoved
.append(entry
.key
);
225 clearHasPendingResourcesIfPossible(element
);
227 // We use the removePendingResource function here because it deals with set lifetime correctly.
228 for (const AtomicString
& id
: toBeRemoved
)
229 removePendingResource(id
);
232 // Remove the element from pending resources that were scheduled for removal.
233 if (!m_pendingResourcesForRemoval
.isEmpty()) {
234 Vector
<AtomicString
> toBeRemoved
;
235 for (const auto& entry
: m_pendingResourcesForRemoval
) {
236 SVGPendingElements
* elements
= entry
.value
.get();
238 ASSERT(!elements
->isEmpty());
240 elements
->remove(element
);
241 if (elements
->isEmpty())
242 toBeRemoved
.append(entry
.key
);
245 // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly.
246 for (const AtomicString
& id
: toBeRemoved
)
247 removePendingResourceForRemoval(id
);
251 PassOwnPtrWillBeRawPtr
<SVGDocumentExtensions::SVGPendingElements
> SVGDocumentExtensions::removePendingResource(const AtomicString
& id
)
253 ASSERT(m_pendingResources
.contains(id
));
254 return m_pendingResources
.take(id
);
257 PassOwnPtrWillBeRawPtr
<SVGDocumentExtensions::SVGPendingElements
> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString
& id
)
259 ASSERT(m_pendingResourcesForRemoval
.contains(id
));
260 return m_pendingResourcesForRemoval
.take(id
);
263 void SVGDocumentExtensions::markPendingResourcesForRemoval(const AtomicString
& id
)
268 ASSERT(!m_pendingResourcesForRemoval
.contains(id
));
270 OwnPtrWillBeMember
<SVGPendingElements
> existing
= m_pendingResources
.take(id
);
271 if (existing
&& !existing
->isEmpty())
272 m_pendingResourcesForRemoval
.add(id
, existing
.release());
275 Element
* SVGDocumentExtensions::removeElementFromPendingResourcesForRemoval(const AtomicString
& id
)
280 SVGPendingElements
* resourceSet
= m_pendingResourcesForRemoval
.get(id
);
281 if (!resourceSet
|| resourceSet
->isEmpty())
284 SVGPendingElements::iterator firstElement
= resourceSet
->begin();
285 Element
* element
= *firstElement
;
287 resourceSet
->remove(firstElement
);
289 if (resourceSet
->isEmpty())
290 removePendingResourceForRemoval(id
);
295 void SVGDocumentExtensions::addSVGRootWithRelativeLengthDescendents(SVGSVGElement
* svgRoot
)
297 ASSERT(!m_inRelativeLengthSVGRootsInvalidation
);
298 m_relativeLengthSVGRoots
.add(svgRoot
);
301 void SVGDocumentExtensions::removeSVGRootWithRelativeLengthDescendents(SVGSVGElement
* svgRoot
)
303 ASSERT(!m_inRelativeLengthSVGRootsInvalidation
);
304 m_relativeLengthSVGRoots
.remove(svgRoot
);
307 bool SVGDocumentExtensions::isSVGRootWithRelativeLengthDescendents(SVGSVGElement
* svgRoot
) const
309 return m_relativeLengthSVGRoots
.contains(svgRoot
);
312 void SVGDocumentExtensions::invalidateSVGRootsWithRelativeLengthDescendents(SubtreeLayoutScope
* scope
)
314 ASSERT(!m_inRelativeLengthSVGRootsInvalidation
);
316 TemporaryChange
<bool> inRelativeLengthSVGRootsChange(m_inRelativeLengthSVGRootsInvalidation
, true);
319 for (SVGSVGElement
* element
: m_relativeLengthSVGRoots
)
320 element
->invalidateRelativeLengthClients(scope
);
323 bool SVGDocumentExtensions::zoomAndPanEnabled() const
325 if (SVGSVGElement
* svg
= rootElement(*m_document
))
326 return svg
->zoomAndPanEnabled();
330 void SVGDocumentExtensions::startPan(const FloatPoint
& start
)
332 if (SVGSVGElement
* svg
= rootElement(*m_document
))
333 m_translate
= FloatPoint(start
.x() - svg
->currentTranslate().x(), start
.y() - svg
->currentTranslate().y());
336 void SVGDocumentExtensions::updatePan(const FloatPoint
& pos
) const
338 if (SVGSVGElement
* svg
= rootElement(*m_document
))
339 svg
->setCurrentTranslate(FloatPoint(pos
.x() - m_translate
.x(), pos
.y() - m_translate
.y()));
342 SVGSVGElement
* SVGDocumentExtensions::rootElement(const Document
& document
)
344 Element
* elem
= document
.documentElement();
345 return isSVGSVGElement(elem
) ? toSVGSVGElement(elem
) : 0;
348 SVGSVGElement
* SVGDocumentExtensions::rootElement() const
351 return rootElement(*m_document
);
354 DEFINE_TRACE(SVGDocumentExtensions
)
357 visitor
->trace(m_document
);
358 visitor
->trace(m_timeContainers
);
359 visitor
->trace(m_relativeLengthSVGRoots
);
360 visitor
->trace(m_pendingResources
);
361 visitor
->trace(m_pendingResourcesForRemoval
);