Bug 1882581: Implement @scope parsing. r=firefox-style-system-reviewers,saschanaz,emilio

Differential Revision: https://phabricator.services.mozilla.com/D203153
This commit is contained in:
David Shin 2024-03-19 13:36:48 +00:00
parent 51479c81df
commit 1e31a04af4
39 changed files with 448 additions and 208 deletions

View File

@ -0,0 +1,14 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://drafts.csswg.org/css-cascade-6/#the-cssscoperule-interface
*/
[Exposed=Window, Pref="layout.css.at-scope.enabled"]
interface CSSScopeRule : CSSGroupingRule {
readonly attribute UTF8String? start;
readonly attribute UTF8String? end;
};

View File

@ -492,6 +492,7 @@ WEBIDL_FILES = [
"CSSPseudoElement.webidl",
"CSSRule.webidl",
"CSSRuleList.webidl",
"CSSScopeRule.webidl",
"CSSStyleDeclaration.webidl",
"CSSStyleRule.webidl",
"CSSStyleSheet.webidl",

View File

@ -433,6 +433,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
case StyleCssRuleType::CounterStyle:
case StyleCssRuleType::FontFeatureValues:
case StyleCssRuleType::FontPaletteValues:
case StyleCssRuleType::Scope:
break;
}

View File

