| 58 | #[inline] |
| 59 | #[allow(clippy::arithmetic_side_effects)] |
| 60 | fn extract_dimension_value(value: &str, allow_percent: bool) -> Option<DimensionValue<'_>> { |
| 61 | let value = value.trim(); |
| 62 | |
| 63 | if value.eq_ignore_ascii_case("auto") { |
| 64 | return Some(DimensionValue::Auto); |
| 65 | } |
| 66 | |
| 67 | // Find where the numeric part ends |
| 68 | let bytes = value.as_bytes(); |
| 69 | let mut end = 0; |
| 70 | let mut has_dot = false; |
| 71 | |
| 72 | // Handle optional leading sign |
| 73 | if bytes.first() == Some(&b'-') || bytes.first() == Some(&b'+') { |
| 74 | end = 1; |
| 75 | } |
| 76 | |
| 77 | // Parse digits and optional decimal point |
| 78 | while end < bytes.len() { |
| 79 | match bytes[end] { |
| 80 | b'0'..=b'9' => end += 1, |
| 81 | b'.' if !has_dot => { |
| 82 | has_dot = true; |
| 83 | end += 1; |
| 84 | } |
| 85 | _ => break, |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | // Must have at least one digit |
| 90 | if end == 0 || (end == 1 && (bytes[0] == b'-' || bytes[0] == b'+')) { |
| 91 | return None; |
| 92 | } |
| 93 | |
| 94 | let numeric_part = &value[..end]; |
| 95 | // Trim whitespace between number and unit (e.g., "100 px") for lenient parsing |
| 96 | let unit_part = value[end..].trim(); |
| 97 | // Strip `!important` suffix if present |
| 98 | let unit_part = unit_part |
| 99 | .strip_suffix("!important") |
| 100 | .map_or(unit_part, str::trim); |
| 101 | |
| 102 | match unit_part { |
| 103 | // Pixel values - strip the 'px' suffix |
| 104 | "" | "px" => Some(DimensionValue::Numeric(numeric_part)), |
| 105 | // Percentage - only allowed for table elements |
| 106 | "%" if allow_percent => Some(DimensionValue::Percent(numeric_part)), |
| 107 | // All other units are not supported |
| 108 | _ => None, |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | /// Find a style property value from stylesheet rules (not pre-existing inline styles). |
| 113 | #[inline] |