| 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
|
| index 6fe3bd159c4320572a88bc727de8d545c1c24ae0..3fdade87ee72f6e812a88399ad324683fc4ee190 100644
|
| --- a/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp
|
| +++ b/third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp
|
| @@ -7,226 +7,133 @@
|
| #include "core/dom/Element.h"
|
| #include "core/dom/TreeScope.h"
|
| #include "core/layout/svg/LayoutSVGResourceContainer.h"
|
| +#include "core/layout/svg/SVGResources.h"
|
| #include "core/layout/svg/SVGResourcesCache.h"
|
| -#include "core/svg/SVGUseElement.h"
|
| #include "platform/wtf/text/AtomicString.h"
|
|
|
| namespace blink {
|
|
|
| -SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* tree_scope)
|
| - : tree_scope_(tree_scope) {}
|
| +SVGTreeScopeResources::Resource::Resource(TreeScope& tree_scope,
|
| + const AtomicString& id)
|
| + : IdTargetObserver(tree_scope.GetIdTargetObserverRegistry(), id),
|
| + tree_scope_(tree_scope),
|
| + target_(tree_scope.getElementById(id)) {}
|
|
|
| -SVGTreeScopeResources::~SVGTreeScopeResources() = default;
|
| +SVGTreeScopeResources::Resource::~Resource() = default;
|
|
|
| -static LayoutSVGResourceContainer* LookupResource(TreeScope& tree_scope,
|
| - const AtomicString& id) {
|
| - Element* element = tree_scope.getElementById(id);
|
| - if (!element)
|
| - return nullptr;
|
| - LayoutObject* layout_object = element->GetLayoutObject();
|
| - if (!layout_object || !layout_object->IsSVGResourceContainer())
|
| - return nullptr;
|
| - return ToLayoutSVGResourceContainer(layout_object);
|
| +DEFINE_TRACE(SVGTreeScopeResources::Resource) {
|
| + visitor->Trace(tree_scope_);
|
| + visitor->Trace(target_);
|
| + visitor->Trace(pending_clients_);
|
| + IdTargetObserver::Trace(visitor);
|
| }
|
|
|
| -void SVGTreeScopeResources::UpdateResource(
|
| - const AtomicString& id,
|
| - LayoutSVGResourceContainer* resource) {
|
| - DCHECK(resource);
|
| - if (resource->IsRegistered() || id.IsEmpty())
|
| - return;
|
| - // Lookup the current resource. (Could differ from what's in the map if an
|
| - // element was just added/removed.)
|
| - LayoutSVGResourceContainer* current_resource =
|
| - LookupResource(*tree_scope_, id);
|
| - // Lookup the currently registered resource.
|
| - auto it = resources_.find(id);
|
| - if (it != resources_.end()) {
|
| - // Is the local map up-to-date already?
|
| - if (it->value == current_resource)
|
| - return;
|
| - UnregisterResource(it);
|
| - }
|
| - if (current_resource)
|
| - RegisterResource(id, current_resource);
|
| +void SVGTreeScopeResources::Resource::AddWatch(SVGElement& element) {
|
| + pending_clients_.insert(&element);
|
| + element.SetHasPendingResources();
|
| }
|
|
|
| -void SVGTreeScopeResources::UpdateResource(
|
| - const AtomicString& old_id,
|
| - const AtomicString& new_id,
|
| - LayoutSVGResourceContainer* resource) {
|
| - RemoveResource(old_id, resource);
|
| - UpdateResource(new_id, resource);
|
| +void SVGTreeScopeResources::Resource::RemoveWatch(SVGElement& element) {
|
| + pending_clients_.erase(&element);
|
| }
|
|
|
| -void SVGTreeScopeResources::RemoveResource(
|
| - const AtomicString& id,
|
| - LayoutSVGResourceContainer* resource) {
|
| - DCHECK(resource);
|
| - if (!resource->IsRegistered() || id.IsEmpty())
|
| - return;
|
| - auto it = resources_.find(id);
|
| - // If this is not the currently registered resource for this id, then do
|
| - // nothing.
|
| - if (it == resources_.end() || it->value != resource)
|
| - return;
|
| - UnregisterResource(it);
|
| - // If the layout tree is being torn down, then don't attempt to update the
|
| - // map, since that layout object is likely to be stale already.
|
| - if (resource->DocumentBeingDestroyed())
|
| - return;
|
| - // Another resource could now be current. Perform a lookup and potentially
|
| - // update the map.
|
| - LayoutSVGResourceContainer* current_resource =
|
| - LookupResource(*tree_scope_, id);
|
| - if (!current_resource)
|
| - return;
|
| - // Since this is a removal, don't allow re-adding the resource.
|
| - if (current_resource == resource)
|
| - return;
|
| - RegisterResource(id, current_resource);
|
| +bool SVGTreeScopeResources::Resource::IsEmpty() const {
|
| + LayoutSVGResourceContainer* container = ResourceContainer();
|
| + return (!container || !container->HasClients()) && pending_clients_.IsEmpty();
|
| }
|
|
|
| -void SVGTreeScopeResources::RegisterResource(
|
| - const AtomicString& id,
|
| - LayoutSVGResourceContainer* resource) {
|
| - DCHECK(!id.IsEmpty());
|
| - DCHECK(resource);
|
| - DCHECK(!resource->IsRegistered());
|
| -
|
| - resources_.Set(id, resource);
|
| - resource->SetRegistered(true);
|
| +void SVGTreeScopeResources::Resource::NotifyResourceClients() {
|
| + HeapHashSet<Member<SVGElement>> pending_clients;
|
| + pending_clients.swap(pending_clients_);
|
|
|
| - NotifyPendingClients(id);
|
| + for (SVGElement* client_element : pending_clients) {
|
| + if (LayoutObject* layout_object = client_element->GetLayoutObject())
|
| + SVGResourcesCache::ResourceReferenceChanged(*layout_object);
|
| + }
|
| }
|
|
|
| -void SVGTreeScopeResources::UnregisterResource(ResourceMap::iterator it) {
|
| - LayoutSVGResourceContainer* resource = it->value;
|
| - DCHECK(resource);
|
| - DCHECK(resource->IsRegistered());
|
| -
|
| - resource->DetachAllClients(it->key);
|
| -
|
| - resource->SetRegistered(false);
|
| - resources_.erase(it);
|
| +LayoutSVGResourceContainer* SVGTreeScopeResources::Resource::ResourceContainer()
|
| + const {
|
| + if (!target_)
|
| + return nullptr;
|
| + LayoutObject* layout_object = target_->GetLayoutObject();
|
| + if (!layout_object || !layout_object->IsSVGResourceContainer())
|
| + return nullptr;
|
| + return ToLayoutSVGResourceContainer(layout_object);
|
| }
|
|
|
| -LayoutSVGResourceContainer* SVGTreeScopeResources::ResourceById(
|
| - const AtomicString& id) const {
|
| - if (id.IsEmpty())
|
| - return nullptr;
|
| - return resources_.at(id);
|
| +void SVGTreeScopeResources::Resource::IdTargetChanged() {
|
| + Element* new_target = tree_scope_->getElementById(Id());
|
| + if (new_target == target_)
|
| + return;
|
| + // Detach clients from the old resource, moving them to the pending list
|
| + // and then notify pending clients.
|
| + if (LayoutSVGResourceContainer* old_resource = ResourceContainer())
|
| + old_resource->MakeClientsPending(*this);
|
| + target_ = new_target;
|
| + NotifyResourceClients();
|
| }
|
|
|
| -void SVGTreeScopeResources::AddPendingResource(const AtomicString& id,
|
| - Element& element) {
|
| - DCHECK(element.isConnected());
|
| +SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* tree_scope)
|
| + : tree_scope_(tree_scope) {}
|
|
|
| - if (id.IsEmpty())
|
| - return;
|
| - auto result = pending_resources_.insert(id, nullptr);
|
| - if (result.is_new_entry)
|
| - result.stored_value->value = new SVGPendingElements;
|
| - result.stored_value->value->insert(&element);
|
| +SVGTreeScopeResources::~SVGTreeScopeResources() = default;
|
|
|
| - element.SetHasPendingResources();
|
| +SVGTreeScopeResources::Resource* SVGTreeScopeResources::ResourceForId(
|
| + const AtomicString& id) {
|
| + if (id.IsEmpty())
|
| + return nullptr;
|
| + auto& entry = resources_.insert(id, nullptr).stored_value->value;
|
| + if (!entry)
|
| + entry = new Resource(*tree_scope_, id);
|
| + return entry;
|
| }
|
|
|
| -bool SVGTreeScopeResources::IsElementPendingResource(
|
| - Element& element,
|
| +SVGTreeScopeResources::Resource* SVGTreeScopeResources::ExistingResourceForId(
|
| const AtomicString& id) const {
|
| if (id.IsEmpty())
|
| - return false;
|
| - const SVGPendingElements* pending_elements = pending_resources_.at(id);
|
| - return pending_elements && pending_elements->Contains(&element);
|
| -}
|
| -
|
| -void SVGTreeScopeResources::ClearHasPendingResourcesIfPossible(
|
| - Element& element) {
|
| - // 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.
|
| - for (const auto& entry : pending_resources_) {
|
| - SVGPendingElements* elements = entry.value.Get();
|
| - DCHECK(elements);
|
| - if (elements->Contains(&element))
|
| - return;
|
| - }
|
| - element.ClearHasPendingResources();
|
| + return nullptr;
|
| + return resources_.at(id);
|
| }
|
|
|
| -void SVGTreeScopeResources::RemoveElementFromPendingResources(
|
| - Element& element) {
|
| - if (pending_resources_.IsEmpty() || !element.HasPendingResources())
|
| +void SVGTreeScopeResources::RemoveUnreferencedResources() {
|
| + if (resources_.IsEmpty())
|
| return;
|
| - // Remove the element from pending resources.
|
| + // Remove resources that are no longer referenced.
|
| Vector<AtomicString> to_be_removed;
|
| - for (const auto& entry : pending_resources_) {
|
| - SVGPendingElements* elements = entry.value.Get();
|
| - DCHECK(elements);
|
| - DCHECK(!elements->IsEmpty());
|
| -
|
| - elements->erase(&element);
|
| - if (elements->IsEmpty())
|
| + for (const auto& entry : resources_) {
|
| + Resource* resource = entry.value.Get();
|
| + DCHECK(resource);
|
| + if (resource->IsEmpty()) {
|
| + resource->Unregister();
|
| to_be_removed.push_back(entry.key);
|
| + }
|
| }
|
| - pending_resources_.RemoveAll(to_be_removed);
|
| -
|
| - ClearHasPendingResourcesIfPossible(element);
|
| + resources_.RemoveAll(to_be_removed);
|
| }
|
|
|
| -void SVGTreeScopeResources::NotifyPendingClients(const AtomicString& id) {
|
| - DCHECK(!id.IsEmpty());
|
| - SVGPendingElements* pending_elements = pending_resources_.Take(id);
|
| - if (!pending_elements)
|
| +void SVGTreeScopeResources::RemoveWatchesForElement(SVGElement& element) {
|
| + if (resources_.IsEmpty() || !element.HasPendingResources())
|
| return;
|
| - // Update cached resources of pending clients.
|
| - for (Element* client_element : *pending_elements) {
|
| - DCHECK(client_element->HasPendingResources());
|
| - ClearHasPendingResourcesIfPossible(*client_element);
|
| -
|
| - LayoutObject* layout_object = client_element->GetLayoutObject();
|
| - if (!layout_object)
|
| - continue;
|
| - DCHECK(layout_object->IsSVG());
|
| -
|
| - StyleDifference diff;
|
| - diff.SetNeedsFullLayout();
|
| - SVGResourcesCache::ClientStyleChanged(layout_object, diff,
|
| - layout_object->StyleRef());
|
| - layout_object->SetNeedsLayoutAndFullPaintInvalidation(
|
| - LayoutInvalidationReason::kSvgResourceInvalidated);
|
| + // Remove the element from pending resources.
|
| + Vector<AtomicString> to_be_removed;
|
| + for (const auto& entry : resources_) {
|
| + Resource* resource = entry.value.Get();
|
| + DCHECK(resource);
|
| + resource->RemoveWatch(element);
|
| + if (resource->IsEmpty()) {
|
| + resource->Unregister();
|
| + to_be_removed.push_back(entry.key);
|
| + }
|
| }
|
| -}
|
| + resources_.RemoveAll(to_be_removed);
|
|
|
| -void SVGTreeScopeResources::NotifyResourceAvailable(const AtomicString& id) {
|
| - if (id.IsEmpty())
|
| - return;
|
| - // Get pending elements for this id.
|
| - SVGPendingElements* pending_elements = pending_resources_.Take(id);
|
| - if (!pending_elements)
|
| - return;
|
| - // Rebuild pending resources for each client of a pending resource that is
|
| - // being removed.
|
| - for (Element* client_element : *pending_elements) {
|
| - DCHECK(client_element->HasPendingResources());
|
| - if (!client_element->HasPendingResources())
|
| - continue;
|
| - // TODO(fs): Ideally we'd always resolve pending resources async instead of
|
| - // inside insertedInto and svgAttributeChanged. For now we only do it for
|
| - // <use> since that would stamp out DOM.
|
| - if (isSVGUseElement(client_element))
|
| - toSVGUseElement(client_element)->InvalidateShadowTree();
|
| - else
|
| - client_element->BuildPendingResource();
|
| -
|
| - ClearHasPendingResourcesIfPossible(*client_element);
|
| - }
|
| + element.ClearHasPendingResources();
|
| }
|
|
|
| DEFINE_TRACE(SVGTreeScopeResources) {
|
| - visitor->Trace(pending_resources_);
|
| + visitor->Trace(resources_);
|
| visitor->Trace(tree_scope_);
|
| }
|
| -}
|
| +
|
| +} // namespace blink
|
|
|