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 --- layout/base/RestyleManager.cpp | 19 +++++++++++++++++++ layout/base/nsCSSFrameConstructor.cpp | 13 ++++++++++++- layout/style/nsCSSPseudoElements.h | 9 +++++++++ layout/style/nsComputedDOMStyle.cpp | 7 +++++-- 4 files changed, 45 insertions(+), 3 deletions(-) (limited to 'layout') diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index c3eeb8c71..16db4c18b 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -1062,6 +1062,25 @@ ElementForStyleContext(nsIContent* aParentContent, return f->GetContent()->AsElement(); } + Element* frameElement = aFrame->GetContent()->AsElement(); + if (frameElement->IsNativeAnonymous() && + nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(aPseudoType)) { + // NAC-implemented pseudos use the closest non-NAC element as their + // element to inherit from. + // + // FIXME(heycam): In theory we shouldn't need to limit this only to + // JS-created pseudo-implementing NAC, as all pseudo-implementing + // should use the closest non-native anonymous ancestor element as + // its originating element. But removing that part of the condition + // reveals some bugs in style resultion with display:contents and + // XBL. See bug 1345809. + Element* originatingElement = + nsContentUtils::GetClosestNonNativeAnonymousAncestor(frameElement); + if (originatingElement) { + return originatingElement; + } + } + if (aParentContent) { return aParentContent->AsElement(); } diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 3be7c2a0b..b574e7690 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -5041,8 +5041,19 @@ nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, aParentStyleContext); } } else { - MOZ_ASSERT(aOriginatingElementOrNull); MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree()); + if (!aOriginatingElementOrNull) { + // For pseudo-implementing NAC created by JS using the ChromeOnly + // document.createElement(..., { pseudo: ... }) API, we find the + // originating element by lookup the tree until we find a non-NAC + // ancestor. (These are the correct semantics for C++-generated pseudo- + // implementing NAC as well, but for those cases we already have a + // correct originating element passed in.) + MOZ_ASSERT(nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)); + aOriginatingElementOrNull = + nsContentUtils::GetClosestNonNativeAnonymousAncestor(aContent->AsElement()); + } + MOZ_ASSERT(aOriginatingElementOrNull); result = styleSet->ResolvePseudoElementStyle(aOriginatingElementOrNull, pseudoType, aParentStyleContext, diff --git a/layout/style/nsCSSPseudoElements.h b/layout/style/nsCSSPseudoElements.h index acf818a2c..64eb2f00c 100644 --- a/layout/style/nsCSSPseudoElements.h +++ b/layout/style/nsCSSPseudoElements.h @@ -36,6 +36,10 @@ #define CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (1<<3) // Is content prevented from parsing selectors containing this pseudo-element? #define CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY (1<<4) +// Can we use the ChromeOnly document.createElement(..., { pseudo: "::foo" }) +// API for creating pseudo-implementing native anonymous content in JS with this +// pseudo-element? +#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC (1<<5) namespace mozilla { @@ -98,6 +102,11 @@ public: static bool PseudoElementSupportsUserActionState(const Type aType); + static bool PseudoElementIsJSCreatedNAC(Type aType) + { + return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC); + } + static bool IsEnabled(Type aType, EnabledState aEnabledState) { return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) || diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 910c1de8a..102e0df18 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -717,7 +717,9 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) // 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. + // with a pseudo-element that contains elements. (We also allow + // the element to be NAC, just in case some chrome JS calls + // getComputedStyle on a NAC-implemented pseudo.) nsStyleContext* topWithPseudoElementData = mStyleContext; while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) { topWithPseudoElementData = topWithPseudoElementData->GetParent(); @@ -728,7 +730,8 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) NS_LITERAL_STRING("we should be in a pseudo-element that is expected to contain elements (")); assertMsg.Append(nsDependentString(pseudoAtom->GetUTF16String())); assertMsg.Append(')'); - NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo), + NS_ASSERTION(nsCSSPseudoElements::PseudoElementContainsElements(pseudo) || + mElement->IsNativeAnonymous(), NS_LossyConvertUTF16toASCII(assertMsg).get()); } #endif -- cgit v1.2.3