mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 11:57:32 +09:00
Issue #1592 - Part 3: Ensure only tree-abiding pseudo-elements will follow ::slotted()
This commit is contained in:
parent
77ad970db6
commit
92b31dd255
7 changed files with 92 additions and 28 deletions
|
|
@ -318,7 +318,8 @@ nsCSSSelector::nsCSSSelector(void)
|
|||
mNext(nullptr),
|
||||
mNameSpace(kNameSpaceID_Unknown),
|
||||
mOperator(0),
|
||||
mPseudoType(CSSPseudoElementType::NotPseudo)
|
||||
mPseudoType(CSSPseudoElementType::NotPseudo),
|
||||
mHybridPseudoType(CSSPseudoElementType::NotPseudo)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsCSSSelector);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,6 +177,10 @@ public:
|
|||
return mLowercaseTag && !mCasedTag;
|
||||
}
|
||||
|
||||
inline bool IsHybridPseudoElement() const {
|
||||
return HybridPseudoType() != mozilla::CSSPseudoElementType::NotPseudo;
|
||||
}
|
||||
|
||||
// Calculate the specificity of this selector (not including its mNext!).
|
||||
int32_t CalcWeight() const;
|
||||
|
||||
|
|
@ -218,6 +222,12 @@ public:
|
|||
void SetPseudoType(mozilla::CSSPseudoElementType aType) {
|
||||
mPseudoType = aType;
|
||||
}
|
||||
mozilla::CSSPseudoElementType HybridPseudoType() const {
|
||||
return mHybridPseudoType;
|
||||
}
|
||||
void SetHybridPseudoType(mozilla::CSSPseudoElementType aType) {
|
||||
mHybridPseudoType = aType;
|
||||
}
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
|
|
@ -241,6 +251,7 @@ private:
|
|||
// The underlying type of CSSPseudoElementType is uint8_t and
|
||||
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)
|
||||
mozilla::CSSPseudoElementType mPseudoType;
|
||||
mozilla::CSSPseudoElementType mHybridPseudoType;
|
||||
|
||||
nsCSSSelector(const nsCSSSelector& aCopy) = delete;
|
||||
nsCSSSelector& operator=(const nsCSSSelector& aCopy) = delete;
|
||||
|
|
|
|||
|
|
@ -6097,6 +6097,8 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
|||
CSSEnabledState enabledState = EnabledState();
|
||||
CSSPseudoElementType pseudoElementType =
|
||||
nsCSSPseudoElements::GetPseudoType(pseudo, enabledState);
|
||||
bool pseudoElementIsTreeAbiding =
|
||||
nsCSSPseudoElements::IsTreeAbidingPseudoElement(pseudoElementType);
|
||||
CSSPseudoClassType pseudoClassType =
|
||||
nsCSSPseudoClasses::GetPseudoType(pseudo, enabledState);
|
||||
bool pseudoClassIsUserAction =
|
||||
|
|
@ -6121,19 +6123,21 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
|||
}
|
||||
}
|
||||
|
||||
// We handle the ::slotted() pseudo-element as if it it were a pseudo-class.
|
||||
// This is because the spec allows it to be followed by ::after/::before,
|
||||
// but our platform does not have a mechanism to handle multiple
|
||||
// pseudo-elements. It would be tedious to refactor pseudo-element
|
||||
// handling to accommodate for an edge case like this.
|
||||
bool isSlotPseudo = false;
|
||||
// We handle certain pseudo-elements as if they were a pseudo-class.
|
||||
// Our platform does not have the mechanism to handle multiple
|
||||
// pseudo-elements and proper storage if they have an argument.
|
||||
CSSPseudoElementType hybridPseudoElementType =
|
||||
CSSPseudoElementType::NotPseudo;
|
||||
if (parsingPseudoElement &&
|
||||
pseudoElementType == CSSPseudoElementType::slotted) {
|
||||
parsingPseudoElement = false;
|
||||
nsCSSPseudoElements::IsHybridPseudoElement(pseudoElementType)) {
|
||||
hybridPseudoElementType = pseudoElementType;
|
||||
pseudoElementType = CSSPseudoElementType::NotPseudo;
|
||||
pseudoClassType = CSSPseudoClassType::slotted;
|
||||
isSlotPseudo = true;
|
||||
aFlags |= SelectorParsingFlags::eDisallowCombinators;
|
||||
parsingPseudoElement = false;
|
||||
|
||||
if (hybridPseudoElementType == CSSPseudoElementType::slotted) {
|
||||
pseudoClassType = CSSPseudoClassType::slotted;
|
||||
aFlags |= SelectorParsingFlags::eDisallowCombinators;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
|
|
@ -6195,21 +6199,35 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
|||
return eSelectorParsingStatus_Error;
|
||||
}
|
||||
|
||||
if (aSelector.IsPseudoElement()) {
|
||||
CSSPseudoElementType type = aSelector.PseudoType();
|
||||
if (aSelector.IsPseudoElement() || aSelector.IsHybridPseudoElement()) {
|
||||
CSSPseudoElementType type = aSelector.IsPseudoElement() ?
|
||||
aSelector.PseudoType() :
|
||||
aSelector.HybridPseudoType();
|
||||
bool supportsTreeAbiding =
|
||||
nsCSSPseudoElements::PseudoElementSupportsTreeAbiding(type);
|
||||
bool supportsUserAction =
|
||||
nsCSSPseudoElements::PseudoElementSupportsUserActionState(type);
|
||||
if (type >= CSSPseudoElementType::Count ||
|
||||
!nsCSSPseudoElements::PseudoElementSupportsUserActionState(type)) {
|
||||
// We only allow user action pseudo-classes on certain pseudo-elements.
|
||||
(!supportsTreeAbiding && !supportsUserAction)) {
|
||||
// We only allow user action pseudo-classes and/or tree-abiding
|
||||
// pseudo-elements on certain pseudo-elements.
|
||||
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNoUserActionPC);
|
||||
UngetToken();
|
||||
return eSelectorParsingStatus_Error;
|
||||
}
|
||||
if (!isPseudoClass || !pseudoClassIsUserAction) {
|
||||
|
||||
if (isPseudoClass &&
|
||||
(!supportsUserAction || !pseudoClassIsUserAction)) {
|
||||
// CSS 4 Selectors says that pseudo-elements can only be followed by
|
||||
// a user action pseudo-class.
|
||||
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
|
||||
UngetToken();
|
||||
return eSelectorParsingStatus_Error;
|
||||
} else if (isPseudoElement &&
|
||||
(!supportsTreeAbiding || !pseudoElementIsTreeAbiding)) {
|
||||
REPORT_UNEXPECTED_TOKEN(PEPseudoClassNotUserAction);
|
||||
UngetToken();
|
||||
return eSelectorParsingStatus_Error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6239,9 +6257,9 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
|||
return parsingStatus;
|
||||
}
|
||||
}
|
||||
else if (CSSPseudoClassType::slotted == pseudoClassType &&
|
||||
!isSlotPseudo) {
|
||||
// Reject the :slotted() pseudo-class form.
|
||||
else if (nsCSSPseudoClasses::IsHybridPseudoElement(pseudoClassType) &&
|
||||
hybridPseudoElementType == CSSPseudoElementType::NotPseudo) {
|
||||
// Reject the single colon syntax for hybrid pseudo-elements.
|
||||
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNewStyleOnly);
|
||||
UngetToken();
|
||||
return eSelectorParsingStatus_Error;
|
||||
|
|
@ -6257,12 +6275,13 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
|||
else {
|
||||
MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
|
||||
"unexpected pseudo with function token");
|
||||
// Ensure that the ::slotted() pseudo-element is rejected if
|
||||
// pseudo-elements are disallowed.
|
||||
if (CSSPseudoClassType::slotted == pseudoClassType &&
|
||||
disallowPseudoElements) {
|
||||
UngetToken();
|
||||
return eSelectorParsingStatus_Error;
|
||||
if (hybridPseudoElementType != CSSPseudoElementType::NotPseudo) {
|
||||
aSelector.SetHybridPseudoType(hybridPseudoElementType);
|
||||
// Ensure hybrid pseudo-elements are rejected if they're not allowed.
|
||||
if (disallowPseudoElements) {
|
||||
UngetToken();
|
||||
return eSelectorParsingStatus_Error;
|
||||
}
|
||||
}
|
||||
parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
|
||||
pseudoClassType,
|
||||
|
|
@ -6752,7 +6771,8 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList,
|
|||
selector->mClassList = pseudoElementArgs.forget();
|
||||
selector->SetPseudoType(pseudoElementType);
|
||||
}
|
||||
} else if (selector->IsPseudoElement()) {
|
||||
} else if (selector->IsPseudoElement() ||
|
||||
selector->IsHybridPseudoElement()) {
|
||||
// Once we parsed a pseudo-element, we can only parse
|
||||
// pseudo-classes (and only a limited set, which
|
||||
// ParsePseudoSelector knows how to handle).
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2)
|
|||
|
||||
// XXX: ::slotted() is treated as if it were a pseudo-class, and
|
||||
// is never parsed as a pseudo-element.
|
||||
CSS_PSEUDO_ELEMENT(slotted, ":slotted", 0)
|
||||
CSS_PSEUDO_ELEMENT(slotted, ":slotted",
|
||||
CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING)
|
||||
|
||||
CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,22 @@ nsCSSPseudoElements::IsCSS2PseudoElement(nsIAtom *aAtom)
|
|||
return result;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsCSSPseudoElements::IsHybridPseudoElement(CSSPseudoElementType aType)
|
||||
{
|
||||
return aType == CSSPseudoElementType::slotted;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
nsCSSPseudoElements::IsTreeAbidingPseudoElement(CSSPseudoElementType aType)
|
||||
{
|
||||
// TODO: ::marker should be added here once we have support for it.
|
||||
return aType == CSSPseudoElementType::after ||
|
||||
aType == CSSPseudoElementType::before ||
|
||||
aType == CSSPseudoElementType::mozPlaceholder ||
|
||||
aType == CSSPseudoElementType::placeholder;
|
||||
}
|
||||
|
||||
/* static */ CSSPseudoElementType
|
||||
nsCSSPseudoElements::GetPseudoType(nsIAtom *aAtom, EnabledState aEnabledState)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@
|
|||
// API for creating pseudo-implementing native anonymous content in JS with this
|
||||
// pseudo-element?
|
||||
#define CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC (1<<5)
|
||||
// Is this pseudo-element a pseudo-element that supports a tree-abiding
|
||||
// pseudo-element following it, such as ::after or ::before? See
|
||||
// https://w3c.github.io/csswg-drafts/css-pseudo-4/#tree-abiding.
|
||||
#define CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING (1<<6)
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
@ -80,6 +84,10 @@ public:
|
|||
|
||||
static bool IsCSS2PseudoElement(nsIAtom *aAtom);
|
||||
|
||||
static bool IsHybridPseudoElement(Type aType);
|
||||
|
||||
static bool IsTreeAbidingPseudoElement(Type aType);
|
||||
|
||||
#define CSS_PSEUDO_ELEMENT(_name, _value, _flags) \
|
||||
static nsICSSPseudoElement* _name;
|
||||
#include "nsCSSPseudoElementList.h"
|
||||
|
|
@ -107,6 +115,12 @@ public:
|
|||
return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC);
|
||||
}
|
||||
|
||||
static bool PseudoElementSupportsTreeAbiding(const Type aType)
|
||||
{
|
||||
return PseudoElementHasFlags(aType,
|
||||
CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING);
|
||||
}
|
||||
|
||||
static bool IsEnabled(Type aType, EnabledState aEnabledState)
|
||||
{
|
||||
return !PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY) ||
|
||||
|
|
|
|||
|
|
@ -1393,6 +1393,7 @@ static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector,
|
|||
if (aSelector->HasTagSelector() || aSelector->mAttrList ||
|
||||
aSelector->mIDList || aSelector->mClassList ||
|
||||
aSelector->IsPseudoElement() ||
|
||||
aSelector->IsHybridPseudoElement() ||
|
||||
// Having this quirk means that some selectors will no longer match,
|
||||
// so it's better to return false when we aren't sure (i.e., the
|
||||
// flags are unknown).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue