e23f4d6c7c
Certain keybindings should continue to work even when a popup
menu is on screen. For instance, the keybinding for showing
the app menu and the keyinding for showing the calendar are
examples.
This is achieved by putting in place a special "POPUP" action
mode, whenever a popup menu is active. This mode replaces
the (e.g., "NORMAL" or "OVERVIEW") action mode that was in place
for as long as the popup menu is active.
But those keybindings should not work when the user is at the
unlock dialog (which uses an action mode of "UNLOCK").
Unfortunately, since commit c79d24b6
they do.
This commit addresses the problem by forcing the action mode
to NONE at the unlock screen when popups are visible.
CVE-2019-3820
Closes https://gitlab.gnome.org/GNOME/gnome-shell/issues/851
180 lines
5.7 KiB
JavaScript
180 lines
5.7 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
const Gtk = imports.gi.Gtk;
|
|
const Lang = imports.lang;
|
|
const St = imports.gi.St;
|
|
|
|
const BoxPointer = imports.ui.boxpointer;
|
|
const Main = imports.ui.main;
|
|
const Params = imports.misc.params;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
const Shell = imports.gi.Shell;
|
|
|
|
var EntryMenu = new Lang.Class({
|
|
Name: 'ShellEntryMenu',
|
|
Extends: PopupMenu.PopupMenu,
|
|
|
|
_init(entry) {
|
|
this.parent(entry, 0, St.Side.TOP);
|
|
|
|
this._entry = entry;
|
|
this._clipboard = St.Clipboard.get_default();
|
|
|
|
// Populate menu
|
|
let item;
|
|
item = new PopupMenu.PopupMenuItem(_("Copy"));
|
|
item.connect('activate', this._onCopyActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._copyItem = item;
|
|
|
|
item = new PopupMenu.PopupMenuItem(_("Paste"));
|
|
item.connect('activate', this._onPasteActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._pasteItem = item;
|
|
|
|
this._passwordItem = null;
|
|
|
|
Main.uiGroup.add_actor(this.actor);
|
|
this.actor.hide();
|
|
},
|
|
|
|
_makePasswordItem() {
|
|
let item = new PopupMenu.PopupMenuItem('');
|
|
item.connect('activate', this._onPasswordActivated.bind(this));
|
|
this.addMenuItem(item);
|
|
this._passwordItem = item;
|
|
},
|
|
|
|
get isPassword() {
|
|
return this._passwordItem != null;
|
|
},
|
|
|
|
set isPassword(v) {
|
|
if (v == this.isPassword)
|
|
return;
|
|
|
|
if (v) {
|
|
this._makePasswordItem();
|
|
this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD;
|
|
} else {
|
|
this._passwordItem.destroy();
|
|
this._passwordItem = null;
|
|
this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL;
|
|
}
|
|
},
|
|
|
|
open(animate) {
|
|
this._updatePasteItem();
|
|
this._updateCopyItem();
|
|
if (this._passwordItem)
|
|
this._updatePasswordItem();
|
|
|
|
this.parent(animate);
|
|
this._entry.add_style_pseudo_class('focus');
|
|
|
|
let direction = Gtk.DirectionType.TAB_FORWARD;
|
|
if (!this.actor.navigate_focus(null, direction, false))
|
|
this.actor.grab_key_focus();
|
|
},
|
|
|
|
_updateCopyItem() {
|
|
let selection = this._entry.clutter_text.get_selection();
|
|
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
|
|
selection && selection != '');
|
|
},
|
|
|
|
_updatePasteItem() {
|
|
this._clipboard.get_text(St.ClipboardType.CLIPBOARD,
|
|
(clipboard, text) => {
|
|
this._pasteItem.setSensitive(text && text != '');
|
|
});
|
|
},
|
|
|
|
_updatePasswordItem() {
|
|
let textHidden = (this._entry.clutter_text.password_char);
|
|
if (textHidden)
|
|
this._passwordItem.label.set_text(_("Show Text"));
|
|
else
|
|
this._passwordItem.label.set_text(_("Hide Text"));
|
|
},
|
|
|
|
_onCopyActivated() {
|
|
let selection = this._entry.clutter_text.get_selection();
|
|
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
|
|
},
|
|
|
|
_onPasteActivated() {
|
|
this._clipboard.get_text(St.ClipboardType.CLIPBOARD,
|
|
(clipboard, text) => {
|
|
if (!text)
|
|
return;
|
|
this._entry.clutter_text.delete_selection();
|
|
let pos = this._entry.clutter_text.get_cursor_position();
|
|
this._entry.clutter_text.insert_text(text, pos);
|
|
});
|
|
},
|
|
|
|
_onPasswordActivated() {
|
|
let visible = !!(this._entry.clutter_text.password_char);
|
|
this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf');
|
|
}
|
|
});
|
|
|
|
function _setMenuAlignment(entry, stageX) {
|
|
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
|
|
if (success)
|
|
entry.menu.setSourceAlignment(entryX / entry.width);
|
|
};
|
|
|
|
function _onButtonPressEvent(actor, event, entry) {
|
|
if (entry.menu.isOpen) {
|
|
entry.menu.close(BoxPointer.PopupAnimation.FULL);
|
|
return Clutter.EVENT_STOP;
|
|
} else if (event.get_button() == 3) {
|
|
let [stageX, stageY] = event.get_coords();
|
|
_setMenuAlignment(entry, stageX);
|
|
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
|
return Clutter.EVENT_STOP;
|
|
}
|
|
return Clutter.EVENT_PROPAGATE;
|
|
};
|
|
|
|
function _onPopup(actor, entry) {
|
|
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
|
if (success)
|
|
entry.menu.setSourceAlignment(textX / entry.width);
|
|
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
|
};
|
|
|
|
function addContextMenu(entry, params) {
|
|
if (entry.menu)
|
|
return;
|
|
|
|
params = Params.parse (params, { isPassword: false, actionMode: Shell.ActionMode.POPUP });
|
|
|
|
entry.menu = new EntryMenu(entry);
|
|
entry.menu.isPassword = params.isPassword;
|
|
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry },
|
|
{ actionMode: params.actionMode });
|
|
entry._menuManager.addMenu(entry.menu);
|
|
|
|
// Add an event handler to both the entry and its clutter_text; the former
|
|
// so padding is included in the clickable area, the latter because the
|
|
// event processing of ClutterText prevents event-bubbling.
|
|
entry.clutter_text.connect('button-press-event', (actor, event) => {
|
|
_onButtonPressEvent(actor, event, entry);
|
|
});
|
|
entry.connect('button-press-event', (actor, event) => {
|
|
_onButtonPressEvent(actor, event, entry);
|
|
});
|
|
|
|
entry.connect('popup-menu', actor => { _onPopup(actor, entry); });
|
|
|
|
entry.connect('destroy', () => {
|
|
entry.menu.destroy();
|
|
entry.menu = null;
|
|
entry._menuManager = null;
|
|
});
|
|
}
|