Add WTF::move()
[WebKit-https.git] / Source / WebCore / svg / SVGDocumentExtensions.cpp
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
41 namespace WebCore {
42
43 SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
44     : m_document(document)
45     , m_resourcesCache(std::make_unique<SVGResourcesCache>())
46 {
47 }
48
49 SVGDocumentExtensions::~SVGDocumentExtensions()
50 {
51 }
52
53 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element)
54 {
55     m_timeContainers.add(element);
56 }
57
58 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element)
59 {
60     m_timeContainers.remove(element);
61 }
62
63 void 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
74 void SVGDocumentExtensions::removeResource(const AtomicString& id)
75 {
76     if (id.isEmpty())
77         return;
78
79     m_resources.remove(id);
80 }
81
82 RenderSVGResourceContainer* SVGDocumentExtensions::resourceById(const AtomicString& id) const
83 {
84     if (id.isEmpty())
85         return 0;
86
87     return m_resources.get(id);
88 }
89
90 void 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
103 void SVGDocumentExtensions::pauseAnimations()
104 {
105     auto end = m_timeContainers.end();
106     for (auto it = m_timeContainers.begin(); it != end; ++it)
107         (*it)->pauseAnimations();
108 }
109
110 void SVGDocumentExtensions::unpauseAnimations()
111 {
112     auto end = m_timeContainers.end();
113     for (auto it = m_timeContainers.begin(); it != end; ++it)
114         (*it)->unpauseAnimations();
115 }
116
117 void 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
131 static void reportMessage(Document* document, MessageLevel level, const String& message)
132 {
133     if (document->frame())
134         document->addConsoleMessage(MessageSource::Rendering, level, message);
135 }
136
137 void SVGDocumentExtensions::reportWarning(const String& message)
138 {
139     reportMessage(m_document, MessageLevel::Warning, "Warning: " + message);
140 }
141
142 void SVGDocumentExtensions::reportError(const String& message)
143 {
144     reportMessage(m_document, MessageLevel::Error, "Error: " + message);
145 }
146
147 void 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
162 bool SVGDocumentExtensions::isIdOfPendingResource(const AtomicString& id) const
163 {
164     if (id.isEmpty())
165         return false;
166
167     return m_pendingResources.contains(id);
168 }
169
170 bool 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
186 bool 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
196 void SVGDocumentExtensions::clearHasPendingResourcesIfPossible(Element* element)
197 {
198     if (!isElementWithPendingResources(element))
199         element->clearHasPendingResources();
200 }
201
202 void 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
249 std::unique_ptr<SVGDocumentExtensions::PendingElements> SVGDocumentExtensions::removePendingResource(const AtomicString& id)
250 {
251     ASSERT(m_pendingResources.contains(id));
252     return m_pendingResources.take(id);
253 }
254
255 std::unique_ptr<SVGDocumentExtensions::PendingElements> SVGDocumentExtensions::removePendingResourceForRemoval(const AtomicString& id)
256 {
257     ASSERT(m_pendingResourcesForRemoval.contains(id));
258     return m_pendingResourcesForRemoval.take(id);
259 }
260
261 void 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
273 Element* 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
293 HashSet<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
302 void 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
317 void 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
335 void SVGDocumentExtensions::rebuildElements()
336 {
337     Vector<SVGElement*> shadowRebuildElements = WTF::move(m_rebuildElements);
338     for (auto* element : shadowRebuildElements)
339         element->svgAttributeChanged(XLinkNames::hrefAttr);
340 }
341
342 void 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
362 void 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
379 void SVGDocumentExtensions::removeAllElementReferencesForTarget(SVGElement* referencedElement)
380 {
381     m_elementDependencies.remove(referencedElement);
382 }
383
384 #if ENABLE(SVG_FONTS)
385 void SVGDocumentExtensions::registerSVGFontFaceElement(SVGFontFaceElement* element)
386 {
387     m_svgFontFaceElements.add(element);
388 }
389
390 void SVGDocumentExtensions::unregisterSVGFontFaceElement(SVGFontFaceElement* element)
391 {
392     ASSERT(m_svgFontFaceElements.contains(element));
393     m_svgFontFaceElements.remove(element);
394 }
395 #endif
396
397 }