Index: third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp |
diff --git a/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..64ff103328242e0dca02052e5e4783e9bcd6d585 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp |
@@ -0,0 +1,194 @@ |
+#include "core/svg/SVGTreeScopeResources.h" |
+ |
+#include "core/dom/Element.h" |
+#include "core/dom/TreeScope.h" |
+#include "wtf/text/AtomicString.h" |
+ |
+namespace blink { |
+ |
+SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* treeScope) |
+ : m_treeScope(treeScope) |
+{ |
+ // Whenever an object of SVGTreeScopeResources is created, to keep the code behave as before, |
+ // the document should also have an instance of SVGDocumentExtensions created. |
+ // Thus below lines are added |
+ if (!treeScope->document().svgExtensions()) |
+ treeScope->document().accessSVGExtensions(); |
+} |
+SVGTreeScopeResources::~SVGTreeScopeResources() |
+{ |
+} |
+void SVGTreeScopeResources::addResource(const AtomicString& id, LayoutSVGResourceContainer* resource) |
+{ |
+ DCHECK(resource); |
+ |
+ if (id.isEmpty()) |
+ return; |
+ // Replaces resource if already present, to handle potential id changes |
+ m_resources.set(id, resource); |
+} |
+ |
+void SVGTreeScopeResources::removeResource(const AtomicString& id) |
+{ |
+ if (id.isEmpty()) |
+ return; |
+ |
+ m_resources.remove(id); |
+} |
+ |
+LayoutSVGResourceContainer* SVGTreeScopeResources::resourceById(const AtomicString& id) const |
+{ |
+ if (id.isEmpty()) |
+ return nullptr; |
+ return m_resources.get(id); |
+} |
+void SVGTreeScopeResources::addPendingResource(const AtomicString& id, blink::Element* element) |
+{ |
+ DCHECK(element); |
+ DCHECK(element->inShadowIncludingDocument()); |
+ |
+ if (id.isEmpty()) |
+ return; |
+ |
+ HeapHashMap<AtomicString, Member<SVGPendingElements>>::AddResult result = m_pendingResources.add(id, nullptr); |
+ if (result.isNewEntry) |
+ result.storedValue->value = new SVGPendingElements; |
+ result.storedValue->value->add(element); |
+ |
+ element->setHasPendingResources(); |
+} |
+ |
+bool SVGTreeScopeResources::hasPendingResource(const AtomicString& id) const |
+{ |
+ if (id.isEmpty()) |
+ return false; |
+ |
+ return m_pendingResources.contains(id); |
+} |
+ |
+bool SVGTreeScopeResources::isElementPendingResources(Element* element) const |
+{ |
+ // This algorithm takes time proportional to the number of pending resources and need not. |
+ // If performance becomes an issue we can keep a counted set of elements and answer the question efficiently. |
+ |
+ DCHECK(element); |
+ |
+ for (const auto& entry : m_pendingResources) { |
+ SVGPendingElements* elements = entry.value.get(); |
+ DCHECK(elements); |
+ |
+ if (elements->contains(element)) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool SVGTreeScopeResources::isElementPendingResource(Element* element, const AtomicString& id) const |
+{ |
+ DCHECK(element); |
+ |
+ if (!hasPendingResource(id)) |
+ return false; |
+ |
+ return m_pendingResources.get(id)->contains(element); |
+} |
+ |
+void SVGTreeScopeResources::clearHasPendingResourcesIfPossible(Element* element) |
+{ |
+ if (!isElementPendingResources(element)) |
+ element->clearHasPendingResources(); |
+} |
+ |
+void SVGTreeScopeResources::removeElementFromPendingResources(Element* element) |
+{ |
+ DCHECK(element); |
+ |
+ // Remove the element from pending resources. |
+ if (!m_pendingResources.isEmpty() && element->hasPendingResources()) { |
+ Vector<AtomicString> toBeRemoved; |
+ for (const auto& entry : m_pendingResources) { |
+ SVGPendingElements* elements = entry.value.get(); |
+ DCHECK(elements); |
+ DCHECK(!elements->isEmpty()); |
+ |
+ elements->remove(element); |
+ if (elements->isEmpty()) |
+ toBeRemoved.append(entry.key); |
+ } |
+ |
+ clearHasPendingResourcesIfPossible(element); |
+ |
+ // We use the removePendingResource function here because it deals with set lifetime correctly. |
+ for (const AtomicString& id : toBeRemoved) |
+ removePendingResource(id); |
+ } |
+ |
+ // Remove the element from pending resources that were scheduled for removal. |
+ if (!m_pendingResourcesForRemoval.isEmpty()) { |
+ Vector<AtomicString> toBeRemoved; |
+ for (const auto& entry : m_pendingResourcesForRemoval) { |
+ SVGPendingElements* elements = entry.value.get(); |
+ DCHECK(elements); |
+ DCHECK(!elements->isEmpty()); |
+ |
+ elements->remove(element); |
+ if (elements->isEmpty()) |
+ toBeRemoved.append(entry.key); |
+ } |
+ |
+ // We use the removePendingResourceForRemoval function here because it deals with set lifetime correctly. |
+ for (const AtomicString& id : toBeRemoved) |
+ removePendingResourceForRemoval(id); |
+ } |
+} |
+ |
+SVGTreeScopeResources::SVGPendingElements* SVGTreeScopeResources::removePendingResource(const AtomicString& id) |
+{ |
+ DCHECK(m_pendingResources.contains(id)); |
+ return m_pendingResources.take(id); |
+} |
+ |
+SVGTreeScopeResources::SVGPendingElements* SVGTreeScopeResources::removePendingResourceForRemoval(const AtomicString& id) |
+{ |
+ DCHECK(m_pendingResourcesForRemoval.contains(id)); |
+ return m_pendingResourcesForRemoval.take(id); |
+} |
+ |
+void SVGTreeScopeResources::markPendingResourcesForRemoval(const AtomicString& id) |
+{ |
+ if (id.isEmpty()) |
+ return; |
+ |
+ DCHECK(!m_pendingResourcesForRemoval.contains(id)); |
+ |
+ Member<SVGPendingElements> existing = m_pendingResources.take(id); |
+ if (existing && !existing->isEmpty()) |
+ m_pendingResourcesForRemoval.add(id, existing.release()); |
+} |
+ |
+Element* SVGTreeScopeResources::removeElementFromPendingResourcesForRemoval(const AtomicString& id) |
+{ |
+ if (id.isEmpty()) |
+ return nullptr; |
+ |
+ SVGPendingElements* resourceSet = m_pendingResourcesForRemoval.get(id); |
+ if (!resourceSet || resourceSet->isEmpty()) |
+ return nullptr; |
+ |
+ SVGPendingElements::iterator firstElement = resourceSet->begin(); |
+ Element* element = *firstElement; |
+ |
+ resourceSet->remove(firstElement); |
+ |
+ if (resourceSet->isEmpty()) |
+ removePendingResourceForRemoval(id); |
+ |
+ return element; |
+} |
+DEFINE_TRACE(SVGTreeScopeResources) |
+{ |
+ visitor->trace(m_treeScope); |
+ visitor->trace(m_pendingResources); |
+ visitor->trace(m_pendingResourcesForRemoval); |
+} |
+} |