Skip to content

Commit

Permalink
LibWeb: Support :open for file and color <input> elements
Browse files Browse the repository at this point in the history
  • Loading branch information
AtkinsSJ committed Jan 31, 2025
1 parent de47e97 commit 6d24844
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
18 changes: 11 additions & 7 deletions Libraries/LibWeb/CSS/SelectorEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,20 +416,24 @@ static bool matches_read_write_pseudo_class(DOM::Element const& element)
return element.is_editable_or_editing_host();
}

// https://www.w3.org/TR/selectors-4/#open-state
// https://drafts.csswg.org/selectors-4/#open-state
static bool matches_open_state_pseudo_class(DOM::Element const& element, bool open)
{
// The :open pseudo-class represents an element that has both “open” and “closed” states,
// and which is currently in the “open” state.
// The :closed pseudo-class represents an element that has both “open” and “closed” states,
// and which is currently in the closed state.

// NOTE: Spec specifically suggests supporting <details>, <dialog>, and <select>.
// There may be others we want to treat as open or closed.
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-open
// The :open pseudo-class must match any element falling into one of the following categories:
// - details elements that have an open attribute
// - dialog elements that have an open attribute
if (is<HTML::HTMLDetailsElement>(element) || is<HTML::HTMLDialogElement>(element))
return open == element.has_attribute(HTML::AttributeNames::open);
if (is<HTML::HTMLSelectElement>(element))
return open == static_cast<HTML::HTMLSelectElement const&>(element).is_open();
// - select elements that are a drop-down box and whose drop-down boxes are open
if (auto const* select = as_if<HTML::HTMLSelectElement>(element))
return open == select->is_open();
// - input elements that support a picker and whose pickers are open
if (auto const* input = as_if<HTML::HTMLInputElement>(element))
return open == (input->supports_a_picker() && input->is_open());

return false;
}
Expand Down
1 change: 1 addition & 0 deletions Libraries/LibWeb/DOM/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum class ShouldComputeRole {
X(HTMLHyperlinkElementHrefChange) \
X(HTMLIFrameElementGeometryChange) \
X(HTMLInputElementSetChecked) \
X(HTMLInputElementSetIsOpen) \
X(HTMLObjectElementUpdateLayoutAndChildObjects) \
X(HTMLOptionElementSelectedChange) \
X(HTMLSelectElementSetIsOpen) \
Expand Down
32 changes: 30 additions & 2 deletions Libraries/LibWeb/HTML/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,11 @@ static void show_the_picker_if_applicable(HTMLInputElement& element)
// 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:
// 4. If element does not support a picker, then return.
if (!element.supports_a_picker())
return;

// 5. If element's type attribute is in the File Upload state, then run these steps in parallel:
if (element.type_state() == HTMLInputElement::TypeAttributeState::FileUpload) {
// NOTE: These steps cannot be fully implemented here, and must be done in the PageClient when the response comes back from the PageHost

Expand All @@ -312,18 +316,20 @@ static void show_the_picker_if_applicable(HTMLInputElement& element)
auto allow_multiple_files = element.has_attribute(HTML::AttributeNames::multiple) ? AllowMultipleFiles::Yes : AllowMultipleFiles::No;
auto weak_element = element.make_weak_ptr<HTMLInputElement>();

element.set_is_open(true);
element.document().browsing_context()->top_level_browsing_context()->page().did_request_file_picker(weak_element, move(accepted_file_types), allow_multiple_files);
return;
}

// 5. Otherwise, the user agent should show any relevant user interface for selecting a value for element,
// 6. 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.set_is_open(true);
element.document().browsing_context()->top_level_browsing_context()->page().did_request_color_picker(weak_element, Color::from_string(element.value()).value_or(Color(0, 0, 0)));
}
}
Expand Down Expand Up @@ -453,6 +459,8 @@ void HTMLInputElement::did_edit_text_node()

void HTMLInputElement::did_pick_color(Optional<Color> picked_color, ColorPickerUpdateState state)
{
set_is_open(false);

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_string_without_alpha());
Expand Down Expand Up @@ -480,6 +488,8 @@ void HTMLInputElement::did_pick_color(Optional<Color> picked_color, ColorPickerU

void HTMLInputElement::did_select_files(Span<SelectedFile> selected_files, MultipleHandling multiple_handling)
{
set_is_open(false);

// 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
Expand Down Expand Up @@ -2765,4 +2775,22 @@ bool HTMLInputElement::is_focusable() const
return m_type != TypeAttributeState::Hidden && enabled();
}

// https://html.spec.whatwg.org/multipage/input.html#input-support-picker
bool HTMLInputElement::supports_a_picker() const
{
// The input element can support a picker. A picker is a user interface element that allows the end user to choose a value.
// Whether an input element supports a picker depends on the type attribute state and implementation-defined behavior.
// An input element must support a picker when its type attribute is in the File Upload state.
return first_is_one_of(type_state(), TypeAttributeState::FileUpload, TypeAttributeState::Color);
}

void HTMLInputElement::set_is_open(bool is_open)
{
if (is_open == m_is_open)
return;

m_is_open = is_open;
invalidate_style(DOM::StyleInvalidationReason::HTMLInputElementSetIsOpen);
}

}
6 changes: 6 additions & 0 deletions Libraries/LibWeb/HTML/HTMLInputElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ class HTMLInputElement final
bool selection_direction_applies() const;
bool has_selectable_text() const;

bool supports_a_picker() const;
bool is_open() const { return m_is_open; }
void set_is_open(bool);

static bool selection_or_range_applies_for_type_state(TypeAttributeState);

Optional<String> selection_direction_binding() { return selection_direction(); }
Expand Down Expand Up @@ -352,6 +356,8 @@ class HTMLInputElement final
String m_last_src_value;

bool m_has_uncommitted_changes { false };

bool m_is_open { false };
};

}
Expand Down

0 comments on commit 6d24844

Please sign in to comment.