Bug 1447925 - Add GetClipboardText() to get text data from clipboard, r=jhorak a=jcristau
authorMartin Stransky <stransky@redhat.com>
Tue, 27 Mar 2018 15:51:07 +0200
changeset 460806 f555ad8e039e173dd5b4e399d5d312d351c26cc6
parent 460805 6631c7ed99f233e27169fa29e5974620d1d8fdf6
child 460807 418c75e8af6ce069471d7a31732249ecbc6e8e58
push id9063
push userbtara@mozilla.com
push dateMon, 16 Apr 2018 15:24:52 +0000
treeherdermozilla-beta@7a38d42b6b69 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhorak, jcristau
bugs1447925
milestone60.0
Bug 1447925 - Add GetClipboardText() to get text data from clipboard, r=jhorak a=jcristau 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_ */