Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: third_party/WebKit/Source/core/svg/SVGTreeScopeResources.cpp

Issue 2824823003: DOM-based SVG resource tracking (Closed)
Patch Set: Update baselines Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/svg/SVGTreeScopeResources.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/svg/SVGTreeScopeResources.h" 5 #include "core/svg/SVGTreeScopeResources.h"
6 6
7 #include "core/dom/Element.h" 7 #include "core/dom/Element.h"
8 #include "core/dom/TreeScope.h" 8 #include "core/dom/TreeScope.h"
9 #include "core/layout/svg/LayoutSVGResourceContainer.h" 9 #include "core/layout/svg/LayoutSVGResourceContainer.h"
10 #include "core/layout/svg/SVGResources.h"
10 #include "core/layout/svg/SVGResourcesCache.h" 11 #include "core/layout/svg/SVGResourcesCache.h"
11 #include "core/svg/SVGUseElement.h"
12 #include "platform/wtf/text/AtomicString.h" 12 #include "platform/wtf/text/AtomicString.h"
13 13
14 namespace blink { 14 namespace blink {
15 15
16 SVGTreeScopeResources::Resource::Resource(TreeScope& tree_scope,
17 const AtomicString& id)
18 : IdTargetObserver(tree_scope.GetIdTargetObserverRegistry(), id),
19 tree_scope_(tree_scope),
20 target_(tree_scope.getElementById(id)) {}
21
22 SVGTreeScopeResources::Resource::~Resource() = default;
23
24 DEFINE_TRACE(SVGTreeScopeResources::Resource) {
25 visitor->Trace(tree_scope_);
26 visitor->Trace(target_);
27 visitor->Trace(pending_clients_);
28 IdTargetObserver::Trace(visitor);
29 }
30
31 void SVGTreeScopeResources::Resource::AddWatch(SVGElement& element) {
32 pending_clients_.insert(&element);
33 element.SetHasPendingResources();
34 }
35
36 void SVGTreeScopeResources::Resource::RemoveWatch(SVGElement& element) {
37 pending_clients_.erase(&element);
38 }
39
40 bool SVGTreeScopeResources::Resource::IsEmpty() const {
41 LayoutSVGResourceContainer* container = ResourceContainer();
42 return (!container || !container->HasClients()) && pending_clients_.IsEmpty();
43 }
44
45 void SVGTreeScopeResources::Resource::NotifyResourceClients() {
46 HeapHashSet<Member<SVGElement>> pending_clients;
47 pending_clients.swap(pending_clients_);
48
49 for (SVGElement* client_element : pending_clients) {
50 if (LayoutObject* layout_object = client_element->GetLayoutObject())
51 SVGResourcesCache::ResourceReferenceChanged(*layout_object);
52 }
53 }
54
55 LayoutSVGResourceContainer* SVGTreeScopeResources::Resource::ResourceContainer()
56 const {
57 if (!target_)
58 return nullptr;
59 LayoutObject* layout_object = target_->GetLayoutObject();
60 if (!layout_object || !layout_object->IsSVGResourceContainer())
61 return nullptr;
62 return ToLayoutSVGResourceContainer(layout_object);
63 }
64
65 void SVGTreeScopeResources::Resource::IdTargetChanged() {
66 Element* new_target = tree_scope_->getElementById(Id());
67 if (new_target == target_)
68 return;
69 // Detach clients from the old resource, moving them to the pending list
70 // and then notify pending clients.
71 if (LayoutSVGResourceContainer* old_resource = ResourceContainer())
72 old_resource->MakeClientsPending(*this);
73 target_ = new_target;
74 NotifyResourceClients();
75 }
76
16 SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* tree_scope) 77 SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* tree_scope)
17 : tree_scope_(tree_scope) {} 78 : tree_scope_(tree_scope) {}
18 79
19 SVGTreeScopeResources::~SVGTreeScopeResources() = default; 80 SVGTreeScopeResources::~SVGTreeScopeResources() = default;
20 81
21 static LayoutSVGResourceContainer* LookupResource(TreeScope& tree_scope, 82 SVGTreeScopeResources::Resource* SVGTreeScopeResources::ResourceForId(
22 const AtomicString& id) { 83 const AtomicString& id) {
23 Element* element = tree_scope.getElementById(id); 84 if (id.IsEmpty())
24 if (!element)
25 return nullptr; 85 return nullptr;
26 LayoutObject* layout_object = element->GetLayoutObject(); 86 auto& entry = resources_.insert(id, nullptr).stored_value->value;
27 if (!layout_object || !layout_object->IsSVGResourceContainer()) 87 if (!entry)
28 return nullptr; 88 entry = new Resource(*tree_scope_, id);
29 return ToLayoutSVGResourceContainer(layout_object); 89 return entry;
30 } 90 }
31 91
32 void SVGTreeScopeResources::UpdateResource( 92 SVGTreeScopeResources::Resource* SVGTreeScopeResources::ExistingResourceForId(
33 const AtomicString& id,
34 LayoutSVGResourceContainer* resource) {
35 DCHECK(resource);
36 if (resource->IsRegistered() || id.IsEmpty())
37 return;
38 // Lookup the current resource. (Could differ from what's in the map if an
39 // element was just added/removed.)
40 LayoutSVGResourceContainer* current_resource =
41 LookupResource(*tree_scope_, id);
42 // Lookup the currently registered resource.
43 auto it = resources_.find(id);
44 if (it != resources_.end()) {
45 // Is the local map up-to-date already?
46 if (it->value == current_resource)
47 return;
48 UnregisterResource(it);
49 }
50 if (current_resource)
51 RegisterResource(id, current_resource);
52 }
53
54 void SVGTreeScopeResources::UpdateResource(
55 const AtomicString& old_id,
56 const AtomicString& new_id,
57 LayoutSVGResourceContainer* resource) {
58 RemoveResource(old_id, resource);
59 UpdateResource(new_id, resource);
60 }
61
62 void SVGTreeScopeResources::RemoveResource(
63 const AtomicString& id,
64 LayoutSVGResourceContainer* resource) {
65 DCHECK(resource);
66 if (!resource->IsRegistered() || id.IsEmpty())
67 return;
68 auto it = resources_.find(id);
69 // If this is not the currently registered resource for this id, then do
70 // nothing.
71 if (it == resources_.end() || it->value != resource)
72 return;
73 UnregisterResource(it);
74 // If the layout tree is being torn down, then don't attempt to update the
75 // map, since that layout object is likely to be stale already.
76 if (resource->DocumentBeingDestroyed())
77 return;
78 // Another resource could now be current. Perform a lookup and potentially
79 // update the map.
80 LayoutSVGResourceContainer* current_resource =
81 LookupResource(*tree_scope_, id);
82 if (!current_resource)
83 return;
84 // Since this is a removal, don't allow re-adding the resource.
85 if (current_resource == resource)
86 return;
87 RegisterResource(id, current_resource);
88 }
89
90 void SVGTreeScopeResources::RegisterResource(
91 const AtomicString& id,
92 LayoutSVGResourceContainer* resource) {
93 DCHECK(!id.IsEmpty());
94 DCHECK(resource);
95 DCHECK(!resource->IsRegistered());
96
97 resources_.Set(id, resource);
98 resource->SetRegistered(true);
99
100 NotifyPendingClients(id);
101 }
102
103 void SVGTreeScopeResources::UnregisterResource(ResourceMap::iterator it) {
104 LayoutSVGResourceContainer* resource = it->value;
105 DCHECK(resource);
106 DCHECK(resource->IsRegistered());
107
108 resource->DetachAllClients(it->key);
109
110 resource->SetRegistered(false);
111 resources_.erase(it);
112 }
113
114 LayoutSVGResourceContainer* SVGTreeScopeResources::ResourceById(
115 const AtomicString& id) const { 93 const AtomicString& id) const {
116 if (id.IsEmpty()) 94 if (id.IsEmpty())
117 return nullptr; 95 return nullptr;
118 return resources_.at(id); 96 return resources_.at(id);
119 } 97 }
120 98
121 void SVGTreeScopeResources::AddPendingResource(const AtomicString& id, 99 void SVGTreeScopeResources::RemoveUnreferencedResources() {
122 Element& element) { 100 if (resources_.IsEmpty())
123 DCHECK(element.isConnected());
124
125 if (id.IsEmpty())
126 return; 101 return;
127 auto result = pending_resources_.insert(id, nullptr); 102 // Remove resources that are no longer referenced.
128 if (result.is_new_entry) 103 Vector<AtomicString> to_be_removed;
129 result.stored_value->value = new SVGPendingElements; 104 for (const auto& entry : resources_) {
130 result.stored_value->value->insert(&element); 105 Resource* resource = entry.value.Get();
131 106 DCHECK(resource);
132 element.SetHasPendingResources(); 107 if (resource->IsEmpty()) {
108 resource->Unregister();
109 to_be_removed.push_back(entry.key);
110 }
111 }
112 resources_.RemoveAll(to_be_removed);
133 } 113 }
134 114
135 bool SVGTreeScopeResources::IsElementPendingResource( 115 void SVGTreeScopeResources::RemoveWatchesForElement(SVGElement& element) {
136 Element& element, 116 if (resources_.IsEmpty() || !element.HasPendingResources())
137 const AtomicString& id) const { 117 return;
138 if (id.IsEmpty()) 118 // Remove the element from pending resources.
139 return false; 119 Vector<AtomicString> to_be_removed;
140 const SVGPendingElements* pending_elements = pending_resources_.at(id); 120 for (const auto& entry : resources_) {
141 return pending_elements && pending_elements->Contains(&element); 121 Resource* resource = entry.value.Get();
142 } 122 DCHECK(resource);
123 resource->RemoveWatch(element);
124 if (resource->IsEmpty()) {
125 resource->Unregister();
126 to_be_removed.push_back(entry.key);
127 }
128 }
129 resources_.RemoveAll(to_be_removed);
143 130
144 void SVGTreeScopeResources::ClearHasPendingResourcesIfPossible(
145 Element& element) {
146 // This algorithm takes time proportional to the number of pending resources
147 // and need not.
148 // If performance becomes an issue we can keep a counted set of elements and
149 // answer the question efficiently.
150 for (const auto& entry : pending_resources_) {
151 SVGPendingElements* elements = entry.value.Get();
152 DCHECK(elements);
153 if (elements->Contains(&element))
154 return;
155 }
156 element.ClearHasPendingResources(); 131 element.ClearHasPendingResources();
157 } 132 }
158 133
159 void SVGTreeScopeResources::RemoveElementFromPendingResources( 134 DEFINE_TRACE(SVGTreeScopeResources) {
160 Element& element) { 135 visitor->Trace(resources_);
161 if (pending_resources_.IsEmpty() || !element.HasPendingResources()) 136 visitor->Trace(tree_scope_);
162 return;
163 // Remove the element from pending resources.
164 Vector<AtomicString> to_be_removed;
165 for (const auto& entry : pending_resources_) {
166 SVGPendingElements* elements = entry.value.Get();
167 DCHECK(elements);
168 DCHECK(!elements->IsEmpty());
169
170 elements->erase(&element);
171 if (elements->IsEmpty())
172 to_be_removed.push_back(entry.key);
173 }
174 pending_resources_.RemoveAll(to_be_removed);
175
176 ClearHasPendingResourcesIfPossible(element);
177 } 137 }
178 138
179 void SVGTreeScopeResources::NotifyPendingClients(const AtomicString& id) { 139 } // namespace blink
180 DCHECK(!id.IsEmpty());
181 SVGPendingElements* pending_elements = pending_resources_.Take(id);
182 if (!pending_elements)
183 return;
184 // Update cached resources of pending clients.
185 for (Element* client_element : *pending_elements) {
186 DCHECK(client_element->HasPendingResources());
187 ClearHasPendingResourcesIfPossible(*client_element);
188
189 LayoutObject* layout_object = client_element->GetLayoutObject();
190 if (!layout_object)
191 continue;
192 DCHECK(layout_object->IsSVG());
193
194 StyleDifference diff;
195 diff.SetNeedsFullLayout();
196 SVGResourcesCache::ClientStyleChanged(layout_object, diff,
197 layout_object->StyleRef());
198 layout_object->SetNeedsLayoutAndFullPaintInvalidation(
199 LayoutInvalidationReason::kSvgResourceInvalidated);
200 }
201 }
202
203 void SVGTreeScopeResources::NotifyResourceAvailable(const AtomicString& id) {
204 if (id.IsEmpty())
205 return;
206 // Get pending elements for this id.
207 SVGPendingElements* pending_elements = pending_resources_.Take(id);
208 if (!pending_elements)
209 return;
210 // Rebuild pending resources for each client of a pending resource that is
211 // being removed.
212 for (Element* client_element : *pending_elements) {
213 DCHECK(client_element->HasPendingResources());
214 if (!client_element->HasPendingResources())
215 continue;
216 // TODO(fs): Ideally we'd always resolve pending resources async instead of
217 // inside insertedInto and svgAttributeChanged. For now we only do it for
218 // <use> since that would stamp out DOM.
219 if (isSVGUseElement(client_element))
220 toSVGUseElement(client_element)->InvalidateShadowTree();
221 else
222 client_element->BuildPendingResource();
223
224 ClearHasPendingResourcesIfPossible(*client_element);
225 }
226 }
227
228 DEFINE_TRACE(SVGTreeScopeResources) {
229 visitor->Trace(pending_resources_);
230 visitor->Trace(tree_scope_);
231 }
232 }
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/svg/SVGTreeScopeResources.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698