1use alloc::{rc::Rc, string::String, vec::Vec};
4use core::{hash::Hasher, num::NonZeroUsize};
5
6use crate::length_num::LengthNum;
7use crate::property::{
8 NodeProperties, NodePropertiesOrder, Property, PropertyMeta, PropertyValueWithGlobal,
9};
10use crate::sheet::{PseudoElements, Rule};
11use crate::sheet::{RuleWeight, Theme};
12use crate::typing::{Length, LengthType};
13
14#[derive(Debug, Clone, PartialEq)]
19pub struct MediaQueryStatus<L: LengthNum> {
20 pub is_screen: bool,
22 pub width: L,
24 pub height: L,
26 pub pixel_ratio: f32,
28 pub base_font_size: L,
30 pub theme: Theme,
32 pub env: EnvValues<L>,
34}
35
36#[derive(Debug, Clone, PartialEq)]
38#[allow(missing_docs)]
39pub struct EnvValues<L: LengthNum> {
40 pub safe_area_inset_left: L,
41 pub safe_area_inset_top: L,
42 pub safe_area_inset_right: L,
43 pub safe_area_inset_bottom: L,
44}
45
46impl<L: LengthNum> Default for EnvValues<L> {
47 fn default() -> Self {
48 Self {
49 safe_area_inset_left: L::zero(),
50 safe_area_inset_top: L::zero(),
51 safe_area_inset_right: L::zero(),
52 safe_area_inset_bottom: L::zero(),
53 }
54 }
55}
56
57impl<L: LengthNum> Default for MediaQueryStatus<L> {
58 fn default() -> Self {
59 Self::default_screen()
60 }
61}
62
63impl<L: LengthNum> MediaQueryStatus<L> {
64 pub fn default_screen() -> Self {
66 Self {
67 is_screen: true,
68 width: L::from_i32(800),
69 height: L::from_i32(600),
70 pixel_ratio: 1.,
71 base_font_size: L::from_i32(16),
72 theme: Theme::Light,
73 env: Default::default(),
74 }
75 }
76
77 pub fn default_screen_with_size(width: L, height: L) -> Self {
79 Self {
80 is_screen: true,
81 width,
82 height,
83 pixel_ratio: 1.,
84 base_font_size: L::from_i32(16),
85 theme: Theme::Light,
86 env: Default::default(),
87 }
88 }
89}
90
91pub trait StyleNodeClass {
93 fn name(&self) -> &str;
95
96 fn scope(&self) -> Option<NonZeroUsize>;
98}
99
100impl StyleNodeClass for (String, Option<NonZeroUsize>) {
101 fn name(&self) -> &str {
102 &self.0
103 }
104
105 fn scope(&self) -> Option<NonZeroUsize> {
106 self.1
107 }
108}
109
110pub enum StyleNodeAttributeCaseSensitivity {
112 CaseSensitive,
114
115 CaseInsensitive,
117}
118
119impl StyleNodeAttributeCaseSensitivity {
120 pub fn eq(&self, a: &str, b: &str) -> bool {
122 match self {
123 Self::CaseSensitive => a == b,
124 Self::CaseInsensitive => a.eq_ignore_ascii_case(b),
125 }
126 }
127
128 pub fn starts_with(&self, a: &str, b: &str) -> bool {
130 match self {
132 Self::CaseSensitive => a.starts_with(b),
133 Self::CaseInsensitive => a.to_ascii_lowercase().starts_with(&b.to_ascii_lowercase()),
134 }
135 }
136
137 pub fn ends_with(&self, a: &str, b: &str) -> bool {
139 match self {
141 Self::CaseSensitive => a.ends_with(b),
142 Self::CaseInsensitive => a.to_ascii_lowercase().ends_with(&b.to_ascii_lowercase()),
143 }
144 }
145
146 pub fn contains(&self, a: &str, b: &str) -> bool {
148 match self {
150 Self::CaseSensitive => a.contains(b),
151 Self::CaseInsensitive => a.to_ascii_lowercase().contains(&b.to_ascii_lowercase()),
152 }
153 }
154}
155
156pub trait StyleNode {
158 type Class: StyleNodeClass;
160
161 type ClassIter<'a>: Iterator<Item = &'a Self::Class>
163 where
164 Self: 'a;
165
166 fn style_scope(&self) -> Option<NonZeroUsize>;
168
169 fn extra_style_scope(&self) -> Option<NonZeroUsize>;
171
172 fn host_style_scope(&self) -> Option<NonZeroUsize>;
174
175 fn tag_name(&self) -> &str;
177
178 fn id(&self) -> Option<&str>;
180
181 fn classes(&self) -> Self::ClassIter<'_>;
183
184 fn attribute(&self, name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)>;
186
187 fn pseudo_element(&self) -> Option<PseudoElements>;
189
190 fn contain_scope(&self, scope: Option<NonZeroUsize>) -> bool {
192 scope.is_none()
193 || self.style_scope() == scope
194 || self.extra_style_scope() == scope
195 || self.host_style_scope() == scope
196 }
197}
198
199#[derive(Debug)]
201pub struct StyleQuery<'a> {
202 pub(super) style_scope: Option<NonZeroUsize>,
203 pub(super) extra_style_scope: Option<NonZeroUsize>,
204 pub(super) host_style_scope: Option<NonZeroUsize>,
205 pub(super) tag_name: &'a str,
206 pub(super) id: &'a str,
207 pub(super) classes: &'a [(String, Option<NonZeroUsize>)],
208}
209
210impl Clone for StyleQuery<'_> {
211 fn clone(&self) -> Self {
212 Self {
213 style_scope: self.style_scope,
214 extra_style_scope: self.extra_style_scope,
215 host_style_scope: self.host_style_scope,
216 tag_name: self.tag_name,
217 id: self.id,
218 classes: self.classes,
219 }
220 }
221}
222
223impl<'a> StyleQuery<'a> {
224 pub fn single(
226 style_scope: Option<NonZeroUsize>,
227 extra_style_scope: Option<NonZeroUsize>,
228 host_style_scope: Option<NonZeroUsize>,
229 tag_name: &'a str,
230 id: &'a str,
231 classes: &'a [(String, Option<NonZeroUsize>)],
232 ) -> Self {
233 Self {
234 style_scope,
235 extra_style_scope,
236 host_style_scope,
237 tag_name,
238 id,
239 classes,
240 }
241 }
242}
243
244impl<'a> StyleNode for StyleQuery<'a> {
245 type Class = (String, Option<NonZeroUsize>);
246 type ClassIter<'c>
247 = core::slice::Iter<'c, Self::Class>
248 where
249 'a: 'c;
250
251 fn style_scope(&self) -> Option<NonZeroUsize> {
252 self.style_scope
253 }
254
255 fn extra_style_scope(&self) -> Option<NonZeroUsize> {
256 self.extra_style_scope
257 }
258
259 fn host_style_scope(&self) -> Option<NonZeroUsize> {
260 self.host_style_scope
261 }
262
263 fn tag_name(&self) -> &str {
264 self.tag_name
265 }
266
267 fn id(&self) -> Option<&str> {
268 Some(self.id)
269 }
270
271 fn classes(&self) -> Self::ClassIter<'_> {
272 self.classes.iter()
273 }
274
275 fn attribute(&self, _name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)> {
276 None
277 }
278
279 fn pseudo_element(&self) -> Option<PseudoElements> {
280 None
281 }
282}
283
284impl<'b, 'a: 'b> StyleNode for &'b StyleQuery<'a> {
285 type Class = (String, Option<NonZeroUsize>);
286 type ClassIter<'c>
287 = core::slice::Iter<'c, Self::Class>
288 where
289 'b: 'c;
290
291 fn style_scope(&self) -> Option<NonZeroUsize> {
292 self.style_scope
293 }
294
295 fn extra_style_scope(&self) -> Option<NonZeroUsize> {
296 self.extra_style_scope
297 }
298
299 fn host_style_scope(&self) -> Option<NonZeroUsize> {
300 self.host_style_scope
301 }
302
303 fn tag_name(&self) -> &str {
304 self.tag_name
305 }
306
307 fn id(&self) -> Option<&str> {
308 Some(self.id)
309 }
310
311 fn classes(&self) -> Self::ClassIter<'_> {
312 self.classes.iter()
313 }
314
315 fn attribute(&self, _name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)> {
316 None
317 }
318
319 fn pseudo_element(&self) -> Option<PseudoElements> {
320 None
321 }
322}
323
324#[derive(Debug, Clone)]
326pub struct MatchedRuleRef<'a> {
327 pub rule: &'a Rc<Rule>,
329 pub weight: RuleWeight,
331}
332
333#[derive(Debug, Clone)]
335pub struct MatchedRule {
336 pub rule: Rc<Rule>,
338 pub weight: RuleWeight,
340 pub style_scope: Option<NonZeroUsize>,
342}
343
344impl PartialEq for MatchedRule {
345 fn eq(&self, other: &Self) -> bool {
346 self.weight.normal() == other.weight.normal()
347 }
348}
349
350impl PartialOrd for MatchedRule {
351 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
352 Some(self.weight.normal().cmp(&other.weight.normal()))
353 }
354}
355
356impl Ord for MatchedRule {
357 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
358 self.weight.normal().cmp(&other.weight.normal())
359 }
360}
361
362impl Eq for MatchedRule {}
363
364#[derive(Debug, Clone)]
366pub struct MatchedRuleList {
367 pub rules: Vec<MatchedRule>,
369}
370
371impl MatchedRuleList {
372 pub fn new_empty() -> Self {
374 Self {
375 rules: Vec::with_capacity(0),
376 }
377 }
378
379 pub fn get_current_font_size<L: LengthNum>(
383 &self,
384 parent_font_size: f32,
385 parent_node_properties: Option<&NodeProperties>,
386 extra_styles: &[PropertyMeta],
387 media_query_status: &MediaQueryStatus<L>,
388 ) -> f32 {
389 let mut font_size_p = None;
391 let mut font_size_w: u64 = 0;
392 fn handle_property_meta<'a>(
393 font_size_p: &mut Option<&'a LengthType>,
394 font_size_w: &mut u64,
395 pm: &'a PropertyMeta,
396 rw: RuleWeight,
397 ) {
398 match pm {
399 PropertyMeta::Normal { property: p } => {
400 if let Property::FontSize(x) = p {
401 let w = rw.normal();
402 if w >= *font_size_w {
403 *font_size_w = w;
404 *font_size_p = Some(x);
405 }
406 }
407 }
408 PropertyMeta::Important { property: p } => {
409 if let Property::FontSize(x) = p {
410 let w = rw.important();
411 if w >= *font_size_w {
412 *font_size_w = w;
413 *font_size_p = Some(x);
414 }
415 }
416 }
417 PropertyMeta::DebugGroup {
418 properties,
419 important,
420 disabled,
421 ..
422 } => {
423 if !disabled {
424 let w = if *important {
425 rw.important()
426 } else {
427 rw.normal()
428 };
429 if w >= *font_size_w {
430 for p in &**properties {
431 if let Property::FontSize(x) = p {
432 *font_size_w = w;
433 *font_size_p = Some(x);
434 }
435 }
436 }
437 }
438 }
439 }
440 }
441 for pm in extra_styles.iter() {
442 handle_property_meta(&mut font_size_p, &mut font_size_w, pm, RuleWeight::inline());
443 }
444 for matched_rule in self.rules.iter() {
445 let rw = matched_rule.weight;
446 if !matched_rule.rule.has_font_size {
447 continue;
448 };
449 for pm in matched_rule.rule.properties.iter() {
450 handle_property_meta(&mut font_size_p, &mut font_size_w, pm, rw);
451 }
452 }
453
454 let default_font_size = media_query_status.base_font_size.to_f32();
456 let parent_font_size_p = parent_node_properties.map(|x| x.font_size_ref());
457 let parent_font_size = parent_font_size.to_f32();
458 let current_font_size = if let Some(p) = font_size_p {
459 p.to_inner(parent_font_size_p, Length::Px(default_font_size), true)
460 .and_then(|x| x.resolve_to_f32(media_query_status, parent_font_size, true))
461 .unwrap_or(parent_font_size)
462 } else {
463 parent_font_size
464 };
465
466 current_font_size
467 }
468
469 pub fn merge_node_properties(
471 &self,
472 node_properties: &mut NodeProperties,
473 parent_node_properties: Option<&NodeProperties>,
474 current_font_size: f32,
475 extra_styles: &[PropertyMeta],
476 ) {
477 let mut order = NodePropertiesOrder::new();
478 let mut merge_property_meta = |pm: &PropertyMeta, rw: RuleWeight| match pm {
479 PropertyMeta::Normal { property: p } => {
480 if order.compare_property(p, rw.normal()) {
481 node_properties.merge_property(p, parent_node_properties, current_font_size)
482 }
483 }
484 PropertyMeta::Important { property: p } => {
485 if order.compare_property(p, rw.important()) {
486 node_properties.merge_property(p, parent_node_properties, current_font_size)
487 }
488 }
489 PropertyMeta::DebugGroup {
490 properties,
491 important,
492 disabled,
493 ..
494 } => {
495 if !disabled {
496 let w = if *important {
497 rw.important()
498 } else {
499 rw.normal()
500 };
501 for p in &**properties {
502 if order.compare_property(p, w) {
503 node_properties.merge_property(
504 p,
505 parent_node_properties,
506 current_font_size,
507 )
508 }
509 }
510 }
511 }
512 };
513 for pm in extra_styles.iter() {
514 merge_property_meta(pm, RuleWeight::inline());
515 }
516 for matched_rule in self.rules.iter() {
517 for pm in matched_rule.rule.properties.iter() {
518 merge_property_meta(pm, matched_rule.weight);
519 }
520 }
521 }
522
523 pub fn for_each_property(&self, mut f: impl FnMut(&Property, u64)) {
525 for matched_rule in self.rules.iter() {
526 let weight = matched_rule.weight;
527 for pm in matched_rule.rule.properties.iter() {
528 if pm.is_disabled() {
529 continue;
530 }
531 let w = if pm.is_important() {
532 weight.important()
533 } else {
534 weight.normal()
535 };
536 for p in pm.iter() {
537 f(p, w);
538 }
539 }
540 }
541 }
542
543 pub fn animation_name_style_scope(&self) -> Option<NonZeroUsize> {
548 let mut w = u64::MIN;
549 let mut ret = None;
550 let mut check_property_meta = |pm: &PropertyMeta, rw: RuleWeight, scope| {
551 for p in pm.iter() {
552 if let Property::AnimationName(..) = p {
553 let self_w = if pm.is_important() {
554 rw.important()
555 } else {
556 rw.normal()
557 };
558 if self_w >= w {
559 w = self_w;
560 ret = scope;
561 }
562 }
563 }
564 };
565 for matched_rule in self.rules.iter() {
566 for pm in matched_rule.rule.properties.iter() {
567 check_property_meta(pm, matched_rule.weight, matched_rule.style_scope);
568 }
569 }
570 ret
571 }
572
573 pub fn fast_hash_value(&self) -> u64 {
577 let mut hasher = ahash::AHasher::default();
578 for matched_rule in self.rules.iter() {
579 let rule: &Rule = &matched_rule.rule;
580 hasher.write_usize(rule as *const Rule as usize);
581 hasher.write_u64(matched_rule.weight.normal());
582 }
583 hasher.finish()
584 }
585}