/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_shadowroot_h__ #define mozilla_dom_shadowroot_h__ #include "mozilla/dom/DocumentBinding.h" #include "mozilla/dom/DocumentFragment.h" #include "mozilla/dom/DocumentOrShadowRoot.h" #include "mozilla/dom/NameSpaceConstants.h" #include "mozilla/dom/ShadowRootBinding.h" #include "mozilla/ServoBindings.h" #include "nsCOMPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsIdentifierMapEntry.h" #include "nsIRadioGroupContainer.h" #include "nsStubMutationObserver.h" #include "nsTHashtable.h" class nsAtom; class nsIContent; class nsXBLPrototypeBinding; namespace mozilla { class EventChainPreVisitor; class ServoStyleRuleMap; namespace css { class Rule; } namespace dom { class Element; class HTMLInputElement; class ShadowRoot final : public DocumentFragment, public DocumentOrShadowRoot, public nsStubMutationObserver, public nsIRadioGroupContainer { public: NS_IMPL_FROMNODE_HELPER(ShadowRoot, IsShadowRoot()); NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot, DocumentFragment) NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED ShadowRoot(Element* aElement, ShadowRootMode aMode, already_AddRefed&& aNodeInfo); void AddSizeOfExcludingThis(nsWindowSizes&, size_t* aNodeSize) const final; // Shadow DOM v1 Element* Host() const { MOZ_ASSERT(GetHost(), "ShadowRoot always has a host, how did we create " "this ShadowRoot?"); return GetHost(); } ShadowRootMode Mode() const { return mMode; } bool IsClosed() const { return mMode == ShadowRootMode::Closed; } void RemoveSheet(StyleSheet* aSheet); void RuleAdded(StyleSheet&, css::Rule&); void RuleRemoved(StyleSheet&, css::Rule&); void RuleChanged(StyleSheet&, css::Rule*); void StyleSheetApplicableStateChanged(StyleSheet&, bool aApplicable); StyleSheetList* StyleSheets() { return &DocumentOrShadowRoot::EnsureDOMStyleSheets(); } /** * Clones internal state, for example stylesheets, of aOther to 'this'. */ void CloneInternalDataFrom(ShadowRoot* aOther); void InsertSheetAt(size_t aIndex, StyleSheet&); // Calls UnbindFromTree for each of our kids, and also flags us as no longer // being connected. void Unbind(); // Calls BindToTree on each of our kids, and also maybe flags us as being // connected. nsresult Bind(); private: void InsertSheetIntoAuthorData(size_t aIndex, StyleSheet&); void AppendStyleSheet(StyleSheet& aSheet) { InsertSheetAt(SheetCount(), aSheet); } /** * Try to reassign an element to a slot and returns whether the assignment * changed. */ void MaybeReassignElement(Element* aElement); /** * Represents the insertion point in a slot for a given node. */ struct SlotAssignment { HTMLSlotElement* mSlot = nullptr; Maybe mIndex; SlotAssignment() = default; SlotAssignment(HTMLSlotElement* aSlot, const Maybe& aIndex) : mSlot(aSlot) , mIndex(aIndex) { } }; /** * Return the assignment corresponding to the content node at this particular * point in time. * * It's the caller's responsibility to actually call InsertAssignedNode / * AppendAssignedNode in the slot as needed. */ SlotAssignment SlotAssignmentFor(nsIContent* aContent); /** * Explicitly invalidates the style and layout of the flattened-tree subtree * rooted at the element. * * You need to use this whenever the flat tree is going to be shuffled in a * way that layout doesn't understand via the usual ContentInserted / * ContentAppended / ContentRemoved notifications. For example, if removing an * element will cause a change in the flat tree such that other element will * start showing up (like fallback content), this method needs to be called on * an ancestor of that element. * * It is important that this runs _before_ actually shuffling the flat tree * around, so that layout knows the actual tree that it needs to invalidate. */ void InvalidateStyleAndLayoutOnSubtree(Element*); public: void AddSlot(HTMLSlotElement* aSlot); void RemoveSlot(HTMLSlotElement* aSlot); bool HasSlots() const { return !mSlotMap.IsEmpty(); }; const RawServoAuthorStyles* GetServoStyles() const { return mServoStyles.get(); } RawServoAuthorStyles* GetServoStyles() { return mServoStyles.get(); } mozilla::ServoStyleRuleMap& ServoStyleRuleMap(); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; void AddToIdTable(Element* aElement, nsAtom* aId); void RemoveFromIdTable(Element* aElement, nsAtom* aId); // WebIDL methods. using mozilla::dom::DocumentOrShadowRoot::GetElementById; Element* GetActiveElement(); void GetInnerHTML(nsAString& aInnerHTML); void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError); /** * These methods allow UA Widget to insert DOM elements into the Shadow ROM * without putting their DOM reflectors to content scope first. * The inserted DOM will have their reflectors in the UA Widget scope. */ nsINode* ImportNodeAndAppendChildAt(nsINode& aParentNode, nsINode& aNode, bool aDeep, mozilla::ErrorResult& rv); nsINode* CreateElementAndAppendChildAt(nsINode& aParentNode, const nsAString& aTagName, mozilla::ErrorResult& rv); bool IsUAWidget() const { return mIsUAWidget; } void SetIsUAWidget() { mIsUAWidget = true; } void GetEventTargetParent(EventChainPreVisitor& aVisitor) override; // nsIRadioGroupContainer NS_IMETHOD WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor, bool aFlushContent) override { return DocumentOrShadowRoot::WalkRadioGroup(aName, aVisitor, aFlushContent); } virtual void SetCurrentRadioButton(const nsAString& aName, HTMLInputElement* aRadio) override { DocumentOrShadowRoot::SetCurrentRadioButton(aName, aRadio); } virtual HTMLInputElement* GetCurrentRadioButton(const nsAString& aName) override { return DocumentOrShadowRoot::GetCurrentRadioButton(aName); } NS_IMETHOD GetNextRadioButton(const nsAString& aName, const bool aPrevious, HTMLInputElement* aFocusedRadio, HTMLInputElement** aRadioOut) override { return DocumentOrShadowRoot::GetNextRadioButton(aName, aPrevious, aFocusedRadio, aRadioOut); } virtual void AddToRadioGroup(const nsAString& aName, HTMLInputElement* aRadio) override { DocumentOrShadowRoot::AddToRadioGroup(aName, aRadio); } virtual void RemoveFromRadioGroup(const nsAString& aName, HTMLInputElement* aRadio) override { DocumentOrShadowRoot::RemoveFromRadioGroup(aName, aRadio); } virtual uint32_t GetRequiredRadioCount(const nsAString& aName) const override { return DocumentOrShadowRoot::GetRequiredRadioCount(aName); } virtual void RadioRequiredWillChange(const nsAString& aName, bool aRequiredAdded) override { DocumentOrShadowRoot::RadioRequiredWillChange(aName, aRequiredAdded); } virtual bool GetValueMissingState(const nsAString& aName) const override { return DocumentOrShadowRoot::GetValueMissingState(aName); } virtual void SetValueMissingState(const nsAString& aName, bool aValue) override { return DocumentOrShadowRoot::SetValueMissingState(aName, aValue); } protected: // FIXME(emilio): This will need to become more fine-grained. void ApplicableRulesChanged(); virtual ~ShadowRoot(); const ShadowRootMode mMode; // The computed data from the style sheets. UniquePtr mServoStyles; UniquePtr mStyleRuleMap; using SlotArray = AutoTArray; // Map from name of slot to an array of all slots in the shadow DOM with with // the given name. The slots are stored as a weak pointer because the elements // are in the shadow tree and should be kept alive by its parent. nsClassHashtable mSlotMap; bool mIsUAWidget; nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_shadowroot_h__