diff --git a/components/script/dom/cssconditionrule.rs b/components/script/dom/cssconditionrule.rs index 2d64a220a7e..7499ac19992 100644 --- a/components/script/dom/cssconditionrule.rs +++ b/components/script/dom/cssconditionrule.rs @@ -2,9 +2,11 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::{Locked, SharedRwLock}; +use style::shared_lock::{Locked, SharedRwLock, SharedRwLockReadGuard}; use style::stylesheets::CssRules as StyleCssRules; use crate::dom::bindings::codegen::Bindings::CSSConditionRuleBinding::CSSConditionRuleMethods; @@ -20,7 +22,7 @@ pub(crate) struct CSSConditionRule { cssgroupingrule: CSSGroupingRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - rules: Arc>, + rules: RefCell>>, } impl CSSConditionRule { @@ -30,7 +32,7 @@ impl CSSConditionRule { ) -> CSSConditionRule { CSSConditionRule { cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet), - rules, + rules: RefCell::new(rules), } } @@ -43,7 +45,16 @@ impl CSSConditionRule { } pub(crate) fn clone_rules(&self) -> Arc> { - self.rules.clone() + self.rules.borrow().clone() + } + + pub(crate) fn update_rules( + &self, + rules: Arc>, + guard: &SharedRwLockReadGuard, + ) { + self.cssgroupingrule.update_rules(&rules, guard); + *self.rules.borrow_mut() = rules; } } diff --git a/components/script/dom/cssfontfacerule.rs b/components/script/dom/cssfontfacerule.rs index e6bcba9aa44..480db9b2f7c 100644 --- a/components/script/dom/cssfontfacerule.rs +++ b/components/script/dom/cssfontfacerule.rs @@ -2,6 +2,8 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::{Locked, ToCssWithGuard}; @@ -20,7 +22,7 @@ pub(crate) struct CSSFontFaceRule { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - fontfacerule: Arc>, + fontfacerule: RefCell>>, } impl CSSFontFaceRule { @@ -30,7 +32,7 @@ impl CSSFontFaceRule { ) -> CSSFontFaceRule { CSSFontFaceRule { cssrule: CSSRule::new_inherited(parent_stylesheet), - fontfacerule, + fontfacerule: RefCell::new(fontfacerule), } } @@ -50,6 +52,10 @@ impl CSSFontFaceRule { can_gc, ) } + + pub(crate) fn update_rule(&self, fontfacerule: Arc>) { + *self.fontfacerule.borrow_mut() = fontfacerule; + } } impl SpecificCSSRule for CSSFontFaceRule { @@ -60,6 +66,7 @@ impl SpecificCSSRule for CSSFontFaceRule { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); self.fontfacerule + .borrow() .read_with(&guard) .to_css_string(&guard) .into() diff --git a/components/script/dom/cssgroupingrule.rs b/components/script/dom/cssgroupingrule.rs index 54b8171eba6..d95f223b0bb 100644 --- a/components/script/dom/cssgroupingrule.rs +++ b/components/script/dom/cssgroupingrule.rs @@ -3,8 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use style::shared_lock::SharedRwLock; -use style::stylesheets::{CssRuleType, CssRuleTypes}; +use servo_arc::Arc; +use style::shared_lock::{Locked, SharedRwLock, SharedRwLockReadGuard}; +use style::stylesheets::{CssRuleType, CssRuleTypes, CssRules}; use crate::dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods; use crate::dom::bindings::error::{ErrorResult, Fallible}; @@ -62,6 +63,16 @@ impl CSSGroupingRule { pub(crate) fn shared_lock(&self) -> &SharedRwLock { self.cssrule.shared_lock() } + + pub(crate) fn update_rules( + &self, + rules: &Arc>, + guard: &SharedRwLockReadGuard, + ) { + if let Some(rulelist) = self.rulelist.get() { + rulelist.update_rules(RulesSource::Rules(rules.clone()), guard); + } + } } impl CSSGroupingRuleMethods for CSSGroupingRule { diff --git a/components/script/dom/cssimportrule.rs b/components/script/dom/cssimportrule.rs index 21101e6a356..6e1b432b1e2 100644 --- a/components/script/dom/cssimportrule.rs +++ b/components/script/dom/cssimportrule.rs @@ -2,6 +2,8 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::{Locked, ToCssWithGuard}; @@ -23,7 +25,7 @@ pub(crate) struct CSSImportRule { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - import_rule: Arc>, + import_rule: RefCell>>, } impl CSSImportRule { @@ -33,7 +35,7 @@ impl CSSImportRule { ) -> Self { CSSImportRule { cssrule: CSSRule::new_inherited(parent_stylesheet), - import_rule, + import_rule: RefCell::new(import_rule), } } @@ -50,6 +52,10 @@ impl CSSImportRule { can_gc, ) } + + pub(crate) fn update_rule(&self, import_rule: Arc>) { + *self.import_rule.borrow_mut() = import_rule; + } } impl SpecificCSSRule for CSSImportRule { @@ -60,6 +66,7 @@ impl SpecificCSSRule for CSSImportRule { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); self.import_rule + .borrow() .read_with(&guard) .to_css_string(&guard) .into() @@ -70,7 +77,7 @@ impl CSSImportRuleMethods for CSSImportRule { /// fn GetLayerName(&self) -> Option { let guard = self.cssrule.shared_lock().read(); - match &self.import_rule.read_with(&guard).layer { + match &self.import_rule.borrow().read_with(&guard).layer { ImportLayer::None => None, ImportLayer::Anonymous => Some(DOMString::new()), ImportLayer::Named(name) => Some(DOMString::from_string(name.to_css_string())), diff --git a/components/script/dom/csskeyframerule.rs b/components/script/dom/csskeyframerule.rs index 1296ee5cb7c..659fac30dc0 100644 --- a/components/script/dom/csskeyframerule.rs +++ b/components/script/dom/csskeyframerule.rs @@ -2,9 +2,11 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::{Locked, ToCssWithGuard}; +use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::CssRuleType; use style::stylesheets::keyframes_rule::Keyframe; @@ -24,7 +26,7 @@ pub(crate) struct CSSKeyframeRule { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - keyframerule: Arc>, + keyframerule: RefCell>>, style_decl: MutNullableDom, } @@ -35,7 +37,7 @@ impl CSSKeyframeRule { ) -> CSSKeyframeRule { CSSKeyframeRule { cssrule: CSSRule::new_inherited(parent_stylesheet), - keyframerule, + keyframerule: RefCell::new(keyframerule), style_decl: Default::default(), } } @@ -56,6 +58,17 @@ impl CSSKeyframeRule { can_gc, ) } + + pub(crate) fn update_rule( + &self, + keyframerule: Arc>, + guard: &SharedRwLockReadGuard, + ) { + if let Some(ref style_decl) = self.style_decl.get() { + style_decl.update_property_declaration_block(&keyframerule.read_with(guard).block); + } + *self.keyframerule.borrow_mut() = keyframerule; + } } impl CSSKeyframeRuleMethods for CSSKeyframeRule { @@ -67,7 +80,7 @@ impl CSSKeyframeRuleMethods for CSSKeyframeRule { self.global().as_window(), CSSStyleOwner::CSSRule( Dom::from_ref(self.upcast()), - self.keyframerule.read_with(&guard).block.clone(), + RefCell::new(self.keyframerule.borrow().read_with(&guard).block.clone()), ), None, CSSModificationAccess::ReadWrite, @@ -85,6 +98,7 @@ impl SpecificCSSRule for CSSKeyframeRule { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); self.keyframerule + .borrow() .read_with(&guard) .to_css_string(&guard) .into() diff --git a/components/script/dom/csskeyframesrule.rs b/components/script/dom/csskeyframesrule.rs index 6c88361eb89..58a13ca9852 100644 --- a/components/script/dom/csskeyframesrule.rs +++ b/components/script/dom/csskeyframesrule.rs @@ -2,10 +2,12 @@ * 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/. */ +use std::cell::RefCell; + use cssparser::{Parser, ParserInput}; use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::{Locked, ToCssWithGuard}; +use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::CssRuleType; use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesRule}; use style::values::KeyframesName; @@ -28,7 +30,7 @@ pub(crate) struct CSSKeyframesRule { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - keyframesrule: Arc>, + keyframesrule: RefCell>>, rulelist: MutNullableDom, } @@ -39,7 +41,7 @@ impl CSSKeyframesRule { ) -> CSSKeyframesRule { CSSKeyframesRule { cssrule: CSSRule::new_inherited(parent_stylesheet), - keyframesrule, + keyframesrule: RefCell::new(keyframesrule), rulelist: MutNullableDom::new(None), } } @@ -67,7 +69,7 @@ impl CSSKeyframesRule { CSSRuleList::new( self.global().as_window(), parent_stylesheet, - RulesSource::Keyframes(self.keyframesrule.clone()), + RulesSource::Keyframes(self.keyframesrule.borrow().clone()), can_gc, ) }) @@ -82,6 +84,7 @@ impl CSSKeyframesRule { // This finds the *last* element matching a selector // because that's the rule that applies. Thus, rposition self.keyframesrule + .borrow() .read_with(&guard) .keyframes .iter() @@ -90,6 +93,18 @@ impl CSSKeyframesRule { None } } + + pub(crate) fn update_rule( + &self, + keyframesrule: Arc>, + guard: &SharedRwLockReadGuard, + ) { + if let Some(rulelist) = self.rulelist.get() { + rulelist.update_rules(RulesSource::Keyframes(keyframesrule.clone()), guard); + } + + *self.keyframesrule.borrow_mut() = keyframesrule; + } } impl CSSKeyframesRuleMethods for CSSKeyframesRule { @@ -108,8 +123,10 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule { ); if let Ok(rule) = rule { + self.cssrule.parent_stylesheet().will_modify(); let mut guard = self.cssrule.shared_lock().write(); self.keyframesrule + .borrow() .write_with(&mut guard) .keyframes .push(rule); @@ -135,7 +152,7 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule { // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name fn Name(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); - DOMString::from(&**self.keyframesrule.read_with(&guard).name.as_atom()) + DOMString::from(&**self.keyframesrule.borrow().read_with(&guard).name.as_atom()) } // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name @@ -143,9 +160,10 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule { // Spec deviation: https://github.com/w3c/csswg-drafts/issues/801 // Setting this property to a CSS-wide keyword or `none` does not throw, // it stores a value that serializes as a quoted string. + self.cssrule.parent_stylesheet().will_modify(); let name = KeyframesName::from_ident(&value); let mut guard = self.cssrule.shared_lock().write(); - self.keyframesrule.write_with(&mut guard).name = name; + self.keyframesrule.borrow().write_with(&mut guard).name = name; self.cssrule.parent_stylesheet().notify_invalidations(); Ok(()) } @@ -159,6 +177,7 @@ impl SpecificCSSRule for CSSKeyframesRule { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); self.keyframesrule + .borrow() .read_with(&guard) .to_css_string(&guard) .into() diff --git a/components/script/dom/csslayerblockrule.rs b/components/script/dom/csslayerblockrule.rs index c04eea68482..4f60aff493b 100644 --- a/components/script/dom/csslayerblockrule.rs +++ b/components/script/dom/csslayerblockrule.rs @@ -2,9 +2,11 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::{Locked, ToCssWithGuard}; +use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::{CssRuleType, CssRules, LayerBlockRule}; use style_traits::ToCss; @@ -23,7 +25,7 @@ pub(crate) struct CSSLayerBlockRule { cssgroupingrule: CSSGroupingRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - layerblockrule: Arc, + layerblockrule: RefCell>, } impl CSSLayerBlockRule { @@ -33,7 +35,7 @@ impl CSSLayerBlockRule { ) -> CSSLayerBlockRule { CSSLayerBlockRule { cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet), - layerblockrule, + layerblockrule: RefCell::new(layerblockrule), } } @@ -55,7 +57,17 @@ impl CSSLayerBlockRule { } pub(crate) fn clone_rules(&self) -> Arc> { - self.layerblockrule.rules.clone() + self.layerblockrule.borrow().rules.clone() + } + + pub(crate) fn update_rule( + &self, + layerblockrule: Arc, + guard: &SharedRwLockReadGuard, + ) { + self.cssgroupingrule + .update_rules(&layerblockrule.rules, guard); + *self.layerblockrule.borrow_mut() = layerblockrule; } } @@ -66,14 +78,14 @@ impl SpecificCSSRule for CSSLayerBlockRule { fn get_css(&self) -> DOMString { let guard = self.cssgroupingrule.shared_lock().read(); - self.layerblockrule.to_css_string(&guard).into() + self.layerblockrule.borrow().to_css_string(&guard).into() } } impl CSSLayerBlockRuleMethods for CSSLayerBlockRule { /// fn Name(&self) -> DOMString { - if let Some(name) = &self.layerblockrule.name { + if let Some(name) = &self.layerblockrule.borrow().name { DOMString::from_string(name.to_css_string()) } else { DOMString::new() diff --git a/components/script/dom/csslayerstatementrule.rs b/components/script/dom/csslayerstatementrule.rs index 1206cff9442..6b100fa748d 100644 --- a/components/script/dom/csslayerstatementrule.rs +++ b/components/script/dom/csslayerstatementrule.rs @@ -2,6 +2,8 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use js::rust::MutableHandleValue; use servo_arc::Arc; @@ -24,7 +26,7 @@ pub(crate) struct CSSLayerStatementRule { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - layerstatementrule: Arc, + layerstatementrule: RefCell>, } impl CSSLayerStatementRule { @@ -34,7 +36,7 @@ impl CSSLayerStatementRule { ) -> CSSLayerStatementRule { CSSLayerStatementRule { cssrule: CSSRule::new_inherited(parent_stylesheet), - layerstatementrule, + layerstatementrule: RefCell::new(layerstatementrule), } } @@ -54,6 +56,10 @@ impl CSSLayerStatementRule { can_gc, ) } + + pub(crate) fn update_rule(&self, layerstatementrule: Arc) { + *self.layerstatementrule.borrow_mut() = layerstatementrule; + } } impl SpecificCSSRule for CSSLayerStatementRule { @@ -63,7 +69,10 @@ impl SpecificCSSRule for CSSLayerStatementRule { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); - self.layerstatementrule.to_css_string(&guard).into() + self.layerstatementrule + .borrow() + .to_css_string(&guard) + .into() } } @@ -72,6 +81,7 @@ impl CSSLayerStatementRuleMethods for CSSLayerStatementRul fn NameList(&self, cx: SafeJSContext, can_gc: CanGc, retval: MutableHandleValue) { let names: Vec = self .layerstatementrule + .borrow() .names .iter() .map(|name| DOMString::from_string(name.to_css_string())) diff --git a/components/script/dom/cssmediarule.rs b/components/script/dom/cssmediarule.rs index 88e1a98286a..20c3c358926 100644 --- a/components/script/dom/cssmediarule.rs +++ b/components/script/dom/cssmediarule.rs @@ -2,9 +2,11 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::ToCssWithGuard; +use style::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::{CssRuleType, MediaRule}; use style_traits::ToCss; @@ -24,7 +26,7 @@ pub(crate) struct CSSMediaRule { cssconditionrule: CSSConditionRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - mediarule: Arc, + mediarule: RefCell>, medialist: MutNullableDom, } @@ -33,7 +35,7 @@ impl CSSMediaRule { let list = mediarule.rules.clone(); CSSMediaRule { cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list), - mediarule, + mediarule: RefCell::new(mediarule), medialist: MutNullableDom::new(None), } } @@ -57,7 +59,7 @@ impl CSSMediaRule { MediaList::new( self.global().as_window(), self.cssconditionrule.parent_stylesheet(), - self.mediarule.media_queries.clone(), + self.mediarule.borrow().media_queries.clone(), can_gc, ) }) @@ -66,8 +68,21 @@ impl CSSMediaRule { /// pub(crate) fn get_condition_text(&self) -> DOMString { let guard = self.cssconditionrule.shared_lock().read(); - let list = self.mediarule.media_queries.read_with(&guard); - list.to_css_string().into() + self.mediarule + .borrow() + .media_queries + .read_with(&guard) + .to_css_string() + .into() + } + + pub(crate) fn update_rule(&self, mediarule: Arc, guard: &SharedRwLockReadGuard) { + self.cssconditionrule + .update_rules(mediarule.rules.clone(), guard); + if let Some(medialist) = self.medialist.get() { + medialist.update_media_list(mediarule.media_queries.clone()); + } + *self.mediarule.borrow_mut() = mediarule; } } @@ -78,7 +93,7 @@ impl SpecificCSSRule for CSSMediaRule { fn get_css(&self) -> DOMString { let guard = self.cssconditionrule.shared_lock().read(); - self.mediarule.to_css_string(&guard).into() + self.mediarule.borrow().to_css_string(&guard).into() } } diff --git a/components/script/dom/cssnamespacerule.rs b/components/script/dom/cssnamespacerule.rs index 2de72c844ef..d41374ef713 100644 --- a/components/script/dom/cssnamespacerule.rs +++ b/components/script/dom/cssnamespacerule.rs @@ -2,6 +2,8 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::ToCssWithGuard; @@ -21,7 +23,7 @@ pub(crate) struct CSSNamespaceRule { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - namespacerule: Arc, + namespacerule: RefCell>, } impl CSSNamespaceRule { @@ -31,7 +33,7 @@ impl CSSNamespaceRule { ) -> CSSNamespaceRule { CSSNamespaceRule { cssrule: CSSRule::new_inherited(parent_stylesheet), - namespacerule, + namespacerule: RefCell::new(namespacerule), } } @@ -51,12 +53,17 @@ impl CSSNamespaceRule { can_gc, ) } + + pub(crate) fn update_rule(&self, namespacerule: Arc) { + *self.namespacerule.borrow_mut() = namespacerule; + } } impl CSSNamespaceRuleMethods for CSSNamespaceRule { // https://drafts.csswg.org/cssom/#dom-cssnamespacerule-prefix fn Prefix(&self) -> DOMString { self.namespacerule + .borrow() .prefix .as_ref() .map(|s| s.to_string().into()) @@ -65,7 +72,7 @@ impl CSSNamespaceRuleMethods for CSSNamespaceRule { // https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri fn NamespaceURI(&self) -> DOMString { - (**self.namespacerule.url).into() + (**self.namespacerule.borrow().url).into() } } @@ -76,6 +83,6 @@ impl SpecificCSSRule for CSSNamespaceRule { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); - self.namespacerule.to_css_string(&guard).into() + self.namespacerule.borrow().to_css_string(&guard).into() } } diff --git a/components/script/dom/cssnesteddeclarations.rs b/components/script/dom/cssnesteddeclarations.rs index 2f2585631e1..635772b8805 100644 --- a/components/script/dom/cssnesteddeclarations.rs +++ b/components/script/dom/cssnesteddeclarations.rs @@ -2,9 +2,11 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::{Locked, ToCssWithGuard}; +use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::{CssRuleType, NestedDeclarationsRule}; use crate::dom::bindings::codegen::Bindings::CSSNestedDeclarationsBinding::CSSNestedDeclarationsMethods; @@ -23,7 +25,7 @@ pub(crate) struct CSSNestedDeclarations { cssrule: CSSRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - nesteddeclarationsrule: Arc>, + nesteddeclarationsrule: RefCell>>, style_decl: MutNullableDom, } @@ -34,7 +36,7 @@ impl CSSNestedDeclarations { ) -> Self { Self { cssrule: CSSRule::new_inherited(parent_stylesheet), - nesteddeclarationsrule, + nesteddeclarationsrule: RefCell::new(nesteddeclarationsrule), style_decl: Default::default(), } } @@ -55,6 +57,18 @@ impl CSSNestedDeclarations { can_gc, ) } + + pub(crate) fn update_rule( + &self, + nesteddeclarationsrule: Arc>, + guard: &SharedRwLockReadGuard, + ) { + if let Some(ref style_decl) = self.style_decl.get() { + style_decl + .update_property_declaration_block(&nesteddeclarationsrule.read_with(guard).block); + } + *self.nesteddeclarationsrule.borrow_mut() = nesteddeclarationsrule; + } } impl SpecificCSSRule for CSSNestedDeclarations { @@ -65,6 +79,7 @@ impl SpecificCSSRule for CSSNestedDeclarations { fn get_css(&self) -> DOMString { let guard = self.cssrule.shared_lock().read(); self.nesteddeclarationsrule + .borrow() .read_with(&guard) .to_css_string(&guard) .into() @@ -80,7 +95,13 @@ impl CSSNestedDeclarationsMethods for CSSNestedDeclaration self.global().as_window(), CSSStyleOwner::CSSRule( Dom::from_ref(self.upcast()), - self.nesteddeclarationsrule.read_with(&guard).block.clone(), + RefCell::new( + self.nesteddeclarationsrule + .borrow() + .read_with(&guard) + .block + .clone(), + ), ), None, CSSModificationAccess::ReadWrite, diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index 172d29c26c9..5e0a2258e20 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -5,7 +5,7 @@ use std::cell::Cell; use dom_struct::dom_struct; -use style::shared_lock::SharedRwLock; +use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard}; use style::stylesheets::{CssRule as StyleCssRule, CssRuleType}; use crate::dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods; @@ -157,7 +157,73 @@ impl CSSRule { } pub(crate) fn shared_lock(&self) -> &SharedRwLock { - &self.parent_stylesheet.style_stylesheet().shared_lock + self.parent_stylesheet.shared_lock() + } + + pub(crate) fn update_rule(&self, style_rule: &StyleCssRule, guard: &SharedRwLockReadGuard) { + match style_rule { + StyleCssRule::Import(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone()); + } + }, + StyleCssRule::Style(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone(), guard); + } + }, + StyleCssRule::FontFace(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone()); + } + }, + StyleCssRule::FontFeatureValues(_) => unimplemented!(), + StyleCssRule::CounterStyle(_) => unimplemented!(), + StyleCssRule::Keyframes(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone(), guard); + } + }, + StyleCssRule::Media(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone(), guard); + } + }, + StyleCssRule::Namespace(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone()); + } + }, + StyleCssRule::Supports(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone(), guard); + } + }, + StyleCssRule::Page(_) => unreachable!(), + StyleCssRule::Container(_) => unimplemented!(), // TODO + StyleCssRule::Document(_) => unimplemented!(), // TODO + StyleCssRule::LayerBlock(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone(), guard); + } + }, + StyleCssRule::LayerStatement(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone()); + } + }, + StyleCssRule::FontPaletteValues(_) => unimplemented!(), // TODO + StyleCssRule::Property(_) => unimplemented!(), // TODO + StyleCssRule::Margin(_) => unimplemented!(), // TODO + StyleCssRule::Scope(_) => unimplemented!(), // TODO + StyleCssRule::StartingStyle(_) => unimplemented!(), // TODO + StyleCssRule::PositionTry(_) => unimplemented!(), // TODO + StyleCssRule::NestedDeclarations(s) => { + if let Some(rule) = self.downcast::() { + rule.update_rule(s.clone(), guard); + } + }, + } } } diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index 899d55cccee..33fa4d7b556 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -4,9 +4,13 @@ #![allow(unsafe_code)] +use std::cell::RefCell; + use dom_struct::dom_struct; +use itertools::izip; +use script_bindings::inheritance::Castable; use servo_arc::Arc; -use style::shared_lock::Locked; +use style::shared_lock::{Locked, SharedRwLockReadGuard}; use style::stylesheets::{ AllowImportRules, CssRuleType, CssRuleTypes, CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError, StylesheetLoader as StyleStylesheetLoader, @@ -44,7 +48,7 @@ pub(crate) struct CSSRuleList { reflector_: Reflector, parent_stylesheet: Dom, #[ignore_malloc_size_of = "Arc"] - rules: RulesSource, + rules: RefCell, dom_rules: DomRefCell>>, } @@ -78,7 +82,7 @@ impl CSSRuleList { CSSRuleList { reflector_: Reflector::new(), parent_stylesheet: Dom::from_ref(parent_stylesheet), - rules, + rules: RefCell::new(rules), dom_rules: DomRefCell::new(dom_rules), } } @@ -107,8 +111,9 @@ impl CSSRuleList { parse_relative_rule_type: Option, can_gc: CanGc, ) -> Fallible { - let css_rules = if let RulesSource::Rules(ref rules) = self.rules { - rules + self.parent_stylesheet.will_modify(); + let css_rules = if let RulesSource::Rules(rules) = &*self.rules.borrow() { + rules.clone() } else { panic!("Called insert_rule on non-CssRule-backed CSSRuleList"); }; @@ -144,6 +149,7 @@ impl CSSRuleList { .map_err(Convert::convert)?; let parent_stylesheet = &*self.parent_stylesheet; + parent_stylesheet.will_modify(); let dom_rule = CSSRule::new_specific(window, parent_stylesheet, new_rule, can_gc); self.dom_rules .borrow_mut() @@ -154,10 +160,12 @@ impl CSSRuleList { /// In case of a keyframe rule, index must be valid. pub(crate) fn remove_rule(&self, index: u32) -> ErrorResult { + self.parent_stylesheet.will_modify(); + let index = index as usize; let mut guard = self.parent_stylesheet.shared_lock().write(); - match self.rules { + match *self.rules.borrow() { RulesSource::Rules(ref css_rules) => { css_rules .write_with(&mut guard) @@ -199,7 +207,7 @@ impl CSSRuleList { rule.or_init(|| { let parent_stylesheet = &self.parent_stylesheet; let lock = parent_stylesheet.shared_lock(); - match self.rules { + match *self.rules.borrow() { RulesSource::Rules(ref rules) => { let rule = { let guard = lock.read(); @@ -235,11 +243,48 @@ impl CSSRuleList { /// Should only be called for keyframes-backed rules, use insert_rule /// for CssRules-backed rules pub(crate) fn append_lazy_dom_rule(&self) { - if let RulesSource::Rules(..) = self.rules { + if let RulesSource::Rules(..) = &*self.rules.borrow() { panic!("Can only call append_lazy_rule with keyframes-backed CSSRules"); } self.dom_rules.borrow_mut().push(MutNullableDom::new(None)); } + + pub(super) fn update_rules(&self, rules: RulesSource, guard: &SharedRwLockReadGuard) { + let dom_rules = self.dom_rules.borrow(); + match rules { + RulesSource::Rules(ref css_rules) => { + if let RulesSource::Keyframes(..) = &*self.rules.borrow() { + panic!("Called update_rules on non-CssRule-backed CSSRuleList with CssRules"); + } + + let css_rules_iter = css_rules.read_with(guard).0.iter(); + for (dom_rule, css_rule) in izip!(dom_rules.iter(), css_rules_iter) { + let Some(dom_rule) = dom_rule.get() else { + continue; + }; + dom_rule.update_rule(css_rule, guard); + } + }, + RulesSource::Keyframes(ref keyframesrule) => { + if let RulesSource::Rules(..) = &*self.rules.borrow() { + panic!("Called update_rules on CssRule-backed CSSRuleList with non-CssRules"); + } + + let keyframerules_iter = keyframesrule.read_with(guard).keyframes.iter(); + for (dom_rule, keyframerule) in izip!(dom_rules.iter(), keyframerules_iter) { + let Some(dom_rule) = dom_rule.get() else { + continue; + }; + let Some(dom_rule) = dom_rule.downcast::() else { + continue; + }; + dom_rule.update_rule(keyframerule.clone(), guard); + } + }, + } + + *self.rules.borrow_mut() = rules; + } } impl CSSRuleListMethods for CSSRuleList { diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 93df19026e8..47457e7b823 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -2,6 +2,7 @@ * 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/. */ +use std::cell::RefCell; use std::cmp::Ordering; use std::sync::LazyLock; @@ -53,7 +54,7 @@ pub(crate) enum CSSStyleOwner { Dom, #[ignore_malloc_size_of = "Arc"] #[no_trace] - Arc>, + RefCell>>, ), } @@ -119,9 +120,10 @@ impl CSSStyleOwner { result }, CSSStyleOwner::CSSRule(ref rule, ref pdb) => { + rule.parent_stylesheet().will_modify(); let result = { let mut guard = rule.shared_lock().write(); - f(&mut *pdb.write_with(&mut guard), &mut changed) + f(&mut *pdb.borrow().write_with(&mut guard), &mut changed) }; if changed { rule.parent_stylesheet().notify_invalidations(); @@ -152,7 +154,7 @@ impl CSSStyleOwner { }, CSSStyleOwner::CSSRule(ref rule, ref pdb) => { let guard = rule.shared_lock().read(); - f(pdb.read_with(&guard)) + f(pdb.borrow().read_with(&guard)) }, } } @@ -265,6 +267,17 @@ impl CSSStyleDeclaration { ) } + pub(crate) fn update_property_declaration_block( + &self, + pdb: &Arc>, + ) { + if let CSSStyleOwner::CSSRule(_, pdb_cell) = &self.owner { + *pdb_cell.borrow_mut() = pdb.clone(); + } else { + panic!("update_rule called on CSSStyleDeclaration with a Element owner"); + } + } + fn get_computed_style(&self, property: PropertyId) -> DOMString { match self.owner { CSSStyleOwner::CSSRule(..) => { diff --git a/components/script/dom/cssstylerule.rs b/components/script/dom/cssstylerule.rs index a94d0b5c6ed..c43d2a29369 100644 --- a/components/script/dom/cssstylerule.rs +++ b/components/script/dom/cssstylerule.rs @@ -2,6 +2,7 @@ * 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/. */ +use std::cell::RefCell; use std::mem; use cssparser::{Parser as CssParser, ParserInput as CssParserInput, ToCss}; @@ -9,7 +10,7 @@ use dom_struct::dom_struct; use selectors::parser::{ParseRelative, SelectorList}; use servo_arc::Arc; use style::selector_parser::SelectorParser; -use style::shared_lock::{Locked, ToCssWithGuard}; +use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::{CssRuleType, CssRules, Origin, StyleRule}; use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::CSSStyleRuleMethods; @@ -29,7 +30,7 @@ pub(crate) struct CSSStyleRule { cssgroupingrule: CSSGroupingRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - stylerule: Arc>, + stylerule: RefCell>>, style_decl: MutNullableDom, } @@ -40,7 +41,7 @@ impl CSSStyleRule { ) -> CSSStyleRule { CSSStyleRule { cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet), - stylerule, + stylerule: RefCell::new(stylerule), style_decl: Default::default(), } } @@ -63,11 +64,28 @@ impl CSSStyleRule { let lock = self.cssgroupingrule.shared_lock(); let mut guard = lock.write(); self.stylerule + .borrow() .write_with(&mut guard) .rules .get_or_insert_with(|| CssRules::new(vec![], lock)) .clone() } + + pub(crate) fn update_rule( + &self, + stylerule: Arc>, + guard: &SharedRwLockReadGuard, + ) { + if let Some(ref rules) = stylerule.read_with(guard).rules { + self.cssgroupingrule.update_rules(rules, guard); + } + + if let Some(ref style_decl) = self.style_decl.get() { + style_decl.update_property_declaration_block(&stylerule.read_with(guard).block); + } + + *self.stylerule.borrow_mut() = stylerule; + } } impl SpecificCSSRule for CSSStyleRule { @@ -78,6 +96,7 @@ impl SpecificCSSRule for CSSStyleRule { fn get_css(&self) -> DOMString { let guard = self.cssgroupingrule.shared_lock().read(); self.stylerule + .borrow() .read_with(&guard) .to_css_string(&guard) .into() @@ -93,7 +112,7 @@ impl CSSStyleRuleMethods for CSSStyleRule { self.global().as_window(), CSSStyleOwner::CSSRule( Dom::from_ref(self.upcast()), - self.stylerule.read_with(&guard).block.clone(), + RefCell::new(self.stylerule.borrow().read_with(&guard).block.clone()), ), None, CSSModificationAccess::ReadWrite, @@ -105,8 +124,13 @@ impl CSSStyleRuleMethods for CSSStyleRule { // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext fn SelectorText(&self) -> DOMString { let guard = self.cssgroupingrule.shared_lock().read(); - let stylerule = self.stylerule.read_with(&guard); - DOMString::from_string(stylerule.selectors.to_css_string()) + DOMString::from_string( + self.stylerule + .borrow() + .read_with(&guard) + .selectors + .to_css_string(), + ) } // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext @@ -115,7 +139,8 @@ impl CSSStyleRuleMethods for CSSStyleRule { .cssgroupingrule .parent_stylesheet() .style_stylesheet() - .contents; + .contents + .clone(); // It's not clear from the spec if we should use the stylesheet's namespaces. // https://github.com/w3c/csswg-drafts/issues/1511 let namespaces = contents.namespaces.read(); @@ -131,10 +156,13 @@ impl CSSStyleRuleMethods for CSSStyleRule { // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style // rule? if let Ok(mut s) = SelectorList::parse(&parser, &mut css_parser, ParseRelative::No) { + self.cssgroupingrule.parent_stylesheet().will_modify(); // This mirrors what we do in CSSStyleOwner::mutate_associated_block. let mut guard = self.cssgroupingrule.shared_lock().write(); - let stylerule = self.stylerule.write_with(&mut guard); - mem::swap(&mut stylerule.selectors, &mut s); + mem::swap( + &mut self.stylerule.borrow().write_with(&mut guard).selectors, + &mut s, + ); self.cssgroupingrule .parent_stylesheet() .notify_invalidations(); diff --git a/components/script/dom/cssstylesheet.rs b/components/script/dom/cssstylesheet.rs index 0e1110deff9..429624ae23c 100644 --- a/components/script/dom/cssstylesheet.rs +++ b/components/script/dom/cssstylesheet.rs @@ -2,16 +2,17 @@ * 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/. */ -use std::cell::Cell; +use std::cell::{Cell, Ref}; use std::rc::Rc; use dom_struct::dom_struct; use js::rust::HandleObject; +use script_bindings::inheritance::Castable; use script_bindings::realms::InRealm; use script_bindings::root::Dom; use servo_arc::Arc; use style::media_queries::MediaList as StyleMediaList; -use style::shared_lock::SharedRwLock; +use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard}; use style::stylesheets::{ AllowImportRules, CssRuleTypes, Origin, Stylesheet as StyleStyleSheet, UrlExtraData, }; @@ -33,6 +34,7 @@ use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::cssrulelist::{CSSRuleList, RulesSource}; use crate::dom::document::Document; use crate::dom::element::Element; +use crate::dom::htmlstyleelement::HTMLStyleElement; use crate::dom::medialist::MediaList; use crate::dom::node::NodeTraits; use crate::dom::stylesheet::StyleSheet; @@ -55,7 +57,12 @@ pub(crate) struct CSSStyleSheet { /// The inner Stylo's [Stylesheet]. #[ignore_malloc_size_of = "Arc"] #[no_trace] - style_stylesheet: Arc, + style_stylesheet: DomRefCell>, + + /// The inner Stylo's [SharedRwLock], stored at here to avoid referencing + /// temporary variables. + #[no_trace] + style_shared_lock: SharedRwLock, /// origin_clean: Cell, @@ -86,7 +93,8 @@ impl CSSStyleSheet { stylesheet: StyleSheet::new_inherited(type_, href, title), owner_node: MutNullableDom::new(owner), rulelist: MutNullableDom::new(None), - style_stylesheet: stylesheet, + style_shared_lock: stylesheet.shared_lock.clone(), + style_stylesheet: DomRefCell::new(stylesheet), origin_clean: Cell::new(true), constructor_document: constructor_document.map(Dom::from_ref), adopters: Default::default(), @@ -150,7 +158,7 @@ impl CSSStyleSheet { fn rulelist(&self, can_gc: CanGc) -> DomRoot { self.rulelist.or_init(|| { - let rules = self.style_stylesheet.contents.rules.clone(); + let rules = self.style_stylesheet.borrow().contents.rules.clone(); CSSRuleList::new( self.global().as_window(), self, @@ -161,7 +169,7 @@ impl CSSStyleSheet { } pub(crate) fn disabled(&self) -> bool { - self.style_stylesheet.disabled() + self.style_stylesheet.borrow().disabled() } pub(crate) fn owner_node(&self) -> Option> { @@ -169,7 +177,7 @@ impl CSSStyleSheet { } pub(crate) fn set_disabled(&self, disabled: bool) { - if self.style_stylesheet.set_disabled(disabled) { + if self.style_stylesheet.borrow().set_disabled(disabled) { self.notify_invalidations(); } } @@ -179,15 +187,11 @@ impl CSSStyleSheet { } pub(crate) fn shared_lock(&self) -> &SharedRwLock { - &self.style_stylesheet.shared_lock + &self.style_shared_lock } - pub(crate) fn style_stylesheet(&self) -> &StyleStyleSheet { - &self.style_stylesheet - } - - pub(crate) fn style_stylesheet_arc(&self) -> &Arc { - &self.style_stylesheet + pub(crate) fn style_stylesheet(&self) -> Ref<'_, Arc> { + self.style_stylesheet.borrow() } pub(crate) fn set_origin_clean(&self, origin_clean: bool) { @@ -231,6 +235,36 @@ impl CSSStyleSheet { } } + pub(crate) fn will_modify(&self) { + let Some(node) = self.owner_node.get() else { + return; + }; + + let Some(node) = node.downcast::() else { + return; + }; + + node.will_modify_stylesheet(); + } + + pub(crate) fn update_style_stylesheet( + &self, + style_stylesheet: &Arc, + guard: &SharedRwLockReadGuard, + ) { + // When the shared `StylesheetContents` is about to be modified, + // `CSSStyleSheet::owner_node` performs a copy-on-write to avoid + // affecting other sharers, see `CSSStyleSheet::will_modify`. And + // then updates the references to `CssRule` or `PropertyDeclarationBlock` + // stored in the CSSOMs to ensure that modifications are made only + // on the new copy. + *self.style_stylesheet.borrow_mut() = style_stylesheet.clone(); + if let Some(rulelist) = self.rulelist.get() { + let rules = style_stylesheet.contents.rules.clone(); + rulelist.update_rules(RulesSource::Rules(rules), guard); + } + } + /// Invalidate all stylesheet set this stylesheet is a part on. pub(crate) fn notify_invalidations(&self) { if let Some(owner) = self.owner_node() { @@ -402,8 +436,12 @@ impl CSSStyleSheetMethods for CSSStyleSheet { let global = sheet.global(); let window = global.as_window(); + sheet.will_modify(); + + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered(); StyleStyleSheet::update_from_str( - &sheet.style_stylesheet, + &sheet.style_stylesheet(), &text, UrlExtraData(window.get_url().get_arc()), None, @@ -441,8 +479,12 @@ impl CSSStyleSheetMethods for CSSStyleSheet { let global = self.global(); let window = global.as_window(); + self.will_modify(); + + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered(); StyleStyleSheet::update_from_str( - &self.style_stylesheet, + &self.style_stylesheet(), &text, UrlExtraData(window.get_url().get_arc()), None, diff --git a/components/script/dom/csssupportsrule.rs b/components/script/dom/csssupportsrule.rs index c920cd5f144..cd5c6d815b1 100644 --- a/components/script/dom/csssupportsrule.rs +++ b/components/script/dom/csssupportsrule.rs @@ -2,9 +2,11 @@ * 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/. */ +use std::cell::RefCell; + use dom_struct::dom_struct; use servo_arc::Arc; -use style::shared_lock::ToCssWithGuard; +use style::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; use style::stylesheets::{CssRuleType, SupportsRule}; use style_traits::ToCss; @@ -22,7 +24,7 @@ pub(crate) struct CSSSupportsRule { cssconditionrule: CSSConditionRule, #[ignore_malloc_size_of = "Arc"] #[no_trace] - supportsrule: Arc, + supportsrule: RefCell>, } impl CSSSupportsRule { @@ -33,7 +35,7 @@ impl CSSSupportsRule { let list = supportsrule.rules.clone(); CSSSupportsRule { cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list), - supportsrule, + supportsrule: RefCell::new(supportsrule), } } @@ -56,7 +58,17 @@ impl CSSSupportsRule { /// pub(crate) fn get_condition_text(&self) -> DOMString { - self.supportsrule.condition.to_css_string().into() + self.supportsrule.borrow().condition.to_css_string().into() + } + + pub(crate) fn update_rule( + &self, + supportsrule: Arc, + guard: &SharedRwLockReadGuard, + ) { + self.cssconditionrule + .update_rules(supportsrule.rules.clone(), guard); + *self.supportsrule.borrow_mut() = supportsrule; } } @@ -67,6 +79,6 @@ impl SpecificCSSRule for CSSSupportsRule { fn get_css(&self) -> DOMString { let guard = self.cssconditionrule.shared_lock().read(); - self.supportsrule.to_css_string(&guard).into() + self.supportsrule.borrow().to_css_string(&guard).into() } } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 044f66c276e..14c6a4596c7 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -4021,7 +4021,7 @@ impl Document { debug_assert!(cssom_stylesheet.is_constructed()); let stylesheets = &mut *self.stylesheets.borrow_mut(); - let sheet = cssom_stylesheet.style_stylesheet_arc().clone(); + let sheet = cssom_stylesheet.style_stylesheet().clone(); let insertion_point = stylesheets .iter() diff --git a/components/script/dom/documentorshadowroot.rs b/components/script/dom/documentorshadowroot.rs index 25af818f7c3..db51cb8e713 100644 --- a/components/script/dom/documentorshadowroot.rs +++ b/components/script/dom/documentorshadowroot.rs @@ -397,7 +397,7 @@ impl DocumentOrShadowRoot { if stylesheet_remove_set.insert(sheet_to_remove) { owner.remove_stylesheet( StylesheetSource::Constructed(sheet_to_remove.clone()), - sheet_to_remove.style_stylesheet_arc(), + &sheet_to_remove.style_stylesheet(), ); sheet_to_remove.remove_adopter(owner); } @@ -416,7 +416,7 @@ impl DocumentOrShadowRoot { // around. owner.remove_stylesheet( StylesheetSource::Constructed(sheet.clone()), - sheet.style_stylesheet_arc(), + &sheet.style_stylesheet(), ); } else { sheet.add_adopter(owner.clone()); diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 333b43d8bbe..a53859fb2ef 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; +use std::sync::atomic::{AtomicBool, Ordering}; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; @@ -11,7 +12,8 @@ use net_traits::ReferrerPolicy; use script_bindings::root::Dom; use servo_arc::Arc; use style::media_queries::MediaList as StyleMediaList; -use style::stylesheets::{AllowImportRules, Origin, Stylesheet, UrlExtraData}; +use style::shared_lock::DeepCloneWithLock; +use style::stylesheets::{AllowImportRules, Origin, Stylesheet, StylesheetContents, UrlExtraData}; use crate::dom::attr::Attr; use crate::dom::bindings::cell::DomRefCell; @@ -29,6 +31,7 @@ use crate::dom::htmlelement::HTMLElement; use crate::dom::medialist::MediaList; use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits, UnbindContext}; use crate::dom::stylesheet::StyleSheet as DOMStyleSheet; +use crate::dom::stylesheetcontentscache::{StylesheetContentsCache, StylesheetContentsCacheKey}; use crate::dom::virtualmethods::VirtualMethods; use crate::script_runtime::CanGc; use crate::stylesheet_loader::{StylesheetLoader, StylesheetOwner}; @@ -39,6 +42,8 @@ pub(crate) struct HTMLStyleElement { #[conditional_malloc_size_of] #[no_trace] stylesheet: DomRefCell>>, + #[no_trace] + stylesheetcontents_cache_key: DomRefCell>, cssom_stylesheet: MutNullableDom, /// parser_inserted: Cell, @@ -57,6 +62,7 @@ impl HTMLStyleElement { HTMLStyleElement { htmlelement: HTMLElement::new_inherited(local_name, prefix, document), stylesheet: DomRefCell::new(None), + stylesheetcontents_cache_key: DomRefCell::new(None), cssom_stylesheet: MutNullableDom::new(None), parser_inserted: Cell::new(creator.is_parser_created()), in_stack_of_open_elements: Cell::new(creator.is_parser_created()), @@ -125,19 +131,41 @@ impl HTMLStyleElement { let shared_lock = node.owner_doc().style_shared_lock().clone(); let mq = Arc::new(shared_lock.wrap(self.create_media_list(&self.Media()))); let loader = StylesheetLoader::for_element(self.upcast()); - let sheet = Stylesheet::from_str( + + let stylesheetcontents_create_callback = || { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered(); + StylesheetContents::from_str( + &data, + UrlExtraData(window.get_url().get_arc()), + Origin::Author, + &shared_lock, + Some(&loader), + window.css_error_reporter(), + doc.quirks_mode(), + AllowImportRules::Yes, + /* sanitized_output = */ None, + ) + }; + + // For duplicate style sheets with identical content, `StylesheetContents` can be reused + // to avoid reedundant parsing of the style sheets. Additionally, the cache hit rate of + // stylo's `CascadeDataCache` can now be significantly improved. When shared `StylesheetContents` + // is modified, copy-on-write will occur, see `CSSStyleSheet::will_modify`. + let (cache_key, contents) = StylesheetContentsCache::get_or_insert_with( &data, + &shared_lock, UrlExtraData(window.get_url().get_arc()), - Origin::Author, - mq, - shared_lock, - Some(&loader), - window.css_error_reporter(), doc.quirks_mode(), - AllowImportRules::Yes, + stylesheetcontents_create_callback, ); - let sheet = Arc::new(sheet); + let sheet = Arc::new(Stylesheet { + contents, + shared_lock, + media: mq, + disabled: AtomicBool::new(false), + }); // No subresource loads were triggered, queue load event if self.pending_loads.get() == 0 { @@ -147,22 +175,44 @@ impl HTMLStyleElement { .queue_simple_event(self.upcast(), atom!("load")); } - self.set_stylesheet(sheet); + self.set_stylesheet(sheet, cache_key, true); } // FIXME(emilio): This is duplicated with HTMLLinkElement::set_stylesheet. + // + // With the reuse of `StylesheetContent` for same stylesheet string content, + // this function has a bit difference with `HTMLLinkElement::set_stylesheet` now. #[cfg_attr(crown, allow(crown::unrooted_must_root))] - pub(crate) fn set_stylesheet(&self, s: Arc) { + pub(crate) fn set_stylesheet( + &self, + s: Arc, + cache_key: Option, + need_clean_cssom: bool, + ) { let stylesheets_owner = self.stylesheet_list_owner(); if let Some(ref s) = *self.stylesheet.borrow() { stylesheets_owner - .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), s) + .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), s); } + + if need_clean_cssom { + self.clean_stylesheet_ownership(); + } else if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() { + let guard = s.shared_lock.read(); + cssom_stylesheet.update_style_stylesheet(&s, &guard); + } + *self.stylesheet.borrow_mut() = Some(s.clone()); - self.clean_stylesheet_ownership(); + *self.stylesheetcontents_cache_key.borrow_mut() = cache_key; stylesheets_owner.add_owned_stylesheet(self.upcast(), s); } + pub(crate) fn will_modify_stylesheet(&self) { + if let Some(stylesheet_with_owned_contents) = self.create_owned_contents_stylesheet() { + self.set_stylesheet(stylesheet_with_owned_contents, None, false); + } + } + pub(crate) fn get_stylesheet(&self) -> Option> { self.stylesheet.borrow().clone() } @@ -184,18 +234,55 @@ impl HTMLStyleElement { }) } + fn create_owned_contents_stylesheet(&self) -> Option> { + let cache_key = self.stylesheetcontents_cache_key.borrow_mut().take()?; + if cache_key.is_uniquely_owned() { + StylesheetContentsCache::remove(cache_key); + return None; + } + + let stylesheet_with_shared_contents = self.stylesheet.borrow().clone()?; + let lock = stylesheet_with_shared_contents.shared_lock.clone(); + let guard = stylesheet_with_shared_contents.shared_lock.read(); + let stylesheet_with_owned_contents = Arc::new(Stylesheet { + contents: Arc::new( + stylesheet_with_shared_contents + .contents + .deep_clone_with_lock(&lock, &guard), + ), + shared_lock: lock, + media: stylesheet_with_shared_contents.media.clone(), + disabled: AtomicBool::new( + stylesheet_with_shared_contents + .disabled + .load(Ordering::SeqCst), + ), + }); + + Some(stylesheet_with_owned_contents) + } + fn clean_stylesheet_ownership(&self) { if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() { + // If the CSSOMs change from having an owner node to being ownerless, they may still + // potentially modify shared stylesheets. Thus, create an new `Stylesheet` with owned + // `StylesheetContents` to ensure that the potentially modifications are only made on + // the owned `StylesheetContents`. + if let Some(stylesheet) = self.create_owned_contents_stylesheet() { + let guard = stylesheet.shared_lock.read(); + cssom_stylesheet.update_style_stylesheet(&stylesheet, &guard); + } cssom_stylesheet.set_owner_node(None); } self.cssom_stylesheet.set(None); } fn remove_stylesheet(&self) { + self.clean_stylesheet_ownership(); if let Some(s) = self.stylesheet.borrow_mut().take() { - self.clean_stylesheet_ownership(); self.stylesheet_list_owner() - .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), &s) + .remove_stylesheet(StylesheetSource::Element(Dom::from_ref(self.upcast())), &s); + let _ = self.stylesheetcontents_cache_key.borrow_mut().take(); } } } diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs index 8d22c8a0faa..8dc53f6a9fd 100644 --- a/components/script/dom/medialist.rs +++ b/components/script/dom/medialist.rs @@ -2,6 +2,8 @@ * 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/. */ +use std::cell::RefCell; + use cssparser::{Parser, ParserInput}; use dom_struct::dom_struct; use servo_arc::Arc; @@ -26,7 +28,7 @@ pub(crate) struct MediaList { parent_stylesheet: Dom, #[ignore_malloc_size_of = "Arc"] #[no_trace] - media_queries: Arc>, + media_queries: RefCell>>, } impl MediaList { @@ -38,7 +40,7 @@ impl MediaList { MediaList { parent_stylesheet: Dom::from_ref(parent_stylesheet), reflector_: Reflector::new(), - media_queries, + media_queries: RefCell::new(media_queries), } } @@ -57,7 +59,7 @@ impl MediaList { } fn shared_lock(&self) -> &SharedRwLock { - &self.parent_stylesheet.style_stylesheet().shared_lock + self.parent_stylesheet.shared_lock() } /// @@ -109,7 +111,11 @@ impl MediaList { pub(crate) fn clone_media_list(&self) -> StyleMediaList { let guard = self.shared_lock().read(); - self.media_queries.read_with(&guard).clone() + self.media_queries.borrow().read_with(&guard).clone() + } + + pub(crate) fn update_media_list(&self, media_queries: Arc>) { + *self.media_queries.borrow_mut() = media_queries; } } @@ -117,14 +123,21 @@ impl MediaListMethods for MediaList { /// fn MediaText(&self) -> DOMString { let guard = self.shared_lock().read(); - DOMString::from(self.media_queries.read_with(&guard).to_css_string()) + DOMString::from( + self.media_queries + .borrow() + .read_with(&guard) + .to_css_string(), + ) } /// fn SetMediaText(&self, value: DOMString) { + self.parent_stylesheet.will_modify(); let global = self.global(); let mut guard = self.shared_lock().write(); - let media_queries = self.media_queries.write_with(&mut guard); + let media_queries_borrowed = self.media_queries.borrow(); + let media_queries = media_queries_borrowed.write_with(&mut guard); *media_queries = Self::parse_media_list(&value, global.as_window()); self.parent_stylesheet.notify_invalidations(); } @@ -132,13 +145,18 @@ impl MediaListMethods for MediaList { // https://drafts.csswg.org/cssom/#dom-medialist-length fn Length(&self) -> u32 { let guard = self.shared_lock().read(); - self.media_queries.read_with(&guard).media_queries.len() as u32 + self.media_queries + .borrow() + .read_with(&guard) + .media_queries + .len() as u32 } /// fn Item(&self, index: u32) -> Option { let guard = self.shared_lock().read(); self.media_queries + .borrow() .read_with(&guard) .media_queries .get(index as usize) @@ -160,18 +178,28 @@ impl MediaListMethods for MediaList { return; } // Step 3 - let m_serialized = m.clone().unwrap().to_css_string(); - let mut guard = self.shared_lock().write(); - let mq = self.media_queries.write_with(&mut guard); - let any = mq - .media_queries - .iter() - .any(|q| m_serialized == q.to_css_string()); - if any { - return; + { + let m_serialized = m.clone().unwrap().to_css_string(); + let guard = self.shared_lock().read(); + let any = self + .media_queries + .borrow() + .read_with(&guard) + .media_queries + .iter() + .any(|q| m_serialized == q.to_css_string()); + if any { + return; + } } // Step 4 - mq.media_queries.push(m.unwrap()); + self.parent_stylesheet.will_modify(); + let mut guard = self.shared_lock().write(); + self.media_queries + .borrow() + .write_with(&mut guard) + .media_queries + .push(m.unwrap()); self.parent_stylesheet.notify_invalidations(); } @@ -185,9 +213,11 @@ impl MediaListMethods for MediaList { return; } // Step 3 + self.parent_stylesheet.will_modify(); let m_serialized = m.unwrap().to_css_string(); let mut guard = self.shared_lock().write(); - let media_list = self.media_queries.write_with(&mut guard); + let media_queries_borrowed = self.media_queries.borrow(); + let media_list = media_queries_borrowed.write_with(&mut guard); let new_vec = media_list .media_queries .drain(..) diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index be62ba38dc6..09da7571d63 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -562,6 +562,7 @@ pub(crate) mod storage; pub(crate) mod storageevent; pub(crate) mod stylepropertymapreadonly; pub(crate) mod stylesheet; +pub(crate) mod stylesheetcontentscache; pub(crate) mod stylesheetlist; pub(crate) mod submitevent; pub(crate) mod subtlecrypto; diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs index 8a2c8f897b2..12ad7912e87 100644 --- a/components/script/dom/shadowroot.rs +++ b/components/script/dom/shadowroot.rs @@ -242,7 +242,7 @@ impl ShadowRoot { debug_assert!(cssom_stylesheet.is_constructed()); let stylesheets = &mut self.author_styles.borrow_mut().stylesheets; - let sheet = cssom_stylesheet.style_stylesheet_arc().clone(); + let sheet = cssom_stylesheet.style_stylesheet().clone(); let insertion_point = stylesheets.iter().last().cloned(); diff --git a/components/script/dom/stylesheetcontentscache.rs b/components/script/dom/stylesheetcontentscache.rs new file mode 100644 index 00000000000..8dbffbdb240 --- /dev/null +++ b/components/script/dom/stylesheetcontentscache.rs @@ -0,0 +1,122 @@ +/* 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/. */ + +use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::hash_map::Entry; +use std::hash::{DefaultHasher, Hash, Hasher}; +use std::rc::Rc; + +use servo_arc::Arc as ServoArc; +use style::context::QuirksMode; +use style::shared_lock::SharedRwLock; +use style::stylesheets::{CssRule, StylesheetContents, UrlExtraData}; +use stylo_atoms::Atom; + +const MAX_LENGTH_OF_TEXT_INSERTED_INTO_TABLE: usize = 1024; +const UNIQUE_OWNED: usize = 2; + +/// Using [`Atom`] as a cache key to avoid inefficient string content comparison. Although +/// the [`Atom`] is already based on reference counting, an extra [`Rc`] is introduced +/// to trace how many [`style::stylesheets::Stylesheet`]s of style elements are sharing +/// same [`StylesheetContents`], based on the following considerations: +/// * The reference count within [`Atom`] is dedicated to lifecycle management and is not +/// suitable for tracking the number of [`StylesheetContents`]s owners. +/// * The reference count within [`Atom`] is not publicly acessible. +#[derive(Clone, Eq, Hash, MallocSizeOf, PartialEq)] +pub(crate) struct StylesheetContentsCacheKey { + #[conditional_malloc_size_of] + stylesheet_text: Rc, + base_url: Atom, + #[ignore_malloc_size_of = "defined in style crate"] + quirks_mode: QuirksMode, +} + +impl StylesheetContentsCacheKey { + fn new(stylesheet_text: &str, base_url: &str, quirks_mode: QuirksMode) -> Self { + // The stylesheet text may be quite lengthy, exceeding hundreds of kilobytes. + // Instead of directly inserting such a huge string into AtomicString table, + // take its hash value and use that. (This is not a cryptographic hash, so a + // page could cause collisions if it wanted to.) + let contents_atom = if stylesheet_text.len() > MAX_LENGTH_OF_TEXT_INSERTED_INTO_TABLE { + let mut hasher = DefaultHasher::new(); + stylesheet_text.hash(&mut hasher); + Atom::from(hasher.finish().to_string().as_str()) + } else { + Atom::from(stylesheet_text) + }; + + Self { + stylesheet_text: Rc::new(contents_atom), + base_url: Atom::from(base_url), + quirks_mode, + } + } + + pub(crate) fn is_uniquely_owned(&self) -> bool { + // The cache itself already holds one reference. + Rc::strong_count(&self.stylesheet_text) <= UNIQUE_OWNED + } +} + +thread_local! { + static STYLESHEETCONTENTS_CACHE: RefCell>> = + RefCell::default(); +} + +pub(crate) struct StylesheetContentsCache; + +impl StylesheetContentsCache { + fn contents_can_be_cached(contents: &StylesheetContents, shared_lock: &SharedRwLock) -> bool { + let guard = shared_lock.read(); + let rules = contents.rules(&guard); + // The copy-on-write can not be performed when the modification happens on the + // imported stylesheet, because it containing cssom has no owner dom node. + !(rules.is_empty() || rules.iter().any(|rule| matches!(rule, CssRule::Import(_)))) + } + + pub(crate) fn get_or_insert_with( + stylesheet_text: &str, + shared_lock: &SharedRwLock, + url_data: UrlExtraData, + quirks_mode: QuirksMode, + stylesheetcontents_create_callback: impl FnOnce() -> ServoArc, + ) -> ( + Option, + ServoArc, + ) { + let cache_key = + StylesheetContentsCacheKey::new(stylesheet_text, url_data.as_str(), quirks_mode); + STYLESHEETCONTENTS_CACHE.with_borrow_mut(|stylesheetcontents_cache| { + let entry = stylesheetcontents_cache.entry(cache_key); + match entry { + Entry::Occupied(occupied_entry) => { + // Use a copy of the cache key from `Entry` instead of the newly created one above + // to correctly update and track to owner count of `StylesheetContents`. + ( + Some(occupied_entry.key().clone()), + occupied_entry.get().clone(), + ) + }, + Entry::Vacant(vacant_entry) => { + let contents = stylesheetcontents_create_callback(); + if Self::contents_can_be_cached(&contents, shared_lock) { + let occupied_entry = vacant_entry.insert_entry(contents.clone()); + // Use a copy of the cache key from `Entry` instead of the newly created one above + // to correctly update and track to owner count of `StylesheetContents`. + (Some(occupied_entry.key().clone()), contents) + } else { + (None, contents) + } + }, + } + }) + } + + pub(crate) fn remove(cache_key: StylesheetContentsCacheKey) { + STYLESHEETCONTENTS_CACHE.with_borrow_mut(|stylesheetcontents_cache| { + stylesheetcontents_cache.remove(&cache_key) + }); + } +} diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 9d49d9a0247..d78b5a6679b 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -214,6 +214,9 @@ impl FetchResponseListener for StylesheetContext { .is_none_or(|generation| generation == link.get_request_generation_id()); if is_stylesheet_load_applicable { let shared_lock = document.style_shared_lock().clone(); + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ParseStylesheet", servo_profiling = true) + .entered(); let sheet = Arc::new(Stylesheet::from_bytes( &data, UrlExtraData(final_url.get_arc()), @@ -235,6 +238,9 @@ impl FetchResponseListener for StylesheetContext { } }, StylesheetContextSource::Import(ref stylesheet) => { + #[cfg(feature = "tracing")] + let _span = + tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered(); Stylesheet::update_from_bytes( stylesheet, &data,