mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
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:
parent
dda565a838
commit
ba4e92b748
@ -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 },
|
||||
|
14
dom/webidl/CSSNestedDeclarations.webidl
Normal file
14
dom/webidl/CSSNestedDeclarations.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-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;
|
||||
};
|
@ -506,6 +506,7 @@ WEBIDL_FILES = [
|
||||
"CSSMediaRule.webidl",
|
||||
"CSSMozDocumentRule.webidl",
|
||||
"CSSNamespaceRule.webidl",
|
||||
"CSSNestedDeclarations.webidl",
|
||||
"CSSPageRule.webidl",
|
||||
"CSSPositionTryRule.webidl",
|
||||
"CSSPropertyRule.webidl",
|
||||
|
@ -474,6 +474,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
|
||||
case StyleCssRuleType::Scope:
|
||||
case StyleCssRuleType::StartingStyle:
|
||||
case StyleCssRuleType::PositionTry:
|
||||
case StyleCssRuleType::NestedDeclarations:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
169
layout/style/CSSNestedDeclarations.cpp
Normal file
169
layout/style/CSSNestedDeclarations.cpp
Normal 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
|
114
layout/style/CSSNestedDeclarations.h
Normal file
114
layout/style/CSSNestedDeclarations.h
Normal 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
|
@ -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) {
|
||||
|
@ -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>);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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() {
|
||||
|
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(),
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
1
testing/web-platform/meta/css/css-nesting/__dir__.ini
Normal file
1
testing/web-platform/meta/css/css-nesting/__dir__.ini
Normal file
@ -0,0 +1 @@
|
||||
prefs: [layout.css.at-scope.enabled:true]
|
@ -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
|
@ -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
|
||||
|
@ -1,2 +0,0 @@
|
||||
[nesting-basic.html]
|
||||
expected: FAIL
|
@ -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
|
@ -1,3 +0,0 @@
|
||||
[custom-property-rule-ambiguity.html]
|
||||
[Nested rule that looks like a custom property declaration]
|
||||
expected: FAIL
|
@ -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(() => {
|
||||
|
Loading…
Reference in New Issue
Block a user