Bug 1434572 - [Wayland] Implement Gtk+ clipboard shortcut for copy->paste between Firefox windows, r?jhorak draft
authorMartin Stransky <stransky@redhat.com>
Thu, 01 Feb 2018 11:23:01 +0100
changeset 754463 8c0f752e1913ec2d420edbf07457a5fee73ef280
parent 754399 38b3c1d03a594664c6b32c35533734283c258f43
push id98884
push userstransky@redhat.com
push dateTue, 13 Feb 2018 16:30:58 +0000
reviewersjhorak
bugs1434572
milestone60.0a1
Bug 1434572 - [Wayland] Implement Gtk+ clipboard shortcut for copy->paste between Firefox windows, r?jhorak When we perform copy -> paste in one Firefox process on Wayland we're locked because Wayland clipboard paste operation just reads data from filedescriptor and does not run main event loop. A solution is to use Gtk+ shortcut here, when clipboard selection owner is the same as data receiver. Gtk+ then does not go through X11/Wayland but calls clipboard data getter callback directly, which we can use on Wayland because it also does not main event loop and the operation stays synchronous. MozReview-Commit-ID: G8myCBUSzxb
widget/gtk/mozgtk/mozgtk.c
widget/gtk/nsClipboardWayland.cpp
widget/gtk/nsClipboardWayland.h
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -67,16 +67,17 @@ STUB(gdk_screen_get_number)
 STUB(gdk_screen_get_resolution)
 STUB(gdk_screen_get_rgba_visual)
 STUB(gdk_screen_get_root_window)
 STUB(gdk_screen_get_system_visual)
 STUB(gdk_screen_get_width)
 STUB(gdk_screen_height)
 STUB(gdk_screen_is_composited)
 STUB(gdk_screen_width)
+STUB(gdk_selection_owner_get)
 STUB(gdk_set_program_class)
 STUB(gdk_unicode_to_keyval)
 STUB(gdk_visual_get_depth)
 STUB(gdk_visual_get_system)
 STUB(gdk_window_add_filter)
 STUB(gdk_window_begin_move_drag)
 STUB(gdk_window_begin_resize_drag)
 STUB(gdk_window_destroy)
--- a/widget/gtk/nsClipboardWayland.cpp
+++ b/widget/gtk/nsClipboardWayland.cpp
@@ -276,16 +276,19 @@ static const struct wl_registry_listener
 };
 
 nsRetrievalContextWayland::nsRetrievalContextWayland(void)
   : mInitialized(false)
   , mSeat(nullptr)
   , mDataDeviceManager(nullptr)
   , mDataOffer(nullptr)
   , mKeyboard(nullptr)
