| 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 | 
|  |