Bug 1918408 - Implement the nested declarations rule. r=dshin,webidl,smaug

I didn't put this behind a pref because it was a bit annoying to do with
the parser changes while keeping it fast, and because, at least nesting
wise, this is unlikely to cause compat issues.

There are some complexities for @scope, which I think I got right.

Fixed the tests not to depend on @scope necessarily per the discussion
in https://github.com/web-platform-tests/interop/issues/697.

Differential Revision: https://phabricator.services.mozilla.com/D222817
This commit is contained in:
Emilio Cobos Álvarez 2024-09-19 20:23:22 +00:00
parent dda565a838
commit ba4e92b748
33 changed files with 866 additions and 437 deletions

View File

@ -325,6 +325,8 @@ let interfaceNamesInGlobalScope = [
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "CSSNamespaceRule", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "CSSNestedDeclarations", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "CSSPageDescriptors", insecureContext: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "CSSPageRule", insecureContext: true },

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-nesting-1/#the-cssnestrule
*/
[Exposed=Window]
interface CSSNestedDeclarations : CSSRule {
// CSSStyleDeclaration instead of CSSStyleProperties for now, see bug 1919582.
[SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style;
};

View File

@ -506,6 +506,7 @@ WEBIDL_FILES = [
"CSSMediaRule.webidl",
"CSSMozDocumentRule.webidl",
"CSSNamespaceRule.webidl",
"CSSNestedDeclarations.webidl",
"CSSPageRule.webidl",
"CSSPositionTryRule.webidl",
"CSSPropertyRule.webidl",

View File

@ -474,6 +474,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
case StyleCssRuleType::Scope:
case StyleCssRuleType::StartingStyle:
case StyleCssRuleType::PositionTry:
case StyleCssRuleType::NestedDeclarations:
break;
}

View File

@ -16,7 +16,6 @@
#include "mozilla/IntegerRange.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/StyleSheetInlines.h"
#include "nsStyleSheetService.h"
using namespace mozilla::dom;
@ -81,6 +80,7 @@ void ServoStyleRuleMap::RuleRemoved(StyleSheet& aStyleSheet,
switch (aStyleRule.Type()) {
case StyleCssRuleType::Style:
case StyleCssRuleType::NestedDeclarations:
case StyleCssRuleType::Import:
case StyleCssRuleType::Media:
case StyleCssRuleType::Supports:
@ -153,6 +153,7 @@ void ServoStyleRuleMap::FillTableFromRule(css::Rule& aRule) {
case StyleCssRuleType::FontFeatureValues:
case StyleCssRuleType::FontPaletteValues:
case StyleCssRuleType::PositionTry:
case StyleCssRuleType::NestedDeclarations:
break;
}
}

View File

@ -13,7 +13,9 @@
opacity: 1;
@starting-style {
opacity: 0;
& {
opacity: 0;
}
}
}
@ -26,7 +28,14 @@
background-color: tomato;
@starting-style {
background-color: gold;
/*
* FIXME(bug 1919853): getCSSStyleRules() doesn't return bare nested
* declarations. Once we figure out the right thing to do there,
* the wrapping `&{}` can go away.
*/
& {
background-color: gold;
}
}
}
</style>

View File

