Files
strans/tests/gtk_live_test.c
Hojun-Cho b61c04b280 gtk: an entry hidden the older way is private too
isprivate() reads the GTK input purpose and nothing else, but the purpose
is not the only way an application marks a password field, and it is not
the older one.  GTK 3.24.52, measured:

	gtk_entry_set_visibility(entry, FALSE)	purpose stays FREE_FORM
	gtk_entry_set_input_purpose(PASSWORD)	purpose PASSWORD

so an entry hidden the first way looks like ordinary text to the module.
This machine runs one.  /usr/libexec/xfce-polkit, the XFCE authentication
dialog, is up right now under GTK_IM_MODULE=strans, and its binary calls
gtk_entry_set_visibility and never gtk_entry_set_input_purpose, so every
password typed into it goes through the engine.  Against the real daemon,
in Korean mode:

	typed hunter2		stored ㅗㅕ숟ㄱ2
	typed correcthorse	stored 책ㄱㄷㅊ쇅ㄴㄷ
	typed P4ssw0rd		stored ㅔ4ㄴㄵ0ㄱㅇ

The field draws bullets, so nothing on screen says why the authentication
failed -- except the pending syllable, which is drawn as itself: three
keys into such an entry the old module leaves 한 on screen where the
field should read ●●●.  With this it reads ●●●, and the entry holds gks.

set_visibility sends the input context no signal, so the question cannot
be answered where the purpose is, in init and notify::input-purpose.  It
has to be asked at the key, of the client window, which is the entry's
own: gdk_window_get_user_data on it returns the GtkEntry.  fcitx5-gtk asks
it the same way in all three of its GTK versions -- gtk3/fcitximcontext
.cpp:1155-1162, under the comment "seems visibility != PASSWORD hint".

It costs one field read and one type check per key, on a path that then
does a socket round trip anyway.  It does not cover XIM: that protocol has
no attribute for this, and no fcitx5 frontend answers it either -- only
its GTK and Qt client modules do -- so an X client reached over XIM still
composes in its password field.  Nor does it notice a "show password" box
switched off in the middle of a composition: there is no signal for
visibility, so the pending text stays in the daemon until the next focus
change.  Noticing that needs a signal connection on a widget the module
does not own, which is a bigger thing than the hole is.

The test is a live one and it earns its line: with the fix removed it says

	gtk_live_test: hidden-entry key reached daemon or did not commit

Both guards were checked by removing them.  Without GTK_IS_ENTRY the run
takes a Gtk-CRITICAL and fails; without the im->win test it exits 139, on
the stalled-peer context, which never gets a client window.  An ishidden
that always answers yes fails earlier still, at the initial protocol
frames.  Breaking the test's own helper so it hands back the toplevel's
window instead of the entry's fails too, which is what says the assertion
watches the right window rather than merely counting no events.

That count is the one thing that had to be made deterministic.  A new
client window invalidates the caret, and the fake daemon records that
frame on its own thread, so sampling the event count straight after
set_client_window raced it: 2 failures in 8 runs.  The block now waits for
that frame and names it, and there were 0 in 20 after.

90 unit, check-live, check-stress and valgrind clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 04:18:41 +09:00

24 KiB