blob: 07abb713198853b787c6324bd480b1e088a2a3d3 [file] [log] [blame]
/*
* Copyright (C) 2020 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Clipboard.h"
#if USE(GTK4)
#include "WebPasteboardProxy.h"
#include <WebCore/PasteboardCustomData.h>
#include <WebCore/SelectionData.h>
#include <WebCore/SharedBuffer.h>
#include <gtk/gtk.h>
#include <wtf/glib/GRefPtr.h>
namespace WebKit {
Clipboard::Clipboard(Type type)
: m_clipboard(type == Type::Clipboard ? gdk_display_get_clipboard(gdk_display_get_default()) : gdk_display_get_primary_clipboard(gdk_display_get_default()))
{
if (type == Type::Primary) {
g_signal_connect(m_clipboard, "notify::local", G_CALLBACK(+[](GdkClipboard* clipboard, GParamSpec*, gpointer) {
if (!gdk_clipboard_is_local(clipboard))
WebPasteboardProxy::singleton().setPrimarySelectionOwner(nullptr);
}), nullptr);
}
}
Clipboard::Type Clipboard::type() const
{
return m_clipboard == gdk_display_get_primary_clipboard(gdk_display_get_default()) ? Type::Primary : Type::Clipboard;
}
void Clipboard::formats(CompletionHandler<void(Vector<String>&&)>&& completionHandler)
{
gsize mimeTypesCount;
const char* const* mimeTypes = gdk_content_formats_get_mime_types(gdk_clipboard_get_formats(m_clipboard), &mimeTypesCount);
Vector<String> result;
result.reserveInitialCapacity(mimeTypesCount);
for (size_t i = 0; i < mimeTypesCount; ++i)
result.uncheckedAppend(String::fromUTF8(mimeTypes[i]));
completionHandler(WTFMove(result));
}
struct ReadTextAsyncData {
WTF_MAKE_STRUCT_FAST_ALLOCATED;
explicit ReadTextAsyncData(CompletionHandler<void(String&&)>&& handler)
: completionHandler(WTFMove(handler))
{
}
CompletionHandler<void(String&&)> completionHandler;
};
void Clipboard::readText(CompletionHandler<void(String&&)>&& completionHandler)
{
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)));
}
struct ReadFilePathsAsyncData {
WTF_MAKE_STRUCT_FAST_ALLOCATED;
explicit ReadFilePathsAsyncData(CompletionHandler<void(Vector<String>&&)>&& handler)
: completionHandler(WTFMove(handler))
{
}
CompletionHandler<void(Vector<String>&&)> completionHandler;
};
void Clipboard::readFilePaths(CompletionHandler<void(Vector<String>&&)>&& completionHandler)
{
gdk_clipboard_read_value_async(m_clipboard, GDK_TYPE_FILE_LIST, G_PRIORITY_DEFAULT, nullptr, [](GObject* clipboard, GAsyncResult* result, gpointer userData) {
std::unique_ptr<ReadFilePathsAsyncData> data(static_cast<ReadFilePathsAsyncData*>(userData));
Vector<String> filePaths;
if (const GValue* value = gdk_clipboard_read_value_finish(GDK_CLIPBOARD(clipboard), result, nullptr)) {
auto* list = static_cast<GSList*>(g_value_get_boxed(value));
for (auto* l = list; l && l->data; l = g_slist_next(l)) {
auto* file = G_FILE(l->data);
if (!g_file_is_native(file))
continue;
GUniquePtr<gchar> filename(g_file_get_path(file));
if (filename)
filePaths.append(String::fromUTF8(filename.get()));
}
}
data->completionHandler(WTFMove(filePaths));
}, new ReadFilePathsAsyncData(WTFMove(completionHandler)));
}
struct ReadBufferAsyncData {
WTF_MAKE_STRUCT_FAST_ALLOCATED;
explicit ReadBufferAsyncData(CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)>&& handler)
: completionHandler(WTFMove(handler))
{
}
CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)> completionHandler;
};
void Clipboard::readBuffer(const char* format, CompletionHandler<void(Ref<WebCore::SharedBuffer>&&)>&& completionHandler)
{
const char* mimeTypes[] = { format, nullptr };
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());
return;
}
GRefPtr<GOutputStream> outputStream = adoptGRef(g_memory_output_stream_new_resizable());
g_output_stream_splice_async(outputStream.get(), inputStream.get(),
static_cast<GOutputStreamSpliceFlags>(G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET),
G_PRIORITY_DEFAULT, nullptr, [](GObject* stream, GAsyncResult* result, gpointer userData) {
std::unique_ptr<ReadBufferAsyncData> data(static_cast<ReadBufferAsyncData*>(userData));
gssize writtenBytes = g_output_stream_splice_finish(G_OUTPUT_STREAM(stream), result, nullptr);
if (writtenBytes <= 0) {
data->completionHandler(WebCore::SharedBuffer::create());
return;
}
GRefPtr<GBytes> bytes = adoptGRef(g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(stream)));
data->completionHandler(WebCore::SharedBuffer::create(bytes.get()));
}, data.release());
}, new ReadBufferAsyncData(WTFMove(completionHandler)));
}
void Clipboard::write(WebCore::SelectionData&& selectionData)
{
Vector<GdkContentProvider*> providers;
if (selectionData.hasMarkup()) {
CString markup = selectionData.markup().utf8();
GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(markup.data(), markup.length()));
providers.append(gdk_content_provider_new_for_bytes("text/html", bytes.get()));
}
if (selectionData.hasURIList()) {
CString uriList = selectionData.uriList().utf8();
GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(uriList.data(), uriList.length()));
providers.append(gdk_content_provider_new_for_bytes("text/uri-list", bytes.get()));
}
if (selectionData.hasImage()) {
GRefPtr<GdkPixbuf> pixbuf = adoptGRef(selectionData.image()->getGdkPixbuf());
providers.append(gdk_content_provider_new_typed(GDK_TYPE_PIXBUF, pixbuf.get()));
}
if (selectionData.hasText())
providers.append(gdk_content_provider_new_typed(G_TYPE_STRING, selectionData.text().utf8().data()));
if (selectionData.canSmartReplace()) {
GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(nullptr, 0));
providers.append(gdk_content_provider_new_for_bytes("application/vnd.webkitgtk.smartpaste", bytes.get()));
}
if (selectionData.hasCustomData()) {
GRefPtr<GBytes> bytes = selectionData.customData()->createGBytes();
providers.append(gdk_content_provider_new_for_bytes(WebCore::PasteboardCustomData::gtkType(), bytes.get()));
}
if (providers.isEmpty()) {
clear();
return;
}
GRefPtr<GdkContentProvider> provider = adoptGRef(gdk_content_provider_new_union(providers.data(), providers.size()));
gdk_clipboard_set_content(m_clipboard, provider.get());
}
void Clipboard::clear()
{
gdk_clipboard_set_content(m_clipboard, nullptr);
}
} // namespace WebKit
#endif