diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index 307503caef..aee78234eb 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -336,8 +337,37 @@ static String strip_newlines(Optional string) return MUST(Infra::strip_and_collapse_whitespace(MUST(builder.to_string()))); } -void HTMLSelectElement::activation_behavior(DOM::Event const&) +// https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable +void HTMLSelectElement::show_the_picker_if_applicable() { + // FIXME: Deduplicate with HTMLInputElement + // To show the picker, if applicable for a select element: + + // 1. If element's relevant global object does not have transient activation, then return. + auto& global_object = relevant_global_object(*this); + if (!is(global_object)) + return; + auto& relevant_global_object = static_cast(global_object); + if (!relevant_global_object.has_transient_activation()) + return; + + // 2. If element is not mutable, then return. + if (!enabled()) + return; + + // 3. Consume user activation given element's relevant global object. + relevant_global_object.consume_user_activation(); + + // 4. If element's type attribute is in the File Upload state, then run these steps in parallel: + // Not Applicable to select elements + + // 5. 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.) + // Populate select items m_select_items.clear(); u32 id_counter = 1; @@ -371,6 +401,41 @@ void HTMLSelectElement::activation_behavior(DOM::Event const&) set_is_open(true); } +// https://html.spec.whatwg.org/multipage/input.html#dom-select-showpicker +WebIDL::ExceptionOr HTMLSelectElement::show_picker() +{ + // FIXME: Deduplicate with HTMLInputElement + // The showPicker() method steps are: + + // 1. If this is not mutable, then throw an "InvalidStateError" DOMException. + if (!enabled()) + return WebIDL::InvalidStateError::create(realm(), "Element is not mutable"_fly_string); + + // 2. If this's relevant settings object's origin is not same origin with this's relevant settings object's top-level origin, + // and this is a select element, then throw a "SecurityError" DOMException. + if (!relevant_settings_object(*this).origin().is_same_origin(relevant_settings_object(*this).top_level_origin)) { + return WebIDL::SecurityError::create(realm(), "Cross origin pickers are not allowed"_fly_string); + } + + // 3. If this's relevant global object does not have transient activation, then throw a "NotAllowedError" DOMException. + // FIXME: The global object we get here should probably not need casted to Window to check for transient activation + auto& global_object = relevant_global_object(*this); + if (!is(global_object) || !static_cast(global_object).has_transient_activation()) { + return WebIDL::NotAllowedError::create(realm(), "Too long since user activation to show picker"_fly_string); + } + + // FIXME: 4. If this is a select element, and this is not being rendered, then throw a "NotSupportedError" DOMException. + + // 5. Show the picker, if applicable, for this. + show_the_picker_if_applicable(); + return {}; +} + +void HTMLSelectElement::activation_behavior(DOM::Event const&) +{ + show_the_picker_if_applicable(); +} + void HTMLSelectElement::did_select_item(Optional const& id) { set_is_open(false); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h index 4456e28a64..dd12a73d24 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.h @@ -53,6 +53,8 @@ public: bool is_open() const { return m_is_open; } void set_is_open(bool); + WebIDL::ExceptionOr show_picker(); + Vector> list_of_options() const; // ^EventTarget @@ -101,6 +103,8 @@ private: virtual void computed_css_values_changed() override; + void show_the_picker_if_applicable(); + void create_shadow_tree_if_needed(); void update_inner_text_element(); void queue_input_and_change_events(); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl index e2aed45b46..20ec7a0363 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLSelectElement.idl @@ -37,5 +37,7 @@ interface HTMLSelectElement : HTMLElement { [FIXME] boolean reportValidity(); [FIXME] undefined setCustomValidity(DOMString error); + undefined showPicker(); + readonly attribute NodeList labels; };