Bug 1447925 - Add GetClipboardText() to get text data from clipboard, r=jhorak
authorMartin Stransky <stransky@redhat.com>
Tue, 27 Mar 2018 15:51:07 +0200
changeset 411469 537057d5ac496ca79514875c88c55b755019d93b
parent 411468 8a98c1c77c43ab2b8461ac81235ca500b0ac3402
child 411470 4087700190a569204fbf01035694cdb74fe9f27a
push id62060
push userstransky@redhat.com
push dateTue, 03 Apr 2018 12:08:27 +0000
treeherderautoland@4087700190a5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak
bugs1447925
milestone61.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 1447925 - Add GetClipboardText() to get text data from clipboard, r=jhorak GetClipboardText() calls gtk_clipboard_request_text() to request text clipboard data from Gtk+ and leave Gtk+ to do clipboard text format conversions. Also unify data getting code for text/data/targets. MozReview-Commit-ID: 9DGSdOACho1
widget/gtk/nsClipboard.h
widget/gtk/nsClipboardX11.cpp
widget/gtk/nsClipboardX11.h
--- a/widget/gtk/nsClipboard.h
+++ b/widget/gtk/nsClipboard.h
@@ -16,16 +16,17 @@
 // Default Gtk MIME for text
 #define GTK_DEFAULT_MIME_TEXT "UTF8_STRING"
 
 class nsRetrievalContext {
 public:
     virtual const char* GetClipboardData(const char* aMimeType,
                                          int32_t aWhichClipboard,
                                          uint32_t* aContentLength) = 0;
+    virtual const char* GetClipboardText(int32_t aWhichClipboard) = 0;
     virtual void ReleaseClipboardData(const char* aClipboardData) = 0;
 
     virtual GdkAtom* GetTargets(int32_t aWhichClipboard,
                                 int* aTargetNum) = 0;
 
     nsRetrievalContext() {};
     virtual ~nsRetrievalContext() {};
 
--- a/widget/gtk/nsClipboardX11.cpp
+++ b/widget/gtk/nsClipboardX11.cpp
@@ -57,18 +57,20 @@ selection_request_filter(GdkXEvent *gdk_
 
         g_object_unref(window);
     }
     return GDK_FILTER_CONTINUE;
 }
 
 nsRetrievalContextX11::nsRetrievalContextX11()
   : mState(INITIAL)
-  , mData(nullptr)
   , mClipboardRequestNumber(0)
+  , mClipboardData(nullptr)
+  , mClipboardDataLength(0)
+  , mTargetMIMEType(gdk_atom_intern("TARGETS", FALSE))
 {
     // A custom event filter to workaround attempting to dereference a null
     // selection requestor in GTK3 versions before 3.11.3. See bug 1178799.
 #if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
     if (gtk_check_version(3, 11, 3))
         gdk_window_add_filter(nullptr, selection_request_filter, nullptr);
 #endif
 }
@@ -134,23 +136,21 @@ checkEventProc(Display *display, XEvent 
                 return True;
             }
         }
     }
 
     return False;
 }
 
-void *
-nsRetrievalContextX11::Wait()
+bool
+nsRetrievalContextX11::WaitForX11Content()
 {
     if (mState == COMPLETED) { // the request completed synchronously
-        void *data = mData;
-        mData = nullptr;
-        return data;
+        return true;
     }
 
     GdkDisplay *gdkDisplay = gdk_display_get_default();
     if (GDK_IS_X11_DISPLAY(gdkDisplay)) {
         Display *xDisplay = GDK_DISPLAY_XDISPLAY(gdkDisplay);
         checkEventContext context;
         context.cbWidget = nullptr;
         context.selAtom = gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION",
@@ -176,51 +176,96 @@ nsRetrievalContextX11::Wait()
                                  (XPointer) &context)) {
 
                 if (xevent.xany.type == SelectionNotify)
                     DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
                 else
                     DispatchPropertyNotifyEvent(context.cbWidget, &xevent);
 
                 if (mState == COMPLETED) {
-                    void *data = mData;
-                    mData = nullptr;
-                    return data;
+                    return true;
                 }
             }
 
             TimeStamp now = TimeStamp::Now();
             struct timeval tv;
             tv.tv_sec = 0;
             tv.tv_usec = std::max<int32_t>(0,
                 kClipboardTimeout - (now - start).ToMicroseconds());
             select_result = select(cnumber, &select_set, nullptr, nullptr, &tv);
         } while (select_result == 1 ||
                  (select_result == -1 && errno == EINTR));
     }
 #ifdef DEBUG_CLIPBOARD
     printf("exceeded clipboard timeout\n");
 #endif
     mState = TIMED_OUT;
