| 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..7afae7c4ad8eaa9ece62ee68b7c55c41dd2841b5
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/svg/SVGElementProxy.cpp
|
| @@ -0,0 +1,193 @@
|
| +// 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());
|
| + 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))
|
| + 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
|
|
|