orinium_browser/browser/core/
tab.rs1use crate::{
4 browser::core::resource_loader::BrowserNetworkError,
5 engine::{html::HtmlNodeType, layouter::types::InfoNode, tree::TreeNode},
6};
7use ui_layout::LayoutNode;
8use url::Url;
9
10pub use super::webview::{FetchKind, WebView, WebViewTask};
11
12pub enum TabTask {
13 Fetch { url: Url, kind: FetchKind },
14 NeedsRedraw,
15}
16
17enum TabError {
18 NetworkError(BrowserNetworkError),
19}
20
21enum TabState {
22 Loading,
23 Loaded,
24 Error(TabError, Option<Url>), }
26
27pub struct Tab {
39 title: Option<String>,
40 base_url: Option<Url>,
41 docment_url: Option<Url>,
42 webview: Option<WebView>,
43 state: TabState,
44}
45
46impl Default for Tab {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl Tab {
53 pub fn new() -> Self {
54 Self {
55 title: None,
56 base_url: None,
57 docment_url: None,
58 webview: None,
59 state: TabState::Loading,
60 }
61 }
62
63 pub fn tick(&mut self) -> Vec<TabTask> {
68 let mut tasks = Vec::new();
69 let Some(wv) = self.webview.as_mut() else {
70 return tasks;
71 };
72
73 for task in wv.tick() {
74 match task {
75 WebViewTask::Fetch { url, kind } => {
76 log::info!("Fetch requested in Tab: url={}", url);
77 tasks.push(TabTask::Fetch { url, kind });
78 }
79 WebViewTask::AskTabHtml => {
80 tasks.push(TabTask::Fetch {
81 url: self.docment_url.as_ref().unwrap().clone(),
82 kind: FetchKind::Html,
83 });
84 }
85 }
86 }
87
88 if wv.needs_redraw() {
89 tasks.push(TabTask::NeedsRedraw);
90 }
91
92 tasks
93 }
94
95 pub fn on_css_fetched(&mut self, css: String) {
97 log::info!("CSS fetched in Tab");
98 if let Some(webview) = self.webview.as_mut() {
99 webview.on_css_fetched(css);
100 }
101 }
102
103 pub fn on_fetch_succeeded_html(&mut self, html: String) {
105 let Some(wv) = self.webview.as_mut() else {
106 return;
107 };
108
109 wv.on_html_fetched(html, self.docment_url.as_ref().unwrap().clone());
110 self.title = wv.title().cloned();
111 let base_url = wv.base_url().unwrap().clone();
112 log::info!("HTML fetched, base_url={}", base_url);
113 self.base_url = Some(base_url);
114
115 if let TabState::Error(TabError::NetworkError(err), url_opt) = &self.state {
116 let error_message = match url_opt {
117 Some(url) => format!("Failed to load {}: {}", url, err),
118 None => format!("Failed to load page: {}", err),
119 };
120
121 let error_message_element = wv
122 .document_info()
123 .unwrap()
124 .dom
125 .get_elements_by_class_name("error-message");
126 let error_message_element = error_message_element.first().unwrap();
127 let new_child = TreeNode::new(HtmlNodeType::Text(error_message));
128 TreeNode::replace_child(error_message_element, 0, new_child);
129
130 wv.update_page();
133 } else {
134 self.state = TabState::Loaded;
135 }
136 }
137
138 pub fn on_fetch_succeeded_css(&mut self, css: String) {
139 let Some(wv) = self.webview.as_mut() else {
140 return;
141 };
142
143 wv.on_css_fetched(css);
144 }
145
146 pub fn on_fetch_failed(&mut self, err: BrowserNetworkError, failed_url: Url) {
148 self.navigate("resource:///error.html".parse().unwrap());
149 self.state = TabState::Error(TabError::NetworkError(err), Some(failed_url));
150 }
151
152 pub fn navigate(&mut self, url: Url) {
153 self.docment_url = Some(url.clone());
154 let mut webview = WebView::new();
155 webview.navigate();
156 self.webview = Some(webview);
157 self.state = TabState::Loading;
158 }
159
160 pub fn move_to(&mut self, href: &str) {
161 let base_url = match self.base_url.as_ref() {
162 Some(u) => u,
163 None => return,
164 };
165
166 let url = super::webview::resolve_url(base_url, href).unwrap();
167
168 self.navigate(url)
170 }
171
172 pub fn relayout(&mut self, viewport: (f32, f32)) {
173 if let Some(wv) = self.webview.as_mut() {
174 wv.relayout(viewport);
175 }
176 }
177
178 pub fn layout_and_info_mut(&mut self) -> Option<(&LayoutNode, &mut InfoNode)> {
181 self.webview
182 .as_mut()
183 .and_then(|wv| wv.layout_and_info_mut())
184 }
185
186 pub fn title(&self) -> Option<String> {
188 self.title.clone()
189 }
190
191 pub fn document_url(&self) -> Option<Url> {
193 self.docment_url.clone()
194 }
195
196 pub fn layout_and_info(&self) -> Option<(&LayoutNode, &InfoNode)> {
197 self.webview.as_ref().and_then(|wv| wv.layout_and_info())
198 }
199
200 pub fn needs_redraw(&self) -> bool {
201 self.webview
202 .as_ref()
203 .map(|wv| wv.needs_redraw())
204 .unwrap_or(false)
205 }
206
207 pub fn clear_redraw_flag(&mut self) {
208 if let Some(wv) = self.webview.as_mut() {
209 wv.clear_redraw_flag();
210 }
211 }
212}