CSSStyleDeclaration should be annotated with CEReactions
[WebKit-https.git] / Source / WebCore / dom / CustomElementReactionQueue.cpp
1 /*
2  * Copyright (C) 2015, 2016 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CustomElementReactionQueue.h"
28
29 #if ENABLE(CUSTOM_ELEMENTS)
30
31 #include "CustomElementRegistry.h"
32 #include "DOMWindow.h"
33 #include "Document.h"
34 #include "Element.h"
35 #include "HTMLNames.h"
36 #include "JSCustomElementInterface.h"
37 #include "JSDOMBinding.h"
38 #include "Microtasks.h"
39 #include <heap/Heap.h>
40 #include <wtf/Optional.h>
41 #include <wtf/Ref.h>
42
43 namespace WebCore {
44
45 class CustomElementReactionQueueItem {
46 public:
47     enum class Type {
48         ElementUpgrade,
49         Connected,
50         Disconnected,
51         Adopted,
52         AttributeChanged,
53     };
54
55     CustomElementReactionQueueItem(Type type)
56         : m_type(type)
57     { }
58
59     CustomElementReactionQueueItem(Document& oldDocument, Document& newDocument)
60         : m_type(Type::Adopted)
61         , m_oldDocument(&oldDocument)
62         , m_newDocument(&newDocument)
63     { }
64
65     CustomElementReactionQueueItem(const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
66         : m_type(Type::AttributeChanged)
67         , m_attributeName(attributeName)
68         , m_oldValue(oldValue)
69         , m_newValue(newValue)
70     { }
71
72     void invoke(Element& element, JSCustomElementInterface& elementInterface)
73     {
74         switch (m_type) {
75         case Type::ElementUpgrade:
76             elementInterface.upgradeElement(element);
77             break;
78         case Type::Connected:
79             elementInterface.invokeConnectedCallback(element);
80             break;
81         case Type::Disconnected:
82             elementInterface.invokeDisconnectedCallback(element);
83             break;
84         case Type::Adopted:
85             elementInterface.invokeAdoptedCallback(element, *m_oldDocument, *m_newDocument);
86             break;
87         case Type::AttributeChanged:
88             ASSERT(m_attributeName);
89             elementInterface.invokeAttributeChangedCallback(element, m_attributeName.value(), m_oldValue, m_newValue);
90             break;
91         }
92     }
93
94 private:
95     Type m_type;
96     RefPtr<Document> m_oldDocument;
97     RefPtr<Document> m_newDocument;
98     Optional<QualifiedName> m_attributeName;
99     AtomicString m_oldValue;
100     AtomicString m_newValue;
101 };
102
103 CustomElementReactionQueue::CustomElementReactionQueue(JSCustomElementInterface& elementInterface)
104     : m_interface(elementInterface)
105 { }
106
107 CustomElementReactionQueue::~CustomElementReactionQueue()
108 {
109     ASSERT(m_items.isEmpty());
110 }
111
112 void CustomElementReactionQueue::clear()
113 {
114     m_items.clear();
115 }
116
117 void CustomElementReactionQueue::enqueueElementUpgrade(Element& element)
118 {
119     auto& queue = CustomElementReactionStack::ensureCurrentQueue(element);
120     queue.m_items.append({CustomElementReactionQueueItem::Type::ElementUpgrade});
121 }
122
123 void CustomElementReactionQueue::enqueueElementUpgradeIfDefined(Element& element)
124 {
125     ASSERT(element.inDocument());
126     ASSERT(element.isCustomElementUpgradeCandidate());
127     auto* window = element.document().domWindow();
128     if (!window)
129         return;
130
131     auto* registry = window->customElementRegistry();
132     if (!registry)
133         return;
134
135     auto* elementInterface = registry->findInterface(element);
136     if (!elementInterface)
137         return;
138
139     element.enqueueToUpgrade(*elementInterface);
140 }
141
142 void CustomElementReactionQueue::enqueueConnectedCallbackIfNeeded(Element& element)
143 {
144     ASSERT(element.isDefinedCustomElement());
145     auto& queue = CustomElementReactionStack::ensureCurrentQueue(element);
146     if (queue.m_interface->hasConnectedCallback())
147         queue.m_items.append({CustomElementReactionQueueItem::Type::Connected});
148 }
149
150 void CustomElementReactionQueue::enqueueDisconnectedCallbackIfNeeded(Element& element)
151 {
152     ASSERT(element.isDefinedCustomElement());
153     auto& queue = CustomElementReactionStack::ensureCurrentQueue(element);
154     if (queue.m_interface->hasDisconnectedCallback())
155         queue.m_items.append({CustomElementReactionQueueItem::Type::Disconnected});
156 }
157
158 void CustomElementReactionQueue::enqueueAdoptedCallbackIfNeeded(Element& element, Document& oldDocument, Document& newDocument)
159 {
160     ASSERT(element.isDefinedCustomElement());
161     auto& queue = CustomElementReactionStack::ensureCurrentQueue(element);
162     if (queue.m_interface->hasAdoptedCallback())
163         queue.m_items.append({oldDocument, newDocument});
164 }
165
166 void CustomElementReactionQueue::enqueueAttributeChangedCallbackIfNeeded(Element& element, const QualifiedName& attributeName, const AtomicString& oldValue, const AtomicString& newValue)
167 {
168     ASSERT(element.isDefinedCustomElement());
169     auto& queue = CustomElementReactionStack::ensureCurrentQueue(element);
170     if (queue.m_interface->observesAttribute(attributeName.localName()))
171         queue.m_items.append({attributeName, oldValue, newValue});
172 }
173
174 void CustomElementReactionQueue::enqueuePostUpgradeReactions(Element& element)
175 {
176     ASSERT(element.isCustomElementUpgradeCandidate());
177     if (!element.hasAttributes() && !element.inDocument())
178         return;
179
180     auto* queue = element.reactionQueue();
181     ASSERT(queue);
182
183     if (element.hasAttributes()) {
184         for (auto& attribute : element.attributesIterator()) {
185             if (queue->m_interface->observesAttribute(attribute.localName()))
186                 queue->m_items.append({attribute.name(), nullAtom, attribute.value()});
187         }
188     }
189
190     if (element.inDocument() && queue->m_interface->hasConnectedCallback())
191         queue->m_items.append({CustomElementReactionQueueItem::Type::Connected});
192 }
193
194 bool CustomElementReactionQueue::observesStyleAttribute() const
195 {
196     return m_interface->observesAttribute(HTMLNames::styleAttr.localName());
197 }
198
199 void CustomElementReactionQueue::invokeAll(Element& element)
200 {
201     while (!m_items.isEmpty()) {
202         Vector<CustomElementReactionQueueItem> items = WTFMove(m_items);
203         for (auto& item : items)
204             item.invoke(element, m_interface.get());
205     }
206 }
207
208 inline void CustomElementReactionStack::ElementQueue::add(Element& element)
209 {
210     // FIXME: Avoid inserting the same element multiple times.
211     m_elements.append(element);
212 }
213
214 inline void CustomElementReactionStack::ElementQueue::invokeAll()
215 {
216     Vector<Ref<Element>> elements;
217     elements.swap(m_elements);
218     for (auto& element : elements) {
219         auto* queue = element->reactionQueue();
220         ASSERT(queue);
221         queue->invokeAll(element.get());
222     }
223     ASSERT(m_elements.isEmpty());
224 }
225
226 CustomElementReactionQueue& CustomElementReactionStack::ensureCurrentQueue(Element& element)
227 {
228     ASSERT(element.reactionQueue());
229     if (!s_currentProcessingStack) {
230         auto& queue = CustomElementReactionStack::ensureBackupQueue();
231         queue.add(element);
232         return *element.reactionQueue();
233     }
234
235     auto*& queue = s_currentProcessingStack->m_queue;
236     if (!queue) // We use a raw pointer to avoid genearing code to delete it in ~CustomElementReactionStack.
237         queue = new ElementQueue;
238     queue->add(element);
239     return *element.reactionQueue();
240 }
241
242 CustomElementReactionStack* CustomElementReactionStack::s_currentProcessingStack = nullptr;
243
244 void CustomElementReactionStack::processQueue()
245 {
246     ASSERT(m_queue);
247     m_queue->invokeAll();
248     delete m_queue;
249     m_queue = nullptr;
250 }
251
252 class BackupElementQueueMicrotask final : public Microtask {
253     WTF_MAKE_FAST_ALLOCATED;
254 private:
255     Result run() final
256     {
257         CustomElementReactionStack::processBackupQueue();
258         return Result::Done;
259     }
260 };
261
262 static bool s_processingBackupElementQueue = false;
263
264 CustomElementReactionStack::ElementQueue& CustomElementReactionStack::ensureBackupQueue()
265 {
266     if (!s_processingBackupElementQueue) {
267         s_processingBackupElementQueue = true;
268         MicrotaskQueue::mainThreadQueue().append(std::make_unique<BackupElementQueueMicrotask>());
269     }
270     return backupElementQueue();
271 }
272
273 void CustomElementReactionStack::processBackupQueue()
274 {
275     backupElementQueue().invokeAll();
276     s_processingBackupElementQueue = false;
277 }
278
279 CustomElementReactionStack::ElementQueue& CustomElementReactionStack::backupElementQueue()
280 {
281     static NeverDestroyed<ElementQueue> queue;
282     return queue.get();
283 }
284
285 }
286
287 #endif