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