mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 20:29:18 +00:00
LibWeb+WebContent: Implement the Element Clear endpoint
This commit is contained in:
parent
516f5f7008
commit
8598ed86fe
Notes:
github-actions[bot]
2024-10-12 13:02:31 +00:00
Author: https://github.com/trflynn89
Commit: 8598ed86fe
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1727
Reviewed-by: https://github.com/gmta
3 changed files with 191 additions and 48 deletions
|
@ -10,8 +10,13 @@
|
||||||
#include <LibWeb/DOM/ShadowRoot.h>
|
#include <LibWeb/DOM/ShadowRoot.h>
|
||||||
#include <LibWeb/Geometry/DOMRect.h>
|
#include <LibWeb/Geometry/DOMRect.h>
|
||||||
#include <LibWeb/Geometry/DOMRectList.h>
|
#include <LibWeb/Geometry/DOMRectList.h>
|
||||||
|
#include <LibWeb/HTML/BrowsingContext.h>
|
||||||
#include <LibWeb/HTML/HTMLBodyElement.h>
|
#include <LibWeb/HTML/HTMLBodyElement.h>
|
||||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||||
|
#include <LibWeb/HTML/HTMLTextAreaElement.h>
|
||||||
|
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||||
|
#include <LibWeb/Page/Page.h>
|
||||||
|
#include <LibWeb/Painting/PaintableBox.h>
|
||||||
#include <LibWeb/WebDriver/ElementReference.h>
|
#include <LibWeb/WebDriver/ElementReference.h>
|
||||||
|
|
||||||
namespace Web::WebDriver {
|
namespace Web::WebDriver {
|
||||||
|
@ -125,6 +130,36 @@ bool is_element_stale(Web::DOM::Node const& element)
|
||||||
return !element.document().is_active() || !element.is_connected();
|
return !element.document().is_active() || !element.is_connected();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-interactable
|
||||||
|
bool is_element_interactable(Web::HTML::BrowsingContext const& browsing_context, Web::DOM::Element const& element)
|
||||||
|
{
|
||||||
|
// An interactable element is an element which is either pointer-interactable or keyboard-interactable.
|
||||||
|
return is_element_keyboard_interactable(element) || is_element_pointer_interactable(browsing_context, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-pointer-interactable
|
||||||
|
bool is_element_pointer_interactable(Web::HTML::BrowsingContext const& browsing_context, Web::DOM::Element const& element)
|
||||||
|
{
|
||||||
|
// A pointer-interactable element is defined to be the first element, defined by the paint order found at the center
|
||||||
|
// point of its rectangle that is inside the viewport, excluding the size of any rendered scrollbars.
|
||||||
|
auto const* document = browsing_context.active_document();
|
||||||
|
if (!document)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto const* paint_root = document->paintable_box();
|
||||||
|
if (!paint_root)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto viewport = browsing_context.page().top_level_traversable()->viewport_rect();
|
||||||
|
auto center_point = in_view_center_point(element, viewport);
|
||||||
|
|
||||||
|
auto result = paint_root->hit_test(center_point, Painting::HitTestType::TextCursor);
|
||||||
|
if (!result.has_value())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return result->dom_node() == &element;
|
||||||
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-keyboard-interactable
|
// https://w3c.github.io/webdriver/#dfn-keyboard-interactable
|
||||||
bool is_element_keyboard_interactable(Web::DOM::Element const& element)
|
bool is_element_keyboard_interactable(Web::DOM::Element const& element)
|
||||||
{
|
{
|
||||||
|
@ -132,6 +167,68 @@ bool is_element_keyboard_interactable(Web::DOM::Element const& element)
|
||||||
return element.is_focusable() || is<HTML::HTMLBodyElement>(element) || element.is_document_element();
|
return element.is_focusable() || is<HTML::HTMLBodyElement>(element) || element.is_document_element();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-editable
|
||||||
|
bool is_element_editable(Web::DOM::Element const& element)
|
||||||
|
{
|
||||||
|
// Editable elements are those that can be used for typing and clearing, and they fall into two subcategories:
|
||||||
|
// "Mutable form control elements" and "Mutable elements".
|
||||||
|
return is_element_mutable_form_control(element) || is_element_mutable(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-mutable-element
|
||||||
|
bool is_element_mutable(Web::DOM::Element const& element)
|
||||||
|
{
|
||||||
|
// Denotes elements that are editing hosts or content editable.
|
||||||
|
if (!is<HTML::HTMLElement>(element))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto const& html_element = static_cast<HTML::HTMLElement const&>(element);
|
||||||
|
return html_element.is_editable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-mutable-form-control-element
|
||||||
|
bool is_element_mutable_form_control(Web::DOM::Element const& element)
|
||||||
|
{
|
||||||
|
// Denotes input elements that are mutable (e.g. that are not read only or disabled) and whose type attribute is
|
||||||
|
// in one of the following states:
|
||||||
|
if (is<HTML::HTMLInputElement>(element)) {
|
||||||
|
auto const& input_element = static_cast<HTML::HTMLInputElement const&>(element);
|
||||||
|
if (!input_element.is_mutable() || !input_element.enabled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Text and Search, URL, Telephone, Email, Password, Date, Month, Week, Time, Local Date and Time, Number,
|
||||||
|
// Range, Color, File Upload
|
||||||
|
switch (input_element.type_state()) {
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Text:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Search:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::URL:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Telephone:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Email:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Password:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Date:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Month:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Week:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Time:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::LocalDateAndTime:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Number:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Range:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::Color:
|
||||||
|
case HTML::HTMLInputElement::TypeAttributeState::FileUpload:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And the textarea element.
|
||||||
|
if (is<HTML::HTMLTextAreaElement>(element)) {
|
||||||
|
auto const& text_area = static_cast<HTML::HTMLTextAreaElement const&>(element);
|
||||||
|
return text_area.enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webdriver/#dfn-non-typeable-form-control
|
// https://w3c.github.io/webdriver/#dfn-non-typeable-form-control
|
||||||
bool is_element_non_typeable_form_control(Web::DOM::Element const& element)
|
bool is_element_non_typeable_form_control(Web::DOM::Element const& element)
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,13 @@ ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_elem
|
||||||
ErrorOr<Web::DOM::Element*, Web::WebDriver::Error> get_known_connected_element(StringView element_id);
|
ErrorOr<Web::DOM::Element*, Web::WebDriver::Error> get_known_connected_element(StringView element_id);
|
||||||
|
|
||||||
bool is_element_stale(Web::DOM::Node const& element);
|
bool is_element_stale(Web::DOM::Node const& element);
|
||||||
|
bool is_element_interactable(Web::HTML::BrowsingContext const&, Web::DOM::Element const&);
|
||||||
|
bool is_element_pointer_interactable(Web::HTML::BrowsingContext const&, Web::DOM::Element const&);
|
||||||
bool is_element_keyboard_interactable(Web::DOM::Element const&);
|
bool is_element_keyboard_interactable(Web::DOM::Element const&);
|
||||||
|
|
||||||
|
bool is_element_editable(Web::DOM::Element const&);
|
||||||
|
bool is_element_mutable(Web::DOM::Element const&);
|
||||||
|
bool is_element_mutable_form_control(Web::DOM::Element const&);
|
||||||
bool is_element_non_typeable_form_control(Web::DOM::Element const&);
|
bool is_element_non_typeable_form_control(Web::DOM::Element const&);
|
||||||
|
|
||||||
ByteString get_or_create_a_shadow_root_reference(Web::DOM::ShadowRoot const& shadow_root);
|
ByteString get_or_create_a_shadow_root_reference(Web::DOM::ShadowRoot const& shadow_root);
|
||||||
|
|
|
@ -1475,40 +1475,75 @@ Messages::WebDriverClient::ElementClickResponse WebDriverConnection::element_cli
|
||||||
// 12.5.2 Element Clear, https://w3c.github.io/webdriver/#dfn-element-clear
|
// 12.5.2 Element Clear, https://w3c.github.io/webdriver/#dfn-element-clear
|
||||||
Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_clear(String const& element_id)
|
Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_clear(String const& element_id)
|
||||||
{
|
{
|
||||||
dbgln("FIXME: WebDriverConnection::element_clear({})", element_id);
|
// https://w3c.github.io/webdriver/#dfn-clear-a-content-editable-element
|
||||||
|
auto clear_content_editable_element = [&](Web::DOM::Element& element) {
|
||||||
|
// 1. If element's innerHTML IDL attribute is an empty string do nothing and return.
|
||||||
|
if (auto result = element.inner_html(); result.is_error() || result.value().is_empty())
|
||||||
|
return;
|
||||||
|
|
||||||
// To clear a content editable element:
|
// 2. Run the focusing steps for element.
|
||||||
{
|
Web::HTML::run_focusing_steps(&element);
|
||||||
// FIXME: 1. If element's innerHTML IDL attribute is an empty string do nothing and return.
|
|
||||||
// FIXME: 2. Run the focusing steps for element.
|
|
||||||
// FIXME: 3. Set element's innerHTML IDL attribute to an empty string.
|
|
||||||
// FIXME: 4. Run the unfocusing steps for the element.
|
|
||||||
}
|
|
||||||
|
|
||||||
// To clear a resettable element:
|
// 3. Set element's innerHTML IDL attribute to an empty string.
|
||||||
{
|
(void)element.set_inner_html({});
|
||||||
// FIXME: 1. Let empty be the result of the first matching condition:
|
|
||||||
{
|
// 4. Run the unfocusing steps for the element.
|
||||||
|
Web::HTML::run_unfocusing_steps(&element);
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-clear-a-resettable-element
|
||||||
|
auto clear_resettable_element = [&](Web::DOM::Element& element) {
|
||||||
|
VERIFY(is<Web::HTML::FormAssociatedElement>(element));
|
||||||
|
auto& form_associated_element = dynamic_cast<Web::HTML::FormAssociatedElement&>(element);
|
||||||
|
|
||||||
|
// 1. Let empty be the result of the first matching condition:
|
||||||
|
auto empty = [&]() {
|
||||||
// -> element is an input element whose type attribute is in the File Upload state
|
// -> 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
|
||||||
// True if the list of selected files has a length of 0, and false otherwise.
|
if (is<Web::HTML::HTMLInputElement>(element)) {
|
||||||
}
|
auto& input_element = static_cast<Web::HTML::HTMLInputElement&>(element);
|
||||||
// -> otherwise
|
|
||||||
{
|
if (input_element.type_state() == Web::HTML::HTMLInputElement::TypeAttributeState::FileUpload)
|
||||||
// True if its value IDL attribute is an empty string, and false otherwise.
|
return input_element.files()->length() == 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// FIXME: 2. If element is a candidate for constraint validation it satisfies its constraints, and empty is true, abort these substeps.
|
// -> otherwise
|
||||||
// FIXME: 3. Invoke the focusing steps for element.
|
// True if its value IDL attribute is an empty string, and false otherwise.
|
||||||
// FIXME: 4. Invoke the clear algorithm for element.
|
return form_associated_element.value().is_empty();
|
||||||
// FIXME: 5. Invoke the unfocusing steps for the element.
|
}();
|
||||||
}
|
|
||||||
|
// 2. If element is a candidate for constraint validation it satisfies its constraints, and empty is true,
|
||||||
|
// abort these substeps.
|
||||||
|
// FIXME: Implement constraint validation.
|
||||||
|
if (empty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 3. Invoke the focusing steps for element.
|
||||||
|
Web::HTML::run_focusing_steps(&element);
|
||||||
|
|
||||||
|
// 4. Invoke the clear algorithm for element.
|
||||||
|
form_associated_element.clear_algorithm();
|
||||||
|
|
||||||
|
// 5. Invoke the unfocusing steps for the element.
|
||||||
|
Web::HTML::run_unfocusing_steps(&element);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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());
|
||||||
|
|
||||||
|
// 2. Try to handle any user prompts with session.
|
||||||
|
TRY(handle_any_user_prompts());
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// 4. If element is not editable, return an error with error code invalid element state.
|
||||||
|
if (!Web::WebDriver::is_element_editable(*element))
|
||||||
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidElementState, "Element is not editable"sv);
|
||||||
|
|
||||||
|
// 5. Scroll into view the element.
|
||||||
|
TRY(scroll_element_into_view(*element));
|
||||||
|
|
||||||
// FIXME: 1. If session's current browsing context is no longer open, return error with error code no such window.
|
|
||||||
// FIXME: 2. Try to handle any user prompts with session.
|
|
||||||
// FIXME: 3. Let element be the result of trying to get a known element with session and element id.
|
|
||||||
// FIXME: 4. If element is not editable, return an error with error code invalid element state.
|
|
||||||
// FIXME: 5. Scroll into view the element.
|
|
||||||
// FIXME: 6. Let timeout be session's session timeouts' implicit wait timeout.
|
// FIXME: 6. Let timeout be session's session timeouts' implicit wait timeout.
|
||||||
// FIXME: 7. Let timer be a new timer.
|
// FIXME: 7. Let timer be a new timer.
|
||||||
// FIXME: 8. If timeout is not null:
|
// FIXME: 8. If timeout is not null:
|
||||||
|
@ -1516,25 +1551,30 @@ Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_cle
|
||||||
// FIXME: 1. Start the timer with timer and timeout.
|
// FIXME: 1. Start the timer with timer and timeout.
|
||||||
}
|
}
|
||||||
// FIXME: 9. Wait for element to become interactable, or timer's timeout fired flag to be set, whichever occurs first.
|
// FIXME: 9. Wait for element to become interactable, or timer's timeout fired flag to be set, whichever occurs first.
|
||||||
// FIXME: 10. If element is not interactable, return error with error code element not interactable.
|
|
||||||
// FIXME: 11. Run the substeps of the first matching statement:
|
|
||||||
{
|
|
||||||
// -> element is a mutable form control element
|
|
||||||
{
|
|
||||||
// Invoke the steps to clear a resettable element.
|
|
||||||
}
|
|
||||||
// -> element is a mutable element
|
|
||||||
{
|
|
||||||
// Invoke the steps to clear a content editable element.
|
|
||||||
}
|
|
||||||
// -> otherwise
|
|
||||||
{
|
|
||||||
// Return error with error code invalid element state.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FIXME: 12. Return success with data null.
|
|
||||||
|
|
||||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnsupportedOperation, "element clear not implemented"sv);
|
// 10. If element is not interactable, return error with error code element not interactable.
|
||||||
|
if (!Web::WebDriver::is_element_interactable(current_browsing_context(), *element))
|
||||||
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ElementNotInteractable, "Element is not interactable"sv);
|
||||||
|
|
||||||
|
// 11. Run the substeps of the first matching statement:
|
||||||
|
// -> element is a mutable form control element
|
||||||
|
if (Web::WebDriver::is_element_mutable_form_control(*element)) {
|
||||||
|
// Invoke the steps to clear a resettable element.
|
||||||
|
clear_resettable_element(*element);
|
||||||
|
}
|
||||||
|
// -> element is a mutable element
|
||||||
|
else if (Web::WebDriver::is_element_mutable(*element)) {
|
||||||
|
// Invoke the steps to clear a content editable element.
|
||||||
|
clear_content_editable_element(*element);
|
||||||
|
}
|
||||||
|
// -> otherwise
|
||||||
|
else {
|
||||||
|
// Return error with error code invalid element state.
|
||||||
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidElementState, "Element is not editable"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. Return success with data null.
|
||||||
|
return JsonValue {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12.5.3 Element Send Keys, https://w3c.github.io/webdriver/#dfn-element-send-keys
|
// 12.5.3 Element Send Keys, https://w3c.github.io/webdriver/#dfn-element-send-keys
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue