author | stransky <stransky@redhat.com> |
Thu, 12 Aug 2021 14:07:58 +0000 | |
changeset 588700 | ca7dd68c51a25ac829033a88cbd98ab18ff4f429 |
parent 588699 | 8b346c50a44c4a98b300bf528e4d3564dd167078 |
child 588701 | f912045db98d9790427541bcfc9f0d0eb07a8f96 |
push id | 147961 |
push user | stransky@redhat.com |
push date | Thu, 12 Aug 2021 14:11:01 +0000 |
treeherder | autoland@ca7dd68c51a2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | rmader |
bugs | 1725149 |
milestone | 93.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
|
--- 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_ */