WebDriverConnection.cpp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  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 <LibJS/Runtime/JSONObject.h>
  14. #include <LibJS/Runtime/Value.h>
  15. #include <LibWeb/CSS/PropertyID.h>
  16. #include <LibWeb/CSS/StyleProperties.h>
  17. #include <LibWeb/CSS/StyleValue.h>
  18. #include <LibWeb/DOM/Document.h>
  19. #include <LibWeb/DOM/Element.h>
  20. #include <LibWeb/Geometry/DOMRect.h>
  21. #include <LibWeb/HTML/AttributeNames.h>
  22. #include <LibWeb/HTML/BrowsingContext.h>
  23. #include <LibWeb/HTML/FormAssociatedElement.h>
  24. #include <LibWeb/HTML/HTMLInputElement.h>
  25. #include <LibWeb/HTML/HTMLOptionElement.h>
  26. #include <LibWeb/Page/Page.h>
  27. #include <LibWeb/Platform/EventLoopPlugin.h>
  28. #include <LibWeb/Platform/Timer.h>
  29. #include <LibWeb/WebDriver/Screenshot.h>
  30. #include <WebContent/ConnectionFromClient.h>
  31. #include <WebContent/PageHost.h>
  32. #include <WebContent/WebDriverConnection.h>
  33. namespace WebContent {
  34. static JsonValue make_success_response(JsonValue value)
  35. {
  36. JsonObject result;
  37. result.set("value", move(value));
  38. return result;
  39. }
  40. static JsonValue serialize_rect(Gfx::IntRect const& rect)
  41. {
  42. JsonObject serialized_rect = {};
  43. serialized_rect.set("x", rect.x());
  44. serialized_rect.set("y", rect.y());
  45. serialized_rect.set("width", rect.width());
  46. serialized_rect.set("height", rect.height());
  47. return make_success_response(move(serialized_rect));
  48. }
  49. static Gfx::IntRect compute_window_rect(Web::Page const& page)
  50. {
  51. return {
  52. page.window_position().x(),
  53. page.window_position().y(),
  54. page.window_size().width(),
  55. page.window_size().height()
  56. };
  57. }
  58. // https://w3c.github.io/webdriver/#dfn-calculate-the-absolute-position
  59. static Gfx::IntPoint calculate_absolute_position_of_element(Web::Page const& page, JS::NonnullGCPtr<Web::Geometry::DOMRect> rect)
  60. {
  61. // 1. Let rect be the value returned by calling getBoundingClientRect().
  62. // 2. Let window be the associated window of current top-level browsing context.
  63. auto const* window = page.top_level_browsing_context().active_window();
  64. // 3. Let x be (scrollX of window + rect’s x coordinate).
  65. auto x = (window ? static_cast<int>(window->scroll_x()) : 0) + static_cast<int>(rect->x());
  66. // 4. Let y be (scrollY of window + rect’s y coordinate).
  67. auto y = (window ? static_cast<int>(window->scroll_y()) : 0) + static_cast<int>(rect->y());
  68. // 5. Return a pair of (x, y).
  69. return { x, y };
  70. }
  71. static Gfx::IntRect calculate_absolute_rect_of_element(Web::Page const& page, Web::DOM::Element const& element)
  72. {
  73. auto bounding_rect = element.get_bounding_client_rect();
  74. auto coordinates = calculate_absolute_position_of_element(page, bounding_rect);
  75. return {
  76. coordinates.x(),
  77. coordinates.y(),
  78. static_cast<int>(bounding_rect->width()),
  79. static_cast<int>(bounding_rect->height())
  80. };
  81. }
  82. // https://w3c.github.io/webdriver/#dfn-get-or-create-a-web-element-reference
  83. static String get_or_create_a_web_element_reference(Web::DOM::Node const& element)
  84. {
  85. // FIXME: 1. For each known element of the current browsing context’s list of known elements:
  86. // FIXME: 1. If known element equals element, return success with known element’s web element reference.
  87. // FIXME: 2. Add element to the list of known elements of the current browsing context.
  88. // FIXME: 3. Return success with the element’s web element reference.
  89. return String::number(element.id());
  90. }
  91. // https://w3c.github.io/webdriver/#dfn-web-element-reference-object
  92. static JsonObject web_element_reference_object(Web::DOM::Node const& element)
  93. {
  94. // https://w3c.github.io/webdriver/#dfn-web-element-identifier
  95. static String const web_element_identifier = "element-6066-11e4-a52e-4f735466cecf"sv;
  96. // 1. Let identifier be the web element identifier.
  97. auto identifier = web_element_identifier;
  98. // 2. Let reference be the result of get or create a web element reference given element.
  99. auto reference = get_or_create_a_web_element_reference(element);
  100. // 3. Return a JSON Object initialized with a property with name identifier and value reference.
  101. JsonObject object;
  102. object.set("name"sv, identifier);
  103. object.set("value"sv, reference);
  104. return object;
  105. }
  106. // https://w3c.github.io/webdriver/#dfn-get-a-known-connected-element
  107. static ErrorOr<Web::DOM::Element*, Web::WebDriver::Error> get_known_connected_element(StringView element_id)
  108. {
  109. // NOTE: The whole concept of "connected elements" is not implemented yet. See get_or_create_a_web_element_reference().
  110. // For now the element is only represented by its ID.
  111. auto element = element_id.to_int();
  112. if (!element.has_value())
  113. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Element ID is not an integer");
  114. auto* node = Web::DOM::Node::from_id(*element);
  115. if (!node || !node->is_element())
  116. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, String::formatted("Could not find element with ID: {}", element_id));
  117. return static_cast<Web::DOM::Element*>(node);
  118. }
  119. // https://w3c.github.io/webdriver/#dfn-scrolls-into-view
  120. static void scroll_element_into_view(Web::DOM::Element& element)
  121. {
  122. // 1. Let options be the following ScrollIntoViewOptions:
  123. Web::DOM::ScrollIntoViewOptions options {};
  124. // Logical scroll position "block"
  125. // "end"
  126. options.block = Web::Bindings::ScrollLogicalPosition::End;
  127. // Logical scroll position "inline"
  128. // "nearest"
  129. options.inline_ = Web::Bindings::ScrollLogicalPosition::Nearest;
  130. // 2. Run Function.[[Call]](scrollIntoView, options) with element as the this value.
  131. element.scroll_into_view(options);
  132. }
  133. template<typename PropertyType = String>
  134. static ErrorOr<PropertyType, Web::WebDriver::Error> get_property(JsonValue const& payload, StringView key)
  135. {
  136. if (!payload.is_object())
  137. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
  138. auto const* property = payload.as_object().get_ptr(key);
  139. if (!property)
  140. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("No property called '{}' present", key));
  141. if constexpr (IsSame<PropertyType, String>) {
  142. if (!property->is_string())
  143. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not a String", key));
  144. return property->as_string();
  145. } else if constexpr (IsSame<PropertyType, JsonArray const*>) {
  146. if (!property->is_array())
  147. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not an Array", key));
  148. return &property->as_array();
  149. } else {
  150. static_assert(DependentFalse<PropertyType>, "get_property invoked with unknown property type");
  151. VERIFY_NOT_REACHED();
  152. }
  153. }
  154. ErrorOr<NonnullRefPtr<WebDriverConnection>> WebDriverConnection::connect(ConnectionFromClient& web_content_client, PageHost& page_host, String const& webdriver_ipc_path)
  155. {
  156. dbgln_if(WEBDRIVER_DEBUG, "Trying to connect to {}", webdriver_ipc_path);
  157. auto socket = TRY(Core::Stream::LocalSocket::connect(webdriver_ipc_path));
  158. dbgln_if(WEBDRIVER_DEBUG, "Connected to WebDriver");
  159. return adopt_nonnull_ref_or_enomem(new (nothrow) WebDriverConnection(move(socket), web_content_client, page_host));
  160. }
  161. WebDriverConnection::WebDriverConnection(NonnullOwnPtr<Core::Stream::LocalSocket> socket, ConnectionFromClient& web_content_client, PageHost& page_host)
  162. : IPC::ConnectionToServer<WebDriverClientEndpoint, WebDriverServerEndpoint>(*this, move(socket))
  163. , m_web_content_client(web_content_client)
  164. , m_page_host(page_host)
  165. {
  166. }
  167. // https://w3c.github.io/webdriver/#dfn-close-the-session
  168. void WebDriverConnection::close_session()
  169. {
  170. // 1. Set the webdriver-active flag to false.
  171. set_is_webdriver_active(false);
  172. // 2. An endpoint node must close any top-level browsing contexts associated with the session, without prompting to unload.
  173. m_page_host.page().top_level_browsing_context().close();
  174. }
  175. void WebDriverConnection::set_is_webdriver_active(bool is_webdriver_active)
  176. {
  177. m_page_host.set_is_webdriver_active(is_webdriver_active);
  178. }
  179. // 10.1 Navigate To, https://w3c.github.io/webdriver/#navigate-to
  180. Messages::WebDriverClient::NavigateToResponse WebDriverConnection::navigate_to(JsonValue const& payload)
  181. {
  182. dbgln_if(WEBDRIVER_DEBUG, "WebDriverConnection::navigate_to {}", payload);
  183. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  184. TRY(ensure_open_top_level_browsing_context());
  185. // 2. Let url be the result of getting the property url from the parameters argument.
  186. if (!payload.is_object() || !payload.as_object().has_string("url"sv))
  187. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload doesn't have a string `url`"sv);
  188. URL url(payload.as_object().get_ptr("url"sv)->as_string());
  189. // 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.
  190. // FIXME: 4. Handle any user prompts and return its value if it is an error.
  191. // FIXME: 5. Let current URL be the current top-level browsing context’s active document’s URL.
  192. // FIXME: 6. If current URL and url do not have the same absolute URL:
  193. // 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.
  194. // 7. Navigate the current top-level browsing context to url.
  195. m_page_host.page().load(url);
  196. // FIXME: 8. If url is special except for file and current URL and URL do not have the same absolute URL:
  197. // FIXME: a. Try to wait for navigation to complete.
  198. // FIXME: b. Try to run the post-navigation checks.
  199. // FIXME: 9. Set the current browsing context with the current top-level browsing context.
  200. // 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.
  201. // 11. Return success with data null.
  202. return make_success_response({});
  203. }
  204. // 10.2 Get Current URL, https://w3c.github.io/webdriver/#get-current-url
  205. Messages::WebDriverClient::GetCurrentUrlResponse WebDriverConnection::get_current_url()
  206. {
  207. dbgln_if(WEBDRIVER_DEBUG, "WebDriverConnection::get_current_url");
  208. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  209. TRY(ensure_open_top_level_browsing_context());
  210. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  211. // 3. Let url be the serialization of the current top-level browsing context’s active document’s document URL.
  212. auto url = m_page_host.page().top_level_browsing_context().active_document()->url().to_string();
  213. // 4. Return success with data url.
  214. return make_success_response(url);
  215. }
  216. // 11.8.1 Get Window Rect, https://w3c.github.io/webdriver/#dfn-get-window-rect
  217. Messages::WebDriverClient::GetWindowRectResponse WebDriverConnection::get_window_rect()
  218. {
  219. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  220. TRY(ensure_open_top_level_browsing_context());
  221. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  222. // 3. Return success with data set to the WindowRect object for the current top-level browsing context.
  223. return serialize_rect(compute_window_rect(m_page_host.page()));
  224. }
  225. // 11.8.2 Set Window Rect, https://w3c.github.io/webdriver/#dfn-set-window-rect
  226. Messages::WebDriverClient::SetWindowRectResponse WebDriverConnection::set_window_rect(JsonValue const& payload)
  227. {
  228. if (!payload.is_object())
  229. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
  230. auto const& properties = payload.as_object();
  231. auto resolve_property = [](auto name, auto const* property, auto min, auto max) -> ErrorOr<Optional<i32>, Web::WebDriver::Error> {
  232. if (!property)
  233. return Optional<i32> {};
  234. if (!property->is_number())
  235. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not a Number", name));
  236. auto number = property->template to_number<i64>();
  237. if (number < min)
  238. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' value {} exceeds the minimum allowed value {}", name, number, min));
  239. if (number > max)
  240. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' value {} exceeds the maximum allowed value {}", name, number, max));
  241. return static_cast<i32>(number);
  242. };
  243. // 1. Let width be the result of getting a property named width from the parameters argument, else let it be null.
  244. auto const* width_property = properties.get_ptr("width"sv);
  245. // 2. Let height be the result of getting a property named height from the parameters argument, else let it be null.
  246. auto const* height_property = properties.get_ptr("height"sv);
  247. // 3. Let x be the result of getting a property named x from the parameters argument, else let it be null.
  248. auto const* x_property = properties.get_ptr("x"sv);
  249. // 4. Let y be the result of getting a property named y from the parameters argument, else let it be null.
  250. auto const* y_property = properties.get_ptr("y"sv);
  251. // 5. If width or height is neither null nor a Number from 0 to 2^31 − 1, return error with error code invalid argument.
  252. auto width = TRY(resolve_property("width"sv, width_property, 0, NumericLimits<i32>::max()));
  253. auto height = TRY(resolve_property("height"sv, height_property, 0, NumericLimits<i32>::max()));
  254. // 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.
  255. auto x = TRY(resolve_property("x"sv, x_property, NumericLimits<i32>::min(), NumericLimits<i32>::max()));
  256. auto y = TRY(resolve_property("y"sv, y_property, NumericLimits<i32>::min(), NumericLimits<i32>::max()));
  257. // 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.
  258. // 8. If the current top-level browsing context is no longer open, return error with error code no such window.
  259. TRY(ensure_open_top_level_browsing_context());
  260. // FIXME: 9. Handle any user prompts and return its value if it is an error.
  261. // FIXME: 10. Fully exit fullscreen.
  262. // 11. Restore the window.
  263. restore_the_window();
  264. Gfx::IntRect window_rect;
  265. // 11. If width and height are not null:
  266. if (width.has_value() && height.has_value()) {
  267. // 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.
  268. // 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.
  269. auto size = m_web_content_client.did_request_resize_window({ *width, *height });
  270. window_rect.set_size(size);
  271. } else {
  272. window_rect.set_size(m_page_host.page().window_size());
  273. }
  274. // 12. If x and y are not null:
  275. if (x.has_value() && y.has_value()) {
  276. // 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.
  277. auto position = m_web_content_client.did_request_reposition_window({ *x, *y });
  278. window_rect.set_location(position);
  279. } else {
  280. window_rect.set_location(m_page_host.page().window_position());
  281. }
  282. // 14. Return success with data set to the WindowRect object for the current top-level browsing context.
  283. return serialize_rect(window_rect);
  284. }
  285. // 11.8.3 Maximize Window, https://w3c.github.io/webdriver/#dfn-maximize-window
  286. Messages::WebDriverClient::MaximizeWindowResponse WebDriverConnection::maximize_window()
  287. {
  288. // 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.
  289. // 2. If the current top-level browsing context is no longer open, return error with error code no such window.
  290. TRY(ensure_open_top_level_browsing_context());
  291. // FIXME: 3. Handle any user prompts and return its value if it is an error.
  292. // FIXME: 4. Fully exit fullscreen.
  293. // 5. Restore the window.
  294. restore_the_window();
  295. // 6. Maximize the window of the current top-level browsing context.
  296. auto window_rect = maximize_the_window();
  297. // 7. Return success with data set to the WindowRect object for the current top-level browsing context.
  298. return serialize_rect(window_rect);
  299. }
  300. // 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
  301. Messages::WebDriverClient::MinimizeWindowResponse WebDriverConnection::minimize_window()
  302. {
  303. // 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.
  304. // 2. If the current top-level browsing context is no longer open, return error with error code no such window.
  305. TRY(ensure_open_top_level_browsing_context());
  306. // FIXME: 3. Handle any user prompts and return its value if it is an error.
  307. // FIXME: 4. Fully exit fullscreen.
  308. // 5. Iconify the window.
  309. auto window_rect = iconify_the_window();
  310. // 6. Return success with data set to the WindowRect object for the current top-level browsing context.
  311. return serialize_rect(window_rect);
  312. }
  313. // 12.3.2 Find Element, https://w3c.github.io/webdriver/#dfn-find-element
  314. Messages::WebDriverClient::FindElementResponse WebDriverConnection::find_element(JsonValue const& payload)
  315. {
  316. // 1. Let location strategy be the result of getting a property called "using".
  317. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  318. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  319. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  320. if (!location_strategy.has_value())
  321. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  322. // 3. Let selector be the result of getting a property called "value".
  323. // 4. If selector is undefined, return error with error code invalid argument.
  324. auto selector = TRY(get_property(payload, "value"sv));
  325. // 5. If the current browsing context is no longer open, return error with error code no such window.
  326. TRY(ensure_open_top_level_browsing_context());
  327. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  328. // 7. Let start node be the current browsing context’s document element.
  329. auto* start_node = m_page_host.page().top_level_browsing_context().active_document();
  330. // 8. If start node is null, return error with error code no such element.
  331. if (!start_node)
  332. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "document element does not exist"sv);
  333. // 9. Let result be the result of trying to Find with start node, location strategy, and selector.
  334. auto result = TRY(find(*start_node, *location_strategy, selector));
  335. // 10. If result is empty, return error with error code no such element. Otherwise, return the first element of result.
  336. if (result.is_empty())
  337. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The requested element does not exist"sv);
  338. return make_success_response(result.at(0));
  339. }
  340. // 12.3.3 Find Elements, https://w3c.github.io/webdriver/#dfn-find-elements
  341. Messages::WebDriverClient::FindElementsResponse WebDriverConnection::find_elements(JsonValue const& payload)
  342. {
  343. // 1. Let location strategy be the result of getting a property called "using".
  344. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  345. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  346. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  347. if (!location_strategy.has_value())
  348. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  349. // 3. Let selector be the result of getting a property called "value".
  350. // 4. If selector is undefined, return error with error code invalid argument.
  351. auto selector = TRY(get_property(payload, "value"sv));
  352. // 5. If the current browsing context is no longer open, return error with error code no such window.
  353. TRY(ensure_open_top_level_browsing_context());
  354. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  355. // 7. Let start node be the current browsing context’s document element.
  356. auto* start_node = m_page_host.page().top_level_browsing_context().active_document();
  357. // 8. If start node is null, return error with error code no such element.
  358. if (!start_node)
  359. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "document element does not exist"sv);
  360. // 9. Return the result of trying to Find with start node, location strategy, and selector.
  361. auto result = TRY(find(*start_node, *location_strategy, selector));
  362. return make_success_response(move(result));
  363. }
  364. // 12.3.4 Find Element From Element, https://w3c.github.io/webdriver/#dfn-find-element-from-element
  365. Messages::WebDriverClient::FindElementFromElementResponse WebDriverConnection::find_element_from_element(JsonValue const& payload, String const& element_id)
  366. {
  367. // 1. Let location strategy be the result of getting a property called "using".
  368. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  369. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  370. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  371. if (!location_strategy.has_value())
  372. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  373. // 3. Let selector be the result of getting a property called "value".
  374. // 4. If selector is undefined, return error with error code invalid argument.
  375. auto selector = TRY(get_property(payload, "value"sv));
  376. // 5. If the current browsing context is no longer open, return error with error code no such window.
  377. TRY(ensure_open_top_level_browsing_context());
  378. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  379. // 7. Let start node be the result of trying to get a known connected element with url variable element id.
  380. auto* start_node = TRY(get_known_connected_element(element_id));
  381. // 8. Let result be the value of trying to Find with start node, location strategy, and selector.
  382. auto result = TRY(find(*start_node, *location_strategy, selector));
  383. // 9. If result is empty, return error with error code no such element. Otherwise, return the first element of result.
  384. if (result.is_empty())
  385. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The requested element does not exist"sv);
  386. return make_success_response(result.at(0));
  387. }
  388. // 12.3.5 Find Elements From Element, https://w3c.github.io/webdriver/#dfn-find-elements-from-element
  389. Messages::WebDriverClient::FindElementsFromElementResponse WebDriverConnection::find_elements_from_element(JsonValue const& payload, String const& element_id)
  390. {
  391. // 1. Let location strategy be the result of getting a property called "using".
  392. auto location_strategy_string = TRY(get_property(payload, "using"sv));
  393. auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
  394. // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
  395. if (!location_strategy.has_value())
  396. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string));
  397. // 3. Let selector be the result of getting a property called "value".
  398. // 4. If selector is undefined, return error with error code invalid argument.
  399. auto selector = TRY(get_property(payload, "value"sv));
  400. // 5. If the current browsing context is no longer open, return error with error code no such window.
  401. TRY(ensure_open_top_level_browsing_context());
  402. // FIXME: 6. Handle any user prompts and return its value if it is an error.
  403. // 7. Let start node be the result of trying to get a known connected element with url variable element id.
  404. auto* start_node = TRY(get_known_connected_element(element_id));
  405. // 8. Return the result of trying to Find with start node, location strategy, and selector.
  406. auto result = TRY(find(*start_node, *location_strategy, selector));
  407. return make_success_response(move(result));
  408. }
  409. // 12.4.1 Is Element Selected, https://w3c.github.io/webdriver/#dfn-is-element-selected
  410. Messages::WebDriverClient::IsElementSelectedResponse WebDriverConnection::is_element_selected(String const& element_id)
  411. {
  412. // 1. If the current browsing context is no longer open, return error with error code no such window.
  413. TRY(ensure_open_top_level_browsing_context());
  414. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  415. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  416. auto* element = TRY(get_known_connected_element(element_id));
  417. // 4. Let selected be the value corresponding to the first matching statement:
  418. bool selected = false;
  419. // element is an input element with a type attribute in the Checkbox- or Radio Button state
  420. if (is<Web::HTML::HTMLInputElement>(*element)) {
  421. // -> The result of element’s checkedness.
  422. auto& input = static_cast<Web::HTML::HTMLInputElement&>(*element);
  423. using enum Web::HTML::HTMLInputElement::TypeAttributeState;
  424. if (input.type_state() == Checkbox || input.type_state() == RadioButton)
  425. selected = input.checked();
  426. }
  427. // element is an option element
  428. else if (is<Web::HTML::HTMLOptionElement>(*element)) {
  429. // -> The result of element’s selectedness.
  430. selected = static_cast<Web::HTML::HTMLOptionElement&>(*element).selected();
  431. }
  432. // Otherwise
  433. // -> False.
  434. // 5. Return success with data selected.
  435. return make_success_response(selected);
  436. }
  437. // 12.4.2 Get Element Attribute, https://w3c.github.io/webdriver/#dfn-get-element-attribute
  438. Messages::WebDriverClient::GetElementAttributeResponse WebDriverConnection::get_element_attribute(String const& element_id, String const& name)
  439. {
  440. // 1. If the current browsing context is no longer open, return error with error code no such window.
  441. TRY(ensure_open_top_level_browsing_context());
  442. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  443. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  444. auto* element = TRY(get_known_connected_element(element_id));
  445. // 4. Let result be the result of the first matching condition:
  446. Optional<String> result;
  447. // -> If name is a boolean attribute
  448. if (Web::HTML::is_boolean_attribute(name)) {
  449. // "true" (string) if the element has the attribute, otherwise null.
  450. if (element->has_attribute(name))
  451. result = "true"sv;
  452. }
  453. // -> Otherwise
  454. else {
  455. // The result of getting an attribute by name name.
  456. result = element->get_attribute(name);
  457. }
  458. // 5. Return success with data result.
  459. if (result.has_value())
  460. return make_success_response(result.release_value());
  461. return make_success_response({});
  462. }
  463. // 12.4.3 Get Element Property, https://w3c.github.io/webdriver/#dfn-get-element-property
  464. Messages::WebDriverClient::GetElementPropertyResponse WebDriverConnection::get_element_property(String const& element_id, String const& name)
  465. {
  466. // 1. If the current browsing context is no longer open, return error with error code no such window.
  467. TRY(ensure_open_top_level_browsing_context());
  468. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  469. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  470. auto* element = TRY(get_known_connected_element(element_id));
  471. Optional<String> result;
  472. // 4. Let property be the result of calling the Object.[[GetProperty]](name) on element.
  473. if (auto property_or_error = element->get(name); !property_or_error.is_throw_completion()) {
  474. auto property = property_or_error.release_value();
  475. // 5. Let result be the value of property if not undefined, or null.
  476. if (!property.is_undefined()) {
  477. if (auto string_or_error = property.to_string(element->vm()); !string_or_error.is_error())
  478. result = string_or_error.release_value();
  479. }
  480. }
  481. // 6. Return success with data result.
  482. if (result.has_value())
  483. return make_success_response(result.release_value());
  484. return make_success_response({});
  485. }
  486. // 12.4.4 Get Element CSS Value, https://w3c.github.io/webdriver/#dfn-get-element-css-value
  487. Messages::WebDriverClient::GetElementCssValueResponse WebDriverConnection::get_element_css_value(String const& element_id, String const& name)
  488. {
  489. // 1. If the current browsing context is no longer open, return error with error code no such window.
  490. TRY(ensure_open_top_level_browsing_context());
  491. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  492. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  493. auto* element = TRY(get_known_connected_element(element_id));
  494. // 4. Let computed value be the result of the first matching condition:
  495. String computed_value;
  496. // -> current browsing context’s active document’s type is not "xml"
  497. if (!m_page_host.page().top_level_browsing_context().active_document()->is_xml_document()) {
  498. // computed value of parameter property name from element’s style declarations. property name is obtained from url variables.
  499. auto property = Web::CSS::property_id_from_string(name);
  500. if (auto* computed_values = element->computed_css_values())
  501. computed_value = computed_values->property(property)->to_string();
  502. }
  503. // -> Otherwise
  504. else {
  505. // "" (empty string)
  506. computed_value = String::empty();
  507. }
  508. // 5. Return success with data computed value.
  509. return make_success_response(move(computed_value));
  510. }
  511. // 12.4.5 Get Element Text, https://w3c.github.io/webdriver/#dfn-get-element-text
  512. Messages::WebDriverClient::GetElementTextResponse WebDriverConnection::get_element_text(String const& element_id)
  513. {
  514. // 1. If the current browsing context is no longer open, return error with error code no such window.
  515. TRY(ensure_open_top_level_browsing_context());
  516. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  517. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  518. auto* element = TRY(get_known_connected_element(element_id));
  519. // 4. Let rendered text be the result of performing implementation-specific steps whose result is exactly the same as the result of a Function.[[Call]](null, element) with bot.dom.getVisibleText as the this value.
  520. auto rendered_text = element->text_content();
  521. // 5. Return success with data rendered text.
  522. return make_success_response(move(rendered_text));
  523. }
  524. // 12.4.6 Get Element Tag Name, https://w3c.github.io/webdriver/#dfn-get-element-tag-name
  525. Messages::WebDriverClient::GetElementTagNameResponse WebDriverConnection::get_element_tag_name(String const& element_id)
  526. {
  527. // 1. If the current browsing context is no longer open, return error with error code no such window.
  528. TRY(ensure_open_top_level_browsing_context());
  529. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  530. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  531. auto* element = TRY(get_known_connected_element(element_id));
  532. // 4. Let qualified name be the result of getting element’s tagName IDL attribute.
  533. auto qualified_name = element->tag_name();
  534. // 5. Return success with data qualified name.
  535. return make_success_response(move(qualified_name));
  536. }
  537. // 12.4.7 Get Element Rect, https://w3c.github.io/webdriver/#dfn-get-element-rect
  538. Messages::WebDriverClient::GetElementRectResponse WebDriverConnection::get_element_rect(String const& element_id)
  539. {
  540. // 1. If the current browsing context is no longer open, return error with error code no such window.
  541. TRY(ensure_open_top_level_browsing_context());
  542. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  543. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  544. auto* element = TRY(get_known_connected_element(element_id));
  545. // 4. Calculate the absolute position of element and let it be coordinates.
  546. // 5. Let rect be element’s bounding rectangle.
  547. auto rect = calculate_absolute_rect_of_element(m_page_host.page(), *element);
  548. // 6. Let body be a new JSON Object initialized with:
  549. // "x"
  550. // The first value of coordinates.
  551. // "y"
  552. // The second value of coordinates.
  553. // "width"
  554. // Value of rect’s width dimension.
  555. // "height"
  556. // Value of rect’s height dimension.
  557. auto body = serialize_rect(rect);
  558. // 7. Return success with data body.
  559. return body;
  560. }
  561. // 12.4.8 Is Element Enabled, https://w3c.github.io/webdriver/#dfn-is-element-enabled
  562. Messages::WebDriverClient::IsElementEnabledResponse WebDriverConnection::is_element_enabled(String const& element_id)
  563. {
  564. // 1. If the current browsing context is no longer open, return error with error code no such window.
  565. TRY(ensure_open_top_level_browsing_context());
  566. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  567. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  568. auto* element = TRY(get_known_connected_element(element_id));
  569. // 4. Let enabled be a boolean initially set to true if the current browsing context’s active document’s type is not "xml".
  570. // 5. Otherwise, let enabled to false and jump to the last step of this algorithm.
  571. bool enabled = !m_page_host.page().top_level_browsing_context().active_document()->is_xml_document();
  572. // 6. Set enabled to false if a form control is disabled.
  573. if (enabled && is<Web::HTML::FormAssociatedElement>(*element)) {
  574. auto& form_associated_element = dynamic_cast<Web::HTML::FormAssociatedElement&>(*element);
  575. enabled = form_associated_element.enabled();
  576. }
  577. // 7. Return success with data enabled.
  578. return make_success_response(enabled);
  579. }
  580. // 13.1 Get Page Source, https://w3c.github.io/webdriver/#dfn-get-page-source
  581. Messages::WebDriverClient::GetSourceResponse WebDriverConnection::get_source()
  582. {
  583. // 1. If the current browsing context is no longer open, return error with error code no such window.
  584. TRY(ensure_open_top_level_browsing_context());
  585. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  586. auto* document = m_page_host.page().top_level_browsing_context().active_document();
  587. Optional<String> source;
  588. // 3. Let source be the result of invoking the fragment serializing algorithm on a fictional node whose only child is the document element providing true for the require well-formed flag. If this causes an exception to be thrown, let source be null.
  589. if (auto result = document->serialize_fragment(Web::DOMParsing::RequireWellFormed::Yes); !result.is_error())
  590. source = result.release_value();
  591. // 4. Let source be the result of serializing to string the current browsing context active document, if source is null.
  592. if (!source.has_value())
  593. source = MUST(document->serialize_fragment(Web::DOMParsing::RequireWellFormed::No));
  594. // 5. Return success with data source.
  595. return make_success_response(source.release_value());
  596. }
  597. // 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
  598. Messages::WebDriverClient::ExecuteScriptResponse WebDriverConnection::execute_script(JsonValue const& payload)
  599. {
  600. // 1. Let body and arguments be the result of trying to extract the script arguments from a request with argument parameters.
  601. auto const& [body, arguments] = TRY(extract_the_script_arguments_from_a_request(payload));
  602. // 2. If the current browsing context is no longer open, return error with error code no such window.
  603. TRY(ensure_open_top_level_browsing_context());
  604. // FIXME: 3. Handle any user prompts, and return its value if it is an error.
  605. // 4., 5.1-5.3.
  606. // FIXME: Move timeouts from WebDriver to WebContent and pass the script timeout through here.
  607. auto result = Web::WebDriver::execute_script(m_page_host.page(), body, move(arguments), {});
  608. dbgln_if(WEBDRIVER_DEBUG, "Executing script returned: {}", result.value);
  609. switch (result.type) {
  610. // 6. If promise is still pending and the session script timeout is reached, return error with error code script timeout.
  611. case Web::WebDriver::ExecuteScriptResultType::Timeout:
  612. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ScriptTimeoutError, "Script timed out");
  613. // 7. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
  614. case Web::WebDriver::ExecuteScriptResultType::PromiseResolved:
  615. return make_success_response(move(result.value));
  616. // 8. Upon rejection of promise with reason r, let result be a JSON clone of r, and return error with error code javascript error and data result.
  617. case Web::WebDriver::ExecuteScriptResultType::PromiseRejected:
  618. case Web::WebDriver::ExecuteScriptResultType::JavaScriptError:
  619. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::JavascriptError, "Script returned an error", move(result.value));
  620. }
  621. VERIFY_NOT_REACHED();
  622. }
  623. // 17.1 Take Screenshot, https://w3c.github.io/webdriver/#take-screenshot
  624. Messages::WebDriverClient::TakeScreenshotResponse WebDriverConnection::take_screenshot()
  625. {
  626. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  627. TRY(ensure_open_top_level_browsing_context());
  628. // 2. When the user agent is next to run the animation frame callbacks:
  629. // a. Let root rect be the current top-level browsing context’s document element’s rectangle.
  630. // b. Let screenshot result be the result of trying to call draw a bounding box from the framebuffer, given root rect as an argument.
  631. // c. Let canvas be a canvas element of screenshot result’s data.
  632. // d. Let encoding result be the result of trying encoding a canvas as Base64 canvas.
  633. // e. Let encoded string be encoding result’s data.
  634. auto* document = m_page_host.page().top_level_browsing_context().active_document();
  635. auto root_rect = calculate_absolute_rect_of_element(m_page_host.page(), *document->document_element());
  636. auto encoded_string = TRY(Web::WebDriver::capture_element_screenshot(
  637. [&](auto const& rect, auto& bitmap) { m_page_host.paint(rect, bitmap); },
  638. m_page_host.page(),
  639. *document->document_element(),
  640. root_rect));
  641. // 3. Return success with data encoded string.
  642. return make_success_response(move(encoded_string));
  643. }
  644. // 17.2 Take Element Screenshot, https://w3c.github.io/webdriver/#dfn-take-element-screenshot
  645. Messages::WebDriverClient::TakeElementScreenshotResponse WebDriverConnection::take_element_screenshot(String const& element_id)
  646. {
  647. // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
  648. TRY(ensure_open_top_level_browsing_context());
  649. // FIXME: 2. Handle any user prompts and return its value if it is an error.
  650. // 3. Let element be the result of trying to get a known connected element with url variable element id.
  651. auto* element = TRY(get_known_connected_element(element_id));
  652. // 4. Scroll into view the element.
  653. scroll_element_into_view(*element);
  654. // 5. When the user agent is next to run the animation frame callbacks:
  655. // a. Let element rect be element’s rectangle.
  656. // b. Let screenshot result be the result of trying to call draw a bounding box from the framebuffer, given element rect as an argument.
  657. // c. Let canvas be a canvas element of screenshot result’s data.
  658. // d. Let encoding result be the result of trying encoding a canvas as Base64 canvas.
  659. // e. Let encoded string be encoding result’s data.
  660. auto element_rect = calculate_absolute_rect_of_element(m_page_host.page(), *element);
  661. auto encoded_string = TRY(Web::WebDriver::capture_element_screenshot(
  662. [&](auto const& rect, auto& bitmap) { m_page_host.paint(rect, bitmap); },
  663. m_page_host.page(),
  664. *element,
  665. element_rect));
  666. // 6. Return success with data encoded string.
  667. return make_success_response(move(encoded_string));
  668. }
  669. // https://w3c.github.io/webdriver/#dfn-no-longer-open
  670. ErrorOr<void, Web::WebDriver::Error> WebDriverConnection::ensure_open_top_level_browsing_context()
  671. {
  672. // A browsing context is said to be no longer open if it has been discarded.
  673. if (m_page_host.page().top_level_browsing_context().has_been_discarded())
  674. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchWindow, "Window not found"sv);
  675. return {};
  676. }
  677. // https://w3c.github.io/webdriver/#dfn-restore-the-window
  678. void WebDriverConnection::restore_the_window()
  679. {
  680. // 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.
  681. m_web_content_client.async_did_request_restore_window();
  682. // 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.
  683. // FIXME: Implement timeouts.
  684. Web::Platform::EventLoopPlugin::the().spin_until([this]() {
  685. auto state = m_page_host.page().top_level_browsing_context().system_visibility_state();
  686. return state == Web::HTML::VisibilityState::Visible;
  687. });
  688. }
  689. // https://w3c.github.io/webdriver/#dfn-maximize-the-window
  690. Gfx::IntRect WebDriverConnection::maximize_the_window()
  691. {
  692. // 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.
  693. auto rect = m_web_content_client.did_request_maximize_window();
  694. // Return when the window has completed the transition, or within an implementation-defined timeout.
  695. return rect;
  696. }
  697. // https://w3c.github.io/webdriver/#dfn-iconify-the-window
  698. Gfx::IntRect WebDriverConnection::iconify_the_window()
  699. {
  700. // 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.
  701. auto rect = m_web_content_client.did_request_minimize_window();
  702. // 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.
  703. // FIXME: Implement timeouts.
  704. Web::Platform::EventLoopPlugin::the().spin_until([this]() {
  705. auto state = m_page_host.page().top_level_browsing_context().system_visibility_state();
  706. return state == Web::HTML::VisibilityState::Hidden;
  707. });
  708. return rect;
  709. }
  710. // https://w3c.github.io/webdriver/#dfn-find
  711. ErrorOr<JsonArray, Web::WebDriver::Error> WebDriverConnection::find(Web::DOM::ParentNode& start_node, Web::WebDriver::LocationStrategy using_, StringView value)
  712. {
  713. // FIXME: 1. Let end time be the current time plus the session implicit wait timeout.
  714. // 2. Let location strategy be equal to using.
  715. auto location_strategy = using_;
  716. // 3. Let selector be equal to value.
  717. auto selector = value;
  718. // 4. Let elements returned be the result of trying to call the relevant element location strategy with arguments start node, and selector.
  719. auto elements = Web::WebDriver::invoke_location_strategy(location_strategy, start_node, selector);
  720. // 5. If a DOMException, SyntaxError, XPathException, or other error occurs during the execution of the element location strategy, return error invalid selector.
  721. if (elements.is_error())
  722. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSelector, String::formatted("The location strategy could not finish: {}", elements.error().message));
  723. // 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.
  724. // 7. Let result be an empty JSON List.
  725. JsonArray result;
  726. result.ensure_capacity(elements.value()->length());
  727. // 8. For each element in elements returned, append the web element reference object for element, to result.
  728. for (size_t i = 0; i < elements.value()->length(); ++i)
  729. result.append(web_element_reference_object(*elements.value()->item(i)));
  730. // 9. Return success with data result.
  731. return result;
  732. }
  733. // https://w3c.github.io/webdriver/#dfn-extract-the-script-arguments-from-a-request
  734. ErrorOr<WebDriverConnection::ScriptArguments, Web::WebDriver::Error> WebDriverConnection::extract_the_script_arguments_from_a_request(JsonValue const& payload)
  735. {
  736. auto* window = m_page_host.page().top_level_browsing_context().active_window();
  737. auto& vm = window->vm();
  738. // 1. Let script be the result of getting a property named script from the parameters.
  739. // 2. If script is not a String, return error with error code invalid argument.
  740. auto script = TRY(get_property(payload, "script"sv));
  741. // 3. Let args be the result of getting a property named args from the parameters.
  742. // 4. If args is not an Array return error with error code invalid argument.
  743. auto const& args = *TRY(get_property<JsonArray const*>(payload, "args"sv));
  744. // 5. Let arguments be the result of calling the JSON deserialize algorithm with arguments args.
  745. auto arguments = JS::MarkedVector<JS::Value> { vm.heap() };
  746. args.for_each([&](auto const& arg) {
  747. arguments.append(JS::JSONObject::parse_json_value(vm, arg));
  748. });
  749. // 6. Return success with data script and arguments.
  750. return ScriptArguments { move(script), move(arguments) };
  751. }
  752. }