@ -86,7 +86,8 @@ void ServoStyleRuleMap::RuleRemoved(StyleSheet& aStyleSheet,
case StyleCssRuleType::Supports:
case StyleCssRuleType::LayerBlock:
case StyleCssRuleType::Container:
case StyleCssRuleType::Document: {
case StyleCssRuleType::Document:
case StyleCssRuleType::Scope: {
// See the comment in SheetRemoved.
mTable.Clear();
break;
@ -124,7 +125,8 @@ void ServoStyleRuleMap::FillTableFromRule(css::Rule& aRule) {
case StyleCssRuleType::Media:
case StyleCssRuleType::Supports:
case StyleCssRuleType::Container:
case StyleCssRuleType::Document: {
case StyleCssRuleType::Document:
case StyleCssRuleType::Scope: {
auto& rule = static_cast<css::GroupRule&>(aRule);
FillTableFromRuleList(*rule.CssRules());
break;

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/CSSScopeRule.h"
#include "mozilla/dom/CSSScopeRuleBinding.h"
#include "mozilla/ServoBindings.h"
namespace mozilla::dom {
CSSScopeRule::CSSScopeRule(RefPtr<StyleScopeRule> aRawRule, StyleSheet* aSheet,
css::Rule* aParentRule, uint32_t aLine,
uint32_t aColumn)
: css::GroupRule(aSheet, aParentRule, aLine, aColumn),
mRawRule(std::move(aRawRule)) {}
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSScopeRule, css::GroupRule)
// QueryInterface implementation for SupportsRule
#ifdef DEBUG
void CSSScopeRule::List(FILE* out, int32_t aIndent) const {
nsAutoCString str;
for (int32_t i = 0; i < aIndent; i++) {
str.AppendLiteral(" ");
}
Servo_ScopeRule_Debug(mRawRule, &str);
fprintf_stderr(out, "%s\n", str.get());
}
#endif
StyleCssRuleType CSSScopeRule::Type() const { return StyleCssRuleType::Scope; }
already_AddRefed<StyleLockedCssRules> CSSScopeRule::GetOrCreateRawRules() {
return Servo_ScopeRule_GetRules(mRawRule).Consume();
}
void CSSScopeRule::SetRawAfterClone(RefPtr<StyleScopeRule> aRaw) {
mRawRule = std::move(aRaw);
css::GroupRule::DidSetRawAfterClone();
}
void CSSScopeRule::GetCssText(nsACString& aCssText) const {
Servo_ScopeRule_GetCssText(mRawRule.get(), &aCssText);
}
void CSSScopeRule::GetStart(nsACString& aStart) const {
Servo_ScopeRule_GetStart(mRawRule.get(), &aStart);
}
void CSSScopeRule::GetEnd(nsACString& aEnd) const {
Servo_ScopeRule_GetEnd(mRawRule.get(), &aEnd);
}
size_t CSSScopeRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
return aMallocSizeOf(this);
}
JSObject* CSSScopeRule::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return CSSScopeRule_Binding::Wrap(aCx, this, aGivenProto);
}
} // namespace mozilla::dom

View File

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CSSScopeRule_h___
#define CSSScopeRule_h___
#include "mozilla/css/GroupRule.h"
#include "mozilla/ServoBindingTypes.h"
namespace mozilla::dom {
class CSSScopeRule final : public css::GroupRule {
public:
CSSScopeRule(RefPtr<StyleScopeRule> aRawRule, StyleSheet* aSheet,
css::Rule* aParentRule, uint32_t aLine, uint32_t aColumn);
NS_DECL_ISUPPORTS_INHERITED
#ifdef DEBUG
void List(FILE* out = stdout, int32_t aIndent = 0) const final;
#endif
StyleScopeRule* Raw() const { return mRawRule; }
void SetRawAfterClone(RefPtr<StyleScopeRule>);
already_AddRefed<StyleLockedCssRules> GetOrCreateRawRules() final;
// WebIDL interface
StyleCssRuleType Type() const final;
void GetCssText(nsACString& aCssText) const final;
void GetStart(nsACString& aStart) const;
void GetEnd(nsACString& aEnd) const;
size_t SizeOfIncludingThis(MallocSizeOf) const override;
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
private:
~CSSScopeRule() = default;
RefPtr<StyleScopeRule> mRawRule;
};
} // namespace mozilla::dom
#endif

View File

@ -100,13 +100,13 @@ void Rule::AssertParentRuleType() {
// this->Type() here since it's virtual.
if (mParentRule) {
auto type = mParentRule->Type();
MOZ_ASSERT(type == StyleCssRuleType::Media ||
type == StyleCssRuleType::Style ||
type == StyleCssRuleType::Document ||
type == StyleCssRuleType::Supports ||
type == StyleCssRuleType::Keyframes ||
type == StyleCssRuleType::LayerBlock ||
type == StyleCssRuleType::Container);
MOZ_ASSERT(
type == StyleCssRuleType::Media || type == StyleCssRuleType::Style ||
type == StyleCssRuleType::Document ||
type == StyleCssRuleType::Supports ||
type == StyleCssRuleType::Keyframes ||
type == StyleCssRuleType::LayerBlock ||
type == StyleCssRuleType::Container || type == StyleCssRuleType::Scope);
}
}
#endif

View File

@ -129,6 +129,7 @@ UNLOCKED_RULE_TYPE(Supports)
UNLOCKED_RULE_TYPE(Document)
UNLOCKED_RULE_TYPE(FontFeatureValues)
UNLOCKED_RULE_TYPE(FontPaletteValues)
UNLOCKED_RULE_TYPE(Scope)
SERVO_ARC_TYPE(AnimationValue, mozilla::StyleAnimationValue)
SERVO_ARC_TYPE(ComputedStyle, mozilla::ComputedStyle)

View File

@ -87,6 +87,7 @@ BASIC_RULE_FUNCS_UNLOCKED(FontPaletteValues)
BASIC_RULE_FUNCS_LOCKED(FontFace)
BASIC_RULE_FUNCS_LOCKED(CounterStyle)
GROUP_RULE_FUNCS_UNLOCKED(Container)
GROUP_RULE_FUNCS_UNLOCKED(Scope)
#undef GROUP_RULE_FUNCS_LOCKED
#undef GROUP_RULE_FUNCS_UNLOCKED

View File

@ -24,6 +24,7 @@
#include "mozilla/dom/CSSPropertyRule.h"
#include "mozilla/dom/CSSStyleRule.h"
#include "mozilla/dom/CSSSupportsRule.h"
#include "mozilla/dom/CSSScopeRule.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/StyleSheet.h"
@ -98,6 +99,7 @@ css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) {
CASE_RULE_UNLOCKED(LayerBlock, LayerBlock)
CASE_RULE_UNLOCKED(LayerStatement, LayerStatement)
CASE_RULE_UNLOCKED(Container, Container)
CASE_RULE_UNLOCKED(Scope, Scope)
#undef CASE_RULE_LOCKED
#undef CASE_RULE_UNLOCKED
#undef CASE_RULE_WITH_PREFIX
@ -276,6 +278,7 @@ void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules,
RULE_CASE_UNLOCKED(LayerBlock, LayerBlock)
RULE_CASE_UNLOCKED(LayerStatement, LayerStatement)
RULE_CASE_UNLOCKED(Container, Container)
RULE_CASE_UNLOCKED(Scope, Scope)
case StyleCssRuleType::Keyframe:
MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
break;

View File

@ -53,6 +53,7 @@ template struct StyleStrong<StyleFontPaletteValuesRule>;
template struct StyleStrong<StyleLockedFontFaceRule>;
template struct StyleStrong<StyleLockedCounterStyleRule>;
template struct StyleStrong<StyleContainerRule>;
template struct StyleStrong<StyleScopeRule>;
template <typename T>
inline void StyleOwnedSlice<T>::Clear() {

View File

@ -41,6 +41,7 @@
#include "mozilla/dom/CSSNamespaceRule.h"
#include "mozilla/dom/CSSPageRule.h"
#include "mozilla/dom/CSSPropertyRule.h"
#include "mozilla/dom/CSSScopeRule.h"
#include "mozilla/dom/CSSSupportsRule.h"
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/Element.h"
@ -1002,6 +1003,7 @@ void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule,
CASE_FOR(LayerBlock, LayerBlock)
CASE_FOR(LayerStatement, LayerStatement)
CASE_FOR(Container, Container)
CASE_FOR(Scope, Scope)
// @namespace can only be inserted / removed when there are only other
// @namespace and @import rules, and can't be mutated.
case StyleCssRuleType::Namespace:

View File

@ -143,6 +143,7 @@ EXPORTS.mozilla.dom += [
"CSSPageRule.h",
"CSSPropertyRule.h",
"CSSRuleList.h",
"CSSScopeRule.h",
"CSSStyleRule.h",
"CSSSupportsRule.h",
"CSSValue.h",
@ -194,6 +195,7 @@ UNIFIED_SOURCES += [
"CSSPageRule.cpp",
"CSSPropertyRule.cpp",
"CSSRuleList.cpp",
"CSSScopeRule.cpp",
"CSSStyleRule.cpp",
"CSSSupportsRule.cpp",
"DeclarationBlock.cpp",

View File

@ -8752,6 +8752,13 @@
mirror: always
rust: true
# Whether @scope rule is enabled
- name: layout.css.at-scope.enabled
type: RelaxedAtomicBool
value: false
mirror: always
rust: true
# Dictates whether or not the prefers contrast media query will be
# usable.
# true: prefers-contrast will toggle based on OS and browser settings.

View File

@ -469,6 +469,23 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
)
}
pub fn parse_forgiving<'i, 't, P>(
parser: &P,
input: &mut CssParser<'i, 't>,
parse_relative: ParseRelative,
) -> Result<Self, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
{
Self::parse_with_state(
parser,
input,
SelectorParsingState::empty(),
ForgivingParsing::Yes,
parse_relative,
)
}
#[inline]
fn parse_with_state<'i, 't, P>(
parser: &P,

View File

@ -16,7 +16,7 @@ use crate::stylesheets::keyframes_rule::Keyframe;
use crate::stylesheets::{
ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule,
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
MediaRule, NamespaceRule, PageRule, PropertyRule, StyleRule, StylesheetContents, SupportsRule,
MediaRule, NamespaceRule, PageRule, PropertyRule, ScopeRule, StyleRule, StylesheetContents, SupportsRule,
};
use servo_arc::Arc;
@ -169,3 +169,8 @@ impl_simple_arc_ffi!(
Servo_AnimationValue_AddRef,
Servo_AnimationValue_Release
);
impl_simple_arc_ffi!(
ScopeRule,
Servo_ScopeRule_AddRef,
Servo_ScopeRule_Release
);

View File

@ -646,6 +646,11 @@ impl StylesheetInvalidationSet {
debug!(" > Found unsupported rule, marking the whole subtree invalid.");
self.invalidate_fully();
},
Scope(..) => {
// Addition/removal of @scope requires re-evaluation of scope proximity to properly
// figure out the styling order.
self.invalidate_fully();
},
}
}
}

View File

@ -26,6 +26,7 @@ mod rules_iterator;
mod style_rule;
mod stylesheet;
pub mod supports_rule;
mod scope_rule;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::sugar::refptr::RefCounted;
@ -70,6 +71,7 @@ pub use self::rules_iterator::{
EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator,
};
pub use self::style_rule::StyleRule;
pub use self::scope_rule::ScopeRule;
pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};
pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
@ -265,6 +267,7 @@ pub enum CssRule {
Document(Arc<DocumentRule>),
LayerBlock(Arc<LayerBlockRule>),
LayerStatement(Arc<LayerStatementRule>),
Scope(Arc<ScopeRule>),
}
impl CssRule {
@ -311,6 +314,9 @@ impl CssRule {
},
// TODO(emilio): Add memory reporting for these rules.
CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0,
CssRule::Scope(ref rule) => {
rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)
}
}
}
}
@ -349,6 +355,7 @@ pub enum CssRuleType {
FontPaletteValues = 19,
// 20 is an arbitrary number to use for Property.
Property = 20,
Scope = 21,
}
impl CssRuleType {
@ -436,6 +443,7 @@ impl CssRule {
CssRule::LayerBlock(_) => CssRuleType::LayerBlock,
CssRule::LayerStatement(_) => CssRuleType::LayerStatement,
CssRule::Container(_) => CssRuleType::Container,
CssRule::Scope(_) => CssRuleType::Scope,
}
}
@ -567,6 +575,9 @@ impl DeepCloneWithLock for CssRule {
CssRule::LayerBlock(ref arc) => {
CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
},
CssRule::Scope(ref arc) => {
CssRule::Scope(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
}
}
}
}
@ -592,6 +603,7 @@ impl ToCssWithGuard for CssRule {
CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest),
CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest),
CssRule::Container(ref rule) => rule.to_css(guard, dest),
CssRule::Scope(ref rule) => rule.to_css(guard, dest),
}
}
}

