2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/dom/MutationObserver.h"
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "core/dom/ExceptionCode.h"
36 #include "core/dom/Microtask.h"
37 #include "core/dom/MutationCallback.h"
38 #include "core/dom/MutationObserverInit.h"
39 #include "core/dom/MutationObserverRegistration.h"
40 #include "core/dom/MutationRecord.h"
41 #include "core/dom/Node.h"
42 #include "core/inspector/InspectorInstrumentation.h"
43 #include "wtf/MainThread.h"
48 static unsigned s_observerPriority
= 0;
50 struct MutationObserver::ObserverLessThan
{
51 bool operator()(const RefPtrWillBeMember
<MutationObserver
>& lhs
, const RefPtrWillBeMember
<MutationObserver
>& rhs
)
53 return lhs
->m_priority
< rhs
->m_priority
;
57 PassRefPtrWillBeRawPtr
<MutationObserver
> MutationObserver::create(PassOwnPtrWillBeRawPtr
<MutationCallback
> callback
)
59 ASSERT(isMainThread());
60 return adoptRefWillBeNoop(new MutationObserver(callback
));
63 MutationObserver::MutationObserver(PassOwnPtrWillBeRawPtr
<MutationCallback
> callback
)
64 : m_callback(callback
)
65 , m_priority(s_observerPriority
++)
69 MutationObserver::~MutationObserver()
72 ASSERT(m_registrations
.isEmpty());
74 if (!m_records
.isEmpty())
75 InspectorInstrumentation::didClearAllMutationRecords(m_callback
->executionContext(), this);
78 void MutationObserver::observe(Node
* node
, const MutationObserverInit
& observerInit
, ExceptionState
& exceptionState
)
81 exceptionState
.throwDOMException(NotFoundError
, "The provided node was null.");
85 MutationObserverOptions options
= 0;
87 if (observerInit
.hasAttributeOldValue() && observerInit
.attributeOldValue())
88 options
|= AttributeOldValue
;
90 HashSet
<AtomicString
> attributeFilter
;
91 if (observerInit
.hasAttributeFilter()) {
92 const Vector
<String
>& sequence
= observerInit
.attributeFilter();
93 for (unsigned i
= 0; i
< sequence
.size(); ++i
)
94 attributeFilter
.add(AtomicString(sequence
[i
]));
95 options
|= AttributeFilter
;
98 bool attributes
= observerInit
.hasAttributes() && observerInit
.attributes();
99 if (attributes
|| (!observerInit
.hasAttributes() && (observerInit
.hasAttributeOldValue() || observerInit
.hasAttributeFilter())))
100 options
|= Attributes
;
102 if (observerInit
.hasCharacterDataOldValue() && observerInit
.characterDataOldValue())
103 options
|= CharacterDataOldValue
;
105 bool characterData
= observerInit
.hasCharacterData() && observerInit
.characterData();
106 if (characterData
|| (!observerInit
.hasCharacterData() && observerInit
.hasCharacterDataOldValue()))
107 options
|= CharacterData
;
109 if (observerInit
.childList())
110 options
|= ChildList
;
112 if (observerInit
.subtree())
115 if (!(options
& Attributes
)) {
116 if (options
& AttributeOldValue
) {
117 exceptionState
.throwTypeError("The options object may only set 'attributeOldValue' to true when 'attributes' is true or not present.");
120 if (options
& AttributeFilter
) {
121 exceptionState
.throwTypeError("The options object may only set 'attributeFilter' when 'attributes' is true or not present.");
125 if (!((options
& CharacterData
) || !(options
& CharacterDataOldValue
))) {
126 exceptionState
.throwTypeError("The options object may only set 'characterDataOldValue' to true when 'characterData' is true or not present.");
130 if (!(options
& (Attributes
| CharacterData
| ChildList
))) {
131 exceptionState
.throwTypeError("The options object must set at least one of 'attributes', 'characterData', or 'childList' to true.");
135 node
->registerMutationObserver(*this, options
, attributeFilter
);
138 MutationRecordVector
MutationObserver::takeRecords()
140 MutationRecordVector records
;
141 records
.swap(m_records
);
142 InspectorInstrumentation::didClearAllMutationRecords(m_callback
->executionContext(), this);
146 void MutationObserver::disconnect()
149 InspectorInstrumentation::didClearAllMutationRecords(m_callback
->executionContext(), this);
150 MutationObserverRegistrationSet
registrations(m_registrations
);
151 for (auto& registration
: registrations
) {
152 // The registration may be already unregistered while iteration.
153 // Only call unregister if it is still in the original set.
154 if (m_registrations
.contains(registration
))
155 registration
->unregister();
157 ASSERT(m_registrations
.isEmpty());
160 void MutationObserver::observationStarted(MutationObserverRegistration
* registration
)
162 ASSERT(!m_registrations
.contains(registration
));
163 m_registrations
.add(registration
);
166 void MutationObserver::observationEnded(MutationObserverRegistration
* registration
)
168 ASSERT(m_registrations
.contains(registration
));
169 m_registrations
.remove(registration
);
172 static MutationObserverSet
& activeMutationObservers()
174 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent
<MutationObserverSet
>, activeObservers
, (adoptPtrWillBeNoop(new MutationObserverSet())));
175 return *activeObservers
;
178 static MutationObserverSet
& suspendedMutationObservers()
180 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent
<MutationObserverSet
>, suspendedObservers
, (adoptPtrWillBeNoop(new MutationObserverSet())));
181 return *suspendedObservers
;
184 static void activateObserver(PassRefPtrWillBeRawPtr
<MutationObserver
> observer
)
186 if (activeMutationObservers().isEmpty())
187 Microtask::enqueueMicrotask(WTF::bind(&MutationObserver::deliverMutations
));
189 activeMutationObservers().add(observer
);
192 void MutationObserver::enqueueMutationRecord(PassRefPtrWillBeRawPtr
<MutationRecord
> mutation
)
194 ASSERT(isMainThread());
195 m_records
.append(mutation
);
196 activateObserver(this);
197 InspectorInstrumentation::didEnqueueMutationRecord(m_callback
->executionContext(), this);
200 void MutationObserver::setHasTransientRegistration()
202 ASSERT(isMainThread());
203 activateObserver(this);
206 WillBeHeapHashSet
<RawPtrWillBeMember
<Node
>> MutationObserver::getObservedNodes() const
208 WillBeHeapHashSet
<RawPtrWillBeMember
<Node
>> observedNodes
;
209 for (const auto& registration
: m_registrations
)
210 registration
->addRegistrationNodesToSet(observedNodes
);
211 return observedNodes
;
214 bool MutationObserver::shouldBeSuspended() const
216 return m_callback
->executionContext() && m_callback
->executionContext()->activeDOMObjectsAreSuspended();
219 void MutationObserver::deliver()
221 ASSERT(!shouldBeSuspended());
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 WillBeHeapVector
<RawPtrWillBeMember
<MutationObserverRegistration
>, 1> transientRegistrations
;
226 for (auto& registration
: m_registrations
) {
227 if (registration
->hasTransientRegistrations())
228 transientRegistrations
.append(registration
);
230 for (size_t i
= 0; i
< transientRegistrations
.size(); ++i
)
231 transientRegistrations
[i
]->clearTransientRegistrations();
233 if (m_records
.isEmpty())
236 MutationRecordVector records
;
237 records
.swap(m_records
);
239 InspectorInstrumentation::willDeliverMutationRecords(m_callback
->executionContext(), this);
240 m_callback
->call(records
, this);
241 InspectorInstrumentation::didDeliverMutationRecords(m_callback
->executionContext());
244 void MutationObserver::resumeSuspendedObservers()
246 ASSERT(isMainThread());
247 if (suspendedMutationObservers().isEmpty())
250 MutationObserverVector suspended
;
251 copyToVector(suspendedMutationObservers(), suspended
);
252 for (size_t i
= 0; i
< suspended
.size(); ++i
) {
253 if (!suspended
[i
]->shouldBeSuspended()) {
254 suspendedMutationObservers().remove(suspended
[i
]);
255 activateObserver(suspended
[i
]);
260 void MutationObserver::deliverMutations()
262 ASSERT(isMainThread());
263 MutationObserverVector observers
;
264 copyToVector(activeMutationObservers(), observers
);
265 activeMutationObservers().clear();
266 std::sort(observers
.begin(), observers
.end(), ObserverLessThan());
267 for (size_t i
= 0; i
< observers
.size(); ++i
) {
268 if (observers
[i
]->shouldBeSuspended())
269 suspendedMutationObservers().add(observers
[i
]);
271 observers
[i
]->deliver();
275 DEFINE_TRACE(MutationObserver
)
278 visitor
->trace(m_callback
);
279 visitor
->trace(m_records
);
280 visitor
->trace(m_registrations
);
281 visitor
->trace(m_callback
);