blob: b3993c107c909d371889e885a2075587a780dd45 [file] [log] [blame]
// 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_CSS_STYLE_SCOPE_FRAME_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_SCOPE_FRAME_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/css/resolver/match_flags.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_vector.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
namespace blink {
class ContainerNode;
class Element;
class StyleScope;
class SelectorChecker;
// The *activations* for a given StyleScope/node, is a list of active
// scopes found in the ancestor chain, their roots (ContainerNode*), and the
// proximities to those roots.
//
// The idea is that, if we're matching a selector ':scope' within some
// StyleScope, we look up the activations for that StyleScope, and
// and check if the current element (`SelectorCheckingContext.element`)
// matches any of the activation roots.
struct CORE_EXPORT StyleScopeActivation {
DISALLOW_NEW();
public:
void Trace(blink::Visitor*) const;
// The root is the node when the activation happened. In other words,
// the node that matched <scope-start>. The root is always an Element for
// activations produced by @scope, however, it may be a non-element for
// the "default activation" (see SelectorChecker::EnsureActivations).
//
// https://drafts.csswg.org/css-cascade-6/#typedef-scope-start
Member<const ContainerNode> root;
// The distance to the root, in terms of number of inclusive ancestors
// between some subject element and the root.
unsigned proximity = 0;
};
struct CORE_EXPORT StyleScopeActivations
: public GarbageCollected<StyleScopeActivations> {
public:
void Trace(blink::Visitor*) const;
HeapVector<StyleScopeActivation> vector;
// Even if `vector` is empty, `match_flags` can be set. For example:
//
// @scope (p:hover) {
// :scope { ... }
// }
//
// When matching :scope against 'p', even if 'p' is not currently hovered,
// (and therefore won't produce a StyleScopeActivation in the vector),
// `match_flags` will contain kAffectedByHover. This allows us to propagate
// the flags when matching :scope, also when the selector does not match.
MatchFlags match_flags = 0;
};
// Stores the current @scope activations for a given subject element.
//
// See `StyleScopeActivation` for more information about activations.
//
// StyleScopeFrames are placed on the stack in `Element::RecalcStyle`, and
// serve as a cache of all @scope activations until that point in the tree.
// The actual contents of a StyleScopeFrame is populated lazily during
// `SelectorChecker::CheckPseudoScope`.
//
// StyleScopeFrames may contain a pointer to a parent frame, in which case
// `SelectorChecker::CheckPseudoScope` will store data applicable to the parent
// element in that frame.
class CORE_EXPORT StyleScopeFrame {
STACK_ALLOCATED();
public:
explicit StyleScopeFrame(Element& element) : element_(element) {}
explicit StyleScopeFrame(Element& element, StyleScopeFrame* parent)
: element_(element), parent_(parent) {}
StyleScopeFrame* GetParentFrameOrNull(Element& parent_element);
StyleScopeFrame& GetParentFrameOrThis(Element& parent_element);
// A StyleScope has been "seen" if `element_` or any of the elements
// in element_'s ancestor chain is a scoping root.
//
// Note that a StyleScope being "seen" does not mean that it's currently
// "in scope" [1], because the scope may be limited [2]. However, if a
// StyleScope has *not* been seen, it's definitely not in scope.
//
// This function is only valid for implicit StyleScopes (IsImplicit()==true).
//
// [1] https://drafts.csswg.org/css-cascade-6/#in-scope
// [2] https://drafts.csswg.org/css-cascade-6/#scoping-limit
bool HasSeenImplicitScope(const StyleScope&);
private:
friend class SelectorChecker;
using ScopeSet = HeapHashSet<Member<const StyleScope>>;
ScopeSet* CalculateSeenImplicitScopes();
Element& element_;
StyleScopeFrame* parent_ = nullptr;
HeapHashMap<Member<const StyleScope>, Member<const StyleScopeActivations>>
data_;
ScopeSet* seen_implicit_scopes_ = nullptr;
};
} // namespace blink
WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::StyleScopeActivation)
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_SCOPE_FRAME_H_