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 { 50 bool operator()(const RefPtr<MutationObserver>& lhs, const RefPtr<MutationObserver>& rhs) 51 { 52 return lhs->m_priority < rhs->m_priority; 53 } 54 }; 55 56 PassRefPtr<MutationObserver> MutationObserver::create(PassOwnPtr<MutationCallback> callback) 57 { 58 ASSERT(isMainThread()); 59 return adoptRef(new MutationObserver(callback)); 60 } 61 62 MutationObserver::MutationObserver(PassOwnPtr<MutationCallback> callback) 63 : m_callback(callback) 64 , m_priority(s_observerPriority++) 65 { 66 ScriptWrappable::init(this); 67 } 68 69 MutationObserver::~MutationObserver() 70 { 71 ASSERT(m_registrations.isEmpty()); 72 } 73 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 139 Vector<RefPtr<MutationRecord> > MutationObserver::takeRecords() 140 { 141 Vector<RefPtr<MutationRecord> > records; 142 records.swap(m_records); 143 return records; 144 } 145 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 154 void MutationObserver::observationStarted(MutationObserverRegistration* registration) 155 { 156 ASSERT(!m_registrations.contains(registration)); 157 m_registrations.add(registration); 158 } 159 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 168 static MutationObserverSet& activeMutationObservers() 169 { 170 DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ()); 171 return activeObservers; 172 } 173 174 static MutationObserverSet& suspendedMutationObservers() 175 { 176 DEFINE_STATIC_LOCAL(MutationObserverSet, suspendedObservers, ()); 177 return suspendedObservers; 178 } 179 180 void MutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation) 181 { 182 ASSERT(isMainThread()); 183 m_records.append(mutation); 184 activeMutationObservers().add(this); 185 } 186 187 void MutationObserver::setHasTransientRegistration() 188 { 189 ASSERT(isMainThread()); 190 activeMutationObservers().add(this); 191 } 192 193 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 201 bool MutationObserver::canDeliver() 202 { 203 return !m_callback->executionContext()->activeDOMObjectsAreSuspended(); 204 } 205 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 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