Skip to content

Commit f8fa490

Browse files
yhaiovyiStranger6667
authored andcommitted
fix: css-inline does not follow cascade/specificity rules
Ref: #142
1 parent a21c92f commit f8fa490

3 files changed

Lines changed: 24 additions & 9 deletions

File tree

css-inline/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ memchr = "2.4"
2525
attohttpc = { version = "0", default-features = false, features = ["compress", "tls-rustls"] }
2626
url = "2"
2727
smallvec = "1"
28-
ahash = "0.7"
28+
indexmap = "1.8.1"
2929
pico-args = { version = "0.3", optional = true }
3030
rayon = { version = "1.5", optional = true }
3131

css-inline/src/lib.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,11 @@ use kuchiki::{
3232
pub mod error;
3333
mod parser;
3434

35-
use ahash::AHashMap;
35+
use indexmap::IndexMap;
3636
pub use error::InlineError;
3737
use smallvec::{smallvec, SmallVec};
3838
use std::{
3939
borrow::Cow,
40-
collections::hash_map::Entry,
4140
fs,
4241
io::{ErrorKind, Write},
4342
};
@@ -239,7 +238,7 @@ impl<'a> CSSInliner<'a> {
239238
// and then reused, which allows O(1) access to find them.
240239
// Internally, their raw pointers are used to implement `Eq`, which seems like the only
241240
// reasonable approach to compare them (performance-wise).
242-
let mut styles = AHashMap::with_capacity(128);
241+
let mut styles = IndexMap::with_capacity(128);
243242
let mut style_tags: SmallVec<[NodeDataRef<ElementData>; 4]> = smallvec![];
244243
if self.options.inline_style_tags {
245244
for style_tag in document
@@ -358,7 +357,7 @@ type NodeId = *const Node;
358357
fn process_css(
359358
document: &NodeRef,
360359
css: &str,
361-
styles: &mut AHashMap<NodeId, AHashMap<String, (Specificity, String)>>,
360+
styles: &mut IndexMap<NodeId, IndexMap<String, (Specificity, String)>>,
362361
) {
363362
let mut parse_input = cssparser::ParserInput::new(css);
364363
let mut parser = cssparser::Parser::new(&mut parse_input);
@@ -374,15 +373,15 @@ fn process_css(
374373
for matching_element in matching_elements {
375374
let element_styles = styles
376375
.entry(&**matching_element.as_node())
377-
.or_insert_with(|| AHashMap::with_capacity(8));
376+
.or_insert_with(|| IndexMap::with_capacity(8));
378377
for (name, value) in &declarations {
379378
match element_styles.entry(name.to_string()) {
380-
Entry::Occupied(mut entry) => {
379+
indexmap::map::Entry::Occupied(mut entry) => {
381380
if entry.get().0 <= specificity {
382381
entry.insert((specificity, (*value).to_string()));
383382
}
384383
}
385-
Entry::Vacant(entry) => {
384+
indexmap::map::Entry::Vacant(entry) => {
386385
entry.insert((specificity, (*value).to_string()));
387386
}
388387
}
@@ -432,7 +431,7 @@ pub fn inline_to<W: Write>(html: &str, target: &mut W) -> Result<()> {
432431

433432
fn merge_styles(
434433
existing_style: &str,
435-
new_styles: &AHashMap<String, (Specificity, String)>,
434+
new_styles: &IndexMap<String, (Specificity, String)>,
436435
) -> Result<String> {
437436
// Parse existing declarations in the "style" attribute
438437
let mut input = cssparser::ParserInput::new(existing_style);

css-inline/tests/test_inlining.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ p.footer { font-size: 1px}"#,
2020
)
2121
}
2222

23+
#[test]
24+
fn maintain_rules_order() {
25+
assert_inlined!(
26+
style = r#"
27+
.test-class {
28+
padding-top: 15px;
29+
padding: 10px;
30+
padding-left: 12px;
31+
}"#,
32+
body = r#"<a class="test-class" href="https://example.com">Test</a>"#,
33+
// Then the final style should come from the more specific selector
34+
expected =
35+
r#"<a class="test-class" href="https://example.com" style="padding-top: 15px;padding: 10px;padding-left: 12px;">Test</a>"#
36+
)
37+
}
38+
2339
#[test]
2440
fn overlap_styles() {
2541
// When two selectors match the same element

0 commit comments

Comments
 (0)