mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 09:30:24 +00:00
LibWeb+LibWebView+WebContent: Add support for <input type="color">
This commit introduces 3 things: - Support for the color type in HTMLInputElement itself - A mechanism for handling non event loop blocking dialogs in Page - The associated plumbing up to ViewImplementation Frontends may add support for the color picker with the ViewImplementation.on_request_color_picker function
This commit is contained in:
parent
759ad905de
commit
2995a2e212
Notes:
sideshowbarker
2024-07-17 09:37:30 +09:00
Author: https://github.com/circl-lastname Commit: https://github.com/SerenityOS/serenity/commit/2995a2e212 Pull-request: https://github.com/SerenityOS/serenity/pull/20951 Reviewed-by: https://github.com/AtkinsSJ ✅ Reviewed-by: https://github.com/trflynn89
14 changed files with 110 additions and 4 deletions
|
@ -69,7 +69,7 @@ JS::GCPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::
|
|||
if (type_state() == TypeAttributeState::Hidden)
|
||||
return nullptr;
|
||||
|
||||
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::FileUpload)
|
||||
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::FileUpload || type_state() == TypeAttributeState::Color)
|
||||
return heap().allocate_without_realm<Layout::ButtonBox>(document(), *this, move(style));
|
||||
|
||||
if (type_state() == TypeAttributeState::Checkbox)
|
||||
|
@ -206,14 +206,16 @@ static void show_the_picker_if_applicable(HTMLInputElement& element)
|
|||
return;
|
||||
}
|
||||
|
||||
// FIXME: show "any relevant user interface" for other type attribute states "in the way [the user agent] normally would"
|
||||
|
||||
// 4. Otherwise, the user agent should show any relevant user interface for selecting a value for element,
|
||||
// in the way it normally would when the user interacts with the control. (If no such UI applies to element, then this step does nothing.)
|
||||
// If such a user interface is shown, it must respect the requirements stated in the relevant parts of the specification for how element
|
||||
// behaves given its type attribute state. (For example, various sections describe restrictions on the resulting value string.)
|
||||
// This step can have side effects, such as closing other pickers that were previously shown by this algorithm.
|
||||
// (If this closes a file selection picker, then per the above that will lead to firing either input and change events, or a cancel event.)
|
||||
if (element.type_state() == HTMLInputElement::TypeAttributeState::Color) {
|
||||
auto weak_element = element.make_weak_ptr<HTMLInputElement>();
|
||||
element.document().browsing_context()->top_level_browsing_context()->page()->did_request_color_picker(weak_element, Color::from_string(MUST(String::from_deprecated_string(element.value()))).value_or(Color(0, 0, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/input.html#dom-input-showpicker
|
||||
|
@ -276,7 +278,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::run_input_activation_behavior()
|
|||
|
||||
// 3. Submit the form owner from the element.
|
||||
TRY(form->submit_form(*this));
|
||||
} else if (type_state() == TypeAttributeState::FileUpload) {
|
||||
} else if (type_state() == TypeAttributeState::FileUpload || type_state() == TypeAttributeState::Color) {
|
||||
show_the_picker_if_applicable(*this);
|
||||
} else {
|
||||
dispatch_event(DOM::Event::create(realm(), EventNames::change));
|
||||
|
@ -303,6 +305,38 @@ void HTMLInputElement::did_edit_text_node(Badge<BrowsingContext>)
|
|||
});
|
||||
}
|
||||
|
||||
void HTMLInputElement::did_pick_color(Optional<Color> picked_color)
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
|
||||
// For input elements without a defined input activation behavior, but to which these events apply
|
||||
// and for which the user interface involves both interactive manipulation and an explicit commit action
|
||||
|
||||
if (type_state() == TypeAttributeState::Color && picked_color.has_value()) {
|
||||
// then when the user changes the element's value
|
||||
m_value = value_sanitization_algorithm(picked_color.value().to_deprecated_string_without_alpha());
|
||||
m_dirty_value = true;
|
||||
|
||||
// the user agent must queue an element task on the user interaction task source
|
||||
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
|
||||
// given the input element to fire an event named input at the input element, with the bubbles and composed attributes initialized to true
|
||||
auto input_event = DOM::Event::create(realm(), HTML::EventNames::input);
|
||||
input_event->set_bubbles(true);
|
||||
input_event->set_composed(true);
|
||||
dispatch_event(*input_event);
|
||||
});
|
||||
|
||||
// and any time the user commits the change, the user agent must queue an element task on the user interaction task source
|
||||
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
|
||||
// given the input element
|
||||
// FIXME: to set its user interacted to true
|
||||
// and fire an event named change at the input element, with the bubbles attribute initialized to true.
|
||||
auto change_event = DOM::Event::create(realm(), HTML::EventNames::change);
|
||||
change_event->set_bubbles(true);
|
||||
dispatch_event(*change_event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DeprecatedString HTMLInputElement::value() const
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/input.html#dom-input-value-filename
|
||||
|
@ -480,6 +514,7 @@ void HTMLInputElement::create_shadow_tree_if_needed()
|
|||
case TypeAttributeState::SubmitButton:
|
||||
case TypeAttributeState::ResetButton:
|
||||
case TypeAttributeState::ImageButton:
|
||||
case TypeAttributeState::Color:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -85,6 +85,8 @@ public:
|
|||
|
||||
bool is_mutable() const { return m_is_mutable; }
|
||||
|
||||
void did_pick_color(Optional<Color> picked_color);
|
||||
|
||||
JS::GCPtr<FileAPI::FileList> files();
|
||||
void set_files(JS::GCPtr<FileAPI::FileList>);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/HTML/BrowsingContext.h>
|
||||
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||
#include <LibWeb/HTML/HTMLMediaElement.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
|
@ -286,6 +287,28 @@ void Page::accept_dialog()
|
|||
}
|
||||
}
|
||||
|
||||
void Page::did_request_color_picker(WeakPtr<HTML::HTMLInputElement> target, Color current_color)
|
||||
{
|
||||
if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) {
|
||||
m_pending_non_blocking_dialog = PendingNonBlockingDialog::ColorPicker;
|
||||
m_pending_non_blocking_dialog_target = move(target);
|
||||
|
||||
m_client.page_did_request_color_picker(current_color);
|
||||
}
|
||||
}
|
||||
|
||||
void Page::color_picker_closed(Optional<Color> picked_color)
|
||||
{
|
||||
if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::ColorPicker) {
|
||||
m_pending_non_blocking_dialog = PendingNonBlockingDialog::None;
|
||||
|
||||
if (m_pending_non_blocking_dialog_target) {
|
||||
m_pending_non_blocking_dialog_target->did_pick_color(picked_color);
|
||||
m_pending_non_blocking_dialog_target.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Page::did_request_media_context_menu(i32 media_id, CSSPixelPoint position, DeprecatedString const& target, unsigned modifiers, MediaContextMenu menu)
|
||||
{
|
||||
m_media_context_menu_element_id = media_id;
|
||||
|
|
|
@ -120,6 +120,14 @@ public:
|
|||
void dismiss_dialog();
|
||||
void accept_dialog();
|
||||
|
||||
void did_request_color_picker(WeakPtr<HTML::HTMLInputElement> target, Color current_color);
|
||||
void color_picker_closed(Optional<Color> picked_color);
|
||||
|
||||
enum class PendingNonBlockingDialog {
|
||||
None,
|
||||
ColorPicker,
|
||||
};
|
||||
|
||||
struct MediaContextMenu {
|
||||
AK::URL media_url;
|
||||
bool is_video { false };
|
||||
|
@ -168,6 +176,9 @@ private:
|
|||
Optional<bool> m_pending_confirm_response;
|
||||
Optional<Optional<String>> m_pending_prompt_response;
|
||||
|
||||
PendingNonBlockingDialog m_pending_non_blocking_dialog { PendingNonBlockingDialog::None };
|
||||
WeakPtr<HTML::HTMLInputElement> m_pending_non_blocking_dialog_target;
|
||||
|
||||
Optional<int> m_media_context_menu_element_id;
|
||||
|
||||
Optional<String> m_user_style_sheet_source;
|
||||
|
@ -241,6 +252,7 @@ public:
|
|||
|
||||
// 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_finish_text_test() {};
|
||||
|
||||
|
|
|
@ -207,6 +207,11 @@ void ViewImplementation::prompt_closed(Optional<String> response)
|
|||
client().async_prompt_closed(move(response));
|
||||
}
|
||||
|
||||
void ViewImplementation::color_picker_closed(Optional<Color> picked_color)
|
||||
{
|
||||
client().async_color_picker_closed(picked_color);
|
||||
}
|
||||
|
||||
void ViewImplementation::toggle_media_play_state()
|
||||
{
|
||||
client().async_toggle_media_play_state();
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
void alert_closed();
|
||||
void confirm_closed(bool accepted);
|
||||
void prompt_closed(Optional<String> response);
|
||||
void color_picker_closed(Optional<Color> picked_color);
|
||||
|
||||
void toggle_media_play_state();
|
||||
void toggle_media_mute_state();
|
||||
|
@ -150,6 +151,7 @@ public:
|
|||
Function<Gfx::IntRect()> on_maximize_window;
|
||||
Function<Gfx::IntRect()> on_minimize_window;
|
||||
Function<Gfx::IntRect()> on_fullscreen_window;
|
||||
Function<void(Color current_color)> on_request_color_picker;
|
||||
Function<void(bool)> on_finish_handling_input_event;
|
||||
Function<void()> on_text_test_finish;
|
||||
|
||||
|
|
|
@ -384,6 +384,12 @@ void WebContentClient::did_request_file(DeprecatedString const& path, i32 reques
|
|||
m_view.on_request_file(path, request_id);
|
||||
}
|
||||
|
||||
void WebContentClient::did_request_color_picker(Color const& current_color)
|
||||
{
|
||||
if (m_view.on_request_color_picker)
|
||||
m_view.on_request_color_picker(current_color);
|
||||
}
|
||||
|
||||
void WebContentClient::did_finish_handling_input_event(bool event_was_accepted)
|
||||
{
|
||||
if (m_view.on_finish_handling_input_event)
|
||||
|
|
|
@ -82,6 +82,7 @@ private:
|
|||
virtual Messages::WebContentClient::DidRequestMinimizeWindowResponse did_request_minimize_window() override;
|
||||
virtual Messages::WebContentClient::DidRequestFullscreenWindowResponse did_request_fullscreen_window() override;
|
||||
virtual void did_request_file(DeprecatedString const& path, i32) override;
|
||||
virtual void did_request_color_picker(Color const& current_color) override;
|
||||
virtual void did_finish_handling_input_event(bool event_was_accepted) override;
|
||||
virtual void did_finish_text_test() override;
|
||||
|
||||
|
|
|
@ -892,6 +892,11 @@ void ConnectionFromClient::prompt_closed(Optional<String> const& response)
|
|||
m_page_host->prompt_closed(response);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::color_picker_closed(Optional<Color> const& picked_color)
|
||||
{
|
||||
m_page_host->color_picker_closed(picked_color);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::toggle_media_play_state()
|
||||
{
|
||||
m_page_host->toggle_media_play_state().release_value_but_fixme_should_propagate_errors();
|
||||
|
|
|
@ -97,6 +97,7 @@ private:
|
|||
virtual void alert_closed() override;
|
||||
virtual void confirm_closed(bool accepted) override;
|
||||
virtual void prompt_closed(Optional<String> const& response) override;
|
||||
virtual void color_picker_closed(Optional<Color> const& picked_color) override;
|
||||
|
||||
virtual void toggle_media_play_state() override;
|
||||
virtual void toggle_media_mute_state() override;
|
||||
|
|
|
@ -358,6 +358,11 @@ void PageHost::prompt_closed(Optional<String> response)
|
|||
page().prompt_closed(move(response));
|
||||
}
|
||||
|
||||
void PageHost::color_picker_closed(Optional<Color> picked_color)
|
||||
{
|
||||
page().color_picker_closed(picked_color);
|
||||
}
|
||||
|
||||
Web::WebIDL::ExceptionOr<void> PageHost::toggle_media_play_state()
|
||||
{
|
||||
return page().toggle_media_play_state();
|
||||
|
@ -453,4 +458,9 @@ void PageHost::request_file(Web::FileRequest file_request)
|
|||
m_client.request_file(move(file_request));
|
||||
}
|
||||
|
||||
void PageHost::page_did_request_color_picker(Color current_color)
|
||||
{
|
||||
m_client.async_did_request_color_picker(current_color);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
void alert_closed();
|
||||
void confirm_closed(bool accepted);
|
||||
void prompt_closed(Optional<String> response);
|
||||
void color_picker_closed(Optional<Color> picked_color);
|
||||
|
||||
Web::WebIDL::ExceptionOr<void> toggle_media_play_state();
|
||||
void toggle_media_mute_state();
|
||||
|
@ -113,6 +114,7 @@ private:
|
|||
virtual void page_did_request_activate_tab() override;
|
||||
virtual void page_did_close_browsing_context(Web::HTML::BrowsingContext const&) override;
|
||||
virtual void request_file(Web::FileRequest) override;
|
||||
virtual void page_did_request_color_picker(Color current_color) override;
|
||||
virtual void page_did_finish_text_test() override;
|
||||
|
||||
explicit PageHost(ConnectionFromClient&);
|
||||
|
|
|
@ -59,6 +59,7 @@ endpoint WebContentClient
|
|||
did_request_minimize_window() => (Gfx::IntRect window_rect)
|
||||
did_request_fullscreen_window() => (Gfx::IntRect window_rect)
|
||||
did_request_file(DeprecatedString path, i32 request_id) =|
|
||||
did_request_color_picker(Color current_color) =|
|
||||
did_finish_handling_input_event(bool event_was_accepted) =|
|
||||
|
||||
did_output_js_console_message(i32 message_index) =|
|
||||
|
|
|
@ -78,6 +78,7 @@ endpoint WebContentServer
|
|||
alert_closed() =|
|
||||
confirm_closed(bool accepted) =|
|
||||
prompt_closed(Optional<String> response) =|
|
||||
color_picker_closed(Optional<Color> picked_color) =|
|
||||
|
||||
toggle_media_play_state() =|
|
||||
toggle_media_mute_state() =|
|
||||
|
|
Loading…
Reference in a new issue