@@ -320,24 +320,25 @@ impl<'a> CSSInliner<'a> {
320
320
styles. sort_unstable_by ( |_, ( a, _) , _, ( b, _) | a. cmp ( b) ) ;
321
321
match element. attributes . get_style_entry ( ) {
322
322
Entry :: Vacant ( entry) => {
323
- let size_estimate : usize = styles
323
+ let estimated_declaration_size : usize = styles
324
324
. iter ( )
325
325
. map ( |( name, ( _, value) ) | {
326
- name. len ( ) . saturating_add ( value. len ( ) ) . saturating_add ( 2 )
326
+ name. len ( )
327
+ . saturating_add ( STYLE_SEPARATOR . len ( ) )
328
+ . saturating_add ( value. len ( ) )
329
+ // Additional byte for the semicolon symbol
330
+ . saturating_add ( 1 )
327
331
} )
328
332
. sum ( ) ;
329
- let mut final_styles = String :: with_capacity ( size_estimate) ;
330
- for ( name, ( _, value) ) in styles {
331
- final_styles. push_str ( name) ;
332
- final_styles. push ( ':' ) ;
333
- replace_double_quotes ! ( final_styles, name, value) ;
334
- final_styles. push ( ';' ) ;
333
+ let mut buffer = String :: with_capacity ( estimated_declaration_size) ;
334
+ for ( property, ( _, value) ) in styles {
335
+ write_declaration ( & mut buffer, property, value) ;
336
+ buffer. push ( ';' ) ;
335
337
}
336
- entry. insert ( final_styles . into ( ) ) ;
338
+ entry. insert ( buffer . into ( ) ) ;
337
339
}
338
340
Entry :: Occupied ( mut entry) => {
339
- let existing_style = entry. get_mut ( ) ;
340
- merge_styles ( existing_style, styles, & mut style_buffer) ?;
341
+ merge_styles ( entry. get_mut ( ) , styles, & mut style_buffer) ?;
341
342
}
342
343
}
343
344
}
@@ -462,96 +463,109 @@ macro_rules! push_or_update {
462
463
}
463
464
464
465
#[ inline]
465
- fn append_style ( style : & mut String , name : & str , value : & str ) {
466
+ fn write_declaration ( style : & mut String , name : & str , value : & str ) {
466
467
style. push_str ( name) ;
467
468
style. push_str ( STYLE_SEPARATOR ) ;
468
469
replace_double_quotes ! ( style, name, value. trim( ) ) ;
469
470
}
470
471
471
- /// Merge a new set of styles into an existing one, considering the rules of CSS precedence.
472
+ /// Merge a new set of styles into an current one, considering the rules of CSS precedence.
472
473
///
473
474
/// The merge process maintains the order of specificity and respects the `!important` rule in CSS.
474
475
fn merge_styles (
475
- existing_style : & mut StrTendril ,
476
+ current_style : & mut StrTendril ,
476
477
new_styles : & IndexMap < & str , ( Specificity , & str ) > ,
477
- style_buffer : & mut SmallVec < [ String ; 8 ] > ,
478
+ declarations_buffer : & mut SmallVec < [ String ; 8 ] > ,
478
479
) -> Result < ( ) > {
479
480
// This function is designed with a focus on reusing existing allocations where possible
480
- // We start by parsing the existing declarations in the "style" attribute
481
- let mut input = cssparser:: ParserInput :: new ( existing_style ) ;
482
- let mut parser = cssparser:: Parser :: new ( & mut input ) ;
483
- let declarations =
481
+ // We start by parsing the current declarations in the "style" attribute
482
+ let mut parser_input = cssparser:: ParserInput :: new ( current_style ) ;
483
+ let mut parser = cssparser:: Parser :: new ( & mut parser_input ) ;
484
+ let current_declarations =
484
485
cssparser:: DeclarationListParser :: new ( & mut parser, parser:: CSSDeclarationListParser ) ;
485
486
// We manually manage the length of our buffer. The buffer may contain slots used
486
487
// in previous runs, and we want to access only the portion that we build in this iteration
487
- // NOTE: Can't use `count`, because `declarations` should be evaluated just once
488
- let mut length: usize = 0 ;
489
- for ( idx, declaration) in declarations. enumerate ( ) {
490
- length = length. saturating_add ( 1 ) ;
491
- let ( name, value) = declaration?;
492
- let size_estimate = name
488
+ let mut parsed_declarations_count: usize = 0 ;
489
+ for ( idx, declaration) in current_declarations. enumerate ( ) {
490
+ parsed_declarations_count = parsed_declarations_count. saturating_add ( 1 ) ;
491
+ let ( property, value) = declaration?;
492
+ let estimated_declaration_size = property
493
493
. len ( )
494
- . saturating_add ( value . len ( ) )
495
- . saturating_add ( STYLE_SEPARATOR . len ( ) ) ;
496
- // We store the existing style declarations in the `style_buffer` for later merging with new styles
497
- // If possible, we reuse existing slots in the `style_buffer` to avoid additional allocations
498
- if let Some ( style ) = style_buffer . get_mut ( idx) {
499
- style . clear ( ) ;
500
- style . reserve ( size_estimate ) ;
501
- append_style ( style , & name , value) ;
494
+ . saturating_add ( STYLE_SEPARATOR . len ( ) )
495
+ . saturating_add ( value . len ( ) ) ;
496
+ // We store the existing style declarations in the buffer for later merging with new styles
497
+ // If possible, we reuse existing slots in the buffer to avoid additional allocations
498
+ if let Some ( buffer ) = declarations_buffer . get_mut ( idx) {
499
+ buffer . clear ( ) ;
500
+ buffer . reserve ( estimated_declaration_size ) ;
501
+ write_declaration ( buffer , & property , value) ;
502
502
} else {
503
- let mut style = String :: with_capacity ( size_estimate ) ;
504
- append_style ( & mut style , & name , value) ;
505
- style_buffer . push ( style ) ;
503
+ let mut buffer = String :: with_capacity ( estimated_declaration_size ) ;
504
+ write_declaration ( & mut buffer , & property , value) ;
505
+ declarations_buffer . push ( buffer ) ;
506
506
} ;
507
507
}
508
508
// Next, we iterate over the new styles and merge them into our existing set
509
509
// New rules will not override old ones unless they are marked as `!important`
510
- for ( name , ( _, value) ) in new_styles {
510
+ for ( property , ( _, value) ) in new_styles {
511
511
match (
512
512
value. strip_suffix ( "!important" ) ,
513
- style_buffer. iter_mut ( ) . find ( |style| {
514
- style. starts_with ( name)
515
- && style. get ( name. len ( ) ..=name. len ( ) . saturating_add ( 1 ) ) == Some ( STYLE_SEPARATOR )
513
+ declarations_buffer. iter_mut ( ) . find ( |style| {
514
+ style. starts_with ( property)
515
+ && style. get ( property. len ( ) ..=property. len ( ) . saturating_add ( 1 ) )
516
+ == Some ( STYLE_SEPARATOR )
516
517
} ) ,
517
518
) {
518
519
// The new rule is `!important` and there's an existing rule with the same name
519
520
// In this case, we override the existing rule with the new one
520
- ( Some ( value) , Some ( style ) ) => {
521
+ ( Some ( value) , Some ( buffer ) ) => {
521
522
// We keep the rule name and the colon-space suffix - '<rule>: `
522
- style . truncate ( name . len ( ) . saturating_add ( STYLE_SEPARATOR . len ( ) ) ) ;
523
- style . push_str ( value. trim ( ) ) ;
523
+ buffer . truncate ( property . len ( ) . saturating_add ( STYLE_SEPARATOR . len ( ) ) ) ;
524
+ buffer . push_str ( value. trim ( ) ) ;
524
525
}
525
526
// There's no existing rule with the same name, but the new rule is `!important`
526
527
// In this case, we add the new rule with the `!important` suffix removed
527
- ( Some ( value) , None ) => push_or_update ! ( style_buffer, length, name, value) ,
528
+ ( Some ( value) , None ) => {
529
+ push_or_update ! (
530
+ declarations_buffer,
531
+ parsed_declarations_count,
532
+ property,
533
+ value
534
+ ) ;
535
+ }
528
536
// There's no existing rule with the same name, and the new rule is not `!important`
529
537
// In this case, we just add the new rule as-is
530
- ( None , None ) => push_or_update ! ( style_buffer, length, name, value) ,
538
+ ( None , None ) => push_or_update ! (
539
+ declarations_buffer,
540
+ parsed_declarations_count,
541
+ property,
542
+ value
543
+ ) ,
531
544
// Rule exists and the new one is not `!important` - leave the existing rule as-is and
532
545
// ignore the new one.
533
546
( None , Some ( _) ) => { }
534
547
}
535
548
}
536
549
537
- // We can now dispose of the parser input, which allows us to clear the `existing_style` string
538
- drop ( input ) ;
539
- // Now we prepare to write the merged styles back into `existing_style`
540
- existing_style . clear ( ) ;
541
- let size_estimate: usize = style_buffer [ ..length ]
550
+ // We can now dispose of the parser input, which allows us to clear the current style string
551
+ drop ( parser_input ) ;
552
+ // Now we prepare to write the merged styles back into current style
553
+ current_style . clear ( ) ;
554
+ let size_estimate: usize = declarations_buffer [ ..parsed_declarations_count ]
542
555
. iter ( )
556
+ // Additional byte for the semicolon symbol
543
557
. map ( |s| s. len ( ) . saturating_add ( 1 ) )
544
558
. sum ( ) ;
545
559
546
- // Reserve enough space in existing_style for the merged style string
547
- existing_style . reserve ( u32:: try_from ( size_estimate) . expect ( "Size overflow" ) ) ;
560
+ // Reserve enough space in current style for the merged style string
561
+ current_style . reserve ( u32:: try_from ( size_estimate) . expect ( "Size overflow" ) ) ;
548
562
549
- // Write the merged styles into `existing_style`
550
- for style in & style_buffer [ ..length ] {
551
- if !existing_style . is_empty ( ) {
552
- existing_style . push_char ( ';' ) ;
563
+ // Write the merged styles into the current style
564
+ for declaration in & declarations_buffer [ ..parsed_declarations_count ] {
565
+ if !current_style . is_empty ( ) {
566
+ current_style . push_char ( ';' ) ;
553
567
}
554
- existing_style . push_slice ( style ) ;
568
+ current_style . push_slice ( declaration ) ;
555
569
}
556
570
Ok ( ( ) )
557
571
}
0 commit comments