Commit 905006a8 authored by Emilio Cobos Álvarez's avatar Emilio Cobos Álvarez Committed by Chromium LUCI CQ

[css-shadow-parts] Don't match disallowed selectors for ::part() pseudo-elements.

Couldn't help myself when I noticed this bug with
::file-selector-button.

Fixed: 978105
Change-Id: I42da51ffa7117e0d4ecd55e2e12fbb7548571378
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2587042
Commit-Queue: Emilio Cobos Álvarez <emilio@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Auto-Submit: Emilio Cobos Álvarez <emilio@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836724}
parent a021ce94
......@@ -134,7 +134,7 @@ void ElementRuleCollector::CollectMatchingRulesForList(
const RuleDataListType* rules,
ShadowV0CascadeOrder cascade_order,
const MatchRequest& match_request,
PartNames* part_names) {
PartRequest* part_request) {
if (!rules)
return;
......@@ -144,7 +144,7 @@ void ElementRuleCollector::CollectMatchingRulesForList(
init.element_style = style_.get();
init.scrollbar = pseudo_style_request_.scrollbar;
init.scrollbar_part = pseudo_style_request_.scrollbar_part;
init.part_names = part_names;
init.part_names = part_request ? &part_request->part_names : nullptr;
SelectorChecker checker(init);
SelectorChecker::SelectorCheckingContext context(&context_.GetElement());
context.scope = match_request.scope;
......@@ -175,12 +175,22 @@ void ElementRuleCollector::CollectMatchingRulesForList(
if (!rule->ShouldConsiderForMatchingRules(include_empty_rules_))
continue;
const auto& selector = rule_data->Selector();
if (UNLIKELY(part_request && part_request->for_shadow_pseudo)) {
if (!selector.IsAllowedAfterPart()) {
DCHECK_EQ(selector.GetPseudoType(), CSSSelector::kPseudoPart);
rejected++;
continue;
}
DCHECK_EQ(selector.Relation(), CSSSelector::kShadowPseudo);
}
SelectorChecker::MatchResult result;
context.selector = &rule_data->Selector();
context.selector = &selector;
context.is_inside_visited_link =
rule_data->LinkMatchType() == CSSSelector::kMatchVisited;
DCHECK(!context.is_inside_visited_link ||
(inside_link_ == EInsideLink::kInsideVisitedLink));
inside_link_ == EInsideLink::kInsideVisitedLink);
if (!checker.Match(context, result)) {
rejected++;
continue;
......@@ -283,9 +293,10 @@ void ElementRuleCollector::CollectMatchingShadowHostRules(
void ElementRuleCollector::CollectMatchingPartPseudoRules(
const MatchRequest& match_request,
PartNames& part_names,
ShadowV0CascadeOrder cascade_order) {
bool for_shadow_pseudo) {
PartRequest request{part_names, for_shadow_pseudo};
CollectMatchingRulesForList(match_request.rule_set->PartPseudoRules(),
cascade_order, match_request, &part_names);
kIgnoreCascadeOrder, match_request, &request);
}
template <class CSSRuleCollection>
......
......@@ -136,10 +136,9 @@ class CORE_EXPORT ElementRuleCollector {
void CollectMatchingShadowHostRules(
const MatchRequest&,
ShadowV0CascadeOrder = kIgnoreCascadeOrder);
void CollectMatchingPartPseudoRules(
const MatchRequest&,
PartNames&,
ShadowV0CascadeOrder = kIgnoreCascadeOrder);
void CollectMatchingPartPseudoRules(const MatchRequest&,
PartNames&,
bool for_shadow_pseudo);
void SortAndTransferMatchedRules();
void ClearMatchedRules();
void AddElementStyleProperties(const CSSPropertyValueSet*,
......@@ -160,11 +159,18 @@ class CORE_EXPORT ElementRuleCollector {
void AddMatchedRulesToTracker(StyleRuleUsageTracker*) const;
private:
struct PartRequest {
PartNames& part_names;
// If this is true, we're matching for a pseudo-element of the part, such as
// ::placeholder.
bool for_shadow_pseudo = false;
};
template <typename RuleDataListType>
void CollectMatchingRulesForList(const RuleDataListType*,
ShadowV0CascadeOrder,
const MatchRequest&,
PartNames* = nullptr);
PartRequest* = nullptr);
bool Match(SelectorChecker&,
const SelectorChecker::SelectorCheckingContext&,
......
......@@ -317,14 +317,14 @@ void ScopedStyleResolver::CollectMatchingTreeBoundaryCrossingRules(
void ScopedStyleResolver::CollectMatchingPartPseudoRules(
ElementRuleCollector& collector,
PartNames& part_names,
ShadowV0CascadeOrder cascade_order) {
bool for_shadow_pseudo) {
wtf_size_t sheet_index = 0;
for (auto sheet : style_sheets_) {
DCHECK(sheet->ownerNode() || sheet->IsConstructed());
MatchRequest match_request(&sheet->Contents()->GetRuleSet(),
&scope_->RootNode(), sheet, sheet_index++);
collector.CollectMatchingPartPseudoRules(match_request, part_names,
cascade_order);
for_shadow_pseudo);
}
}
......
......@@ -78,10 +78,9 @@ class CORE_EXPORT ScopedStyleResolver final
void CollectMatchingTreeBoundaryCrossingRules(
ElementRuleCollector&,
ShadowV0CascadeOrder = kIgnoreCascadeOrder);
void CollectMatchingPartPseudoRules(
ElementRuleCollector&,
PartNames& part_names,
ShadowV0CascadeOrder = kIgnoreCascadeOrder);
void CollectMatchingPartPseudoRules(ElementRuleCollector&,
PartNames& part_names,
bool for_shadow_pseudo);
void MatchPageRules(PageRuleCollector&);
void CollectFeaturesTo(RuleFeatureSet&,
HeapHashSet<Member<const StyleSheetContents>>&
......
......@@ -400,7 +400,7 @@ const static TextTrack* GetTextTrackFromElement(const Element& element) {
}
static void MatchVTTRules(const Element& element,
ElementRuleCollector& collector) {
ElementRuleCollector& collector) {
const TextTrack* text_track = GetTextTrackFromElement(element);
if (!text_track)
return;
......@@ -458,12 +458,15 @@ void StyleResolver::MatchPseudoPartRulesForUAHost(
// We allow ::placeholder pseudo element after ::part(). See
// MatchSlottedRulesForUAHost for a more detailed explanation.
DCHECK(element.OwnerShadowHost());
MatchPseudoPartRules(*element.OwnerShadowHost(), collector);
MatchPseudoPartRules(*element.OwnerShadowHost(), collector,
/* for_shadow_pseudo */ true);
}
void StyleResolver::MatchPseudoPartRules(const Element& element,
ElementRuleCollector& collector) {
MatchPseudoPartRulesForUAHost(element, collector);
ElementRuleCollector& collector,
bool for_shadow_pseudo) {
if (!for_shadow_pseudo)
MatchPseudoPartRulesForUAHost(element, collector);
DOMTokenList* part = element.GetPart();
if (!part)
return;
......@@ -480,7 +483,8 @@ void StyleResolver::MatchPseudoPartRules(const Element& element,
TreeScope& tree_scope = host->GetTreeScope();
if (ScopedStyleResolver* resolver = tree_scope.GetScopedStyleResolver()) {
collector.ClearMatchedRules();
resolver->CollectMatchingPartPseudoRules(collector, current_names);
resolver->CollectMatchingPartPseudoRules(collector, current_names,
for_shadow_pseudo);
collector.SortAndTransferMatchedRules();
collector.FinishAddingAuthorRulesForTreeScope(resolver->GetTreeScope());
}
......
......@@ -187,7 +187,9 @@ class CORE_EXPORT StyleResolver final : public GarbageCollected<StyleResolver> {
void MatchUserRules(ElementRuleCollector&);
// This matches `::part` selectors. It looks in ancestor scopes as far as
// part mapping requires.
void MatchPseudoPartRules(const Element&, ElementRuleCollector&);
void MatchPseudoPartRules(const Element&,
ElementRuleCollector&,
bool for_shadow_pseudo = false);
void MatchPseudoPartRulesForUAHost(const Element&, ElementRuleCollector&);
void MatchScopedRulesV0(const Element&,
ElementRuleCollector&,
......
<!doctype html>
<title>CSS Test Reference</title>
<style>
input {
border: 1px solid green;
}
</style>
<input type=file part=input>
<input type=text part=input placeholder=Foobar>
<!doctype html>
<title>::part() doesn't incorrectly match pseudo-elements of the part</title>
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=978105">
<link rel="help" href="https://drafts.csswg.org/css-shadow-parts/">
<link rel="match" href="part-nested-pseudo-ref.html">
<style>
#host::part(input) {
border: 1px solid green;
}
</style>
<div id="host"></div>
<script>
document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = `
<input type=file part=input>
<input type=text part=input placeholder=Foobar>
`;
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment