slotchange event doesn't get fired when inserting, removing, or renaming slot elements
[WebKit-https.git] / Source / WebCore / html / HTMLSlotElement.cpp
1 /*
2  * Copyright (C) 2015 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 "HTMLSlotElement.h"
28
29 #include "Event.h"
30 #include "EventNames.h"
31 #include "HTMLNames.h"
32 #include "MutationObserver.h"
33 #include "ShadowRoot.h"
34 #include "Text.h"
35 #include <wtf/IsoMallocInlines.h>
36
37 namespace WebCore {
38
39 WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLSlotElement);
40
41 using namespace HTMLNames;
42
43 Ref<HTMLSlotElement> HTMLSlotElement::create(const QualifiedName& tagName, Document& document)
44 {
45     return adoptRef(*new HTMLSlotElement(tagName, document));
46 }
47
48 HTMLSlotElement::HTMLSlotElement(const QualifiedName& tagName, Document& document)
49     : HTMLElement(tagName, document)
50 {
51     ASSERT(hasTagName(slotTag));
52 }
53
54 HTMLSlotElement::InsertedIntoAncestorResult HTMLSlotElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
55 {
56     auto insertionResult = HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
57     ASSERT_UNUSED(insertionResult, insertionResult == InsertedIntoAncestorResult::Done);
58
59     if (insertionType.treeScopeChanged && isInShadowTree()) {
60         if (auto* shadowRoot = containingShadowRoot())
61             shadowRoot->addSlotElementByName(attributeWithoutSynchronization(nameAttr), *this);
62     }
63
64     return InsertedIntoAncestorResult::Done;
65 }
66
67 void HTMLSlotElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
68 {
69     if (removalType.treeScopeChanged && oldParentOfRemovedTree.isInShadowTree()) {
70         auto* oldShadowRoot = oldParentOfRemovedTree.containingShadowRoot();
71         ASSERT(oldShadowRoot);
72         oldShadowRoot->removeSlotElementByName(attributeWithoutSynchronization(nameAttr), *this, oldParentOfRemovedTree);
73     }
74
75     HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
76 }
77
78 void HTMLSlotElement::childrenChanged(const ChildChange& childChange)
79 {
80     HTMLElement::childrenChanged(childChange);
81
82     if (isInShadowTree()) {
83         if (auto* shadowRoot = containingShadowRoot())
84             shadowRoot->slotFallbackDidChange(*this);
85     }
86 }
87
88 void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
89 {
90     HTMLElement::attributeChanged(name, oldValue, newValue, reason);
91
92     if (isInShadowTree() && name == nameAttr) {
93         if (auto shadowRoot = makeRefPtr(containingShadowRoot()))
94             shadowRoot->renameSlotElement(*this, oldValue, newValue);
95     }
96 }
97
98 const Vector<Node*>* HTMLSlotElement::assignedNodes() const
99 {
100     auto shadowRoot = makeRefPtr(containingShadowRoot());
101     if (!shadowRoot)
102         return nullptr;
103
104     return shadowRoot->assignedNodesForSlot(*this);
105 }
106
107 static void flattenAssignedNodes(Vector<Ref<Node>>& nodes, const HTMLSlotElement& slot)
108 {
109     auto* assignedNodes = slot.assignedNodes();
110     if (!assignedNodes) {
111         for (RefPtr<Node> child = slot.firstChild(); child; child = child->nextSibling()) {
112             if (is<HTMLSlotElement>(*child))
113                 flattenAssignedNodes(nodes, downcast<HTMLSlotElement>(*child));
114             else if (is<Text>(*child) || is<Element>(*child))
115                 nodes.append(*child);
116         }
117         return;
118     }
119     for (RefPtr<Node> node : *assignedNodes) {
120         if (is<HTMLSlotElement>(*node))
121             flattenAssignedNodes(nodes, downcast<HTMLSlotElement>(*node));
122         else
123             nodes.append(*node);
124     }
125 }
126
127 Vector<Ref<Node>> HTMLSlotElement::assignedNodes(const AssignedNodesOptions& options) const
128 {
129     if (options.flatten) {
130         if (!isInShadowTree())
131             return { };
132         Vector<Ref<Node>> nodes;
133         flattenAssignedNodes(nodes, *this);
134         return nodes;
135     }
136     auto* assignedNodes = this->assignedNodes();
137     if (!assignedNodes)
138         return { };
139     return assignedNodes->map([] (Node* node) { return makeRef(*node); });
140 }
141
142 Vector<Ref<Element>> HTMLSlotElement::assignedElements(const AssignedNodesOptions& options) const
143 {
144     auto nodes = assignedNodes(options);
145
146     Vector<Ref<Element>> elements;
147     elements.reserveCapacity(nodes.size());
148     for (auto& node : nodes) {
149         if (is<Element>(node))
150             elements.uncheckedAppend(static_reference_cast<Element>(WTFMove(node)));
151     }
152
153     return elements;
154 }
155
156 void HTMLSlotElement::enqueueSlotChangeEvent()
157 {
158     // https://dom.spec.whatwg.org/#signal-a-slot-change
159     if (m_inSignalSlotList)
160         return;
161     m_inSignalSlotList = true;
162     MutationObserver::enqueueSlotChangeEvent(*this);
163 }
164
165 void HTMLSlotElement::dispatchSlotChangeEvent()
166 {
167     m_inSignalSlotList = false;
168
169     Ref<Event> event = Event::create(eventNames().slotchangeEvent, Event::CanBubble::Yes, Event::IsCancelable::No);
170     event->setTarget(this);
171     dispatchEvent(event);
172 }
173
174 }
175