| 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" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 return; | 31 return; |
| 32 // Replaces resource if already present, to handle potential id changes. | 32 // Replaces resource if already present, to handle potential id changes. |
| 33 m_resources.set(id, resource); | 33 m_resources.set(id, resource); |
| 34 | 34 |
| 35 SVGPendingElements* pendingElements = m_pendingResources.take(id); | 35 SVGPendingElements* pendingElements = m_pendingResources.take(id); |
| 36 if (!pendingElements) | 36 if (!pendingElements) |
| 37 return; | 37 return; |
| 38 // Update cached resources of pending clients. | 38 // Update cached resources of pending clients. |
| 39 for (Element* clientElement : *pendingElements) { | 39 for (Element* clientElement : *pendingElements) { |
| 40 DCHECK(clientElement->hasPendingResources()); | 40 DCHECK(clientElement->hasPendingResources()); |
| 41 clearHasPendingResourcesIfPossible(clientElement); | 41 clearHasPendingResourcesIfPossible(*clientElement); |
| 42 | 42 |
| 43 LayoutObject* layoutObject = clientElement->layoutObject(); | 43 LayoutObject* layoutObject = clientElement->layoutObject(); |
| 44 if (!layoutObject) | 44 if (!layoutObject) |
| 45 continue; | 45 continue; |
| 46 DCHECK(layoutObject->isSVG()); | 46 DCHECK(layoutObject->isSVG()); |
| 47 | 47 |
| 48 StyleDifference diff; | 48 StyleDifference diff; |
| 49 diff.setNeedsFullLayout(); | 49 diff.setNeedsFullLayout(); |
| 50 SVGResourcesCache::clientStyleChanged(layoutObject, diff, | 50 SVGResourcesCache::clientStyleChanged(layoutObject, diff, |
| 51 layoutObject->styleRef()); | 51 layoutObject->styleRef()); |
| 52 layoutObject->setNeedsLayoutAndFullPaintInvalidation( | 52 layoutObject->setNeedsLayoutAndFullPaintInvalidation( |
| 53 LayoutInvalidationReason::SvgResourceInvalidated); | 53 LayoutInvalidationReason::SvgResourceInvalidated); |
| 54 } | 54 } |
| 55 } | 55 } |
| 56 | 56 |
| 57 void SVGTreeScopeResources::removeResource(const AtomicString& id) { | 57 void SVGTreeScopeResources::removeResource(const AtomicString& id) { |
| 58 if (id.isEmpty()) | 58 if (id.isEmpty()) |
| 59 return; | 59 return; |
| 60 m_resources.erase(id); | 60 m_resources.erase(id); |
| 61 } | 61 } |
| 62 | 62 |
| 63 LayoutSVGResourceContainer* SVGTreeScopeResources::resourceById( | 63 LayoutSVGResourceContainer* SVGTreeScopeResources::resourceById( |
| 64 const AtomicString& id) const { | 64 const AtomicString& id) const { |
| 65 if (id.isEmpty()) | 65 if (id.isEmpty()) |
| 66 return nullptr; | 66 return nullptr; |
| 67 return m_resources.at(id); | 67 return m_resources.at(id); |
| 68 } | 68 } |
| 69 | 69 |
| 70 void SVGTreeScopeResources::addPendingResource(const AtomicString& id, | 70 void SVGTreeScopeResources::addPendingResource(const AtomicString& id, |
| 71 Element* element) { | 71 Element& element) { |
| 72 DCHECK(element); | 72 DCHECK(element.isConnected()); |
| 73 DCHECK(element->isConnected()); | |
| 74 | 73 |
| 75 if (id.isEmpty()) | 74 if (id.isEmpty()) |
| 76 return; | 75 return; |
| 77 | 76 auto result = m_pendingResources.insert(id, nullptr); |
| 78 HeapHashMap<AtomicString, Member<SVGPendingElements>>::AddResult result = | |
| 79 m_pendingResources.insert(id, nullptr); | |
| 80 if (result.isNewEntry) | 77 if (result.isNewEntry) |
| 81 result.storedValue->value = new SVGPendingElements; | 78 result.storedValue->value = new SVGPendingElements; |
| 82 result.storedValue->value->insert(element); | 79 result.storedValue->value->insert(&element); |
| 83 | 80 |
| 84 element->setHasPendingResources(); | 81 element.setHasPendingResources(); |
| 85 } | 82 } |
| 86 | 83 |
| 87 bool SVGTreeScopeResources::hasPendingResource(const AtomicString& id) const { | 84 bool SVGTreeScopeResources::isElementPendingResource( |
| 85 Element& element, |
| 86 const AtomicString& id) const { |
| 88 if (id.isEmpty()) | 87 if (id.isEmpty()) |
| 89 return false; | 88 return false; |
| 90 return m_pendingResources.contains(id); | 89 const SVGPendingElements* pendingElements = m_pendingResources.at(id); |
| 90 return pendingElements && pendingElements->contains(&element); |
| 91 } | 91 } |
| 92 | 92 |
| 93 bool SVGTreeScopeResources::isElementPendingResources(Element* element) const { | 93 void SVGTreeScopeResources::clearHasPendingResourcesIfPossible( |
| 94 Element& element) { |
| 94 // This algorithm takes time proportional to the number of pending resources | 95 // This algorithm takes time proportional to the number of pending resources |
| 95 // and need not. | 96 // and need not. |
| 96 // If performance becomes an issue we can keep a counted set of elements and | 97 // If performance becomes an issue we can keep a counted set of elements and |
| 97 // answer the question efficiently. | 98 // answer the question efficiently. |
| 98 DCHECK(element); | |
| 99 | |
| 100 for (const auto& entry : m_pendingResources) { | 99 for (const auto& entry : m_pendingResources) { |
| 101 SVGPendingElements* elements = entry.value.get(); | 100 SVGPendingElements* elements = entry.value.get(); |
| 102 DCHECK(elements); | 101 DCHECK(elements); |
| 103 if (elements->contains(element)) | 102 if (elements->contains(&element)) |
| 104 return true; | 103 return; |
| 105 } | 104 } |
| 106 return false; | 105 element.clearHasPendingResources(); |
| 107 } | |
| 108 | |
| 109 bool SVGTreeScopeResources::isElementPendingResource( | |
| 110 Element* element, | |
| 111 const AtomicString& id) const { | |
| 112 DCHECK(element); | |
| 113 if (!hasPendingResource(id)) | |
| 114 return false; | |
| 115 return m_pendingResources.at(id)->contains(element); | |
| 116 } | |
| 117 | |
| 118 void SVGTreeScopeResources::clearHasPendingResourcesIfPossible( | |
| 119 Element* element) { | |
| 120 if (!isElementPendingResources(element)) | |
| 121 element->clearHasPendingResources(); | |
| 122 } | 106 } |
| 123 | 107 |
| 124 void SVGTreeScopeResources::removeElementFromPendingResources( | 108 void SVGTreeScopeResources::removeElementFromPendingResources( |
| 125 Element* element) { | 109 Element& element) { |
| 126 DCHECK(element); | 110 if (m_pendingResources.isEmpty() || !element.hasPendingResources()) |
| 111 return; |
| 112 // Remove the element from pending resources. |
| 113 Vector<AtomicString> toBeRemoved; |
| 114 for (const auto& entry : m_pendingResources) { |
| 115 SVGPendingElements* elements = entry.value.get(); |
| 116 DCHECK(elements); |
| 117 DCHECK(!elements->isEmpty()); |
| 127 | 118 |
| 128 // Remove the element from pending resources. | 119 elements->erase(&element); |
| 129 if (!m_pendingResources.isEmpty() && element->hasPendingResources()) { | 120 if (elements->isEmpty()) |
| 130 Vector<AtomicString> toBeRemoved; | 121 toBeRemoved.push_back(entry.key); |
| 131 for (const auto& entry : m_pendingResources) { | 122 } |
| 132 SVGPendingElements* elements = entry.value.get(); | 123 m_pendingResources.removeAll(toBeRemoved); |
| 133 DCHECK(elements); | |
| 134 DCHECK(!elements->isEmpty()); | |
| 135 | 124 |
| 136 elements->erase(element); | 125 clearHasPendingResourcesIfPossible(element); |
| 137 if (elements->isEmpty()) | |
| 138 toBeRemoved.push_back(entry.key); | |
| 139 } | |
| 140 | |
| 141 clearHasPendingResourcesIfPossible(element); | |
| 142 | |
| 143 // We use the removePendingResource function here because it deals with set | |
| 144 // lifetime correctly. | |
| 145 for (const AtomicString& id : toBeRemoved) | |
| 146 removePendingResource(id); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 SVGTreeScopeResources::SVGPendingElements* | |
| 151 SVGTreeScopeResources::removePendingResource(const AtomicString& id) { | |
| 152 DCHECK(m_pendingResources.contains(id)); | |
| 153 return m_pendingResources.take(id); | |
| 154 } | 126 } |
| 155 | 127 |
| 156 void SVGTreeScopeResources::notifyResourceAvailable(const AtomicString& id) { | 128 void SVGTreeScopeResources::notifyResourceAvailable(const AtomicString& id) { |
| 157 if (id.isEmpty()) | 129 if (id.isEmpty()) |
| 158 return; | 130 return; |
| 159 // Get pending elements for this id. | 131 // Get pending elements for this id. |
| 160 SVGPendingElements* pendingElements = m_pendingResources.take(id); | 132 SVGPendingElements* pendingElements = m_pendingResources.take(id); |
| 161 if (!pendingElements) | 133 if (!pendingElements) |
| 162 return; | 134 return; |
| 163 // Rebuild pending resources for each client of a pending resource that is | 135 // Rebuild pending resources for each client of a pending resource that is |
| 164 // being removed. | 136 // being removed. |
| 165 for (Element* clientElement : *pendingElements) { | 137 for (Element* clientElement : *pendingElements) { |
| 166 DCHECK(clientElement->hasPendingResources()); | 138 DCHECK(clientElement->hasPendingResources()); |
| 167 if (!clientElement->hasPendingResources()) | 139 if (!clientElement->hasPendingResources()) |
| 168 continue; | 140 continue; |
| 169 // TODO(fs): Ideally we'd always resolve pending resources async instead of | 141 // TODO(fs): Ideally we'd always resolve pending resources async instead of |
| 170 // inside insertedInto and svgAttributeChanged. For now we only do it for | 142 // inside insertedInto and svgAttributeChanged. For now we only do it for |
| 171 // <use> since that would stamp out DOM. | 143 // <use> since that would stamp out DOM. |
| 172 if (isSVGUseElement(clientElement)) | 144 if (isSVGUseElement(clientElement)) |
| 173 toSVGUseElement(clientElement)->invalidateShadowTree(); | 145 toSVGUseElement(clientElement)->invalidateShadowTree(); |
| 174 else | 146 else |
| 175 clientElement->buildPendingResource(); | 147 clientElement->buildPendingResource(); |
| 176 | 148 |
| 177 clearHasPendingResourcesIfPossible(clientElement); | 149 clearHasPendingResourcesIfPossible(*clientElement); |
| 178 } | 150 } |
| 179 } | 151 } |
| 180 | 152 |
| 181 DEFINE_TRACE(SVGTreeScopeResources) { | 153 DEFINE_TRACE(SVGTreeScopeResources) { |
| 182 visitor->trace(m_pendingResources); | 154 visitor->trace(m_pendingResources); |
| 183 } | 155 } |
| 184 } | 156 } |
| OLD | NEW |