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_eval_async(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
14 fn context_get(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
15
16 fn webview_create() -> Rid;
17 fn webview_set_rule_list(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
18 fn webview_load(webview: Rid, request: Rid) -> FFIResult;
19 fn webview_load_html(
20 webview: Rid,
21 string_ptr: *const u8,
22 len: usize,
23 url_ptr: *const u8,
24 url_len: usize,
25 ) -> FFIResult;
26 fn webview_wait_for_load(webview: Rid) -> FFIResult;
27 fn webview_eval(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
28 fn webview_eval_async(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
29 fn webview_add_user_script(
30 webview: Rid,
31 string_ptr: *const u8,
32 len: usize,
33 at_document_end: bool,
34 for_main_frame_only: bool,
35 ) -> FFIResult;
36}
37
38#[derive(PartialEq, Eq, Debug, Clone)]
40pub enum JsError {
41 MissingResult,
42 InvalidContext,
43 InvalidString,
44 InvalidHandler,
45 InvalidRequest,
46 InvalidRuleList,
47}
48
49impl JsError {
50 fn from(value: FFIResult) -> Option<Self> {
51 match value {
52 -1 => Some(Self::MissingResult),
53 -2 => Some(Self::InvalidContext),
54 -3 => Some(Self::InvalidString),
55 -4 => Some(Self::InvalidHandler),
56 -5 => Some(Self::InvalidRequest),
57 -6 => Some(Self::InvalidRuleList),
58 _ => None,
59 }
60 }
61}
62
63pub struct JsContext {
65 rid: Rid,
66}
67
68impl JsContext {
69 pub fn new() -> Self {
71 let rid = unsafe { context_create() };
72 Self { rid }
73 }
74
75 pub fn eval(&self, js: &str) -> Result<String, JsError> {
77 let js_bytes = js.as_bytes();
78 let result = unsafe { context_eval(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
79 if let Some(error) = JsError::from(result) {
80 Err(error)
81 } else {
82 Ok(read_string_and_destroy(result).unwrap_or_default())
83 }
84 }
85
86 pub fn eval_async(&self, js: &str) -> Result<String, JsError> {
88 let js_bytes = js.as_bytes();
89 let result = unsafe { context_eval_async(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
90 if let Some(error) = JsError::from(result) {
91 Err(error)
92 } else {
93 Ok(read_string_and_destroy(result).unwrap_or_default())
94 }
95 }
96
97 pub fn get(&self, variable: &str) -> Result<String, JsError> {
99 let var_bytes = variable.as_bytes();
100 let result = unsafe { context_get(self.rid, var_bytes.as_ptr(), var_bytes.len()) };
101 if let Some(error) = JsError::from(result) {
102 Err(error)
103 } else {
104 Ok(read_string_and_destroy(result).unwrap_or_default())
105 }
106 }
107}
108
109impl Default for JsContext {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl Drop for JsContext {
116 fn drop(&mut self) {
117 unsafe { destroy(self.rid) }
118 }
119}
120
121pub struct WebView {
125 rid: Rid,
126}
127
128impl WebView {
129 pub fn new() -> Self {
131 let rid = unsafe { webview_create() };
132 Self { rid }
133 }
134
135 pub fn set_rule_list(&self, json: &str) -> Result<(), JsError> {
140 let json_bytes = json.as_bytes();
141 let result =
142 unsafe { webview_set_rule_list(self.rid, json_bytes.as_ptr(), json_bytes.len()) };
143 if let Some(error) = JsError::from(result) {
144 Err(error)
145 } else {
146 Ok(())
147 }
148 }
149
150 pub fn load(&self, request: Request) -> Result<(), JsError> {
152 let request_descriptor = request.rid;
153 let result = unsafe { webview_load(self.rid, request_descriptor) };
154 if let Some(error) = JsError::from(result) {
155 Err(error)
156 } else {
157 Ok(())
158 }
159 }
160
161 pub fn load_blocking(&self, request: Request) -> Result<(), JsError> {
163 self.load(request)?;
164 self.wait_for_load();
165 Ok(())
166 }
167
168 pub fn load_html(&self, html: &str, base_url: Option<&str>) -> Result<(), JsError> {
170 let html_bytes = html.as_bytes();
171 let url_bytes = base_url.map(|s| s.as_bytes()).unwrap_or_default();
172 let result = unsafe {
173 webview_load_html(
174 self.rid,
175 html_bytes.as_ptr(),
176 html_bytes.len(),
177 url_bytes.as_ptr(),
178 url_bytes.len(),
179 )
180 };
181 if let Some(error) = JsError::from(result) {
182 Err(error)
183 } else {
184 Ok(())
185 }
186 }
187
188 pub fn load_html_blocking(&self, html: &str, base_url: Option<&str>) -> Result<(), JsError> {
190 self.load_html(html, base_url)?;
191 self.wait_for_load();
192 Ok(())
193 }
194
195 pub fn wait_for_load(&self) {
197 unsafe { webview_wait_for_load(self.rid) };
198 }
199
200 pub fn eval(&self, js: &str) -> Result<String, JsError> {
202 let js_bytes = js.as_bytes();
203 let result = unsafe { webview_eval(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
204 if let Some(error) = JsError::from(result) {
205 Err(error)
206 } else {
207 Ok(read_string_and_destroy(result).unwrap_or_default())
208 }
209 }
210
211 pub fn eval_async(&self, js: &str) -> Result<String, JsError> {
213 let js_bytes = js.as_bytes();
214 let result = unsafe { webview_eval_async(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
215 if let Some(error) = JsError::from(result) {
216 Err(error)
217 } else {
218 Ok(read_string_and_destroy(result).unwrap_or_default())
219 }
220 }
221
222 pub fn add_user_script(&self, script: WebViewUserScript) -> Result<(), JsError> {
224 let source_bytes = script.source.as_bytes();
225 let result = unsafe {
226 webview_add_user_script(
227 self.rid,
228 source_bytes.as_ptr(),
229 source_bytes.len(),
230 script.at_document_end,
231 script.for_main_frame_only,
232 )
233 };
234 if let Some(error) = JsError::from(result) {
235 Err(error)
236 } else {
237 Ok(())
238 }
239 }
240}
241
242impl Default for WebView {
243 fn default() -> Self {
244 Self::new()
245 }
246}
247
248impl Drop for WebView {
249 fn drop(&mut self) {
250 unsafe { destroy(self.rid) }
251 }
252}
253
254#[derive(Default)]
255pub struct WebViewUserScript {
257 pub source: String,
259 pub at_document_end: bool,
261 pub for_main_frame_only: bool,
263}
264
265impl WebViewUserScript {
266 pub fn new(source: String) -> Self {
268 Self {
269 source,
270 ..Default::default()
271 }
272 }
273}