From 61c0f67c8c9d194261d536d948f37b774c7853f4 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 1 May 2025 11:38:31 -0400 Subject: [PATCH] LibWeb+LibWebVew+WebContent+UI: Add IPC to retrieve the system clipboard We currently have a single IPC to set clipboard data. We will also need an IPC to retrieve that data from the UI. This defines system clipboard data in LibWeb to handle this transfer, and adds the IPC to provide it. --- Libraries/LibWeb/CMakeLists.txt | 1 + Libraries/LibWeb/Clipboard/Clipboard.cpp | 19 ++++---- .../LibWeb/Clipboard/SystemClipboard.cpp | 41 ++++++++++++++++++ Libraries/LibWeb/Clipboard/SystemClipboard.h | 43 +++++++++++++++++++ Libraries/LibWeb/Forward.h | 3 ++ Libraries/LibWeb/Page/Page.cpp | 16 +++++++ Libraries/LibWeb/Page/Page.h | 10 ++++- Libraries/LibWebView/ViewImplementation.cpp | 6 +++ Libraries/LibWebView/ViewImplementation.h | 5 ++- Libraries/LibWebView/WebContentClient.cpp | 12 +++++- Libraries/LibWebView/WebContentClient.h | 3 +- Services/WebContent/ConnectionFromClient.cpp | 6 +++ Services/WebContent/ConnectionFromClient.h | 2 + Services/WebContent/PageClient.cpp | 9 +++- Services/WebContent/PageClient.h | 3 +- Services/WebContent/WebContentClient.ipc | 8 +++- Services/WebContent/WebContentServer.ipc | 3 ++ UI/AppKit/Interface/LadybirdWebView.mm | 41 +++++++++++++++--- UI/AppKit/Utilities/Conversions.h | 1 + UI/AppKit/Utilities/Conversions.mm | 5 +++ UI/Qt/StringUtils.cpp | 10 +++++ UI/Qt/StringUtils.h | 5 +++ UI/Qt/Tab.cpp | 31 +++++++++++-- 23 files changed, 255 insertions(+), 28 deletions(-) create mode 100644 Libraries/LibWeb/Clipboard/SystemClipboard.cpp create mode 100644 Libraries/LibWeb/Clipboard/SystemClipboard.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 633d0612c7c..1e7d73d8123 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -34,6 +34,7 @@ set(SOURCES Clipboard/Clipboard.cpp Clipboard/ClipboardEvent.cpp Clipboard/ClipboardItem.cpp + Clipboard/SystemClipboard.cpp Compression/CompressionStream.cpp Compression/DecompressionStream.cpp ContentSecurityPolicy/Directives/Directive.cpp diff --git a/Libraries/LibWeb/Clipboard/Clipboard.cpp b/Libraries/LibWeb/Clipboard/Clipboard.cpp index 8d025d941c3..7241d991669 100644 --- a/Libraries/LibWeb/Clipboard/Clipboard.cpp +++ b/Libraries/LibWeb/Clipboard/Clipboard.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -40,43 +41,43 @@ void Clipboard::initialize(JS::Realm& realm) } // https://w3c.github.io/clipboard-apis/#os-specific-well-known-format -static StringView os_specific_well_known_format(StringView mime_type_string) +static String os_specific_well_known_format(StringView mime_type_string) { // NOTE: Here we always takes the Linux case, and defer to the browser process to handle OS specific implementations. auto mime_type = MimeSniff::MimeType::parse(mime_type_string); // 1. Let wellKnownFormat be an empty string. - StringView well_known_format {}; + String well_known_format {}; // 2. If mimeType’s essence is "text/plain", then - if (mime_type->essence() == "text/plain"sv) { + if (auto const& essence = mime_type->essence(); essence == "text/plain"sv) { // On Windows, follow the convention described below: // Assign CF_UNICODETEXT to wellKnownFormat. // On MacOS, follow the convention described below: // Assign NSPasteboardTypeString to wellKnownFormat. // On Linux, ChromeOS, and Android, follow the convention described below: // Assign "text/plain" to wellKnownFormat. - well_known_format = "text/plain"sv; + well_known_format = essence; } // 3. Else, if mimeType’s essence is "text/html", then - if (mime_type->essence() == "text/html"sv) { + else if (essence == "text/html"sv) { // On Windows, follow the convention described below: // Assign CF_HTML to wellKnownFormat. // On MacOS, follow the convention described below: // Assign NSHTMLPboardType to wellKnownFormat. // On Linux, ChromeOS, and Android, follow the convention described below: // Assign "text/html" to wellKnownFormat. - well_known_format = "text/html"sv; + well_known_format = essence; } // 4. Else, if mimeType’s essence is "image/png", then - if (mime_type->essence() == "image/png"sv) { + else if (essence == "image/png"sv) { // On Windows, follow the convention described below: // Assign "PNG" to wellKnownFormat. // On MacOS, follow the convention described below: // Assign NSPasteboardTypePNG to wellKnownFormat. // On Linux, ChromeOS, and Android, follow the convention described below: // Assign "image/png" to wellKnownFormat. - well_known_format = "image/png"sv; + well_known_format = essence; } // 5. Return wellKnownFormat. @@ -113,7 +114,7 @@ static void write_blobs_and_option_to_clipboard(JS::Realm& realm, ReadonlySpanraw_bytes())); // 4. Insert payload and presentationStyle into the system clipboard using formatString as the native clipboard format. - window.page().client().page_did_insert_clipboard_entry(payload, presentation_style, format_string); + window.page().client().page_did_insert_clipboard_entry({ payload.to_byte_string(), move(format_string) }, presentation_style); } // FIXME: 3. Write web custom formats given webCustomFormats. diff --git a/Libraries/LibWeb/Clipboard/SystemClipboard.cpp b/Libraries/LibWeb/Clipboard/SystemClipboard.cpp new file mode 100644 index 00000000000..5338dd88528 --- /dev/null +++ b/Libraries/LibWeb/Clipboard/SystemClipboard.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +template<> +ErrorOr IPC::encode(Encoder& encoder, Web::Clipboard::SystemClipboardRepresentation const& output) +{ + TRY(encoder.encode(output.data)); + TRY(encoder.encode(output.mime_type)); + return {}; +} + +template<> +ErrorOr IPC::decode(Decoder& decoder) +{ + auto data = TRY(decoder.decode()); + auto mime_type = TRY(decoder.decode()); + + return Web::Clipboard::SystemClipboardRepresentation { move(data), move(mime_type) }; +} + +template<> +ErrorOr IPC::encode(Encoder& encoder, Web::Clipboard::SystemClipboardItem const& output) +{ + TRY(encoder.encode(output.system_clipboard_representations)); + return {}; +} + +template<> +ErrorOr IPC::decode(Decoder& decoder) +{ + auto system_clipboard_representation = TRY(decoder.decode>()); + + return Web::Clipboard::SystemClipboardItem { move(system_clipboard_representation) }; +} diff --git a/Libraries/LibWeb/Clipboard/SystemClipboard.h b/Libraries/LibWeb/Clipboard/SystemClipboard.h new file mode 100644 index 00000000000..4182e919cca --- /dev/null +++ b/Libraries/LibWeb/Clipboard/SystemClipboard.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::Clipboard { + +// https://w3c.github.io/clipboard-apis/#system-clipboard-representation +struct SystemClipboardRepresentation { + ByteString data; + String mime_type; +}; + +// https://w3c.github.io/clipboard-apis/#system-clipboard-item +struct SystemClipboardItem { + Vector system_clipboard_representations; +}; + +} + +namespace IPC { + +template<> +ErrorOr encode(Encoder&, Web::Clipboard::SystemClipboardRepresentation const&); + +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Web::Clipboard::SystemClipboardItem const&); + +template<> +ErrorOr decode(Decoder&); + +} diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index c835778091a..7dfd13b08a1 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -92,6 +92,9 @@ enum class XMLHttpRequestResponseType : u8; namespace Web::Clipboard { class Clipboard; class ClipboardItem; + +struct SystemClipboardItem; +struct SystemClipboardRepresentation; } namespace Web::Compression { diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp index 21bb34cad60..3e0d9419c65 100644 --- a/Libraries/LibWeb/Page/Page.cpp +++ b/Libraries/LibWeb/Page/Page.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ void Page::visit_edges(JS::Cell::Visitor& visitor) visitor.visit(m_client); visitor.visit(m_window_rect_observer); visitor.visit(m_on_pending_dialog_closed); + visitor.visit(m_pending_clipboard_requests); } HTML::Navigable& Page::focused_navigable() @@ -454,6 +456,20 @@ void Page::select_dropdown_closed(Optional const& selected_item_id) } } +void Page::request_clipboard_entries(ClipboardRequest request) +{ + auto request_id = m_next_clipboard_request_id++; + m_pending_clipboard_requests.set(request_id, request); + + client().page_did_request_clipboard_entries(request_id); +} + +void Page::retrieved_clipboard_entries(u64 request_id, Vector items) +{ + if (auto request = m_pending_clipboard_requests.take(request_id); request.has_value()) + (*request)->function()(move(items)); +} + void Page::register_media_element(Badge, UniqueNodeID media_id) { m_media_elements.append(media_id); diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index f9da700cb87..f80c8f97ee5 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -164,6 +164,10 @@ public: void did_request_select_dropdown(WeakPtr target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items); void select_dropdown_closed(Optional const& selected_item_id); + using ClipboardRequest = GC::Ref)>>; + void request_clipboard_entries(ClipboardRequest); + void retrieved_clipboard_entries(u64 request_id, Vector); + enum class PendingNonBlockingDialog { None, ColorPicker, @@ -272,6 +276,9 @@ private: PendingNonBlockingDialog m_pending_non_blocking_dialog { PendingNonBlockingDialog::None }; WeakPtr m_pending_non_blocking_dialog_target; + HashMap m_pending_clipboard_requests; + u64 m_next_clipboard_request_id { 0 }; + Vector m_media_elements; Optional m_media_context_menu_element_id; @@ -393,7 +400,8 @@ public: virtual void page_did_change_theme_color(Gfx::Color) { } - virtual void page_did_insert_clipboard_entry([[maybe_unused]] StringView data, [[maybe_unused]] StringView presentation_style, [[maybe_unused]] StringView mime_type) { } + virtual void page_did_insert_clipboard_entry(Clipboard::SystemClipboardRepresentation const&, [[maybe_unused]] StringView presentation_style) { } + virtual void page_did_request_clipboard_entries([[maybe_unused]] u64 request_id) { } virtual void page_did_change_audio_play_state(HTML::AudioPlayState) { } diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp index 6c83ad5ac3d..3f746d9366e 100644 --- a/Libraries/LibWebView/ViewImplementation.cpp +++ b/Libraries/LibWebView/ViewImplementation.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -455,6 +456,11 @@ void ViewImplementation::select_dropdown_closed(Optional const& selected_it client().async_select_dropdown_closed(page_id(), selected_item_id); } +void ViewImplementation::retrieved_clipboard_entries(u64 request_id, ReadonlySpan items) +{ + client().async_retrieved_clipboard_entries(page_id(), request_id, items); +} + void ViewImplementation::toggle_media_play_state() { client().async_toggle_media_play_state(page_id()); diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index 4ab2468fcd3..ace300d2d43 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -130,6 +130,8 @@ public: void file_picker_closed(Vector selected_files); void select_dropdown_closed(Optional const& selected_item_id); + void retrieved_clipboard_entries(u64 request_id, ReadonlySpan); + void toggle_media_play_state(); void toggle_media_mute_state(); void toggle_media_loop_state(); @@ -225,7 +227,8 @@ public: Function on_set_browser_zoom; Function const& total_match_count)> on_find_in_page; Function on_theme_color_change; - Function on_insert_clipboard_entry; + Function on_insert_clipboard_entry; + Function on_request_clipboard_entries; Function on_audio_play_state_changed; Function on_navigation_buttons_state_changed; Function on_web_content_crashed; diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index 0147996af37..089201ee85c 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -633,11 +633,19 @@ void WebContentClient::did_change_theme_color(u64 page_id, Gfx::Color color) } } -void WebContentClient::did_insert_clipboard_entry(u64 page_id, String data, String presentation_style, String mime_type) +void WebContentClient::did_insert_clipboard_entry(u64 page_id, Web::Clipboard::SystemClipboardRepresentation entry, String presentation_style) { if (auto view = view_for_page_id(page_id); view.has_value()) { if (view->on_insert_clipboard_entry) - view->on_insert_clipboard_entry(data, presentation_style, mime_type); + view->on_insert_clipboard_entry(move(entry), presentation_style); + } +} + +void WebContentClient::did_request_clipboard_entries(u64 page_id, u64 request_id) +{ + if (auto view = view_for_page_id(page_id); view.has_value()) { + if (view->on_request_clipboard_entries) + view->on_request_clipboard_entries(request_id); } } diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h index 9c8feb017e4..072beee6db4 100644 --- a/Libraries/LibWebView/WebContentClient.h +++ b/Libraries/LibWebView/WebContentClient.h @@ -126,7 +126,8 @@ private: virtual void did_set_browser_zoom(u64 page_id, double factor) override; virtual void did_find_in_page(u64 page_id, size_t current_match_index, Optional total_match_count) override; virtual void did_change_theme_color(u64 page_id, Gfx::Color color) override; - virtual void did_insert_clipboard_entry(u64 page_id, String data, String presentation_style, String mime_type) override; + virtual void did_insert_clipboard_entry(u64 page_id, Web::Clipboard::SystemClipboardRepresentation, String presentation_style) override; + virtual void did_request_clipboard_entries(u64 page_id, u64 request_id) override; virtual void did_change_audio_play_state(u64 page_id, Web::HTML::AudioPlayState) override; virtual void did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) override; virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap, i32 back_bitmap_id, Gfx::ShareableBitmap) override; diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 5b5e13d6919..a9ca53dc6ee 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -1257,6 +1257,12 @@ void ConnectionFromClient::select_dropdown_closed(u64 page_id, Optional sel page->page().select_dropdown_closed(selected_item_id); } +void ConnectionFromClient::retrieved_clipboard_entries(u64 page_id, u64 request_id, Vector items) +{ + if (auto page = this->page(page_id); page.has_value()) + page->page().retrieved_clipboard_entries(request_id, move(items)); +} + void ConnectionFromClient::toggle_media_play_state(u64 page_id) { if (auto page = this->page(page_id); page.has_value()) diff --git a/Services/WebContent/ConnectionFromClient.h b/Services/WebContent/ConnectionFromClient.h index 7347961ec52..9308788f9d9 100644 --- a/Services/WebContent/ConnectionFromClient.h +++ b/Services/WebContent/ConnectionFromClient.h @@ -129,6 +129,8 @@ private: virtual void file_picker_closed(u64 page_id, Vector selected_files) override; virtual void select_dropdown_closed(u64 page_id, Optional selected_item_id) override; + virtual void retrieved_clipboard_entries(u64 page_id, u64 request_id, Vector) override; + virtual void toggle_media_play_state(u64 page_id) override; virtual void toggle_media_mute_state(u64 page_id) override; virtual void toggle_media_loop_state(u64 page_id) override; diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index 3fb349aca11..a8ec9fdf65d 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -649,9 +649,14 @@ void PageClient::page_did_change_theme_color(Gfx::Color color) client().async_did_change_theme_color(m_id, color); } -void PageClient::page_did_insert_clipboard_entry(StringView data, StringView presentation_style, StringView mime_type) +void PageClient::page_did_insert_clipboard_entry(Web::Clipboard::SystemClipboardRepresentation const& entry, StringView presentation_style) { - client().async_did_insert_clipboard_entry(m_id, data, presentation_style, mime_type); + client().async_did_insert_clipboard_entry(m_id, entry, presentation_style); +} + +void PageClient::page_did_request_clipboard_entries(u64 request_id) +{ + client().async_did_request_clipboard_entries(m_id, request_id); } void PageClient::page_did_change_audio_play_state(Web::HTML::AudioPlayState play_state) diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h index 6eb3fb6e615..e90f299e7dc 100644 --- a/Services/WebContent/PageClient.h +++ b/Services/WebContent/PageClient.h @@ -169,7 +169,8 @@ private: virtual void page_did_set_test_timeout(double milliseconds) override; virtual void page_did_set_browser_zoom(double factor) override; virtual void page_did_change_theme_color(Gfx::Color color) override; - virtual void page_did_insert_clipboard_entry(StringView data, StringView presentation_style, StringView mime_type) override; + virtual void page_did_insert_clipboard_entry(Web::Clipboard::SystemClipboardRepresentation const&, StringView presentation_style) override; + virtual void page_did_request_clipboard_entries(u64 request_id) override; virtual void page_did_change_audio_play_state(Web::HTML::AudioPlayState) override; virtual void page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) override; virtual IPC::File request_worker_agent(Web::Bindings::AgentType) override; diff --git a/Services/WebContent/WebContentClient.ipc b/Services/WebContent/WebContentClient.ipc index ea8a2558c11..d1cc70796aa 100644 --- a/Services/WebContent/WebContentClient.ipc +++ b/Services/WebContent/WebContentClient.ipc @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -21,7 +23,6 @@ #include #include #include -#include endpoint WebContentClient { @@ -91,7 +92,10 @@ endpoint WebContentClient did_request_select_dropdown(u64 page_id, Gfx::IntPoint content_position, i32 minimum_width, Vector items) =| did_finish_handling_input_event(u64 page_id, Web::EventResult event_result) =| did_change_theme_color(u64 page_id, Gfx::Color color) =| - did_insert_clipboard_entry(u64 page_id, String data, String presentation_style, String mime_type) =| + + did_insert_clipboard_entry(u64 page_id, Web::Clipboard::SystemClipboardRepresentation entry, String presentation_style) =| + did_request_clipboard_entries(u64 page_id, u64 request_id) =| + did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) =| did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) =| diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc index 82637b6d83c..1620a261bee 100644 --- a/Services/WebContent/WebContentServer.ipc +++ b/Services/WebContent/WebContentServer.ipc @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,8 @@ endpoint WebContentServer file_picker_closed(u64 page_id, Vector selected_files) =| select_dropdown_closed(u64 page_id, Optional selected_item_id) =| + retrieved_clipboard_entries(u64 page_id, u64 request_id, Vector items) =| + toggle_media_play_state(u64 page_id) =| toggle_media_mute_state(u64 page_id) =| toggle_media_loop_state(u64 page_id) =| diff --git a/UI/AppKit/Interface/LadybirdWebView.mm b/UI/AppKit/Interface/LadybirdWebView.mm index ae8c0b2d9db..9cb31824de9 100644 --- a/UI/AppKit/Interface/LadybirdWebView.mm +++ b/UI/AppKit/Interface/LadybirdWebView.mm @@ -1064,19 +1064,50 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_ totalMatchCount:total_match_count]; }; - m_web_view_bridge->on_insert_clipboard_entry = [](auto const& data, auto const&, auto const& mime_type) { + m_web_view_bridge->on_insert_clipboard_entry = [](Web::Clipboard::SystemClipboardRepresentation const& entry, auto const&) { NSPasteboardType pasteboard_type = nil; // https://w3c.github.io/clipboard-apis/#os-specific-well-known-format - if (mime_type == "text/plain"sv) + if (entry.mime_type == "text/plain"sv) pasteboard_type = NSPasteboardTypeString; - else if (mime_type == "text/html"sv) + else if (entry.mime_type == "text/html"sv) pasteboard_type = NSPasteboardTypeHTML; - else if (mime_type == "text/png"sv) + else if (entry.mime_type == "image/png"sv) pasteboard_type = NSPasteboardTypePNG; if (pasteboard_type) - copy_data_to_clipboard(data, pasteboard_type); + copy_data_to_clipboard(entry.data, pasteboard_type); + }; + + m_web_view_bridge->on_request_clipboard_entries = [weak_self](auto request_id) { + LadybirdWebView* self = weak_self; + if (self == nil) { + return; + } + + Vector items; + Vector representations; + + auto* pasteBoard = [NSPasteboard generalPasteboard]; + + for (NSPasteboardType type : [pasteBoard types]) { + String mime_type; + + if (type == NSPasteboardTypeString) + mime_type = "text/plain"_string; + else if (type == NSPasteboardTypeHTML) + mime_type = "text/html"_string; + else if (type == NSPasteboardTypePNG) + mime_type = "image/png"_string; + + auto data = Ladybird::ns_data_to_string([pasteBoard dataForType:type]); + representations.empend(move(data), move(mime_type)); + } + + if (!representations.is_empty()) + items.empend(AK::move(representations)); + + m_web_view_bridge->retrieved_clipboard_entries(request_id, items); }; m_web_view_bridge->on_audio_play_state_changed = [weak_self](auto play_state) { diff --git a/UI/AppKit/Utilities/Conversions.h b/UI/AppKit/Utilities/Conversions.h index b50c5539ffc..b48d460b8ac 100644 --- a/UI/AppKit/Utilities/Conversions.h +++ b/UI/AppKit/Utilities/Conversions.h @@ -23,6 +23,7 @@ String ns_string_to_string(NSString*); ByteString ns_string_to_byte_string(NSString*); NSString* string_to_ns_string(StringView); +ByteString ns_data_to_string(NSData*); NSData* string_to_ns_data(StringView); NSDictionary* deserialize_json_to_dictionary(StringView); diff --git a/UI/AppKit/Utilities/Conversions.mm b/UI/AppKit/Utilities/Conversions.mm index cfa2e8cf7d2..ae97bec8d01 100644 --- a/UI/AppKit/Utilities/Conversions.mm +++ b/UI/AppKit/Utilities/Conversions.mm @@ -28,6 +28,11 @@ NSString* string_to_ns_string(StringView string) return [[NSString alloc] initWithData:string_to_ns_data(string) encoding:NSUTF8StringEncoding]; } +ByteString ns_data_to_string(NSData* data) +{ + return { reinterpret_cast([data bytes]), [data length] }; +} + NSData* string_to_ns_data(StringView string) { return [NSData dataWithBytes:string.characters_without_null_termination() length:string.length()]; diff --git a/UI/Qt/StringUtils.cpp b/UI/Qt/StringUtils.cpp index 1f5f113b6ae..2642384f191 100644 --- a/UI/Qt/StringUtils.cpp +++ b/UI/Qt/StringUtils.cpp @@ -13,6 +13,11 @@ AK::ByteString ak_byte_string_from_qstring(QString const& qstring) return AK::ByteString(utf8_data.data(), utf8_data.size()); } +AK::ByteString ak_byte_string_from_qbytearray(QByteArray const& qbytearray) +{ + return AK::ByteString(qbytearray.data(), qbytearray.size()); +} + String ak_string_from_qstring(QString const& qstring) { auto utf8_data = qstring.toUtf8(); @@ -24,6 +29,11 @@ QString qstring_from_ak_string(StringView ak_string) return QString::fromUtf8(ak_string.characters_without_null_termination(), static_cast(ak_string.length())); } +QByteArray qbytearray_from_ak_string(StringView ak_string) +{ + return { ak_string.characters_without_null_termination(), static_cast(ak_string.length()) }; +} + Optional ak_url_from_qstring(QString const& qstring) { auto utf8_data = qstring.toUtf8(); diff --git a/UI/Qt/StringUtils.h b/UI/Qt/StringUtils.h index a0bd03bd538..358c8e96049 100644 --- a/UI/Qt/StringUtils.h +++ b/UI/Qt/StringUtils.h @@ -13,11 +13,16 @@ #include #include +#include #include #include AK::ByteString ak_byte_string_from_qstring(QString const&); +AK::ByteString ak_byte_string_from_qbytearray(QByteArray const&); + String ak_string_from_qstring(QString const&); QString qstring_from_ak_string(StringView); +QByteArray qbytearray_from_ak_string(StringView); + Optional ak_url_from_qstring(QString const&); URL::URL ak_url_from_qurl(QUrl const&); diff --git a/UI/Qt/Tab.cpp b/UI/Qt/Tab.cpp index a5a7cbc63a1..7c50695c152 100644 --- a/UI/Qt/Tab.cpp +++ b/UI/Qt/Tab.cpp @@ -369,16 +369,39 @@ Tab::Tab(BrowserWindow* window, RefPtr parent_client, view().did_update_window_rect(); }; - view().on_insert_clipboard_entry = [](auto const& data, auto const&, auto const& mime_type) { - QByteArray qdata { data.bytes_as_string_view().characters_without_null_termination(), static_cast(data.bytes_as_string_view().length()) }; - + view().on_insert_clipboard_entry = [](Web::Clipboard::SystemClipboardRepresentation const& entry, auto const&) { auto* mime_data = new QMimeData(); - mime_data->setData(qstring_from_ak_string(mime_type), qdata); + mime_data->setData(qstring_from_ak_string(entry.mime_type), qbytearray_from_ak_string(entry.data)); auto* clipboard = QGuiApplication::clipboard(); clipboard->setMimeData(mime_data); }; + view().on_request_clipboard_entries = [this](auto request_id) { + auto const* clipboard = QGuiApplication::clipboard(); + + auto const* mime_data = clipboard->mimeData(); + if (!mime_data) { + view().retrieved_clipboard_entries(request_id, {}); + return; + } + + Vector items; + Vector representations; + + for (auto const& format : mime_data->formats()) { + auto data = ak_byte_string_from_qbytearray(mime_data->data(format)); + auto mime_type = ak_string_from_qstring(format); + + representations.empend(AK::move(data), AK::move(mime_type)); + } + + if (!representations.is_empty()) + items.empend(AK::move(representations)); + + view().retrieved_clipboard_entries(request_id, items); + }; + view().on_audio_play_state_changed = [this](auto play_state) { emit audio_play_state_changed(tab_index(), play_state); };