+  , mClipboardRequestNumber(0)
+  , mClipboardData(nullptr)
+  , mClipboardDataLength(0)
 {
     const gchar* charset;
     g_get_charset(&charset);
     mTextPlainLocale = g_strdup_printf("text/plain;charset=%s", charset);
 
     // Available as of GTK 3.8+
     static auto sGdkWaylandDisplayGetWlDisplay =
         (wl_display *(*)(GdkDisplay *))
@@ -337,69 +340,134 @@ nsRetrievalContextWayland::GetTargets(in
     for (int32_t j = 0; j < length; j++) {
         targetList[j] = mTargetMIMETypes[j];
     }
 
     *aTargetNum = length;
     return targetList;
 }
 
+struct FastTrackClipboard
+{
+    FastTrackClipboard(int aClipboardRequestNumber,
+                       nsRetrievalContextWayland* aRetrievalContex)
+    : mClipboardRequestNumber(aClipboardRequestNumber)
+    , mRetrievalContex(aRetrievalContex)
+    {}
+
+    int                        mClipboardRequestNumber;
+    nsRetrievalContextWayland* mRetrievalContex;
+};
+
+static void
+wayland_clipboard_contents_received(GtkClipboard     *clipboard,
+                                    GtkSelectionData *selection_data,
+                                    gpointer          data)
+{
+    FastTrackClipboard* fastTrack = static_cast<FastTrackClipboard*>(data);
+    fastTrack->mRetrievalContex->TransferFastTrackClipboard(
+        fastTrack->mClipboardRequestNumber, selection_data);
+    delete fastTrack;
+}
+
+void
+nsRetrievalContextWayland::TransferFastTrackClipboard(
+    int aClipboardRequestNumber, GtkSelectionData *aSelectionData)
+{
+    if (mClipboardRequestNumber == aClipboardRequestNumber) {
+        mClipboardDataLength = gtk_selection_data_get_length(aSelectionData);
+        if (mClipboardDataLength > 0) {
+            mClipboardData = reinterpret_cast<char*>(
+                g_malloc(sizeof(char)*mClipboardDataLength));
+            memcpy(mClipboardData, gtk_selection_data_get_data(aSelectionData),
+                   sizeof(char)*mClipboardDataLength);
+        }
+    } else {
+        NS_WARNING("Received obsoleted clipboard data!");
+    }
+}
+
 const char*
 nsRetrievalContextWayland::GetClipboardData(const char* aMimeType,
                                             int32_t aWhichClipboard,
                                             uint32_t* aContentLength)
 {
-    NS_ASSERTION(mDataOffer, "Requested data without valid data offer!");
+    /* If actual clipboard data is owned by us we don't need to go
+     * through Wayland but we ask Gtk+ to directly call data
+     * getter callback nsClipboard::SelectionGetEvent().
+     * see gtk_selection_convert() at gtk+/gtkselection.c.
+     */
+    GdkAtom selection = GetSelectionAtom(aWhichClipboard);
+    if (gdk_selection_owner_get(selection)) {
+        mClipboardRequestNumber++;
+        gtk_clipboard_request_contents(gtk_clipboard_get(selection),
+            gdk_atom_intern(aMimeType, FALSE),
+            wayland_clipboard_contents_received,
+            new FastTrackClipboard(mClipboardRequestNumber, this));
+    } else {
+        /* TODO: We need to implement GDK_SELECTION_PRIMARY (X11 text selection)
+         * for Wayland backend.
+         */
+        if (selection == GDK_SELECTION_PRIMARY)
+             return nullptr;
+
+        NS_ASSERTION(mDataOffer, "Requested data without valid data offer!");
+
+        if (!mDataOffer) {
+            // TODO
+            // Something went wrong. We're requested to provide clipboard data
+            // but we haven't got any from wayland. Looks like rhbz#1455915.
+            return nullptr;
+        }
+
+        int pipe_fd[2];
+        if (pipe(pipe_fd) == -1)
+            return nullptr;
 
-    if (!mDataOffer) {
-        // TODO
-        // Something went wrong. We're requested to provide clipboard data
-        // but we haven't got any from wayland. Looks like rhbz#1455915.
-        return nullptr;
+        wl_data_offer_receive(mDataOffer, aMimeType, pipe_fd[1]);
+        close(pipe_fd[1]);
+        wl_display_flush(mDisplay);
+
+        struct pollfd fds;
+        fds.fd = pipe_fd[0];
+        fds.events = POLLIN;
+
+        // Choose some reasonable timeout here
+        int ret = poll(&fds, 1, kClipboardTimeout / 1000);
+        if (!ret || ret == -1) {
+            close(pipe_fd[0]);
+            return nullptr;
+        }
+
+        GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
+        GError* error = nullptr;
+
+        g_io_channel_set_encoding(channel, nullptr, &error);
+        if (!error) {
+            gsize length = 0;
+            g_io_channel_read_to_end(channel, &mClipboardData, &length, &error);
+            mClipboardDataLength = length;
+        }
+
+        if (error) {
+            NS_WARNING(
+                nsPrintfCString("Unexpected error when reading clipboard data: %s",
+                                error->message).get());
+            g_error_free(error);
+        }
+
+        g_io_channel_unref(channel);
+        close(pipe_fd[0]);
     }
 
-    int pipe_fd[2];
-    if (pipe(pipe_fd) == -1)
-        return nullptr;
-
-    wl_data_offer_receive(mDataOffer, aMimeType, pipe_fd[1]);
-    close(pipe_fd[1]);
-    wl_display_flush(mDisplay);
-
-    struct pollfd fds;
-    fds.fd = pipe_fd[0];
-    fds.events = POLLIN;
-
-    // Choose some reasonable timeout here
-    int ret = poll(&fds, 1, kClipboardTimeout / 1000);
-    if (!ret || ret == -1) {
-        close(pipe_fd[0]);
-        return nullptr;
-    }
-
-    GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]);
-    GError* error = nullptr;
-    gchar *clipboardData = nullptr;
-    gsize  dataLength = 0;
-
-    g_io_channel_set_encoding(channel, nullptr, &error);
-    if (!error) {
-        g_io_channel_read_to_end(channel, &clipboardData, &dataLength, &error);
-    }
-
-    if (error) {
-        NS_WARNING(
-            nsPrintfCString("Unexpected error when reading clipboard data: %s",
-                            error->message).get());
-        g_error_free(error);
-    }
-
-    g_io_channel_unref(channel);
-    close(pipe_fd[0]);
-
-    *aContentLength = dataLength;
-    return reinterpret_cast<const char*>(clipboardData);
+    *aContentLength = mClipboardDataLength;
+    return reinterpret_cast<const char*>(mClipboardData);
 }
 
 void nsRetrievalContextWayland::ReleaseClipboardData(const char* aClipboardData)
 {
+    NS_ASSERTION(aClipboardData == mClipboardData,
+        "Releasing unknown clipboard data!");
     g_free((void*)aClipboardData);
+
+    mClipboardData = nullptr;
+    mClipboardDataLength = 0;
 }
