fix(autofill): kill residual hotkey chord; dedicated fail-balloon setting

Residual new-entry trigger (1 in 6): password chars go out as
KEYEVENTF_UNICODE (VK_PACKET, can't match a hotkey) — the real chord risk is
the Ctrl+A clear-field, which sends a real VK_A. If the user re-presses
Ctrl+Shift mid-sequence, that VK_A becomes physical Ctrl+Shift+A = our own
new-entry hotkey. ForceReleaseModifiers now runs inside
SendSelectAllAndDelete, at the risky instant, not just once up front.

Balloon: was gated by "Show tray notifications" (OFF for this user) — now
gated by its own synced setting "Tray alert when autofill is blocked"
(autofillFailBalloon, Settings > Autofill, default ON), carried as notify=0
on cmd://autofill/execute. ShowBalloon no longer gates internally; each
caller applies its own setting.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-12 05:23:02 +01:00
parent 9a72fc0424
commit 72dcc3dd82
4 changed files with 48 additions and 10 deletions
+10 -4
View File
@@ -127,8 +127,8 @@ type
procedure ShowFirstTimeBalloon;
function FindFMXAppWindow: HWND;
public
// Tray balloon (gated by the "Show tray notifications" setting). Used
// for messages the user must see while the window is hidden — e.g. an
// Tray balloon (caller applies its own gating setting). Used for
// messages the user must see while the window is hidden — e.g. an
// autofill blocked by an elevated target.
procedure ShowBalloon(const ATitle, AText: string; AWarning: Boolean = False);
constructor Create(AMainForm: TForm);
@@ -623,8 +623,8 @@ procedure TPMBridge.ShowBalloon(const ATitle, AText: string;
var
LBalloon: TNotifyIconData;
begin
// Gated by the "Show tray notifications" user setting.
if not FShowNotifications then Exit;
// No gating here — each caller applies its own setting (first-time tray
// balloon → FShowNotifications; autofill-blocked → autofillFailBalloon).
// Build a separate TNotifyIconData with NIF_INFO set, NIM_MODIFY on the
// same uID. szInfo/szInfoTitle carry the balloon content.
FillChar(LBalloon, SizeOf(LBalloon), 0);
@@ -1091,6 +1091,12 @@ procedure SendSelectAllAndDelete;
var
LInputs: array[0..5] of TInput;
begin
// This is the ONLY real-VK chord we inject (password chars go out as
// KEYEVENTF_UNICODE / VK_PACKET, which can't match a RegisterHotKey).
// If the user re-presses Ctrl+Shift mid-sequence, our VK_A below becomes
// a physical Ctrl+Shift+A = our own new-entry hotkey firing mid-fill.
// Force-release right here, at the risky instant — not just once up front.
ForceReleaseModifiers;
FillChar(LInputs, SizeOf(LInputs), 0);
LInputs[0].Itype := INPUT_KEYBOARD;
LInputs[0].ki.wVk := VK_CONTROL;