@ -0,0 +1,169 @@
/* -*- 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/CSSNestedDeclarations.h"
#include "mozilla/dom/CSSNestedDeclarationsBinding.h"
#include "mozilla/DeclarationBlock.h"
namespace mozilla::dom {
CSSNestedDeclarationsDeclaration::CSSNestedDeclarationsDeclaration(
already_AddRefed<StyleLockedDeclarationBlock> aDecls)
: mDecls(new DeclarationBlock(std::move(aDecls))) {
mDecls->SetOwningRule(Rule());
}
CSSNestedDeclarationsDeclaration::~CSSNestedDeclarationsDeclaration() {
mDecls->SetOwningRule(nullptr);
}
// QueryInterface implementation for CSSNestedDeclarationsDeclaration
NS_INTERFACE_MAP_BEGIN(CSSNestedDeclarationsDeclaration)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
// We forward the cycle collection interfaces to Rule(), which is
// never null (in fact, we're part of that object!)
if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
return Rule()->QueryInterface(aIID, aInstancePtr);
}
NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
NS_IMPL_ADDREF_USING_AGGREGATOR(CSSNestedDeclarationsDeclaration, Rule())
NS_IMPL_RELEASE_USING_AGGREGATOR(CSSNestedDeclarationsDeclaration, Rule())
css::Rule* CSSNestedDeclarationsDeclaration::GetParentRule() { return Rule(); }
nsINode* CSSNestedDeclarationsDeclaration::GetAssociatedNode() const {
return Rule()->GetAssociatedDocumentOrShadowRoot();
}
nsISupports* CSSNestedDeclarationsDeclaration::GetParentObject() const {
return Rule()->GetParentObject();
}
DeclarationBlock* CSSNestedDeclarationsDeclaration::GetOrCreateCSSDeclaration(
Operation aOperation, DeclarationBlock** aCreated) {
if (aOperation != Operation::Read) {
if (StyleSheet* sheet = Rule()->GetStyleSheet()) {
sheet->WillDirty();
}
}
return mDecls;
}
void CSSNestedDeclarationsDeclaration::SetRawAfterClone(
RefPtr<StyleLockedDeclarationBlock> aRaw) {
auto block = MakeRefPtr<DeclarationBlock>(aRaw.forget());
mDecls->SetOwningRule(nullptr);
mDecls = std::move(block);
mDecls->SetOwningRule(Rule());
}
nsresult CSSNestedDeclarationsDeclaration::SetCSSDeclaration(
DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
CSSNestedDeclarations* rule = Rule();
if (StyleSheet* sheet = rule->GetStyleSheet()) {
if (aDecl != mDecls) {
mDecls->SetOwningRule(nullptr);
RefPtr<DeclarationBlock> decls = aDecl;
Servo_NestedDeclarationsRule_SetStyle(rule->Raw(), decls->Raw());
mDecls = std::move(decls);
mDecls->SetOwningRule(rule);
}
sheet->RuleChanged(rule, StyleRuleChangeKind::StyleRuleDeclarations);
}
return NS_OK;
}
nsDOMCSSDeclaration::ParsingEnvironment
CSSNestedDeclarationsDeclaration::GetParsingEnvironment(nsIPrincipal*) const {
return GetParsingEnvironmentForRule(Rule(),
StyleCssRuleType::NestedDeclarations);
}
CSSNestedDeclarations::CSSNestedDeclarations(
already_AddRefed<StyleLockedNestedDeclarationsRule> aRawRule,
StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine,
uint32_t aColumn)
: css::Rule(aSheet, aParentRule, aLine, aColumn),
mRawRule(aRawRule),
mDecls(Servo_NestedDeclarationsRule_GetStyle(mRawRule).Consume()) {}
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(CSSNestedDeclarations, css::Rule)
NS_IMPL_CYCLE_COLLECTION_CLASS(CSSNestedDeclarations)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CSSNestedDeclarations, css::Rule)
// Keep this in sync with IsCCLeaf.
// Trace the wrapper for our declaration. This just expands out
// NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
// directly because the wrapper is on the declaration, not on us.
tmp->mDecls.TraceWrapper(aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CSSNestedDeclarations)
// Keep this in sync with IsCCLeaf.
// Unlink the wrapper for our declaration.
// Note that this has to happen before unlinking css::Rule.
tmp->UnlinkDeclarationWrapper(tmp->mDecls);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(css::Rule)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CSSNestedDeclarations,
css::Rule)
// Keep this in sync with IsCCLeaf.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
bool CSSNestedDeclarations::IsCCLeaf() const {
if (!css::Rule::IsCCLeaf()) {
return false;
}
return !mDecls.PreservingWrapper();
}
void CSSNestedDeclarations::GetCssText(nsACString& aCssText) const {
Servo_NestedDeclarationsRule_GetCssText(mRawRule, &aCssText);
}
#ifdef DEBUG
void CSSNestedDeclarations::List(FILE* out, int32_t aIndent) const {
nsAutoCString str;
for (int32_t i = 0; i < aIndent; i++) {
str.AppendLiteral(" ");
}
Servo_NestedDeclarationsRule_Debug(mRawRule, &str);
fprintf_stderr(out, "%s\n", str.get());
}
#endif
StyleCssRuleType CSSNestedDeclarations::Type() const {
return StyleCssRuleType::NestedDeclarations;
}
size_t CSSNestedDeclarations::SizeOfIncludingThis(
MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mRawRule
// - mDecls
return n;
}
void CSSNestedDeclarations::SetRawAfterClone(
RefPtr<StyleLockedNestedDeclarationsRule> aRaw) {
mRawRule = std::move(aRaw);
mDecls.SetRawAfterClone(
Servo_NestedDeclarationsRule_GetStyle(mRawRule).Consume());
}
JSObject* CSSNestedDeclarations::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return CSSNestedDeclarations_Binding::Wrap(aCx, this, aGivenProto);
}
} // namespace mozilla::dom

View File

@ -0,0 +1,114 @@
/* -*- 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 mozilla_CSSNestedDeclarations_h
#define mozilla_CSSNestedDeclarations_h
#include "mozilla/css/Rule.h"
#include "mozilla/ServoBindingTypes.h"
#include "nsDOMCSSDeclaration.h"
namespace mozilla {
class DeclarationBlock;
namespace dom {
class DocGroup;
class CSSNestedDeclarations;
class CSSNestedDeclarationsDeclaration final : public nsDOMCSSDeclaration {
public:
NS_DECL_ISUPPORTS_INHERITED
css::Rule* GetParentRule() final;
nsINode* GetAssociatedNode() const final;
nsISupports* GetParentObject() const final;
protected:
DeclarationBlock* GetOrCreateCSSDeclaration(
Operation aOperation, DeclarationBlock** aCreated) final;
nsresult SetCSSDeclaration(DeclarationBlock* aDecl,
MutationClosureData* aClosureData) final;
ParsingEnvironment GetParsingEnvironment(
nsIPrincipal* aSubjectPrincipal) const final;
private:
// For accessing the constructor.
friend class CSSNestedDeclarations;
explicit CSSNestedDeclarationsDeclaration(
already_AddRefed<StyleLockedDeclarationBlock> aDecls);
~CSSNestedDeclarationsDeclaration();
inline CSSNestedDeclarations* Rule();
inline const CSSNestedDeclarations* Rule() const;
void SetRawAfterClone(RefPtr<StyleLockedDeclarationBlock>);
RefPtr<DeclarationBlock> mDecls;
};
class CSSNestedDeclarations final : public css::Rule {
public:
CSSNestedDeclarations(
already_AddRefed<StyleLockedNestedDeclarationsRule> aRawRule,
StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine,
uint32_t aColumn);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CSSNestedDeclarations,
css::Rule)
bool IsCCLeaf() const final MOZ_MUST_OVERRIDE;
// WebIDL interface
StyleCssRuleType Type() const final;
void GetCssText(nsACString& aCssText) const final;
nsICSSDeclaration* Style() { return &mDecls; }
StyleLockedNestedDeclarationsRule* Raw() const { return mRawRule.get(); }
void SetRawAfterClone(RefPtr<StyleLockedNestedDeclarationsRule>);
// Methods of mozilla::css::Rule
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const final;
#ifdef DEBUG
void List(FILE* out = stdout, int32_t aIndent = 0) const final;
#endif
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
private:
~CSSNestedDeclarations() = default;
void GetSelectorDataAtIndex(uint32_t aSelectorIndex, bool aDesugared,
nsACString* aText, uint64_t* aSpecificity);
// For computing the offset of mDecls.
friend class CSSNestedDeclarationsDeclaration;
RefPtr<StyleLockedNestedDeclarationsRule> mRawRule;
CSSNestedDeclarationsDeclaration mDecls;
};
CSSNestedDeclarations* CSSNestedDeclarationsDeclaration::Rule() {
return reinterpret_cast<CSSNestedDeclarations*>(
reinterpret_cast<uint8_t*>(this) -
offsetof(CSSNestedDeclarations, mDecls));
}
const CSSNestedDeclarations* CSSNestedDeclarationsDeclaration::Rule() const {
return reinterpret_cast<const CSSNestedDeclarations*>(
reinterpret_cast<const uint8_t*>(this) -
offsetof(CSSNestedDeclarations, mDecls));
}
// CSSNestedDeclarations is the only rule type that doesn't end up with "Rule".
// This alias helps for consistency.
using CSSNestedDeclarationsRule = CSSNestedDeclarations;
} // namespace dom
} // namespace mozilla
#endif // mozilla_CSSNestedDeclarations_h

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/CSSStyleRule.h"
#include "mozilla/dom/CSSStyleRuleBinding.h"
#include "mozilla/CSSEnabledState.h"
#include "mozilla/DeclarationBlock.h"
@ -13,7 +14,6 @@
#include "mozilla/dom/ShadowRoot.h"
#include "nsCSSPseudoElements.h"
#include "mozAutoDocUpdate.h"
#include "nsISupports.h"
namespace mozilla::dom {
@ -66,6 +66,14 @@ DeclarationBlock* CSSStyleRuleDeclaration::GetOrCreateCSSDeclaration(
return mDecls;
}
void CSSStyleRuleDeclaration::SetRawAfterClone(
RefPtr<StyleLockedDeclarationBlock> aRaw) {
auto block = MakeRefPtr<DeclarationBlock>(aRaw.forget());
mDecls->SetOwningRule(nullptr);
mDecls = std::move(block);
mDecls->SetOwningRule(Rule());
}
void CSSStyleRule::SetRawAfterClone(RefPtr<StyleLockedStyleRule> aRaw) {
mRawRule = std::move(aRaw);
mDecls.SetRawAfterClone(Servo_StyleRule_GetStyle(mRawRule).Consume());
@ -76,14 +84,6 @@ already_AddRefed<StyleLockedCssRules> CSSStyleRule::GetOrCreateRawRules() {
return Servo_StyleRule_EnsureRules(mRawRule, IsReadOnly()).Consume();
}
void CSSStyleRuleDeclaration::SetRawAfterClone(
RefPtr<StyleLockedDeclarationBlock> aRaw) {
auto block = MakeRefPtr<DeclarationBlock>(aRaw.forget());
mDecls->SetOwningRule(nullptr);
mDecls = std::move(block);
mDecls->SetOwningRule(Rule());
}
nsresult CSSStyleRuleDeclaration::SetCSSDeclaration(
DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
CSSStyleRule* rule = Rule();
@ -102,8 +102,7 @@ nsresult CSSStyleRuleDeclaration::SetCSSDeclaration(
}
nsDOMCSSDeclaration::ParsingEnvironment
CSSStyleRuleDeclaration::GetParsingEnvironment(
nsIPrincipal* aSubjectPrincipal) const {
CSSStyleRuleDeclaration::GetParsingEnvironment(nsIPrincipal*) const {
return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Style);
}
@ -180,8 +179,6 @@ void CSSStyleRule::GetCssText(nsACString& aCssText) const {
Servo_StyleRule_GetCssText(mRawRule, &aCssText);
}
nsICSSDeclaration* CSSStyleRule::Style() { return &mDecls; }
/* CSSStyleRule implementation */
void CSSStyleRule::GetSelectorText(nsACString& aSelectorText) {
@ -286,10 +283,6 @@ bool CSSStyleRule::SelectorMatchesElement(uint32_t aSelectorIndex,
aRelevantLinkVisited);
}
NotNull<DeclarationBlock*> CSSStyleRule::GetDeclarationBlock() const {
return WrapNotNull(mDecls.mDecls);
}
SelectorWarningKind ToWebIDLSelectorWarningKind(
StyleSelectorWarningKind aKind) {
switch (aKind) {

View File

@ -9,9 +9,7 @@
#include "mozilla/css/GroupRule.h"
#include "mozilla/ServoBindingTypes.h"
#include "mozilla/NotNull.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/dom/CSSStyleRuleBinding.h"
#include "nsDOMCSSDeclaration.h"
@ -22,6 +20,7 @@ class DeclarationBlock;
namespace dom {
class DocGroup;
class CSSStyleRule;
struct SelectorWarning;
class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration {
public:
@ -32,8 +31,8 @@ class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration {
nsISupports* GetParentObject() const final;
protected:
mozilla::DeclarationBlock* GetOrCreateCSSDeclaration(
Operation aOperation, mozilla::DeclarationBlock** aCreated) final;
DeclarationBlock* GetOrCreateCSSDeclaration(
Operation aOperation, DeclarationBlock** aCreated) final;
nsresult SetCSSDeclaration(DeclarationBlock* aDecl,
MutationClosureData* aClosureData) final;
ParsingEnvironment GetParsingEnvironment(
@ -82,7 +81,7 @@ class CSSStyleRule final : public css::GroupRule, public SupportsWeakPtr {
void GetCssText(nsACString& aCssText) const final;
void GetSelectorText(nsACString& aSelectorText);
void SetSelectorText(const nsACString& aSelectorText);
nsICSSDeclaration* Style();
nsICSSDeclaration* Style() { return &mDecls; }
StyleLockedStyleRule* Raw() const { return mRawRule; }
void SetRawAfterClone(RefPtr<StyleLockedStyleRule>);

View File

@ -91,6 +91,7 @@ GROUP_RULE_FUNCS_UNLOCKED(Container)
GROUP_RULE_FUNCS_UNLOCKED(Scope)
GROUP_RULE_FUNCS_UNLOCKED(StartingStyle)
BASIC_RULE_FUNCS_LOCKED(PositionTry)
BASIC_RULE_FUNCS_LOCKED(NestedDeclarations)
#undef GROUP_RULE_FUNCS_LOCKED
#undef GROUP_RULE_FUNCS_UNLOCKED

View File

@ -20,6 +20,7 @@
#include "mozilla/dom/CSSMarginRule.h"
#include "mozilla/dom/CSSMediaRule.h"
#include "mozilla/dom/CSSMozDocumentRule.h"
#include "mozilla/dom/CSSNestedDeclarations.h"
#include "mozilla/dom/CSSNamespaceRule.h"
#include "mozilla/dom/CSSPageRule.h"
#include "mozilla/dom/CSSPropertyRule.h"
@ -28,7 +29,6 @@
#include "mozilla/dom/CSSStyleRule.h"
#include "mozilla/dom/CSSSupportsRule.h"
#include "mozilla/dom/CSSPositionTryRule.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/dom/Document.h"
@ -106,6 +106,7 @@ css::Rule* ServoCSSRuleList::GetRule(uint32_t aIndex) {
CASE_RULE_UNLOCKED(Scope, Scope)
CASE_RULE_UNLOCKED(StartingStyle, StartingStyle)
CASE_RULE_LOCKED(PositionTry, PositionTry)
CASE_RULE_LOCKED(NestedDeclarations, NestedDeclarations)
#undef CASE_RULE_LOCKED
#undef CASE_RULE_UNLOCKED
#undef CASE_RULE_WITH_PREFIX
@ -294,6 +295,7 @@ void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules,
RULE_CASE_UNLOCKED(Scope, Scope)
RULE_CASE_UNLOCKED(StartingStyle, StartingStyle)
RULE_CASE_LOCKED(PositionTry, PositionTry)
RULE_CASE_LOCKED(NestedDeclarations, NestedDeclarations)
case StyleCssRuleType::Keyframe:
MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
break;

View File

@ -25,3 +25,4 @@ SERVO_LOCKED_ARC_TYPE(PageRule)
SERVO_LOCKED_ARC_TYPE(FontFaceRule)
SERVO_LOCKED_ARC_TYPE(CounterStyleRule)
SERVO_LOCKED_ARC_TYPE(PositionTryRule)
SERVO_LOCKED_ARC_TYPE(NestedDeclarationsRule)

View File

@ -57,6 +57,7 @@ template struct StyleStrong<StyleContainerRule>;
template struct StyleStrong<StyleScopeRule>;
template struct StyleStrong<StyleStartingStyleRule>;
template struct StyleStrong<StyleLockedPositionTryRule>;
template struct StyleStrong<StyleLockedNestedDeclarationsRule>;
template <typename T>
inline void StyleOwnedSlice<T>::Clear() {

View File

@ -1020,6 +1020,7 @@ void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule,
CASE_FOR(Scope, Scope)
CASE_FOR(StartingStyle, StartingStyle)
CASE_FOR(PositionTry, PositionTry)
CASE_FOR(NestedDeclarations, NestedDeclarations)
// @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

@ -142,6 +142,7 @@ EXPORTS.mozilla.dom += [
"CSSMediaRule.h",
"CSSMozDocumentRule.h",
"CSSNamespaceRule.h",
"CSSNestedDeclarations.h",
"CSSPageRule.h",
"CSSPositionTryRule.h",
"CSSPropertyRule.h",
@ -197,6 +198,7 @@ UNIFIED_SOURCES += [
"CSSMediaRule.cpp",
"CSSMozDocumentRule.cpp",
"CSSNamespaceRule.cpp",
"CSSNestedDeclarations.cpp",
"CSSPageRule.cpp",
"CSSPositionTryRule.cpp",
"CSSPropertyRule.cpp",

View File

@ -382,6 +382,14 @@ pub struct SelectorList<Impl: SelectorImpl>(
);
impl<Impl: SelectorImpl> SelectorList<Impl> {
/// See Arc::mark_as_intentionally_leaked
pub fn mark_as_intentionally_leaked(&self) {
if let ArcUnionBorrow::Second(ref list) = self.0.borrow() {
list.with_arc(|list| list.mark_as_intentionally_leaked())
}
self.slice().iter().for_each(|s| s.mark_as_intentionally_leaked())
}
pub fn from_one(selector: Selector<Impl>) -> Self {
#[cfg(debug_assertions)]
let selector_repr = unsafe { *(&selector as *const _ as *const usize) };
@ -475,15 +483,14 @@ pub enum ParseRelative {
}
impl<Impl: SelectorImpl> SelectorList<Impl> {
/// Returns a selector list with a single `&`
pub fn ampersand() -> Self {
Self::from_one(Selector::ampersand())
}
/// Returns a selector list with a single `:scope`
/// Returns a selector list with a single `:scope` selector (with specificity)
pub fn scope() -> Self {
Self::from_one(Selector::scope())
}
/// Returns a selector list with a single implicit `:scope` selector (no specificity)
pub fn implicit_scope() -> Self {
Self::from_one(Selector::implicit_scope())
}
/// Parse a comma-separated list of Selectors.
/// <https://drafts.csswg.org/selectors/#grouping>
@ -822,16 +829,6 @@ impl<Impl: SelectorImpl> Selector<Impl> {
self.0.mark_as_intentionally_leaked()
}
fn ampersand() -> Self {
Self(ThinArc::from_header_and_iter(
SpecificityAndFlags {
specificity: 0,
flags: SelectorFlags::HAS_PARENT,
},
std::iter::once(Component::ParentSelector),
))
}
fn scope() -> Self {
Self(ThinArc::from_header_and_iter(
SpecificityAndFlags {
@ -842,6 +839,17 @@ impl<Impl: SelectorImpl> Selector<Impl> {
))
}
/// An implicit scope selector, much like :where(:scope).
fn implicit_scope() -> Self {
Self(ThinArc::from_header_and_iter(
SpecificityAndFlags {
specificity: 0,
flags: SelectorFlags::HAS_SCOPE,
},
std::iter::once(Component::ImplicitScope),
))
}
#[inline]
pub fn specificity(&self) -> u32 {
self.0.header.specificity

View File

@ -17,7 +17,7 @@ use crate::stylesheets::{
ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule,
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
MarginRule, MediaRule, NamespaceRule, PageRule, PositionTryRule, PropertyRule, ScopeRule,
StartingStyleRule, StyleRule, StylesheetContents, SupportsRule,
StartingStyleRule, StyleRule, StylesheetContents, SupportsRule, NestedDeclarationsRule,
};
use servo_arc::Arc;
@ -188,3 +188,9 @@ impl_locked_arc_ffi!(
Servo_PositionTryRule_AddRef,
Servo_PositionTryRule_Release
);
impl_locked_arc_ffi!(
NestedDeclarationsRule,
LockedNestedDeclarationsRule,
Servo_NestedDeclarationsRule_AddRef,
Servo_NestedDeclarationsRule_Release
);

View File

@ -597,6 +597,9 @@ impl StylesheetInvalidationSet {
}
}
},
NestedDeclarations(..) => {
// Our containing style rule would handle invalidation for us.
},
Namespace(..) => {
// It's not clear what handling changes for this correctly would
// look like.

View File

@ -17,6 +17,7 @@ mod loader;
mod margin_rule;
mod media_rule;
mod namespace_rule;
mod nested_declarations_rule;
pub mod origin;
mod page_rule;
pub mod position_try_rule;
@ -35,7 +36,7 @@ use crate::gecko_bindings::sugar::refptr::RefCounted;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::{bindings, structs};
use crate::parser::{NestingContext, ParserContext};
use crate::properties::PropertyDeclarationBlock;
use crate::properties::{parse_property_declaration_list, PropertyDeclarationBlock};
use crate::shared_lock::{DeepCloneWithLock, Locked};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
@ -63,6 +64,7 @@ pub use self::loader::StylesheetLoader;
pub use self::margin_rule::{MarginRule, MarginRuleType};
pub use self::media_rule::MediaRule;
pub use self::namespace_rule::NamespaceRule;
pub use self::nested_declarations_rule::NestedDeclarationsRule;
pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
pub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors};
pub use self::position_try_rule::PositionTryRule;
@ -346,6 +348,7 @@ pub enum CssRule {
Scope(Arc<ScopeRule>),
StartingStyle(Arc<StartingStyleRule>),
PositionTry(Arc<Locked<PositionTryRule>>),
NestedDeclarations(Arc<Locked<NestedDeclarationsRule>>),
}
impl CssRule {
@ -401,6 +404,9 @@ impl CssRule {
CssRule::PositionTry(ref lock) => {
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
},
CssRule::NestedDeclarations(ref lock) => {
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
},
}
}
}
@ -444,6 +450,8 @@ pub enum CssRuleType {
StartingStyle = 22,
// https://drafts.csswg.org/css-anchor-position-1/#om-position-try
PositionTry = 23,
// https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule
NestedDeclarations = 24,
}
impl CssRuleType {
@ -537,14 +545,13 @@ impl CssRule {
CssRule::Scope(_) => CssRuleType::Scope,
CssRule::StartingStyle(_) => CssRuleType::StartingStyle,
CssRule::PositionTry(_) => CssRuleType::PositionTry,
CssRule::NestedDeclarations(_) => CssRuleType::NestedDeclarations,
}
}
/// Parse a CSS rule.
///
/// Returns a parsed CSS rule and the final state of the parser.
///
/// Input state is None for a nested rule
/// This mostly implements steps 3..7 of https://drafts.csswg.org/cssom/#insert-a-css-rule
pub fn parse(
css: &str,
insert_rule_context: InsertRuleContext,
@ -584,7 +591,7 @@ impl CssRule {
let mut input = Parser::new(&mut input);
// nested rules are in the body state
let mut rule_parser = TopLevelRuleParser {
let mut parser = TopLevelRuleParser {
context,
shared_lock: &shared_lock,
loader,
@ -593,37 +600,43 @@ impl CssRule {
insert_rule_context: Some(insert_rule_context),
allow_import_rules,
declaration_parser_state: Default::default(),
first_declaration_block: Default::default(),
wants_first_declaration_block: false,
error_reporting_state: Default::default(),
rules: Default::default(),
};
match parse_one_rule(&mut input, &mut rule_parser) {
Ok(_) => Ok(rule_parser.rules.pop().unwrap()),
Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)),
if input.try_parse(|input| parse_one_rule(input, &mut parser)).is_ok() {
return Ok(parser.rules.pop().unwrap());
}
let error = parser.dom_error.take().unwrap_or(RulesMutateError::Syntax);
// If new rule is a syntax error, and nested is set, perform the following substeps:
if matches!(error, RulesMutateError::Syntax) && parser.can_parse_declarations() {
let declarations = parse_property_declaration_list(&parser.context, &mut input, &[]);
if !declarations.is_empty() {
return Ok(CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(NestedDeclarationsRule {
block: Arc::new(parser.shared_lock.wrap(declarations)),
source_location: input.current_source_location(),
}))));
}
}
Err(error)
}
}
impl DeepCloneWithLock for CssRule {
/// Deep clones this CssRule.
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
) -> CssRule {
fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> CssRule {
match *self {
CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()),
CssRule::Import(ref arc) => {
let rule = arc
.read_with(guard)
.deep_clone_with_lock(lock, guard);
let rule = arc.read_with(guard).deep_clone_with_lock(lock, guard);
CssRule::Import(Arc::new(lock.wrap(rule)))
},
CssRule::Style(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Style(Arc::new(
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
))
CssRule::Style(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::Container(ref arc) => {
CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard)))
@ -643,9 +656,7 @@ impl DeepCloneWithLock for CssRule {
},
CssRule::Keyframes(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Keyframes(Arc::new(
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
))
CssRule::Keyframes(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::Margin(ref arc) => {
CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard)))
@ -655,9 +666,7 @@ impl DeepCloneWithLock for CssRule {
},
CssRule::Page(ref arc) => {
let rule = arc.read_with(guard);
CssRule::Page(Arc::new(
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
))
CssRule::Page(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::Property(ref arc) => {
// @property rules are immutable, so we don't need any of the `Locked`
@ -679,9 +688,11 @@ impl DeepCloneWithLock for CssRule {
},
CssRule::PositionTry(ref arc) => {
let rule = arc.read_with(guard);
CssRule::PositionTry(Arc::new(
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
))
CssRule::PositionTry(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
},
CssRule::NestedDeclarations(ref arc) => {
let decls = arc.read_with(guard);
CssRule::NestedDeclarations(Arc::new(lock.wrap(decls.clone())))
},
}
}
@ -711,6 +722,7 @@ impl ToCssWithGuard for CssRule {
CssRule::Scope(ref rule) => rule.to_css(guard, dest),
CssRule::StartingStyle(ref rule) => rule.to_css(guard, dest),
CssRule::PositionTry(ref lock) => lock.read_with(guard).to_css(guard, dest),
CssRule::NestedDeclarations(ref lock) => lock.read_with(guard).to_css(guard, dest),
}
}
}

View File

@ -0,0 +1,50 @@
/* 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 nested declarations rule.
//! https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule
use crate::properties::PropertyDeclarationBlock;
use crate::shared_lock::{
DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
};
use crate::str::CssStringWriter;
use cssparser::SourceLocation;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use servo_arc::Arc;
/// A nested declarations rule.
#[derive(Clone, Debug, ToShmem)]
pub struct NestedDeclarationsRule {
/// The declarations.
pub block: Arc<Locked<PropertyDeclarationBlock>>,
/// The source position this rule was found at.
pub source_location: SourceLocation,
}
impl NestedDeclarationsRule {
/// Measure heap usage.
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops)
}
}
impl DeepCloneWithLock for NestedDeclarationsRule {
fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
Self {
block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
source_location: self.source_location.clone(),
}
}
}
impl ToCssWithGuard for NestedDeclarationsRule {
fn to_css(
&self,
guard: &SharedRwLockReadGuard,
dest: &mut CssStringWriter,
) -> std::fmt::Result {
self.block.read_with(guard).to_css(dest)
}
}

View File

@ -23,15 +23,13 @@ use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
use crate::stylesheets::position_try_rule::PositionTryRule;
use crate::stylesheets::scope_rule::{ScopeBounds, ScopeRule};
use crate::stylesheets::starting_style_rule::StartingStyleRule;
use crate::stylesheets::supports_rule::SupportsCondition;
use crate::stylesheets::{
AllowImportRules, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules, DocumentRule,
FontFeatureValuesRule, FontPaletteValuesRule, KeyframesRule, MarginRule, MarginRuleType,
MediaRule, NamespaceRule, PageRule, PageSelectors, RulesMutateError, StyleRule,
StylesheetLoader, SupportsRule,
StylesheetLoader, SupportsRule, StartingStyleRule, NestedDeclarationsRule, PositionTryRule
};
use crate::values::computed::font::FamilyName;
use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
@ -105,6 +103,11 @@ pub struct TopLevelRuleParser<'a, 'i> {
pub insert_rule_context: Option<InsertRuleContext<'a>>,
/// Whether @import rules will be allowed.
pub allow_import_rules: AllowImportRules,
/// Whether to keep declarations into first_declaration_block, rather than turning it into a
/// nested declarations rule.
pub wants_first_declaration_block: bool,
/// The first declaration block, only relevant when wants_first_declaration_block is true.
pub first_declaration_block: PropertyDeclarationBlock,
/// Parser state for declaration blocks in either nested rules or style rules.
pub declaration_parser_state: DeclarationParserState<'i>,
/// State we keep around only for error reporting purposes. Right now that contains just the
@ -137,6 +140,38 @@ impl<'a, 'i> TopLevelRuleParser<'a, 'i> {
self.state
}
/// If we're in a nested state, this returns whether declarations can be parsed. See
/// RuleBodyItemParser::parse_declarations().
#[inline]
pub fn can_parse_declarations(&self) -> bool {
// We also have to check for page rules here because we currently don't
// have a bespoke parser for page rules, and parse them as though they
// are style rules.
self.in_style_or_page_rule()
}
#[inline]
fn in_style_rule(&self) -> bool {
self.context
.nesting_context
.rule_types
.contains(CssRuleType::Style)
}
#[inline]
fn in_page_rule(&self) -> bool {
self.context
.nesting_context
.rule_types
.contains(CssRuleType::Page)
}
#[inline]
fn in_style_or_page_rule(&self) -> bool {
let types = CssRuleTypes::from_bits(CssRuleType::Style.bit() | CssRuleType::Page.bit());
self.context.nesting_context.rule_types.intersects(types)
}
/// Checks whether we can parse a rule that would transition us to
/// `new_state`.
///
@ -458,65 +493,11 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
struct NestedRuleParser<'a, 'i>(TopLevelRuleParser<'a, 'i>);
struct NestedParseResult {
first_declaration_block: PropertyDeclarationBlock,
rules: Vec<CssRule>,
declarations: PropertyDeclarationBlock,
}
impl NestedParseResult {
fn into_rules(
mut self,
shared_lock: &SharedRwLock,
source_location: SourceLocation,
) -> Arc<Locked<CssRules>> {
lazy_static! {
static ref AMPERSAND: SelectorList<SelectorImpl> = {
let list = SelectorList::ampersand();
list.slice()
.iter()
.for_each(|selector| selector.mark_as_intentionally_leaked());
list
};
};
if !self.declarations.is_empty() {
self.rules.insert(
0,
CssRule::Style(Arc::new(shared_lock.wrap(StyleRule {
selectors: AMPERSAND.clone(),
block: Arc::new(shared_lock.wrap(self.declarations)),
rules: None,
source_location,
}))),
)
}
CssRules::new(self.rules, shared_lock)
}
}
impl<'a, 'i> NestedRuleParser<'a, 'i> {
#[inline]
fn in_style_rule(&self) -> bool {
self.context
.nesting_context
.rule_types
.contains(CssRuleType::Style)
}
#[inline]
fn in_page_rule(&self) -> bool {
self.context
.nesting_context
.rule_types
.contains(CssRuleType::Page)
}
#[inline]
fn in_style_or_page_rule(&self) -> bool {
let types = CssRuleTypes::from_bits(CssRuleType::Style.bit() | CssRuleType::Page.bit());
self.context.nesting_context.rule_types.intersects(types)
}
#[inline]
fn parse_relative(&self) -> ParseRelative {
self.context.nesting_context.parse_relative
@ -557,15 +538,29 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
r
}
fn parse_nested_rules(
&mut self,
input: &mut Parser<'i, '_>,
rule_type: CssRuleType,
source_location: SourceLocation,
) -> Arc<Locked<CssRules>> {
let rules = self.parse_nested(input, rule_type, /* wants_first_declaration_block = */ false, source_location).rules;
CssRules::new(rules, &self.shared_lock)
}
fn parse_nested(
&mut self,
input: &mut Parser<'i, '_>,
rule_type: CssRuleType,
wants_first_declaration_block: bool,
source_location: SourceLocation,
) -> NestedParseResult {
debug_assert!(!self.wants_first_declaration_block, "Should've flushed previous declarations");
self.nest_for_rule(rule_type, |parser| {
parser.wants_first_declaration_block = wants_first_declaration_block;
let parse_declarations = parser.parse_declarations();
let mut old_declaration_state = std::mem::take(&mut parser.declaration_parser_state);
let mut rules = std::mem::take(&mut parser.rules);
let mut first_declaration_block = std::mem::take(&mut parser.first_declaration_block);
let mut iter = RuleBodyParser::new(input, parser);
while let Some(result) = iter.next() {
match result {
@ -583,26 +578,20 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
},
}
}
let declarations = if parse_declarations {
let top = &mut **parser;
top.declaration_parser_state
.report_errors_if_needed(&top.context, &top.error_reporting_state);
parser.declaration_parser_state.take_declarations()
} else {
PropertyDeclarationBlock::default()
};
parser.flush_declarations(source_location);
debug_assert!(
!parser.wants_first_declaration_block,
"Flushing declarations should take care of this."
);
debug_assert!(
!parser.declaration_parser_state.has_parsed_declarations(),
"Parsed but didn't consume declarations"
);
std::mem::swap(
&mut parser.declaration_parser_state,
&mut old_declaration_state,
);
std::mem::swap(&mut parser.rules, &mut rules);
std::mem::swap(&mut parser.first_declaration_block, &mut first_declaration_block);
NestedParseResult {
first_declaration_block,
rules,
declarations,
}
})
}
@ -647,6 +636,28 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
fn handle_error_reporting_selectors_post(&mut self) {
self.error_reporting_state.pop();
}
#[inline]
fn flush_declarations(&mut self, source_location: SourceLocation) {
let parser = &mut **self;
let wants_first_declaration_block = parser.wants_first_declaration_block;
parser.wants_first_declaration_block = false;
parser.declaration_parser_state.report_errors_if_needed(&parser.context, &parser.error_reporting_state);
if !parser.declaration_parser_state.has_parsed_declarations() {
return;
}
let declarations = parser.declaration_parser_state.take_declarations();
if wants_first_declaration_block {
debug_assert!(parser.first_declaration_block.is_empty(), "How?");
parser.first_declaration_block = declarations;
} else {
let nested_rule = CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(NestedDeclarationsRule {
block: Arc::new(parser.shared_lock.wrap(declarations)),
source_location,
})));
parser.rules.push(nested_rule);
}
}
}
impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
@ -760,10 +771,12 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
self.dom_error = Some(RulesMutateError::HierarchyRequest);
return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(prelude.name().into())));
}
let source_location = start.source_location();
self.flush_declarations(source_location);
let rule = match prelude {
AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
CssRule::FontFace(Arc::new(p.shared_lock.wrap(
parse_font_face_block(&p.context, input, start.source_location()).into(),
parse_font_face_block(&p.context, input, source_location).into(),
)))
}),
AtRulePrelude::FontFeatureValues(family_names) => {
@ -772,7 +785,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
&p.context,
input,
family_names,
start.source_location(),
source_location,
)))
})
},
@ -782,35 +795,29 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
&p.context,
input,
name,
start.source_location(),
source_location,
)))
})
},
AtRulePrelude::CounterStyle(name) => {
let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
parse_counter_style_body(name, &p.context, input, start.source_location())
parse_counter_style_body(name, &p.context, input, source_location)
})?;
CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
},
AtRulePrelude::Media(media_queries) => {
let source_location = start.source_location();
CssRule::Media(Arc::new(MediaRule {
media_queries,
rules: self
.parse_nested(input, CssRuleType::Media)
.into_rules(self.shared_lock, source_location),
rules: self.parse_nested_rules(input, CssRuleType::Media, source_location),
source_location,
}))
},
AtRulePrelude::Supports(condition) => {
let enabled =
self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
let source_location = start.source_location();
CssRule::Supports(Arc::new(SupportsRule {
condition,
rules: self
.parse_nested(input, CssRuleType::Supports)
.into_rules(self.shared_lock, source_location),
rules: self.parse_nested_rules(input, CssRuleType::Supports, source_location),
enabled,
source_location,
}))
@ -822,12 +829,11 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
name,
keyframes: parse_keyframe_list(&mut top.context, input, top.shared_lock),
vendor_prefix,
source_location: start.source_location(),
source_location,
})))
})
},
AtRulePrelude::Page(selectors) => {
let source_location = start.source_location();
let page_rule = if !static_prefs::pref!("layout.css.margin-rules.enabled") {
let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
parse_property_declaration_list(&p.context, input, &[])
@ -839,11 +845,11 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
source_location,
}
} else {
let result = self.parse_nested(input, CssRuleType::Page);
let result = self.parse_nested(input, CssRuleType::Page, true, source_location);
PageRule {
selectors,
rules: CssRules::new(result.rules, self.shared_lock),
block: Arc::new(self.shared_lock.wrap(result.declarations)),
block: Arc::new(self.shared_lock.wrap(result.first_declaration_block)),
source_location,
}
};
@ -851,19 +857,16 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
},
AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| {
let rule_data =
parse_property_block(&p.context, input, name, start.source_location())?;
parse_property_block(&p.context, input, name, source_location)?;
Ok::<CssRule, ParseError<'i>>(CssRule::Property(Arc::new(rule_data)))
})?,
AtRulePrelude::Document(condition) => {
if !cfg!(feature = "gecko") {
unreachable!()
}
let source_location = start.source_location();
CssRule::Document(Arc::new(DocumentRule {
condition,
rules: self
.parse_nested(input, CssRuleType::Document)
.into_rules(self.shared_lock, source_location),
rules: self.parse_nested_rules(input, CssRuleType::Document, source_location),
source_location,
}))
},
@ -871,9 +874,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
let source_location = start.source_location();
CssRule::Container(Arc::new(ContainerRule {
condition,
rules: self
.parse_nested(input, CssRuleType::Container)
.into_rules(self.shared_lock, source_location),
rules: self.parse_nested_rules(input, CssRuleType::Container, source_location),
source_location,
}))
},
@ -882,12 +883,9 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
0 | 1 => names.into_iter().next(),
_ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
};
let source_location = start.source_location();
CssRule::LayerBlock(Arc::new(LayerBlockRule {
name,
rules: self
.parse_nested(input, CssRuleType::LayerBlock)
.into_rules(self.shared_lock, source_location),
rules: self.parse_nested_rules(input, CssRuleType::LayerBlock, source_location),
source_location,
}))
},
@ -898,7 +896,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
CssRule::Margin(Arc::new(MarginRule {
rule_type,
block: Arc::new(self.shared_lock.wrap(declarations)),
source_location: start.source_location(),
source_location,
}))
},
AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => {
@ -906,21 +904,15 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
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),
rules: self.parse_nested_rules(input, CssRuleType::Scope, source_location),
source_location,
}))
},
AtRulePrelude::StartingStyle => {
let source_location = start.source_location();
CssRule::StartingStyle(Arc::new(StartingStyleRule {
rules: self
.parse_nested(input, CssRuleType::StartingStyle)
.into_rules(self.shared_lock, source_location),
rules: self.parse_nested_rules(input, CssRuleType::StartingStyle, source_location),
source_location,
}))
},
@ -931,7 +923,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
CssRule::PositionTry(Arc::new(self.shared_lock.wrap(PositionTryRule {
name,
block: Arc::new(self.shared_lock.wrap(declarations)),
source_location: start.source_location(),
source_location,
})))
},
};
@ -948,6 +940,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
if self.in_style_rule() {
return Err(());
}
let source_location = start.source_location();
let rule = match prelude {
AtRulePrelude::Layer(names) => {
if names.is_empty() {
@ -955,11 +948,12 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
}
CssRule::LayerStatement(Arc::new(LayerStatementRule {
names,
source_location: start.source_location(),
source_location,
}))
},
_ => return Err(()),
};
self.flush_declarations(source_location);
self.rules.push(rule);
Ok(())
}
@ -989,15 +983,17 @@ impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
start: &ParserState,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> {
let source_location = start.source_location();
let reporting_errors = self.context.error_reporting_enabled();
if reporting_errors {
self.handle_error_reporting_selectors_pre(start, &selectors);
}
let result = self.parse_nested(input, CssRuleType::Style);
self.flush_declarations(source_location);
let result = self.parse_nested(input, CssRuleType::Style, true, source_location);
if reporting_errors {
self.handle_error_reporting_selectors_post();
}
let block = Arc::new(self.shared_lock.wrap(result.declarations));
let block = Arc::new(self.shared_lock.wrap(result.first_declaration_block));
let top = &mut **self;
top.rules
.push(CssRule::Style(Arc::new(top.shared_lock.wrap(StyleRule {
@ -1008,7 +1004,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
} else {
Some(CssRules::new(result.rules, top.shared_lock))
},
source_location: start.source_location(),
source_location,
}))));
Ok(())
}
@ -1036,9 +1032,6 @@ impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> for NestedRuleP
/// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse
/// raw declarations there.
fn parse_declarations(&self) -> bool {
// We also have to check for page rules here because we currently don't
// have a bespoke parser for page rules, and parse them as though they
// are style rules.
self.in_style_or_page_rule()
self.can_parse_declarations()
}
}

