diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/dom/CustomElementReactionQueue.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/dom/CustomElementReactionQueue.cpp')
-rw-r--r-- | Source/WebCore/dom/CustomElementReactionQueue.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/Source/WebCore/dom/CustomElementReactionQueue.cpp b/Source/WebCore/dom/CustomElementReactionQueue.cpp new file mode 100644 index 000000000..a7dab97ad --- /dev/null +++ b/Source/WebCore/dom/CustomElementReactionQueue.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CustomElementReactionQueue.h" + +#include "CustomElementRegistry.h" +#include "DOMWindow.h" +#include "Document.h" +#include "Element.h" +#include "HTMLNames.h" +#include "JSCustomElementInterface.h" +#include "JSDOMBinding.h" +#include "Microtasks.h" +#include <heap/Heap.h> +#include <wtf/Optional.h> +#include <wtf/Ref.h> +#include <wtf/SetForScope.h> + +namespace WebCore { + +class CustomElementReactionQueueItem { +public: + enum class Type { + ElementUpgrade, + Connected, + Disconnected, + Adopted, + AttributeChanged, + }; + + CustomElementReactionQueueItem(Type type) + : m_type(type) + { } + + CustomElementReactionQueueItem(Document& oldDocument, Document& newDocument) + : m_type(Type::Adopted) + , m_oldDocument(&oldDocument) + , m_newDocument(&newDocument) + { } + + CustomElementReactionQueueItem(const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) + : m_type(Type::AttributeChanged) + , m_attributeName(attributeName) + , m_oldValue(oldValue) + , m_newValue(newValue) + { } + + void invoke(Element& element, JSCustomElementInterface& elementInterface) + { + switch (m_type) { + case Type::ElementUpgrade: + elementInterface.upgradeElement(element); + break; + case Type::Connected: + elementInterface.invokeConnectedCallback(element); + break; + case Type::Disconnected: + elementInterface.invokeDisconnectedCallback(element); + break; + case Type::Adopted: + elementInterface.invokeAdoptedCallback(element, *m_oldDocument, *m_newDocument); + break; + case Type::AttributeChanged: + ASSERT(m_attributeName); + elementInterface.invokeAttributeChangedCallback(element, m_attributeName.value(), m_oldValue, m_newValue); + break; + } + } + +private: + Type m_type; + RefPtr<Document> m_oldDocument; + RefPtr<Document> m_newDocument; + std::optional<QualifiedName> m_attributeName; + AtomicString m_oldValue; + AtomicString m_newValue; +}; + +CustomElementReactionQueue::CustomElementReactionQueue(JSCustomElementInterface& elementInterface) + : m_interface(elementInterface) +{ } + +CustomElementReactionQueue::~CustomElementReactionQueue() +{ + ASSERT(m_items.isEmpty()); +} + +void CustomElementReactionQueue::clear() +{ + m_items.clear(); +} + +void CustomElementReactionQueue::enqueueElementUpgrade(Element& element) +{ + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + queue.m_items.append({CustomElementReactionQueueItem::Type::ElementUpgrade}); +} + +void CustomElementReactionQueue::enqueueElementUpgradeIfDefined(Element& element) +{ + ASSERT(element.isConnected()); + ASSERT(element.isCustomElementUpgradeCandidate()); + auto* window = element.document().domWindow(); + if (!window) + return; + + auto* registry = window->customElementRegistry(); + if (!registry) + return; + + auto* elementInterface = registry->findInterface(element); + if (!elementInterface) + return; + + element.enqueueToUpgrade(*elementInterface); +} + +void CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(Element& element) +{ + ASSERT(element.isDefinedCustomElement()); + ASSERT(element.document().refCount() > 0); + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->hasConnectedCallback()) + queue.m_items.append({CustomElementReactionQueueItem::Type::Connected}); +} + +void CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(Element& element) +{ + ASSERT(element.isDefinedCustomElement()); + if (element.document().refCount() <= 0) + return; // Don't enqueue disconnectedCallback if the entire document is getting destructed. + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->hasDisconnectedCallback()) + queue.m_items.append({CustomElementReactionQueueItem::Type::Disconnected}); +} + +void CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(Element& element, Document& oldDocument, Document& newDocument) +{ + ASSERT(element.isDefinedCustomElement()); + ASSERT(element.document().refCount() > 0); + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->hasAdoptedCallback()) + queue.m_items.append({oldDocument, newDocument}); +} + +void CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue) +{ + ASSERT(element.isDefinedCustomElement()); + ASSERT(element.document().refCount() > 0); + auto& queue = CustomElementReactionStack::ensureCurrentQueue(element); + if (queue.m_interface->observesAttribute(attributeName.localName())) + queue.m_items.append({attributeName, oldValue, newValue}); +} + +void CustomElementReactionQueue::enqueuePostUpgradeReactions(Element& element) +{ + ASSERT(element.isCustomElementUpgradeCandidate()); + if (!element.hasAttributes() && !element.isConnected()) + return; + + auto* queue = element.reactionQueue(); + ASSERT(queue); + + if (element.hasAttributes()) { + for (auto& attribute : element.attributesIterator()) { + if (queue->m_interface->observesAttribute(attribute.localName())) + queue->m_items.append({attribute.name(), nullAtom, attribute.value()}); + } + } + + if (element.isConnected() && queue->m_interface->hasConnectedCallback()) + queue->m_items.append({CustomElementReactionQueueItem::Type::Connected}); +} + +bool CustomElementReactionQueue::observesStyleAttribute() const +{ + return m_interface->observesAttribute(HTMLNames::styleAttr.localName()); +} + +void CustomElementReactionQueue::invokeAll(Element& element) +{ + while (!m_items.isEmpty()) { + Vector<CustomElementReactionQueueItem> items = WTFMove(m_items); + for (auto& item : items) + item.invoke(element, m_interface.get()); + } +} + +inline void CustomElementReactionStack::ElementQueue::add(Element& element) +{ + RELEASE_ASSERT(!m_invoking); + // FIXME: Avoid inserting the same element multiple times. + m_elements.append(element); +} + +inline void CustomElementReactionStack::ElementQueue::invokeAll() +{ + RELEASE_ASSERT(!m_invoking); + SetForScope<bool> invoking(m_invoking, true); + Vector<Ref<Element>> elements; + elements.swap(m_elements); + RELEASE_ASSERT(m_elements.isEmpty()); + for (auto& element : elements) { + auto* queue = element->reactionQueue(); + ASSERT(queue); + queue->invokeAll(element.get()); + } + RELEASE_ASSERT(m_elements.isEmpty()); +} + +CustomElementReactionQueue& CustomElementReactionStack::ensureCurrentQueue(Element& element) +{ + ASSERT(element.reactionQueue()); + if (!s_currentProcessingStack) { + auto& queue = CustomElementReactionStack::ensureBackupQueue(); + queue.add(element); + return *element.reactionQueue(); + } + + auto*& queue = s_currentProcessingStack->m_queue; + if (!queue) // We use a raw pointer to avoid genearing code to delete it in ~CustomElementReactionStack. + queue = new ElementQueue; + queue->add(element); + return *element.reactionQueue(); +} + +CustomElementReactionStack* CustomElementReactionStack::s_currentProcessingStack = nullptr; + +void CustomElementReactionStack::processQueue() +{ + ASSERT(m_queue); + m_queue->invokeAll(); + delete m_queue; + m_queue = nullptr; +} + +class BackupElementQueueMicrotask final : public Microtask { + WTF_MAKE_FAST_ALLOCATED; +private: + Result run() final + { + CustomElementReactionStack::processBackupQueue(); + return Result::Done; + } +}; + +static bool s_processingBackupElementQueue = false; + +CustomElementReactionStack::ElementQueue& CustomElementReactionStack::ensureBackupQueue() +{ + if (!s_processingBackupElementQueue) { + s_processingBackupElementQueue = true; + MicrotaskQueue::mainThreadQueue().append(std::make_unique<BackupElementQueueMicrotask>()); + } + return backupElementQueue(); +} + +void CustomElementReactionStack::processBackupQueue() +{ + backupElementQueue().invokeAll(); + s_processingBackupElementQueue = false; +} + +CustomElementReactionStack::ElementQueue& CustomElementReactionStack::backupElementQueue() +{ + static NeverDestroyed<ElementQueue> queue; + return queue.get(); +} + +} |