Blame js/ui/components/keyring.js

Packit d345d1
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
Packit d345d1
Packit d345d1
const { Clutter, Gcr, Gio, GObject, Pango, Shell, St } = imports.gi;
Packit d345d1
Packit d345d1
const Animation = imports.ui.animation;
Packit d345d1
const Dialog = imports.ui.dialog;
Packit d345d1
const ModalDialog = imports.ui.modalDialog;
Packit d345d1
const ShellEntry = imports.ui.shellEntry;
Packit d345d1
const CheckBox = imports.ui.checkBox;
Packit d345d1
Packit d345d1
var WORK_SPINNER_ICON_SIZE = 16;
Packit d345d1
Packit d345d1
var KeyringDialog = class extends ModalDialog.ModalDialog {
Packit d345d1
    constructor() {
Packit d345d1
        super({ styleClass: 'prompt-dialog' });
Packit d345d1
Packit d345d1
        this.prompt = new Shell.KeyringPrompt();
Packit d345d1
        this.prompt.connect('show-password', this._onShowPassword.bind(this));
Packit d345d1
        this.prompt.connect('show-confirm', this._onShowConfirm.bind(this));
Packit d345d1
        this.prompt.connect('prompt-close', this._onHidePrompt.bind(this));
Packit d345d1
Packit d345d1
        let icon = new Gio.ThemedIcon({ name: 'dialog-password-symbolic' });
Packit d345d1
        this._content = new Dialog.MessageDialogContent({ icon });
Packit d345d1
        this.contentLayout.add(this._content);
Packit d345d1
Packit d345d1
        // FIXME: Why does this break now?
Packit d345d1
        /*
Packit d345d1
        this.prompt.bind_property('message', this._content, 'title', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
        this.prompt.bind_property('description', this._content, 'body', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
        */
Packit d345d1
        this.prompt.connect('notify::message', () => {
Packit d345d1
            this._content.title = this.prompt.message;
Packit d345d1
        });
Packit d345d1
        this._content.title = this.prompt.message;
Packit d345d1
Packit d345d1
        this.prompt.connect('notify::description', () => {
Packit d345d1
            this._content.body = this.prompt.description;
Packit d345d1
        });
Packit d345d1
        this._content.body = this.prompt.description;
Packit d345d1
Packit d345d1
        this._workSpinner = null;
Packit d345d1
        this._controlTable = null;
Packit d345d1
Packit d345d1
        this._cancelButton = this.addButton({ label: '',
Packit d345d1
                                              action: this._onCancelButton.bind(this),
Packit d345d1
                                              key: Clutter.Escape });
Packit d345d1
        this._continueButton = this.addButton({ label: '',
Packit d345d1
                                                action: this._onContinueButton.bind(this),
Packit d345d1
                                                default: true });
Packit d345d1
Packit d345d1
        this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
        this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _setWorking(working) {
Packit d345d1
        if (!this._workSpinner)
Packit d345d1
            return;
Packit d345d1
Packit d345d1
        if (working)
Packit d345d1
            this._workSpinner.play();
Packit d345d1
        else
Packit d345d1
            this._workSpinner.stop();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _buildControlTable() {
Packit d345d1
        let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
Packit d345d1
        let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
Packit d345d1
                                    layout_manager: layout });
Packit d345d1
        layout.hookup_style(table);
Packit d345d1
        let rtl = table.get_text_direction() == Clutter.TextDirection.RTL;
Packit d345d1
        let row = 0;
Packit d345d1
Packit d345d1
        if (this.prompt.password_visible) {
Packit d345d1
            let label = new St.Label({ style_class: 'prompt-dialog-password-label',
Packit d345d1
                                       x_align: Clutter.ActorAlign.START,
Packit d345d1
                                       y_align: Clutter.ActorAlign.CENTER });
Packit d345d1
            label.set_text(_("Password:"));
Packit d345d1
            label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
Packit d345d1
            this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
Packit d345d1
                                                 text: '',
Packit d345d1
                                                 can_focus: true,
Packit d345d1
                                                 x_expand: true });
Packit d345d1
            this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
Packit d345d1
            ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
Packit d345d1
            this._passwordEntry.clutter_text.connect('activate', this._onPasswordActivate.bind(this));
Packit d345d1
Packit d345d1
            this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true);
