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