diff options
Diffstat (limited to 'Source/WebCore/dom/MutationObserver.cpp')
-rw-r--r-- | Source/WebCore/dom/MutationObserver.cpp | 197 |
1 files changed, 127 insertions, 70 deletions
diff --git a/Source/WebCore/dom/MutationObserver.cpp b/Source/WebCore/dom/MutationObserver.cpp index bf00fac40..0b4b1f90d 100644 --- a/Source/WebCore/dom/MutationObserver.cpp +++ b/Source/WebCore/dom/MutationObserver.cpp @@ -32,9 +32,10 @@ #include "MutationObserver.h" -#include "Dictionary.h" #include "Document.h" #include "ExceptionCode.h" +#include "HTMLSlotElement.h" +#include "Microtasks.h" #include "MutationCallback.h" #include "MutationObserverRegistration.h" #include "MutationRecord.h" @@ -45,14 +46,14 @@ namespace WebCore { static unsigned s_observerPriority = 0; -PassRefPtr<MutationObserver> MutationObserver::create(PassRefPtr<MutationCallback> callback) +Ref<MutationObserver> MutationObserver::create(Ref<MutationCallback>&& callback) { ASSERT(isMainThread()); - return adoptRef(new MutationObserver(callback)); + return adoptRef(*new MutationObserver(WTFMove(callback))); } -MutationObserver::MutationObserver(PassRefPtr<MutationCallback> callback) - : m_callback(callback) +MutationObserver::MutationObserver(Ref<MutationCallback>&& callback) + : m_callback(WTFMove(callback)) , m_priority(s_observerPriority++) { } @@ -70,46 +71,43 @@ bool MutationObserver::validateOptions(MutationObserverOptions options) && ((options & CharacterData) || !(options & CharacterDataOldValue)); } -void MutationObserver::observe(Node* node, const Dictionary& optionsDictionary, ExceptionCode& ec) +ExceptionOr<void> MutationObserver::observe(Node& node, const Init& init) { - if (!node) { - ec = NOT_FOUND_ERR; - return; - } - - static const struct { - const char* name; - MutationObserverOptions value; - } booleanOptions[] = { - { "childList", ChildList }, - { "attributes", Attributes }, - { "characterData", CharacterData }, - { "subtree", Subtree }, - { "attributeOldValue", AttributeOldValue }, - { "characterDataOldValue", CharacterDataOldValue } - }; MutationObserverOptions options = 0; - for (unsigned i = 0; i < sizeof(booleanOptions) / sizeof(booleanOptions[0]); ++i) { - bool value = false; - if (optionsDictionary.get(booleanOptions[i].name, value) && value) - options |= booleanOptions[i].value; - } + + if (init.childList) + options |= ChildList; + if (init.subtree) + options |= Subtree; + if (init.attributeOldValue.value_or(false)) + options |= AttributeOldValue; + if (init.characterDataOldValue.value_or(false)) + options |= CharacterDataOldValue; HashSet<AtomicString> attributeFilter; - if (optionsDictionary.get("attributeFilter", attributeFilter)) + if (init.attributeFilter) { + for (auto& value : init.attributeFilter.value()) + attributeFilter.add(value); options |= AttributeFilter; - - if (!validateOptions(options)) { - ec = SYNTAX_ERR; - return; } - node->registerMutationObserver(this, options, attributeFilter); + if (init.attributes ? init.attributes.value() : (options & (AttributeFilter | AttributeOldValue))) + options |= Attributes; + + if (init.characterData ? init.characterData.value() : (options & CharacterDataOldValue)) + options |= CharacterData; + + if (!validateOptions(options)) + return Exception { TypeError }; + + node.registerMutationObserver(*this, options, attributeFilter); + + return { }; } -Vector<RefPtr<MutationRecord>> MutationObserver::takeRecords() +Vector<Ref<MutationRecord>> MutationObserver::takeRecords() { - Vector<RefPtr<MutationRecord>> records; + Vector<Ref<MutationRecord>> records; records.swap(m_records); return records; } @@ -118,60 +116,100 @@ void MutationObserver::disconnect() { m_records.clear(); HashSet<MutationObserverRegistration*> registrations(m_registrations); - for (HashSet<MutationObserverRegistration*>::iterator iter = registrations.begin(); iter != registrations.end(); ++iter) - MutationObserverRegistration::unregisterAndDelete(*iter); + for (auto* registration : registrations) + registration->node().unregisterMutationObserver(*registration); } -void MutationObserver::observationStarted(MutationObserverRegistration* registration) +void MutationObserver::observationStarted(MutationObserverRegistration& registration) { - ASSERT(!m_registrations.contains(registration)); - m_registrations.add(registration); + ASSERT(!m_registrations.contains(®istration)); + m_registrations.add(®istration); } -void MutationObserver::observationEnded(MutationObserverRegistration* registration) +void MutationObserver::observationEnded(MutationObserverRegistration& registration) { - ASSERT(m_registrations.contains(registration)); - m_registrations.remove(registration); + ASSERT(m_registrations.contains(®istration)); + m_registrations.remove(®istration); } typedef HashSet<RefPtr<MutationObserver>> MutationObserverSet; static MutationObserverSet& activeMutationObservers() { - DEFINE_STATIC_LOCAL(MutationObserverSet, activeObservers, ()); + static NeverDestroyed<MutationObserverSet> activeObservers; return activeObservers; } static MutationObserverSet& suspendedMutationObservers() { - DEFINE_STATIC_LOCAL(MutationObserverSet, suspendedObservers, ()); + static NeverDestroyed<MutationObserverSet> suspendedObservers; return suspendedObservers; } -void MutationObserver::enqueueMutationRecord(PassRefPtr<MutationRecord> mutation) +// https://dom.spec.whatwg.org/#signal-slot-list +static Vector<RefPtr<HTMLSlotElement>>& signalSlotList() +{ + static NeverDestroyed<Vector<RefPtr<HTMLSlotElement>>> list; + return list; +} + +static bool mutationObserverCompoundMicrotaskQueuedFlag; + +class MutationObserverMicrotask final : public Microtask { + WTF_MAKE_FAST_ALLOCATED; +private: + Result run() final + { + MutationObserver::notifyMutationObservers(); + return Result::Done; + } +}; + +static void queueMutationObserverCompoundMicrotask() +{ + if (mutationObserverCompoundMicrotaskQueuedFlag) + return; + mutationObserverCompoundMicrotaskQueuedFlag = true; + MicrotaskQueue::mainThreadQueue().append(std::make_unique<MutationObserverMicrotask>()); +} + +void MutationObserver::enqueueMutationRecord(Ref<MutationRecord>&& mutation) { ASSERT(isMainThread()); - m_records.append(mutation); + m_records.append(WTFMove(mutation)); activeMutationObservers().add(this); + + queueMutationObserverCompoundMicrotask(); +} + +void MutationObserver::enqueueSlotChangeEvent(HTMLSlotElement& slot) +{ + ASSERT(isMainThread()); + ASSERT(!signalSlotList().contains(&slot)); + signalSlotList().append(&slot); + + queueMutationObserverCompoundMicrotask(); } void MutationObserver::setHasTransientRegistration() { ASSERT(isMainThread()); activeMutationObservers().add(this); + + queueMutationObserverCompoundMicrotask(); } -HashSet<Node*> MutationObserver::getObservedNodes() const +HashSet<Node*> MutationObserver::observedNodes() const { HashSet<Node*> observedNodes; - for (HashSet<MutationObserverRegistration*>::const_iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter) - (*iter)->addRegistrationNodesToSet(observedNodes); + for (auto* registration : m_registrations) + registration->addRegistrationNodesToSet(observedNodes); return observedNodes; } bool MutationObserver::canDeliver() { - return !m_callback->scriptExecutionContext()->activeDOMObjectsAreSuspended(); + return m_callback->canInvokeCallback(); } void MutationObserver::deliver() @@ -181,24 +219,28 @@ void MutationObserver::deliver() // Calling clearTransientRegistrations() can modify m_registrations, so it's necessary // to make a copy of the transient registrations before operating on them. Vector<MutationObserverRegistration*, 1> transientRegistrations; - for (HashSet<MutationObserverRegistration*>::iterator iter = m_registrations.begin(); iter != m_registrations.end(); ++iter) { - if ((*iter)->hasTransientRegistrations()) - transientRegistrations.append(*iter); + for (auto* registration : m_registrations) { + if (registration->hasTransientRegistrations()) + transientRegistrations.append(registration); } - for (size_t i = 0; i < transientRegistrations.size(); ++i) - transientRegistrations[i]->clearTransientRegistrations(); + for (auto& registration : transientRegistrations) + registration->clearTransientRegistrations(); if (m_records.isEmpty()) return; - Vector<RefPtr<MutationRecord>> records; + Vector<Ref<MutationRecord>> records; records.swap(m_records); m_callback->call(records, this); } -void MutationObserver::deliverAllMutations() +void MutationObserver::notifyMutationObservers() { + // https://dom.spec.whatwg.org/#notify-mutation-observers + // 1. Unset mutation observer compound microtask queued flag. + mutationObserverCompoundMicrotaskQueuedFlag = false; + ASSERT(isMainThread()); static bool deliveryInProgress = false; if (deliveryInProgress) @@ -208,29 +250,44 @@ void MutationObserver::deliverAllMutations() if (!suspendedMutationObservers().isEmpty()) { Vector<RefPtr<MutationObserver>> suspended; copyToVector(suspendedMutationObservers(), suspended); - for (size_t i = 0; i < suspended.size(); ++i) { - if (!suspended[i]->canDeliver()) + for (auto& observer : suspended) { + if (!observer->canDeliver()) continue; - suspendedMutationObservers().remove(suspended[i]); - activeMutationObservers().add(suspended[i]); + suspendedMutationObservers().remove(observer); + activeMutationObservers().add(observer); } } - while (!activeMutationObservers().isEmpty()) { - Vector<RefPtr<MutationObserver>> observers; - copyToVector(activeMutationObservers(), observers); + while (!activeMutationObservers().isEmpty() || !signalSlotList().isEmpty()) { + // 2. Let notify list be a copy of unit of related similar-origin browsing contexts' list of MutationObserver objects. + Vector<RefPtr<MutationObserver>> notifyList; + copyToVector(activeMutationObservers(), notifyList); activeMutationObservers().clear(); - std::sort(observers.begin(), observers.end(), [](const RefPtr<MutationObserver>& lhs, const RefPtr<MutationObserver>& rhs) { + std::sort(notifyList.begin(), notifyList.end(), [](auto& lhs, auto& rhs) { return lhs->m_priority < rhs->m_priority; }); - for (size_t i = 0; i < observers.size(); ++i) { - if (observers[i]->canDeliver()) - observers[i]->deliver(); + // 3. Let signalList be a copy of unit of related similar-origin browsing contexts' signal slot list. + // 4. Empty unit of related similar-origin browsing contexts' signal slot list. + Vector<RefPtr<HTMLSlotElement>> slotList; + if (!signalSlotList().isEmpty()) { + slotList.swap(signalSlotList()); + for (auto& slot : slotList) + slot->didRemoveFromSignalSlotList(); + } + + // 5. For each MutationObserver object mo in notify list, execute a compound microtask subtask + for (auto& observer : notifyList) { + if (observer->canDeliver()) + observer->deliver(); else - suspendedMutationObservers().add(observers[i]); + suspendedMutationObservers().add(observer); } + + // 6. For each slot slot in signalList, in order, fire an event named slotchange, with its bubbles attribute set to true, at slot. + for (auto& slot : slotList) + slot->dispatchSlotChangeEvent(); } deliveryInProgress = false; |