View File

@ -24,6 +24,7 @@ use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCon
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
use crate::stylesheets::supports_rule::SupportsCondition;
use crate::stylesheets::scope_rule::{ScopeBounds, ScopeRule};
use crate::stylesheets::{
AllowImportRules, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules, DocumentRule,
FontFeatureValuesRule, FontPaletteValuesRule, KeyframesRule, MarginRule, MarginRuleType,
@ -231,6 +232,8 @@ pub enum AtRulePrelude {
Namespace(Option<Prefix>, Namespace),
/// A @layer rule prelude.
Layer(Vec<LayerName>),
/// A @scope rule prelude.
Scope(ScopeBounds),
}
impl AtRulePrelude {
@ -251,6 +254,7 @@ impl AtRulePrelude {
Self::Margin(..) => "margin",
Self::Namespace(..) => "namespace",
Self::Layer(..) => "layer",
Self::Scope(..) => "scope",
}
}
}
@ -507,7 +511,8 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
AtRulePrelude::Supports(..) |
AtRulePrelude::Container(..) |
AtRulePrelude::Document(..) |
AtRulePrelude::Layer(..) => true,
AtRulePrelude::Layer(..) |
AtRulePrelude::Scope(..) => true,
AtRulePrelude::Namespace(..) |
AtRulePrelude::FontFace |
@ -701,6 +706,10 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
let cond = DocumentCondition::parse(&self.context, input)?;
AtRulePrelude::Document(cond)
},
"scope" if static_prefs::pref!("layout.css.at-scope.enabled") => {
let bounds = ScopeBounds::parse(&self.context, input, self.in_style_rule());
AtRulePrelude::Scope(bounds)
},
_ => {
if static_prefs::pref!("layout.css.margin-rules.enabled") {
if let Some(margin_rule_type) = MarginRuleType::match_name(&name) {
@ -867,6 +876,16 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
// These rules don't have blocks.
return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock));
},
AtRulePrelude::Scope(bounds) => {
let source_location = start.source_location();
CssRule::Scope(Arc::new(ScopeRule {
bounds,
rules: self
.parse_nested(input, CssRuleType::Scope)
.into_rules(self.shared_lock, source_location),
source_location,
}))
},
};
self.rules.push(rule);
Ok(())

