mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
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:
parent
51479c81df
commit
1e31a04af4
14
dom/webidl/CSSScopeRule.webidl
Normal file
14
dom/webidl/CSSScopeRule.webidl
Normal 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;
|
||||
};
|
@ -492,6 +492,7 @@ WEBIDL_FILES = [
|
||||
"CSSPseudoElement.webidl",
|
||||
"CSSRule.webidl",
|
||||
"CSSRuleList.webidl",
|
||||
"CSSScopeRule.webidl",
|
||||
"CSSStyleDeclaration.webidl",
|
||||
"CSSStyleRule.webidl",
|
||||
"CSSStyleSheet.webidl",
|
||||
|
@ -433,6 +433,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
|
||||
case StyleCssRuleType::CounterStyle:
|
||||
case StyleCssRuleType::FontFeatureValues:
|
||||
case StyleCssRuleType::FontPaletteValues:
|
||||
case StyleCssRuleType::Scope:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
66
layout/style/CSSScopeRule.cpp
Normal file
66
layout/style/CSSScopeRule.cpp
Normal 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
|
49
layout/style/CSSScopeRule.h
Normal file
49
layout/style/CSSScopeRule.h
Normal 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
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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();
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
162
servo/components/style/stylesheets/scope_rule.rs
Normal file
162
servo/components/style/stylesheets/scope_rule.rs
Normal 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 }
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
||||
|
@ -3234,7 +3234,8 @@ impl CascadeData {
|
||||
CssRule::LayerBlock(..) |
|
||||
CssRule::LayerStatement(..) |
|
||||
CssRule::FontPaletteValues(..) |
|
||||
CssRule::FontFeatureValues(..) => {
|
||||
CssRule::FontFeatureValues(..) |
|
||||
CssRule::Scope(..) => {
|
||||
// Not affected by device changes.
|
||||
continue;
|
||||
},
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -1,5 +0,0 @@
|
||||
[scope-deep.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[Deep @scope nesting]
|
||||
expected: FAIL
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -46,3 +46,6 @@
|
||||
|
||||
[Scoped nested group rule]
|
||||
expected: FAIL
|
||||
|
||||
[Nesting-selector in <scope-end>]
|
||||
expected: FAIL
|
||||
|
@ -6,6 +6,3 @@
|
||||
|
||||
[Proximity wins over order of appearance]
|
||||
expected: FAIL
|
||||
|
||||
[Specificity wins over proximity]
|
||||
expected: FAIL
|
||||
|
@ -1,7 +1,4 @@
|
||||
[scope-shadow.tentative.html]
|
||||
[@scope can match :host]
|
||||
expected: FAIL
|
||||
|
||||
[@scope can match :host(...)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,3 @@
|
||||
prefs: [layout.css.at-scope.enabled:true]
|
||||
[conditional-rules.html]
|
||||
expected: FAIL
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user