OLD | NEW |
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/SVGResourcesCache.h" | 10 #include "core/layout/svg/SVGResourcesCache.h" |
11 #include "core/svg/SVGUseElement.h" | 11 #include "core/svg/SVGUseElement.h" |
12 #include "wtf/text/AtomicString.h" | 12 #include "wtf/text/AtomicString.h" |
13 | 13 |
14 namespace blink { | 14 namespace blink { |
15 | 15 |
16 SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* treeScope) { | 16 SVGTreeScopeResources::SVGTreeScopeResources(TreeScope* treeScope) |
| 17 : m_treeScope(treeScope) { |
17 // Whenever an object of SVGTreeScopeResources is created, to keep the code | 18 // Whenever an object of SVGTreeScopeResources is created, to keep the code |
18 // behave as before, | 19 // behave as before, |
19 // the document should also have an instance of SVGDocumentExtensions created. | 20 // the document should also have an instance of SVGDocumentExtensions created. |
20 // Thus below line is added. | 21 // Thus below line is added. |
21 treeScope->document().accessSVGExtensions(); | 22 treeScope->document().accessSVGExtensions(); |
22 } | 23 } |
23 | 24 |
24 SVGTreeScopeResources::~SVGTreeScopeResources() = default; | 25 SVGTreeScopeResources::~SVGTreeScopeResources() = default; |
25 | 26 |
| 27 static LayoutSVGResourceContainer* lookupResource(TreeScope& treeScope, |
| 28 const AtomicString& id) { |
| 29 Element* element = treeScope.getElementById(id); |
| 30 if (!element) |
| 31 return nullptr; |
| 32 LayoutObject* layoutObject = element->layoutObject(); |
| 33 if (!layoutObject || !layoutObject->isSVGResourceContainer()) |
| 34 return nullptr; |
| 35 return toLayoutSVGResourceContainer(layoutObject); |
| 36 } |
| 37 |
26 void SVGTreeScopeResources::updateResource( | 38 void SVGTreeScopeResources::updateResource( |
27 const AtomicString& id, | 39 const AtomicString& id, |
28 LayoutSVGResourceContainer* resource) { | 40 LayoutSVGResourceContainer* resource) { |
29 DCHECK(resource); | 41 DCHECK(resource); |
30 if (id.isEmpty()) | 42 if (resource->isRegistered() || id.isEmpty()) |
31 return; | 43 return; |
32 // Replaces resource if already present, to handle potential id changes. | 44 // Lookup the current resource. (Could differ from what's in the map if an |
33 m_resources.set(id, resource); | 45 // element was just added/removed.) |
34 | 46 LayoutSVGResourceContainer* currentResource = |
35 SVGPendingElements* pendingElements = m_pendingResources.take(id); | 47 lookupResource(*m_treeScope, id); |
36 if (!pendingElements) | 48 // Lookup the currently registered resource. |
37 return; | 49 auto it = m_resources.find(id); |
38 // Update cached resources of pending clients. | 50 if (it != m_resources.end()) { |
39 for (Element* clientElement : *pendingElements) { | 51 // Is the local map up-to-date already? |
40 DCHECK(clientElement->hasPendingResources()); | 52 if (it->value == currentResource) |
41 clearHasPendingResourcesIfPossible(*clientElement); | 53 return; |
42 | 54 unregisterResource(it); |
43 LayoutObject* layoutObject = clientElement->layoutObject(); | |
44 if (!layoutObject) | |
45 continue; | |
46 DCHECK(layoutObject->isSVG()); | |
47 | |
48 StyleDifference diff; | |
49 diff.setNeedsFullLayout(); | |
50 SVGResourcesCache::clientStyleChanged(layoutObject, diff, | |
51 layoutObject->styleRef()); | |
52 layoutObject->setNeedsLayoutAndFullPaintInvalidation( | |
53 LayoutInvalidationReason::SvgResourceInvalidated); | |
54 } | 55 } |
| 56 if (currentResource) |
| 57 registerResource(id, currentResource); |
55 } | 58 } |
56 | 59 |
57 void SVGTreeScopeResources::removeResource(const AtomicString& id) { | 60 void SVGTreeScopeResources::updateResource( |
58 if (id.isEmpty()) | 61 const AtomicString& oldId, |
| 62 const AtomicString& newId, |
| 63 LayoutSVGResourceContainer* resource) { |
| 64 removeResource(oldId, resource); |
| 65 updateResource(newId, resource); |
| 66 } |
| 67 |
| 68 void SVGTreeScopeResources::removeResource( |
| 69 const AtomicString& id, |
| 70 LayoutSVGResourceContainer* resource) { |
| 71 DCHECK(resource); |
| 72 if (!resource->isRegistered() || id.isEmpty()) |
59 return; | 73 return; |
60 m_resources.erase(id); | 74 auto it = m_resources.find(id); |
| 75 // If this is not the currently registered resource for this id, then do |
| 76 // nothing. |
| 77 if (it == m_resources.end() || it->value != resource) |
| 78 return; |
| 79 unregisterResource(it); |
| 80 // If the layout tree is being torn down, then don't attempt to update the |
| 81 // map, since that layout object is likely to be stale already. |
| 82 if (resource->documentBeingDestroyed()) |
| 83 return; |
| 84 // Another resource could now be current. Perform a lookup and potentially |
| 85 // update the map. |
| 86 LayoutSVGResourceContainer* currentResource = |
| 87 lookupResource(*m_treeScope, id); |
| 88 if (!currentResource) |
| 89 return; |
| 90 // Since this is a removal, don't allow re-adding the resource. |
| 91 if (currentResource == resource) |
| 92 return; |
| 93 registerResource(id, currentResource); |
| 94 } |
| 95 |
| 96 void SVGTreeScopeResources::registerResource( |
| 97 const AtomicString& id, |
| 98 LayoutSVGResourceContainer* resource) { |
| 99 DCHECK(!id.isEmpty()); |
| 100 DCHECK(resource); |
| 101 DCHECK(!resource->isRegistered()); |
| 102 |
| 103 m_resources.set(id, resource); |
| 104 resource->setRegistered(true); |
| 105 |
| 106 notifyPendingClients(id); |
| 107 } |
| 108 |
| 109 void SVGTreeScopeResources::unregisterResource(ResourceMap::iterator it) { |
| 110 LayoutSVGResourceContainer* resource = it->value; |
| 111 DCHECK(resource); |
| 112 DCHECK(resource->isRegistered()); |
| 113 |
| 114 resource->detachAllClients(it->key); |
| 115 |
| 116 resource->setRegistered(false); |
| 117 m_resources.remove(it); |
61 } | 118 } |
62 | 119 |
63 LayoutSVGResourceContainer* SVGTreeScopeResources::resourceById( | 120 LayoutSVGResourceContainer* SVGTreeScopeResources::resourceById( |
64 const AtomicString& id) const { | 121 const AtomicString& id) const { |
65 if (id.isEmpty()) | 122 if (id.isEmpty()) |
66 return nullptr; | 123 return nullptr; |
67 return m_resources.at(id); | 124 return m_resources.at(id); |
68 } | 125 } |
69 | 126 |
70 void SVGTreeScopeResources::addPendingResource(const AtomicString& id, | 127 void SVGTreeScopeResources::addPendingResource(const AtomicString& id, |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
118 | 175 |
119 elements->erase(&element); | 176 elements->erase(&element); |
120 if (elements->isEmpty()) | 177 if (elements->isEmpty()) |
121 toBeRemoved.push_back(entry.key); | 178 toBeRemoved.push_back(entry.key); |
122 } | 179 } |
123 m_pendingResources.removeAll(toBeRemoved); | 180 m_pendingResources.removeAll(toBeRemoved); |
124 | 181 |
125 clearHasPendingResourcesIfPossible(element); | 182 clearHasPendingResourcesIfPossible(element); |
126 } | 183 } |
127 | 184 |
| 185 void SVGTreeScopeResources::notifyPendingClients(const AtomicString& id) { |
| 186 DCHECK(!id.isEmpty()); |
| 187 SVGPendingElements* pendingElements = m_pendingResources.take(id); |
| 188 if (!pendingElements) |
| 189 return; |
| 190 // Update cached resources of pending clients. |
| 191 for (Element* clientElement : *pendingElements) { |
| 192 DCHECK(clientElement->hasPendingResources()); |
| 193 clearHasPendingResourcesIfPossible(*clientElement); |
| 194 |
| 195 LayoutObject* layoutObject = clientElement->layoutObject(); |
| 196 if (!layoutObject) |
| 197 continue; |
| 198 DCHECK(layoutObject->isSVG()); |
| 199 |
| 200 StyleDifference diff; |
| 201 diff.setNeedsFullLayout(); |
| 202 SVGResourcesCache::clientStyleChanged(layoutObject, diff, |
| 203 layoutObject->styleRef()); |
| 204 layoutObject->setNeedsLayoutAndFullPaintInvalidation( |
| 205 LayoutInvalidationReason::SvgResourceInvalidated); |
| 206 } |
| 207 } |
| 208 |
128 void SVGTreeScopeResources::notifyResourceAvailable(const AtomicString& id) { | 209 void SVGTreeScopeResources::notifyResourceAvailable(const AtomicString& id) { |
129 if (id.isEmpty()) | 210 if (id.isEmpty()) |
130 return; | 211 return; |
131 // Get pending elements for this id. | 212 // Get pending elements for this id. |
132 SVGPendingElements* pendingElements = m_pendingResources.take(id); | 213 SVGPendingElements* pendingElements = m_pendingResources.take(id); |
133 if (!pendingElements) | 214 if (!pendingElements) |
134 return; | 215 return; |
135 // Rebuild pending resources for each client of a pending resource that is | 216 // Rebuild pending resources for each client of a pending resource that is |
136 // being removed. | 217 // being removed. |
137 for (Element* clientElement : *pendingElements) { | 218 for (Element* clientElement : *pendingElements) { |
138 DCHECK(clientElement->hasPendingResources()); | 219 DCHECK(clientElement->hasPendingResources()); |
139 if (!clientElement->hasPendingResources()) | 220 if (!clientElement->hasPendingResources()) |
140 continue; | 221 continue; |
141 // TODO(fs): Ideally we'd always resolve pending resources async instead of | 222 // TODO(fs): Ideally we'd always resolve pending resources async instead of |
142 // inside insertedInto and svgAttributeChanged. For now we only do it for | 223 // inside insertedInto and svgAttributeChanged. For now we only do it for |
143 // <use> since that would stamp out DOM. | 224 // <use> since that would stamp out DOM. |
144 if (isSVGUseElement(clientElement)) | 225 if (isSVGUseElement(clientElement)) |
145 toSVGUseElement(clientElement)->invalidateShadowTree(); | 226 toSVGUseElement(clientElement)->invalidateShadowTree(); |
146 else | 227 else |
147 clientElement->buildPendingResource(); | 228 clientElement->buildPendingResource(); |
148 | 229 |
149 clearHasPendingResourcesIfPossible(*clientElement); | 230 clearHasPendingResourcesIfPossible(*clientElement); |
150 } | 231 } |
151 } | 232 } |
152 | 233 |
153 DEFINE_TRACE(SVGTreeScopeResources) { | 234 DEFINE_TRACE(SVGTreeScopeResources) { |
154 visitor->trace(m_pendingResources); | 235 visitor->trace(m_pendingResources); |
| 236 visitor->trace(m_treeScope); |
155 } | 237 } |
156 } | 238 } |
OLD | NEW |