1use std::collections::{HashMap, HashSet};
14
15use xfa_dom_resolver::data_dom::{DataDom, DataNodeId};
16use xfa_dom_resolver::som::{
17 parse_som, resolve_data_path, SomExpression, SomIndex, SomRoot, SomSelector,
18};
19use xfa_layout_engine::form::{FormNodeId, FormNodeType, FormTree, GroupKind, Presence};
20
21use super::RuntimeMetadata;
22
23pub const MAX_MUTATIONS_PER_DOC: usize = 4096;
25pub const MAX_INSTANCES_PER_SUBFORM: u32 = 256;
27pub const MAX_ITEMS_PER_LISTBOX: u32 = 4096;
29pub const MAX_RESOLVE_CALLS_PER_SCRIPT: u32 = 1024;
31pub const MAX_RESOLVE_RESULTS: usize = 256;
33pub const MAX_SOM_DEPTH: usize = 16;
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct MutationLogEntry {
39 pub node_id: FormNodeId,
41 pub script_idx: usize,
43 pub before: String,
45 pub after: String,
47}
48
49#[derive(Debug)]
51pub struct HostBindings {
52 form: *mut FormTree,
53 root_id: FormNodeId,
54 current_id: Option<FormNodeId>,
55 current_activity: Option<String>,
56 current_script_idx: usize,
57 next_script_idx: usize,
58 generation: u64,
59 mutation_log: Vec<MutationLogEntry>,
60 mutation_count_this_doc: usize,
61 resolve_count_this_script: u32,
62 metadata: RuntimeMetadata,
63 static_page_count: u32,
64 zero_instance_runs: HashMap<(FormNodeId, String), u64>,
65 data_dom: Option<*const DataDom>,
69 presave_gate: bool,
78 captured_occur_mutations: Vec<(FormNodeId, String, i64)>,
83 som_fail_log: Vec<crate::dynamic::SomFailEntry>,
86 instance_write_log: Vec<crate::dynamic::InstanceWriteEntry>,
89 declared_subform_names: HashSet<String>,
98 interactive: bool,
105}
106
107impl Default for HostBindings {
108 fn default() -> Self {
109 Self {
110 form: std::ptr::null_mut(),
111 root_id: FormNodeId(0),
112 current_id: None,
113 current_activity: None,
114 current_script_idx: 0,
115 next_script_idx: 0,
116 generation: 0,
117 mutation_log: Vec::new(),
118 mutation_count_this_doc: 0,
119 resolve_count_this_script: 0,
120 metadata: RuntimeMetadata::default(),
121 static_page_count: 0,
122 zero_instance_runs: HashMap::new(),
123 data_dom: None,
124 presave_gate: false,
125 captured_occur_mutations: Vec::new(),
126 som_fail_log: Vec::new(),
127 instance_write_log: Vec::new(),
128 declared_subform_names: HashSet::new(),
129 interactive: false,
130 }
131 }
132}
133
134impl HostBindings {
135 pub fn new() -> Self {
137 Self::default()
138 }
139
140 pub fn set_form_handle(&mut self, form: *mut FormTree, root_id: FormNodeId) {
142 self.form = form;
143 self.root_id = root_id;
144 if form.is_null() {
145 self.current_id = None;
146 self.current_activity = None;
147 self.current_script_idx = 0;
148 self.zero_instance_runs.clear();
149 }
150 }
151
152 pub fn reset_per_document(&mut self) {
161 self.form = std::ptr::null_mut();
162 self.root_id = FormNodeId(0);
163 self.current_id = None;
164 self.current_activity = None;
165 self.current_script_idx = 0;
166 self.next_script_idx = 0;
167 self.generation = self.generation.wrapping_add(1);
168 self.mutation_log.clear();
169 self.mutation_count_this_doc = 0;
170 self.resolve_count_this_script = 0;
171 self.metadata = RuntimeMetadata::default();
172 self.static_page_count = 0;
173 self.zero_instance_runs.clear();
174 self.captured_occur_mutations.clear();
175 self.presave_gate = false;
181 self.interactive = false;
185 }
186
187 pub fn set_data_handle(&mut self, dom: *const DataDom) {
191 self.data_dom = Some(dom);
192 }
193
194 pub fn set_declared_subform_names(&mut self, names: HashSet<String>) {
199 self.declared_subform_names = names;
200 }
201
202 pub fn is_declared_absent_node(&self, name: &str) -> bool {
211 self.declared_subform_names.contains(name)
212 }
213
214 pub fn reset_per_script(&mut self, current_id: FormNodeId, activity: Option<&str>) {
216 self.current_id = Some(current_id);
217 self.current_activity = activity.map(str::to_string);
218 self.current_script_idx = self.next_script_idx;
219 self.next_script_idx = self.next_script_idx.saturating_add(1);
220 self.resolve_count_this_script = 0;
221 }
222
223 pub fn set_static_page_count(&mut self, page_count: u32) {
225 self.static_page_count = page_count;
226 }
227
228 pub fn set_presave_gate(&mut self, enabled: bool) {
234 self.presave_gate = enabled;
235 }
236
237 #[doc(hidden)]
239 pub fn presave_gate(&self) -> bool {
240 self.presave_gate
241 }
242
243 pub fn generation(&self) -> u64 {
246 self.generation
247 }
248
249 pub fn current_node(&self) -> Option<FormNodeId> {
251 self.current_id
252 }
253
254 pub fn node_is_container(&mut self, node_id: FormNodeId, generation: u64) -> bool {
260 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
261 if !self.handle_is_live(node_id, generation) {
262 return false;
263 }
264 let Some(form) = self.form_ref() else {
265 return false;
266 };
267 is_instance_node(&form.get(node_id).node_type)
268 }
269
270 pub fn parent_of_node(&mut self, node_id: FormNodeId, generation: u64) -> Option<FormNodeId> {
277 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
278 if !self.handle_is_live(node_id, generation) {
279 return None;
280 }
281 let form = self.form_ref()?;
282 if self.root_id.0 >= form.nodes.len() {
283 return None;
284 }
285 let parents = build_parent_map(form, self.root_id);
286 parents.get(&node_id).copied()
287 }
288
289 pub fn take_metadata(&mut self) -> RuntimeMetadata {
291 std::mem::take(&mut self.metadata)
292 }
293
294 pub fn take_diag_logs(&mut self) -> crate::js_runtime::RuntimeDiagLogs {
296 crate::js_runtime::RuntimeDiagLogs {
297 som_fail_log: std::mem::take(&mut self.som_fail_log),
298 instance_write_log: std::mem::take(&mut self.instance_write_log),
299 }
300 }
301
302 pub fn mutation_log(&self) -> &[MutationLogEntry] {
304 &self.mutation_log
305 }
306
307 pub fn get_raw_value(&mut self, node_id: FormNodeId, generation: u64) -> Option<String> {
310 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
311 if !self.handle_is_live(node_id, generation) {
312 return None;
313 }
314 let Some(form) = self.form_ref() else {
315 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
316 return None;
317 };
318 if let FormNodeType::Field { value } = &form.get(node_id).node_type {
319 return Some(value.clone());
320 }
321 if self.interactive && form.meta(node_id).group_kind == GroupKind::ExclusiveChoice {
329 return form.get(node_id).children.iter().find_map(|&child| {
330 match &form.get(child).node_type {
331 FormNodeType::Field { value } if !value.is_empty() => Some(value.clone()),
332 _ => None,
333 }
334 });
335 }
336 None
337 }
338
339 pub fn node_name(&self, node_id: FormNodeId, generation: u64) -> Option<String> {
342 if !self.handle_is_live(node_id, generation) {
343 return None;
344 }
345 let form = self.form_ref()?;
346 Some(form.get(node_id).name.clone())
347 }
348
349 pub fn occur_resolve(&mut self, node_id: FormNodeId, generation: u64) -> bool {
352 self.metadata.occur_lookups_total = self.metadata.occur_lookups_total.saturating_add(1);
353 let live = self.handle_is_live(node_id, generation);
354 if live {
355 self.metadata.occur_lookup_successes =
356 self.metadata.occur_lookup_successes.saturating_add(1);
357 } else {
358 self.metadata.occur_lookup_failures =
359 self.metadata.occur_lookup_failures.saturating_add(1);
360 }
361 live
362 }
363
364 pub fn occur_read(&mut self, node_id: FormNodeId, generation: u64, prop: &str) -> i64 {
368 self.metadata.occur_property_reads = self.metadata.occur_property_reads.saturating_add(1);
369 if !self.handle_is_live(node_id, generation) {
370 return -1;
371 }
372 let Some(form) = self.form_ref() else {
373 return -1;
374 };
375 let occur = &form.get(node_id).occur;
376 match prop {
377 "min" => occur.min as i64,
378 "max" => occur.max.map(|m| m as i64).unwrap_or(-1),
379 "initial" => occur.initial as i64,
380 _ => -1,
381 }
382 }
383
384 pub fn occur_capture(
390 &mut self,
391 node_id: FormNodeId,
392 generation: u64,
393 prop: &str,
394 _value: i64,
395 ) -> bool {
396 let _ = generation;
397 self.metadata.occur_property_writes = self.metadata.occur_property_writes.saturating_add(1);
398 match prop {
399 "min" => {
400 self.metadata.occur_min_writes = self.metadata.occur_min_writes.saturating_add(1)
401 }
402 "max" => {
403 self.metadata.occur_max_writes = self.metadata.occur_max_writes.saturating_add(1)
404 }
405 _ => {}
406 }
407 self.metadata.occur_mutations_captured =
408 self.metadata.occur_mutations_captured.saturating_add(1);
409 if prop == "min" || prop == "max" {
413 self.captured_occur_mutations
414 .push((node_id, prop.to_string(), _value));
415 }
416 true
417 }
418
419 pub fn take_occur_mutations(&mut self) -> Vec<(FormNodeId, String, i64)> {
423 std::mem::take(&mut self.captured_occur_mutations)
424 }
425
426 pub fn set_raw_value(&mut self, node_id: FormNodeId, value: String, generation: u64) -> bool {
428 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
429 if !self.write_activity_allowed()
430 || !self.handle_is_live(node_id, generation)
431 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
432 {
433 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
434 return false;
435 }
436
437 let Some((before, after)) = self.write_field_value(node_id, value) else {
438 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
439 return false;
440 };
441
442 self.mutation_log.push(MutationLogEntry {
443 node_id,
444 script_idx: self.current_script_idx,
445 before,
446 after,
447 });
448 self.metadata.mutations = self.metadata.mutations.saturating_add(1);
449 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
450 true
451 }
452
453 pub fn set_interactive(&mut self, enabled: bool) {
456 self.interactive = enabled;
457 }
458
459 pub fn interactive(&self) -> bool {
462 self.interactive
463 }
464
465 pub fn set_presence(&mut self, node_id: FormNodeId, value: String, generation: u64) -> bool {
473 if !self.interactive {
479 return false;
480 }
481 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
482 if !self.write_activity_allowed()
483 || !self.handle_is_live(node_id, generation)
484 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
485 {
486 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
487 return false;
488 }
489
490 let new_presence = match value.trim().to_ascii_lowercase().as_str() {
491 "visible" | "open" => Presence::Visible,
492 "hidden" => Presence::Hidden,
493 "invisible" => Presence::Invisible,
494 "inactive" => Presence::Inactive,
495 _ => {
496 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
497 return false;
498 }
499 };
500
501 let Some(form) = self.form_mut() else {
502 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
503 return false;
504 };
505 let meta = form.meta_mut(node_id);
506 if meta.presence == new_presence {
507 return false;
508 }
509 meta.presence = new_presence;
510 self.metadata.mutations = self.metadata.mutations.saturating_add(1);
511 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
512 true
513 }
514
515 pub fn get_presence(&mut self, node_id: FormNodeId, generation: u64) -> String {
522 if !self.interactive {
523 return "visible".to_string();
524 }
525 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
526 if !self.handle_is_live(node_id, generation) {
527 return "visible".to_string();
528 }
529 let Some(form) = self.form_ref() else {
530 return "visible".to_string();
531 };
532 match form.meta(node_id).presence {
533 Presence::Visible => "visible",
534 Presence::Hidden => "hidden",
535 Presence::Invisible => "invisible",
536 Presence::Inactive => "inactive",
537 }
538 .to_string()
539 }
540
541 fn note_som(&mut self, resolved: bool, path: &str) {
547 self.metadata.som_lookups_total = self.metadata.som_lookups_total.saturating_add(1);
548 if resolved {
549 self.metadata.som_lookup_successes =
550 self.metadata.som_lookup_successes.saturating_add(1);
551 } else {
552 self.metadata.som_lookup_failures = self.metadata.som_lookup_failures.saturating_add(1);
553 if path == "occur" || path.starts_with("occur.") || path.starts_with("occur[") {
554 self.metadata.som_occur_path_refs =
555 self.metadata.som_occur_path_refs.saturating_add(1);
556 }
557 }
558 }
559
560 pub fn resolve_node(&mut self, path: &str) -> Option<FormNodeId> {
562 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
563 let nodes = match self.resolve_path(path) {
564 ResolveOutcome::Ok(nodes) => {
565 self.note_som(true, path);
566 nodes
567 }
568 ResolveOutcome::NoMatch => {
569 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
570 self.note_som(false, path);
571 self.debug_log_resolve_miss("resolve_node:NoMatch", path);
572 return None;
573 }
574 ResolveOutcome::BindingError => {
575 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
576 self.debug_log_resolve_miss("resolve_node:BindingError", path);
577 return None;
578 }
579 };
580
581 let Some(form) = self.form_ref() else {
582 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
583 return None;
584 };
585 let found = nodes
586 .into_iter()
587 .find(|node_id| matches!(form.get(*node_id).node_type, FormNodeType::Field { .. }));
588 if found.is_none() {
589 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
590 self.debug_log_resolve_miss("resolve_node:NoField", path);
591 }
592 found
593 }
594
595 pub fn resolve_nodes(&mut self, path: &str) -> Vec<FormNodeId> {
598 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
599 let nodes = match self.resolve_path(path) {
600 ResolveOutcome::Ok(nodes) => nodes,
601 ResolveOutcome::NoMatch => {
602 self.debug_log_resolve_miss("resolve_nodes:NoMatch", path);
603 return Vec::new();
604 }
605 ResolveOutcome::BindingError => {
606 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
607 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
608 self.debug_log_resolve_miss("resolve_nodes:BindingError", path);
609 return Vec::new();
610 }
611 };
612
613 let Some(form) = self.form_ref() else {
614 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
615 return Vec::new();
616 };
617 nodes
618 .into_iter()
619 .filter(|node_id| matches!(form.get(*node_id).node_type, FormNodeType::Field { .. }))
620 .take(MAX_RESOLVE_RESULTS)
621 .collect()
622 }
623
624 pub fn resolve_implicit(&mut self, current_id: FormNodeId, name: &str) -> Option<FormNodeId> {
631 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
632 match self.resolve_implicit_inner(current_id, name) {
633 ResolveOutcome::Ok(nodes) => {
634 self.note_som(true, name);
635 nodes.into_iter().next()
636 }
637 ResolveOutcome::NoMatch => {
638 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
639 self.note_som(false, name);
640 self.debug_log_resolve_miss("resolve_implicit:NoMatch", name);
641 None
642 }
643 ResolveOutcome::BindingError => {
644 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
645 None
646 }
647 }
648 }
649
650 pub fn resolve_implicit_candidates(
656 &mut self,
657 current_id: FormNodeId,
658 name: &str,
659 ) -> Vec<FormNodeId> {
660 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
661 match self.resolve_implicit_inner(current_id, name) {
662 ResolveOutcome::Ok(nodes) => {
663 self.note_som(true, name);
664 nodes
665 }
666 ResolveOutcome::NoMatch => {
667 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
668 self.note_som(false, name);
669 self.debug_log_resolve_miss("resolve_implicit_candidates:NoMatch", name);
670 Vec::new()
671 }
672 ResolveOutcome::BindingError => {
673 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
674 Vec::new()
675 }
676 }
677 }
678
679 pub fn resolve_implicit_candidates_quiet(
686 &mut self,
687 current_id: FormNodeId,
688 name: &str,
689 ) -> Vec<FormNodeId> {
690 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
691 match self.resolve_implicit_inner(current_id, name) {
692 ResolveOutcome::Ok(nodes) => nodes,
693 ResolveOutcome::NoMatch => Vec::new(),
694 ResolveOutcome::BindingError => {
695 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
696 Vec::new()
697 }
698 }
699 }
700
701 pub fn resolve_child(&mut self, parent_id: FormNodeId, name: &str) -> Option<FormNodeId> {
703 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
704 match self.resolve_child_inner(parent_id, name) {
705 ResolveOutcome::Ok(nodes) => {
706 self.note_som(true, name);
707 nodes.into_iter().next()
708 }
709 ResolveOutcome::NoMatch => {
710 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
711 self.note_som(false, name);
712 self.debug_log_resolve_miss("resolve_child:NoMatch", name);
713 None
714 }
715 ResolveOutcome::BindingError => {
716 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
717 None
718 }
719 }
720 }
721
722 pub fn resolve_child_candidates(
729 &mut self,
730 parent_ids: &[FormNodeId],
731 name: &str,
732 ) -> Vec<FormNodeId> {
733 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
734 match self.resolve_child_candidates_inner(parent_ids, name) {
735 ResolveOutcome::Ok(nodes) => {
736 self.note_som(true, name);
737 nodes
738 }
739 ResolveOutcome::NoMatch => {
740 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
741 self.note_som(false, name);
742 self.debug_log_resolve_miss("resolve_child_candidates:NoMatch", name);
743 Vec::new()
744 }
745 ResolveOutcome::BindingError => {
746 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
747 Vec::new()
748 }
749 }
750 }
751
752 pub fn resolve_child_candidates_quiet(
758 &mut self,
759 parent_ids: &[FormNodeId],
760 name: &str,
761 ) -> Vec<FormNodeId> {
762 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
763 match self.resolve_child_candidates_inner(parent_ids, name) {
764 ResolveOutcome::Ok(nodes) => nodes,
765 ResolveOutcome::NoMatch => Vec::new(),
766 ResolveOutcome::BindingError => {
767 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
768 Vec::new()
769 }
770 }
771 }
772
773 pub fn resolve_scoped_candidates(
779 &mut self,
780 scope_ids: &[FormNodeId],
781 name: &str,
782 ) -> Vec<FormNodeId> {
783 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
784 match self.resolve_scoped_candidates_inner(scope_ids, name) {
785 ResolveOutcome::Ok(nodes) => {
786 self.note_som(true, name);
787 nodes
788 }
789 ResolveOutcome::NoMatch => {
790 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
791 self.note_som(false, name);
792 self.debug_log_resolve_miss("resolve_scoped_candidates:NoMatch", name);
793 Vec::new()
794 }
795 ResolveOutcome::BindingError => {
796 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
797 Vec::new()
798 }
799 }
800 }
801
802 pub fn resolve_implicit_candidates_hinted(
815 &mut self,
816 current_id: FormNodeId,
817 name: &str,
818 next_hint: &str,
819 ) -> Vec<FormNodeId> {
820 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
821 let name = name.trim();
822 let hint = next_hint.trim();
823 if name.is_empty() || !self.consume_resolve_call() {
824 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
825 return Vec::new();
826 }
827 let Some(form) = self.form_ref() else {
828 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
829 return Vec::new();
830 };
831 if current_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
832 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
833 return Vec::new();
834 }
835 let parents = build_parent_map(form, self.root_id);
836 let baseline = match resolve_implicit_candidates_in_scope(form, &parents, current_id, name)
837 {
838 ResolveOutcome::Ok(nodes) => nodes,
839 ResolveOutcome::NoMatch => {
840 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
841 self.debug_log_resolve_miss("resolve_implicit_candidates_hinted:NoMatch", name);
842 return Vec::new();
843 }
844 ResolveOutcome::BindingError => {
845 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
846 return Vec::new();
847 }
848 };
849 if hint.is_empty() {
850 return baseline;
851 }
852 let baseline_hit: Vec<FormNodeId> = baseline
853 .iter()
854 .copied()
855 .filter(|n| subtree_contains_name(form, *n, hint, MAX_SOM_DEPTH))
856 .collect();
857 if !baseline_hit.is_empty() {
858 return baseline_hit;
859 }
860 let mut widened: Vec<FormNodeId> = Vec::new();
864 let mut scope = Some(current_id);
865 let mut depth = 0usize;
866 while let Some(scope_id) = scope {
867 if depth > MAX_SOM_DEPTH || scope_id.0 >= form.nodes.len() {
868 break;
869 }
870 let mut candidates =
871 collect_named_descendant_candidates(form, scope_id, name, MAX_SOM_DEPTH);
872 order_candidates(form, &mut candidates);
873 for node_id in candidates {
874 if !widened.contains(&node_id)
875 && subtree_contains_name(form, node_id, hint, MAX_SOM_DEPTH)
876 {
877 widened.push(node_id);
878 if widened.len() >= MAX_RESOLVE_CANDIDATES {
879 return widened;
880 }
881 }
882 }
883 scope = parents.get(&scope_id).copied();
884 depth += 1;
885 }
886 if widened.is_empty() {
887 baseline
888 } else {
889 widened
890 }
891 }
892
893 pub fn resolve_child_candidates_hinted(
900 &mut self,
901 parent_ids: &[FormNodeId],
902 name: &str,
903 next_hint: &str,
904 ) -> Vec<FormNodeId> {
905 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
906 let candidates = match self.resolve_child_candidates_inner(parent_ids, name) {
907 ResolveOutcome::Ok(nodes) => nodes,
908 ResolveOutcome::NoMatch => {
909 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
910 return Vec::new();
911 }
912 ResolveOutcome::BindingError => {
913 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
914 return Vec::new();
915 }
916 };
917 self.apply_lookahead_filter(candidates, next_hint)
918 }
919
920 fn apply_lookahead_filter(
921 &self,
922 candidates: Vec<FormNodeId>,
923 next_hint: &str,
924 ) -> Vec<FormNodeId> {
925 let hint = next_hint.trim();
926 if hint.is_empty() || candidates.len() <= 1 {
927 return candidates;
928 }
929 let Some(form) = self.form_ref() else {
930 return candidates;
931 };
932 let filtered: Vec<FormNodeId> = candidates
933 .iter()
934 .copied()
935 .filter(|node_id| subtree_contains_name(form, *node_id, hint, MAX_SOM_DEPTH))
936 .collect();
937 if filtered.is_empty() {
938 candidates
939 } else {
940 filtered
941 }
942 }
943
944 pub fn resolve_with_full_chain(
970 &mut self,
971 parent_ids: &[FormNodeId],
972 chain: &[String],
973 implicit_origin: Option<FormNodeId>,
974 ) -> Vec<FormNodeId> {
975 self.resolve_with_full_chain_impl(parent_ids, chain, implicit_origin, false)
976 }
977
978 pub fn resolve_with_full_chain_strict(
982 &mut self,
983 parent_ids: &[FormNodeId],
984 chain: &[String],
985 implicit_origin: Option<FormNodeId>,
986 ) -> Vec<FormNodeId> {
987 self.resolve_with_full_chain_impl(parent_ids, chain, implicit_origin, true)
988 }
989
990 fn resolve_with_full_chain_impl(
991 &mut self,
992 parent_ids: &[FormNodeId],
993 chain: &[String],
994 implicit_origin: Option<FormNodeId>,
995 strict: bool,
996 ) -> Vec<FormNodeId> {
997 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
998 if !self.consume_resolve_call() {
999 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1000 return Vec::new();
1001 }
1002 let Some(form) = self.form_ref() else {
1003 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1004 return Vec::new();
1005 };
1006 if self.root_id.0 >= form.nodes.len() {
1007 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1008 return Vec::new();
1009 }
1010 if parent_ids
1011 .iter()
1012 .any(|node_id| node_id.0 >= form.nodes.len())
1013 {
1014 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1015 return Vec::new();
1016 }
1017 if chain.is_empty() {
1018 return parent_ids.to_vec();
1019 }
1020 if chain.len() > MAX_SOM_DEPTH {
1021 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1022 return Vec::new();
1023 }
1024 for seg in chain {
1025 if seg.trim().is_empty() {
1026 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1027 return Vec::new();
1028 }
1029 }
1030
1031 let parents = build_parent_map(form, self.root_id);
1032
1033 let entry_name = chain[0].trim();
1035 let initial: Vec<FormNodeId> = if parent_ids.is_empty() {
1036 let Some(origin) = implicit_origin else {
1037 return Vec::new();
1038 };
1039 if origin.0 >= form.nodes.len() {
1040 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1041 return Vec::new();
1042 }
1043 match resolve_implicit_candidates_in_scope(form, &parents, origin, entry_name) {
1044 ResolveOutcome::Ok(nodes) => nodes,
1045 ResolveOutcome::NoMatch => return Vec::new(),
1046 ResolveOutcome::BindingError => {
1047 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1048 return Vec::new();
1049 }
1050 }
1051 } else {
1052 let mut direct = Vec::new();
1057 for &parent_id in parent_ids {
1058 for &child_id in &form.get(parent_id).children {
1059 if form.get(child_id).name == entry_name {
1060 push_unique_candidate(&mut direct, child_id);
1061 if direct.len() >= MAX_RESOLVE_CANDIDATES {
1062 break;
1063 }
1064 }
1065 }
1066 if direct.len() >= MAX_RESOLVE_CANDIDATES {
1067 break;
1068 }
1069 }
1070 if !direct.is_empty() {
1071 direct
1072 } else {
1073 let mut descendants = Vec::new();
1074 for &parent_id in parent_ids {
1075 let mut local = collect_named_descendant_candidates(
1076 form,
1077 parent_id,
1078 entry_name,
1079 MAX_SOM_DEPTH,
1080 );
1081 order_candidates(form, &mut local);
1082 for node_id in local {
1083 push_unique_candidate(&mut descendants, node_id);
1084 if descendants.len() >= MAX_RESOLVE_CANDIDATES {
1085 break;
1086 }
1087 }
1088 if descendants.len() >= MAX_RESOLVE_CANDIDATES {
1089 break;
1090 }
1091 }
1092 descendants
1093 }
1094 };
1095
1096 if initial.is_empty() {
1097 return Vec::new();
1098 }
1099
1100 let mut layer = initial;
1104 for seg in chain.iter().skip(1) {
1105 let seg_name = seg.trim();
1106 let mut next_layer: Vec<FormNodeId> = Vec::new();
1107 for &node_id in &layer {
1108 let mut found_any = false;
1110 for &child_id in &form.get(node_id).children {
1111 if form.get(child_id).name == seg_name {
1112 push_unique_candidate(&mut next_layer, child_id);
1113 found_any = true;
1114 if next_layer.len() >= MAX_RESOLVE_CANDIDATES {
1115 break;
1116 }
1117 }
1118 }
1119 if !found_any {
1120 if let ResolveOutcome::Ok(scoped) =
1123 resolve_implicit_candidates_in_scope(form, &parents, node_id, seg_name)
1124 {
1125 for cand in scoped {
1126 push_unique_candidate(&mut next_layer, cand);
1127 if next_layer.len() >= MAX_RESOLVE_CANDIDATES {
1128 break;
1129 }
1130 }
1131 }
1132 }
1133 if next_layer.len() >= MAX_RESOLVE_CANDIDATES {
1134 break;
1135 }
1136 }
1137 if next_layer.is_empty() {
1138 if strict {
1145 return Vec::new();
1146 }
1147 return layer;
1148 }
1149 layer = next_layer;
1150 }
1151 layer
1152 }
1153
1154 pub fn instance_count(&mut self, parent_id: FormNodeId) -> u32 {
1156 self.instance_count_inner(parent_id, None)
1157 }
1158
1159 pub fn instance_count_for_handle(&mut self, parent_id: FormNodeId, generation: u64) -> u32 {
1161 self.instance_count_inner(parent_id, Some(generation))
1162 }
1163
1164 pub fn instance_index(&mut self, node_id: FormNodeId) -> u32 {
1166 self.instance_index_inner(node_id, None)
1167 }
1168
1169 pub fn instance_index_for_handle(&mut self, node_id: FormNodeId, generation: u64) -> u32 {
1171 self.instance_index_inner(node_id, Some(generation))
1172 }
1173
1174 pub fn has_zero_instance_run(
1179 &mut self,
1180 parent_id: FormNodeId,
1181 generation: u64,
1182 name: &str,
1183 ) -> bool {
1184 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1185 let name = name.trim();
1186 if name.is_empty() || !self.consume_resolve_call() {
1187 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1188 return false;
1189 }
1190 let Some(form) = self.form_ref() else {
1191 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1192 return false;
1193 };
1194 if generation != self.generation || parent_id.0 >= form.nodes.len() {
1195 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1196 return false;
1197 }
1198 self.zero_instance_runs
1199 .contains_key(&(parent_id, name.to_string()))
1200 }
1201
1202 #[allow(clippy::result_unit_err)]
1205 pub fn instance_set(&mut self, parent_id: FormNodeId, n: u32) -> Result<u32, ()> {
1206 self.instance_set_inner(parent_id, None, n)
1207 }
1208
1209 #[allow(clippy::result_unit_err)]
1211 pub fn instance_set_for_handle(
1212 &mut self,
1213 parent_id: FormNodeId,
1214 generation: u64,
1215 n: u32,
1216 ) -> Result<u32, ()> {
1217 self.instance_set_inner(parent_id, Some(generation), n)
1218 }
1219
1220 #[allow(clippy::result_unit_err)]
1222 pub fn instance_add(&mut self, parent_id: FormNodeId) -> Result<FormNodeId, ()> {
1223 self.instance_add_inner(parent_id, None)
1224 }
1225
1226 #[allow(clippy::result_unit_err)]
1228 pub fn instance_add_for_handle(
1229 &mut self,
1230 parent_id: FormNodeId,
1231 generation: u64,
1232 ) -> Result<FormNodeId, ()> {
1233 self.instance_add_inner(parent_id, Some(generation))
1234 }
1235
1236 #[allow(clippy::result_unit_err)]
1238 pub fn instance_remove(&mut self, parent_id: FormNodeId, index: u32) -> Result<(), ()> {
1239 self.instance_remove_inner(parent_id, None, index)
1240 }
1241
1242 #[allow(clippy::result_unit_err)]
1244 pub fn instance_remove_for_handle(
1245 &mut self,
1246 parent_id: FormNodeId,
1247 generation: u64,
1248 index: u32,
1249 ) -> Result<(), ()> {
1250 self.instance_remove_inner(parent_id, Some(generation), index)
1251 }
1252
1253 #[allow(clippy::result_unit_err)]
1255 pub fn list_clear(&mut self, field_id: FormNodeId) -> Result<(), ()> {
1256 self.list_clear_inner(field_id, None)
1257 }
1258
1259 #[allow(clippy::result_unit_err)]
1261 pub fn list_clear_for_handle(
1262 &mut self,
1263 field_id: FormNodeId,
1264 generation: u64,
1265 ) -> Result<(), ()> {
1266 self.list_clear_inner(field_id, Some(generation))
1267 }
1268
1269 #[allow(clippy::result_unit_err)]
1271 pub fn list_add(
1272 &mut self,
1273 field_id: FormNodeId,
1274 display: String,
1275 save: Option<String>,
1276 ) -> Result<(), ()> {
1277 self.list_add_inner(field_id, None, display, save)
1278 }
1279
1280 #[allow(clippy::result_unit_err)]
1282 pub fn list_add_for_handle(
1283 &mut self,
1284 field_id: FormNodeId,
1285 generation: u64,
1286 display: String,
1287 save: Option<String>,
1288 ) -> Result<(), ()> {
1289 self.list_add_inner(field_id, Some(generation), display, save)
1290 }
1291
1292 pub fn bound_item_for_handle(
1303 &mut self,
1304 field_id: FormNodeId,
1305 generation: u64,
1306 display_value: String,
1307 ) -> String {
1308 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1309 if !self.handle_is_live(field_id, generation) {
1310 return display_value;
1311 }
1312 let Some(form) = self.form_ref() else {
1313 return display_value;
1314 };
1315 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
1316 return display_value;
1317 }
1318 let meta = form.meta(field_id);
1319 for (display, save) in &meta.runtime_listbox_items {
1320 if display == &display_value {
1321 return save.clone();
1322 }
1323 }
1324 for (idx, display) in meta.display_items.iter().enumerate() {
1325 if display == &display_value {
1326 return meta
1327 .save_items
1328 .get(idx)
1329 .cloned()
1330 .unwrap_or_else(|| display_value.clone());
1331 }
1332 }
1333 display_value
1334 }
1335
1336 pub fn num_pages(&mut self) -> u32 {
1338 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1339 self.static_page_count
1340 }
1341
1342 pub fn metadata_binding_error(&mut self) {
1344 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1345 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1346 }
1347
1348 pub fn metadata_resolve_failure(&mut self) {
1350 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1351 self.metadata.resolve_failures = self.metadata.resolve_failures.saturating_add(1);
1352 }
1353
1354 pub fn metadata_unsupported_host_call(&mut self) {
1361 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1362 self.metadata.unsupported_host_calls =
1363 self.metadata.unsupported_host_calls.saturating_add(1);
1364 }
1365
1366 pub fn metadata_probe_skip(&mut self) {
1371 self.metadata.probe_skips = self.metadata.probe_skips.saturating_add(1);
1372 }
1373
1374 pub fn metadata_som_data_root_hit(&mut self) {
1376 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1377 self.metadata.som_data_root_hits = self.metadata.som_data_root_hits.saturating_add(1);
1378 }
1379
1380 pub fn metadata_som_items_path_hit(&mut self) {
1382 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1383 self.metadata.som_items_path_hits = self.metadata.som_items_path_hits.saturating_add(1);
1384 }
1385
1386 fn consume_resolve_call(&mut self) -> bool {
1387 if self.resolve_count_this_script >= MAX_RESOLVE_CALLS_PER_SCRIPT {
1388 return false;
1389 }
1390 self.resolve_count_this_script = self.resolve_count_this_script.saturating_add(1);
1391 true
1392 }
1393
1394 fn resolve_path(&mut self, path: &str) -> ResolveOutcome {
1395 if !self.consume_resolve_call() {
1396 return ResolveOutcome::BindingError;
1397 }
1398
1399 let Some(form) = self.form_ref() else {
1400 return ResolveOutcome::BindingError;
1401 };
1402 let Some(current_id) = self.current_id else {
1403 return ResolveOutcome::BindingError;
1404 };
1405 if current_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
1406 return ResolveOutcome::BindingError;
1407 }
1408
1409 let normalized = normalize_resolve_path(path.trim());
1410 let mut expr = match parse_som(&normalized) {
1411 Ok(expr) => expr,
1412 Err(_) => return ResolveOutcome::BindingError,
1413 };
1414 if expr.segments.len() > MAX_SOM_DEPTH {
1415 return ResolveOutcome::BindingError;
1416 }
1417 if matches!(
1418 expr.segments.last().map(|segment| &segment.selector),
1419 Some(SomSelector::Name(name)) if name == "rawValue"
1420 ) {
1421 expr.segments.pop();
1422 }
1423
1424 let parents = build_parent_map(form, self.root_id);
1425 let resolver = HostSomResolver {
1426 form,
1427 root_id: self.root_id,
1428 parents: &parents,
1429 current_id,
1430 };
1431 match resolver.resolve_expression(&expr) {
1432 Some(nodes) if !nodes.is_empty() => ResolveOutcome::Ok(nodes),
1433 _ => ResolveOutcome::NoMatch,
1434 }
1435 }
1436
1437 fn resolve_implicit_inner(&mut self, current_id: FormNodeId, name: &str) -> ResolveOutcome {
1438 let name = name.trim();
1439 if name.is_empty() || !self.consume_resolve_call() {
1440 return ResolveOutcome::BindingError;
1441 }
1442
1443 let Some(form) = self.form_ref() else {
1444 return ResolveOutcome::BindingError;
1445 };
1446 if current_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
1447 return ResolveOutcome::BindingError;
1448 }
1449
1450 let parents = build_parent_map(form, self.root_id);
1451 resolve_implicit_candidates_in_scope(form, &parents, current_id, name)
1452 }
1453
1454 fn resolve_child_inner(&mut self, parent_id: FormNodeId, name: &str) -> ResolveOutcome {
1455 self.resolve_child_candidates_inner(&[parent_id], name)
1456 }
1457
1458 fn resolve_child_candidates_inner(
1459 &mut self,
1460 parent_ids: &[FormNodeId],
1461 name: &str,
1462 ) -> ResolveOutcome {
1463 let name = name.trim();
1464 if name.is_empty() || !self.consume_resolve_call() {
1465 return ResolveOutcome::BindingError;
1466 }
1467
1468 let Some(form) = self.form_ref() else {
1469 return ResolveOutcome::BindingError;
1470 };
1471 if parent_ids.is_empty()
1472 || parent_ids
1473 .iter()
1474 .any(|node_id| node_id.0 >= form.nodes.len())
1475 {
1476 return ResolveOutcome::BindingError;
1477 }
1478
1479 let mut direct = Vec::new();
1480 for &parent_id in parent_ids {
1481 for &child_id in &form.get(parent_id).children {
1482 if form.get(child_id).name == name {
1483 push_unique_candidate(&mut direct, child_id);
1484 if direct.len() >= MAX_RESOLVE_CANDIDATES {
1485 return ResolveOutcome::Ok(direct);
1486 }
1487 }
1488 }
1489 }
1490 if !direct.is_empty() {
1491 return ResolveOutcome::Ok(direct);
1492 }
1493
1494 let mut descendants = Vec::new();
1495 for &parent_id in parent_ids {
1496 let mut local =
1497 collect_named_descendant_candidates(form, parent_id, name, MAX_SOM_DEPTH);
1498 order_candidates(form, &mut local);
1499 for node_id in local {
1500 push_unique_candidate(&mut descendants, node_id);
1501 if descendants.len() >= MAX_RESOLVE_CANDIDATES {
1502 return ResolveOutcome::Ok(descendants);
1503 }
1504 }
1505 }
1506 if descendants.is_empty() {
1507 ResolveOutcome::NoMatch
1508 } else {
1509 ResolveOutcome::Ok(descendants)
1510 }
1511 }
1512
1513 fn resolve_scoped_candidates_inner(
1514 &mut self,
1515 scope_ids: &[FormNodeId],
1516 name: &str,
1517 ) -> ResolveOutcome {
1518 let name = name.trim();
1519 if name.is_empty() || !self.consume_resolve_call() {
1520 return ResolveOutcome::BindingError;
1521 }
1522
1523 let Some(form) = self.form_ref() else {
1524 return ResolveOutcome::BindingError;
1525 };
1526 if scope_ids.is_empty()
1527 || self.root_id.0 >= form.nodes.len()
1528 || scope_ids
1529 .iter()
1530 .any(|node_id| node_id.0 >= form.nodes.len())
1531 {
1532 return ResolveOutcome::BindingError;
1533 }
1534
1535 let parents = build_parent_map(form, self.root_id);
1536 let mut out = Vec::new();
1537 for &scope_id in scope_ids {
1538 if let ResolveOutcome::Ok(nodes) =
1539 resolve_implicit_candidates_in_scope(form, &parents, scope_id, name)
1540 {
1541 for node_id in nodes {
1542 push_unique_candidate(&mut out, node_id);
1543 if out.len() >= MAX_RESOLVE_CANDIDATES {
1544 return ResolveOutcome::Ok(out);
1545 }
1546 }
1547 }
1548 }
1549 if out.is_empty() {
1550 ResolveOutcome::NoMatch
1551 } else {
1552 ResolveOutcome::Ok(out)
1553 }
1554 }
1555
1556 fn instance_count_inner(&mut self, parent_id: FormNodeId, generation: Option<u64>) -> u32 {
1557 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1558 let Some(run) = self.read_instance_run(parent_id, generation) else {
1559 return 0;
1560 };
1561 run.nodes.len() as u32
1562 }
1563
1564 fn instance_index_inner(&mut self, node_id: FormNodeId, generation: Option<u64>) -> u32 {
1565 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1566 let Some(run) = self.read_instance_run(node_id, generation) else {
1567 return 0;
1568 };
1569 run.nodes
1570 .iter()
1571 .position(|candidate| *candidate == node_id)
1572 .unwrap_or(0) as u32
1573 }
1574
1575 fn instance_set_inner(
1576 &mut self,
1577 parent_id: FormNodeId,
1578 generation: Option<u64>,
1579 n: u32,
1580 ) -> Result<u32, ()> {
1581 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1582 if !self.write_activity_allowed()
1583 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1584 || !self.consume_resolve_call()
1585 {
1586 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1587 return Err(());
1588 }
1589
1590 let Some(run) = self.live_instance_run(parent_id, generation) else {
1591 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1592 return Err(());
1593 };
1594 let Some(target_count) = self.clamped_instance_count(run.prototype_id, n) else {
1595 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1596 return Err(());
1597 };
1598
1599 let target_count = target_count as usize;
1600 let prototype_id = run.prototype_id;
1601 let parent_id = run.parent_id;
1602 let first_pos = run.first_position;
1603 let Some(prototype_name) = self
1604 .form_ref()
1605 .and_then(|form| form.nodes.get(prototype_id.0))
1606 .map(|node| node.name.clone())
1607 else {
1608 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1609 return Err(());
1610 };
1611 let remove_ids = run.nodes;
1612 let old_count_set = remove_ids.len();
1613 let parent_name_set = self
1615 .form_ref()
1616 .and_then(|form| form.nodes.get(parent_id.0))
1617 .map(|n| n.name.clone())
1618 .unwrap_or_default();
1619
1620 let mut new_ids = Vec::with_capacity(target_count);
1621 if target_count > 0 {
1622 self.normalize_instance_occurrence(prototype_id);
1623 new_ids.push(prototype_id);
1624 for _ in 1..target_count {
1625 let cloned_id = self.clone_subtree(prototype_id)?;
1626 new_ids.push(cloned_id);
1627 }
1628 }
1629
1630 let Some(form) = self.form_mut() else {
1631 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1632 return Err(());
1633 };
1634 let parent = form.get_mut(parent_id);
1635 parent
1636 .children
1637 .retain(|child_id| !remove_ids.contains(child_id));
1638 let insert_pos = first_pos.min(parent.children.len());
1639 for (offset, node_id) in new_ids.iter().copied().enumerate() {
1640 parent.children.insert(insert_pos + offset, node_id);
1641 }
1642
1643 let key = (parent_id, prototype_name.clone());
1644 if target_count == 0 {
1645 self.zero_instance_runs.insert(key, self.generation);
1646 } else {
1647 self.zero_instance_runs.remove(&key);
1648 }
1649 self.record_instance_write();
1650 self.record_instance_write_detail(
1651 parent_id,
1652 &parent_name_set,
1653 &prototype_name,
1654 old_count_set,
1655 target_count,
1656 );
1657 Ok(target_count as u32)
1658 }
1659
1660 fn instance_add_inner(
1661 &mut self,
1662 parent_id: FormNodeId,
1663 generation: Option<u64>,
1664 ) -> Result<FormNodeId, ()> {
1665 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1666 if !self.write_activity_allowed()
1667 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1668 || !self.consume_resolve_call()
1669 {
1670 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1671 return Err(());
1672 }
1673
1674 let Some(run) = self.live_instance_run(parent_id, generation) else {
1675 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1676 return Err(());
1677 };
1678 let zero_key = self
1679 .form_ref()
1680 .and_then(|form| form.nodes.get(run.prototype_id.0))
1681 .map(|node| (run.parent_id, node.name.clone()));
1682 let add_proto_name = self
1684 .form_ref()
1685 .and_then(|form| form.nodes.get(run.prototype_id.0))
1686 .map(|n| n.name.clone())
1687 .unwrap_or_default();
1688 let add_parent_name = self
1689 .form_ref()
1690 .and_then(|form| form.nodes.get(run.parent_id.0))
1691 .map(|n| n.name.clone())
1692 .unwrap_or_default();
1693 let add_old_count = run.nodes.len();
1694 let add_parent_id = run.parent_id;
1695 let Some(max_allowed) = self.max_instances_for(run.prototype_id) else {
1696 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1697 return Err(());
1698 };
1699 if run.nodes.len() as u32 >= max_allowed {
1700 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1701 return Err(());
1702 }
1703
1704 let cloned_id = self.clone_subtree(run.prototype_id)?;
1705 let Some(form) = self.form_mut() else {
1706 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1707 return Err(());
1708 };
1709 form.get_mut(run.parent_id)
1710 .children
1711 .insert(run.last_position + 1, cloned_id);
1712
1713 if let Some(key) = zero_key {
1714 self.zero_instance_runs.remove(&key);
1715 }
1716 self.record_instance_write();
1717 self.record_instance_write_detail(
1718 add_parent_id,
1719 &add_parent_name,
1720 &add_proto_name,
1721 add_old_count,
1722 add_old_count + 1,
1723 );
1724 Ok(cloned_id)
1725 }
1726
1727 fn instance_remove_inner(
1728 &mut self,
1729 parent_id: FormNodeId,
1730 generation: Option<u64>,
1731 index: u32,
1732 ) -> Result<(), ()> {
1733 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1734 if !self.write_activity_allowed()
1735 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1736 || !self.consume_resolve_call()
1737 {
1738 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1739 return Err(());
1740 }
1741
1742 let Some(run) = self.live_instance_run(parent_id, generation) else {
1743 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1744 return Err(());
1745 };
1746 let zero_key = self
1747 .form_ref()
1748 .and_then(|form| form.nodes.get(run.prototype_id.0))
1749 .map(|node| (run.parent_id, node.name.clone()));
1750 let rm_proto_name = self
1752 .form_ref()
1753 .and_then(|form| form.nodes.get(run.prototype_id.0))
1754 .map(|n| n.name.clone())
1755 .unwrap_or_default();
1756 let rm_parent_name = self
1757 .form_ref()
1758 .and_then(|form| form.nodes.get(run.parent_id.0))
1759 .map(|n| n.name.clone())
1760 .unwrap_or_default();
1761 let rm_old_count = run.nodes.len();
1762 let rm_parent_id = run.parent_id;
1763 let min_allowed = self
1764 .form_ref()
1765 .and_then(|form| form.nodes.get(run.prototype_id.0))
1766 .map(|node| node.occur.min)
1767 .unwrap_or(1);
1768 if run.nodes.len() as u32 <= min_allowed {
1769 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1770 return Err(());
1771 }
1772 let Some(remove_position) = run.positions.get(index as usize).copied() else {
1773 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1774 return Err(());
1775 };
1776
1777 let Some(form) = self.form_mut() else {
1778 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1779 return Err(());
1780 };
1781 form.get_mut(run.parent_id).children.remove(remove_position);
1782
1783 if let Some(key) = zero_key {
1784 if run.nodes.len() == 1 {
1785 self.zero_instance_runs.insert(key, self.generation);
1786 } else {
1787 self.zero_instance_runs.remove(&key);
1788 }
1789 }
1790 self.record_instance_write();
1791 self.record_instance_write_detail(
1792 rm_parent_id,
1793 &rm_parent_name,
1794 &rm_proto_name,
1795 rm_old_count,
1796 rm_old_count.saturating_sub(1),
1797 );
1798 Ok(())
1799 }
1800
1801 fn read_instance_run(
1802 &mut self,
1803 node_id: FormNodeId,
1804 generation: Option<u64>,
1805 ) -> Option<InstanceRun> {
1806 if !self.consume_resolve_call() {
1807 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1808 return None;
1809 }
1810 let Some(form) = self.form_ref() else {
1811 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1812 return None;
1813 };
1814 if generation.is_some_and(|value| value != self.generation) {
1815 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1816 return None;
1817 }
1818 if node_id.0 >= form.nodes.len() || self.root_id.0 >= form.nodes.len() {
1819 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1820 return None;
1821 }
1822
1823 let parents = build_parent_map(form, self.root_id);
1824 if !parents.contains_key(&node_id) {
1825 if node_id == self.root_id {
1826 return Some(InstanceRun {
1827 parent_id: node_id,
1828 positions: vec![0],
1829 nodes: vec![node_id],
1830 prototype_id: node_id,
1831 first_position: 0,
1832 last_position: 0,
1833 });
1834 }
1835 return None;
1836 }
1837
1838 build_instance_run(form, &parents, node_id)
1839 }
1840
1841 fn live_instance_run(
1842 &self,
1843 node_id: FormNodeId,
1844 generation: Option<u64>,
1845 ) -> Option<InstanceRun> {
1846 let form = self.form_ref()?;
1847 if generation.is_some_and(|value| value != self.generation)
1848 || node_id.0 >= form.nodes.len()
1849 || self.root_id.0 >= form.nodes.len()
1850 || !is_instance_node(&form.get(node_id).node_type)
1851 {
1852 return None;
1853 }
1854 let parents = build_parent_map(form, self.root_id);
1855 build_instance_run(form, &parents, node_id)
1856 }
1857
1858 fn clamped_instance_count(&self, prototype_id: FormNodeId, requested: u32) -> Option<u32> {
1859 let min_allowed = self.form_ref()?.get(prototype_id).occur.min;
1860 let max_allowed = self.max_instances_for(prototype_id)?;
1861 if min_allowed > max_allowed {
1862 return None;
1863 }
1864 Some(requested.clamp(min_allowed, max_allowed))
1865 }
1866
1867 fn max_instances_for(&self, prototype_id: FormNodeId) -> Option<u32> {
1868 let occur = &self.form_ref()?.get(prototype_id).occur;
1869 let max = occur.max.unwrap_or(u32::MAX);
1870 Some(max.min(MAX_INSTANCES_PER_SUBFORM))
1871 }
1872
1873 fn clone_subtree(&mut self, source_id: FormNodeId) -> Result<FormNodeId, ()> {
1874 let (mut new_node, mut new_meta, child_ids) = {
1875 let Some(form) = self.form_ref() else {
1876 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1877 return Err(());
1878 };
1879 if source_id.0 >= form.nodes.len() {
1880 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1881 return Err(());
1882 }
1883 (
1884 form.get(source_id).clone(),
1885 form.meta(source_id).clone(),
1886 form.get(source_id).children.clone(),
1887 )
1888 };
1889
1890 let mut new_children = Vec::with_capacity(child_ids.len());
1891 for child_id in child_ids {
1892 new_children.push(self.clone_subtree(child_id)?);
1893 }
1894 new_node.children = new_children;
1895 new_node.occur.initial = 1;
1898 new_meta.xfa_id = None;
1899
1900 let Some(form) = self.form_mut() else {
1901 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1902 return Err(());
1903 };
1904 Ok(form.add_node_with_meta(new_node, new_meta))
1905 }
1906
1907 fn normalize_instance_occurrence(&mut self, node_id: FormNodeId) {
1908 if let Some(form) = self.form_mut() {
1909 if let Some(node) = form.nodes.get_mut(node_id.0) {
1910 node.occur.initial = 1;
1913 }
1914 }
1915 }
1916
1917 fn record_instance_write(&mut self) {
1918 self.metadata.instance_writes = self.metadata.instance_writes.saturating_add(1);
1919 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
1920 }
1921
1922 fn record_instance_write_detail(
1926 &mut self,
1927 parent_id: FormNodeId,
1928 parent_name: &str,
1929 proto_name: &str,
1930 old_count: usize,
1931 new_count: usize,
1932 ) {
1933 if !crate::dynamic::runtime_diag_enabled() || self.instance_write_log.len() >= 200 {
1934 return;
1935 }
1936 let parent_node_id = parent_id.0;
1937 self.instance_write_log
1938 .push(crate::dynamic::InstanceWriteEntry {
1939 script_idx: self.current_script_idx,
1940 activity: self.current_activity.as_deref().unwrap_or("").to_string(),
1941 parent_node_id,
1942 parent_node_name: parent_name.to_string(),
1943 prototype_node_name: proto_name.to_string(),
1944 old_count,
1945 new_count,
1946 });
1947 }
1948
1949 fn record_list_write(&mut self) {
1950 self.metadata.list_writes = self.metadata.list_writes.saturating_add(1);
1951 self.mutation_count_this_doc = self.mutation_count_this_doc.saturating_add(1);
1952 }
1953
1954 fn list_clear_inner(
1955 &mut self,
1956 field_id: FormNodeId,
1957 generation: Option<u64>,
1958 ) -> Result<(), ()> {
1959 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1960 if !self.write_activity_allowed()
1961 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1962 || !self.consume_resolve_call()
1963 {
1964 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1965 return Err(());
1966 }
1967 let current_generation = self.generation;
1968 let Some(form) = self.form_mut() else {
1969 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1970 return Err(());
1971 };
1972 if generation.is_some_and(|value| value != current_generation)
1973 || field_id.0 >= form.nodes.len()
1974 {
1975 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1976 return Err(());
1977 }
1978 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
1979 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
1980 return Err(());
1981 }
1982 form.meta_mut(field_id).runtime_listbox_items.clear();
1983 self.record_list_write();
1984 Ok(())
1985 }
1986
1987 fn list_add_inner(
1988 &mut self,
1989 field_id: FormNodeId,
1990 generation: Option<u64>,
1991 display: String,
1992 save: Option<String>,
1993 ) -> Result<(), ()> {
1994 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
1995 if !self.write_activity_allowed()
1996 || self.mutation_count_this_doc >= MAX_MUTATIONS_PER_DOC
1997 || !self.consume_resolve_call()
1998 {
1999 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2000 return Err(());
2001 }
2002 let current_generation = self.generation;
2003 let Some(form) = self.form_mut() else {
2004 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2005 return Err(());
2006 };
2007 if generation.is_some_and(|value| value != current_generation)
2008 || field_id.0 >= form.nodes.len()
2009 {
2010 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2011 return Err(());
2012 }
2013 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
2014 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2015 return Err(());
2016 }
2017 let meta = form.meta_mut(field_id);
2018 if meta.runtime_listbox_items.len() >= MAX_ITEMS_PER_LISTBOX as usize {
2019 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2020 return Err(());
2021 }
2022 let save_value = save.unwrap_or_else(|| display.clone());
2023 meta.runtime_listbox_items.push((display, save_value));
2024 self.record_list_write();
2025 Ok(())
2026 }
2027
2028 fn write_activity_allowed(&self) -> bool {
2029 let base = matches!(
2030 self.current_activity.as_deref(),
2031 Some("initialize")
2032 | Some("calculate")
2033 | Some("validate")
2034 | Some("docReady")
2035 | Some("layoutReady")
2036 );
2037 if base {
2038 return true;
2039 }
2040 if self.interactive
2045 && matches!(
2046 self.current_activity.as_deref(),
2047 Some("change") | Some("click") | Some("exit") | Some("enter")
2048 )
2049 {
2050 return true;
2051 }
2052 self.presave_gate && matches!(self.current_activity.as_deref(), Some("preSave"))
2058 }
2059
2060 fn handle_is_live(&self, node_id: FormNodeId, generation: u64) -> bool {
2061 generation == self.generation
2062 && self
2063 .form_ref()
2064 .is_some_and(|form| node_id.0 < form.nodes.len())
2065 }
2066
2067 fn write_field_value(
2068 &mut self,
2069 node_id: FormNodeId,
2070 value: String,
2071 ) -> Option<(String, String)> {
2072 let form = self.form_mut()?;
2073 let node = form.nodes.get_mut(node_id.0)?;
2074 let FormNodeType::Field { value: field_value } = &mut node.node_type else {
2075 return None;
2076 };
2077 let before = field_value.clone();
2078 *field_value = value;
2079 Some((before, field_value.clone()))
2080 }
2081
2082 fn form_ref(&self) -> Option<&FormTree> {
2083 if self.form.is_null() {
2084 None
2085 } else {
2086 unsafe { self.form.as_ref() }
2090 }
2091 }
2092
2093 fn form_mut(&mut self) -> Option<&mut FormTree> {
2094 if self.form.is_null() {
2095 None
2096 } else {
2097 unsafe { self.form.as_mut() }
2100 }
2101 }
2102
2103 fn data_dom_ref(&self) -> Option<&DataDom> {
2105 self.data_dom.map(|ptr| unsafe { &*ptr })
2109 }
2110
2111 pub fn data_children(&mut self, raw_id: usize) -> Vec<usize> {
2114 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2115 let result = {
2117 let Some(dom) = self.data_dom_ref() else {
2118 return Vec::new();
2119 };
2120 let id = DataNodeId::from_raw(raw_id);
2121 if dom.get(id).is_none() {
2122 return Vec::new();
2123 }
2124 dom.children(id)
2125 .iter()
2126 .take(MAX_RESOLVE_RESULTS)
2127 .map(|c| c.as_raw())
2128 .collect::<Vec<_>>()
2129 };
2130 self.metadata.data_reads = self.metadata.data_reads.saturating_add(1);
2131 result
2132 }
2133
2134 pub fn data_value(&mut self, raw_id: usize) -> Option<String> {
2137 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2138 let result = {
2139 let dom = self.data_dom_ref()?;
2140 let id = DataNodeId::from_raw(raw_id);
2141 dom.get(id)?;
2142 dom.value(id).ok().map(|s| s.to_owned())
2143 };
2144 if result.is_some() {
2145 self.metadata.data_reads = self.metadata.data_reads.saturating_add(1);
2146 }
2147 result
2148 }
2149
2150 pub fn data_child_by_name(&mut self, parent_raw: usize, name: &str) -> Option<usize> {
2153 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2154 let result = {
2155 let dom = self.data_dom_ref()?;
2156 let parent = DataNodeId::from_raw(parent_raw);
2157 dom.get(parent)?;
2158 dom.children_by_name(parent, name)
2159 .into_iter()
2160 .next()
2161 .map(|id| id.as_raw())
2162 };
2163 if result.is_some() {
2164 self.metadata.data_reads = self.metadata.data_reads.saturating_add(1);
2165 }
2166 result
2167 }
2168
2169 pub fn data_bound_record(
2172 &mut self,
2173 form_node_id: FormNodeId,
2174 generation: u64,
2175 ) -> Option<usize> {
2176 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2177 if generation != self.generation {
2178 return None;
2179 }
2180 let form = self.form_ref()?;
2181 if form_node_id.0 >= form.nodes.len() {
2182 return None;
2183 }
2184 form.meta(form_node_id).bound_data_node
2185 }
2186
2187 pub fn get_display_items(&mut self, field_id: FormNodeId, generation: u64) -> Vec<String> {
2199 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2200 if !self.handle_is_live(field_id, generation) {
2201 return Vec::new();
2202 }
2203 let Some(form) = self.form_ref() else {
2204 return Vec::new();
2205 };
2206 if !matches!(form.get(field_id).node_type, FormNodeType::Field { .. }) {
2207 return Vec::new();
2208 }
2209 let meta = form.meta(field_id);
2210 if !meta.runtime_listbox_items.is_empty() {
2211 return meta
2212 .runtime_listbox_items
2213 .iter()
2214 .map(|(display, _save)| display.clone())
2215 .collect();
2216 }
2217 meta.display_items.clone()
2218 }
2219
2220 pub fn data_resolve_node(&mut self, path: &str) -> Option<usize> {
2224 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2225 if !self.consume_resolve_call() {
2226 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2227 return None;
2228 }
2229 {
2231 let dom = self.data_dom_ref()?;
2232 resolve_data_path(dom, path, None)
2233 .ok()?
2234 .into_iter()
2235 .next()
2236 .map(|id| id.as_raw())
2237 }
2238 }
2239
2240 pub fn data_resolve_nodes(&mut self, path: &str) -> Vec<usize> {
2243 self.metadata.host_calls = self.metadata.host_calls.saturating_add(1);
2244 if !self.consume_resolve_call() {
2245 self.metadata.binding_errors = self.metadata.binding_errors.saturating_add(1);
2246 return Vec::new();
2247 }
2248 {
2249 let Some(dom) = self.data_dom_ref() else {
2250 return Vec::new();
2251 };
2252 resolve_data_path(dom, path, None)
2253 .unwrap_or_default()
2254 .into_iter()
2255 .take(MAX_RESOLVE_RESULTS)
2256 .map(|id| id.as_raw())
2257 .collect()
2258 }
2259 }
2260}
2261
2262enum ResolveOutcome {
2263 Ok(Vec<FormNodeId>),
2264 NoMatch,
2265 BindingError,
2266}
2267
2268#[derive(Debug, Clone)]
2269struct InstanceRun {
2270 parent_id: FormNodeId,
2271 positions: Vec<usize>,
2272 nodes: Vec<FormNodeId>,
2273 prototype_id: FormNodeId,
2274 first_position: usize,
2275 last_position: usize,
2276}
2277
2278fn build_instance_run(
2279 form: &FormTree,
2280 parents: &HashMap<FormNodeId, FormNodeId>,
2281 node_id: FormNodeId,
2282) -> Option<InstanceRun> {
2283 let parent_id = parents.get(&node_id).copied()?;
2284 let name = form.get(node_id).name.clone();
2285 let parent = form.get(parent_id);
2286 let mut positions = Vec::new();
2287 let mut nodes = Vec::new();
2288 for (position, child_id) in parent.children.iter().copied().enumerate() {
2289 if form.get(child_id).name == name {
2290 positions.push(position);
2291 nodes.push(child_id);
2292 }
2293 }
2294 if !nodes.contains(&node_id) {
2295 return None;
2296 }
2297 Some(InstanceRun {
2298 parent_id,
2299 prototype_id: nodes[0],
2300 first_position: positions[0],
2301 last_position: *positions.last()?,
2302 positions,
2303 nodes,
2304 })
2305}
2306
2307fn is_instance_node(node_type: &FormNodeType) -> bool {
2308 matches!(
2309 node_type,
2310 FormNodeType::Root
2311 | FormNodeType::Subform
2312 | FormNodeType::Area
2313 | FormNodeType::ExclGroup
2314 | FormNodeType::SubformSet
2315 )
2316}
2317
2318impl HostBindings {
2319 fn debug_log_resolve_miss(&mut self, kind: &str, path: &str) {
2326 if std::env::var("XFA_JS_DEBUG").ok().as_deref() == Some("1") {
2327 eprintln!("XFA_JS_DEBUG {kind} path={path:?}");
2328 }
2329 if crate::dynamic::runtime_diag_enabled() && self.som_fail_log.len() < 200 {
2330 self.som_fail_log.push(crate::dynamic::SomFailEntry {
2331 path: path.to_string(),
2332 kind: kind.to_string(),
2333 script_idx: self.current_script_idx,
2334 activity: self.current_activity.as_deref().unwrap_or("").to_string(),
2335 });
2336 }
2337 }
2338}
2339
2340fn normalize_resolve_path(path: &str) -> String {
2341 if let Some(rest) = path.strip_prefix("this.") {
2342 format!("$.{rest}")
2343 } else if path == "this" {
2344 "$".to_string()
2345 } else {
2346 path.to_string()
2347 }
2348}
2349
2350struct HostSomResolver<'a> {
2351 form: &'a FormTree,
2352 root_id: FormNodeId,
2353 parents: &'a HashMap<FormNodeId, FormNodeId>,
2354 current_id: FormNodeId,
2355}
2356
2357impl HostSomResolver<'_> {
2358 fn resolve_expression(&self, expr: &SomExpression) -> Option<Vec<FormNodeId>> {
2359 match expr.root {
2360 SomRoot::Data | SomRoot::Record | SomRoot::Template => None,
2361 SomRoot::CurrentContainer => {
2362 if expr.segments.is_empty() {
2363 Some(vec![self.current_id])
2364 } else {
2365 Some(self.follow_absolute(vec![self.current_id], &expr.segments))
2366 }
2367 }
2368 SomRoot::Form => {
2369 if expr.segments.is_empty() {
2370 Some(vec![self.root_id])
2371 } else {
2372 Some(self.follow_absolute(vec![self.root_id], &expr.segments))
2373 }
2374 }
2375 SomRoot::Xfa => {
2376 let segments = strip_xfa_form_prefix(&expr.segments);
2377 if segments.is_empty() {
2378 Some(vec![self.root_id])
2379 } else {
2380 Some(self.follow_absolute(vec![self.root_id], segments))
2381 }
2382 }
2383 SomRoot::Unqualified => {
2384 if expr.segments.is_empty() {
2385 Some(vec![self.current_id])
2386 } else {
2387 Some(self.follow_unqualified(&expr.segments))
2388 }
2389 }
2390 }
2391 }
2392
2393 fn follow_absolute(
2394 &self,
2395 mut current: Vec<FormNodeId>,
2396 segments: &[xfa_dom_resolver::som::SomSegment],
2397 ) -> Vec<FormNodeId> {
2398 for (idx, segment) in segments.iter().enumerate() {
2399 let allow_self = idx == 0;
2400 current = current
2401 .into_iter()
2402 .flat_map(|node_id| self.step_from_node(node_id, segment, allow_self))
2403 .collect();
2404 if current.is_empty() {
2405 break;
2406 }
2407 }
2408 current
2409 }
2410
2411 fn follow_unqualified(
2412 &self,
2413 segments: &[xfa_dom_resolver::som::SomSegment],
2414 ) -> Vec<FormNodeId> {
2415 let Some((first, rest)) = segments.split_first() else {
2416 return vec![self.current_id];
2417 };
2418
2419 let mut scope = Some(self.current_id);
2420 while let Some(scope_id) = scope {
2421 let anchors: Vec<_> = descendants_inclusive(self.form, scope_id)
2422 .into_iter()
2423 .filter(|node_id| self.node_matches_segment(*node_id, first))
2424 .collect();
2425 let matched = self.follow_remaining(anchors, rest);
2426 if !matched.is_empty() {
2427 return matched;
2428 }
2429 scope = self.parents.get(&scope_id).copied();
2430 }
2431
2432 let anchors: Vec<_> = descendants_inclusive(self.form, self.root_id)
2433 .into_iter()
2434 .filter(|node_id| self.node_matches_segment(*node_id, first))
2435 .collect();
2436 self.follow_remaining(anchors, rest)
2437 }
2438
2439 fn follow_remaining(
2440 &self,
2441 mut current: Vec<FormNodeId>,
2442 segments: &[xfa_dom_resolver::som::SomSegment],
2443 ) -> Vec<FormNodeId> {
2444 for segment in segments {
2445 current = current
2446 .into_iter()
2447 .flat_map(|node_id| self.step_from_node(node_id, segment, false))
2448 .collect();
2449 if current.is_empty() {
2450 break;
2451 }
2452 }
2453 current
2454 }
2455
2456 fn step_from_node(
2457 &self,
2458 node_id: FormNodeId,
2459 segment: &xfa_dom_resolver::som::SomSegment,
2460 allow_self: bool,
2461 ) -> Vec<FormNodeId> {
2462 if let SomSelector::Name(name) = &segment.selector {
2463 if name == ".." {
2464 if let Some(&parent_id) = self.parents.get(&node_id) {
2465 return apply_index_to_single(parent_id, segment.index);
2466 }
2467 return Vec::new();
2468 }
2469 }
2470
2471 if allow_self && self.node_matches_selector(node_id, &segment.selector) {
2472 return apply_index_to_single(node_id, segment.index);
2473 }
2474
2475 let matches: Vec<_> = self
2476 .form
2477 .get(node_id)
2478 .children
2479 .iter()
2480 .copied()
2481 .filter(|child_id| self.node_matches_selector(*child_id, &segment.selector))
2482 .collect();
2483
2484 apply_index(matches, segment.index)
2485 }
2486
2487 fn node_matches_segment(
2488 &self,
2489 node_id: FormNodeId,
2490 segment: &xfa_dom_resolver::som::SomSegment,
2491 ) -> bool {
2492 if !self.node_matches_selector(node_id, &segment.selector) {
2493 return false;
2494 }
2495
2496 match segment.index {
2497 SomIndex::All => true,
2498 SomIndex::None => self.sibling_position(node_id, &segment.selector) == Some(0),
2499 SomIndex::Specific(idx) => {
2500 self.sibling_position(node_id, &segment.selector) == Some(idx)
2501 }
2502 }
2503 }
2504
2505 fn sibling_position(&self, node_id: FormNodeId, selector: &SomSelector) -> Option<usize> {
2506 let Some(parent_id) = self.parents.get(&node_id).copied() else {
2507 return self.node_matches_selector(node_id, selector).then_some(0);
2508 };
2509
2510 self.form
2511 .get(parent_id)
2512 .children
2513 .iter()
2514 .copied()
2515 .filter(|candidate| self.node_matches_selector(*candidate, selector))
2516 .position(|candidate| candidate == node_id)
2517 }
2518
2519 fn node_matches_selector(&self, node_id: FormNodeId, selector: &SomSelector) -> bool {
2520 match selector {
2521 SomSelector::Name(name) => self.form.get(node_id).name == *name,
2522 SomSelector::Class(class_name) => self.node_matches_class(node_id, class_name),
2523 SomSelector::AllChildren => true,
2524 }
2525 }
2526
2527 fn node_matches_class(&self, node_id: FormNodeId, class_name: &str) -> bool {
2528 let class_name = class_name.to_ascii_lowercase();
2529 match class_name.as_str() {
2530 "subform" => matches!(
2531 self.form.get(node_id).node_type,
2532 FormNodeType::Root | FormNodeType::Subform
2533 ),
2534 "pageset" => matches!(self.form.get(node_id).node_type, FormNodeType::PageSet),
2535 "pagearea" => matches!(
2536 self.form.get(node_id).node_type,
2537 FormNodeType::PageArea { .. }
2538 ),
2539 "field" => matches!(self.form.get(node_id).node_type, FormNodeType::Field { .. }),
2540 "draw" => matches!(
2541 self.form.get(node_id).node_type,
2542 FormNodeType::Draw(_) | FormNodeType::Image { .. }
2543 ),
2544 "exclgroup" => self.form.meta(node_id).group_kind == GroupKind::ExclusiveChoice,
2545 _ => false,
2546 }
2547 }
2548}
2549
2550fn strip_xfa_form_prefix(
2551 segments: &[xfa_dom_resolver::som::SomSegment],
2552) -> &[xfa_dom_resolver::som::SomSegment] {
2553 match segments.first() {
2554 Some(segment)
2555 if matches!(&segment.selector, SomSelector::Name(name) if name == "form")
2556 && matches!(segment.index, SomIndex::None) =>
2557 {
2558 &segments[1..]
2559 }
2560 _ => segments,
2561 }
2562}
2563
2564fn apply_index(matches: Vec<FormNodeId>, index: SomIndex) -> Vec<FormNodeId> {
2565 match index {
2566 SomIndex::None => matches.into_iter().take(1).collect(),
2567 SomIndex::Specific(idx) => matches.get(idx).copied().into_iter().collect(),
2568 SomIndex::All => matches,
2569 }
2570}
2571
2572fn apply_index_to_single(node_id: FormNodeId, index: SomIndex) -> Vec<FormNodeId> {
2573 match index {
2574 SomIndex::None | SomIndex::Specific(0) | SomIndex::All => vec![node_id],
2575 SomIndex::Specific(_) => Vec::new(),
2576 }
2577}
2578
2579fn descendants_inclusive(form: &FormTree, root_id: FormNodeId) -> Vec<FormNodeId> {
2580 let mut out = Vec::new();
2581 collect_descendants(form, root_id, &mut out);
2582 out
2583}
2584
2585fn collect_descendants(form: &FormTree, node_id: FormNodeId, out: &mut Vec<FormNodeId>) {
2586 out.push(node_id);
2587 for &child_id in &form.get(node_id).children {
2588 collect_descendants(form, child_id, out);
2589 }
2590}
2591
2592const MAX_RESOLVE_CANDIDATES: usize = 32;
2597
2598fn collect_named_descendant_candidates(
2618 form: &FormTree,
2619 scope_id: FormNodeId,
2620 name: &str,
2621 max_depth: usize,
2622) -> Vec<FormNodeId> {
2623 let mut candidates: Vec<FormNodeId> = Vec::new();
2624 find_named_descendant_inner(form, scope_id, name, 0, max_depth, &mut candidates);
2625 candidates
2626}
2627
2628fn order_candidates(form: &FormTree, candidates: &mut [FormNodeId]) {
2629 candidates.sort_by(|left, right| {
2639 let left_is_field = matches!(form.get(*left).node_type, FormNodeType::Field { .. });
2640 let right_is_field = matches!(form.get(*right).node_type, FormNodeType::Field { .. });
2641 match (left_is_field, right_is_field) {
2642 (true, false) => std::cmp::Ordering::Less,
2643 (false, true) => std::cmp::Ordering::Greater,
2644 (true, true) => std::cmp::Ordering::Equal,
2645 (false, false) => form
2646 .get(*right)
2647 .children
2648 .len()
2649 .cmp(&form.get(*left).children.len()),
2650 }
2651 });
2652}
2653
2654fn push_unique_candidate(candidates: &mut Vec<FormNodeId>, node_id: FormNodeId) {
2655 if candidates.len() < MAX_RESOLVE_CANDIDATES && !candidates.contains(&node_id) {
2656 candidates.push(node_id);
2657 }
2658}
2659
2660fn resolve_implicit_candidates_in_scope(
2661 form: &FormTree,
2662 parents: &HashMap<FormNodeId, FormNodeId>,
2663 current_id: FormNodeId,
2664 name: &str,
2665) -> ResolveOutcome {
2666 let mut scope = Some(current_id);
2671 let mut depth = 0usize;
2672 while let Some(scope_id) = scope {
2673 if depth > MAX_SOM_DEPTH {
2674 return ResolveOutcome::BindingError;
2675 }
2676 if scope_id.0 >= form.nodes.len() {
2677 return ResolveOutcome::BindingError;
2678 }
2679 let mut candidates =
2680 collect_named_descendant_candidates(form, scope_id, name, MAX_SOM_DEPTH);
2681 if !candidates.is_empty() {
2682 order_candidates(form, &mut candidates);
2683 if !form.get(candidates[0]).children.is_empty() {
2684 return ResolveOutcome::Ok(candidates);
2685 }
2686 }
2687 scope = parents.get(&scope_id).copied();
2688 depth += 1;
2689 }
2690
2691 let mut scope = Some(current_id);
2694 let mut depth = 0usize;
2695 while let Some(scope_id) = scope {
2696 if depth > MAX_SOM_DEPTH {
2697 return ResolveOutcome::BindingError;
2698 }
2699 if scope_id.0 >= form.nodes.len() {
2700 return ResolveOutcome::BindingError;
2701 }
2702 if form.get(scope_id).name == name {
2703 return ResolveOutcome::Ok(vec![scope_id]);
2704 }
2705 scope = parents.get(&scope_id).copied();
2706 depth += 1;
2707 }
2708
2709 let mut scope = Some(current_id);
2711 let mut depth = 0usize;
2712 while let Some(scope_id) = scope {
2713 if depth > MAX_SOM_DEPTH {
2714 return ResolveOutcome::BindingError;
2715 }
2716 if scope_id.0 >= form.nodes.len() {
2717 return ResolveOutcome::BindingError;
2718 }
2719 let mut candidates =
2720 collect_named_descendant_candidates(form, scope_id, name, MAX_SOM_DEPTH);
2721 if !candidates.is_empty() {
2722 order_candidates(form, &mut candidates);
2723 return ResolveOutcome::Ok(candidates);
2724 }
2725 scope = parents.get(&scope_id).copied();
2726 depth += 1;
2727 }
2728
2729 ResolveOutcome::NoMatch
2730}
2731
2732fn subtree_contains_name(
2739 form: &FormTree,
2740 node_id: FormNodeId,
2741 name: &str,
2742 max_depth: usize,
2743) -> bool {
2744 if node_id.0 >= form.nodes.len() {
2745 return false;
2746 }
2747 subtree_contains_name_inner(form, node_id, name, 0, max_depth)
2748}
2749
2750fn subtree_contains_name_inner(
2751 form: &FormTree,
2752 node_id: FormNodeId,
2753 name: &str,
2754 depth: usize,
2755 max_depth: usize,
2756) -> bool {
2757 if depth >= max_depth {
2758 return false;
2759 }
2760 for &child_id in &form.get(node_id).children {
2761 if form.get(child_id).name == name {
2762 return true;
2763 }
2764 if subtree_contains_name_inner(form, child_id, name, depth + 1, max_depth) {
2765 return true;
2766 }
2767 }
2768 false
2769}
2770
2771fn find_named_descendant_inner(
2772 form: &FormTree,
2773 node_id: FormNodeId,
2774 name: &str,
2775 depth: usize,
2776 max_depth: usize,
2777 candidates: &mut Vec<FormNodeId>,
2778) {
2779 if depth >= max_depth || candidates.len() >= MAX_RESOLVE_CANDIDATES {
2780 return;
2781 }
2782 for &child_id in &form.get(node_id).children {
2783 if candidates.len() >= MAX_RESOLVE_CANDIDATES {
2784 return;
2785 }
2786 if form.get(child_id).name == name {
2787 candidates.push(child_id);
2788 } else {
2792 find_named_descendant_inner(form, child_id, name, depth + 1, max_depth, candidates);
2793 }
2794 }
2795}
2796
2797fn build_parent_map(form: &FormTree, root_id: FormNodeId) -> HashMap<FormNodeId, FormNodeId> {
2798 let mut parents = HashMap::new();
2799 populate_parent_map(form, root_id, &mut parents);
2800 parents
2801}
2802
2803fn populate_parent_map(
2804 form: &FormTree,
2805 node_id: FormNodeId,
2806 parents: &mut HashMap<FormNodeId, FormNodeId>,
2807) {
2808 for &child_id in &form.get(node_id).children {
2809 parents.insert(child_id, node_id);
2810 populate_parent_map(form, child_id, parents);
2811 }
2812}
2813
2814#[cfg(test)]
2815mod tests {
2816 use super::*;
2817 use xfa_layout_engine::form::{FormNode, Occur};
2818 use xfa_layout_engine::text::FontMetrics;
2819 use xfa_layout_engine::types::{BoxModel, LayoutStrategy};
2820
2821 fn add_node(tree: &mut FormTree, name: &str, node_type: FormNodeType) -> FormNodeId {
2822 tree.add_node(FormNode {
2823 name: name.to_string(),
2824 node_type,
2825 box_model: BoxModel::default(),
2826 layout: LayoutStrategy::TopToBottom,
2827 children: Vec::new(),
2828 occur: Occur::once(),
2829 font: FontMetrics::default(),
2830 calculate: None,
2831 validate: None,
2832 column_widths: Vec::new(),
2833 col_span: 1,
2834 })
2835 }
2836
2837 #[test]
2838 fn raw_value_get_set_and_generation_guard() {
2839 let mut tree = FormTree::new();
2840 let root = add_node(&mut tree, "root", FormNodeType::Root);
2841 let field = add_node(
2842 &mut tree,
2843 "Field1",
2844 FormNodeType::Field {
2845 value: "old".to_string(),
2846 },
2847 );
2848 tree.get_mut(root).children = vec![field];
2849
2850 let mut host = HostBindings::new();
2851 host.reset_per_document();
2852 host.set_form_handle(&mut tree as *mut FormTree, root);
2853 host.reset_per_script(field, Some("calculate"));
2854 let generation = host.generation();
2855
2856 assert_eq!(
2857 host.get_raw_value(field, generation),
2858 Some("old".to_string())
2859 );
2860 assert!(host.set_raw_value(field, "new".to_string(), generation));
2861 assert_eq!(
2862 host.get_raw_value(field, generation),
2863 Some("new".to_string())
2864 );
2865
2866 host.reset_per_document();
2867 assert_eq!(host.get_raw_value(field, generation), None);
2868 }
2869
2870 #[test]
2871 fn multi_segment_som_chain_lookahead() {
2872 let mut tree = FormTree::new();
2878 let root = add_node(&mut tree, "root", FormNodeType::Root);
2879 let f = add_node(&mut tree, "F", FormNodeType::Subform);
2880 let p1_empty = add_node(&mut tree, "P1", FormNodeType::Subform);
2881 let stub = add_node(&mut tree, "Stub", FormNodeType::Subform);
2882 let p1_rich = add_node(&mut tree, "P1", FormNodeType::Subform);
2883 let x = add_node(
2884 &mut tree,
2885 "X",
2886 FormNodeType::Field {
2887 value: "answer".to_string(),
2888 },
2889 );
2890 tree.get_mut(p1_empty).children = vec![stub];
2891 tree.get_mut(p1_rich).children = vec![x];
2892 tree.get_mut(f).children = vec![p1_empty, p1_rich];
2893 tree.get_mut(root).children = vec![f];
2894
2895 let mut host = HostBindings::new();
2896 host.reset_per_document();
2897 host.set_form_handle(&mut tree as *mut FormTree, root);
2898 host.reset_per_script(root, Some("calculate"));
2899
2900 let plain = host.resolve_child_candidates(&[f], "P1");
2902 assert!(plain.contains(&p1_empty) && plain.contains(&p1_rich));
2903
2904 let hinted = host.resolve_child_candidates_hinted(&[f], "P1", "X");
2907 assert_eq!(hinted, vec![p1_rich]);
2908
2909 let hinted_none = host.resolve_child_candidates_hinted(&[f], "P1", "Nope");
2912 assert!(hinted_none.contains(&p1_empty) && hinted_none.contains(&p1_rich));
2913
2914 let implicit = host.resolve_implicit_candidates_hinted(root, "P1", "X");
2917 assert_eq!(implicit, vec![p1_rich]);
2918 }
2919
2920 #[test]
2921 fn resolve_node_rejects_over_depth() {
2922 let mut tree = FormTree::new();
2923 let root = add_node(&mut tree, "root", FormNodeType::Root);
2924 let field = add_node(
2925 &mut tree,
2926 "Field1",
2927 FormNodeType::Field {
2928 value: String::new(),
2929 },
2930 );
2931 tree.get_mut(root).children = vec![field];
2932
2933 let mut host = HostBindings::new();
2934 host.reset_per_document();
2935 host.set_form_handle(&mut tree as *mut FormTree, root);
2936 host.reset_per_script(root, Some("calculate"));
2937 let long_path = (0..=MAX_SOM_DEPTH)
2938 .map(|idx| format!("n{idx}"))
2939 .collect::<Vec<_>>()
2940 .join(".");
2941
2942 assert_eq!(host.resolve_node(&long_path), None);
2943 assert_eq!(host.take_metadata().binding_errors, 1);
2944 }
2945
2946 fn build_chain_disambiguation_tree() -> (
2955 FormTree,
2956 FormNodeId, FormNodeId, FormNodeId, ) {
2960 let mut tree = FormTree::new();
2961 let root = add_node(&mut tree, "root", FormNodeType::Root);
2962
2963 let a1 = add_node(&mut tree, "A", FormNodeType::Subform);
2964 let b1 = add_node(&mut tree, "B", FormNodeType::Subform);
2965 tree.get_mut(b1).children = vec![];
2966 tree.get_mut(a1).children = vec![b1];
2967
2968 let a2 = add_node(&mut tree, "A", FormNodeType::Subform);
2969 let b2 = add_node(&mut tree, "B", FormNodeType::Subform);
2970 let c2 = add_node(&mut tree, "C", FormNodeType::Subform);
2971 let x2 = add_node(
2972 &mut tree,
2973 "X",
2974 FormNodeType::Field {
2975 value: "winner".to_string(),
2976 },
2977 );
2978 tree.get_mut(c2).children = vec![x2];
2979 tree.get_mut(b2).children = vec![c2];
2980 tree.get_mut(a2).children = vec![b2];
2981
2982 let a3 = add_node(&mut tree, "A", FormNodeType::Subform);
2983 let b3 = add_node(&mut tree, "B", FormNodeType::Subform);
2984 let z3 = add_node(
2985 &mut tree,
2986 "Z",
2987 FormNodeType::Field {
2988 value: "decoy".to_string(),
2989 },
2990 );
2991 tree.get_mut(b3).children = vec![z3];
2992 tree.get_mut(a3).children = vec![b3];
2993
2994 tree.get_mut(root).children = vec![a1, a2, a3];
2995 (tree, root, a2, x2)
2996 }
2997
2998 #[test]
2999 fn full_chain_implicit_pins_winning_branch() {
3000 let (mut tree, root, _a2, x2) = build_chain_disambiguation_tree();
3004 let mut host = HostBindings::new();
3005 host.reset_per_document();
3006 host.set_form_handle(&mut tree as *mut FormTree, root);
3007 host.reset_per_script(root, Some("calculate"));
3008
3009 let chain = vec![
3010 "A".to_string(),
3011 "B".to_string(),
3012 "C".to_string(),
3013 "X".to_string(),
3014 ];
3015 let result = host.resolve_with_full_chain(&[], &chain, Some(root));
3016 assert_eq!(result, vec![x2], "chain must pin onto the X under A2");
3017 }
3018
3019 #[test]
3020 fn full_chain_child_entry_uses_parent_ids() {
3021 let (mut tree, root, _a2, x2) = build_chain_disambiguation_tree();
3025 let mut host = HostBindings::new();
3026 host.reset_per_document();
3027 host.set_form_handle(&mut tree as *mut FormTree, root);
3028 host.reset_per_script(root, Some("calculate"));
3029
3030 let chain = vec![
3031 "A".to_string(),
3032 "B".to_string(),
3033 "C".to_string(),
3034 "X".to_string(),
3035 ];
3036 let result = host.resolve_with_full_chain(&[root], &chain, None);
3037 assert_eq!(result, vec![x2]);
3038 }
3039
3040 #[test]
3041 fn full_chain_dead_end_returns_deepest_layer() {
3042 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
3046 let mut host = HostBindings::new();
3047 host.reset_per_document();
3048 host.set_form_handle(&mut tree as *mut FormTree, root);
3049 host.reset_per_script(root, Some("calculate"));
3050
3051 let chain = vec![
3052 "A".to_string(),
3053 "B".to_string(),
3054 "C".to_string(),
3055 "Bogus".to_string(),
3056 ];
3057 let result = host.resolve_with_full_chain(&[], &chain, Some(root));
3058 assert_eq!(result.len(), 1);
3061 }
3062
3063 #[test]
3064 fn full_chain_no_match_returns_empty() {
3065 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
3067 let mut host = HostBindings::new();
3068 host.reset_per_document();
3069 host.set_form_handle(&mut tree as *mut FormTree, root);
3070 host.reset_per_script(root, Some("calculate"));
3071
3072 let chain = vec!["Nope".to_string(), "X".to_string()];
3073 let result = host.resolve_with_full_chain(&[], &chain, Some(root));
3074 assert!(result.is_empty());
3075 }
3076
3077 #[test]
3078 fn full_chain_rejects_overdepth_and_empty_segments() {
3079 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
3080 let mut host = HostBindings::new();
3081 host.reset_per_document();
3082 host.set_form_handle(&mut tree as *mut FormTree, root);
3083 host.reset_per_script(root, Some("calculate"));
3084
3085 let overlong: Vec<String> = (0..=MAX_SOM_DEPTH).map(|i| format!("S{i}")).collect();
3086 let result = host.resolve_with_full_chain(&[], &overlong, Some(root));
3087 assert!(result.is_empty());
3088 let with_empty = vec!["A".to_string(), "".to_string()];
3090 let result = host.resolve_with_full_chain(&[], &with_empty, Some(root));
3091 assert!(result.is_empty());
3092 }
3093
3094 #[test]
3095 fn full_chain_empty_chain_returns_parents() {
3096 let (mut tree, root, _a2, _x2) = build_chain_disambiguation_tree();
3100 let mut host = HostBindings::new();
3101 host.reset_per_document();
3102 host.set_form_handle(&mut tree as *mut FormTree, root);
3103 host.reset_per_script(root, Some("calculate"));
3104
3105 let result = host.resolve_with_full_chain(&[root], &[], None);
3106 assert_eq!(result, vec![root]);
3107 }
3108}