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/. */
7 #ifndef mozilla_dom_shadowroot_h__
8 #define mozilla_dom_shadowroot_h__
10 #include "mozilla/DOMEventTargetHelper.h"
11 #include "mozilla/dom/DocumentBinding.h"
12 #include "mozilla/dom/DocumentFragment.h"
13 #include "mozilla/dom/DocumentOrShadowRoot.h"
14 #include "mozilla/dom/NameSpaceConstants.h"
15 #include "mozilla/dom/ShadowRootBinding.h"
16 #include "mozilla/ServoBindings.h"
18 #include "nsCycleCollectionParticipant.h"
19 #include "nsIRadioGroupContainer.h"
20 #include "nsStubMutationObserver.h"
21 #include "nsTHashtable.h"
28 class EventChainPreVisitor
;
29 class ServoStyleRuleMap
;
31 enum class StyleRuleChangeKind
: uint32_t;
41 class HTMLInputElement
;
43 class ShadowRoot final
: public DocumentFragment
,
44 public DocumentOrShadowRoot
,
45 public nsIRadioGroupContainer
{
46 friend class DocumentOrShadowRoot
;
49 NS_IMPL_FROMNODE_HELPER(ShadowRoot
, IsShadowRoot());
51 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot
, DocumentFragment
)
52 NS_DECL_ISUPPORTS_INHERITED
54 ShadowRoot(Element
* aElement
, ShadowRootMode aMode
,
55 Element::DelegatesFocus aDelegatesFocus
,
56 SlotAssignmentMode aSlotAssignment
,
57 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
);
59 void AddSizeOfExcludingThis(nsWindowSizes
&, size_t* aNodeSize
) const final
;
61 // Try to reassign an element or text to a slot.
62 void MaybeReassignContent(nsIContent
& aElementOrText
);
63 // Called when an element is inserted as a direct child of our host. Tries to
64 // slot the child in one of our slots.
65 void MaybeSlotHostChild(nsIContent
&);
66 // Called when a direct child of our host is removed. Tries to un-slot the
67 // child from the currently-assigned slot, if any.
68 void MaybeUnslotHostChild(nsIContent
&);
71 Element
* Host() const {
73 "ShadowRoot always has a host, how did we create "
78 ShadowRootMode
Mode() const { return mMode
; }
79 bool DelegatesFocus() const {
80 return mDelegatesFocus
== Element::DelegatesFocus::Yes
;
82 SlotAssignmentMode
SlotAssignment() const { return mSlotAssignment
; }
83 bool IsClosed() const { return mMode
== ShadowRootMode::Closed
; }
85 void RemoveSheetFromStyles(StyleSheet
&);
86 void RuleAdded(StyleSheet
&, css::Rule
&);
87 void RuleRemoved(StyleSheet
&, css::Rule
&);
88 void RuleChanged(StyleSheet
&, css::Rule
*, StyleRuleChangeKind
);
89 void ImportRuleLoaded(CSSImportRule
&, StyleSheet
&);
90 void SheetCloned(StyleSheet
&);
91 void StyleSheetApplicableStateChanged(StyleSheet
&);
94 * Clones internal state, for example stylesheets, of aOther to 'this'.
96 void CloneInternalDataFrom(ShadowRoot
* aOther
);
97 void InsertSheetAt(size_t aIndex
, StyleSheet
&);
99 // Calls UnbindFromTree for each of our kids, and also flags us as no longer
103 // Only intended for UA widgets / special shadow roots, or for handling
104 // failure cases when adopting (see BlastSubtreeToPieces).
106 // Forgets our shadow host and unbinds all our kids.
109 // Calls BindToTree on each of our kids, and also maybe flags us as being
114 * Explicitly invalidates the style and layout of the flattened-tree subtree
115 * rooted at the element.
117 * You need to use this whenever the flat tree is going to be shuffled in a
118 * way that layout doesn't understand via the usual ContentInserted /
119 * ContentAppended / ContentRemoved notifications. For example, if removing an
120 * element will cause a change in the flat tree such that other element will
121 * start showing up (like fallback content), this method needs to be called on
122 * an ancestor of that element.
124 * It is important that this runs _before_ actually shuffling the flat tree
125 * around, so that layout knows the actual tree that it needs to invalidate.
127 void InvalidateStyleAndLayoutOnSubtree(Element
*);
130 void InsertSheetIntoAuthorData(size_t aIndex
, StyleSheet
&,
131 const nsTArray
<RefPtr
<StyleSheet
>>&);
133 void AppendStyleSheet(StyleSheet
& aSheet
) {
134 InsertSheetAt(SheetCount(), aSheet
);
138 * Represents the insertion point in a slot for a given node.
140 struct SlotInsertionPoint
{
141 HTMLSlotElement
* mSlot
= nullptr;
142 Maybe
<uint32_t> mIndex
;
144 SlotInsertionPoint() = default;
145 SlotInsertionPoint(HTMLSlotElement
* aSlot
, const Maybe
<uint32_t>& aIndex
)
146 : mSlot(aSlot
), mIndex(aIndex
) {}
150 * Return the assignment corresponding to the content node at this particular
153 * It's the caller's responsibility to actually call InsertAssignedNode /
154 * AppendAssignedNode in the slot as needed.
156 SlotInsertionPoint
SlotInsertionPointFor(nsIContent
&);
159 * Returns the effective slot name for a given slottable. In most cases, this
160 * is just the value of the slot attribute, if any, or the empty string, but
161 * this also deals with the <details> shadow tree specially.
163 void GetSlotNameFor(const nsIContent
&, nsAString
&) const;
166 * Re-assign the current main summary if it has changed.
168 * Must be called only if mIsDetailsShadowTree is true.
170 enum class SummaryChangeReason
{ Deletion
, Insertion
};
171 void MaybeReassignMainSummary(SummaryChangeReason
);
174 void AddSlot(HTMLSlotElement
* aSlot
);
175 void RemoveSlot(HTMLSlotElement
* aSlot
);
176 bool HasSlots() const { return !mSlotMap
.IsEmpty(); };
177 HTMLSlotElement
* GetDefaultSlot() const {
178 SlotArray
* list
= mSlotMap
.Get(u
""_ns
);
179 return list
? (*list
)->ElementAt(0) : nullptr;
182 void PartAdded(const Element
&);
183 void PartRemoved(const Element
&);
185 IMPL_EVENT_HANDLER(slotchange
);
187 const nsTArray
<const Element
*>& Parts() const { return mParts
; }
189 const RawServoAuthorStyles
* GetServoStyles() const {
190 return mServoStyles
.get();
193 RawServoAuthorStyles
* GetServoStyles() { return mServoStyles
.get(); }
195 mozilla::ServoStyleRuleMap
& ServoStyleRuleMap();
197 JSObject
* WrapNode(JSContext
*, JS::Handle
<JSObject
*> aGivenProto
) final
;
199 void NodeInfoChanged(Document
* aOldDoc
) override
;
201 void AddToIdTable(Element
* aElement
, nsAtom
* aId
);
202 void RemoveFromIdTable(Element
* aElement
, nsAtom
* aId
);
205 using mozilla::dom::DocumentOrShadowRoot::GetElementById
;
207 Element
* GetActiveElement();
210 * These methods allow UA Widget to insert DOM elements into the Shadow ROM
211 * without putting their DOM reflectors to content scope first.
212 * The inserted DOM will have their reflectors in the UA Widget scope.
214 nsINode
* ImportNodeAndAppendChildAt(nsINode
& aParentNode
, nsINode
& aNode
,
215 bool aDeep
, mozilla::ErrorResult
& rv
);
217 nsINode
* CreateElementAndAppendChildAt(nsINode
& aParentNode
,
218 const nsAString
& aTagName
,
219 mozilla::ErrorResult
& rv
);
221 bool IsUAWidget() const { return mIsUAWidget
; }
223 void SetIsUAWidget() {
224 MOZ_ASSERT(!HasChildren());
225 SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET
);
229 bool IsAvailableToElementInternals() const {
230 return mIsAvailableToElementInternals
;
233 void SetAvailableToElementInternals() {
234 mIsAvailableToElementInternals
= true;
237 void GetEventTargetParent(EventChainPreVisitor
& aVisitor
) override
;
239 // nsIRadioGroupContainer
240 NS_IMETHOD
WalkRadioGroup(const nsAString
& aName
,
241 nsIRadioVisitor
* aVisitor
) override
{
242 return DocumentOrShadowRoot::WalkRadioGroup(aName
, aVisitor
);
244 virtual void SetCurrentRadioButton(const nsAString
& aName
,
245 HTMLInputElement
* aRadio
) override
{
246 DocumentOrShadowRoot::SetCurrentRadioButton(aName
, aRadio
);
248 virtual HTMLInputElement
* GetCurrentRadioButton(
249 const nsAString
& aName
) override
{
250 return DocumentOrShadowRoot::GetCurrentRadioButton(aName
);
253 GetNextRadioButton(const nsAString
& aName
, const bool aPrevious
,
254 HTMLInputElement
* aFocusedRadio
,
255 HTMLInputElement
** aRadioOut
) override
{
256 return DocumentOrShadowRoot::GetNextRadioButton(aName
, aPrevious
,
257 aFocusedRadio
, aRadioOut
);
259 virtual void AddToRadioGroup(const nsAString
& aName
,
260 HTMLInputElement
* aRadio
) override
{
261 DocumentOrShadowRoot::AddToRadioGroup(aName
, aRadio
);
263 virtual void RemoveFromRadioGroup(const nsAString
& aName
,
264 HTMLInputElement
* aRadio
) override
{
265 DocumentOrShadowRoot::RemoveFromRadioGroup(aName
, aRadio
);
267 virtual uint32_t GetRequiredRadioCount(
268 const nsAString
& aName
) const override
{
269 return DocumentOrShadowRoot::GetRequiredRadioCount(aName
);
271 virtual void RadioRequiredWillChange(const nsAString
& aName
,
272 bool aRequiredAdded
) override
{
273 DocumentOrShadowRoot::RadioRequiredWillChange(aName
, aRequiredAdded
);
275 virtual bool GetValueMissingState(const nsAString
& aName
) const override
{
276 return DocumentOrShadowRoot::GetValueMissingState(aName
);
278 virtual void SetValueMissingState(const nsAString
& aName
,
279 bool aValue
) override
{
280 return DocumentOrShadowRoot::SetValueMissingState(aName
, aValue
);
284 // FIXME(emilio): This will need to become more fine-grained.
285 void ApplicableRulesChanged();
287 virtual ~ShadowRoot();
289 const ShadowRootMode mMode
;
291 Element::DelegatesFocus mDelegatesFocus
;
293 const SlotAssignmentMode mSlotAssignment
;
295 // The computed data from the style sheets.
296 UniquePtr
<RawServoAuthorStyles
> mServoStyles
;
297 UniquePtr
<mozilla::ServoStyleRuleMap
> mStyleRuleMap
;
299 using SlotArray
= TreeOrderedArray
<HTMLSlotElement
>;
300 // Map from name of slot to an array of all slots in the shadow DOM with with
301 // the given name. The slots are stored as a weak pointer because the elements
302 // are in the shadow tree and should be kept alive by its parent.
303 nsClassHashtable
<nsStringHashKey
, SlotArray
> mSlotMap
;
305 // Unordered array of all elements that have a part attribute in this shadow
307 nsTArray
<const Element
*> mParts
;
309 bool mIsUAWidget
: 1;
311 // Whether this is the <details> internal shadow tree.
312 bool mIsDetailsShadowTree
: 1;
314 // https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals
315 bool mIsAvailableToElementInternals
: 1;
317 nsresult
Clone(dom::NodeInfo
*, nsINode
** aResult
) const override
;
321 } // namespace mozilla
323 #endif // mozilla_dom_shadowroot_h__