Chromium Code Reviews| Index: third_party/WebKit/Source/core/svg/SVGElementProxy.cpp |
| diff --git a/third_party/WebKit/Source/core/svg/SVGElementProxy.cpp b/third_party/WebKit/Source/core/svg/SVGElementProxy.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e222f2cd2b8fc878ed8bd3595840c93d978596e7 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/svg/SVGElementProxy.cpp |
| @@ -0,0 +1,195 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "core/svg/SVGElementProxy.h" |
| + |
| +#include "core/dom/IdTargetObserver.h" |
| +#include "core/fetch/FetchInitiatorTypeNames.h" |
| +#include "core/fetch/FetchRequest.h" |
| +#include "core/fetch/ResourceFetcher.h" |
| +#include "core/svg/SVGElement.h" |
| +#include "core/svg/SVGFilterElement.h" |
| +#include "core/svg/SVGResourceClient.h" |
| + |
| +namespace blink { |
| + |
| +class SVGElementProxy::IdObserver : public IdTargetObserver { |
| + public: |
| + IdObserver(TreeScope& treeScope, SVGElementProxy& proxy) |
| + : IdTargetObserver(treeScope.idTargetObserverRegistry(), proxy.id()), |
| + m_treeScope(&treeScope) {} |
| + |
| + void addClient(SVGResourceClient* client) { m_clients.add(client); } |
| + bool removeClient(SVGResourceClient* client) { |
| + return m_clients.remove(client); |
| + } |
| + |
| + TreeScope* treeScope() const { return m_treeScope; } |
| + void transferClients(IdObserver& observer) { |
| + for (const auto& client : m_clients) |
| + observer.m_clients.add(client.key, client.value); |
| + m_clients.clear(); |
| + } |
| + |
| + DEFINE_INLINE_VIRTUAL_TRACE() { |
| + visitor->trace(m_clients); |
| + visitor->trace(m_treeScope); |
| + IdTargetObserver::trace(visitor); |
| + } |
| + |
| + void contentChanged() { |
| + DCHECK(lifecycle().state() <= DocumentLifecycle::CompositingClean || |
| + lifecycle().state() >= DocumentLifecycle::PaintClean); |
| + HeapVector<Member<SVGResourceClient>> clients; |
| + copyToVector(m_clients, clients); |
| + for (SVGResourceClient* client : clients) |
| + client->resourceContentChanged(); |
| + } |
| + |
| + private: |
| + const DocumentLifecycle& lifecycle() const { |
| + return m_treeScope->document().lifecycle(); |
| + } |
| + void idTargetChanged() override { |
| + DCHECK(lifecycle().stateAllowsTreeMutations()); |
| + DCHECK(lifecycle().state() <= DocumentLifecycle::CompositingClean || |
|
esprehn
2016/11/04 23:58:07
I'm not sure this holds true, can't something like
fs
2016/11/07 12:59:20
Since both of these are not strict less/greater th
|
| + lifecycle().state() >= DocumentLifecycle::PaintClean); |
| + HeapVector<Member<SVGResourceClient>> clients; |
| + copyToVector(m_clients, clients); |
| + for (SVGResourceClient* client : clients) |
| + client->resourceElementChanged(); |
| + } |
| + HeapHashCountedSet<Member<SVGResourceClient>> m_clients; |
| + Member<TreeScope> m_treeScope; |
| +}; |
| + |
| +SVGElementProxy::SVGElementProxy(const AtomicString& id) |
| + : m_id(id), m_isLocal(true) {} |
| + |
| +SVGElementProxy::SVGElementProxy(const String& url, const AtomicString& id) |
| + : m_id(id), m_url(url), m_isLocal(false) {} |
| + |
| +SVGElementProxy::~SVGElementProxy() {} |
| + |
| +void SVGElementProxy::addClient(SVGResourceClient* client) { |
| + // An empty id will never be a valid element reference. |
| + if (m_id.isEmpty()) |
| + return; |
| + if (m_document) { |
| + m_document->addClient(client); |
| + return; |
| + } |
| + TreeScope* clientScope = client->treeScope(); |
| + TreeScope* lookupScope = |
| + clientScope ? treeScopeForLookup(*clientScope) : nullptr; |
| + if (!lookupScope) |
| + return; |
| + // Ensure sure we have an observer registered for this tree scope. |
| + auto& scopeObserver = |
| + m_observers.add(lookupScope, nullptr).storedValue->value; |
| + if (!scopeObserver) |
| + scopeObserver = new IdObserver(*lookupScope, *this); |
| + |
| + auto& observer = m_clients.add(client, nullptr).storedValue->value; |
| + if (!observer) |
| + observer = scopeObserver; |
| + |
| + DCHECK(observer && scopeObserver); |
| + |
| + // If the client moved to a different scope, we need to unregister the old |
| + // observer and transfer any clients from it before replacing it. Thus any |
| + // clients that remain to be removed will be transferred to the new observer, |
| + // and hence removed from it instead. |
| + if (observer != scopeObserver) { |
| + observer->unregister(); |
| + observer->transferClients(*scopeObserver); |
| + observer = scopeObserver; |
| + } |
| + observer->addClient(client); |
| +} |
| + |
| +void SVGElementProxy::removeClient(SVGResourceClient* client) { |
| + // An empty id will never be a valid element reference. |
| + if (m_id.isEmpty()) |
| + return; |
| + if (m_document) { |
| + m_document->removeClient(client); |
| + return; |
| + } |
| + auto entry = m_clients.find(client); |
| + if (entry == m_clients.end()) |
| + return; |
| + IdObserver* observer = entry->value; |
| + DCHECK(observer); |
| + // If the client is not the last client in the scope, then no further action |
| + // needs to be taken. |
| + if (!observer->removeClient(client)) |
| + return; |
| + // Unregister and drop the scope association, then drop the client. |
| + observer->unregister(); |
| + m_observers.remove(observer->treeScope()); |
| + m_clients.remove(entry); |
| +} |
| + |
| +void SVGElementProxy::resolve(Document& document) { |
| + if (m_isLocal || m_id.isEmpty() || m_url.isEmpty()) |
| + return; |
| + FetchRequest request(ResourceRequest(m_url), FetchInitiatorTypeNames::css); |
| + m_document = DocumentResource::fetchSVGDocument(request, document.fetcher()); |
| + m_url = String(); |
| +} |
| + |
| +TreeScope* SVGElementProxy::treeScopeForLookup(TreeScope& treeScope) const { |
| + if (m_isLocal) |
| + return &treeScope; |
| + if (!m_document) |
| + return nullptr; |
| + return m_document->document(); |
| +} |
| + |
| +SVGElement* SVGElementProxy::findElement(TreeScope& treeScope) { |
| + // An empty id will never be a valid element reference. |
| + if (m_id.isEmpty()) |
| + return nullptr; |
| + TreeScope* lookupScope = treeScopeForLookup(treeScope); |
| + if (!lookupScope) |
| + return nullptr; |
| + if (Element* targetElement = lookupScope->getElementById(m_id)) { |
| + if (isSVGFilterElement(*targetElement)) { |
| + toSVGFilterElement(*targetElement).elementProxySet().add(*this); |
| + return toSVGElement(targetElement); |
| + } |
| + } |
| + return nullptr; |
| +} |
| + |
| +void SVGElementProxy::contentChanged(TreeScope& treeScope) { |
| + if (auto observer = m_observers.get(&treeScope)) |
|
esprehn
2016/11/04 23:58:07
We usually do auto* or auto& to make it clear you'
fs
2016/11/07 12:59:20
Fixed.
|
| + observer->contentChanged(); |
| +} |
| + |
| +DEFINE_TRACE(SVGElementProxy) { |
| + visitor->trace(m_clients); |
| + visitor->trace(m_observers); |
| + visitor->trace(m_document); |
| +} |
| + |
| +void SVGElementProxySet::add(SVGElementProxy& elementProxy) { |
| + m_elementProxies.add(&elementProxy); |
| +} |
| + |
| +bool SVGElementProxySet::isEmpty() const { |
| + return m_elementProxies.isEmpty(); |
| +} |
| + |
| +void SVGElementProxySet::notifyContentChanged(TreeScope& treeScope) { |
| + for (SVGElementProxy* proxy : m_elementProxies) |
| + proxy->contentChanged(treeScope); |
| +} |
| + |
| +DEFINE_TRACE(SVGElementProxySet) { |
| + visitor->trace(m_elementProxies); |
| +} |
| + |
| +} // namespace blink |