mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 17:27:35 +00:00
Ladybird+LibWeb+WebContent: Parse the <input type=file> accept attribute
This parses the accept attribute value for file input types and passes it along to the browser chromes.
This commit is contained in:
parent
e4213f5767
commit
6760d236e4
20 changed files with 267 additions and 24 deletions
|
@ -268,6 +268,7 @@ set(SOURCES
|
|||
HTML/EventLoop/Task.cpp
|
||||
HTML/EventLoop/TaskQueue.cpp
|
||||
HTML/EventNames.cpp
|
||||
HTML/FileFilter.cpp
|
||||
HTML/Focus.cpp
|
||||
HTML/FormAssociatedElement.cpp
|
||||
HTML/FormControlInfrastructure.cpp
|
||||
|
|
61
Userland/Libraries/LibWeb/HTML/FileFilter.cpp
Normal file
61
Userland/Libraries/LibWeb/HTML/FileFilter.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <LibWeb/HTML/FileFilter.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
void FileFilter::add_filter(FilterType filter)
|
||||
{
|
||||
if (!filters.contains_slow(filter))
|
||||
filters.append(move(filter));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::FileFilter::MimeType const& mime_type)
|
||||
{
|
||||
TRY(encoder.encode(mime_type.value));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::FileFilter::MimeType> IPC::decode(Decoder& decoder)
|
||||
{
|
||||
auto value = TRY(decoder.decode<String>());
|
||||
return Web::HTML::FileFilter::MimeType { move(value) };
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::FileFilter::Extension const& extension)
|
||||
{
|
||||
TRY(encoder.encode(extension.value));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::FileFilter::Extension> IPC::decode(Decoder& decoder)
|
||||
{
|
||||
auto value = TRY(decoder.decode<String>());
|
||||
return Web::HTML::FileFilter::Extension { move(value) };
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<void> IPC::encode(Encoder& encoder, Web::HTML::FileFilter const& filter)
|
||||
{
|
||||
TRY(encoder.encode(filter.filters));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::FileFilter> IPC::decode(Decoder& decoder)
|
||||
{
|
||||
auto filters = TRY(decoder.decode<Vector<Web::HTML::FileFilter::FilterType>>());
|
||||
return Web::HTML::FileFilter { move(filters) };
|
||||
}
|
62
Userland/Libraries/LibWeb/HTML/FileFilter.h
Normal file
62
Userland/Libraries/LibWeb/HTML/FileFilter.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
struct FileFilter {
|
||||
enum class FileType {
|
||||
Audio,
|
||||
Image,
|
||||
Video,
|
||||
};
|
||||
|
||||
struct MimeType {
|
||||
bool operator==(MimeType const&) const = default;
|
||||
String value;
|
||||
};
|
||||
|
||||
struct Extension {
|
||||
bool operator==(Extension const&) const = default;
|
||||
String value;
|
||||
};
|
||||
|
||||
using FilterType = Variant<FileType, MimeType, Extension>;
|
||||
|
||||
void add_filter(FilterType);
|
||||
|
||||
Vector<FilterType> filters;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, Web::HTML::FileFilter::MimeType const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::FileFilter::MimeType> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, Web::HTML::FileFilter::Extension const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::FileFilter::Extension> decode(Decoder&);
|
||||
|
||||
template<>
|
||||
ErrorOr<void> encode(Encoder&, Web::HTML::FileFilter const&);
|
||||
|
||||
template<>
|
||||
ErrorOr<Web::HTML::FileFilter> decode(Decoder&);
|
||||
|
||||
}
|
|
@ -39,6 +39,7 @@
|
|||
#include <LibWeb/Layout/CheckBox.h>
|
||||
#include <LibWeb/Layout/ImageBox.h>
|
||||
#include <LibWeb/Layout/RadioButton.h>
|
||||
#include <LibWeb/MimeSniff/MimeType.h>
|
||||
#include <LibWeb/MimeSniff/Resource.h>
|
||||
#include <LibWeb/Namespace.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
@ -181,6 +182,43 @@ void HTMLInputElement::set_files(JS::GCPtr<FileAPI::FileList> files)
|
|||
m_selected_files = files;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/input.html#attr-input-accept
|
||||
FileFilter HTMLInputElement::parse_accept_attribute() const
|
||||
{
|
||||
FileFilter filter;
|
||||
|
||||
// If specified, the attribute must consist of a set of comma-separated tokens, each of which must be an ASCII
|
||||
// case-insensitive match for one of the following:
|
||||
get_attribute_value(HTML::AttributeNames::accept).bytes_as_string_view().for_each_split_view(',', SplitBehavior::Nothing, [&](StringView value) {
|
||||
// The string "audio/*"
|
||||
// Indicates that sound files are accepted.
|
||||
if (value.equals_ignoring_ascii_case("audio/*"sv))
|
||||
filter.add_filter(FileFilter::FileType::Audio);
|
||||
|
||||
// The string "video/*"
|
||||
// Indicates that video files are accepted.
|
||||
if (value.equals_ignoring_ascii_case("video/*"sv))
|
||||
filter.add_filter(FileFilter::FileType::Video);
|
||||
|
||||
// The string "image/*"
|
||||
// Indicates that image files are accepted.
|
||||
if (value.equals_ignoring_ascii_case("image/*"sv))
|
||||
filter.add_filter(FileFilter::FileType::Image);
|
||||
|
||||
// A valid MIME type string with no parameters
|
||||
// Indicates that files of the specified type are accepted.
|
||||
else if (auto mime_type = MUST(MimeSniff::MimeType::parse(value)); mime_type.has_value() && mime_type->parameters().is_empty())
|
||||
filter.add_filter(FileFilter::MimeType { mime_type->essence() });
|
||||
|
||||
// A string whose first character is a U+002E FULL STOP character (.)
|
||||
// Indicates that files with the specified file extension are accepted.
|
||||
else if (value.starts_with('.'))
|
||||
filter.add_filter(FileFilter::Extension { MUST(String::from_utf8(value.substring_view(1))) });
|
||||
});
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/input.html#update-the-file-selection
|
||||
void HTMLInputElement::update_the_file_selection(JS::NonnullGCPtr<FileAPI::FileList> files)
|
||||
{
|
||||
|
@ -227,12 +265,11 @@ static void show_the_picker_if_applicable(HTMLInputElement& element)
|
|||
// with the bubbles attribute initialized to true.
|
||||
// 5. Otherwise, update the file selection for element.
|
||||
|
||||
auto accepted_file_types = element.parse_accept_attribute();
|
||||
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().did_request_file_picker(weak_element, allow_multiple_files);
|
||||
element.document().browsing_context()->top_level_browsing_context()->page().did_request_file_picker(weak_element, move(accepted_file_types), allow_multiple_files);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/FileAPI/FileList.h>
|
||||
#include <LibWeb/HTML/ColorPickerUpdateState.h>
|
||||
#include <LibWeb/HTML/FileFilter.h>
|
||||
#include <LibWeb/HTML/FormAssociatedElement.h>
|
||||
#include <LibWeb/HTML/HTMLElement.h>
|
||||
#include <LibWeb/Layout/ImageProvider.h>
|
||||
|
@ -102,6 +103,8 @@ public:
|
|||
JS::GCPtr<FileAPI::FileList> files();
|
||||
void set_files(JS::GCPtr<FileAPI::FileList>);
|
||||
|
||||
FileFilter parse_accept_attribute() const;
|
||||
|
||||
// NOTE: User interaction
|
||||
// https://html.spec.whatwg.org/multipage/input.html#update-the-file-selection
|
||||
void update_the_file_selection(JS::NonnullGCPtr<FileAPI::FileList>);
|
||||
|
|
|
@ -344,13 +344,13 @@ 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)
|
||||
void Page::did_request_file_picker(WeakPtr<HTML::HTMLInputElement> target, HTML::FileFilter accepted_file_types, 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);
|
||||
m_client->page_did_request_file_picker(move(accepted_file_types), allow_multiple_files);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/ActivateTab.h>
|
||||
#include <LibWeb/HTML/ColorPickerUpdateState.h>
|
||||
#include <LibWeb/HTML/FileFilter.h>
|
||||
#include <LibWeb/HTML/SelectItem.h>
|
||||
#include <LibWeb/HTML/TokenizedFeatures.h>
|
||||
#include <LibWeb/HTML/WebViewHints.h>
|
||||
|
@ -134,7 +135,7 @@ 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 did_request_file_picker(WeakPtr<HTML::HTMLInputElement> target, HTML::FileFilter accepted_file_types, 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);
|
||||
|
@ -285,7 +286,7 @@ public:
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable
|
||||
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_file_picker([[maybe_unused]] HTML::FileFilter accepted_file_types, 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() {};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue