blob: 11f5ad21bc0abb8273d486c6beb1c1ae6ae17176 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/css/check_pseudo_has_fast_reject_filter.h"
#include "third_party/blink/renderer/core/css/css_selector.h"
namespace blink {
namespace {
// Salt to separate otherwise identical string hashes so a class-selector like
// .article won't match <article> elements.
enum { kTagNameSalt = 13, kIdSalt = 17, kClassSalt = 19, kAttributeSalt = 23 };
inline bool IsExcludedAttribute(const AtomicString& name) {
return name == html_names::kClassAttr.LocalName() ||
name == html_names::kIdAttr.LocalName() ||
name == html_names::kStyleAttr.LocalName();
}
inline unsigned GetTagHash(const AtomicString& tag_name) {
return tag_name.Hash() * kTagNameSalt;
}
inline unsigned GetClassHash(const AtomicString& class_name) {
return class_name.Hash() * kClassSalt;
}
inline unsigned GetIdHash(const AtomicString& id) {
return id.Hash() * kIdSalt;
}
inline unsigned GetAttributeHash(const AtomicString& attribute_name) {
return attribute_name.Hash() * kAttributeSalt;
}
} // namespace
void CheckPseudoHasFastRejectFilter::AddElementIdentifierHashes(
const Element& element) {
DCHECK(filter_.get());
filter_->Add(GetTagHash(element.LocalNameForSelectorMatching()));
if (element.HasID()) {
filter_->Add(GetIdHash(element.IdForStyleResolution()));
}
if (element.HasClass()) {
const SpaceSplitString& class_names = element.ClassNames();
wtf_size_t count = class_names.size();
for (wtf_size_t i = 0; i < count; ++i) {
filter_->Add(GetClassHash(class_names[i]));
}
}
AttributeCollection attributes = element.AttributesWithoutUpdate();
for (const auto& attribute_item : attributes) {
auto attribute_name = attribute_item.LocalName();
if (IsExcludedAttribute(attribute_name)) {
continue;
}
auto lower = attribute_name.IsLowerASCII() ? attribute_name
: attribute_name.LowerASCII();
filter_->Add(GetAttributeHash(lower));
}
}
bool CheckPseudoHasFastRejectFilter::FastReject(
const Vector<unsigned>& pseudo_has_argument_hashes) const {
DCHECK(filter_.get());
if (pseudo_has_argument_hashes.empty()) {
return false;
}
for (unsigned hash : pseudo_has_argument_hashes) {
if (!filter_->MayContain(hash)) {
return true;
}
}
return false;
}
// static
void CheckPseudoHasFastRejectFilter::CollectPseudoHasArgumentHashes(
Vector<unsigned>& pseudo_has_argument_hashes,
const CSSSelector* simple_selector) {
DCHECK(simple_selector);
switch (simple_selector->Match()) {
case CSSSelector::kId:
if (simple_selector->Value().empty()) {
break;
}
pseudo_has_argument_hashes.push_back(GetIdHash(simple_selector->Value()));
break;
case CSSSelector::kClass:
if (simple_selector->Value().empty()) {
break;
}
pseudo_has_argument_hashes.push_back(
GetClassHash(simple_selector->Value()));
break;
case CSSSelector::kTag:
if (simple_selector->TagQName().LocalName() !=
CSSSelector::UniversalSelectorAtom()) {
pseudo_has_argument_hashes.push_back(
GetTagHash(simple_selector->TagQName().LocalName()));
}
break;
case CSSSelector::kAttributeExact:
case CSSSelector::kAttributeSet:
case CSSSelector::kAttributeList:
case CSSSelector::kAttributeContain:
case CSSSelector::kAttributeBegin:
case CSSSelector::kAttributeEnd:
case CSSSelector::kAttributeHyphen: {
auto attribute_name = simple_selector->Attribute().LocalName();
if (IsExcludedAttribute(attribute_name)) {
break;
}
auto lower_name = attribute_name.IsLowerASCII()
? attribute_name
: attribute_name.LowerASCII();
pseudo_has_argument_hashes.push_back(GetAttributeHash(lower_name));
} break;
default:
break;
}
}
void CheckPseudoHasFastRejectFilter::AllocateBloomFilter() {
if (filter_) {
return;
}
filter_ = std::make_unique<FastRejectFilter>();
}
} // namespace blink