1use super::{
3 FFIResult, Rid,
4 net::Request,
5 std::{destroy, read_string_and_destroy},
6};
7use crate::{
8 AidokuError,
9 alloc::{String, Vec},
10 imports::std::read,
11};
12
13#[link(wasm_import_module = "js")]
14unsafe extern "C" {
15 fn context_create() -> Rid;
16 fn context_eval(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
17 fn context_eval_async(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
18 fn context_get(context: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
19
20 fn webview_create() -> Rid;
21 fn webview_set_rule_list(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
22 fn webview_load(webview: Rid, request: Rid) -> FFIResult;
23 fn webview_load_html(
24 webview: Rid,
25 string_ptr: *const u8,
26 len: usize,
27 url_ptr: *const u8,
28 url_len: usize,
29 ) -> FFIResult;
30 fn webview_wait_for_load(webview: Rid) -> FFIResult;
31 fn webview_eval(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
32 fn webview_eval_async(webview: Rid, string_ptr: *const u8, len: usize) -> FFIResult;
33 fn webview_add_user_script(
34 webview: Rid,
35 string_ptr: *const u8,
36 len: usize,
37 at_document_end: bool,
38 for_main_frame_only: bool,
39 ) -> FFIResult;
40 fn webview_get_cookies(webview: Rid) -> FFIResult;
41 fn webview_delete_cookie(
42 webview: Rid,
43 name_ptr: *const u8,
44 name_len: usize,
45 value_ptr: *const u8,
46 value_len: usize,
47 domain_ptr: *const u8,
48 domain_len: usize,
49 ) -> FFIResult;
50}
51
52#[derive(PartialEq, Eq, Debug, Clone)]
54pub enum JsError {
55 MissingResult,
56 InvalidContext,
57 InvalidString,
58 InvalidHandler,
59 InvalidRequest,
60 InvalidRuleList,
61 FailedEncoding,
62 MissingCookie,
63}
64
65impl JsError {
66 fn from(value: FFIResult) -> Option<Self> {
67 match value {
68 -1 => Some(Self::MissingResult),
69 -2 => Some(Self::InvalidContext),
70 -3 => Some(Self::InvalidString),
71 -4 => Some(Self::InvalidHandler),
72 -5 => Some(Self::InvalidRequest),
73 -6 => Some(Self::InvalidRuleList),
74 -7 => Some(Self::FailedEncoding),
75 -8 => Some(Self::MissingCookie),
76 _ => None,
77 }
78 }
79}
80
81pub struct JsContext {
83 rid: Rid,
84}
85
86impl JsContext {
87 pub fn new() -> Self {
89 let rid = unsafe { context_create() };
90 Self { rid }
91 }
92
93 pub fn eval(&self, js: &str) -> Result<String, JsError> {
95 let js_bytes = js.as_bytes();
96 let result = unsafe { context_eval(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
97 if let Some(error) = JsError::from(result) {
98 Err(error)
99 } else {
100 Ok(read_string_and_destroy(result).unwrap_or_default())
101 }
102 }
103
104 pub fn eval_async(&self, js: &str) -> Result<String, JsError> {
106 let js_bytes = js.as_bytes();
107 let result = unsafe { context_eval_async(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
108 if let Some(error) = JsError::from(result) {
109 Err(error)
110 } else {
111 Ok(read_string_and_destroy(result).unwrap_or_default())
112 }
113 }
114
115 pub fn get(&self, variable: &str) -> Result<String, JsError> {
117 let var_bytes = variable.as_bytes();
118 let result = unsafe { context_get(self.rid, var_bytes.as_ptr(), var_bytes.len()) };
119 if let Some(error) = JsError::from(result) {
120 Err(error)
121 } else {
122 Ok(read_string_and_destroy(result).unwrap_or_default())
123 }
124 }
125}
126
127impl Default for JsContext {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl Drop for JsContext {
134 fn drop(&mut self) {
135 unsafe { destroy(self.rid) }
136 }
137}
138
139pub struct WebView {
143 rid: Rid,
144}
145
146impl WebView {
147 pub fn new() -> Self {
149 let rid = unsafe { webview_create() };
150 Self { rid }
151 }
152
153 pub fn set_rule_list(&self, json: &str) -> Result<(), JsError> {
158 let json_bytes = json.as_bytes();
159 let result =
160 unsafe { webview_set_rule_list(self.rid, json_bytes.as_ptr(), json_bytes.len()) };
161 if let Some(error) = JsError::from(result) {
162 Err(error)
163 } else {
164 Ok(())
165 }
166 }
167
168 pub fn load(&self, request: Request) -> Result<(), JsError> {
170 let request_descriptor = request.rid;
171 let result = unsafe { webview_load(self.rid, request_descriptor) };
172 if let Some(error) = JsError::from(result) {
173 Err(error)
174 } else {
175 Ok(())
176 }
177 }
178
179 pub fn load_blocking(&self, request: Request) -> Result<(), JsError> {
181 self.load(request)?;
182 self.wait_for_load();
183 Ok(())
184 }
185
186 pub fn load_html(&self, html: &str, base_url: Option<&str>) -> Result<(), JsError> {
188 let html_bytes = html.as_bytes();
189 let url_bytes = base_url.map(|s| s.as_bytes()).unwrap_or_default();
190 let result = unsafe {
191 webview_load_html(
192 self.rid,
193 html_bytes.as_ptr(),
194 html_bytes.len(),
195 url_bytes.as_ptr(),
196 url_bytes.len(),
197 )
198 };
199 if let Some(error) = JsError::from(result) {
200 Err(error)
201 } else {
202 Ok(())
203 }
204 }
205
206 pub fn load_html_blocking(&self, html: &str, base_url: Option<&str>) -> Result<(), JsError> {
208 self.load_html(html, base_url)?;
209 self.wait_for_load();
210 Ok(())
211 }
212
213 pub fn wait_for_load(&self) {
215 unsafe { webview_wait_for_load(self.rid) };
216 }
217
218 pub fn eval(&self, js: &str) -> Result<String, JsError> {
220 let js_bytes = js.as_bytes();
221 let result = unsafe { webview_eval(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
222 if let Some(error) = JsError::from(result) {
223 Err(error)
224 } else {
225 Ok(read_string_and_destroy(result).unwrap_or_default())
226 }
227 }
228
229 pub fn eval_async(&self, js: &str) -> Result<String, JsError> {
231 let js_bytes = js.as_bytes();
232 let result = unsafe { webview_eval_async(self.rid, js_bytes.as_ptr(), js_bytes.len()) };
233 if let Some(error) = JsError::from(result) {
234 Err(error)
235 } else {
236 Ok(read_string_and_destroy(result).unwrap_or_default())
237 }
238 }
239
240 pub fn add_user_script(&self, script: WebViewUserScript) -> Result<(), JsError> {
242 let source_bytes = script.source.as_bytes();
243 let result = unsafe {
244 webview_add_user_script(
245 self.rid,
246 source_bytes.as_ptr(),
247 source_bytes.len(),
248 script.at_document_end,
249 script.for_main_frame_only,
250 )
251 };
252 if let Some(error) = JsError::from(result) {
253 Err(error)
254 } else {
255 Ok(())
256 }
257 }
258
259 pub fn get_cookies(&self) -> Result<Vec<Cookie>, AidokuError> {
261 let result = unsafe { webview_get_cookies(self.rid) };
262 if let Some(error) = JsError::from(result) {
263 Err(error.into())
264 } else {
265 read(result)
266 }
267 }
268
269 pub fn delete_cookie(&self, cookie: Cookie) -> Result<(), JsError> {
271 let result = unsafe {
272 webview_delete_cookie(
273 self.rid,
274 cookie.name.as_ptr(),
275 cookie.name.len(),
276 cookie.value.as_ptr(),
277 cookie.value.len(),
278 cookie.domain.as_ptr(),
279 cookie.domain.len(),
280 )
281 };
282 if let Some(error) = JsError::from(result) {
283 Err(error)
284 } else {
285 Ok(())
286 }
287 }
288
289 pub fn delete_all_cookies(&self) -> Result<(), JsError> {
291 let result = unsafe {
292 webview_delete_cookie(
293 self.rid,
294 core::ptr::null(),
295 -1i32 as usize,
296 core::ptr::null(),
297 0,
298 core::ptr::null(),
299 0,
300 )
301 };
302 if let Some(error) = JsError::from(result) {
303 Err(error)
304 } else {
305 Ok(())
306 }
307 }
308}
309
310impl Default for WebView {
311 fn default() -> Self {
312 Self::new()
313 }
314}
315
316impl Drop for WebView {
317 fn drop(&mut self) {
318 unsafe { destroy(self.rid) }
319 }
320}
321
322#[derive(Default)]
323pub struct WebViewUserScript {
325 pub source: String,
327 pub at_document_end: bool,
329 pub for_main_frame_only: bool,
331}
332
333impl WebViewUserScript {
334 pub fn new(source: String) -> Self {
336 Self {
337 source,
338 ..Default::default()
339 }
340 }
341}
342
343#[derive(Debug, serde::Deserialize)]
344pub struct Cookie {
345 pub name: String,
346 pub value: String,
347 pub expires_date: Option<i64>,
348 pub domain: String,
349 pub path: String,
350 pub is_secure: bool,
351 pub is_http_only: bool,
352}