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:
@@ -325,6 +325,8 @@ let interfaceNamesInGlobalScope = [
|
|||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
{ name: "CSSNamespaceRule", insecureContext: true },
|
{ name: "CSSNamespaceRule", insecureContext: true },
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// 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 },
|
{ name: "CSSPageDescriptors", insecureContext: true },
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||||
{ name: "CSSPageRule", insecureContext: true },
|
{ 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",
|
"CSSMediaRule.webidl",
|
||||||
"CSSMozDocumentRule.webidl",
|
"CSSMozDocumentRule.webidl",
|
||||||
"CSSNamespaceRule.webidl",
|
"CSSNamespaceRule.webidl",
|
||||||
|
"CSSNestedDeclarations.webidl",
|
||||||
"CSSPageRule.webidl",
|
"CSSPageRule.webidl",
|
||||||
"CSSPositionTryRule.webidl",
|
"CSSPositionTryRule.webidl",
|
||||||
"CSSPropertyRule.webidl",
|
"CSSPropertyRule.webidl",
|
||||||
|
|||||||
@@ -474,6 +474,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
|
|||||||
case StyleCssRuleType::Scope:
|
case StyleCssRuleType::Scope:
|
||||||
case StyleCssRuleType::StartingStyle:
|
case StyleCssRuleType::StartingStyle:
|
||||||
case StyleCssRuleType::PositionTry:
|
case StyleCssRuleType::PositionTry:
|
||||||
|
case StyleCssRuleType::NestedDeclarations:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "mozilla/IntegerRange.h"
|
#include "mozilla/IntegerRange.h"
|
||||||
#include "mozilla/ServoStyleSet.h"
|
#include "mozilla/ServoStyleSet.h"
|
||||||
#include "mozilla/StyleSheetInlines.h"
|
#include "mozilla/StyleSheetInlines.h"
|
||||||
#include "nsStyleSheetService.h"
|
|
||||||
|
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
@@ -81,6 +80,7 @@ void ServoStyleRuleMap::RuleRemoved(StyleSheet& aStyleSheet,
|
|||||||
|
|
||||||
switch (aStyleRule.Type()) {
|
switch (aStyleRule.Type()) {
|
||||||
case StyleCssRuleType::Style:
|
case StyleCssRuleType::Style:
|
||||||
|
case StyleCssRuleType::NestedDeclarations:
|
||||||
case StyleCssRuleType::Import:
|
case StyleCssRuleType::Import:
|
||||||
case StyleCssRuleType::Media:
|
case StyleCssRuleType::Media:
|
||||||
case StyleCssRuleType::Supports:
|
case StyleCssRuleType::Supports:
|
||||||
@@ -153,6 +153,7 @@ void ServoStyleRuleMap::FillTableFromRule(css::Rule& aRule) {
|
|||||||
case StyleCssRuleType::FontFeatureValues:
|
case StyleCssRuleType::FontFeatureValues:
|
||||||
case StyleCssRuleType::FontPaletteValues:
|
case StyleCssRuleType::FontPaletteValues:
|
||||||
case StyleCssRuleType::PositionTry:
|
case StyleCssRuleType::PositionTry:
|
||||||
|
case StyleCssRuleType::NestedDeclarations:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
@starting-style {
|
@starting-style {
|
||||||
opacity: 0;
|
& {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +28,14 @@
|
|||||||
background-color: tomato;
|
background-color: tomato;
|
||||||
|
|
||||||
@starting-style {
|
@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>
|
</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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/dom/CSSStyleRule.h"
|
#include "mozilla/dom/CSSStyleRule.h"
|
||||||
|
#include "mozilla/dom/CSSStyleRuleBinding.h"
|
||||||
|
|
||||||
#include "mozilla/CSSEnabledState.h"
|
#include "mozilla/CSSEnabledState.h"
|
||||||
#include "mozilla/DeclarationBlock.h"
|
#include "mozilla/DeclarationBlock.h"
|
||||||
@@ -13,7 +14,6 @@
|
|||||||
#include "mozilla/dom/ShadowRoot.h"
|
#include "mozilla/dom/ShadowRoot.h"
|
||||||
#include "nsCSSPseudoElements.h"
|
#include "nsCSSPseudoElements.h"
|
||||||
|
|
||||||
#include "mozAutoDocUpdate.h"
|
|
||||||
#include "nsISupports.h"
|
#include "nsISupports.h"
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
@@ -66,6 +66,14 @@ DeclarationBlock* CSSStyleRuleDeclaration::GetOrCreateCSSDeclaration(
|
|||||||
return mDecls;
|
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) {
|
void CSSStyleRule::SetRawAfterClone(RefPtr<StyleLockedStyleRule> aRaw) {
|
||||||
mRawRule = std::move(aRaw);
|
mRawRule = std::move(aRaw);
|
||||||
mDecls.SetRawAfterClone(Servo_StyleRule_GetStyle(mRawRule).Consume());
|
mDecls.SetRawAfterClone(Servo_StyleRule_GetStyle(mRawRule).Consume());
|
||||||
@@ -76,14 +84,6 @@ already_AddRefed<StyleLockedCssRules> CSSStyleRule::GetOrCreateRawRules() {
|
|||||||
return Servo_StyleRule_EnsureRules(mRawRule, IsReadOnly()).Consume();
|
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(
|
nsresult CSSStyleRuleDeclaration::SetCSSDeclaration(
|
||||||
DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
|
DeclarationBlock* aDecl, MutationClosureData* aClosureData) {
|
||||||
CSSStyleRule* rule = Rule();
|
CSSStyleRule* rule = Rule();
|
||||||
@@ -102,8 +102,7 @@ nsresult CSSStyleRuleDeclaration::SetCSSDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsDOMCSSDeclaration::ParsingEnvironment
|
nsDOMCSSDeclaration::ParsingEnvironment
|
||||||
CSSStyleRuleDeclaration::GetParsingEnvironment(
|
CSSStyleRuleDeclaration::GetParsingEnvironment(nsIPrincipal*) const {
|
||||||
nsIPrincipal* aSubjectPrincipal) const {
|
|
||||||
return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Style);
|
return GetParsingEnvironmentForRule(Rule(), StyleCssRuleType::Style);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,8 +179,6 @@ void CSSStyleRule::GetCssText(nsACString& aCssText) const {
|
|||||||
Servo_StyleRule_GetCssText(mRawRule, &aCssText);
|
Servo_StyleRule_GetCssText(mRawRule, &aCssText);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsICSSDeclaration* CSSStyleRule::Style() { return &mDecls; }
|
|
||||||
|
|
||||||
/* CSSStyleRule implementation */
|
/* CSSStyleRule implementation */
|
||||||
|
|
||||||
void CSSStyleRule::GetSelectorText(nsACString& aSelectorText) {
|
void CSSStyleRule::GetSelectorText(nsACString& aSelectorText) {
|
||||||
@@ -286,10 +283,6 @@ bool CSSStyleRule::SelectorMatchesElement(uint32_t aSelectorIndex,
|
|||||||
aRelevantLinkVisited);
|
aRelevantLinkVisited);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotNull<DeclarationBlock*> CSSStyleRule::GetDeclarationBlock() const {
|
|
||||||
return WrapNotNull(mDecls.mDecls);
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectorWarningKind ToWebIDLSelectorWarningKind(
|
SelectorWarningKind ToWebIDLSelectorWarningKind(
|
||||||
StyleSelectorWarningKind aKind) {
|
StyleSelectorWarningKind aKind) {
|
||||||
switch (aKind) {
|
switch (aKind) {
|
||||||
|
|||||||
@@ -9,9 +9,7 @@
|
|||||||
|
|
||||||
#include "mozilla/css/GroupRule.h"
|
#include "mozilla/css/GroupRule.h"
|
||||||
#include "mozilla/ServoBindingTypes.h"
|
#include "mozilla/ServoBindingTypes.h"
|
||||||
#include "mozilla/NotNull.h"
|
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
#include "mozilla/dom/CSSStyleRuleBinding.h"
|
|
||||||
|
|
||||||
#include "nsDOMCSSDeclaration.h"
|
#include "nsDOMCSSDeclaration.h"
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ class DeclarationBlock;
|
|||||||
namespace dom {
|
namespace dom {
|
||||||
class DocGroup;
|
class DocGroup;
|
||||||
class CSSStyleRule;
|
class CSSStyleRule;
|
||||||
|
struct SelectorWarning;
|
||||||
|
|
||||||
class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration {
|
class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration {
|
||||||
public:
|
public:
|
||||||
@@ -32,8 +31,8 @@ class CSSStyleRuleDeclaration final : public nsDOMCSSDeclaration {
|
|||||||
nsISupports* GetParentObject() const final;
|
nsISupports* GetParentObject() const final;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
mozilla::DeclarationBlock* GetOrCreateCSSDeclaration(
|
DeclarationBlock* GetOrCreateCSSDeclaration(
|
||||||
Operation aOperation, mozilla::DeclarationBlock** aCreated) final;
|
Operation aOperation, DeclarationBlock** aCreated) final;
|
||||||
nsresult SetCSSDeclaration(DeclarationBlock* aDecl,
|
nsresult SetCSSDeclaration(DeclarationBlock* aDecl,
|
||||||
MutationClosureData* aClosureData) final;
|
MutationClosureData* aClosureData) final;
|
||||||
ParsingEnvironment GetParsingEnvironment(
|
ParsingEnvironment GetParsingEnvironment(
|
||||||
@@ -82,7 +81,7 @@ class CSSStyleRule final : public css::GroupRule, public SupportsWeakPtr {
|
|||||||
void GetCssText(nsACString& aCssText) const final;
|
void GetCssText(nsACString& aCssText) const final;
|
||||||
void GetSelectorText(nsACString& aSelectorText);
|
void GetSelectorText(nsACString& aSelectorText);
|
||||||
void SetSelectorText(const nsACString& aSelectorText);
|
void SetSelectorText(const nsACString& aSelectorText);
|
||||||
nsICSSDeclaration* Style();
|
nsICSSDeclaration* Style() { return &mDecls; }
|
||||||
|
|
||||||
StyleLockedStyleRule* Raw() const { return mRawRule; }
|
StyleLockedStyleRule* Raw() const { return mRawRule; }
|
||||||
void SetRawAfterClone(RefPtr<StyleLockedStyleRule>);
|
void SetRawAfterClone(RefPtr<StyleLockedStyleRule>);
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ GROUP_RULE_FUNCS_UNLOCKED(Container)
|
|||||||
GROUP_RULE_FUNCS_UNLOCKED(Scope)
|
GROUP_RULE_FUNCS_UNLOCKED(Scope)
|
||||||
GROUP_RULE_FUNCS_UNLOCKED(StartingStyle)
|
GROUP_RULE_FUNCS_UNLOCKED(StartingStyle)
|
||||||
BASIC_RULE_FUNCS_LOCKED(PositionTry)
|
BASIC_RULE_FUNCS_LOCKED(PositionTry)
|
||||||
|
BASIC_RULE_FUNCS_LOCKED(NestedDeclarations)
|
||||||
|
|
||||||
#undef GROUP_RULE_FUNCS_LOCKED
|
#undef GROUP_RULE_FUNCS_LOCKED
|
||||||
#undef GROUP_RULE_FUNCS_UNLOCKED
|
#undef GROUP_RULE_FUNCS_UNLOCKED
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "mozilla/dom/CSSMarginRule.h"
|
#include "mozilla/dom/CSSMarginRule.h"
|
||||||
#include "mozilla/dom/CSSMediaRule.h"
|
#include "mozilla/dom/CSSMediaRule.h"
|
||||||
#include "mozilla/dom/CSSMozDocumentRule.h"
|
#include "mozilla/dom/CSSMozDocumentRule.h"
|
||||||
|
#include "mozilla/dom/CSSNestedDeclarations.h"
|
||||||
#include "mozilla/dom/CSSNamespaceRule.h"
|
#include "mozilla/dom/CSSNamespaceRule.h"
|
||||||
#include "mozilla/dom/CSSPageRule.h"
|
#include "mozilla/dom/CSSPageRule.h"
|
||||||
#include "mozilla/dom/CSSPropertyRule.h"
|
#include "mozilla/dom/CSSPropertyRule.h"
|
||||||
@@ -28,7 +29,6 @@
|
|||||||
#include "mozilla/dom/CSSStyleRule.h"
|
#include "mozilla/dom/CSSStyleRule.h"
|
||||||
#include "mozilla/dom/CSSSupportsRule.h"
|
#include "mozilla/dom/CSSSupportsRule.h"
|
||||||
#include "mozilla/dom/CSSPositionTryRule.h"
|
#include "mozilla/dom/CSSPositionTryRule.h"
|
||||||
#include "mozilla/IntegerRange.h"
|
|
||||||
#include "mozilla/ServoBindings.h"
|
#include "mozilla/ServoBindings.h"
|
||||||
#include "mozilla/StyleSheet.h"
|
#include "mozilla/StyleSheet.h"
|
||||||
#include "mozilla/dom/Document.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(Scope, Scope)
|
||||||
CASE_RULE_UNLOCKED(StartingStyle, StartingStyle)
|
CASE_RULE_UNLOCKED(StartingStyle, StartingStyle)
|
||||||
CASE_RULE_LOCKED(PositionTry, PositionTry)
|
CASE_RULE_LOCKED(PositionTry, PositionTry)
|
||||||
|
CASE_RULE_LOCKED(NestedDeclarations, NestedDeclarations)
|
||||||
#undef CASE_RULE_LOCKED
|
#undef CASE_RULE_LOCKED
|
||||||
#undef CASE_RULE_UNLOCKED
|
#undef CASE_RULE_UNLOCKED
|
||||||
#undef CASE_RULE_WITH_PREFIX
|
#undef CASE_RULE_WITH_PREFIX
|
||||||
@@ -294,6 +295,7 @@ void ServoCSSRuleList::SetRawContents(RefPtr<StyleLockedCssRules> aNewRules,
|
|||||||
RULE_CASE_UNLOCKED(Scope, Scope)
|
RULE_CASE_UNLOCKED(Scope, Scope)
|
||||||
RULE_CASE_UNLOCKED(StartingStyle, StartingStyle)
|
RULE_CASE_UNLOCKED(StartingStyle, StartingStyle)
|
||||||
RULE_CASE_LOCKED(PositionTry, PositionTry)
|
RULE_CASE_LOCKED(PositionTry, PositionTry)
|
||||||
|
RULE_CASE_LOCKED(NestedDeclarations, NestedDeclarations)
|
||||||
case StyleCssRuleType::Keyframe:
|
case StyleCssRuleType::Keyframe:
|
||||||
MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
|
MOZ_ASSERT_UNREACHABLE("keyframe rule cannot be here");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ SERVO_LOCKED_ARC_TYPE(PageRule)
|
|||||||
SERVO_LOCKED_ARC_TYPE(FontFaceRule)
|
SERVO_LOCKED_ARC_TYPE(FontFaceRule)
|
||||||
SERVO_LOCKED_ARC_TYPE(CounterStyleRule)
|
SERVO_LOCKED_ARC_TYPE(CounterStyleRule)
|
||||||
SERVO_LOCKED_ARC_TYPE(PositionTryRule)
|
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<StyleScopeRule>;
|
||||||
template struct StyleStrong<StyleStartingStyleRule>;
|
template struct StyleStrong<StyleStartingStyleRule>;
|
||||||
template struct StyleStrong<StyleLockedPositionTryRule>;
|
template struct StyleStrong<StyleLockedPositionTryRule>;
|
||||||
|
template struct StyleStrong<StyleLockedNestedDeclarationsRule>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void StyleOwnedSlice<T>::Clear() {
|
inline void StyleOwnedSlice<T>::Clear() {
|
||||||
|
|||||||
@@ -1020,6 +1020,7 @@ void ServoStyleSet::RuleChangedInternal(StyleSheet& aSheet, css::Rule& aRule,
|
|||||||
CASE_FOR(Scope, Scope)
|
CASE_FOR(Scope, Scope)
|
||||||
CASE_FOR(StartingStyle, StartingStyle)
|
CASE_FOR(StartingStyle, StartingStyle)
|
||||||
CASE_FOR(PositionTry, PositionTry)
|
CASE_FOR(PositionTry, PositionTry)
|
||||||
|
CASE_FOR(NestedDeclarations, NestedDeclarations)
|
||||||
// @namespace can only be inserted / removed when there are only other
|
// @namespace can only be inserted / removed when there are only other
|
||||||
// @namespace and @import rules, and can't be mutated.
|
// @namespace and @import rules, and can't be mutated.
|
||||||
case StyleCssRuleType::Namespace:
|
case StyleCssRuleType::Namespace:
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ EXPORTS.mozilla.dom += [
|
|||||||
"CSSMediaRule.h",
|
"CSSMediaRule.h",
|
||||||
"CSSMozDocumentRule.h",
|
"CSSMozDocumentRule.h",
|
||||||
"CSSNamespaceRule.h",
|
"CSSNamespaceRule.h",
|
||||||
|
"CSSNestedDeclarations.h",
|
||||||
"CSSPageRule.h",
|
"CSSPageRule.h",
|
||||||
"CSSPositionTryRule.h",
|
"CSSPositionTryRule.h",
|
||||||
"CSSPropertyRule.h",
|
"CSSPropertyRule.h",
|
||||||
@@ -197,6 +198,7 @@ UNIFIED_SOURCES += [
|
|||||||
"CSSMediaRule.cpp",
|
"CSSMediaRule.cpp",
|
||||||
"CSSMozDocumentRule.cpp",
|
"CSSMozDocumentRule.cpp",
|
||||||
"CSSNamespaceRule.cpp",
|
"CSSNamespaceRule.cpp",
|
||||||
|
"CSSNestedDeclarations.cpp",
|
||||||
"CSSPageRule.cpp",
|
"CSSPageRule.cpp",
|
||||||
"CSSPositionTryRule.cpp",
|
"CSSPositionTryRule.cpp",
|
||||||
"CSSPropertyRule.cpp",
|
"CSSPropertyRule.cpp",
|
||||||
|
|||||||
@@ -382,6 +382,14 @@ pub struct SelectorList<Impl: SelectorImpl>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
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 {
|
pub fn from_one(selector: Selector<Impl>) -> Self {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let selector_repr = unsafe { *(&selector as *const _ as *const usize) };
|
let selector_repr = unsafe { *(&selector as *const _ as *const usize) };
|
||||||
@@ -475,15 +483,14 @@ pub enum ParseRelative {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
impl<Impl: SelectorImpl> SelectorList<Impl> {
|
||||||
/// Returns a selector list with a single `&`
|
/// Returns a selector list with a single `:scope` selector (with specificity)
|
||||||
pub fn ampersand() -> Self {
|
|
||||||
Self::from_one(Selector::ampersand())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a selector list with a single `:scope`
|
|
||||||
pub fn scope() -> Self {
|
pub fn scope() -> Self {
|
||||||
Self::from_one(Selector::scope())
|
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.
|
/// Parse a comma-separated list of Selectors.
|
||||||
/// <https://drafts.csswg.org/selectors/#grouping>
|
/// <https://drafts.csswg.org/selectors/#grouping>
|
||||||
@@ -822,16 +829,6 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
|||||||
self.0.mark_as_intentionally_leaked()
|
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 {
|
fn scope() -> Self {
|
||||||
Self(ThinArc::from_header_and_iter(
|
Self(ThinArc::from_header_and_iter(
|
||||||
SpecificityAndFlags {
|
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]
|
#[inline]
|
||||||
pub fn specificity(&self) -> u32 {
|
pub fn specificity(&self) -> u32 {
|
||||||
self.0.header.specificity
|
self.0.header.specificity
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::stylesheets::{
|
|||||||
ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule,
|
ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule,
|
||||||
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
|
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
|
||||||
MarginRule, MediaRule, NamespaceRule, PageRule, PositionTryRule, PropertyRule, ScopeRule,
|
MarginRule, MediaRule, NamespaceRule, PageRule, PositionTryRule, PropertyRule, ScopeRule,
|
||||||
StartingStyleRule, StyleRule, StylesheetContents, SupportsRule,
|
StartingStyleRule, StyleRule, StylesheetContents, SupportsRule, NestedDeclarationsRule,
|
||||||
};
|
};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
|
|
||||||
@@ -188,3 +188,9 @@ impl_locked_arc_ffi!(
|
|||||||
Servo_PositionTryRule_AddRef,
|
Servo_PositionTryRule_AddRef,
|
||||||
Servo_PositionTryRule_Release
|
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(..) => {
|
Namespace(..) => {
|
||||||
// It's not clear what handling changes for this correctly would
|
// It's not clear what handling changes for this correctly would
|
||||||
// look like.
|
// look like.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod loader;
|
|||||||
mod margin_rule;
|
mod margin_rule;
|
||||||
mod media_rule;
|
mod media_rule;
|
||||||
mod namespace_rule;
|
mod namespace_rule;
|
||||||
|
mod nested_declarations_rule;
|
||||||
pub mod origin;
|
pub mod origin;
|
||||||
mod page_rule;
|
mod page_rule;
|
||||||
pub mod position_try_rule;
|
pub mod position_try_rule;
|
||||||
@@ -35,7 +36,7 @@ use crate::gecko_bindings::sugar::refptr::RefCounted;
|
|||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use crate::gecko_bindings::{bindings, structs};
|
use crate::gecko_bindings::{bindings, structs};
|
||||||
use crate::parser::{NestingContext, ParserContext};
|
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::{DeepCloneWithLock, Locked};
|
||||||
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
|
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
|
||||||
use crate::str::CssStringWriter;
|
use crate::str::CssStringWriter;
|
||||||
@@ -63,6 +64,7 @@ pub use self::loader::StylesheetLoader;
|
|||||||
pub use self::margin_rule::{MarginRule, MarginRuleType};
|
pub use self::margin_rule::{MarginRule, MarginRuleType};
|
||||||
pub use self::media_rule::MediaRule;
|
pub use self::media_rule::MediaRule;
|
||||||
pub use self::namespace_rule::NamespaceRule;
|
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::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
|
||||||
pub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors};
|
pub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors};
|
||||||
pub use self::position_try_rule::PositionTryRule;
|
pub use self::position_try_rule::PositionTryRule;
|
||||||
@@ -346,6 +348,7 @@ pub enum CssRule {
|
|||||||
Scope(Arc<ScopeRule>),
|
Scope(Arc<ScopeRule>),
|
||||||
StartingStyle(Arc<StartingStyleRule>),
|
StartingStyle(Arc<StartingStyleRule>),
|
||||||
PositionTry(Arc<Locked<PositionTryRule>>),
|
PositionTry(Arc<Locked<PositionTryRule>>),
|
||||||
|
NestedDeclarations(Arc<Locked<NestedDeclarationsRule>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CssRule {
|
impl CssRule {
|
||||||
@@ -401,6 +404,9 @@ impl CssRule {
|
|||||||
CssRule::PositionTry(ref lock) => {
|
CssRule::PositionTry(ref lock) => {
|
||||||
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
|
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,
|
StartingStyle = 22,
|
||||||
// https://drafts.csswg.org/css-anchor-position-1/#om-position-try
|
// https://drafts.csswg.org/css-anchor-position-1/#om-position-try
|
||||||
PositionTry = 23,
|
PositionTry = 23,
|
||||||
|
// https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule
|
||||||
|
NestedDeclarations = 24,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CssRuleType {
|
impl CssRuleType {
|
||||||
@@ -537,14 +545,13 @@ impl CssRule {
|
|||||||
CssRule::Scope(_) => CssRuleType::Scope,
|
CssRule::Scope(_) => CssRuleType::Scope,
|
||||||
CssRule::StartingStyle(_) => CssRuleType::StartingStyle,
|
CssRule::StartingStyle(_) => CssRuleType::StartingStyle,
|
||||||
CssRule::PositionTry(_) => CssRuleType::PositionTry,
|
CssRule::PositionTry(_) => CssRuleType::PositionTry,
|
||||||
|
CssRule::NestedDeclarations(_) => CssRuleType::NestedDeclarations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a CSS rule.
|
/// Parse a CSS rule.
|
||||||
///
|
///
|
||||||
/// Returns a parsed CSS rule and the final state of the parser.
|
/// This mostly implements steps 3..7 of https://drafts.csswg.org/cssom/#insert-a-css-rule
|
||||||
///
|
|
||||||
/// Input state is None for a nested rule
|
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
css: &str,
|
css: &str,
|
||||||
insert_rule_context: InsertRuleContext,
|
insert_rule_context: InsertRuleContext,
|
||||||
@@ -584,7 +591,7 @@ impl CssRule {
|
|||||||
let mut input = Parser::new(&mut input);
|
let mut input = Parser::new(&mut input);
|
||||||
|
|
||||||
// nested rules are in the body state
|
// nested rules are in the body state
|
||||||
let mut rule_parser = TopLevelRuleParser {
|
let mut parser = TopLevelRuleParser {
|
||||||
context,
|
context,
|
||||||
shared_lock: &shared_lock,
|
shared_lock: &shared_lock,
|
||||||
loader,
|
loader,
|
||||||
@@ -593,37 +600,43 @@ impl CssRule {
|
|||||||
insert_rule_context: Some(insert_rule_context),
|
insert_rule_context: Some(insert_rule_context),
|
||||||
allow_import_rules,
|
allow_import_rules,
|
||||||
declaration_parser_state: Default::default(),
|
declaration_parser_state: Default::default(),
|
||||||
|
first_declaration_block: Default::default(),
|
||||||
|
wants_first_declaration_block: false,
|
||||||
error_reporting_state: Default::default(),
|
error_reporting_state: Default::default(),
|
||||||
rules: Default::default(),
|
rules: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match parse_one_rule(&mut input, &mut rule_parser) {
|
if input.try_parse(|input| parse_one_rule(input, &mut parser)).is_ok() {
|
||||||
Ok(_) => Ok(rule_parser.rules.pop().unwrap()),
|
return Ok(parser.rules.pop().unwrap());
|
||||||
Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl DeepCloneWithLock for CssRule {
|
||||||
/// Deep clones this CssRule.
|
/// Deep clones this CssRule.
|
||||||
fn deep_clone_with_lock(
|
fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> CssRule {
|
||||||
&self,
|
|
||||||
lock: &SharedRwLock,
|
|
||||||
guard: &SharedRwLockReadGuard,
|
|
||||||
) -> CssRule {
|
|
||||||
match *self {
|
match *self {
|
||||||
CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()),
|
CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()),
|
||||||
CssRule::Import(ref arc) => {
|
CssRule::Import(ref arc) => {
|
||||||
let rule = arc
|
let rule = arc.read_with(guard).deep_clone_with_lock(lock, guard);
|
||||||
.read_with(guard)
|
|
||||||
.deep_clone_with_lock(lock, guard);
|
|
||||||
CssRule::Import(Arc::new(lock.wrap(rule)))
|
CssRule::Import(Arc::new(lock.wrap(rule)))
|
||||||
},
|
},
|
||||||
CssRule::Style(ref arc) => {
|
CssRule::Style(ref arc) => {
|
||||||
let rule = arc.read_with(guard);
|
let rule = arc.read_with(guard);
|
||||||
CssRule::Style(Arc::new(
|
CssRule::Style(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
|
||||||
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
CssRule::Container(ref arc) => {
|
CssRule::Container(ref arc) => {
|
||||||
CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard)))
|
CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard)))
|
||||||
@@ -643,9 +656,7 @@ impl DeepCloneWithLock for CssRule {
|
|||||||
},
|
},
|
||||||
CssRule::Keyframes(ref arc) => {
|
CssRule::Keyframes(ref arc) => {
|
||||||
let rule = arc.read_with(guard);
|
let rule = arc.read_with(guard);
|
||||||
CssRule::Keyframes(Arc::new(
|
CssRule::Keyframes(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
|
||||||
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
CssRule::Margin(ref arc) => {
|
CssRule::Margin(ref arc) => {
|
||||||
CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard)))
|
CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard)))
|
||||||
@@ -655,9 +666,7 @@ impl DeepCloneWithLock for CssRule {
|
|||||||
},
|
},
|
||||||
CssRule::Page(ref arc) => {
|
CssRule::Page(ref arc) => {
|
||||||
let rule = arc.read_with(guard);
|
let rule = arc.read_with(guard);
|
||||||
CssRule::Page(Arc::new(
|
CssRule::Page(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
|
||||||
lock.wrap(rule.deep_clone_with_lock(lock, guard)),
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
CssRule::Property(ref arc) => {
|
CssRule::Property(ref arc) => {
|
||||||
// @property rules are immutable, so we don't need any of the `Locked`
|
// @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) => {
|
CssRule::PositionTry(ref arc) => {
|
||||||
let rule = arc.read_with(guard);
|
let rule = arc.read_with(guard);
|
||||||
CssRule::PositionTry(Arc::new(
|
CssRule::PositionTry(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))
|
||||||
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::Scope(ref rule) => rule.to_css(guard, dest),
|
||||||
CssRule::StartingStyle(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::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::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
|
||||||
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
|
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
|
||||||
use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
|
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::scope_rule::{ScopeBounds, ScopeRule};
|
||||||
use crate::stylesheets::starting_style_rule::StartingStyleRule;
|
|
||||||
use crate::stylesheets::supports_rule::SupportsCondition;
|
use crate::stylesheets::supports_rule::SupportsCondition;
|
||||||
use crate::stylesheets::{
|
use crate::stylesheets::{
|
||||||
AllowImportRules, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules, DocumentRule,
|
AllowImportRules, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules, DocumentRule,
|
||||||
FontFeatureValuesRule, FontPaletteValuesRule, KeyframesRule, MarginRule, MarginRuleType,
|
FontFeatureValuesRule, FontPaletteValuesRule, KeyframesRule, MarginRule, MarginRuleType,
|
||||||
MediaRule, NamespaceRule, PageRule, PageSelectors, RulesMutateError, StyleRule,
|
MediaRule, NamespaceRule, PageRule, PageSelectors, RulesMutateError, StyleRule,
|
||||||
StylesheetLoader, SupportsRule,
|
StylesheetLoader, SupportsRule, StartingStyleRule, NestedDeclarationsRule, PositionTryRule
|
||||||
};
|
};
|
||||||
use crate::values::computed::font::FamilyName;
|
use crate::values::computed::font::FamilyName;
|
||||||
use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
|
use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};
|
||||||
@@ -105,6 +103,11 @@ pub struct TopLevelRuleParser<'a, 'i> {
|
|||||||
pub insert_rule_context: Option<InsertRuleContext<'a>>,
|
pub insert_rule_context: Option<InsertRuleContext<'a>>,
|
||||||
/// Whether @import rules will be allowed.
|
/// Whether @import rules will be allowed.
|
||||||
pub allow_import_rules: AllowImportRules,
|
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.
|
/// Parser state for declaration blocks in either nested rules or style rules.
|
||||||
pub declaration_parser_state: DeclarationParserState<'i>,
|
pub declaration_parser_state: DeclarationParserState<'i>,
|
||||||
/// State we keep around only for error reporting purposes. Right now that contains just the
|
/// 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
|
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
|
/// Checks whether we can parse a rule that would transition us to
|
||||||
/// `new_state`.
|
/// `new_state`.
|
||||||
///
|
///
|
||||||
@@ -458,65 +493,11 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {
|
|||||||
struct NestedRuleParser<'a, 'i>(TopLevelRuleParser<'a, 'i>);
|
struct NestedRuleParser<'a, 'i>(TopLevelRuleParser<'a, 'i>);
|
||||||
|
|
||||||
struct NestedParseResult {
|
struct NestedParseResult {
|
||||||
|
first_declaration_block: PropertyDeclarationBlock,
|
||||||
rules: Vec<CssRule>,
|
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> {
|
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]
|
#[inline]
|
||||||
fn parse_relative(&self) -> ParseRelative {
|
fn parse_relative(&self) -> ParseRelative {
|
||||||
self.context.nesting_context.parse_relative
|
self.context.nesting_context.parse_relative
|
||||||
@@ -557,15 +538,29 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
|
|||||||
r
|
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(
|
fn parse_nested(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &mut Parser<'i, '_>,
|
input: &mut Parser<'i, '_>,
|
||||||
rule_type: CssRuleType,
|
rule_type: CssRuleType,
|
||||||
|
wants_first_declaration_block: bool,
|
||||||
|
source_location: SourceLocation,
|
||||||
) -> NestedParseResult {
|
) -> NestedParseResult {
|
||||||
|
debug_assert!(!self.wants_first_declaration_block, "Should've flushed previous declarations");
|
||||||
self.nest_for_rule(rule_type, |parser| {
|
self.nest_for_rule(rule_type, |parser| {
|
||||||
|
parser.wants_first_declaration_block = wants_first_declaration_block;
|
||||||
let parse_declarations = parser.parse_declarations();
|
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 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);
|
let mut iter = RuleBodyParser::new(input, parser);
|
||||||
while let Some(result) = iter.next() {
|
while let Some(result) = iter.next() {
|
||||||
match result {
|
match result {
|
||||||
@@ -583,26 +578,20 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let declarations = if parse_declarations {
|
parser.flush_declarations(source_location);
|
||||||
let top = &mut **parser;
|
debug_assert!(
|
||||||
top.declaration_parser_state
|
!parser.wants_first_declaration_block,
|
||||||
.report_errors_if_needed(&top.context, &top.error_reporting_state);
|
"Flushing declarations should take care of this."
|
||||||
parser.declaration_parser_state.take_declarations()
|
);
|
||||||
} else {
|
|
||||||
PropertyDeclarationBlock::default()
|
|
||||||
};
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!parser.declaration_parser_state.has_parsed_declarations(),
|
!parser.declaration_parser_state.has_parsed_declarations(),
|
||||||
"Parsed but didn't consume 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.rules, &mut rules);
|
||||||
|
std::mem::swap(&mut parser.first_declaration_block, &mut first_declaration_block);
|
||||||
NestedParseResult {
|
NestedParseResult {
|
||||||
|
first_declaration_block,
|
||||||
rules,
|
rules,
|
||||||
declarations,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -647,6 +636,28 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
|
|||||||
fn handle_error_reporting_selectors_post(&mut self) {
|
fn handle_error_reporting_selectors_post(&mut self) {
|
||||||
self.error_reporting_state.pop();
|
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> {
|
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);
|
self.dom_error = Some(RulesMutateError::HierarchyRequest);
|
||||||
return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(prelude.name().into())));
|
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 {
|
let rule = match prelude {
|
||||||
AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
|
AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {
|
||||||
CssRule::FontFace(Arc::new(p.shared_lock.wrap(
|
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) => {
|
AtRulePrelude::FontFeatureValues(family_names) => {
|
||||||
@@ -772,7 +785,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
&p.context,
|
&p.context,
|
||||||
input,
|
input,
|
||||||
family_names,
|
family_names,
|
||||||
start.source_location(),
|
source_location,
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -782,35 +795,29 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
&p.context,
|
&p.context,
|
||||||
input,
|
input,
|
||||||
name,
|
name,
|
||||||
start.source_location(),
|
source_location,
|
||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
AtRulePrelude::CounterStyle(name) => {
|
AtRulePrelude::CounterStyle(name) => {
|
||||||
let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {
|
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)))
|
CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))
|
||||||
},
|
},
|
||||||
AtRulePrelude::Media(media_queries) => {
|
AtRulePrelude::Media(media_queries) => {
|
||||||
let source_location = start.source_location();
|
|
||||||
CssRule::Media(Arc::new(MediaRule {
|
CssRule::Media(Arc::new(MediaRule {
|
||||||
media_queries,
|
media_queries,
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::Media, source_location),
|
||||||
.parse_nested(input, CssRuleType::Media)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
source_location,
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
AtRulePrelude::Supports(condition) => {
|
AtRulePrelude::Supports(condition) => {
|
||||||
let enabled =
|
let enabled =
|
||||||
self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
|
self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));
|
||||||
let source_location = start.source_location();
|
|
||||||
CssRule::Supports(Arc::new(SupportsRule {
|
CssRule::Supports(Arc::new(SupportsRule {
|
||||||
condition,
|
condition,
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::Supports, source_location),
|
||||||
.parse_nested(input, CssRuleType::Supports)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
enabled,
|
enabled,
|
||||||
source_location,
|
source_location,
|
||||||
}))
|
}))
|
||||||
@@ -822,12 +829,11 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
name,
|
name,
|
||||||
keyframes: parse_keyframe_list(&mut top.context, input, top.shared_lock),
|
keyframes: parse_keyframe_list(&mut top.context, input, top.shared_lock),
|
||||||
vendor_prefix,
|
vendor_prefix,
|
||||||
source_location: start.source_location(),
|
source_location,
|
||||||
})))
|
})))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
AtRulePrelude::Page(selectors) => {
|
AtRulePrelude::Page(selectors) => {
|
||||||
let source_location = start.source_location();
|
|
||||||
let page_rule = if !static_prefs::pref!("layout.css.margin-rules.enabled") {
|
let page_rule = if !static_prefs::pref!("layout.css.margin-rules.enabled") {
|
||||||
let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
|
let declarations = self.nest_for_rule(CssRuleType::Page, |p| {
|
||||||
parse_property_declaration_list(&p.context, input, &[])
|
parse_property_declaration_list(&p.context, input, &[])
|
||||||
@@ -839,11 +845,11 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
source_location,
|
source_location,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let result = self.parse_nested(input, CssRuleType::Page);
|
let result = self.parse_nested(input, CssRuleType::Page, true, source_location);
|
||||||
PageRule {
|
PageRule {
|
||||||
selectors,
|
selectors,
|
||||||
rules: CssRules::new(result.rules, self.shared_lock),
|
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,
|
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| {
|
AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| {
|
||||||
let rule_data =
|
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)))
|
Ok::<CssRule, ParseError<'i>>(CssRule::Property(Arc::new(rule_data)))
|
||||||
})?,
|
})?,
|
||||||
AtRulePrelude::Document(condition) => {
|
AtRulePrelude::Document(condition) => {
|
||||||
if !cfg!(feature = "gecko") {
|
if !cfg!(feature = "gecko") {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
let source_location = start.source_location();
|
|
||||||
CssRule::Document(Arc::new(DocumentRule {
|
CssRule::Document(Arc::new(DocumentRule {
|
||||||
condition,
|
condition,
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::Document, source_location),
|
||||||
.parse_nested(input, CssRuleType::Document)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
source_location,
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
@@ -871,9 +874,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
let source_location = start.source_location();
|
let source_location = start.source_location();
|
||||||
CssRule::Container(Arc::new(ContainerRule {
|
CssRule::Container(Arc::new(ContainerRule {
|
||||||
condition,
|
condition,
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::Container, source_location),
|
||||||
.parse_nested(input, CssRuleType::Container)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
source_location,
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
@@ -882,12 +883,9 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
0 | 1 => names.into_iter().next(),
|
0 | 1 => names.into_iter().next(),
|
||||||
_ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
|
_ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
|
||||||
};
|
};
|
||||||
let source_location = start.source_location();
|
|
||||||
CssRule::LayerBlock(Arc::new(LayerBlockRule {
|
CssRule::LayerBlock(Arc::new(LayerBlockRule {
|
||||||
name,
|
name,
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::LayerBlock, source_location),
|
||||||
.parse_nested(input, CssRuleType::LayerBlock)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
source_location,
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
@@ -898,7 +896,7 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
CssRule::Margin(Arc::new(MarginRule {
|
CssRule::Margin(Arc::new(MarginRule {
|
||||||
rule_type,
|
rule_type,
|
||||||
block: Arc::new(self.shared_lock.wrap(declarations)),
|
block: Arc::new(self.shared_lock.wrap(declarations)),
|
||||||
source_location: start.source_location(),
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => {
|
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));
|
return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock));
|
||||||
},
|
},
|
||||||
AtRulePrelude::Scope(bounds) => {
|
AtRulePrelude::Scope(bounds) => {
|
||||||
let source_location = start.source_location();
|
|
||||||
CssRule::Scope(Arc::new(ScopeRule {
|
CssRule::Scope(Arc::new(ScopeRule {
|
||||||
bounds,
|
bounds,
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::Scope, source_location),
|
||||||
.parse_nested(input, CssRuleType::Scope)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
source_location,
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
AtRulePrelude::StartingStyle => {
|
AtRulePrelude::StartingStyle => {
|
||||||
let source_location = start.source_location();
|
|
||||||
CssRule::StartingStyle(Arc::new(StartingStyleRule {
|
CssRule::StartingStyle(Arc::new(StartingStyleRule {
|
||||||
rules: self
|
rules: self.parse_nested_rules(input, CssRuleType::StartingStyle, source_location),
|
||||||
.parse_nested(input, CssRuleType::StartingStyle)
|
|
||||||
.into_rules(self.shared_lock, source_location),
|
|
||||||
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 {
|
CssRule::PositionTry(Arc::new(self.shared_lock.wrap(PositionTryRule {
|
||||||
name,
|
name,
|
||||||
block: Arc::new(self.shared_lock.wrap(declarations)),
|
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() {
|
if self.in_style_rule() {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
let source_location = start.source_location();
|
||||||
let rule = match prelude {
|
let rule = match prelude {
|
||||||
AtRulePrelude::Layer(names) => {
|
AtRulePrelude::Layer(names) => {
|
||||||
if names.is_empty() {
|
if names.is_empty() {
|
||||||
@@ -955,11 +948,12 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
}
|
}
|
||||||
CssRule::LayerStatement(Arc::new(LayerStatementRule {
|
CssRule::LayerStatement(Arc::new(LayerStatementRule {
|
||||||
names,
|
names,
|
||||||
source_location: start.source_location(),
|
source_location,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
_ => return Err(()),
|
_ => return Err(()),
|
||||||
};
|
};
|
||||||
|
self.flush_declarations(source_location);
|
||||||
self.rules.push(rule);
|
self.rules.push(rule);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -989,15 +983,17 @@ impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
start: &ParserState,
|
start: &ParserState,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<(), ParseError<'i>> {
|
) -> Result<(), ParseError<'i>> {
|
||||||
|
let source_location = start.source_location();
|
||||||
let reporting_errors = self.context.error_reporting_enabled();
|
let reporting_errors = self.context.error_reporting_enabled();
|
||||||
if reporting_errors {
|
if reporting_errors {
|
||||||
self.handle_error_reporting_selectors_pre(start, &selectors);
|
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 {
|
if reporting_errors {
|
||||||
self.handle_error_reporting_selectors_post();
|
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;
|
let top = &mut **self;
|
||||||
top.rules
|
top.rules
|
||||||
.push(CssRule::Style(Arc::new(top.shared_lock.wrap(StyleRule {
|
.push(CssRule::Style(Arc::new(top.shared_lock.wrap(StyleRule {
|
||||||
@@ -1008,7 +1004,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
|
|||||||
} else {
|
} else {
|
||||||
Some(CssRules::new(result.rules, top.shared_lock))
|
Some(CssRules::new(result.rules, top.shared_lock))
|
||||||
},
|
},
|
||||||
source_location: start.source_location(),
|
source_location,
|
||||||
}))));
|
}))));
|
||||||
Ok(())
|
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
|
/// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse
|
||||||
/// raw declarations there.
|
/// raw declarations there.
|
||||||
fn parse_declarations(&self) -> bool {
|
fn parse_declarations(&self) -> bool {
|
||||||
// We also have to check for page rules here because we currently don't
|
self.can_parse_declarations()
|
||||||
// have a bespoke parser for page rules, and parse them as though they
|
|
||||||
// are style rules.
|
|
||||||
self.in_style_or_page_rule()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ where
|
|||||||
CssRule::LayerStatement(_) |
|
CssRule::LayerStatement(_) |
|
||||||
CssRule::FontFeatureValues(_) |
|
CssRule::FontFeatureValues(_) |
|
||||||
CssRule::FontPaletteValues(_) |
|
CssRule::FontPaletteValues(_) |
|
||||||
|
CssRule::NestedDeclarations(_) |
|
||||||
CssRule::PositionTry(_) => None,
|
CssRule::PositionTry(_) => None,
|
||||||
CssRule::Page(ref page_rule) => {
|
CssRule::Page(ref page_rule) => {
|
||||||
let page_rule = page_rule.read_with(guard);
|
let page_rule = page_rule.read_with(guard);
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ impl SanitizationKind {
|
|||||||
CssRule::FontFace(..) |
|
CssRule::FontFace(..) |
|
||||||
CssRule::Namespace(..) |
|
CssRule::Namespace(..) |
|
||||||
CssRule::Style(..) |
|
CssRule::Style(..) |
|
||||||
|
CssRule::NestedDeclarations(..) |
|
||||||
CssRule::PositionTry(..) => true,
|
CssRule::PositionTry(..) => true,
|
||||||
|
|
||||||
CssRule::Keyframes(..) |
|
CssRule::Keyframes(..) |
|
||||||
@@ -463,6 +464,8 @@ impl Stylesheet {
|
|||||||
insert_rule_context: None,
|
insert_rule_context: None,
|
||||||
allow_import_rules,
|
allow_import_rules,
|
||||||
declaration_parser_state: Default::default(),
|
declaration_parser_state: Default::default(),
|
||||||
|
first_declaration_block: Default::default(),
|
||||||
|
wants_first_declaration_block: false,
|
||||||
error_reporting_state: Default::default(),
|
error_reporting_state: Default::default(),
|
||||||
rules: Vec::new(),
|
rules: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,9 +50,8 @@ use crate::stylesheets::{
|
|||||||
};
|
};
|
||||||
use crate::stylesheets::{
|
use crate::stylesheets::{
|
||||||
CssRule, EffectiveRulesIterator, Origin, OriginSet, PagePseudoClassFlags, PageRule, PerOrigin,
|
CssRule, EffectiveRulesIterator, Origin, OriginSet, PagePseudoClassFlags, PageRule, PerOrigin,
|
||||||
PerOriginIter,
|
PerOriginIter, StylesheetContents, StylesheetInDocument,
|
||||||
};
|
};
|
||||||
use crate::stylesheets::{StyleRule, StylesheetContents, StylesheetInDocument};
|
|
||||||
use crate::values::{computed, AtomIdent};
|
use crate::values::{computed, AtomIdent};
|
||||||
use crate::AllocErr;
|
use crate::AllocErr;
|
||||||
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
|
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 /
|
/// A struct containing state from ancestor rules like @layer / @import /
|
||||||
/// @container / nesting / @scope.
|
/// @container / nesting / @scope.
|
||||||
struct ContainingRuleState {
|
struct ContainingRuleState {
|
||||||
@@ -642,6 +653,7 @@ struct ContainingRuleState {
|
|||||||
scope_condition_id: ScopeConditionId,
|
scope_condition_id: ScopeConditionId,
|
||||||
scope_matches_shadow_host: ScopeMatchesShadowHost,
|
scope_matches_shadow_host: ScopeMatchesShadowHost,
|
||||||
ancestor_selector_lists: SmallVec<[SelectorList<SelectorImpl>; 2]>,
|
ancestor_selector_lists: SmallVec<[SelectorList<SelectorImpl>; 2]>,
|
||||||
|
nested_declarations_context: NestedDeclarationsContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ContainingRuleState {
|
impl Default for ContainingRuleState {
|
||||||
@@ -654,6 +666,7 @@ impl Default for ContainingRuleState {
|
|||||||
ancestor_selector_lists: Default::default(),
|
ancestor_selector_lists: Default::default(),
|
||||||
scope_condition_id: ScopeConditionId::none(),
|
scope_condition_id: ScopeConditionId::none(),
|
||||||
scope_matches_shadow_host: Default::default(),
|
scope_matches_shadow_host: Default::default(),
|
||||||
|
nested_declarations_context: NestedDeclarationsContext::Style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -666,6 +679,7 @@ struct SavedContainingRuleState {
|
|||||||
in_starting_style: bool,
|
in_starting_style: bool,
|
||||||
scope_condition_id: ScopeConditionId,
|
scope_condition_id: ScopeConditionId,
|
||||||
scope_matches_shadow_host: ScopeMatchesShadowHost,
|
scope_matches_shadow_host: ScopeMatchesShadowHost,
|
||||||
|
nested_declarations_context: NestedDeclarationsContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainingRuleState {
|
impl ContainingRuleState {
|
||||||
@@ -678,6 +692,7 @@ impl ContainingRuleState {
|
|||||||
in_starting_style: self.in_starting_style,
|
in_starting_style: self.in_starting_style,
|
||||||
scope_condition_id: self.scope_condition_id,
|
scope_condition_id: self.scope_condition_id,
|
||||||
scope_matches_shadow_host: self.scope_matches_shadow_host,
|
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.in_starting_style = saved.in_starting_style;
|
||||||
self.scope_condition_id = saved.scope_condition_id;
|
self.scope_condition_id = saved.scope_condition_id;
|
||||||
self.scope_matches_shadow_host = saved.scope_matches_shadow_host;
|
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 {
|
impl Stylist {
|
||||||
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
|
/// Construct a new `Stylist`, using given `Device` and `QuirksMode`.
|
||||||
/// If more members are added here, think about whether they should
|
/// If more members are added here, think about whether they should
|
||||||
@@ -2695,13 +2713,12 @@ pub struct CascadeData {
|
|||||||
num_declarations: usize,
|
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> {
|
fn parent_selector_for_scope(parent: Option<&SelectorList<SelectorImpl>>) -> &SelectorList<SelectorImpl> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SCOPE: SelectorList<SelectorImpl> = {
|
static ref SCOPE: SelectorList<SelectorImpl> = {
|
||||||
let list = SelectorList::scope();
|
let list = SelectorList::scope();
|
||||||
list.slice()
|
list.mark_as_intentionally_leaked();
|
||||||
.iter()
|
|
||||||
.for_each(|selector| selector.mark_as_intentionally_leaked());
|
|
||||||
list
|
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>(
|
fn add_rule_list<S>(
|
||||||
&mut self,
|
&mut self,
|
||||||
rules: std::slice::Iter<CssRule>,
|
rules: std::slice::Iter<CssRule>,
|
||||||
@@ -3243,164 +3423,26 @@ impl CascadeData {
|
|||||||
match *rule {
|
match *rule {
|
||||||
CssRule::Style(ref locked) => {
|
CssRule::Style(ref locked) => {
|
||||||
let style_rule = locked.read_with(guard);
|
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 has_nested_rules = style_rule.rules.is_some();
|
||||||
let mut ancestor_selectors =
|
let mut replaced_selectors = ReplacedSelectors::new();
|
||||||
containing_rule_state.ancestor_selector_lists.last_mut();
|
let ancestor_selectors = containing_rule_state.ancestor_selector_lists.last();
|
||||||
let mut replaced_selectors = SmallVec::<[Selector<SelectorImpl>; 4]>::new();
|
let collect_replaced_selectors = has_nested_rules && ancestor_selectors.is_some();
|
||||||
let collect_replaced_selectors =
|
self.add_styles(
|
||||||
has_nested_rules && ancestor_selectors.is_some();
|
StyleSource::from_rule(locked.clone()),
|
||||||
|
&style_rule.selectors,
|
||||||
for selector in style_rule.selectors.slice() {
|
&style_rule.block,
|
||||||
self.num_selectors += 1;
|
ancestor_selectors,
|
||||||
|
&containing_rule_state,
|
||||||
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,
|
|
||||||
);
|
|
||||||
|
|
||||||
if collect_replaced_selectors {
|
if collect_replaced_selectors {
|
||||||
replaced_selectors.push(rule.selector.clone())
|
Some(&mut replaced_selectors)
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
// NOTE(emilio): It's fine to look at :host and then at
|
None
|
||||||
// ::slotted(..), since :host::slotted(..) could never
|
},
|
||||||
// possibly match, as <slot> is not a valid shadow host.
|
guard,
|
||||||
// :scope may match featureless shadow host if the scope
|
rebuild_kind,
|
||||||
// root is the shadow root.
|
precomputed_pseudo_element_decls.as_deref_mut(),
|
||||||
// See https://github.com/w3c/csswg-drafts/issues/9025
|
quirks_mode,
|
||||||
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;
|
|
||||||
if has_nested_rules {
|
if has_nested_rules {
|
||||||
handled = false;
|
handled = false;
|
||||||
list_for_nested_rules = Some(if collect_replaced_selectors {
|
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) => {
|
CssRule::Keyframes(ref keyframes_rule) => {
|
||||||
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
||||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||||
@@ -3594,6 +3666,7 @@ impl CascadeData {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
CssRule::Style(..) => {
|
CssRule::Style(..) => {
|
||||||
|
containing_rule_state.nested_declarations_context = NestedDeclarationsContext::Style;
|
||||||
if let Some(s) = list_for_nested_rules {
|
if let Some(s) = list_for_nested_rules {
|
||||||
containing_rule_state.ancestor_selector_lists.push(s);
|
containing_rule_state.ancestor_selector_lists.push(s);
|
||||||
}
|
}
|
||||||
@@ -3610,6 +3683,7 @@ impl CascadeData {
|
|||||||
containing_rule_state.in_starting_style = true;
|
containing_rule_state.in_starting_style = true;
|
||||||
},
|
},
|
||||||
CssRule::Scope(ref rule) => {
|
CssRule::Scope(ref rule) => {
|
||||||
|
containing_rule_state.nested_declarations_context = NestedDeclarationsContext::Scope;
|
||||||
let id = ScopeConditionId(self.scope_conditions.len() as u16);
|
let id = ScopeConditionId(self.scope_conditions.len() as u16);
|
||||||
let mut matches_shadow_host = false;
|
let mut matches_shadow_host = false;
|
||||||
let implicit_scope_root = if let Some(start) = rule.bounds.start.as_ref() {
|
let implicit_scope_root = if let Some(start) = rule.bounds.start.as_ref() {
|
||||||
@@ -3649,11 +3723,12 @@ impl CascadeData {
|
|||||||
None => selector.clone(),
|
None => selector.clone(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let parent = parent_selector_for_scope(start.as_ref());
|
||||||
let end = rule.bounds
|
let end = rule.bounds
|
||||||
.end
|
.end
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|selector| selector.replace_parent_selector(parent_selector_for_scope(start.as_ref())));
|
.map(|selector| selector.replace_parent_selector(parent));
|
||||||
containing_rule_state.ancestor_selector_lists.push(parent_selector_for_scope(start.as_ref()).clone());
|
containing_rule_state.ancestor_selector_lists.push(parent.clone());
|
||||||
ScopeBoundsWithHashes::new(quirks_mode, start, end)
|
ScopeBoundsWithHashes::new(quirks_mode, start, end)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3776,6 +3851,7 @@ impl CascadeData {
|
|||||||
while let Some(rule) = iter.next() {
|
while let Some(rule) = iter.next() {
|
||||||
match *rule {
|
match *rule {
|
||||||
CssRule::Style(..) |
|
CssRule::Style(..) |
|
||||||
|
CssRule::NestedDeclarations(..) |
|
||||||
CssRule::Namespace(..) |
|
CssRule::Namespace(..) |
|
||||||
CssRule::FontFace(..) |
|
CssRule::FontFace(..) |
|
||||||
CssRule::Container(..) |
|
CssRule::Container(..) |
|
||||||
@@ -4015,12 +4091,8 @@ pub struct Rule {
|
|||||||
pub scope_condition_id: ScopeConditionId,
|
pub scope_condition_id: ScopeConditionId,
|
||||||
|
|
||||||
/// The actual style rule.
|
/// The actual style rule.
|
||||||
#[cfg_attr(
|
#[ignore_malloc_size_of = "Secondary ref. Primary ref is in StyleRule under Stylesheet." ]
|
||||||
feature = "gecko",
|
pub style_source: StyleSource,
|
||||||
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>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SelectorMapEntry for Rule {
|
impl SelectorMapEntry for Rule {
|
||||||
@@ -4043,9 +4115,8 @@ impl Rule {
|
|||||||
cascade_data: &CascadeData,
|
cascade_data: &CascadeData,
|
||||||
scope_proximity: ScopeProximity,
|
scope_proximity: ScopeProximity,
|
||||||
) -> ApplicableDeclarationBlock {
|
) -> ApplicableDeclarationBlock {
|
||||||
let source = StyleSource::from_rule(self.style_rule.clone());
|
|
||||||
ApplicableDeclarationBlock::new(
|
ApplicableDeclarationBlock::new(
|
||||||
source,
|
self.style_source.clone(),
|
||||||
self.source_order,
|
self.source_order,
|
||||||
level,
|
level,
|
||||||
self.specificity(),
|
self.specificity(),
|
||||||
@@ -4058,17 +4129,17 @@ impl Rule {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
selector: Selector<SelectorImpl>,
|
selector: Selector<SelectorImpl>,
|
||||||
hashes: AncestorHashes,
|
hashes: AncestorHashes,
|
||||||
style_rule: Arc<Locked<StyleRule>>,
|
style_source: StyleSource,
|
||||||
source_order: u32,
|
source_order: u32,
|
||||||
layer_id: LayerId,
|
layer_id: LayerId,
|
||||||
container_condition_id: ContainerConditionId,
|
container_condition_id: ContainerConditionId,
|
||||||
is_starting_style: bool,
|
is_starting_style: bool,
|
||||||
scope_condition_id: ScopeConditionId,
|
scope_condition_id: ScopeConditionId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Rule {
|
Self {
|
||||||
selector,
|
selector,
|
||||||
hashes,
|
hashes,
|
||||||
style_rule,
|
style_source,
|
||||||
source_order,
|
source_order,
|
||||||
layer_id,
|
layer_id,
|
||||||
container_condition_id,
|
container_condition_id,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ use style::font_face::{self, FontFaceSourceFormat, FontFaceSourceListComponent,
|
|||||||
use style::gecko::arc_types::{
|
use style::gecko::arc_types::{
|
||||||
LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
|
LockedCounterStyleRule, LockedCssRules, LockedDeclarationBlock, LockedFontFaceRule,
|
||||||
LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList, LockedPageRule,
|
LockedImportRule, LockedKeyframe, LockedKeyframesRule, LockedMediaList, LockedPageRule,
|
||||||
LockedPositionTryRule, LockedStyleRule,
|
LockedPositionTryRule, LockedNestedDeclarationsRule, LockedStyleRule,
|
||||||
};
|
};
|
||||||
use style::gecko::data::{
|
use style::gecko::data::{
|
||||||
AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
|
AuthorStyles, GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl,
|
||||||
@@ -135,10 +135,10 @@ use style::stylesheets::{
|
|||||||
AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRuleTypes,
|
AllowImportRules, ContainerRule, CounterStyleRule, CssRule, CssRuleType, CssRuleTypes,
|
||||||
CssRules, CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule,
|
CssRules, CssRulesHelpers, DocumentRule, FontFaceRule, FontFeatureValuesRule,
|
||||||
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
|
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
|
||||||
MarginRule, MediaRule, NamespaceRule, Origin, OriginSet, PagePseudoClassFlags, PageRule,
|
MarginRule, MediaRule, NamespaceRule, NestedDeclarationsRule, Origin, OriginSet,
|
||||||
PositionTryRule, PropertyRule, SanitizationData, SanitizationKind, ScopeRule,
|
PagePseudoClassFlags, PageRule, PositionTryRule, PropertyRule, SanitizationData,
|
||||||
StartingStyleRule, StyleRule, StylesheetContents, StylesheetLoader as StyleStylesheetLoader,
|
SanitizationKind, ScopeRule, StartingStyleRule, StyleRule, StylesheetContents,
|
||||||
SupportsRule, UrlExtraData,
|
StylesheetLoader as StyleStylesheetLoader, SupportsRule, UrlExtraData,
|
||||||
};
|
};
|
||||||
use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
|
use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
@@ -2585,6 +2585,30 @@ impl_basic_rule_funcs! { (PositionTry, PositionTryRule, Locked<PositionTryRule>)
|
|||||||
changed: Servo_StyleSet_PositionTryRuleChanged,
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_StyleRule_GetStyle(
|
pub extern "C" fn Servo_StyleRule_GetStyle(
|
||||||
rule: &LockedStyleRule,
|
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]
|
[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]
|
[Nested declarations rule responds to parent selector text change]
|
||||||
expected: FAIL
|
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);
|
assert_equals(s.cssRules.length, 1);
|
||||||
let outer = s.cssRules[0];
|
let outer = s.cssRules[0];
|
||||||
assert_equals(outer.cssRules.length, 2);
|
if (window.CSSScopeRule) {
|
||||||
|
assert_equals(outer.cssRules.length, 2);
|
||||||
|
|
||||||
// @scope
|
// @scope
|
||||||
let scope = outer.cssRules[0];
|
let scope = outer.cssRules[0];
|
||||||
assert_equals(scope.cssRules.length, 3);
|
assert_true(scope instanceof CSSScopeRule);
|
||||||
assert_true(scope.cssRules[0] instanceof CSSNestedDeclarations);
|
assert_equals(scope.cssRules.length, 3);
|
||||||
assert_equals(scope.cssRules[0].cssText, `--x: 1; --y: 1;`);
|
assert_true(scope.cssRules[0] instanceof CSSNestedDeclarations);
|
||||||
assert_equals(scope.cssRules[1].cssText, `.b { }`); // Implicit :scope here.
|
assert_equals(scope.cssRules[0].cssText, `--x: 1; --y: 1;`);
|
||||||
assert_true(scope.cssRules[2] instanceof CSSNestedDeclarations);
|
assert_equals(scope.cssRules[1].cssText, `.b { }`); // Implicit :scope here.
|
||||||
assert_equals(scope.cssRules[2].cssText, `--z: 1;`);
|
assert_true(scope.cssRules[2] instanceof CSSNestedDeclarations);
|
||||||
|
assert_equals(scope.cssRules[2].cssText, `--z: 1;`);
|
||||||
|
|
||||||
assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
|
assert_true(outer.cssRules[1] instanceof CSSNestedDeclarations);
|
||||||
assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
|
assert_equals(outer.cssRules[1].cssText, `--w: 1;`);
|
||||||
|
} else {
|
||||||
|
assert_equals(outer.cssRules.length, 0);
|
||||||
|
}
|
||||||
}, 'Nested @scope rule');
|
}, 'Nested @scope rule');
|
||||||
|
|
||||||
test(() => {
|
test(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user