From 108521a5669baceb1ea82f5c476d8be5f5f2dc2e Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sun, 25 Feb 2024 13:02:47 -0500 Subject: [PATCH] LibWeb+LibWebView+WebContent: Implement more behavior We had previous implemented some plumbing for file input elements in commit 636602a54e55945f7394805975a69dc40934331c. This implements the return path for chromes to inform WebContent of the file(s) the user selected. This patch includes a dummy implementation for headless-browser to enable testing. --- .../Tools/CodeGenerators/IPCCompiler/main.cpp | 2 +- .../Userland/Libraries/LibWeb/HTML/BUILD.gn | 1 + Tests/LibWeb/Text/expected/input-file.txt | 7 ++ Tests/LibWeb/Text/input/input-file.html | 32 +++++++++ Userland/Libraries/LibWeb/CMakeLists.txt | 1 + Userland/Libraries/LibWeb/Forward.h | 2 + .../LibWeb/HTML/HTMLInputElement.cpp | 53 +++++++++++++- .../Libraries/LibWeb/HTML/HTMLInputElement.h | 2 + .../Libraries/LibWeb/HTML/SelectedFile.cpp | 70 +++++++++++++++++++ Userland/Libraries/LibWeb/HTML/SelectedFile.h | 48 +++++++++++++ Userland/Libraries/LibWeb/Page/Page.cpp | 25 +++++++ Userland/Libraries/LibWeb/Page/Page.h | 6 +- .../LibWebView/ViewImplementation.cpp | 5 ++ .../Libraries/LibWebView/ViewImplementation.h | 2 + .../Libraries/LibWebView/WebContentClient.cpp | 13 ++++ .../Libraries/LibWebView/WebContentClient.h | 1 + .../WebContent/ConnectionFromClient.cpp | 14 ++++ .../WebContent/ConnectionFromClient.h | 1 + Userland/Services/WebContent/PageClient.cpp | 5 ++ Userland/Services/WebContent/PageClient.h | 1 + .../Services/WebContent/WebContentClient.ipc | 2 + .../Services/WebContent/WebContentServer.ipc | 2 + Userland/Utilities/headless-browser.cpp | 17 +++++ 23 files changed, 307 insertions(+), 5 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/input-file.txt create mode 100644 Tests/LibWeb/Text/input/input-file.html create mode 100644 Userland/Libraries/LibWeb/HTML/SelectedFile.cpp create mode 100644 Userland/Libraries/LibWeb/HTML/SelectedFile.h diff --git a/Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp b/Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp index 88b8a2003b..b3f6734624 100644 --- a/Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/IPCCompiler/main.cpp @@ -70,7 +70,7 @@ static bool is_primitive_type(ByteString const& type) static bool is_simple_type(ByteString const& type) { // Small types that it makes sense just to pass by value. - return type.is_one_of("Gfx::Color", "Web::DevicePixels", "Gfx::IntPoint", "Gfx::FloatPoint", "Web::DevicePixelPoint", "Gfx::IntSize", "Gfx::FloatSize", "Web::DevicePixelSize", "Core::File::OpenMode", "Web::Cookie::Source"); + return type.is_one_of("Gfx::Color", "Web::DevicePixels", "Gfx::IntPoint", "Gfx::FloatPoint", "Web::DevicePixelPoint", "Gfx::IntSize", "Gfx::FloatSize", "Web::DevicePixelSize", "Core::File::OpenMode", "Web::Cookie::Source", "Web::HTML::AllowMultipleFiles"); } static bool is_primitive_or_simple_type(ByteString const& type) diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn index 920adbabcf..84c30ce754 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn @@ -140,6 +140,7 @@ source_set("HTML") { "PotentialCORSRequest.cpp", "PromiseRejectionEvent.cpp", "SelectItem.cpp", + "SelectedFile.cpp", "SessionHistoryEntry.cpp", "SharedImageRequest.cpp", "SourceSet.cpp", diff --git a/Tests/LibWeb/Text/expected/input-file.txt b/Tests/LibWeb/Text/expected/input-file.txt new file mode 100644 index 0000000000..6e3a94c410 --- /dev/null +++ b/Tests/LibWeb/Text/expected/input-file.txt @@ -0,0 +1,7 @@ +Select file...file1 Select files...4 files selected. input1: +file1: Contents for file1 +input2: +file1: Contents for file1 +file2: Contents for file2 +file3: Contents for file3 +file4: Contents for file4 diff --git a/Tests/LibWeb/Text/input/input-file.html b/Tests/LibWeb/Text/input/input-file.html new file mode 100644 index 0000000000..0ee499460a --- /dev/null +++ b/Tests/LibWeb/Text/input/input-file.html @@ -0,0 +1,32 @@ + + + + diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index e9b4b480fb..b9d51888b1 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -389,6 +389,7 @@ set(SOURCES HTML/Scripting/TemporaryExecutionContext.cpp HTML/Scripting/WindowEnvironmentSettingsObject.cpp HTML/Scripting/WorkerEnvironmentSettingsObject.cpp + HTML/SelectedFile.cpp HTML/SelectItem.cpp HTML/SessionHistoryEntry.cpp HTML/SharedImageRequest.cpp diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 4adba1b4ea..0406287bd1 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -445,6 +445,7 @@ class Path2D; class Plugin; class PluginArray; class PromiseRejectionEvent; +class SelectedFile; class SharedImageRequest; class Storage; class SubmitEvent; @@ -468,6 +469,7 @@ class WorkerGlobalScope; class WorkerLocation; class WorkerNavigator; +enum class AllowMultipleFiles; enum class MediaSeekMode; enum class SandboxingFlagSet; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 51735c72c5..c0902bb29a 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -213,12 +215,12 @@ static void show_the_picker_if_applicable(HTMLInputElement& element) // with the bubbles attribute initialized to true. // 5. Otherwise, update the file selection for element. - bool const multiple = element.has_attribute(HTML::AttributeNames::multiple); - auto weak_element = element.make_weak_ptr(); + auto allow_multiple_files = element.has_attribute(HTML::AttributeNames::multiple) ? AllowMultipleFiles::Yes : AllowMultipleFiles::No; + auto weak_element = element.make_weak_ptr(); // FIXME: Pass along accept attribute information https://html.spec.whatwg.org/multipage/input.html#attr-input-accept // The accept attribute may be specified to provide user agents with a hint of what file types will be accepted. - element.document().browsing_context()->top_level_browsing_context()->page().client().page_did_request_file_picker(weak_element, multiple); + element.document().browsing_context()->top_level_browsing_context()->page().did_request_file_picker(weak_element, allow_multiple_files); return; } @@ -380,6 +382,51 @@ void HTMLInputElement::did_pick_color(Optional picked_color) } } +void HTMLInputElement::did_select_files(Span selected_files) +{ + // https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable + // 4. If the user dismissed the prompt without changing their selection, then queue an element task on the user + // interaction task source given element to fire an event named cancel at element, with the bubbles attribute + // initialized to true. + if (selected_files.is_empty()) { + queue_an_element_task(HTML::Task::Source::UserInteraction, [this]() { + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::cancel, { .bubbles = true })); + }); + + return; + } + + Vector> files; + files.ensure_capacity(selected_files.size()); + + for (auto& selected_file : selected_files) { + auto contents = selected_file.take_contents(); + + auto mime_type = MUST(MimeSniff::Resource::sniff(contents)); + auto blob = FileAPI::Blob::create(realm(), move(contents), mime_type.essence()); + + // FIXME: The FileAPI should use ByteString for file names. + auto file_name = MUST(String::from_byte_string(selected_file.name())); + + auto file = MUST(FileAPI::File::create(realm(), { JS::make_handle(blob) }, file_name)); + files.unchecked_append(file); + } + + // https://html.spec.whatwg.org/multipage/input.html#update-the-file-selection + // 1. Queue an element task on the user interaction task source given element and the following steps: + queue_an_element_task(HTML::Task::Source::UserInteraction, [this, files = move(files)]() mutable { + // 1. Update element's selected files so that it represents the user's selection. + m_selected_files = FileAPI::FileList::create(realm(), move(files)); + update_file_input_shadow_tree(); + + // 2. Fire an event named input at the input element, with the bubbles and composed attributes initialized to true. + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::input, { .bubbles = true, .composed = true })); + + // 3. Fire an event named change at the input element, with the bubbles attribute initialized to true. + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::change, { .bubbles = true })); + }); +} + String HTMLInputElement::value() const { switch (value_attribute_mode()) { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index 95c287e7d8..278d5d1532 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -94,6 +94,8 @@ public: void did_pick_color(Optional picked_color); + void did_select_files(Span selected_files); + JS::GCPtr files(); void set_files(JS::GCPtr); diff --git a/Userland/Libraries/LibWeb/HTML/SelectedFile.cpp b/Userland/Libraries/LibWeb/HTML/SelectedFile.cpp new file mode 100644 index 0000000000..be31a4e1b3 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/SelectedFile.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Web::HTML { + +ErrorOr SelectedFile::from_file_path(ByteString const& file_path) +{ + // https://html.spec.whatwg.org/multipage/input.html#file-upload-state-(type=file):concept-input-file-path + // Filenames must not contain path components, even in the case that a user has selected an entire directory + // hierarchy or multiple files with the same name from different directories. + auto name = LexicalPath::basename(file_path); + + auto file = TRY(Core::File::open(file_path, Core::File::OpenMode::Read)); + return SelectedFile { move(name), IPC::File { *file } }; +} + +SelectedFile::SelectedFile(ByteString name, ByteBuffer contents) + : m_name(move(name)) + , m_file_or_contents(move(contents)) +{ +} + +SelectedFile::SelectedFile(ByteString name, IPC::File file) + : m_name(move(name)) + , m_file_or_contents(move(file)) +{ +} + +ByteBuffer SelectedFile::take_contents() +{ + VERIFY(m_file_or_contents.has()); + return move(m_file_or_contents.get()); +} + +} + +template<> +ErrorOr IPC::encode(Encoder& encoder, Web::HTML::SelectedFile const& file) +{ + TRY(encoder.encode(file.name())); + TRY(encoder.encode(file.file_or_contents())); + return {}; +} + +template<> +ErrorOr IPC::decode(Decoder& decoder) +{ + auto name = TRY(decoder.decode()); + auto file_or_contents = TRY((decoder.decode>())); + + ByteBuffer contents; + + if (file_or_contents.has()) { + auto file = TRY(Core::File::adopt_fd(file_or_contents.get().take_fd(), Core::File::OpenMode::Read)); + contents = TRY(file->read_until_eof()); + } else { + contents = move(file_or_contents.get()); + } + + return Web::HTML::SelectedFile { move(name), move(contents) }; +} diff --git a/Userland/Libraries/LibWeb/HTML/SelectedFile.h b/Userland/Libraries/LibWeb/HTML/SelectedFile.h new file mode 100644 index 0000000000..8d1739231d --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/SelectedFile.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web::HTML { + +enum class AllowMultipleFiles { + No, + Yes, +}; + +class SelectedFile { +public: + static ErrorOr from_file_path(ByteString const& file_path); + + SelectedFile(ByteString name, ByteBuffer contents); + SelectedFile(ByteString name, IPC::File file); + + ByteString const& name() const { return m_name; } + auto const& file_or_contents() const { return m_file_or_contents; } + ByteBuffer take_contents(); + +private: + ByteString m_name; + Variant m_file_or_contents; +}; + +} + +namespace IPC { + +template<> +ErrorOr encode(Encoder&, Web::HTML::SelectedFile const&); + +template<> +ErrorOr decode(Decoder&); + +} diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp index e6df976060..dfdce259f5 100644 --- a/Userland/Libraries/LibWeb/Page/Page.cpp +++ b/Userland/Libraries/LibWeb/Page/Page.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -343,6 +344,30 @@ void Page::color_picker_update(Optional picked_color, HTML::ColorPickerUp } } +void Page::did_request_file_picker(WeakPtr target, HTML::AllowMultipleFiles allow_multiple_files) +{ + if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) { + m_pending_non_blocking_dialog = PendingNonBlockingDialog::FilePicker; + m_pending_non_blocking_dialog_target = move(target); + + m_client->page_did_request_file_picker(allow_multiple_files); + } +} + +void Page::file_picker_closed(Span selected_files) +{ + if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::FilePicker) { + m_pending_non_blocking_dialog = PendingNonBlockingDialog::None; + + if (m_pending_non_blocking_dialog_target) { + auto& input_element = verify_cast(*m_pending_non_blocking_dialog_target); + input_element.did_select_files(selected_files); + + m_pending_non_blocking_dialog_target.clear(); + } + } +} + void Page::did_request_select_dropdown(WeakPtr target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items) { if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) { diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h index f1e360c70a..595a29ea8e 100644 --- a/Userland/Libraries/LibWeb/Page/Page.h +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -134,12 +134,16 @@ public: void did_request_color_picker(WeakPtr target, Color current_color); void color_picker_update(Optional picked_color, HTML::ColorPickerUpdateState state); + void did_request_file_picker(WeakPtr target, HTML::AllowMultipleFiles); + void file_picker_closed(Span selected_files); + void did_request_select_dropdown(WeakPtr target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items); void select_dropdown_closed(Optional value); enum class PendingNonBlockingDialog { None, ColorPicker, + FilePicker, Select, }; @@ -280,8 +284,8 @@ public: virtual void request_file(FileRequest) = 0; // https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable - virtual void page_did_request_file_picker(WeakPtr, [[maybe_unused]] bool multiple) {}; virtual void page_did_request_color_picker([[maybe_unused]] Color current_color) {}; + virtual void page_did_request_file_picker(Web::HTML::AllowMultipleFiles) {}; virtual void page_did_request_select_dropdown([[maybe_unused]] Web::CSSPixelPoint content_position, [[maybe_unused]] Web::CSSPixels minimum_width, [[maybe_unused]] Vector items) {}; virtual void page_did_finish_text_test() {}; diff --git a/Userland/Libraries/LibWebView/ViewImplementation.cpp b/Userland/Libraries/LibWebView/ViewImplementation.cpp index 2b5d5e61cd..833abb4509 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.cpp +++ b/Userland/Libraries/LibWebView/ViewImplementation.cpp @@ -250,6 +250,11 @@ void ViewImplementation::color_picker_update(Optional picked_color, Web:: client().async_color_picker_update(page_id(), picked_color, state); } +void ViewImplementation::file_picker_closed(Vector selected_files) +{ + client().async_file_picker_closed(page_id(), move(selected_files)); +} + void ViewImplementation::select_dropdown_closed(Optional value) { client().async_select_dropdown_closed(page_id(), value); diff --git a/Userland/Libraries/LibWebView/ViewImplementation.h b/Userland/Libraries/LibWebView/ViewImplementation.h index f1c107c679..805728ccd9 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.h +++ b/Userland/Libraries/LibWebView/ViewImplementation.h @@ -86,6 +86,7 @@ public: void confirm_closed(bool accepted); void prompt_closed(Optional response); void color_picker_update(Optional picked_color, Web::HTML::ColorPickerUpdateState state); + void file_picker_closed(Vector selected_files); void select_dropdown_closed(Optional value); void toggle_media_play_state(); @@ -164,6 +165,7 @@ public: Function on_minimize_window; Function on_fullscreen_window; Function on_request_color_picker; + Function on_request_file_picker; Function items)> on_request_select_dropdown; Function on_finish_handling_input_event; Function on_text_test_finish; diff --git a/Userland/Libraries/LibWebView/WebContentClient.cpp b/Userland/Libraries/LibWebView/WebContentClient.cpp index 21875ce0b0..1f423eb833 100644 --- a/Userland/Libraries/LibWebView/WebContentClient.cpp +++ b/Userland/Libraries/LibWebView/WebContentClient.cpp @@ -806,6 +806,19 @@ void WebContentClient::did_request_color_picker(u64 page_id, Color const& curren view.on_request_color_picker(current_color); } +void WebContentClient::did_request_file_picker(u64 page_id, Web::HTML::AllowMultipleFiles allow_multiple_files) +{ + auto maybe_view = m_views.get(page_id); + if (!maybe_view.has_value()) { + dbgln("Received request file picker for unknown page ID {}", page_id); + return; + } + auto& view = *maybe_view.value(); + + if (view.on_request_file_picker) + view.on_request_file_picker(allow_multiple_files); +} + void WebContentClient::did_request_select_dropdown(u64 page_id, Gfx::IntPoint content_position, i32 minimum_width, Vector const& items) { auto maybe_view = m_views.get(page_id); diff --git a/Userland/Libraries/LibWebView/WebContentClient.h b/Userland/Libraries/LibWebView/WebContentClient.h index 85af653e7d..f55b1f5124 100644 --- a/Userland/Libraries/LibWebView/WebContentClient.h +++ b/Userland/Libraries/LibWebView/WebContentClient.h @@ -89,6 +89,7 @@ private: virtual Messages::WebContentClient::DidRequestFullscreenWindowResponse did_request_fullscreen_window(u64 page_id) override; virtual void did_request_file(u64 page_id, ByteString const& path, i32) override; virtual void did_request_color_picker(u64 page_id, Color const& current_color) override; + virtual void did_request_file_picker(u64 page_id, Web::HTML::AllowMultipleFiles) override; virtual void did_request_select_dropdown(u64 page_id, Gfx::IntPoint content_position, i32 minimum_width, Vector const& items) override; virtual void did_finish_handling_input_event(u64 page_id, bool event_was_accepted) override; virtual void did_finish_text_test(u64 page_id) override; diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index cbcaf800f0..585a58e02c 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -1317,6 +1319,18 @@ void ConnectionFromClient::color_picker_update(u64 page_id, Optional cons page.page().color_picker_update(picked_color, state); } +void ConnectionFromClient::file_picker_closed(u64 page_id, Vector const& selected_files) +{ + auto maybe_page = page(page_id); + if (!maybe_page.has_value()) { + dbgln("ConnectionFromClient::color_picker_update: No page with ID {}", page_id); + return; + } + + auto& page = maybe_page.release_value(); + page.page().file_picker_closed(const_cast&>(selected_files)); +} + void ConnectionFromClient::select_dropdown_closed(u64 page_id, Optional const& value) { auto maybe_page = page(page_id); diff --git a/Userland/Services/WebContent/ConnectionFromClient.h b/Userland/Services/WebContent/ConnectionFromClient.h index 5dd491c47f..6e3159c9d8 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.h +++ b/Userland/Services/WebContent/ConnectionFromClient.h @@ -106,6 +106,7 @@ private: virtual void confirm_closed(u64 page_id, bool accepted) override; virtual void prompt_closed(u64 page_id, Optional const& response) override; virtual void color_picker_update(u64 page_id, Optional const& picked_color, Web::HTML::ColorPickerUpdateState const& state) override; + virtual void file_picker_closed(u64 page_id, Vector const& selected_files) override; virtual void select_dropdown_closed(u64 page_id, Optional const& value) override; virtual void toggle_media_play_state(u64 page_id) override; diff --git a/Userland/Services/WebContent/PageClient.cpp b/Userland/Services/WebContent/PageClient.cpp index 73dc906753..3d66bf2fdf 100644 --- a/Userland/Services/WebContent/PageClient.cpp +++ b/Userland/Services/WebContent/PageClient.cpp @@ -549,6 +549,11 @@ void PageClient::page_did_request_color_picker(Color current_color) client().async_did_request_color_picker(m_id, current_color); } +void PageClient::page_did_request_file_picker(Web::HTML::AllowMultipleFiles allow_multiple_files) +{ + client().async_did_request_file_picker(m_id, allow_multiple_files); +} + void PageClient::page_did_request_select_dropdown(Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items) { client().async_did_request_select_dropdown(m_id, page().css_to_device_point(content_position).to_type(), minimum_width * device_pixels_per_css_pixel(), items); diff --git a/Userland/Services/WebContent/PageClient.h b/Userland/Services/WebContent/PageClient.h index fb40371dd3..8936cfdf23 100644 --- a/Userland/Services/WebContent/PageClient.h +++ b/Userland/Services/WebContent/PageClient.h @@ -133,6 +133,7 @@ private: virtual void page_did_close_top_level_traversable() override; virtual void request_file(Web::FileRequest) override; virtual void page_did_request_color_picker(Color current_color) override; + virtual void page_did_request_file_picker(Web::HTML::AllowMultipleFiles) override; virtual void page_did_request_select_dropdown(Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items) override; virtual void page_did_finish_text_test() override; virtual void page_did_change_theme_color(Gfx::Color color) override; diff --git a/Userland/Services/WebContent/WebContentClient.ipc b/Userland/Services/WebContent/WebContentClient.ipc index 57b8729a56..0abce74f54 100644 --- a/Userland/Services/WebContent/WebContentClient.ipc +++ b/Userland/Services/WebContent/WebContentClient.ipc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,7 @@ endpoint WebContentClient did_request_fullscreen_window(u64 page_id) => (Gfx::IntRect window_rect) did_request_file(u64 page_id, ByteString path, i32 request_id) =| did_request_color_picker(u64 page_id, Color current_color) =| + did_request_file_picker(u64 page_id, Web::HTML::AllowMultipleFiles allow_multiple_files) =| did_request_select_dropdown(u64 page_id, Gfx::IntPoint content_position, i32 minimum_width, Vector items) =| did_finish_handling_input_event(u64 page_id, bool event_was_accepted) =| did_change_theme_color(u64 page_id, Gfx::Color color) =| diff --git a/Userland/Services/WebContent/WebContentServer.ipc b/Userland/Services/WebContent/WebContentServer.ipc index 7db19cae92..f16e836254 100644 --- a/Userland/Services/WebContent/WebContentServer.ipc +++ b/Userland/Services/WebContent/WebContentServer.ipc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,7 @@ endpoint WebContentServer confirm_closed(u64 page_id, bool accepted) =| prompt_closed(u64 page_id, Optional response) =| color_picker_update(u64 page_id, Optional picked_color, Web::HTML::ColorPickerUpdateState state) =| + file_picker_closed(u64 page_id, Vector selected_files) =| select_dropdown_closed(u64 page_id, Optional value) =| toggle_media_play_state(u64 page_id) =| diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index 5990263444..433e53aafc 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -433,6 +435,21 @@ static ErrorOr run_test(HeadlessWebContentView& view, StringView inp promise->resolve({}); }; view.on_text_test_finish = {}; + + view.on_request_file_picker = [&](auto allow_multiple_files) { + // Create some dummy files for tests. + Vector selected_files; + selected_files.empend("file1"sv, MUST(ByteBuffer::copy("Contents for file1"sv.bytes()))); + + if (allow_multiple_files == Web::HTML::AllowMultipleFiles::Yes) { + selected_files.empend("file2"sv, MUST(ByteBuffer::copy("Contents for file2"sv.bytes()))); + selected_files.empend("file3"sv, MUST(ByteBuffer::copy("Contents for file3"sv.bytes()))); + selected_files.empend("file4"sv, MUST(ByteBuffer::copy("Contents for file4"sv.bytes()))); + } + + view.file_picker_closed(move(selected_files)); + }; + view.load(URL("about:blank"sv)); MUST(promise->await());