Commit c5406164 authored by Anders Hartvoll Ruud's avatar Anders Hartvoll Ruud Committed by Commit Bot

[:is/:where] Improve :host-context and whole subtree invalidation

 - The insertion point crossing flag needs to be reset in a similar
   way to the tree crossing flag. Otherwise flags may "leak" from
   one nested complex selector to another unrelated nested complex
   selector.
 - In ExtractInvalidationSetFeaturesFromSelectorList, don't return
   as soon as we encounter kRequiresSubtreeInvalidation, as this
   causes selectors following a :host-context to be skipped, for
   example we would ignore '.b' in ':is(:host-context(.a), .b)'.
 - We should also not propagate the kRequiresSubtreeInvalidation
   value from nested complex selectors, as it causes whole subtree
   invalid for _everything_ in the top-level rightmost compound,
   even the parts unrelated to kRequiresSubtreeInvalidation. For
   example, for ':is(:host-context(.a), .b)', .b would be marked as
   whole subtree invalid. Instead we rely on this being handled locally
   inside each call to UpdateInvalidationSetsForComplex.

Bug: 568705
Change-Id: If54b7c006a4c1d886513a0bfb3e075fd4f71be63
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2438417
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: default avatarRune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812879}
parent 1bafedbf
......@@ -717,8 +717,7 @@ void RuleFeatureSet::UpdateRuleSetInvalidation(
type_rule_invalidation_set_->AddTagName(tag_name);
}
RuleFeatureSet::FeatureInvalidationType
RuleFeatureSet::ExtractInvalidationSetFeaturesFromSelectorList(
void RuleFeatureSet::ExtractInvalidationSetFeaturesFromSelectorList(
const CSSSelector& simple_selector,
InvalidationSetFeatures& features,
PositionType position) {
......@@ -726,7 +725,7 @@ RuleFeatureSet::ExtractInvalidationSetFeaturesFromSelectorList(
const CSSSelectorList* selector_list = simple_selector.SelectorList();
if (!selector_list)
return kNormalInvalidation;
return;
DCHECK(SupportsInvalidationWithSelectorList(simple_selector.GetPseudoType()));
const CSSSelector* sub_selector = selector_list->First();
......@@ -741,7 +740,7 @@ RuleFeatureSet::ExtractInvalidationSetFeaturesFromSelectorList(
*sub_selector, complex_features, position,
simple_selector.GetPseudoType()) == kRequiresSubtreeInvalidation) {
features.invalidation_flags.SetWholeSubtreeInvalid(true);
return kRequiresSubtreeInvalidation;
continue;
}
all_sub_selectors_have_features_for_ruleset_invalidation &=
complex_features.has_features_for_rule_set_invalidation;
......@@ -760,7 +759,6 @@ RuleFeatureSet::ExtractInvalidationSetFeaturesFromSelectorList(
features.NarrowToFeatures(any_features);
features.has_features_for_rule_set_invalidation |=
all_sub_selectors_have_features_for_ruleset_invalidation;
return kNormalInvalidation;
}
const CSSSelector* RuleFeatureSet::ExtractInvalidationSetFeaturesFromCompound(
......@@ -806,12 +804,8 @@ const CSSSelector* RuleFeatureSet::ExtractInvalidationSetFeaturesFromCompound(
invalidation_set->SetInvalidatesSelf();
}
if (ExtractInvalidationSetFeaturesFromSelectorList(*simple_selector,
features, position) ==
kRequiresSubtreeInvalidation) {
DCHECK(features.invalidation_flags.WholeSubtreeInvalid());
return nullptr;
}
ExtractInvalidationSetFeaturesFromSelectorList(*simple_selector, features,
position);
if (features.invalidation_flags.InvalidatesParts())
metadata_.invalidates_parts = true;
......@@ -886,9 +880,13 @@ void RuleFeatureSet::AddFeaturesToInvalidationSetsForSelectorList(
AutoRestoreMaxDirectAdjacentSelectors restore_max(sibling_features);
AutoRestoreTreeBoundaryCrossingFlag restore_tree_boundary(
descendant_features);
AutoRestoreInsertionPointCrossingFlag restore_insertion_point(
descendant_features);
if (simple_selector.IsHostPseudoClass())
descendant_features.invalidation_flags.SetTreeBoundaryCrossing(true);
if (simple_selector.IsV0InsertionPointCrossing())
descendant_features.invalidation_flags.SetInsertionPointCrossing(true);
descendant_features.has_features_for_rule_set_invalidation = false;
......@@ -947,8 +945,6 @@ void RuleFeatureSet::AddFeaturesToInvalidationSetsForSimpleSelector(
return;
}
if (simple_selector.IsV0InsertionPointCrossing())
descendant_features.invalidation_flags.SetInsertionPointCrossing(true);
if (simple_selector.GetPseudoType() == CSSSelector::kPseudoPart)
descendant_features.invalidation_flags.SetInvalidatesParts(true);
......
......@@ -343,6 +343,26 @@ class CORE_EXPORT RuleFeatureSet {
bool original_value_;
};
// For :is(.a, :host-context(.b), .c) .d, the invalidation set for .c should
// not be marked as insertion point crossing.
class AutoRestoreInsertionPointCrossingFlag {
STACK_ALLOCATED();
public:
explicit AutoRestoreInsertionPointCrossingFlag(
InvalidationSetFeatures& features)
: features_(features),
original_value_(
features.invalidation_flags.InsertionPointCrossing()) {}
~AutoRestoreInsertionPointCrossingFlag() {
features_.invalidation_flags.SetInsertionPointCrossing(original_value_);
}
private:
InvalidationSetFeatures& features_;
bool original_value_;
};
static void ExtractInvalidationSetFeature(const CSSSelector&,
InvalidationSetFeatures&);
......@@ -381,10 +401,9 @@ class CORE_EXPORT RuleFeatureSet {
InvalidationSetFeatures&,
PositionType,
CSSSelector::PseudoType = CSSSelector::kPseudoUnknown);
FeatureInvalidationType ExtractInvalidationSetFeaturesFromSelectorList(
const CSSSelector&,
InvalidationSetFeatures&,
PositionType);
void ExtractInvalidationSetFeaturesFromSelectorList(const CSSSelector&,
InvalidationSetFeatures&,
PositionType);
void UpdateFeaturesFromCombinator(
const CSSSelector&,
const CSSSelector* last_compound_selector_in_adjacent_chain,
......
......@@ -1533,6 +1533,36 @@ RefTestData ref_equal_test_data[] = {
{":is(.a, .b) :is(.c, .d)::part(foo)",
".a .c::part(foo), .a .d ::part(foo),"
".b .c::part(foo), .b .d ::part(foo)"},
{":is(.a, .b)::first-letter", ".a::first-letter, .b::first-letter"},
{":is(.a, .b .c)::first-line", ".a::first-line, .b .c::first-line"},
// TODO(andruud): Here we would normally expect a ref:
// '.a::first-line, .b + .c::first-line', however the latter selector
// currently marks the sibling invalidation set for .b as whole subtree
// invalid, whereas the :is() version does not. This could be improved.
{":is(.a, .b + .c)::first-line", ".a::first-line, .b + .c, .b + .c *"},
{":is(.a, .b ~ .c > .d)::first-line",
".a::first-line, .b ~ .c > .d::first-line"},
{":is(.a, :host-context(.b), .c)", ".a, :host-context(.b), .c"},
{":is(.a, :host-context(.b), .c) .d", ".a .d, :host-context(.b) .d, .c .d"},
{":is(.a, :host-context(.b), .c) + .d",
".a + .d, :host-context(.b) + .d, .c + .d"},
{":host-context(.a) :is(.b, .c)",
":host-context(.a) .b, :host-context(.a) .c"},
{":host-context(:is(.a))", ":host-context(.a)"},
{":host-context(:is(.a, .b))", ":host-context(.a), :host-context(.b)"},
{":is(.a, .b + .c).d", ".a.d, .b + .c.d"},
{".a :is(.b .c .d).e", ".a .d.e, .b .c .d.e"},
{":is(*)", "*"},
{".a :is(*)", ".a *"},
{":is(*) .a", "* .a"},
{".a + :is(*)", ".a + *"},
{":is(*) + .a", "* + .a"},
{".a + :is(.b, *)", ".a + .b, .a + *"},
{":is(.a, *) + .b", ".a + .b, * + .b"},
{".a :is(.b, *)", ".a .b, .a *"},
{":is(.a, *) .b", ".a .b, * .b"},
{":is(.a + .b, .c) *", ".a + .b *, .c *"},
{":is(.a + *, .c) *", ".a + * *, .c *"},
// clang-format on
};
......
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