blob: 343856cca272977768353892ebf1f9665797f138 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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/resolver/cascade_expansion.h"
#include "third_party/blink/renderer/core/css/resolver/match_result.h"
#include "third_party/blink/renderer/core/css/rule_set.h"
namespace blink {
namespace {
CascadeFilter AddValidPropertiesFilter(
CascadeFilter filter,
const MatchedProperties& matched_properties) {
switch (static_cast<ValidPropertyFilter>(
matched_properties.types_.valid_property_filter)) {
case ValidPropertyFilter::kNoFilter:
return filter;
case ValidPropertyFilter::kCue:
return filter.Add(CSSProperty::kValidForCue, false);
case ValidPropertyFilter::kFirstLetter:
return filter.Add(CSSProperty::kValidForFirstLetter, false);
case ValidPropertyFilter::kMarker:
return filter.Add(CSSProperty::kValidForMarker, false);
case ValidPropertyFilter::kHighlight:
return filter.Add(CSSProperty::kValidForHighlight, false);
}
}
CascadeFilter AddLinkFilter(CascadeFilter filter,
const MatchedProperties& matched_properties) {
switch (matched_properties.types_.link_match_type) {
case CSSSelector::kMatchVisited:
return filter.Add(CSSProperty::kVisited, false);
case CSSSelector::kMatchLink:
return filter.Add(CSSProperty::kVisited, true);
case CSSSelector::kMatchAll:
return filter;
default:
return filter.Add(CSSProperty::kProperty, true);
}
}
CascadeFilter AmendFilter(CascadeFilter filter,
const MatchedProperties& matched_properties) {
return AddLinkFilter(AddValidPropertiesFilter(filter, matched_properties),
matched_properties);
}
} // anonymous namespace
CascadeExpansion::CascadeExpansion(const MatchedProperties& matched_properties,
const Document& document,
CascadeFilter filter,
size_t matched_properties_index)
: document_(document),
matched_properties_(matched_properties),
size_(matched_properties.properties->PropertyCount()),
filter_(AmendFilter(filter, matched_properties)),
matched_properties_index_(matched_properties_index) {
// We can't handle a MatchResult with more than 0xFFFF MatchedProperties,
// or a MatchedProperties object with more than 0xFFFF declarations. If this
// happens, we skip right to the end, and emit nothing.
if (size_ > kMaxDeclarationIndex + 1 ||
matched_properties_index_ > kMaxMatchedPropertiesIndex) {
index_ = size_;
} else {
Next();
}
}
CascadeExpansion::CascadeExpansion(const CascadeExpansion& o)
: document_(o.document_),
state_(o.state_),
matched_properties_(o.matched_properties_),
priority_(o.priority_),
index_(o.index_),
size_(o.size_),
filter_(o.filter_),
matched_properties_index_(o.matched_properties_index_),
id_(o.id_),
property_(id_ == CSSPropertyID::kVariable ? &custom_ : o.property_),
custom_(o.custom_) {}
void CascadeExpansion::Next() {
do {
switch (state_) {
case State::kInit:
AdvanceNormal();
break;
case State::kNormal:
if (ShouldEmitVisited() && AdvanceVisited())
break;
AdvanceNormal();
break;
case State::kVisited:
AdvanceNormal();
break;
case State::kAll:
AdvanceAll();
break;
}
} while (!AtEnd() && filter_.Rejects(*property_));
}
bool CascadeExpansion::IsAffectedByAll(CSSPropertyID id) {
const CSSProperty& property = CSSProperty::Get(id);
return !property.IsShorthand() && property.IsAffectedByAll();
}
bool CascadeExpansion::ShouldEmitVisited() const {
// This check is slightly redundant, as the emitted property would anyway
// be skipped by the do-while in Next(). However, it's probably good to avoid
// entering State::kVisited at all, if we can avoid it.
return !filter_.Rejects(CSSProperty::kVisited, true);
}
void CascadeExpansion::AdvanceNormal() {
state_ = State::kNormal;
++index_;
if (AtEnd())
return;
auto reference = PropertyAt(index_);
const auto& metadata = reference.PropertyMetadata();
id_ = metadata.PropertyID();
priority_ = CascadePriority(
matched_properties_.types_.origin, metadata.important_,
matched_properties_.types_.tree_order,
EncodeMatchResultPosition(matched_properties_index_, index_));
switch (id_) {
case CSSPropertyID::kVariable:
custom_ = CustomProperty(reference.Name().ToAtomicString(), document_);
property_ = &custom_;
break;
case CSSPropertyID::kAll:
state_ = State::kAll;
id_ = kFirstCSSProperty;
property_ = &CSSProperty::Get(id_);
// If this DCHECK is triggered, it means firstCSSProperty is not affected
// by 'all', and we need a function for figuring out the first property
// that _is_ affected by 'all'.
DCHECK(IsAffectedByAll(id_));
break;
default:
property_ = &CSSProperty::Get(id_);
break;
}
DCHECK(property_);
}
bool CascadeExpansion::AdvanceVisited() {
DCHECK(ShouldEmitVisited());
DCHECK(property_);
const CSSProperty* visited = property_->GetVisitedProperty();
if (!visited)
return false;
property_ = visited;
id_ = visited->PropertyID();
state_ = State::kVisited;
return true;
}
void CascadeExpansion::AdvanceAll() {
state_ = State::kAll;
int i = static_cast<int>(id_) + 1;
int end = kIntLastCSSProperty + 1;
for (; i < end; ++i) {
id_ = ConvertToCSSPropertyID(i);
if (IsAffectedByAll(id_))
break;
}
if (i >= end)
AdvanceNormal();
else
property_ = &CSSProperty::Get(id_);
}
CSSPropertyValueSet::PropertyReference CascadeExpansion::PropertyAt(
size_t index) const {
DCHECK(!AtEnd());
return matched_properties_.properties->PropertyAt(index_);
}
uint16_t CascadeExpansion::TreeOrder() const {
return matched_properties_.types_.tree_order;
}
} // namespace blink