1use cssparser::Parser;
11use selectors::matching::QuirksMode;
12use selectors::parser::{ParseRelative, SelectorList};
13use style::font_face::FontFaceRule;
14use style::invalidation::stylesheets::RuleChangeKind;
15use style::parser::ParserContext;
16use style::properties::font_face::DescriptorId as FontFaceDescriptorId;
17use style::properties::{
18 Importance, PropertyDeclarationBlock, PropertyId, SourcePropertyDeclaration,
19 parse_one_declaration_into,
20};
21use style::selector_parser::SelectorParser;
22use style::servo_arc::Arc as ServoArc;
23use style::shared_lock::{Locked, SharedRwLockReadGuard, ToCssWithGuard};
24use style::stylesheets::keyframes_rule::{Keyframe, KeyframesRule};
25use style::stylesheets::{
26 AllowImportRules, CssRule, CssRuleRef, CssRuleType, CssRuleTypes, CssRules, DocumentStyleSheet,
27 Origin, OriginSet, RulesMutateError, StylesheetInDocument,
28};
29use style_traits::{CssStringWriter, ParsingMode, ToCss};
30use cssparser::ToCss as _;
32
33use blitz_traits::node_id::NodeId;
34
35use crate::BaseDocument;
36use crate::net::StylesheetLoader;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum CssomError {
42 NotFound,
45 Syntax,
47 IndexSize,
49 HierarchyRequest,
51 InvalidState,
53 NotSupported,
55}
56
57impl CssomError {
58 pub fn dom_exception_name(self) -> &'static str {
60 match self {
61 Self::NotFound => "NotFoundError",
62 Self::Syntax => "SyntaxError",
63 Self::IndexSize => "IndexSizeError",
64 Self::HierarchyRequest => "HierarchyRequestError",
65 Self::InvalidState => "InvalidStateError",
66 Self::NotSupported => "NotSupportedError",
67 }
68 }
69}
70
71impl From<RulesMutateError> for CssomError {
72 fn from(err: RulesMutateError) -> Self {
73 match err {
74 RulesMutateError::Syntax => Self::Syntax,
75 RulesMutateError::IndexSize => Self::IndexSize,
76 RulesMutateError::HierarchyRequest => Self::HierarchyRequest,
77 RulesMutateError::InvalidState => Self::InvalidState,
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
84pub struct CssRuleInfo {
85 pub interface: &'static str,
87 pub rule_type: u16,
89 pub css_text: String,
91 pub has_child_rules: bool,
93 pub has_style: bool,
95 pub attributes: Vec<(&'static str, String)>,
97}
98
99#[derive(Clone)]
101enum RuleList {
102 Css(ServoArc<Locked<CssRules>>),
103 Keyframes(ServoArc<Locked<KeyframesRule>>),
104}
105
106#[derive(Clone)]
108enum RuleHandle {
109 Rule(CssRule),
110 Keyframe(ServoArc<Locked<Keyframe>>),
111}
112
113enum DeclarationTarget {
115 Block {
116 block: ServoArc<Locked<PropertyDeclarationBlock>>,
117 rule_type: CssRuleType,
118 },
119 FontFace(ServoArc<Locked<FontFaceRule>>),
120}
121
122fn child_rule_list(rule: &CssRule, guard: &SharedRwLockReadGuard) -> Option<RuleList> {
123 let rules = match rule {
124 CssRule::Style(r) => r.read_with(guard).rules.clone()?,
125 CssRule::Media(r) => r.rules.clone(),
126 CssRule::Supports(r) => r.rules.clone(),
127 CssRule::Container(r) => r.rules.clone(),
128 CssRule::Page(r) => r.read_with(guard).rules.clone(),
129 CssRule::Document(r) => r.rules.clone(),
130 CssRule::LayerBlock(r) => r.rules.clone(),
131 CssRule::Scope(r) => r.rules.clone(),
132 CssRule::StartingStyle(r) => r.rules.clone(),
133 CssRule::Keyframes(r) => return Some(RuleList::Keyframes(r.clone())),
134 _ => return None,
135 };
136 Some(RuleList::Css(rules))
137}
138
139fn declaration_target(
140 handle: &RuleHandle,
141 guard: &SharedRwLockReadGuard,
142) -> Option<DeclarationTarget> {
143 let (block, rule_type) = match handle {
144 RuleHandle::Keyframe(k) => (k.read_with(guard).block.clone(), CssRuleType::Keyframe),
145 RuleHandle::Rule(rule) => match rule {
146 CssRule::Style(r) => (r.read_with(guard).block.clone(), CssRuleType::Style),
147 CssRule::Page(r) => (r.read_with(guard).block.clone(), CssRuleType::Page),
148 CssRule::Margin(r) => (r.block.clone(), CssRuleType::Margin),
149 CssRule::NestedDeclarations(r) => (
150 r.read_with(guard).block.clone(),
151 CssRuleType::NestedDeclarations,
152 ),
153 CssRule::PositionTry(r) => (r.read_with(guard).block.clone(), CssRuleType::PositionTry),
154 CssRule::FontFace(r) => return Some(DeclarationTarget::FontFace(r.clone())),
155 _ => return None,
156 },
157 };
158 Some(DeclarationTarget::Block { block, rule_type })
159}
160
161fn css_rule_interface(rule: &CssRule) -> &'static str {
162 match rule {
163 CssRule::Style(_) => "CSSStyleRule",
164 CssRule::Namespace(_) => "CSSNamespaceRule",
165 CssRule::Import(_) => "CSSImportRule",
166 CssRule::Media(_) => "CSSMediaRule",
167 CssRule::CustomMedia(_) => "CSSCustomMediaRule",
168 CssRule::Container(_) => "CSSContainerRule",
169 CssRule::FontFace(_) => "CSSFontFaceRule",
170 CssRule::FontFeatureValues(_) => "CSSFontFeatureValuesRule",
171 CssRule::FontPaletteValues(_) => "CSSFontPaletteValuesRule",
172 CssRule::CounterStyle(_) => "CSSCounterStyleRule",
173 CssRule::Keyframes(_) => "CSSKeyframesRule",
174 CssRule::Margin(_) => "CSSMarginRule",
175 CssRule::Supports(_) => "CSSSupportsRule",
176 CssRule::Page(_) => "CSSPageRule",
177 CssRule::Property(_) => "CSSPropertyRule",
178 CssRule::Document(_) => "CSSMozDocumentRule",
179 CssRule::LayerBlock(_) => "CSSLayerBlockRule",
180 CssRule::LayerStatement(_) => "CSSLayerStatementRule",
181 CssRule::Scope(_) => "CSSScopeRule",
182 CssRule::StartingStyle(_) => "CSSStartingStyleRule",
183 CssRule::AppearanceBase(_) => "CSSAppearanceBaseRule",
184 CssRule::PositionTry(_) => "CSSPositionTryRule",
185 CssRule::NestedDeclarations(_) => "CSSNestedDeclarations",
186 CssRule::ViewTransition(_) => "CSSViewTransitionRule",
187 }
188}
189
190fn comma_separated<'a, T: ToCss + 'a>(items: impl IntoIterator<Item = &'a T>) -> String {
192 let mut css = CssStringWriter::new();
193 for (i, item) in items.into_iter().enumerate() {
194 if i > 0 {
195 css.push_str(", ");
196 }
197 let _ = item.to_css(&mut style_traits::CssWriter::new(&mut css));
198 }
199 css
200}
201
202fn rule_attributes(rule: &CssRule, guard: &SharedRwLockReadGuard) -> Vec<(&'static str, String)> {
203 match rule {
204 CssRule::Style(r) => {
205 vec![("selectorText", r.read_with(guard).selectors.to_css_string())]
206 }
207 CssRule::Namespace(r) => vec![
208 ("namespaceURI", r.url.to_string()),
209 (
210 "prefix",
211 r.prefix.as_ref().map(|p| p.to_string()).unwrap_or_default(),
212 ),
213 ],
214 CssRule::Import(r) => {
215 let r = r.read_with(guard);
216 vec![
217 ("href", r.url.as_str().to_string()),
218 (
219 "media",
220 r.stylesheet
221 .media(guard)
222 .map(|m| m.to_css_string())
223 .unwrap_or_default(),
224 ),
225 ]
226 }
227 CssRule::Media(r) => vec![(
228 "conditionText",
229 r.media_queries.read_with(guard).to_css_string(),
230 )],
231 CssRule::Supports(r) => vec![("conditionText", r.condition.to_css_string())],
232 CssRule::Container(r) => vec![("conditionText", r.conditions.to_css_string())],
233 CssRule::Page(r) => vec![("selectorText", r.read_with(guard).selectors.to_css_string())],
234 CssRule::Keyframes(r) => vec![("name", r.read_with(guard).name.to_css_string())],
235 CssRule::LayerBlock(r) => vec![(
236 "name",
237 r.name
238 .as_ref()
239 .map(|n| n.to_css_string())
240 .unwrap_or_default(),
241 )],
242 CssRule::LayerStatement(r) => vec![("nameList", comma_separated(r.names.iter()))],
243 CssRule::Property(r) => {
244 let mut attrs = vec![("name", r.name.to_css_string())];
245 for (attr, id) in [
246 ("syntax", style::properties::property::DescriptorId::Syntax),
247 (
248 "inherits",
249 style::properties::property::DescriptorId::Inherits,
250 ),
251 (
252 "initialValue",
253 style::properties::property::DescriptorId::InitialValue,
254 ),
255 ] {
256 let mut css = CssStringWriter::new();
257 let _ = r.descriptors.get(id, &mut css);
258 attrs.push((attr, css));
259 }
260 attrs
261 }
262 CssRule::CounterStyle(r) => vec![("name", r.read_with(guard).name().to_css_string())],
263 CssRule::FontFeatureValues(r) => {
264 vec![("fontFamily", comma_separated(r.family_names.iter()))]
265 }
266 CssRule::FontPaletteValues(r) => vec![
267 ("name", r.name.to_css_string()),
268 ("fontFamily", comma_separated(r.family_names.iter())),
269 (
270 "basePalette",
271 r.base_palette
272 .as_ref()
273 .map(|b| b.to_css_string())
274 .unwrap_or_default(),
275 ),
276 ("overrideColors", comma_separated(r.override_colors.iter())),
277 ],
278 CssRule::Margin(r) => vec![("name", format!("{:?}", r.rule_type))],
279 CssRule::PositionTry(r) => vec![("name", r.read_with(guard).name.to_css_string())],
280 _ => Vec::new(),
281 }
282}
283
284impl BaseDocument {
285 pub fn stylesheet_owner_nodes(&self) -> impl Iterator<Item = NodeId> + '_ {
292 self.nodes_to_stylesheet.keys().copied()
293 }
294
295 pub fn node_has_stylesheet(&self, node_id: NodeId) -> bool {
297 self.nodes_to_stylesheet.contains_key(&node_id)
298 }
299
300 pub fn stylesheet_generation(&self) -> u64 {
304 self.stylesheet_generation
305 }
306
307 pub fn stylesheet_disabled(&self, node_id: NodeId) -> bool {
311 self.nodes_to_stylesheet
312 .get(&node_id)
313 .is_some_and(|sheet| sheet.0.disabled())
314 }
315
316 pub fn set_stylesheet_disabled(&mut self, node_id: NodeId, disabled: bool) -> bool {
322 let Some(sheet) = self.nodes_to_stylesheet.get(&node_id) else {
323 return false;
324 };
325 if sheet.0.set_disabled(disabled) {
326 self.stylist
327 .force_stylesheet_origins_dirty(OriginSet::all());
328 }
329 true
330 }
331
332 fn stylesheet_loader(&self) -> StylesheetLoader {
333 StylesheetLoader {
334 tx: self.tx.clone(),
335 doc_id: self.id(),
336 net_provider: self.net_provider.clone(),
337 shell_provider: self.shell_provider.clone(),
338 abort_signal: self.abort_signal.clone(),
339 import_depth: 0,
340 }
341 }
342
343 fn resolve_rule(
346 sheet: &DocumentStyleSheet,
347 guard: &SharedRwLockReadGuard,
348 path: &[usize],
349 on_ancestor: impl FnMut(&CssRule),
350 ) -> Option<RuleHandle> {
351 let (index, parent_path) = path.split_last()?;
352 let list = Self::resolve_rule_list(sheet, guard, parent_path, on_ancestor)?;
353 Some(match list {
354 RuleList::Css(rules) => RuleHandle::Rule(rules.read_with(guard).0.get(*index)?.clone()),
355 RuleList::Keyframes(keyframes) => {
356 RuleHandle::Keyframe(keyframes.read_with(guard).keyframes.get(*index)?.clone())
357 }
358 })
359 }
360
361 fn resolve_rule_list(
366 sheet: &DocumentStyleSheet,
367 guard: &SharedRwLockReadGuard,
368 path: &[usize],
369 mut on_ancestor: impl FnMut(&CssRule),
370 ) -> Option<RuleList> {
371 let mut list = RuleList::Css(sheet.contents(guard).rules.clone());
372 for &index in path {
373 let RuleList::Css(rules) = &list else {
374 return None;
375 };
376 let rules = rules.read_with(guard);
377 let rule = rules.0.get(index)?;
378 let child_list = child_rule_list(rule, guard)?;
379 on_ancestor(rule);
380 list = child_list;
381 }
382 Some(list)
383 }
384
385 fn resolve_rule_list_with_ancestors(
387 sheet: &DocumentStyleSheet,
388 guard: &SharedRwLockReadGuard,
389 path: &[usize],
390 ) -> Option<(Vec<CssRule>, RuleList)> {
391 let mut ancestors = Vec::with_capacity(path.len());
392 let list =
393 Self::resolve_rule_list(sheet, guard, path, |rule| ancestors.push(rule.clone()))?;
394 Some((ancestors, list))
395 }
396
397 pub fn stylesheet_rule_count(&self, node_id: NodeId, path: &[usize]) -> Option<usize> {
399 let sheet = self.nodes_to_stylesheet.get(&node_id)?;
400 let guard = self.guard.read();
401 let list = Self::resolve_rule_list(sheet, &guard, path, |_| {})?;
402 Some(match list {
403 RuleList::Css(rules) => rules.read_with(&guard).0.len(),
404 RuleList::Keyframes(keyframes) => keyframes.read_with(&guard).keyframes.len(),
405 })
406 }
407
408 pub fn stylesheet_rule_info(&self, node_id: NodeId, path: &[usize]) -> Option<CssRuleInfo> {
410 let sheet = self.nodes_to_stylesheet.get(&node_id)?;
411 let guard = self.guard.read();
412 let handle = Self::resolve_rule(sheet, &guard, path, |_| {})?;
413 let mut css_text = CssStringWriter::new();
414 let has_style = declaration_target(&handle, &guard).is_some();
415 Some(match handle {
416 RuleHandle::Rule(rule) => {
417 let _ = rule.to_css(&guard, &mut css_text);
418 CssRuleInfo {
419 interface: css_rule_interface(&rule),
420 rule_type: rule.rule_type() as u16,
421 css_text,
422 has_child_rules: child_rule_list(&rule, &guard).is_some(),
423 has_style,
424 attributes: rule_attributes(&rule, &guard),
425 }
426 }
427 RuleHandle::Keyframe(keyframe) => {
428 let keyframe = keyframe.read_with(&guard);
429 let _ = keyframe.to_css(&guard, &mut css_text);
430 CssRuleInfo {
431 interface: "CSSKeyframeRule",
432 rule_type: CssRuleType::Keyframe as u16,
433 css_text,
434 has_child_rules: false,
435 has_style,
436 attributes: vec![("keyText", keyframe.selector.to_css_string())],
437 }
438 }
439 })
440 }
441
442 pub fn stylesheet_insert_rule(
446 &mut self,
447 node_id: NodeId,
448 path: &[usize],
449 rule: &str,
450 index: usize,
451 ) -> Result<usize, CssomError> {
452 let sheet = self
453 .nodes_to_stylesheet
454 .get(&node_id)
455 .ok_or(CssomError::NotFound)?
456 .clone();
457 let loader = self.stylesheet_loader();
458 let lock = &self.guard;
459
460 let (ancestors, list) = {
461 let guard = lock.read();
462 Self::resolve_rule_list_with_ancestors(&sheet, &guard, path)
463 .ok_or(CssomError::NotFound)?
464 };
465
466 let (new_rule, index) = match &list {
467 RuleList::Css(rules) => {
468 let new_rule = {
469 let guard = lock.read();
470 let contents = sheet.contents(&guard);
471 let containing_rule_types =
472 ancestors
473 .iter()
474 .fold(CssRuleTypes::default(), |types, rule| {
475 CssRuleTypes::from_bits(types.bits() | rule.rule_type().bit())
476 });
477 let parse_relative_rule_type = ancestors.last().map(|rule| rule.rule_type());
478 rules.read_with(&guard).parse_rule_for_insert(
479 lock,
480 rule,
481 contents,
482 index,
483 containing_rule_types,
484 parse_relative_rule_type,
485 Some(&loader),
486 AllowImportRules::Yes,
487 )?
488 };
489 {
490 let mut guard = lock.write();
491 rules
492 .write_with(&mut guard)
493 .0
494 .insert(index, new_rule.clone());
495 }
496 (new_rule, index)
497 }
498 RuleList::Keyframes(keyframes) => {
499 let keyframe = {
500 let guard = lock.read();
501 let contents = sheet.contents(&guard);
502 Keyframe::parse(rule, contents, lock).map_err(|_| CssomError::Syntax)?
503 };
504 let index = {
505 let mut guard = lock.write();
506 let keyframes = &mut keyframes.write_with(&mut guard).keyframes;
507 keyframes.push(keyframe);
508 keyframes.len() - 1
509 };
510 (CssRule::Keyframes(keyframes.clone()), index)
511 }
512 };
513
514 let guard = lock.read();
515 if let CssRule::FontFace(_) = &new_rule {
516 crate::net::fetch_font_face_rules(
517 std::iter::once(&new_rule),
518 self.tx.clone(),
519 self.id(),
520 Some(node_id),
521 &self.net_provider,
522 &self.shell_provider,
523 &guard,
524 self.abort_signal.as_ref(),
525 );
526 }
527 let (change_kind, ancestors) = match &list {
528 RuleList::Css(_) => (RuleChangeKind::Insertion, ancestors.as_slice()),
529 RuleList::Keyframes(_) => (
531 RuleChangeKind::Generic,
532 &ancestors[..ancestors.len().saturating_sub(1)],
533 ),
534 };
535 let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
536 self.stylist
537 .rule_changed(&sheet, &new_rule, &guard, change_kind, &ancestor_refs);
538 Ok(index)
539 }
540
541 pub fn stylesheet_delete_rule(
545 &mut self,
546 node_id: NodeId,
547 path: &[usize],
548 index: usize,
549 ) -> Result<(), CssomError> {
550 let sheet = self
551 .nodes_to_stylesheet
552 .get(&node_id)
553 .ok_or(CssomError::NotFound)?
554 .clone();
555 let lock = &self.guard;
556
557 let (ancestors, list) = {
558 let guard = lock.read();
559 Self::resolve_rule_list_with_ancestors(&sheet, &guard, path)
560 .ok_or(CssomError::NotFound)?
561 };
562
563 let (removed_rule, change_kind, ancestors) = match &list {
564 RuleList::Css(rules) => {
565 let mut guard = lock.write();
566 let rules = rules.write_with(&mut guard);
567 let removed = rules.0.get(index).cloned().ok_or(CssomError::IndexSize)?;
568 rules.remove_rule(index)?;
569 (removed, RuleChangeKind::Removal, ancestors.as_slice())
570 }
571 RuleList::Keyframes(keyframes) => {
572 let mut guard = lock.write();
573 let list = &mut keyframes.write_with(&mut guard).keyframes;
574 if index >= list.len() {
575 return Err(CssomError::IndexSize);
576 }
577 list.remove(index);
578 (
579 CssRule::Keyframes(keyframes.clone()),
580 RuleChangeKind::Generic,
581 &ancestors[..ancestors.len().saturating_sub(1)],
582 )
583 }
584 };
585
586 let guard = lock.read();
587 let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
588 self.stylist
589 .rule_changed(&sheet, &removed_rule, &guard, change_kind, &ancestor_refs);
590 Ok(())
591 }
592
593 fn rule_declaration_target(
595 &self,
596 node_id: NodeId,
597 path: &[usize],
598 guard: &SharedRwLockReadGuard,
599 ) -> Option<DeclarationTarget> {
600 let sheet = self.nodes_to_stylesheet.get(&node_id)?;
601 let handle = Self::resolve_rule(sheet, guard, path, |_| {})?;
602 declaration_target(&handle, guard)
603 }
604
605 fn rule_declaration_target_mut(
609 &self,
610 node_id: NodeId,
611 path: &[usize],
612 ) -> Option<(DocumentStyleSheet, Vec<CssRule>, CssRule, DeclarationTarget)> {
613 let sheet = self.nodes_to_stylesheet.get(&node_id)?.clone();
614 let guard = self.guard.read();
615 let mut ancestors = Vec::with_capacity(path.len());
616 let handle = Self::resolve_rule(&sheet, &guard, path, |rule| ancestors.push(rule.clone()))?;
617 let target = declaration_target(&handle, &guard)?;
618 let (changed_rule, ancestors) = match handle {
621 RuleHandle::Rule(rule) => (rule, ancestors),
622 RuleHandle::Keyframe(_) => {
623 let mut ancestors = ancestors;
624 let rule = ancestors.pop()?;
625 (rule, ancestors)
626 }
627 };
628 Some((sheet, ancestors, changed_rule, target))
629 }
630
631 pub fn stylesheet_rule_style_css_text(
633 &self,
634 node_id: NodeId,
635 path: &[usize],
636 ) -> Option<String> {
637 let guard = self.guard.read();
638 let target = self.rule_declaration_target(node_id, path, &guard)?;
639 let mut css = CssStringWriter::new();
640 match target {
641 DeclarationTarget::Block { block, .. } => {
642 let _ = block.read_with(&guard).to_css(&mut css);
643 }
644 DeclarationTarget::FontFace(rule) => {
645 let _ = rule
646 .read_with(&guard)
647 .descriptors
648 .to_css(&mut style_traits::CssWriter::new(&mut css));
649 let trimmed = css.trim_end().len();
651 css.truncate(trimmed);
652 }
653 }
654 Some(css)
655 }
656
657 pub fn stylesheet_rule_style_property_names(
660 &self,
661 node_id: NodeId,
662 path: &[usize],
663 ) -> Option<Vec<String>> {
664 let guard = self.guard.read();
665 let target = self.rule_declaration_target(node_id, path, &guard)?;
666 Some(match target {
667 DeclarationTarget::Block { block, .. } => block
668 .read_with(&guard)
669 .declarations()
670 .iter()
671 .map(|decl| decl.id().name().into_owned())
672 .collect(),
673 DeclarationTarget::FontFace(rule) => {
674 let descriptors = &rule.read_with(&guard).descriptors;
675 (0..descriptors.len())
676 .filter_map(|i| descriptors.at(i))
677 .map(|id| id.name().to_string())
678 .collect()
679 }
680 })
681 }
682
683 pub fn stylesheet_rule_style_get_property(
688 &self,
689 node_id: NodeId,
690 path: &[usize],
691 property: &str,
692 ) -> Option<(String, bool)> {
693 let guard = self.guard.read();
694 let target = self.rule_declaration_target(node_id, path, &guard)?;
695 let mut css = CssStringWriter::new();
696 let important = match target {
697 DeclarationTarget::Block { block, .. } => {
698 let Ok(property_id) = PropertyId::parse_enabled_for_all_content(property) else {
699 return Some((String::new(), false));
700 };
701 let block = block.read_with(&guard);
702 let _ = block.property_value_to_css(&property_id, &mut css);
703 block.property_priority(&property_id).important()
704 }
705 DeclarationTarget::FontFace(rule) => {
706 let Ok(id) = FontFaceDescriptorId::from_ident(property) else {
707 return Some((String::new(), false));
708 };
709 let _ = rule.read_with(&guard).descriptors.get(id, &mut css);
710 false
711 }
712 };
713 Some((css, important))
714 }
715
716 pub fn stylesheet_rule_style_set_property(
720 &mut self,
721 node_id: NodeId,
722 path: &[usize],
723 property: &str,
724 value: &str,
725 important: bool,
726 ) -> Result<(), CssomError> {
727 if value.trim().is_empty() {
728 self.stylesheet_rule_style_remove_property(node_id, path, property)?;
729 return Ok(());
730 }
731 let (sheet, ancestors, rule, target) = self
732 .rule_declaration_target_mut(node_id, path)
733 .ok_or(CssomError::NotFound)?;
734 let url_data = self.url.url_extra_data();
735 let lock = &self.guard;
736
737 let change_kind = match target {
738 DeclarationTarget::Block { block, rule_type } => {
739 let Ok(property_id) = PropertyId::parse_enabled_for_all_content(property) else {
740 return Ok(());
741 };
742 let mut source = SourcePropertyDeclaration::default();
743 if parse_one_declaration_into(
744 &mut source,
745 property_id,
746 value,
747 Origin::Author,
748 &url_data,
749 None,
750 ParsingMode::DEFAULT,
751 QuirksMode::NoQuirks,
752 rule_type,
753 )
754 .is_err()
755 {
756 return Ok(());
757 }
758 let importance = if important {
759 Importance::Important
760 } else {
761 Importance::Normal
762 };
763 let mut guard = lock.write();
764 block
765 .write_with(&mut guard)
766 .extend(source.drain(), importance);
767 match rule_type {
768 CssRuleType::PositionTry => RuleChangeKind::PositionTryDeclarations,
769 _ => RuleChangeKind::StyleRuleDeclarations,
770 }
771 }
772 DeclarationTarget::FontFace(font_face) => {
773 let Ok(id) = FontFaceDescriptorId::from_ident(property) else {
774 return Ok(());
775 };
776 let context = ParserContext::new(
777 Origin::Author,
778 &url_data,
779 Some(CssRuleType::FontFace),
780 ParsingMode::DEFAULT,
781 QuirksMode::NoQuirks,
782 Default::default(),
783 None,
784 None,
785 Default::default(),
786 );
787 let mut parser = Parser::new(value);
788 let mut guard = lock.write();
789 let descriptors = &mut font_face.write_with(&mut guard).descriptors;
790 if descriptors.set(id, &context, &mut parser).is_err() {
791 return Ok(());
792 }
793 RuleChangeKind::Generic
794 }
795 };
796
797 let guard = lock.read();
798 let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
799 self.stylist
800 .rule_changed(&sheet, &rule, &guard, change_kind, &ancestor_refs);
801 Ok(())
802 }
803
804 pub fn stylesheet_rule_style_remove_property(
808 &mut self,
809 node_id: NodeId,
810 path: &[usize],
811 property: &str,
812 ) -> Result<String, CssomError> {
813 let (sheet, ancestors, rule, target) = self
814 .rule_declaration_target_mut(node_id, path)
815 .ok_or(CssomError::NotFound)?;
816 let lock = &self.guard;
817 let mut removed_value = CssStringWriter::new();
818
819 let change_kind = match target {
820 DeclarationTarget::Block { block, rule_type } => {
821 let Ok(property_id) = PropertyId::parse_enabled_for_all_content(property) else {
822 return Ok(removed_value);
823 };
824 let mut guard = lock.write();
825 let block = block.write_with(&mut guard);
826 let _ = block.property_value_to_css(&property_id, &mut removed_value);
827 let Some(first_declaration) = block.first_declaration_to_remove(&property_id)
828 else {
829 return Ok(removed_value);
830 };
831 block.remove_property(&property_id, first_declaration);
832 match rule_type {
833 CssRuleType::PositionTry => RuleChangeKind::PositionTryDeclarations,
834 _ => RuleChangeKind::StyleRuleDeclarations,
835 }
836 }
837 DeclarationTarget::FontFace(font_face) => {
838 let Ok(id) = FontFaceDescriptorId::from_ident(property) else {
839 return Ok(removed_value);
840 };
841 let mut guard = lock.write();
842 let descriptors = &mut font_face.write_with(&mut guard).descriptors;
843 let _ = descriptors.get(id, &mut removed_value);
844 if !descriptors.remove(id) {
845 return Ok(removed_value);
846 }
847 RuleChangeKind::Generic
848 }
849 };
850
851 let guard = lock.read();
852 let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
853 self.stylist
854 .rule_changed(&sheet, &rule, &guard, change_kind, &ancestor_refs);
855 Ok(removed_value)
856 }
857
858 pub fn stylesheet_rule_set_selector_text(
862 &mut self,
863 node_id: NodeId,
864 path: &[usize],
865 selectors: &str,
866 ) -> Result<(), CssomError> {
867 let sheet = self
868 .nodes_to_stylesheet
869 .get(&node_id)
870 .ok_or(CssomError::NotFound)?
871 .clone();
872 let lock = &self.guard;
873
874 let (ancestors, style_rule) = {
875 let guard = lock.read();
876 let mut ancestors = Vec::with_capacity(path.len());
877 let handle =
878 Self::resolve_rule(&sheet, &guard, path, |rule| ancestors.push(rule.clone()))
879 .ok_or(CssomError::NotFound)?;
880 let RuleHandle::Rule(CssRule::Style(style_rule)) = handle else {
881 return Err(CssomError::NotSupported);
882 };
883 (ancestors, style_rule)
884 };
885
886 let parse_relative = ancestors
890 .iter()
891 .rev()
892 .find_map(|rule| match rule.rule_type() {
893 CssRuleType::Style => Some(ParseRelative::ForNesting),
894 CssRuleType::Scope => Some(ParseRelative::ForScope),
895 _ => None,
896 })
897 .unwrap_or(ParseRelative::No);
898
899 let new_selectors = {
900 let guard = lock.read();
901 let contents = sheet.contents(&guard);
902 let selector_parser = SelectorParser {
903 stylesheet_origin: contents.origin,
904 namespaces: &contents.namespaces,
905 url_data: &contents.url_data,
906 for_supports_rule: false,
907 };
908 let mut parser = Parser::new(selectors);
909 let Ok(new_selectors) =
910 SelectorList::parse(&selector_parser, &mut parser, parse_relative)
911 else {
912 return Ok(());
913 };
914 new_selectors
915 };
916
917 {
918 let mut guard = lock.write();
919 style_rule.write_with(&mut guard).selectors = new_selectors;
920 }
921
922 let guard = lock.read();
923 let rule = CssRule::Style(style_rule);
924 let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
925 self.stylist.rule_changed(
926 &sheet,
927 &rule,
928 &guard,
929 RuleChangeKind::Generic,
930 &ancestor_refs,
931 );
932 Ok(())
933 }
934
935 pub fn stylesheet_rule_style_set_css_text(
938 &mut self,
939 node_id: NodeId,
940 path: &[usize],
941 css: &str,
942 ) -> Result<(), CssomError> {
943 let (sheet, ancestors, rule, target) = self
944 .rule_declaration_target_mut(node_id, path)
945 .ok_or(CssomError::NotFound)?;
946 let DeclarationTarget::Block { block, rule_type } = target else {
947 return Err(CssomError::NotSupported);
948 };
949 let new_block = style::properties::declaration_block::parse_style_attribute(
950 css,
951 &self.url.url_extra_data(),
952 None,
953 QuirksMode::NoQuirks,
954 rule_type,
955 );
956 let lock = &self.guard;
957 {
958 let mut guard = lock.write();
959 *block.write_with(&mut guard) = new_block;
960 }
961 let guard = lock.read();
962 let ancestor_refs: Vec<CssRuleRef> = ancestors.iter().map(CssRuleRef::from).collect();
963 let change_kind = match rule_type {
964 CssRuleType::PositionTry => RuleChangeKind::PositionTryDeclarations,
965 _ => RuleChangeKind::StyleRuleDeclarations,
966 };
967 self.stylist
968 .rule_changed(&sheet, &rule, &guard, change_kind, &ancestor_refs);
969 Ok(())
970 }
971}