[WTF] Clean up StringStatics.cpp by using LazyNeverDestroyed<> for Atoms
[WebKit-https.git] / Source / WebCore / dom / SlotAssignment.cpp
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "SlotAssignment.h"
28
29
30 #include "HTMLSlotElement.h"
31 #include "ShadowRoot.h"
32 #include "TypedElementDescendantIterator.h"
33
34 namespace WebCore {
35
36 using namespace HTMLNames;
37
38 static const AtomicString& slotNameFromAttributeValue(const AtomicString& value)
39 {
40     return value == nullAtom() ? SlotAssignment::defaultSlotName() : value;
41 }
42
43 static const AtomicString& slotNameFromSlotAttribute(const Node& child)
44 {
45     if (is<Text>(child))
46         return SlotAssignment::defaultSlotName();
47
48     return slotNameFromAttributeValue(downcast<Element>(child).attributeWithoutSynchronization(slotAttr));
49 }
50
51 SlotAssignment::SlotAssignment()
52 {
53 }
54
55 SlotAssignment::~SlotAssignment()
56 {
57 }
58
59 HTMLSlotElement* SlotAssignment::findAssignedSlot(const Node& node, ShadowRoot& shadowRoot)
60 {
61     if (!is<Text>(node) && !is<Element>(node))
62         return nullptr;
63
64     auto slotName = slotNameForHostChild(node);
65     auto it = m_slots.find(slotName);
66     if (it == m_slots.end())
67         return nullptr;
68
69     return findFirstSlotElement(*it->value, shadowRoot);
70 }
71
72 void SlotAssignment::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
73 {
74 #ifndef NDEBUG
75     ASSERT(!m_slotElementsForConsistencyCheck.contains(&slotElement));
76     m_slotElementsForConsistencyCheck.add(&slotElement);
77 #endif
78
79     // FIXME: We should be able to do a targeted reconstruction.
80     shadowRoot.host()->invalidateStyleAndRenderersForSubtree();
81
82     const AtomicString& slotName = slotNameFromAttributeValue(name);
83     auto addResult = m_slots.add(slotName, std::unique_ptr<SlotInfo>());
84     if (addResult.isNewEntry) {
85         addResult.iterator->value = std::make_unique<SlotInfo>(slotElement);
86         if (slotName == defaultSlotName()) // Because assignSlots doesn't collect nodes assigned to the default slot as an optimzation.
87             m_slotAssignmentsIsValid = false;
88         return;
89     }
90
91     auto& slotInfo = *addResult.iterator->value;
92
93     if (!slotInfo.hasSlotElements())
94         slotInfo.element = &slotElement;
95     else {
96         slotInfo.element = nullptr;
97 #ifndef NDEBUG
98         m_needsToResolveSlotElements = true;
99 #endif
100     }
101     slotInfo.elementCount++;
102 }
103
104 void SlotAssignment::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
105 {
106 #ifndef NDEBUG
107     ASSERT(m_slotElementsForConsistencyCheck.contains(&slotElement));
108     m_slotElementsForConsistencyCheck.remove(&slotElement);
109 #endif
110
111     if (auto* host = shadowRoot.host()) // FIXME: We should be able to do a targeted reconstruction.
112         host->invalidateStyleAndRenderersForSubtree();
113
114     auto it = m_slots.find(slotNameFromAttributeValue(name));
115     RELEASE_ASSERT(it != m_slots.end());
116
117     auto& slotInfo = *it->value;
118     RELEASE_ASSERT(slotInfo.hasSlotElements());
119
120     slotInfo.elementCount--;
121     if (slotInfo.element == &slotElement) {
122         slotInfo.element = nullptr;
123 #ifndef NDEBUG
124         m_needsToResolveSlotElements = true;
125 #endif
126     }
127     ASSERT(slotInfo.element || m_needsToResolveSlotElements);
128 }
129
130 void SlotAssignment::didChangeSlot(const AtomicString& slotAttrValue, ShadowRoot& shadowRoot)
131 {
132     auto& slotName = slotNameFromAttributeValue(slotAttrValue);
133     auto it = m_slots.find(slotName);
134     if (it == m_slots.end())
135         return;
136     
137     it->value->assignedNodes.clear();
138     m_slotAssignmentsIsValid = false;
139
140     HTMLSlotElement* slotElement = findFirstSlotElement(*it->value, shadowRoot);
141     if (!slotElement)
142         return;
143
144     shadowRoot.host()->invalidateStyleAndRenderersForSubtree();
145
146     if (shadowRoot.mode() == ShadowRootMode::UserAgent)
147         return;
148
149     slotElement->enqueueSlotChangeEvent();
150 }
151
152 void SlotAssignment::hostChildElementDidChange(const Element& childElement, ShadowRoot& shadowRoot)
153 {
154     didChangeSlot(childElement.attributeWithoutSynchronization(slotAttr), shadowRoot);
155 }
156
157 const Vector<Node*>* SlotAssignment::assignedNodesForSlot(const HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
158 {
159     ASSERT(slotElement.containingShadowRoot() == &shadowRoot);
160     const AtomicString& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr));
161     auto it = m_slots.find(slotName);
162     RELEASE_ASSERT(it != m_slots.end());
163
164     auto& slotInfo = *it->value;
165     if (!m_slotAssignmentsIsValid)
166         assignSlots(shadowRoot);
167
168     if (!slotInfo.assignedNodes.size())
169         return nullptr;
170
171     RELEASE_ASSERT(slotInfo.hasSlotElements());
172     if (slotInfo.hasDuplicatedSlotElements() && findFirstSlotElement(slotInfo, shadowRoot) != &slotElement)
173         return nullptr;
174
175     return &slotInfo.assignedNodes;
176 }
177
178 const AtomicString& SlotAssignment::slotNameForHostChild(const Node& child) const
179 {
180     return slotNameFromSlotAttribute(child);
181 }
182
183 HTMLSlotElement* SlotAssignment::findFirstSlotElement(SlotInfo& slotInfo, ShadowRoot& shadowRoot)
184 {
185     if (slotInfo.shouldResolveSlotElement())
186         resolveAllSlotElements(shadowRoot);
187
188 #ifndef NDEBUG
189     ASSERT(!slotInfo.element || m_slotElementsForConsistencyCheck.contains(slotInfo.element));
190     ASSERT(!!slotInfo.element == !!slotInfo.elementCount);
191 #endif
192
193     return slotInfo.element;
194 }
195
196 void SlotAssignment::resolveAllSlotElements(ShadowRoot& shadowRoot)
197 {
198 #ifndef NDEBUG
199     ASSERT(m_needsToResolveSlotElements);
200     m_needsToResolveSlotElements = false;
201 #endif
202
203     // FIXME: It's inefficient to reset all values. We should be able to void this in common case.
204     for (auto& entry : m_slots)
205         entry.value->element = nullptr;
206
207     unsigned slotCount = m_slots.size();
208     for (auto& slotElement : descendantsOfType<HTMLSlotElement>(shadowRoot)) {
209         auto& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr));
210
211         auto it = m_slots.find(slotName);
212         RELEASE_ASSERT(it != m_slots.end());
213
214         SlotInfo& slotInfo = *it->value;
215         bool hasSeenSlotWithSameName = !!slotInfo.element;
216         if (hasSeenSlotWithSameName)
217             continue;
218
219         slotInfo.element = &slotElement;
220         slotCount--;
221         if (!slotCount)
222             break;
223     }
224 }
225
226 void SlotAssignment::assignSlots(ShadowRoot& shadowRoot)
227 {
228     ASSERT(!m_slotAssignmentsIsValid);
229     m_slotAssignmentsIsValid = true;
230
231     for (auto& entry : m_slots)
232         entry.value->assignedNodes.shrink(0);
233
234     auto& host = *shadowRoot.host();
235     for (auto* child = host.firstChild(); child; child = child->nextSibling()) {
236         if (!is<Text>(*child) && !is<Element>(*child))
237             continue;
238         auto slotName = slotNameForHostChild(*child);
239         assignToSlot(*child, slotName);
240     }
241
242     for (auto& entry : m_slots)
243         entry.value->assignedNodes.shrinkToFit();
244 }
245
246 void SlotAssignment::assignToSlot(Node& child, const AtomicString& slotName)
247 {
248     ASSERT(!slotName.isNull());
249     if (slotName == defaultSlotName()) {
250         auto defaultSlotEntry = m_slots.find(defaultSlotName());
251         if (defaultSlotEntry != m_slots.end())
252             defaultSlotEntry->value->assignedNodes.append(&child);
253         return;
254     }
255
256     auto addResult = m_slots.add(slotName, std::make_unique<SlotInfo>());
257     addResult.iterator->value->assignedNodes.append(&child);
258 }
259
260 }
261
262