use super::BrowserJsRuntime;
impl BrowserJsRuntime {
pub fn execute_script(&mut self, _name: &str, source: &str) -> Result<(), String> {
self.runtime
.execute_script("<script>", source.to_string())
.map_err(|e| format!("JS error: {}", e))?;
Ok(())
}
pub fn execute_script_guarded(&mut self, _name: &str, source: &str) -> Result<(), String> {
self.execute_script_with_timeout(source, std::time::Duration::from_secs(5))
}
pub fn execute_script_with_timeout(&mut self, source: &str, timeout: std::time::Duration) -> Result<(), String> {
if timeout.is_zero() {
self.runtime
.execute_script("<script>", source.to_string())
.map_err(|e| format!("JS error: {}", e))?;
return Ok(());
}
let isolate_handle = self.runtime.v8_isolate().thread_safe_handle();
let pair = std::sync::Arc::new((std::sync::Mutex::new(false), std::sync::Condvar::new()));
let pair_clone = pair.clone();
let watchdog = std::thread::spawn(move || {
let (lock, cvar) = &*pair_clone;
let mut cancelled = lock.lock().unwrap();
let deadline = std::time::Instant::now() + timeout;
loop {
let remaining = deadline.saturating_duration_since(std::time::Instant::now());
if remaining.is_zero() {
isolate_handle.terminate_execution();
return;
}
let result = cvar.wait_timeout(cancelled, remaining).unwrap();
cancelled = result.0;
if *cancelled {
return;
}
}
});
let result = self.runtime.execute_script("<script>", source.to_string());
{
let (lock, cvar) = &*pair;
let mut cancelled = lock.lock().unwrap();
*cancelled = true;
cvar.notify_one();
}
let _ = watchdog.join();
self.runtime.v8_isolate().cancel_terminate_execution();
match result {
Ok(_) => Ok(()),
Err(e) => {
let msg = e.to_string();
if msg.contains("Uncaught Error: execution terminated") {
tracing::warn!("Script killed after {}s timeout", timeout.as_secs());
Ok(())
} else {
Err(format!("JS error: {}", msg))
}
}
}
}
pub async fn run_event_loop(&mut self) -> Result<(), String> {
self.runtime
.run_event_loop(deno_core::PollEventLoopOptions::default())
.await
.map_err(|e| format!("Event loop error: {}", e))
}
pub async fn resolve_promises(&mut self) {
let _ = tokio::time::timeout(
tokio::time::Duration::from_secs(5),
self.runtime.run_event_loop(deno_core::PollEventLoopOptions::default()),
)
.await;
}
}