Packit d345d1
Packit d345d1
            if (rtl) {
Packit d345d1
                layout.attach(this._workSpinner.actor, 0, row, 1, 1);
Packit d345d1
                layout.attach(this._passwordEntry, 1, row, 1, 1);
Packit d345d1
                layout.attach(label, 2, row, 1, 1);
Packit d345d1
            } else {
Packit d345d1
                layout.attach(label, 0, row, 1, 1);
Packit d345d1
                layout.attach(this._passwordEntry, 1, row, 1, 1);
Packit d345d1
                layout.attach(this._workSpinner.actor, 2, row, 1, 1);
Packit d345d1
            }
Packit d345d1
            row++;
Packit d345d1
        } else {
Packit d345d1
            this._workSpinner = null;
Packit d345d1
            this._passwordEntry = null;
Packit d345d1
        }
Packit d345d1
Packit d345d1
        if (this.prompt.confirm_visible) {
Packit d345d1
            var label = new St.Label(({ style_class: 'prompt-dialog-password-label',
Packit d345d1
                                        x_align: Clutter.ActorAlign.START,
Packit d345d1
                                        y_align: Clutter.ActorAlign.CENTER }));
Packit d345d1
            label.set_text(_("Type again:"));
Packit d345d1
            this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
Packit d345d1
                                                text: '',
Packit d345d1
                                                can_focus: true,
Packit d345d1
                                                x_expand: true });
Packit d345d1
            this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
Packit d345d1
            ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
Packit d345d1
            this._confirmEntry.clutter_text.connect('activate', this._onConfirmActivate.bind(this));
Packit d345d1
            if (rtl) {
Packit d345d1
                layout.attach(this._confirmEntry, 0, row, 1, 1);
Packit d345d1
                layout.attach(label, 1, row, 1, 1);
Packit d345d1
            } else {
Packit d345d1
                layout.attach(label, 0, row, 1, 1);
Packit d345d1
                layout.attach(this._confirmEntry, 1, row, 1, 1);
Packit d345d1
            }
Packit d345d1
            row++;
Packit d345d1
        } else {
Packit d345d1
            this._confirmEntry = null;
Packit d345d1
        }
Packit d345d1
Packit d345d1
        this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null);
Packit d345d1
        this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null);
Packit d345d1
Packit d345d1
        if (this.prompt.choice_visible) {
Packit d345d1
            let choice = new CheckBox.CheckBox();
Packit d345d1
            this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
            this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
Packit d345d1
            layout.attach(choice.actor, rtl ? 0 : 1, row, 1, 1);
Packit d345d1
            row++;
Packit d345d1
        }
Packit d345d1
Packit d345d1
        let warning = new St.Label({ style_class: 'prompt-dialog-error-label',
Packit d345d1
                                     x_align: Clutter.ActorAlign.START });
Packit d345d1
        warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
Packit d345d1
        warning.clutter_text.line_wrap = true;
Packit d345d1
        layout.attach(warning, rtl ? 0 : 1, row, 1, 1);
Packit d345d1
        this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
        this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
Packit d345d1
Packit d345d1
        if (this._controlTable) {
Packit d345d1
            this._controlTable.destroy_all_children();
Packit d345d1
            this._controlTable.destroy();
Packit d345d1
        }
Packit d345d1
Packit d345d1
        this._controlTable = table;
