1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "mozilla/dom/DocGroup.h"
8 #include "mozilla/dom/Document.h"
9 #include "mozilla/dom/HTMLSlotElement.h"
10 #include "mozilla/dom/HTMLSlotElementBinding.h"
11 #include "mozilla/dom/HTMLUnknownElement.h"
12 #include "mozilla/dom/ShadowRoot.h"
13 #include "nsContentUtils.h"
14 #include "nsGkAtoms.h"
15
NS_NewHTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo,mozilla::dom::FromParser aFromParser)16 nsGenericHTMLElement* NS_NewHTMLSlotElement(
17 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
18 mozilla::dom::FromParser aFromParser) {
19 RefPtr<mozilla::dom::NodeInfo> nodeInfo(std::move(aNodeInfo));
20 auto* nim = nodeInfo->NodeInfoManager();
21 return new (nim) mozilla::dom::HTMLSlotElement(nodeInfo.forget());
22 }
23
24 namespace mozilla::dom {
25
HTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)26 HTMLSlotElement::HTMLSlotElement(
27 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
28 : nsGenericHTMLElement(std::move(aNodeInfo)) {}
29
30 HTMLSlotElement::~HTMLSlotElement() = default;
31
NS_IMPL_ADDREF_INHERITED(HTMLSlotElement,nsGenericHTMLElement)32 NS_IMPL_ADDREF_INHERITED(HTMLSlotElement, nsGenericHTMLElement)
33 NS_IMPL_RELEASE_INHERITED(HTMLSlotElement, nsGenericHTMLElement)
34
35 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLSlotElement, nsGenericHTMLElement,
36 mAssignedNodes)
37
38 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLSlotElement)
39 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
40
41 NS_IMPL_ELEMENT_CLONE(HTMLSlotElement)
42
43 nsresult HTMLSlotElement::BindToTree(BindContext& aContext, nsINode& aParent) {
44 RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
45
46 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
47 NS_ENSURE_SUCCESS(rv, rv);
48
49 ShadowRoot* containingShadow = GetContainingShadow();
50 if (containingShadow && !oldContainingShadow) {
51 containingShadow->AddSlot(this);
52 }
53
54 return NS_OK;
55 }
56
UnbindFromTree(bool aNullParent)57 void HTMLSlotElement::UnbindFromTree(bool aNullParent) {
58 RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
59
60 nsGenericHTMLElement::UnbindFromTree(aNullParent);
61
62 if (oldContainingShadow && !GetContainingShadow()) {
63 oldContainingShadow->RemoveSlot(this);
64 }
65 }
66
BeforeSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValueOrString * aValue,bool aNotify)67 nsresult HTMLSlotElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
68 const nsAttrValueOrString* aValue,
69 bool aNotify) {
70 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) {
71 if (ShadowRoot* containingShadow = GetContainingShadow()) {
72 containingShadow->RemoveSlot(this);
73 }
74 }
75
76 return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
77 aNotify);
78 }
79
AfterSetAttr(int32_t aNameSpaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)80 nsresult HTMLSlotElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
81 const nsAttrValue* aValue,
82 const nsAttrValue* aOldValue,
83 nsIPrincipal* aSubjectPrincipal,
84 bool aNotify) {
85 if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::name) {
86 if (ShadowRoot* containingShadow = GetContainingShadow()) {
87 containingShadow->AddSlot(this);
88 }
89 }
90
91 return nsGenericHTMLElement::AfterSetAttr(
92 aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
93 }
94
95 /**
96 * Flatten assigned nodes given a slot, as in:
97 * https://dom.spec.whatwg.org/#find-flattened-slotables
98 */
FlattenAssignedNodes(HTMLSlotElement * aSlot,nsTArray<RefPtr<nsINode>> & aNodes)99 static void FlattenAssignedNodes(HTMLSlotElement* aSlot,
100 nsTArray<RefPtr<nsINode>>& aNodes) {
101 if (!aSlot->GetContainingShadow()) {
102 return;
103 }
104
105 const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
106
107 // If assignedNodes is empty, use children of slot as fallback content.
108 if (assignedNodes.IsEmpty()) {
109 for (nsIContent* child = aSlot->GetFirstChild(); child;
110 child = child->GetNextSibling()) {
111 if (!child->IsSlotable()) {
112 continue;
113 }
114
115 if (auto* slot = HTMLSlotElement::FromNode(child)) {
116 FlattenAssignedNodes(slot, aNodes);
117 } else {
118 aNodes.AppendElement(child);
119 }
120 }
121 return;
122 }
123
124 for (const RefPtr<nsINode>& assignedNode : assignedNodes) {
125 auto* slot = HTMLSlotElement::FromNode(assignedNode);
126 if (slot && slot->GetContainingShadow()) {
127 FlattenAssignedNodes(slot, aNodes);
128 } else {
129 aNodes.AppendElement(assignedNode);
130 }
131 }
132 }
133
AssignedNodes(const AssignedNodesOptions & aOptions,nsTArray<RefPtr<nsINode>> & aNodes)134 void HTMLSlotElement::AssignedNodes(const AssignedNodesOptions& aOptions,
135 nsTArray<RefPtr<nsINode>>& aNodes) {
136 if (aOptions.mFlatten) {
137 return FlattenAssignedNodes(this, aNodes);
138 }
139
140 aNodes = mAssignedNodes.Clone();
141 }
142
AssignedElements(const AssignedNodesOptions & aOptions,nsTArray<RefPtr<Element>> & aElements)143 void HTMLSlotElement::AssignedElements(const AssignedNodesOptions& aOptions,
144 nsTArray<RefPtr<Element>>& aElements) {
145 AutoTArray<RefPtr<nsINode>, 128> assignedNodes;
146 AssignedNodes(aOptions, assignedNodes);
147 for (const RefPtr<nsINode>& assignedNode : assignedNodes) {
148 if (assignedNode->IsElement()) {
149 aElements.AppendElement(assignedNode->AsElement());
150 }
151 }
152 }
153
AssignedNodes() const154 const nsTArray<RefPtr<nsINode>>& HTMLSlotElement::AssignedNodes() const {
155 return mAssignedNodes;
156 }
157
InsertAssignedNode(uint32_t aIndex,nsIContent & aNode)158 void HTMLSlotElement::InsertAssignedNode(uint32_t aIndex, nsIContent& aNode) {
159 MOZ_ASSERT(!aNode.GetAssignedSlot(), "Losing track of a slot");
160 mAssignedNodes.InsertElementAt(aIndex, &aNode);
161 aNode.SetAssignedSlot(this);
162 }
163
AppendAssignedNode(nsIContent & aNode)164 void HTMLSlotElement::AppendAssignedNode(nsIContent& aNode) {
165 MOZ_ASSERT(!aNode.GetAssignedSlot(), "Losing track of a slot");
166 mAssignedNodes.AppendElement(&aNode);
167 aNode.SetAssignedSlot(this);
168 }
169
RemoveAssignedNode(nsIContent & aNode)170 void HTMLSlotElement::RemoveAssignedNode(nsIContent& aNode) {
171 // This one runs from unlinking, so we can't guarantee that the slot pointer
172 // hasn't been cleared.
173 MOZ_ASSERT(!aNode.GetAssignedSlot() || aNode.GetAssignedSlot() == this,
174 "How exactly?");
175 mAssignedNodes.RemoveElement(&aNode);
176 aNode.SetAssignedSlot(nullptr);
177 }
178
ClearAssignedNodes()179 void HTMLSlotElement::ClearAssignedNodes() {
180 for (RefPtr<nsINode>& node : mAssignedNodes) {
181 MOZ_ASSERT(!node->AsContent()->GetAssignedSlot() ||
182 node->AsContent()->GetAssignedSlot() == this,
183 "How exactly?");
184 node->AsContent()->SetAssignedSlot(nullptr);
185 }
186
187 mAssignedNodes.Clear();
188 }
189
EnqueueSlotChangeEvent()190 void HTMLSlotElement::EnqueueSlotChangeEvent() {
191 if (mInSignalSlotList) {
192 return;
193 }
194
195 // FIXME(bug 1459704): Need to figure out how to deal with microtasks posted
196 // during shutdown.
197 if (gXPCOMThreadsShutDown) {
198 return;
199 }
200
201 DocGroup* docGroup = OwnerDoc()->GetDocGroup();
202 if (!docGroup) {
203 return;
204 }
205
206 mInSignalSlotList = true;
207 docGroup->SignalSlotChange(*this);
208 }
209
FireSlotChangeEvent()210 void HTMLSlotElement::FireSlotChangeEvent() {
211 nsContentUtils::DispatchTrustedEvent(
212 OwnerDoc(), static_cast<nsIContent*>(this), u"slotchange"_ns,
213 CanBubble::eYes, Cancelable::eNo);
214 }
215
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)216 JSObject* HTMLSlotElement::WrapNode(JSContext* aCx,
217 JS::Handle<JSObject*> aGivenProto) {
218 return HTMLSlotElement_Binding::Wrap(aCx, this, aGivenProto);
219 }
220
221 } // namespace mozilla::dom
222