Skip to content

Commit

Permalink
Create helpers for easier iteration of elements in a CSS toggle(-grou…
Browse files Browse the repository at this point in the history
…p) scope.

This refactors existing code that iterates through the elements in the
scope of a CSS toggle group into code that is usable for both toggle
scopes and toggle group scopes.  The iteration of toggle scopes (as
opposed to toggle group scopes) is unused as of this commit, but it will
be useful for implementing accessibility heuristics, which require a
good bit of enumeration of the elements in a CSS toggle scope.

Support for toggles is controlled by the CSSToggles flag (currently off)
in RuntimeEnabledFeatures.

Bug: 1250716
Change-Id: Id882631c769a72a9e7ad7c05ab47e8693e947c06
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4184799
Commit-Queue: David Baron <dbaron@chromium.org>
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1100597}
  • Loading branch information
dbaron authored and Chromium LUCI CQ committed Feb 2, 2023
1 parent f3d25a5 commit ef2c8d9
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 48 deletions.
1 change: 1 addition & 0 deletions third_party/blink/renderer/core/dom/build.gni
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ blink_core_sources_dom = [
"css_toggle_event.h",
"css_toggle_map.cc",
"css_toggle_map.h",
"css_toggle_traversal.h",
"create_element_flags.h",
"dataset_dom_string_map.cc",
"dataset_dom_string_map.h",
Expand Down
55 changes: 7 additions & 48 deletions third_party/blink/renderer/core/dom/css_toggle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/css_toggle_event.h"
#include "third_party/blink/renderer/core/dom/css_toggle_map.h"
#include "third_party/blink/renderer/core/dom/css_toggle_traversal.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
Expand Down Expand Up @@ -576,6 +577,8 @@ std::pair<Element*, ToggleScope> FindToggleGroupElement(

if (const ComputedStyle* style = element->GetComputedStyle()) {
if (const ToggleGroupList* toggle_groups = style->ToggleGroup()) {
// TODO(https://github.com/tabatkins/css-toggle/issues/25):
// Consider multiple occurrences of the same name.
for (const auto& group : toggle_groups->Groups()) {
if (group.Name() == name &&
(allow_narrow_scope || group.Scope() == ToggleScope::kWide)) {
Expand Down Expand Up @@ -613,56 +616,13 @@ void CSSToggle::MakeRestOfToggleGroupZero() {
const AtomicString& name = Name();
auto [toggle_group_element, toggle_scope] =
FindToggleGroupElement(toggle_element, name);
Element* stay_within;
switch (toggle_scope) {
case ToggleScope::kNarrow:
stay_within = toggle_group_element;
break;
case ToggleScope::kWide:
stay_within = toggle_group_element->parentElement();
break;
}

Element* e = toggle_group_element;
do {
for (Element* e :
CSSToggleGroupScopeRange(toggle_group_element, name, toggle_scope)) {
if (e == toggle_element) {
e = ElementTraversal::Next(*e, stay_within);
continue;
}
if (e != toggle_group_element) {
// Skip descendants in a different group.
//
// TODO(dbaron): What if style is null? See
// https://github.com/tabatkins/css-toggle/issues/24 .
if (const ComputedStyle* style = e->GetComputedStyle()) {
if (const ToggleGroupList* toggle_groups = style->ToggleGroup()) {
bool found_group = false; // to continue the outer loop
for (const ToggleGroup& group : toggle_groups->Groups()) {
if (group.Name() == name) {
// TODO(https://github.com/tabatkins/css-toggle/issues/25):
// Consider multiple occurrences of the same name.
switch (group.Scope()) {
case ToggleScope::kWide:
if (e != stay_within && e->parentElement()) {
e = ElementTraversal::NextSkippingChildren(
*e->parentElement(), stay_within);
} else {
e = nullptr;
}
break;
case ToggleScope::kNarrow:
e = ElementTraversal::NextSkippingChildren(*e, stay_within);
break;
}
found_group = true;
break;
}
}
if (found_group)
continue;
}
}
}

if (CSSToggleMap* toggle_map = e->GetToggleMap()) {
ToggleMap& toggles = toggle_map->Toggles();
auto iter = toggles.find(name);
Expand All @@ -673,8 +633,7 @@ void CSSToggle::MakeRestOfToggleGroupZero() {
}
}
}
e = ElementTraversal::Next(*e, stay_within);
} while (e);
}
}

void CSSToggle::FireToggleChangeEvent() {
Expand Down
154 changes: 154 additions & 0 deletions third_party/blink/renderer/core/dom/css_toggle_traversal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_TRAVERSAL_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_TRAVERSAL_H_

#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/renderer/core/dom/css_toggle.h"
#include "third_party/blink/renderer/core/dom/css_toggle_map.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/toggle_group.h"
#include "third_party/blink/renderer/core/style/toggle_group_list.h"

namespace blink {

struct CSSToggleGroupScopeIteratorTraits {
static absl::optional<ToggleScope> Lookup(Element* element,
const AtomicString& name) {
// TODO(dbaron): What if style is null? See
// https://github.com/tabatkins/css-toggle/issues/24 .
if (const ComputedStyle* style = element->GetComputedStyle()) {
if (const ToggleGroupList* toggle_groups = style->ToggleGroup()) {
for (const ToggleGroup& group : toggle_groups->Groups()) {
// TODO(https://github.com/tabatkins/css-toggle/issues/25):
// Consider multiple occurrences of the same name.
if (group.Name() == name) {
return group.Scope();
}
}
}
}
return absl::nullopt;
}
};

struct CSSToggleScopeIteratorTraits {
static absl::optional<ToggleScope> Lookup(Element* element,
const AtomicString& name) {
if (CSSToggleMap* toggle_map = element->GetToggleMap()) {
const auto& toggles = toggle_map->Toggles();
auto iter = toggles.find(name);
if (iter != toggles.end()) {
return iter->value->Scope();
}
}
return absl::nullopt;
}
};

// An iterator over the elements that are in the scope of a named toggle
// or toggle group. (This excludes elements that are not in the scope
// because they are in the scope of a closer toggle or toggle group with
// the same name.)
template <class IteratorTraits>
class CSSTogglesScopeIteratorBase {
STACK_ALLOCATED();

private:
Element* current_;
Element* stay_within_;
AtomicString name_;

public:
CSSTogglesScopeIteratorBase(Element* current,
Element* stay_within,
const AtomicString& name)
: current_(current), stay_within_(stay_within), name_(name) {}

void operator++() {
Element* e = ElementTraversal::Next(*current_, stay_within_);
while (e) {
// Skip descendants in a different group.
absl::optional<ToggleScope> element_scope =
IteratorTraits::Lookup(e, name_);
if (!element_scope) {
break;
}
switch (*element_scope) {
case ToggleScope::kWide:
if (e->parentElement()) {
e = ElementTraversal::NextSkippingChildren(*e->parentElement(),
stay_within_);
} else {
e = nullptr;
}
break;
case ToggleScope::kNarrow:
e = ElementTraversal::NextSkippingChildren(*e, stay_within_);
break;
}
}
current_ = e;
}

Element* operator*() const { return current_; }

bool operator==(const CSSTogglesScopeIteratorBase& other) const {
DCHECK(stay_within_ == other.stay_within_);
DCHECK(name_ == other.name_);
return current_ == other.current_;
}

bool operator!=(const CSSTogglesScopeIteratorBase& other) const {
return !(*this == other);
}
};

// The elements that are in the scope of a named toggle or toggle group.
// (This excludes elements that are not in the scope because they are in
// the scope of a closer toggle or toggle group with the same name.)
template <class IteratorTraits>
class CSSTogglesScopeRangeBase {
STACK_ALLOCATED();

private:
Element* establishing_element_;
Element* stay_within_;
AtomicString name_;

public:
typedef CSSTogglesScopeIteratorBase<IteratorTraits> Iterator;

CSSTogglesScopeRangeBase(Element* establishing_element,
const AtomicString& name,
ToggleScope scope)
: establishing_element_(establishing_element), name_(name) {
switch (scope) {
case ToggleScope::kNarrow:
stay_within_ = establishing_element;
break;
case ToggleScope::kWide:
stay_within_ = establishing_element->parentElement();
break;
}
}

Iterator begin() {
return Iterator(establishing_element_, stay_within_, name_);
}
Iterator end() { return Iterator(nullptr, stay_within_, name_); }
};

typedef CSSTogglesScopeRangeBase<CSSToggleGroupScopeIteratorTraits>
CSSToggleGroupScopeRange;
typedef CSSTogglesScopeRangeBase<CSSToggleScopeIteratorTraits>
CSSToggleScopeRange;

} // namespace blink

#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_TRAVERSAL_H_

0 comments on commit ef2c8d9

Please sign in to comment.