ul_next/javascript/
context.rs

1use core::fmt;
2use std::sync::Arc;
3
4use crate::Library;
5
6use super::{JSObject, JSString, JSValue};
7
8/// JavaScript execution context.
9///
10/// This struct represents a JavaScript execution context. It is the top-level
11/// object for evaluating JavaScript code.
12///
13/// Can be obtained initially from [`View::lock_js_context`](crate::view::View::lock_js_context).
14pub struct JSContext {
15    pub(crate) internal: ul_sys::JSContextRef,
16    pub(crate) lib: Arc<Library>,
17}
18
19impl JSContext {
20    pub(crate) fn copy_from_raw(lib: Arc<Library>, ctx: ul_sys::JSContextRef) -> Self {
21        assert!(!ctx.is_null());
22
23        let ctx = unsafe { lib.ultralight().JSGlobalContextRetain(ctx as _) };
24
25        Self { internal: ctx, lib }
26    }
27
28    /// Create a new JavaScript execution context.
29    pub fn new(lib: Arc<Library>) -> Self {
30        let ctx = unsafe { lib.ultralight().JSGlobalContextCreate(std::ptr::null_mut()) };
31
32        Self { internal: ctx, lib }
33    }
34
35    /// Get the global object for this context.
36    pub fn global_object(&self) -> JSObject {
37        JSObject::copy_from_raw(self, unsafe {
38            self.lib
39                .ultralight()
40                .JSContextGetGlobalObject(self.internal)
41        })
42    }
43
44    /// Get the global context for this context.
45    pub fn global_context(&self) -> JSContext {
46        JSContext::copy_from_raw(self.lib.clone(), unsafe {
47            self.lib
48                .ultralight()
49                .JSContextGetGlobalContext(self.internal)
50        })
51    }
52
53    /// Get the name of this context.
54    ///
55    /// A JSGlobalContext's name is exposed when inspecting the context to
56    /// make it easier to identify the context you would like to inspect.
57    ///
58    /// The context may not have a name, in which case this method will return [`None`].
59    pub fn name(&self) -> Option<JSString> {
60        let name = unsafe {
61            self.lib
62                .ultralight()
63                .JSGlobalContextCopyName(self.internal as _)
64        };
65        if name.is_null() {
66            return None;
67        }
68        Some(JSString::from_raw(self.lib.clone(), name))
69    }
70
71    /// Gets whether the context is inspectable in Web Inspector.
72    pub fn is_inspectable(&self) -> bool {
73        unsafe {
74            self.lib
75                .ultralight()
76                .JSGlobalContextIsInspectable(self.internal as _)
77        }
78    }
79
80    /// Evaluate a JavaScript script in this context.
81    ///
82    /// If an exception is thrown during evaluation, it will be returned as an
83    /// [`Err`] value, otherwise the result of the script evaluation will be
84    /// returned as an [`Ok`] value.
85    pub fn evaluate_script(&self, script: &str) -> Result<JSValue, JSValue> {
86        let script = JSString::new(self.lib.clone(), script);
87        let mut exception = std::ptr::null();
88        let ret = unsafe {
89            self.lib.ultralight().JSEvaluateScript(
90                self.internal,
91                script.internal,
92                std::ptr::null_mut(),
93                std::ptr::null_mut(),
94                0,
95                &mut exception,
96            )
97        };
98
99        if !exception.is_null() {
100            Err(JSValue::copy_from_raw(self, exception))
101        } else if ret.is_null() {
102            Err(JSValue::new_string(self, "Failed to evaluate script"))
103        } else {
104            Ok(JSValue::copy_from_raw(self, ret))
105        }
106    }
107
108    /// Checks for syntax errors in a string of JavaScript.
109    ///
110    /// `true` if the script is syntactically correct, otherwise `false`.
111    pub fn check_script_syntax(&self, script: &str) -> Result<bool, JSValue> {
112        let script = JSString::new(self.lib.clone(), script);
113        let mut exception = std::ptr::null();
114        let ret = unsafe {
115            self.lib.ultralight().JSCheckScriptSyntax(
116                self.internal,
117                script.internal,
118                std::ptr::null_mut(),
119                0,
120                &mut exception,
121            )
122        };
123
124        if !exception.is_null() {
125            return Err(JSValue::copy_from_raw(self, exception));
126        }
127
128        Ok(ret)
129    }
130
131    /// Performs a JavaScript garbage collection.
132    ///
133    /// JavaScript values that are on the machine stack, in a register,
134    /// protected by `JSValueProtect`, set as the global object of an execution context,
135    /// or reachable from any such value will not be collected.
136    ///
137    /// During JavaScript execution, you are not required to call this function; the
138    /// JavaScript engine will garbage collect as needed. JavaScript values created
139    /// within a context group are automatically destroyed when the last reference
140    /// to the context group is released.
141    pub fn garbage_collect(&self) {
142        unsafe {
143            self.lib.ultralight().JSGarbageCollect(self.internal);
144        }
145    }
146}
147
148impl fmt::Debug for JSContext {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.debug_struct("JSContext")
151            .field("name", &self.name())
152            .finish()
153    }
154}
155
156impl Clone for JSContext {
157    fn clone(&self) -> Self {
158        Self::copy_from_raw(self.lib.clone(), self.internal)
159    }
160}
161
162impl Drop for JSContext {
163    fn drop(&mut self) {
164        unsafe {
165            self.lib
166                .ultralight()
167                .JSGlobalContextRelease(self.internal as _);
168        }
169    }
170}