From 0d362ca50335d964a78dbba7e7d32574ee67899a Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 17 Apr 2020 05:01:17 -0400 Subject: Bug 1330843 - Allow JS to create NAC pseudo-elements Tag #1375 --- dom/base/nsContentUtils.cpp | 13 ++++++++++++ dom/base/nsContentUtils.h | 6 ++++++ dom/base/nsDocument.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++- dom/webidl/Document.webidl | 3 +++ 4 files changed, 70 insertions(+), 1 deletion(-) (limited to 'dom') diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 6a819818c..c53b3d834 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -9871,3 +9871,16 @@ nsContentUtils::IsLocalRefURL(const nsString& aString) return false; } + +/* static */ Element* +nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement) +{ + MOZ_ASSERT(aElement); + MOZ_ASSERT(aElement->IsNativeAnonymous()); + + Element* e = aElement; + while (e && e->IsNativeAnonymous()) { + e = e->GetParentElement(); + } + return e; +} diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 3f1a25504..4ccc1aa25 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -2762,6 +2762,12 @@ public: static bool IsWebComponentsEnabled() { return sIsWebComponentsEnabled; } + /** + * Walks up the tree from aElement until it finds an element that is + * not native anonymous content. aElement must be NAC itself. + */ + static Element* GetClosestNonNativeAnonymousAncestor(Element* aElement); + static bool IsCustomElementsEnabled() { return sIsCustomElementsEnabled; } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 45c80ca7f..d0e861b1a 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -5378,6 +5378,20 @@ nsDocument::GetCustomElementRegistry() return registry.forget(); } +// We only support pseudo-elements with two colons in this function. +static CSSPseudoElementType +GetPseudoElementType(const nsString& aString, ErrorResult& aRv) +{ + MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null"); + if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return CSSPseudoElementType::NotPseudo; + } + nsCOMPtr pseudo = NS_Atomize(Substring(aString, 1)); + return nsCSSPseudoElements::GetPseudoType(pseudo, + nsCSSProps::EnabledState::eInUASheets); +} + already_AddRefed nsDocument::CreateElement(const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions, @@ -5395,10 +5409,36 @@ nsDocument::CreateElement(const nsAString& aTagName, } const nsString* is = nullptr; + CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo; + if (aOptions.IsElementCreationOptions()) { + const ElementCreationOptions& options = + aOptions.GetAsElementCreationOptions(); + + if (CustomElementRegistry::IsCustomElementEnabled() && + options.mIs.WasPassed()) { + is = &options.mIs.Value(); + } + + // Check 'pseudo' and throw an exception if it's not one allowed + // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC. + if (options.mPseudo.WasPassed()) { + pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv); + if (rv.Failed() || + pseudoType == CSSPseudoElementType::NotPseudo || + !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) { + rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return nullptr; + } + } + } RefPtr elem = CreateElem( needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is); + if (pseudoType != CSSPseudoElementType::NotPseudo) { + elem->SetPseudoElementType(pseudoType); + } + if (is) { elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true); } @@ -5413,8 +5453,8 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI, { *aReturn = nullptr; ElementCreationOptionsOrString options; - options.SetAsString(); + options.SetAsString(); ErrorResult rv; nsCOMPtr element = CreateElementNS(aNamespaceURI, aQualifiedName, options, rv); @@ -5439,6 +5479,13 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI, } const nsString* is = nullptr; + if (CustomElementRegistry::IsCustomElementEnabled() && + aOptions.IsElementCreationOptions()) { + const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions(); + if (options.mIs.WasPassed()) { + is = &options.mIs.Value(); + } + } nsCOMPtr element; rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index b28903ae2..cad6e1c39 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -17,6 +17,9 @@ enum VisibilityState { "hidden", "visible", "prerender" }; /* https://dom.spec.whatwg.org/#dictdef-elementcreationoptions */ dictionary ElementCreationOptions { DOMString is; + + [ChromeOnly] + DOMString pseudo; }; /* http://dom.spec.whatwg.org/#interface-document */ -- cgit v1.2.3