ElementLocationStrategies.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
  3. * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibWeb/DOM/Element.h>
  8. #include <LibWeb/DOM/HTMLCollection.h>
  9. #include <LibWeb/DOM/ParentNode.h>
  10. #include <LibWeb/DOM/StaticNodeList.h>
  11. #include <LibWeb/WebDriver/ElementLocationStrategies.h>
  12. #include <LibWeb/WebDriver/ElementReference.h>
  13. namespace Web::WebDriver {
  14. // https://w3c.github.io/webdriver/#css-selectors
  15. static ErrorOr<GC::Ref<DOM::NodeList>, Error> locate_element_by_css_selector(DOM::ParentNode& start_node, StringView selector)
  16. {
  17. // 1. Let elements be the result of calling querySelectorAll() with start node as this and selector as the argument.
  18. // If this causes an exception to be thrown, return error with error code invalid selector.
  19. auto elements = start_node.query_selector_all(selector);
  20. if (elements.is_exception())
  21. return Error::from_code(ErrorCode::InvalidSelector, "querySelectorAll() failed"sv);
  22. // 2.Return success with data elements.
  23. return elements.release_value();
  24. }
  25. // https://w3c.github.io/webdriver/#link-text
  26. static ErrorOr<GC::Ref<DOM::NodeList>, Error> locate_element_by_link_text(DOM::ParentNode& start_node, StringView selector)
  27. {
  28. auto& realm = start_node.realm();
  29. // 1. Let elements be the result of calling querySelectorAll() with start node as this and "a" as the argument. If
  30. // this throws an exception, return error with error code unknown error.
  31. auto elements = start_node.query_selector_all("a"sv);
  32. if (elements.is_exception())
  33. return Error::from_code(ErrorCode::UnknownError, "querySelectorAll() failed"sv);
  34. // 2. Let result be an empty NodeList.
  35. Vector<GC::Root<DOM::Node>> result;
  36. // 3. For each element in elements:
  37. for (size_t i = 0; i < elements.value()->length(); ++i) {
  38. auto& element = const_cast<DOM::Node&>(*elements.value()->item(i));
  39. // 1. Let rendered text be the value that would be returned via a call to Get Element Text for element.
  40. auto rendered_text = element_rendered_text(element);
  41. // 2. Let trimmed text be the result of removing all whitespace from the start and end of the string rendered text.
  42. auto trimmed_text = MUST(rendered_text.trim_whitespace());
  43. // 3. If trimmed text equals selector, append element to result.
  44. if (trimmed_text == selector)
  45. result.append(element);
  46. }
  47. // 4. Return success with data result.
  48. return DOM::StaticNodeList::create(realm, move(result));
  49. }
  50. // https://w3c.github.io/webdriver/#partial-link-text
  51. static ErrorOr<GC::Ref<DOM::NodeList>, Error> locate_element_by_partial_link_text(DOM::ParentNode& start_node, StringView selector)
  52. {
  53. auto& realm = start_node.realm();
  54. // 1. Let elements be the result of calling querySelectorAll() with start node as this and "a" as the argument. If
  55. // this throws an exception, return error with error code unknown error.
  56. auto elements = start_node.query_selector_all("a"sv);
  57. if (elements.is_exception())
  58. return Error::from_code(ErrorCode::UnknownError, "querySelectorAll() failed"sv);
  59. // 2. Let result be an empty NodeList.
  60. Vector<GC::Root<DOM::Node>> result;
  61. // 3. For each element in elements:
  62. for (size_t i = 0; i < elements.value()->length(); ++i) {
  63. auto& element = const_cast<DOM::Node&>(*elements.value()->item(i));
  64. // 1. Let rendered text be the value that would be returned via a call to Get Element Text for element.
  65. auto rendered_text = element_rendered_text(element);
  66. // 2. If rendered text contains selector, append element to result.
  67. if (rendered_text.contains(selector))
  68. result.append(element);
  69. }
  70. // 4. Return success with data result.
  71. return DOM::StaticNodeList::create(realm, move(result));
  72. }
  73. // https://w3c.github.io/webdriver/#tag-name
  74. static GC::Ref<DOM::NodeList> locate_element_by_tag_name(DOM::ParentNode& start_node, StringView selector)
  75. {
  76. auto& realm = start_node.realm();
  77. // To find a web element with the Tag Name strategy return success with data set to the result of calling
  78. // getElementsByTagName() with start node as this and selector as the argument.
  79. auto elements = start_node.get_elements_by_tag_name(MUST(FlyString::from_utf8(selector)));
  80. // FIXME: Having to convert this to a NodeList is a bit awkward.
  81. Vector<GC::Root<DOM::Node>> result;
  82. for (size_t i = 0; i < elements->length(); ++i) {
  83. auto* element = elements->item(i);
  84. result.append(*element);
  85. }
  86. return DOM::StaticNodeList::create(realm, move(result));
  87. }
  88. // https://w3c.github.io/webdriver/#xpath
  89. static ErrorOr<GC::Ref<DOM::NodeList>, Error> locate_element_by_x_path(DOM::ParentNode&, StringView)
  90. {
  91. return Error::from_code(ErrorCode::UnsupportedOperation, "Not implemented: locate element by XPath"sv);
  92. }
  93. Optional<LocationStrategy> location_strategy_from_string(StringView type)
  94. {
  95. if (type == "css selector"sv)
  96. return LocationStrategy::CssSelector;
  97. if (type == "link text"sv)
  98. return LocationStrategy::LinkText;
  99. if (type == "partial link text"sv)
  100. return LocationStrategy::PartialLinkText;
  101. if (type == "tag name"sv)
  102. return LocationStrategy::TagName;
  103. if (type == "xpath"sv)
  104. return LocationStrategy::XPath;
  105. return {};
  106. }
  107. ErrorOr<GC::Ref<DOM::NodeList>, Error> invoke_location_strategy(LocationStrategy type, DOM::ParentNode& start_node, StringView selector)
  108. {
  109. switch (type) {
  110. case LocationStrategy::CssSelector:
  111. return locate_element_by_css_selector(start_node, selector);
  112. case LocationStrategy::LinkText:
  113. return locate_element_by_link_text(start_node, selector);
  114. case LocationStrategy::PartialLinkText:
  115. return locate_element_by_partial_link_text(start_node, selector);
  116. case LocationStrategy::TagName:
  117. return locate_element_by_tag_name(start_node, selector);
  118. case LocationStrategy::XPath:
  119. return locate_element_by_x_path(start_node, selector);
  120. }
  121. VERIFY_NOT_REACHED();
  122. }
  123. }