style: Factor out adding a rule in CascadeData::add_rule

This shouldn't have any behavior change, but is necessary because for
cascade layers we are going to need to handle the child rules / sheets
ourselves, in order to handle nested layers properly.

Differential Revision: https://phabricator.services.mozilla.com/D124334
This commit is contained in:
Emilio Cobos Álvarez 2023-05-27 06:01:30 +02:00 committed by Oriol Brufau
parent 9822db5d3c
commit 9976f9a589

View file

@ -2125,6 +2125,179 @@ impl CascadeData {
}
}
#[inline]
fn add_rule<S>(
&mut self,
rule: &CssRule,
_device: &Device,
quirks_mode: QuirksMode,
stylesheet: &S,
guard: &SharedRwLockReadGuard,
rebuild_kind: SheetRebuildKind,
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), FailedAllocationError>
where
S: StylesheetInDocument + 'static,
{
match *rule {
CssRule::Style(ref locked) => {
let style_rule = locked.read_with(&guard);
self.num_declarations += style_rule.block.read_with(&guard).len();
for selector in &style_rule.selectors.0 {
self.num_selectors += 1;
let pseudo_element = selector.pseudo_element();
if let Some(pseudo) = pseudo_element {
if pseudo.is_precomputed() {
debug_assert!(selector.is_universal());
debug_assert_eq!(stylesheet.contents().origin, Origin::UserAgent);
precomputed_pseudo_element_decls
.as_mut()
.expect("Expected precomputed declarations for the UA level")
.get_or_insert_with(pseudo, Vec::new)
.push(ApplicableDeclarationBlock::new(
StyleSource::from_rule(locked.clone()),
self.rules_source_order,
CascadeLevel::UANormal,
selector.specificity(),
));
continue;
}
if pseudo.is_unknown_webkit_pseudo_element() {
continue;
}
}
let hashes = AncestorHashes::new(&selector, quirks_mode);
let rule = Rule::new(
selector.clone(),
hashes,
locked.clone(),
self.rules_source_order,
);
if rebuild_kind.should_rebuild_invalidation() {
self.invalidation_map.note_selector(selector, quirks_mode)?;
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: &mut needs_revalidation,
passed_rightmost_selector: false,
attribute_dependencies: &mut self.attribute_dependencies,
state_dependencies: &mut self.state_dependencies,
document_state_dependencies: &mut self.document_state_dependencies,
mapped_ids: &mut self.mapped_ids,
};
rule.selector.visit(&mut visitor);
if needs_revalidation {
self.selectors_for_cache_revalidation.insert(
RevalidationSelectorAndHashes::new(
rule.selector.clone(),
rule.hashes.clone(),
),
quirks_mode,
)?;
}
}
// Part is special, since given it doesn't have any
// selectors inside, it's not worth using a whole
// SelectorMap for it.
if let Some(parts) = selector.parts() {
// ::part() has all semantics, so we just need to
// put any of them in the selector map.
//
// We choose the last one quite arbitrarily,
// expecting it's slightly more likely to be more
// specific.
self.part_rules
.get_or_insert_with(|| Box::new(Default::default()))
.for_insertion(pseudo_element)
.try_entry(parts.last().unwrap().clone().0)?
.or_insert_with(SmallVec::new)
.try_push(rule)?;
} else {
// NOTE(emilio): It's fine to look at :host and then at
// ::slotted(..), since :host::slotted(..) could never
// possibly match, as <slot> is not a valid shadow host.
let rules =
if selector.is_featureless_host_selector_or_pseudo_element() {
self.host_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else if selector.is_slotted() {
self.slotted_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else {
&mut self.normal_rules
}
.for_insertion(pseudo_element);
rules.insert(rule, quirks_mode)?;
}
}
self.rules_source_order += 1;
},
CssRule::Import(ref lock) => {
if rebuild_kind.should_rebuild_invalidation() {
let import_rule = lock.read_with(guard);
self.effective_media_query_results
.saw_effective(import_rule);
}
// NOTE: effective_rules visits the inner stylesheet if
// appropriate.
},
CssRule::Media(ref lock) => {
if rebuild_kind.should_rebuild_invalidation() {
let media_rule = lock.read_with(guard);
self.effective_media_query_results.saw_effective(media_rule);
}
},
CssRule::Keyframes(ref keyframes_rule) => {
let keyframes_rule = keyframes_rule.read_with(guard);
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
// Don't let a prefixed keyframes animation override a non-prefixed one.
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
self.animations
.get(keyframes_rule.name.as_atom())
.map_or(true, |rule| rule.vendor_prefix.is_some());
if needs_insertion {
let animation = KeyframesAnimation::from_keyframes(
&keyframes_rule.keyframes,
keyframes_rule.vendor_prefix.clone(),
guard,
);
debug!("Found valid keyframe animation: {:?}", animation);
self.animations
.try_insert(keyframes_rule.name.as_atom().clone(), animation)?;
}
},
#[cfg(feature = "gecko")]
CssRule::FontFace(ref rule) => {
self.extra_data.add_font_face(rule);
},
#[cfg(feature = "gecko")]
CssRule::FontFeatureValues(ref rule) => {
self.extra_data.add_font_feature_values(rule);
},
#[cfg(feature = "gecko")]
CssRule::CounterStyle(ref rule) => {
self.extra_data.add_counter_style(guard, rule);
},
#[cfg(feature = "gecko")]
CssRule::Page(ref rule) => {
self.extra_data.add_page(rule);
},
// We don't care about any other rule.
_ => {},
}
Ok(())
}
// Returns Err(..) to signify OOM
fn add_stylesheet<S>(
&mut self,
@ -2143,169 +2316,22 @@ impl CascadeData {
}
let contents = stylesheet.contents();
let origin = contents.origin;
if rebuild_kind.should_rebuild_invalidation() {
self.effective_media_query_results.saw_effective(contents);
}
for rule in stylesheet.effective_rules(device, guard) {
match *rule {
CssRule::Style(ref locked) => {
let style_rule = locked.read_with(&guard);
self.num_declarations += style_rule.block.read_with(&guard).len();
for selector in &style_rule.selectors.0 {
self.num_selectors += 1;
let pseudo_element = selector.pseudo_element();
if let Some(pseudo) = pseudo_element {
if pseudo.is_precomputed() {
debug_assert!(selector.is_universal());
debug_assert!(matches!(origin, Origin::UserAgent));
precomputed_pseudo_element_decls
.as_mut()
.expect("Expected precomputed declarations for the UA level")
.get_or_insert_with(pseudo, Vec::new)
.push(ApplicableDeclarationBlock::new(
StyleSource::from_rule(locked.clone()),
self.rules_source_order,
CascadeLevel::UANormal,
selector.specificity(),
));
continue;
}
if pseudo.is_unknown_webkit_pseudo_element() {
continue;
}
}
let hashes = AncestorHashes::new(&selector, quirks_mode);
let rule = Rule::new(
selector.clone(),
hashes,
locked.clone(),
self.rules_source_order,
);
if rebuild_kind.should_rebuild_invalidation() {
self.invalidation_map.note_selector(selector, quirks_mode)?;
let mut needs_revalidation = false;
let mut visitor = StylistSelectorVisitor {
needs_revalidation: &mut needs_revalidation,
passed_rightmost_selector: false,
attribute_dependencies: &mut self.attribute_dependencies,
state_dependencies: &mut self.state_dependencies,
document_state_dependencies: &mut self.document_state_dependencies,
mapped_ids: &mut self.mapped_ids,
};
rule.selector.visit(&mut visitor);
if needs_revalidation {
self.selectors_for_cache_revalidation.insert(
RevalidationSelectorAndHashes::new(
rule.selector.clone(),
rule.hashes.clone(),
),
quirks_mode,
)?;
}
}
// Part is special, since given it doesn't have any
// selectors inside, it's not worth using a whole
// SelectorMap for it.
if let Some(parts) = selector.parts() {
// ::part() has all semantics, so we just need to
// put any of them in the selector map.
//
// We choose the last one quite arbitrarily,
// expecting it's slightly more likely to be more
// specific.
self.part_rules
.get_or_insert_with(|| Box::new(Default::default()))
.for_insertion(pseudo_element)
.try_entry(parts.last().unwrap().clone().0)?
.or_insert_with(SmallVec::new)
.try_push(rule)?;
} else {
// NOTE(emilio): It's fine to look at :host and then at
// ::slotted(..), since :host::slotted(..) could never
// possibly match, as <slot> is not a valid shadow host.
let rules =
if selector.is_featureless_host_selector_or_pseudo_element() {
self.host_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else if selector.is_slotted() {
self.slotted_rules
.get_or_insert_with(|| Box::new(Default::default()))
} else {
&mut self.normal_rules
}
.for_insertion(pseudo_element);
rules.insert(rule, quirks_mode)?;
}
}
self.rules_source_order += 1;
},
CssRule::Import(ref lock) => {
if rebuild_kind.should_rebuild_invalidation() {
let import_rule = lock.read_with(guard);
self.effective_media_query_results
.saw_effective(import_rule);
}
// NOTE: effective_rules visits the inner stylesheet if
// appropriate.
},
CssRule::Media(ref lock) => {
if rebuild_kind.should_rebuild_invalidation() {
let media_rule = lock.read_with(guard);
self.effective_media_query_results.saw_effective(media_rule);
}
},
CssRule::Keyframes(ref keyframes_rule) => {
let keyframes_rule = keyframes_rule.read_with(guard);
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
// Don't let a prefixed keyframes animation override a non-prefixed one.
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
self.animations
.get(keyframes_rule.name.as_atom())
.map_or(true, |rule| rule.vendor_prefix.is_some());
if needs_insertion {
let animation = KeyframesAnimation::from_keyframes(
&keyframes_rule.keyframes,
keyframes_rule.vendor_prefix.clone(),
guard,
);
debug!("Found valid keyframe animation: {:?}", animation);
self.animations
.try_insert(keyframes_rule.name.as_atom().clone(), animation)?;
}
},
#[cfg(feature = "gecko")]
CssRule::FontFace(ref rule) => {
self.extra_data.add_font_face(rule);
},
#[cfg(feature = "gecko")]
CssRule::FontFeatureValues(ref rule) => {
self.extra_data.add_font_feature_values(rule);
},
#[cfg(feature = "gecko")]
CssRule::CounterStyle(ref rule) => {
self.extra_data.add_counter_style(guard, rule);
},
#[cfg(feature = "gecko")]
CssRule::Page(ref rule) => {
self.extra_data.add_page(rule);
},
// We don't care about any other rule.
_ => {},
}
self.add_rule(
rule,
device,
quirks_mode,
stylesheet,
guard,
rebuild_kind,
precomputed_pseudo_element_decls.as_deref_mut(),
)?;
}
Ok(())