mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
LibWeb+LibWebView+WebContent: Implement more <input type=file> behavior
We had previous implemented some plumbing for file input elements in
commit 636602a54e
.
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.
This commit is contained in:
parent
435c2c24d1
commit
108521a566
Notes:
sideshowbarker
2024-07-17 04:10:16 +09:00
Author: https://github.com/trflynn89 Commit: https://github.com/SerenityOS/serenity/commit/108521a566 Pull-request: https://github.com/SerenityOS/serenity/pull/23346
23 changed files with 307 additions and 5 deletions
|
@ -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)
|
||||
|
|
|
@ -140,6 +140,7 @@ source_set("HTML") {
|
|||
"PotentialCORSRequest.cpp",
|
||||
"PromiseRejectionEvent.cpp",
|
||||
"SelectItem.cpp",
|
||||
"SelectedFile.cpp",
|
||||
"SessionHistoryEntry.cpp",
|
||||
"SharedImageRequest.cpp",
|
||||
"SourceSet.cpp",
|
||||
|
|
7
Tests/LibWeb/Text/expected/input-file.txt
Normal file
7
Tests/LibWeb/Text/expected/input-file.txt
Normal file
|
@ -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
|
32
Tests/LibWeb/Text/input/input-file.html
Normal file
32
Tests/LibWeb/Text/input/input-file.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<input id="input1" type="file" />
|
||||
<input id="input2" type="file" multiple />
|
||||
<script src="./include.js"></script>
|
||||
<script type="text/javascript">
|
||||
const runTest = async id => {
|
||||
let input = document.getElementById(id);
|
||||
|
||||
return new Promise(resolve => {
|
||||
input.addEventListener("input", async () => {
|
||||
println(`${id}:`);
|
||||
|
||||
for (let i = 0; i < input.files.length; ++i) {
|
||||
const file = input.files.item(i);
|
||||
const text = await file.text();
|
||||
|
||||
println(`${file.name}: ${text}`);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
|
||||
internals.dispatchUserActivatedEvent(input, new Event("mousedown"));
|
||||
input.showPicker();
|
||||
});
|
||||
};
|
||||
|
||||
asyncTest(async done => {
|
||||
await runTest("input1");
|
||||
await runTest("input2");
|
||||
done();
|
||||
});
|
||||
</script>
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <LibWeb/HTML/Numbers.h>
|
||||
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
#include <LibWeb/HTML/SharedImageRequest.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
|
@ -37,6 +38,7 @@
|
|||
#include <LibWeb/Layout/CheckBox.h>
|
||||
#include <LibWeb/Layout/ImageBox.h>
|
||||
#include <LibWeb/Layout/RadioButton.h>
|
||||
#include <LibWeb/MimeSniff/Resource.h>
|
||||
#include <LibWeb/Namespace.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
#include <LibWeb/UIEvents/EventNames.h>
|
||||
|
@ -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<DOM::EventTarget>();
|
||||
auto allow_multiple_files = element.has_attribute(HTML::AttributeNames::multiple) ? AllowMultipleFiles::Yes : AllowMultipleFiles::No;
|
||||
auto weak_element = element.make_weak_ptr<HTMLInputElement>();
|
||||
|
||||
// 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<Color> picked_color)
|
|||
}
|
||||
}
|
||||
|
||||
void HTMLInputElement::did_select_files(Span<SelectedFile> 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<JS::NonnullGCPtr<FileAPI::File>> 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()) {
|
||||
|
|
|
@ -94,6 +94,8 @@ public:
|
|||
|
||||
void did_pick_color(Optional<Color> picked_color);
|
||||
|
||||
void did_select_files(Span<SelectedFile> selected_files);
|
||||
|
||||
JS::GCPtr<FileAPI::FileList> files();
|
||||
void set_files(JS::GCPtr<FileAPI::FileList>);
|
||||
|
||||
|
|
70
Userland/Libraries/LibWeb/HTML/SelectedFile.cpp
Normal file
70
Userland/Libraries/LibWeb/HTML/SelectedFile.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
ErrorOr<SelectedFile> 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<ByteBuffer>());
|
||||
return move(m_file_or_contents.get<ByteBuffer>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::SelectedFile const& file)
|
||||
{
|
||||
TRY(encoder.encode(file.name()));
|
||||
TRY(encoder.encode(file.file_or_contents()));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::SelectedFile> IPC::decode(Decoder& decoder)
|
||||
{
|
||||
auto name = TRY(decoder.decode<ByteString>());
|
||||
auto file_or_contents = TRY((decoder.decode<Variant<IPC::File, ByteBuffer>>()));
|
||||
|
||||
ByteBuffer contents;
|
||||
|
||||
if (file_or_contents.has<IPC::File>()) {
|
||||
auto file = TRY(Core::File::adopt_fd(file_or_contents.get<IPC::File>().take_fd(), Core::File::OpenMode::Read));
|
||||
contents = TRY(file->read_until_eof());
|
||||
} else {
|
||||
contents = move(file_or_contents.get<ByteBuffer>());
|
||||
}
|
||||
|
||||
return Web::HTML::SelectedFile { move(name), move(contents) };
|
||||
}
|
48
Userland/Libraries/LibWeb/HTML/SelectedFile.h
Normal file
48
Userland/Libraries/LibWeb/HTML/SelectedFile.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibIPC/File.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
enum class AllowMultipleFiles {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
class SelectedFile {
|
||||
public:
|
||||
static ErrorOr<SelectedFile> 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<IPC::File, ByteBuffer> m_file_or_contents;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, Web::HTML::SelectedFile const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::SelectedFile> decode(Decoder&);
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
#include <LibWeb/HTML/HTMLSelectElement.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
|
@ -343,6 +344,30 @@ void Page::color_picker_update(Optional<Color> picked_color, HTML::ColorPickerUp
|
|||
}
|
||||
}
|
||||
|
||||
void Page::did_request_file_picker(WeakPtr<HTML::HTMLInputElement> 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<HTML::SelectedFile> 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<HTML::HTMLInputElement>(*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<HTML::HTMLSelectElement> target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items)
|
||||
{
|
||||
if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) {
|
||||
|
|
|
@ -134,12 +134,16 @@ public:
|
|||
void did_request_color_picker(WeakPtr<HTML::HTMLInputElement> target, Color current_color);
|
||||
void color_picker_update(Optional<Color> picked_color, HTML::ColorPickerUpdateState state);
|
||||
|
||||
void did_request_file_picker(WeakPtr<HTML::HTMLInputElement> target, HTML::AllowMultipleFiles);
|
||||
void file_picker_closed(Span<HTML::SelectedFile> selected_files);
|
||||
|
||||
void did_request_select_dropdown(WeakPtr<HTML::HTMLSelectElement> target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items);
|
||||
void select_dropdown_closed(Optional<String> 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<DOM::EventTarget>, [[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<Web::HTML::SelectItem> items) {};
|
||||
|
||||
virtual void page_did_finish_text_test() {};
|
||||
|
|
|
@ -250,6 +250,11 @@ void ViewImplementation::color_picker_update(Optional<Color> picked_color, Web::
|
|||
client().async_color_picker_update(page_id(), picked_color, state);
|
||||
}
|
||||
|
||||
void ViewImplementation::file_picker_closed(Vector<Web::HTML::SelectedFile> selected_files)
|
||||
{
|
||||
client().async_file_picker_closed(page_id(), move(selected_files));
|
||||
}
|
||||
|
||||
void ViewImplementation::select_dropdown_closed(Optional<String> value)
|
||||
{
|
||||
client().async_select_dropdown_closed(page_id(), value);
|
||||
|
|
|
@ -86,6 +86,7 @@ public:
|
|||
void confirm_closed(bool accepted);
|
||||
void prompt_closed(Optional<String> response);
|
||||
void color_picker_update(Optional<Color> picked_color, Web::HTML::ColorPickerUpdateState state);
|
||||
void file_picker_closed(Vector<Web::HTML::SelectedFile> selected_files);
|
||||
void select_dropdown_closed(Optional<String> value);
|
||||
|
||||
void toggle_media_play_state();
|
||||
|
@ -164,6 +165,7 @@ public:
|
|||
Function<Gfx::IntRect()> on_minimize_window;
|
||||
Function<Gfx::IntRect()> on_fullscreen_window;
|
||||
Function<void(Color current_color)> on_request_color_picker;
|
||||
Function<void(Web::HTML::AllowMultipleFiles)> on_request_file_picker;
|
||||
Function<void(Gfx::IntPoint content_position, i32 minimum_width, Vector<Web::HTML::SelectItem> items)> on_request_select_dropdown;
|
||||
Function<void(bool)> on_finish_handling_input_event;
|
||||
Function<void()> on_text_test_finish;
|
||||
|
|
|
@ -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<Web::HTML::SelectItem> const& items)
|
||||
{
|
||||
auto maybe_view = m_views.get(page_id);
|
||||
|
|
|
@ -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<Web::HTML::SelectItem> 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;
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/HTML/BrowsingContext.h>
|
||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
#include <LibWeb/HTML/Storage.h>
|
||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
@ -1317,6 +1319,18 @@ void ConnectionFromClient::color_picker_update(u64 page_id, Optional<Color> cons
|
|||
page.page().color_picker_update(picked_color, state);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::file_picker_closed(u64 page_id, Vector<Web::HTML::SelectedFile> 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<Vector<Web::HTML::SelectedFile>&>(selected_files));
|
||||
}
|
||||
|
||||
void ConnectionFromClient::select_dropdown_closed(u64 page_id, Optional<String> const& value)
|
||||
{
|
||||
auto maybe_page = page(page_id);
|
||||
|
|
|
@ -106,6 +106,7 @@ private:
|
|||
virtual void confirm_closed(u64 page_id, bool accepted) override;
|
||||
virtual void prompt_closed(u64 page_id, Optional<String> const& response) override;
|
||||
virtual void color_picker_update(u64 page_id, Optional<Color> const& picked_color, Web::HTML::ColorPickerUpdateState const& state) override;
|
||||
virtual void file_picker_closed(u64 page_id, Vector<Web::HTML::SelectedFile> const& selected_files) override;
|
||||
virtual void select_dropdown_closed(u64 page_id, Optional<String> const& value) override;
|
||||
|
||||
virtual void toggle_media_play_state(u64 page_id) override;
|
||||
|
|
|
@ -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<Web::HTML::SelectItem> items)
|
||||
{
|
||||
client().async_did_request_select_dropdown(m_id, page().css_to_device_point(content_position).to_type<int>(), minimum_width * device_pixels_per_css_pixel(), items);
|
||||
|
|
|
@ -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<Web::HTML::SelectItem> items) override;
|
||||
virtual void page_did_finish_text_test() override;
|
||||
virtual void page_did_change_theme_color(Gfx::Color color) override;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <LibWeb/Cookie/ParsedCookie.h>
|
||||
#include <LibWeb/CSS/Selector.h>
|
||||
#include <LibWeb/HTML/ActivateTab.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
#include <LibWeb/HTML/SelectItem.h>
|
||||
#include <LibWeb/HTML/WebViewHints.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
@ -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<Web::HTML::SelectItem> items) =|
|
||||
did_finish_handling_input_event(u64 page_id, bool event_was_accepted) =|
|
||||
did_change_theme_color(u64 page_id, Gfx::Color color) =|
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
#include <LibWeb/CSS/Selector.h>
|
||||
#include <LibWeb/HTML/ColorPickerUpdateState.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
#include <LibWeb/WebDriver/ExecuteScript.h>
|
||||
#include <LibWebView/Attribute.h>
|
||||
|
||||
|
@ -93,6 +94,7 @@ endpoint WebContentServer
|
|||
confirm_closed(u64 page_id, bool accepted) =|
|
||||
prompt_closed(u64 page_id, Optional<String> response) =|
|
||||
color_picker_update(u64 page_id, Optional<Color> picked_color, Web::HTML::ColorPickerUpdateState state) =|
|
||||
file_picker_closed(u64 page_id, Vector<Web::HTML::SelectedFile> selected_files) =|
|
||||
select_dropdown_closed(u64 page_id, Optional<String> value) =|
|
||||
|
||||
toggle_media_play_state(u64 page_id) =|
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/JsonObject.h>
|
||||
|
@ -44,6 +45,7 @@
|
|||
#include <LibWeb/Cookie/Cookie.h>
|
||||
#include <LibWeb/Cookie/ParsedCookie.h>
|
||||
#include <LibWeb/HTML/ActivateTab.h>
|
||||
#include <LibWeb/HTML/SelectedFile.h>
|
||||
#include <LibWeb/Worker/WebWorkerClient.h>
|
||||
#include <LibWebView/CookieJar.h>
|
||||
#include <LibWebView/Database.h>
|
||||
|
@ -433,6 +435,21 @@ static ErrorOr<TestResult> 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<Web::HTML::SelectedFile> 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());
|
||||
|
||||
|
|
Loading…
Reference in a new issue