Bug 1404842 - P1: Implement Element.attachShadow and Element.slot, r=smaug
MozReview-Commit-ID: KWy8mDqEw4o
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1137,19 +1137,88 @@ Element::RemoveFromIdTable()
} else {
nsIDocument* doc = GetUncomposedDoc();
if (doc && (!IsInAnonymousSubtree() || doc->IsXULDocument())) {
doc->RemoveFromIdTable(this, id);
}
}
}
+void
+Element::SetSlot(const nsAString& aName, ErrorResult& aError)
+{
+ aError = SetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName, true);
+}
+
+void
+Element::GetSlot(nsAString& aName)
+{
+ GetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName);
+}
+
+// https://dom.spec.whatwg.org/#dom-element-attachshadow
+already_AddRefed<ShadowRoot>
+Element::AttachShadow(const ShadowRootInit& aInit, ErrorResult& aError)
+{
+ /**
+ * 1. If context object’s namespace is not the HTML namespace,
+ * then throw a "NotSupportedError" DOMException.
+ */
+ if (!IsHTMLElement()) {
+ aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+
+ /**
+ * 2. If context object’s local name is not
+ * a valid custom element name, "article", "aside", "blockquote",
+ * "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6",
+ * "header", "main" "nav", "p", "section", or "span",
+ * then throw a "NotSupportedError" DOMException.
+ */
+ nsAtom* nameAtom = NodeInfo()->NameAtom();
+ if (!(nsContentUtils::IsCustomElementName(nameAtom) ||
+ nameAtom == nsGkAtoms::article ||
+ nameAtom == nsGkAtoms::aside ||
+ nameAtom == nsGkAtoms::blockquote ||
+ nameAtom == nsGkAtoms::body ||
+ nameAtom == nsGkAtoms::div ||
+ nameAtom == nsGkAtoms::footer ||
+ nameAtom == nsGkAtoms::h1 ||
+ nameAtom == nsGkAtoms::h2 ||
+ nameAtom == nsGkAtoms::h3 ||
+ nameAtom == nsGkAtoms::h4 ||
+ nameAtom == nsGkAtoms::h5 ||
+ nameAtom == nsGkAtoms::h6 ||
+ nameAtom == nsGkAtoms::header ||
+ nameAtom == nsGkAtoms::main ||
+ nameAtom == nsGkAtoms::nav ||
+ nameAtom == nsGkAtoms::p ||
+ nameAtom == nsGkAtoms::section ||
+ nameAtom == nsGkAtoms::span)) {
+ aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+
+ return AttachShadowInternal(aInit.mMode == ShadowRootMode::Closed, aError);
+}
+
already_AddRefed<ShadowRoot>
Element::CreateShadowRoot(ErrorResult& aError)
{
+ return AttachShadowInternal(false, aError);
+}
+
+already_AddRefed<ShadowRoot>
+Element::AttachShadowInternal(bool aClosed, ErrorResult& aError)
+{
+ /**
+ * 3. If context object is a shadow host, then throw
+ * an "InvalidStateError" DOMException.
+ */
if (GetShadowRoot()) {
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
nsAutoScriptBlocker scriptBlocker;
RefPtr<mozilla::dom::NodeInfo> nodeInfo;
@@ -1176,29 +1245,41 @@ Element::CreateShadowRoot(ErrorResult& a
MOZ_ASSERT(!GetPrimaryFrame());
// Unlike for XBL, false is the default for inheriting style.
protoBinding->SetInheritsStyle(false);
// Calling SetPrototypeBinding takes ownership of protoBinding.
docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
- RefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
- protoBinding);
+ /**
+ * 4. Let shadow be a new shadow root whose node document is
+ * context object’s node document, host is context object,
+ * and mode is init’s mode.
+ */
+ RefPtr<ShadowRoot> shadowRoot =
+ new ShadowRoot(this, aClosed, nodeInfo.forget(), protoBinding);
shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
+ /**
+ * 5. Set context object’s shadow root to shadow.
+ */
SetShadowRoot(shadowRoot);
// xblBinding takes ownership of docInfo.
RefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
shadowRoot->SetAssociatedBinding(xblBinding);
xblBinding->SetBoundElement(this);
SetXBLBinding(xblBinding);
+
+ /**
+ * 6. Return shadow.
+ */
return shadowRoot.forget();
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DestinationInsertionPointList, mParent,
mDestinationPoints)
NS_INTERFACE_TABLE_HEAD(DestinationInsertionPointList)
NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -601,16 +601,19 @@ protected:
* time and other places where we don't want to notify a state
* change.
*/
void RemoveStatesSilently(EventStates aStates)
{
mState &= ~aStates;
}
+ already_AddRefed<ShadowRoot> AttachShadowInternal(bool aClosed,
+ ErrorResult& aError);
+
private:
// Need to allow the ESM, nsGlobalWindow, and the focus manager to
// set our state
friend class mozilla::EventStateManager;
friend class ::nsGlobalWindow;
friend class ::nsFocusManager;
// Allow CusomtElementRegistry to call AddStates.
@@ -1077,16 +1080,23 @@ public:
Attr* GetAttributeNodeNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName);
already_AddRefed<Attr> SetAttributeNodeNS(Attr& aNewAttr,
ErrorResult& aError);
already_AddRefed<DOMRectList> GetClientRects();
already_AddRefed<DOMRect> GetBoundingClientRect();
+ // Shadow DOM v1
+ already_AddRefed<ShadowRoot> AttachShadow(const ShadowRootInit& aInit,
+ ErrorResult& aError);
+ void SetSlot(const nsAString& aName, ErrorResult& aError);
+ void GetSlot(nsAString& aName);
+
+ // [deprecated] Shadow DOM v0
already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
already_AddRefed<DestinationInsertionPointList> GetDestinationInsertionPoints();
ShadowRoot *FastGetShadowRoot() const
{
nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
return slots ? slots->mShadowRoot.get() : nullptr;
}
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -46,25 +46,26 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHE
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
-ShadowRoot::ShadowRoot(Element* aElement,
+ShadowRoot::ShadowRoot(Element* aElement, bool aClosed,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
nsXBLPrototypeBinding* aProtoBinding)
: DocumentFragment(aNodeInfo)
, mProtoBinding(aProtoBinding)
, mInsertionPointChanged(false)
, mIsComposedDocParticipant(false)
{
SetHost(aElement);
+ mMode = aClosed ? ShadowRootMode::Closed : ShadowRootMode::Open;
// Nodes in a shadow tree should never store a value
// in the subtree root pointer, nodes in the shadow tree
// track the subtree root using GetContainingShadow().
ClearSubtreeRootPointer();
SetFlags(NODE_IS_IN_SHADOW_TREE);
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -36,20 +36,28 @@ public:
DocumentFragment)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
- ShadowRoot(Element* aElement,
+ ShadowRoot(Element* aElement, bool aClosed,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
nsXBLPrototypeBinding* aProtoBinding);
+ // Shadow DOM v1
+ Element* Host();
+ ShadowRootMode Mode()
+ {
+ return mMode;
+ }
+
+ // [deprecated] Shadow DOM v0
void AddToIdTable(Element* aElement, nsAtom* aId);
void RemoveFromIdTable(Element* aElement, nsAtom* aId);
void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
void RemoveSheet(StyleSheet* aSheet);
bool ApplyAuthorStyles();
void SetApplyAuthorStyles(bool aApplyAuthorStyles);
StyleSheetList* StyleSheets();
@@ -116,28 +124,29 @@ public:
GetElementsByTagName(const nsAString& aNamespaceURI);
already_AddRefed<nsContentList>
GetElementsByTagNameNS(const nsAString& aNamespaceURI,
const nsAString& aLocalName);
already_AddRefed<nsContentList>
GetElementsByClassName(const nsAString& aClasses);
void GetInnerHTML(nsAString& aInnerHTML);
void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
- Element* Host();
void StyleSheetChanged();
bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
{
mIsComposedDocParticipant = aIsComposedDocParticipant;
}
protected:
virtual ~ShadowRoot();
+ ShadowRootMode mMode;
+
// An array of content insertion points that are a descendant of the ShadowRoot
// sorted in tree order. Insertion points are responsible for notifying
// the ShadowRoot when they are removed or added as a descendant. The insertion
// points are kept alive by the parent node, thus weak references are held
// by the array.
nsTArray<HTMLContentElement*> mInsertionPoints;
nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -230,26 +230,38 @@ partial interface Element {
// http://www.w3.org/TR/selectors-api/#interface-definitions
partial interface Element {
[Throws, Pure]
Element? querySelector(DOMString selectors);
[Throws, Pure]
NodeList querySelectorAll(DOMString selectors);
};
-// http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-element-interface
+// https://dom.spec.whatwg.org/#dictdef-shadowrootinit
+dictionary ShadowRootInit {
+ required ShadowRootMode mode;
+};
+
+// https://dom.spec.whatwg.org/#element
partial interface Element {
- [Throws,Pref="dom.webcomponents.enabled"]
- ShadowRoot createShadowRoot();
- [Pref="dom.webcomponents.enabled"]
- NodeList getDestinationInsertionPoints();
+ // Shadow DOM v1
+ [Throws, Pref="dom.webcomponents.enabled"]
+ ShadowRoot attachShadow(ShadowRootInit shadowRootInitDict);
[Pref="dom.webcomponents.enabled"]
readonly attribute ShadowRoot? shadowRoot;
[Pref="dom.webcomponents.enabled"]
readonly attribute HTMLSlotElement? assignedSlot;
+ [CEReactions, Unscopable, SetterThrows, Pref="dom.webcomponents.enabled"]
+ attribute DOMString slot;
+
+ // [deprecated] Shadow DOM v0
+ [Throws, Pref="dom.webcomponents.enabled"]
+ ShadowRoot createShadowRoot();
+ [Pref="dom.webcomponents.enabled"]
+ NodeList getDestinationInsertionPoints();
};
Element implements ChildNode;
Element implements NonDocumentTypeChildNode;
Element implements ParentNode;
Element implements Animatable;
Element implements GeometryUtils;
--- a/dom/webidl/ShadowRoot.webidl
+++ b/dom/webidl/ShadowRoot.webidl
@@ -5,22 +5,33 @@
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
+// https://dom.spec.whatwg.org/#enumdef-shadowrootmode
+enum ShadowRootMode {
+ "open",
+ "closed"
+};
+
+// https://dom.spec.whatwg.org/#shadowroot
[Pref="dom.webcomponents.enabled"]
interface ShadowRoot : DocumentFragment
{
+ // Shadow DOM v1
+ readonly attribute ShadowRootMode mode;
+ readonly attribute Element host;
+
+ // [deprecated] Shadow DOM v0
Element? getElementById(DOMString elementId);
HTMLCollection getElementsByTagName(DOMString localName);
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
HTMLCollection getElementsByClassName(DOMString classNames);
[CEReactions, SetterThrows, TreatNullAs=EmptyString]
attribute DOMString innerHTML;
- readonly attribute Element host;
attribute boolean applyAuthorStyles;
readonly attribute StyleSheetList styleSheets;
};