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