Reuse StylesheetContent for inline style sheets with identical content (#38540)

For duplicate style sheets with identical content, `StylesheetContents`
can be reused to avoid redundant parsing of the inline style sheets.
Since duplicate stylesheets is a common case with web components, this
change will significantly improve performance. Additionally, the cache
hit rate of stylo's `CascadeDataCache` can now be significantly
improved.

When shared `StylesheetContents` is modified, copy-on-write will occur
to avoid affecting other sharers. And then updates the references to
`CssRule` or `PropertyDeclarationBlock` stored in the CSSOMs to ensure
that modifications are made only on the new copy.

Signed-off-by: sharpshooter_pt <ibluegalaxy_taoj@163.com>
This commit is contained in:
JoeDow 2025-08-20 20:31:49 +08:00 committed by GitHub
parent f6b77f94e2
commit 6e6ef513a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 711 additions and 125 deletions

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::{Locked, SharedRwLock}; use style::shared_lock::{Locked, SharedRwLock, SharedRwLockReadGuard};
use style::stylesheets::CssRules as StyleCssRules; use style::stylesheets::CssRules as StyleCssRules;
use crate::dom::bindings::codegen::Bindings::CSSConditionRuleBinding::CSSConditionRuleMethods; use crate::dom::bindings::codegen::Bindings::CSSConditionRuleBinding::CSSConditionRuleMethods;
@ -20,7 +22,7 @@ pub(crate) struct CSSConditionRule {
cssgroupingrule: CSSGroupingRule, cssgroupingrule: CSSGroupingRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
rules: Arc<Locked<StyleCssRules>>, rules: RefCell<Arc<Locked<StyleCssRules>>>,
} }
impl CSSConditionRule { impl CSSConditionRule {
@ -30,7 +32,7 @@ impl CSSConditionRule {
) -> CSSConditionRule { ) -> CSSConditionRule {
CSSConditionRule { CSSConditionRule {
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet), cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet),
rules, rules: RefCell::new(rules),
} }
} }
@ -43,7 +45,16 @@ impl CSSConditionRule {
} }
pub(crate) fn clone_rules(&self) -> Arc<Locked<StyleCssRules>> { pub(crate) fn clone_rules(&self) -> Arc<Locked<StyleCssRules>> {
self.rules.clone() self.rules.borrow().clone()
}
pub(crate) fn update_rules(
&self,
rules: Arc<Locked<StyleCssRules>>,
guard: &SharedRwLockReadGuard,
) {
self.cssgroupingrule.update_rules(&rules, guard);
*self.rules.borrow_mut() = rules;
} }
} }

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, ToCssWithGuard};
@ -20,7 +22,7 @@ pub(crate) struct CSSFontFaceRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
fontfacerule: Arc<Locked<FontFaceRule>>, fontfacerule: RefCell<Arc<Locked<FontFaceRule>>>,
} }
impl CSSFontFaceRule { impl CSSFontFaceRule {
@ -30,7 +32,7 @@ impl CSSFontFaceRule {
) -> CSSFontFaceRule { ) -> CSSFontFaceRule {
CSSFontFaceRule { CSSFontFaceRule {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
fontfacerule, fontfacerule: RefCell::new(fontfacerule),
} }
} }
@ -50,6 +52,10 @@ impl CSSFontFaceRule {
can_gc, can_gc,
) )
} }
pub(crate) fn update_rule(&self, fontfacerule: Arc<Locked<FontFaceRule>>) {
*self.fontfacerule.borrow_mut() = fontfacerule;
}
} }
impl SpecificCSSRule for CSSFontFaceRule { impl SpecificCSSRule for CSSFontFaceRule {
@ -60,6 +66,7 @@ impl SpecificCSSRule for CSSFontFaceRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
self.fontfacerule self.fontfacerule
.borrow()
.read_with(&guard) .read_with(&guard)
.to_css_string(&guard) .to_css_string(&guard)
.into() .into()

View file

@ -3,8 +3,9 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct; use dom_struct::dom_struct;
use style::shared_lock::SharedRwLock; use servo_arc::Arc;
use style::stylesheets::{CssRuleType, CssRuleTypes}; 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::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods;
use crate::dom::bindings::error::{ErrorResult, Fallible}; use crate::dom::bindings::error::{ErrorResult, Fallible};
@ -62,6 +63,16 @@ impl CSSGroupingRule {
pub(crate) fn shared_lock(&self) -> &SharedRwLock { pub(crate) fn shared_lock(&self) -> &SharedRwLock {
self.cssrule.shared_lock() self.cssrule.shared_lock()
} }
pub(crate) fn update_rules(
&self,
rules: &Arc<Locked<CssRules>>,
guard: &SharedRwLockReadGuard,
) {
if let Some(rulelist) = self.rulelist.get() {
rulelist.update_rules(RulesSource::Rules(rules.clone()), guard);
}
}
} }
impl CSSGroupingRuleMethods<crate::DomTypeHolder> for CSSGroupingRule { impl CSSGroupingRuleMethods<crate::DomTypeHolder> for CSSGroupingRule {

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, ToCssWithGuard};
@ -23,7 +25,7 @@ pub(crate) struct CSSImportRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
import_rule: Arc<Locked<ImportRule>>, import_rule: RefCell<Arc<Locked<ImportRule>>>,
} }
impl CSSImportRule { impl CSSImportRule {
@ -33,7 +35,7 @@ impl CSSImportRule {
) -> Self { ) -> Self {
CSSImportRule { CSSImportRule {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
import_rule, import_rule: RefCell::new(import_rule),
} }
} }
@ -50,6 +52,10 @@ impl CSSImportRule {
can_gc, can_gc,
) )
} }
pub(crate) fn update_rule(&self, import_rule: Arc<Locked<ImportRule>>) {
*self.import_rule.borrow_mut() = import_rule;
}
} }
impl SpecificCSSRule for CSSImportRule { impl SpecificCSSRule for CSSImportRule {
@ -60,6 +66,7 @@ impl SpecificCSSRule for CSSImportRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
self.import_rule self.import_rule
.borrow()
.read_with(&guard) .read_with(&guard)
.to_css_string(&guard) .to_css_string(&guard)
.into() .into()
@ -70,7 +77,7 @@ impl CSSImportRuleMethods<crate::DomTypeHolder> for CSSImportRule {
/// <https://drafts.csswg.org/cssom-1/#dom-cssimportrule-layername> /// <https://drafts.csswg.org/cssom-1/#dom-cssimportrule-layername>
fn GetLayerName(&self) -> Option<DOMString> { fn GetLayerName(&self) -> Option<DOMString> {
let guard = self.cssrule.shared_lock().read(); 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::None => None,
ImportLayer::Anonymous => Some(DOMString::new()), ImportLayer::Anonymous => Some(DOMString::new()),
ImportLayer::Named(name) => Some(DOMString::from_string(name.to_css_string())), ImportLayer::Named(name) => Some(DOMString::from_string(name.to_css_string())),

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard};
use style::stylesheets::CssRuleType; use style::stylesheets::CssRuleType;
use style::stylesheets::keyframes_rule::Keyframe; use style::stylesheets::keyframes_rule::Keyframe;
@ -24,7 +26,7 @@ pub(crate) struct CSSKeyframeRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
keyframerule: Arc<Locked<Keyframe>>, keyframerule: RefCell<Arc<Locked<Keyframe>>>,
style_decl: MutNullableDom<CSSStyleDeclaration>, style_decl: MutNullableDom<CSSStyleDeclaration>,
} }
@ -35,7 +37,7 @@ impl CSSKeyframeRule {
) -> CSSKeyframeRule { ) -> CSSKeyframeRule {
CSSKeyframeRule { CSSKeyframeRule {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
keyframerule, keyframerule: RefCell::new(keyframerule),
style_decl: Default::default(), style_decl: Default::default(),
} }
} }
@ -56,6 +58,17 @@ impl CSSKeyframeRule {
can_gc, can_gc,
) )
} }
pub(crate) fn update_rule(
&self,
keyframerule: Arc<Locked<Keyframe>>,
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<crate::DomTypeHolder> for CSSKeyframeRule { impl CSSKeyframeRuleMethods<crate::DomTypeHolder> for CSSKeyframeRule {
@ -67,7 +80,7 @@ impl CSSKeyframeRuleMethods<crate::DomTypeHolder> for CSSKeyframeRule {
self.global().as_window(), self.global().as_window(),
CSSStyleOwner::CSSRule( CSSStyleOwner::CSSRule(
Dom::from_ref(self.upcast()), Dom::from_ref(self.upcast()),
self.keyframerule.read_with(&guard).block.clone(), RefCell::new(self.keyframerule.borrow().read_with(&guard).block.clone()),
), ),
None, None,
CSSModificationAccess::ReadWrite, CSSModificationAccess::ReadWrite,
@ -85,6 +98,7 @@ impl SpecificCSSRule for CSSKeyframeRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
self.keyframerule self.keyframerule
.borrow()
.read_with(&guard) .read_with(&guard)
.to_css_string(&guard) .to_css_string(&guard)
.into() .into()

View file

@ -2,10 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use cssparser::{Parser, ParserInput}; use cssparser::{Parser, ParserInput};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard};
use style::stylesheets::CssRuleType; use style::stylesheets::CssRuleType;
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesRule}; use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesRule};
use style::values::KeyframesName; use style::values::KeyframesName;
@ -28,7 +30,7 @@ pub(crate) struct CSSKeyframesRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
keyframesrule: Arc<Locked<KeyframesRule>>, keyframesrule: RefCell<Arc<Locked<KeyframesRule>>>,
rulelist: MutNullableDom<CSSRuleList>, rulelist: MutNullableDom<CSSRuleList>,
} }
@ -39,7 +41,7 @@ impl CSSKeyframesRule {
) -> CSSKeyframesRule { ) -> CSSKeyframesRule {
CSSKeyframesRule { CSSKeyframesRule {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
keyframesrule, keyframesrule: RefCell::new(keyframesrule),
rulelist: MutNullableDom::new(None), rulelist: MutNullableDom::new(None),
} }
} }
@ -67,7 +69,7 @@ impl CSSKeyframesRule {
CSSRuleList::new( CSSRuleList::new(
self.global().as_window(), self.global().as_window(),
parent_stylesheet, parent_stylesheet,
RulesSource::Keyframes(self.keyframesrule.clone()), RulesSource::Keyframes(self.keyframesrule.borrow().clone()),
can_gc, can_gc,
) )
}) })
@ -82,6 +84,7 @@ impl CSSKeyframesRule {
// This finds the *last* element matching a selector // This finds the *last* element matching a selector
// because that's the rule that applies. Thus, rposition // because that's the rule that applies. Thus, rposition
self.keyframesrule self.keyframesrule
.borrow()
.read_with(&guard) .read_with(&guard)
.keyframes .keyframes
.iter() .iter()
@ -90,6 +93,18 @@ impl CSSKeyframesRule {
None None
} }
} }
pub(crate) fn update_rule(
&self,
keyframesrule: Arc<Locked<KeyframesRule>>,
guard: &SharedRwLockReadGuard,
) {
if let Some(rulelist) = self.rulelist.get() {
rulelist.update_rules(RulesSource::Keyframes(keyframesrule.clone()), guard);
}
*self.keyframesrule.borrow_mut() = keyframesrule;
}
} }
impl CSSKeyframesRuleMethods<crate::DomTypeHolder> for CSSKeyframesRule { impl CSSKeyframesRuleMethods<crate::DomTypeHolder> for CSSKeyframesRule {
@ -108,8 +123,10 @@ impl CSSKeyframesRuleMethods<crate::DomTypeHolder> for CSSKeyframesRule {
); );
if let Ok(rule) = rule { if let Ok(rule) = rule {
self.cssrule.parent_stylesheet().will_modify();
let mut guard = self.cssrule.shared_lock().write(); let mut guard = self.cssrule.shared_lock().write();
self.keyframesrule self.keyframesrule
.borrow()
.write_with(&mut guard) .write_with(&mut guard)
.keyframes .keyframes
.push(rule); .push(rule);
@ -135,7 +152,7 @@ impl CSSKeyframesRuleMethods<crate::DomTypeHolder> for CSSKeyframesRule {
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
fn Name(&self) -> DOMString { fn Name(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); 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 // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
@ -143,9 +160,10 @@ impl CSSKeyframesRuleMethods<crate::DomTypeHolder> for CSSKeyframesRule {
// Spec deviation: https://github.com/w3c/csswg-drafts/issues/801 // Spec deviation: https://github.com/w3c/csswg-drafts/issues/801
// Setting this property to a CSS-wide keyword or `none` does not throw, // Setting this property to a CSS-wide keyword or `none` does not throw,
// it stores a value that serializes as a quoted string. // it stores a value that serializes as a quoted string.
self.cssrule.parent_stylesheet().will_modify();
let name = KeyframesName::from_ident(&value); let name = KeyframesName::from_ident(&value);
let mut guard = self.cssrule.shared_lock().write(); 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(); self.cssrule.parent_stylesheet().notify_invalidations();
Ok(()) Ok(())
} }
@ -159,6 +177,7 @@ impl SpecificCSSRule for CSSKeyframesRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
self.keyframesrule self.keyframesrule
.borrow()
.read_with(&guard) .read_with(&guard)
.to_css_string(&guard) .to_css_string(&guard)
.into() .into()

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; 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::stylesheets::{CssRuleType, CssRules, LayerBlockRule};
use style_traits::ToCss; use style_traits::ToCss;
@ -23,7 +25,7 @@ pub(crate) struct CSSLayerBlockRule {
cssgroupingrule: CSSGroupingRule, cssgroupingrule: CSSGroupingRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
layerblockrule: Arc<LayerBlockRule>, layerblockrule: RefCell<Arc<LayerBlockRule>>,
} }
impl CSSLayerBlockRule { impl CSSLayerBlockRule {
@ -33,7 +35,7 @@ impl CSSLayerBlockRule {
) -> CSSLayerBlockRule { ) -> CSSLayerBlockRule {
CSSLayerBlockRule { CSSLayerBlockRule {
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet), cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet),
layerblockrule, layerblockrule: RefCell::new(layerblockrule),
} }
} }
@ -55,7 +57,17 @@ impl CSSLayerBlockRule {
} }
pub(crate) fn clone_rules(&self) -> Arc<Locked<CssRules>> { pub(crate) fn clone_rules(&self) -> Arc<Locked<CssRules>> {
self.layerblockrule.rules.clone() self.layerblockrule.borrow().rules.clone()
}
pub(crate) fn update_rule(
&self,
layerblockrule: Arc<LayerBlockRule>,
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 { fn get_css(&self) -> DOMString {
let guard = self.cssgroupingrule.shared_lock().read(); let guard = self.cssgroupingrule.shared_lock().read();
self.layerblockrule.to_css_string(&guard).into() self.layerblockrule.borrow().to_css_string(&guard).into()
} }
} }
impl CSSLayerBlockRuleMethods<crate::DomTypeHolder> for CSSLayerBlockRule { impl CSSLayerBlockRuleMethods<crate::DomTypeHolder> for CSSLayerBlockRule {
/// <https://drafts.csswg.org/css-cascade-5/#dom-csslayerblockrule-name> /// <https://drafts.csswg.org/css-cascade-5/#dom-csslayerblockrule-name>
fn Name(&self) -> DOMString { 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()) DOMString::from_string(name.to_css_string())
} else { } else {
DOMString::new() DOMString::new()

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::rust::MutableHandleValue; use js::rust::MutableHandleValue;
use servo_arc::Arc; use servo_arc::Arc;
@ -24,7 +26,7 @@ pub(crate) struct CSSLayerStatementRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
layerstatementrule: Arc<LayerStatementRule>, layerstatementrule: RefCell<Arc<LayerStatementRule>>,
} }
impl CSSLayerStatementRule { impl CSSLayerStatementRule {
@ -34,7 +36,7 @@ impl CSSLayerStatementRule {
) -> CSSLayerStatementRule { ) -> CSSLayerStatementRule {
CSSLayerStatementRule { CSSLayerStatementRule {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
layerstatementrule, layerstatementrule: RefCell::new(layerstatementrule),
} }
} }
@ -54,6 +56,10 @@ impl CSSLayerStatementRule {
can_gc, can_gc,
) )
} }
pub(crate) fn update_rule(&self, layerstatementrule: Arc<LayerStatementRule>) {
*self.layerstatementrule.borrow_mut() = layerstatementrule;
}
} }
impl SpecificCSSRule for CSSLayerStatementRule { impl SpecificCSSRule for CSSLayerStatementRule {
@ -63,7 +69,10 @@ impl SpecificCSSRule for CSSLayerStatementRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); 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<crate::DomTypeHolder> for CSSLayerStatementRul
fn NameList(&self, cx: SafeJSContext, can_gc: CanGc, retval: MutableHandleValue) { fn NameList(&self, cx: SafeJSContext, can_gc: CanGc, retval: MutableHandleValue) {
let names: Vec<DOMString> = self let names: Vec<DOMString> = self
.layerstatementrule .layerstatementrule
.borrow()
.names .names
.iter() .iter()
.map(|name| DOMString::from_string(name.to_css_string())) .map(|name| DOMString::from_string(name.to_css_string()))

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::ToCssWithGuard; use style::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use style::stylesheets::{CssRuleType, MediaRule}; use style::stylesheets::{CssRuleType, MediaRule};
use style_traits::ToCss; use style_traits::ToCss;
@ -24,7 +26,7 @@ pub(crate) struct CSSMediaRule {
cssconditionrule: CSSConditionRule, cssconditionrule: CSSConditionRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
mediarule: Arc<MediaRule>, mediarule: RefCell<Arc<MediaRule>>,
medialist: MutNullableDom<MediaList>, medialist: MutNullableDom<MediaList>,
} }
@ -33,7 +35,7 @@ impl CSSMediaRule {
let list = mediarule.rules.clone(); let list = mediarule.rules.clone();
CSSMediaRule { CSSMediaRule {
cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list), cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list),
mediarule, mediarule: RefCell::new(mediarule),
medialist: MutNullableDom::new(None), medialist: MutNullableDom::new(None),
} }
} }
@ -57,7 +59,7 @@ impl CSSMediaRule {
MediaList::new( MediaList::new(
self.global().as_window(), self.global().as_window(),
self.cssconditionrule.parent_stylesheet(), self.cssconditionrule.parent_stylesheet(),
self.mediarule.media_queries.clone(), self.mediarule.borrow().media_queries.clone(),
can_gc, can_gc,
) )
}) })
@ -66,8 +68,21 @@ impl CSSMediaRule {
/// <https://drafts.csswg.org/css-conditional-3/#the-cssmediarule-interface> /// <https://drafts.csswg.org/css-conditional-3/#the-cssmediarule-interface>
pub(crate) fn get_condition_text(&self) -> DOMString { pub(crate) fn get_condition_text(&self) -> DOMString {
let guard = self.cssconditionrule.shared_lock().read(); let guard = self.cssconditionrule.shared_lock().read();
let list = self.mediarule.media_queries.read_with(&guard); self.mediarule
list.to_css_string().into() .borrow()
.media_queries
.read_with(&guard)
.to_css_string()
.into()
}
pub(crate) fn update_rule(&self, mediarule: Arc<MediaRule>, 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 { fn get_css(&self) -> DOMString {
let guard = self.cssconditionrule.shared_lock().read(); let guard = self.cssconditionrule.shared_lock().read();
self.mediarule.to_css_string(&guard).into() self.mediarule.borrow().to_css_string(&guard).into()
} }
} }

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::ToCssWithGuard; use style::shared_lock::ToCssWithGuard;
@ -21,7 +23,7 @@ pub(crate) struct CSSNamespaceRule {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
namespacerule: Arc<NamespaceRule>, namespacerule: RefCell<Arc<NamespaceRule>>,
} }
impl CSSNamespaceRule { impl CSSNamespaceRule {
@ -31,7 +33,7 @@ impl CSSNamespaceRule {
) -> CSSNamespaceRule { ) -> CSSNamespaceRule {
CSSNamespaceRule { CSSNamespaceRule {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
namespacerule, namespacerule: RefCell::new(namespacerule),
} }
} }
@ -51,12 +53,17 @@ impl CSSNamespaceRule {
can_gc, can_gc,
) )
} }
pub(crate) fn update_rule(&self, namespacerule: Arc<NamespaceRule>) {
*self.namespacerule.borrow_mut() = namespacerule;
}
} }
impl CSSNamespaceRuleMethods<crate::DomTypeHolder> for CSSNamespaceRule { impl CSSNamespaceRuleMethods<crate::DomTypeHolder> for CSSNamespaceRule {
// https://drafts.csswg.org/cssom/#dom-cssnamespacerule-prefix // https://drafts.csswg.org/cssom/#dom-cssnamespacerule-prefix
fn Prefix(&self) -> DOMString { fn Prefix(&self) -> DOMString {
self.namespacerule self.namespacerule
.borrow()
.prefix .prefix
.as_ref() .as_ref()
.map(|s| s.to_string().into()) .map(|s| s.to_string().into())
@ -65,7 +72,7 @@ impl CSSNamespaceRuleMethods<crate::DomTypeHolder> for CSSNamespaceRule {
// https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri // https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri
fn NamespaceURI(&self) -> DOMString { 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 { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
self.namespacerule.to_css_string(&guard).into() self.namespacerule.borrow().to_css_string(&guard).into()
} }
} }

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard};
use style::stylesheets::{CssRuleType, NestedDeclarationsRule}; use style::stylesheets::{CssRuleType, NestedDeclarationsRule};
use crate::dom::bindings::codegen::Bindings::CSSNestedDeclarationsBinding::CSSNestedDeclarationsMethods; use crate::dom::bindings::codegen::Bindings::CSSNestedDeclarationsBinding::CSSNestedDeclarationsMethods;
@ -23,7 +25,7 @@ pub(crate) struct CSSNestedDeclarations {
cssrule: CSSRule, cssrule: CSSRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
nesteddeclarationsrule: Arc<Locked<NestedDeclarationsRule>>, nesteddeclarationsrule: RefCell<Arc<Locked<NestedDeclarationsRule>>>,
style_decl: MutNullableDom<CSSStyleDeclaration>, style_decl: MutNullableDom<CSSStyleDeclaration>,
} }
@ -34,7 +36,7 @@ impl CSSNestedDeclarations {
) -> Self { ) -> Self {
Self { Self {
cssrule: CSSRule::new_inherited(parent_stylesheet), cssrule: CSSRule::new_inherited(parent_stylesheet),
nesteddeclarationsrule, nesteddeclarationsrule: RefCell::new(nesteddeclarationsrule),
style_decl: Default::default(), style_decl: Default::default(),
} }
} }
@ -55,6 +57,18 @@ impl CSSNestedDeclarations {
can_gc, can_gc,
) )
} }
pub(crate) fn update_rule(
&self,
nesteddeclarationsrule: Arc<Locked<NestedDeclarationsRule>>,
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 { impl SpecificCSSRule for CSSNestedDeclarations {
@ -65,6 +79,7 @@ impl SpecificCSSRule for CSSNestedDeclarations {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
self.nesteddeclarationsrule self.nesteddeclarationsrule
.borrow()
.read_with(&guard) .read_with(&guard)
.to_css_string(&guard) .to_css_string(&guard)
.into() .into()
@ -80,7 +95,13 @@ impl CSSNestedDeclarationsMethods<crate::DomTypeHolder> for CSSNestedDeclaration
self.global().as_window(), self.global().as_window(),
CSSStyleOwner::CSSRule( CSSStyleOwner::CSSRule(
Dom::from_ref(self.upcast()), Dom::from_ref(self.upcast()),
self.nesteddeclarationsrule.read_with(&guard).block.clone(), RefCell::new(
self.nesteddeclarationsrule
.borrow()
.read_with(&guard)
.block
.clone(),
),
), ),
None, None,
CSSModificationAccess::ReadWrite, CSSModificationAccess::ReadWrite,

View file

@ -5,7 +5,7 @@
use std::cell::Cell; use std::cell::Cell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use style::shared_lock::SharedRwLock; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
use style::stylesheets::{CssRule as StyleCssRule, CssRuleType}; use style::stylesheets::{CssRule as StyleCssRule, CssRuleType};
use crate::dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods; use crate::dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods;
@ -157,7 +157,73 @@ impl CSSRule {
} }
pub(crate) fn shared_lock(&self) -> &SharedRwLock { 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::<CSSImportRule>() {
rule.update_rule(s.clone());
}
},
StyleCssRule::Style(s) => {
if let Some(rule) = self.downcast::<CSSStyleRule>() {
rule.update_rule(s.clone(), guard);
}
},
StyleCssRule::FontFace(s) => {
if let Some(rule) = self.downcast::<CSSFontFaceRule>() {
rule.update_rule(s.clone());
}
},
StyleCssRule::FontFeatureValues(_) => unimplemented!(),
StyleCssRule::CounterStyle(_) => unimplemented!(),
StyleCssRule::Keyframes(s) => {
if let Some(rule) = self.downcast::<CSSKeyframesRule>() {
rule.update_rule(s.clone(), guard);
}
},
StyleCssRule::Media(s) => {
if let Some(rule) = self.downcast::<CSSMediaRule>() {
rule.update_rule(s.clone(), guard);
}
},
StyleCssRule::Namespace(s) => {
if let Some(rule) = self.downcast::<CSSNamespaceRule>() {
rule.update_rule(s.clone());
}
},
StyleCssRule::Supports(s) => {
if let Some(rule) = self.downcast::<CSSSupportsRule>() {
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::<CSSLayerBlockRule>() {
rule.update_rule(s.clone(), guard);
}
},
StyleCssRule::LayerStatement(s) => {
if let Some(rule) = self.downcast::<CSSLayerStatementRule>() {
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::<CSSNestedDeclarations>() {
rule.update_rule(s.clone(), guard);
}
},
}
} }
} }