View File

@ -116,6 +116,7 @@ where
Some(supports_rule.rules.read_with(guard).0.iter())
},
CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()),
CssRule::Scope(ref rule) => Some(rule.rules.read_with(guard).0.iter())
}
}
}

View File

@ -0,0 +1,162 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! A [`@scope`][scope] rule.
//!
//! [scope]: https://drafts.csswg.org/css-cascade-6/#scoped-styles
use crate::parser::ParserContext;
use crate::selector_parser::{SelectorImpl, SelectorParser};
use crate::shared_lock::{
DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
};
use crate::str::CssStringWriter;
use crate::stylesheets::CssRules;
use cssparser::{Parser, SourceLocation, ToCss};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalSizeOf, MallocUnconditionalShallowSizeOf};
use selectors::parser::{ParseRelative, SelectorList};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::CssWriter;
/// A scoped rule.
#[derive(Debug, ToShmem)]
pub struct ScopeRule {
/// Bounds at which this rule applies.
pub bounds: ScopeBounds,
/// The nested rules inside the block.
pub rules: Arc<Locked<CssRules>>,
/// The source position where this rule was found.
pub source_location: SourceLocation,
}
impl DeepCloneWithLock for ScopeRule {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
params: &DeepCloneParams,
) -> Self {
let rules = self.rules.read_with(guard);
Self {
bounds: self.bounds.clone(),
rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
source_location: self.source_location.clone(),
}
}
}
impl ToCssWithGuard for ScopeRule {
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@scope")?;
{
let mut writer = CssWriter::new(dest);
if let Some(start) = self.bounds.start.as_ref() {
writer.write_str(" (")?;
start.to_css(&mut writer)?;
writer.write_char(')')?;
}
if let Some(end) = self.bounds.end.as_ref() {
writer.write_str(" to (")?;
end.to_css(&mut writer)?;
writer.write_char(')')?;
}
}
self.rules.read_with(guard).to_css_block(guard, dest)
}
}
impl ScopeRule {
/// Measure heap usage.
#[cfg(feature = "gecko")]
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
self.rules.unconditional_shallow_size_of(ops) +
self.rules.read_with(guard).size_of(guard, ops) +
self.bounds.size_of(ops)
}
}
/// Bounds of the scope.
#[derive(Debug, Clone, ToShmem)]
pub struct ScopeBounds {
/// Start of the scope.
pub start: Option<SelectorList<SelectorImpl>>,
/// End of the scope.
pub end: Option<SelectorList<SelectorImpl>>,
}
impl ScopeBounds {
#[cfg(feature = "gecko")]
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
fn bound_size_of(bound: &Option<SelectorList<SelectorImpl>>, ops: &mut MallocSizeOfOps) -> usize {
bound.as_ref().map(|list| list.unconditional_size_of(ops)).unwrap_or(0)
}
bound_size_of(&self.start, ops) + bound_size_of(&self.end, ops)
}
}
fn parse_scope<'a>(
context: &ParserContext,
input: &mut Parser<'a, '_>,
in_style_rule: bool,
for_end: bool
) -> Option<SelectorList<SelectorImpl>> {
input.try_parse(|input| {
if for_end {
input.expect_ident_matching("to")?;
}
input.expect_parenthesis_block()?;
input.parse_nested_block(|input| {
if input.is_exhausted() {
return Ok(None);
}
let selector_parser = SelectorParser {
stylesheet_origin: context.stylesheet_origin,
namespaces: &context.namespaces,
url_data: context.url_data,
for_supports_rule: false,
};
let parse_relative = if for_end {
// TODO(dshin): scope-end can be a relative selector, with the anchor being `:scope`.
ParseRelative::No
} else if in_style_rule {
ParseRelative::ForNesting
} else {
ParseRelative::No
};
Ok(Some(SelectorList::parse_forgiving(
&selector_parser,
input,
parse_relative,
)?))
})
})
.ok()
.flatten()
}
impl ScopeBounds {
/// Parse a container condition.
pub fn parse<'a>(
context: &ParserContext,
input: &mut Parser<'a, '_>,
in_style_rule: bool,
) -> Self {
let start = parse_scope(
context,
input,
in_style_rule,
false
);
let end = parse_scope(
context,
input,
in_style_rule,
true
);
Self { start, end }
}
}

