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/MutationCallback.h"
40 #include "core/dom/MutationObserverRegistration.h"
41 #include "core/dom/MutationRecord.h"
42 #include "core/dom/Node.h"
43 #include "wtf/MainThread.h"
44
45 namespace WebCore {
46
47 static unsigned s_observerPriority = 0;
48
49 struct MutationObserver::ObserverLessThan {
operator ()WebCore::MutationObserver::ObserverLessThan50 bool operator()(const RefPtr<MutationObserver>& lhs, const RefPtr<MutationObserver>& rhs)
51 {
52 return lhs->m_priority < rhs->m_priority;
53 }
54 };
55
create(PassOwnPtr<MutationCallback> callback)56 PassRefPtr<MutationObserver> MutationObserver::create(PassOwnPtr<MutationCallback> callback)
57 {
58 ASSERT(isMainThread());
59 return adoptRef(new MutationObserver(callback));
60 }
61
MutationObserver(PassOwnPtr<MutationCallback> callback)62 MutationObserver::MutationObserver(PassOwnPtr<MutationCallback> callback)
63 : m_callback(callback)
64 , m_priority(s_observerPriority++)
65 {
66 ScriptWrappable::init(this);
67 }
68
~MutationObserver()69 MutationObserver::~MutationObserver()
70 {
71 ASSERT(m_registrations.isEmpty());
72 }
73
observe(Node * node,const Dictionary & optionsDictionary,ExceptionState & exceptionState)74 void MutationObserver::observe(Node* node, const Dictionary& optionsDictionary, ExceptionState& exceptionState)
75 {
76 if (!node) {
77 exceptionState.throwDOMException(NotFoundError, "The provided node was null.");
78 return;
79 }
80
81 MutationObserverOptions options = 0;
82
83 bool attributeOldValue = false;
84 bool attributeOldValuePresent = optionsDictionary.get("attributeOldValue", attributeOldValue);
85 if (attributeOldValue)
86 options |= AttributeOldValue;
87
88 HashSet<AtomicString> attributeFilter;
89 bool attributeFilterPresent = optionsDictionary.get("attributeFilter", attributeFilter);
90 if (attributeFilterPresent)
91 options |= AttributeFilter;
92
93 bool attributes = false;
94 bool attributesPresent = optionsDictionary.get("attributes", attributes);
95 if (attributes || (!attributesPresent && (attributeOldValuePresent || attributeFilterPresent)))
96 options |= Attributes;
97
98 bool characterDataOldValue = false;
99 bool characterDataOldValuePresent = optionsDictionary.get("characterDataOldValue", characterDataOldValue);
100 if (characterDataOldValue)
101 options |= CharacterDataOldValue;
102
103 bool characterData = false;
104 bool characterDataPresent = optionsDictionary.get("characterData", characterData);
105 if (characterData || (!characterDataPresent && characterDataOldValuePresent))
106 options |= CharacterData;
107
108 bool childListValue = false;
109 if (optionsDictionary.get("childList", childListValue) && childListValue)
110 options |= ChildList;
111
112 bool subtreeValue = false;
113 if (optionsDictionary.get("subtree", subtreeValue) && subtreeValue)
114 options |= Subtree;
115
116 if (!(options & Attributes)) {
117 if (options & AttributeOldValue) {
118 exceptionState.throwDOMException(TypeError, "The options object may only set 'attributeOldValue' to true when 'attributes' is true or not present.");
119 return;
120 }
121 if (options & AttributeFilter) {
122 exceptionState.throwDOMException(TypeError, "The options object may only set 'attributeFilter' when 'attributes' is true or not present.");
123 return;
124 }
125 }
126 if (!((options & CharacterData) || !(options & CharacterDataOldValue))) {
127 exceptionState.throwDOMException(TypeError, "The options object may only set 'characterDataOldValue' to true when 'characterData' is true or not present.");
128 return;
129 }
130
131 if (!(options & (Attributes | CharacterData | ChildList))) {
132 exceptionState.throwDOMException(TypeError, "The options object must set at least one of 'attributes', 'characterData', or 'childList' to true.");
133 return;
134 }
135
136 node->registerMutationObserver(this, options, attributeFilter);
137 }
138
takeRecords()139 Vector<RefPtr<MutationRecord> > MutationObserver::takeRecords()
140 {
141 Vector<RefPtr<MutationRecord> > records;
142 records.swap(m_records);
143 return records;
144 }
145
disconnect()146 void MutationObserver::disconnect()
147 {
148 m_records.clear();
149 HashSet<MutationObserverRegistration*> registrations(m_registrations);
150 for (HashSet<MutationObserverRegistration*>::iterator iter = registrations.begin(); iter != registrations.end(); ++iter)
151 (*iter)->unregister();
152 }
153
observationStarted(MutationObserverRegistration * registration)154 void MutationObserver::observationStarted(MutationObserverRegistration* registration)
155 {
156 ASSERT(!m_registrations.contains(registration));
157 m_registrations.add(registration);
158 }
159
observationEnded(MutationObserverRegistration * registration)160 void MutationObserver::observationEnded(MutationObserverRegistration* registration)
161 {
162 ASSERT(m_registrations.contains(registration));
163 m_registrations.remove(registration);
164 }
165
166 typedef HashSet<RefPtr<MutationObserver> > MutationObserverSet;
167
activeMutationObservers()168 static MutationObserverSet& activeMutationObservers()
169 {
170 DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ());
171 return activeObservers;
172 }
173
suspendedMutationObservers()174 static MutationObserverSet& suspendedMutationObservers()
175 {
176 DEFINE_STATIC_LOCAL(MutationObserverSet, suspendedObservers, ());
177 return suspendedObservers;
178 }
179
enqueueMutationRecord(PassRefPtr<MutationRecord> mutation)180 void MutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation)
181 {
182 ASSERT(isMainThread());
183 m_records.append(mutation);
184 activeMutationObservers().add(this);
185 }
186
setHasTransientRegistration()187 void MutationObserver::setHasTransientRegistration()
188 {
189 ASSERT(isMainThread());
190 activeMutationObservers().add(this);
191 }
192
getObservedNodes() const193 HashSet<Node*> MutationObserver::getObservedNodes() const
194 {
195 HashSet<Node*> observedNodes;
196 for (HashSet<MutationObserverRegistration*>::const_iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter)
197 (*iter)->addRegistrationNodesToSet(observedNodes);
198 return observedNodes;
199 }
200
canDeliver()201 bool MutationObserver::canDeliver()
202 {
203 return !m_callback->executionContext()->activeDOMObjectsAreSuspended();
204 }
205
deliver()206 void MutationObserver::deliver()
207 {
208 ASSERT(canDeliver());
209
210 // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary
211 // to make a copy of the transient registrations before operating on them.
212 Vector<MutationObserverRegistration*, 1> transientRegistrations;
213 for (HashSet<MutationObserverRegistration*>::iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter) {
214 if ((*iter)->hasTransientRegistrations())
215 transientRegistrations.append(*iter);
216 }
217 for (size_t i = 0; i < transientRegistrations.size(); ++i)
218 transientRegistrations[i]->clearTransientRegistrations();
219
220 if (m_records.isEmpty())
221 return;
222
223 Vector<RefPtr<MutationRecord> > records;
224 records.swap(m_records);
225
226 m_callback->call(records, this);
227 }
228
deliverAllMutations()229 void MutationObserver::deliverAllMutations()
230 {
231 ASSERT(isMainThread());
232 static bool deliveryInProgress = false;
233 if (deliveryInProgress)
234 return;
235 deliveryInProgress = true;
236
237 if (!suspendedMutationObservers().isEmpty()) {
238 Vector<RefPtr<MutationObserver> > suspended;
239 copyToVector(suspendedMutationObservers(), suspended);
240 for (size_t i = 0; i < suspended.size(); ++i) {
241 if (!suspended[i]->canDeliver())
242 continue;
243
244 suspendedMutationObservers().remove(suspended[i]);
245 activeMutationObservers().add(suspended[i]);
246 }
247 }
248
249 while (!activeMutationObservers().isEmpty()) {
250 Vector<RefPtr<MutationObserver> > observers;
251 copyToVector(activeMutationObservers(), observers);
252 activeMutationObservers().clear();
253 std::sort(observers.begin(), observers.end(), ObserverLessThan());
254 for (size_t i = 0; i < observers.size(); ++i) {
255 if (observers[i]->canDeliver())
256 observers[i]->deliver();
257 else
258 suspendedMutationObservers().add(observers[i]);
259 }
260 }
261
262 deliveryInProgress = false;
263 }
264
265 } // namespace WebCore
266