-    return nullptr;
+    return false;
 }
 
 // Call this when data has been retrieved.
-void nsRetrievalContextX11::Complete(GtkSelectionData* aData,
+void nsRetrievalContextX11::Complete(ClipboardDataType aDataType,
+                                     const void* aData,
                                      int aDataRequestNumber)
 {
   if (mClipboardRequestNumber != aDataRequestNumber) {
       NS_WARNING("nsRetrievalContextX11::Complete() got obsoleted clipboard data.");
       return;
   }
 
   if (mState == INITIAL) {
       mState = COMPLETED;
-      mData = gtk_selection_data_get_length(aData) >= 0 ?
-              gtk_selection_data_copy(aData) : nullptr;
+
+      MOZ_ASSERT(mClipboardData == nullptr &&
+                 mClipboardDataLength == 0,
+                 "We're leaking clipboard data!");
+
+      switch (aDataType) {
+      case CLIPBOARD_TEXT:
+          {
+              const char* text = static_cast<const char*>(aData);
+              if (text) {
+                  mClipboardDataLength = sizeof(char) * (strlen(text) + 1);
+                  mClipboardData = moz_xmalloc(mClipboardDataLength);
+                  memcpy(mClipboardData, text, mClipboardDataLength);
+              }
+          }
+          break;
+      case CLIPBOARD_TARGETS:
+          {
+              const GtkSelectionData *selection =
+                  static_cast<const GtkSelectionData *>(aData);
+
+              gint n_targets = 0;
+              GdkAtom *targets = nullptr;
+
+              if (!gtk_selection_data_get_targets(selection, &targets, &n_targets) ||
+                  !n_targets) {
+                  return;
+              }
+
+              mClipboardData = targets;
+              mClipboardDataLength = n_targets;
+          }
+          break;
+      case CLIPBOARD_DATA:
+          {
+              const GtkSelectionData *selection =
+                  static_cast<const GtkSelectionData *>(aData);
+
+              gint dataLength = gtk_selection_data_get_length(selection);
+              if (dataLength > 0) {
+                  mClipboardDataLength = dataLength;
+                  mClipboardData = moz_xmalloc(dataLength);
+                  memcpy(mClipboardData, gtk_selection_data_get_data(selection),
+                         dataLength);
+              }
+          }
+          break;
+      }
   } else {
       // Already timed out
       MOZ_ASSERT(mState == TIMED_OUT);
   }
 }
 
 static void
 clipboard_contents_received(GtkClipboard     *clipboard,
@@ -228,86 +273,110 @@ clipboard_contents_received(GtkClipboard
                             gpointer          data)
 {
     ClipboardRequestHandler *handler =
         static_cast<ClipboardRequestHandler*>(data);
     handler->Complete(selection_data);
     delete handler;
 }
 
-GtkSelectionData*
-nsRetrievalContextX11::WaitForContents(GtkClipboard *clipboard,
-                                       const char *aMimeType)
+static void
+clipboard_text_received(GtkClipboard     *clipboard,
+                        const gchar      *text,
+                        gpointer          data)
+{
+    ClipboardRequestHandler *handler =
+        static_cast<ClipboardRequestHandler*>(data);
+    handler->Complete(text);
+    delete handler;
+}
+
+bool
+nsRetrievalContextX11::WaitForClipboardData(ClipboardDataType aDataType,
+                                            GtkClipboard *clipboard,
+                                            const char *aMimeType)
 {
     mState = INITIAL;
-    NS_ASSERTION(!mData, "Leaking clipboard content!");
+    NS_ASSERTION(!mClipboardData, "Leaking clipboard content!");
 
     // Call ClipboardRequestHandler() with unique clipboard request number.
     // The request number pairs gtk_clipboard_request_contents() data request
     // with clipboard_contents_received() callback where the data
     // is provided by Gtk.
     mClipboardRequestNumber++;
     ClipboardRequestHandler* handler =
-        new ClipboardRequestHandler(this, mClipboardRequestNumber);
+        new ClipboardRequestHandler(this, aDataType, mClipboardRequestNumber);
 
-    gtk_clipboard_request_contents(clipboard,
-                                   gdk_atom_intern(aMimeType, FALSE),
-                                   clipboard_contents_received,
-                                   handler);
-    return static_cast<GtkSelectionData*>(Wait());
+    switch (aDataType) {
+    case CLIPBOARD_DATA:
+        gtk_clipboard_request_contents(clipboard,
+            gdk_atom_intern(aMimeType, FALSE), clipboard_contents_received,
+            handler);
+        break;
+    case CLIPBOARD_TEXT:
+        gtk_clipboard_request_text(clipboard, clipboard_text_received,
+            handler);
+        break;
+    case CLIPBOARD_TARGETS:
+        gtk_clipboard_request_contents(clipboard,
+            mTargetMIMEType, clipboard_contents_received,
+            handler);
+        break;
+    }
+
+    return WaitForX11Content();
 }
 
 GdkAtom*
 nsRetrievalContextX11::GetTargets(int32_t aWhichClipboard, int* aTargetNums)
 {
-  *aTargetNums = 0;
+    GtkClipboard *clipboard =
+        gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
 
-  GtkClipboard *clipboard =
-      gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
-
-  GtkSelectionData *selection_data = WaitForContents(clipboard, "TARGETS");
-  if (!selection_data)
-      return nullptr;
+    if (!WaitForClipboardData(CLIPBOARD_TARGETS, clipboard))
+        return nullptr;
 
-  gint n_targets = 0;
-  GdkAtom *targets = nullptr;
+    *aTargetNums = mClipboardDataLength;
+    GdkAtom* targets = static_cast<GdkAtom*>(mClipboardData);
 
-  if (!gtk_selection_data_get_targets(selection_data, &targets, &n_targets) ||
-      !n_targets) {
-      return nullptr;
-  }
+    // We don't hold the target list internally but we transfer the ownership.
+    mClipboardData = nullptr;
+    mClipboardDataLength = 0;
 
-  gtk_selection_data_free(selection_data);
-
-  *aTargetNums = n_targets;
-  return targets;
+    return targets;
 }
 
 const char*
 nsRetrievalContextX11::GetClipboardData(const char* aMimeType,
                                         int32_t aWhichClipboard,
                                         uint32_t* aContentLength)
 {
     GtkClipboard *clipboard;
     clipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
 
-    GtkSelectionData *selectionData = WaitForContents(clipboard, aMimeType);
-    if (!selectionData)
+    if (!WaitForClipboardData(CLIPBOARD_DATA, clipboard, aMimeType))
         return nullptr;
 
-    char* clipboardData = nullptr;
-    int contentLength = gtk_selection_data_get_length(selectionData);
-    if (contentLength > 0) {
-        clipboardData = reinterpret_cast<char*>(
-            moz_xmalloc(sizeof(char)*contentLength));
-        memcpy(clipboardData, gtk_selection_data_get_data(selectionData),
-            sizeof(char)*contentLength);
-    }
-    gtk_selection_data_free(selectionData);
+    *aContentLength = mClipboardDataLength;
+    return static_cast<const char*>(mClipboardData);
+}
 
-    *aContentLength = contentLength;
-    return (const char*)clipboardData;
+const char*
+nsRetrievalContextX11::GetClipboardText(int32_t aWhichClipboard)
+{
+    GtkClipboard *clipboard;
+    clipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
+
+    if (!WaitForClipboardData(CLIPBOARD_TEXT, clipboard))
+        return nullptr;
+
+    return static_cast<const char*>(mClipboardData);
 }
 
 void nsRetrievalContextX11::ReleaseClipboardData(const char* aClipboardData)
 {
-    free((void *)aClipboardData);
+    NS_ASSERTION(aClipboardData == mClipboardData,
+        "Releasing unknown clipboard data!");
+    free((void*)aClipboardData);
+
+    mClipboardData = nullptr;
+    mClipboardDataLength = 0;
 }
--- a/widget/gtk/nsClipboardX11.h
+++ b/widget/gtk/nsClipboardX11.h
@@ -6,61 +6,78 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __nsClipboardX11_h_
 #define __nsClipboardX11_h_
 
 #include "nsIClipboard.h"
 #include <gtk/gtk.h>
 
+enum ClipboardDataType {
+    CLIPBOARD_DATA,
+    CLIPBOARD_TEXT,
+    CLIPBOARD_TARGETS
+};
+
 class nsRetrievalContextX11 : public nsRetrievalContext
 {
 public:
     enum State { INITIAL, COMPLETED, TIMED_OUT };
 
     virtual const char* GetClipboardData(const char* aMimeType,
                                          int32_t aWhichClipboard,
                                          uint32_t* aContentLength) override;
+    virtual const char* GetClipboardText(int32_t aWhichClipboard) override;
     virtual void ReleaseClipboardData(const char* aClipboardData) override;
 
     virtual GdkAtom* GetTargets(int32_t aWhichClipboard,
                                 int* aTargetNums) override;
 
-    // Call this when data has been retrieved.
-    void Complete(GtkSelectionData* aData, int aDataRequestNumber);
+    // Call this when data or text has been retrieved.
+    void Complete(ClipboardDataType aDataType,
+                  const void* aData,
+                  int aDataRequestNumber);
 
     nsRetrievalContextX11();
     virtual ~nsRetrievalContextX11() override;
 
 private:
-    GtkSelectionData* WaitForContents(GtkClipboard *clipboard,
-                                      const char *aMimeType);
+    bool WaitForClipboardData(ClipboardDataType aDataType,
+                              GtkClipboard *clipboard,
+                              const char *aMimeType = nullptr);
+
     /**
      * Spins X event loop until timing out or being completed. Returns
      * null if we time out, otherwise returns the completed data (passing
      * ownership to caller).
      */
-    void *Wait();
+    bool WaitForX11Content();
 
-    State mState;
-    void* mData;
-    int   mClipboardRequestNumber;
+    State     mState;
+    int       mClipboardRequestNumber;
+    void*     mClipboardData;
+    uint32_t  mClipboardDataLength;
+    GdkAtom   mTargetMIMEType;
 };
 
 class ClipboardRequestHandler
 {
 public:
-    ClipboardRequestHandler(nsRetrievalContextX11 *aContext, int aDataRequestNumber)
+    ClipboardRequestHandler(nsRetrievalContextX11 *aContext,
+                            ClipboardDataType aDataType,
+                            int aDataRequestNumber)
       : mContext(aContext)
       , mDataRequestNumber(aDataRequestNumber)
+      , mDataType(aDataType)
       {}
 
-    void  Complete(GtkSelectionData* aData)
+    void Complete(const void *aData)
     {
-      mContext->Complete(aData, mDataRequestNumber);
+      mContext->Complete(mDataType, aData, mDataRequestNumber);
     }
 
 private:
     nsRetrievalContextX11 *mContext;
     int                    mDataRequestNumber;
+    ClipboardDataType      mDataType;
 };
 
 #endif /* __nsClipboardX11_h_ */