Packit d345d1
        this._content.messageBox.add(table, { x_fill: true, y_fill: true });
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _updateSensitivity(sensitive) {
Packit d345d1
        if (this._passwordEntry) {
Packit d345d1
            this._passwordEntry.reactive = sensitive;
Packit d345d1
            this._passwordEntry.clutter_text.editable = sensitive;
Packit d345d1
        }
Packit d345d1
Packit d345d1
        if (this._confirmEntry) {
Packit d345d1
            this._confirmEntry.reactive = sensitive;
Packit d345d1
            this._confirmEntry.clutter_text.editable = sensitive;
Packit d345d1
        }
Packit d345d1
Packit d345d1
        this._continueButton.can_focus = sensitive;
Packit d345d1
        this._continueButton.reactive = sensitive;
Packit d345d1
        this._setWorking(!sensitive);
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _ensureOpen() {
Packit d345d1
        // NOTE: ModalDialog.open() is safe to call if the dialog is
Packit d345d1
        // already open - it just returns true without side-effects
Packit d345d1
        if (this.open())
Packit d345d1
          return true;
Packit d345d1
Packit d345d1
        // The above fail if e.g. unable to get input grab
Packit d345d1
        //
Packit d345d1
        // In an ideal world this wouldn't happen (because the
Packit d345d1
        // Shell is in complete control of the session) but that's
Packit d345d1
        // just not how things work right now.
Packit d345d1
Packit d345d1
        log('keyringPrompt: Failed to show modal dialog.' +
Packit d345d1
            ' Dismissing prompt request');
Packit d345d1
        this.prompt.cancel()
Packit d345d1
        return false;
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onShowPassword(prompt) {
Packit d345d1
        this._buildControlTable();
Packit d345d1
        this._ensureOpen();
Packit d345d1
        this._updateSensitivity(true);
Packit d345d1
        this._passwordEntry.grab_key_focus();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onShowConfirm(prompt) {
Packit d345d1
        this._buildControlTable();
Packit d345d1
        this._ensureOpen();
Packit d345d1
        this._updateSensitivity(true);
Packit d345d1
        this._continueButton.grab_key_focus();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onHidePrompt(prompt) {
Packit d345d1
        this.close();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onPasswordActivate() {
Packit d345d1
        if (this.prompt.confirm_visible)
Packit d345d1
            this._confirmEntry.grab_key_focus();
Packit d345d1
        else
Packit d345d1
            this._onContinueButton();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onConfirmActivate() {
Packit d345d1
        this._onContinueButton();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onContinueButton() {
Packit d345d1
        this._updateSensitivity(false);
Packit d345d1
        this.prompt.complete();
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _onCancelButton() {
Packit d345d1
        this.prompt.cancel();
Packit d345d1
    }
Packit d345d1
};
Packit d345d1
Packit d345d1
var KeyringDummyDialog = class {
Packit d345d1
    constructor() {
Packit d345d1
        this.prompt = new Shell.KeyringPrompt();
Packit d345d1
        this.prompt.connect('show-password', this._cancelPrompt.bind(this));
Packit d345d1
        this.prompt.connect('show-confirm', this._cancelPrompt.bind(this));
Packit d345d1
    }
Packit d345d1
Packit d345d1
    _cancelPrompt() {
Packit d345d1
        this.prompt.cancel();
Packit d345d1
    }
Packit d345d1
};
Packit d345d1
Packit d345d1
var KeyringPrompter = class {
Packit d345d1
    constructor() {
Packit d345d1
        this._prompter = new Gcr.SystemPrompter();
Packit d345d1
        this._prompter.connect('new-prompt', () => {
Packit d345d1
            let dialog = this._enabled ? new KeyringDialog()
Packit d345d1
                                       : new KeyringDummyDialog();
Packit d345d1
            this._currentPrompt = dialog.prompt;
Packit d345d1
            return this._currentPrompt;
Packit d345d1
        });
Packit d345d1
        this._dbusId = null;
Packit d345d1
        this._registered = false;
Packit d345d1
        this._enabled = false;
Packit d345d1
        this._currentPrompt = null;
Packit d345d1
    }
Packit d345d1
Packit d345d1
    enable() {
Packit d345d1
        if (!this._registered) {
Packit d345d1
            this._prompter.register(Gio.DBus.session);
Packit d345d1
            this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Packit d345d1
                                                     Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
Packit d345d1
            this._registered = true;
Packit d345d1
        }
Packit d345d1
        this._enabled = true;
Packit d345d1
    }
Packit d345d1
Packit d345d1
    disable() {
Packit d345d1
        this._enabled = false;
Packit d345d1
Packit d345d1
        if (this._prompter.prompting)
Packit d345d1
            this._currentPrompt.cancel();
Packit d345d1
        this._currentPrompt = null;
Packit d345d1
    }
Packit d345d1
};
Packit d345d1
Packit d345d1
var Component = KeyringPrompter;