1use std::str::FromStr;
10use std::sync::OnceLock;
11
12use app_units::Au;
13use cssparser::color::{OPAQUE, parse_named_color};
14use euclid::num::Zero;
15use num_traits::ToPrimitive;
16use selectors::attr::AttrSelectorOperation;
17use servo_arc::Arc;
18
19use super::shadow_parts::ShadowParts;
20use crate::color::AbsoluteColor;
21use crate::derives::*;
22use crate::properties::PropertyDeclarationBlock;
23use crate::shared_lock::{Locked, SharedRwLock};
24use crate::str::{
25 HTML_SPACE_CHARACTERS, read_exponent, read_fraction, read_numbers, split_commas,
26 split_html_space_chars, str_join,
27};
28use crate::values::AtomString;
29use crate::values::specified::LengthPercentage;
30use crate::{Atom, LocalName, Namespace, Prefix};
31
32const UNSIGNED_LONG_MAX: u32 = 2147483647;
34
35#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
36pub enum LengthOrPercentageOrAuto {
37 Auto,
38 Percentage(f32),
39 Length(Au),
40}
41
42#[derive(Clone, Debug, MallocSizeOf)]
43pub enum AttrValue {
44 String(String),
48 Atom(Atom),
49
50 TokenList(OnceLock<String>, Vec<Atom>),
54 UInt(OnceLock<String>, u32),
55 Int(OnceLock<String>, i32),
56 Double(OnceLock<String>, f64),
57 Declaration {
62 #[ignore_malloc_size_of = "Arc"]
63 block: Arc<Locked<PropertyDeclarationBlock>>,
64 lock: SharedRwLock,
65 serialization: OnceLock<String>,
66 },
67
68 LengthPercentage(String, Option<LengthPercentage>),
72 Color(String, Option<AbsoluteColor>),
73 Dimension(String, LengthOrPercentageOrAuto),
74 ResolvedUrl(
79 String,
80 #[ignore_malloc_size_of = "Arc"] Option<Arc<url::Url>>,
81 ),
82 ShadowParts(String, ShadowParts),
84}
85
86impl From<String> for AttrValue {
87 fn from(value: String) -> Self {
88 Self::String(value)
89 }
90}
91
92impl From<u32> for AttrValue {
93 fn from(value: u32) -> Self {
94 Self::UInt(OnceLock::new(), value)
95 }
96}
97
98impl From<i32> for AttrValue {
99 fn from(value: i32) -> Self {
100 Self::Int(OnceLock::new(), value)
101 }
102}
103
104impl From<f64> for AttrValue {
105 fn from(value: f64) -> Self {
106 Self::Double(OnceLock::new(), value)
107 }
108}
109
110impl From<Vec<Atom>> for AttrValue {
111 fn from(value: Vec<Atom>) -> Self {
112 Self::TokenList(OnceLock::new(), value)
113 }
114}
115
116fn do_parse_integer<T: Iterator<Item = char>>(input: T) -> Result<i64, ()> {
120 let mut input = input
121 .skip_while(|c| HTML_SPACE_CHARACTERS.iter().any(|s| s == c))
122 .peekable();
123
124 let sign = match input.peek() {
125 None => return Err(()),
126 Some(&'-') => {
127 input.next();
128 -1
129 },
130 Some(&'+') => {
131 input.next();
132 1
133 },
134 Some(_) => 1,
135 };
136
137 let (value, _) = read_numbers(input);
138
139 value.and_then(|value| value.checked_mul(sign)).ok_or(())
140}
141
142pub fn parse_integer<T: Iterator<Item = char>>(input: T) -> Result<i32, ()> {
145 do_parse_integer(input).and_then(|result| result.to_i32().ok_or(()))
146}
147
148pub fn parse_unsigned_integer<T: Iterator<Item = char>>(input: T) -> Result<u32, ()> {
151 do_parse_integer(input).and_then(|result| result.to_u32().ok_or(()))
152}
153
154pub fn parse_double(string: &str) -> Result<f64, ()> {
157 let trimmed = string.trim_matches(HTML_SPACE_CHARACTERS);
158 let mut input = trimmed.chars().peekable();
159
160 let (value, divisor, chars_skipped) = match input.peek() {
161 None => return Err(()),
162 Some(&'-') => {
163 input.next();
164 (-1f64, -1f64, 1)
165 },
166 Some(&'+') => {
167 input.next();
168 (1f64, 1f64, 1)
169 },
170 _ => (1f64, 1f64, 0),
171 };
172
173 let (value, value_digits) = if let Some(&'.') = input.peek() {
174 (0f64, 0)
175 } else {
176 let (read_val, read_digits) = read_numbers(input);
177 (
178 value * read_val.and_then(|result| result.to_f64()).unwrap_or(1f64),
179 read_digits,
180 )
181 };
182
183 let input = trimmed
184 .chars()
185 .skip(value_digits + chars_skipped)
186 .peekable();
187
188 let (mut value, fraction_digits) = read_fraction(input, divisor, value);
189
190 let input = trimmed
191 .chars()
192 .skip(value_digits + chars_skipped + fraction_digits)
193 .peekable();
194
195 if let Some(exp) = read_exponent(input) {
196 value *= 10f64.powi(exp)
197 };
198
199 Ok(value)
200}
201
202impl AttrValue {
203 pub fn from_serialized_tokenlist(tokens: String) -> AttrValue {
204 let atoms =
205 split_html_space_chars(&tokens)
206 .map(Atom::from)
207 .fold(vec![], |mut acc, atom| {
208 if !acc.contains(&atom) {
209 acc.push(atom)
210 }
211 acc
212 });
213 AttrValue::TokenList(tokens.into(), atoms)
214 }
215
216 pub fn from_comma_separated_tokenlist(tokens: String) -> AttrValue {
217 let atoms = split_commas(&tokens)
218 .map(Atom::from)
219 .fold(vec![], |mut acc, atom| {
220 if !acc.contains(&atom) {
221 acc.push(atom)
222 }
223 acc
224 });
225 AttrValue::TokenList(tokens.into(), atoms)
226 }
227
228 pub fn from_u32(string: String, default: u32) -> AttrValue {
230 let result = parse_unsigned_integer(string.chars()).unwrap_or(default);
231 let result = if result > UNSIGNED_LONG_MAX {
232 default
233 } else {
234 result
235 };
236 AttrValue::UInt(string.into(), result)
237 }
238
239 pub fn from_i32(string: String, default: i32) -> AttrValue {
240 let result = parse_integer(string.chars()).unwrap_or(default);
241 AttrValue::Int(string.into(), result)
242 }
243
244 pub fn from_double(string: String, default: f64) -> AttrValue {
246 let result = parse_double(&string).unwrap_or(default);
247
248 if result.is_normal() {
249 AttrValue::Double(string.into(), result)
250 } else {
251 AttrValue::Double(string.into(), default)
252 }
253 }
254
255 pub fn from_limited_i32(string: String, default: i32) -> AttrValue {
257 let result = parse_integer(string.chars()).unwrap_or(default);
258
259 if result < 0 {
260 AttrValue::Int(string.into(), default)
261 } else {
262 AttrValue::Int(string.into(), result)
263 }
264 }
265
266 pub fn from_limited_u32(string: String, default: u32) -> AttrValue {
268 let result = parse_unsigned_integer(string.chars()).unwrap_or(default);
269 let result = if result == 0 || result > UNSIGNED_LONG_MAX {
270 default
271 } else {
272 result
273 };
274 AttrValue::UInt(string.into(), result)
275 }
276
277 pub fn from_atomic(string: String) -> AttrValue {
278 AttrValue::Atom(string.into())
279 }
280
281 pub fn from_resolved_url(base: &Arc<::url::Url>, url: String) -> AttrValue {
282 let joined = base.join(&url).ok().map(Arc::new);
283 AttrValue::ResolvedUrl(url, joined)
284 }
285
286 pub fn from_legacy_color(string: String) -> AttrValue {
287 let parsed = parse_legacy_color(&string).ok();
288 AttrValue::Color(string, parsed)
289 }
290
291 pub fn from_dimension(string: String) -> AttrValue {
292 let parsed = parse_length(&string);
293 AttrValue::Dimension(string, parsed)
294 }
295
296 pub fn from_nonzero_dimension(string: String) -> AttrValue {
297 let parsed = parse_nonzero_length(&string);
298 AttrValue::Dimension(string, parsed)
299 }
300
301 pub fn from_shadow_parts(string: String) -> AttrValue {
302 let shadow_parts = ShadowParts::parse(&string);
303 AttrValue::ShadowParts(string, shadow_parts)
304 }
305
306 pub fn from_declaration(
307 block: Arc<Locked<PropertyDeclarationBlock>>,
308 lock: SharedRwLock,
309 ) -> AttrValue {
310 AttrValue::Declaration {
311 block,
312 lock,
313 serialization: OnceLock::new(),
314 }
315 }
316
317 pub fn as_tokens(&self) -> &[Atom] {
323 match *self {
324 AttrValue::TokenList(_, ref tokens) => tokens,
325 _ => panic!("Tokens not found"),
326 }
327 }
328
329 pub fn as_atom(&self) -> &Atom {
335 match *self {
336 AttrValue::Atom(ref value) => value,
337 _ => panic!("Atom not found"),
338 }
339 }
340
341 pub fn as_length_percentage(&self) -> Option<&LengthPercentage> {
347 match *self {
348 AttrValue::LengthPercentage(_, ref length_percentage) => length_percentage.as_ref(),
349 _ => panic!("LengthPercentage not found"),
350 }
351 }
352
353 pub fn as_color(&self) -> Option<&AbsoluteColor> {
359 match *self {
360 AttrValue::Color(_, ref color) => color.as_ref(),
361 _ => panic!("Color not found"),
362 }
363 }
364
365 pub fn as_dimension(&self) -> &LengthOrPercentageOrAuto {
371 match *self {
372 AttrValue::Dimension(_, ref l) => l,
373 _ => panic!("Dimension not found"),
374 }
375 }
376
377 pub fn as_resolved_url(&self) -> Option<&Arc<::url::Url>> {
383 match *self {
384 AttrValue::ResolvedUrl(_, ref url) => url.as_ref(),
385 _ => panic!("Url not found"),
386 }
387 }
388
389 pub fn as_int(&self) -> i32 {
397 if let AttrValue::Int(_, value) = *self {
398 value
399 } else {
400 panic!("Int not found");
401 }
402 }
403
404 pub fn as_uint(&self) -> u32 {
412 if let AttrValue::UInt(_, value) = *self {
413 value
414 } else {
415 panic!("Uint not found");
416 }
417 }
418
419 pub fn as_uint_px_dimension(&self) -> LengthOrPercentageOrAuto {
429 if let AttrValue::UInt(_, value) = *self {
430 LengthOrPercentageOrAuto::Length(Au::from_px(value as i32))
431 } else {
432 panic!("Uint not found");
433 }
434 }
435
436 pub fn as_shadow_parts(&self) -> &ShadowParts {
445 if let AttrValue::ShadowParts(_, value) = &self {
446 value
447 } else {
448 panic!("Not a shadowpart attribute");
449 }
450 }
451
452 pub fn eval_selector(&self, selector: &AttrSelectorOperation<&AtomString>) -> bool {
453 selector.eval_str(self)
457 }
458}
459
460impl ::std::ops::Deref for AttrValue {
461 type Target = str;
462
463 fn deref(&self) -> &str {
464 match self {
465 AttrValue::String(value) => &value,
466 AttrValue::Atom(atom) => &atom,
467 AttrValue::TokenList(serialization, tokens) => {
468 serialization.get_or_init(|| {
469 str_join(tokens, "\x20")
471 })
472 },
473 AttrValue::UInt(serialization, value) => {
474 serialization.get_or_init(|| value.to_string())
475 },
476 AttrValue::Double(serialization, value) => {
477 serialization.get_or_init(|| value.to_string())
478 },
479 AttrValue::Int(serialization, value) => serialization.get_or_init(|| value.to_string()),
480 AttrValue::Declaration {
481 block,
482 lock,
483 serialization,
484 } => serialization.get_or_init(|| {
485 let mut serialization = String::new();
486 block
487 .read_with(&lock.read())
488 .to_css(&mut serialization)
489 .expect("Should always be able to produce a valid serialization");
490 serialization
491 }),
492 AttrValue::LengthPercentage(serialization, _)
493 | AttrValue::Color(serialization, _)
494 | AttrValue::Dimension(serialization, _)
495 | AttrValue::ResolvedUrl(serialization, _)
496 | AttrValue::ShadowParts(serialization, _) => &serialization,
497 }
498 }
499}
500
501impl PartialEq<Atom> for AttrValue {
502 fn eq(&self, other: &Atom) -> bool {
503 match *self {
504 AttrValue::Atom(ref value) => value == other,
505 _ => other == &**self,
506 }
507 }
508}
509
510pub fn parse_nonzero_length(value: &str) -> LengthOrPercentageOrAuto {
512 match parse_length(value) {
513 LengthOrPercentageOrAuto::Length(x) if x == Au::zero() => LengthOrPercentageOrAuto::Auto,
514 LengthOrPercentageOrAuto::Percentage(x) if x == 0. => LengthOrPercentageOrAuto::Auto,
515 x => x,
516 }
517}
518
519pub fn parse_legacy_color(mut input: &str) -> Result<AbsoluteColor, ()> {
523 if input.is_empty() {
525 return Err(());
526 }
527
528 input = input.trim_matches(HTML_SPACE_CHARACTERS);
530
531 if input.eq_ignore_ascii_case("transparent") {
533 return Err(());
534 }
535
536 if let Ok((r, g, b)) = parse_named_color(input) {
538 return Ok(AbsoluteColor::srgb_legacy(r, g, b, OPAQUE));
539 }
540
541 if input.len() == 4 {
543 if let (b'#', Ok(r), Ok(g), Ok(b)) = (
544 input.as_bytes()[0],
545 hex(input.as_bytes()[1] as char),
546 hex(input.as_bytes()[2] as char),
547 hex(input.as_bytes()[3] as char),
548 ) {
549 return Ok(AbsoluteColor::srgb_legacy(r * 17, g * 17, b * 17, OPAQUE));
550 }
551 }
552
553 let mut new_input = String::new();
555 for ch in input.chars() {
556 if ch as u32 > 0xffff {
557 new_input.push_str("00")
558 } else {
559 new_input.push(ch)
560 }
561 }
562 let mut input = &*new_input;
563
564 for (char_count, (index, _)) in input.char_indices().enumerate() {
566 if char_count == 128 {
567 input = &input[..index];
568 break;
569 }
570 }
571
572 if input.as_bytes()[0] == b'#' {
574 input = &input[1..]
575 }
576
577 let mut new_input = Vec::new();
579 for ch in input.chars() {
580 if hex(ch).is_ok() {
581 new_input.push(ch as u8)
582 } else {
583 new_input.push(b'0')
584 }
585 }
586 let mut input = new_input;
587
588 while input.is_empty() || (input.len() % 3) != 0 {
590 input.push(b'0')
591 }
592
593 let mut length = input.len() / 3;
595 let (mut red, mut green, mut blue) = (
596 &input[..length],
597 &input[length..length * 2],
598 &input[length * 2..],
599 );
600
601 if length > 8 {
603 red = &red[length - 8..];
604 green = &green[length - 8..];
605 blue = &blue[length - 8..];
606 length = 8
607 }
608
609 while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' {
611 red = &red[1..];
612 green = &green[1..];
613 blue = &blue[1..];
614 length -= 1
615 }
616
617 return Ok(AbsoluteColor::srgb_legacy(
619 hex_string(red).unwrap(),
620 hex_string(green).unwrap(),
621 hex_string(blue).unwrap(),
622 OPAQUE,
623 ));
624
625 fn hex(ch: char) -> Result<u8, ()> {
626 match ch {
627 '0'..='9' => Ok((ch as u8) - b'0'),
628 'a'..='f' => Ok((ch as u8) - b'a' + 10),
629 'A'..='F' => Ok((ch as u8) - b'A' + 10),
630 _ => Err(()),
631 }
632 }
633
634 fn hex_string(string: &[u8]) -> Result<u8, ()> {
635 match string.len() {
636 0 => Err(()),
637 1 => hex(string[0] as char),
638 _ => {
639 let upper = hex(string[0] as char)?;
640 let lower = hex(string[1] as char)?;
641 Ok((upper << 4) | lower)
642 },
643 }
644 }
645}
646
647pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
652 value = value.trim_start_matches(HTML_SPACE_CHARACTERS);
656
657 match value.chars().nth(0) {
659 Some('0'..='9') => {},
660 _ => return LengthOrPercentageOrAuto::Auto,
661 }
662
663 let mut end_index = value.len();
671 let (mut found_full_stop, mut found_percent) = (false, false);
672 for (i, ch) in value.chars().enumerate() {
673 match ch {
674 '0'..='9' => continue,
675 '%' => {
676 found_percent = true;
677 end_index = i;
678 break;
679 },
680 '.' if !found_full_stop => {
681 found_full_stop = true;
682 continue;
683 },
684 _ => {
685 end_index = i;
686 break;
687 },
688 }
689 }
690 value = &value[..end_index];
691
692 if found_percent {
693 let result: Result<f32, _> = FromStr::from_str(value);
694 match result {
695 Ok(number) => return LengthOrPercentageOrAuto::Percentage((number as f32) / 100.0),
696 Err(_) => return LengthOrPercentageOrAuto::Auto,
697 }
698 }
699
700 match FromStr::from_str(value) {
701 Ok(number) => LengthOrPercentageOrAuto::Length(Au::from_f64_px(number)),
702 Err(_) => LengthOrPercentageOrAuto::Auto,
703 }
704}
705
706#[derive(Clone, Debug, MallocSizeOf)]
708pub struct AttrIdentifier {
709 pub local_name: LocalName,
710 pub name: LocalName,
711 pub namespace: Namespace,
712 pub prefix: Option<Prefix>,
713}