Home | History | Annotate | Download | only in dom
      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