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 "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 
     46 namespace blink {
     47 
     48 static unsigned s_observerPriority = 0;
     49 
     50 struct 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 
     57 PassRefPtrWillBeRawPtr<MutationObserver> MutationObserver::create(PassOwnPtr<MutationCallback> callback)
     58 {
     59     ASSERT(isMainThread());
     60     return adoptRefWillBeNoop(new MutationObserver(callback));
     61 }
     62 
     63 MutationObserver::MutationObserver(PassOwnPtr<MutationCallback> callback)
     64     : m_callback(callback)
     65     , m_priority(s_observerPriority++)
     66 {
     67 }
     68 
     69 MutationObserver::~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 
     78 void 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 
    138 MutationRecordVector MutationObserver::takeRecords()
    139 {
    140     MutationRecordVector records;
    141     records.swap(m_records);
    142     InspectorInstrumentation::didClearAllMutationRecords(m_callback->executionContext(), this);
    143     return records;
    144 }
    145 
    146 void 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 
    156 void MutationObserver::observationStarted(MutationObserverRegistration* registration)
    157 {
    158     ASSERT(!m_registrations.contains(registration));
    159     m_registrations.add(registration);
    160 }
    161 
    162 void MutationObserver::observationEnded(MutationObserverRegistration* registration)
    163 {
    164     ASSERT(m_registrations.contains(registration));
    165     m_registrations.remove(registration);
    166 }
    167 
    168 static MutationObserverSet& activeMutationObservers()
    169 {
    170     DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<MutationObserverSet>, activeObservers, (adoptPtrWillBeNoop(new MutationObserverSet())));
    171     return *activeObservers;
    172 }
    173 
    174 static MutationObserverSet& suspendedMutationObservers()
    175 {
    176     DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<MutationObserverSet>, suspendedObservers, (adoptPtrWillBeNoop(new MutationObserverSet())));
    177     return *suspendedObservers;
    178 }
    179 
    180 static void activateObserver(PassRefPtrWillBeRawPtr<MutationObserver> observer)
    181 {
    182     if (activeMutationObservers().isEmpty())
    183         Microtask::enqueueMicrotask(WTF::bind(&MutationObserver::deliverMutations));
    184 
    185     activeMutationObservers().add(observer);
    186 }
    187 
    188 void 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 
    196 void MutationObserver::setHasTransientRegistration()
    197 {
    198     ASSERT(isMainThread());
    199     activateObserver(this);
    200 }
    201 
    202 WillBeHeapHashSet<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 
    210 bool MutationObserver::canDeliver()
    211 {
    212     return !m_callback->executionContext()->activeDOMObjectsAreSuspended();
    213 }
    214 
    215 void 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 
    240 void 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 
    256 void 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 
    271 void 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