LibWeb: Apply scopes from imported stylesheets
Treat a scoped `@import` rule as a scope descriptor while traversing style-producing rules from imported stylesheets. Imported rules now inherit an outer scope for unscoped imports and replace it when the import itself carries scope(...). Scope resolution was generalized to handle both CSSScopeRule and CSSImportRule descriptors. Import-scope boundaries are matched using the stylesheet that parsed the `@import` rule, while normal imported selectors keep using the imported stylesheet context. This lets implicit scopes, nested `@scope` rules, :scope, and top-level `&` behave as the Cascade 6 model requires.
This commit is contained in:
parent
538dca4448
commit
85e14f5f1f
15 changed files with 148 additions and 58 deletions
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/CSS/CSSImportRule.h>
|
||||
#include <LibWeb/CSS/CSSLayerBlockRule.h>
|
||||
#include <LibWeb/CSS/CSSScopeRule.h>
|
||||
#include <LibWeb/CSS/Fetch.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
|
|
@ -295,6 +296,28 @@ Optional<SelectorList> const& CSSImportRule::scope_end_selectors() const
|
|||
return m_scope->end_selectors;
|
||||
}
|
||||
|
||||
Optional<SelectorList> const& CSSImportRule::scope_start_selectors_for_matching() const
|
||||
{
|
||||
VERIFY(m_scope.has_value());
|
||||
if (!m_scope->start_selectors.has_value())
|
||||
return m_scope->start_selectors;
|
||||
|
||||
if (!m_cached_scope_start_selectors_for_matching.has_value())
|
||||
m_cached_scope_start_selectors_for_matching = absolutize_selectors_relative_to(*m_scope->start_selectors, nullptr);
|
||||
return m_cached_scope_start_selectors_for_matching;
|
||||
}
|
||||
|
||||
Optional<SelectorList> const& CSSImportRule::scope_end_selectors_for_matching() const
|
||||
{
|
||||
VERIFY(m_scope.has_value());
|
||||
if (!m_scope->end_selectors.has_value())
|
||||
return m_scope->end_selectors;
|
||||
|
||||
if (!m_cached_scope_end_selectors_for_matching.has_value())
|
||||
m_cached_scope_end_selectors_for_matching = adapt_scope_end_selectors_for_matching(*m_scope->end_selectors);
|
||||
return m_cached_scope_end_selectors_for_matching;
|
||||
}
|
||||
|
||||
Optional<FlyString> CSSImportRule::internal_qualified_layer_name(Badge<StyleScope>) const
|
||||
{
|
||||
if (!m_layer.has_value())
|
||||
|
|
@ -313,6 +336,13 @@ bool CSSImportRule::matches() const
|
|||
return m_media->matches();
|
||||
}
|
||||
|
||||
void CSSImportRule::clear_caches()
|
||||
{
|
||||
Base::clear_caches();
|
||||
m_cached_scope_start_selectors_for_matching.clear();
|
||||
m_cached_scope_end_selectors_for_matching.clear();
|
||||
}
|
||||
|
||||
void CSSImportRule::dump(StringBuilder& builder, int indent_levels) const
|
||||
{
|
||||
Base::dump(builder, indent_levels);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ public:
|
|||
bool has_scope() const { return m_scope.has_value(); }
|
||||
Optional<SelectorList> const& scope_start_selectors() const;
|
||||
Optional<SelectorList> const& scope_end_selectors() const;
|
||||
Optional<SelectorList> const& scope_start_selectors_for_matching() const;
|
||||
Optional<SelectorList> const& scope_end_selectors_for_matching() const;
|
||||
|
||||
Optional<FlyString> internal_layer_name() const { return m_layer_internal; }
|
||||
Optional<FlyString> internal_qualified_layer_name(Badge<StyleScope>) const;
|
||||
|
|
@ -57,6 +59,7 @@ private:
|
|||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual void clear_caches() override;
|
||||
virtual void dump(StringBuilder&, int indent_levels) const override;
|
||||
|
||||
virtual void set_parent_style_sheet(CSSStyleSheet*) override;
|
||||
|
|
@ -73,6 +76,8 @@ private:
|
|||
Optional<FlyString> m_layer;
|
||||
Optional<FlyString> m_layer_internal;
|
||||
Optional<ImportScope> m_scope;
|
||||
mutable Optional<SelectorList> m_cached_scope_start_selectors_for_matching;
|
||||
mutable Optional<SelectorList> m_cached_scope_end_selectors_for_matching;
|
||||
RefPtr<Supports> m_supports;
|
||||
GC::Ref<MediaList> m_media;
|
||||
GC::Ptr<CSSStyleSheet> m_style_sheet;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public:
|
|||
void set_parent_rule(CSSRule*);
|
||||
|
||||
CSSStyleSheet* parent_style_sheet() { return m_parent_style_sheet.ptr(); }
|
||||
CSSStyleSheet const* parent_style_sheet() const { return m_parent_style_sheet.ptr(); }
|
||||
MUST_UPCALL virtual void set_parent_style_sheet(CSSStyleSheet*);
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibWeb/Bindings/CSSScopeRule.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/CSS/CSSImportRule.h>
|
||||
#include <LibWeb/CSS/CSSRuleList.h>
|
||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||
#include <LibWeb/Dump.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
|
@ -65,7 +67,7 @@ Optional<SelectorList> const& CSSScopeRule::start_selectors_for_matching() const
|
|||
return m_cached_start_selectors_for_matching;
|
||||
}
|
||||
|
||||
static SelectorList adapt_end_selectors(SelectorList const& selectors)
|
||||
SelectorList adapt_scope_end_selectors_for_matching(SelectorList const& selectors)
|
||||
{
|
||||
// NB: This is a modified version of adapt_nested_relative_selector_list().
|
||||
// End selectors are implicitly prepended with `:scope` if that doesn't already appear.
|
||||
|
|
@ -101,10 +103,53 @@ Optional<SelectorList> const& CSSScopeRule::end_selectors_for_matching() const
|
|||
return m_end_selectors;
|
||||
|
||||
if (!m_cached_end_selectors_for_matching.has_value())
|
||||
m_cached_end_selectors_for_matching = adapt_end_selectors(*m_end_selectors);
|
||||
m_cached_end_selectors_for_matching = adapt_scope_end_selectors_for_matching(*m_end_selectors);
|
||||
return m_cached_end_selectors_for_matching;
|
||||
}
|
||||
|
||||
Optional<SelectorList> const& scope_start_selectors_for_matching(CSSRule const& scope_rule)
|
||||
{
|
||||
if (auto const* css_scope_rule = as_if<CSSScopeRule const>(scope_rule))
|
||||
return css_scope_rule->start_selectors_for_matching();
|
||||
if (auto const* import_rule = as_if<CSSImportRule const>(scope_rule)) {
|
||||
VERIFY(import_rule->has_scope());
|
||||
return import_rule->scope_start_selectors_for_matching();
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
Optional<SelectorList> const& scope_end_selectors_for_matching(CSSRule const& scope_rule)
|
||||
{
|
||||
if (auto const* css_scope_rule = as_if<CSSScopeRule const>(scope_rule))
|
||||
return css_scope_rule->end_selectors_for_matching();
|
||||
if (auto const* import_rule = as_if<CSSImportRule const>(scope_rule)) {
|
||||
VERIFY(import_rule->has_scope());
|
||||
return import_rule->scope_end_selectors_for_matching();
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
GC::Ptr<CSSImportRule const> nearest_scoped_owner_import(CSSStyleSheet const* style_sheet)
|
||||
{
|
||||
for (auto const* current_style_sheet = style_sheet; current_style_sheet;) {
|
||||
auto owner_rule = current_style_sheet->owner_rule();
|
||||
if (auto const* import_rule = as_if<CSSImportRule const>(owner_rule.ptr()); import_rule && import_rule->has_scope())
|
||||
return import_rule;
|
||||
current_style_sheet = owner_rule ? owner_rule->parent_style_sheet() : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GC::Ptr<CSSRule const> nearest_ancestor_scope_rule_for_matching(CSSRule const& scope_rule)
|
||||
{
|
||||
for (auto const* parent_rule = scope_rule.parent_rule(); parent_rule; parent_rule = parent_rule->parent_rule()) {
|
||||
if (is<CSSScopeRule>(*parent_rule))
|
||||
return parent_rule;
|
||||
}
|
||||
|
||||
return nearest_scoped_owner_import(scope_rule.parent_style_sheet());
|
||||
}
|
||||
|
||||
GC::Ptr<CSSScopeRule const> CSSScopeRule::nearest_ancestor_scope_rule() const
|
||||
{
|
||||
if (m_cached_nearest_ancestor_scope_rule.has_value())
|
||||
|
|
|
|||
|
|
@ -51,4 +51,10 @@ private:
|
|||
template<>
|
||||
inline bool CSSRule::fast_is<CSSScopeRule>() const { return type() == CSSRule::Type::Scope; }
|
||||
|
||||
SelectorList adapt_scope_end_selectors_for_matching(SelectorList const&);
|
||||
Optional<SelectorList> const& scope_start_selectors_for_matching(CSSRule const&);
|
||||
Optional<SelectorList> const& scope_end_selectors_for_matching(CSSRule const&);
|
||||
GC::Ptr<CSSImportRule const> nearest_scoped_owner_import(CSSStyleSheet const*);
|
||||
GC::Ptr<CSSRule const> nearest_ancestor_scope_rule_for_matching(CSSRule const&);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -252,10 +252,10 @@ Vector<HasInvalidationMetadata> const* StyleComputer::has_invalidation_metadata_
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static bool scope_selector_matches(Selector const& selector, DOM::Element const& element, DOM::Element const& subject, MatchingRule const& rule, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root, GC::Ptr<DOM::ParentNode const> scope)
|
||||
static bool scope_selector_matches(Selector const& selector, DOM::Element const& element, DOM::Element const& subject, CSSStyleSheet const& scope_style_sheet, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root, GC::Ptr<DOM::ParentNode const> scope)
|
||||
{
|
||||
SelectorEngine::MatchContext context {
|
||||
.style_sheet_for_rule = *rule.sheet,
|
||||
.style_sheet_for_rule = scope_style_sheet,
|
||||
.subject = subject,
|
||||
.rule_shadow_root = rule_root,
|
||||
.collect_per_element_selector_involvement_metadata = true,
|
||||
|
|
@ -268,20 +268,22 @@ struct ResolvedScope {
|
|||
size_t proximity { NumericLimits<size_t>::max() };
|
||||
};
|
||||
|
||||
static Optional<ResolvedScope> resolve_single_scope(DOM::AbstractElement abstract_element, MatchingRule const& rule, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root, CSSScopeRule const& scope_rule, GC::Ptr<DOM::Element const> outer_root)
|
||||
static Optional<ResolvedScope> resolve_single_scope(DOM::AbstractElement abstract_element, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root, CSSRule const& scope_rule, GC::Ptr<DOM::Element const> outer_root)
|
||||
{
|
||||
GC::Ptr<DOM::Element const> root;
|
||||
size_t proximity = 0;
|
||||
auto const* owner_style_sheet = scope_rule.parent_style_sheet();
|
||||
VERIFY(owner_style_sheet);
|
||||
|
||||
// https://drafts.csswg.org/css-cascade-6/#scope-limits
|
||||
// Finding the scoping root(s)
|
||||
// For each element matched by <scope-start>, create a scope using that element as the scoping root.
|
||||
if (scope_rule.start_selectors_for_matching().has_value()) {
|
||||
if (scope_start_selectors_for_matching(scope_rule).has_value()) {
|
||||
for (auto const* candidate = &abstract_element.element(); candidate; candidate = candidate->parent_element().ptr(), ++proximity) {
|
||||
if (outer_root && !outer_root->is_inclusive_ancestor_of(*candidate))
|
||||
break;
|
||||
for (auto const& selector : *scope_rule.start_selectors_for_matching()) {
|
||||
if (scope_selector_matches(selector, *candidate, abstract_element.element(), rule, shadow_host, rule_root, outer_root)) {
|
||||
for (auto const& selector : *scope_start_selectors_for_matching(scope_rule)) {
|
||||
if (scope_selector_matches(selector, *candidate, abstract_element.element(), *owner_style_sheet, shadow_host, rule_root, outer_root)) {
|
||||
root = candidate;
|
||||
break;
|
||||
}
|
||||
|
|
@ -293,7 +295,7 @@ static Optional<ResolvedScope> resolve_single_scope(DOM::AbstractElement abstrac
|
|||
root = [&] -> GC::Ptr<DOM::Element const> {
|
||||
// If no <scope-start> is specified, the scoping root is the parent element of the owner node of the
|
||||
// stylesheet where the @scope rule is defined.
|
||||
if (auto* owner_node = const_cast<CSSStyleSheet&>(*rule.sheet).owner_node()) {
|
||||
if (auto* owner_node = const_cast<CSSStyleSheet&>(*owner_style_sheet).owner_node()) {
|
||||
if (auto parent = owner_node->parent_element())
|
||||
return parent;
|
||||
}
|
||||
|
|
@ -304,7 +306,7 @@ static Optional<ResolvedScope> resolve_single_scope(DOM::AbstractElement abstrac
|
|||
return rule_root->host();
|
||||
|
||||
// Otherwise, the scoping root is the root of the containing node tree.
|
||||
if (auto document = rule.sheet->owning_document())
|
||||
if (auto document = owner_style_sheet->owning_document())
|
||||
return document->document_element();
|
||||
return nullptr;
|
||||
}();
|
||||
|
|
@ -322,10 +324,10 @@ static Optional<ResolvedScope> resolve_single_scope(DOM::AbstractElement abstrac
|
|||
// Finding any scoping limits
|
||||
// For each scope created by a scoping root, its scoping limits are set to all elements that are descendants of
|
||||
// the scoping root and that match <scope-end>, interpreting :scope and & exactly as in scoped style rules.
|
||||
if (scope_rule.end_selectors_for_matching().has_value()) {
|
||||
if (scope_end_selectors_for_matching(scope_rule).has_value()) {
|
||||
for (auto const* candidate = &abstract_element.element(); candidate; candidate = candidate->parent_element().ptr()) {
|
||||
for (auto const& selector : *scope_rule.end_selectors_for_matching()) {
|
||||
if (scope_selector_matches(selector, *candidate, abstract_element.element(), rule, shadow_host, rule_root, root))
|
||||
for (auto const& selector : *scope_end_selectors_for_matching(scope_rule)) {
|
||||
if (scope_selector_matches(selector, *candidate, abstract_element.element(), *owner_style_sheet, shadow_host, rule_root, root))
|
||||
return {};
|
||||
}
|
||||
if (candidate == root)
|
||||
|
|
@ -336,7 +338,7 @@ static Optional<ResolvedScope> resolve_single_scope(DOM::AbstractElement abstrac
|
|||
return ResolvedScope { root, proximity };
|
||||
}
|
||||
|
||||
static Optional<ResolvedScope> resolve_scope_chain(DOM::AbstractElement abstract_element, MatchingRule const& rule, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root, CSSScopeRule const& scope_rule)
|
||||
static Optional<ResolvedScope> resolve_scope_chain(DOM::AbstractElement abstract_element, MatchingRule const& rule, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root, CSSRule const& scope_rule)
|
||||
{
|
||||
// https://drafts.csswg.org/css-cascade-6/#cascade-proximity
|
||||
// Scope Proximity
|
||||
|
|
@ -344,7 +346,7 @@ static Optional<ResolvedScope> resolve_scope_chain(DOM::AbstractElement abstract
|
|||
// the fewest generational or sibling-element hops between the scoping root and the scoped style rule subject wins.
|
||||
// For this purpose, style rules without a scoping root are considered to have infinite proximity hops.
|
||||
GC::Ptr<DOM::Element const> outer_root;
|
||||
if (auto ancestor_scope_rule = scope_rule.nearest_ancestor_scope_rule()) {
|
||||
if (auto ancestor_scope_rule = nearest_ancestor_scope_rule_for_matching(scope_rule)) {
|
||||
auto resolved_ancestor_scope = resolve_scope_chain(abstract_element, rule, shadow_host, rule_root, *ancestor_scope_rule);
|
||||
if (!resolved_ancestor_scope.has_value())
|
||||
return {};
|
||||
|
|
@ -352,7 +354,7 @@ static Optional<ResolvedScope> resolve_scope_chain(DOM::AbstractElement abstract
|
|||
outer_root = resolved_ancestor_scope->root;
|
||||
}
|
||||
|
||||
return resolve_single_scope(abstract_element, rule, shadow_host, rule_root, scope_rule, outer_root);
|
||||
return resolve_single_scope(abstract_element, shadow_host, rule_root, scope_rule, outer_root);
|
||||
}
|
||||
|
||||
static Optional<ResolvedScope> resolve_scope(DOM::AbstractElement abstract_element, MatchingRule const& rule, GC::Ptr<DOM::Element const> shadow_host, GC::Ptr<DOM::ShadowRoot const> rule_root)
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ static GC::Ptr<CSSContainerRule const> current_container_rule(Vector<GC::Ptr<CSS
|
|||
return container_rule_stack.last();
|
||||
}
|
||||
|
||||
static void collect_scope_boundary_selector_dependencies(CSSScopeRule const& scope_rule, StyleCache& style_cache)
|
||||
static void collect_scope_boundary_selector_dependencies(CSSRule const& scope_rule, StyleCache& style_cache)
|
||||
{
|
||||
auto collect_selector_list = [&](Optional<SelectorList> const& selector_list) {
|
||||
if (!selector_list.has_value())
|
||||
|
|
@ -255,43 +255,49 @@ static void collect_scope_boundary_selector_dependencies(CSSScopeRule const& sco
|
|||
}
|
||||
};
|
||||
|
||||
for (auto const* current_scope_rule = &scope_rule; current_scope_rule; current_scope_rule = current_scope_rule->nearest_ancestor_scope_rule().ptr()) {
|
||||
collect_selector_list(current_scope_rule->start_selectors_for_matching());
|
||||
collect_selector_list(current_scope_rule->end_selectors_for_matching());
|
||||
for (auto const* current_scope_rule = &scope_rule; current_scope_rule; current_scope_rule = nearest_ancestor_scope_rule_for_matching(*current_scope_rule).ptr()) {
|
||||
collect_selector_list(scope_start_selectors_for_matching(*current_scope_rule));
|
||||
collect_selector_list(scope_end_selectors_for_matching(*current_scope_rule));
|
||||
}
|
||||
}
|
||||
|
||||
using RuleCacheStyleRuleCallback = Function<void(CSSRule const&, GC::Ptr<CSSContainerRule const>, GC::Ptr<CSSScopeRule const>)>;
|
||||
using RuleCacheStyleRuleCallback = Function<void(CSSRule const&, CSSStyleSheet const&, GC::Ptr<CSSContainerRule const>, GC::Ptr<CSSRule const>)>;
|
||||
|
||||
static void for_each_style_producing_rule_for_rule_cache(
|
||||
CSSRuleList const& rule_list,
|
||||
CSSStyleSheet const& current_style_sheet,
|
||||
Vector<GC::Ptr<CSSContainerRule const>>& container_rule_stack,
|
||||
GC::Ptr<CSSScopeRule const> scope_rule,
|
||||
GC::Ptr<CSSRule const> scope_rule,
|
||||
RuleCacheStyleRuleCallback const& callback);
|
||||
|
||||
static void for_each_style_producing_rule_for_rule_cache(
|
||||
CSSStyleSheet const& sheet,
|
||||
Vector<GC::Ptr<CSSContainerRule const>>& container_rule_stack,
|
||||
GC::Ptr<CSSScopeRule const> scope_rule,
|
||||
GC::Ptr<CSSRule const> scope_rule,
|
||||
RuleCacheStyleRuleCallback const& callback)
|
||||
{
|
||||
if (!sheet.media()->matches())
|
||||
return;
|
||||
for_each_style_producing_rule_for_rule_cache(sheet.rules(), container_rule_stack, scope_rule, callback);
|
||||
for_each_style_producing_rule_for_rule_cache(sheet.rules(), sheet, container_rule_stack, scope_rule, callback);
|
||||
}
|
||||
|
||||
static void for_each_style_producing_rule_for_rule_cache(
|
||||
CSSRuleList const& rule_list,
|
||||
CSSStyleSheet const& current_style_sheet,
|
||||
Vector<GC::Ptr<CSSContainerRule const>>& container_rule_stack,
|
||||
GC::Ptr<CSSScopeRule const> scope_rule,
|
||||
GC::Ptr<CSSRule const> scope_rule,
|
||||
RuleCacheStyleRuleCallback const& callback)
|
||||
{
|
||||
for (auto const& rule : rule_list) {
|
||||
switch (rule->type()) {
|
||||
case CSSRule::Type::Import: {
|
||||
auto const& import_rule = as<CSSImportRule>(*rule);
|
||||
if (import_rule.loaded_style_sheet())
|
||||
for_each_style_producing_rule_for_rule_cache(*import_rule.loaded_style_sheet(), container_rule_stack, scope_rule, callback);
|
||||
if (import_rule.loaded_style_sheet()) {
|
||||
GC::Ptr<CSSRule const> import_scope_rule = scope_rule;
|
||||
if (import_rule.has_scope())
|
||||
import_scope_rule = &import_rule;
|
||||
for_each_style_producing_rule_for_rule_cache(*import_rule.loaded_style_sheet(), container_rule_stack, import_scope_rule, callback);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -299,35 +305,35 @@ static void for_each_style_producing_rule_for_rule_cache(
|
|||
auto const& container_rule = as<CSSContainerRule>(*rule);
|
||||
// @container conditions are element-dependent, so keep their style rules and evaluate the container later.
|
||||
container_rule_stack.append(&container_rule);
|
||||
for_each_style_producing_rule_for_rule_cache(container_rule.css_rules(), container_rule_stack, scope_rule, callback);
|
||||
for_each_style_producing_rule_for_rule_cache(container_rule.css_rules(), current_style_sheet, container_rule_stack, scope_rule, callback);
|
||||
container_rule_stack.take_last();
|
||||
break;
|
||||
}
|
||||
|
||||
case CSSRule::Type::Scope: {
|
||||
auto const& nested_scope_rule = as<CSSScopeRule>(*rule);
|
||||
for_each_style_producing_rule_for_rule_cache(nested_scope_rule.css_rules(), container_rule_stack, &nested_scope_rule, callback);
|
||||
for_each_style_producing_rule_for_rule_cache(nested_scope_rule.css_rules(), current_style_sheet, container_rule_stack, &nested_scope_rule, callback);
|
||||
break;
|
||||
}
|
||||
|
||||
case CSSRule::Type::Media:
|
||||
case CSSRule::Type::Supports:
|
||||
if (as<CSSConditionRule>(*rule).condition_matches())
|
||||
for_each_style_producing_rule_for_rule_cache(as<CSSGroupingRule>(*rule).css_rules(), container_rule_stack, scope_rule, callback);
|
||||
for_each_style_producing_rule_for_rule_cache(as<CSSGroupingRule>(*rule).css_rules(), current_style_sheet, container_rule_stack, scope_rule, callback);
|
||||
break;
|
||||
|
||||
case CSSRule::Type::LayerBlock:
|
||||
case CSSRule::Type::Page:
|
||||
for_each_style_producing_rule_for_rule_cache(as<CSSGroupingRule>(*rule).css_rules(), container_rule_stack, scope_rule, callback);
|
||||
for_each_style_producing_rule_for_rule_cache(as<CSSGroupingRule>(*rule).css_rules(), current_style_sheet, container_rule_stack, scope_rule, callback);
|
||||
break;
|
||||
|
||||
case CSSRule::Type::Style:
|
||||
callback(*rule, current_container_rule(container_rule_stack), scope_rule);
|
||||
for_each_style_producing_rule_for_rule_cache(as<CSSGroupingRule>(*rule).css_rules(), container_rule_stack, scope_rule, callback);
|
||||
callback(*rule, current_style_sheet, current_container_rule(container_rule_stack), scope_rule);
|
||||
for_each_style_producing_rule_for_rule_cache(as<CSSGroupingRule>(*rule).css_rules(), current_style_sheet, container_rule_stack, scope_rule, callback);
|
||||
break;
|
||||
|
||||
case CSSRule::Type::NestedDeclarations:
|
||||
callback(*rule, current_container_rule(container_rule_stack), scope_rule);
|
||||
callback(*rule, current_style_sheet, current_container_rule(container_rule_stack), scope_rule);
|
||||
break;
|
||||
|
||||
case CSSRule::Type::CounterStyle:
|
||||
|
|
@ -386,7 +392,7 @@ void StyleScope::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin
|
|||
|
||||
size_t rule_index = 0;
|
||||
Vector<GC::Ptr<CSSContainerRule const>> container_rule_stack;
|
||||
for_each_style_producing_rule_for_rule_cache(sheet, container_rule_stack, nullptr, [&](auto const& rule, auto container_rule, auto scope_rule) {
|
||||
for_each_style_producing_rule_for_rule_cache(sheet, container_rule_stack, nullptr, [&](auto const& rule, auto const& current_style_sheet, auto container_rule, auto scope_rule) {
|
||||
if (container_rule && container_rule->contains_size_feature())
|
||||
style_cache.has_size_container_queries = true;
|
||||
|
||||
|
|
@ -407,10 +413,10 @@ void StyleScope::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin
|
|||
for (CSS::Selector const& selector : absolutized_selectors) {
|
||||
MatchingRule matching_rule {
|
||||
.rule = &rule,
|
||||
.sheet = sheet,
|
||||
.sheet = current_style_sheet,
|
||||
.container_rule = container_rule,
|
||||
.scope_rule = scope_rule,
|
||||
.default_namespace = sheet.default_namespace(),
|
||||
.default_namespace = current_style_sheet.default_namespace(),
|
||||
.selector = selector,
|
||||
.style_sheet_index = style_sheet_index,
|
||||
.rule_index = rule_index,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ struct MatchingRule {
|
|||
GC::Ptr<CSSRule const> rule; // Either CSSStyleRule or CSSNestedDeclarations
|
||||
GC::Ptr<CSSStyleSheet const> sheet;
|
||||
GC::Ptr<CSSContainerRule const> container_rule;
|
||||
GC::Ptr<CSSScopeRule const> scope_rule;
|
||||
GC::Ptr<CSSRule const> scope_rule; // Either CSSScopeRule or CSSImportRule
|
||||
Optional<FlyString> default_namespace;
|
||||
Selector const& selector;
|
||||
size_t style_sheet_index { 0 };
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
1 Pass
|
||||
1 Fail
|
||||
2 Pass
|
||||
Pass Scope-imported rule applies within implicit scope
|
||||
Fail Scope-imported rule does not apply outside of implicit scope
|
||||
Pass Scope-imported rule does not apply outside of implicit scope
|
||||
|
|
@ -2,7 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
1 Pass
|
||||
1 Fail
|
||||
2 Pass
|
||||
Pass @scope within scope-imported stylesheet matches
|
||||
Fail @scope within scope-imported does not ignore import scope
|
||||
Pass @scope within scope-imported does not ignore import scope
|
||||
|
|
@ -2,7 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
1 Pass
|
||||
1 Fail
|
||||
2 Pass
|
||||
Pass A stylesheet may be imported multiple times, and scoped differently
|
||||
Fail Scope-imported rule does not apply outside of scope
|
||||
Pass Scope-imported rule does not apply outside of scope
|
||||
|
|
@ -2,6 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
2 Fail
|
||||
Fail The & selector matches the scoping root
|
||||
Fail The & selector behaves like :scope
|
||||
2 Pass
|
||||
Pass The & selector matches the scoping root
|
||||
Pass The & selector behaves like :scope
|
||||
|
|
@ -2,7 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
1 Pass
|
||||
1 Fail
|
||||
2 Pass
|
||||
Pass Scope-imported rule applies within scope, above limit
|
||||
Fail Scope-imported rule does not apply below limit
|
||||
Pass Scope-imported rule does not apply below limit
|
||||
|
|
@ -2,5 +2,5 @@ Harness status: OK
|
|||
|
||||
Found 1 tests
|
||||
|
||||
1 Fail
|
||||
Fail The :scope pseudo-class works in imported stylesheets
|
||||
1 Pass
|
||||
Pass The :scope pseudo-class works in imported stylesheets
|
||||
|
|
@ -2,7 +2,6 @@ Harness status: OK
|
|||
|
||||
Found 2 tests
|
||||
|
||||
1 Pass
|
||||
1 Fail
|
||||
2 Pass
|
||||
Pass Scope-imported rule applies within scope
|
||||
Fail Scope-imported rule does not apply outside of scope
|
||||
Pass Scope-imported rule does not apply outside of scope
|
||||
Loading…
Reference in a new issue