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 { 52 bool operator()(const RefPtrWillBeMember<MutationObserver>& lhs, const RefPtrWillBeMember<MutationObserver>& rhs) 53 { 54 return lhs->m_priority < rhs->m_priority; 55 } 56 }; 57 58 PassRefPtrWillBeRawPtr<MutationObserver> MutationObserver::create(PassOwnPtr<MutationCallback> callback) 59 { 60 ASSERT(isMainThread()); 61 return adoptRefWillBeNoop(new MutationObserver(callback)); 62 } 63 64 MutationObserver::MutationObserver(PassOwnPtr<MutationCallback> callback) 65 : m_callback(callback) 66 , m_priority(s_observerPriority++) 67 { 68 ScriptWrappable::init(this); 69 } 70 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 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 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 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 163 void MutationObserver::observationStarted(MutationObserverRegistration* registration) 164 { 165 ASSERT(!m_registrations.contains(registration)); 166 m_registrations.add(registration); 167 } 168 169 void MutationObserver::observationEnded(MutationObserverRegistration* registration) 170 { 171 ASSERT(m_registrations.contains(registration)); 172 m_registrations.remove(registration); 173 } 174 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 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 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 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 213 void MutationObserver::setHasTransientRegistration() 214 { 215 ASSERT(isMainThread()); 216 activateObserver(this); 217 } 218 219 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 227 bool MutationObserver::canDeliver() 228 { 229 return !m_callback->executionContext()->activeDOMObjectsAreSuspended(); 230 } 231 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 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 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 288 void MutationObserver::trace(Visitor* visitor) 289 { 290 visitor->trace(m_records); 291 visitor->trace(m_registrations); 292 } 293 294 } // namespace WebCore 295