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