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 #ifndef mozilla_dom_shadowroot_h__
8 #define mozilla_dom_shadowroot_h__
9 
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"
17 #include "nsCOMPtr.h"
18 #include "nsCycleCollectionParticipant.h"
19 #include "nsIRadioGroupContainer.h"
20 #include "nsStubMutationObserver.h"
21 #include "nsTHashtable.h"
22 
23 class nsAtom;
24 class nsIContent;
25 class nsXBLPrototypeBinding;
26 
27 namespace mozilla {
28 
29 class EventChainPreVisitor;
30 class ServoStyleRuleMap;
31 
32 enum class StyleRuleChangeKind : uint32_t;
33 
34 namespace css {
35 class Rule;
36 }
37 
38 namespace dom {
39 
40 class CSSImportRule;
41 class Element;
42 class HTMLInputElement;
43 
44 class ShadowRoot final : public DocumentFragment,
45                          public DocumentOrShadowRoot,
46                          public nsIRadioGroupContainer {
47   friend class DocumentOrShadowRoot;
48 
49  public:
50   NS_IMPL_FROMNODE_HELPER(ShadowRoot, IsShadowRoot());
51 
52   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot, DocumentFragment)
53   NS_DECL_ISUPPORTS_INHERITED
54 
55   ShadowRoot(Element* aElement, ShadowRootMode aMode,
56              Element::DelegatesFocus aDelegatesFocus,
57              SlotAssignmentMode aSlotAssignment,
58              already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
59 
60   void AddSizeOfExcludingThis(nsWindowSizes&, size_t* aNodeSize) const final;
61 
62   // Try to reassign an element or text to a slot.
63   void MaybeReassignContent(nsIContent& aElementOrText);
64   // Called when an element is inserted as a direct child of our host. Tries to
65   // slot the child in one of our slots.
66   void MaybeSlotHostChild(nsIContent&);
67   // Called when a direct child of our host is removed. Tries to un-slot the
68   // child from the currently-assigned slot, if any.
69   void MaybeUnslotHostChild(nsIContent&);
70 
71   // Find the first focusable element in this tree.
72   Element* GetFirstFocusable(bool aWithMouse) const;
73 
74   // Shadow DOM v1
Host()75   Element* Host() const {
76     MOZ_ASSERT(GetHost(),
77                "ShadowRoot always has a host, how did we create "
78                "this ShadowRoot?");
79     return GetHost();
80   }
81 
Mode()82   ShadowRootMode Mode() const { return mMode; }
DelegatesFocus()83   bool DelegatesFocus() const {
84     return mDelegatesFocus == Element::DelegatesFocus::Yes;
85   }
SlotAssignment()86   SlotAssignmentMode SlotAssignment() const { return mSlotAssignment; }
IsClosed()87   bool IsClosed() const { return mMode == ShadowRootMode::Closed; }
88 
89   void RemoveSheetFromStyles(StyleSheet&);
90   void RuleAdded(StyleSheet&, css::Rule&);
91   void RuleRemoved(StyleSheet&, css::Rule&);
92   void RuleChanged(StyleSheet&, css::Rule*, StyleRuleChangeKind);
93   void ImportRuleLoaded(CSSImportRule&, StyleSheet&);
94   void SheetCloned(StyleSheet&);
95   void StyleSheetApplicableStateChanged(StyleSheet&);
96 
97   /**
98    * Clones internal state, for example stylesheets, of aOther to 'this'.
99    */
100   void CloneInternalDataFrom(ShadowRoot* aOther);
101   void InsertSheetAt(size_t aIndex, StyleSheet&);
102 
103   // Calls UnbindFromTree for each of our kids, and also flags us as no longer
104   // being connected.
105   void Unbind();
106 
107   // Only intended for UA widgets / special shadow roots, or for handling
108   // failure cases when adopting (see BlastSubtreeToPieces).
109   //
110   // Forgets our shadow host and unbinds all our kids.
111   void Unattach();
112 
113   // Calls BindToTree on each of our kids, and also maybe flags us as being
114   // connected.
115   nsresult Bind();
116 
117   /**
118    * Explicitly invalidates the style and layout of the flattened-tree subtree
119    * rooted at the element.
120    *
121    * You need to use this whenever the flat tree is going to be shuffled in a
122    * way that layout doesn't understand via the usual ContentInserted /
123    * ContentAppended / ContentRemoved notifications. For example, if removing an
124    * element will cause a change in the flat tree such that other element will
125    * start showing up (like fallback content), this method needs to be called on
126    * an ancestor of that element.
127    *
128    * It is important that this runs _before_ actually shuffling the flat tree
129    * around, so that layout knows the actual tree that it needs to invalidate.
130    */
131   void InvalidateStyleAndLayoutOnSubtree(Element*);
132 
133  private:
134   void InsertSheetIntoAuthorData(size_t aIndex, StyleSheet&,
135                                  const nsTArray<RefPtr<StyleSheet>>&);
136 
AppendStyleSheet(StyleSheet & aSheet)137   void AppendStyleSheet(StyleSheet& aSheet) {
138     InsertSheetAt(SheetCount(), aSheet);
139   }
140 
141   /**
142    * Represents the insertion point in a slot for a given node.
143    */
144   struct SlotInsertionPoint {
145     HTMLSlotElement* mSlot = nullptr;
146     Maybe<uint32_t> mIndex;
147 
148     SlotInsertionPoint() = default;
SlotInsertionPointSlotInsertionPoint149     SlotInsertionPoint(HTMLSlotElement* aSlot, const Maybe<uint32_t>& aIndex)
150         : mSlot(aSlot), mIndex(aIndex) {}
151   };
152 
153   /**
154    * Return the assignment corresponding to the content node at this particular
155    * point in time.
156    *
157    * It's the caller's responsibility to actually call InsertAssignedNode /
158    * AppendAssignedNode in the slot as needed.
159    */
160   SlotInsertionPoint SlotInsertionPointFor(nsIContent&);
161 
162  public:
163   void AddSlot(HTMLSlotElement* aSlot);
164   void RemoveSlot(HTMLSlotElement* aSlot);
HasSlots()165   bool HasSlots() const { return !mSlotMap.IsEmpty(); };
GetDefaultSlot()166   HTMLSlotElement* GetDefaultSlot() const {
167     SlotArray* list = mSlotMap.Get(u""_ns);
168     return list ? (*list)->ElementAt(0) : nullptr;
169   }
170 
171   void PartAdded(const Element&);
172   void PartRemoved(const Element&);
173 
174   IMPL_EVENT_HANDLER(slotchange);
175 
Parts()176   const nsTArray<const Element*>& Parts() const { return mParts; }
177 
GetServoStyles()178   const RawServoAuthorStyles* GetServoStyles() const {
179     return mServoStyles.get();
180   }
181 
GetServoStyles()182   RawServoAuthorStyles* GetServoStyles() { return mServoStyles.get(); }
183 
184   mozilla::ServoStyleRuleMap& ServoStyleRuleMap();
185 
186   JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) final;
187 
NodeInfoChanged(Document * aOldDoc)188   void NodeInfoChanged(Document* aOldDoc) override {
189     DocumentFragment::NodeInfoChanged(aOldDoc);
190     ClearAdoptedStyleSheets();
191   }
192 
193   void AddToIdTable(Element* aElement, nsAtom* aId);
194   void RemoveFromIdTable(Element* aElement, nsAtom* aId);
195 
196   // WebIDL methods.
197   using mozilla::dom::DocumentOrShadowRoot::GetElementById;
198 
199   Element* GetActiveElement();
200 
201   /**
202    * These methods allow UA Widget to insert DOM elements into the Shadow ROM
203    * without putting their DOM reflectors to content scope first.
204    * The inserted DOM will have their reflectors in the UA Widget scope.
205    */
206   nsINode* ImportNodeAndAppendChildAt(nsINode& aParentNode, nsINode& aNode,
207                                       bool aDeep, mozilla::ErrorResult& rv);
208 
209   nsINode* CreateElementAndAppendChildAt(nsINode& aParentNode,
210                                          const nsAString& aTagName,
211                                          mozilla::ErrorResult& rv);
212 
IsUAWidget()213   bool IsUAWidget() const { return mIsUAWidget; }
214 
SetIsUAWidget()215   void SetIsUAWidget() {
216     MOZ_ASSERT(!HasChildren());
217     SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
218     mIsUAWidget = true;
219   }
220 
IsAvailableToElementInternals()221   bool IsAvailableToElementInternals() const {
222     return mIsAvailableToElementInternals;
223   }
224 
SetAvailableToElementInternals()225   void SetAvailableToElementInternals() {
226     mIsAvailableToElementInternals = true;
227   }
228 
229   void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
230 
231   // nsIRadioGroupContainer
WalkRadioGroup(const nsAString & aName,nsIRadioVisitor * aVisitor)232   NS_IMETHOD WalkRadioGroup(const nsAString& aName,
233                             nsIRadioVisitor* aVisitor) override {
234     return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor);
235   }
SetCurrentRadioButton(const nsAString & aName,HTMLInputElement * aRadio)236   virtual void SetCurrentRadioButton(const nsAString& aName,
237                                      HTMLInputElement* aRadio) override {
238     DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio);
239   }
GetCurrentRadioButton(const nsAString & aName)240   virtual HTMLInputElement* GetCurrentRadioButton(
241       const nsAString& aName) override {
242     return DocumentOrShadowRoot::GetCurrentRadioButton(aName);
243   }
244   NS_IMETHOD
GetNextRadioButton(const nsAString & aName,const bool aPrevious,HTMLInputElement * aFocusedRadio,HTMLInputElement ** aRadioOut)245   GetNextRadioButton(const nsAString& aName, const bool aPrevious,
246                      HTMLInputElement* aFocusedRadio,
247                      HTMLInputElement** aRadioOut) override {
248     return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious,
249                                                     aFocusedRadio, aRadioOut);
250   }
AddToRadioGroup(const nsAString & aName,HTMLInputElement * aRadio)251   virtual void AddToRadioGroup(const nsAString& aName,
252                                HTMLInputElement* aRadio) override {
253     DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio);
254   }
RemoveFromRadioGroup(const nsAString & aName,HTMLInputElement * aRadio)255   virtual void RemoveFromRadioGroup(const nsAString& aName,
256                                     HTMLInputElement* aRadio) override {
257     DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio);
258   }
GetRequiredRadioCount(const nsAString & aName)259   virtual uint32_t GetRequiredRadioCount(
260       const nsAString& aName) const override {
261     return DocumentOrShadowRoot::GetRequiredRadioCount(aName);
262   }
RadioRequiredWillChange(const nsAString & aName,bool aRequiredAdded)263   virtual void RadioRequiredWillChange(const nsAString& aName,
264                                        bool aRequiredAdded) override {
265     DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded);
266   }
GetValueMissingState(const nsAString & aName)267   virtual bool GetValueMissingState(const nsAString& aName) const override {
268     return DocumentOrShadowRoot::GetValueMissingState(aName);
269   }
SetValueMissingState(const nsAString & aName,bool aValue)270   virtual void SetValueMissingState(const nsAString& aName,
271                                     bool aValue) override {
272     return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
273   }
274 
275  protected:
276   // FIXME(emilio): This will need to become more fine-grained.
277   void ApplicableRulesChanged();
278 
279   virtual ~ShadowRoot();
280 
281   const ShadowRootMode mMode;
282 
283   Element::DelegatesFocus mDelegatesFocus;
284 
285   const SlotAssignmentMode mSlotAssignment;
286 
287   // The computed data from the style sheets.
288   UniquePtr<RawServoAuthorStyles> mServoStyles;
289   UniquePtr<mozilla::ServoStyleRuleMap> mStyleRuleMap;
290 
291   using SlotArray = TreeOrderedArray<HTMLSlotElement>;
292   // Map from name of slot to an array of all slots in the shadow DOM with with
293   // the given name. The slots are stored as a weak pointer because the elements
294   // are in the shadow tree and should be kept alive by its parent.
295   nsClassHashtable<nsStringHashKey, SlotArray> mSlotMap;
296 
297   // Unordered array of all elements that have a part attribute in this shadow
298   // tree.
299   nsTArray<const Element*> mParts;
300 
301   bool mIsUAWidget : 1;
302 
303   // https://dom.spec.whatwg.org/#shadowroot-available-to-element-internals
304   bool mIsAvailableToElementInternals : 1;
305 
306   nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
307 };
308 
309 }  // namespace dom
310 }  // namespace mozilla
311 
312 #endif  // mozilla_dom_shadowroot_h__
313