summaryrefslogtreecommitdiff
path: root/layout/style/nsCSSRuleProcessor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/nsCSSRuleProcessor.cpp')
-rw-r--r--layout/style/nsCSSRuleProcessor.cpp124
1 files changed, 108 insertions, 16 deletions
diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp
index aed26a1c01..04b4e3608e 100644
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -48,6 +48,7 @@
#include "nsCSSRules.h"
#include "nsStyleSet.h"
#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ShadowRoot.h"
#include "nsNthIndexCache.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/EventStates.h"
@@ -1321,9 +1322,17 @@ struct NodeMatchContext {
// mForStyling is false, we have to assume we don't know.)
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)
, mIsRelevantLink(aIsRelevantLink)
+ , mIsFeatureless(aIsFeatureless)
{
}
};
@@ -1610,6 +1619,11 @@ StateSelectorMatches(Element* aElement,
return true;
}
+static bool AnySelectorInArgListMatches(Element* aElement,
+ nsPseudoClassList* aList,
+ NodeMatchContext& aNodeMatchContext,
+ TreeMatchContext& aTreeMatchContext);
+
static bool
StateSelectorMatches(Element* aElement,
nsCSSSelector* aSelector,
@@ -1631,6 +1645,17 @@ StateSelectorMatches(Element* aElement,
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:
// * when non-null, it indicates that we're processing a negation,
// which is done only when SelectorMatches calls itself recursively
@@ -1651,6 +1676,11 @@ static bool SelectorMatches(Element* aElement,
"is false since we don't know how to set it correctly in "
"Has(Attribute|State)DependentStyle");
+ if (aNodeMatchContext.mIsFeatureless &&
+ !CanMatchFeaturelessElement(aSelector)) {
+ return false;
+ }
+
// namespace/tag match
// optimization : bail out early if we can
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
@@ -1849,18 +1879,9 @@ static bool SelectorMatches(Element* aElement,
case CSSPseudoClassType::any:
{
- nsCSSSelectorList *l;
- for (l = pseudoClass->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;
- }
- }
- if (!l) {
+ if (!AnySelectorInArgListMatches(aElement, pseudoClass,
+ aNodeMatchContext,
+ aTreeMatchContext)) {
return false;
}
}
@@ -2242,6 +2263,26 @@ static bool SelectorMatches(Element* aElement,
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
#ifdef DEBUG
@@ -2331,7 +2372,9 @@ SelectorMatchesTree(Element* aPrevElement,
MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
nsCSSSelector* selector = aSelector;
Element* prevElement = aPrevElement;
+ bool crossedShadowRootBoundary = false;
while (selector) { // check compound selectors
+ bool contentIsFeatureless = false;
NS_ASSERTION(!selector->mNext ||
selector->mNext->mOperator != char16_t(0),
"compound selector without combinator");
@@ -2362,6 +2405,20 @@ SelectorMatchesTree(Element* aPrevElement,
// to test against is the parent
else {
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
// element parents.
if (content && content->IsElement()) {
@@ -2413,7 +2470,8 @@ SelectorMatchesTree(Element* aPrevElement,
}
const bool isRelevantLink = (aFlags & eLookForRelevantLink) &&
nsCSSRuleProcessor::IsLink(element);
- NodeMatchContext nodeContext(EventStates(), isRelevantLink);
+
+ NodeMatchContext nodeContext(EventStates(), isRelevantLink, contentIsFeatureless);
if (isRelevantLink) {
// If we find an ancestor of the matched node that is a link
// during the matching process, then it's the relevant link (see
@@ -2483,6 +2541,20 @@ void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
// We won't match; nothing else to do here
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,
data->mScope)) {
// The selector is for a rule in a scoped style sheet, and the subject
@@ -2542,7 +2614,8 @@ nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
if (cascade) {
NodeMatchContext nodeContext(EventStates(),
- nsCSSRuleProcessor::IsLink(aData->mElement));
+ nsCSSRuleProcessor::IsLink(aData->mElement),
+ aData->mElementIsFeatureless);
cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
}
}
@@ -2824,6 +2897,24 @@ AttributeEnumFunc(nsCSSSelector* aSelector,
nsRestyleHint possibleChange =
RestyleHintForSelectorWithAttributeChange(aData->change,
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
// all the bits of possibleChange, don't bother calling SelectorMatches, since
@@ -3295,7 +3386,7 @@ AddSelector(RuleCascadeData* aCascade,
}
}
- // Recur through any :-moz-any selectors
+ // Recur through any :-moz-any or :host-context selectors
for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
pseudoClass; pseudoClass = pseudoClass->mNext) {
if (pseudoClass->mType == CSSPseudoClassType::any) {
@@ -3943,6 +4034,7 @@ TreeMatchContext::InitAncestors(Element *aElement)
for (uint32_t i = ancestors.Length(); i-- != 0; ) {
mAncestorFilter.PushAncestor(ancestors[i]);
PushStyleScope(ancestors[i]);
+ PushShadowHost(ancestors[i]);
}
}
}