[gnome-shell] ibusManager, inputMethod: Cancel async ibus calls chain on disconnect



commit 1cc766d6369361a3c8b80acfb6236c7ef8e0bdaf
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Wed Sep 25 20:56:46 2019 +0200

    ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
    
    The shell tries to spawn the ibus daemon on startup if unavailable, however
    as per commit 8adfc5b1 we also force restarting it once the X11 server is
    available.
    Unfortunately this could cause a race if we disconnect while we were already
    connected to an ibus daemon, but still in the process of going through the
    various nested calls.
    In fact the ::disconnect callback didn't stop any further async ibus call
    that, even if failing, would have eventually triggered the emission of a
    'ready' signal and to the Keyboard's callback, leading under X11 to a full
    grab owned by ibus daemon.
    
    In order to avoid this and keep control of the calls order, use in both
    IbusManager and InputMethod a cancellable that is setup before connecting to
    the bus, and that is cancelled on disconnection.
    Then handle the finish() calls properly, using try/catch to validate the
    returned value, taking in account the potential error and just not
    proceeding in case of cancellation.
    
    Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712

 js/misc/ibusManager.js | 52 ++++++++++++++++++++++++++++++++++++++------------
 js/misc/inputMethod.js | 47 ++++++++++++++++++++++++++++++---------------
 2 files changed, 72 insertions(+), 27 deletions(-)
---
diff --git a/js/misc/ibusManager.js b/js/misc/ibusManager.js
index 2011321eeb..55e2fd97bd 100644
--- a/js/misc/ibusManager.js
+++ b/js/misc/ibusManager.js
@@ -72,6 +72,11 @@ var IBusManager = class {
     }
 
     _clear() {
+        if (this._cancellable) {
+            this._cancellable.cancel();
+            this._cancellable = null;
+        }
+
         if (this._panelService)
             this._panelService.destroy();
 
@@ -86,28 +91,41 @@ var IBusManager = class {
     }
 
     _onConnected() {
-        this._ibus.list_engines_async(-1, null, this._initEngines.bind(this));
+        this._cancellable = new Gio.Cancellable();
+        this._ibus.list_engines_async(-1, this._cancellable,
+            this._initEngines.bind(this));
         this._ibus.request_name_async(IBus.SERVICE_PANEL,
-                                      IBus.BusNameFlag.REPLACE_EXISTING,
-                                      -1, null,
-                                      this._initPanelService.bind(this));
+            IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable,
+            this._initPanelService.bind(this));
     }
 
     _initEngines(ibus, result) {
-        let enginesList = this._ibus.list_engines_async_finish(result);
-        if (enginesList) {
+        try {
+            let enginesList = this._ibus.list_engines_async_finish(result);
             for (let i = 0; i < enginesList.length; ++i) {
                 let name = enginesList[i].get_name();
                 this._engines.set(name, enginesList[i]);
             }
             this._updateReadiness();
-        } else {
+        } catch (e) {
+            if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                return;
+
+            logError(e);
             this._clear();
         }
     }
 
     _initPanelService(ibus, result) {
-        let success = this._ibus.request_name_async_finish(result);
+        let success = false;
+        try {
+            success = !!this._ibus.request_name_async_finish(result);
+        } catch (e) {
+            if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                return;
+            logError(e);
+        }
+
         if (success) {
             this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
                                                          object_path: IBus.PATH_PANEL });
@@ -134,7 +152,7 @@ var IBusManager = class {
             } catch (e) {
             }
             // If an engine is already active we need to get its properties
-            this._ibus.get_global_engine_async(-1, null, (i, result) => {
+            this._ibus.get_global_engine_async(-1, this._cancellable, (_bus, result) => {
                 let engine;
                 try {
                     engine = this._ibus.get_global_engine_async_finish(result);
@@ -206,8 +224,18 @@ var IBusManager = class {
             return;
         }
 
-        this._ibus.set_global_engine_async(id, this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
-                                           null, callback || null);
+        this._ibus.set_global_engine_async(id,
+            this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
+            this._cancellable, (_bus, res) => {
+                try {
+                    this._ibus.set_global_engine_async_finish(res);
+                } catch (e) {
+                    if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                        logError(e);
+                }
+                if (callback)
+                    callback();
+            });
     }
 
     preloadEngines(ids) {
@@ -227,7 +255,7 @@ var IBusManager = class {
                     this._ibus.preload_engines_async(
                         ids,
                         -1,
-                        null,
+                        this._cancellable,
                         null);
                     this._preloadEnginesId = 0;
                     return GLib.SOURCE_REMOVE;
diff --git a/js/misc/inputMethod.js b/js/misc/inputMethod.js
index 1ca70c3df5..42bf7f6321 100644
--- a/js/misc/inputMethod.js
+++ b/js/misc/inputMethod.js
@@ -1,6 +1,6 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported InputMethod */
-const { Clutter, GLib, GObject, IBus } = imports.gi;
+const { Clutter, GLib, Gio, GObject, IBus } = imports.gi;
 
 const Keyboard = imports.ui.status.keyboard;
 
@@ -47,12 +47,22 @@ class InputMethod extends Clutter.InputMethod {
     }
 
     _onConnected() {
-        this._ibus.create_input_context_async ('gnome-shell', -1, null,
-                                               this._setContext.bind(this));
+        this._cancellable = new Gio.Cancellable();
+        this._ibus.create_input_context_async ('gnome-shell', -1,
+            this._cancellable, this._setContext.bind(this));
     }
 
     _setContext(bus, res) {
-        this._context = this._ibus.create_input_context_async_finish(res);
+        try {
+            this._context = this._ibus.create_input_context_async_finish(res);
+        } catch (e) {
+            if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+                logError(e);
+                this._clear();
+            }
+            return;
+        }
+
         this._context.connect('commit-text', this._onCommitText.bind(this));
         this._context.connect('delete-surrounding-text', this._onDeleteSurroundingText.bind(this));
         this._context.connect('update-preedit-text', this._onUpdatePreeditText.bind(this));
@@ -64,6 +74,11 @@ class InputMethod extends Clutter.InputMethod {
     }
 
     _clear() {
+        if (this._cancellable) {
+            this._cancellable.cancel();
+            this._cancellable = null;
+        }
+
         this._context = null;
         this._hints = 0;
         this._purpose = 0;
@@ -244,17 +259,19 @@ class InputMethod extends Clutter.InputMethod {
         if (event.type() == Clutter.EventType.KEY_RELEASE)
             state |= IBus.ModifierType.RELEASE_MASK;
 
-        this._context.process_key_event_async(event.get_key_symbol(),
-                                              event.get_key_code() - 8, // Convert XKB keycodes to evcodes
-                                              state, -1, null,
-                                              (context, res) => {
-                                                  try {
-                                                      let retval = 
context.process_key_event_async_finish(res);
-                                                      this.notify_key_event(event, retval);
-                                                  } catch (e) {
-                                                      log(`Error processing key on IM: ${e.message}`);
-                                                  }
-                                              });
+        this._context.process_key_event_async(
+            event.get_key_symbol(),
+            event.get_key_code() - 8, // Convert XKB keycodes to evcodes
+            state, -1, this._cancellable,
+            (context, res) => {
+                try {
+                    let retval = context.process_key_event_async_finish(res);
+                    this.notify_key_event(event, retval);
+                } catch (e) {
+                    if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+                        log(`Error processing key on IM: ${e.message}`);
+                }
+            });
         return true;
     }
 });


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]