Skip to main content

blitz_script/
engine.rs

1//! Execution control seam for script backends.
2
3use std::ops::{Deref, DerefMut};
4
5use blitz_dom::NodeId;
6use blitz_traits::events::{DomEvent, EventState};
7use url::Url;
8use web_time::{Duration, Instant};
9
10pub use crate::job_budget::{JobBudget, JobSlice};
11use crate::runtime::BoaScriptEngine;
12use crate::runtime::{DiagnosticBatch, DiagnosticEntry};
13
14/// Operations used by the document and event driver to enter script execution.
15///
16/// JobScope is an owned guard, so a checkpoint can share the outer budget
17/// without retaining a borrow of the engine during a host callback.
18pub trait ScriptEngine {
19    type JobScope;
20    fn eval(&mut self, code: &str, description: &str);
21    fn eval_at(&mut self, code: &str, url: Option<&Url>, description: &str);
22    fn eval_internal(&mut self, code: &str, description: &str);
23    fn eval_module(&mut self, code: &str, url: Option<&Url>, description: &str);
24    fn eval_json(&mut self, code: &str, description: &str) -> Result<serde_json::Value, String>;
25    fn set_import_map(&self, json: &str, base_url: Option<&Url>);
26    fn poll_module_evaluations(&mut self);
27    fn has_pending_module_evaluations(&self) -> bool;
28    fn has_pending_module_loads(&self) -> bool;
29    fn begin_job_slice(&self) -> Self::JobScope;
30    fn job_budget(&self) -> Duration;
31    fn set_job_budget(&self, duration: Duration);
32    fn pending_task_count(&self) -> u64;
33    fn job_slice_exhausted(&self) -> bool;
34    fn jobs_remain(&self) -> bool;
35    fn run_jobs(&mut self, description: &str);
36    fn run_jobs_with_progress(&mut self, description: &str) -> bool;
37    fn next_timer_deadline(&self) -> Option<Instant>;
38    fn run_due_timers(&mut self, profiling: bool) -> bool;
39    fn dispatch_dom_event(
40        &mut self,
41        chain: &[NodeId],
42        event: &DomEvent,
43        event_state: &mut EventState,
44        profiling: bool,
45    ) -> bool;
46    fn dispatch_document_event(&mut self, name: &str) -> bool;
47    fn dispatch_node_event(&mut self, node_id: NodeId, name: &str) -> bool;
48    fn dispatch_window_event(&mut self, name: &str) -> bool;
49    fn set_document_ready_state(&mut self, next: &'static str) -> bool;
50    fn sweep_detached_nodes(&mut self);
51    fn poll_domc(&mut self) -> bool;
52    fn console_entries_after(&self, sequence: u64) -> Vec<DiagnosticEntry>;
53    fn runtime_errors_after(&self, sequence: u64) -> Vec<DiagnosticEntry>;
54    fn diagnostic_batch(
55        &self,
56        console_after: u64,
57        errors_after: u64,
58        limit: usize,
59    ) -> DiagnosticBatch;
60}
61
62/// Statically dispatched owner of a script backend.
63///
64/// Deref is the phase-zero bridge for the existing DOM bootstrap and public
65/// Boa context callback. New execution entry points belong on ScriptEngine.
66/// Backend-specific document bootstrap uses the explicit backend accessors.
67pub struct Engine<E: ScriptEngine> {
68    backend: E,
69}
70
71impl<E: ScriptEngine> Engine<E> {
72    pub fn new(backend: E) -> Self {
73        Self { backend }
74    }
75
76    pub fn backend(&self) -> &E {
77        &self.backend
78    }
79
80    pub fn backend_mut(&mut self) -> &mut E {
81        &mut self.backend
82    }
83
84    pub fn eval(&mut self, code: &str, description: &str) {
85        self.backend.eval(code, description)
86    }
87
88    pub fn eval_at(&mut self, code: &str, url: Option<&Url>, description: &str) {
89        self.backend.eval_at(code, url, description)
90    }
91
92    pub fn eval_internal(&mut self, code: &str, description: &str) {
93        self.backend.eval_internal(code, description)
94    }
95
96    pub fn eval_module(&mut self, code: &str, url: Option<&Url>, description: &str) {
97        self.backend.eval_module(code, url, description)
98    }
99
100    pub fn eval_json(
101        &mut self,
102        code: &str,
103        description: &str,
104    ) -> Result<serde_json::Value, String> {
105        self.backend.eval_json(code, description)
106    }
107
108    pub fn set_import_map(&self, json: &str, base_url: Option<&Url>) {
109        self.backend.set_import_map(json, base_url)
110    }
111
112    pub fn poll_module_evaluations(&mut self) {
113        self.backend.poll_module_evaluations()
114    }
115
116    pub fn has_pending_module_evaluations(&self) -> bool {
117        self.backend.has_pending_module_evaluations()
118    }
119
120    pub fn has_pending_module_loads(&self) -> bool {
121        self.backend.has_pending_module_loads()
122    }
123
124    pub fn begin_job_slice(&self) -> E::JobScope {
125        self.backend.begin_job_slice()
126    }
127
128    pub fn job_budget(&self) -> Duration {
129        self.backend.job_budget()
130    }
131
132    pub fn set_job_budget(&self, duration: Duration) {
133        self.backend.set_job_budget(duration)
134    }
135
136    pub fn pending_task_count(&self) -> u64 {
137        self.backend.pending_task_count()
138    }
139
140    pub fn job_slice_exhausted(&self) -> bool {
141        self.backend.job_slice_exhausted()
142    }
143
144    pub fn jobs_remain(&self) -> bool {
145        self.backend.jobs_remain()
146    }
147
148    pub fn run_jobs(&mut self, description: &str) {
149        self.backend.run_jobs(description)
150    }
151
152    pub fn run_jobs_with_progress(&mut self, description: &str) -> bool {
153        self.backend.run_jobs_with_progress(description)
154    }
155
156    pub fn next_timer_deadline(&self) -> Option<Instant> {
157        self.backend.next_timer_deadline()
158    }
159
160    pub fn run_due_timers(&mut self, profiling: bool) -> bool {
161        self.backend.run_due_timers(profiling)
162    }
163
164    pub fn dispatch_dom_event(
165        &mut self,
166        chain: &[NodeId],
167        event: &DomEvent,
168        event_state: &mut EventState,
169        profiling: bool,
170    ) -> bool {
171        self.backend
172            .dispatch_dom_event(chain, event, event_state, profiling)
173    }
174
175    pub fn dispatch_document_event(&mut self, name: &str) -> bool {
176        self.backend.dispatch_document_event(name)
177    }
178
179    pub fn dispatch_node_event(&mut self, node_id: NodeId, name: &str) -> bool {
180        self.backend.dispatch_node_event(node_id, name)
181    }
182
183    pub fn dispatch_window_event(&mut self, name: &str) -> bool {
184        self.backend.dispatch_window_event(name)
185    }
186
187    pub fn set_document_ready_state(&mut self, next: &'static str) -> bool {
188        self.backend.set_document_ready_state(next)
189    }
190
191    pub fn sweep_detached_nodes(&mut self) {
192        self.backend.sweep_detached_nodes()
193    }
194
195    pub fn poll_domc(&mut self) -> bool {
196        self.backend.poll_domc()
197    }
198
199    pub fn console_entries_after(&self, sequence: u64) -> Vec<DiagnosticEntry> {
200        self.backend.console_entries_after(sequence)
201    }
202
203    pub fn runtime_errors_after(&self, sequence: u64) -> Vec<DiagnosticEntry> {
204        self.backend.runtime_errors_after(sequence)
205    }
206
207    pub fn diagnostic_batch(
208        &self,
209        console_after: u64,
210        errors_after: u64,
211        limit: usize,
212    ) -> DiagnosticBatch {
213        self.backend
214            .diagnostic_batch(console_after, errors_after, limit)
215    }
216}
217
218impl<E: ScriptEngine> Deref for Engine<E> {
219    type Target = E;
220
221    fn deref(&self) -> &E {
222        &self.backend
223    }
224}
225
226impl<E: ScriptEngine> DerefMut for Engine<E> {
227    fn deref_mut(&mut self) -> &mut E {
228        &mut self.backend
229    }
230}
231
232impl ScriptEngine for BoaScriptEngine {
233    type JobScope = JobSlice;
234
235    fn eval(&mut self, code: &str, description: &str) {
236        BoaScriptEngine::eval(self, code, description)
237    }
238
239    fn eval_at(&mut self, code: &str, url: Option<&Url>, description: &str) {
240        BoaScriptEngine::eval_at(self, code, url, description)
241    }
242
243    fn eval_internal(&mut self, code: &str, description: &str) {
244        BoaScriptEngine::eval_internal(self, code, description)
245    }
246
247    fn eval_module(&mut self, code: &str, url: Option<&Url>, description: &str) {
248        BoaScriptEngine::eval_module(self, code, url, description)
249    }
250
251    fn eval_json(&mut self, code: &str, description: &str) -> Result<serde_json::Value, String> {
252        BoaScriptEngine::eval_json(self, code, description)
253    }
254
255    fn set_import_map(&self, json: &str, base_url: Option<&Url>) {
256        BoaScriptEngine::set_import_map(self, json, base_url)
257    }
258
259    fn poll_module_evaluations(&mut self) {
260        BoaScriptEngine::poll_module_evaluations(self)
261    }
262
263    fn has_pending_module_evaluations(&self) -> bool {
264        BoaScriptEngine::has_pending_module_evaluations(self)
265    }
266
267    fn has_pending_module_loads(&self) -> bool {
268        BoaScriptEngine::has_pending_module_loads(self)
269    }
270
271    fn begin_job_slice(&self) -> Self::JobScope {
272        BoaScriptEngine::begin_job_slice(self)
273    }
274
275    fn job_budget(&self) -> Duration {
276        BoaScriptEngine::job_budget(self)
277    }
278
279    fn set_job_budget(&self, duration: Duration) {
280        BoaScriptEngine::set_job_budget(self, duration)
281    }
282
283    fn pending_task_count(&self) -> u64 {
284        BoaScriptEngine::pending_task_count(self)
285    }
286
287    fn job_slice_exhausted(&self) -> bool {
288        BoaScriptEngine::job_slice_exhausted(self)
289    }
290
291    fn jobs_remain(&self) -> bool {
292        BoaScriptEngine::jobs_remain(self)
293    }
294
295    fn run_jobs(&mut self, description: &str) {
296        BoaScriptEngine::run_jobs(self, description)
297    }
298
299    fn run_jobs_with_progress(&mut self, description: &str) -> bool {
300        BoaScriptEngine::run_jobs_with_progress(self, description)
301    }
302
303    fn next_timer_deadline(&self) -> Option<Instant> {
304        BoaScriptEngine::next_timer_deadline(self)
305    }
306
307    fn run_due_timers(&mut self, profiling: bool) -> bool {
308        BoaScriptEngine::run_due_timers(self, profiling)
309    }
310
311    fn dispatch_dom_event(
312        &mut self,
313        chain: &[NodeId],
314        event: &DomEvent,
315        event_state: &mut EventState,
316        profiling: bool,
317    ) -> bool {
318        BoaScriptEngine::dispatch_dom_event(self, chain, event, event_state, profiling)
319    }
320
321    fn dispatch_document_event(&mut self, name: &str) -> bool {
322        BoaScriptEngine::dispatch_document_event(self, name)
323    }
324
325    fn dispatch_node_event(&mut self, node_id: NodeId, name: &str) -> bool {
326        BoaScriptEngine::dispatch_node_event(self, node_id, name)
327    }
328
329    fn dispatch_window_event(&mut self, name: &str) -> bool {
330        BoaScriptEngine::dispatch_window_event(self, name)
331    }
332
333    fn set_document_ready_state(&mut self, next: &'static str) -> bool {
334        BoaScriptEngine::set_document_ready_state(self, next)
335    }
336
337    fn sweep_detached_nodes(&mut self) {
338        BoaScriptEngine::sweep_detached_nodes(self)
339    }
340
341    fn poll_domc(&mut self) -> bool {
342        BoaScriptEngine::poll_domc(self)
343    }
344
345    fn console_entries_after(&self, sequence: u64) -> Vec<DiagnosticEntry> {
346        BoaScriptEngine::console_entries_after(self, sequence)
347    }
348
349    fn runtime_errors_after(&self, sequence: u64) -> Vec<DiagnosticEntry> {
350        BoaScriptEngine::runtime_errors_after(self, sequence)
351    }
352
353    fn diagnostic_batch(
354        &self,
355        console_after: u64,
356        errors_after: u64,
357        limit: usize,
358    ) -> DiagnosticBatch {
359        BoaScriptEngine::diagnostic_batch(self, console_after, errors_after, limit)
360    }
361}