View File

@ -333,7 +333,10 @@ impl SanitizationKind {
// TODO(emilio): Perhaps Layer should not be always sanitized? But
// we sanitize @media and co, so this seems safer for now.
CssRule::LayerStatement(..) |
CssRule::LayerBlock(..) => false,
CssRule::LayerBlock(..) |
// TODO(dshin): Same comment as Layer applies - shouldn't give away
// something like display size - erring on the side of "safe" for now.
CssRule::Scope(..) => false,
CssRule::FontFace(..) | CssRule::Namespace(..) | CssRule::Style(..) => true,

View File

@ -3234,7 +3234,8 @@ impl CascadeData {
CssRule::LayerBlock(..) |
CssRule::LayerStatement(..) |
CssRule::FontPaletteValues(..) |
CssRule::FontFeatureValues(..) => {
CssRule::FontFeatureValues(..) |
CssRule::Scope(..) => {
// Not affected by device changes.
continue;
},

View File

@ -139,7 +139,7 @@ use style::stylesheets::{
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
MediaRule, NamespaceRule, Origin, OriginSet, PagePseudoClassFlags, PageRule, PropertyRule,
SanitizationData, SanitizationKind, StyleRule, StylesheetContents,
StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData, ScopeRule,
};
use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
use style::thread_state;
@ -2483,6 +2483,14 @@ impl_basic_rule_funcs! { (CounterStyle, CounterStyleRule, Locked<CounterStyleRul
changed: Servo_StyleSet_CounterStyleRuleChanged,
}
impl_group_rule_funcs! { (Scope, ScopeRule, ScopeRule),
get_rules: Servo_ScopeRule_GetRules,
getter: Servo_CssRules_GetScopeRuleAt,
debug: Servo_ScopeRule_Debug,
to_css: Servo_ScopeRule_GetCssText,
changed: Servo_StyleSet_ScopeRuleChanged,
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetStyle(
rule: &LockedStyleRule,
@ -8644,6 +8652,24 @@ pub extern "C" fn Servo_LayerBlockRule_GetName(rule: &LayerBlockRule, result: &m
}
}
#[no_mangle]
pub extern "C" fn Servo_ScopeRule_GetStart(rule: &ScopeRule, result: &mut nsACString) {
if let Some(v) = rule.bounds.start.as_ref() {
v.to_css(&mut CssWriter::new(result)).unwrap();
} else {
result.set_is_void(true);
}
}
#[no_mangle]
pub extern "C" fn Servo_ScopeRule_GetEnd(rule: &ScopeRule, result: &mut nsACString) {
if let Some(v) = rule.bounds.end.as_ref() {
v.to_css(&mut CssWriter::new(result)).unwrap();
} else {
result.set_is_void(true);
}
}
#[no_mangle]
pub extern "C" fn Servo_LayerStatementRule_GetNameCount(rule: &LayerStatementRule) -> usize {
rule.names.len()

View File

@ -244,7 +244,7 @@ class StartupCache : public nsIMemoryReporter {
nsTArray<decltype(mTable)> mOldTables MOZ_GUARDED_BY(mTableLock);
size_t mAllowedInvalidationsCount;
nsCOMPtr<nsIFile> mFile;
loader::AutoMemMap mCacheData MOZ_GUARDED_BY(mTableLock);
mozilla::loader::AutoMemMap mCacheData MOZ_GUARDED_BY(mTableLock);
Mutex mTableLock;
nsCOMPtr<nsIObserverService> mObserverService;

View File

@ -1 +1 @@
prefs: [layout.css.import-supports.enabled:true, layout.css.properties-and-values.enabled:true]
prefs: [layout.css.import-supports.enabled:true, layout.css.properties-and-values.enabled:true, layout.css.at-scope.enabled:true]

View File

@ -1,80 +0,0 @@
[at-scope-parsing.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[@scope (.a) is valid]
expected: FAIL
[@scope (.a + .b) is valid]
expected: FAIL
[@scope (.a:hover) is valid]
expected: FAIL
[@scope (.a:hover, #b, div) is valid]
expected: FAIL
[@scope (:is(div, span)) is valid]
expected: FAIL
[@scope (.a) to (.b) is valid]
expected: FAIL
[@scope (.a)to (.b) is valid]
expected: FAIL
[@scope (.a) to (.b:hover, #c, div) is valid]
expected: FAIL
[@scope (.c <> .d) is valid]
expected: FAIL
[@scope (.a, .c <> .d) is valid]
expected: FAIL
[@scope (.a <> .b, .c) is valid]
expected: FAIL
[@scope (div::before) is valid]
expected: FAIL
[@scope (div::after) is valid]
expected: FAIL
[@scope (slotted(div)) is valid]
expected: FAIL
[@scope (.a) to (div::before) is valid]
expected: FAIL
[@scope is valid]
expected: FAIL
[@scope (.a) to (&) is valid]
expected: FAIL
[@scope (.a) to (& > &) is valid]
expected: FAIL
[@scope (.a) to (> .b) is valid]
expected: FAIL
[@scope (.a) to (+ .b) is valid]
expected: FAIL
[@scope (.a) to (~ .b) is valid]
expected: FAIL
[@scope to (.a) is valid]
expected: FAIL
[@scope (> &) to (>>) is valid]
expected: FAIL
[@scope () is valid]
expected: FAIL
[@scope to () is valid]
expected: FAIL
[@scope () to () is valid]
expected: FAIL

View File

@ -1,35 +0,0 @@
[idlharness.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[CSSScopeRule interface: existence and properties of interface object]
expected: FAIL
[CSSScopeRule interface object length]
expected: FAIL
[CSSScopeRule interface object name]
expected: FAIL
[CSSScopeRule interface: existence and properties of interface prototype object]
expected: FAIL
[CSSScopeRule interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[CSSScopeRule interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
[CSSScopeRule interface: attribute start]
expected: FAIL
[CSSScopeRule interface: attribute end]
expected: FAIL
[Stringification of scope]
expected: FAIL
[CSSScopeRule interface: scope must inherit property "start" with the proper type]
expected: FAIL
[CSSScopeRule interface: scope must inherit property "end" with the proper type]
expected: FAIL

View File

@ -1,39 +0,0 @@
[scope-cssom.html]
[CSSScopeRule.cssText, implicit scope]
expected: FAIL
[CSSScopeRule.cssText, root only]
expected: FAIL
[CSSScopeRule.cssText, root and limit]
expected: FAIL
[CSSScopeRule.cssText, limit only]
expected: FAIL
[CSSScopeRule.start, implicit scope]
expected: FAIL
[CSSScopeRule.start, root only]
expected: FAIL
[CSSScopeRule.start, root and limit]
expected: FAIL
[CSSScopeRule.start, limit only]
expected: FAIL
[CSSScopeRule.end, implicit scope]
expected: FAIL
[CSSScopeRule.end, root only]
expected: FAIL
[CSSScopeRule.end, root and limit]
expected: FAIL
[CSSScopeRule.end, limit only]
expected: FAIL
[CSSScopeRule is a CSSGroupingRule]
expected: FAIL

View File

@ -1,5 +0,0 @@
[scope-deep.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[Deep @scope nesting]
expected: FAIL

View File

@ -17,9 +17,6 @@
[Inner @scope with :scope in from-selector]
expected: FAIL
[Multiple scopes from same @scope-rule, only one limited]
expected: FAIL
[Nested scopes]
expected: FAIL
@ -43,3 +40,15 @@
[Scope root with :has()]
expected: FAIL
[Scope can not match its own root without :scope]
expected: FAIL
[Multiple scopes from same @scope-rule, both limited]
expected: FAIL
[Nested scopes, reverse]
expected: FAIL
[Scope with no elements]
expected: FAIL

View File

@ -16,3 +16,9 @@
[Implicit @scope with limit]
expected: FAIL
[@scope with effectively empty :is() must not match anything]
expected: FAIL
[Implicit @scope has implicitly added :scope descendant combinator]
expected: FAIL

View File

@ -1,12 +0,0 @@
[scope-name-defining-rules.html]
[@keyframes is unaffected by @scope]
expected: FAIL
[@keyframes is unaffected by non-matching @scope]
expected: FAIL
[@property is unaffected by @scope]
expected: FAIL
[@property is unaffected by non-matching @scope]
expected: FAIL

View File

@ -46,3 +46,6 @@
[Scoped nested group rule]
expected: FAIL
[Nesting-selector in <scope-end>]
expected: FAIL

View File

@ -6,6 +6,3 @@
[Proximity wins over order of appearance]
expected: FAIL
[Specificity wins over proximity]
expected: FAIL

View File

@ -1,7 +1,4 @@
[scope-shadow.tentative.html]
[@scope can match :host]
expected: FAIL
[@scope can match :host(...)]
expected: FAIL

View File

@ -1,16 +1,4 @@
[scope-visited-cssom.html]
[:link as scoped selector]
expected: FAIL
[:not(:visited) as scoped selector]
expected: FAIL
[:link as scoping root]
expected: FAIL
[:not(:visited) as scoping root]
expected: FAIL
[:link as scoping root, :scope]
expected: FAIL
@ -22,3 +10,9 @@
[:not(:link) as scoping limit]
expected: FAIL
[:visited as scoping root]
expected: FAIL
[:not(:link) as scoping root]
expected: FAIL

View File

@ -0,0 +1,3 @@
prefs: [layout.css.at-scope.enabled:true]
[conditional-rules.html]
expected: FAIL

View File

@ -75,4 +75,5 @@
test_invalid('@scope (.a');
test_invalid('@scope (.a to (.b)');
test_invalid('@scope ( to (.b)');
test_invalid('@scope (.a) from (.c)');
</script>