From ae78cbc0f945bf49367b54da27bf62744f7c0b78 Mon Sep 17 00:00:00 2001 From: Lahfir Date: Tue, 14 Apr 2026 00:47:57 -0700 Subject: [PATCH] fix: compare AX elements via CFEqual in web_action_had_effect AXUIElementCopyAttributeValue follows the CoreFoundation Create rule and returns a freshly-allocated CF object on every call, so raw pointer equality (`before.0 != after.0`) between two separate copies of AXFocusedUIElement was ALWAYS true even when the focused element was unchanged. That made Step 1 (AXPress) of activate_web_element appear successful every time and left Steps 2-4 of the escalation chain (AXConfirm bypass, child actions, CGClick fallback) dead code on web elements. Replace the pointer comparison with CFEqual, which compares accessibility element identity rather than heap addresses. --- crates/macos/src/actions/chain_steps.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/macos/src/actions/chain_steps.rs b/crates/macos/src/actions/chain_steps.rs index 5d46444..b4f468c 100644 --- a/crates/macos/src/actions/chain_steps.rs +++ b/crates/macos/src/actions/chain_steps.rs @@ -135,16 +135,13 @@ mod imp { .is_ok() } - /// Check if a web action actually changed something in the app. - /// Compares the focused UI element before vs after — if the app's - /// focused element changed, the action had a real DOM effect. fn web_action_had_effect(app: &AXElement, focused_before: Option<&AXElement>) -> bool { + use core_foundation::base::{CFEqual, CFTypeRef}; let focused_after = crate::tree::copy_element_attr(app, "AXFocusedUIElement"); match (focused_before, &focused_after) { - (Some(before), Some(after)) => { - // Different element pointers = focus moved = real effect - before.0 != after.0 - } + (Some(before), Some(after)) => unsafe { + CFEqual(before.0 as CFTypeRef, after.0 as CFTypeRef) == 0 + }, (None, Some(_)) => true, _ => false, }