M6: a script's field-highlight change reaches the next edit of a batch, and is kept only when its request is

This commit is contained in:
Eric Kaden committed 2026-10-24 14:50:22 -04:00
1 parent 4a6d2b9c1f
commit e0eb1eff01
4 files changed
+50 -2

No files matched your search

+6
View File
@@ -2687,6 +2687,8 @@ fn run_edit(doc: &mut pdfcraft_cos::Document, edit: &Edit, cx: &mut EditCtx) ->
let ran = xfa::on_event(doc, &tpl, som, activity, &mut run);
cx.xfa_datasets = run.datasets;
cx.xfa_ran_away |= run.ran_away;
// The next edit of a batch reads the highlight this one's scripts set.
cx.highlight = run.highlight;
ran.map_err(EditError::Invalid)?;
}
Edit::SetFieldImage { name, image } => {
@@ -3484,6 +3486,8 @@ fn xfa_after_data_change(doc: &mut pdfcraft_cos::Document, cx: &mut EditCtx, cal
.unwrap_or_else(|m| Err(format!("laying the form out again failed unexpectedly ({m})")));
cx.xfa_datasets = run.datasets;
cx.xfa_ran_away |= run.ran_away;
// The next edit of a batch reads the highlight this one's scripts set.
cx.highlight = run.highlight;
if let Err(e) = r {
cx.xfa_out.errors.push(e);
}
@@ -4071,6 +4075,8 @@ impl Session {
.unwrap_or_else(|m| Err(format!("the form's scripts failed unexpectedly ({m})")));
cx.xfa_datasets = run.datasets;
cx.xfa_ran_away |= run.ran_away;
// The next edit of a batch reads the highlight this one's scripts set.
cx.highlight = run.highlight;
if let Err(e) = ran {
cx.xfa_out.errors.push(e);
}
+29
View File
@@ -8102,6 +8102,35 @@ fn a_forms_layout_ready_scripts_run_once_it_is_laid_out() {
assert!(s.take_js_output(id).requests.is_empty());
}
/// In a batch of XFA events, a later event's scripts read the field highlight an earlier one's set:
/// `on`'s click turns it on, and `look`'s click, in the same batch, records what it reads.
#[test]
fn a_later_xfa_event_in_a_batch_reads_the_highlight_an_earlier_one_set() {
let script = |js: &str| format!(r#"<event activity="click"><script contentType="application/x-javascript">{js}</script></event>"#);
let xdp = format!(
r##"<?xml version="1.0" encoding="UTF-8"?>
<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
<subform name="form" layout="tb">
<pageSet><pageArea name="front"><contentArea x="0.5in" y="0.5in" w="7.5in" h="10in"/><medium short="8.5in" long="11in"/></pageArea></pageSet>
<field name="on" w="1in" h="0.3in"><ui><button/></ui>{on}</field>
<field name="look" w="1in" h="0.3in"><ui><button/></ui>{look}</field>
<field name="seen" w="2in" h="0.3in"><ui><textEdit/></ui></field>
</subform>
</template>
</xdp:xdp>
"##,
on = script("app.runtimeHighlight = true;"),
look = script("xfa.form.form.seen.rawValue = String(app.runtimeHighlight);"),
);
let mut s = Session::new();
let id = s.open("batch.pdf", None, Arc::new(pdfcraft_xfa::fixtures::shell(&xdp)), None).expect("opens");
let click = |som: &str| Edit::XfaEvent { som: som.into(), activity: "click".into() };
s.apply(id, Edit::Batch { edits: vec![click("form[0].on[0]"), click("form[0].look[0]")], label: "Click".into() }).expect("clicks");
let seen = s.get(id).unwrap().form.iter().find(|f| f.name == "seen").map(|f| f.value.clone()).unwrap_or_default();
assert_eq!(seen, vec!["true".to_string()]);
}
/// Field scripts of one edit read the field highlight an earlier one set: `on`'s calculation turns
/// it on, and `seen`'s, which runs after it, records what it reads.
#[test]
+10
View File
@@ -1579,6 +1579,16 @@ mod runtime_highlight {
assert_eq!(o.effects, vec![XfaEffect::Highlight(true)]);
let o = run_xfa(script, &ev, &XfaDoc { highlight: true, ..Default::default() }, &root, Limits::default());
assert_eq!((o.result.as_deref(), o.effects.len()), (Some("true,true"), 0), "already on: nothing to ask for");
// Past the effect cap the request is dropped, and the script still reads the setting the
// viewer has.
let full = format!(
"for (var i = 0; i < {}; i++) {{ xfa.host.beep(); }} app.runtimeHighlight = true; String(app.runtimeHighlight)",
crate::xfa::MAX_EFFECTS
);
let o = run_xfa(&full, &ev, &XfaDoc::default(), &root, Limits::default());
assert_eq!(o.error, None, "{o:?}");
assert_eq!(o.result.as_deref(), Some("false"));
assert!(!o.effects.contains(&XfaEffect::Highlight(true)));
}
}
+5 -2
View File
@@ -1159,9 +1159,12 @@ fn runtime_highlight(_: &JsValue, _: &[JsValue], ctx: &mut Context) -> JsResult<
fn set_runtime_highlight(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
let on = arg(args, 0).to_boolean();
let mut h = host(ctx)?.borrow_mut();
if h.doc.highlight != on {
let e = XfaEffect::Highlight(on);
// Changed only when the request is kept: past the effect cap the script goes on reading the
// setting the viewer has.
if h.doc.highlight != on && h.reserve(&e, payload(&e)) {
h.doc.highlight = on;
h.push(XfaEffect::Highlight(on));
h.record(e);
}
Ok(JsValue::undefined())
}