MCPcopy Create free account
hub / github.com/Stranger6667/css-inline / merge_styles

Function merge_styles

css-inline/src/html/serializer.rs:699–814  ·  view source on GitHub ↗

Merge a new set of styles into an current one, considering the rules of CSS precedence. The merge process maintains the order of specificity and respects the `!important` rule in CSS.

(
    writer: &mut Wr,
    current_style: &StrTendril,
    new_styles: &ElementStyleMap<'_>,
    declarations_buffer: &mut SmallVec<[Vec<u8>; 8]>,
    minify_css: bool,
)

Source from the content-addressed store, hash-verified

697///
698/// The merge process maintains the order of specificity and respects the `!important` rule in CSS.
699fn merge_styles<Wr: Write>(
700 writer: &mut Wr,
701 current_style: &StrTendril,
702 new_styles: &ElementStyleMap<'_>,
703 declarations_buffer: &mut SmallVec<[Vec<u8>; 8]>,
704 minify_css: bool,
705) -> Result<(), InlineError> {
706 // This function is designed with a focus on reusing existing allocations where possible
707 // We start by parsing the current declarations in the "style" attribute
708 let mut parser_input = cssparser::ParserInput::new(current_style);
709 let mut parser = cssparser::Parser::new(&mut parser_input);
710 let mut declaration_parser = parser::CSSDeclarationListParser;
711 let current_declarations = cssparser::RuleBodyParser::new(&mut parser, &mut declaration_parser);
712 // We manually manage the length of our buffer. The buffer may contain slots used
713 // in previous runs, and we want to access only the portion that we build in this iteration
714 let mut parsed_declarations_count: usize = 0;
715 for (idx, declaration) in current_declarations.enumerate() {
716 parsed_declarations_count = parsed_declarations_count.saturating_add(1);
717 let (property, value) = declaration?;
718 let estimated_declaration_size = property
719 .len()
720 .saturating_add(STYLE_SEPARATOR.len())
721 .saturating_add(value.len());
722 // We store the existing style declarations in the buffer for later merging with new styles
723 // If possible, we reuse existing slots in the buffer to avoid additional allocations
724 if let Some(buffer) = declarations_buffer.get_mut(idx) {
725 buffer.clear();
726 buffer.reserve(estimated_declaration_size);
727 write_declaration(buffer, &property, value, minify_css)?;
728 } else {
729 let mut buffer = Vec::with_capacity(estimated_declaration_size);
730 write_declaration(&mut buffer, &property, value, minify_css)?;
731 declarations_buffer.push(buffer);
732 }
733 }
734 // Keep the number of current declarations to write them last as they have the precedence
735 let current_declarations_count = parsed_declarations_count;
736 // Next, we iterate over the new styles and merge them into our existing set
737 // New rules will not override old ones unless they are marked as `!important`
738 let sep = if minify_css {
739 STYLE_SEPARATOR_MIN
740 } else {
741 STYLE_SEPARATOR
742 };
743 for (property, _, value) in new_styles {
744 match (
745 value.trim_end().strip_suffix("!important"),
746 declarations_buffer
747 .iter_mut()
748 .take(parsed_declarations_count)
749 .find(|style| {
750 style.starts_with(property.as_bytes())
751 && style.get(property.len()..property.len().saturating_add(sep.len()))
752 == Some(sep)
753 }),
754 ) {
755 // The new rule is `!important` and there's an existing rule with the same name
756 // Only override if the existing inline rule is NOT `!important`.

Callers 1

start_elemMethod · 0.85

Calls 6

write_declarationFunction · 0.85
write_declaration_valueFunction · 0.85
pushMethod · 0.80
findMethod · 0.80
as_bytesMethod · 0.80
getMethod · 0.45

Tested by

no test coverage detected