Skip to content

Commit

Permalink
[GTK4] Ensure that clipboard read operations work synchronously
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=265088

Reviewed by NOBODY (OOPS!).

Often clipboard operations are triggered as a result of a
button press event, which is handled with synchronous
messages between web and UI process. However, GTK4 clipboard
reads are asynchronous, and this can easily cause deadlocks
when the UI process is both waiting for the press event to be
handled by the web process before returning to the main loop
and the GTK4 clipboard is waiting for the GAsyncReadyCallback
to read from its stream at the same time, before allowing
WebKit's pasteboard to finish and let the press event to finish
being handled.

This explains why many of the GTK4 pasteboard and clipboard
tests are currently timing out -- the UI process is in
a deadlock state.

This deadlock can be prevented by using a GMainLoop to make
sure that the GTK Clipboard will call the GAsyncReadyCallback
for the clipboard operation in time, without deadlocking.

* Source/WebKit/UIProcess/gtk/ClipboardGtk4.cpp:
(WebKit::ReadTextAsyncData::ReadTextAsyncData):
(WebKit::Clipboard::readText):
(WebKit::ReadBufferAsyncData::ReadBufferAsyncData):
(WebKit::Clipboard::readBuffer):
  • Loading branch information
csaavedra committed Nov 18, 2023
1 parent 811592c commit ac10a38
Showing 1 changed file with 17 additions and 4 deletions.
21 changes: 17 additions & 4 deletions Source/WebKit/UIProcess/gtk/ClipboardGtk4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,27 @@ void Clipboard::formats(CompletionHandler<void(Vector<String>&&)>&& completionHa
struct ReadTextAsyncData {
WTF_MAKE_STRUCT_FAST_ALLOCATED;

explicit ReadTextAsyncData(CompletionHandler<void(String&&)>&& handler)
explicit ReadTextAsyncData(CompletionHandler<void(String&&)>&& handler, GRefPtr<GMainLoop> mainLoop)
: completionHandler(WTFMove(handler))
, loop(mainLoop)
{
}

CompletionHandler<void(String&&)> completionHandler;
GRefPtr<GMainLoop> loop;
};

void Clipboard::readText(CompletionHandler<void(String&&)>&& completionHandler)
{
GRefPtr<GMainLoop> loop = adoptGRef(g_main_loop_new(0, FALSE));
gdk_clipboard_read_text_async(m_clipboard, nullptr, [](GObject* clipboard, GAsyncResult* result, gpointer userData) {
std::unique_ptr<ReadTextAsyncData> data(static_cast<ReadTextAsyncData*>(userData));
GUniquePtr<char> text(gdk_clipboard_read_text_finish(GDK_CLIPBOARD(clipboard), result, nullptr));
data->completionHandler(String::fromUTF8(text.get()));
}, new ReadTextAsyncData(WTFMove(completionHandler)));
g_main_loop_quit(data->loop.get());
}, new ReadTextAsyncData(WTFMove(completionHandler), loop));

g_main_loop_run(loop.get());
}

struct ReadFilePathsAsyncData {
Expand Down Expand Up @@ -129,22 +135,26 @@ void Clipboard::readFilePaths(CompletionHandler<void(Vector<String>&&)>&& comple
struct ReadBufferAsyncData {
WTF_MAKE_STRUCT_FAST_ALLOCATED;

explicit ReadBufferAsyncData(CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)>&& handler)
explicit ReadBufferAsyncData(CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)>&& handler, GRefPtr<GMainLoop> mainLoop)
: completionHandler(WTFMove(handler))
, loop(mainLoop)
{
}

CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)> completionHandler;
GRefPtr<GMainLoop> loop;
};

void Clipboard::readBuffer(const char* format, CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)>&& completionHandler)
{
const char* mimeTypes[] = { format, nullptr };
GRefPtr<GMainLoop> loop = adoptGRef(g_main_loop_new(0, FALSE));
gdk_clipboard_read_async(m_clipboard, mimeTypes, G_PRIORITY_DEFAULT, nullptr, [](GObject* clipboard, GAsyncResult* result, gpointer userData) {
std::unique_ptr<ReadBufferAsyncData> data(static_cast<ReadBufferAsyncData*>(userData));
GRefPtr<GInputStream> inputStream = adoptGRef(gdk_clipboard_read_finish(GDK_CLIPBOARD(clipboard), result, nullptr, nullptr));
if (!inputStream) {
data->completionHandler(WebCore::SharedBuffer::create());
g_main_loop_quit(data->loop.get());
return;
}

Expand All @@ -156,13 +166,16 @@ void Clipboard::readBuffer(const char* format, CompletionHandler<void(Ref<WebCor
gssize writtenBytes = g_output_stream_splice_finish(G_OUTPUT_STREAM(stream), result, nullptr);
if (writtenBytes <= 0) {
data->completionHandler(WebCore::SharedBuffer::create());
g_main_loop_quit(data->loop.get());
return;
}

GRefPtr<GBytes> bytes = adoptGRef(g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(stream)));
data->completionHandler(WebCore::SharedBuffer::create(bytes.get()));
g_main_loop_quit(data->loop.get());
}, data.release());
}, new ReadBufferAsyncData(WTFMove(completionHandler)));
}, new ReadBufferAsyncData(WTFMove(completionHandler), loop));
g_main_loop_run(loop.get());
}

struct ReadURLAsyncData {
Expand Down

0 comments on commit ac10a38

Please sign in to comment.