diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
index bc2ea4d7ad8d..1b6f8f831d04 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
@@ -82,6 +82,7 @@ class HTMLInputElement final
WebIDL::ExceptionOr set_relevant_value(String const& value) override { return set_value(value); }
virtual void set_dirty_value_flag(bool flag) override { m_dirty_value = flag; }
+ virtual void set_dirty_checkedness(bool flag) { m_dirty_checkedness = flag; }
void commit_pending_changes();
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h
index ab0c300ff09c..314db3b6b820 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h
@@ -122,6 +122,8 @@ class HTMLTextAreaElement final
void set_dirty_value_flag(Badge, bool flag) { m_dirty_value = flag; }
+ void set_raw_value(String);
+
protected:
void selection_was_changed(size_t selection_start, size_t selection_end) override;
@@ -131,8 +133,6 @@ class HTMLTextAreaElement final
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
- void set_raw_value(String);
-
// ^DOM::Element
virtual i32 default_tab_index_value() const override;
diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp
index a6e7f1f6e4f4..b5da36d2869e 100644
--- a/Userland/Services/WebContent/WebDriverConnection.cpp
+++ b/Userland/Services/WebContent/WebDriverConnection.cpp
@@ -36,7 +36,9 @@
#include
#include
#include
+#include
#include
+#include
#include
#include
#include
@@ -46,6 +48,7 @@
#include
#include
#include
+#include
#include
namespace WebContent {
@@ -1447,58 +1450,199 @@ Messages::WebDriverClient::ElementClickResponse WebDriverConnection::element_cli
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnsupportedOperation, "Click not implemented"sv);
}
+// https://w3c.github.io/webdriver/#dfn-clear-algorithm
+static void invoke_the_clear_algorithm_for_element(Web::DOM::Element* element)
+{
+ using namespace Web::HTML;
+ // Some resettable elements define their own clear algorithm. Unlike their associated reset algorithms,
+ // changes made to form controls as part of these algorithms do count as changes caused by the user
+ // (and thus, e.g. do cause input events to fire). When the clear algorithm is invoked for an element
+ // that does not define its own clear algorithm, its reset algorithm must be invoked instead.
+
+ // The clear algorithm for input elements is to
+ if (is(*element)) {
+ auto& input = static_cast(*element);
+ // set the dirty value flag and dirty checkedness flag back to false,
+ input.set_dirty_value_flag(false);
+ input.set_dirty_checkedness(false);
+ // set the value of the element to an empty string,
+ (void)input.set_value({});
+ // set the checkedness of the element to true if the element has a checked content attribute and false if it does not,
+ // FIXME: check the if^
+ input.set_checked(true);
+ // empty the list of selected files,
+ input.set_files({});
+
+ // and then invoke the value sanitization algorithm iff the type attribute's current state defines one.
+ // FIXME: this is currently private
+ // it could make sense to define a public overload taking no parameters so we can
+ // simply "invoke the value sanitization algorithm"
+ // input.value_sanitization_algorithm({});
+ return;
+ }
+
+ // The clear algorithm for textarea elements is to set the dirty value flag back to false,
+ // and set the raw value of element to an empty string.
+ if (is(*element)) {
+ auto& input = static_cast(*element);
+ input.set_dirty_value_flag(false);
+ input.set_raw_value({});
+ return;
+ }
+
+ // The clear algorithm for output elements is set the element's value mode flag to default and then
+ // to set the element's textContent IDL attribute to an empty string (thus clearing the element's child nodes).
+ if (is(*element)) {
+ auto& input = static_cast(*element);
+ // FIXME: Mode flag?
+ input.set_text_content({});
+ return;
+ }
+
+ // FIXME: From first paragraph in this function:
+ // "When the clear algorithm is invoked for an element
+ // that does not define its own clear algorithm, its reset algorithm must be invoked instead."
+ // Resettable elements listed here: https://html.spec.whatwg.org/#category-reset
+ // select and form-associated custom elements not accounted for above
+
+ if (is(*element))
+ verify_cast(*element).reset_algorithm();
+
+ // FIXME: https://html.spec.whatwg.org/#autonomous-custom-element
+ // autonomous elements don't seem to have much infrastructure yet
+ // at some point along the line want to check that element's
+ // m_custom_element_definition.m_form_associated == true
+ // but not clear if you'd want to do that from a generic DOM::Element
+}
+
// 12.5.2 Element Clear, https://w3c.github.io/webdriver/#dfn-element-clear
-Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_clear(String const&)
+Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_clear(String const& element_id)
{
dbgln("FIXME: WebDriverConnection::element_clear()");
- // FIXME: 1. If element's innerHTML IDL attribute is an empty string do nothing and return.
+ // To clear a content editable element:
+ auto clear_a_content_editable_element = [](Web::DOM::Element* element) {
+ // 1. If element's innerHTML IDL attribute is an empty string do nothing and return.
+ if (element->inner_html().value() == "")
+ return;
- // FIXME: 2. Run the focusing steps for element.
+ // 2. Run the focusing steps for element.
+ Web::HTML::run_focusing_steps(element);
- // FIXME: 3. Set element's innerHTML IDL attribute to an empty string.
+ // 3. Set element's innerHTML IDL attribute to an empty string.
+ (void)element->set_inner_html(""sv);
- // FIXME: 4. Run the unfocusing steps for the element.
+ // 4. Run the unfocusing steps for the element.
+ Web::HTML::run_unfocusing_steps(element);
+ };
// To clear a resettable element:
+ auto clear_a_resettable_element = [](Web::DOM::Element* element) {
+ // 1. Let empty be the result of the first matching condition:
+ bool empty {};
- // FIXME: 1. Let empty be the result of the first matching condition:
-
- {
// -> element is an input element whose type attribute is in the File Upload state
- // True if the list of selected files has a length of 0, and false otherwise.
+ if (is(*element)) {
+
+ auto& input = static_cast(*element);
+
+ if (input.type_state() == Web::HTML::HTMLInputElement::TypeAttributeState::FileUpload)
+ // True if the list of selected files has a length of 0, and false otherwise.
+ empty = (input.files()->length() == 0);
+ }
// -> otherwise
- // True if its value IDL attribute is an empty string, and false otherwise.
- }
+ else
+ // True if its value IDL attribute is an empty string, and false otherwise.
+ empty = (element->node_value() == "");
+
+ // FIXME: 2. If element is a candidate for constraint validation, it satisfies its constraints, and empty is true, abort these substeps.
+ //
+ // Candidate for constraint violation:
+ // https://html.spec.whatwg.org/#candidate-for-constraint-validation
+ //
+ // Candidate for constraint validation depends on element_is_barred_from_constraint_validation:
+ // https://html.spec.whatwg.org/#barred-from-constraint-validation
+ // conditions defined throughout this link ^
+ // ctrl-f looks like the best way to search through them all
+ //
+ auto element_is_barred_from_constraint_validation = [&]() {
+ using namespace Web::HTML;
+ if (is(*element)) {
+ auto const& input = verify_cast(*element);
+ auto const type_state = input.type_state();
+
+ if (type_state == HTMLInputElement::TypeAttributeState::Hidden // If an input element's type attribute is in the Hidden state, it is barred from constraint validation.
+ || type_state == HTMLInputElement::TypeAttributeState::ResetButton // 4.10.5.1.20 Reset Button state (type=reset) : The element is barred from constraint validation.
+ || type_state == HTMLInputElement::TypeAttributeState::Button // 4.10.5.1.20 Reset Button state (type=reset): The element is barred from constraint validation.
+ // FIXME: If the readonly attribute is specified on an input element, the element is barred from constraint validation.
+ // FIXME: If an element has a datalist element ancestor...
+ // FIXME: If the readonly attribute is specified on a textarea element...
+ // FIXME: If an element is disabled...
+ // FIXME: If the readonly attribute is specified on a form-associated custom element...
+ )
+
+ return true;
+ }
- // FIXME: 2. If element is a candidate for constraint validation it satisfies its constraints, and empty is true, abort these substeps.
+ return false;
+ };
- // FIXME: 3. Invoke the focusing steps for element.
+ // https://html.spec.whatwg.org/#concept-fv-valid
+ // FIXME: implement element_satisfies_its_constraints
+ auto element_satisfies_its_constraints = []() { return false; };
+
+ // https://html.spec.whatwg.org/#barred-from-constraint-validation
+ auto element_is_a_candidate_for_constraint_violation = [&]() {
+ // FIXME: multiple inheritance heirarchy here makes casting from an element ptr
+ // to a FormAssociatedElement, which defines is_submittable(), hard
+ if (is(*element)) {
+ // auto& input = verify_cast(*element);
+ return (/*input.is_submittable() &&*/ element_is_barred_from_constraint_validation());
+ }
+ return false;
+ };
- // FIXME: 4. Invoke the clear algorithm for element.
+ if (element_is_a_candidate_for_constraint_violation() && element_satisfies_its_constraints() && empty)
+ return;
- // FIXME: 5. Invoke the unfocusing steps for the element.
+ // 3. Invoke the focusing steps for element.
+ Web::HTML::run_focusing_steps(element);
+
+ // 4. Invoke the clear algorithm for element.
+ invoke_the_clear_algorithm_for_element(element);
+
+ // 5. Invoke the unfocusing steps for the element.
+ Web::HTML::run_unfocusing_steps(element);
+ };
// The remote end steps, given session, URL variables and parameters are:
- // FIXME: 1. If session's current browsing context is no longer open, return error with error code no such window.
+ // 1. If session's current browsing context is no longer open, return error with error code no such window.
+ TRY(ensure_current_browsing_context_is_open());
- // FIXME: 2. Try to handle any user prompts with session.
+ // 2. Try to handle any user prompts with session.
+ TRY(handle_any_user_prompts());
- // FIXME: 3. Let element be the result of trying to get a known element with session and element id.
+ // 3. Let element be the result of trying to get a known element with session and element id.
+ auto* element = TRY(Web::WebDriver::get_known_connected_element(element_id));
// FIXME: 4. If element is not editable, return an error with error code invalid element state.
+ if (!element->is_editable()) { }
- // FIXME: 5. Scroll into view the element.
+ // 5. Scroll into view the element.
+ (void)scroll_element_into_view(*element);
- // FIXME: 6. Let timeout be session's session timeouts' implicit wait timeout.
+ // 6. Let timeout be session's session timeouts' implicit wait timeout.
+ auto timeout = m_timeouts_configuration.implicit_wait_timeout;
// FIXME: 7. Let timer be a new timer.
+ // might want this to be a one shot
+ auto timer = Core::Timer::create();
- // FIXME: 8. If timeout is not null:
-
- {
+ // 8. If timeout is not null:
+ if (timeout != 0u) {
// FIXME: 1. Start the timer with timer and timeout.
+ timer->start();
}
// FIXME: 9. Wait for element to become interactable, or timer's timeout fired flag to be set, whichever occurs first.
@@ -1509,16 +1653,17 @@ Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_cle
{
// -> element is a mutable form control element
-
- // Invoke the steps to clear a resettable element.
-
+ if (true)
+ // Invoke the steps to clear a resettable element.
+ clear_a_resettable_element(element);
// -> element is a mutable element
-
- // Invoke the steps to clear a content editable element.
-
+ else if (false)
+ // Invoke the steps to clear a content editable element.
+ clear_a_content_editable_element(element);
// -> otherwise
-
- // Return error with error code invalid element state.
+ else
+ // Return error with error code invalid element state.
+ return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidElementState, "element clear: element not matching statement"sv);
}
// FIXME: 12. Return success with data null.