Skip to content

Commit

Permalink
DocumentRules: Implement matching for selector_matches
Browse files Browse the repository at this point in the history
This CL integrates document speculation rules with the style engine
for style invalidation. The general flow is as follows:

1) DocumentSpeculationRules notifies StyleEngine when a new
   SpeculationRuleSet is added/removed, and provides a list of
   selectors from all the selector_matches predicates
2) StyleEngine updates the global RuleSet with these selectors, these
   are now taken into account when building InvalidationSets
3) When style is computed for a link in UpdateStyle, it is matched
   against all of the selectors from speculation rules, and a list of
   matching selectors is stored in ComputedStyle
4) If the list of matching selectors for a list changes,
   DocumentSpeculationRules is notified (and the link is invalidated
   and UpdateSpeculationCandidates is queued)
5) When actually matching links against a CSSSelectorPredicate, we
   check to see if the ComputedStyle for the link has the selector
   in the matching list (it matches iff it is in the list)

This CL has two major issues that will be addressed in follow-ups:

1) UpdateSpeculationCandidates can currently be called when style
   isn't clean (so ComputedStyle is not up-to-date); this is a problem
   only when there exists at least one selector_matches predicate
2) display:none links will not have a ComputedStyle set after
   UpdateStyle (which currently means matching against them with a
   selector_matches predicate always returns false)

Bug: 1371522
Change-Id: I5629ea24a2047acf452ca9b68b252162a929fca0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4156272
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Adithya Srinivasan <adithyas@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1097380}
  • Loading branch information
a4sriniv authored and Chromium LUCI CQ committed Jan 26, 2023
1 parent 2301714 commit f003a1d
Show file tree
Hide file tree
Showing 19 changed files with 625 additions and 23 deletions.
24 changes: 24 additions & 0 deletions third_party/blink/renderer/core/css/css_global_rule_set.cc
Expand Up @@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/css/rule_set.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"

namespace blink {

Expand All @@ -33,6 +34,23 @@ void CSSGlobalRuleSet::InitWatchedSelectorsRuleSet(Document& document) {
}
}

void CSSGlobalRuleSet::UpdateDocumentRulesSelectorsRuleSet(Document& document) {
MarkDirty();
document_rules_selectors_rule_set_ = nullptr;
const HeapVector<Member<StyleRule>>& document_rules_selectors =
DocumentSpeculationRules::From(document).selectors();
if (document_rules_selectors.empty()) {
return;
}
document_rules_selectors_rule_set_ = MakeGarbageCollected<RuleSet>();
MediaQueryEvaluator* medium =
MakeGarbageCollected<MediaQueryEvaluator>(document.GetFrame());
for (StyleRule* selector : document_rules_selectors) {
document_rules_selectors_rule_set_->AddStyleRule(selector, *medium,
kRuleHasNoSpecialState);
}
}

