LibWeb: Implement HTMLInputElement's selected coordinates

When an <input type=image> button is clicked, we now send the (x,y)
coordinates of the click event (relative to the image) along with the
form submission data.

Regarding the text test, we can currently only test this feature with
dialogs. The headless-browser test infrastructure cannot yet handle the
resulting navigation that would occur if we were to test with normal
form submission.
This commit is contained in:
Timothy Flynn 2024-02-19 00:21:27 -05:00 committed by Andreas Kling
parent 36302388a3
commit 94c67c364d
6 changed files with 89 additions and 13 deletions

View file

@ -0,0 +1 @@
10,20

View file

@ -0,0 +1,34 @@
<head>
<!--
These style rules ensure the (x,y) coordinates clicked below are the same as the resulting
coordinates in the form submission event.
-->
<style type="text/css">
dialog {
margin: 0;
padding: 0;
}
</style>
</head>
<dialog id="dialog" open>
<form id="form" method="dialog">
<input value="well hello friends!" />
<input id="image" type="image" src="../../../Layout/input/120.png" />
</form>
</dialog>
<script src="../include.js"></script>
<script>
asyncTest(done => {
let dialog = document.getElementById("dialog");
let image = document.getElementById("image");
dialog.addEventListener("close", e => {
println(dialog.returnValue);
done();
});
const imageRect = image.getBoundingClientRect();
internals.click(imageRect.x + 10, imageRect.y + 20);
});
</script>

View file

@ -88,13 +88,32 @@ WebIDL::ExceptionOr<Optional<Vector<XHR::FormDataEntry>>> construct_entry_list(J
// 2. If the field element is an input element whose type attribute is in the Image Button state, then:
if (auto* input_element = dynamic_cast<HTML::HTMLInputElement*>(control.ptr()); input_element && input_element->type_state() == HTMLInputElement::TypeAttributeState::ImageButton) {
// FIXME: 1. If the field element has a name attribute specified and its value is not the empty string, let name be that value followed by a single U+002E FULL STOP character (.). Otherwise, let name be the empty string.
// FIXME: 2. Let namex be the string consisting of the concatenation of name and a single U0078 LATIN SMALL LETTER X character (x).
// FIXME: 3. Let namey be the string consisting of the concatenation of name and a single U+0079 LATIN SMALL LETTER Y character (y).
// FIXME: 4. The field element is submitter, and before this algorithm was invoked the user indicated a coordinate. Let x be the x-component of the coordinate selected by the user, and let y be the y-component of the coordinate selected by the user.
// FIXME: 5. Create an entry with namex and x, and append it to entry list.
// FIXME: 6. Create an entry with namey and y, and append it to entry list.
// 7. Continue.
// 1. If the field element is not submitter, then continue.
if (input_element != submitter.ptr())
continue;
// 2. If the field element has a name attribute specified and its value is not the empty string, let name be
// that value followed by U+002E (.). Otherwise, let name be the empty string.
String name;
if (auto value = input_element->get_attribute(AttributeNames::name); value.has_value() && !value->is_empty())
name = MUST(String::formatted("{}.", *value));
// 3. Let namex be the concatenation of name and U+0078 (x).
auto name_x = MUST(String::formatted("{}x", name));
// 4. Let namey be the concatenation of name and U+0079 (y).
auto name_y = MUST(String::formatted("{}y", name));
// 5. Let (x, y) be the selected coordinate.
auto [x, y] = input_element->selected_coordinate();
// 6. Create an entry with namex and x, and append it to entry list.
entry_list.append(XHR::FormDataEntry { .name = move(name_x), .value = MUST(String::number(x)) });
// 7. Create an entry with namey and y, and append it to entry list.
entry_list.append(XHR::FormDataEntry { .name = move(name_y), .value = MUST(String::number(y)) });
// 8. Continue.
continue;
}

View file

@ -189,12 +189,22 @@ WebIDL::ExceptionOr<void> HTMLFormElement::submit_form(JS::NonnullGCPtr<HTMLElem
// 3. Let result be null.
Optional<String> result;
// FIXME: 4. If submitter is an input element whose type attribute is in the Image Button state, then:
// 1. Let (x, y) be the selected coordinate.
// 2. Set result to the concatenation of x, ",", and y.
// 4. If submitter is an input element whose type attribute is in the Image Button state, then:
if (is<HTMLInputElement>(*submitter)) {
auto const& input_element = static_cast<HTMLInputElement const&>(*submitter);
if (input_element.type_state() == HTMLInputElement::TypeAttributeState::ImageButton) {
// 1. Let (x, y) be the selected coordinate.
auto [x, y] = input_element.selected_coordinate();
// 2. Set result to the concatenation of x, ",", and y.
result = MUST(String::formatted("{},{}", x, y));
}
}
// 5. Otherwise, if submitter has a value, then set result to that value.
result = submitter->get_attribute_value(AttributeNames::value);
if (!result.has_value())
result = submitter->get_attribute_value(AttributeNames::value);
// 6. Close the dialog subject with result.
subject->close(move(result));

View file

@ -39,6 +39,7 @@
#include <LibWeb/Namespace.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/UIEvents/EventNames.h>
#include <LibWeb/UIEvents/MouseEvent.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
@ -304,8 +305,12 @@ WebIDL::ExceptionOr<void> HTMLInputElement::run_input_activation_behavior(DOM::E
if (!document().is_fully_active())
return {};
// FIXME: 3. If the user activated the control while explicitly selecting a coordinate, then set the element's selected
// coordinate to that coordinate.
// 3. If the user activated the control while explicitly selecting a coordinate, then set the element's selected
// coordinate to that coordinate.
if (event.is_trusted() && is<UIEvents::MouseEvent>(event)) {
auto const& mouse_event = static_cast<UIEvents::MouseEvent const&>(event);
m_selected_coordinate = { mouse_event.offset_x(), mouse_event.offset_y() };
}
// 4. Submit the element's form owner from the element with userInvolvement set to event's user navigation involvement.
TRY(form->submit_form(*this, { .user_involvement = user_navigation_involvement(event) }));

View file

@ -104,6 +104,12 @@ public:
unsigned size() const;
WebIDL::ExceptionOr<void> set_size(unsigned value);
struct SelectedCoordinate {
double x { 0.0 };
double y { 0.0 };
};
SelectedCoordinate selected_coordinate() const { return m_selected_coordinate; }
JS::Object* value_as_date() const;
WebIDL::ExceptionOr<void> set_value_as_date(Optional<JS::Handle<JS::Object>> const&);
@ -252,6 +258,7 @@ private:
JS::GCPtr<DecodedImageData> image_data() const;
JS::GCPtr<SharedImageRequest> m_image_request;
SelectedCoordinate m_selected_coordinate;
Optional<DOM::DocumentLoadEventDelayer> m_load_event_delayer;