Skip to main content

bt_script/
lib.rs

1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::error::Error as StdError;
4use std::fmt;
5use std::rc::Rc;
6use std::sync::atomic::{AtomicU64, Ordering};
7
8mod evaluator;
9mod parser;
10mod syntax;
11
12#[derive(Clone, Debug, Default)]
13pub struct ScriptParser;
14
15#[derive(Clone, Debug, Default)]
16pub struct Evaluator;
17
18#[derive(Clone, Debug, Default)]
19pub struct ScriptHeap;
20
21#[derive(Clone, Debug, Default)]
22pub struct GlobalEnvironment;
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum ScriptErrorKind {
26    Parse,
27    Runtime,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct ElementHandle(u64);
32
33impl ElementHandle {
34    pub const fn new(raw: u64) -> Self {
35        Self(raw)
36    }
37
38    pub const fn raw(self) -> u64 {
39        self.0
40    }
41}
42
43#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct NodeHandle(u64);
45
46impl NodeHandle {
47    pub const fn new(raw: u64) -> Self {
48        Self(raw)
49    }
50
51    pub const fn raw(self) -> u64 {
52        self.0
53    }
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub enum ListenerTarget {
58    Window,
59    Document,
60    Element(ElementHandle),
61}
62
63#[derive(Clone, Debug, Default, PartialEq, Eq)]
64pub struct KeyboardEventInit {
65    pub key: String,
66    pub code: Option<String>,
67    pub ctrl_key: bool,
68    pub meta_key: bool,
69    pub shift_key: bool,
70    pub alt_key: bool,
71    pub repeat: bool,
72    pub is_composing: bool,
73}
74
75#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
76pub enum HtmlCollectionScope {
77    Document,
78    Element(ElementHandle),
79    Node(NodeHandle),
80}
81
82#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
83pub enum HtmlCollectionTarget {
84    Children(ElementHandle),
85    ByTagName {
86        scope: HtmlCollectionScope,
87        tag_name: String,
88    },
89    ByTagNameNs {
90        scope: HtmlCollectionScope,
91        namespace_uri: String,
92        local_name: String,
93    },
94    ByClassName {
95        scope: HtmlCollectionScope,
96        class_names: String,
97    },
98    FormElements(ElementHandle),
99    SelectOptions(ElementHandle),
100    SelectSelectedOptions(ElementHandle),
101    DocumentPlugins,
102    DocumentLinks,
103    DocumentAnchors,
104    DocumentChildren,
105    WindowFrames,
106    MapAreas(ElementHandle),
107    TableTBodies(ElementHandle),
108    TableRows(ElementHandle),
109    RowCells(ElementHandle),
110}
111
112#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
113pub enum StyleSheetListTarget {
114    Document,
115}
116
117#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
118pub enum StorageTarget {
119    Local,
120    Session,
121}
122
123#[derive(Clone, Debug, PartialEq, Eq)]
124pub struct DateValue {
125    pub epoch_ms: Option<i64>,
126}
127
128#[derive(Clone, Debug, PartialEq, Eq)]
129pub struct IntlNumberFormatValue {
130    pub locale: String,
131    pub style: String,
132    pub currency: Option<String>,
133    pub use_grouping: bool,
134    pub minimum_integer_digits: usize,
135    pub minimum_fraction_digits: Option<usize>,
136    pub maximum_fraction_digits: Option<usize>,
137    pub minimum_significant_digits: Option<usize>,
138    pub maximum_significant_digits: Option<usize>,
139}
140
141#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct IntlDateTimeFormatValue {
143    pub locale: String,
144    pub time_zone: Option<String>,
145    pub year: Option<String>,
146    pub month: Option<String>,
147    pub day: Option<String>,
148    pub hour: Option<String>,
149    pub minute: Option<String>,
150    pub second: Option<String>,
151    pub hour12: Option<bool>,
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
155pub struct IntlCollatorValue {
156    pub locale: String,
157    pub numeric: bool,
158    pub sensitivity: Option<String>,
159    pub usage: Option<String>,
160}
161
162#[derive(Clone, Debug, PartialEq, Eq)]
163pub struct MediaQueryListState {
164    media: String,
165    matches: bool,
166}
167
168impl MediaQueryListState {
169    pub fn new(media: impl Into<String>, matches: bool) -> Self {
170        Self {
171            media: media.into(),
172            matches,
173        }
174    }
175
176    pub fn media(&self) -> &str {
177        &self.media
178    }
179
180    pub fn matches(&self) -> bool {
181        self.matches
182    }
183}
184
185#[derive(Clone, Debug, PartialEq, Eq)]
186pub struct StringListState {
187    items: Vec<String>,
188}
189
190impl StringListState {
191    pub fn new(items: Vec<String>) -> Self {
192        Self { items }
193    }
194
195    pub fn length(&self) -> usize {
196        self.items.len()
197    }
198
199    pub fn item(&self, index: usize) -> Option<&str> {
200        self.items.get(index).map(String::as_str)
201    }
202
203    pub fn contains(&self, value: &str) -> bool {
204        self.items.iter().any(|item| item == value)
205    }
206
207    pub fn items(&self) -> &[String] {
208        &self.items
209    }
210}
211
212#[derive(Clone, Debug, PartialEq, Eq)]
213pub struct MimeTypeArrayState {
214    items: Vec<String>,
215}
216
217impl MimeTypeArrayState {
218    pub fn new(items: Vec<String>) -> Self {
219        Self { items }
220    }
221
222    pub fn length(&self) -> usize {
223        self.items.len()
224    }
225
226    pub fn item(&self, index: usize) -> Option<&str> {
227        self.items.get(index).map(String::as_str)
228    }
229
230    pub fn named_item(&self, name: &str) -> Option<&str> {
231        self.items
232            .iter()
233            .find(|item| item.as_str() == name)
234            .map(String::as_str)
235    }
236
237    pub fn items(&self) -> &[String] {
238        &self.items
239    }
240}
241
242#[derive(Clone, Debug, PartialEq, Eq)]
243struct AttributeState {
244    namespace_uri: Option<String>,
245    name: String,
246    value: String,
247    owner_element: Option<ElementHandle>,
248}
249
250#[derive(Clone, Debug, PartialEq)]
251pub struct AttributeHandle(Rc<RefCell<AttributeState>>);
252
253impl AttributeHandle {
254    pub fn new(
255        namespace_uri: Option<String>,
256        name: impl Into<String>,
257        value: impl Into<String>,
258        owner_element: Option<ElementHandle>,
259    ) -> Self {
260        Self(Rc::new(RefCell::new(AttributeState {
261            namespace_uri,
262            name: name.into(),
263            value: value.into(),
264            owner_element,
265        })))
266    }
267
268    pub fn namespace_uri(&self) -> Option<String> {
269        self.0.borrow().namespace_uri.clone()
270    }
271
272    pub fn name(&self) -> String {
273        self.0.borrow().name.clone()
274    }
275
276    pub fn value(&self) -> String {
277        self.0.borrow().value.clone()
278    }
279
280    pub fn set_value(&self, value: impl Into<String>) {
281        self.0.borrow_mut().value = value.into();
282    }
283
284    pub fn owner_element(&self) -> Option<ElementHandle> {
285        self.0.borrow().owner_element
286    }
287
288    pub fn set_owner_element(&self, owner_element: Option<ElementHandle>) {
289        self.0.borrow_mut().owner_element = owner_element;
290    }
291
292    pub fn local_name(&self) -> String {
293        let name = self.name();
294        name.split_once(':')
295            .map(|(_, local_name)| local_name.to_string())
296            .unwrap_or(name)
297    }
298
299    pub fn prefix(&self) -> Option<String> {
300        self.name()
301            .split_once(':')
302            .map(|(prefix, _)| prefix.to_string())
303    }
304}
305
306#[derive(Clone, Debug, PartialEq, Eq)]
307pub struct ScreenOrientationState {
308    orientation_type: String,
309    angle: i64,
310}
311
312impl ScreenOrientationState {
313    pub fn new(orientation_type: impl Into<String>, angle: i64) -> Self {
314        Self {
315            orientation_type: orientation_type.into(),
316            angle,
317        }
318    }
319
320    pub fn orientation_type(&self) -> &str {
321        &self.orientation_type
322    }
323
324    pub fn angle(&self) -> i64 {
325        self.angle
326    }
327}
328
329#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
330pub enum RadioNodeListTarget {
331    FormElements {
332        element: ElementHandle,
333        name: String,
334    },
335}
336
337#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
338pub(crate) enum PropertyKey {
339    String(String),
340    Symbol(u64),
341}
342
343#[derive(Clone, Debug, PartialEq)]
344pub(crate) enum PropertyValue {
345    Data(ScriptValue),
346    Accessor {
347        getter: Option<ScriptFunction>,
348        setter: Option<ScriptFunction>,
349    },
350}
351
352#[derive(Clone, Debug, PartialEq)]
353struct ObjectState {
354    properties: Vec<(PropertyKey, PropertyValue)>,
355}
356
357#[derive(Clone, Debug)]
358pub struct ObjectHandle(Rc<RefCell<ObjectState>>);
359
360impl ObjectHandle {
361    pub fn new() -> Self {
362        Self(Rc::new(RefCell::new(ObjectState {
363            properties: Vec::new(),
364        })))
365    }
366
367    pub fn identity(&self) -> usize {
368        Rc::as_ptr(&self.0) as usize
369    }
370}
371
372impl PartialEq for ObjectHandle {
373    fn eq(&self, other: &Self) -> bool {
374        Rc::ptr_eq(&self.0, &other.0)
375    }
376}
377
378#[derive(Clone, Debug, PartialEq)]
379struct ArrayState {
380    items: Vec<ScriptValue>,
381    properties: Vec<(PropertyKey, PropertyValue)>,
382}
383
384#[derive(Clone, Debug)]
385pub struct ArrayHandle(Rc<RefCell<ArrayState>>);
386
387impl ArrayHandle {
388    pub fn new(items: Vec<ScriptValue>) -> Self {
389        Self(Rc::new(RefCell::new(ArrayState {
390            items,
391            properties: Vec::new(),
392        })))
393    }
394
395    pub fn identity(&self) -> usize {
396        Rc::as_ptr(&self.0) as usize
397    }
398}
399
400impl PartialEq for ArrayHandle {
401    fn eq(&self, other: &Self) -> bool {
402        Rc::ptr_eq(&self.0, &other.0)
403    }
404}
405
406#[derive(Clone, Debug, PartialEq)]
407struct MapState {
408    entries: Vec<(MapKey, ScriptValue)>,
409    properties: Vec<(PropertyKey, PropertyValue)>,
410}
411
412#[derive(Clone, Debug)]
413pub struct MapHandle(Rc<RefCell<MapState>>);
414
415impl MapHandle {
416    pub fn new() -> Self {
417        Self(Rc::new(RefCell::new(MapState {
418            entries: Vec::new(),
419            properties: Vec::new(),
420        })))
421    }
422
423    pub fn identity(&self) -> usize {
424        Rc::as_ptr(&self.0) as usize
425    }
426}
427
428impl PartialEq for MapHandle {
429    fn eq(&self, other: &Self) -> bool {
430        Rc::ptr_eq(&self.0, &other.0)
431    }
432}
433
434#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
435pub(crate) enum MapKey {
436    Undefined,
437    Null,
438    Boolean(bool),
439    Number(u64),
440    String(String),
441    Symbol(u64),
442    Object(usize),
443    Array(usize),
444    Map(usize),
445}
446
447#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
448pub struct SymbolValue {
449    id: u64,
450    description: Option<String>,
451}
452
453impl SymbolValue {
454    pub fn new(description: Option<String>) -> Self {
455        static NEXT_ID: AtomicU64 = AtomicU64::new(1);
456        Self {
457            id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
458            description,
459        }
460    }
461
462    pub fn from_parts(id: u64, description: Option<String>) -> Self {
463        Self { id, description }
464    }
465
466    pub fn id(&self) -> u64 {
467        self.id
468    }
469
470    pub fn description(&self) -> Option<&str> {
471        self.description.as_deref()
472    }
473}
474
475#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
476pub struct RegExpValue {
477    pattern: String,
478    flags: String,
479}
480
481impl RegExpValue {
482    pub fn new(pattern: impl Into<String>, flags: impl Into<String>) -> Self {
483        Self {
484            pattern: pattern.into(),
485            flags: canonicalize_regexp_flags(flags.into()),
486        }
487    }
488
489    pub fn pattern(&self) -> &str {
490        &self.pattern
491    }
492
493    pub fn flags(&self) -> &str {
494        &self.flags
495    }
496
497    pub fn is_global(&self) -> bool {
498        self.flags.contains('g')
499    }
500
501    pub fn is_ignore_case(&self) -> bool {
502        self.flags.contains('i')
503    }
504
505    pub fn is_multiline(&self) -> bool {
506        self.flags.contains('m')
507    }
508
509    pub fn is_dot_all(&self) -> bool {
510        self.flags.contains('s')
511    }
512
513    pub fn is_unicode(&self) -> bool {
514        self.flags.contains('u')
515    }
516
517    pub fn is_sticky(&self) -> bool {
518        self.flags.contains('y')
519    }
520}
521
522fn canonicalize_regexp_flags(flags: String) -> String {
523    let mut output = String::new();
524    for flag in ['g', 'i', 'm', 's', 'u', 'y'] {
525        if flags.contains(flag) {
526            output.push(flag);
527        }
528    }
529    output
530}
531
532#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
533pub enum StyleSheetTarget {
534    OwnerNode(ElementHandle),
535}
536
537#[derive(Clone, Debug, PartialEq)]
538struct CollectionEntryState {
539    index: usize,
540    value: ScriptValue,
541}
542
543#[derive(Clone, Debug, PartialEq)]
544pub struct CollectionEntryHandle(Rc<RefCell<CollectionEntryState>>);
545
546impl CollectionEntryHandle {
547    pub fn new(index: usize, value: ScriptValue) -> Self {
548        Self(Rc::new(RefCell::new(CollectionEntryState { index, value })))
549    }
550
551    pub fn index(&self) -> usize {
552        self.0.borrow().index
553    }
554
555    pub fn value(&self) -> ScriptValue {
556        self.0.borrow().value.clone()
557    }
558}
559
560#[derive(Clone, Debug, PartialEq, Eq)]
561pub enum HtmlCollectionNamedItem {
562    Element(ElementHandle),
563    RadioNodeList(RadioNodeListTarget),
564}
565
566#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
567pub enum NodeListTarget {
568    Snapshot(Vec<ElementHandle>),
569    ByName(String),
570    Labels(ElementHandle),
571    ChildNodes(HtmlCollectionScope),
572}
573
574#[derive(Clone, Debug, PartialEq)]
575struct CollectionIteratorState {
576    items: Vec<ScriptValue>,
577    index: usize,
578}
579
580#[derive(Clone, Debug, PartialEq)]
581pub struct CollectionIteratorHandle(Rc<RefCell<CollectionIteratorState>>);
582
583impl CollectionIteratorHandle {
584    pub fn new(items: Vec<ScriptValue>) -> Self {
585        Self(Rc::new(RefCell::new(CollectionIteratorState {
586            items,
587            index: 0,
588        })))
589    }
590
591    pub fn next_result(&self) -> IteratorResult {
592        let mut state = self.0.borrow_mut();
593        if state.index >= state.items.len() {
594            return IteratorResult::new(None, true);
595        }
596
597        let value = state.items[state.index].clone();
598        state.index += 1;
599        IteratorResult::new(Some(value), false)
600    }
601}
602
603#[derive(Clone, Debug, PartialEq)]
604pub struct IteratorResult {
605    value: Option<ScriptValue>,
606    done: bool,
607}
608
609impl IteratorResult {
610    pub fn new(value: Option<ScriptValue>, done: bool) -> Self {
611        Self { value, done }
612    }
613
614    pub fn value(&self) -> Option<ScriptValue> {
615        self.value.clone()
616    }
617
618    pub fn done(&self) -> bool {
619        self.done
620    }
621}
622
623#[derive(Clone, Copy, Debug, PartialEq, Eq)]
624#[repr(u8)]
625pub enum EventPhase {
626    None = 0,
627    Capturing = 1,
628    AtTarget = 2,
629    Bubbling = 3,
630}
631
632#[derive(Clone, Debug, PartialEq, Eq)]
633struct ScriptEventState {
634    event_type: String,
635    target: ListenerTarget,
636    current_target: Option<ListenerTarget>,
637    bubbles: bool,
638    cancelable: bool,
639    default_prevented: bool,
640    propagation_stopped: bool,
641    immediate_propagation_stopped: bool,
642    phase: EventPhase,
643    key: Option<String>,
644    code: Option<String>,
645    ctrl_key: bool,
646    meta_key: bool,
647    shift_key: bool,
648    alt_key: bool,
649    repeat: bool,
650    is_composing: bool,
651    is_trusted: bool,
652}
653
654#[derive(Clone, Debug, PartialEq, Eq)]
655pub struct ScriptEventHandle(Rc<RefCell<ScriptEventState>>);
656
657impl ScriptEventHandle {
658    pub fn new(
659        event_type: impl Into<String>,
660        target: ListenerTarget,
661        bubbles: bool,
662        cancelable: bool,
663    ) -> Self {
664        Self(Rc::new(RefCell::new(ScriptEventState {
665            event_type: event_type.into(),
666            target,
667            current_target: None,
668            bubbles,
669            cancelable,
670            default_prevented: false,
671            propagation_stopped: false,
672            immediate_propagation_stopped: false,
673            phase: EventPhase::None,
674            key: None,
675            code: None,
676            ctrl_key: false,
677            meta_key: false,
678            shift_key: false,
679            alt_key: false,
680            repeat: false,
681            is_composing: false,
682            is_trusted: false,
683        })))
684    }
685
686    pub fn new_keyboard(
687        event_type: impl Into<String>,
688        target: ListenerTarget,
689        bubbles: bool,
690        cancelable: bool,
691        init: &KeyboardEventInit,
692    ) -> Self {
693        Self(Rc::new(RefCell::new(ScriptEventState {
694            event_type: event_type.into(),
695            target,
696            current_target: None,
697            bubbles,
698            cancelable,
699            default_prevented: false,
700            propagation_stopped: false,
701            immediate_propagation_stopped: false,
702            phase: EventPhase::None,
703            key: Some(init.key.clone()),
704            code: init.code.clone(),
705            ctrl_key: init.ctrl_key,
706            meta_key: init.meta_key,
707            shift_key: init.shift_key,
708            alt_key: init.alt_key,
709            repeat: init.repeat,
710            is_composing: init.is_composing,
711            is_trusted: false,
712        })))
713    }
714
715    pub fn event_type(&self) -> String {
716        self.0.borrow().event_type.clone()
717    }
718
719    pub fn target(&self) -> ListenerTarget {
720        self.0.borrow().target
721    }
722
723    pub fn current_target(&self) -> Option<ListenerTarget> {
724        self.0.borrow().current_target
725    }
726
727    pub fn set_current_target(&self, target: Option<ListenerTarget>) {
728        self.0.borrow_mut().current_target = target;
729    }
730
731    pub fn bubbles(&self) -> bool {
732        self.0.borrow().bubbles
733    }
734
735    pub fn cancelable(&self) -> bool {
736        self.0.borrow().cancelable
737    }
738
739    pub fn default_prevented(&self) -> bool {
740        self.0.borrow().default_prevented
741    }
742
743    pub fn propagation_stopped(&self) -> bool {
744        self.0.borrow().propagation_stopped
745    }
746
747    pub fn immediate_propagation_stopped(&self) -> bool {
748        self.0.borrow().immediate_propagation_stopped
749    }
750
751    pub fn event_phase(&self) -> EventPhase {
752        self.0.borrow().phase
753    }
754
755    pub fn key(&self) -> Option<String> {
756        self.0.borrow().key.clone()
757    }
758
759    pub fn code(&self) -> Option<String> {
760        self.0.borrow().code.clone()
761    }
762
763    pub fn ctrl_key(&self) -> bool {
764        self.0.borrow().ctrl_key
765    }
766
767    pub fn meta_key(&self) -> bool {
768        self.0.borrow().meta_key
769    }
770
771    pub fn shift_key(&self) -> bool {
772        self.0.borrow().shift_key
773    }
774
775    pub fn alt_key(&self) -> bool {
776        self.0.borrow().alt_key
777    }
778
779    pub fn repeat(&self) -> bool {
780        self.0.borrow().repeat
781    }
782
783    pub fn is_composing(&self) -> bool {
784        self.0.borrow().is_composing
785    }
786
787    pub fn is_trusted(&self) -> bool {
788        self.0.borrow().is_trusted
789    }
790
791    pub fn set_phase(&self, phase: EventPhase) {
792        self.0.borrow_mut().phase = phase;
793    }
794
795    pub fn prevent_default(&self) {
796        let mut state = self.0.borrow_mut();
797        if state.cancelable {
798            state.default_prevented = true;
799        }
800    }
801
802    pub fn stop_propagation(&self) {
803        self.0.borrow_mut().propagation_stopped = true;
804    }
805
806    pub fn stop_immediate_propagation(&self) {
807        let mut state = self.0.borrow_mut();
808        state.propagation_stopped = true;
809        state.immediate_propagation_stopped = true;
810    }
811}
812
813#[derive(Clone, Debug, PartialEq)]
814pub enum ScriptValue {
815    Undefined,
816    Null,
817    Boolean(bool),
818    Number(f64),
819    String(String),
820    Object(ObjectHandle),
821    Array(ArrayHandle),
822    Map(MapHandle),
823    Symbol(SymbolValue),
824    RegExp(RegExpValue),
825    Element(ElementHandle),
826    Attribute(AttributeHandle),
827    ClassList(ElementHandle),
828    Dataset(ElementHandle),
829    TemplateContent(ElementHandle),
830    NamedNodeMap(ElementHandle),
831    HtmlCollection(HtmlCollectionTarget),
832    StyleSheetList(StyleSheetListTarget),
833    Storage(StorageTarget),
834    MediaQueryList(MediaQueryListState),
835    StringList(StringListState),
836    MimeTypeArray(MimeTypeArrayState),
837    Navigator,
838    Clipboard,
839    History,
840    Screen,
841    ScreenOrientation(ScreenOrientationState),
842    StyleSheet(StyleSheetTarget),
843    Node(NodeHandle),
844    NodeList(NodeListTarget),
845    RadioNodeList(RadioNodeListTarget),
846    CollectionEntry(CollectionEntryHandle),
847    CollectionIterator(CollectionIteratorHandle),
848    IteratorResult(Box<IteratorResult>),
849    Date(DateValue),
850    IntlNumberFormat(IntlNumberFormatValue),
851    IntlDateTimeFormat(IntlDateTimeFormatValue),
852    IntlCollator(IntlCollatorValue),
853    Document,
854    Window,
855    ObjectNamespace,
856    ArrayNamespace,
857    IntlNamespace,
858    Event(ScriptEventHandle),
859    Function(ScriptFunction),
860}
861
862#[derive(Clone, Debug, PartialEq)]
863pub struct ScriptFunction {
864    pub params: Vec<String>,
865    pub body_source: String,
866    pub captured_bindings: Rc<BTreeMap<String, ScriptValue>>,
867}
868
869impl ScriptFunction {
870    pub fn new(params: Vec<String>, body_source: impl Into<String>) -> Self {
871        Self {
872            params,
873            body_source: body_source.into(),
874            captured_bindings: Rc::new(BTreeMap::new()),
875        }
876    }
877
878    pub fn with_captured_bindings(
879        mut self,
880        captured_bindings: BTreeMap<String, ScriptValue>,
881    ) -> Self {
882        self.captured_bindings = Rc::new(captured_bindings);
883        self
884    }
885}
886
887#[derive(Clone, Debug, PartialEq, Eq)]
888pub struct ScriptError {
889    kind: ScriptErrorKind,
890    message: String,
891}
892
893impl ScriptError {
894    pub fn new(message: impl Into<String>) -> Self {
895        Self::runtime(message)
896    }
897
898    pub fn parse(message: impl Into<String>) -> Self {
899        Self {
900            kind: ScriptErrorKind::Parse,
901            message: message.into(),
902        }
903    }
904
905    pub fn runtime(message: impl Into<String>) -> Self {
906        Self {
907            kind: ScriptErrorKind::Runtime,
908            message: message.into(),
909        }
910    }
911
912    pub fn message(&self) -> &str {
913        &self.message
914    }
915
916    pub fn kind(&self) -> ScriptErrorKind {
917        self.kind
918    }
919
920    pub fn phase_not_ready(capability: &str) -> Self {
921        Self::runtime(format!(
922            "{capability} is planned for a later phase of browser_tester"
923        ))
924    }
925}
926
927impl fmt::Display for ScriptError {
928    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
929        f.write_str(&self.message)
930    }
931}
932
933impl StdError for ScriptError {}
934
935pub type Result<T> = std::result::Result<T, ScriptError>;
936
937pub trait HostBindings {
938    fn on_eval(&mut self, _code: &str, _source_name: &str) -> Result<()> {
939        Ok(())
940    }
941
942    fn on_microtask_checkpoint(&mut self) -> Result<()> {
943        Ok(())
944    }
945
946    fn document_get_element_by_id(&mut self, _id: &str) -> Result<Option<ElementHandle>> {
947        Err(ScriptError::phase_not_ready("document.getElementById"))
948    }
949
950    fn document_create_element(&mut self, _tag_name: &str) -> Result<ElementHandle> {
951        Err(ScriptError::phase_not_ready("document.createElement"))
952    }
953
954    fn document_create_element_ns(
955        &mut self,
956        _namespace_uri: &str,
957        _tag_name: &str,
958    ) -> Result<ElementHandle> {
959        Err(ScriptError::phase_not_ready("document.createElementNS"))
960    }
961
962    fn document_create_text_node(&mut self, _text: &str) -> Result<NodeHandle> {
963        Err(ScriptError::phase_not_ready("document.createTextNode"))
964    }
965
966    fn document_create_comment(&mut self, _text: &str) -> Result<NodeHandle> {
967        Err(ScriptError::phase_not_ready("document.createComment"))
968    }
969
970    fn document_normalize(&mut self) -> Result<()> {
971        Err(ScriptError::phase_not_ready("Document.normalize"))
972    }
973
974    fn node_clone(&mut self, _node: NodeHandle, _deep: bool) -> Result<NodeHandle> {
975        Err(ScriptError::phase_not_ready("Node.cloneNode"))
976    }
977
978    fn node_normalize(&mut self, _node: NodeHandle) -> Result<()> {
979        Err(ScriptError::phase_not_ready("Node.normalize"))
980    }
981
982    fn node_replace_with(&mut self, _node: NodeHandle, _children: Vec<NodeHandle>) -> Result<()> {
983        Err(ScriptError::phase_not_ready("Node.replaceWith"))
984    }
985
986    fn node_before(&mut self, _node: NodeHandle, _children: Vec<NodeHandle>) -> Result<()> {
987        Err(ScriptError::phase_not_ready("Node.before"))
988    }
989
990    fn node_after(&mut self, _node: NodeHandle, _children: Vec<NodeHandle>) -> Result<()> {
991        Err(ScriptError::phase_not_ready("Node.after"))
992    }
993
994    fn document_contains(&mut self, _node: NodeHandle) -> Result<bool> {
995        Err(ScriptError::phase_not_ready("document.contains"))
996    }
997
998    fn node_contains(&mut self, _node: NodeHandle, _other: NodeHandle) -> Result<bool> {
999        Err(ScriptError::phase_not_ready("Node.contains"))
1000    }
1001
1002    fn node_compare_document_position(
1003        &mut self,
1004        _node: NodeHandle,
1005        _other: NodeHandle,
1006    ) -> Result<u16> {
1007        Err(ScriptError::phase_not_ready("Node.compareDocumentPosition"))
1008    }
1009
1010    fn node_is_equal_node(&mut self, _node: NodeHandle, _other: NodeHandle) -> Result<bool> {
1011        Err(ScriptError::phase_not_ready("Node.isEqualNode"))
1012    }
1013
1014    fn template_content_is_equal_node(
1015        &mut self,
1016        _fragment: ElementHandle,
1017        _other: ElementHandle,
1018    ) -> Result<bool> {
1019        Err(ScriptError::phase_not_ready("template.content.isEqualNode"))
1020    }
1021
1022    fn document_has_child_nodes(&mut self) -> Result<bool> {
1023        Err(ScriptError::phase_not_ready("document.hasChildNodes"))
1024    }
1025
1026    fn node_has_child_nodes(&mut self, _node: NodeHandle) -> Result<bool> {
1027        Err(ScriptError::phase_not_ready("Node.hasChildNodes"))
1028    }
1029
1030    fn document_document_element(&mut self) -> Result<Option<ElementHandle>> {
1031        Err(ScriptError::phase_not_ready("document.documentElement"))
1032    }
1033
1034    fn document_head(&mut self) -> Result<Option<ElementHandle>> {
1035        Err(ScriptError::phase_not_ready("document.head"))
1036    }
1037
1038    fn document_body(&mut self) -> Result<Option<ElementHandle>> {
1039        Err(ScriptError::phase_not_ready("document.body"))
1040    }
1041
1042    fn document_scrolling_element(&mut self) -> Result<Option<ElementHandle>> {
1043        Err(ScriptError::phase_not_ready("document.scrollingElement"))
1044    }
1045
1046    fn document_active_element(&mut self) -> Result<Option<ElementHandle>> {
1047        Err(ScriptError::phase_not_ready("document.activeElement"))
1048    }
1049
1050    fn document_has_focus(&mut self) -> Result<bool> {
1051        Err(ScriptError::phase_not_ready("document.hasFocus"))
1052    }
1053
1054    fn element_click(&mut self, _element: ElementHandle) -> Result<()> {
1055        Err(ScriptError::phase_not_ready("Element.click"))
1056    }
1057
1058    fn element_focus(&mut self, _element: ElementHandle) -> Result<()> {
1059        Err(ScriptError::phase_not_ready("Element.focus"))
1060    }
1061
1062    fn element_blur(&mut self, _element: ElementHandle) -> Result<()> {
1063        Err(ScriptError::phase_not_ready("Element.blur"))
1064    }
1065
1066    fn document_visibility_state(&mut self) -> Result<String> {
1067        Err(ScriptError::phase_not_ready("document.visibilityState"))
1068    }
1069
1070    fn document_hidden(&mut self) -> Result<bool> {
1071        Err(ScriptError::phase_not_ready("document.hidden"))
1072    }
1073
1074    fn document_title(&mut self) -> Result<String> {
1075        Err(ScriptError::phase_not_ready("document.title"))
1076    }
1077
1078    fn document_set_title(&mut self, _value: &str) -> Result<()> {
1079        Err(ScriptError::phase_not_ready("document.title"))
1080    }
1081
1082    fn document_location(&mut self) -> Result<String> {
1083        Err(ScriptError::phase_not_ready("document.location"))
1084    }
1085
1086    fn document_set_location(&mut self, _value: &str) -> Result<()> {
1087        Err(ScriptError::phase_not_ready("document.location"))
1088    }
1089
1090    fn document_location_assign(&mut self, _value: &str) -> Result<()> {
1091        Err(ScriptError::phase_not_ready("document.location.assign"))
1092    }
1093
1094    fn document_location_replace(&mut self, _value: &str) -> Result<()> {
1095        Err(ScriptError::phase_not_ready("document.location.replace"))
1096    }
1097
1098    fn document_location_reload(&mut self) -> Result<()> {
1099        Err(ScriptError::phase_not_ready("document.location.reload"))
1100    }
1101
1102    fn document_url(&mut self) -> Result<String> {
1103        Err(ScriptError::phase_not_ready("document.URL"))
1104    }
1105
1106    fn document_document_uri(&mut self) -> Result<String> {
1107        Err(ScriptError::phase_not_ready("document.documentURI"))
1108    }
1109
1110    fn document_base_uri(&mut self) -> Result<String> {
1111        Err(ScriptError::phase_not_ready("document.baseURI"))
1112    }
1113
1114    fn document_origin(&mut self) -> Result<String> {
1115        Err(ScriptError::phase_not_ready("document.origin"))
1116    }
1117
1118    fn document_domain(&mut self) -> Result<String> {
1119        Err(ScriptError::phase_not_ready("document.domain"))
1120    }
1121
1122    fn document_referrer(&mut self) -> Result<String> {
1123        Err(ScriptError::phase_not_ready("document.referrer"))
1124    }
1125
1126    fn document_cookie(&mut self) -> Result<String> {
1127        Err(ScriptError::phase_not_ready("document.cookie"))
1128    }
1129
1130    fn document_set_cookie(&mut self, _value: &str) -> Result<()> {
1131        Err(ScriptError::phase_not_ready("document.cookie"))
1132    }
1133
1134    fn document_write(&mut self, _html: &str) -> Result<()> {
1135        Err(ScriptError::phase_not_ready("document.write"))
1136    }
1137
1138    fn document_writeln(&mut self, _html: &str) -> Result<()> {
1139        Err(ScriptError::phase_not_ready("document.writeln"))
1140    }
1141
1142    fn document_open(&mut self) -> Result<()> {
1143        Err(ScriptError::phase_not_ready("document.open"))
1144    }
1145
1146    fn document_close(&mut self) -> Result<()> {
1147        Err(ScriptError::phase_not_ready("document.close"))
1148    }
1149
1150    fn match_media(&mut self, _query: &str) -> Result<MediaQueryListState> {
1151        Err(ScriptError::phase_not_ready("window.matchMedia"))
1152    }
1153
1154    fn match_media_add_listener(&mut self, _query: &str) -> Result<()> {
1155        Err(ScriptError::phase_not_ready("MediaQueryList.addListener"))
1156    }
1157
1158    fn match_media_remove_listener(&mut self, _query: &str) -> Result<()> {
1159        Err(ScriptError::phase_not_ready(
1160            "MediaQueryList.removeListener",
1161        ))
1162    }
1163
1164    fn window_open(
1165        &mut self,
1166        _url: Option<&str>,
1167        _target: Option<&str>,
1168        _features: Option<&str>,
1169    ) -> Result<()> {
1170        Err(ScriptError::phase_not_ready("window.open"))
1171    }
1172
1173    fn window_close(&mut self) -> Result<()> {
1174        Err(ScriptError::phase_not_ready("window.close"))
1175    }
1176
1177    fn window_print(&mut self) -> Result<()> {
1178        Err(ScriptError::phase_not_ready("window.print"))
1179    }
1180
1181    fn window_request_animation_frame(&mut self, _callback: ScriptFunction) -> Result<u64> {
1182        Err(ScriptError::phase_not_ready("window.requestAnimationFrame"))
1183    }
1184
1185    fn window_cancel_animation_frame(&mut self, _handle: u64) -> Result<()> {
1186        Err(ScriptError::phase_not_ready("window.cancelAnimationFrame"))
1187    }
1188
1189    fn window_set_timeout(&mut self, _callback: ScriptFunction, _delay_ms: i64) -> Result<u64> {
1190        Err(ScriptError::phase_not_ready("window.setTimeout"))
1191    }
1192
1193    fn window_clear_timeout(&mut self, _handle: u64) -> Result<()> {
1194        Err(ScriptError::phase_not_ready("window.clearTimeout"))
1195    }
1196
1197    fn window_set_interval(&mut self, _callback: ScriptFunction, _delay_ms: i64) -> Result<u64> {
1198        Err(ScriptError::phase_not_ready("window.setInterval"))
1199    }
1200
1201    fn window_clear_interval(&mut self, _handle: u64) -> Result<()> {
1202        Err(ScriptError::phase_not_ready("window.clearInterval"))
1203    }
1204
1205    fn window_alert(&mut self, _message: &str) -> Result<()> {
1206        Err(ScriptError::phase_not_ready("window.alert"))
1207    }
1208
1209    fn window_confirm(&mut self, _message: &str) -> Result<bool> {
1210        Err(ScriptError::phase_not_ready("window.confirm"))
1211    }
1212
1213    fn window_prompt(
1214        &mut self,
1215        _message: &str,
1216        _default_text: Option<&str>,
1217    ) -> Result<Option<String>> {
1218        Err(ScriptError::phase_not_ready("window.prompt"))
1219    }
1220
1221    fn html_collection_window_frames_items(&mut self) -> Result<Vec<ElementHandle>> {
1222        Err(ScriptError::phase_not_ready("window.frames"))
1223    }
1224
1225    fn html_collection_window_frames_named_item(
1226        &mut self,
1227        _name: &str,
1228    ) -> Result<Option<ElementHandle>> {
1229        Err(ScriptError::phase_not_ready("window.frames"))
1230    }
1231
1232    fn window_navigator_user_agent(&mut self) -> Result<String> {
1233        Err(ScriptError::phase_not_ready("window.navigator.userAgent"))
1234    }
1235
1236    fn window_navigator_app_code_name(&mut self) -> Result<String> {
1237        Err(ScriptError::phase_not_ready("window.navigator.appCodeName"))
1238    }
1239
1240    fn window_navigator_app_name(&mut self) -> Result<String> {
1241        Err(ScriptError::phase_not_ready("window.navigator.appName"))
1242    }
1243
1244    fn window_navigator_app_version(&mut self) -> Result<String> {
1245        Err(ScriptError::phase_not_ready("window.navigator.appVersion"))
1246    }
1247
1248    fn window_navigator_product(&mut self) -> Result<String> {
1249        Err(ScriptError::phase_not_ready("window.navigator.product"))
1250    }
1251
1252    fn window_navigator_product_sub(&mut self) -> Result<String> {
1253        Err(ScriptError::phase_not_ready("window.navigator.productSub"))
1254    }
1255
1256    fn window_navigator_platform(&mut self) -> Result<String> {
1257        Err(ScriptError::phase_not_ready("window.navigator.platform"))
1258    }
1259
1260    fn window_navigator_language(&mut self) -> Result<String> {
1261        Err(ScriptError::phase_not_ready("window.navigator.language"))
1262    }
1263
1264    fn window_navigator_oscpu(&mut self) -> Result<String> {
1265        Err(ScriptError::phase_not_ready("window.navigator.oscpu"))
1266    }
1267
1268    fn window_navigator_user_language(&mut self) -> Result<String> {
1269        Err(ScriptError::phase_not_ready(
1270            "window.navigator.userLanguage",
1271        ))
1272    }
1273
1274    fn window_navigator_browser_language(&mut self) -> Result<String> {
1275        Err(ScriptError::phase_not_ready(
1276            "window.navigator.browserLanguage",
1277        ))
1278    }
1279
1280    fn window_navigator_system_language(&mut self) -> Result<String> {
1281        Err(ScriptError::phase_not_ready(
1282            "window.navigator.systemLanguage",
1283        ))
1284    }
1285
1286    fn window_navigator_languages(&mut self) -> Result<Vec<String>> {
1287        Err(ScriptError::phase_not_ready("window.navigator.languages"))
1288    }
1289
1290    fn window_navigator_mime_types(&mut self) -> Result<Vec<String>> {
1291        Err(ScriptError::phase_not_ready("window.navigator.mimeTypes"))
1292    }
1293
1294    fn clipboard_write_text(&mut self, _text: &str) -> Result<()> {
1295        Err(ScriptError::phase_not_ready(
1296            "navigator.clipboard.writeText",
1297        ))
1298    }
1299
1300    fn clipboard_read_text(&mut self) -> Result<String> {
1301        Err(ScriptError::phase_not_ready("navigator.clipboard.readText"))
1302    }
1303
1304    fn window_navigator_cookie_enabled(&mut self) -> Result<bool> {
1305        Err(ScriptError::phase_not_ready(
1306            "window.navigator.cookieEnabled",
1307        ))
1308    }
1309
1310    fn window_navigator_on_line(&mut self) -> Result<bool> {
1311        Err(ScriptError::phase_not_ready("window.navigator.onLine"))
1312    }
1313
1314    fn window_navigator_webdriver(&mut self) -> Result<bool> {
1315        Err(ScriptError::phase_not_ready("window.navigator.webdriver"))
1316    }
1317
1318    fn window_navigator_vendor(&mut self) -> Result<String> {
1319        Err(ScriptError::phase_not_ready("window.navigator.vendor"))
1320    }
1321
1322    fn window_navigator_vendor_sub(&mut self) -> Result<String> {
1323        Err(ScriptError::phase_not_ready("window.navigator.vendorSub"))
1324    }
1325
1326    fn window_navigator_pdf_viewer_enabled(&mut self) -> Result<bool> {
1327        Err(ScriptError::phase_not_ready(
1328            "window.navigator.pdfViewerEnabled",
1329        ))
1330    }
1331
1332    fn window_navigator_do_not_track(&mut self) -> Result<String> {
1333        Err(ScriptError::phase_not_ready("window.navigator.doNotTrack"))
1334    }
1335
1336    fn window_navigator_java_enabled(&mut self) -> Result<bool> {
1337        Err(ScriptError::phase_not_ready(
1338            "window.navigator.javaEnabled()",
1339        ))
1340    }
1341
1342    fn random_f64(&mut self) -> Result<f64> {
1343        Err(ScriptError::phase_not_ready("Math.random"))
1344    }
1345
1346    fn window_navigator_hardware_concurrency(&mut self) -> Result<i64> {
1347        Err(ScriptError::phase_not_ready(
1348            "window.navigator.hardwareConcurrency",
1349        ))
1350    }
1351
1352    fn window_navigator_max_touch_points(&mut self) -> Result<i64> {
1353        Err(ScriptError::phase_not_ready(
1354            "window.navigator.maxTouchPoints",
1355        ))
1356    }
1357
1358    fn window_history_length(&mut self) -> Result<usize> {
1359        Err(ScriptError::phase_not_ready("window.history"))
1360    }
1361
1362    fn window_history_scroll_restoration(&mut self) -> Result<String> {
1363        Err(ScriptError::phase_not_ready(
1364            "window.history.scrollRestoration",
1365        ))
1366    }
1367
1368    fn set_window_history_scroll_restoration(&mut self, _value: &str) -> Result<()> {
1369        Err(ScriptError::phase_not_ready(
1370            "window.history.scrollRestoration",
1371        ))
1372    }
1373
1374    fn window_history_state(&mut self) -> Result<Option<String>> {
1375        Err(ScriptError::phase_not_ready("window.history.state"))
1376    }
1377
1378    fn window_history_push_state(
1379        &mut self,
1380        _state: Option<&str>,
1381        _url: Option<&str>,
1382    ) -> Result<()> {
1383        Err(ScriptError::phase_not_ready("window.history.pushState()"))
1384    }
1385
1386    fn window_history_replace_state(
1387        &mut self,
1388        _state: Option<&str>,
1389        _url: Option<&str>,
1390    ) -> Result<()> {
1391        Err(ScriptError::phase_not_ready(
1392            "window.history.replaceState()",
1393        ))
1394    }
1395
1396    fn window_history_back(&mut self) -> Result<()> {
1397        Err(ScriptError::phase_not_ready("window.history.back()"))
1398    }
1399
1400    fn window_history_forward(&mut self) -> Result<()> {
1401        Err(ScriptError::phase_not_ready("window.history.forward()"))
1402    }
1403
1404    fn window_history_go(&mut self, _delta: i64) -> Result<()> {
1405        Err(ScriptError::phase_not_ready("window.history.go()"))
1406    }
1407
1408    fn window_scroll_x(&mut self) -> Result<i64> {
1409        Err(ScriptError::phase_not_ready("window.scrollX"))
1410    }
1411
1412    fn window_scroll_y(&mut self) -> Result<i64> {
1413        Err(ScriptError::phase_not_ready("window.scrollY"))
1414    }
1415
1416    fn window_page_x_offset(&mut self) -> Result<i64> {
1417        Err(ScriptError::phase_not_ready("window.pageXOffset"))
1418    }
1419
1420    fn window_page_y_offset(&mut self) -> Result<i64> {
1421        Err(ScriptError::phase_not_ready("window.pageYOffset"))
1422    }
1423
1424    fn window_device_pixel_ratio(&mut self) -> Result<f64> {
1425        Err(ScriptError::phase_not_ready("window.devicePixelRatio"))
1426    }
1427
1428    fn window_inner_width(&mut self) -> Result<i64> {
1429        Err(ScriptError::phase_not_ready("window.innerWidth"))
1430    }
1431
1432    fn window_inner_height(&mut self) -> Result<i64> {
1433        Err(ScriptError::phase_not_ready("window.innerHeight"))
1434    }
1435
1436    fn window_outer_width(&mut self) -> Result<i64> {
1437        Err(ScriptError::phase_not_ready("window.outerWidth"))
1438    }
1439
1440    fn window_outer_height(&mut self) -> Result<i64> {
1441        Err(ScriptError::phase_not_ready("window.outerHeight"))
1442    }
1443
1444    fn window_screen_x(&mut self) -> Result<i64> {
1445        Err(ScriptError::phase_not_ready("window.screenX"))
1446    }
1447
1448    fn window_screen_y(&mut self) -> Result<i64> {
1449        Err(ScriptError::phase_not_ready("window.screenY"))
1450    }
1451
1452    fn window_screen_left(&mut self) -> Result<i64> {
1453        Err(ScriptError::phase_not_ready("window.screenLeft"))
1454    }
1455
1456    fn window_screen_top(&mut self) -> Result<i64> {
1457        Err(ScriptError::phase_not_ready("window.screenTop"))
1458    }
1459
1460    fn window_screen_width(&mut self) -> Result<i64> {
1461        Err(ScriptError::phase_not_ready("window.screen.width"))
1462    }
1463
1464    fn window_screen_height(&mut self) -> Result<i64> {
1465        Err(ScriptError::phase_not_ready("window.screen.height"))
1466    }
1467
1468    fn window_screen_avail_width(&mut self) -> Result<i64> {
1469        Err(ScriptError::phase_not_ready("window.screen.availWidth"))
1470    }
1471
1472    fn window_screen_avail_height(&mut self) -> Result<i64> {
1473        Err(ScriptError::phase_not_ready("window.screen.availHeight"))
1474    }
1475
1476    fn window_screen_avail_left(&mut self) -> Result<i64> {
1477        Err(ScriptError::phase_not_ready("window.screen.availLeft"))
1478    }
1479
1480    fn window_screen_avail_top(&mut self) -> Result<i64> {
1481        Err(ScriptError::phase_not_ready("window.screen.availTop"))
1482    }
1483
1484    fn window_screen_color_depth(&mut self) -> Result<i64> {
1485        Err(ScriptError::phase_not_ready("window.screen.colorDepth"))
1486    }
1487
1488    fn window_screen_pixel_depth(&mut self) -> Result<i64> {
1489        Err(ScriptError::phase_not_ready("window.screen.pixelDepth"))
1490    }
1491
1492    fn window_screen_orientation(&mut self) -> Result<ScreenOrientationState> {
1493        Err(ScriptError::phase_not_ready("window.screen.orientation"))
1494    }
1495
1496    fn window_scroll_to(&mut self, _x: i64, _y: i64) -> Result<()> {
1497        Err(ScriptError::phase_not_ready("window.scrollTo"))
1498    }
1499
1500    fn window_scroll_by(&mut self, _x: i64, _y: i64) -> Result<()> {
1501        Err(ScriptError::phase_not_ready("window.scrollBy"))
1502    }
1503
1504    fn window_name(&mut self) -> Result<String> {
1505        Err(ScriptError::phase_not_ready("window.name"))
1506    }
1507
1508    fn set_window_name(&mut self, _value: &str) -> Result<()> {
1509        Err(ScriptError::phase_not_ready("window.name"))
1510    }
1511
1512    fn storage_length(&mut self, target: StorageTarget) -> Result<usize> {
1513        Err(ScriptError::phase_not_ready(match target {
1514            StorageTarget::Local => "window.localStorage",
1515            StorageTarget::Session => "window.sessionStorage",
1516        }))
1517    }
1518
1519    fn storage_get_item(&mut self, target: StorageTarget, _key: &str) -> Result<Option<String>> {
1520        Err(ScriptError::phase_not_ready(match target {
1521            StorageTarget::Local => "window.localStorage",
1522            StorageTarget::Session => "window.sessionStorage",
1523        }))
1524    }
1525
1526    fn storage_set_item(&mut self, target: StorageTarget, _key: &str, _value: &str) -> Result<()> {
1527        Err(ScriptError::phase_not_ready(match target {
1528            StorageTarget::Local => "window.localStorage",
1529            StorageTarget::Session => "window.sessionStorage",
1530        }))
1531    }
1532
1533    fn storage_remove_item(&mut self, target: StorageTarget, _key: &str) -> Result<()> {
1534        Err(ScriptError::phase_not_ready(match target {
1535            StorageTarget::Local => "window.localStorage",
1536            StorageTarget::Session => "window.sessionStorage",
1537        }))
1538    }
1539
1540    fn storage_clear(&mut self, target: StorageTarget) -> Result<()> {
1541        Err(ScriptError::phase_not_ready(match target {
1542            StorageTarget::Local => "window.localStorage",
1543            StorageTarget::Session => "window.sessionStorage",
1544        }))
1545    }
1546
1547    fn storage_key(&mut self, target: StorageTarget, _index: usize) -> Result<Option<String>> {
1548        Err(ScriptError::phase_not_ready(match target {
1549            StorageTarget::Local => "window.localStorage",
1550            StorageTarget::Session => "window.sessionStorage",
1551        }))
1552    }
1553
1554    fn document_current_script(&mut self) -> Result<Option<ElementHandle>> {
1555        Err(ScriptError::phase_not_ready("document.currentScript"))
1556    }
1557
1558    fn document_ready_state(&mut self) -> Result<String> {
1559        Err(ScriptError::phase_not_ready("document.readyState"))
1560    }
1561
1562    fn document_compat_mode(&mut self) -> Result<String> {
1563        Err(ScriptError::phase_not_ready("document.compatMode"))
1564    }
1565
1566    fn document_character_set(&mut self) -> Result<String> {
1567        Err(ScriptError::phase_not_ready("document.characterSet"))
1568    }
1569
1570    fn document_content_type(&mut self) -> Result<String> {
1571        Err(ScriptError::phase_not_ready("document.contentType"))
1572    }
1573
1574    fn document_design_mode(&mut self) -> Result<String> {
1575        Err(ScriptError::phase_not_ready("document.designMode"))
1576    }
1577
1578    fn document_set_design_mode(&mut self, _value: &str) -> Result<()> {
1579        Err(ScriptError::phase_not_ready("document.designMode"))
1580    }
1581
1582    fn document_dir(&mut self) -> Result<String> {
1583        Err(ScriptError::phase_not_ready("document.dir"))
1584    }
1585
1586    fn document_set_dir(&mut self, _value: &str) -> Result<()> {
1587        Err(ScriptError::phase_not_ready("document.dir"))
1588    }
1589
1590    fn document_query_selector(&mut self, _selector: &str) -> Result<Option<ElementHandle>> {
1591        Err(ScriptError::phase_not_ready("document.querySelector"))
1592    }
1593
1594    fn document_query_selector_all(&mut self, _selector: &str) -> Result<Vec<ElementHandle>> {
1595        Err(ScriptError::phase_not_ready("document.querySelectorAll"))
1596    }
1597
1598    fn document_get_elements_by_name(&mut self, _name: &str) -> Result<Vec<ElementHandle>> {
1599        Err(ScriptError::phase_not_ready("document.getElementsByName"))
1600    }
1601
1602    fn document_style_sheets_items(&mut self) -> Result<Vec<ElementHandle>> {
1603        Err(ScriptError::phase_not_ready("document.styleSheets"))
1604    }
1605
1606    fn document_style_sheets_named_item(&mut self, _name: &str) -> Result<Option<ElementHandle>> {
1607        Err(ScriptError::phase_not_ready(
1608            "document.styleSheets.namedItem()",
1609        ))
1610    }
1611
1612    fn node_child_nodes_items(&mut self, _scope: HtmlCollectionScope) -> Result<Vec<NodeHandle>> {
1613        Err(ScriptError::phase_not_ready("Node.childNodes"))
1614    }
1615
1616    fn node_parent(&mut self, _node: NodeHandle) -> Result<Option<NodeHandle>> {
1617        Err(ScriptError::phase_not_ready("Node.parentNode"))
1618    }
1619
1620    fn node_text_content(&mut self, _node: NodeHandle) -> Result<String> {
1621        Err(ScriptError::phase_not_ready("Node.textContent"))
1622    }
1623
1624    fn node_type(&mut self, _node: NodeHandle) -> Result<u8> {
1625        Err(ScriptError::phase_not_ready("Node.nodeType"))
1626    }
1627
1628    fn node_name(&mut self, _node: NodeHandle) -> Result<String> {
1629        Err(ScriptError::phase_not_ready("Node.nodeName"))
1630    }
1631
1632    fn node_namespace_uri(&mut self, _node: NodeHandle) -> Result<Option<String>> {
1633        Err(ScriptError::phase_not_ready("Node.namespaceURI"))
1634    }
1635
1636    fn element_children(&mut self, _element: ElementHandle) -> Result<Vec<ElementHandle>> {
1637        Err(ScriptError::phase_not_ready("element.children"))
1638    }
1639
1640    fn element_tag_name(&mut self, _element: ElementHandle) -> Result<String> {
1641        Err(ScriptError::phase_not_ready("element.tagName"))
1642    }
1643
1644    fn element_base_uri(&mut self, _element: ElementHandle) -> Result<String> {
1645        Err(ScriptError::phase_not_ready("element.baseURI"))
1646    }
1647
1648    fn element_origin(&mut self, _element: ElementHandle) -> Result<String> {
1649        Err(ScriptError::phase_not_ready("element.origin"))
1650    }
1651
1652    fn element_is_content_editable(&mut self, _element: ElementHandle) -> Result<bool> {
1653        Err(ScriptError::phase_not_ready("element.isContentEditable"))
1654    }
1655
1656    fn element_labels(&mut self, _element: ElementHandle) -> Result<Vec<ElementHandle>> {
1657        Err(ScriptError::phase_not_ready("element.labels"))
1658    }
1659
1660    fn html_collection_named_item(
1661        &mut self,
1662        _element: ElementHandle,
1663        _name: &str,
1664    ) -> Result<Option<ElementHandle>> {
1665        Err(ScriptError::phase_not_ready("HTMLCollection.namedItem"))
1666    }
1667
1668    fn html_collection_tag_name_items(
1669        &mut self,
1670        _collection: HtmlCollectionTarget,
1671    ) -> Result<Vec<ElementHandle>> {
1672        Err(ScriptError::phase_not_ready(
1673            "HTMLCollection.getElementsByTagName",
1674        ))
1675    }
1676
1677    fn html_collection_tag_name_named_item(
1678        &mut self,
1679        _collection: HtmlCollectionTarget,
1680        _name: &str,
1681    ) -> Result<Option<ElementHandle>> {
1682        Err(ScriptError::phase_not_ready(
1683            "HTMLCollection.getElementsByTagName",
1684        ))
1685    }
1686
1687    fn html_collection_tag_name_ns_items(
1688        &mut self,
1689        _collection: HtmlCollectionTarget,
1690    ) -> Result<Vec<ElementHandle>> {
1691        Err(ScriptError::phase_not_ready(
1692            "HTMLCollection.getElementsByTagNameNS",
1693        ))
1694    }
1695
1696    fn html_collection_tag_name_ns_named_item(
1697        &mut self,
1698        _collection: HtmlCollectionTarget,
1699        _name: &str,
1700    ) -> Result<Option<ElementHandle>> {
1701        Err(ScriptError::phase_not_ready(
1702            "HTMLCollection.getElementsByTagNameNS",
1703        ))
1704    }
1705
1706    fn html_collection_class_name_items(
1707        &mut self,
1708        _collection: HtmlCollectionTarget,
1709    ) -> Result<Vec<ElementHandle>> {
1710        Err(ScriptError::phase_not_ready(
1711            "HTMLCollection.getElementsByClassName",
1712        ))
1713    }
1714
1715    fn html_collection_class_name_named_item(
1716        &mut self,
1717        _collection: HtmlCollectionTarget,
1718        _name: &str,
1719    ) -> Result<Option<ElementHandle>> {
1720        Err(ScriptError::phase_not_ready(
1721            "HTMLCollection.getElementsByClassName",
1722        ))
1723    }
1724
1725    fn html_collection_form_elements_items(
1726        &mut self,
1727        _element: ElementHandle,
1728    ) -> Result<Vec<ElementHandle>> {
1729        Err(ScriptError::phase_not_ready("form.elements"))
1730    }
1731
1732    fn html_collection_form_elements_named_item(
1733        &mut self,
1734        _element: ElementHandle,
1735        _name: &str,
1736    ) -> Result<Option<ElementHandle>> {
1737        Err(ScriptError::phase_not_ready("form.elements"))
1738    }
1739
1740    fn html_collection_form_elements_named_items(
1741        &mut self,
1742        _element: ElementHandle,
1743        _name: &str,
1744    ) -> Result<Vec<ElementHandle>> {
1745        Err(ScriptError::phase_not_ready("form.elements"))
1746    }
1747
1748    fn radio_node_list_set_value(
1749        &mut self,
1750        _target: &RadioNodeListTarget,
1751        _value: &str,
1752    ) -> Result<()> {
1753        Err(ScriptError::phase_not_ready(
1754            "RadioNodeList.value assignment",
1755        ))
1756    }
1757
1758    fn html_collection_select_options_items(
1759        &mut self,
1760        _element: ElementHandle,
1761    ) -> Result<Vec<ElementHandle>> {
1762        Err(ScriptError::phase_not_ready("select.options"))
1763    }
1764
1765    fn html_collection_select_options_named_item(
1766        &mut self,
1767        _element: ElementHandle,
1768        _name: &str,
1769    ) -> Result<Option<ElementHandle>> {
1770        Err(ScriptError::phase_not_ready("select.options"))
1771    }
1772
1773    fn html_collection_select_options_add(
1774        &mut self,
1775        _element: ElementHandle,
1776        _option: ElementHandle,
1777    ) -> Result<()> {
1778        Err(ScriptError::phase_not_ready("select.options.add"))
1779    }
1780
1781    fn html_collection_select_options_remove(
1782        &mut self,
1783        _element: ElementHandle,
1784        _index: usize,
1785    ) -> Result<()> {
1786        Err(ScriptError::phase_not_ready("select.options.remove"))
1787    }
1788
1789    fn html_collection_select_selected_options_items(
1790        &mut self,
1791        _element: ElementHandle,
1792    ) -> Result<Vec<ElementHandle>> {
1793        Err(ScriptError::phase_not_ready("select.selectedOptions"))
1794    }
1795
1796    fn html_collection_select_selected_options_named_item(
1797        &mut self,
1798        _element: ElementHandle,
1799        _name: &str,
1800    ) -> Result<Option<ElementHandle>> {
1801        Err(ScriptError::phase_not_ready("select.selectedOptions"))
1802    }
1803
1804    fn html_collection_document_links_items(&mut self) -> Result<Vec<ElementHandle>> {
1805        Err(ScriptError::phase_not_ready("document.links"))
1806    }
1807
1808    fn html_collection_document_links_named_item(
1809        &mut self,
1810        _name: &str,
1811    ) -> Result<Option<ElementHandle>> {
1812        Err(ScriptError::phase_not_ready("document.links"))
1813    }
1814
1815    fn html_collection_document_anchors_items(&mut self) -> Result<Vec<ElementHandle>> {
1816        Err(ScriptError::phase_not_ready("document.anchors"))
1817    }
1818
1819    fn html_collection_document_anchors_named_item(
1820        &mut self,
1821        _name: &str,
1822    ) -> Result<Option<ElementHandle>> {
1823        Err(ScriptError::phase_not_ready("document.anchors"))
1824    }
1825
1826    fn html_collection_document_children_items(&mut self) -> Result<Vec<ElementHandle>> {
1827        Err(ScriptError::phase_not_ready("document.children"))
1828    }
1829
1830    fn html_collection_document_children_named_item(
1831        &mut self,
1832        _name: &str,
1833    ) -> Result<Option<ElementHandle>> {
1834        Err(ScriptError::phase_not_ready("document.children"))
1835    }
1836
1837    fn html_collection_map_areas_items(
1838        &mut self,
1839        _element: ElementHandle,
1840    ) -> Result<Vec<ElementHandle>> {
1841        Err(ScriptError::phase_not_ready("map.areas"))
1842    }
1843
1844    fn html_collection_map_areas_named_item(
1845        &mut self,
1846        _element: ElementHandle,
1847        _name: &str,
1848    ) -> Result<Option<ElementHandle>> {
1849        Err(ScriptError::phase_not_ready("map.areas"))
1850    }
1851
1852    fn html_collection_table_rows_items(
1853        &mut self,
1854        _element: ElementHandle,
1855    ) -> Result<Vec<ElementHandle>> {
1856        Err(ScriptError::phase_not_ready("table.rows"))
1857    }
1858
1859    fn html_collection_table_rows_named_item(
1860        &mut self,
1861        _element: ElementHandle,
1862        _name: &str,
1863    ) -> Result<Option<ElementHandle>> {
1864        Err(ScriptError::phase_not_ready("table.rows"))
1865    }
1866
1867    fn html_collection_table_bodies_items(
1868        &mut self,
1869        _element: ElementHandle,
1870    ) -> Result<Vec<ElementHandle>> {
1871        Err(ScriptError::phase_not_ready("table.tBodies"))
1872    }
1873
1874    fn html_collection_table_bodies_named_item(
1875        &mut self,
1876        _element: ElementHandle,
1877        _name: &str,
1878    ) -> Result<Option<ElementHandle>> {
1879        Err(ScriptError::phase_not_ready("table.tBodies"))
1880    }
1881
1882    fn html_collection_row_cells_items(
1883        &mut self,
1884        _element: ElementHandle,
1885    ) -> Result<Vec<ElementHandle>> {
1886        Err(ScriptError::phase_not_ready("tr.cells"))
1887    }
1888
1889    fn html_collection_row_cells_named_item(
1890        &mut self,
1891        _element: ElementHandle,
1892        _name: &str,
1893    ) -> Result<Option<ElementHandle>> {
1894        Err(ScriptError::phase_not_ready("tr.cells"))
1895    }
1896
1897    fn element_text_content(&mut self, _element: ElementHandle) -> Result<String> {
1898        Err(ScriptError::phase_not_ready("element.textContent"))
1899    }
1900
1901    fn element_set_text_content(&mut self, _element: ElementHandle, _value: &str) -> Result<()> {
1902        Err(ScriptError::phase_not_ready(
1903            "element.textContent assignment",
1904        ))
1905    }
1906
1907    fn element_inner_html(&mut self, _element: ElementHandle) -> Result<String> {
1908        Err(ScriptError::phase_not_ready("element.innerHTML"))
1909    }
1910
1911    fn element_set_inner_html(&mut self, _element: ElementHandle, _value: &str) -> Result<()> {
1912        Err(ScriptError::phase_not_ready("element.innerHTML assignment"))
1913    }
1914
1915    fn element_outer_html(&mut self, _element: ElementHandle) -> Result<String> {
1916        Err(ScriptError::phase_not_ready("element.outerHTML"))
1917    }
1918
1919    fn element_set_outer_html(&mut self, _element: ElementHandle, _value: &str) -> Result<()> {
1920        Err(ScriptError::phase_not_ready("element.outerHTML assignment"))
1921    }
1922
1923    fn element_insert_adjacent_html(
1924        &mut self,
1925        _element: ElementHandle,
1926        _position: &str,
1927        _value: &str,
1928    ) -> Result<()> {
1929        Err(ScriptError::phase_not_ready("element.insertAdjacentHTML"))
1930    }
1931
1932    fn element_value(&mut self, _element: ElementHandle) -> Result<String> {
1933        Err(ScriptError::phase_not_ready("element.value"))
1934    }
1935
1936    fn element_set_value(&mut self, _element: ElementHandle, _value: &str) -> Result<()> {
1937        Err(ScriptError::phase_not_ready("element.value assignment"))
1938    }
1939
1940    fn element_checked(&mut self, _element: ElementHandle) -> Result<bool> {
1941        Err(ScriptError::phase_not_ready("element.checked"))
1942    }
1943
1944    fn element_set_checked(&mut self, _element: ElementHandle, _checked: bool) -> Result<()> {
1945        Err(ScriptError::phase_not_ready("element.checked assignment"))
1946    }
1947
1948    fn element_indeterminate(&mut self, _element: ElementHandle) -> Result<bool> {
1949        Err(ScriptError::phase_not_ready("element.indeterminate"))
1950    }
1951
1952    fn element_set_indeterminate(
1953        &mut self,
1954        _element: ElementHandle,
1955        _indeterminate: bool,
1956    ) -> Result<()> {
1957        Err(ScriptError::phase_not_ready(
1958            "element.indeterminate assignment",
1959        ))
1960    }
1961
1962    fn element_get_attribute(
1963        &mut self,
1964        _element: ElementHandle,
1965        _name: &str,
1966    ) -> Result<Option<String>> {
1967        Err(ScriptError::phase_not_ready("element.getAttribute"))
1968    }
1969
1970    fn element_set_attribute(
1971        &mut self,
1972        _element: ElementHandle,
1973        _name: &str,
1974        _value: &str,
1975    ) -> Result<()> {
1976        Err(ScriptError::phase_not_ready("element.setAttribute"))
1977    }
1978
1979    fn element_remove_attribute(&mut self, _element: ElementHandle, _name: &str) -> Result<()> {
1980        Err(ScriptError::phase_not_ready("element.removeAttribute"))
1981    }
1982
1983    fn element_has_attribute(&mut self, _element: ElementHandle, _name: &str) -> Result<bool> {
1984        Err(ScriptError::phase_not_ready("element.hasAttribute"))
1985    }
1986
1987    fn element_attribute_names(&mut self, _element: ElementHandle) -> Result<Vec<String>> {
1988        Err(ScriptError::phase_not_ready("Element.attributes"))
1989    }
1990
1991    fn element_toggle_attribute(
1992        &mut self,
1993        _element: ElementHandle,
1994        _name: &str,
1995        _force: Option<bool>,
1996    ) -> Result<bool> {
1997        Err(ScriptError::phase_not_ready("element.toggleAttribute"))
1998    }
1999
2000    fn element_append_child(&mut self, _parent: ElementHandle, _child: NodeHandle) -> Result<()> {
2001        Err(ScriptError::phase_not_ready("element.appendChild"))
2002    }
2003
2004    fn element_insert_before(
2005        &mut self,
2006        _parent: ElementHandle,
2007        _child: NodeHandle,
2008        _reference: Option<NodeHandle>,
2009    ) -> Result<()> {
2010        Err(ScriptError::phase_not_ready("element.insertBefore"))
2011    }
2012
2013    fn element_replace_child(
2014        &mut self,
2015        _parent: ElementHandle,
2016        _new_child: NodeHandle,
2017        _old_child: NodeHandle,
2018    ) -> Result<()> {
2019        Err(ScriptError::phase_not_ready("element.replaceChild"))
2020    }
2021
2022    fn element_replace_children(
2023        &mut self,
2024        _parent: ElementHandle,
2025        _children: Vec<NodeHandle>,
2026    ) -> Result<()> {
2027        Err(ScriptError::phase_not_ready("element.replaceChildren"))
2028    }
2029
2030    fn element_append(
2031        &mut self,
2032        _element: ElementHandle,
2033        _children: Vec<NodeHandle>,
2034    ) -> Result<()> {
2035        Err(ScriptError::phase_not_ready("element.append"))
2036    }
2037
2038    fn element_prepend(
2039        &mut self,
2040        _element: ElementHandle,
2041        _children: Vec<NodeHandle>,
2042    ) -> Result<()> {
2043        Err(ScriptError::phase_not_ready("element.prepend"))
2044    }
2045
2046    fn element_before(
2047        &mut self,
2048        _element: ElementHandle,
2049        _children: Vec<NodeHandle>,
2050    ) -> Result<()> {
2051        Err(ScriptError::phase_not_ready("element.before"))
2052    }
2053
2054    fn element_after(&mut self, _element: ElementHandle, _children: Vec<NodeHandle>) -> Result<()> {
2055        Err(ScriptError::phase_not_ready("element.after"))
2056    }
2057
2058    fn element_remove(&mut self, _element: ElementHandle) -> Result<()> {
2059        Err(ScriptError::phase_not_ready("element.remove"))
2060    }
2061
2062    fn element_query_selector(
2063        &mut self,
2064        _element: ElementHandle,
2065        _selector: &str,
2066    ) -> Result<Option<ElementHandle>> {
2067        Err(ScriptError::phase_not_ready("element.querySelector"))
2068    }
2069
2070    fn element_query_selector_all(
2071        &mut self,
2072        _element: ElementHandle,
2073        _selector: &str,
2074    ) -> Result<Vec<ElementHandle>> {
2075        Err(ScriptError::phase_not_ready("element.querySelectorAll"))
2076    }
2077
2078    fn element_matches(&mut self, _element: ElementHandle, _selector: &str) -> Result<bool> {
2079        Err(ScriptError::phase_not_ready("element.matches"))
2080    }
2081
2082    fn element_closest(
2083        &mut self,
2084        _element: ElementHandle,
2085        _selector: &str,
2086    ) -> Result<Option<ElementHandle>> {
2087        Err(ScriptError::phase_not_ready("element.closest"))
2088    }
2089
2090    fn register_event_listener(
2091        &mut self,
2092        _target: ListenerTarget,
2093        _event_type: &str,
2094        _handler: ScriptFunction,
2095    ) -> Result<()> {
2096        self.register_event_listener_with_capture(_target, _event_type, false, _handler)
2097    }
2098
2099    fn register_event_listener_with_capture(
2100        &mut self,
2101        _target: ListenerTarget,
2102        _event_type: &str,
2103        _capture: bool,
2104        _handler: ScriptFunction,
2105    ) -> Result<()> {
2106        Err(ScriptError::phase_not_ready("addEventListener"))
2107    }
2108}
2109
2110#[derive(Clone, Debug, Default)]
2111pub struct ScriptRuntime {
2112    parser: ScriptParser,
2113    evaluator: Evaluator,
2114    heap: ScriptHeap,
2115    globals: GlobalEnvironment,
2116    queued_microtasks: usize,
2117}
2118
2119impl ScriptRuntime {
2120    pub fn new() -> Self {
2121        Self::default()
2122    }
2123
2124    pub fn parser(&self) -> &ScriptParser {
2125        &self.parser
2126    }
2127
2128    pub fn evaluator(&self) -> &Evaluator {
2129        &self.evaluator
2130    }
2131
2132    pub fn heap(&self) -> &ScriptHeap {
2133        &self.heap
2134    }
2135
2136    pub fn globals(&self) -> &GlobalEnvironment {
2137        &self.globals
2138    }
2139
2140    pub fn eval_program<H: HostBindings>(
2141        &mut self,
2142        code: &str,
2143        source_name: &str,
2144        host: &mut H,
2145    ) -> Result<()> {
2146        host.on_eval(code, source_name)?;
2147        let program = self.parser.parse_program(code)?;
2148        self.evaluator.eval_program(&program, host)
2149    }
2150
2151    pub fn eval_program_with_bindings<H: HostBindings>(
2152        &mut self,
2153        code: &str,
2154        source_name: &str,
2155        host: &mut H,
2156        initial_bindings: BTreeMap<String, ScriptValue>,
2157    ) -> Result<()> {
2158        host.on_eval(code, source_name)?;
2159        let program = self.parser.parse_program(code)?;
2160        self.evaluator
2161            .eval_program_with_bindings(&program, host, initial_bindings)
2162    }
2163
2164    pub fn queue_microtask(&mut self) {
2165        self.queued_microtasks += 1;
2166    }
2167
2168    pub fn queued_microtasks(&self) -> usize {
2169        self.queued_microtasks
2170    }
2171
2172    pub fn run_microtasks<H: HostBindings>(&mut self, host: &mut H) -> Result<()> {
2173        while self.queued_microtasks > 0 {
2174            self.queued_microtasks -= 1;
2175            host.on_microtask_checkpoint()?;
2176        }
2177        Ok(())
2178    }
2179}
2180
2181impl ScriptParser {
2182    pub(crate) fn parse_program(&self, code: &str) -> Result<syntax::Program> {
2183        parser::parse_program(code)
2184    }
2185}
2186
2187impl Evaluator {
2188    pub(crate) fn eval_program<H: HostBindings>(
2189        &self,
2190        program: &syntax::Program,
2191        host: &mut H,
2192    ) -> Result<()> {
2193        evaluator::eval_program(program, host)
2194    }
2195
2196    pub(crate) fn eval_program_with_bindings<H: HostBindings>(
2197        &self,
2198        program: &syntax::Program,
2199        host: &mut H,
2200        mut initial_bindings: BTreeMap<String, ScriptValue>,
2201    ) -> Result<()> {
2202        match evaluator::eval_program_with_bindings(program, host, &mut initial_bindings)? {
2203            evaluator::EvalControl::Continue => Ok(()),
2204            evaluator::EvalControl::Return(_) => Err(ScriptError::new("return outside function")),
2205            evaluator::EvalControl::Break => Err(ScriptError::new("break outside loop")),
2206            evaluator::EvalControl::ContinueLoop => Err(ScriptError::new("continue outside loop")),
2207        }
2208    }
2209}
2210
2211#[cfg(test)]
2212mod tests {
2213    use super::{HostBindings, ScriptRuntime};
2214
2215    #[derive(Default)]
2216    struct RecordingHost {
2217        evals: Vec<(String, String)>,
2218        microtask_ticks: usize,
2219    }
2220
2221    impl HostBindings for RecordingHost {
2222        fn on_eval(&mut self, code: &str, source_name: &str) -> super::Result<()> {
2223            self.evals.push((source_name.to_owned(), code.to_owned()));
2224            Ok(())
2225        }
2226
2227        fn on_microtask_checkpoint(&mut self) -> super::Result<()> {
2228            self.microtask_ticks += 1;
2229            Ok(())
2230        }
2231
2232        fn document_scrolling_element(&mut self) -> super::Result<Option<super::ElementHandle>> {
2233            Ok(None)
2234        }
2235    }
2236
2237    #[test]
2238    fn eval_program_delegates_to_host_bindings() {
2239        let mut runtime = ScriptRuntime::new();
2240        let mut host = RecordingHost::default();
2241        runtime
2242            .eval_program("const value = 'x';", "inline-script", &mut host)
2243            .expect("host callback should succeed");
2244        assert_eq!(
2245            host.evals,
2246            vec![(
2247                "inline-script".to_string(),
2248                "const value = 'x';".to_string(),
2249            )]
2250        );
2251    }
2252
2253    #[test]
2254    fn queued_microtasks_are_drained_in_order() {
2255        let mut runtime = ScriptRuntime::new();
2256        let mut host = RecordingHost::default();
2257        runtime.queue_microtask();
2258        runtime.queue_microtask();
2259        runtime
2260            .run_microtasks(&mut host)
2261            .expect("microtasks should drain");
2262        assert_eq!(runtime.queued_microtasks(), 0);
2263        assert_eq!(host.microtask_ticks, 2);
2264    }
2265}