Blame SOURCES/0001-Don-t-crash-on-accesses-to-stale-window-backed-apps.patch

ab48da
From 82757fd7bd099d849c11847be75f9f4cf43544a7 Mon Sep 17 00:00:00 2001
ab48da
From: "Owen W. Taylor" <otaylor@fishsoup.net>
ab48da
Date: Wed, 7 Oct 2015 22:56:11 -0400
ab48da
Subject: [PATCH] Don't crash on accesses to stale window-backed apps
ab48da
ab48da
The JS code could still be holding on to a reference to a window-backed app
ab48da
after all windows have vanished. (For example, the dash queues an idle to
ab48da
refetch apps and display them.) Avoid dying with an error message if we
ab48da
attempt to activate or otherwise manipulate such a window.
ab48da
ab48da
https://bugzilla.gnome.org/show_bug.cgi?id=674799
ab48da
---
ab48da
 src/shell-app.c | 32 +++++++++++++++++++++++---------
ab48da
 1 file changed, 23 insertions(+), 9 deletions(-)
ab48da
ab48da
diff --git a/src/shell-app.c b/src/shell-app.c
ab48da
index 051f84f..36aa47a 100644
ab48da
--- a/src/shell-app.c
ab48da
+++ b/src/shell-app.c
ab48da
@@ -146,93 +146,99 @@ static void
ab48da
 shell_app_set_property (GObject      *gobject,
ab48da
                         guint         prop_id,
ab48da
                         const GValue *value,
ab48da
                         GParamSpec   *pspec)
ab48da
 {
ab48da
   ShellApp *app = SHELL_APP (gobject);
ab48da
 
ab48da
   switch (prop_id)
ab48da
     {
ab48da
     case PROP_APP_INFO:
ab48da
       _shell_app_set_app_info (app, g_value_get_object (value));
ab48da
       break;
ab48da
     default:
ab48da
       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
ab48da
       break;
ab48da
     }
ab48da
 }
ab48da
 
ab48da
 const char *
ab48da
 shell_app_get_id (ShellApp *app)
ab48da
 {
ab48da
   if (app->info)
ab48da
     return g_app_info_get_id (G_APP_INFO (app->info));
ab48da
   return app->window_id_string;
ab48da
 }
ab48da
 
ab48da
 static MetaWindow *
ab48da
 window_backed_app_get_window (ShellApp     *app)
ab48da
 {
ab48da
   g_assert (app->info == NULL);
ab48da
-  g_assert (app->running_state);
ab48da
-  g_assert (app->running_state->windows);
ab48da
-  return app->running_state->windows->data;
ab48da
+  if (app->running_state)
ab48da
+    {
ab48da
+      g_assert (app->running_state->windows);
ab48da
+      return app->running_state->windows->data;
ab48da
+    }
ab48da
+  else
ab48da
+    return NULL;
ab48da
 }
ab48da
 
ab48da
 static ClutterActor *
ab48da
 window_backed_app_get_icon (ShellApp *app,
ab48da
                             int       size)
ab48da
 {
ab48da
-  MetaWindow *window;
ab48da
+  MetaWindow *window = NULL;
ab48da
   ClutterActor *actor;
ab48da
   gint scale;
ab48da
   ShellGlobal *global;
ab48da
   StThemeContext *context;
ab48da
 
ab48da
   global = shell_global_get ();
ab48da
   context = st_theme_context_get_for_stage (shell_global_get_stage (global));
ab48da
   g_object_get (context, "scale-factor", &scale, NULL);
ab48da
 
ab48da
   size *= scale;
ab48da
 
ab48da
   /* During a state transition from running to not-running for
ab48da
    * window-backend apps, it's possible we get a request for the icon.
ab48da
    * Avoid asserting here and just return an empty image.
ab48da
    */
ab48da
-  if (app->running_state == NULL)
ab48da
+  if (app->running_state != NULL)
ab48da
+    window = window_backed_app_get_window (app);
ab48da
+
ab48da
+  if (window == NULL)
ab48da
     {
ab48da
       actor = clutter_texture_new ();
ab48da
       g_object_set (actor, "opacity", 0, "width", (float) size, "height", (float) size, NULL);
ab48da
       return actor;
ab48da
     }
ab48da
 
ab48da
-  window = window_backed_app_get_window (app);
ab48da
   actor = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
ab48da
                                                                G_OBJECT (window),
ab48da
                                                                "icon");
ab48da
   g_object_set (actor, "width", (float) size, "height", (float) size, NULL);
ab48da
   return actor;
ab48da
 }
ab48da
 
ab48da
 /**
ab48da
  * shell_app_create_icon_texture:
ab48da
  *
ab48da
  * Look up the icon for this application, and create a #ClutterTexture
ab48da
  * for it at the given size.
ab48da
  *
ab48da
  * Return value: (transfer none): A floating #ClutterActor
ab48da
  */
ab48da
 ClutterActor *
ab48da
 shell_app_create_icon_texture (ShellApp   *app,
ab48da
                                int         size)