View File

@ -71,6 +71,7 @@ where
CssRule::LayerStatement(_) |
CssRule::FontFeatureValues(_) |
CssRule::FontPaletteValues(_) |
CssRule::NestedDeclarations(_) |
CssRule::PositionTry(_) => None,
CssRule::Page(ref page_rule) => {
let page_rule = page_rule.read_with(guard);

View File

@ -354,6 +354,7 @@ impl SanitizationKind {
CssRule::FontFace(..) |
CssRule::Namespace(..) |
CssRule::Style(..) |
CssRule::NestedDeclarations(..) |
CssRule::PositionTry(..) => true,
CssRule::Keyframes(..) |
@ -463,6 +464,8 @@ impl Stylesheet {
insert_rule_context: None,
allow_import_rules,
declaration_parser_state: Default::default(),
first_declaration_block: Default::default(),
wants_first_declaration_block: false,
error_reporting_state: Default::default(),
rules: Vec::new(),
};

View File

@ -50,9 +50,8 @@ use crate::stylesheets::{
};
use crate::stylesheets::{
CssRule, EffectiveRulesIterator, Origin, OriginSet, PagePseudoClassFlags, PageRule, PerOrigin,
PerOriginIter,
PerOriginIter, StylesheetContents, StylesheetInDocument,
};
use crate::stylesheets::{StyleRule, StylesheetContents, StylesheetInDocument};
use crate::values::{computed, AtomIdent};
use crate::AllocErr;
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
@ -632,6 +631,18 @@ impl ScopeMatchesShadowHost {
}
}
/// Nested declarations have effectively two behaviors:
/// * Inside style rules (where they behave as the containing selector).
/// * Inside @scope (where they behave as :where(:scope)).
/// It is a bit unfortunate ideally we wouldn't need this, because scope also pushes to the
/// ancestor_selector_lists, but the behavior isn't quite the same as wrapping in `&`, see
/// https://github.com/w3c/csswg-drafts/issues/10431
#[derive(Copy, Clone)]
enum NestedDeclarationsContext {
Style,
Scope,
}
/// A struct containing state from ancestor rules like @layer / @import /
/// @container / nesting / @scope.
struct ContainingRuleState {
@ -642,6 +653,7 @@ struct ContainingRuleState {
scope_condition_id: ScopeConditionId,
scope_matches_shadow_host: ScopeMatchesShadowHost,
ancestor_selector_lists: SmallVec<[SelectorList<SelectorImpl>; 2]>,
nested_declarations_context: NestedDeclarationsContext,
}
impl Default for ContainingRuleState {
@ -654,6 +666,7 @@ impl Default for ContainingRuleState {
ancestor_selector_lists: Default::default(),
scope_condition_id: ScopeConditionId::none(),
scope_matches_shadow_host: Default::default(),
nested_declarations_context: NestedDeclarationsContext::Style,
}
}
}
@ -666,6 +679,7 @@ struct SavedContainingRuleState {
in_starting_style: bool,
scope_condition_id: ScopeConditionId,
scope_matches_shadow_host: ScopeMatchesShadowHost,
nested_declarations_context: NestedDeclarationsContext,
}
impl ContainingRuleState {
@ -678,6 +692,7 @@ impl ContainingRuleState {
in_starting_style: self.in_starting_style,
scope_condition_id: self.scope_condition_id,
scope_matches_shadow_host: self.scope_matches_shadow_host,
nested_declarations_context: self.nested_declarations_context,
}
}
@ -692,9 +707,12 @@ impl ContainingRuleState {
self.in_starting_style = saved.in_starting_style;
self.scope_condition_id = saved.scope_condition_id;
self.scope_matches_shadow_host = saved.scope_matches_shadow_host;
self.nested_declarations_context = saved.nested_declarations_context;
}
}
type ReplacedSelectors = SmallVec<[Selector<SelectorImpl>; 4]>;
impl Stylist {
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
/// If more members are added here, think about whether they should
@ -2695,13 +2713,12 @@ pub struct CascadeData {
num_declarations: usize,
}
// TODO(emilio, dshin): According to https://github.com/w3c/csswg-drafts/issues/10431 other browsers don't quite do this.
fn parent_selector_for_scope(parent: Option<&SelectorList<SelectorImpl>>) -> &SelectorList<SelectorImpl> {
lazy_static! {
static ref SCOPE: SelectorList<SelectorImpl> = {
let list = SelectorList::scope();
list.slice()
.iter()
.for_each(|selector| selector.mark_as_intentionally_leaked());
list.mark_as_intentionally_leaked();
list
};
};
@ -3220,6 +3237,169 @@ impl CascadeData {
}
}
fn add_styles(
&mut self,
style_source: StyleSource,
selectors: &SelectorList<SelectorImpl>,
declarations: &Arc<Locked<PropertyDeclarationBlock>>,
ancestor_selectors: Option<&SelectorList<SelectorImpl>>,
containing_rule_state: &ContainingRuleState,
mut replaced_selectors: Option<&mut ReplacedSelectors>,
guard: &SharedRwLockReadGuard,
rebuild_kind: SheetRebuildKind,
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
quirks_mode: QuirksMode,
) -> Result<(), AllocErr> {
self.num_declarations += declarations.read_with(guard).len();
for selector in selectors.slice() {
self.num_selectors += 1;
let pseudo_element = selector.pseudo_element();
if let Some(pseudo) = pseudo_element {
if pseudo.is_precomputed() {
debug_assert!(selector.is_universal());
debug_assert!(ancestor_selectors.is_none());
debug_assert_eq!(containing_rule_state.layer_id, LayerId::root());
// Because we precompute pseudos, we cannot possibly calculate scope proximity.
debug_assert_eq!(
containing_rule_state.scope_condition_id,
ScopeConditionId::none()
);
precomputed_pseudo_element_decls
.as_mut()
.expect("Expected precomputed declarations for the UA level")
.get_or_insert_with(pseudo, Vec::new)
.push(ApplicableDeclarationBlock::new(
style_source.clone(),
self.rules_source_order,
CascadeLevel::UANormal,
selector.specificity(),
LayerOrder::root(),
ScopeProximity::infinity(),
));
continue;
}
if pseudo.is_unknown_webkit_pseudo_element() {
continue;
}
}
let selector = match ancestor_selectors {
Some(ref s) => selector.replace_parent_selector(&s),
None => selector.clone(),
};
let hashes = AncestorHashes::new(&selector, quirks_mode);
let rule = Rule::new(
selector,
hashes,
style_source.clone(),
self.rules_source_order,
containing_rule_state.layer_id,
containing_rule_state.container_condition_id,
containing_rule_state.in_starting_style,
containing_rule_state.scope_condition_id,
);
if let Some(ref mut replaced_selectors) = replaced_selectors {
replaced_selectors.push(rule.selector.clone())
}
if rebuild_kind.should_rebuild_invalidation() {
note_selector_for_invalidation(
&rule.selector,
quirks_mode,
&mut self.invalidation_map,
&mut self.relative_selector_invalidation_map,
)?;
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: &mut needs_revalidation,
passed_rightmost_selector: false,
in_selector_list_of: SelectorListKind::default(),
mapped_ids: &mut self.mapped_ids,
nth_of_mapped_ids: &mut self.nth_of_mapped_ids,
attribute_dependencies: &mut self.attribute_dependencies,
nth_of_class_dependencies: &mut self.nth_of_class_dependencies,
nth_of_attribute_dependencies: &mut self
.nth_of_attribute_dependencies,
nth_of_custom_state_dependencies: &mut self
.nth_of_custom_state_dependencies,
state_dependencies: &mut self.state_dependencies,
nth_of_state_dependencies: &mut self.nth_of_state_dependencies,
document_state_dependencies: &mut self.document_state_dependencies,
};
rule.selector.visit(&mut visitor);
if needs_revalidation {
self.selectors_for_cache_revalidation.insert(
RevalidationSelectorAndHashes::new(
rule.selector.clone(),
rule.hashes.clone(),
),
quirks_mode,
)?;
}
}
// Part is special, since given it doesn't have any
// selectors inside, it's not worth using a whole
// SelectorMap for it.
if let Some(parts) = rule.selector.parts() {
// ::part() has all semantics, so we just need to
// put any of them in the selector map.
//
// We choose the last one quite arbitrarily,
// expecting it's slightly more likely to be more
// specific.
let map = self
.part_rules
.get_or_insert_with(|| Box::new(Default::default()))
.for_insertion(pseudo_element);
map.try_reserve(1)?;
let vec = map.entry(parts.last().unwrap().clone().0).or_default();
vec.try_reserve(1)?;
vec.push(rule);
} else {
// NOTE(emilio): It's fine to look at :host and then at
// ::slotted(..), since :host::slotted(..) could never
// possibly match, as <slot> is not a valid shadow host.
// :scope may match featureless shadow host if the scope
// root is the shadow root.
// See https://github.com/w3c/csswg-drafts/issues/9025
let potentially_matches_featureless_host = rule
.selector
.matches_featureless_host_selector_or_pseudo_element();
let matches_featureless_host = if potentially_matches_featureless_host
.intersects(FeaturelessHostMatches::FOR_HOST)
{
true
} else if potentially_matches_featureless_host
.intersects(FeaturelessHostMatches::FOR_SCOPE)
{
containing_rule_state.scope_matches_shadow_host ==
ScopeMatchesShadowHost::Yes
} else {
false
};
let rules = if matches_featureless_host {
self.featureless_host_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else if rule.selector.is_slotted() {
self.slotted_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else {
&mut self.normal_rules
}
.for_insertion(pseudo_element);
rules.insert(rule, quirks_mode)?;
}
}
self.rules_source_order += 1;
Ok(())
}
fn add_rule_list<S>(
&mut self,
rules: std::slice::Iter<CssRule>,
@ -3243,164 +3423,26 @@ impl CascadeData {
match *rule {
CssRule::Style(ref locked) => {
let style_rule = locked.read_with(guard);
self.num_declarations += style_rule.block.read_with(&guard).len();
let has_nested_rules = style_rule.rules.is_some();
let mut ancestor_selectors =
containing_rule_state.ancestor_selector_lists.last_mut();
let mut replaced_selectors = SmallVec::<[Selector<SelectorImpl>; 4]>::new();
let collect_replaced_selectors =
has_nested_rules && ancestor_selectors.is_some();
for selector in style_rule.selectors.slice() {
self.num_selectors += 1;
let pseudo_element = selector.pseudo_element();
if let Some(pseudo) = pseudo_element {
if pseudo.is_precomputed() {
debug_assert!(selector.is_universal());
debug_assert!(ancestor_selectors.is_none());
debug_assert!(!has_nested_rules);
debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent);
debug_assert_eq!(containing_rule_state.layer_id, LayerId::root());
// Because we precompute pseudos, we cannot possibly calculate scope proximity.
debug_assert_eq!(
containing_rule_state.scope_condition_id,
ScopeConditionId::none()
);
precomputed_pseudo_element_decls
.as_mut()
.expect("Expected precomputed declarations for the UA level")
.get_or_insert_with(pseudo, Vec::new)
.push(ApplicableDeclarationBlock::new(
StyleSource::from_rule(locked.clone()),
self.rules_source_order,
CascadeLevel::UANormal,
selector.specificity(),
LayerOrder::root(),
ScopeProximity::infinity(),
));
continue;
}
if pseudo.is_unknown_webkit_pseudo_element() {
continue;
}
}
let selector = match ancestor_selectors {
Some(ref mut s) => selector.replace_parent_selector(&s),
None => selector.clone(),
};
let hashes = AncestorHashes::new(&selector, quirks_mode);
let rule = Rule::new(
selector,
hashes,
locked.clone(),
self.rules_source_order,
containing_rule_state.layer_id,
containing_rule_state.container_condition_id,
containing_rule_state.in_starting_style,
containing_rule_state.scope_condition_id,
);
let mut replaced_selectors = ReplacedSelectors::new();
let ancestor_selectors = containing_rule_state.ancestor_selector_lists.last();
let collect_replaced_selectors = has_nested_rules && ancestor_selectors.is_some();
self.add_styles(
StyleSource::from_rule(locked.clone()),
&style_rule.selectors,
&style_rule.block,
ancestor_selectors,
&containing_rule_state,
if collect_replaced_selectors {
replaced_selectors.push(rule.selector.clone())
}
if rebuild_kind.should_rebuild_invalidation() {
note_selector_for_invalidation(
&rule.selector,
quirks_mode,
&mut self.invalidation_map,
&mut self.relative_selector_invalidation_map,
)?;
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: &mut needs_revalidation,
passed_rightmost_selector: false,
in_selector_list_of: SelectorListKind::default(),
mapped_ids: &mut self.mapped_ids,
nth_of_mapped_ids: &mut self.nth_of_mapped_ids,
attribute_dependencies: &mut self.attribute_dependencies,
nth_of_class_dependencies: &mut self.nth_of_class_dependencies,
nth_of_attribute_dependencies: &mut self
.nth_of_attribute_dependencies,
nth_of_custom_state_dependencies: &mut self
.nth_of_custom_state_dependencies,
state_dependencies: &mut self.state_dependencies,
nth_of_state_dependencies: &mut self.nth_of_state_dependencies,
document_state_dependencies: &mut self.document_state_dependencies,
};
rule.selector.visit(&mut visitor);
if needs_revalidation {
self.selectors_for_cache_revalidation.insert(
RevalidationSelectorAndHashes::new(
rule.selector.clone(),
rule.hashes.clone(),
),
quirks_mode,
)?;
}
}
// Part is special, since given it doesn't have any
// selectors inside, it's not worth using a whole
// SelectorMap for it.
if let Some(parts) = rule.selector.parts() {
// ::part() has all semantics, so we just need to
// put any of them in the selector map.
//
// We choose the last one quite arbitrarily,
// expecting it's slightly more likely to be more
// specific.
let map = self
.part_rules
.get_or_insert_with(|| Box::new(Default::default()))
.for_insertion(pseudo_element);
map.try_reserve(1)?;
let vec = map.entry(parts.last().unwrap().clone().0).or_default();
vec.try_reserve(1)?;
vec.push(rule);
Some(&mut replaced_selectors)
} else {
// NOTE(emilio): It's fine to look at :host and then at
// ::slotted(..), since :host::slotted(..) could never
// possibly match, as <slot> is not a valid shadow host.
// :scope may match featureless shadow host if the scope
// root is the shadow root.
// See https://github.com/w3c/csswg-drafts/issues/9025
let potentially_matches_featureless_host = rule
.selector
.matches_featureless_host_selector_or_pseudo_element();
let matches_featureless_host = if potentially_matches_featureless_host
.intersects(FeaturelessHostMatches::FOR_HOST)
{
true
} else if potentially_matches_featureless_host
.intersects(FeaturelessHostMatches::FOR_SCOPE)
{
containing_rule_state.scope_matches_shadow_host ==
ScopeMatchesShadowHost::Yes
} else {
false
};
let rules = if matches_featureless_host {
self.featureless_host_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else if rule.selector.is_slotted() {
self.slotted_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else {
&mut self.normal_rules
}
.for_insertion(pseudo_element);
rules.insert(rule, quirks_mode)?;
}
}
self.rules_source_order += 1;
handled = true;
None
},
guard,
rebuild_kind,
precomputed_pseudo_element_decls.as_deref_mut(),
quirks_mode,
)?;
if has_nested_rules {
handled = false;
list_for_nested_rules = Some(if collect_replaced_selectors {
@ -3410,6 +3452,36 @@ impl CascadeData {
});
}
},
CssRule::NestedDeclarations(ref rule) => {
lazy_static! {
static ref IMPLICIT_SCOPE: SelectorList<SelectorImpl> = {
let list = SelectorList::implicit_scope();
list.mark_as_intentionally_leaked();
list
};
};
if let Some(ref ancestor_selectors) = containing_rule_state.ancestor_selector_lists.last() {
let decls = &rule.read_with(guard).block;
let selectors = match containing_rule_state.nested_declarations_context {
NestedDeclarationsContext::Style => ancestor_selectors,
NestedDeclarationsContext::Scope => &*IMPLICIT_SCOPE,
};
self.add_styles(
StyleSource::from_declarations(decls.clone()),
selectors,
decls,
/* ancestor_selectors = */ None,
&containing_rule_state,
/* replaced_selectors = */ None,
guard,
// We don't need to rebuild invalidation data, since our ancestor style
// rule would've done this.
SheetRebuildKind::CascadeOnly,
precomputed_pseudo_element_decls.as_deref_mut(),
quirks_mode,
)?;
}
},
CssRule::Keyframes(ref keyframes_rule) => {
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
let keyframes_rule = keyframes_rule.read_with(guard);
@ -3594,6 +3666,7 @@ impl CascadeData {
}
},
CssRule::Style(..) => {
containing_rule_state.nested_declarations_context = NestedDeclarationsContext::Style;
if let Some(s) = list_for_nested_rules {
containing_rule_state.ancestor_selector_lists.push(s);
}
@ -3610,6 +3683,7 @@ impl CascadeData {
containing_rule_state.in_starting_style = true;
},
CssRule::Scope(ref rule) => {
containing_rule_state.nested_declarations_context = NestedDeclarationsContext::Scope;
let id = ScopeConditionId(self.scope_conditions.len() as u16);
let mut matches_shadow_host = false;
let implicit_scope_root = if let Some(start) = rule.bounds.start.as_ref() {
@ -3649,11 +3723,12 @@ impl CascadeData {
None => selector.clone(),
}
});
let parent = parent_selector_for_scope(start.as_ref());
let end = rule.bounds
.end
.as_ref()
.map(|selector| selector.replace_parent_selector(parent_selector_for_scope(start.as_ref())));
containing_rule_state.ancestor_selector_lists.push(parent_selector_for_scope(start.as_ref()).clone());
.map(|selector| selector.replace_parent_selector(parent));
containing_rule_state.ancestor_selector_lists.push(parent.clone());
ScopeBoundsWithHashes::new(quirks_mode, start, end)
};
@ -3776,6 +3851,7 @@ impl CascadeData {
while let Some(rule) = iter.next() {
match *rule {
CssRule::Style(..) |
CssRule::NestedDeclarations(..) |
CssRule::Namespace(..) |
CssRule::FontFace(..) |
CssRule::Container(..) |
@ -4015,12 +4091,8 @@ pub struct Rule {
pub scope_condition_id: ScopeConditionId,
/// The actual style rule.
#[cfg_attr(
feature = "gecko",
ignore_malloc_size_of = "Secondary ref. Primary ref is in StyleRule under Stylesheet."
)]
#[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
pub style_rule: Arc<Locked<StyleRule>>,
#[ignore_malloc_size_of = "Secondary ref. Primary ref is in StyleRule under Stylesheet." ]
pub style_source: StyleSource,
}
impl SelectorMapEntry for Rule {
@ -4043,9 +4115,8 @@ impl Rule {
cascade_data: &CascadeData,
scope_proximity: ScopeProximity,
) -> ApplicableDeclarationBlock {
let source = StyleSource::from_rule(self.style_rule.clone());
ApplicableDeclarationBlock::new(
source,
self.style_source.clone(),
self.source_order,
level,
self.specificity(),
@ -4058,17 +4129,17 @@ impl Rule {
pub fn new(
selector: Selector<SelectorImpl>,
hashes: AncestorHashes,
style_rule: Arc<Locked<StyleRule>>,
style_source: StyleSource,
source_order: u32,
layer_id: LayerId,
container_condition_id: ContainerConditionId,
is_starting_style: bool,
scope_condition_id: ScopeConditionId,
) -> Self {
Rule {
Self {
selector,
hashes,
style_rule,
style_source,
source_order,
layer_id,
container_condition_id,

View File

@ -37,7 +37,7 @@ use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent,
use style::gecko::arc_types::{
LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList, LockedPageRule,
LockedPositionTryRule, LockedStyleRule,
LockedPositionTryRule, LockedNestedDeclarationsRule, LockedStyleRule,
};
use style::gecko::data::{
AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
@ -135,10 +135,10 @@ use style::stylesheets::{
AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRuleTypes,
CssRules, CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule,
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
MarginRule, MediaRule, NamespaceRule, Origin, OriginSet, PagePseudoClassFlags, PageRule,
PositionTryRule, PropertyRule, SanitizationData, SanitizationKind, ScopeRule,
StartingStyleRule, StyleRule, StylesheetContents, StylesheetLoader as StyleStylesheetLoader,
SupportsRule, UrlExtraData,
MarginRule, MediaRule, NamespaceRule, NestedDeclarationsRule, Origin, OriginSet,
PagePseudoClassFlags, PageRule, PositionTryRule, PropertyRule, SanitizationData,
SanitizationKind, ScopeRule, StartingStyleRule, StyleRule, StylesheetContents,
StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
};
use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
use style::thread_state;
@ -2585,6 +2585,30 @@ impl_basic_rule_funcs! { (PositionTry, PositionTryRule, Locked<PositionTryRule>)
changed: Servo_StyleSet_PositionTryRuleChanged,
}
impl_basic_rule_funcs! { (NestedDeclarations, NestedDeclarationsRule, Locked<NestedDeclarationsRule>),
getter: Servo_CssRules_GetNestedDeclarationsRuleAt,
debug: Servo_NestedDeclarationsRule_Debug,
to_css: Servo_NestedDeclarationsRule_GetCssText,
changed: Servo_StyleSet_NestedDeclarationsRuleChanged,
}
#[no_mangle]
pub extern "C" fn Servo_NestedDeclarationsRule_GetStyle(
rule: &LockedNestedDeclarationsRule,
) -> Strong<LockedDeclarationBlock> {
read_locked_arc(rule, |rule: &NestedDeclarationsRule| rule.block.clone().into())
}
#[no_mangle]
pub extern "C" fn Servo_NestedDeclarationsRule_SetStyle(
rule: &LockedNestedDeclarationsRule,
declarations: &LockedDeclarationBlock,
) {
write_locked_arc(rule, |rule: &mut NestedDeclarationsRule| {
rule.block = unsafe { Arc::from_raw_addrefed(declarations) };
})
}
#[no_mangle]
pub extern "C" fn Servo_StyleRule_GetStyle(
rule: &LockedStyleRule,

View File

@ -0,0 +1 @@
prefs: [layout.css.at-scope.enabled:true]

View File

@ -1,24 +0,0 @@
[nested-declarations-cssom.html]
[Trailing declarations]
expected: FAIL
[Mixed declarations]
expected: FAIL
[CSSNestedDeclarations.style]
expected: FAIL
[Nested group rule]
expected: FAIL
[Nested @scope rule]
expected: FAIL
[Inner rule starting with an ident]
expected: FAIL
[Inserting a CSSNestedDeclaration rule into style rule]
expected: FAIL
[Inserting a CSSNestedDeclaration rule into nested group rule]
expected: FAIL

View File

@ -1,21 +1,3 @@
[nested-declarations-matching.html]
[Trailing declarations apply after any preceding rules]
expected: FAIL
[Trailing declarations apply after any preceding rules (no leading)]
expected: FAIL
[Trailing declarations apply after any preceding rules (multiple)]
expected: FAIL
[Nested declarations rule has top-level specificity behavior (max matching)]
expected: FAIL
[Bare declartaion in nested grouping rule can match pseudo-element]
expected: FAIL
[Nested group rules have top-level specificity behavior]
expected: FAIL
[Nested declarations rule responds to parent selector text change]
expected: FAIL

View File

@ -1,2 +0,0 @@
[nesting-basic.html]
expected: FAIL

View File

@ -1,12 +0,0 @@
[serialize-group-rules-with-decls.html]
[Mixed declarations/rules are on two lines.]
expected: FAIL
[Implicit rule is serialized]
expected: FAIL
[Implicit like rule after decls]
expected: FAIL
[Implicit like rule after decls, missing closing braces]
expected: FAIL

View File

@ -1,3 +0,0 @@
[custom-property-rule-ambiguity.html]
[Nested rule that looks like a custom property declaration]
expected: FAIL

View File

@ -113,19 +113,24 @@
`);
assert_equals(s.cssRules.length, 1);
let outer = s.cssRules[0];
assert_equals(outer.cssRules.length, 2);
if (window.CSSScopeRule) {
assert_equals(outer.cssRules.length, 2);
// @scope
let scope = outer.cssRules[0];
assert_equals(scope.cssRules.length, 3);
assert_true(scope.cssRules[0] instanceof CSSNestedDeclarations);
assert_equals(scope.cssRules[0].cssText, `--x: 1; --y: 1;`);
assert_equals(scope.cssRules[1].cssText, `.b { }`); // Implicit :scope here.
assert_true(scope.cssRules[2] instanceof CSSNestedDeclarations);
assert_equals(scope.cssRules[2].cssText, `--z: 1;`);
// @scope
let scope = outer.cssRules[0];
assert_true(scope instanceof CSSScopeRule);
assert_equals(scope.cssRules.length, 3);
assert_true(scope.cssRules[0] instanceof CSSNestedDeclarations);
assert_equals(scope.cssRules[0].cssText, `--x: 1; --y: 1;`);
assert_equals(scope.cssRules[1].cssText, `.b { }`); // Implicit :scope here.
assert_true(scope.cssRules[2] instanceof CSSNestedDeclarations);
assert_equals(scope.cssRules[2].cssText, `--z: 1;`);
assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
} else {
assert_equals(outer.cssRules.length, 0);
}
}, 'Nested @scope rule');
test(() => {