• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Google 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 are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30 
31 #include "config.h"
32 #include "core/dom/MutationObserver.h"
33 
34 #include <algorithm>
35 #include "bindings/v8/Dictionary.h"
36 #include "bindings/v8/ExceptionState.h"
37 #include "core/dom/Document.h"
38 #include "core/dom/ExceptionCode.h"
39 #include "core/dom/Microtask.h"
40 #include "core/dom/MutationCallback.h"
41 #include "core/dom/MutationObserverRegistration.h"
42 #include "core/dom/MutationRecord.h"
43 #include "core/dom/Node.h"
44 #include "core/inspector/InspectorInstrumentation.h"
45 #include "wtf/MainThread.h"
46 
47 namespace WebCore {
48 
49 static unsigned s_observerPriority = 0;
50 
51 struct MutationObserver::ObserverLessThan {
operator ()WebCore::MutationObserver::ObserverLessThan52     bool operator()(const RefPtrWillBeMember<MutationObserver>& lhs, const RefPtrWillBeMember<MutationObserver>& rhs)
53     {
54         return lhs->m_priority < rhs->m_priority;
55     }
56 };
57 
create(PassOwnPtr<MutationCallback> callback)58 PassRefPtrWillBeRawPtr<MutationObserver> MutationObserver::create(PassOwnPtr<MutationCallback> callback)
59 {
60     ASSERT(isMainThread());
61     return adoptRefWillBeNoop(new MutationObserver(callback));
62 }
63 
MutationObserver(PassOwnPtr<MutationCallback> callback)64 MutationObserver::MutationObserver(PassOwnPtr<MutationCallback> callback)
65     : m_callback(callback)
66     , m_priority(s_observerPriority++)
67 {
68     ScriptWrappable::init(this);
69 }
70 
~MutationObserver()71 MutationObserver::~MutationObserver()
72 {
73 #if !ENABLE(OILPAN)
74     ASSERT(m_registrations.isEmpty());
75 #endif
76     if (!m_records.isEmpty())
77         InspectorInstrumentation::didClearAllMutationRecords(m_callback->executionContext(), this);
78 }
79 
observe(Node * node,const Dictionary & optionsDictionary,ExceptionState & exceptionState)80 void MutationObserver::observe(Node* node, const Dictionary& optionsDictionary, ExceptionState& exceptionState)
81 {
82     if (!node) {
83         exceptionState.throwDOMException(NotFoundError, "The provided node was null.");
84         return;
85     }
86 
87     MutationObserverOptions options = 0;
88 
89     bool attributeOldValue = false;
90     bool attributeOldValuePresent = optionsDictionary.get("attributeOldValue", attributeOldValue);
91     if (attributeOldValue)
92         options |= AttributeOldValue;
93 
94     HashSet<AtomicString> attributeFilter;
95     bool attributeFilterPresent = optionsDictionary.get("attributeFilter", attributeFilter);
96     if (attributeFilterPresent)
97         options |= AttributeFilter;
98 
99     bool attributes = false;
100     bool attributesPresent = optionsDictionary.get("attributes", attributes);
101     if (attributes || (!attributesPresent && (attributeOldValuePresent || attributeFilterPresent)))
102         options |= Attributes;
103 
104     bool characterDataOldValue = false;
105     bool characterDataOldValuePresent = optionsDictionary.get("characterDataOldValue", characterDataOldValue);
106     if (characterDataOldValue)
107         options |= CharacterDataOldValue;
108 
109     bool characterData = false;
110     bool characterDataPresent = optionsDictionary.get("characterData", characterData);
111     if (characterData || (!characterDataPresent && characterDataOldValuePresent))
112         options |= CharacterData;
113 
114     bool childListValue = false;
115     if (optionsDictionary.get("childList", childListValue) && childListValue)
116         options |= ChildList;
117 
118     bool subtreeValue = false;
119     if (optionsDictionary.get("subtree", subtreeValue) && subtreeValue)
120         options |= Subtree;
121 
122     if (!(options & Attributes)) {
123         if (options & AttributeOldValue) {
124             exceptionState.throwTypeError("The options object may only set 'attributeOldValue' to true when 'attributes' is true or not present.");
125             return;
126         }
127         if (options & AttributeFilter) {
128             exceptionState.throwTypeError("The options object may only set 'attributeFilter' when 'attributes' is true or not present.");
129             return;
130         }
131     }
132     if (!((options & CharacterData) || !(options & CharacterDataOldValue))) {
133         exceptionState.throwTypeError("The options object may only set 'characterDataOldValue' to true when 'characterData' is true or not present.");
134         return;
135     }
136 
137     if (!(options & (Attributes | CharacterData | ChildList))) {
138         exceptionState.throwTypeError("The options object must set at least one of 'attributes', 'characterData', or 'childList' to true.");
139         return;
140     }
141 
142     node->registerMutationObserver(*this, options, attributeFilter);
143 }
144 
takeRecords()145 MutationRecordVector MutationObserver::takeRecords()
146 {
147     MutationRecordVector records;
148     records.swap(m_records);
149     InspectorInstrumentation::didClearAllMutationRecords(m_callback->executionContext(), this);
150     return records;
151 }
152 
disconnect()153 void MutationObserver::disconnect()
154 {
155     m_records.clear();
156     InspectorInstrumentation::didClearAllMutationRecords(m_callback->executionContext(), this);
157     MutationObserverRegistrationSet registrations(m_registrations);
158     for (MutationObserverRegistrationSet::iterator iter = registrations.begin(); iter != registrations.end(); ++iter)
159         (*iter)->unregister();
160     ASSERT(m_registrations.isEmpty());
161 }
162 
observationStarted(MutationObserverRegistration * registration)163 void MutationObserver::observationStarted(MutationObserverRegistration* registration)
164 {
165     ASSERT(!m_registrations.contains(registration));
166     m_registrations.add(registration);
167 }
168 
observationEnded(MutationObserverRegistration * registration)169 void MutationObserver::observationEnded(MutationObserverRegistration* registration)
170 {
171     ASSERT(m_registrations.contains(registration));
172     m_registrations.remove(registration);
173 }
174 
activeMutationObservers()175 static MutationObserverSet& activeMutationObservers()
176 {
177 #if ENABLE(OILPAN)
178     DEFINE_STATIC_LOCAL(Persistent<MutationObserverSet>, activeObservers, (new MutationObserverSet()));
179     return *activeObservers;
180 #else
181     DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ());
182     return activeObservers;
183 #endif
184 }
185 
suspendedMutationObservers()186 static MutationObserverSet& suspendedMutationObservers()
187 {
188 #if ENABLE(OILPAN)
189     DEFINE_STATIC_LOCAL(Persistent<MutationObserverSet>, suspendedObservers, (new MutationObserverSet()));
190     return *suspendedObservers;
191 #else
192     DEFINE_STATIC_LOCAL(MutationObserverSet, suspendedObservers, ());
193     return suspendedObservers;
194 #endif
195 }
196 
activateObserver(PassRefPtrWillBeRawPtr<MutationObserver> observer)197 static void activateObserver(PassRefPtrWillBeRawPtr<MutationObserver> observer)
198 {
199     if (activeMutationObservers().isEmpty())
200         Microtask::enqueueMicrotask(WTF::bind(&MutationObserver::deliverMutations));
201 
202     activeMutationObservers().add(observer);
203 }
204 
enqueueMutationRecord(PassRefPtrWillBeRawPtr<MutationRecord> mutation)205 void MutationObserver::enqueueMutationRecord(PassRefPtrWillBeRawPtr<MutationRecord> mutation)
206 {
207     ASSERT(isMainThread());
208     m_records.append(mutation);
209     activateObserver(this);
210     InspectorInstrumentation::didEnqueueMutationRecord(m_callback->executionContext(), this);
211 }
212 
setHasTransientRegistration()213 void MutationObserver::setHasTransientRegistration()
214 {
215     ASSERT(isMainThread());
216     activateObserver(this);
217 }
218 
getObservedNodes() const219 HashSet<Node*> MutationObserver::getObservedNodes() const
220 {
221     HashSet<Node*> observedNodes;
222     for (MutationObserverRegistrationSet::const_iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter)
223         (*iter)->addRegistrationNodesToSet(observedNodes);
224     return observedNodes;
225 }
226 
canDeliver()227 bool MutationObserver::canDeliver()
228 {
229     return !m_callback->executionContext()->activeDOMObjectsAreSuspended();
230 }
231 
deliver()232 void MutationObserver::deliver()
233 {
234     ASSERT(canDeliver());
235 
236     // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary
237     // to make a copy of the transient registrations before operating on them.
238     WillBeHeapVector<RawPtrWillBeMember<MutationObserverRegistration>, 1> transientRegistrations;
239     for (MutationObserverRegistrationSet::iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter) {
240         if ((*iter)->hasTransientRegistrations())
241             transientRegistrations.append(*iter);
242     }
243     for (size_t i = 0; i < transientRegistrations.size(); ++i)
244         transientRegistrations[i]->clearTransientRegistrations();
245 
246     if (m_records.isEmpty())
247         return;
248 
249     MutationRecordVector records;
250     records.swap(m_records);
251 
252     InspectorInstrumentation::willDeliverMutationRecords(m_callback->executionContext(), this);
253     m_callback->call(records, this);
254     InspectorInstrumentation::didDeliverMutationRecords(m_callback->executionContext());
255 }
256 
resumeSuspendedObservers()257 void MutationObserver::resumeSuspendedObservers()
258 {
259     ASSERT(isMainThread());
260     if (suspendedMutationObservers().isEmpty())
261         return;
262 
263     MutationObserverVector suspended;
264     copyToVector(suspendedMutationObservers(), suspended);
265     for (size_t i = 0; i < suspended.size(); ++i) {
266         if (suspended[i]->canDeliver()) {
267             suspendedMutationObservers().remove(suspended[i]);
268             activateObserver(suspended[i]);
269         }
270     }
271 }
272 
deliverMutations()273 void MutationObserver::deliverMutations()
274 {
275     ASSERT(isMainThread());
276     MutationObserverVector observers;
277     copyToVector(activeMutationObservers(), observers);
278     activeMutationObservers().clear();
279     std::sort(observers.begin(), observers.end(), ObserverLessThan());
280     for (size_t i = 0; i < observers.size(); ++i) {
281         if (observers[i]->canDeliver())
282             observers[i]->deliver();
283         else
284             suspendedMutationObservers().add(observers[i]);
285     }
286 }
287 
trace(Visitor * visitor)288 void MutationObserver::trace(Visitor* visitor)
289 {
290     visitor->trace(m_records);
291     visitor->trace(m_registrations);
292 }
293 
294 } // namespace WebCore
295