ab48da
 {
ab48da
   GIcon *icon;
ab48da
   gint scale;
ab48da
   ClutterActor *ret;
ab48da
   ShellGlobal *global;
ab48da
   StThemeContext *context;
ab48da
 
ab48da
   global = shell_global_get ();
ab48da
   context = st_theme_context_get_for_stage (shell_global_get_stage (global));
ab48da
   g_object_get (context, "scale-factor", &scale, NULL);
ab48da
   ret = NULL;
ab48da
 
ab48da
@@ -415,63 +421,65 @@ shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection directio
ab48da
   texture = st_texture_cache_load (st_texture_cache_get_default (),
ab48da
                                    cache_key,
ab48da
                                    ST_TEXTURE_CACHE_POLICY_FOREVER,
ab48da
                                    shell_app_create_faded_icon_cpu,
ab48da
                                    &data,
ab48da
                                    NULL);
ab48da
   g_free (cache_key);
ab48da
 
ab48da
   if (texture != COGL_INVALID_HANDLE)
ab48da
     {
ab48da
       result = clutter_texture_new ();
ab48da
       clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (result), texture);
ab48da
     }
ab48da
   else
ab48da
     {
ab48da
       result = clutter_texture_new ();
ab48da
       g_object_set (result, "opacity", 0, "width", (float) size * scale, "height", (float) size * scale, NULL);
ab48da
 
ab48da
     }
ab48da
   return result;
ab48da
 }
ab48da
 
ab48da
 const char *
ab48da
 shell_app_get_name (ShellApp *app)
ab48da
 {
ab48da
   if (app->info)
ab48da
     return g_app_info_get_name (G_APP_INFO (app->info));
ab48da
   else
ab48da
     {
ab48da
       MetaWindow *window = window_backed_app_get_window (app);
ab48da
-      const char *name;
ab48da
+      const char *name = NULL;
ab48da
+
ab48da
+      if (window)
ab48da
+        name = meta_window_get_title (window);
ab48da
 
ab48da
-      name = meta_window_get_title (window);
ab48da
       if (!name)
ab48da
         name = C_("program", "Unknown");
ab48da
       return name;
ab48da
     }
ab48da
 }
ab48da
 
ab48da
 const char *
ab48da
 shell_app_get_description (ShellApp *app)
ab48da
 {
ab48da
   if (app->info)
ab48da
     return g_app_info_get_description (G_APP_INFO (app->info));
ab48da
   else
ab48da
     return NULL;
ab48da
 }
ab48da
 
ab48da
 /**
ab48da
  * shell_app_is_window_backed:
ab48da
  *
ab48da
  * A window backed application is one which represents just an open
ab48da
  * window, i.e. there's no .desktop file assocation, so we don't know
ab48da
  * how to launch it again.
ab48da
  */
ab48da
 gboolean
ab48da
 shell_app_is_window_backed (ShellApp *app)
ab48da
 {
ab48da
   return app->info == NULL;
ab48da
 }
ab48da
 
ab48da
 typedef struct {
ab48da
   MetaWorkspace *workspace;
ab48da
@@ -1325,61 +1333,67 @@ app_child_setup (gpointer user_data)
ab48da
       do
ab48da
         res = dup2 (journalfd, 1);
ab48da
       while (G_UNLIKELY (res == -1 && errno == EINTR));
ab48da
       do
ab48da
         res = dup2 (journalfd, 2);
ab48da
       while (G_UNLIKELY (res == -1 && errno == EINTR));
ab48da
       (void) close (journalfd);
ab48da
     }
ab48da
 }
ab48da
 #endif
ab48da
 
ab48da
 /**
ab48da
  * shell_app_launch:
ab48da
  * @timestamp: Event timestamp, or 0 for current event timestamp
ab48da
  * @workspace: Start on this workspace, or -1 for default
ab48da
  * @error: A #GError
ab48da
  */
ab48da
 gboolean
ab48da
 shell_app_launch (ShellApp     *app,
ab48da
                   guint         timestamp,
ab48da
                   int           workspace,
ab48da
                   GError      **error)
ab48da
 {
ab48da
   ShellGlobal *global;
ab48da
   GAppLaunchContext *context;
ab48da
   gboolean ret;
ab48da
 
ab48da
   if (app->info == NULL)
ab48da
     {
ab48da
       MetaWindow *window = window_backed_app_get_window (app);
ab48da
-      meta_window_activate (window, timestamp);
ab48da
+      /* We don't use an error return if there no longer any windows, because the
ab48da
+       * user attempting to activate a stale window backed app isn't something
ab48da
+       * we would expect the caller to meaningfully handle or display an error
ab48da
+       * message to the user.
ab48da
+       */
ab48da
+      if (window)
ab48da
+        meta_window_activate (window, timestamp);
ab48da
       return TRUE;
ab48da
     }
ab48da
 
ab48da
   global = shell_global_get ();
ab48da
   context = shell_global_create_app_launch_context (global, timestamp, workspace);
ab48da
 
ab48da
   ret = g_desktop_app_info_launch_uris_as_manager (app->info, NULL,
ab48da
                                                    context,
ab48da
                                                    G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
ab48da
 #ifdef HAVE_SYSTEMD
ab48da
                                                    app_child_setup, (gpointer)shell_app_get_id (app),
ab48da
 #else
ab48da
                                                    NULL, NULL,
ab48da
 #endif
ab48da
                                                    _gather_pid_callback, app,
ab48da
                                                    error);
ab48da
   g_object_unref (context);
ab48da
 
ab48da
   return ret;
ab48da
 }
ab48da
 
ab48da
 /**
ab48da
  * shell_app_launch_action:
ab48da
  * @app: the #ShellApp
ab48da
  * @action_name: the name of the action to launch (as obtained by
ab48da
  *               g_desktop_app_info_list_actions())
ab48da
  * @timestamp: Event timestamp, or 0 for current event timestamp
ab48da
  * @workspace: Start on this workspace, or -1 for default
ab48da
  */
ab48da
 void
ab48da
-- 
ab48da
2.7.4
ab48da