ladybird/Libraries/LibWeb/CSS/Parser/RuleContext.cpp
Sam Atkins 5c928eb7eb LibWeb/CSS: Implement the @scope rule
`@scope (a) to (b) {}` applies its contained style rules to elements
that have `a` as a parent, and do not have `a b` as a parent. Both the
`a` and `b` selector lists are optional.

Because it's situational whether a `@scope` will apply to a given
element, we store the ancestor scope on the `MatchingRule`, similar to
`@container`, and then determine during matching whether all the parent
`@scope`s match or not.

The rules for how selectors inside `@scope` are adjusted and interpreted
are a bit confusing. Unlike for other at-rules, nested style rules
inside `@scope` do not get a leading `&` added during parsing. To
support this, `adapt_nested_relative_selector_list()` now takes a flag
for whether its parent is a `@scope` or not.

`@scope` can also contain nested declarations without itself being
nested inside a style rule.

When determining their selectors, nested declarations rules adopt the
`@scope`'s scoping root if it has one, or otherwise fall back to the
parent element of the `<style>` element (not implemented here,) or the
`:root`. These are required to have zero specificity, so we wrap the
selector in `:where()`.
2026-05-22 10:00:42 +01:00

91 lines
3.2 KiB
C++

/*
* Copyright (c) 2025-2026, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CSS/CSSFontFeatureValuesRule.h>
#include <LibWeb/CSS/CSSMarginRule.h>
#include <LibWeb/CSS/Parser/RuleContext.h>
namespace Web::CSS::Parser {
RuleContext rule_context_type_for_rule(CSSRule::Type rule_type)
{
switch (rule_type) {
case CSSRule::Type::Container:
return RuleContext::AtContainer;
case CSSRule::Type::CounterStyle:
return RuleContext::AtCounterStyle;
case CSSRule::Type::Style:
return RuleContext::Style;
case CSSRule::Type::Media:
return RuleContext::AtMedia;
case CSSRule::Type::FontFace:
return RuleContext::AtFontFace;
case CSSRule::Type::FontFeatureValues:
return RuleContext::AtFontFeatureValues;
case CSSRule::Type::Function:
return RuleContext::AtFunction;
case CSSRule::Type::Keyframes:
return RuleContext::AtKeyframes;
case CSSRule::Type::Keyframe:
return RuleContext::Keyframe;
case CSSRule::Type::Supports:
return RuleContext::AtSupports;
case CSSRule::Type::Scope:
return RuleContext::AtScope;
case CSSRule::Type::LayerBlock:
return RuleContext::AtLayer;
case CSSRule::Type::Margin:
return RuleContext::Margin;
case CSSRule::Type::NestedDeclarations:
return RuleContext::Style;
case CSSRule::Type::Page:
return RuleContext::AtPage;
case CSSRule::Type::Property:
return RuleContext::AtProperty;
// Other types shouldn't be trying to create a context.
case CSSRule::Type::Import:
case CSSRule::Type::LayerStatement:
case CSSRule::Type::Namespace:
case CSSRule::Type::FunctionDeclarations:
break;
}
VERIFY_NOT_REACHED();
}
RuleContext rule_context_type_for_at_rule(FlyString const& name)
{
if (name.equals_ignoring_ascii_case("media"sv))
return RuleContext::AtMedia;
if (name.equals_ignoring_ascii_case("container"sv))
return RuleContext::AtContainer;
if (name.equals_ignoring_ascii_case("counter-style"sv))
return RuleContext::AtCounterStyle;
if (name.equals_ignoring_ascii_case("font-face"sv))
return RuleContext::AtFontFace;
if (name.equals_ignoring_ascii_case("keyframes"sv) || name.equals_ignoring_ascii_case("-webkit-keyframes"sv))
return RuleContext::AtKeyframes;
if (name.equals_ignoring_ascii_case("font-feature-values"sv))
return RuleContext::AtFontFeatureValues;
if (name.equals_ignoring_ascii_case("function"sv))
return RuleContext::AtFunction;
if (CSSFontFeatureValuesRule::is_font_feature_value_type_at_keyword(name))
return RuleContext::FontFeatureValue;
if (name.equals_ignoring_ascii_case("supports"sv))
return RuleContext::AtSupports;
if (name.equals_ignoring_ascii_case("scope"sv))
return RuleContext::AtScope;
if (name.equals_ignoring_ascii_case("layer"sv))
return RuleContext::AtLayer;
if (name.equals_ignoring_ascii_case("property"sv))
return RuleContext::AtProperty;
if (name.equals_ignoring_ascii_case("page"sv))
return RuleContext::AtPage;
if (is_margin_rule_name(name))
return RuleContext::Margin;
return RuleContext::Unknown;
}
}