Bug 1725149 [Wayland] Implement async clipboard, r=rmader
authorstransky <stransky@redhat.com>
Thu, 12 Aug 2021 14:07:58 +0000
changeset 588700 ca7dd68c51a25ac829033a88cbd98ab18ff4f429
parent 588699 8b346c50a44c4a98b300bf528e4d3564dd167078
child 588701 f912045db98d9790427541bcfc9f0d0eb07a8f96
push id147961
push userstransky@redhat.com
push dateThu, 12 Aug 2021 14:11:01 +0000
treeherderautoland@ca7dd68c51a2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrmader
bugs1725149
milestone93.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 1725149 [Wayland] Implement async clipboard, r=rmader When widget.wayland.async-clipboard.enabled use async clipboard and D&D handlers provided by Gtk. Differential Revision: https://phabricator.services.mozilla.com/D122353
modules/libpref/init/StaticPrefList.yaml
widget/gtk/moz.build
widget/gtk/nsClipboard.cpp
widget/gtk/nsClipboard.h
widget/gtk/nsClipboardWayland.cpp
widget/gtk/nsClipboardWayland.h
widget/gtk/nsClipboardWaylandAsync.cpp
widget/gtk/nsClipboardWaylandAsync.h
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -11844,16 +11844,22 @@
   mirror: always
 
 # Use DMABuf backend for WebGL.
 - name: widget.dmabuf-webgl.enabled
   type: RelaxedAtomicBool
   value: true
   mirror: always
 
+# Use async clipboard on Wayland
+- name: widget.wayland.async-clipboard.enabled
+  type: bool
+  value: false
+  mirror: once
+
 # Force fractional scaling using wp_viewporter. Valid values: 0.5 - 8
 - name: widget.wayland.fractional_buffer_scale
   type: float
   value: 0.0f
   mirror: once
 
 - name: widget.wayland.multi-buffer-software-backend.enabled
   type: bool
