1use super::{
3 FFIResult, Rid,
4 net::Request,
5 std::{destroy, read_string_and_destroy},
6};
7use crate::alloc::String;
8
9#[link(wasm_import_module = "js")]
10unsafe extern "C" {
11 fn context_create() -> Rid;
12 fn context_eval(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
13 fn context_get(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
14
15 fn webview_create() -> Rid;
16 fn webview_load(webview: Rid, request: Rid) -> FFIResult;
17 fn webview_load_html(
18 webview: Rid,
19 string_ptr: *const u8,
20 len: usize,
21 url_ptr: *const u8,
22 url_len: usize,
23 ) -> FFIResult;
24 fn webview_wait_for_load(webview: Rid) -> FFIResult;
25 fn webview_eval(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
26}
27
28#[derive(PartialEq, Eq, Debug, Clone)]
30pub enum JsError {
31 MissingResult,
32 InvalidContext,
33 InvalidString,
34 InvalidHandler,
35 InvalidRequest,
36}
37
38impl JsError {
39 fn from(value: FFIResult) -> Option<Self> {
40 match value {
41 -1 => Some(Self::MissingResult),
42 -2 => Some(Self::InvalidContext),
43 -3 => Some(Self::InvalidString),
44 -4 => Some(Self::InvalidHandler),
45 -5 => Some(Self::InvalidRequest),
46 _ => None,
47 }
48 }
49}
50
51pub struct JsContext {
53 rid: Rid,
54}
55
56impl JsContext {
57 pub fn new() -> Self {
59 let rid = unsafe { context_create() };
60 Self { rid }
61 }
62
63 pub fn eval(&self, js: &str) -> Result<String, JsError> {
65 let js_bytes = js.as_bytes();
66 let result = unsafe { context_eval(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
67 if let Some(error) = JsError::from(result) {
68 Err(error)
69 } else {
70 Ok(read_string_and_destroy(result).unwrap_or_default())
71 }
72 }
73
74 pub fn get(&self, variable: &str) -> Result<String, JsError> {
76 let var_bytes = variable.as_bytes();
77 let result = unsafe { context_get(self.rid, var_bytes.as_ptr(), var_bytes.len()) };
78 if let Some(error) = JsError::from(result) {
79 Err(error)
80 } else {
81 Ok(read_string_and_destroy(result).unwrap_or_default())
82 }
83 }
84}
85
86impl Default for JsContext {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91
92impl Drop for JsContext {
93 fn drop(&mut self) {
94 unsafe { destroy(self.rid) }
95 }
96}
97
98pub struct WebView {
102 rid: Rid,
103}
104
105impl WebView {
106 pub fn new() -> Self {
108 let rid = unsafe { webview_create() };
109 Self { rid }
110 }
111
112 pub fn load(&self, request: Request) -> Result<(), JsError> {
114 let request_descriptor = request.rid;
115 let result = unsafe { webview_load(self.rid, request_descriptor) };
116 if let Some(error) = JsError::from(result) {
117 Err(error)
118 } else {
119 Ok(())
120 }
121 }
122
123 pub fn load_blocking(&self, request: Request) -> Result<(), JsError> {
125 self.load(request)?;
126 self.wait_for_load();
127 Ok(())
128 }
129
130 pub fn load_html(&self, html: &str, base_url: Option<&str>) -> Result<(), JsError> {
132 let html_bytes = html.as_bytes();
133 let url_bytes = base_url.map(|s| s.as_bytes()).unwrap_or_default();
134 let result = unsafe {
135 webview_load_html(
136 self.rid,
137 html_bytes.as_ptr(),
138 html_bytes.len(),
139 url_bytes.as_ptr(),
140 url_bytes.len(),
141 )
142 };
143 if let Some(error) = JsError::from(result) {
144 Err(error)
145 } else {
146 Ok(())
147 }
148 }
149
150 pub fn load_html_blocking(&self, html: &str, base_url: Option<&str>) -> Result<(), JsError> {
152 self.load_html(html, base_url)?;
153 self.wait_for_load();
154 Ok(())
155 }
156
157 pub fn wait_for_load(&self) {
159 unsafe { webview_wait_for_load(self.rid) };
160 }
161
162 pub fn eval(&self, js: &str) -> Result<String, JsError> {
164 let js_bytes = js.as_bytes();
165 let result = unsafe { webview_eval(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
166 if let Some(error) = JsError::from(result) {
167 Err(error)
168 } else {
169 Ok(read_string_and_destroy(result).unwrap_or_default())
170 }
171 }
172}
173
174impl Default for WebView {
175 fn default() -> Self {
176 Self::new()
177 }
178}
179
180impl Drop for WebView {
181 fn drop(&mut self) {
182 unsafe { destroy(self.rid) }
183 }
184}