Clear m_pendingTargets in MutationObserver::takeRecords
[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 "GCReachableRef.h"
38 #include "HTMLSlotElement.h"
39 #include "Microtasks.h"
40 #include "MutationCallback.h"
41 #include "MutationObserverRegistration.h"
42 #include "MutationRecord.h"
43 #include <algorithm>
44 #include <wtf/IsoMallocInlines.h>
45 #include <wtf/MainThread.h>
46 #include <wtf/NeverDestroyed.h>
47
48 namespace WebCore {
49
50 WTF_MAKE_ISO_ALLOCATED_IMPL(MutationObserver);
51
52 static unsigned s_observerPriority = 0;
53
54 Ref<MutationObserver> MutationObserver::create(Ref<MutationCallback>&& callback)
55 {
56     ASSERT(isMainThread());
57     return adoptRef(*new MutationObserver(WTFMove(callback)));
58 }
59
60 MutationObserver::MutationObserver(Ref<MutationCallback>&& callback)
61     : m_callback(WTFMove(callback))
62     , m_priority(s_observerPriority++)
63 {
64 }
65
66 MutationObserver::~MutationObserver()
67 {
68     ASSERT(m_registrations.isEmpty());
69 }
70
71 bool MutationObserver::validateOptions(MutationObserverOptions options)
72 {
73     return (options & (Attributes | CharacterData | ChildList))
74         && ((options & Attributes) || !(options & AttributeOldValue))
75         && ((options & Attributes) || !(options & AttributeFilter))
76         && ((options & CharacterData) || !(options & CharacterDataOldValue));
77 }
78
79 ExceptionOr<void> MutationObserver::observe(Node& node, const Init& init)
80 {
81     MutationObserverOptions options = 0;
82
83     if (init.childList)
84         options |= ChildList;
85     if (init.subtree)
86         options |= Subtree;
87     if (init.attributeOldValue.value_or(false))
88         options |= AttributeOldValue;
89     if (init.characterDataOldValue.value_or(false))
90         options |= CharacterDataOldValue;
91
92     HashSet<AtomicString> attributeFilter;
93     if (init.attributeFilter) {
94         for (auto& value : init.attributeFilter.value())
95             attributeFilter.add(value);
96         options |= AttributeFilter;
97     }
98
99     if (init.attributes ? init.attributes.value() : (options & (AttributeFilter | AttributeOldValue)))
100         options |= Attributes;
101
102     if (init.characterData ? init.characterData.value() : (options & CharacterDataOldValue))
103         options |= CharacterData;
104
105     if (!validateOptions(options))
106         return Exception { TypeError };
107
108     node.registerMutationObserver(*this, options, attributeFilter);
109
110     return { };
111 }
112
113 auto MutationObserver::takeRecords() -> TakenRecords
114 {
115     return { WTFMove(m_records), WTFMove(m_pendingTargets) };
116 }
117
118 void MutationObserver::disconnect()
119 {
120     m_pendingTargets.clear();
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<GCReachableRef<HTMLSlotElement>>& signalSlotList()
155 {
156     static NeverDestroyed<Vector<GCReachableRef<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     ASSERT(mutation->target());
184     m_pendingTargets.add(*mutation->target());
185     m_records.append(WTFMove(mutation));
186     activeMutationObservers().add(this);
187
188     queueMutationObserverCompoundMicrotask();
189 }
190
191 void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot)
192 {
193     ASSERT(isMainThread());
194     ASSERT(signalSlotList().findMatching([&slot](auto& entry) { return entry.ptr() == &slot; }) == notFound);
195     signalSlotList().append(slot);
196
197     queueMutationObserverCompoundMicrotask();
198 }
199
200 void MutationObserver::setHasTransientRegistration()
201 {
202     ASSERT(isMainThread());
203     activeMutationObservers().add(this);
204
205     queueMutationObserverCompoundMicrotask();
206 }
207
208 HashSet<Node*> MutationObserver::observedNodes() const
209 {
210     HashSet<Node*> observedNodes;
211     for (auto* registration : m_registrations)
212         registration->addRegistrationNodesToSet(observedNodes);
213     return observedNodes;
214 }
215
216 bool MutationObserver::canDeliver()
217 {
218     return m_callback->canInvokeCallback();
219 }
220
221 void MutationObserver::deliver()
222 {
223     ASSERT(canDeliver());
224
225     // Calling takeTransientRegistrations() can modify m_registrations, so it's necessary
226     // to make a copy of the transient registrations before operating on them.
227     Vector<MutationObserverRegistration*, 1> transientRegistrations;
228     Vector<std::unique_ptr<HashSet<GCReachableRef<Node>>>, 1> nodesToKeepAlive;
229     HashSet<GCReachableRef<Node>> pendingTargets;
230     pendingTargets.swap(m_pendingTargets);
231     for (auto* registration : m_registrations) {
232         if (registration->hasTransientRegistrations())
233             transientRegistrations.append(registration);
234     }
235     for (auto& registration : transientRegistrations)
236         nodesToKeepAlive.append(registration->takeTransientRegistrations());
237
238     if (m_records.isEmpty()) {
239         ASSERT(m_pendingTargets.isEmpty());
240         return;
241     }
242
243     Vector<Ref<MutationRecord>> records;
244     records.swap(m_records);
245
246     // FIXME: Keep mutation observer callback as long as its observed nodes are alive. See https://webkit.org/b/179224.
247     if (m_callback->hasCallback())
248         m_callback->handleEvent(*this, records, *this);
249 }
250
251 void MutationObserver::notifyMutationObservers()
252 {
253     // https://dom.spec.whatwg.org/#notify-mutation-observers
254     // 1. Unset mutation observer compound microtask queued flag.
255     mutationObserverCompoundMicrotaskQueuedFlag = false;
256
257     ASSERT(isMainThread());
258     static bool deliveryInProgress = false;
259     if (deliveryInProgress)
260         return;
261     deliveryInProgress = true;
262
263     if (!suspendedMutationObservers().isEmpty()) {
264         for (auto& observer : copyToVector(suspendedMutationObservers())) {
265             if (!observer->canDeliver())
266                 continue;
267
268             suspendedMutationObservers().remove(observer);
269             activeMutationObservers().add(observer);
270         }
271     }
272
273     while (!activeMutationObservers().isEmpty() || !signalSlotList().isEmpty()) {
274         // 2. Let notify list be a copy of unit of related similar-origin browsing contexts' list of MutationObserver objects.
275         auto notifyList = copyToVector(activeMutationObservers());
276         activeMutationObservers().clear();
277         std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) {
278             return lhs->m_priority < rhs->m_priority;
279         });
280
281         // 3. Let signalList be a copy of unit of related similar-origin browsing contexts' signal slot list.
282         // 4. Empty unit of related similar-origin browsing contexts' signal slot list.
283         Vector<GCReachableRef<HTMLSlotElement>> slotList;
284         if (!signalSlotList().isEmpty()) {
285             slotList.swap(signalSlotList());
286             for (auto& slot : slotList)
287                 slot->didRemoveFromSignalSlotList();
288         }
289
290         // 5. For each MutationObserver object mo in notify list, execute a compound microtask subtask
291         for (auto& observer : notifyList) {
292             if (observer->canDeliver())
293                 observer->deliver();
294             else
295                 suspendedMutationObservers().add(observer);
296         }
297
298         // 6. For each slot slot in signalList, in order, fire an event named slotchange, with its bubbles attribute set to true, at slot.
299         for (auto& slot : slotList)
300             slot->dispatchSlotChangeEvent();
301     }
302
303     deliveryInProgress = false;
304 }
305
306 } // namespace WebCore