8423ba44fe
These traditionally got the various ClutterEvent subtype structs as their argument, so it was not allowed to use ClutterEvent generic getter methods in these vfuncs. These methods used direct access to struct fields instead. This got spoiled with the move to make ClutterEvent opaque types, since these are no longer public structs so GNOME Shell most silently failed to fetch the expected values from event fields. But since they are not ClutterEvents either, the getters could not be used on them. Mutter is changing so that these vmethods all contain an alias to the one and only Clutter.Event type, thus lifting those barriers, and making it possible to use the ClutterEvent methods in these vfuncs. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2950 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2872>
711 lines
23 KiB
JavaScript
711 lines
23 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||
|
||
import Clutter from 'gi://Clutter';
|
||
import GLib from 'gi://GLib';
|
||
import GObject from 'gi://GObject';
|
||
import Pango from 'gi://Pango';
|
||
import Shell from 'gi://Shell';
|
||
import St from 'gi://St';
|
||
|
||
import * as Animation from '../ui/animation.js';
|
||
import * as AuthList from './authList.js';
|
||
import * as Batch from './batch.js';
|
||
import * as GdmUtil from './util.js';
|
||
import * as Params from '../misc/params.js';
|
||
import * as ShellEntry from '../ui/shellEntry.js';
|
||
import * as UserWidget from '../ui/userWidget.js';
|
||
import {wiggle} from '../misc/animationUtils.js';
|
||
|
||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 16;
|
||
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1000;
|
||
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 300;
|
||
|
||
const MESSAGE_FADE_OUT_ANIMATION_TIME = 500;
|
||
|
||
/** @enum {number} */
|
||
export const AuthPromptMode = {
|
||
UNLOCK_ONLY: 0,
|
||
UNLOCK_OR_LOG_IN: 1,
|
||
};
|
||
|
||
/** @enum {number} */
|
||
export const AuthPromptStatus = {
|
||
NOT_VERIFYING: 0,
|
||
VERIFYING: 1,
|
||
VERIFICATION_FAILED: 2,
|
||
VERIFICATION_SUCCEEDED: 3,
|
||
VERIFICATION_CANCELLED: 4,
|
||
VERIFICATION_IN_PROGRESS: 5,
|
||
};
|
||
|
||
/** @enum {number} */
|
||
export const BeginRequestType = {
|
||
PROVIDE_USERNAME: 0,
|
||
DONT_PROVIDE_USERNAME: 1,
|
||
REUSE_USERNAME: 2,
|
||
};
|
||
|
||
export const AuthPrompt = GObject.registerClass({
|
||
Signals: {
|
||
'cancelled': {},
|
||
'failed': {},
|
||
'next': {},
|
||
'prompted': {},
|
||
'reset': { param_types: [GObject.TYPE_UINT] },
|
||
},
|
||
}, class AuthPrompt extends St.BoxLayout {
|
||
_init(gdmClient, mode) {
|
||
super._init({
|
||
style_class: 'login-dialog-prompt-layout',
|
||
vertical: true,
|
||
x_expand: true,
|
||
x_align: Clutter.ActorAlign.CENTER,
|
||
reactive: true,
|
||
});
|
||
|
||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||
|
||
this._gdmClient = gdmClient;
|
||
this._mode = mode;
|
||
this._defaultButtonWellActor = null;
|
||
this._cancelledRetries = 0;
|
||
|
||
let reauthenticationOnly;
|
||
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
|
||
reauthenticationOnly = true;
|
||
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
|
||
reauthenticationOnly = false;
|
||
|
||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly });
|
||
|
||
this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this));
|
||
this._userVerifier.connect('show-message', this._onShowMessage.bind(this));
|
||
this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this));
|
||
this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this));
|
||
this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
|
||
this._userVerifier.connect('reset', this._onReset.bind(this));
|
||
this._userVerifier.connect('smartcard-status-changed', this._onSmartcardStatusChanged.bind(this));
|
||
this._userVerifier.connect('credential-manager-authenticated', this._onCredentialManagerAuthenticated.bind(this));
|
||
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||
|
||
this.connect('destroy', this._onDestroy.bind(this));
|
||
|
||
this._userWell = new St.Bin({
|
||
x_expand: true,
|
||
y_expand: true,
|
||
});
|
||
this.add_child(this._userWell);
|
||
|
||
this._hasCancelButton = this._mode === AuthPromptMode.UNLOCK_OR_LOG_IN;
|
||
|
||
this._initInputRow();
|
||
|
||
let capsLockPlaceholder = new St.Label();
|
||
this.add_child(capsLockPlaceholder);
|
||
|
||
this._capsLockWarningLabel = new ShellEntry.CapsLockWarning({
|
||
x_expand: true,
|
||
x_align: Clutter.ActorAlign.CENTER,
|
||
});
|
||
this.add_child(this._capsLockWarningLabel);
|
||
|
||
this._capsLockWarningLabel.bind_property('visible',
|
||
capsLockPlaceholder, 'visible',
|
||
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN);
|
||
|
||
this._message = new St.Label({
|
||
opacity: 0,
|
||
styleClass: 'login-dialog-message',
|
||
y_expand: true,
|
||
x_expand: true,
|
||
y_align: Clutter.ActorAlign.START,
|
||
x_align: Clutter.ActorAlign.CENTER,
|
||
});
|
||
this._message.clutter_text.line_wrap = true;
|
||
this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
|
||
this.add_child(this._message);
|
||
}
|
||
|
||
_onDestroy() {
|
||
this._inactiveEntry.destroy();
|
||
this._inactiveEntry = null;
|
||
this._userVerifier.destroy();
|
||
this._userVerifier = null;
|
||
}
|
||
|
||
vfunc_key_press_event(event) {
|
||
if (event.get_key_symbol() === Clutter.KEY_Escape)
|
||
this.cancel();
|
||
return super.vfunc_key_press_event(event);
|
||
}
|
||
|
||
_initInputRow() {
|
||
this._mainBox = new St.BoxLayout({
|
||
style_class: 'login-dialog-button-box',
|
||
vertical: false,
|
||
});
|
||
this.add_child(this._mainBox);
|
||
|
||
this.cancelButton = new St.Button({
|
||
style_class: 'login-dialog-button cancel-button',
|
||
accessible_name: _('Cancel'),
|
||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||
reactive: this._hasCancelButton,
|
||
can_focus: this._hasCancelButton,
|
||
x_align: Clutter.ActorAlign.START,
|
||
y_align: Clutter.ActorAlign.CENTER,
|
||
icon_name: 'go-previous-symbolic',
|
||
});
|
||
if (this._hasCancelButton)
|
||
this.cancelButton.connect('clicked', () => this.cancel());
|
||
else
|
||
this.cancelButton.opacity = 0;
|
||
this._mainBox.add_child(this.cancelButton);
|
||
|
||
this._authList = new AuthList.AuthList();
|
||
this._authList.set({
|
||
visible: false,
|
||
});
|
||
this._authList.connect('activate', (list, key) => {
|
||
this._authList.reactive = false;
|
||
this._authList.ease({
|
||
opacity: 0,
|
||
duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
|
||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||
onComplete: () => {
|
||
this._authList.clear();
|
||
this._authList.hide();
|
||
this._userVerifier.selectChoice(this._queryingService, key);
|
||
},
|
||
});
|
||
});
|
||
this._mainBox.add_child(this._authList);
|
||
|
||
let entryParams = {
|
||
style_class: 'login-dialog-prompt-entry',
|
||
can_focus: true,
|
||
x_expand: true,
|
||
};
|
||
|
||
this._entry = null;
|
||
|
||
this._textEntry = new St.Entry(entryParams);
|
||
ShellEntry.addContextMenu(this._textEntry, {actionMode: Shell.ActionMode.NONE});
|
||
|
||
this._passwordEntry = new St.PasswordEntry(entryParams);
|
||
ShellEntry.addContextMenu(this._passwordEntry, {actionMode: Shell.ActionMode.NONE});
|
||
|
||
this._entry = this._passwordEntry;
|
||
this._mainBox.add_child(this._entry);
|
||
this._entry.grab_key_focus();
|
||
this._inactiveEntry = this._textEntry;
|
||
|
||
this._timedLoginIndicator = new St.Bin({
|
||
style_class: 'login-dialog-timed-login-indicator',
|
||
scale_x: 0,
|
||
});
|
||
|
||
this.add_child(this._timedLoginIndicator);
|
||
|
||
[this._textEntry, this._passwordEntry].forEach(entry => {
|
||
entry.clutter_text.connect('text-changed', () => {
|
||
if (!this._userVerifier.hasPendingMessages)
|
||
this._fadeOutMessage();
|
||
});
|
||
|
||
entry.clutter_text.connect('activate', () => {
|
||
let shouldSpin = entry === this._passwordEntry;
|
||
if (entry.reactive)
|
||
this._activateNext(shouldSpin);
|
||
});
|
||
});
|
||
|
||
this._defaultButtonWell = new St.Widget({
|
||
layout_manager: new Clutter.BinLayout(),
|
||
x_align: Clutter.ActorAlign.END,
|
||
y_align: Clutter.ActorAlign.CENTER,
|
||
});
|
||
this._defaultButtonWell.add_constraint(new Clutter.BindConstraint({
|
||
source: this.cancelButton,
|
||
coordinate: Clutter.BindCoordinate.WIDTH,
|
||
}));
|
||
this._mainBox.add_child(this._defaultButtonWell);
|
||
|
||
this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE);
|
||
this._defaultButtonWell.add_child(this._spinner);
|
||
}
|
||
|
||
showTimedLoginIndicator(time) {
|
||
let hold = new Batch.Hold();
|
||
|
||
this.hideTimedLoginIndicator();
|
||
|
||
const startTime = GLib.get_monotonic_time();
|
||
|
||
this._timedLoginTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 33,
|
||
() => {
|
||
const currentTime = GLib.get_monotonic_time();
|
||
const elapsedTime = (currentTime - startTime) / GLib.USEC_PER_SEC;
|
||
this._timedLoginIndicator.scale_x = elapsedTime / time;
|
||
if (elapsedTime >= time) {
|
||
this._timedLoginTimeoutId = 0;
|
||
hold.release();
|
||
return GLib.SOURCE_REMOVE;
|
||
}
|
||
|
||
return GLib.SOURCE_CONTINUE;
|
||
});
|
||
|
||
GLib.Source.set_name_by_id(this._timedLoginTimeoutId, '[gnome-shell] this._timedLoginTimeoutId');
|
||
|
||
return hold;
|
||
}
|
||
|
||
hideTimedLoginIndicator() {
|
||
if (this._timedLoginTimeoutId) {
|
||
GLib.source_remove(this._timedLoginTimeoutId);
|
||
this._timedLoginTimeoutId = 0;
|
||
}
|
||
this._timedLoginIndicator.scale_x = 0.;
|
||
}
|
||
|
||
_activateNext(shouldSpin) {
|
||
this.verificationStatus = AuthPromptStatus.VERIFICATION_IN_PROGRESS;
|
||
this.updateSensitivity(false);
|
||
|
||
if (shouldSpin)
|
||
this.startSpinning();
|
||
|
||
if (this._queryingService)
|
||
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
|
||
else
|
||
this._preemptiveAnswer = this._entry.text;
|
||
|
||
this.emit('next');
|
||
}
|
||
|
||
_updateEntry(secret) {
|
||
if (secret && this._entry !== this._passwordEntry) {
|
||
this._mainBox.replace_child(this._entry, this._passwordEntry);
|
||
this._entry = this._passwordEntry;
|
||
this._inactiveEntry = this._textEntry;
|
||
} else if (!secret && this._entry !== this._textEntry) {
|
||
this._mainBox.replace_child(this._entry, this._textEntry);
|
||
this._entry = this._textEntry;
|
||
this._inactiveEntry = this._passwordEntry;
|
||
}
|
||
this._capsLockWarningLabel.visible = secret;
|
||
}
|
||
|
||
_onAskQuestion(verifier, serviceName, question, secret) {
|
||
if (this._queryingService)
|
||
this.clear();
|
||
|
||
this._queryingService = serviceName;
|
||
if (this._preemptiveAnswer) {
|
||
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
|
||
this._preemptiveAnswer = null;
|
||
return;
|
||
}
|
||
|
||
this._updateEntry(secret);
|
||
|
||
// Hack: The question string comes directly from PAM, if it's "Password:"
|
||
// we replace it with our own to allow localization, if it's something
|
||
// else we remove the last colon and any trailing or leading spaces.
|
||
if (question === 'Password:' || question === 'Password: ')
|
||
this.setQuestion(_('Password'));
|
||
else
|
||
this.setQuestion(question.replace(/[::] *$/, '').trim());
|
||
|
||
this.updateSensitivity(true);
|
||
this.emit('prompted');
|
||
}
|
||
|
||
_onShowChoiceList(userVerifier, serviceName, promptMessage, choiceList) {
|
||
if (this._queryingService)
|
||
this.clear();
|
||
|
||
this._queryingService = serviceName;
|
||
|
||
if (this._preemptiveAnswer)
|
||
this._preemptiveAnswer = null;
|
||
|
||
this.setChoiceList(promptMessage, choiceList);
|
||
this.updateSensitivity(true);
|
||
this.emit('prompted');
|
||
}
|
||
|
||
_onCredentialManagerAuthenticated() {
|
||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||
this.reset();
|
||
}
|
||
|
||
_onSmartcardStatusChanged() {
|
||
this.smartcardDetected = this._userVerifier.smartcardDetected;
|
||
|
||
// Most of the time we want to reset if the user inserts or removes
|
||
// a smartcard. Smartcard insertion "preempts" what the user was
|
||
// doing, and smartcard removal aborts the preemption.
|
||
// The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
|
||
// with a smartcard
|
||
// 2) Don't reset if we've already succeeded at verification and
|
||
// the user is getting logged in.
|
||
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
|
||
this.verificationStatus == AuthPromptStatus.VERIFYING &&
|
||
this.smartcardDetected)
|
||
return;
|
||
|
||
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||
this.reset();
|
||
}
|
||
|
||
_onShowMessage(_userVerifier, serviceName, message, type) {
|
||
let wiggleParameters = {duration: 0};
|
||
|
||
if (type === GdmUtil.MessageType.ERROR &&
|
||
this._userVerifier.serviceIsFingerprint(serviceName)) {
|
||
// TODO: Use Await for wiggle to be over before unfreezing the user verifier queue
|
||
wiggleParameters = {
|
||
duration: 65,
|
||
wiggleCount: 3,
|
||
};
|
||
this._userVerifier.increaseCurrentMessageTimeout(
|
||
wiggleParameters.duration * (wiggleParameters.wiggleCount + 2));
|
||
}
|
||
|
||
this.setMessage(message, type, wiggleParameters);
|
||
this.emit('prompted');
|
||
}
|
||
|
||
_onVerificationFailed(userVerifier, serviceName, canRetry) {
|
||
const wasQueryingService = this._queryingService === serviceName;
|
||
|
||
if (wasQueryingService) {
|
||
this._queryingService = null;
|
||
this.clear();
|
||
}
|
||
|
||
this.updateSensitivity(canRetry);
|
||
this.setActorInDefaultButtonWell(null);
|
||
|
||
if (!canRetry)
|
||
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
|
||
|
||
if (wasQueryingService)
|
||
wiggle(this._entry);
|
||
}
|
||
|
||
_onVerificationComplete() {
|
||
this.setActorInDefaultButtonWell(null);
|
||
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
|
||
this.cancelButton.reactive = false;
|
||
this.cancelButton.can_focus = false;
|
||
}
|
||
|
||
_onReset() {
|
||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||
this.reset();
|
||
}
|
||
|
||
setActorInDefaultButtonWell(actor, animate) {
|
||
if (!this._defaultButtonWellActor &&
|
||
!actor)
|
||
return;
|
||
|
||
let oldActor = this._defaultButtonWellActor;
|
||
|
||
if (oldActor)
|
||
oldActor.remove_all_transitions();
|
||
|
||
let wasSpinner;
|
||
if (oldActor == this._spinner)
|
||
wasSpinner = true;
|
||
else
|
||
wasSpinner = false;
|
||
|
||
let isSpinner;
|
||
if (actor == this._spinner)
|
||
isSpinner = true;
|
||
else
|
||
isSpinner = false;
|
||
|
||
if (this._defaultButtonWellActor != actor && oldActor) {
|
||
if (!animate) {
|
||
oldActor.opacity = 0;
|
||
|
||
if (wasSpinner) {
|
||
if (this._spinner)
|
||
this._spinner.stop();
|
||
}
|
||
} else {
|
||
oldActor.ease({
|
||
opacity: 0,
|
||
duration: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||
mode: Clutter.AnimationMode.LINEAR,
|
||
onComplete: () => {
|
||
if (wasSpinner) {
|
||
if (this._spinner)
|
||
this._spinner.stop();
|
||
}
|
||
},
|
||
});
|
||
}
|
||
}
|
||
|
||
if (actor) {
|
||
if (isSpinner)
|
||
this._spinner.play();
|
||
|
||
if (!animate) {
|
||
actor.opacity = 255;
|
||
} else {
|
||
actor.ease({
|
||
opacity: 255,
|
||
duration: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
|
||
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
|
||
mode: Clutter.AnimationMode.LINEAR,
|
||
});
|
||
}
|
||
}
|
||
|
||
this._defaultButtonWellActor = actor;
|
||
}
|
||
|
||
startSpinning() {
|
||
this.setActorInDefaultButtonWell(this._spinner, true);
|
||
}
|
||
|
||
stopSpinning() {
|
||
this.setActorInDefaultButtonWell(null, false);
|
||
}
|
||
|
||
clear() {
|
||
this._entry.text = '';
|
||
this.stopSpinning();
|
||
this._authList.clear();
|
||
this._authList.hide();
|
||
}
|
||
|
||
setQuestion(question) {
|
||
this._entry.hint_text = question;
|
||
|
||
this._authList.hide();
|
||
this._entry.show();
|
||
this._entry.grab_key_focus();
|
||
}
|
||
|
||
_fadeInChoiceList() {
|
||
this._authList.set({
|
||
opacity: 0,
|
||
visible: true,
|
||
reactive: false,
|
||
});
|
||
this._authList.ease({
|
||
opacity: 255,
|
||
duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
|
||
transition: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||
onComplete: () => (this._authList.reactive = true),
|
||
});
|
||
}
|
||
|
||
setChoiceList(promptMessage, choiceList) {
|
||
this._authList.clear();
|
||
this._authList.label.text = promptMessage;
|
||
for (let key in choiceList) {
|
||
let text = choiceList[key];
|
||
this._authList.addItem(key, text);
|
||
}
|
||
|
||
this._entry.hide();
|
||
if (this._message.text === '')
|
||
this._message.hide();
|
||
this._fadeInChoiceList();
|
||
}
|
||
|
||
getAnswer() {
|
||
let text;
|
||
|
||
if (this._preemptiveAnswer) {
|
||
text = this._preemptiveAnswer;
|
||
this._preemptiveAnswer = null;
|
||
} else {
|
||
text = this._entry.get_text();
|
||
}
|
||
|
||
return text;
|
||
}
|
||
|
||
_fadeOutMessage() {
|
||
if (this._message.opacity == 0)
|
||
return;
|
||
this._message.remove_all_transitions();
|
||
this._message.ease({
|
||
opacity: 0,
|
||
duration: MESSAGE_FADE_OUT_ANIMATION_TIME,
|
||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||
});
|
||
}
|
||
|
||
setMessage(message, type, wiggleParameters = {duration: 0}) {
|
||
if (type == GdmUtil.MessageType.ERROR)
|
||
this._message.add_style_class_name('login-dialog-message-warning');
|
||
else
|
||
this._message.remove_style_class_name('login-dialog-message-warning');
|
||
|
||
if (type == GdmUtil.MessageType.HINT)
|
||
this._message.add_style_class_name('login-dialog-message-hint');
|
||
else
|
||
this._message.remove_style_class_name('login-dialog-message-hint');
|
||
|
||
this._message.show();
|
||
if (message) {
|
||
this._message.remove_all_transitions();
|
||
this._message.text = message;
|
||
this._message.opacity = 255;
|
||
} else {
|
||
this._message.opacity = 0;
|
||
}
|
||
|
||
wiggle(this._message, wiggleParameters);
|
||
}
|
||
|
||
updateSensitivity(sensitive) {
|
||
if (this._entry.reactive === sensitive)
|
||
return;
|
||
|
||
this._entry.reactive = sensitive;
|
||
|
||
if (sensitive) {
|
||
this._entry.grab_key_focus();
|
||
} else {
|
||
this.grab_key_focus();
|
||
|
||
if (this._entry === this._passwordEntry)
|
||
this._entry.password_visible = false;
|
||
}
|
||
}
|
||
|
||
vfunc_hide() {
|
||
this.setActorInDefaultButtonWell(null, true);
|
||
super.vfunc_hide();
|
||
this._message.opacity = 0;
|
||
|
||
this.setUser(null);
|
||
|
||
this.updateSensitivity(true);
|
||
this._entry.set_text('');
|
||
}
|
||
|
||
setUser(user) {
|
||
let oldChild = this._userWell.get_child();
|
||
if (oldChild)
|
||
oldChild.destroy();
|
||
|
||
let userWidget = new UserWidget.UserWidget(user, Clutter.Orientation.VERTICAL);
|
||
this._userWell.set_child(userWidget);
|
||
|
||
if (!user)
|
||
this._updateEntry(false);
|
||
}
|
||
|
||
reset() {
|
||
let oldStatus = this.verificationStatus;
|
||
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
|
||
this.cancelButton.reactive = this._hasCancelButton;
|
||
this.cancelButton.can_focus = this._hasCancelButton;
|
||
this._preemptiveAnswer = null;
|
||
|
||
if (this._userVerifier)
|
||
this._userVerifier.cancel();
|
||
|
||
this._queryingService = null;
|
||
this.clear();
|
||
this._message.opacity = 0;
|
||
this.setUser(null);
|
||
this._updateEntry(true);
|
||
this.stopSpinning();
|
||
|
||
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
|
||
this.emit('failed');
|
||
else if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
|
||
this.emit('cancelled');
|
||
|
||
let beginRequestType;
|
||
|
||
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
|
||
// The user is constant at the unlock screen, so it will immediately
|
||
// respond to the request with the username
|
||
if (oldStatus === AuthPromptStatus.VERIFICATION_CANCELLED)
|
||
return;
|
||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||
} else if (this._userVerifier.foregroundServiceDeterminesUsername()) {
|
||
// We don't need to know the username if the user preempted the login screen
|
||
// with a smartcard or with preauthenticated oVirt credentials
|
||
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
|
||
} else if (oldStatus === AuthPromptStatus.VERIFICATION_IN_PROGRESS) {
|
||
// We're going back to retry with current user
|
||
beginRequestType = BeginRequestType.REUSE_USERNAME;
|
||
} else {
|
||
// In all other cases, we should get the username up front.
|
||
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
|
||
}
|
||
|
||
this.emit('reset', beginRequestType);
|
||
}
|
||
|
||
addCharacter(unichar) {
|
||
if (!this._entry.visible)
|
||
return;
|
||
|
||
this._entry.grab_key_focus();
|
||
this._entry.clutter_text.insert_unichar(unichar);
|
||
}
|
||
|
||
begin(params) {
|
||
params = Params.parse(params, {
|
||
userName: null,
|
||
hold: null,
|
||
});
|
||
|
||
this.updateSensitivity(false);
|
||
|
||
let hold = params.hold;
|
||
if (!hold)
|
||
hold = new Batch.Hold();
|
||
|
||
this._userVerifier.begin(params.userName, hold);
|
||
this.verificationStatus = AuthPromptStatus.VERIFYING;
|
||
}
|
||
|
||
finish(onComplete) {
|
||
if (!this._userVerifier.hasPendingMessages) {
|
||
this._userVerifier.clear();
|
||
onComplete();
|
||
return;
|
||
}
|
||
|
||
let signalId = this._userVerifier.connect('no-more-messages', () => {
|
||
this._userVerifier.disconnect(signalId);
|
||
this._userVerifier.clear();
|
||
onComplete();
|
||
});
|
||
}
|
||
|
||
cancel() {
|
||
if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED)
|
||
return;
|
||
|
||
if (this.verificationStatus === AuthPromptStatus.VERIFICATION_IN_PROGRESS) {
|
||
this._cancelledRetries++;
|
||
if (this._cancelledRetries > this._userVerifier.allowedFailures)
|
||
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
|
||
} else {
|
||
this.verificationStatus = AuthPromptStatus.VERIFICATION_CANCELLED;
|
||
}
|
||
|
||
this.reset();
|
||
}
|
||
});
|