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