AlmaLinux changes: Use AlmaLinux icon as activities button

Use unbranded illustrations

Fix Firefox desktop filename in favorites
This commit is contained in:
Eduard Abdullin 2026-08-14 01:47:16 +00:00 committed by root
commit fce3901f94
4 changed files with 1276 additions and 671 deletions

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,6 @@ Patch: disable-unlock-entry-until-question.patch
Patch: 0001-main-Register-session-with-GDM-on-startup.patch
Patch: 0001-loginManager-Update-RegisterSession.patch
# Passwordless work
Patch: pre-changes-for-passwordless-gdm-backport.patch
Patch: 0001-Support-for-web-login-and-unified-auth-mechanism.patch
Patch: post-changes-for-passwordless-gdm-backport.patch

View File

@ -1,7 +1,7 @@
From 45dd4808e26dabd03c25c126e97b22a20b4bcfa5 Mon Sep 17 00:00:00 2001
From bc94cdc84421125da2dcc5fc3d627a475bd565cc Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Tue, 2 Jun 2026 12:07:45 +0200
Subject: [PATCH 1/5] Use Object.keys(this._sections) instead of
Subject: [PATCH 01/12] Use Object.keys(this._sections) instead of
this._sections.keys()
---
@ -25,10 +25,10 @@ index 5aaa45fd3..e2c516585 100644
2.55.0
From 2f4e4d9bff76e6b4db951890b82282cc7a01921a Mon Sep 17 00:00:00 2001
From e1e99c9b443a60cf176d21b8538fc7f1ebd8ed65 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Tue, 2 Jun 2026 12:52:09 +0200
Subject: [PATCH 2/5] Don't use _once timeout variants
Subject: [PATCH 02/12] Don't use _once timeout variants
---
js/gdm/authServices.js | 7 +++++--
@ -98,16 +98,15 @@ index cf6a4e1fb..0271d98f2 100644
2.55.0
From 398fd1ad475e2092977cb210e12d3403bc0fdbaa Mon Sep 17 00:00:00 2001
From 9927abed68a443ee9f00ddcc5e4c48187a3c70da Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Tue, 9 Jun 2026 09:57:09 +0200
Subject: [PATCH 3/5] gdm: Use alternative to Promise.withResolvers()
Subject: [PATCH 03/12] gdm: Use alternative to Promise.withResolvers()
It doesn't exist in this version
---
js/gdm/authServices.js | 6 +++++-
js/gdm/userVerifier.js | 6 +++++-
2 files changed, 10 insertions(+), 2 deletions(-)
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/js/gdm/authServices.js b/js/gdm/authServices.js
index e13d4106e..e5cf21c96 100644
@ -126,31 +125,14 @@ index e13d4106e..e5cf21c96 100644
const task = Gio.Task.new(this, cancellable, () => {
try {
const res = task.propagate_boolean();
diff --git a/js/gdm/userVerifier.js b/js/gdm/userVerifier.js
index 02decbf86..27777c6f1 100644
--- a/js/gdm/userVerifier.js
+++ b/js/gdm/userVerifier.js
@@ -223,7 +223,11 @@ export class ShellUserVerifier extends Signals.EventEmitter {
return;
const message = this.currentMessage;
- const {promise, resolve} = Promise.withResolvers();
+ let resolve, reject;
+ const promise = new Promise((res, rej) => {
+ resolve = res;
+ reject = rej;
+ });
this._showMessageResolver = resolve;
this.emit('show-message', {
--
2.55.0
From dda52fc6a0e12af8dc3b6eca5f853923c311d837 Mon Sep 17 00:00:00 2001
From 8b06c49cb2e31b26cbf155bae0795fb7c0d7d6f6 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Thu, 6 Aug 2026 23:58:46 +0200
Subject: [PATCH 4/5] gdm: Use old ButtonMask enum
Subject: [PATCH 04/12] gdm: Use old ButtonMask enum
In this version it still used as ONE, TWO and THREE
---
@ -187,7 +169,7 @@ index e2c516585..ca9a0b9d1 100644
this.bind_property('reactive',
this, 'can-focus',
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 007c1538c..7e69525f9 100644
index 4c1697f08..31addc47f 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -291,7 +291,7 @@ export const AuthPrompt = GObject.registerClass({
@ -215,10 +197,11 @@ index 3b6496296..34c371b8d 100644
--
2.55.0
From 5e3b6d53a6e34e27e6c38d517fe51cfe52b2b96e Mon Sep 17 00:00:00 2001
From 4373ce16c8653c523fcca8879915254aa76d21d0 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Fri, 7 Aug 2026 11:42:37 +0200
Subject: [PATCH 5/5] style: Fix color styles to adapt when on light-mode
Subject: [PATCH 05/12] style: Fix color styles to adapt when on light-mode
(classic-mode)
---
@ -276,3 +259,866 @@ index da04425bd..98ecf7b0f 100644
--
2.55.0
From 3745870fd893ae84cde12f4a84e4bd9b2ea65762 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 12:37:41 +0200
Subject: [PATCH 06/12] gdm: Restore util.js and misc/smartcardManager.js as
compat shims
Several symbols moved out of util.js into userVerifier.js and
settings.js, and smartcardManager.js moved from misc/ to gdm/.
Extensions importing from the old locations would break.
Re-export the moved symbols from util.js, and add back
misc/smartcardManager.js as a thin re-export of the new gdm/
location, to keep the old import paths and names working.
---
js/gdm/util.js | 21 +++++++++++++++++++++
js/js-resources.gresource.xml | 1 +
js/misc/smartcardManager.js | 3 +++
3 files changed, 25 insertions(+)
create mode 100644 js/misc/smartcardManager.js
diff --git a/js/gdm/util.js b/js/gdm/util.js
index 10f9131d9..d9a7e2239 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -3,6 +3,27 @@ import Clutter from 'gi://Clutter';
import * as Batch from './batch.js';
import * as Main from '../ui/main.js';
+// Re-exported for backward compatibility: these symbols used to live in
+// this file before being split into userVerifier.js and settings.js.
+export {MessageType, ShellUserVerifier} from './userVerifier.js';
+export {
+ LOGIN_SCREEN_SCHEMA,
+ PASSWORD_AUTHENTICATION_KEY,
+ FINGERPRINT_AUTHENTICATION_KEY,
+ SMARTCARD_AUTHENTICATION_KEY,
+ BANNER_MESSAGE_KEY,
+ BANNER_MESSAGE_SOURCE_KEY,
+ BANNER_MESSAGE_TEXT_KEY,
+ BANNER_MESSAGE_PATH_KEY,
+ ALLOWED_FAILURES_KEY,
+ LOGO_KEY,
+ DISABLE_USER_LIST_KEY,
+} from './settings.js';
+
+export const PASSWORD_SERVICE_NAME = 'gdm-password';
+export const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
+export const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
+
export const CLONE_FADE_ANIMATION_TIME = 250;
/**
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index daff41d6a..a1c0e6e3c 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -50,6 +50,7 @@
<file>misc/permissionStore.js</file>
<file>misc/signals.js</file>
<file>misc/signalTracker.js</file>
+ <file>misc/smartcardManager.js</file>
<file>misc/systemActions.js</file>
<file>misc/timeLimitsManager.js</file>
<file>misc/util.js</file>
diff --git a/js/misc/smartcardManager.js b/js/misc/smartcardManager.js
new file mode 100644
index 000000000..df62ec048
--- /dev/null
+++ b/js/misc/smartcardManager.js
@@ -0,0 +1,3 @@
+// Re-exported for backward compatibility: this module used to live here
+// before being moved to gdm/smartcardManager.js.
+export {getSmartcardManager} from '../gdm/smartcardManager.js';
--
2.55.0
From bd1327dfb48dc9533d6e9055e6a42f0c1c1024bf Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 12:37:50 +0200
Subject: [PATCH 07/12] authPrompt: Restore BeginRequestType as alias for
ResetType
ResetType was renamed from BeginRequestType. Keep the old name
exported as an alias so extensions referencing
AuthPrompt.BeginRequestType keep working.
---
js/gdm/authPrompt.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 31addc47f..4f7ceb86c 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -44,6 +44,9 @@ export const ResetType = {
REUSE_USERNAME: 2,
};
+// Kept for backward compatibility: ResetType used to be named BeginRequestType.
+export const BeginRequestType = ResetType;
+
export const AuthPrompt = GObject.registerClass({
Signals: {
'cancelled': {},
--
2.55.0
From e5d4b76c4de043cb26f88065e623028970a5e1af Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 12:38:25 +0200
Subject: [PATCH 08/12] Revert "gdm/authServices: Use per-request callbacks to
handle services queries"
This reverts commit 929f864f1135a4225c09857ae3655d16de6c9251.
The per-request callback pattern removed ShellUserVerifier.answerQuery()
and selectChoice() as public methods, breaking extensions that call them
directly (e.g. ShellUserVerifier subclasses used by external IdP
integrations). Restore the original answerQuery()/selectChoice() API so
those extensions keep working, at the cost of the per-request context
safety the callback pattern added.
---
js/gdm/authPrompt.js | 82 ++++++++--------------------
js/gdm/authServices.js | 14 ++++-
js/gdm/authServicesLegacy.js | 29 ++++------
js/gdm/authServicesSSSDSwitchable.js | 55 +++++++------------
js/gdm/userVerifier.js | 16 +++++-
5 files changed, 79 insertions(+), 117 deletions(-)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 4f7ceb86c..bbf6c4e4e 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -194,7 +194,20 @@ export const AuthPrompt = GObject.registerClass({
this._authList = new AuthList.AuthList();
this._authList.hide();
- this._authListActivateId = 0;
+ this._authList.connect('activate', (list, key) => {
+ this._authList.reactive = false;
+ this._authList.ease({
+ opacity: 0,
+ duration: MESSAGE_FADE_OUT_ANIMATION_TIME * 0.5,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._authListTitle.child.text = '';
+ this._authList.clear();
+ this._authList.hide();
+ this._userVerifier.selectChoice(this._queryingService, key);
+ },
+ });
+ });
this.add_child(this._authList);
this._authListTitle = new St.Bin({
@@ -300,7 +313,6 @@ export const AuthPrompt = GObject.registerClass({
x_expand: true,
y_expand: true,
});
- this._authButton.connect('clicked', () => this._completePendingCallback());
this._mainBox.add_child(this._authButton);
this._webLoginDialog = new WebLogin.WebLoginDialog();
@@ -372,7 +384,7 @@ export const AuthPrompt = GObject.registerClass({
this.startSpinning({animate: true});
if (this._queryingService) {
- this._completePendingCallback(this._entry.text);
+ this._userVerifier.answerQuery(this._queryingService, this._entry.text);
} else {
this._preemptiveAnswer = this._entry.text;
@@ -410,37 +422,17 @@ export const AuthPrompt = GObject.registerClass({
this._capsLockWarningLabel.visible = secret;
}
- _setPendingCallback(callback) {
- if (this._pendingCallback)
- throw new Error('A pending request is already active');
- this._pendingCallback = callback;
- }
-
- _completePendingCallback(...args) {
- if (!this._pendingCallback)
- throw new Error('No pending request to complete');
-
- const callback = this._pendingCallback;
- this._pendingCallback = null;
-
- this._userVerifier.handlePendingMessages()
- .then(() => callback(...args))
- .catch(logErrorUnlessCancelled);
- }
-
- _onAskQuestion({serviceName, question, secret, answerHandler}) {
+ _onAskQuestion({serviceName, question, secret}) {
if (this._queryingService)
this.clear();
this._queryingService = serviceName;
this.promptStep++;
- this._setPendingCallback(answerHandler);
-
const preemptiveAnswer = this._preemptiveAnswer;
this._clearPreemptiveState();
if (preemptiveAnswer) {
- this._completePendingCallback(preemptiveAnswer);
+ this._userVerifier.answerQuery(this._queryingService, preemptiveAnswer);
return;
}
@@ -457,7 +449,7 @@ export const AuthPrompt = GObject.registerClass({
this.emit('prompted');
}
- _onShowChoiceList({serviceName, promptMessage, choiceList, choiceHandler}) {
+ _onShowChoiceList({serviceName, promptMessage, choiceList}) {
if (this._queryingService)
this.clear();
@@ -466,9 +458,6 @@ export const AuthPrompt = GObject.registerClass({
this._clearPreemptiveState();
- this._connectAuthListActivate();
- this._setPendingCallback(choiceHandler);
-
this.setChoiceList(promptMessage, choiceList);
this.updateSensitivity({sensitive: true});
this.emit('prompted');
@@ -512,7 +501,10 @@ export const AuthPrompt = GObject.registerClass({
this._clearPreemptiveState();
- this._setPendingCallback(callback);
+ const authButtonClickedId = this._authButton.connect('clicked', () => {
+ this._authButton.disconnect(authButtonClickedId);
+ callback();
+ });
this._authButton.set_label(label);
@@ -687,10 +679,8 @@ export const AuthPrompt = GObject.registerClass({
this._authListTitle.child.text = '';
this._authList.clear();
this._authList.hide();
- this._disconnectAuthListActivate();
this._authButton.hide();
this._closeWebLoginDialog();
- this._pendingCallback = null;
[this._mainBox, this._webLoginDialog].forEach(widget => {
widget.opacity = 255;
@@ -714,34 +704,6 @@ export const AuthPrompt = GObject.registerClass({
this.updateSensitivity({sensitive: true});
}
- _connectAuthListActivate() {
- if (this._authListActivateId)
- return;
-
- this._authListActivateId =
- this._authList.connect('activate', (list, key) => {
- this._authList.reactive = false;
- this._authList.ease({
- opacity: 0,
- duration: MESSAGE_FADE_OUT_ANIMATION_TIME * 0.5,
- mode: Clutter.AnimationMode.EASE_OUT_QUAD,
- onComplete: () => {
- this._authListTitle.child.text = '';
- this._authList.clear();
- this._authList.hide();
- this._completePendingCallback(key);
- },
- });
- });
- }
-
- _disconnectAuthListActivate() {
- if (this._authListActivateId) {
- this._authList.disconnect(this._authListActivateId);
- this._authListActivateId = 0;
- }
- }
-
_fadeInElement(element) {
if (element.visible)
return;
diff --git a/js/gdm/authServices.js b/js/gdm/authServices.js
index e5cf21c96..e5c60e1d7 100644
--- a/js/gdm/authServices.js
+++ b/js/gdm/authServices.js
@@ -81,7 +81,6 @@ export class AuthServices extends GObject.Object {
* @param {string} serviceName
* @param {string} question
* @param {boolean} [secret]
- * @param {Function} answerHandler
*/
'ask-question': {param_types: [GObject.TYPE_JSOBJECT]},
/**
@@ -93,7 +92,6 @@ export class AuthServices extends GObject.Object {
* @param {string} serviceName
* @param {string} promptMessage
* @param {object} [choiceList] - keys map to {title: string}
- * @param {Function} [choiceHandler]
*/
'show-choice-list': {param_types: [GObject.TYPE_JSOBJECT]},
/**
@@ -167,6 +165,14 @@ export class AuthServices extends GObject.Object {
return this._handleGetSupportedRoles();
}
+ selectChoice(serviceName, key) {
+ this._handleSelectChoice(serviceName, key);
+ }
+
+ answerQuery(serviceName, answer) {
+ this._handleAnswerQuery(serviceName, answer);
+ }
+
async beginVerification(userName, userVerifierProxies) {
this._cancellable?.cancel();
this._cancellable = new Gio.Cancellable();
@@ -483,6 +489,10 @@ export class AuthServices extends GObject.Object {
return this.constructor.SupportedRoles;
}
+ _handleSelectChoice() {}
+
+ _handleAnswerQuery() {}
+
_handleBeginVerification() {}
_handleSelectMechanism() {
diff --git a/js/gdm/authServicesLegacy.js b/js/gdm/authServicesLegacy.js
index 0e5c525de..f257b55fc 100644
--- a/js/gdm/authServicesLegacy.js
+++ b/js/gdm/authServicesLegacy.js
@@ -77,6 +77,14 @@ export class AuthServicesLegacy extends AuthServices {
this._fingerprintReadyTimeoutId = 0;
}
+ _handleSelectChoice(serviceName, key) {
+ if (serviceName !== this._selectedMechanism?.serviceName)
+ return;
+
+ this._userVerifierChoiceList.call_select_choice(
+ serviceName, key, this._cancellable).catch(logErrorUnlessCancelled);
+ }
+
_handleAnswerQuery(serviceName, answer) {
if (serviceName !== this._selectedMechanism?.serviceName)
return;
@@ -246,11 +254,7 @@ export class AuthServicesLegacy extends AuthServices {
if (serviceName !== this._selectedMechanism?.serviceName)
return;
- this.emit('ask-question', {
- serviceName,
- question,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
- });
+ this.emit('ask-question', {serviceName, question});
}
_handleOnSecretInfoQuery(serviceName, secretQuestion) {
@@ -262,7 +266,7 @@ export class AuthServicesLegacy extends AuthServices {
token = this._credentialManagers[serviceName].token;
if (token) {
- this._handleAnswerQuery(serviceName, token);
+ this.answerQuery(serviceName, token);
return;
}
@@ -270,7 +274,6 @@ export class AuthServicesLegacy extends AuthServices {
serviceName,
question: secretQuestion,
secret: true,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
});
}
@@ -346,17 +349,7 @@ export class AuthServicesLegacy extends AuthServices {
for (const [key, value] of Object.entries(list.deepUnpack()))
choiceList[key] = {title: value};
- this.emit('show-choice-list', {
- serviceName,
- promptMessage,
- choiceList,
- choiceHandler: key => {
- if (serviceName !== this._selectedMechanism?.serviceName)
- return;
- this._userVerifierChoiceList.call_select_choice(
- serviceName, key, this._cancellable).catch(logErrorUnlessCancelled);
- },
- });
+ this.emit('show-choice-list', {serviceName, promptMessage, choiceList});
}
_handleGetCredentialManagerServices() {
diff --git a/js/gdm/authServicesSSSDSwitchable.js b/js/gdm/authServicesSSSDSwitchable.js
index 0271d98f2..3a81b99c2 100644
--- a/js/gdm/authServicesSSSDSwitchable.js
+++ b/js/gdm/authServicesSSSDSwitchable.js
@@ -74,6 +74,23 @@ export class AuthServicesSSSDSwitchable extends AuthServices {
}
}
+ _handleSelectChoice(serviceName, key) {
+ if (serviceName !== this._selectedMechanism?.serviceName)
+ return;
+
+ if (this._selectedMechanism.role === Role.SMARTCARD) {
+ const certificates = this._selectedMechanism.certificates;
+ const cert = certificates.find(c => c.keyId === key);
+ this._selectedSmartcard = cert;
+ this._promptStatus = PromptStatus.PIN_PROMPT;
+ this.emit('ask-question', {
+ serviceName,
+ question: cert.pinPrompt,
+ secret: true,
+ });
+ }
+ }
+
_handleAnswerQuery(serviceName, answer) {
if (serviceName !== this._selectedMechanism?.serviceName)
return;
@@ -349,7 +366,6 @@ export class AuthServicesSSSDSwitchable extends AuthServices {
serviceName,
question: secretQuestion,
secret: true,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
});
}
}
@@ -437,12 +453,7 @@ export class AuthServicesSSSDSwitchable extends AuthServices {
const {serviceName, prompt} = this._selectedMechanism;
this._promptStatus = PromptStatus.PASSWORD_PROMPT;
- this.emit('ask-question', {
- serviceName,
- question: prompt,
- secret: true,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
- });
+ this.emit('ask-question', {serviceName, question: prompt, secret: true});
}
_startSmartcardLogin() {
@@ -455,7 +466,6 @@ export class AuthServicesSSSDSwitchable extends AuthServices {
serviceName,
question: certificates[0].pinPrompt,
secret: true,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
});
return;
}
@@ -469,27 +479,7 @@ export class AuthServicesSSSDSwitchable extends AuthServices {
: _('Select Identity');
this._promptStatus = PromptStatus.CERT_LIST_PROMPT;
- this.emit('show-choice-list', {
- serviceName,
- promptMessage,
- choiceList,
- choiceHandler: key => {
- if (serviceName !== this._selectedMechanism?.serviceName)
- return;
-
- if (this._selectedMechanism.role === Role.SMARTCARD) {
- const cert = this._selectedMechanism.certificates.find(c => c.keyId === key);
- this._selectedSmartcard = cert;
- this._promptStatus = PromptStatus.PIN_PROMPT;
- this.emit('ask-question', {
- serviceName,
- question: cert.pinPrompt,
- secret: true,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
- });
- }
- },
- });
+ this.emit('show-choice-list', {serviceName, promptMessage, choiceList});
}
_parseCertInstruction(certInstruction) {
@@ -524,12 +514,7 @@ export class AuthServicesSSSDSwitchable extends AuthServices {
}
this._promptStatus = PromptStatus.PIN_PROMPT;
- this.emit('ask-question', {
- serviceName,
- question: pinPrompt,
- secret: true,
- answerHandler: answer => this._handleAnswerQuery(serviceName, answer),
- });
+ this.emit('ask-question', {serviceName, question: pinPrompt, secret: true});
if (pinAttempts <= 3 && pinAttempts > 0) {
const message = _('You have %d attempts left. If the passkey gets locked, you may not able to access your account.').format(pinAttempts);
diff --git a/js/gdm/userVerifier.js b/js/gdm/userVerifier.js
index 6c63a313d..116c4ae1d 100644
--- a/js/gdm/userVerifier.js
+++ b/js/gdm/userVerifier.js
@@ -180,6 +180,18 @@ export class ShellUserVerifier extends Signals.EventEmitter {
this._settings = null;
}
+ selectChoice(serviceName, key) {
+ this._authServices.forEach(s => s.selectChoice(serviceName, key));
+ }
+
+ async answerQuery(serviceName, answer) {
+ // Wait for pending messages to be displayed before answering to
+ // ensure no messages get lost
+ await this._handlePendingMessages().catch(logErrorUnlessCancelled);
+
+ this._authServices.forEach(s => s.answerQuery(serviceName, answer));
+ }
+
addCredentialManager(serviceName, credentialManager) {
this._authServices.forEach(s => s.addCredentialManager(serviceName, credentialManager));
}
@@ -464,14 +476,14 @@ export class ShellUserVerifier extends Signals.EventEmitter {
async _waitPendingMessages({task}) {
try {
- await this.handlePendingMessages();
+ await this._handlePendingMessages();
task.return_boolean(true);
} catch (e) {
task.return_error(e);
}
}
- handlePendingMessages() {
+ _handlePendingMessages() {
if (!this.hasPendingMessages)
return Promise.resolve();
--
2.55.0
From 3aae4d78f1432281589ff688d799c582bf80156b Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 12:40:53 +0200
Subject: [PATCH 09/12] gdm: Restore positional signal signatures on
ShellUserVerifier
Signals on ShellUserVerifier ('ask-question', 'show-message',
'show-choice-list', 'verification-failed', 'reset') were changed to
pass a single object argument instead of the original positional
arguments, breaking extensions that connect to them with the old
handler signatures (e.g. (verifier, serviceName, question, secret)).
Restore the original positional argument order. Where the object
carried genuinely new information (like the show-message wiggle
resolver), append it as a trailing positional argument, which old
handlers simply ignore.
---
js/gdm/authPrompt.js | 12 ++++++++----
js/gdm/userVerifier.js | 20 +++++++++++---------
2 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index bbf6c4e4e..97129ca00 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -98,13 +98,17 @@ export const AuthPrompt = GObject.registerClass({
this._userVerifier = this._createUserVerifier(this._gdmClient, {reauthenticationOnly});
this._userVerifier.connectObject(
- 'ask-question', (_, args) => this._onAskQuestion(args),
- 'show-message', (_, args) => this._onShowMessage(args),
- 'show-choice-list', (_, args) => this._onShowChoiceList(args),
+ 'ask-question', (_, serviceName, question, secret) =>
+ this._onAskQuestion({serviceName, question, secret}),
+ 'show-message', (_, serviceName, message, type, shouldWiggle) =>
+ this._onShowMessage({message, type, shouldWiggle}),
+ 'show-choice-list', (_, serviceName, promptMessage, choiceList) =>
+ this._onShowChoiceList({serviceName, promptMessage, choiceList}),
'show-button', (_, args) => this._onShowButton(args),
'mechanisms-changed', (_, args) => this.emit('mechanisms-changed', args),
'web-login', (_, args) => this._onWebLogin(args),
- 'verification-failed', (_, args) => this._onVerificationFailed(args),
+ 'verification-failed', (_, serviceName, canRetry) =>
+ this._onVerificationFailed({serviceName, canRetry}),
'verification-complete', () => this._onVerificationComplete(),
'reset', (_, args) => this._onReset(args),
this);
diff --git a/js/gdm/userVerifier.js b/js/gdm/userVerifier.js
index 116c4ae1d..e5d3a1835 100644
--- a/js/gdm/userVerifier.js
+++ b/js/gdm/userVerifier.js
@@ -242,11 +242,11 @@ export class ShellUserVerifier extends Signals.EventEmitter {
const message = this.currentMessage;
delete this._currentMessageExtraInterval;
- this.emit('show-message', {
- message: message.text,
- type: message.type,
- shouldWiggle: message.wiggle,
- });
+ this.emit('show-message',
+ message.serviceName,
+ message.text,
+ message.type,
+ message.wiggle);
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
message.interval + (this._currentMessageExtraInterval | 0), () => {
@@ -299,7 +299,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
this._messageQueueTimeoutId = 0;
}
- this.emit('show-message', {type: MessageType.NONE});
+ this.emit('show-message', null, null, MessageType.NONE);
}
_reportInitError(initError) {
@@ -404,7 +404,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
_connectAuthServices() {
this._authServices.forEach(authServices => {
authServices.connectObject(
- 'ask-question', (_, args) => this.emit('ask-question', args),
+ 'ask-question', (_, args) =>
+ this.emit('ask-question', args.serviceName, args.question, args.secret ?? false),
'queue-message', (_, args) => this._queueMessage(args),
'queue-priority-message', (_, args) => this._queuePriorityMessage(args),
'wait-pending-messages', (_, args) => this._waitPendingMessages(args),
@@ -412,7 +413,8 @@ export class ShellUserVerifier extends Signals.EventEmitter {
'verification-failed', (_, args) => this._verificationFailed(args),
'verification-complete', () => this.emit('verification-complete'),
'reset', (_, args) => this.emit('reset', args),
- 'show-choice-list', (_, args) => this.emit('show-choice-list', args),
+ 'show-choice-list', (_, args) =>
+ this.emit('show-choice-list', args.serviceName, args.promptMessage, args.choiceList),
'show-button', (_, args) => this.emit('show-button', args),
'mechanisms-changed', () => this._onMechanismsChanged(),
'web-login', (_, args) => this.emit('web-login', args),
@@ -422,7 +424,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
_verificationFailed({serviceName, canRetry}) {
this._filterServiceMessages({serviceName, messageType: MessageType.ERROR});
- this.emit('verification-failed', {serviceName, canRetry});
+ this.emit('verification-failed', serviceName, canRetry);
}
get selectedMechanism() {
--
2.55.0
From 9c61a84b465df3bd0c83bbc6e3be5db811b5d04e Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 12:42:29 +0200
Subject: [PATCH 10/12] gdm: Add backward-compatible property and method shims
Several ShellUserVerifier properties and methods were dropped when
the class gained support for multiple concurrently-active AuthServices
(reauthenticating, smartcardDetected, serviceIsForeground(),
foregroundServiceDeterminesUsername(), serviceIsDefault(),
serviceIsFingerprint(), finishMessageQueue()), and AuthPrompt's
addCharacter() was renamed to startPreemptiveInput().
Restore reauthenticating as a real, tracked property, and add the
rest back as deprecated shims (documented with why they can no longer
be meaningfully implemented) so extensions calling them don't crash
with a TypeError.
---
js/gdm/authPrompt.js | 8 ++++++
js/gdm/userVerifier.js | 61 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 97129ca00..d062cdc4b 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -923,6 +923,14 @@ export const AuthPrompt = GObject.registerClass({
this._entry.clutter_text.insert_unichar(unichar);
}
+ /**
+ * @deprecated Renamed to startPreemptiveInput(), kept for backward
+ * compatibility.
+ */
+ addCharacter(unichar) {
+ this.startPreemptiveInput(unichar);
+ }
+
_clearPreemptiveState() {
this._preemptiveInput = false;
this._preemptiveAnswer = null;
diff --git a/js/gdm/userVerifier.js b/js/gdm/userVerifier.js
index e5d3a1835..2e863cb8b 100644
--- a/js/gdm/userVerifier.js
+++ b/js/gdm/userVerifier.js
@@ -65,6 +65,9 @@ export class ShellUserVerifier extends Signals.EventEmitter {
this._messageQueue = [];
this._messageQueueTimeoutId = 0;
+ // Kept for backward compatibility, see begin()/_getUserVerifierProxies().
+ this.reauthenticating = false;
+
this._settings = new Gio.Settings({schema_id: LOGIN_SCREEN_SCHEMA});
this._settings.connect('changed', () => this._onSettingsChanged());
this._updateAuthServices();
@@ -82,9 +85,21 @@ export class ShellUserVerifier extends Signals.EventEmitter {
return this._messageQueue ? this._messageQueue[0] : null;
}
+ /**
+ * @deprecated Kept for backward compatibility, always returns false.
+ * Smartcard detection is now internal to AuthServicesLegacy and no
+ * longer surfaced as a top-level property; the 'smartcard-status-changed'
+ * signal has also been removed. Use the 'mechanisms-changed' signal and
+ * check a mechanism's role instead.
+ */
+ get smartcardDetected() {
+ return false;
+ }
+
async begin(userName, hold) {
this._cancellable?.cancel();
this._cancellable = new Gio.Cancellable();
+ this.reauthenticating = false;
try {
const proxies = await this._getUserVerifierProxies(userName, this._cancellable);
@@ -114,6 +129,51 @@ export class ShellUserVerifier extends Signals.EventEmitter {
return this._authServices.some(s => s.needsUsername());
}
+ /**
+ * @deprecated Kept for backward compatibility. Replaced by needsUsername(),
+ * of which this is the logical inverse: the "foreground service" concept
+ * no longer exists now that multiple AuthServices can be active at once,
+ * each exposing its own selectable mechanisms.
+ */
+ foregroundServiceDeterminesUsername() {
+ return !this.needsUsername();
+ }
+
+ /**
+ * @deprecated Kept for backward compatibility, always returns false.
+ * There is no single "foreground service" anymore: each AuthServices
+ * instance tracks its own selected mechanism (see selectedMechanism).
+ */
+ serviceIsForeground(_serviceName) {
+ return false;
+ }
+
+ /**
+ * @deprecated Kept for backward compatibility, always returns false.
+ * Service roles are now assigned dynamically across AuthServices
+ * instances (see _redistributeRoles()) rather than through a single
+ * fixed default service.
+ */
+ serviceIsDefault(_serviceName) {
+ return false;
+ }
+
+ /**
+ * @deprecated Kept for backward compatibility, always returns false.
+ * Fingerprint handling is now internal to AuthServicesLegacy; use the
+ * 'mechanisms-changed' signal and check a mechanism's role instead.
+ */
+ serviceIsFingerprint(_serviceName) {
+ return false;
+ }
+
+ /**
+ * @deprecated Renamed to the private _finishMessageQueue().
+ */
+ finishMessageQueue() {
+ this._finishMessageQueue();
+ }
+
setDriverService(serviceName) {
if (this._driverService === serviceName)
return;
@@ -322,6 +382,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
try {
proxies.userVerifier = await this._client.open_reauthentication_channel(
userName, cancellable);
+ this.reauthenticating = true;
} catch (e) {
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
throw e;
--
2.55.0
From 173fd8c7d0ace6b253df40d7db1260d0b202b17e Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 13:08:48 +0200
Subject: [PATCH 11/12] authPrompt: updateSensitivity() signature
updateSensitivity() started requiring a {sensitive} options object
instead of accepting a plain boolean. Restore its call signature.
This also fixes _onUserStoppedTypePreemptiveAnswer(), which was
already calling updateSensitivity(false) with a bare boolean and
silently getting undefined out of the {sensitive} destructuring.
---
js/gdm/authPrompt.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index d062cdc4b..8d4de6bde 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -783,7 +783,11 @@ export const AuthPrompt = GObject.registerClass({
wiggle(this._message, wiggleParameters);
}
- updateSensitivity({sensitive}) {
+ updateSensitivity(params) {
+ // Kept for backward compatibility: this used to take a plain
+ // boolean instead of a {sensitive} options object.
+ const {sensitive} = typeof params === 'boolean' ? {sensitive: params} : params;
+
if (sensitive && this._preemptiveAnswer)
return;
--
2.55.0
From f3ee5bfd61b258f90fcfda784bac7e99194a89a5 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Wed, 12 Aug 2026 17:08:36 +0200
Subject: [PATCH 12/12] gdm/userVerifier: needsUsername should use every
When every authServices needUsername then return true, otherwise return
false.
---
js/gdm/userVerifier.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/gdm/userVerifier.js b/js/gdm/userVerifier.js
index 2e863cb8b..e0a85c693 100644
--- a/js/gdm/userVerifier.js
+++ b/js/gdm/userVerifier.js
@@ -126,7 +126,7 @@ export class ShellUserVerifier extends Signals.EventEmitter {
}
needsUsername() {
- return this._authServices.some(s => s.needsUsername());
+ return this._authServices.every(s => s.needsUsername());
}
/**
--
2.55.0

View File

@ -1,217 +0,0 @@
From 1c91eaa7fb93536a1e3e2695d856721d403171f6 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Tue, 5 May 2026 17:26:27 +0200
Subject: [PATCH 1/3] animationUtils: Skip no-op wiggle to avoid cancelling
pending transitions
When wiggle() is called with duration 0, the async easeAsync calls
yield between microtasks, leaving pending operations that remove
transitions on the same property. This becomes a problem when two
setMessage() calls happen in the same call stack (e.g. via
_queuePriorityMessage): the first fires a no-op wiggle(dur:0) and
the second starts the real wiggle(dur:65). When microtasks drain,
the first wiggle's pending easeAsync calls overwrite the second
wiggle's active transition, effectively cancelling the real animation.
Return early when duration is 0, since the animation is a no-op anyway.
Fixes: fdadbb7a5a2 ("animationUtils: Use easeAsync() for wiggle")
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/work_items/9207
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4209>
---
js/misc/animationUtils.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/misc/animationUtils.js b/js/misc/animationUtils.js
index cf6889609..9d065cf78 100644
--- a/js/misc/animationUtils.js
+++ b/js/misc/animationUtils.js
@@ -84,7 +84,7 @@ export function ensureActorVisibleInScrollView(scrollView, actor) {
* @param {number} params.wiggleCount - the number of times to wiggle the actor
*/
export function wiggle(actor, params) {
- if (!St.Settings.get().enable_animations)
+ if (!St.Settings.get().enable_animations || params?.duration === 0)
return;
params = Params.parse(params, {
--
2.55.0
From b5a37cbdd89eeed44966455ba22b0f10a3f25091 Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Tue, 5 May 2026 21:32:06 +0200
Subject: [PATCH 2/3] authPrompt: Call wiggle directly in _onShowMessage
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4209>
---
js/gdm/authPrompt.js | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 3b4a2f798..f05856416 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -394,21 +394,20 @@ export const AuthPrompt = GObject.registerClass({
}
_onShowMessage(_userVerifier, serviceName, message, type) {
- let wiggleParameters = {duration: 0};
+ this.setMessage(message, type);
+ this.emit('prompted');
if (type === GdmUtil.MessageType.ERROR &&
this._userVerifier.serviceIsFingerprint(serviceName)) {
// TODO: Use Await for wiggle to be over before unfreezing the user verifier queue
- wiggleParameters = {
+ const wiggleParameters = {
duration: 65,
wiggleCount: 3,
};
this._userVerifier.increaseCurrentMessageTimeout(
wiggleParameters.duration * (wiggleParameters.wiggleCount + 2));
+ wiggle(this._message, wiggleParameters);
}
-
- this.setMessage(message, type, wiggleParameters);
- this.emit('prompted');
}
_onVerificationFailed(userVerifier, serviceName, canRetry) {
@@ -587,7 +586,7 @@ export const AuthPrompt = GObject.registerClass({
});
}
- setMessage(message, type, wiggleParameters = {duration: 0}) {
+ setMessage(message, type) {
if (type === GdmUtil.MessageType.ERROR)
this._message.add_style_class_name('login-dialog-message-warning');
else
@@ -607,8 +606,6 @@ export const AuthPrompt = GObject.registerClass({
} else {
this._message.opacity = 0;
}
-
- wiggle(this._message, wiggleParameters);
}
updateSensitivity(sensitive) {
--
2.55.0
From 1e3f937a6ccfc8ea79c80d78df121102cffbdeff Mon Sep 17 00:00:00 2001
From: Joan Torres Lopez <joantolo@redhat.com>
Date: Tue, 5 May 2026 17:28:18 +0200
Subject: [PATCH 3/3] authPrompt: Await wiggle via show-message promise
resolver
Pass promise resolver through the show-message signal so the
message queue waits for the wiggle animation to finish before scheduling
the next message timeout. This replaces the previous approach of manually
increasing the timeout duration to account for the wiggle.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4209>
---
js/gdm/authPrompt.js | 21 +++++++++------------
js/gdm/util.js | 27 +++++++++++++++++----------
2 files changed, 26 insertions(+), 22 deletions(-)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index f05856416..b1e8202bd 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -393,21 +393,18 @@ export const AuthPrompt = GObject.registerClass({
this.reset();
}
- _onShowMessage(_userVerifier, serviceName, message, type) {
+ _onShowMessage(_userVerifier, serviceName, message, type, showMessageResolver) {
this.setMessage(message, type);
this.emit('prompted');
- if (type === GdmUtil.MessageType.ERROR &&
- this._userVerifier.serviceIsFingerprint(serviceName)) {
- // TODO: Use Await for wiggle to be over before unfreezing the user verifier queue
- const wiggleParameters = {
- duration: 65,
- wiggleCount: 3,
- };
- this._userVerifier.increaseCurrentMessageTimeout(
- wiggleParameters.duration * (wiggleParameters.wiggleCount + 2));
- wiggle(this._message, wiggleParameters);
- }
+ const shouldWiggle = type === GdmUtil.MessageType.ERROR &&
+ this._userVerifier.serviceIsFingerprint(serviceName);
+
+ const wigglePromise = shouldWiggle
+ ? wiggle(this._message, {duration: 65, wiggleCount: 3})
+ : Promise.resolve();
+
+ showMessageResolver?.(wigglePromise);
}
_onVerificationFailed(userVerifier, serviceName, canRetry) {
diff --git a/js/gdm/util.js b/js/gdm/util.js
index 4b0f763f0..e529f84fa 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -262,11 +262,6 @@ export class ShellUserVerifier extends Signals.EventEmitter {
this.emit('no-more-messages');
}
- increaseCurrentMessageTimeout(interval) {
- if (!this._messageQueueTimeoutId && interval > 0)
- this._currentMessageExtraInterval = interval;
- }
-
_serviceHasPendingMessages(serviceName) {
return this._messageQueue.some(m => m.serviceName === serviceName);
}
@@ -279,17 +274,23 @@ export class ShellUserVerifier extends Signals.EventEmitter {
this._queuePriorityMessage(serviceName, null, messageType);
}
- _queueMessageTimeout() {
- if (this._messageQueueTimeoutId !== 0)
+ async _queueMessageTimeout() {
+ if (this._messageQueueTimeoutId !== 0 || this._showMessageResolver)
return;
const message = this.currentMessage;
+ const {promise, resolve} = Promise.withResolvers();
+ this._showMessageResolver = resolve;
- delete this._currentMessageExtraInterval;
- this.emit('show-message', message.serviceName, message.text, message.type);
+ this.emit('show-message', message.serviceName, message.text, message.type, this._showMessageResolver);
+
+ await promise.catch(logError);
+ if (!this._showMessageResolver)
+ return;
+ this._showMessageResolver = null;
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
- message.interval + (this._currentMessageExtraInterval | 0), () => {
+ message.interval, () => {
this._messageQueueTimeoutId = 0;
if (this._messageQueue.length > 1) {
@@ -332,6 +333,12 @@ export class ShellUserVerifier extends Signals.EventEmitter {
GLib.source_remove(this._messageQueueTimeoutId);
this._messageQueueTimeoutId = 0;
}
+
+ if (this._showMessageResolver) {
+ this._showMessageResolver();
+ this._showMessageResolver = null;
+ }
+
this.emit('show-message', null, null, MessageType.NONE);
}
--
2.55.0