feat(forms): open, Will Save and Will Print actions, widget pointer/focus scripts, setItems/setAction/toSource (port of #344) (#6969)

* feat(js, forms): field setItems, setAction and a bounded toSource (#269)

Ported from #344 by @DWAA1660 onto the per-document script session.

- `field.setItems(items)` replaces a combo or list box's items (text, or
  [text, export] pairs). The value stays when an item still has it, and
  otherwise becomes the first item's. Written as the field's /Opt (pairs
  only where the export value differs), /I dropped, field redrawn.
- `field.setAction(trigger, script)` installs a JavaScript action for one
  trigger (MouseUp/Down/Enter/Exit, OnFocus/OnBlur, Keystroke, Validate,
  Calculate, Format), replacing only that trigger's action.
- `Object.prototype.toSource()` writes plain data (strings, numbers,
  booleans, null, undefined, arrays, objects) that eval reads back. It
  refuses functions, cycles, more than 64 levels, 10,000 values or 1 MiB
  with an error the script can catch.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* feat(engine): run a form's open, Will Save and Will Print actions and its widgets' pointer and focus scripts (#269)

Ported from #344 by @DWAA1660 onto the per-document script session: each
runs in the document's retained realm, so state the document scripts and
earlier events left is kept.

- The catalog /OpenAction's JavaScript (and its /Next chain, bounded and
  cycle-safe; other action types skipped) runs once on opening, after the
  document scripts, for background opens and reloads too, and again on
  Revert in a fresh realm. Event Doc/Open.
- `Session::run_document_actions` runs /AA /WS (Will Save) or /AA /WP
  (Will Print). A failing action is reported and never stops the save or
  print. doc_save and doc_print run them first and report what they said
  (`will_save`, `will_print`).
- Scripts that run unasked (open, Will Save, Will Print, pointer over,
  focus) may only ask for highlight, zoom, focus or a page; a click's
  Mouse Down / Mouse Up keep their requests.
- `Session::field_trigger` runs a widget's Mouse Enter, Mouse Exit, Mouse
  Down, Mouse Up, Focus or Blur JavaScript (one undoable edit), reading only
  that widget's dictionary. `field_focus` now runs an AcroForm widget's On
  Focus / On Blur as well as XFA enter/exit.
- New tool `js_event`; form_focus's description says AcroForm focus
  scripts run too.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* feat(ui): Will Save before saving, Will Print before printing, and widget pointer scripts in the app (#269, #247)

Ported from #344 by @DWAA1660.

- Save (both paths) runs the document's Will Save actions first, and Print
  its Will Print actions; what they change is saved or printed, and what
  they say is shown.
- The pointer moving onto or off a form widget queues its Mouse Enter /
  Mouse Exit script, a press its Mouse Down. A click on a check box or radio
  button queues its Mouse Up after the toggle, so the script reads the value
  the click stored (#247). The queue runs after the frame's edit.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* docs(parity): document actions and widget triggers are partly supported (#269)

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* test: check toSource's text (scripts can't eval it) and the trigger event names (#269)

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* test: a check box's Mouse Up reads its new value; keep the fixtures on screen and the realm across an in-place save (#247, #269)

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* fix(engine, forms, js, ui): review fixes for document actions and field triggers

- widget trigger actions follow /Next, each action its own edit; errors don't stop the chain
- setAction installs on the field the object stands for when names repeat
- toSource refuses non-plain objects and a __proto__ key
- doc_print checks the destination before Will Print; js_event returns only its own output
- combo/list clicks run Mouse Up; a refused click's Mouse Up doesn't run
- label crops are re-read when Will Print changed the document

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* forms: run a pointer trigger on the field it hit and refresh the edited field's draft

A trigger now carries the field's object, so of fields sharing a name the one under the
pointer runs its own script (Session::field_trigger_target); js_event passes the field it
resolved. A trigger script changing the field being edited shows in its editor unless the
user already typed there, as focus scripts do.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

* forms: carry the field's identity through focus scripts and js_event

Focus transitions now track the field object, so moving between fields sharing a name is a
move and the field that gained or lost focus runs its own script
(Session::field_focus_target). js_event takes an optional field_id from form_fields to pick
one of fields sharing a name.

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>

---------

Co-authored-by: DWAA1660 <dwatnip123@gmail.com>
This commit is contained in:
FredAppsandDWAA1660 authored and GitHub committed 2026-11-11 13:09:37 +01:00
1 parent 3a117c3c0e
commit 059dbd6e00
32 files changed
+1597 -74

No files matched your search

+8 -8
View File
@@ -5348,11 +5348,11 @@ title = "Document actions (will close, will save, did save, will print, did prin
area = "F"
tier = "P1"
milestone = "M6"
status = "planned"
commands = []
tools = []
evidence = []
notes = ""
status = "partial"
commands = ["file.save", "print.dialog"]
tools = ["doc_open", "doc_save", "doc_print"]
evidence = ["crates/engine/src/document_actions_tests.rs::the_open_action_runs_once_on_opening_in_the_document_realm", "crates/engine/src/document_actions_tests.rs::an_open_action_chain_runs_in_order_and_a_cycle_runs_each_action_once", "crates/engine/src/document_actions_tests.rs::an_open_action_may_not_open_a_url_or_print", "crates/engine/src/document_actions_tests.rs::the_open_action_does_not_run_with_javascript_off", "crates/engine/src/document_actions_tests.rs::revert_runs_the_open_action_again_in_a_fresh_realm", "crates/engine/src/document_actions_tests.rs::will_save_changes_what_is_saved_and_a_failing_one_does_not_stop_the_save", "crates/engine/src/document_actions_tests.rs::will_print_runs_in_the_document_realm", "crates/automation/tests/it/document_actions.rs::open_will_save_will_print_and_field_events_share_the_document_realm"]
notes = "The catalog /OpenAction's JavaScript (with its /Next chain, bounded and cycle-safe) runs once on opening after the document scripts, and again on Revert in a fresh realm; /AA /WS runs before a save (the app's Save and Save As, doc_save) and /AA /WP before printing (the app's Print, doc_print), in the document's realm, so what they change is saved or printed. A failing action is reported and never stops the save or print. These scripts run unasked, so they may only ask for highlighting, zoom, focus or a page. Missing: Will Close (/WC), Did Save (/DS), Did Print (/DP) and page open/close actions (#269, ported from #344)."
[[feature]]
id = "form.js-host"
@@ -5362,9 +5362,9 @@ tier = "P0"
milestone = "M6"
status = "partial"
commands = ["tools.js_console"]
tools = ["js_run"]
evidence = ["crates/automation/tests/it/timesheet_compat.rs::timesheet_keypad_geometry_icons_calculation_and_persistence", "crates/automation/tests/it/timesheet_compat.rs::popup_and_manual_response_resume_the_same_script_and_validate_choices", "crates/js/src/session.rs::document_realm_keeps_keypad_state_and_isolates_documents", "crates/js/src/tests.rs::calculate_scripts_read_fields_and_set_the_value", "crates/engine/src/tests.rs::form_javascript_validates_calculates_and_formats", "crates/automation/tests/it/automation.rs::javascript_through_tools", "crates/js/src/tests.rs::an_xfa_form_sees_the_xfa_plugin_installed", "crates/js/src/tests.rs::a_plain_form_sees_the_xfa_version_but_not_installed", "crates/engine/src/tests.rs::scripts_in_an_xfa_form_see_the_xfa_plugin_installed", "crates/js/src/tests.rs::scand_allows_separators_between_fields_but_nothing_before_or_after_them", "crates/js/src/tests.rs::app_alert_keeps_its_title_icon_and_buttons", "crates/js/src/tests.rs::app_alert_reads_the_object_form", "crates/js/src/tests.rs::app_alert_falls_back_on_bad_icon_and_button_numbers", "crates/js/src/tests.rs::app_alert_titles_are_clipped_and_charged", "crates/ui-egui/tests/it/javascript.rs::every_message_a_script_shows_takes_the_toast_in_turn", "crates/ui-egui/tests/it/forms.rs::a_form_button_opens_its_attached_instructions_in_a_new_tab", "crates/js/src/tests.rs::widget_rect_reads_and_writes_a_four_number_array", "crates/engine/src/tests.rs::javascript_field_rect_moves_first_widget_and_undoes"]
notes = "Document-scoped native Boa realms shared by button, console and field events; synchronous calculateNow in CO order; normal captions/icons; rect and appearance/tooltip writeback; effective page boxes; native explicit-button popup/response/alert continuation and viewer requests, including opening or saving attachments from form buttons. Headless js_run has deterministic ordered answers. Partial: web prompt UI, timers, annotation/layer APIs, non-normal button faces, widget-indexed Field access and remaining document action triggers are not implemented. See docs/acroform-script-workflows.md for lifecycle, limits and validation scope."
tools = ["js_run", "js_event"]
evidence = ["crates/js/src/tests.rs::to_source_writes_plain_state_as_a_literal", "crates/js/src/tests.rs::to_source_refuses_cycles_functions_and_oversized_state", "crates/js/src/tests.rs::set_items_and_set_action_change_the_field", "crates/engine/src/document_actions_tests.rs::set_items_and_set_action_are_written_to_the_document", "crates/engine/src/document_actions_tests.rs::pointer_and_focus_triggers_run_the_widget_scripts", "crates/engine/src/document_actions_tests.rs::only_a_click_trigger_may_print", "crates/engine/src/document_actions_tests.rs::a_check_box_mouse_up_reads_the_value_its_click_stored", "crates/ui-egui/tests/it/field_triggers.rs::the_pointer_entering_and_leaving_a_widget_runs_its_scripts", "crates/ui-egui/tests/it/field_triggers.rs::a_check_box_mouse_up_script_sees_the_value_its_click_stored", "crates/automation/tests/it/timesheet_compat.rs::timesheet_keypad_geometry_icons_calculation_and_persistence", "crates/automation/tests/it/timesheet_compat.rs::popup_and_manual_response_resume_the_same_script_and_validate_choices", "crates/js/src/session.rs::document_realm_keeps_keypad_state_and_isolates_documents", "crates/js/src/tests.rs::calculate_scripts_read_fields_and_set_the_value", "crates/engine/src/tests.rs::form_javascript_validates_calculates_and_formats", "crates/automation/tests/it/automation.rs::javascript_through_tools", "crates/js/src/tests.rs::an_xfa_form_sees_the_xfa_plugin_installed", "crates/js/src/tests.rs::a_plain_form_sees_the_xfa_version_but_not_installed", "crates/engine/src/tests.rs::scripts_in_an_xfa_form_see_the_xfa_plugin_installed", "crates/js/src/tests.rs::scand_allows_separators_between_fields_but_nothing_before_or_after_them", "crates/js/src/tests.rs::app_alert_keeps_its_title_icon_and_buttons", "crates/js/src/tests.rs::app_alert_reads_the_object_form", "crates/js/src/tests.rs::app_alert_falls_back_on_bad_icon_and_button_numbers", "crates/js/src/tests.rs::app_alert_titles_are_clipped_and_charged", "crates/ui-egui/tests/it/javascript.rs::every_message_a_script_shows_takes_the_toast_in_turn", "crates/ui-egui/tests/it/forms.rs::a_form_button_opens_its_attached_instructions_in_a_new_tab", "crates/js/src/tests.rs::widget_rect_reads_and_writes_a_four_number_array", "crates/engine/src/tests.rs::javascript_field_rect_moves_first_widget_and_undoes"]
notes = "Document-scoped native Boa realms shared by button, console and field events; synchronous calculateNow in CO order; normal captions/icons; rect and appearance/tooltip writeback; effective page boxes; native explicit-button popup/response/alert continuation and viewer requests, including opening or saving attachments from form buttons. Headless js_run has deterministic ordered answers. A widget's Mouse Enter, Mouse Exit, Mouse Down, Focus and Blur JavaScript run in the app and through js_event (and form_focus for focus), and a check box's or radio button's Mouse Up runs after its click stored the new value (#247); field.setItems, field.setAction and a bounded Object.prototype.toSource (plain data only) are written back to the document (#269, ported from #344). Partial: web prompt UI, timers, annotation/layer APIs, non-normal button faces, widget-indexed Field access, and Will Close / Did Save / Did Print are not implemented. See docs/acroform-script-workflows.md for lifecycle, limits and validation scope."
[[feature]]
id = "form.js-console"