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}