Reject exporting a partial Play Action call to .atn (#7602)

Fixes #7186

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
NetterSilver
2026-10-19 13:45:07 -04:00
committed by GitHub
co-authored by Claude Haiku 5.5 Brandon Thomas
parent 990483a95e
commit 2ef8a9d3f5
2 changed files with 21 additions and 0 deletions
+4
View File
@@ -1984,6 +1984,10 @@ fn play_from(d: &Descriptor) -> Mapped {
fn play_to(_: &str, p: &Value) -> Option<Descriptor> {
let name = jstr(p, "action")?;
// The Play Action descriptor written here carries no starting step, so a call from a later step is not a whole call.
if p.get("from").is_some_and(|v| !v.is_null() && v.as_f64() != Some(0.0)) {
return None;
}
let mut items = vec![ReferenceItem::Name { class: class("Actn"), name: UnicodeString::new_nul(name) }];
if let Some(set) = jstr(p, "set") {
items.push(ReferenceItem::Name { class: class("ASet"), name: UnicodeString::new_nul(set) });
+17
View File
@@ -1239,6 +1239,23 @@ fn add_noise_with_mask_target_is_not_exported_as_pixel_filter() {
}
}
/// A Play Action that starts the called action at a later step must not export as a call of the
/// whole action: that would replay the steps the recording deliberately skipped.
#[test]
fn partial_play_action_is_not_exported_as_whole_action_call() {
let partial = Action { name: "Parent".into(), steps: vec![("actions.play".into(), json!({"action": "Child", "from": 1}))], ..Default::default() };
assert!(export("Set", &[&partial], &[], false).is_err());
let (_, skipped) = export("Set", &[&partial], &[], true).unwrap();
assert_eq!(skipped, vec![Skipped { action: "Parent".into(), step: 0, id: "actions.play".into() }]);
for params in [json!({"action": "Child"}), json!({"action": "Child", "from": 0})] {
let whole = Action { name: "Parent".into(), steps: vec![("actions.play".into(), params)], ..Default::default() };
let (bytes, skipped) = export("Set", &[&whole], &[], false).unwrap();
assert!(skipped.is_empty());
let imported = import(&bytes).unwrap();
assert_eq!(imported.actions[0].steps, vec![("actions.play".to_string(), json!({"action": "Child"}))]);
}
}
/// #7646: an action's function key keeps Photoshop's Shift and Command options through
/// `.atn` load and save, as `Shift+F5`, `Cmd+F5` and `Cmd+Shift+F5` shortcuts.
#[test]