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