Bug 1434572 - [Wayland] Implement Gtk+ clipboard shortcut for copy->paste between Firefox windows, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Thu, 01 Feb 2018 11:23:01 +0100
changeset 404390 1f5f5fe0abea007f339aa361970fd75ab0b84589
parent 404389 4943df550a2d717e9fcff2374e2814e3ee7c6930
child 404391 022b0205b80ef91d441838a2d2b55d6cafd5465c
push id100000
push userncsoregi@mozilla.com
push dateMon, 19 Feb 2018 16:46:49 +0000
treeherdermozilla-inbound@176fb0d46b64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1434572
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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_ */