Skip to content

Commit

Permalink
Cleanup affected-by-has flag setting logic in CheckPseudoHas
Browse files Browse the repository at this point in the history
Cleanup logic of setting AncestorsOrAncestorSiblingsAffectedByHas and
SiblingsAffectedByHasFlags.

To simplify the logic, CheckPseudoHasArgumentTraversalIterator behavior
was changed to traverse until it reaches to the :has() scope element,
even for fixed adjacent distance.

With the changed logic, we can set SiblingsAffectedByHasFlags in
addition to AncestorsOrAncestorSiblingsAffectedByHas while traversing.

Bug: 669058
Change-Id: I2cc18127d49aef6f6d09ecd41f71018dad028fde
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3624879
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1001342}
  • Loading branch information
byung-woo authored and Chromium LUCI CQ committed May 10, 2022
1 parent 1b8d8b1 commit a8f389c
Show file tree
Hide file tree
Showing 4 changed files with 891 additions and 285 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ CheckPseudoHasArgumentContext::CheckPseudoHasArgumentContext(
traversal_scope_ = kFixedDepthDescendants;
else
traversal_scope_ = kSubtree;
siblings_affected_by_has_flags_ =
SiblingsAffectedByHasFlags::kNoSiblingsAffectedByHasFlags;
break;
case CSSSelector::kRelativeIndirectAdjacent:
case CSSSelector::kRelativeDirectAdjacent:
Expand All @@ -116,6 +118,8 @@ CheckPseudoHasArgumentContext::CheckPseudoHasArgumentContext(
traversal_scope_ = kOneNextSibling;
else
traversal_scope_ = kAllNextSiblings;
siblings_affected_by_has_flags_ =
SiblingsAffectedByHasFlags::kFlagForSiblingRelationship;
} else {
if (AdjacentDistanceFixed()) {
if (DepthFixed())
Expand All @@ -128,6 +132,8 @@ CheckPseudoHasArgumentContext::CheckPseudoHasArgumentContext(
else
traversal_scope_ = kAllNextSiblingSubtrees;
}
siblings_affected_by_has_flags_ =
SiblingsAffectedByHasFlags::kFlagForSiblingDescendantRelationship;
}
break;
default:
Expand All @@ -140,56 +146,66 @@ CheckPseudoHasArgumentTraversalIterator::
CheckPseudoHasArgumentTraversalIterator(
Element& has_scope_element,
CheckPseudoHasArgumentContext& context)
: has_scope_element_(&has_scope_element), context_(context) {
if (!context_.AdjacentDistanceFixed()) {
// Set the traversal_end_ as the next sibling of the :has scope element,
: has_scope_element_(&has_scope_element),
depth_limit_(context.DepthLimit()) {
if (!context.AdjacentDistanceFixed()) {
// Set the last_element_ as the next sibling of the :has scope element,
// and move to the last sibling of the :has scope element, and move again
// to the last descendant of the last sibling.
traversal_end_ = ElementTraversal::NextSibling(*has_scope_element_);
if (!traversal_end_) {
current_ = nullptr;
last_element_ = ElementTraversal::NextSibling(*has_scope_element_);
if (!last_element_) {
DCHECK_EQ(current_element_, nullptr);
return;
}
Element* last_sibling =
Traversal<Element>::LastChild(*has_scope_element_->parentNode());
current_ = LastWithin(last_sibling);
if (!current_)
current_ = last_sibling;
} else if (context_.AdjacentDistanceLimit() == 0) {
DCHECK_GT(context_.DepthLimit(), 0);
// Set the traversal_end_ as the first child of the :has scope element,
ElementTraversal::LastChild(*has_scope_element_->parentNode());
current_element_ = LastWithin(last_sibling);
if (!current_element_)
current_element_ = last_sibling;
} else if (context.AdjacentDistanceLimit() == 0) {
DCHECK_GT(context.DepthLimit(), 0);
// Set the last_element_ as the first child of the :has scope element,
// and move to the last descendant of the :has scope element without
// exceeding the depth limit.
traversal_end_ = ElementTraversal::FirstChild(*has_scope_element_);
if (!traversal_end_) {
current_ = nullptr;
last_element_ = ElementTraversal::FirstChild(*has_scope_element_);
if (!last_element_) {
DCHECK_EQ(current_element_, nullptr);
return;
}
current_ = LastWithin(has_scope_element_);
DCHECK(current_);
current_element_ = LastWithin(has_scope_element_);
DCHECK(current_element_);
} else {
// Set the traversal_end_ as the element at the adjacent distance of the
// :has scope element, and move to the last descendant of the element
// without exceeding the depth limit.
// Set last_element_ as the next sibling of the :has() scope element, set
// the sibling_at_fixed_distance_ as the element at the adjacent distance
// of the :has scope element, and move to the last descendant of the sibling
// at fixed distance without exceeding the depth limit.
int distance = 1;
for (traversal_end_ = ElementTraversal::NextSibling(*has_scope_element_);
distance < context_.AdjacentDistanceLimit() && traversal_end_;
distance++,
traversal_end_ = ElementTraversal::NextSibling(*traversal_end_)) {
Element* old_sibling = nullptr;
Element* sibling = ElementTraversal::NextSibling(*has_scope_element_);
for (; distance < context.AdjacentDistanceLimit() && sibling;
distance++, sibling = ElementTraversal::NextSibling(*sibling)) {
old_sibling = sibling;
}
if (!traversal_end_) {
current_ = nullptr;
return;
if (sibling) {
sibling_at_fixed_distance_ = sibling;
current_element_ = LastWithin(sibling_at_fixed_distance_);
if (!current_element_)
current_element_ = sibling_at_fixed_distance_;
} else {
current_element_ = old_sibling;
if (!current_element_)
return;
// set the depth_limit_ to 0 so that the iterator only traverse to the
// siblings of the :has() scope element.
depth_limit_ = 0;
}
if ((current_ = LastWithin(traversal_end_)))
return;
current_ = traversal_end_;
last_element_ = ElementTraversal::NextSibling(*has_scope_element_);
}
}

Element* CheckPseudoHasArgumentTraversalIterator::LastWithin(Element* element) {
// If the current depth is at the depth limit, return null.
if (depth_ == context_.DepthLimit())
if (current_depth_ == depth_limit_)
return nullptr;

// Return the last element of the pre-order traversal starting from the passed
Expand All @@ -198,30 +214,38 @@ Element* CheckPseudoHasArgumentTraversalIterator::LastWithin(Element* element) {
for (Element* descendant = ElementTraversal::LastChild(*element); descendant;
descendant = ElementTraversal::LastChild(*descendant)) {
last_descendant = descendant;
if (++depth_ == context_.DepthLimit())
if (++current_depth_ == depth_limit_)
break;
}
return last_descendant;
}

void CheckPseudoHasArgumentTraversalIterator::operator++() {
DCHECK(current_);
DCHECK_NE(current_, has_scope_element_);
if (current_ == traversal_end_) {
current_ = nullptr;
DCHECK(current_element_);
DCHECK_NE(current_element_, has_scope_element_);
if (current_element_ == last_element_) {
current_element_ = nullptr;
return;
}

// If current element is the sibling at fixed distance, set the depth_limit_
// to 0 so that the iterator only traverse to the siblings of the :has() scope
// element.
if (current_depth_ == 0 && sibling_at_fixed_distance_ == current_element_) {
sibling_at_fixed_distance_ = nullptr;
depth_limit_ = 0;
}

// Move to the previous element in DOM tree order within the depth limit.
if (Element* next = Traversal<Element>::PreviousSibling(*current_)) {
if (Element* next = ElementTraversal::PreviousSibling(*current_element_)) {
Element* last_descendant = LastWithin(next);
current_ = last_descendant ? last_descendant : next;
current_element_ = last_descendant ? last_descendant : next;
} else {
DCHECK_GT(depth_, 0);
depth_--;
current_ = current_->parentElement();
DCHECK_GT(current_depth_, 0);
current_depth_--;
current_element_ = current_element_->parentElement();
}
DCHECK(current_);
DCHECK(current_element_);
}

} // namespace blink
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class CORE_EXPORT CheckPseudoHasArgumentContext {
return traversal_scope_;
}

SiblingsAffectedByHasFlags GetSiblingsAffectedByHasFlags() const {
return siblings_affected_by_has_flags_;
}

const CSSSelector* HasArgument() const { return has_argument_; }

private:
Expand Down Expand Up @@ -220,6 +224,7 @@ class CORE_EXPORT CheckPseudoHasArgumentContext {
bool sibling_combinator_at_rightmost_{false};
bool sibling_combinator_between_child_or_descendant_combinator_{false};
CheckPseudoHasArgumentTraversalScope traversal_scope_;
SiblingsAffectedByHasFlags siblings_affected_by_has_flags_;
const CSSSelector* has_argument_;
};

Expand Down Expand Up @@ -261,31 +266,27 @@ class CORE_EXPORT CheckPseudoHasArgumentContext {
// adjacent sibling of the div element. To implement this, we need a
// way to limit the traversal depth and a way to check whether the
// iterator is currently at the fixed depth or not.
class CheckPseudoHasArgumentTraversalIterator {
class CORE_EXPORT CheckPseudoHasArgumentTraversalIterator {
STACK_ALLOCATED();

public:
CheckPseudoHasArgumentTraversalIterator(Element&,
CheckPseudoHasArgumentContext&);
void operator++();
Element* CurrentElement() const { return current_; }
bool AtEnd() const { return !current_; }
bool AtFixedDepth() const { return depth_ == context_.DepthLimit(); }
bool UnderDepthLimit() const { return depth_ <= context_.DepthLimit(); }
inline int Depth() const { return depth_; }
Element* CurrentElement() const { return current_element_; }
bool AtEnd() const { return !current_element_; }
inline int CurrentDepth() const { return current_depth_; }
inline Element* ScopeElement() const { return has_scope_element_; }
inline const CheckPseudoHasArgumentContext& Context() const {
return context_;
}

private:
inline Element* LastWithin(Element*);

Element* const has_scope_element_;
const CheckPseudoHasArgumentContext& context_;
int depth_{0};
Element* current_{nullptr};
Element* traversal_end_{nullptr};
int depth_limit_;
Element* last_element_{nullptr};
Element* sibling_at_fixed_distance_{nullptr};
Element* current_element_{nullptr};
int current_depth_{0};
};

} // namespace blink
Expand Down

0 comments on commit a8f389c

Please sign in to comment.