WebDriverConnection.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /*
  2. * Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
  3. * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
  5. * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  6. * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
  7. *
  8. * SPDX-License-Identifier: BSD-2-Clause
  9. */
  10. #include <AK/JsonObject.h>
  11. #include <AK/JsonValue.h>
  12. #include <AK/Vector.h>
  13. #include <LibWeb/DOM/Document.h>
  14. #include <LibWeb/HTML/BrowsingContext.h>
  15. #include <LibWeb/Page/Page.h>
  16. #include <LibWeb/Platform/EventLoopPlugin.h>
  17. #include <LibWeb/Platform/Timer.h>
  18. #include <WebContent/ConnectionFromClient.h>
  19. #include <WebContent/PageHost.h>
  20. #include <WebContent/WebDriverConnection.h>
  21. namespace WebContent {
  22. static JsonValue make_success_response(JsonValue value)
  23. {
  24. JsonObject result;
  25. result.set("value", move(value));
  26. return result;
  27. }
  28. static JsonValue serialize_rect(Gfx::IntRect const& rect)
  29. {
  30. JsonObject serialized_rect = {};
  31. serialized_rect.set("x", rect.x());
  32. serialized_rect.set("y", rect.y());
  33. serialized_rect.set("width", rect.width());
  34. serialized_rect.set("height", rect.height());
  35. return make_success_response(move(serialized_rect));
  36. }
  37. static Gfx::IntRect compute_window_rect(Web::Page const& page)
  38. {
  39. return {
  40. page.window_position().x(),
  41. page.window_position().y(),
  42. page.window_size().width(),
  43. page.window_size().height()
  44. };
  45. }
  46. // https://w3c.github.io/webdriver/#dfn-get-or-create-a-web-element-reference
  47. static String get_or_create_a_web_element_reference(Web::DOM::Node const& element)
  48. {
  49. // FIXME: 1. For each known element of the current browsing context’s list of known elements:
  50. // FIXME: 1. If known element equals element, return success with known element’s web element reference.
  51. // FIXME: 2. Add element to the list of known elements of the current browsing context.
  52. // FIXME: 3. Return success with the element’s web element reference.
  53. return String::number(element.id());
  54. }
  55. // https://w3c.github.io/webdriver/#dfn-web-element-reference-object
  56. static JsonObject web_element_reference_object(Web::DOM::Node const& element)
  57. {
  58. // https://w3c.github.io/webdriver/#dfn-web-element-identifier
  59. static String const web_element_identifier = "element-6066-11e4-a52e-4f735466cecf"sv;
  60. // 1. Let identifier be the web element identifier.
  61. auto identifier = web_element_identifier;
  62. // 2. Let reference be the result of get or create a web element reference given element.
  63. auto reference = get_or_create_a_web_element_reference(element);
  64. // 3. Return a JSON Object initialized with a property with name identifier and value reference.
  65. JsonObject object;
  66. object.set("name"sv, identifier);
  67. object.set("value"sv, reference);
  68. return object;
  69. }
  70. // https://w3c.github.io/webdriver/#dfn-get-a-known-connected-element
  71. static ErrorOr<Web::DOM::ParentNode*, Web::WebDriver::Error> get_known_connected_element(StringView element_id)
  72. {
  73. // NOTE: The whole concept of "connected elements" is not implemented yet. See get_or_create_a_web_element_reference().
  74. // For now the element is only represented by its ID.
  75. auto element = element_id.to_int();
  76. if (!element.has_value())
  77. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Element ID is not an integer");
  78. auto* node = Web::DOM::Node::from_id(*element);
  79. if (!node)
  80. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, String::formatted("Could not find element with ID: {}", element_id));
  81. if (!is<Web::DOM::ParentNode>(node))
  82. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, String::formatted("Element with ID is not a parent node: {}", element_id));
  83. return static_cast<Web::DOM::ParentNode*>(node);
  84. }
  85. static ErrorOr<String, Web::WebDriver::Error> get_property(JsonValue const& payload, StringView key)
  86. {
  87. if (!payload.is_object())
  88. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
  89. auto const* property = payload.as_object().get_ptr(key);
  90. if (!property)
  91. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("No property called '{}' present", key));
  92. if (!property->is_string())
  93. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not a String", key));
  94. return property->as_string();
  95. }
  96. ErrorOr<NonnullRefPtr<WebDriverConnection>> WebDriverConnection::connect(ConnectionFromClient& web_content_client, PageHost& page_host, String const& webdriver_ipc_path)
  97. {
  98. dbgln_if(WEBDRIVER_DEBUG, "Trying to connect to {}", webdriver_ipc_path);
  99. auto socket = TRY(Core::Stream::LocalSocket::connect(webdriver_ipc_path));
  100. dbgln_if(WEBDRIVER_DEBUG, "Connected to WebDriver");
  101. return adopt_nonnull_ref_or_enomem(new (nothrow) WebDriverConnection(move(socket), web_content_client, page_host));
  102. }
  103. WebDriverConnection::WebDriverConnection(NonnullOwnPtr<Core::Stream::LocalSocket> socket, ConnectionFromClient& web_content_client, PageHost& page_host)
  104. : IPC::ConnectionToServer<WebDriverClientEndpoint, WebDriverServerEndpoint>(*this, move(socket))
  105. , m_web_content_client(web_content_client)
  106. , m_page_host(page_host)
  107. {
  108. }
  109. // https://w3c.github.io/webdriver/#dfn-close-the-session
  110. void WebDriverConnection::close_session()
  111. {
  112. // 1. Set the webdriver-active flag to false.
  113. set_is_webdriver_active(false);
  114. // 2. An endpoint node must close any top-level browsing contexts associated with the session, without prompting to unload.
  115. m_page_host.page().top_level_browsing_context().close();
  116. }
  117. void WebDriverConnection::set_is_webdriver_active(bool is_webdriver_active)
  118. {
  119. m_page_host.set_is_webdriver_active(is_webdriver_active);
  120. }
  121. // 10.1 Navigate To, https://w3c.github.io/webdriver/#navigate-to
  122. Messages::WebDriverClient::NavigateToResponse WebDriverConnection::navigate_to(JsonValue const& payload)
  123. {
  124. dbgln_if(WEBDRIVER_DEBUG, "WebDriverConnection::navigate_to {}", payload);
  125. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  126. TRY(ensure_open_top_level_browsing_context());
  127. // 2. Let url be the result of getting the property url from the parameters argument.
  128. if (!payload.is_object() || !payload.as_object().has_string("url"sv))
  129. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload doesn't have a string `url`"sv);
  130. URL url(payload.as_object().get_ptr("url"sv)->as_string());
  131. // FIXME: 3. If url is not an absolute URL or is not an absolute URL with fragment or not a local scheme, return error with error code invalid argument.
  132. // FIXME: 4. Handle any user prompts and return its value if it is an error.
  133. // FIXME: 5. Let current URL be the current top-level browsing context’s active document’s URL.
  134. // FIXME: 6. If current URL and url do not have the same absolute URL:
  135. // FIXME: a. If timer has not been started, start a timer. If this algorithm has not completed before timer reaches the session’s session page load timeout in milliseconds, return an error with error code timeout.
  136. // 7. Navigate the current top-level browsing context to url.
  137. m_page_host.page().load(url);
  138. // FIXME: 8. If url is special except for file and current URL and URL do not have the same absolute URL:
  139. // FIXME: a. Try to wait for navigation to complete.
  140. // FIXME: b. Try to run the post-navigation checks.
  141. // FIXME: 9. Set the current browsing context with the current top-level browsing context.
  142. // FIXME: 10. If the current top-level browsing context contains a refresh state pragma directive of time 1 second or less, wait until the refresh timeout has elapsed, a new navigate has begun, and return to the first step of this algorithm.
  143. // 11. Return success with data null.
  144. return make_success_response({});
  145. }
  146. // 10.2 Get Current URL, https://w3c.github.io/webdriver/#get-current-url
  147. Messages::WebDriverClient::GetCurrentUrlResponse WebDriverConnection::get_current_url()
  148. {
  149. dbgln_if(WEBDRIVER_DEBUG, "WebDriverConnection::get_current_url");
  150. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  151. TRY(ensure_open_top_level_browsing_context());
  152. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  153. // 3. Let url be the serialization of the current top-level browsing context’s active document’s document URL.
  154. auto url = m_page_host.page().top_level_browsing_context().active_document()->url().to_string();
  155. // 4. Return success with data url.
  156. return make_success_response(url);
  157. }
  158. // 11.8.1 Get Window Rect, https://w3c.github.io/webdriver/#dfn-get-window-rect
  159. Messages::WebDriverClient::GetWindowRectResponse WebDriverConnection::get_window_rect()
  160. {
  161. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  162. TRY(ensure_open_top_level_browsing_context());
  163. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  164. // 3. Return success with data set to the WindowRect object for the current top-level browsing context.
  165. return serialize_rect(compute_window_rect(m_page_host.page()));
  166. }
  167. // 11.8.2 Set Window Rect, https://w3c.github.io/webdriver/#dfn-set-window-rect
  168. Messages::WebDriverClient::SetWindowRectResponse WebDriverConnection::set_window_rect(JsonValue const& payload)
  169. {
  170. if (!payload.is_object())
  171. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
  172. auto const& properties = payload.as_object();
  173. auto resolve_property = [](auto name, auto const* property, auto min, auto max) -> ErrorOr<Optional<i32>, Web::WebDriver::Error> {
  174. if (!property)
  175. return Optional<i32> {};
  176. if (!property->is_number())
  177. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not a Number", name));
  178. auto number = property->template to_number<i64>();
  179. if (number < min)
  180. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' value {} exceeds the minimum allowed value {}", name, number, min));
  181. if (number > max)
  182. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' value {} exceeds the maximum allowed value {}", name, number, max));
  183. return static_cast<i32>(number);
  184. };
  185. // 1. Let width be the result of getting a property named width from the parameters argument, else let it be null.
  186. auto const* width_property = properties.get_ptr("width"sv);
  187. // 2. Let height be the result of getting a property named height from the parameters argument, else let it be null.
  188. auto const* height_property = properties.get_ptr("height"sv);
  189. // 3. Let x be the result of getting a property named x from the parameters argument, else let it be null.
  190. auto const* x_property = properties.get_ptr("x"sv);
  191. // 4. Let y be the result of getting a property named y from the parameters argument, else let it be null.
  192. auto const* y_property = properties.get_ptr("y"sv);
  193. // 5. If width or height is neither null nor a Number from 0 to 2^31 − 1, return error with error code invalid argument.
  194. auto width = TRY(resolve_property("width"sv, width_property, 0, NumericLimits<i32>::max()));
  195. auto height = TRY(resolve_property("height"sv, height_property, 0, NumericLimits<i32>::max()));
  196. // 6. If x or y is neither null nor a Number from −(2^31) to 2^31 − 1, return error with error code invalid argument.
  197. auto x = TRY(resolve_property("x"sv, x_property, NumericLimits<i32>::min(), NumericLimits<i32>::max()));
  198. auto y = TRY(resolve_property("y"sv, y_property, NumericLimits<i32>::min(), NumericLimits<i32>::max()));
  199. // 7. If the remote end does not support the Set Window Rect command for the current top-level browsing context for any reason, return error with error code unsupported operation.
  200. // 8. If the current top-level browsing context is no longer open, return error with error code no such window.
  201. TRY(ensure_open_top_level_browsing_context());
  202. // FIXME: 9. Handle any user prompts and return its value if it is an error.
  203. // FIXME: 10. Fully exit fullscreen.
  204. // 11. Restore the window.
  205. restore_the_window();
  206. Gfx::IntRect window_rect;
  207. // 11. If width and height are not null:
  208. if (width.has_value() && height.has_value()) {
  209. // a. Set the width, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to width.
  210. // b. Set the height, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to height.
  211. auto size = m_web_content_client.did_request_resize_window({ *width, *height });
  212. window_rect.set_size(size);
  213. } else {
  214. window_rect.set_size(m_page_host.page().window_size());
  215. }
  216. // 12. If x and y are not null:
  217. if (x.has_value() && y.has_value()) {
  218. // a. Run the implementation-specific steps to set the position of the operating system level window containing the current top-level browsing context to the position given by the x and y coordinates.
  219. auto position = m_web_content_client.did_request_reposition_window({ *x, *y });
  220. window_rect.set_location(position);
  221. } else {
  222. window_rect.set_location(m_page_host.page().window_position());
  223. }
  224. // 14. Return success with data set to the WindowRect object for the current top-level browsing context.
  225. return serialize_rect(window_rect);
  226. }
  227. // 11.8.3 Maximize Window, https://w3c.github.io/webdriver/#dfn-maximize-window
  228. Messages::WebDriverClient::MaximizeWindowResponse WebDriverConnection::maximize_window()
  229. {
  230. // 1. If the remote end does not support the Maximize Window command for the current top-level browsing context for any reason, return error with error code unsupported operation.
  231. // 2. If the current top-level browsing context is no longer open, return error with error code no such window.
  232. TRY(ensure_open_top_level_browsing_context());
  233. // FIXME: 3. Handle any user prompts and return its value if it is an error.
  234. // FIXME: 4. Fully exit fullscreen.
  235. // 5. Restore the window.
  236. restore_the_window();
  237. // 6. Maximize the window of the current top-level browsing context.
  238. auto window_rect = maximize_the_window();
  239. // 7. Return success with data set to the WindowRect object for the current top-level browsing context.
  240. return serialize_rect(window_rect);
  241. }
  242. // 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
  243. Messages::WebDriverClient::MinimizeWindowResponse WebDriverConnection::minimize_window()
  244. {
  245. // 1. If the remote end does not support the Minimize Window command for the current top-level browsing context for any reason, return error with error code unsupported operation.
  246. // 2. If the current top-level browsing context is no longer open, return error with error code no such window.
  247. TRY(ensure_open_top_level_browsing_context());
  248. // FIXME: 3. Handle any user prompts and return its value if it is an error.
  249. // FIXME: 4. Fully exit fullscreen.
  250. // 5. Iconify the window.
  251. auto window_rect = iconify_the_window();
  252. // 6. Return success with data set to the WindowRect object for the current top-level browsing context.
  253. return serialize_rect(window_rect);
  254. }
  255. // 12.3.2 Find Element, https://w3c.github.io/webdriver/#dfn-find-element
  256. Messages::WebDriverClient::FindElementResponse WebDriverConnection::find_element(JsonValue const& payload)
  257. {
  258. // 1. Let location strategy be the result of getting a property called "using".
  259. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  260. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  261. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  262. if (!location_strategy.has_value())
  263. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  264. // 3. Let selector be the result of getting a property called "value".
  265. // 4. If selector is undefined, return error with error code invalid argument.
  266. auto selector = TRY(get_property(payload, "value"sv));
  267. // 5. If the current browsing context is no longer open, return error with error code no such window.
  268. TRY(ensure_open_top_level_browsing_context());
  269. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  270. // 7. Let start node be the current browsing context’s document element.
  271. auto* start_node = m_page_host.page().top_level_browsing_context().active_document();
  272. // 8. If start node is null, return error with error code no such element.
  273. if (!start_node)
  274. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "document element does not exist"sv);
  275. // 9. Let result be the result of trying to Find with start node, location strategy, and selector.
  276. auto result = TRY(find(*start_node, *location_strategy, selector));
  277. // 10. If result is empty, return error with error code no such element. Otherwise, return the first element of result.
  278. if (result.is_empty())
  279. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The requested element does not exist"sv);
  280. return make_success_response(result.at(0));
  281. }
  282. // 12.3.3 Find Elements, https://w3c.github.io/webdriver/#dfn-find-elements
  283. Messages::WebDriverClient::FindElementsResponse WebDriverConnection::find_elements(JsonValue const& payload)
  284. {
  285. // 1. Let location strategy be the result of getting a property called "using".
  286. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  287. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  288. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  289. if (!location_strategy.has_value())
  290. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  291. // 3. Let selector be the result of getting a property called "value".
  292. // 4. If selector is undefined, return error with error code invalid argument.
  293. auto selector = TRY(get_property(payload, "value"sv));
  294. // 5. If the current browsing context is no longer open, return error with error code no such window.
  295. TRY(ensure_open_top_level_browsing_context());
  296. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  297. // 7. Let start node be the current browsing context’s document element.
  298. auto* start_node = m_page_host.page().top_level_browsing_context().active_document();
  299. // 8. If start node is null, return error with error code no such element.
  300. if (!start_node)
  301. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "document element does not exist"sv);
  302. // 9. Return the result of trying to Find with start node, location strategy, and selector.
  303. auto result = TRY(find(*start_node, *location_strategy, selector));
  304. return make_success_response(move(result));
  305. }
  306. // 12.3.4 Find Element From Element, https://w3c.github.io/webdriver/#dfn-find-element-from-element
  307. Messages::WebDriverClient::FindElementFromElementResponse WebDriverConnection::find_element_from_element(JsonValue const& payload, String const& element_id)
  308. {
  309. // 1. Let location strategy be the result of getting a property called "using".
  310. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  311. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  312. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  313. if (!location_strategy.has_value())
  314. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  315. // 3. Let selector be the result of getting a property called "value".
  316. // 4. If selector is undefined, return error with error code invalid argument.
  317. auto selector = TRY(get_property(payload, "value"sv));
  318. // 5. If the current browsing context is no longer open, return error with error code no such window.
  319. TRY(ensure_open_top_level_browsing_context());
  320. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  321. // 7. Let start node be the result of trying to get a known connected element with url variable element id.
  322. auto* start_node = TRY(get_known_connected_element(element_id));
  323. // 8. Let result be the value of trying to Find with start node, location strategy, and selector.
  324. auto result = TRY(find(*start_node, *location_strategy, selector));
  325. // 9. If result is empty, return error with error code no such element. Otherwise, return the first element of result.
  326. if (result.is_empty())
  327. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The requested element does not exist"sv);
  328. return make_success_response(result.at(0));
  329. }
  330. // 12.3.5 Find Elements From Element, https://w3c.github.io/webdriver/#dfn-find-elements-from-element
  331. Messages::WebDriverClient::FindElementsFromElementResponse WebDriverConnection::find_elements_from_element(JsonValue const& payload, String const& element_id)
  332. {
  333. // 1. Let location strategy be the result of getting a property called "using".
  334. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  335. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  336. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  337. if (!location_strategy.has_value())
  338. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  339. // 3. Let selector be the result of getting a property called "value".
  340. // 4. If selector is undefined, return error with error code invalid argument.
  341. auto selector = TRY(get_property(payload, "value"sv));
  342. // 5. If the current browsing context is no longer open, return error with error code no such window.
  343. TRY(ensure_open_top_level_browsing_context());
  344. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  345. // 7. Let start node be the result of trying to get a known connected element with url variable element id.
  346. auto* start_node = TRY(get_known_connected_element(element_id));
  347. // 8. Return the result of trying to Find with start node, location strategy, and selector.
  348. auto result = TRY(find(*start_node, *location_strategy, selector));
  349. return make_success_response(move(result));
  350. }
  351. // https://w3c.github.io/webdriver/#dfn-no-longer-open
  352. ErrorOr<void, Web::WebDriver::Error> WebDriverConnection::ensure_open_top_level_browsing_context()
  353. {
  354. // A browsing context is said to be no longer open if it has been discarded.
  355. if (m_page_host.page().top_level_browsing_context().has_been_discarded())
  356. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchWindow, "Window not found"sv);
  357. return {};
  358. }
  359. // https://w3c.github.io/webdriver/#dfn-restore-the-window
  360. void WebDriverConnection::restore_the_window()
  361. {
  362. // To restore the window, given an operating system level window with an associated top-level browsing context, run implementation-specific steps to restore or unhide the window to the visible screen.
  363. m_web_content_client.async_did_request_restore_window();
  364. // Do not return from this operation until the visibility state of the top-level browsing context’s active document has reached the visible state, or until the operation times out.
  365. // FIXME: Implement timeouts.
  366. Web::Platform::EventLoopPlugin::the().spin_until([this]() {
  367. auto state = m_page_host.page().top_level_browsing_context().system_visibility_state();
  368. return state == Web::HTML::VisibilityState::Visible;
  369. });
  370. }
  371. // https://w3c.github.io/webdriver/#dfn-maximize-the-window
  372. Gfx::IntRect WebDriverConnection::maximize_the_window()
  373. {
  374. // To maximize the window, given an operating system level window with an associated top-level browsing context, run the implementation-specific steps to transition the operating system level window into the maximized window state.
  375. auto rect = m_web_content_client.did_request_maximize_window();
  376. // Return when the window has completed the transition, or within an implementation-defined timeout.
  377. return rect;
  378. }
  379. // https://w3c.github.io/webdriver/#dfn-iconify-the-window
  380. Gfx::IntRect WebDriverConnection::iconify_the_window()
  381. {
  382. // To iconify the window, given an operating system level window with an associated top-level browsing context, run implementation-specific steps to iconify, minimize, or hide the window from the visible screen.
  383. auto rect = m_web_content_client.did_request_minimize_window();
  384. // Do not return from this operation until the visibility state of the top-level browsing context’s active document has reached the hidden state, or until the operation times out.
  385. // FIXME: Implement timeouts.
  386. Web::Platform::EventLoopPlugin::the().spin_until([this]() {
  387. auto state = m_page_host.page().top_level_browsing_context().system_visibility_state();
  388. return state == Web::HTML::VisibilityState::Hidden;
  389. });
  390. return rect;
  391. }
  392. // https://w3c.github.io/webdriver/#dfn-find
  393. ErrorOr<JsonArray, Web::WebDriver::Error> WebDriverConnection::find(Web::DOM::ParentNode& start_node, Web::WebDriver::LocationStrategy using_, StringView value)
  394. {
  395. // FIXME: 1. Let end time be the current time plus the session implicit wait timeout.
  396. // 2. Let location strategy be equal to using.
  397. auto location_strategy = using_;
  398. // 3. Let selector be equal to value.
  399. auto selector = value;
  400. // 4. Let elements returned be the result of trying to call the relevant element location strategy with arguments start node, and selector.
  401. auto elements = Web::WebDriver::invoke_location_strategy(location_strategy, start_node, selector);
  402. // 5. If a DOMException, SyntaxError, XPathException, or other error occurs during the execution of the element location strategy, return error invalid selector.
  403. if (elements.is_error())
  404. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSelector, String::formatted("The location strategy could not finish: {}", elements.error().message));
  405. // FIXME: 6. If elements returned is empty and the current time is less than end time return to step 4. Otherwise, continue to the next step.
  406. // 7. Let result be an empty JSON List.
  407. JsonArray result;
  408. result.ensure_capacity(elements.value()->length());
  409. // 8. For each element in elements returned, append the web element reference object for element, to result.
  410. for (size_t i = 0; i < elements.value()->length(); ++i)
  411. result.append(web_element_reference_object(*elements.value()->item(i)));
  412. // 9. Return success with data result.
  413. return result;
  414. }
  415. }