Bug 1318542 - part1: Allow JS to create NAC pseudo-elements. r?
MozReview-Commit-ID: 2aBPoCOsT6R
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5463,16 +5463,34 @@ nsDocument::GetCustomElementRegistry()
RefPtr<CustomElementRegistry> registry = window->CustomElements();
if (!registry) {
return nullptr;
}
return registry.forget();
}
+// We only support pseudo-elements with two colons in this function.
+static CSSPseudoElementType
+GetPseudoElementType(const nsString& aString, ErrorResult& aRv)
+{
+ if (aString.IsEmpty()) {
+ return CSSPseudoElementType::NotPseudo;
+ }
+
+ if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return CSSPseudoElementType::NotPseudo;
+ }
+
+ nsCOMPtr<nsIAtom> pseudo = NS_Atomize(Substring(aString, 1));
+ return nsCSSPseudoElements::GetPseudoType(pseudo,
+ nsCSSProps::EnabledState::eInUASheets);
+}
+
already_AddRefed<Element>
nsDocument::CreateElement(const nsAString& aTagName,
const ElementCreationOptionsOrString& aOptions,
ErrorResult& rv)
{
rv = nsContentUtils::CheckQName(aTagName, false);
if (rv.Failed()) {
return nullptr;
@@ -5480,28 +5498,55 @@ nsDocument::CreateElement(const nsAStrin
bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
nsAutoString lcTagName;
if (needsLowercase) {
nsContentUtils::ASCIIToLower(aTagName, lcTagName);
}
const nsString* is = nullptr;
+ CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
if (aOptions.IsElementCreationOptions()) {
+ const ElementCreationOptions& options =
+ aOptions.GetAsElementCreationOptions();
+
// Throw NotFoundError if 'is' is not-null and definition is null
- is = CheckCustomElementName(aOptions.GetAsElementCreationOptions(),
+ is = CheckCustomElementName(options,
needsLowercase ? lcTagName : aTagName, mDefaultElementType, rv);
if (rv.Failed()) {
return nullptr;
}
+
+ // Check 'pseudo' and throw an exception if it's not a recognized
+ // pseduo-element name, or if it's one of the structural pseudo-elements
+ // we don't want to support.
+ if (options.mPseudo.WasPassed()) {
+ pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
+ if (rv.Failed()) {
+ return nullptr;
+ }
+ if (pseudoType != CSSPseudoElementType::NotPseudo) {
+ if (nsCSSPseudoElements::PseudoElementContainsElements(pseudoType) ||
+ pseudoType == CSSPseudoElementType::before ||
+ pseudoType == CSSPseudoElementType::after) {
+ rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return nullptr;
+ }
+ }
+ }
}
RefPtr<Element> elem = CreateElem(
needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
+ if (pseudoType != CSSPseudoElementType::NotPseudo) {
+ elem->SetProperty(nsGkAtoms::pseudoProperty,
+ reinterpret_cast<void*>(pseudoType));
+ }
+
return elem.forget();
}
NS_IMETHODIMP
nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
const nsAString& aQualifiedName,
nsIDOMElement** aReturn)
{
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2144,16 +2144,17 @@ GK_ATOM(transitionsOfBeforeProperty, "Tr
GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
GK_ATOM(baseURIProperty, "baseURIProperty")
GK_ATOM(lockedStyleStates, "lockedStyleStates")
GK_ATOM(apzCallbackTransform, "apzCallbackTransform")
GK_ATOM(restylableAnonymousNode, "restylableAnonymousNode")
GK_ATOM(paintRequestTime, "PaintRequestTime")
+GK_ATOM(pseudoProperty, "PseudoProperty") // CSSPseudoElementType
// Languages for lang-specific transforms
GK_ATOM(Japanese, "ja")
GK_ATOM(Chinese, "zh-CN")
GK_ATOM(Taiwanese, "zh-TW")
GK_ATOM(HongKongChinese, "zh-HK")
GK_ATOM(Unicode, "x-unicode")
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -12,16 +12,19 @@ interface URI;
interface nsIDocShell;
interface nsILoadGroup;
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 */
[Constructor]
interface Document : Node {
[Throws]
readonly attribute DOMImplementation implementation;
[Pure, Throws, BinaryName="documentURIFromJS", NeedsCallerType]
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -5039,27 +5039,47 @@ nsCSSFrameConstructor::ResolveStyleConte
nsIContent* aContent,
nsFrameConstructorState* aState)
{
StyleSetHandle styleSet = mPresShell->StyleSet();
aContent->OwnerDoc()->FlushPendingLinkUpdates();
RefPtr<nsStyleContext> result;
if (aContent->IsElement()) {
- if (aState) {
- result = styleSet->ResolveStyleFor(aContent->AsElement(),
- aParentStyleContext,
- ConsumeStyleBehavior::Consume,
- LazyComputeBehavior::Assert,
- aState->mTreeMatchContext);
+ auto pseudoType = CSSPseudoElementType(reinterpret_cast<uintptr_t>(
+ aContent->GetProperty(nsGkAtoms::pseudoProperty)));
+ if (pseudoType == CSSPseudoElementType(0)) {
+ // Although CSSPseudoElementType::after == 0, we disallow that specifying
+ // that pseudo in nsDocument::CreateElement, so we can just check for 0
+ // rather than passing the nsresult argument to GetProperty.
+ if (aState) {
+ result = styleSet->ResolveStyleFor(aContent->AsElement(),
+ aParentStyleContext,
+ ConsumeStyleBehavior::Consume,
+ LazyComputeBehavior::Assert,
+ aState->mTreeMatchContext);
+ } else {
+ result = styleSet->ResolveStyleFor(aContent->AsElement(),
+ aParentStyleContext,
+ ConsumeStyleBehavior::Consume,
+ LazyComputeBehavior::Assert);
+ }
} else {
- result = styleSet->ResolveStyleFor(aContent->AsElement(),
- aParentStyleContext,
- ConsumeStyleBehavior::Consume,
- LazyComputeBehavior::Assert);
+ MOZ_ASSERT(aContent->GetParentElement() &&
+ aContent->GetParentElement()->IsInNativeAnonymousSubtree(),
+ "only use nsGkAtoms::pseudoProperty for elements in native "
+ "anonymous subtrees");
+ Element* parent = aContent->GetParentElement();
+ while (!parent->IsRootOfNativeAnonymousSubtree()) {
+ parent = parent->GetParentElement();
+ }
+ result = styleSet->ResolvePseudoElementStyle(parent,
+ pseudoType,
+ aParentStyleContext,
+ aContent->AsElement());
}
} else {
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
"shouldn't waste time creating style contexts for "
"comments and processing instructions");
result = styleSet->ResolveStyleForText(aContent, aParentStyleContext);
}
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -677,17 +677,18 @@ nsComputedDOMStyle::UpdateCurrentStyleSo
SetFrameStyleContext(mInnerFrame->StyleContext());
NS_ASSERTION(mStyleContext, "Frame without style context?");
}
}
if (!mStyleContext || mStyleContext->HasPseudoElementData()) {
#ifdef DEBUG
- if (mStyleContext) {
+ if (mStyleContext &&
+ !mContent->AsElement()->GetProperty(nsGkAtoms::pseudoProperty)) {
// We want to check that going through this path because of
// HasPseudoElementData is rare, because it slows us down a good
// bit. So check that we're really inside something associated
// with a pseudo-element that contains elements.
nsStyleContext* topWithPseudoElementData = mStyleContext;
while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) {
topWithPseudoElementData = topWithPseudoElementData->GetParent();
}