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