diff --git a/delphi-backend/Source/PM.Bridge.pas b/delphi-backend/Source/PM.Bridge.pas index 21eeb76..eabf502 100644 --- a/delphi-backend/Source/PM.Bridge.pas +++ b/delphi-backend/Source/PM.Bridge.pas @@ -30,6 +30,7 @@ interface uses System.SysUtils, System.Classes, System.Math, System.Types, + System.Diagnostics, FMX.Types, FMX.Forms, Winapi.Windows, Winapi.ShellAPI, Winapi.Messages, Winapi.MultiMon, PM.SingleInstance, PM.UserPrefs; @@ -162,6 +163,8 @@ type // settings_json : elle peut etre allumee coffre ferme, quand aucune session // n'existe pour lire les reglages du serveur. FLazyAlphaPct: Integer; + // Voir la propriete LastFillWaits. + FLastFillWaits: string; // Dernier etat connu du survol, pour ne reposer l'opacite que sur un // changement - SetLayeredWindowAttributes force un repaint a chaque appel. FLazyHover: Boolean; @@ -250,6 +253,10 @@ type function ExecuteAutofill(ATargetHWND: HWND; const AUsername, APassword: string; AUsernameOnly: Boolean = False; ARestoreAfter: Boolean = False; AClearFirst: Boolean = True): Boolean; + /// Le detail des deux attentes du dernier remplissage, pour le journal de + /// l'hote. Une chaine plutot qu'un evenement : ExecuteAutofill s'execute + /// sur le thread principal, juste avant que l'hote journalise. + property LastFillWaits: string read FLastFillWaits; property SecureClipboard: TSecureClipboard read FSecureClipboard; property TrayAdded: Boolean read FTrayAdded; // Show or hide the lazy strip. Idempotent, and remembered across launches. @@ -1989,11 +1996,23 @@ begin Sleep(MinimizeSettleMs); end; + // LES DEUX SEULES ATTENTES QUI PEUVENT ENFLER, mesurees separement. + // + // Le total d'un remplissage est d'environ 660 ms par construction - des + // Sleep fixes. Quand il depasse plusieurs secondes, c'est forcement l'une de + // ces deux-la : la prise du premier plan, qui sonde jusqu'a 600 ms, ou + // l'attente de la relache des modificateurs, jusqu'a 1000 ms. Les confondre + // ferait chercher du cote du moteur web, qui n'y est pour rien. + var LSw := TStopwatch.StartNew; if ATargetHWND <> 0 then ForceForegroundWindow(ATargetHWND); + var LFgMs := LSw.ElapsedMilliseconds; + LSw := TStopwatch.StartNew; WaitForModifierRelease(1000); ForceReleaseModifiers; // timeout hit with keys still down → clean state + FLastFillWaits := Format('foreground=%d ms, modifiers=%d ms', + [LFgMs, LSw.ElapsedMilliseconds]); Sleep(FocusSettleDelayMs); // Never type into our own window: if the target refused the foreground, diff --git a/delphi-backend/UMainForm.pas b/delphi-backend/UMainForm.pas index 232208b..06b1557 100644 --- a/delphi-backend/UMainForm.pas +++ b/delphi-backend/UMainForm.pas @@ -18,6 +18,7 @@ interface uses System.SysUtils, System.Classes, System.UITypes, System.NetEncoding, System.StrUtils, System.Generics.Collections, System.IOUtils, System.JSON, + System.Diagnostics, System.Net.HttpClient, System.Net.URLClient, Winapi.Windows, Winapi.ShellAPI, FMX.Forms, FMX.Controls, FMX.Controls.Presentation, FMX.StdCtrls, @@ -109,6 +110,16 @@ type FQuitting: Boolean; // set when user picks "Quit" in tray menu — bypasses // FormCloseQuery's minimize-to-tray intercept. FAutofillTargetHWND: HWND; // saved at hotkey time, consumed on /execute + // CHRONOMETRE DU RACCOURCI, arme au moment ou l'on appelle la page. + // + // Un autofill lent apres une periode d'inactivite peut l'etre pour deux + // raisons sans rapport : le moteur WebView2 qui se reveille et met des + // secondes a repondre, ou la sequence de frappe elle-meme (prise du premier + // plan, attente des modificateurs). Les deux se ressemblent parfaitement de + // l'exterieur - l'utilisateur voit "ca a mis trois secondes" - et se + // corrigent a des endroits opposes. On mesure donc les deux segments + // separement plutot que de deviner lequel accuser. + FAutofillClock: TStopwatch; // Pending payload for the 60ms delay timer (focus settle before SendInput). // Cleared inside AutofillTimerTick. FAutofillPendingHWND: HWND; @@ -1153,6 +1164,16 @@ begin FAutofillPendingNotify := GetParam('notify') <> '0'; // absent = ON FAutofillTargetHWND := 0; + // PREMIER SEGMENT : combien la PAGE a mis a repondre. + // + // Tout ce qui precede est du JavaScript - reveil du moteur, appariement du + // titre, dechiffrement du mot de passe. Si le chiffre est gros ici et petit + // plus bas, c'est le renderer qu'il faut regarder, pas la sequence de + // frappe. Le contraire designe SendInput et la prise du premier plan. + if FAutofillClock.IsRunning then + LogLine(Format('Autofill: page answered in %d ms', + [FAutofillClock.ElapsedMilliseconds])); + // Small timer so SetForegroundWindow has time to take effect before // SendInput fires — avoids the first keystrokes going to our window. // TTimer.OnTimer is a TNotifyEvent (method, not anon proc) → we use a @@ -1891,9 +1912,18 @@ begin var LFillOk := FBridge.ExecuteAutofill(TargetHwnd, PendingUser, PendingPass, UserOnly, not HideAfter, ClearFirst); ForegroundAfter := GetForegroundWindow; - LogLine(Format('Autofill executed — target=%s, foreground_after=%s, ok=%s', + // SECOND SEGMENT, et le total. La difference entre les deux chiffres est le + // cout de la frappe : minimisation, prise du premier plan, attente de la + // relache des modificateurs, puis SendInput. + LogLine(Format('Autofill executed — target=%s, foreground_after=%s, ok=%s, ' + + 'total=%d ms', [IntToHex(TargetHwnd, 8), IntToHex(ForegroundAfter, 8), - BoolToStr(LFillOk, True)])); + BoolToStr(LFillOk, True), FAutofillClock.ElapsedMilliseconds])); + FAutofillClock.Stop; + // Le detail des deux attentes internes : sans lui, un total de trois secondes + // ne dit pas LAQUELLE a enfle, et les deux se corrigent differemment. + if FBridge.LastFillWaits <> '' then + LogLine('Autofill waits — ' + FBridge.LastFillWaits); // Tell JS whether the keystrokes were actually sent — the success toast // must not lie when the target runs elevated (UIPI drops our input). WebBrowser.ExecuteJavaScript( @@ -1942,6 +1972,9 @@ begin else LKind := 'full'; end; + // Arme AVANT l'appel : ce qui suit se passe dans le moteur, et c'est + // justement ce temps-la qu'on cherche a voir. + FAutofillClock := TStopwatch.StartNew; WebBrowser.ExecuteJavaScript( 'if(window.Bridge&&typeof Bridge.onAutofillRequest==="function")' + 'Bridge.onAutofillRequest("' + LTitle + '","' + LKind + '")');