View file

@ -4,9 +4,13 @@
#![allow(unsafe_code)] #![allow(unsafe_code)]
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use itertools::izip;
use script_bindings::inheritance::Castable;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::Locked; use style::shared_lock::{Locked, SharedRwLockReadGuard};
use style::stylesheets::{ use style::stylesheets::{
AllowImportRules, CssRuleType, CssRuleTypes, CssRules, CssRulesHelpers, KeyframesRule, AllowImportRules, CssRuleType, CssRuleTypes, CssRules, CssRulesHelpers, KeyframesRule,
RulesMutateError, StylesheetLoader as StyleStylesheetLoader, RulesMutateError, StylesheetLoader as StyleStylesheetLoader,
@ -44,7 +48,7 @@ pub(crate) struct CSSRuleList {
reflector_: Reflector, reflector_: Reflector,
parent_stylesheet: Dom<CSSStyleSheet>, parent_stylesheet: Dom<CSSStyleSheet>,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
rules: RulesSource, rules: RefCell<RulesSource>,
dom_rules: DomRefCell<Vec<MutNullableDom<CSSRule>>>, dom_rules: DomRefCell<Vec<MutNullableDom<CSSRule>>>,
} }
@ -78,7 +82,7 @@ impl CSSRuleList {
CSSRuleList { CSSRuleList {
reflector_: Reflector::new(), reflector_: Reflector::new(),
parent_stylesheet: Dom::from_ref(parent_stylesheet), parent_stylesheet: Dom::from_ref(parent_stylesheet),
rules, rules: RefCell::new(rules),
dom_rules: DomRefCell::new(dom_rules), dom_rules: DomRefCell::new(dom_rules),
} }
} }
@ -107,8 +111,9 @@ impl CSSRuleList {
parse_relative_rule_type: Option<CssRuleType>, parse_relative_rule_type: Option<CssRuleType>,
can_gc: CanGc, can_gc: CanGc,
) -> Fallible<u32> { ) -> Fallible<u32> {
let css_rules = if let RulesSource::Rules(ref rules) = self.rules { self.parent_stylesheet.will_modify();
rules let css_rules = if let RulesSource::Rules(rules) = &*self.rules.borrow() {
rules.clone()
} else { } else {
panic!("Called insert_rule on non-CssRule-backed CSSRuleList"); panic!("Called insert_rule on non-CssRule-backed CSSRuleList");
}; };
@ -144,6 +149,7 @@ impl CSSRuleList {
.map_err(Convert::convert)?; .map_err(Convert::convert)?;
let parent_stylesheet = &*self.parent_stylesheet; let parent_stylesheet = &*self.parent_stylesheet;
parent_stylesheet.will_modify();
let dom_rule = CSSRule::new_specific(window, parent_stylesheet, new_rule, can_gc); let dom_rule = CSSRule::new_specific(window, parent_stylesheet, new_rule, can_gc);
self.dom_rules self.dom_rules
.borrow_mut() .borrow_mut()
@ -154,10 +160,12 @@ impl CSSRuleList {
/// In case of a keyframe rule, index must be valid. /// In case of a keyframe rule, index must be valid.
pub(crate) fn remove_rule(&self, index: u32) -> ErrorResult { pub(crate) fn remove_rule(&self, index: u32) -> ErrorResult {
self.parent_stylesheet.will_modify();
let index = index as usize; let index = index as usize;
let mut guard = self.parent_stylesheet.shared_lock().write(); let mut guard = self.parent_stylesheet.shared_lock().write();
match self.rules { match *self.rules.borrow() {
RulesSource::Rules(ref css_rules) => { RulesSource::Rules(ref css_rules) => {
css_rules css_rules
.write_with(&mut guard) .write_with(&mut guard)
@ -199,7 +207,7 @@ impl CSSRuleList {
rule.or_init(|| { rule.or_init(|| {
let parent_stylesheet = &self.parent_stylesheet; let parent_stylesheet = &self.parent_stylesheet;
let lock = parent_stylesheet.shared_lock(); let lock = parent_stylesheet.shared_lock();
match self.rules { match *self.rules.borrow() {
RulesSource::Rules(ref rules) => { RulesSource::Rules(ref rules) => {
let rule = { let rule = {
let guard = lock.read(); let guard = lock.read();
@ -235,11 +243,48 @@ impl CSSRuleList {
/// Should only be called for keyframes-backed rules, use insert_rule /// Should only be called for keyframes-backed rules, use insert_rule
/// for CssRules-backed rules /// for CssRules-backed rules
pub(crate) fn append_lazy_dom_rule(&self) { 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"); panic!("Can only call append_lazy_rule with keyframes-backed CSSRules");
} }
self.dom_rules.borrow_mut().push(MutNullableDom::new(None)); 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::<CSSKeyframeRule>() else {
continue;
};
dom_rule.update_rule(keyframerule.clone(), guard);
}
},
}
*self.rules.borrow_mut() = rules;
}
} }
impl CSSRuleListMethods<crate::DomTypeHolder> for CSSRuleList { impl CSSRuleListMethods<crate::DomTypeHolder> for CSSRuleList {

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::LazyLock; use std::sync::LazyLock;
@ -53,7 +54,7 @@ pub(crate) enum CSSStyleOwner {
Dom<CSSRule>, Dom<CSSRule>,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
Arc<Locked<PropertyDeclarationBlock>>, RefCell<Arc<Locked<PropertyDeclarationBlock>>>,
), ),
} }
@ -119,9 +120,10 @@ impl CSSStyleOwner {
result result
}, },
CSSStyleOwner::CSSRule(ref rule, ref pdb) => { CSSStyleOwner::CSSRule(ref rule, ref pdb) => {
rule.parent_stylesheet().will_modify();
let result = { let result = {
let mut guard = rule.shared_lock().write(); 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 { if changed {
rule.parent_stylesheet().notify_invalidations(); rule.parent_stylesheet().notify_invalidations();
@ -152,7 +154,7 @@ impl CSSStyleOwner {
}, },
CSSStyleOwner::CSSRule(ref rule, ref pdb) => { CSSStyleOwner::CSSRule(ref rule, ref pdb) => {
let guard = rule.shared_lock().read(); 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<Locked<PropertyDeclarationBlock>>,
) {
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 { fn get_computed_style(&self, property: PropertyId) -> DOMString {
match self.owner { match self.owner {
CSSStyleOwner::CSSRule(..) => { CSSStyleOwner::CSSRule(..) => {

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use std::mem; use std::mem;
use cssparser::{Parser as CssParser, ParserInput as CssParserInput, ToCss}; use cssparser::{Parser as CssParser, ParserInput as CssParserInput, ToCss};
@ -9,7 +10,7 @@ use dom_struct::dom_struct;
use selectors::parser::{ParseRelative, SelectorList}; use selectors::parser::{ParseRelative, SelectorList};
use servo_arc::Arc; use servo_arc::Arc;
use style::selector_parser::SelectorParser; 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 style::stylesheets::{CssRuleType, CssRules, Origin, StyleRule};
use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::CSSStyleRuleMethods; use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::CSSStyleRuleMethods;
@ -29,7 +30,7 @@ pub(crate) struct CSSStyleRule {
cssgroupingrule: CSSGroupingRule, cssgroupingrule: CSSGroupingRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
stylerule: Arc<Locked<StyleRule>>, stylerule: RefCell<Arc<Locked<StyleRule>>>,
style_decl: MutNullableDom<CSSStyleDeclaration>, style_decl: MutNullableDom<CSSStyleDeclaration>,
} }
@ -40,7 +41,7 @@ impl CSSStyleRule {
) -> CSSStyleRule { ) -> CSSStyleRule {
CSSStyleRule { CSSStyleRule {
cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet), cssgroupingrule: CSSGroupingRule::new_inherited(parent_stylesheet),
stylerule, stylerule: RefCell::new(stylerule),
style_decl: Default::default(), style_decl: Default::default(),
} }
} }
@ -63,11 +64,28 @@ impl CSSStyleRule {
let lock = self.cssgroupingrule.shared_lock(); let lock = self.cssgroupingrule.shared_lock();
let mut guard = lock.write(); let mut guard = lock.write();
self.stylerule self.stylerule
.borrow()
.write_with(&mut guard) .write_with(&mut guard)
.rules .rules
.get_or_insert_with(|| CssRules::new(vec![], lock)) .get_or_insert_with(|| CssRules::new(vec![], lock))
.clone() .clone()
} }
pub(crate) fn update_rule(
&self,
stylerule: Arc<Locked<StyleRule>>,
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 { impl SpecificCSSRule for CSSStyleRule {
@ -78,6 +96,7 @@ impl SpecificCSSRule for CSSStyleRule {
fn get_css(&self) -> DOMString { fn get_css(&self) -> DOMString {
let guard = self.cssgroupingrule.shared_lock().read(); let guard = self.cssgroupingrule.shared_lock().read();
self.stylerule self.stylerule
.borrow()
.read_with(&guard) .read_with(&guard)
.to_css_string(&guard) .to_css_string(&guard)
.into() .into()
@ -93,7 +112,7 @@ impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
self.global().as_window(), self.global().as_window(),
CSSStyleOwner::CSSRule( CSSStyleOwner::CSSRule(
Dom::from_ref(self.upcast()), Dom::from_ref(self.upcast()),
self.stylerule.read_with(&guard).block.clone(), RefCell::new(self.stylerule.borrow().read_with(&guard).block.clone()),
), ),
None, None,
CSSModificationAccess::ReadWrite, CSSModificationAccess::ReadWrite,
@ -105,8 +124,13 @@ impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
fn SelectorText(&self) -> DOMString { fn SelectorText(&self) -> DOMString {
let guard = self.cssgroupingrule.shared_lock().read(); let guard = self.cssgroupingrule.shared_lock().read();
let stylerule = self.stylerule.read_with(&guard); DOMString::from_string(
DOMString::from_string(stylerule.selectors.to_css_string()) self.stylerule
.borrow()
.read_with(&guard)
.selectors
.to_css_string(),
)
} }
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext // https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
@ -115,7 +139,8 @@ impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
.cssgroupingrule .cssgroupingrule
.parent_stylesheet() .parent_stylesheet()
.style_stylesheet() .style_stylesheet()
.contents; .contents
.clone();
// It's not clear from the spec if we should use the stylesheet's namespaces. // It's not clear from the spec if we should use the stylesheet's namespaces.
// https://github.com/w3c/csswg-drafts/issues/1511 // https://github.com/w3c/csswg-drafts/issues/1511
let namespaces = contents.namespaces.read(); let namespaces = contents.namespaces.read();
@ -131,10 +156,13 @@ impl CSSStyleRuleMethods<crate::DomTypeHolder> for CSSStyleRule {
// TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style
// rule? // rule?
if let Ok(mut s) = SelectorList::parse(&parser, &mut css_parser, ParseRelative::No) { 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. // This mirrors what we do in CSSStyleOwner::mutate_associated_block.
let mut guard = self.cssgroupingrule.shared_lock().write(); let mut guard = self.cssgroupingrule.shared_lock().write();
let stylerule = self.stylerule.write_with(&mut guard); mem::swap(
mem::swap(&mut stylerule.selectors, &mut s); &mut self.stylerule.borrow().write_with(&mut guard).selectors,
&mut s,
);
self.cssgroupingrule self.cssgroupingrule
.parent_stylesheet() .parent_stylesheet()
.notify_invalidations(); .notify_invalidations();

View file

@ -2,16 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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 std::rc::Rc;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use js::rust::HandleObject; use js::rust::HandleObject;
use script_bindings::inheritance::Castable;
use script_bindings::realms::InRealm; use script_bindings::realms::InRealm;
use script_bindings::root::Dom; use script_bindings::root::Dom;
use servo_arc::Arc; use servo_arc::Arc;
use style::media_queries::MediaList as StyleMediaList; use style::media_queries::MediaList as StyleMediaList;
use style::shared_lock::SharedRwLock; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard};
use style::stylesheets::{ use style::stylesheets::{
AllowImportRules, CssRuleTypes, Origin, Stylesheet as StyleStyleSheet, UrlExtraData, 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::cssrulelist::{CSSRuleList, RulesSource};
use crate::dom::document::Document; use crate::dom::document::Document;
use crate::dom::element::Element; use crate::dom::element::Element;
use crate::dom::htmlstyleelement::HTMLStyleElement;
use crate::dom::medialist::MediaList; use crate::dom::medialist::MediaList;
use crate::dom::node::NodeTraits; use crate::dom::node::NodeTraits;
use crate::dom::stylesheet::StyleSheet; use crate::dom::stylesheet::StyleSheet;
@ -55,7 +57,12 @@ pub(crate) struct CSSStyleSheet {
/// The inner Stylo's [Stylesheet]. /// The inner Stylo's [Stylesheet].
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
style_stylesheet: Arc<StyleStyleSheet>, style_stylesheet: DomRefCell<Arc<StyleStyleSheet>>,
/// The inner Stylo's [SharedRwLock], stored at here to avoid referencing
/// temporary variables.
#[no_trace]
style_shared_lock: SharedRwLock,
/// <https://drafts.csswg.org/cssom/#concept-css-style-sheet-origin-clean-flag> /// <https://drafts.csswg.org/cssom/#concept-css-style-sheet-origin-clean-flag>
origin_clean: Cell<bool>, origin_clean: Cell<bool>,
@ -86,7 +93,8 @@ impl CSSStyleSheet {
stylesheet: StyleSheet::new_inherited(type_, href, title), stylesheet: StyleSheet::new_inherited(type_, href, title),
owner_node: MutNullableDom::new(owner), owner_node: MutNullableDom::new(owner),
rulelist: MutNullableDom::new(None), rulelist: MutNullableDom::new(None),
style_stylesheet: stylesheet, style_shared_lock: stylesheet.shared_lock.clone(),
style_stylesheet: DomRefCell::new(stylesheet),
origin_clean: Cell::new(true), origin_clean: Cell::new(true),
constructor_document: constructor_document.map(Dom::from_ref), constructor_document: constructor_document.map(Dom::from_ref),
adopters: Default::default(), adopters: Default::default(),
@ -150,7 +158,7 @@ impl CSSStyleSheet {
fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> { fn rulelist(&self, can_gc: CanGc) -> DomRoot<CSSRuleList> {
self.rulelist.or_init(|| { self.rulelist.or_init(|| {
let rules = self.style_stylesheet.contents.rules.clone(); let rules = self.style_stylesheet.borrow().contents.rules.clone();
CSSRuleList::new( CSSRuleList::new(
self.global().as_window(), self.global().as_window(),
self, self,
@ -161,7 +169,7 @@ impl CSSStyleSheet {
} }
pub(crate) fn disabled(&self) -> bool { pub(crate) fn disabled(&self) -> bool {
self.style_stylesheet.disabled() self.style_stylesheet.borrow().disabled()
} }
pub(crate) fn owner_node(&self) -> Option<DomRoot<Element>> { pub(crate) fn owner_node(&self) -> Option<DomRoot<Element>> {
@ -169,7 +177,7 @@ impl CSSStyleSheet {
} }
pub(crate) fn set_disabled(&self, disabled: bool) { 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(); self.notify_invalidations();
} }
} }
@ -179,15 +187,11 @@ impl CSSStyleSheet {
} }
pub(crate) fn shared_lock(&self) -> &SharedRwLock { pub(crate) fn shared_lock(&self) -> &SharedRwLock {
&self.style_stylesheet.shared_lock &self.style_shared_lock
} }
pub(crate) fn style_stylesheet(&self) -> &StyleStyleSheet { pub(crate) fn style_stylesheet(&self) -> Ref<'_, Arc<StyleStyleSheet>> {
&self.style_stylesheet self.style_stylesheet.borrow()
}
pub(crate) fn style_stylesheet_arc(&self) -> &Arc<StyleStyleSheet> {
&self.style_stylesheet
} }
pub(crate) fn set_origin_clean(&self, origin_clean: bool) { 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::<HTMLStyleElement>() else {
return;
};
node.will_modify_stylesheet();
}
pub(crate) fn update_style_stylesheet(
&self,
style_stylesheet: &Arc<StyleStyleSheet>,
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. /// Invalidate all stylesheet set this stylesheet is a part on.
pub(crate) fn notify_invalidations(&self) { pub(crate) fn notify_invalidations(&self) {
if let Some(owner) = self.owner_node() { if let Some(owner) = self.owner_node() {
@ -402,8 +436,12 @@ impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet {
let global = sheet.global(); let global = sheet.global();
let window = global.as_window(); 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( StyleStyleSheet::update_from_str(
&sheet.style_stylesheet, &sheet.style_stylesheet(),
&text, &text,
UrlExtraData(window.get_url().get_arc()), UrlExtraData(window.get_url().get_arc()),
None, None,
@ -441,8 +479,12 @@ impl CSSStyleSheetMethods<crate::DomTypeHolder> for CSSStyleSheet {
let global = self.global(); let global = self.global();
let window = global.as_window(); 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( StyleStyleSheet::update_from_str(
&self.style_stylesheet, &self.style_stylesheet(),
&text, &text,
UrlExtraData(window.get_url().get_arc()), UrlExtraData(window.get_url().get_arc()),
None, None,

View file

@ -2,9 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
use style::shared_lock::ToCssWithGuard; use style::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use style::stylesheets::{CssRuleType, SupportsRule}; use style::stylesheets::{CssRuleType, SupportsRule};
use style_traits::ToCss; use style_traits::ToCss;
@ -22,7 +24,7 @@ pub(crate) struct CSSSupportsRule {
cssconditionrule: CSSConditionRule, cssconditionrule: CSSConditionRule,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
supportsrule: Arc<SupportsRule>, supportsrule: RefCell<Arc<SupportsRule>>,
} }
impl CSSSupportsRule { impl CSSSupportsRule {
@ -33,7 +35,7 @@ impl CSSSupportsRule {
let list = supportsrule.rules.clone(); let list = supportsrule.rules.clone();
CSSSupportsRule { CSSSupportsRule {
cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list), cssconditionrule: CSSConditionRule::new_inherited(parent_stylesheet, list),
supportsrule, supportsrule: RefCell::new(supportsrule),
} }
} }
@ -56,7 +58,17 @@ impl CSSSupportsRule {
/// <https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface> /// <https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface>
pub(crate) fn get_condition_text(&self) -> DOMString { 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<SupportsRule>,
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 { fn get_css(&self) -> DOMString {
let guard = self.cssconditionrule.shared_lock().read(); let guard = self.cssconditionrule.shared_lock().read();
self.supportsrule.to_css_string(&guard).into() self.supportsrule.borrow().to_css_string(&guard).into()
} }
} }

View file

@ -4021,7 +4021,7 @@ impl Document {
debug_assert!(cssom_stylesheet.is_constructed()); debug_assert!(cssom_stylesheet.is_constructed());
let stylesheets = &mut *self.stylesheets.borrow_mut(); 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 let insertion_point = stylesheets
.iter() .iter()

View file

@ -397,7 +397,7 @@ impl DocumentOrShadowRoot {
if stylesheet_remove_set.insert(sheet_to_remove) { if stylesheet_remove_set.insert(sheet_to_remove) {
owner.remove_stylesheet( owner.remove_stylesheet(
StylesheetSource::Constructed(sheet_to_remove.clone()), StylesheetSource::Constructed(sheet_to_remove.clone()),
sheet_to_remove.style_stylesheet_arc(), &sheet_to_remove.style_stylesheet(),
); );
sheet_to_remove.remove_adopter(owner); sheet_to_remove.remove_adopter(owner);
} }
@ -416,7 +416,7 @@ impl DocumentOrShadowRoot {
// around. // around.
owner.remove_stylesheet( owner.remove_stylesheet(
StylesheetSource::Constructed(sheet.clone()), StylesheetSource::Constructed(sheet.clone()),
sheet.style_stylesheet_arc(), &sheet.style_stylesheet(),
); );
} else { } else {
sheet.add_adopter(owner.clone()); sheet.add_adopter(owner.clone());

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::Cell; use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix}; use html5ever::{LocalName, Prefix};
@ -11,7 +12,8 @@ use net_traits::ReferrerPolicy;
use script_bindings::root::Dom; use script_bindings::root::Dom;
use servo_arc::Arc; use servo_arc::Arc;
use style::media_queries::MediaList as StyleMediaList; 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::attr::Attr;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
@ -29,6 +31,7 @@ use crate::dom::htmlelement::HTMLElement;
use crate::dom::medialist::MediaList; use crate::dom::medialist::MediaList;
use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits, UnbindContext}; use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits, UnbindContext};
use crate::dom::stylesheet::StyleSheet as DOMStyleSheet; use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
use crate::dom::stylesheetcontentscache::{StylesheetContentsCache, StylesheetContentsCacheKey};
use crate::dom::virtualmethods::VirtualMethods; use crate::dom::virtualmethods::VirtualMethods;
use crate::script_runtime::CanGc; use crate::script_runtime::CanGc;
use crate::stylesheet_loader::{StylesheetLoader, StylesheetOwner}; use crate::stylesheet_loader::{StylesheetLoader, StylesheetOwner};
@ -39,6 +42,8 @@ pub(crate) struct HTMLStyleElement {
#[conditional_malloc_size_of] #[conditional_malloc_size_of]
#[no_trace] #[no_trace]
stylesheet: DomRefCell<Option<Arc<Stylesheet>>>, stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
#[no_trace]
stylesheetcontents_cache_key: DomRefCell<Option<StylesheetContentsCacheKey>>,
cssom_stylesheet: MutNullableDom<CSSStyleSheet>, cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
/// <https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts> /// <https://html.spec.whatwg.org/multipage/#a-style-sheet-that-is-blocking-scripts>
parser_inserted: Cell<bool>, parser_inserted: Cell<bool>,
@ -57,6 +62,7 @@ impl HTMLStyleElement {
HTMLStyleElement { HTMLStyleElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document), htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
stylesheet: DomRefCell::new(None), stylesheet: DomRefCell::new(None),
stylesheetcontents_cache_key: DomRefCell::new(None),
cssom_stylesheet: MutNullableDom::new(None), cssom_stylesheet: MutNullableDom::new(None),
parser_inserted: Cell::new(creator.is_parser_created()), parser_inserted: Cell::new(creator.is_parser_created()),
in_stack_of_open_elements: 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 shared_lock = node.owner_doc().style_shared_lock().clone();
let mq = Arc::new(shared_lock.wrap(self.create_media_list(&self.Media()))); let mq = Arc::new(shared_lock.wrap(self.create_media_list(&self.Media())));
let loader = StylesheetLoader::for_element(self.upcast()); 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, &data,
&shared_lock,
UrlExtraData(window.get_url().get_arc()), UrlExtraData(window.get_url().get_arc()),
Origin::Author,
mq,
shared_lock,
Some(&loader),
window.css_error_reporter(),
doc.quirks_mode(), 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 // No subresource loads were triggered, queue load event
if self.pending_loads.get() == 0 { if self.pending_loads.get() == 0 {
@ -147,22 +175,44 @@ impl HTMLStyleElement {
.queue_simple_event(self.upcast(), atom!("load")); .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. // 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))] #[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn set_stylesheet(&self, s: Arc<Stylesheet>) { pub(crate) fn set_stylesheet(
&self,
s: Arc<Stylesheet>,
cache_key: Option<StylesheetContentsCacheKey>,
need_clean_cssom: bool,
) {
let stylesheets_owner = self.stylesheet_list_owner(); let stylesheets_owner = self.stylesheet_list_owner();
if let Some(ref s) = *self.stylesheet.borrow() { if let Some(ref s) = *self.stylesheet.borrow() {
stylesheets_owner 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.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); 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<Arc<Stylesheet>> { pub(crate) fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
self.stylesheet.borrow().clone() self.stylesheet.borrow().clone()
} }
@ -184,18 +234,55 @@ impl HTMLStyleElement {
}) })
} }
fn create_owned_contents_stylesheet(&self) -> Option<Arc<Stylesheet>> {
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) { fn clean_stylesheet_ownership(&self) {
if let Some(cssom_stylesheet) = self.cssom_stylesheet.get() { 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); cssom_stylesheet.set_owner_node(None);
} }
self.cssom_stylesheet.set(None); self.cssom_stylesheet.set(None);
} }
fn remove_stylesheet(&self) { fn remove_stylesheet(&self) {
self.clean_stylesheet_ownership();
if let Some(s) = self.stylesheet.borrow_mut().take() { if let Some(s) = self.stylesheet.borrow_mut().take() {
self.clean_stylesheet_ownership();
self.stylesheet_list_owner() 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();
} }
} }
} }

View file

@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
use cssparser::{Parser, ParserInput}; use cssparser::{Parser, ParserInput};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use servo_arc::Arc; use servo_arc::Arc;
@ -26,7 +28,7 @@ pub(crate) struct MediaList {
parent_stylesheet: Dom<CSSStyleSheet>, parent_stylesheet: Dom<CSSStyleSheet>,
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
#[no_trace] #[no_trace]
media_queries: Arc<Locked<StyleMediaList>>, media_queries: RefCell<Arc<Locked<StyleMediaList>>>,
} }
impl MediaList { impl MediaList {
@ -38,7 +40,7 @@ impl MediaList {
MediaList { MediaList {
parent_stylesheet: Dom::from_ref(parent_stylesheet), parent_stylesheet: Dom::from_ref(parent_stylesheet),
reflector_: Reflector::new(), reflector_: Reflector::new(),
media_queries, media_queries: RefCell::new(media_queries),
} }
} }
@ -57,7 +59,7 @@ impl MediaList {
} }
fn shared_lock(&self) -> &SharedRwLock { fn shared_lock(&self) -> &SharedRwLock {
&self.parent_stylesheet.style_stylesheet().shared_lock self.parent_stylesheet.shared_lock()
} }
/// <https://drafts.csswg.org/cssom/#parse-a-media-query-list> /// <https://drafts.csswg.org/cssom/#parse-a-media-query-list>
@ -109,7 +111,11 @@ impl MediaList {
pub(crate) fn clone_media_list(&self) -> StyleMediaList { pub(crate) fn clone_media_list(&self) -> StyleMediaList {
let guard = self.shared_lock().read(); 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<Locked<StyleMediaList>>) {
*self.media_queries.borrow_mut() = media_queries;
} }
} }
@ -117,14 +123,21 @@ impl MediaListMethods<crate::DomTypeHolder> for MediaList {
/// <https://drafts.csswg.org/cssom/#dom-medialist-mediatext> /// <https://drafts.csswg.org/cssom/#dom-medialist-mediatext>
fn MediaText(&self) -> DOMString { fn MediaText(&self) -> DOMString {
let guard = self.shared_lock().read(); 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(),
)
} }
/// <https://drafts.csswg.org/cssom/#dom-medialist-mediatext> /// <https://drafts.csswg.org/cssom/#dom-medialist-mediatext>
fn SetMediaText(&self, value: DOMString) { fn SetMediaText(&self, value: DOMString) {
self.parent_stylesheet.will_modify();
let global = self.global(); let global = self.global();
let mut guard = self.shared_lock().write(); 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()); *media_queries = Self::parse_media_list(&value, global.as_window());
self.parent_stylesheet.notify_invalidations(); self.parent_stylesheet.notify_invalidations();
} }
@ -132,13 +145,18 @@ impl MediaListMethods<crate::DomTypeHolder> for MediaList {
// https://drafts.csswg.org/cssom/#dom-medialist-length // https://drafts.csswg.org/cssom/#dom-medialist-length
fn Length(&self) -> u32 { fn Length(&self) -> u32 {
let guard = self.shared_lock().read(); 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
} }
/// <https://drafts.csswg.org/cssom/#dom-medialist-item> /// <https://drafts.csswg.org/cssom/#dom-medialist-item>
fn Item(&self, index: u32) -> Option<DOMString> { fn Item(&self, index: u32) -> Option<DOMString> {
let guard = self.shared_lock().read(); let guard = self.shared_lock().read();
self.media_queries self.media_queries
.borrow()
.read_with(&guard) .read_with(&guard)
.media_queries .media_queries
.get(index as usize) .get(index as usize)
@ -160,18 +178,28 @@ impl MediaListMethods<crate::DomTypeHolder> for MediaList {
return; return;
} }
// Step 3 // Step 3
let m_serialized = m.clone().unwrap().to_css_string(); {
let mut guard = self.shared_lock().write(); let m_serialized = m.clone().unwrap().to_css_string();
let mq = self.media_queries.write_with(&mut guard); let guard = self.shared_lock().read();
let any = mq let any = self
.media_queries .media_queries
.iter() .borrow()
.any(|q| m_serialized == q.to_css_string()); .read_with(&guard)
if any { .media_queries
return; .iter()
.any(|q| m_serialized == q.to_css_string());
if any {
return;
}
} }
// Step 4 // 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(); self.parent_stylesheet.notify_invalidations();
} }
@ -185,9 +213,11 @@ impl MediaListMethods<crate::DomTypeHolder> for MediaList {
return; return;
} }
// Step 3 // Step 3
self.parent_stylesheet.will_modify();
let m_serialized = m.unwrap().to_css_string(); let m_serialized = m.unwrap().to_css_string();
let mut guard = self.shared_lock().write(); 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 let new_vec = media_list
.media_queries .media_queries
.drain(..) .drain(..)

View file

@ -562,6 +562,7 @@ pub(crate) mod storage;
pub(crate) mod storageevent; pub(crate) mod storageevent;
pub(crate) mod stylepropertymapreadonly; pub(crate) mod stylepropertymapreadonly;
pub(crate) mod stylesheet; pub(crate) mod stylesheet;
pub(crate) mod stylesheetcontentscache;
pub(crate) mod stylesheetlist; pub(crate) mod stylesheetlist;
pub(crate) mod submitevent; pub(crate) mod submitevent;
pub(crate) mod subtlecrypto; pub(crate) mod subtlecrypto;

View file

@ -242,7 +242,7 @@ impl ShadowRoot {
debug_assert!(cssom_stylesheet.is_constructed()); debug_assert!(cssom_stylesheet.is_constructed());
let stylesheets = &mut self.author_styles.borrow_mut().stylesheets; 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(); let insertion_point = stylesheets.iter().last().cloned();

View file

@ -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<Atom>,
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<HashMap<StylesheetContentsCacheKey, ServoArc<StylesheetContents>>> =
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<StylesheetContents>,
) -> (
Option<StylesheetContentsCacheKey>,
ServoArc<StylesheetContents>,
) {
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)
});
}
}

View file

@ -214,6 +214,9 @@ impl FetchResponseListener for StylesheetContext {
.is_none_or(|generation| generation == link.get_request_generation_id()); .is_none_or(|generation| generation == link.get_request_generation_id());
if is_stylesheet_load_applicable { if is_stylesheet_load_applicable {
let shared_lock = document.style_shared_lock().clone(); 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( let sheet = Arc::new(Stylesheet::from_bytes(
&data, &data,
UrlExtraData(final_url.get_arc()), UrlExtraData(final_url.get_arc()),
@ -235,6 +238,9 @@ impl FetchResponseListener for StylesheetContext {
} }
}, },
StylesheetContextSource::Import(ref stylesheet) => { StylesheetContextSource::Import(ref stylesheet) => {
#[cfg(feature = "tracing")]
let _span =
tracing::trace_span!("ParseStylesheet", servo_profiling = true).entered();
Stylesheet::update_from_bytes( Stylesheet::update_from_bytes(
stylesheet, stylesheet,
&data, &data,