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