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