--- a/widget/gtk/moz.build
+++ b/widget/gtk/moz.build
@@ -81,16 +81,17 @@ if CONFIG["ACCESSIBILITY"]:
     ]
 
 if CONFIG["MOZ_WAYLAND"]:
     UNIFIED_SOURCES += [
         "DMABufLibWrapper.cpp",
         "DMABufSurface.cpp",
         "MozContainerWayland.cpp",
         "nsClipboardWayland.cpp",
+        "nsClipboardWaylandAsync.cpp",
         "nsWaylandDisplay.cpp",
         "WaylandBuffer.cpp",
         "WindowSurfaceWayland.cpp",
         "WindowSurfaceWaylandMultiBuffer.cpp",
     ]
     EXPORTS.mozilla.widget += [
         "DMABufLibWrapper.h",
         "DMABufSurface.h",
--- a/widget/gtk/nsClipboard.cpp
+++ b/widget/gtk/nsClipboard.cpp
@@ -7,16 +7,17 @@
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsArrayUtils.h"
 #include "nsClipboard.h"
 #include "nsClipboardX11.h"
 #if defined(MOZ_WAYLAND)
 #  include "nsClipboardWayland.h"
+#  include "nsClipboardWaylandAsync.h"
 #endif
 #include "nsContentUtils.h"
 #include "HeadlessClipboard.h"
 #include "nsSupportsPrimitives.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsPrimitiveHelpers.h"
 #include "nsImageToPixbuf.h"
@@ -91,20 +92,24 @@ nsClipboard::~nsClipboard() {
     gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
   }
 }
 
 NS_IMPL_ISUPPORTS(nsClipboard, nsIClipboard, nsIObserver)
 
 nsresult nsClipboard::Init(void) {
   if (widget::GdkIsX11Display()) {
-    mContext = MakeUnique<nsRetrievalContextX11>();
+    mContext = new nsRetrievalContextX11();
 #if defined(MOZ_WAYLAND)
   } else if (widget::GdkIsWaylandDisplay()) {
-    mContext = MakeUnique<nsRetrievalContextWayland>();
+    if (StaticPrefs::widget_wayland_async_clipboard_enabled_AtStartup()) {
+      mContext = new nsRetrievalContextWaylandAsync();
+    } else {
+      mContext = new nsRetrievalContextWayland();
+    }
 #endif
   } else {
     NS_WARNING("Missing nsRetrievalContext for nsClipboard!");
     return NS_OK;
   }
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
@@ -242,16 +247,17 @@ void nsClipboard::SetTransferableData(ns
   aTransferable->SetTransferData(aFlavor.get(), wrapper);
 }
 
 NS_IMETHODIMP
 nsClipboard::GetData(nsITransferable* aTransferable, int32_t aWhichClipboard) {
   LOGCLIP(("nsClipboard::GetData (%s)\n",
            aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"));
 
+  // TODO: Ensure we don't re-enter here.
   if (!aTransferable || !mContext) {
     return NS_ERROR_FAILURE;
   }
 
   // Get a list of flavors this transferable can import
   nsTArray<nsCString> flavors;
   nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors);
   if (NS_FAILED(rv)) {
--- a/widget/gtk/nsClipboard.h
+++ b/widget/gtk/nsClipboard.h
@@ -22,30 +22,35 @@ extern mozilla::LazyLogModule gClipboard
 #else
 #  define LOGCLIP(args)
 #endif /* MOZ_LOGGING */
 
 enum ClipboardDataType { CLIPBOARD_DATA, CLIPBOARD_TEXT, CLIPBOARD_TARGETS };
 
 class nsRetrievalContext {
  public:
+  // We intentionally use unsafe thread refcount as clipboard is used in
+  // main thread only.
+  NS_INLINE_DECL_REFCOUNTING(nsRetrievalContext)
+
   // Get actual clipboard content (GetClipboardData/GetClipboardText)
   // which has to be released by ReleaseClipboardData().
   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;
 
   // Get data mime types which can be obtained from clipboard.
   // The returned array has to be released by g_free().
   virtual GdkAtom* GetTargets(int32_t aWhichClipboard, int* aTargetNum) = 0;
 
   virtual bool HasSelectionSupport(void) = 0;
 
+ protected:
   virtual ~nsRetrievalContext() = default;
 };
 
 class nsClipboard : public nsIClipboard, public nsIObserver {
  public:
   nsClipboard();
 
   NS_DECL_ISUPPORTS
@@ -76,17 +81,17 @@ class nsClipboard : public nsIClipboard,
   void ClearTransferable(int32_t aWhichClipboard);
 
   // Hang on to our owners and transferables so we can transfer data
   // when asked.
   nsCOMPtr<nsIClipboardOwner> mSelectionOwner;
   nsCOMPtr<nsIClipboardOwner> mGlobalOwner;
   nsCOMPtr<nsITransferable> mSelectionTransferable;
   nsCOMPtr<nsITransferable> mGlobalTransferable;
-  mozilla::UniquePtr<nsRetrievalContext> mContext;
+  RefPtr<nsRetrievalContext> mContext;
 };
 
 extern const int kClipboardTimeout;
 
 GdkAtom GetSelectionAtom(int32_t aWhichClipboard);
 int GetGeckoClipboardType(GtkClipboard* aGtkClipboard);
 
 #endif /* __nsClipboard_h_ */
--- a/widget/gtk/nsClipboardWayland.cpp
+++ b/widget/gtk/nsClipboardWayland.cpp
@@ -858,18 +858,17 @@ bool nsRetrievalContextWayland::HasSelec
 }
 
 void nsRetrievalContextWayland::ClearDragAndDropDataOffer(void) {
   LOGDRAG(("nsRetrievalContextWayland::ClearDragAndDropDataOffer()\n"));
   mDragContext = nullptr;
 }
 
 nsRetrievalContextWayland::nsRetrievalContextWayland(void)
-    : mInitialized(false),
-      mDisplay(WaylandDisplayGet()),
+    : mDisplay(WaylandDisplayGet()),
       mClipboardRequestNumber(0),
       mClipboardData(nullptr),
       mClipboardDataLength(0) {
   wl_data_device* dataDevice = wl_data_device_manager_get_data_device(
       mDisplay->GetDataDeviceManager(), mDisplay->GetSeat());
   wl_data_device_add_listener(dataDevice, &data_device_listener, this);
 
   if (mDisplay->GetPrimarySelectionDeviceManagerZwpV1()) {
@@ -882,30 +881,28 @@ nsRetrievalContextWayland::nsRetrievalCo
   } else if (mDisplay->GetPrimarySelectionDeviceManagerGtk()) {
     gtk_primary_selection_device* primaryDataDevice =
         gtk_primary_selection_device_manager_get_device(
             mDisplay->GetPrimarySelectionDeviceManagerGtk(),
             mDisplay->GetSeat());
     gtk_primary_selection_device_add_listener(
         primaryDataDevice, &primary_selection_device_listener_gtk, this);
   }
-
-  mInitialized = true;
 }
 
 nsRetrievalContextWayland::~nsRetrievalContextWayland(void) {}
 
 struct FastTrackClipboard {
   FastTrackClipboard(ClipboardDataType aDataType, int aClipboardRequestNumber,
-                     nsRetrievalContextWayland* aRetrievalContex)
+                     RefPtr<nsRetrievalContextWayland> aRetrievalContex)
       : mClipboardRequestNumber(aClipboardRequestNumber),
-        mRetrievalContex(aRetrievalContex),
+        mRetrievalContex(std::move(aRetrievalContex)),
         mDataType(aDataType) {}
   int mClipboardRequestNumber;
-  nsRetrievalContextWayland* mRetrievalContex;
+  RefPtr<nsRetrievalContextWayland> mRetrievalContex;
   ClipboardDataType mDataType;
 };
 
 static void wayland_clipboard_contents_received(
     GtkClipboard* clipboard, GtkSelectionData* selection_data, gpointer data) {
   LOGCLIP(("wayland_clipboard_contents_received() selection_data = %p\n",
            selection_data));
   FastTrackClipboard* fastTrack = static_cast<FastTrackClipboard*>(data);
--- a/widget/gtk/nsClipboardWayland.h
+++ b/widget/gtk/nsClipboardWayland.h
@@ -13,18 +13,16 @@
 #include <nsTArray.h>
 
 #include "mozilla/Mutex.h"
 #include "nsIThread.h"
 #include "mozilla/UniquePtr.h"
 #include "nsClipboard.h"
 #include "nsWaylandDisplay.h"
 
-struct FastTrackClipboard;
-
 class DataOffer {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataOffer)
 
  public:
   explicit DataOffer(wl_data_offer* aDataOffer);
 
   virtual bool MatchesOffer(wl_data_offer* aDataOffer) {
     return aDataOffer == mWaylandDataOffer;
@@ -127,25 +125,24 @@ class nsRetrievalContextWayland : public
   RefPtr<DataOffer> GetDragContext() { return mDragContext; }
 
   void ClearDragAndDropDataOffer();
 
   void TransferFastTrackClipboard(ClipboardDataType aDataType,
                                   int aClipboardRequestNumber,
                                   GtkSelectionData* aSelectionData);
 
+ private:
   virtual ~nsRetrievalContextWayland() override;
 
- private:
   RefPtr<DataOffer> FindActiveOffer(wl_data_offer* aDataOffer,
                                     bool aRemove = false);
   void InsertOffer(RefPtr<DataOffer> aDataOffer);
 
  private:
-  bool mInitialized;
   RefPtr<mozilla::widget::nsWaylandDisplay> mDisplay;
 
   // Data offers provided by Wayland data device
   nsTArray<RefPtr<DataOffer>> mActiveOffers;
   RefPtr<DataOffer> mClipboardOffer;
   RefPtr<DataOffer> mPrimaryOffer;
   RefPtr<DataOffer> mDragContext;
 
new file mode 100644
--- /dev/null
+++ b/widget/gtk/nsClipboardWaylandAsync.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+
+#include "nsArrayUtils.h"
+#include "nsClipboard.h"
+#include "nsClipboardWaylandAsync.h"
+#include "nsSupportsPrimitives.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsPrimitiveHelpers.h"
+#include "nsImageToPixbuf.h"
+#include "nsStringStream.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "nsWindow.h"
+
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <string.h>
+
+using namespace mozilla;
+using namespace mozilla::widget;
+
+nsRetrievalContextWaylandAsync::nsRetrievalContextWaylandAsync(void)
+    : mClipboardRequestNumber(0),
+      mClipboardDataReceived(),
+      mClipboardData(nullptr),
+      mClipboardDataLength(0),
+      mMutex("nsRetrievalContextWaylandAsync") {}
+
+struct AsyncClipboardData {
+  AsyncClipboardData(ClipboardDataType aDataType, int aClipboardRequestNumber,
+                     RefPtr<nsRetrievalContextWaylandAsync> aRetrievalContex)
+      : mClipboardRequestNumber(aClipboardRequestNumber),
+        mRetrievalContex(std::move(aRetrievalContex)),
+        mDataType(aDataType) {}
+  int mClipboardRequestNumber;
+  RefPtr<nsRetrievalContextWaylandAsync> mRetrievalContex;
+  ClipboardDataType mDataType;
+};
+
+static void wayland_clipboard_contents_received(
+    GtkClipboard* clipboard, GtkSelectionData* selection_data, gpointer data) {
+  LOGCLIP(("wayland_clipboard_contents_received() selection_data = %p\n",
+           selection_data));
+  AsyncClipboardData* fastTrack = static_cast<AsyncClipboardData*>(data);
+  fastTrack->mRetrievalContex->TransferAsyncClipboardData(
+      fastTrack->mDataType, fastTrack->mClipboardRequestNumber, selection_data);
+  delete fastTrack;
+}
+
+static void wayland_clipboard_text_received(GtkClipboard* clipboard,
+                                            const gchar* text, gpointer data) {
+  LOGCLIP(("wayland_clipboard_text_received() text = %p\n", text));
+  AsyncClipboardData* fastTrack = static_cast<AsyncClipboardData*>(data);
+  fastTrack->mRetrievalContex->TransferAsyncClipboardData(
+      fastTrack->mDataType, fastTrack->mClipboardRequestNumber, (void*)text);
+  delete fastTrack;
+}
+
+void nsRetrievalContextWaylandAsync::TransferAsyncClipboardData(
+    ClipboardDataType aDataType, int aClipboardRequestNumber,
+    const void* aData) {
+  LOGCLIP(
+      ("nsRetrievalContextWaylandAsync::TransferAsyncClipboardData(), "
+       "aSelectionData = %p\n",
+       aData));
+
+  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
+                     "Clipboard contains old data?");
+
+  if (mClipboardRequestNumber != aClipboardRequestNumber) {
+    LOGCLIP(("    request number does not match!\n"));
+  }
+  LOGCLIP(("    request number matches\n"));
+
+  int dataLength = 0;
+  if (aDataType == CLIPBOARD_TARGETS || aDataType == CLIPBOARD_DATA) {
+    dataLength = gtk_selection_data_get_length((GtkSelectionData*)aData);
+  } else {
+    dataLength = strlen((const char*)aData);
+  }
+
+  mClipboardDataReceived = true;
+
+  // Negative size means no data or data error.
+  if (dataLength <= 0) {
+    return;
+  }
+
+  switch (aDataType) {
+    case CLIPBOARD_TARGETS: {
+      LOGCLIP(("    getting %d bytes of clipboard targets.\n", dataLength));
+      gint n_targets = 0;
+      GdkAtom* targets = nullptr;
+      if (!gtk_selection_data_get_targets((GtkSelectionData*)aData, &targets,
+                                          &n_targets) ||
+          !n_targets) {
+        // We failed to get targes
+        return;
+      }
+      mClipboardData = reinterpret_cast<char*>(targets);
+      mClipboardDataLength = n_targets;
+      break;
+    }
+    case CLIPBOARD_TEXT: {
+      LOGCLIP(("    getting %d bytes of text.\n", dataLength));
+      mClipboardDataLength = dataLength;
+      mClipboardData = reinterpret_cast<char*>(
+          g_malloc(sizeof(char) * (mClipboardDataLength + 1)));
+      memcpy(mClipboardData, aData, sizeof(char) * mClipboardDataLength);
+      mClipboardData[mClipboardDataLength] = '\0';
+      LOGCLIP(("    done, mClipboardData = %p\n", mClipboardData));
+      break;
+    }
+    case CLIPBOARD_DATA: {
+      LOGCLIP(("    getting %d bytes of data.\n", dataLength));
+      mClipboardDataLength = dataLength;
+      mClipboardData = reinterpret_cast<char*>(
+          g_malloc(sizeof(char) * mClipboardDataLength));
+      memcpy(mClipboardData,
+             gtk_selection_data_get_data((GtkSelectionData*)aData),
+             sizeof(char) * mClipboardDataLength);
+      LOGCLIP(("    done, mClipboardData = %p\n", mClipboardData));
+      break;
+    }
+  }
+}
+
+GdkAtom* nsRetrievalContextWaylandAsync::GetTargets(int32_t aWhichClipboard,
+                                                    int* aTargetNum) {
+  LOGCLIP(("nsRetrievalContextWaylandAsync::GetTargets()\n"));
+
+  if (!mMutex.TryLock()) {
+    LOGCLIP(("  nsRetrievalContextWaylandAsync is already used!\n"));
+    *aTargetNum = 0;
+    return nullptr;
+  }
+
+  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
+                     "Clipboard contains old data?");
+
+  GdkAtom selection = GetSelectionAtom(aWhichClipboard);
+  mClipboardDataReceived = false;
+  mClipboardRequestNumber++;
+  gtk_clipboard_request_contents(
+      gtk_clipboard_get(selection), gdk_atom_intern("TARGETS", FALSE),
+      wayland_clipboard_contents_received,
+      new AsyncClipboardData(CLIPBOARD_TARGETS, mClipboardRequestNumber, this));
+
+  if (!WaitForClipboardContent()) {
+    *aTargetNum = 0;
+    return nullptr;
+  }
+
+  // mClipboardDataLength is only signed integer, see
+  // nsRetrievalContextWaylandAsync::TransferAsyncClipboardData()
+  *aTargetNum = (int)mClipboardDataLength;
+  GdkAtom* targets = static_cast<GdkAtom*>((void*)mClipboardData);
+
+  // We don't hold the target list internally but we transfer the ownership.
+  mClipboardData = nullptr;
+  mClipboardDataLength = 0;
+
+  mMutex.Unlock();
+  return targets;
+}
+
+const char* nsRetrievalContextWaylandAsync::GetClipboardData(
+    const char* aMimeType, int32_t aWhichClipboard, uint32_t* aContentLength) {
+  LOGCLIP(("nsRetrievalContextWaylandAsync::GetClipboardData() mime %s\n",
+           aMimeType));
+
+  if (!mMutex.TryLock()) {
+    LOGCLIP(("  nsRetrievalContextWaylandAsync is already used!\n"));
+    *aContentLength = 0;
+    return nullptr;
+  }
+
+  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
+                     "Clipboard contains old data?");
+
+  GdkAtom selection = GetSelectionAtom(aWhichClipboard);
+  mClipboardDataReceived = false;
+  mClipboardRequestNumber++;
+  gtk_clipboard_request_contents(
+      gtk_clipboard_get(selection), gdk_atom_intern(aMimeType, FALSE),
+      wayland_clipboard_contents_received,
+      new AsyncClipboardData(CLIPBOARD_DATA, mClipboardRequestNumber, this));
+
+  if (!WaitForClipboardContent()) {
+    *aContentLength = 0;
+    mMutex.Unlock();
+    return nullptr;
+  }
+
+  *aContentLength = mClipboardDataLength;
+  return reinterpret_cast<const char*>(mClipboardData);
+}
+
+const char* nsRetrievalContextWaylandAsync::GetClipboardText(
+    int32_t aWhichClipboard) {
+  GdkAtom selection = GetSelectionAtom(aWhichClipboard);
+
+  LOGCLIP(("nsRetrievalContextWaylandAsync::GetClipboardText(), clipboard %s\n",
+           (selection == GDK_SELECTION_PRIMARY) ? "Primary" : "Selection"));
+
+  if (!mMutex.TryLock()) {
+    LOGCLIP(("  nsRetrievalContextWaylandAsync is already used!\n"));
+    return nullptr;
+  }
+
+  MOZ_RELEASE_ASSERT(mClipboardData == nullptr && mClipboardDataLength == 0,
+                     "Clipboard contains old data?");
+
+  mClipboardDataReceived = false;
+  mClipboardRequestNumber++;
+  gtk_clipboard_request_text(
+      gtk_clipboard_get(selection), wayland_clipboard_text_received,
+      new AsyncClipboardData(CLIPBOARD_TEXT, mClipboardRequestNumber, this));
+
+  if (!WaitForClipboardContent()) {
+    mMutex.Unlock();
+    return nullptr;
+  }
+  return reinterpret_cast<const char*>(mClipboardData);
+}
+
+bool nsRetrievalContextWaylandAsync::WaitForClipboardContent() {
+  PRTime entryTime = PR_Now();
+  while (!mClipboardDataReceived) {
+    // check the number of iterations
+    LOGCLIP(("doing iteration...\n"));
+    PR_Sleep(20 * PR_TicksPerSecond() / 1000); /* sleep for 20 ms/iteration */
+    if (PR_Now() - entryTime > kClipboardTimeout) {
+      LOGCLIP(("  failed to get async clipboard data in time limit\n"));
+      break;
+    }
+    gtk_main_iteration();
+  }
+  return mClipboardDataReceived && mClipboardData != nullptr;
+}
+
+void nsRetrievalContextWaylandAsync::ReleaseClipboardData(
+    const char* aClipboardData) {
+  LOGCLIP(("nsRetrievalContextWaylandAsync::ReleaseClipboardData [%p]\n",
+           aClipboardData));
+  if (aClipboardData != mClipboardData) {
+    NS_WARNING("Wayland clipboard: Releasing unknown clipboard data!");
+  }
+  g_free((void*)mClipboardData);
+  mClipboardDataLength = 0;
+  mClipboardData = nullptr;
+
+  mMutex.Unlock();
+}
new file mode 100644
--- /dev/null
+++ b/widget/gtk/nsClipboardWaylandAsync.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsClipboardWaylandAsync_h_
+#define __nsClipboardWaylandAsync_h_
+
+#include <gtk/gtk.h>
+#include <gdk/gdkwayland.h>
+#include <nsTArray.h>
+
+#include "mozilla/Mutex.h"
+#include "nsIThread.h"
+#include "mozilla/UniquePtr.h"
+#include "nsClipboard.h"
+#include "nsWaylandDisplay.h"
+
+class nsRetrievalContextWaylandAsync : public nsRetrievalContext {
+ public:
+  nsRetrievalContextWaylandAsync();
+
+  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* aTargetNum) override;
+
+  void TransferAsyncClipboardData(ClipboardDataType aDataType,
+                                  int aClipboardRequestNumber,
+                                  const void* aData);
+
+  bool HasSelectionSupport(void) override { return true; }
+
+ private:
+  bool WaitForClipboardContent();
+
+ private:
+  int mClipboardRequestNumber;
+  bool mClipboardDataReceived;
+  char* mClipboardData;
+  uint32_t mClipboardDataLength;
+  mozilla::Mutex mMutex;
+};
+
+#endif /* __nsClipboardWayland_h_ */