void CSSGlobalRuleSet::Update(Document& document) {
if (!is_dirty_) {
return;
Expand All @@ -52,18 +70,24 @@ void CSSGlobalRuleSet::Update(Document& document) {
features_.Merge(watched_selectors_rule_set_->Features());
}

if (document_rules_selectors_rule_set_) {
features_.Merge(document_rules_selectors_rule_set_->Features());
}

document.GetStyleEngine().CollectFeaturesTo(features_);
}

void CSSGlobalRuleSet::Dispose() {
features_.Clear();
watched_selectors_rule_set_ = nullptr;
document_rules_selectors_rule_set_ = nullptr;
has_fullscreen_ua_style_ = false;
is_dirty_ = true;
}

void CSSGlobalRuleSet::Trace(Visitor* visitor) const {
visitor->Trace(watched_selectors_rule_set_);
visitor->Trace(document_rules_selectors_rule_set_);
}

} // namespace blink
8 changes: 8 additions & 0 deletions third_party/blink/renderer/core/css/css_global_rule_set.h
Expand Up @@ -29,6 +29,7 @@ class CSSGlobalRuleSet final : public GarbageCollected<CSSGlobalRuleSet> {

void Dispose();
void InitWatchedSelectorsRuleSet(Document&);
void UpdateDocumentRulesSelectorsRuleSet(Document&);
void MarkDirty() { is_dirty_ = true; }
bool IsDirty() const { return is_dirty_; }
void Update(Document&);
Expand All @@ -37,6 +38,9 @@ class CSSGlobalRuleSet final : public GarbageCollected<CSSGlobalRuleSet> {
RuleSet* WatchedSelectorsRuleSet() const {
return watched_selectors_rule_set_;
}
RuleSet* DocumentRulesSelectorsRuleSet() const {
return document_rules_selectors_rule_set_;
}
bool HasFullscreenUAStyle() const { return has_fullscreen_ua_style_; }

void Trace(Visitor*) const;
Expand All @@ -49,6 +53,10 @@ class CSSGlobalRuleSet final : public GarbageCollected<CSSGlobalRuleSet> {
// Rules injected from extensions.
Member<RuleSet> watched_selectors_rule_set_;

// Rules extracted from CSS selector document rule predicates in speculation
// rules.
Member<RuleSet> document_rules_selectors_rule_set_;

bool has_fullscreen_ua_style_ = false;
bool is_dirty_ = true;
};
Expand Down
9 changes: 5 additions & 4 deletions third_party/blink/renderer/core/css/element_rule_collector.cc
Expand Up @@ -188,15 +188,16 @@ class CascadeLayerSeeker {
if (vtt_originating_element) {
return nullptr;
}
// Assume there are no UA cascade layers, so we only check user layers.
if (!style_sheet) {
return nullptr;
}
if (scope) {
DCHECK(scope->ContainingTreeScope().GetScopedStyleResolver());
return scope->ContainingTreeScope()
.GetScopedStyleResolver()
->GetCascadeLayerMap();
}
// Assume there are no UA cascade layers, so we only check user layers.
if (!style_sheet) {
return nullptr;
}
Document* document = style_sheet->OwnerDocument();
if (!document) {
return nullptr;
Expand Down
50 changes: 37 additions & 13 deletions third_party/blink/renderer/core/css/resolver/style_resolver.cc
Expand Up @@ -1459,6 +1459,12 @@ void StyleResolver::ApplyBaseStyleNoCache(
match_result.PseudoElementStyles());

ApplyCallbackSelectors(state);
if (element->IsLink() && (element->HasTagName(html_names::kATag) ||
element->HasTagName(html_names::kAreaTag))) {
// TODO(crbug.com/1371522): Revisit scoping root used after
// https://github.com/WICG/nav-speculation/issues/240 is resolved.
ApplyDocumentRulesSelectors(state, &GetDocument());
}

// Cache our original display.
state.StyleBuilder().SetOriginalDisplay(state.StyleBuilder().Display());
Expand Down Expand Up @@ -2344,33 +2350,51 @@ void StyleResolver::CascadeAndApplyMatchedProperties(StyleResolverState& state,
}

void StyleResolver::ApplyCallbackSelectors(StyleResolverState& state) {
RuleSet* watched_selectors_rule_set =
GetDocument().GetStyleEngine().WatchedSelectorsRuleSet();
if (!watched_selectors_rule_set) {
StyleRuleList* rules = CollectMatchingRulesFromRuleSet(
state, GetDocument().GetStyleEngine().WatchedSelectorsRuleSet(),
/*scope=*/nullptr);
if (!rules) {
return;
}
for (auto rule : *rules) {
state.StyleBuilder().AddCallbackSelector(rule->SelectorsText());
}
}

void StyleResolver::ApplyDocumentRulesSelectors(StyleResolverState& state,
ContainerNode* scope) {
StyleRuleList* rules = CollectMatchingRulesFromRuleSet(
state, GetDocument().GetStyleEngine().DocumentRulesSelectorsRuleSet(),
scope);
if (!rules) {
return;
}
for (auto rule : *rules) {
state.StyleBuilder().AddDocumentRulesSelector(rule);
}
}

StyleRuleList* StyleResolver::CollectMatchingRulesFromRuleSet(
StyleResolverState& state,
RuleSet* rule_set,
ContainerNode* scope) {
if (!rule_set) {
return nullptr;
}

MatchResult match_result;
ElementRuleCollector collector(state.ElementContext(), StyleRecalcContext(),
selector_filter_, match_result,
state.StyleBuilder().InsideLink());
collector.SetMode(SelectorChecker::kCollectingStyleRules);

MatchRequest match_request(watched_selectors_rule_set);
MatchRequest match_request(rule_set, scope);
collector.CollectMatchingRules(match_request);
collector.SortAndTransferMatchedRules();

if (tracker_) {
AddMatchedRulesToTracker(collector);
}

StyleRuleList* rules = collector.MatchedStyleRuleList();
if (!rules) {
return;
}
for (auto rule : *rules) {
state.StyleBuilder().AddCallbackSelector(rule->SelectorsText());
}
return collector.MatchedStyleRuleList();
}

// Font properties are also handled by FontStyleResolver outside the main
Expand Down
4 changes: 4 additions & 0 deletions third_party/blink/renderer/core/css/resolver/style_resolver.h
Expand Up @@ -311,6 +311,10 @@ class CORE_EXPORT StyleResolver final : public GarbageCollected<StyleResolver> {
bool ApplyAnimatedStyle(StyleResolverState&, StyleCascade&);

void ApplyCallbackSelectors(StyleResolverState&);
void ApplyDocumentRulesSelectors(StyleResolverState&, ContainerNode* scope);
StyleRuleList* CollectMatchingRulesFromRuleSet(StyleResolverState&,
RuleSet*,
ContainerNode* scope);

Document& GetDocument() const { return *document_; }

Expand Down
22 changes: 22 additions & 0 deletions third_party/blink/renderer/core/css/style_engine.cc
Expand Up @@ -426,6 +426,28 @@ void StyleEngine::WatchedSelectorsChanged() {
style_change_reason::kDeclarativeContent));
}

void StyleEngine::DocumentRulesSelectorsChanged() {
DCHECK(global_rule_set_);
Member<RuleSet> old_rule_set =
global_rule_set_->DocumentRulesSelectorsRuleSet();
global_rule_set_->UpdateDocumentRulesSelectorsRuleSet(GetDocument());
Member<RuleSet> new_rule_set =
global_rule_set_->DocumentRulesSelectorsRuleSet();
DCHECK_NE(old_rule_set, new_rule_set);

const unsigned changed_rule_flags = 0;
HeapHashSet<Member<RuleSet>> changed_rule_sets;
if (old_rule_set) {
changed_rule_sets.insert(old_rule_set);
}
if (new_rule_set) {
changed_rule_sets.insert(new_rule_set);
}

InvalidateForRuleSetChanges(GetDocument(), changed_rule_sets,
changed_rule_flags, kInvalidateAllScopes);
}

bool StyleEngine::ShouldUpdateDocumentStyleSheetCollection() const {
return document_scope_dirty_;
}
Expand Down
5 changes: 5 additions & 0 deletions third_party/blink/renderer/core/css/style_engine.h
Expand Up @@ -227,6 +227,7 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
void AdoptedStyleSheetRemoved(TreeScope& tree_scope, CSSStyleSheet* sheet);

void WatchedSelectorsChanged();
void DocumentRulesSelectorsChanged();
void InitialStyleChanged();
void ColorSchemeChanged();
void SetOwnerColorScheme(mojom::blink::ColorScheme);
Expand All @@ -247,6 +248,10 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
DCHECK(global_rule_set_);
return global_rule_set_->WatchedSelectorsRuleSet();
}
RuleSet* DocumentRulesSelectorsRuleSet() {
DCHECK(global_rule_set_);
return global_rule_set_->DocumentRulesSelectorsRuleSet();
}

// Helper class for making sure RuleSets that are ensured when collecting
// sheets for a TreeScope are not shared between two equal sheets which
Expand Down
38 changes: 38 additions & 0 deletions third_party/blink/renderer/core/dom/element.cc
Expand Up @@ -142,6 +142,8 @@
#include "third_party/blink/renderer/core/html/forms/html_options_collection.h"
#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
#include "third_party/blink/renderer/core/html/forms/html_select_menu_element.h"
#include "third_party/blink/renderer/core/html/html_anchor_element.h"
#include "third_party/blink/renderer/core/html/html_area_element.h"
#include "third_party/blink/renderer/core/html/html_body_element.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
#include "third_party/blink/renderer/core/html/html_document.h"
Expand Down Expand Up @@ -183,6 +185,7 @@
#include "third_party/blink/renderer/core/scroll/scrollable_area.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
#include "third_party/blink/renderer/core/speculation_rules/document_speculation_rules.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/style/toggle_root_list.h"
#include "third_party/blink/renderer/core/svg/svg_a_element.h"
Expand All @@ -199,6 +202,7 @@
#include "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h"
#include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_context_data.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
Expand Down Expand Up @@ -3768,6 +3772,8 @@ StyleRecalcChange Element::RecalcOwnStyle(
}
child_change = ApplyComputedStyleDiff(child_change, diff);
UpdateCallbackSelectors(old_style.get(), new_style.get());
NotifyIfMatchedDocumentRulesSelectorsChanged(old_style.get(),
new_style.get());
}

if (auto* context = GetDisplayLockContext()) {
Expand Down Expand Up @@ -4034,6 +4040,38 @@ void Element::UpdateCallbackSelectors(const ComputedStyle* old_style,
}
}

void Element::NotifyIfMatchedDocumentRulesSelectorsChanged(
const ComputedStyle* old_style,
const ComputedStyle* new_style) {
if (!IsLink() ||
!(HasTagName(html_names::kATag) || HasTagName(html_names::kAreaTag))) {
return;
}
auto get_selectors_from_computed_style = [](const ComputedStyle* style) {
HeapHashSet<WeakMember<StyleRule>> empty_set;
if (!style || !style->DocumentRulesSelectors()) {
return empty_set;
}
return *style->DocumentRulesSelectors();
};

const HeapHashSet<WeakMember<StyleRule>>& old_document_rules_selectors =
get_selectors_from_computed_style(old_style);
const HeapHashSet<WeakMember<StyleRule>>& new_document_rules_selectors =
get_selectors_from_computed_style(new_style);
if (old_document_rules_selectors.empty() &&
new_document_rules_selectors.empty()) {
return;
}
if (old_document_rules_selectors != new_document_rules_selectors) {
HTMLAnchorElement* link = nullptr;
link = HasTagName(html_names::kATag) ? To<HTMLAnchorElement>(this)
: To<HTMLAreaElement>(this);
DocumentSpeculationRules::From(GetDocument())
.LinkMatchedSelectorsUpdated(link);
}
}

ShadowRoot& Element::CreateAndAttachShadowRoot(ShadowRootType type) {
#if DCHECK_IS_ON()
NestingLevelIncrementer slot_assignment_recalc_forbidden_scope(
Expand Down
3 changes: 3 additions & 0 deletions third_party/blink/renderer/core/dom/element.h
Expand Up @@ -1501,6 +1501,9 @@ class CORE_EXPORT Element : public ContainerNode, public Animatable {

inline void UpdateCallbackSelectors(const ComputedStyle* old_style,
const ComputedStyle* new_style);
inline void NotifyIfMatchedDocumentRulesSelectorsChanged(
const ComputedStyle* old_style,
const ComputedStyle* new_style);

// Clone is private so that non-virtual CloneElementWithChildren and
// CloneElementWithoutChildren are used instead.
Expand Down

0 comments on commit f003a1d

Please sign in to comment.