[vte] widget: Fix crash when destroyed while waiting for clipboard text



commit 7c504396bb0c5dd87f3a348988a589782e7cd742
Author: Christian Persch <chpe gnome org>
Date:   Thu Jan 14 17:05:33 2016 +0100

    widget: Fix crash when destroyed while waiting for clipboard text
    
    When the terminal is destroyed before the result of the clipboard
    request comes in, we would crash. Unforunately, there is no way
    to cancel a pending request, so we need this elaborate setup.

 src/vte.cc         |   24 +++----------
 src/vteinternal.hh |   88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 18 deletions(-)
---
diff --git a/src/vte.cc b/src/vte.cc
index e6f4bcb..83e83a5 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -5467,16 +5467,6 @@ vte_cell_is_selected(VteTerminal *terminal, glong col, glong row, gpointer data)
        return vte_cell_is_between(col, row, ss.col, ss.row, se.col, se.row);
 }
 
-/* Once we get text data, actually paste it in. */
-static void
-vte_terminal_paste_cb(GtkClipboard *clipboard,
-                      char const* text,
-                      gpointer data)
-{
-        VteTerminalPrivate *that = static_cast<VteTerminalPrivate*>(data);
-        that->widget_paste_received(text);
-}
-
 void
 VteTerminalPrivate::widget_paste_received(char const* text)
 {
@@ -6469,14 +6459,12 @@ VteTerminalPrivate::widget_paste(GdkAtom board)
                 return;
 
        auto clip = gtk_clipboard_get_for_display(gtk_widget_get_display(m_widget), board);
-       if (clip != nullptr) {
-               _vte_debug_print(VTE_DEBUG_SELECTION,
-                               "Requesting clipboard contents.\n");
-                // FIXME FIXMEchpe!! we need to invalidate this request when the widget is destroyed before 
receiving the text!
-               gtk_clipboard_request_text(clip,
-                                          vte_terminal_paste_cb,
-                                          this);
-       }
+       if (!clip)
+                return;
+
+        _vte_debug_print(VTE_DEBUG_SELECTION, "Requesting clipboard contents.\n");
+
+        m_paste_request.request_text(clip, &VteTerminalPrivate::widget_paste_received, this);
 }
 
 void
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 204bed9..b792f91 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -184,6 +184,92 @@ struct vte_scrolling_region {
         int start, end;
 };
 
+template <class T>
+class ClipboardTextRequestGtk {
+public:
+        typedef void (T::* Callback)(char const*);
+
+        ClipboardTextRequestGtk() : m_request(nullptr) { }
+        ~ClipboardTextRequestGtk() { cancel(); }
+
+        void request_text(GtkClipboard *clipboard,
+                          Callback callback,
+                          T* that)
+        {
+                cancel();
+                new Request(clipboard, callback, that, &m_request);
+        }
+
+private:
+
+        class Request {
+        public:
+                Request(GtkClipboard *clipboard,
+                        Callback callback,
+                        T* that,
+                        Request** location) :
+                        m_callback(callback),
+                        m_that(that),
+                        m_location(location)
+                {
+                        /* We need to store this here instead of doing it after the |new| above,
+                         * since gtk_clipboard_request_text may dispatch the callback
+                         * immediately or only later, with no way to know this beforehand.
+                         */
+                        *m_location = this;
+                        gtk_clipboard_request_text(clipboard, text_received, this);
+                }
+
+                ~Request()
+                {
+                        invalidate();
+                }
+
+                void cancel()
+                {
+                        invalidate();
+                        m_that = nullptr;
+                        m_location = nullptr;
+                }
+
+        private:
+                Callback m_callback;
+                T *m_that;
+                Request** m_location;
+
+                void invalidate()
+                {
+                        if (m_that && m_location)
+                                *m_location = nullptr;
+                }
+
+                void dispatch(char const *text)
+                {
+                        if (m_that) {
+                                g_assert(m_location == nullptr || *m_location == this);
+
+                                (m_that->*m_callback)(text);
+                        }
+                }
+
+                static void text_received(GtkClipboard *clipboard, char const* text, gpointer data) {
+                        Request* request = reinterpret_cast<Request*>(data);
+                        request->dispatch(text);
+                        delete request;
+                }
+        };
+
+private:
+        void cancel()
+        {
+                if (m_request)
+                        m_request->cancel();
+                g_assert(m_request == nullptr);
+        }
+
+        Request *m_request;
+};
+
 /* Terminal private data. */
 class VteTerminalPrivate {
 public:
@@ -288,6 +374,8 @@ public:
 #endif
        GtkClipboard *clipboard[LAST_VTE_SELECTION];
 
+        ClipboardTextRequestGtk<VteTerminalPrivate> m_paste_request;
+
        /* Miscellaneous options. */
        VteEraseBinding backspace_binding, delete_binding;
        gboolean meta_sends_escape;


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