1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
|
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "base/memory/stack_allocated.h"
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/substring_set_matcher/substring_set_matcher.h"
#include "base/types/pass_key.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/cascade_layer.h"
#include "third_party/blink/renderer/core/css/css_keyframes_rule.h"
#include "third_party/blink/renderer/core/css/css_position_try_rule.h"
#include "third_party/blink/renderer/core/css/media_query_evaluator.h"
#include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
#include "third_party/blink/renderer/core/css/robin_hood_map.h"
#include "third_party/blink/renderer/core/css/rule_feature_set.h"
#include "third_party/blink/renderer/core/css/style_rule.h"
#include "third_party/blink/renderer/core/css/style_rule_counter_style.h"
#include "third_party/blink/renderer/core/css/style_rule_font_feature_values.h"
#include "third_party/blink/renderer/core/css/style_rule_font_palette_values.h"
#include "third_party/blink/renderer/core/css/style_rule_view_transition.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_linked_stack.h"
#include "third_party/blink/renderer/platform/wtf/casting.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
namespace blink {
class RuleSet;
using AddRuleFlags = unsigned;
enum AddRuleFlag {
kRuleHasNoSpecialState = 0,
kRuleIsVisitedDependent = 1 << 0,
kRuleIsStartingStyle = 1 << 1,
};
// Some CSS properties do not apply to certain pseudo-elements, and need to be
// ignored when resolving styles. Be aware that these values are used in a
// bitfield. Make sure that it's large enough to hold new values.
// See MatchedProperties::Data::valid_property_filter.
enum class ValidPropertyFilter : unsigned {
// All properties are valid. This is the common case.
kNoFilter,
// Defined in a ::cue pseudo-element scope. Only properties listed
// in https://w3c.github.io/webvtt/#the-cue-pseudo-element are valid.
kCue,
// Defined in a ::first-letter pseudo-element scope. Only properties listed in
// https://drafts.csswg.org/css-pseudo-4/#first-letter-styling are valid.
kFirstLetter,
// Defined in a ::first-line pseudo-element scope. Only properties listed in
// https://drafts.csswg.org/css-pseudo-4/#first-line-styling are valid.
kFirstLine,
// Defined in a ::marker pseudo-element scope. Only properties listed in
// https://drafts.csswg.org/css-pseudo-4/#marker-pseudo are valid.
kMarker,
// Defined in a highlight pseudo-element scope like ::selection and
// ::target-text. Theoretically only properties listed in
// https://drafts.csswg.org/css-pseudo-4/#highlight-styling should be valid,
// but for highlight pseudos using originating inheritance instead of
// highlight inheritance we allow a different set of rules for
// compatibility reasons.
kHighlightLegacy,
// Defined in a highlight pseudo-element scope like ::selection and
// ::target-text. Only properties listed in
// https://drafts.csswg.org/css-pseudo-4/#highlight-styling are valid.
kHighlight,
// Defined in a @position-try rule. Only properties listed in
// https://drafts.csswg.org/css-anchor-position-1/#fallback-rule are valid.
kPositionTry,
// Defined in an @page rule.
// See https://drafts.csswg.org/css-page-3/#page-property-list
kPageContext,
};
class CSSSelector;
class MediaQueryEvaluator;
class StyleSheetContents;
// This is a wrapper around a StyleRule, pointing to one of the N complex
// selectors in the StyleRule. This allows us to treat each selector
// independently but still tie them back to the original StyleRule. If multiple
// selectors from a single rule match the same element we can see that as one
// match for the rule. It computes some information about the wrapped selector
// and makes it accessible cheaply.
class CORE_EXPORT RuleData {
DISALLOW_NEW();
public:
// NOTE: If you move the RuleData to a different RuleSet
// (and thus a different bloom_hash_backing from what you
// give to the constructor), you will need to call
// MovedToDifferentRuleSet() below. Otherwise,
// DescendantSelectorIdentifierHashes() will return a slice
// into a nonexistent backing (and GetPosition() will return
// a bogus value, which cannot be used for Seeker lookups).
RuleData(StyleRule*,
unsigned selector_index,
unsigned position,
const StyleScope*,
AddRuleFlags,
Vector<uint16_t>& bloom_hash_backing);
unsigned GetPosition() const { return position_; }
StyleRule* Rule() const { return rule_.Get(); }
const CSSSelector& Selector() const {
return rule_->SelectorAt(selector_index_);
}
CSSSelector& MutableSelector() {
return rule_->MutableSelectorAt(selector_index_);
}
unsigned SelectorIndex() const { return selector_index_; }
bool IsEntirelyCoveredByBucketing() const {
return is_entirely_covered_by_bucketing_;
}
void ComputeEntirelyCoveredByBucketing();
void ResetEntirelyCoveredByBucketing();
bool SelectorIsEasy() const { return is_easy_; }
bool IsStartingStyle() const { return is_starting_style_; }
unsigned Specificity() const { return specificity_; }
unsigned LinkMatchType() const { return link_match_type_; }
ValidPropertyFilter GetValidPropertyFilter() const {
return static_cast<ValidPropertyFilter>(valid_property_filter_);
}
// Member functions related to the descendant Bloom filter.
const base::span<const uint16_t> DescendantSelectorIdentifierHashes(
const Vector<uint16_t>& backing) const {
return UNSAFE_TODO({backing.data() + bloom_hash_pos_, bloom_hash_size_});
}
void ComputeBloomFilterHashes(const StyleScope* style_scope,
Vector<uint16_t>& backing);
void MovedToDifferentRuleSet(const Vector<uint16_t>& old_backing,
Vector<uint16_t>& new_backing,
unsigned new_position);
void Trace(Visitor*) const;
// This number is picked fairly arbitrary. If lowered, be aware that there
// might be sites and extensions using style rules with selector lists
// exceeding the number of simple selectors to fit in this bitfield.
// See https://crbug.com/312913 and https://crbug.com/704562
static constexpr size_t kSelectorIndexBits = 13;
// This number was picked fairly arbitrarily. We can probably lower it if we
// need to. Some simple testing showed <100,000 RuleData's on large sites.
static constexpr size_t kPositionBits = 18;
private:
Member<StyleRule> rule_;
unsigned selector_index_ : kSelectorIndexBits;
unsigned position_ : kPositionBits;
unsigned unused_bit_ : 1;
// 31 bits above (1 free bit).
unsigned specificity_ : 24;
unsigned link_match_type_ : 2;
unsigned valid_property_filter_ : 3;
unsigned is_entirely_covered_by_bucketing_ : 1;
unsigned is_easy_ : 1; // See EasySelectorChecker.
unsigned is_starting_style_ : 1; // Inside @starting-style {}.
// 32 bits above
// Reference into a slice of bloom_hash_backing_ in the parent RuleSet.
// We can probably steal a couple of bits here if needed, but if you do,
// remember to adjust the clamping in ComputeBloomFilterHashes() too.
unsigned bloom_hash_size_ : 8;
unsigned bloom_hash_pos_ : 24;
};
} // namespace blink
WTF_ALLOW_MOVE_AND_INIT_WITH_MEM_FUNCTIONS(blink::RuleData)
namespace blink {
struct SameSizeAsRuleData {
DISALLOW_NEW();
Member<void*> a;
unsigned b;
unsigned c;
unsigned d;
};
ASSERT_SIZE(RuleData, SameSizeAsRuleData);
// A memory-efficient and (fairly) cache-efficient mapping from bucket key
// (e.g. CSS class, tag name, attribute key, etc.) to a collection of rules
// (RuleData objects). It uses a vector as backing storage, and generally works
// in two phases:
//
// - During RuleSet setup (before compaction), we simply add rules to the
// back of the vector, ie., the elements will be in a random order.
// - Before rule matching, we need to _compact_ the rule map. This is done
// by grouping/sorting the vector by bucket, so that everything that belongs
// to the same vector lives together and can easily be picked out.
//
// The normal flow is that you first add all rules, call Compact(), then call
// Find() as many times as you need. (Compact() is a moderately expensive
// operation, which is why we don't want to be doing it too much.) However, in
// certain cases related to UA stylesheets, we may need to insert new rules
// on-the-fly (e.g., when seeing a <video> element for the first time, we
// insert additional rules related to it); if so, you need to call Uncompact()
// before adding them, then Compact() again.
class RuleMap {
DISALLOW_NEW();
private:
// A collection of rules that are in the same bucket. Before compaction,
// they are scattered around in the bucket vector; after compaction,
// each bucket will be contiguous.
struct Extent {
Extent() : bucket_number(0) {}
union {
// [0..num_buckets). Valid before compaction.
unsigned bucket_number;
// Into the backing vector. Valid after compaction.
unsigned start_index;
};
// How many rules are in this bucket. Will naturally not change
// by compaction.
wtf_size_t length = 0;
};
public:
// Returns false on failure (which should be very rare).
bool Add(const AtomicString& key, const RuleData& rule_data);
void AddFilteredRulesFromOtherSet(
const RuleMap& other,
const HeapHashSet<Member<StyleRule>>& only_include,
const RuleSet& old_rule_set,
RuleSet& new_rule_set);
base::span<const RuleData> Find(const AtomicString& key) const {
// Go through all the buckets and check for equality, brute force.
// Note that we don't check for IsNull() to get an early abort
// on empty buckets; the comparison of AtomicString is so cheap
// that it actually benchmarks negatively. This loop typically
// gets unrolled and inlined, resulting in a very tight lookup.
const RobinHoodMap<AtomicString, Extent>::Bucket* bucket =
buckets.Find(key);
if (bucket == nullptr) {
return {};
} else {
return UNSAFE_TODO(
{backing.begin() + bucket->value.start_index, bucket->value.length});
}
}
bool IsEmpty() const { return backing.empty(); }
bool IsCompacted() const { return compacted; }
void Compact();
void Uncompact();
void Trace(Visitor* visitor) const { visitor->Trace(backing); }
struct ConstIterator {
STACK_ALLOCATED();
public:
RobinHoodMap<AtomicString, Extent>::const_iterator sub_it;
const RuleMap* rule_map;
WTF::KeyValuePair<AtomicString, base::span<const RuleData>> operator*()
const {
return {sub_it->key, rule_map->GetRulesFromExtent(sub_it->value)};
}
bool operator==(const ConstIterator& other) const {
DCHECK_EQ(rule_map, other.rule_map);
return sub_it == other.sub_it;
}
bool operator!=(const ConstIterator& other) const {
DCHECK_EQ(rule_map, other.rule_map);
return sub_it != other.sub_it;
}
ConstIterator& operator++() {
++sub_it;
return *this;
}
};
ConstIterator begin() const { return {buckets.begin(), this}; }
ConstIterator end() const { return {buckets.end(), this}; }
private:
base::span<RuleData> GetRulesFromExtent(Extent extent) {
return UNSAFE_TODO({backing.begin() + extent.start_index, extent.length});
}
base::span<const RuleData> GetRulesFromExtent(Extent extent) const {
return UNSAFE_TODO({backing.begin() + extent.start_index, extent.length});
}
base::span<unsigned> GetBucketNumberFromExtent(Extent extent) {
return UNSAFE_TODO(
{bucket_number_.begin() + extent.start_index, extent.length});
}
base::span<const unsigned> GetBucketNumberFromExtent(Extent extent) const {
return UNSAFE_TODO(
{bucket_number_.begin() + extent.start_index, extent.length});
}
RobinHoodMap<AtomicString, Extent> buckets;
// Contains all the rules from all the buckets; after compaction,
// they will be contiguous in memory and you can do easily lookups
// on them through Find(); before, they are identified
// by having the group number in bucket_data_.
//
// The inline size is, perhaps surprisingly, to reduce GC pressure
// for _large_ vectors. Setting an inline size (other than zero)
// causes Vector, and by extension HeapVector, to grow more
// aggressively on push_back(), leading to fewer memory allocations
// that need freeing. We call ShrinkToFit() on compaction, so the
// excess increase (which would normally be the downside of this
// strategy) is not a big problem for us.
//
// Of course, we also save a few allocations for the rule sets
// that are tiny. Most RuleMaps are either ~1–2 entries or in
// the hundreds/thousands.
HeapVector<RuleData, 4> backing;
// Used by RuleMap before compaction, to hold what bucket the corresponding
// RuleData (by index) is to be sorted into (this member is 1:1 with backing).
// After compaction, the vector is emptied to save memory.
Vector<unsigned> bucket_number_;
wtf_size_t num_buckets = 0;
bool compacted = false;
};
// Holds RuleData objects. It partitions them into various indexed groups,
// e.g. it stores separately rules that match against id, class, tag, shadow
// host, etc. It indexes these by some key where possible, e.g. rules that match
// against tag name are indexed by that tag. Rules that don't fall into a
// specific group are appended to the "universal" rules. The grouping is done to
// optimize finding what rules apply to an element under consideration by
// ElementRuleCollector::CollectMatchingRules.
class CORE_EXPORT RuleSet final : public GarbageCollected<RuleSet> {
public:
RuleSet() = default;
RuleSet(const RuleSet&) = delete;
RuleSet& operator=(const RuleSet&) = delete;
void AddRulesFromSheet(const StyleSheetContents*,
const MediaQueryEvaluator&,
CascadeLayer* = nullptr,
const StyleScope* = nullptr);
// “within_mixin” means that we are currently adding this rule
// as part of @apply in a mixin, and all rules we add must be
// duplicated and reparented. This is also propagated through
// AddChildRules().
void AddStyleRule(StyleRule* style_rule,
StyleRule* parent_rule,
const MediaQueryEvaluator& medium,
AddRuleFlags add_rule_flags,
bool within_mixin,
const ContainerQuery* container_query = nullptr,
CascadeLayer* cascade_layer = nullptr,
const StyleScope* style_scope = nullptr);
// Adds RuleDatas (and only RuleDatas) from the other set, but only if they
// correspond to rules in “only_include”. This is used when creating diff
// rulesets for invalidation, and the resulting RuleSets are not usable
// for anything else. In particular, cascade layers are not copied
// and RuleData offsets are not adjusted (so CascadePriority would be wrong
// if merging RuleDatas from different RuleSets). This means that the only
// thing you can really do with this RuleSet afterwards is
// ElementRuleCollector's CheckIfAnyRuleMatches(); the regular
// Collect*Rules() functions are bound to give you trouble.
void AddFilteredRulesFromOtherSet(
const RuleSet& other,
const HeapHashSet<Member<StyleRule>>& only_include);
void AddFilteredRulesFromOtherBucket(
const RuleSet& other,
const HeapVector<RuleData>& src,
const HeapHashSet<Member<StyleRule>>& only_include,
HeapVector<RuleData>* dst);
const RuleFeatureSet& Features() const { return features_; }
base::span<const RuleData> IdRules(const AtomicString& key) const {
return id_rules_.Find(key);
}
base::span<const RuleData> ClassRules(const AtomicString& key) const {
return class_rules_.Find(key);
}
bool HasAnyAttrRules() const { return !attr_rules_.IsEmpty(); }
base::span<const RuleData> AttrRules(const AtomicString& key) const {
return attr_rules_.Find(key);
}
bool CanIgnoreEntireList(base::span<const RuleData> list,
const AtomicString& key,
const AtomicString& value) const;
base::span<const RuleData> TagRules(const AtomicString& key) const {
return tag_rules_.Find(key);
}
base::span<const RuleData> UAShadowPseudoElementRules(
const AtomicString& key) const {
return ua_shadow_pseudo_element_rules_.Find(key);
}
base::span<const RuleData> LinkPseudoClassRules() const {
return link_pseudo_class_rules_;
}
base::span<const RuleData> CuePseudoRules() const {
return cue_pseudo_rules_;
}
base::span<const RuleData> FocusPseudoClassRules() const {
return focus_pseudo_class_rules_;
}
base::span<const RuleData> FocusVisiblePseudoClassRules() const {
return focus_visible_pseudo_class_rules_;
}
base::span<const RuleData> RootElementRules() const {
return root_element_rules_;
}
base::span<const RuleData> UniversalRules() const { return universal_rules_; }
base::span<const RuleData> ShadowHostRules() const {
return shadow_host_rules_;
}
base::span<const RuleData> PartPseudoRules() const {
return part_pseudo_rules_;
}
base::span<const RuleData> SelectorFragmentAnchorRules() const {
return selector_fragment_anchor_rules_;
}
const HeapVector<Member<StyleRulePage>>& PageRules() const {
return page_rules_;
}
const HeapVector<Member<StyleRuleFontFace>>& FontFaceRules() const {
return font_face_rules_;
}
const HeapVector<Member<StyleRuleKeyframes>>& KeyframesRules() const {
return keyframes_rules_;
}
const HeapVector<Member<StyleRuleProperty>>& PropertyRules() const {
return property_rules_;
}
const HeapVector<Member<StyleRuleCounterStyle>>& CounterStyleRules() const {
return counter_style_rules_;
}
const HeapVector<Member<StyleRuleFontPaletteValues>>& FontPaletteValuesRules()
const {
return font_palette_values_rules_;
}
const HeapVector<Member<StyleRuleFontFeatureValues>>& FontFeatureValuesRules()
const {
return font_feature_values_rules_;
}
const HeapVector<Member<StyleRuleViewTransition>>& ViewTransitionRules()
const {
return view_transition_rules_;
}
const HeapVector<Member<StyleRulePositionTry>>& PositionTryRules() const {
return position_try_rules_;
}
const HeapVector<Member<StyleRuleFunction>>& FunctionRules() const {
return function_rules_;
}
base::span<const RuleData> SlottedPseudoElementRules() const {
return slotted_pseudo_element_rules_;
}
bool HasCascadeLayers() const { return implicit_outer_layer_ != nullptr; }
const CascadeLayer& CascadeLayers() const {
DCHECK(implicit_outer_layer_);
return *implicit_outer_layer_;
}
unsigned RuleCount() const { return rule_count_; }
void CompactRulesIfNeeded() {
if (need_compaction_) {
CompactRules();
}
}
void AssertCompacted() const { DCHECK(!need_compaction_); }
bool HasSlottedRules() const {
return !slotted_pseudo_element_rules_.empty();
}
bool HasPartPseudoRules() const { return !part_pseudo_rules_.empty(); }
bool HasBucketForStyleAttribute() const { return has_bucket_for_style_attr_; }
bool MustCheckUniversalBucketForShadowHost() const {
return must_check_universal_bucket_for_shadow_host_;
}
bool HasUAShadowPseudoElementRules() const {
return !ua_shadow_pseudo_element_rules_.IsEmpty();
}
// If a single @scope rule covers all rules in this RuleSet,
// returns the corresponding StyleScope rule, or returns nullptr otherwise.
//
// This is useful for rejecting entire RuleSets early when implicit @scopes
// aren't in scope.
//
// See ElementRuleCollector::CanRejectScope.
const StyleScope* SingleScope() const {
if (scope_intervals_.size() == 1u) {
const Interval<StyleScope>& interval = scope_intervals_.front();
if (interval.start_position == 0) {
return interval.value.Get();
}
}
return nullptr;
}
bool DidMediaQueryResultsChange(const MediaQueryEvaluator& evaluator) const;
// We use a vector of Interval<T> to represent that rules with positions
// between start_position (inclusive) and the next Interval<T>'s
// start_position (exclusive) share some property:
//
// - If T = CascadeLayer, belong to the given layer.
// - If T = ContainerQuery, are predicated on the given container query.
// - If T = StyleScope, are declared in the given @style scope.
//
// We do this instead of putting the data directly onto the RuleData,
// because most rules don't need these fields and websites can have a large
// number of RuleData objects (30k+). Since neighboring rules tend to have the
// same values for these (often nullptr), we save memory and cache space at
// the cost of a some extra seeking through these lists when matching rules.
template <class T>
class Interval {
DISALLOW_NEW();
public:
Interval(const T* passed_value, unsigned passed_position)
: value(passed_value), start_position(passed_position) {}
const Member<const T> value;
const unsigned start_position = 0;
void Trace(Visitor*) const;
};
const HeapVector<Interval<CascadeLayer>>& LayerIntervals() const {
return layer_intervals_;
}
const HeapVector<Interval<ContainerQuery>>& ContainerQueryIntervals() const {
return container_query_intervals_;
}
const HeapVector<Interval<StyleScope>>& ScopeIntervals() const {
return scope_intervals_;
}
const Vector<uint16_t>& BloomHashBacking() const {
return bloom_hash_backing_;
}
#if DCHECK_IS_ON()
void Show() const;
const HeapVector<RuleData>& AllRulesForTest() const { return all_rules_; }
#endif // DCHECK_IS_ON()
void Trace(Visitor*) const;
private:
FRIEND_TEST_ALL_PREFIXES(RuleSetTest, RuleCountNotIncreasedByInvalidRuleData);
FRIEND_TEST_ALL_PREFIXES(RuleSetTest, RuleDataPositionLimit);
friend class RuleSetCascadeLayerTest;
friend class RuleMap; // For scope_intervals_ and
// NewlyAddedFromDifferentRuleSet().
using SubstringMatcherMap =
HashMap<AtomicString, std::unique_ptr<base::SubstringSetMatcher>>;
void AddToRuleSet(const AtomicString& key, RuleMap&, const RuleData&);
void AddToRuleSet(HeapVector<RuleData>&, const RuleData&);
void AddPageRule(StyleRulePage*);
void AddFontFaceRule(StyleRuleFontFace*);
void AddKeyframesRule(StyleRuleKeyframes*);
void AddPropertyRule(StyleRuleProperty*);
void AddCounterStyleRule(StyleRuleCounterStyle*);
void AddFontPaletteValuesRule(StyleRuleFontPaletteValues*);
void AddFontFeatureValuesRule(StyleRuleFontFeatureValues*);
void AddPositionTryRule(StyleRulePositionTry*);
void AddFunctionRule(StyleRuleFunction*);
void AddViewTransitionRule(StyleRuleViewTransition*);
bool MatchMediaForAddRules(const MediaQueryEvaluator& evaluator,
const MediaQuerySet* media_queries);
void AddChildRules(StyleRule* parent_rule,
base::span<const Member<StyleRuleBase>>,
const MediaQueryEvaluator& medium,
AddRuleFlags,
const ContainerQuery*,
CascadeLayer*,
const StyleScope*,
bool within_mixin);
// Determines whether or not CSSSelector::is_covered_by_bucketing_ should
// be computed during calls to FindBestRuleSetAndAdd.
enum class BucketCoverage {
kIgnore,
kCompute,
};
template <BucketCoverage bucket_coverage>
void FindBestRuleSetAndAdd(CSSSelector&, const RuleData&, const StyleScope*);
void AddRule(StyleRule*,
unsigned selector_index,
AddRuleFlags,
const ContainerQuery*,
const CascadeLayer*,
const StyleScope*);
// Must be called when a RuleData has been added to this RuleSet
// through some form that does not go through AddRule();
// used during creation of diff rulesets (AddFilteredRulesFromOtherSet()).
// In particular, it will adjust the position of new_rule_data,
// add it to the necessary intervals for diff rulesets, and adjust
// rule_count_.
//
// Used only by RuleSet itself, and RuleMap (through a friend declaration).
void NewlyAddedFromDifferentRuleSet(const RuleData& old_rule_data,
const StyleScope* style_scope,
const RuleSet& old_rule_set,
RuleData& new_rule_data);
void SortKeyframesRulesIfNeeded();
void CompactRules();
static void CreateSubstringMatchers(
RuleMap& attr_map,
const HeapVector<Interval<StyleScope>>& scope_intervals,
SubstringMatcherMap& substring_matcher_map);
#if DCHECK_IS_ON()
void AssertRuleListsSorted() const;
#endif
CascadeLayer* EnsureImplicitOuterLayer() {
if (!implicit_outer_layer_) {
implicit_outer_layer_ = MakeGarbageCollected<CascadeLayer>();
}
return implicit_outer_layer_.Get();
}
CascadeLayer* GetOrAddSubLayer(CascadeLayer*,
const StyleRuleBase::LayerName&);
void AddRuleToLayerIntervals(const CascadeLayer*, unsigned position);
// May return nullptr for the implicit outer layer.
const CascadeLayer* GetLayerForTest(const RuleData&) const;
RuleMap id_rules_;
RuleMap class_rules_;
RuleMap attr_rules_;
// A structure for quickly rejecting an entire attribute rule set
// (from attr_rules_). If we have many rules in the same bucket,
// we build up a case-insensitive substring-matching structure of all
// the values we can match on (all attribute selectors are either substring,
// or something stricter than substring). We can then use that structure
// to see in linear time (of the length of the attribute value in the DOM)
// whether we can have any matches at all.
//
// If we find any matches, we need to recheck each rule, because the rule in
// question may actually be case-sensitive, or we might want e.g. a prefix
// match instead of a substring match. (We could solve prefix/suffix by
// means of inserting special start-of-string and end-of-string tokens,
// but we keep it simple for now.) Also, the way we use the
// SubstringSetMatcher, we don't actually get back which rules matched.
//
// This element does not exist, if there are few enough rules that we don't
// deem this step worth it, or if the build of the tree failed. (In
// particular, if there is only a single rule in this bucket, it's
// pointless to run the entire Aho-Corasick algorithm instead of just
// doing a simple match.) Check GetMinimumRulesetSizeForSubstringMatcher()
// before looking up for a cheaper test.
SubstringMatcherMap attr_substring_matchers_;
RuleMap tag_rules_;
RuleMap ua_shadow_pseudo_element_rules_;
HeapVector<RuleData> link_pseudo_class_rules_;
HeapVector<RuleData> cue_pseudo_rules_;
HeapVector<RuleData> focus_pseudo_class_rules_;
HeapVector<RuleData> focus_visible_pseudo_class_rules_;
HeapVector<RuleData> universal_rules_;
HeapVector<RuleData> shadow_host_rules_;
HeapVector<RuleData> part_pseudo_rules_;
HeapVector<RuleData> slotted_pseudo_element_rules_;
HeapVector<RuleData> selector_fragment_anchor_rules_;
HeapVector<RuleData> root_element_rules_;
RuleFeatureSet features_;
HeapVector<Member<StyleRulePage>> page_rules_;
HeapVector<Member<StyleRuleFontFace>> font_face_rules_;
HeapVector<Member<StyleRuleFontPaletteValues>> font_palette_values_rules_;
HeapVector<Member<StyleRuleFontFeatureValues>> font_feature_values_rules_;
HeapVector<Member<StyleRuleViewTransition>> view_transition_rules_;
HeapVector<Member<StyleRuleKeyframes>> keyframes_rules_;
HeapVector<Member<StyleRuleProperty>> property_rules_;
HeapVector<Member<StyleRuleCounterStyle>> counter_style_rules_;
HeapVector<Member<StyleRulePositionTry>> position_try_rules_;
HeapVector<MediaQuerySetResult> media_query_set_results_;
HeapVector<Member<StyleRuleFunction>> function_rules_;
HeapHashMap<AtomicString, Member<StyleRuleMixin>> mixins_;
// Whether there is a ruleset bucket for rules with a selector on
// the style attribute (which is rare, but allowed). If so, the caller
// may need to take extra steps to synchronize the style attribute on
// an element before looking for appropriate buckets.
bool has_bucket_for_style_attr_ = false;
// Whether we need to check the universal bucket for rules when calculating
// style for the shadow host. There are two reasons why this may need to
// be checked:
//
// 1. Since the :scope pseudo-class can match a shadow host when that host
// is the scoping root,
// ElementRuleCollector::CollectMatchingShadowHostRules also needs to
// collect rules from the universal bucket, but this is only required when
// :scope is actually present.
//
// 2. Combination rules such as :is(:host, .foo) will be bucketed into the
// universal bucket and not into the bucket for :host. If this happens,
// we will need to check the universal bucket, too.
//
// Nothing else in the universal bucket can match the host from inside
// the shadow tree.
bool must_check_universal_bucket_for_shadow_host_ = false;
unsigned rule_count_ = 0;
bool need_compaction_ = false;
// nullptr if the stylesheet doesn't explicitly declare any layer.
Member<CascadeLayer> implicit_outer_layer_;
// Empty vector if the stylesheet doesn't explicitly declare any layer.
HeapVector<Interval<CascadeLayer>> layer_intervals_;
// Empty vector if the stylesheet doesn't use any container queries.
HeapVector<Interval<ContainerQuery>> container_query_intervals_;
// Empty vector if the stylesheet doesn't use any @scopes.
HeapVector<Interval<StyleScope>> scope_intervals_;
// Backing store for the Bloom filter hashes for each RuleData.
// It is stored here so that we can have a variable number of them
// (without the overhead of a Vector in each RuleData).
Vector<uint16_t> bloom_hash_backing_;
#if DCHECK_IS_ON()
HeapVector<RuleData> all_rules_;
// If true, we don't DCHECK that these are unsorted, since they
// came from merged+filtered rulesets, which only happens when
// making diff rulesets for invalidation. Those do not care
// about the ordering, since they do not use the CascadeLayerSeeker.
bool allow_unsorted_ = false;
#endif // DCHECK_IS_ON()
};
} // namespace blink
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
blink::RuleSet::Interval<blink::CascadeLayer>)
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
blink::RuleSet::Interval<blink::ContainerQuery>)
WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(
blink::RuleSet::Interval<blink::StyleScope>)
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RULE_SET_H_
|