Use "= default" to denote default constructor or destructor
[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() = default;
52
53 SlotAssignment::~SlotAssignment() = default;
54
55 HTMLSlotElement* SlotAssignment::findAssignedSlot(const Node& node, ShadowRoot& shadowRoot)
56 {
57     if (!is<Text>(node) && !is<Element>(node))
58         return nullptr;
59
60     auto slotName = slotNameForHostChild(node);
61     auto it = m_slots.find(slotName);
62     if (it == m_slots.end())
63         return nullptr;
64
65     return findFirstSlotElement(*it->value, shadowRoot);
66 }
67
68 void SlotAssignment::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
69 {
70 #ifndef NDEBUG
71     ASSERT(!m_slotElementsForConsistencyCheck.contains(&slotElement));
72     m_slotElementsForConsistencyCheck.add(&slotElement);
73 #endif
74
75     // FIXME: We should be able to do a targeted reconstruction.
76     shadowRoot.host()->invalidateStyleAndRenderersForSubtree();
77
78     const AtomicString& slotName = slotNameFromAttributeValue(name);
79     auto addResult = m_slots.add(slotName, std::unique_ptr<SlotInfo>());
80     if (addResult.isNewEntry) {
81         addResult.iterator->value = std::make_unique<SlotInfo>(slotElement);
82         if (slotName == defaultSlotName()) // Because assignSlots doesn't collect nodes assigned to the default slot as an optimzation.
83             m_slotAssignmentsIsValid = false;
84         return;
85     }
86
87     auto& slotInfo = *addResult.iterator->value;
88
89     if (!slotInfo.hasSlotElements())
90         slotInfo.element = &slotElement;
91     else {
92         slotInfo.element = nullptr;
93 #ifndef NDEBUG
94         m_needsToResolveSlotElements = true;
95 #endif
96     }
97     slotInfo.elementCount++;
98 }
99
100 void SlotAssignment::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
101 {
102 #ifndef NDEBUG
103     ASSERT(m_slotElementsForConsistencyCheck.contains(&slotElement));
104     m_slotElementsForConsistencyCheck.remove(&slotElement);
105 #endif
106
107     if (auto* host = shadowRoot.host()) // FIXME: We should be able to do a targeted reconstruction.
108         host->invalidateStyleAndRenderersForSubtree();
109
110     auto it = m_slots.find(slotNameFromAttributeValue(name));
111     RELEASE_ASSERT(it != m_slots.end());
112
113     auto& slotInfo = *it->value;
114     RELEASE_ASSERT(slotInfo.hasSlotElements());
115
116     slotInfo.elementCount--;
117     if (slotInfo.element == &slotElement) {
118         slotInfo.element = nullptr;
119 #ifndef NDEBUG
120         m_needsToResolveSlotElements = true;
121 #endif
122     }
123     ASSERT(slotInfo.element || m_needsToResolveSlotElements);
124 }
125
126 void SlotAssignment::didChangeSlot(const AtomicString& slotAttrValue, ShadowRoot& shadowRoot)
127 {
128     auto& slotName = slotNameFromAttributeValue(slotAttrValue);
129     auto it = m_slots.find(slotName);
130     if (it == m_slots.end())
131         return;
132     
133     it->value->assignedNodes.clear();
134     m_slotAssignmentsIsValid = false;
135
136     RefPtr<HTMLSlotElement> slotElement = findFirstSlotElement(*it->value, shadowRoot);
137     if (!slotElement)
138         return;
139
140     shadowRoot.host()->invalidateStyleAndRenderersForSubtree();
141
142     if (shadowRoot.mode() == ShadowRootMode::UserAgent)
143         return;
144
145     slotElement->enqueueSlotChangeEvent();
146 }
147
148 void SlotAssignment::hostChildElementDidChange(const Element& childElement, ShadowRoot& shadowRoot)
149 {
150     didChangeSlot(childElement.attributeWithoutSynchronization(slotAttr), shadowRoot);
151 }
152
153 const Vector<Node*>* SlotAssignment::assignedNodesForSlot(const HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
154 {
155     ASSERT(slotElement.containingShadowRoot() == &shadowRoot);
156     const AtomicString& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr));
157     auto it = m_slots.find(slotName);
158     RELEASE_ASSERT(it != m_slots.end());
159
160     auto& slotInfo = *it->value;
161     if (!m_slotAssignmentsIsValid)
162         assignSlots(shadowRoot);
163
164     if (!slotInfo.assignedNodes.size())
165         return nullptr;
166
167     RELEASE_ASSERT(slotInfo.hasSlotElements());
168     if (slotInfo.hasDuplicatedSlotElements() && findFirstSlotElement(slotInfo, shadowRoot) != &slotElement)
169         return nullptr;
170
171     return &slotInfo.assignedNodes;
172 }
173
174 const AtomicString& SlotAssignment::slotNameForHostChild(const Node& child) const
175 {
176     return slotNameFromSlotAttribute(child);
177 }
178
179 HTMLSlotElement* SlotAssignment::findFirstSlotElement(SlotInfo& slotInfo, ShadowRoot& shadowRoot)
180 {
181     if (slotInfo.shouldResolveSlotElement())
182         resolveAllSlotElements(shadowRoot);
183
184 #ifndef NDEBUG
185     ASSERT(!slotInfo.element || m_slotElementsForConsistencyCheck.contains(slotInfo.element));
186     ASSERT(!!slotInfo.element == !!slotInfo.elementCount);
187 #endif
188
189     return slotInfo.element;
190 }
191
192 void SlotAssignment::resolveAllSlotElements(ShadowRoot& shadowRoot)
193 {
194 #ifndef NDEBUG
195     ASSERT(m_needsToResolveSlotElements);
196     m_needsToResolveSlotElements = false;
197 #endif
198
199     // FIXME: It's inefficient to reset all values. We should be able to void this in common case.
200     for (auto& entry : m_slots)
201         entry.value->element = nullptr;
202
203     unsigned slotCount = m_slots.size();
204     for (auto& slotElement : descendantsOfType<HTMLSlotElement>(shadowRoot)) {
205         auto& slotName = slotNameFromAttributeValue(slotElement.attributeWithoutSynchronization(nameAttr));
206
207         auto it = m_slots.find(slotName);
208         RELEASE_ASSERT(it != m_slots.end());
209
210         SlotInfo& slotInfo = *it->value;
211         bool hasSeenSlotWithSameName = !!slotInfo.element;
212         if (hasSeenSlotWithSameName)
213             continue;
214
215         slotInfo.element = &slotElement;
216         slotCount--;
217         if (!slotCount)
218             break;
219     }
220 }
221
222 void SlotAssignment::assignSlots(ShadowRoot& shadowRoot)
223 {
224     ASSERT(!m_slotAssignmentsIsValid);
225     m_slotAssignmentsIsValid = true;
226
227     for (auto& entry : m_slots)
228         entry.value->assignedNodes.shrink(0);
229
230     auto& host = *shadowRoot.host();
231     for (auto* child = host.firstChild(); child; child = child->nextSibling()) {
232         if (!is<Text>(*child) && !is<Element>(*child))
233             continue;
234         auto slotName = slotNameForHostChild(*child);
235         assignToSlot(*child, slotName);
236     }
237
238     for (auto& entry : m_slots)
239         entry.value->assignedNodes.shrinkToFit();
240 }
241
242 void SlotAssignment::assignToSlot(Node& child, const AtomicString& slotName)
243 {
244     ASSERT(!slotName.isNull());
245     if (slotName == defaultSlotName()) {
246         auto defaultSlotEntry = m_slots.find(defaultSlotName());
247         if (defaultSlotEntry != m_slots.end())
248             defaultSlotEntry->value->assignedNodes.append(&child);
249         return;
250     }
251
252     auto addResult = m_slots.add(slotName, std::make_unique<SlotInfo>());
253     addResult.iterator->value->assignedNodes.append(&child);
254 }
255
256 }
257
258