azul_core/
display_list.rs

1use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::ToString, vec::Vec};
2use core::fmt;
3
4use azul_css::{
5    BoxShadowClipMode, ColorU, ConicGradient, CssPropertyValue, LayoutBorderBottomWidth,
6    LayoutBorderLeftWidth, LayoutBorderRightWidth, LayoutBorderTopWidth, LayoutPoint, LayoutRect,
7    LayoutSize, LinearGradient, RadialGradient, StyleBackgroundPosition, StyleBackgroundRepeat,
8    StyleBackgroundSize, StyleBorderBottomColor, StyleBorderBottomLeftRadius,
9    StyleBorderBottomRightRadius, StyleBorderBottomStyle, StyleBorderLeftColor,
10    StyleBorderLeftStyle, StyleBorderRightColor, StyleBorderRightStyle, StyleBorderTopColor,
11    StyleBorderTopLeftRadius, StyleBorderTopRightRadius, StyleBorderTopStyle, StyleBoxShadow,
12    StyleMixBlendMode,
13};
14use rust_fontconfig::FcFontCache;
15
16use crate::{
17    app_resources::{
18        AddImageMsg, DpiScaleFactor, Epoch, ExternalImageId, FontInstanceKey, GlTextureCache,
19        GlyphOptions, IdNamespace, ImageCache, ImageDescriptor, ImageKey, LoadFontFn, OpacityKey,
20        ParseFontFn, PrimitiveFlags, RendererResources, ResourceUpdate, TransformKey,
21    },
22    callbacks::{DocumentId, DomNodeId, PipelineId},
23    dom::{ScrollTagId, TagId},
24    gl::{OptionGlContextPtr, Texture},
25    id_tree::NodeId,
26    styled_dom::{ContentGroup, DomId, NodeHierarchyItemId, StyledDom},
27    ui_solver::{ComputedTransform3D, ExternalScrollId, LayoutResult, PositionInfo},
28    window::{FullWindowState, LogicalPosition, LogicalRect, LogicalSize},
29};
30
31pub type GlyphIndex = u32;
32
33#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
34pub struct GlyphInstance {
35    pub index: GlyphIndex,
36    pub point: LogicalPosition,
37    pub size: LogicalSize,
38}
39
40impl GlyphInstance {
41    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
42        self.point.scale_for_dpi(scale_factor);
43        self.size.scale_for_dpi(scale_factor);
44    }
45}
46
47#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
48pub struct DisplayListImageMask {
49    pub image: ImageKey,
50    pub rect: LogicalRect,
51    pub repeat: bool,
52}
53
54impl DisplayListImageMask {
55    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
56        self.rect.scale_for_dpi(scale_factor);
57    }
58}
59
60#[derive(Debug, Clone, PartialEq, PartialOrd)]
61pub struct CachedDisplayList {
62    pub root: DisplayListMsg,
63    pub root_size: LogicalSize,
64}
65
66impl CachedDisplayList {
67    pub fn empty() -> Self {
68        Self {
69            root: DisplayListMsg::Frame(DisplayListFrame::root(
70                LayoutSize::zero(),
71                LayoutPoint::zero(),
72            )),
73            root_size: LogicalSize::zero(),
74        }
75    }
76
77    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
78        self.root_size.width *= scale_factor;
79        self.root_size.height *= scale_factor;
80        self.root.scale_for_dpi(scale_factor);
81    }
82}
83
84#[derive(Debug, Clone, PartialEq, PartialOrd)]
85pub enum DisplayListMsg {
86    // nested display list
87    IFrame(PipelineId, LogicalSize, Epoch, Box<CachedDisplayList>),
88    Frame(DisplayListFrame),
89    ScrollFrame(DisplayListScrollFrame),
90}
91
92impl DisplayListMsg {
93    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
94        match self {
95            DisplayListMsg::IFrame(_, s, _, dl) => {
96                s.width *= scale_factor;
97                s.height *= scale_factor;
98                dl.scale_for_dpi(scale_factor);
99            }
100            DisplayListMsg::Frame(f) => {
101                f.scale_for_dpi(scale_factor);
102            }
103            DisplayListMsg::ScrollFrame(sf) => {
104                sf.scale_for_dpi(scale_factor);
105            }
106        }
107    }
108
109    pub fn get_transform_key(&self) -> Option<&(TransformKey, ComputedTransform3D)> {
110        use self::DisplayListMsg::*;
111        match self {
112            Frame(f) => f.transform.as_ref(),
113            ScrollFrame(sf) => sf.frame.transform.as_ref(),
114            IFrame(_, _, _, _) => None,
115        }
116    }
117
118    pub fn get_mix_blend_mode(&self) -> Option<&StyleMixBlendMode> {
119        use self::DisplayListMsg::*;
120        match self {
121            Frame(f) => f.mix_blend_mode.as_ref(),
122            ScrollFrame(sf) => sf.frame.mix_blend_mode.as_ref(),
123            IFrame(_, _, _, _) => None,
124        }
125    }
126
127    // warning: recursive function!
128    pub fn has_mix_blend_mode_children(&self) -> bool {
129        use self::DisplayListMsg::*;
130        match self {
131            Frame(f) => f.children.iter().any(|f| f.get_mix_blend_mode().is_some()),
132            ScrollFrame(sf) => sf
133                .frame
134                .children
135                .iter()
136                .any(|f| f.get_mix_blend_mode().is_some()),
137            IFrame(_, _, _, _) => false,
138        }
139    }
140
141    pub fn get_opacity_key(&self) -> Option<&(OpacityKey, f32)> {
142        use self::DisplayListMsg::*;
143        match self {
144            Frame(f) => f.opacity.as_ref(),
145            ScrollFrame(sf) => sf.frame.opacity.as_ref(),
146            IFrame(_, _, _, _) => None,
147        }
148    }
149
150    pub fn get_image_mask(&self) -> Option<&DisplayListImageMask> {
151        use self::DisplayListMsg::*;
152        match self {
153            Frame(f) => f.clip_mask.as_ref(),
154            ScrollFrame(sf) => sf.frame.clip_mask.as_ref(),
155            IFrame(_, _, _, _) => None,
156        }
157    }
158
159    pub fn get_position(&self) -> PositionInfo {
160        use self::DisplayListMsg::*;
161        use crate::ui_solver::PositionInfoInner;
162        match self {
163            Frame(f) => f.position.clone(),
164            ScrollFrame(sf) => sf.frame.position.clone(),
165            IFrame(_, _, _, _) => PositionInfo::Static(PositionInfoInner::zero()),
166        }
167    }
168
169    pub fn is_content_empty(&self) -> bool {
170        use self::DisplayListMsg::*;
171        match self {
172            Frame(f) => f.content.is_empty(),
173            ScrollFrame(sf) => sf.frame.content.is_empty(),
174            IFrame(_, _, _, _) => false,
175        }
176    }
177
178    pub fn has_no_children(&self) -> bool {
179        use self::DisplayListMsg::*;
180        match self {
181            Frame(f) => f.children.is_empty(),
182            ScrollFrame(sf) => sf.frame.children.is_empty(),
183            IFrame(_, _, _, _) => false,
184        }
185    }
186
187    pub fn push_content(&mut self, content: LayoutRectContent) {
188        use self::DisplayListMsg::*;
189        match self {
190            Frame(f) => {
191                f.content.push(content);
192            }
193            ScrollFrame(sf) => {
194                sf.frame.content.push(content);
195            }
196            IFrame(_, _, _, _) => {} // invalid
197        }
198    }
199
200    pub fn append_child(&mut self, child: Self) {
201        use self::DisplayListMsg::*;
202        match self {
203            Frame(f) => {
204                f.children.push(child);
205            }
206            ScrollFrame(sf) => {
207                sf.frame.children.push(child);
208            }
209            IFrame(_, _, _, _) => {} // invalid
210        }
211    }
212
213    pub fn append_children(&mut self, mut children: Vec<Self>) {
214        use self::DisplayListMsg::*;
215        match self {
216            Frame(f) => {
217                f.children.append(&mut children);
218            }
219            ScrollFrame(sf) => {
220                sf.frame.children.append(&mut children);
221            }
222            IFrame(_, _, _, _) => {} // invalid
223        }
224    }
225
226    pub fn get_size(&self) -> LogicalSize {
227        use self::DisplayListMsg::*;
228        match self {
229            Frame(f) => f.size,
230            ScrollFrame(sf) => sf.frame.size,
231            IFrame(_, s, _, _) => *s,
232        }
233    }
234}
235
236#[derive(Clone, PartialEq, PartialOrd)]
237pub struct DisplayListScrollFrame {
238    /// Containing rect of the parent node
239    pub parent_rect: LogicalRect,
240    /// Bounding rect of the (overflowing) content of the scroll frame
241    pub content_rect: LogicalRect,
242    /// The scroll ID is the hash of the DOM node, so that scrolling
243    /// positions can be tracked across multiple frames
244    pub scroll_id: ExternalScrollId,
245    /// The scroll tag is used for hit-testing
246    pub scroll_tag: ScrollTagId,
247    /// Content + children of the scroll clip
248    pub frame: DisplayListFrame,
249}
250
251impl DisplayListScrollFrame {
252    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
253        self.parent_rect.scale_for_dpi(scale_factor);
254        self.content_rect.scale_for_dpi(scale_factor);
255        self.frame.scale_for_dpi(scale_factor);
256    }
257}
258
259impl fmt::Debug for DisplayListScrollFrame {
260    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261        write!(f, "DisplayListScrollFrame {{\r\n")?;
262        write!(f, "    parent_rect: {}\r\n", self.parent_rect)?;
263        write!(f, "    content_rect: {}\r\n", self.content_rect)?;
264        write!(f, "    scroll_tag: {}\r\n", self.scroll_tag)?;
265        write!(f, "    frame: DisplayListFrame {{\r\n")?;
266        let frame = format!("{:#?}", self.frame);
267        let frame = frame
268            .lines()
269            .map(|l| format!("        {}", l))
270            .collect::<Vec<_>>()
271            .join("\r\n");
272        write!(f, "{}\r\n", frame)?;
273        write!(f, "    }}\r\n")?;
274        write!(f, "}}")?;
275        Ok(())
276    }
277}
278
279#[derive(Clone, PartialEq, PartialOrd)]
280pub struct DisplayListFrame {
281    pub size: LogicalSize,
282    pub position: PositionInfo,
283    pub flags: PrimitiveFlags,
284    pub mix_blend_mode: Option<StyleMixBlendMode>,
285    pub clip_children: Option<LogicalSize>,
286    pub clip_mask: Option<DisplayListImageMask>,
287    /// Border radius, set to none only if overflow: visible is set!
288    pub border_radius: StyleBorderRadius,
289    pub tag: Option<TagId>,
290    // box shadow has to be pushed twice: once as inset and once as outset
291    pub box_shadow: Option<BoxShadow>,
292    pub transform: Option<(TransformKey, ComputedTransform3D)>,
293    pub opacity: Option<(OpacityKey, f32)>,
294    pub content: Vec<LayoutRectContent>,
295    pub children: Vec<DisplayListMsg>,
296}
297
298impl fmt::Debug for DisplayListFrame {
299    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300        let print_no_comma_rect = !self.border_radius.is_none()
301            || self.tag.is_some()
302            || !self.content.is_empty()
303            || !self.children.is_empty();
304
305        write!(
306            f,
307            "rect: {:#?} @ {:?}{}",
308            self.size,
309            self.position,
310            if !print_no_comma_rect { "" } else { "," }
311        )?;
312
313        if !self.border_radius.is_none() {
314            write!(f, "\r\nborder_radius: {:#?}", self.border_radius)?;
315        }
316        if let Some(tag) = &self.tag {
317            write!(f, "\r\ntag: {}", tag.0)?;
318        }
319        if !self.content.is_empty() {
320            write!(f, "\r\ncontent: {:#?}", self.content)?;
321        }
322        if !self.children.is_empty() {
323            write!(f, "\r\nchildren: {:#?}", self.children)?;
324        }
325
326        Ok(())
327    }
328}
329
330impl DisplayListFrame {
331    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
332        self.size.width *= scale_factor;
333        self.size.height *= scale_factor;
334        self.position.scale_for_dpi(scale_factor);
335        self.clip_children
336            .as_mut()
337            .map(|s| s.scale_for_dpi(scale_factor));
338        self.clip_mask
339            .as_mut()
340            .map(|s| s.scale_for_dpi(scale_factor));
341        self.border_radius.scale_for_dpi(scale_factor);
342        self.transform
343            .as_mut()
344            .map(|(k, v)| v.scale_for_dpi(scale_factor));
345        for c in self.content.iter_mut() {
346            c.scale_for_dpi(scale_factor);
347        }
348        for c in self.children.iter_mut() {
349            c.scale_for_dpi(scale_factor);
350        }
351    }
352
353    pub fn root(dimensions: LayoutSize, root_origin: LayoutPoint) -> Self {
354        use crate::ui_solver::PositionInfoInner;
355        DisplayListFrame {
356            tag: None,
357            size: LogicalSize::new(dimensions.width as f32, dimensions.height as f32),
358            clip_children: None,
359            mix_blend_mode: None,
360            position: PositionInfo::Static(PositionInfoInner {
361                x_offset: root_origin.x as f32,
362                y_offset: root_origin.y as f32,
363                static_x_offset: root_origin.x as f32,
364                static_y_offset: root_origin.y as f32,
365            }),
366            flags: PrimitiveFlags {
367                is_backface_visible: true,
368                is_scrollbar_container: false,
369                is_scrollbar_thumb: false,
370                prefer_compositor_surface: true,
371                supports_external_compositor_surface: true,
372            },
373            border_radius: StyleBorderRadius::default(),
374            box_shadow: None,
375            transform: None,
376            opacity: None,
377            content: vec![],
378            children: vec![],
379            clip_mask: None,
380        }
381    }
382}
383
384#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub enum ImageRendering {
386    Auto,
387    CrispEdges,
388    Pixelated,
389}
390
391#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
392pub enum AlphaType {
393    Alpha,
394    PremultipliedAlpha,
395}
396
397#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
398pub struct StyleBorderRadius {
399    pub top_left: Option<CssPropertyValue<StyleBorderTopLeftRadius>>,
400    pub top_right: Option<CssPropertyValue<StyleBorderTopRightRadius>>,
401    pub bottom_left: Option<CssPropertyValue<StyleBorderBottomLeftRadius>>,
402    pub bottom_right: Option<CssPropertyValue<StyleBorderBottomRightRadius>>,
403}
404
405impl StyleBorderRadius {
406    pub fn is_none(&self) -> bool {
407        self.top_left.is_none()
408            && self.top_right.is_none()
409            && self.bottom_left.is_none()
410            && self.bottom_right.is_none()
411    }
412
413    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
414        self.top_left
415            .as_mut()
416            .map(|s| s.scale_for_dpi(scale_factor));
417        self.top_right
418            .as_mut()
419            .map(|s| s.scale_for_dpi(scale_factor));
420        self.bottom_left
421            .as_mut()
422            .map(|s| s.scale_for_dpi(scale_factor));
423        self.bottom_right
424            .as_mut()
425            .map(|s| s.scale_for_dpi(scale_factor));
426    }
427}
428
429impl fmt::Debug for StyleBorderRadius {
430    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431        write!(f, "StyleBorderRadius {{")?;
432        if let Some(tl) = &self.top_left {
433            write!(f, "\r\n\ttop-left: {:?},", tl)?;
434        }
435        if let Some(tr) = &self.top_right {
436            write!(f, "\r\n\ttop-right: {:?},", tr)?;
437        }
438        if let Some(bl) = &self.bottom_left {
439            write!(f, "\r\n\tbottom-left: {:?},", bl)?;
440        }
441        if let Some(br) = &self.bottom_right {
442            write!(f, "\r\n\tbottom-right: {:?},", br)?;
443        }
444        write!(f, "\r\n}}")
445    }
446}
447
448macro_rules! tlbr_debug {
449    ($struct_name:ident) => {
450        impl fmt::Debug for $struct_name {
451            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
452                write!(f, "{} {{", stringify!($struct_name))?;
453                if let Some(t) = &self.top {
454                    write!(f, "\r\n\ttop: {:?},", t)?;
455                }
456                if let Some(r) = &self.right {
457                    write!(f, "\r\n\tright: {:?},", r)?;
458                }
459                if let Some(b) = &self.bottom {
460                    write!(f, "\r\n\tbottom: {:?},", b)?;
461                }
462                if let Some(l) = &self.left {
463                    write!(f, "\r\n\tleft: {:?},", l)?;
464                }
465                write!(f, "\r\n}}")
466            }
467        }
468    };
469}
470
471#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
472pub struct StyleBorderWidths {
473    pub top: Option<CssPropertyValue<LayoutBorderTopWidth>>,
474    pub right: Option<CssPropertyValue<LayoutBorderRightWidth>>,
475    pub bottom: Option<CssPropertyValue<LayoutBorderBottomWidth>>,
476    pub left: Option<CssPropertyValue<LayoutBorderLeftWidth>>,
477}
478
479impl StyleBorderWidths {
480    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
481        self.top.as_mut().map(|s| s.scale_for_dpi(scale_factor));
482        self.right.as_mut().map(|s| s.scale_for_dpi(scale_factor));
483        self.bottom.as_mut().map(|s| s.scale_for_dpi(scale_factor));
484        self.left.as_mut().map(|s| s.scale_for_dpi(scale_factor));
485    }
486
487    #[inline]
488    pub fn left_width(&self) -> f32 {
489        self.left
490            .unwrap_or_default()
491            .get_property_owned()
492            .unwrap_or_default()
493            .inner
494            .to_pixels(0.0)
495    }
496
497    #[inline]
498    pub fn right_width(&self) -> f32 {
499        self.right
500            .unwrap_or_default()
501            .get_property_owned()
502            .unwrap_or_default()
503            .inner
504            .to_pixels(0.0)
505    }
506
507    #[inline]
508    pub fn top_width(&self) -> f32 {
509        self.top
510            .unwrap_or_default()
511            .get_property_owned()
512            .unwrap_or_default()
513            .inner
514            .to_pixels(0.0)
515    }
516
517    #[inline]
518    pub fn bottom_width(&self) -> f32 {
519        self.bottom
520            .unwrap_or_default()
521            .get_property_owned()
522            .unwrap_or_default()
523            .inner
524            .to_pixels(0.0)
525    }
526
527    #[inline]
528    pub fn total_horizontal(&self) -> f32 {
529        self.left_width() + self.right_width()
530    }
531
532    #[inline]
533    pub fn total_vertical(&self) -> f32 {
534        self.top_width() + self.bottom_width()
535    }
536}
537
538tlbr_debug!(StyleBorderWidths);
539
540#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
541pub struct StyleBorderColors {
542    pub top: Option<CssPropertyValue<StyleBorderTopColor>>,
543    pub right: Option<CssPropertyValue<StyleBorderRightColor>>,
544    pub bottom: Option<CssPropertyValue<StyleBorderBottomColor>>,
545    pub left: Option<CssPropertyValue<StyleBorderLeftColor>>,
546}
547
548tlbr_debug!(StyleBorderColors);
549
550#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
551pub struct StyleBorderStyles {
552    pub top: Option<CssPropertyValue<StyleBorderTopStyle>>,
553    pub right: Option<CssPropertyValue<StyleBorderRightStyle>>,
554    pub bottom: Option<CssPropertyValue<StyleBorderBottomStyle>>,
555    pub left: Option<CssPropertyValue<StyleBorderLeftStyle>>,
556}
557
558tlbr_debug!(StyleBorderStyles);
559
560#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
561pub struct BoxShadow {
562    pub clip_mode: BoxShadowClipMode,
563    pub top: Option<CssPropertyValue<StyleBoxShadow>>,
564    pub right: Option<CssPropertyValue<StyleBoxShadow>>,
565    pub bottom: Option<CssPropertyValue<StyleBoxShadow>>,
566    pub left: Option<CssPropertyValue<StyleBoxShadow>>,
567}
568
569tlbr_debug!(BoxShadow);
570
571#[derive(Clone, PartialEq, PartialOrd)]
572pub enum LayoutRectContent {
573    Text {
574        glyphs: Vec<GlyphInstance>,
575        font_instance_key: FontInstanceKey,
576        color: ColorU,
577        glyph_options: Option<GlyphOptions>,
578        overflow: (bool, bool),
579        text_shadow: Option<StyleBoxShadow>,
580    },
581    Background {
582        content: RectBackground,
583        size: Option<StyleBackgroundSize>,
584        offset: Option<StyleBackgroundPosition>,
585        repeat: Option<StyleBackgroundRepeat>,
586    },
587    Image {
588        size: LogicalSize,
589        offset: LogicalPosition,
590        image_rendering: ImageRendering,
591        alpha_type: AlphaType,
592        image_key: ImageKey,
593        background_color: ColorU,
594    },
595    Border {
596        widths: StyleBorderWidths,
597        colors: StyleBorderColors,
598        styles: StyleBorderStyles,
599    },
600}
601
602impl LayoutRectContent {
603    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
604        use self::LayoutRectContent::*;
605        match self {
606            Text {
607                glyphs,
608                font_instance_key,
609                color,
610                glyph_options,
611                overflow,
612                text_shadow,
613            } => {
614                for g in glyphs.iter_mut() {
615                    g.scale_for_dpi(scale_factor);
616                }
617                text_shadow.as_mut().map(|s| s.scale_for_dpi(scale_factor));
618            }
619            Background {
620                content,
621                size,
622                offset,
623                repeat,
624            } => {
625                content.scale_for_dpi(scale_factor);
626                size.as_mut().map(|s| s.scale_for_dpi(scale_factor));
627                offset.as_mut().map(|s| s.scale_for_dpi(scale_factor));
628            }
629            Image {
630                size,
631                offset,
632                image_rendering,
633                alpha_type,
634                image_key,
635                background_color,
636            } => {
637                size.scale_for_dpi(scale_factor);
638                offset.scale_for_dpi(scale_factor);
639            }
640            Border {
641                widths,
642                colors,
643                styles,
644            } => {
645                widths.scale_for_dpi(scale_factor);
646            }
647        }
648    }
649}
650
651impl fmt::Debug for LayoutRectContent {
652    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
653        use self::LayoutRectContent::*;
654        match self {
655            Text {
656                glyphs,
657                font_instance_key,
658                color,
659                glyph_options,
660                overflow,
661                text_shadow,
662            } => {
663                let glyphs_str = glyphs
664                    .iter()
665                    .map(|g| format!("        {:?}", g))
666                    .collect::<Vec<_>>()
667                    .join(",\r\n");
668                write!(
669                    f,
670                    "Text {{\r\n\
671                       .    glyphs: [\r\n{}\r\n],\r\n\
672                       .    font_instance_key: {:?},\r\n\
673                       .    color: {},\r\n\
674                       .    glyph_options: {:?},\r\n\
675                       .    overflow: {:?},\r\n\
676                       .    text_shadow: {:?},\r\n\
677                    }}",
678                    glyphs_str, font_instance_key.key, color, glyph_options, overflow, text_shadow
679                )
680            }
681            Background {
682                content,
683                size,
684                offset,
685                repeat,
686            } => {
687                write!(f, "Background {{\r\n")?;
688                write!(f, "    content: {:?},\r\n", content)?;
689                write!(f, "    size: {:?},\r\n", size)?;
690                write!(f, "    offset: {:?},\r\n", offset)?;
691                write!(f, "    repeat: {:?},\r\n", repeat)?;
692                write!(f, "}}")
693            }
694            Image {
695                size,
696                offset,
697                image_rendering,
698                alpha_type,
699                image_key,
700                background_color,
701            } => {
702                write!(
703                    f,
704                    "Image {{\r\nsize: {:?},\r\noffset: {:?},\r\nimage_rendering: \
705                     {:?},\r\nalpha_type: {:?},\r\nimage_key: {:?},\r\nbackground_color: \
706                     {:?}\r\n}}",
707                    size, offset, image_rendering, alpha_type, image_key, background_color
708                )
709            }
710            Border {
711                widths,
712                colors,
713                styles,
714            } => {
715                write!(
716                    f,
717                    "Border {{\r\nwidths: {:?},\r\ncolors: {:?},\r\nstyles: {:?}\r\n}}",
718                    widths, colors, styles,
719                )
720            }
721        }
722    }
723}
724
725#[derive(Clone, PartialEq, PartialOrd)]
726pub enum RectBackground {
727    LinearGradient(LinearGradient),
728    RadialGradient(RadialGradient),
729    ConicGradient(ConicGradient),
730    Image((ImageKey, ImageDescriptor)),
731    Color(ColorU),
732}
733
734impl fmt::Debug for RectBackground {
735    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
736        use self::RectBackground::*;
737        match self {
738            LinearGradient(l) => write!(f, "{:?}", l),
739            RadialGradient(r) => write!(f, "{:?}", r),
740            ConicGradient(c) => write!(f, "{:?}", c),
741            Image(id) => write!(f, "image({:#?})", id),
742            Color(c) => write!(f, "{}", c),
743        }
744    }
745}
746
747impl RectBackground {
748    pub fn scale_for_dpi(&mut self, scale_factor: f32) {
749        match self {
750            RectBackground::Image((_key, descriptor)) => {
751                descriptor.width =
752                    libm::round(descriptor.width as f64 * scale_factor as f64) as usize;
753                descriptor.height =
754                    libm::round(descriptor.height as f64 * scale_factor as f64) as usize;
755            }
756            _ => {}
757        }
758    }
759    pub fn get_content_size(&self) -> Option<(f32, f32)> {
760        match self {
761            RectBackground::Image((_key, descriptor)) => {
762                Some((descriptor.width as f32, descriptor.height as f32))
763            }
764            _ => None,
765        }
766    }
767}
768
769// ------------------- NEW DISPLAY LIST CODE
770
771/// Since the display list can take a lot of parameters, we don't want to
772/// continually pass them as parameters of the function and rather use a
773/// struct to pass them around. This is purely for ergonomic reasons.
774///
775/// `DisplayListParametersRef` has only members that are
776///  **immutable references** to other things that need to be passed down the display list
777#[derive(Clone)]
778pub struct DisplayListParametersRef<'a> {
779    /// ID of this Dom
780    pub dom_id: DomId,
781    /// Document ID (window ID)
782    pub document_id: &'a DocumentId,
783    /// Epoch of all the OpenGL textures
784    pub epoch: Epoch,
785    /// The CSS that should be applied to the DOM
786    pub full_window_state: &'a FullWindowState,
787    /// Cached layouts (+ solved layouts for iframes)
788    pub layout_results: &'a [LayoutResult],
789    /// Cached rendered OpenGL textures
790    pub gl_texture_cache: &'a GlTextureCache,
791    /// Cached IDs for CSS backgrounds
792    pub image_cache: &'a ImageCache,
793    /// Reference to the RendererResources, necessary to query info about image and font keys
794    pub renderer_resources: &'a RendererResources,
795}
796
797// todo: very unclean
798pub type LayoutFn = fn(
799    StyledDom,
800    &ImageCache,
801    &FcFontCache,
802    &mut RendererResources,
803    DpiScaleFactor,
804    &mut Vec<ResourceUpdate>,
805    IdNamespace,
806    &DocumentId,
807    Epoch,
808    &RenderCallbacks,
809    &FullWindowState,
810) -> Vec<LayoutResult>;
811pub type GlStoreImageFn = fn(DocumentId, Epoch, Texture) -> ExternalImageId;
812
813#[derive(Debug, Default)]
814pub struct SolvedLayout {
815    pub layout_results: Vec<LayoutResult>,
816}
817
818#[derive(Clone)]
819pub struct RenderCallbacks {
820    pub insert_into_active_gl_textures_fn: GlStoreImageFn,
821    pub layout_fn: LayoutFn,
822    pub load_font_fn: LoadFontFn,
823    pub parse_font_fn: ParseFontFn,
824}
825
826impl SolvedLayout {
827    /// Does the layout, updates the image + font resources for the RenderAPI
828    pub fn new(
829        styled_dom: StyledDom,
830        epoch: Epoch,
831        document_id: &DocumentId,
832        full_window_state: &FullWindowState,
833        all_resource_updates: &mut Vec<ResourceUpdate>,
834        id_namespace: IdNamespace,
835        image_cache: &ImageCache,
836        system_fonts: &FcFontCache,
837        callbacks: &RenderCallbacks,
838        renderer_resources: &mut RendererResources,
839        current_window_dpi: DpiScaleFactor,
840    ) -> Self {
841        Self {
842            layout_results: (callbacks.layout_fn)(
843                styled_dom,
844                image_cache,
845                system_fonts,
846                renderer_resources,
847                current_window_dpi,
848                all_resource_updates,
849                id_namespace,
850                document_id,
851                epoch,
852                callbacks,
853                &full_window_state,
854            ),
855        }
856    }
857}
858
859pub fn push_rectangles_into_displaylist<'a>(
860    root_content_group: &ContentGroup,
861    referenced_content: &DisplayListParametersRef<'a>,
862) -> Option<DisplayListMsg> {
863    let mut content = displaylist_handle_rect(
864        root_content_group.root.into_crate_internal().unwrap(),
865        referenced_content,
866    )?;
867
868    let children = root_content_group
869        .children
870        .as_ref()
871        .iter()
872        .filter_map(|child_content_group| {
873            push_rectangles_into_displaylist(child_content_group, referenced_content)
874        })
875        .collect();
876
877    content.append_children(children);
878
879    Some(content)
880}
881
882/// Push a single rectangle into the display list builder
883pub fn displaylist_handle_rect<'a>(
884    rect_idx: NodeId,
885    referenced_content: &DisplayListParametersRef<'a>,
886) -> Option<DisplayListMsg> {
887    use azul_css::LayoutDisplay;
888
889    use crate::{app_resources::ResolvedImage, dom::NodeType::*, styled_dom::AzTagId};
890
891    let DisplayListParametersRef {
892        dom_id,
893        layout_results,
894        gl_texture_cache,
895        renderer_resources,
896        image_cache,
897        ..
898    } = referenced_content;
899
900    let layout_result = &layout_results[dom_id.inner];
901    let styled_node = &layout_result.styled_dom.styled_nodes.as_container()[rect_idx];
902    let positioned_rect = &layout_result.rects.as_ref()[rect_idx];
903    let html_node = &layout_result.styled_dom.node_data.as_container()[rect_idx];
904
905    let tag_id = styled_node.tag_id.into_option().or({
906        layout_result
907            .scrollable_nodes
908            .overflowing_nodes
909            .get(&NodeHierarchyItemId::from_crate_internal(Some(rect_idx)))
910            .map(|scrolled| AzTagId::from_crate_internal(scrolled.scroll_tag_id.0))
911    });
912
913    let clip_mask = html_node.get_clip_mask().and_then(|m| {
914        let clip_mask_hash = m.image.get_hash();
915        let ResolvedImage { key, .. } = renderer_resources.get_image(&clip_mask_hash)?;
916        Some(DisplayListImageMask {
917            image: *key,
918            rect: m.rect,
919            repeat: m.repeat,
920        })
921    });
922
923    // do not push display:none items in any way
924    //
925    // TODO: this currently operates on the visual order, not on the DOM order!
926    let display = layout_result
927        .styled_dom
928        .get_css_property_cache()
929        .get_display(&html_node, &rect_idx, &styled_node.state)
930        .cloned()
931        .unwrap_or_default();
932
933    if display == CssPropertyValue::None || display == CssPropertyValue::Exact(LayoutDisplay::None)
934    {
935        return None;
936    }
937
938    let overflow_horizontal_hidden = layout_result
939        .styled_dom
940        .get_css_property_cache()
941        .is_horizontal_overflow_hidden(&html_node, &rect_idx, &styled_node.state);
942    let overflow_vertical_hidden = layout_result
943        .styled_dom
944        .get_css_property_cache()
945        .is_vertical_overflow_hidden(&html_node, &rect_idx, &styled_node.state);
946
947    let overflow_horizontal_visible = layout_result
948        .styled_dom
949        .get_css_property_cache()
950        .is_horizontal_overflow_visible(&html_node, &rect_idx, &styled_node.state);
951    let overflow_vertical_visible = layout_result
952        .styled_dom
953        .get_css_property_cache()
954        .is_vertical_overflow_visible(&html_node, &rect_idx, &styled_node.state);
955
956    let mix_blend_mode = layout_result
957        .styled_dom
958        .get_css_property_cache()
959        .get_mix_blend_mode(&html_node, &rect_idx, &styled_node.state)
960        .and_then(|p| p.get_property())
961        .cloned();
962
963    let mut frame = DisplayListFrame {
964        tag: tag_id.map(|t| t.into_crate_internal()),
965        size: positioned_rect.size,
966        mix_blend_mode,
967        clip_children: match layout_result
968            .scrollable_nodes
969            .clip_nodes
970            .get(&rect_idx)
971            .copied()
972        {
973            Some(s) => {
974                // never clip children if overflow:visible is set!
975                if overflow_horizontal_visible && overflow_vertical_visible {
976                    None
977                } else {
978                    Some(s)
979                }
980            }
981            None => {
982                // if the frame has overflow:hidden set, clip the
983                // children to the current frames extents
984                if overflow_horizontal_hidden && overflow_vertical_hidden {
985                    Some(positioned_rect.size)
986                } else {
987                    None
988                }
989            }
990        },
991        position: positioned_rect.position.clone(),
992        border_radius: StyleBorderRadius {
993            top_left: layout_result
994                .styled_dom
995                .get_css_property_cache()
996                .get_border_top_left_radius(&html_node, &rect_idx, &styled_node.state)
997                .cloned(),
998            top_right: layout_result
999                .styled_dom
1000                .get_css_property_cache()
1001                .get_border_top_right_radius(&html_node, &rect_idx, &styled_node.state)
1002                .cloned(),
1003            bottom_left: layout_result
1004                .styled_dom
1005                .get_css_property_cache()
1006                .get_border_bottom_left_radius(&html_node, &rect_idx, &styled_node.state)
1007                .cloned(),
1008            bottom_right: layout_result
1009                .styled_dom
1010                .get_css_property_cache()
1011                .get_border_bottom_right_radius(&html_node, &rect_idx, &styled_node.state)
1012                .cloned(),
1013        },
1014        flags: PrimitiveFlags {
1015            is_backface_visible: false, // TODO!
1016            is_scrollbar_container: false,
1017            is_scrollbar_thumb: false,
1018            prefer_compositor_surface: false,
1019            supports_external_compositor_surface: false,
1020        },
1021        content: Vec::new(),
1022        children: Vec::new(),
1023        box_shadow: None,
1024        transform: layout_result
1025            .gpu_value_cache
1026            .transform_keys
1027            .get(&rect_idx)
1028            .and_then(|key| {
1029                Some((
1030                    *key,
1031                    layout_result
1032                        .gpu_value_cache
1033                        .current_transform_values
1034                        .get(&rect_idx)
1035                        .cloned()?,
1036                ))
1037            }),
1038        opacity: layout_result
1039            .gpu_value_cache
1040            .opacity_keys
1041            .get(&rect_idx)
1042            .and_then(|key| {
1043                Some((
1044                    *key,
1045                    layout_result
1046                        .gpu_value_cache
1047                        .current_opacity_values
1048                        .get(&rect_idx)
1049                        .cloned()?,
1050                ))
1051            }),
1052        clip_mask,
1053    };
1054
1055    // push box shadow
1056    let box_shadow_left = layout_result
1057        .styled_dom
1058        .get_css_property_cache()
1059        .get_box_shadow_left(&html_node, &rect_idx, &styled_node.state);
1060    let box_shadow_right = layout_result
1061        .styled_dom
1062        .get_css_property_cache()
1063        .get_box_shadow_right(&html_node, &rect_idx, &styled_node.state);
1064    let box_shadow_top = layout_result
1065        .styled_dom
1066        .get_css_property_cache()
1067        .get_box_shadow_top(&html_node, &rect_idx, &styled_node.state);
1068    let box_shadow_bottom = layout_result
1069        .styled_dom
1070        .get_css_property_cache()
1071        .get_box_shadow_bottom(&html_node, &rect_idx, &styled_node.state);
1072
1073    let box_shadows = [
1074        &box_shadow_left,
1075        &box_shadow_right,
1076        &box_shadow_top,
1077        &box_shadow_bottom,
1078    ];
1079
1080    let box_shadow = if box_shadows.iter().all(|b| b.is_some()) {
1081        let mut clip_mode = None;
1082
1083        if box_shadows.iter().all(|b| {
1084            b.and_then(|b| b.get_property().map(|p| p.clip_mode)) == Some(BoxShadowClipMode::Outset)
1085        }) {
1086            clip_mode = Some(BoxShadowClipMode::Outset);
1087        } else if box_shadows.iter().all(|b| {
1088            b.and_then(|b| b.get_property().map(|p| p.clip_mode)) == Some(BoxShadowClipMode::Inset)
1089        }) {
1090            clip_mode = Some(BoxShadowClipMode::Inset);
1091        }
1092
1093        clip_mode.map(|c| BoxShadow {
1094            clip_mode: c,
1095            left: box_shadow_left.cloned(),
1096            right: box_shadow_right.cloned(),
1097            top: box_shadow_top.cloned(),
1098            bottom: box_shadow_bottom.cloned(),
1099        })
1100    } else {
1101        None
1102    };
1103
1104    frame.box_shadow = box_shadow;
1105
1106    // push background
1107    let bg_opt = layout_result
1108        .styled_dom
1109        .get_css_property_cache()
1110        .get_background_content(&html_node, &rect_idx, &styled_node.state);
1111
1112    if let Some(bg) = bg_opt.as_ref().and_then(|br| br.get_property()) {
1113        use azul_css::{
1114            StyleBackgroundPositionVec, StyleBackgroundRepeatVec, StyleBackgroundSizeVec,
1115        };
1116
1117        let default_bg_size_vec: StyleBackgroundSizeVec = Vec::new().into();
1118        let default_bg_position_vec: StyleBackgroundPositionVec = Vec::new().into();
1119        let default_bg_repeat_vec: StyleBackgroundRepeatVec = Vec::new().into();
1120
1121        let bg_sizes_opt = layout_result
1122            .styled_dom
1123            .get_css_property_cache()
1124            .get_background_size(&html_node, &rect_idx, &styled_node.state);
1125        let bg_positions_opt = layout_result
1126            .styled_dom
1127            .get_css_property_cache()
1128            .get_background_position(&html_node, &rect_idx, &styled_node.state);
1129        let bg_repeats_opt = layout_result
1130            .styled_dom
1131            .get_css_property_cache()
1132            .get_background_repeat(&html_node, &rect_idx, &styled_node.state);
1133
1134        let bg_sizes = bg_sizes_opt
1135            .as_ref()
1136            .and_then(|p| p.get_property())
1137            .unwrap_or(&default_bg_size_vec);
1138        let bg_positions = bg_positions_opt
1139            .as_ref()
1140            .and_then(|p| p.get_property())
1141            .unwrap_or(&default_bg_position_vec);
1142        let bg_repeats = bg_repeats_opt
1143            .as_ref()
1144            .and_then(|p| p.get_property())
1145            .unwrap_or(&default_bg_repeat_vec);
1146
1147        for (bg_index, bg) in bg.iter().enumerate() {
1148            use azul_css::{AzString, StyleBackgroundContent::*};
1149
1150            fn get_image_background_key(
1151                renderer_resources: &RendererResources,
1152                image_cache: &ImageCache,
1153                background_image_id: &AzString,
1154            ) -> Option<(ImageKey, ImageDescriptor)> {
1155                let image_ref = image_cache.get_css_image_id(background_image_id)?;
1156                let image_ref_hash = image_ref.get_hash();
1157                let ResolvedImage { key, descriptor } =
1158                    renderer_resources.get_image(&image_ref_hash)?;
1159                Some((*key, descriptor.clone()))
1160            }
1161
1162            let background_content = match bg {
1163                LinearGradient(lg) => Some(RectBackground::LinearGradient(lg.clone())),
1164                RadialGradient(rg) => Some(RectBackground::RadialGradient(rg.clone())),
1165                ConicGradient(cg) => Some(RectBackground::ConicGradient(cg.clone())),
1166                Image(i) => get_image_background_key(&renderer_resources, &image_cache, i)
1167                    .map(RectBackground::Image),
1168                Color(c) => Some(RectBackground::Color(*c)),
1169            };
1170
1171            let bg_size = bg_sizes.get(bg_index).or(bg_sizes.get(0)).copied();
1172            let bg_position = bg_positions.get(bg_index).or(bg_positions.get(0)).copied();
1173            let bg_repeat = bg_repeats.get(bg_index).or(bg_repeats.get(0)).copied();
1174
1175            if let Some(background_content) = background_content {
1176                frame.content.push(LayoutRectContent::Background {
1177                    content: background_content,
1178                    size: bg_size.clone(),
1179                    offset: bg_position.clone(),
1180                    repeat: bg_repeat.clone(),
1181                });
1182            }
1183        }
1184    }
1185
1186    match html_node.get_node_type() {
1187        Div | Body | Br => {}
1188        Text(_) => {
1189            use crate::app_resources::get_inline_text;
1190
1191            // compute the layouted glyphs here, this way it's easier
1192            // to reflow text since there is no cache that needs to be updated
1193            //
1194            // if the text is reflowed, the display list needs to update anyway
1195            if let (
1196                Some(words),
1197                Some(shaped_words),
1198                Some(word_positions),
1199                Some((_, inline_text_layout)),
1200            ) = (
1201                layout_result.words_cache.get(&rect_idx),
1202                layout_result.shaped_words_cache.get(&rect_idx),
1203                layout_result.positioned_words_cache.get(&rect_idx),
1204                positioned_rect.resolved_text_layout_options.as_ref(),
1205            ) {
1206                let inline_text = get_inline_text(
1207                    &words,
1208                    &shaped_words,
1209                    &word_positions.0,
1210                    &inline_text_layout,
1211                );
1212                let layouted_glyphs = inline_text.get_layouted_glyphs();
1213
1214                if !layouted_glyphs.glyphs.is_empty() {
1215                    let font_instance_key = word_positions.1;
1216                    let text_color = layout_result
1217                        .styled_dom
1218                        .get_css_property_cache()
1219                        .get_text_color_or_default(&html_node, &rect_idx, &styled_node.state);
1220
1221                    let text_shadow = layout_result
1222                        .styled_dom
1223                        .get_css_property_cache()
1224                        .get_text_shadow(&html_node, &rect_idx, &styled_node.state)
1225                        .and_then(|p| p.get_property())
1226                        .cloned();
1227
1228                    frame.content.push(LayoutRectContent::Text {
1229                        text_shadow,
1230                        glyphs: layouted_glyphs.glyphs,
1231                        font_instance_key,
1232                        color: text_color.inner,
1233                        glyph_options: None,
1234                        overflow: (overflow_horizontal_visible, overflow_vertical_visible),
1235                    });
1236                }
1237            }
1238        }
1239        Image(image_ref) => {
1240            use crate::app_resources::DecodedImage;
1241
1242            let image_hash = image_ref.get_hash();
1243            let image_size = image_ref.get_size();
1244
1245            match image_ref.get_data() {
1246                DecodedImage::NullImage { .. } => frame.content.push(LayoutRectContent::Image {
1247                    size: image_size,
1248                    offset: LogicalPosition::zero(),
1249                    image_rendering: ImageRendering::Auto,
1250                    alpha_type: AlphaType::Alpha,
1251                    image_key: ImageKey::DUMMY,
1252                    background_color: ColorU::WHITE,
1253                }),
1254                DecodedImage::Gl(_) | DecodedImage::Raw(_) => {
1255                    if let Some(ResolvedImage { key, .. }) =
1256                        renderer_resources.get_image(&image_hash)
1257                    {
1258                        frame.content.push(LayoutRectContent::Image {
1259                            size: image_size,
1260                            offset: LogicalPosition::zero(),
1261                            image_rendering: ImageRendering::Auto,
1262                            alpha_type: AlphaType::PremultipliedAlpha,
1263                            image_key: *key,
1264                            background_color: ColorU::WHITE,
1265                        });
1266                    }
1267                }
1268                DecodedImage::Callback(_) => {
1269                    if let Some((key, descriptor, _)) = gl_texture_cache
1270                        .solved_textures
1271                        .get(&dom_id)
1272                        .and_then(|textures| textures.get(&rect_idx))
1273                    {
1274                        frame.content.push(LayoutRectContent::Image {
1275                            size: LogicalSize::new(
1276                                descriptor.width as f32,
1277                                descriptor.height as f32,
1278                            ),
1279                            offset: LogicalPosition::zero(),
1280                            image_rendering: ImageRendering::Auto,
1281                            alpha_type: AlphaType::Alpha,
1282                            image_key: *key,
1283                            background_color: ColorU::WHITE,
1284                        })
1285                    }
1286                }
1287            }
1288        }
1289        IFrame(_) => {
1290            if let Some(iframe_dom_id) =
1291                layout_result
1292                    .iframe_mapping
1293                    .iter()
1294                    .find_map(|(node_id, dom_id)| {
1295                        if *node_id == rect_idx {
1296                            Some(*dom_id)
1297                        } else {
1298                            None
1299                        }
1300                    })
1301            {
1302                let iframe_pipeline_id = PipelineId(
1303                    iframe_dom_id.inner.max(core::u32::MAX as usize) as u32,
1304                    referenced_content.document_id.id,
1305                );
1306                let cached_display_list = LayoutResult::get_cached_display_list(
1307                    referenced_content.document_id,
1308                    iframe_dom_id, // <- important, otherwise it would recurse infinitely
1309                    referenced_content.epoch,
1310                    referenced_content.layout_results,
1311                    referenced_content.full_window_state,
1312                    referenced_content.gl_texture_cache,
1313                    referenced_content.renderer_resources,
1314                    referenced_content.image_cache,
1315                );
1316                let iframe_clip_size = positioned_rect.size;
1317                frame.children.push(DisplayListMsg::IFrame(
1318                    iframe_pipeline_id,
1319                    iframe_clip_size,
1320                    referenced_content.epoch,
1321                    Box::new(cached_display_list),
1322                ));
1323            }
1324        }
1325    };
1326
1327    if layout_result
1328        .styled_dom
1329        .get_css_property_cache()
1330        .has_border(&html_node, &rect_idx, &styled_node.state)
1331    {
1332        frame.content.push(LayoutRectContent::Border {
1333            widths: StyleBorderWidths {
1334                top: layout_result
1335                    .styled_dom
1336                    .get_css_property_cache()
1337                    .get_border_top_width(&html_node, &rect_idx, &styled_node.state)
1338                    .cloned(),
1339                left: layout_result
1340                    .styled_dom
1341                    .get_css_property_cache()
1342                    .get_border_left_width(&html_node, &rect_idx, &styled_node.state)
1343                    .cloned(),
1344                bottom: layout_result
1345                    .styled_dom
1346                    .get_css_property_cache()
1347                    .get_border_bottom_width(&html_node, &rect_idx, &styled_node.state)
1348                    .cloned(),
1349                right: layout_result
1350                    .styled_dom
1351                    .get_css_property_cache()
1352                    .get_border_right_width(&html_node, &rect_idx, &styled_node.state)
1353                    .cloned(),
1354            },
1355            colors: StyleBorderColors {
1356                top: layout_result
1357                    .styled_dom
1358                    .get_css_property_cache()
1359                    .get_border_top_color(&html_node, &rect_idx, &styled_node.state)
1360                    .cloned(),
1361                left: layout_result
1362                    .styled_dom
1363                    .get_css_property_cache()
1364                    .get_border_left_color(&html_node, &rect_idx, &styled_node.state)
1365                    .cloned(),
1366                bottom: layout_result
1367                    .styled_dom
1368                    .get_css_property_cache()
1369                    .get_border_bottom_color(&html_node, &rect_idx, &styled_node.state)
1370                    .cloned(),
1371                right: layout_result
1372                    .styled_dom
1373                    .get_css_property_cache()
1374                    .get_border_right_color(&html_node, &rect_idx, &styled_node.state)
1375                    .cloned(),
1376            },
1377            styles: StyleBorderStyles {
1378                top: layout_result
1379                    .styled_dom
1380                    .get_css_property_cache()
1381                    .get_border_top_style(&html_node, &rect_idx, &styled_node.state)
1382                    .cloned(),
1383                left: layout_result
1384                    .styled_dom
1385                    .get_css_property_cache()
1386                    .get_border_left_style(&html_node, &rect_idx, &styled_node.state)
1387                    .cloned(),
1388                bottom: layout_result
1389                    .styled_dom
1390                    .get_css_property_cache()
1391                    .get_border_bottom_style(&html_node, &rect_idx, &styled_node.state)
1392                    .cloned(),
1393                right: layout_result
1394                    .styled_dom
1395                    .get_css_property_cache()
1396                    .get_border_right_style(&html_node, &rect_idx, &styled_node.state)
1397                    .cloned(),
1398            },
1399        });
1400    }
1401
1402    match layout_result
1403        .scrollable_nodes
1404        .overflowing_nodes
1405        .get(&NodeHierarchyItemId::from_crate_internal(Some(rect_idx)))
1406    {
1407        Some(scroll_node) => Some(DisplayListMsg::ScrollFrame(DisplayListScrollFrame {
1408            parent_rect: scroll_node.parent_rect,
1409            content_rect: scroll_node.child_rect,
1410            scroll_id: scroll_node.parent_external_scroll_id,
1411            scroll_tag: scroll_node.scroll_tag_id,
1412            frame,
1413        })),
1414        None => Some(DisplayListMsg::Frame(frame)),
1415    }
1416}