--- a/widget/gtk/nsClipboardWayland.h
+++ b/widget/gtk/nsClipboardWayland.h
@@ -8,16 +8,18 @@
 #ifndef __nsClipboardWayland_h_
 #define __nsClipboardWayland_h_
 
 #include "nsIClipboard.h"
 #include <gtk/gtk.h>
 #include <gdk/gdkwayland.h>
 #include <nsTArray.h>
 
+struct FastTrackClipboard;
+
 class nsRetrievalContextWayland : public nsRetrievalContext
 {
 public:
     nsRetrievalContextWayland();
 
     virtual const char* GetClipboardData(const char* aMimeType,
                                          int32_t aWhichClipboard,
                                          uint32_t* aContentLength) override;
@@ -25,25 +27,31 @@ public:
 
     virtual GdkAtom* GetTargets(int32_t aWhichClipboard,
                                 int* aTargetNum) override;
 
     void SetDataOffer(wl_data_offer *aDataOffer);
     void AddMIMEType(const char *aMimeType);
     void ResetMIMETypeList(void);
     void ConfigureKeyboard(wl_seat_capability caps);
+    void TransferFastTrackClipboard(int aClipboardRequestNumber,
+                                    GtkSelectionData *aSelectionData);
 
     void InitDataDeviceManager(wl_registry *registry, uint32_t id, uint32_t version);
     void InitSeat(wl_registry *registry, uint32_t id, uint32_t version, void *data);
     virtual ~nsRetrievalContextWayland() override;
 
 private:
     bool                        mInitialized;
     wl_display                 *mDisplay;
     wl_seat                    *mSeat;
     wl_data_device_manager     *mDataDeviceManager;
     wl_data_offer              *mDataOffer;
     wl_keyboard                *mKeyboard;
     nsTArray<GdkAtom>           mTargetMIMETypes;
     gchar                      *mTextPlainLocale;
+
+    int                         mClipboardRequestNumber;
+    char*                       mClipboardData;
+    uint32_t                    mClipboardDataLength;
 };
 
 #endif /* __nsClipboardWayland_h_ */