Use a 1-byte enum class for TextDirection
[WebKit-https.git] / Source / WebCore / dom / MutationObserver.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2018 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33
34 #include "MutationObserver.h"
35
36 #include "Document.h"
37 #include "HTMLSlotElement.h"
38 #include "Microtasks.h"
39 #include "MutationCallback.h"
40 #include "MutationObserverRegistration.h"
41 #include "MutationRecord.h"
42 #include <algorithm>
43 #include <wtf/IsoMallocInlines.h>
44 #include <wtf/MainThread.h>
45 #include <wtf/NeverDestroyed.h>
46
47 namespace WebCore {
48
49 WTF_MAKE_ISO_ALLOCATED_IMPL(MutationObserver);
50
51 static unsigned s_observerPriority = 0;
52
53 Ref<MutationObserver> MutationObserver::create(Ref<MutationCallback>&& callback)
54 {
55     ASSERT(isMainThread());
56     return adoptRef(*new MutationObserver(WTFMove(callback)));
57 }
58
59 MutationObserver::MutationObserver(Ref<MutationCallback>&& callback)
60     : m_callback(WTFMove(callback))
61     , m_priority(s_observerPriority++)
62 {
63 }
64
65 MutationObserver::~MutationObserver()
66 {
67     ASSERT(m_registrations.isEmpty());
68 }
69
70 bool MutationObserver::validateOptions(MutationObserverOptions options)
71 {
72     return (options & (Attributes | CharacterData | ChildList))
73         && ((options & Attributes) || !(options & AttributeOldValue))
74         && ((options & Attributes) || !(options & AttributeFilter))
75         && ((options & CharacterData) || !(options & CharacterDataOldValue));
76 }
77
78 ExceptionOr<void> MutationObserver::observe(Node& node, const Init& init)
79 {
80     MutationObserverOptions options = 0;
81
82     if (init.childList)
83         options |= ChildList;
84     if (init.subtree)
85         options |= Subtree;
86     if (init.attributeOldValue.value_or(false))
87         options |= AttributeOldValue;
88     if (init.characterDataOldValue.value_or(false))
89         options |= CharacterDataOldValue;
90
91     HashSet<AtomicString> attributeFilter;
92     if (init.attributeFilter) {
93         for (auto& value : init.attributeFilter.value())
94             attributeFilter.add(value);
95         options |= AttributeFilter;
96     }
97
98     if (init.attributes ? init.attributes.value() : (options & (AttributeFilter | AttributeOldValue)))
99         options |= Attributes;
100
101     if (init.characterData ? init.characterData.value() : (options & CharacterDataOldValue))
102         options |= CharacterData;
103
104     if (!validateOptions(options))
105         return Exception { TypeError };
106
107     node.registerMutationObserver(*this, options, attributeFilter);
108
109     return { };
110 }
111
112 Vector<Ref<MutationRecord>> MutationObserver::takeRecords()
113 {
114     Vector<Ref<MutationRecord>> records;
115     records.swap(m_records);
116     return records;
117 }
118
119 void MutationObserver::disconnect()
120 {
121     m_records.clear();
122     HashSet<MutationObserverRegistration*> registrations(m_registrations);
123     for (auto* registration : registrations)
124         registration->node().unregisterMutationObserver(*registration);
125 }
126
127 void MutationObserver::observationStarted(MutationObserverRegistration& registration)
128 {
129     ASSERT(!m_registrations.contains(&registration));
130     m_registrations.add(&registration);
131 }
132
133 void MutationObserver::observationEnded(MutationObserverRegistration& registration)
134 {
135     ASSERT(m_registrations.contains(&registration));
136     m_registrations.remove(&registration);
137 }
138
139 typedef HashSet<RefPtr<MutationObserver>> MutationObserverSet;
140
141 static MutationObserverSet& activeMutationObservers()
142 {
143     static NeverDestroyed<MutationObserverSet> activeObservers;
144     return activeObservers;
145 }
146
147 static MutationObserverSet& suspendedMutationObservers()
148 {
149     static NeverDestroyed<MutationObserverSet> suspendedObservers;
150     return suspendedObservers;
151 }
152
153 // https://dom.spec.whatwg.org/#signal-slot-list
154 static Vector<RefPtr<HTMLSlotElement>>& signalSlotList()
155 {
156     static NeverDestroyed<Vector<RefPtr<HTMLSlotElement>>> list;
157     return list;
158 }
159
160 static bool mutationObserverCompoundMicrotaskQueuedFlag;
161
162 class MutationObserverMicrotask final : public Microtask {
163     WTF_MAKE_FAST_ALLOCATED;
164 private:
165     Result run() final
166     {
167         MutationObserver::notifyMutationObservers();
168         return Result::Done;
169     }
170 };
171
172 static void queueMutationObserverCompoundMicrotask()
173 {
174     if (mutationObserverCompoundMicrotaskQueuedFlag)
175         return;
176     mutationObserverCompoundMicrotaskQueuedFlag = true;
177     MicrotaskQueue::mainThreadQueue().append(std::make_unique<MutationObserverMicrotask>());
178 }
179
180 void MutationObserver::enqueueMutationRecord(Ref<MutationRecord>&& mutation)
181 {
182     ASSERT(isMainThread());
183     m_records.append(WTFMove(mutation));
184     activeMutationObservers().add(this);
185
186     queueMutationObserverCompoundMicrotask();
187 }
188
189 void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot)
190 {
191     ASSERT(isMainThread());
192     ASSERT(!signalSlotList().contains(&slot));
193     signalSlotList().append(&slot);
194
195     queueMutationObserverCompoundMicrotask();
196 }
197
198 void MutationObserver::setHasTransientRegistration()
199 {
200     ASSERT(isMainThread());
201     activeMutationObservers().add(this);
202
203     queueMutationObserverCompoundMicrotask();
204 }
205
206 HashSet<Node*> MutationObserver::observedNodes() const
207 {
208     HashSet<Node*> observedNodes;
209     for (auto* registration : m_registrations)
210         registration->addRegistrationNodesToSet(observedNodes);
211     return observedNodes;
212 }
213
214 bool MutationObserver::canDeliver()
215 {
216     return m_callback->canInvokeCallback();
217 }
218
219 void MutationObserver::deliver()
220 {
221     ASSERT(canDeliver());
222
223     // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary
224     // to make a copy of the transient registrations before operating on them.
225     Vector<MutationObserverRegistration*, 1> transientRegistrations;
226     for (auto* registration : m_registrations) {
227         if (registration->hasTransientRegistrations())
228             transientRegistrations.append(registration);
229     }
230     for (auto& registration : transientRegistrations)
231         registration->clearTransientRegistrations();
232
233     if (m_records.isEmpty())
234         return;
235
236     Vector<Ref<MutationRecord>> records;
237     records.swap(m_records);
238
239     // FIXME: Keep mutation observer callback as long as its observed nodes are alive. See https://webkit.org/b/179224.
240     if (m_callback->hasCallback())
241         m_callback->handleEvent(*this, records, *this);
242 }
243
244 void MutationObserver::notifyMutationObservers()
245 {
246     // https://dom.spec.whatwg.org/#notify-mutation-observers
247     // 1. Unset mutation observer compound microtask queued flag.
248     mutationObserverCompoundMicrotaskQueuedFlag = false;
249
250     ASSERT(isMainThread());
251     static bool deliveryInProgress = false;
252     if (deliveryInProgress)
253         return;
254     deliveryInProgress = true;
255
256     if (!suspendedMutationObservers().isEmpty()) {
257         for (auto& observer : copyToVector(suspendedMutationObservers())) {
258             if (!observer->canDeliver())
259                 continue;
260
261             suspendedMutationObservers().remove(observer);
262             activeMutationObservers().add(observer);
263         }
264     }
265
266     while (!activeMutationObservers().isEmpty() || !signalSlotList().isEmpty()) {
267         // 2. Let notify list be a copy of unit of related similar-origin browsing contexts' list of MutationObserver objects.
268         auto notifyList = copyToVector(activeMutationObservers());
269         activeMutationObservers().clear();
270         std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) {
271             return lhs->m_priority < rhs->m_priority;
272         });
273
274         // 3. Let signalList be a copy of unit of related similar-origin browsing contexts' signal slot list.
275         // 4. Empty unit of related similar-origin browsing contexts' signal slot list.
276         Vector<RefPtr<HTMLSlotElement>> slotList;
277         if (!signalSlotList().isEmpty()) {
278             slotList.swap(signalSlotList());
279             for (auto& slot : slotList)
280                 slot->didRemoveFromSignalSlotList();
281         }
282
283         // 5. For each MutationObserver object mo in notify list, execute a compound microtask subtask
284         for (auto& observer : notifyList) {
285             if (observer->canDeliver())
286                 observer->deliver();
287             else
288                 suspendedMutationObservers().add(observer);
289         }
290
291         // 6. For each slot slot in signalList, in order, fire an event named slotchange, with its bubbles attribute set to true, at slot.
292         for (auto& slot : slotList)
293             slot->dispatchSlotChangeEvent();
294     }
295
296     deliveryInProgress = false;
297 }
298
299 } // namespace WebCore