mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-09 16:57:30 +09:00
Issue #1593 - Part 1: Import William Chen's patches w/o selector implementation, fixed up.
This commit is contained in:
parent
ecdf4bfb29
commit
7472bc02be
10 changed files with 273 additions and 32 deletions
|
|
@ -108,6 +108,7 @@ public:
|
||||||
|
|
||||||
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
|
void SetInsertionPointChanged() { mInsertionPointChanged = true; }
|
||||||
|
|
||||||
|
nsXBLBinding* GetAssociatedBinding() { return mAssociatedBinding; }
|
||||||
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
|
void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
|
||||||
|
|
||||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include "nsXBLPrototypeBinding.h"
|
#include "nsXBLPrototypeBinding.h"
|
||||||
#include "nsXBLDocumentInfo.h"
|
#include "nsXBLDocumentInfo.h"
|
||||||
#include "mozilla/dom/XBLChildrenElement.h"
|
#include "mozilla/dom/XBLChildrenElement.h"
|
||||||
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
|
|
||||||
#include "nsIStyleRuleProcessor.h"
|
#include "nsIStyleRuleProcessor.h"
|
||||||
#include "nsRuleProcessorData.h"
|
#include "nsRuleProcessorData.h"
|
||||||
|
|
@ -663,13 +664,44 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
|
|
||||||
NS_ASSERTION(aData->mElement, "How did that happen?");
|
NS_ASSERTION(aData->mElement, "How did that happen?");
|
||||||
|
|
||||||
|
// Walk the rules in shadow root for :host pseudo-class rules.
|
||||||
|
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||||
|
aData->mElementIsFeatureless = true;
|
||||||
|
|
||||||
|
ShadowRoot* currentShadow = aData->mElement->GetShadowRoot();
|
||||||
|
// XXX: Obviously not the right approach for Shadow DOM v1.
|
||||||
|
// Per spec, rules from younger shadow DOM win over rules from older shadow
|
||||||
|
// DOM. Iterate to the oldest shadow root on the host then walk back to the
|
||||||
|
// youngest when walking rules.
|
||||||
|
while (currentShadow) {
|
||||||
|
ShadowRoot* olderShadow = currentShadow->GetOlderShadowRoot();
|
||||||
|
if (!olderShadow) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentShadow = olderShadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentShadow) {
|
||||||
|
nsXBLBinding* associatedBinding = currentShadow->GetAssociatedBinding();
|
||||||
|
if (associatedBinding) {
|
||||||
|
aData->mTreeMatchContext.mScopedRoot = aData->mElement;
|
||||||
|
associatedBinding->WalkRules(aFunc, aData);
|
||||||
|
}
|
||||||
|
currentShadow = currentShadow->GetYoungerShadowRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
aData->mElementIsFeatureless = false;
|
||||||
|
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||||
|
|
||||||
// Walk the binding scope chain, starting with the binding attached to our
|
// Walk the binding scope chain, starting with the binding attached to our
|
||||||
// content, up till we run out of scopes or we get cut off.
|
// content, up till we run out of scopes or we get cut off.
|
||||||
nsIContent *content = aData->mElement;
|
nsIContent *content = aData->mElement;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
nsXBLBinding *binding = content->GetXBLBinding();
|
nsXBLBinding *binding = content->GetXBLBinding();
|
||||||
if (binding) {
|
if (binding &&
|
||||||
|
// Unlike XBL, styles in the shadow root are not applied to the host.
|
||||||
|
!(content == aData->mElement && binding->IsShadowRootBinding())) {
|
||||||
aData->mTreeMatchContext.mScopedRoot = content;
|
aData->mTreeMatchContext.mScopedRoot = content;
|
||||||
binding->WalkRules(aFunc, aData);
|
binding->WalkRules(aFunc, aData);
|
||||||
// If we're not looking at our original content, allow the binding to cut
|
// If we're not looking at our original content, allow the binding to cut
|
||||||
|
|
@ -701,15 +733,41 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
|
|
||||||
typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
|
typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
|
||||||
|
|
||||||
|
// XXX: Figure out what the anonymous namespace is for. Is this even appropriate now that we're using iterators instead of enumerators?
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct RuleProcessorsData {
|
||||||
|
RuleProcessorSet* mSet;
|
||||||
|
bool mOnlyWalkShadowHosts;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
static RuleProcessorSet*
|
static RuleProcessorSet*
|
||||||
GetContentSetRuleProcessors(nsTHashtable<nsRefPtrHashKey<nsIContent>>* aContentSet)
|
GetContentSetRuleProcessors(nsTHashtable<nsRefPtrHashKey<nsIContent>>* aContentSet)
|
||||||
{
|
{
|
||||||
RuleProcessorSet* set = nullptr;
|
RuleProcessorsData* data = nullptr;
|
||||||
|
RuleProcessorSet* set = data->mSet;
|
||||||
|
|
||||||
|
|
||||||
for (auto iter = aContentSet->Iter(); !iter.Done(); iter.Next()) {
|
for (auto iter = aContentSet->Iter(); !iter.Done(); iter.Next()) {
|
||||||
nsIContent* boundContent = iter.Get()->GetKey();
|
nsIContent* boundContent = iter.Get()->GetKey();
|
||||||
for (nsXBLBinding* binding = boundContent->GetXBLBinding(); binding;
|
|
||||||
binding = binding->GetBaseBinding()) {
|
// If we are only walking rules for shadow root hosts, skip other types
|
||||||
|
// of bound content.
|
||||||
|
ShadowRoot *shadowRoot = boundContent->GetShadowRoot();
|
||||||
|
if (data->mOnlyWalkShadowHosts && !shadowRoot) {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bound content may have multiple rule processors, potentially one
|
||||||
|
// for its immediate binding, and one more for each binding in the
|
||||||
|
// inheritance chain. Additionally, a bound content may host multiple
|
||||||
|
// shadow roots, each with its own rule processor.
|
||||||
|
nsXBLBinding *binding = boundContent->GetXBLBinding();
|
||||||
|
while (binding) {
|
||||||
nsIStyleRuleProcessor* ruleProc =
|
nsIStyleRuleProcessor* ruleProc =
|
||||||
binding->PrototypeBinding()->GetRuleProcessor();
|
binding->PrototypeBinding()->GetRuleProcessor();
|
||||||
if (ruleProc) {
|
if (ruleProc) {
|
||||||
|
|
@ -718,15 +776,30 @@ GetContentSetRuleProcessors(nsTHashtable<nsRefPtrHashKey<nsIContent>>* aContentS
|
||||||
}
|
}
|
||||||
set->PutEntry(ruleProc);
|
set->PutEntry(ruleProc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding = binding->GetBaseBinding();
|
||||||
|
// XXX: Obviously not the right approach for Shadow DOM v1.
|
||||||
|
// If there isn't a base binding, start walking the binding of the
|
||||||
|
// next older shadow root hosted by the bound content (if any).
|
||||||
|
if (!binding && shadowRoot) {
|
||||||
|
shadowRoot = shadowRoot->GetOlderShadowRoot();
|
||||||
|
if (shadowRoot) {
|
||||||
|
binding = shadowRoot->GetAssociatedBinding();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: I think since this calls GetContentSetRuleProcessors directly now, no changes should be needed here? The original enumerator patch used the RuleProcessorsData function from the anonymous namespace here.
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
ElementDependentRuleProcessorData* aData)
|
ElementDependentRuleProcessorData* aData,
|
||||||
|
bool aOnlyWalkShadowRootRules)
|
||||||
{
|
{
|
||||||
if (!mBoundContentSet) {
|
if (!mBoundContentSet) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -744,6 +817,19 @@ nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The approach in WalkAllShadowRootHostRules seems reasonable on the surface and reminds me of what is done with mScopedRoot elsewhere. But something important is missing, either here or in the code that would normally use it.
|
||||||
|
|
||||||
|
void
|
||||||
|
nsBindingManager::WalkAllShadowRootHostRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
|
ElementDependentRuleProcessorData* aData)
|
||||||
|
{
|
||||||
|
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||||
|
WalkAllRules(aFunc, aData, true);
|
||||||
|
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//XXX: I think since this calls GetContentSetRuleProcessors directly now, no changes should be needed here? The original enumerator patch used the RuleProcessorsData function from the anonymous namespace here.
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
|
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
|
||||||
bool* aRulesChanged)
|
bool* aRulesChanged)
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,11 @@ public:
|
||||||
bool* aCutOffInheritance);
|
bool* aCutOffInheritance);
|
||||||
|
|
||||||
void WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
void WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
ElementDependentRuleProcessorData* aData);
|
ElementDependentRuleProcessorData* aData,
|
||||||
|
bool aOnlyWalkShadowRootRules = false);
|
||||||
|
|
||||||
|
void WalkAllShadowRootHostRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
|
ElementDependentRuleProcessorData* aData);
|
||||||
/**
|
/**
|
||||||
* Do any processing that needs to happen as a result of a change in
|
* Do any processing that needs to happen as a result of a change in
|
||||||
* the characteristics of the medium, and return whether this rule
|
* the characteristics of the medium, and return whether this rule
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,11 @@ public:
|
||||||
|
|
||||||
mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild);
|
mozilla::dom::XBLChildrenElement* FindInsertionPointFor(nsIContent* aChild);
|
||||||
|
|
||||||
|
bool IsShadowRootBinding()
|
||||||
|
{
|
||||||
|
return mIsShadowRootBinding;
|
||||||
|
}
|
||||||
|
|
||||||
bool HasFilteredInsertionPoints()
|
bool HasFilteredInsertionPoints()
|
||||||
{
|
{
|
||||||
return !mInsertionPoints.IsEmpty();
|
return !mInsertionPoints.IsEmpty();
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,8 @@ nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
|
||||||
: mType(aType),
|
: mType(aType),
|
||||||
mNext(nullptr)
|
mNext(nullptr)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType),
|
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType) ||
|
||||||
|
nsCSSPseudoClasses::HasOptionalSelectorListArg(aType),
|
||||||
"unexpected pseudo-class");
|
"unexpected pseudo-class");
|
||||||
NS_ASSERTION(aSelectorList, "selector list expected");
|
NS_ASSERTION(aSelectorList, "selector list expected");
|
||||||
MOZ_COUNT_CTOR(nsPseudoClassList);
|
MOZ_COUNT_CTOR(nsPseudoClassList);
|
||||||
|
|
@ -313,6 +314,7 @@ nsCSSSelector::nsCSSSelector(void)
|
||||||
mPseudoClassList(nullptr),
|
mPseudoClassList(nullptr),
|
||||||
mAttrList(nullptr),
|
mAttrList(nullptr),
|
||||||
mNegations(nullptr),
|
mNegations(nullptr),
|
||||||
|
mExplicitUniversal(false),
|
||||||
mNext(nullptr),
|
mNext(nullptr),
|
||||||
mNameSpace(kNameSpaceID_Unknown),
|
mNameSpace(kNameSpaceID_Unknown),
|
||||||
mOperator(0),
|
mOperator(0),
|
||||||
|
|
@ -382,6 +384,17 @@ void nsCSSSelector::Reset(void)
|
||||||
mOperator = char16_t(0);
|
mOperator = char16_t(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nsCSSSelector::HasFeatureSelectors()
|
||||||
|
{
|
||||||
|
return mExplicitUniversal || mLowercaseTag || mCasedTag ||
|
||||||
|
mIDList || mClassList || mAttrList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsCSSSelector::SetHasExplicitUniversal()
|
||||||
|
{
|
||||||
|
mExplicitUniversal = true;
|
||||||
|
}
|
||||||
|
|
||||||
void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
|
void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
|
||||||
{
|
{
|
||||||
mNameSpace = aNameSpace;
|
mNameSpace = aNameSpace;
|
||||||
|
|
@ -750,9 +763,9 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
|
||||||
// Universal selector: avoid writing the universal selector when we
|
// Universal selector: avoid writing the universal selector when we
|
||||||
// can avoid it, especially since we're required to avoid it for the
|
// can avoid it, especially since we're required to avoid it for the
|
||||||
// inside of :not()
|
// inside of :not()
|
||||||
if (wroteNamespace ||
|
if (wroteNamespace || mExplicitUniversal ||
|
||||||
(!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
|
(!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
|
||||||
(aIsNegated || !mNegations))) {
|
aIsNegated)) {
|
||||||
aString.Append(char16_t('*'));
|
aString.Append(char16_t('*'));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -760,9 +773,7 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
|
||||||
nsAutoString tag;
|
nsAutoString tag;
|
||||||
(isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
|
(isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
|
||||||
if (isPseudoElement) {
|
if (isPseudoElement) {
|
||||||
if (!mNext) {
|
if (mExplicitUniversal) {
|
||||||
// Lone pseudo-element selector -- toss in a wildcard type selector
|
|
||||||
// XXXldb Why?
|
|
||||||
aString.Append(char16_t('*'));
|
aString.Append(char16_t('*'));
|
||||||
}
|
}
|
||||||
// While our atoms use one colon, most pseudo-elements require two
|
// While our atoms use one colon, most pseudo-elements require two
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,8 @@ public:
|
||||||
nsCSSSelector* Clone() const { return Clone(true, true); }
|
nsCSSSelector* Clone() const { return Clone(true, true); }
|
||||||
|
|
||||||
void Reset(void);
|
void Reset(void);
|
||||||
|
bool HasFeatureSelectors();
|
||||||
|
void SetHasExplicitUniversal();
|
||||||
void SetNameSpace(int32_t aNameSpace);
|
void SetNameSpace(int32_t aNameSpace);
|
||||||
void SetTag(const nsString& aTag);
|
void SetTag(const nsString& aTag);
|
||||||
void AddID(const nsString& aID);
|
void AddID(const nsString& aID);
|
||||||
|
|
@ -231,9 +233,10 @@ public:
|
||||||
// the argument to functional pseudos
|
// the argument to functional pseudos
|
||||||
nsAttrSelector* mAttrList;
|
nsAttrSelector* mAttrList;
|
||||||
nsCSSSelector* mNegations;
|
nsCSSSelector* mNegations;
|
||||||
nsCSSSelector* mNext;
|
bool mExplicitUniversal; // True if universal selector explicitly
|
||||||
|
nsCSSSelector* mNext; // appears in the selector
|
||||||
int32_t mNameSpace;
|
int32_t mNameSpace;
|
||||||
char16_t mOperator;
|
char mOperator;
|
||||||
private:
|
private:
|
||||||
// The underlying type of CSSPseudoElementType is uint8_t and
|
// The underlying type of CSSPseudoElementType is uint8_t and
|
||||||
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)
|
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)
|
||||||
|
|
|
||||||
|
|
@ -5641,6 +5641,7 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
|
||||||
aSelector.SetTag(mToken.mIdent);
|
aSelector.SetTag(mToken.mIdent);
|
||||||
}
|
}
|
||||||
else if (mToken.IsSymbol('*')) { // universal selector
|
else if (mToken.IsSymbol('*')) { // universal selector
|
||||||
|
aSelector.SetHasExplicitUniversal();
|
||||||
aDataMask |= SEL_MASK_ELEM;
|
aDataMask |= SEL_MASK_ELEM;
|
||||||
// don't set tag
|
// don't set tag
|
||||||
}
|
}
|
||||||
|
|
@ -5652,6 +5653,7 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
|
||||||
}
|
}
|
||||||
else { // was universal element selector
|
else { // was universal element selector
|
||||||
SetDefaultNamespaceOnSelector(aSelector);
|
SetDefaultNamespaceOnSelector(aSelector);
|
||||||
|
aSelector.SetHasExplicitUniversal();
|
||||||
aDataMask |= SEL_MASK_ELEM;
|
aDataMask |= SEL_MASK_ELEM;
|
||||||
// don't set any tag in the selector
|
// don't set any tag in the selector
|
||||||
}
|
}
|
||||||
|
|
@ -6097,7 +6099,8 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||||
CSSPseudoClassType::negation == pseudoClassType ||
|
CSSPseudoClassType::negation == pseudoClassType ||
|
||||||
nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
|
nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
|
||||||
nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
|
nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
|
||||||
nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
|
nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType)) &&
|
||||||
|
!nsCSSPseudoClasses::HasOptionalSelectorListArg(pseudoClassType)) {
|
||||||
// There are no other function pseudos
|
// There are no other function pseudos
|
||||||
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
|
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
|
||||||
UngetToken();
|
UngetToken();
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
#include "nsCSSRules.h"
|
#include "nsCSSRules.h"
|
||||||
#include "nsStyleSet.h"
|
#include "nsStyleSet.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
#include "nsNthIndexCache.h"
|
#include "nsNthIndexCache.h"
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/EventStates.h"
|
#include "mozilla/EventStates.h"
|
||||||
|
|
@ -1342,9 +1343,17 @@ struct NodeMatchContext {
|
||||||
// mForStyling is false, we have to assume we don't know.)
|
// mForStyling is false, we have to assume we don't know.)
|
||||||
const bool mIsRelevantLink;
|
const bool mIsRelevantLink;
|
||||||
|
|
||||||
NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
|
// If the node should be considered featureless (as specified in
|
||||||
|
// selectors 4), then mIsFeature should be set to true to prevent
|
||||||
|
// matching unless the selector is a special pseudo class or pseudo
|
||||||
|
// element that matches featureless elements.
|
||||||
|
const bool mIsFeatureless;
|
||||||
|
|
||||||
|
NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink,
|
||||||
|
bool aIsFeatureless = false)
|
||||||
: mStateMask(aStateMask)
|
: mStateMask(aStateMask)
|
||||||
, mIsRelevantLink(aIsRelevantLink)
|
, mIsRelevantLink(aIsRelevantLink)
|
||||||
|
, mIsFeatureless(aIsFeatureless)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1631,6 +1640,11 @@ StateSelectorMatches(Element* aElement,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool AnySelectorInArgListMatches(Element* aElement,
|
||||||
|
nsPseudoClassList* aList,
|
||||||
|
NodeMatchContext& aNodeMatchContext,
|
||||||
|
TreeMatchContext& aTreeMatchContext);
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
StateSelectorMatches(Element* aElement,
|
StateSelectorMatches(Element* aElement,
|
||||||
nsCSSSelector* aSelector,
|
nsCSSSelector* aSelector,
|
||||||
|
|
@ -1652,6 +1666,17 @@ StateSelectorMatches(Element* aElement,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns whether aSelector can match featureless elements.
|
||||||
|
static bool CanMatchFeaturelessElement(nsCSSSelector* aSelector)
|
||||||
|
{
|
||||||
|
if (aSelector->HasFeatureSelectors()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// |aDependence| has two functions:
|
// |aDependence| has two functions:
|
||||||
// * when non-null, it indicates that we're processing a negation,
|
// * when non-null, it indicates that we're processing a negation,
|
||||||
// which is done only when SelectorMatches calls itself recursively
|
// which is done only when SelectorMatches calls itself recursively
|
||||||
|
|
@ -1672,6 +1697,11 @@ static bool SelectorMatches(Element* aElement,
|
||||||
"is false since we don't know how to set it correctly in "
|
"is false since we don't know how to set it correctly in "
|
||||||
"Has(Attribute|State)DependentStyle");
|
"Has(Attribute|State)DependentStyle");
|
||||||
|
|
||||||
|
if (aNodeMatchContext.mIsFeatureless &&
|
||||||
|
!CanMatchFeaturelessElement(aSelector)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// namespace/tag match
|
// namespace/tag match
|
||||||
// optimization : bail out early if we can
|
// optimization : bail out early if we can
|
||||||
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
|
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
|
||||||
|
|
@ -1870,18 +1900,9 @@ static bool SelectorMatches(Element* aElement,
|
||||||
|
|
||||||
case CSSPseudoClassType::any:
|
case CSSPseudoClassType::any:
|
||||||
{
|
{
|
||||||
nsCSSSelectorList *l;
|
if (!AnySelectorInArgListMatches(aElement, pseudoClass,
|
||||||
for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
|
aNodeMatchContext,
|
||||||
nsCSSSelector *s = l->mSelectors;
|
aTreeMatchContext)) {
|
||||||
MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(),
|
|
||||||
"parser failed");
|
|
||||||
if (SelectorMatches(
|
|
||||||
aElement, s, aNodeMatchContext, aTreeMatchContext,
|
|
||||||
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!l) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2263,6 +2284,26 @@ static bool SelectorMatches(Element* aElement,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool AnySelectorInArgListMatches(Element* aElement,
|
||||||
|
nsPseudoClassList* aList,
|
||||||
|
NodeMatchContext& aNodeMatchContext,
|
||||||
|
TreeMatchContext& aTreeMatchContext)
|
||||||
|
{
|
||||||
|
nsCSSSelectorList *l;
|
||||||
|
for (l = aList->u.mSelectors; l; l = l->mNext) {
|
||||||
|
nsCSSSelector *s = l->mSelectors;
|
||||||
|
MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(),
|
||||||
|
"parser failed");
|
||||||
|
if (SelectorMatches(
|
||||||
|
aElement, s, aNodeMatchContext, aTreeMatchContext,
|
||||||
|
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!l;
|
||||||
|
}
|
||||||
|
|
||||||
#undef STATE_CHECK
|
#undef STATE_CHECK
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -2352,7 +2393,9 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
|
MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
|
||||||
nsCSSSelector* selector = aSelector;
|
nsCSSSelector* selector = aSelector;
|
||||||
Element* prevElement = aPrevElement;
|
Element* prevElement = aPrevElement;
|
||||||
|
bool crossedShadowRootBoundary = false;
|
||||||
while (selector) { // check compound selectors
|
while (selector) { // check compound selectors
|
||||||
|
bool contentIsFeatureless = false;
|
||||||
NS_ASSERTION(!selector->mNext ||
|
NS_ASSERTION(!selector->mNext ||
|
||||||
selector->mNext->mOperator != char16_t(0),
|
selector->mNext->mOperator != char16_t(0),
|
||||||
"compound selector without combinator");
|
"compound selector without combinator");
|
||||||
|
|
@ -2383,6 +2426,20 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
// to test against is the parent
|
// to test against is the parent
|
||||||
else {
|
else {
|
||||||
nsIContent *content = prevElement->GetParent();
|
nsIContent *content = prevElement->GetParent();
|
||||||
|
|
||||||
|
// In the shadow tree, the shadow host behaves as if it
|
||||||
|
// is a featureless parent of top-level elements of the shadow
|
||||||
|
// tree. Only cross shadow root boundary when the selector is the
|
||||||
|
// left most selector because ancestors of the host are not in
|
||||||
|
// the selector match list.
|
||||||
|
ShadowRoot* shadowRoot = content ?
|
||||||
|
ShadowRoot::FromNode(content) : nullptr;
|
||||||
|
if (shadowRoot && !selector->mNext && !crossedShadowRootBoundary) {
|
||||||
|
content = shadowRoot->GetHost();
|
||||||
|
crossedShadowRootBoundary = true;
|
||||||
|
contentIsFeatureless = true;
|
||||||
|
}
|
||||||
|
|
||||||
// GetParent could return a document fragment; we only want
|
// GetParent could return a document fragment; we only want
|
||||||
// element parents.
|
// element parents.
|
||||||
if (content && content->IsElement()) {
|
if (content && content->IsElement()) {
|
||||||
|
|
@ -2434,7 +2491,8 @@ SelectorMatchesTree(Element* aPrevElement,
|
||||||
}
|
}
|
||||||
const bool isRelevantLink = (aFlags & eLookForRelevantLink) &&
|
const bool isRelevantLink = (aFlags & eLookForRelevantLink) &&
|
||||||
nsCSSRuleProcessor::IsLink(element);
|
nsCSSRuleProcessor::IsLink(element);
|
||||||
NodeMatchContext nodeContext(EventStates(), isRelevantLink);
|
|
||||||
|
NodeMatchContext nodeContext(EventStates(), isRelevantLink, contentIsFeatureless);
|
||||||
if (isRelevantLink) {
|
if (isRelevantLink) {
|
||||||
// If we find an ancestor of the matched node that is a link
|
// If we find an ancestor of the matched node that is a link
|
||||||
// during the matching process, then it's the relevant link (see
|
// during the matching process, then it's the relevant link (see
|
||||||
|
|
@ -2504,6 +2562,20 @@ void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
||||||
// We won't match; nothing else to do here
|
// We won't match; nothing else to do here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If mOnlyMatchHostPseudo is set, then we only want to match against
|
||||||
|
// selectors that contain a :host-context pseudo class.
|
||||||
|
if (data->mTreeMatchContext.mOnlyMatchHostPseudo) {
|
||||||
|
nsCSSSelector* selector = aSelector;
|
||||||
|
while (selector && selector->mNext != nullptr) {
|
||||||
|
selector = selector->mNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seenHostPseudo = false;
|
||||||
|
|
||||||
|
if (!seenHostPseudo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
|
if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
|
||||||
data->mScope)) {
|
data->mScope)) {
|
||||||
// The selector is for a rule in a scoped style sheet, and the subject
|
// The selector is for a rule in a scoped style sheet, and the subject
|
||||||
|
|
@ -2563,7 +2635,8 @@ nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
|
||||||
|
|
||||||
if (cascade) {
|
if (cascade) {
|
||||||
NodeMatchContext nodeContext(EventStates(),
|
NodeMatchContext nodeContext(EventStates(),
|
||||||
nsCSSRuleProcessor::IsLink(aData->mElement));
|
nsCSSRuleProcessor::IsLink(aData->mElement),
|
||||||
|
aData->mElementIsFeatureless);
|
||||||
cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
|
cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2847,6 +2920,24 @@ AttributeEnumFunc(nsCSSSelector* aSelector,
|
||||||
nsRestyleHint possibleChange =
|
nsRestyleHint possibleChange =
|
||||||
RestyleHintForSelectorWithAttributeChange(aData->change,
|
RestyleHintForSelectorWithAttributeChange(aData->change,
|
||||||
aSelector, aRightmostSelector);
|
aSelector, aRightmostSelector);
|
||||||
|
// If mOnlyMatchHostPseudo is set, then we only want to match against
|
||||||
|
// selectors that contain a :host-context pseudo class.
|
||||||
|
if (data->mTreeMatchContext.mOnlyMatchHostPseudo) {
|
||||||
|
nsCSSSelector* selector = aSelector;
|
||||||
|
while (selector && selector->mNext != nullptr) {
|
||||||
|
selector = selector->mNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seenHostPseudo = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!seenHostPseudo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// If, ignoring eRestyle_SomeDescendants, enumData->change already includes
|
// If, ignoring eRestyle_SomeDescendants, enumData->change already includes
|
||||||
// all the bits of possibleChange, don't bother calling SelectorMatches, since
|
// all the bits of possibleChange, don't bother calling SelectorMatches, since
|
||||||
|
|
@ -3318,7 +3409,7 @@ AddSelector(RuleCascadeData* aCascade,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recur through any :-moz-any selectors
|
// Recur through any :-moz-any or :host-context selectors
|
||||||
for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
|
for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
|
||||||
pseudoClass; pseudoClass = pseudoClass->mNext) {
|
pseudoClass; pseudoClass = pseudoClass->mNext) {
|
||||||
if (pseudoClass->mType == CSSPseudoClassType::any) {
|
if (pseudoClass->mType == CSSPseudoClassType::any) {
|
||||||
|
|
@ -3970,6 +4061,7 @@ TreeMatchContext::InitAncestors(Element *aElement)
|
||||||
for (uint32_t i = ancestors.Length(); i-- != 0; ) {
|
for (uint32_t i = ancestors.Length(); i-- != 0; ) {
|
||||||
mAncestorFilter.PushAncestor(ancestors[i]);
|
mAncestorFilter.PushAncestor(ancestors[i]);
|
||||||
PushStyleScope(ancestors[i]);
|
PushStyleScope(ancestors[i]);
|
||||||
|
PushShadowHost(ancestors[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,23 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PushShadowHost(mozilla::dom::Element* aElement)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(aElement, "aElement must not be null");
|
||||||
|
if (aElement->GetShadowRoot()) {
|
||||||
|
mShadowHosts.AppendElement(aElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopShadowHost(mozilla::dom::Element* aElement)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(aElement, "aElement must not be null");
|
||||||
|
if (mShadowHosts.SafeLastElement(nullptr) == aElement) {
|
||||||
|
mShadowHosts.TruncateLength(mShadowHosts.Length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PopStyleScopeForSelectorMatching(mozilla::dom::Element* aElement)
|
bool PopStyleScopeForSelectorMatching(mozilla::dom::Element* aElement)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mForScopedStyle, "only call PopStyleScopeForSelectorMatching "
|
NS_ASSERTION(mForScopedStyle, "only call PopStyleScopeForSelectorMatching "
|
||||||
|
|
@ -233,6 +250,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||||
: mPushedAncestor(false)
|
: mPushedAncestor(false)
|
||||||
, mPushedStyleScope(false)
|
, mPushedStyleScope(false)
|
||||||
|
, mPushedShadowHost(false)
|
||||||
, mTreeMatchContext(aTreeMatchContext)
|
, mTreeMatchContext(aTreeMatchContext)
|
||||||
, mElement(nullptr)
|
, mElement(nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -245,8 +263,10 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
mElement = aElement;
|
mElement = aElement;
|
||||||
mPushedAncestor = true;
|
mPushedAncestor = true;
|
||||||
mPushedStyleScope = true;
|
mPushedStyleScope = true;
|
||||||
|
mPushedShadowHost = true;
|
||||||
mTreeMatchContext.mAncestorFilter.PushAncestor(aElement);
|
mTreeMatchContext.mAncestorFilter.PushAncestor(aElement);
|
||||||
mTreeMatchContext.PushStyleScope(aElement);
|
mTreeMatchContext.PushStyleScope(aElement);
|
||||||
|
mTreeMatchContext.PushShadowHost(aElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,11 +298,15 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
if (mPushedStyleScope) {
|
if (mPushedStyleScope) {
|
||||||
mTreeMatchContext.PopStyleScope(mElement);
|
mTreeMatchContext.PopStyleScope(mElement);
|
||||||
}
|
}
|
||||||
|
if (mPushedShadowHost) {
|
||||||
|
mTreeMatchContext.PopShadowHost(mElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mPushedAncestor;
|
bool mPushedAncestor;
|
||||||
bool mPushedStyleScope;
|
bool mPushedStyleScope;
|
||||||
|
bool mPushedShadowHost;
|
||||||
TreeMatchContext& mTreeMatchContext;
|
TreeMatchContext& mTreeMatchContext;
|
||||||
mozilla::dom::Element* mElement;
|
mozilla::dom::Element* mElement;
|
||||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||||
|
|
@ -342,6 +366,11 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
// The document we're working with.
|
// The document we're working with.
|
||||||
nsIDocument* const mDocument;
|
nsIDocument* const mDocument;
|
||||||
|
|
||||||
|
// Only selectors that contain :host or :host-context pseudo class
|
||||||
|
// should be matched against elements. All other selectors should not
|
||||||
|
// match.
|
||||||
|
bool mOnlyMatchHostPseudo;
|
||||||
|
|
||||||
// Root of scoped stylesheet (set and unset by the supplier of the
|
// Root of scoped stylesheet (set and unset by the supplier of the
|
||||||
// scoped stylesheet).
|
// scoped stylesheet).
|
||||||
nsIContent* mScopedRoot;
|
nsIContent* mScopedRoot;
|
||||||
|
|
@ -383,6 +412,9 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
// <style scoped> child).
|
// <style scoped> child).
|
||||||
AutoTArray<mozilla::dom::Element*, 1> mStyleScopes;
|
AutoTArray<mozilla::dom::Element*, 1> mStyleScopes;
|
||||||
|
|
||||||
|
// List of ancestor elements that are a shadow root host.
|
||||||
|
AutoTArray<mozilla::dom::Element*, 1> mShadowHosts;
|
||||||
|
|
||||||
// The current style scope element for selector matching.
|
// The current style scope element for selector matching.
|
||||||
mozilla::dom::Element* mCurrentStyleScope;
|
mozilla::dom::Element* mCurrentStyleScope;
|
||||||
|
|
||||||
|
|
@ -396,6 +428,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
||||||
, mHaveSpecifiedScope(false)
|
, mHaveSpecifiedScope(false)
|
||||||
, mVisitedHandling(aVisitedHandling)
|
, mVisitedHandling(aVisitedHandling)
|
||||||
, mDocument(aDocument)
|
, mDocument(aDocument)
|
||||||
|
, mOnlyMatchHostPseudo(false)
|
||||||
, mScopedRoot(nullptr)
|
, mScopedRoot(nullptr)
|
||||||
, mIsHTMLDocument(aDocument->IsHTMLDocument())
|
, mIsHTMLDocument(aDocument->IsHTMLDocument())
|
||||||
, mCompatMode(aDocument->GetCompatibilityMode())
|
, mCompatMode(aDocument->GetCompatibilityMode())
|
||||||
|
|
@ -437,6 +470,7 @@ struct MOZ_STACK_CLASS ElementDependentRuleProcessorData :
|
||||||
: RuleProcessorData(aPresContext, aRuleWalker)
|
: RuleProcessorData(aPresContext, aRuleWalker)
|
||||||
, mElement(aElement)
|
, mElement(aElement)
|
||||||
, mTreeMatchContext(aTreeMatchContext)
|
, mTreeMatchContext(aTreeMatchContext)
|
||||||
|
, mElementIsFeatureless(false)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aElement, "null element leaked into SelectorMatches");
|
NS_ASSERTION(aElement, "null element leaked into SelectorMatches");
|
||||||
NS_ASSERTION(aElement->OwnerDoc(), "Document-less node here?");
|
NS_ASSERTION(aElement->OwnerDoc(), "Document-less node here?");
|
||||||
|
|
@ -446,6 +480,7 @@ struct MOZ_STACK_CLASS ElementDependentRuleProcessorData :
|
||||||
|
|
||||||
mozilla::dom::Element* const mElement; // weak ref, must not be null
|
mozilla::dom::Element* const mElement; // weak ref, must not be null
|
||||||
TreeMatchContext& mTreeMatchContext;
|
TreeMatchContext& mTreeMatchContext;
|
||||||
|
bool mElementIsFeatureless;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MOZ_STACK_CLASS ElementRuleProcessorData :
|
struct MOZ_STACK_CLASS ElementRuleProcessorData :
|
||||||
|
|
|
||||||
|
|
@ -1311,8 +1311,9 @@ nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||||
if (mBindingManager) {
|
if (mBindingManager) {
|
||||||
// We can supply additional document-level sheets that should be walked.
|
// We can supply additional document-level sheets that should be walked.
|
||||||
if (aWalkAllXBLStylesheets) {
|
if (aWalkAllXBLStylesheets) {
|
||||||
mBindingManager->WalkAllRules(aFunc, aData);
|
mBindingManager->WalkAllRules(aFunc, aData, false);
|
||||||
} else {
|
} else {
|
||||||
|
mBindingManager->WalkAllShadowRootHostRules(aFunc, aData);
|
||||||
mBindingManager->WalkRules(aFunc, aData, &cutOffInheritance);
|
mBindingManager->WalkRules(aFunc, aData, &cutOffInheritance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue