atmos/os_lib/web_engine/
frames.rs1use alloc::boxed::Box;
10use alloc::collections::BTreeMap;
11use alloc::format;
12use alloc::string::String;
13
14use super::WebEngine;
15use crate::kernel::draw;
16
17pub(crate) const MAX_FRAME_DEPTH: u32 = 2;
19pub(crate) const MAX_FRAMES_PER_DOC: usize = 4;
21pub(crate) const MIN_FRAME_SIZE: i32 = 8;
23
24pub(crate) struct FrameParent<'a> {
26 pub is_https: bool,
27 pub host: &'a str,
28 pub path: &'a str,
29 pub depth: u32,
30}
31
32pub(crate) fn frame_url(parent: &FrameParent<'_>, src: &str) -> Option<String> {
35 let src = src.trim();
36 if src.is_empty() {
37 return None;
38 }
39 if src.starts_with("https://") || src.starts_with("http://") {
40 return Some(String::from(src));
41 }
42 let scheme = if parent.is_https { "https:" } else { "http:" };
43 if let Some(rest) = src.strip_prefix("//") {
44 return Some(format!("{}//{}", scheme, rest));
45 }
46 if src.contains(':') && !src.starts_with('/') {
47 let before_slash = src.split('/').next().unwrap_or("");
49 if before_slash.contains(':') {
50 return None;
51 }
52 }
53 let base = format!("{}//{}{}", scheme, parent.host, parent.path);
54 Some(super::url_resolve::resolve_url(&base, src))
55}
56
57pub(crate) fn frame_key(ordinal: usize, url: &str) -> String {
60 format!("{}|{}", ordinal, url)
61}
62
63#[allow(clippy::too_many_arguments)]
66pub(crate) fn draw_child_frame(
67 frames: &mut BTreeMap<String, Box<WebEngine>>,
68 parent: &FrameParent<'_>,
69 key: &str,
70 url: &str,
71 screen: &draw::Screen,
72 x: i32,
73 y: i32,
74 w: i32,
75 h: i32,
76) -> bool {
77 if parent.depth >= MAX_FRAME_DEPTH || w < MIN_FRAME_SIZE || h < MIN_FRAME_SIZE {
78 return false;
79 }
80 if !frames.contains_key(key) {
81 if frames.len() >= MAX_FRAMES_PER_DOC {
82 return false;
83 }
84 let mut child = Box::new(WebEngine::new());
85 child.frame_depth = parent.depth + 1;
86 child.js_runtime.set_frame_depth(child.frame_depth);
87 child.win_w = w as u32;
88 child.win_h = h as u32;
89 child.top_offset = 0;
90 let (to_https, to_host) = split_scheme_host(url);
91 let referer = super::referrer::referrer_for(parent.is_https, parent.host, parent.path, to_https, to_host);
92 child.load_frame(url, referer);
93 frames.insert(String::from(key), child);
94 }
95 let Some(child) = frames.get_mut(key) else { return false };
96 if child.win_w != w as u32 || child.win_h != h as u32 {
98 child.win_w = w as u32;
99 child.win_h = h as u32;
100 let html = child.last_html.clone();
101 if !html.is_empty() {
102 child.parse_and_layout(&html, None, 0, w as u32, h as u32);
103 }
104 }
105 if x < 0 || y < 0 {
107 return true;
108 }
109 let (x, y, w, h) = (x as u32, y as u32, w as u32, h as u32);
110 screen.push_clip(x, y, x + w, y + h);
111 child.draw(screen, x, y, w, h, 0);
112 screen.pop_clip();
113 true
114}
115
116fn split_scheme_host(url: &str) -> (bool, &str) {
118 let (https, rest) = if let Some(r) = url.strip_prefix("https://") {
119 (true, r)
120 } else {
121 (false, url.strip_prefix("http://").unwrap_or(url))
122 };
123 let end = rest.find(['/', '?', '#']).unwrap_or(rest.len());
124 (https, rest.get(..end).unwrap_or(rest))
125}