Actions.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*
  2. * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Enumerate.h>
  7. #include <AK/GenericShorthands.h>
  8. #include <AK/JsonArray.h>
  9. #include <AK/JsonObject.h>
  10. #include <AK/JsonValue.h>
  11. #include <AK/Math.h>
  12. #include <AK/Utf8View.h>
  13. #include <LibWeb/WebDriver/Actions.h>
  14. #include <LibWeb/WebDriver/ElementReference.h>
  15. #include <LibWeb/WebDriver/InputState.h>
  16. #include <LibWeb/WebDriver/Properties.h>
  17. namespace Web::WebDriver {
  18. static Optional<ActionObject::Subtype> action_object_subtype_from_string(StringView action_subtype)
  19. {
  20. if (action_subtype == "pause"sv)
  21. return ActionObject::Subtype::Pause;
  22. if (action_subtype == "keyUp"sv)
  23. return ActionObject::Subtype::KeyUp;
  24. if (action_subtype == "keyDown"sv)
  25. return ActionObject::Subtype::KeyDown;
  26. if (action_subtype == "pointerUp"sv)
  27. return ActionObject::Subtype::PointerUp;
  28. if (action_subtype == "pointerDown"sv)
  29. return ActionObject::Subtype::PointerDown;
  30. if (action_subtype == "pointerMove"sv)
  31. return ActionObject::Subtype::PointerMove;
  32. if (action_subtype == "pointerCancel"sv)
  33. return ActionObject::Subtype::PointerCancel;
  34. if (action_subtype == "scroll"sv)
  35. return ActionObject::Subtype::Scroll;
  36. return {};
  37. }
  38. static ActionObject::Fields fields_from_subtype(ActionObject::Subtype subtype)
  39. {
  40. switch (subtype) {
  41. case ActionObject::Subtype::Pause:
  42. return ActionObject::PauseFields {};
  43. case ActionObject::Subtype::KeyUp:
  44. case ActionObject::Subtype::KeyDown:
  45. return ActionObject::KeyFields {};
  46. case ActionObject::Subtype::PointerUp:
  47. case ActionObject::Subtype::PointerDown:
  48. return ActionObject::PointerUpDownFields {};
  49. case ActionObject::Subtype::PointerMove:
  50. return ActionObject::PointerMoveFields {};
  51. case ActionObject::Subtype::PointerCancel:
  52. return ActionObject::PointerCancelFields {};
  53. case ActionObject::Subtype::Scroll:
  54. return ActionObject::ScrollFields {};
  55. }
  56. VERIFY_NOT_REACHED();
  57. }
  58. ActionObject::ActionObject(String id, InputSourceType type, Subtype subtype)
  59. : id(move(id))
  60. , type(type)
  61. , subtype(subtype)
  62. , fields(fields_from_subtype(subtype))
  63. {
  64. }
  65. void ActionObject::set_pointer_type(PointerInputSource::Subtype pointer_type)
  66. {
  67. fields.visit(
  68. [&](OneOf<PointerUpDownFields, PointerMoveFields, PointerCancelFields> auto& fields) {
  69. fields.pointer_type = pointer_type;
  70. },
  71. [](auto const&) { VERIFY_NOT_REACHED(); });
  72. }
  73. static Optional<ActionObject::Origin> determine_origin(ActionsOptions const& actions_options, Optional<JsonValue const&> const& origin)
  74. {
  75. if (!origin.has_value())
  76. return ActionObject::OriginType::Viewport;
  77. if (origin->is_string()) {
  78. if (origin->as_string() == "viewport"sv)
  79. return ActionObject::OriginType::Viewport;
  80. if (origin->as_string() == "pointer"sv)
  81. return ActionObject::OriginType::Pointer;
  82. }
  83. if (origin->is_object()) {
  84. if (actions_options.is_element_origin(origin->as_object()))
  85. return MUST(String::from_byte_string(extract_web_element_reference(origin->as_object())));
  86. }
  87. return {};
  88. }
  89. // https://w3c.github.io/webdriver/#dfn-process-pointer-parameters
  90. struct PointerParameters {
  91. PointerInputSource::Subtype pointer_type { PointerInputSource::Subtype::Mouse };
  92. };
  93. static ErrorOr<PointerParameters, WebDriver::Error> process_pointer_parameters(Optional<JsonValue const&> const& parameters_data)
  94. {
  95. // 1. Let parameters be the default pointer parameters.
  96. PointerParameters parameters;
  97. // 2. If parameters data is undefined, return success with data parameters.
  98. if (!parameters_data.has_value())
  99. return parameters;
  100. // 3. If parameters data is not an Object, return error with error code invalid argument.
  101. if (!parameters_data->is_object())
  102. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'parameters' is not an Object");
  103. // 4. Let pointer type be the result of getting a property named "pointerType" from parameters data.
  104. auto pointer_type = TRY(get_optional_property(parameters_data->as_object(), "pointerType"sv));
  105. // 5. If pointer type is not undefined:
  106. if (pointer_type.has_value()) {
  107. // 1. If pointer type does not have one of the values "mouse", "pen", or "touch", return error with error code
  108. // invalid argument.
  109. auto parsed_pointer_type = pointer_input_source_subtype_from_string(*pointer_type);
  110. if (!parsed_pointer_type.has_value())
  111. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'pointerType' must be one of 'mouse', 'pen', or 'touch'");
  112. // 2. Set the pointerType property of parameters to pointer type.
  113. parameters.pointer_type = *parsed_pointer_type;
  114. }
  115. // 6. Return success with data parameters.
  116. return parameters;
  117. }
  118. // https://w3c.github.io/webdriver/#dfn-process-a-pause-action
  119. static ErrorOr<void, WebDriver::Error> process_pause_action(JsonObject const& action_item, ActionObject& action)
  120. {
  121. // 1. Let duration be the result of getting the property "duration" from action item.
  122. // 2. If duration is not undefined and duration is not an Integer greater than or equal to 0, return error with error code invalid argument.
  123. // 3. Set the duration property of action to duration.
  124. if (auto duration = TRY(get_optional_property_with_limits<i64>(action_item, "duration"sv, 0, {})); duration.has_value())
  125. action.pause_fields().duration = AK::Duration::from_milliseconds(*duration);
  126. // 4. Return success with data action.
  127. return {};
  128. }
  129. // https://w3c.github.io/webdriver/#dfn-process-a-null-action
  130. static ErrorOr<ActionObject, WebDriver::Error> process_null_action(String id, JsonObject const& action_item)
  131. {
  132. // 1. Let subtype be the result of getting a property named "type" from action item.
  133. auto subtype = action_object_subtype_from_string(TRY(get_property(action_item, "type"sv)));
  134. // 2. If subtype is not "pause", return error with error code invalid argument.
  135. if (subtype != ActionObject::Subtype::Pause)
  136. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'type' must be 'pause'");
  137. // 3. Let action be an action object constructed with arguments id, "none", and subtype.
  138. ActionObject action { move(id), InputSourceType::None, *subtype };
  139. // 4. Let result be the result of trying to process a pause action with arguments action item and action.
  140. TRY(process_pause_action(action_item, action));
  141. // 5. Return result.
  142. return action;
  143. }
  144. // https://w3c.github.io/webdriver/#dfn-process-a-key-action
  145. static ErrorOr<ActionObject, WebDriver::Error> process_key_action(String id, JsonObject const& action_item)
  146. {
  147. using enum ActionObject::Subtype;
  148. // 1. Let subtype be the result of getting a property named "type" from action item.
  149. auto subtype = action_object_subtype_from_string(TRY(get_property(action_item, "type"sv)));
  150. // 2. If subtype is not one of the values "keyUp", "keyDown", or "pause", return an error with error code invalid argument.
  151. if (!first_is_one_of(subtype, KeyUp, KeyDown, Pause))
  152. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'type' must be one of 'keyUp', 'keyDown', or 'pause'");
  153. // 3. Let action be an action object constructed with arguments id, "key", and subtype.
  154. ActionObject action { move(id), InputSourceType::Key, *subtype };
  155. // 4. If subtype is "pause", let result be the result of trying to process a pause action with arguments action
  156. // item and action, and return result.
  157. if (subtype == Pause) {
  158. TRY(process_pause_action(action_item, action));
  159. return action;
  160. }
  161. // 5. Let key be the result of getting a property named "value" from action item.
  162. auto key = TRY(get_property(action_item, "value"sv));
  163. // 6. If key is not a String containing a single unicode code point [or grapheme cluster?] return error with error
  164. // code invalid argument.
  165. if (Utf8View { key }.length() != 1) {
  166. // FIXME: The spec seems undecided on whether grapheme clusters should be supported. Update this step to check
  167. // for graphemes if we end up needing to support them.
  168. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'value' must be a single code point");
  169. }
  170. // 7. Set the value property on action to key.
  171. action.key_fields().value = MUST(String::from_byte_string(key));
  172. // 8. Return success with data action.
  173. return action;
  174. }
  175. // Common steps between:
  176. // https://w3c.github.io/webdriver/#dfn-process-a-pointer-up-or-pointer-down-action
  177. // https://w3c.github.io/webdriver/#dfn-process-a-pointer-move-action
  178. static ErrorOr<void, WebDriver::Error> process_pointer_action_common(JsonObject const& action_item, ActionObject::PointerFields& fields)
  179. {
  180. // 4. Let width be the result of getting the property width from action item.
  181. // 5. If width is not undefined and width is not a Number greater than or equal to 0 return error with error code invalid argument.
  182. // 6. Set the width property of action to width.
  183. fields.width = TRY(get_optional_property_with_limits<double>(action_item, "width"sv, 0.0, {}));
  184. // 7. Let height be the result of getting the property height from action item.
  185. // 8. If height is not undefined and height is not a Number greater than or equal to 0 return error with error code invalid argument.
  186. // 9. Set the height property of action to height.
  187. fields.height = TRY(get_optional_property_with_limits<double>(action_item, "height"sv, 0.0, {}));
  188. // 10. Let pressure be the result of getting the property pressure from action item.
  189. // 11. If pressure is not undefined and pressure is not a Number greater than or equal to 0 and less than or equal to 1 return error with error code invalid argument.
  190. // 12. Set the pressure property of action to pressure.
  191. fields.pressure = TRY(get_optional_property_with_limits<double>(action_item, "pressure"sv, 0.0, 1.0));
  192. // 13. Let tangentialPressure be the result of getting the property tangentialPressure from action item.
  193. // 14. If tangentialPressure is not undefined and tangentialPressure is not a Number greater than or equal to -1 and less than or equal to 1 return error with error code invalid argument.
  194. // 15. Set the tangentialPressure property of action to tangentialPressure.
  195. fields.tangential_pressure = TRY(get_optional_property_with_limits<double>(action_item, "tangentialPressure"sv, -1.0, 1.0));
  196. // 16. Let tiltX be the result of getting the property tiltX from action item.
  197. // 17. If tiltX is not undefined and tiltX is not an Integer greater than or equal to -90 and less than or equal to 90 return error with error code invalid argument.
  198. // 18. Set the tiltX property of action to tiltX.
  199. fields.tilt_x = TRY(get_optional_property_with_limits<i32>(action_item, "tiltX"sv, -90, 90));
  200. // 19. Let tiltY be the result of getting the property tiltY from action item.
  201. // 20. If tiltY is not undefined and tiltY is not an Integer greater than or equal to -90 and less than or equal to 90 return error with error code invalid argument.
  202. // 21. Set the tiltY property of action to tiltY.
  203. fields.tilt_y = TRY(get_optional_property_with_limits<i32>(action_item, "tiltY"sv, -90, 90));
  204. // 22. Let twist be the result of getting the property twist from action item.
  205. // 23. If twist is not undefined and twist is not an Integer greater than or equal to 0 and less than or equal to 359 return error with error code invalid argument.
  206. // 24. Set the twist property of action to twist.
  207. fields.twist = TRY(get_optional_property_with_limits<u32>(action_item, "twist"sv, 0, 359));
  208. // 25. Let altitudeAngle be the result of getting the property altitudeAngle from action item.
  209. // 26. If altitudeAngle is not undefined and altitudeAngle is not a Number greater than or equal to 0 and less than or equal to π/2 return error with error code invalid argument.
  210. // 27. Set the altitudeAngle property of action to altitudeAngle.
  211. fields.altitude_angle = TRY(get_optional_property_with_limits<double>(action_item, "altitudeAngle"sv, 0.0, AK::Pi<double> / 2.0));
  212. // 28. Let azimuthAngle be the result of getting the property azimuthAngle from action item.
  213. // 29. If azimuthAngle is not undefined and azimuthAngle is not a Number greater than or equal to 0 and less than or equal to 2π return error with error code invalid argument.
  214. // 30. Set the azimuthAngle property of action to azimuthAngle.
  215. fields.azimuth_angle = TRY(get_optional_property_with_limits<double>(action_item, "azimuthAngle"sv, 0.0, AK::Pi<double> * 2.0));
  216. // 31. Return success with data null.
  217. return {};
  218. }
  219. // https://w3c.github.io/webdriver/#dfn-process-a-pointer-up-or-pointer-down-action
  220. static ErrorOr<void, WebDriver::Error> process_pointer_up_or_down_action(JsonObject const& action_item, ActionObject& action)
  221. {
  222. auto& fields = action.pointer_up_down_fields();
  223. // 1. Let button be the result of getting the property button from action item.
  224. // 2. If button is not an Integer greater than or equal to 0 return error with error code invalid argument.
  225. // 3. Set the button property of action to button.
  226. fields.button = UIEvents::button_code_to_mouse_button(TRY(get_property_with_limits<i16>(action_item, "button"sv, 0, {})));
  227. return process_pointer_action_common(action_item, fields);
  228. }
  229. // https://w3c.github.io/webdriver/#dfn-process-a-pointer-move-action
  230. static ErrorOr<void, WebDriver::Error> process_pointer_move_action(JsonObject const& action_item, ActionObject& action, ActionsOptions const& actions_options)
  231. {
  232. auto& fields = action.pointer_move_fields();
  233. // 1. Let duration be the result of getting the property duration from action item.
  234. // 2. If duration is not undefined and duration is not an Integer greater than or equal to 0, return error with error code invalid argument.
  235. // 3. Set the duration property of action to duration.
  236. if (auto duration = TRY(get_optional_property_with_limits<i64>(action_item, "duration"sv, 0, {})); duration.has_value())
  237. fields.duration = AK::Duration::from_milliseconds(*duration);
  238. // 4. Let origin be the result of getting the property origin from action item.
  239. // 5. If origin is undefined let origin equal "viewport".
  240. auto origin = determine_origin(actions_options, action_item.get("origin"sv));
  241. // 6. If origin is not equal to "viewport" or "pointer", and actions options is element origin steps given origin
  242. // return false, return error with error code invalid argument.
  243. if (!origin.has_value())
  244. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'origin' must be 'viewport', 'pointer', or an element origin");
  245. // 7. Set the origin property of action to origin.
  246. fields.origin = origin.release_value();
  247. // 8. Let x be the result of getting the property x from action item.
  248. // 9. If x is not an Integer, return error with error code invalid argument.
  249. // 10. Set the x property of action to x.
  250. fields.position.set_x(TRY(get_property<i32>(action_item, "x"sv)));
  251. // 11. Let y be the result of getting the property y from action item.
  252. // 12. If y is not an Integer, return error with error code invalid argument.
  253. // 13. Set the y property of action to y.
  254. fields.position.set_y(TRY(get_property<i32>(action_item, "y"sv)));
  255. return process_pointer_action_common(action_item, fields);
  256. }
  257. // https://w3c.github.io/webdriver/#dfn-process-a-pointer-action
  258. static ErrorOr<ActionObject, WebDriver::Error> process_pointer_action(String id, PointerParameters const& parameters, JsonObject const& action_item, ActionsOptions const& actions_options)
  259. {
  260. using enum ActionObject::Subtype;
  261. // 1. Let subtype be the result of getting a property named "type" from action item.
  262. auto subtype = action_object_subtype_from_string(TRY(get_property(action_item, "type"sv)));
  263. // 2. If subtype is not one of the values "pause", "pointerUp", "pointerDown", "pointerMove", or "pointerCancel", return an error with error code invalid argument.
  264. if (!first_is_one_of(subtype, Pause, PointerUp, PointerDown, PointerMove, PointerCancel))
  265. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'type' must be one of 'pause', 'pointerUp', 'pointerDown', 'pointerMove', or 'pointerCancel'");
  266. // 3. Let action be an action object constructed with arguments id, "pointer", and subtype.
  267. ActionObject action { move(id), InputSourceType::Pointer, *subtype };
  268. // 4. If subtype is "pause", let result be the result of trying to process a pause action with arguments action
  269. // item, action, and actions options, and return result.
  270. if (subtype == Pause) {
  271. TRY(process_pause_action(action_item, action));
  272. return action;
  273. }
  274. // 5. Set the pointerType property of action equal to the pointerType property of parameters.
  275. action.set_pointer_type(parameters.pointer_type);
  276. // 6. If subtype is "pointerUp" or "pointerDown", process a pointer up or pointer down action with arguments action
  277. // item and action. If doing so results in an error, return that error.
  278. if (subtype == PointerUp || subtype == PointerDown) {
  279. TRY(process_pointer_up_or_down_action(action_item, action));
  280. }
  281. // 7. If subtype is "pointerMove" process a pointer move action with arguments action item, action, and actions
  282. // options. If doing so results in an error, return that error.
  283. else if (subtype == PointerMove) {
  284. TRY(process_pointer_move_action(action_item, action, actions_options));
  285. }
  286. // 8. If subtype is "pointerCancel" process a pointer cancel action. If doing so results in an error, return that error.
  287. else if (subtype == PointerCancel) {
  288. // FIXME: There are no spec steps to "process a pointer cancel action" yet.
  289. return WebDriver::Error::from_code(WebDriver::ErrorCode::UnsupportedOperation, "pointerCancel events not implemented"sv);
  290. }
  291. // 9. Return success with data action.
  292. return action;
  293. }
  294. // https://w3c.github.io/webdriver/#dfn-process-a-wheel-action
  295. static ErrorOr<ActionObject, WebDriver::Error> process_wheel_action(String id, JsonObject const& action_item, ActionsOptions const& actions_options)
  296. {
  297. using enum ActionObject::Subtype;
  298. // 1. Let subtype be the result of getting a property named "type" from action item.
  299. auto subtype = action_object_subtype_from_string(TRY(get_property(action_item, "type"sv)));
  300. // 2. If subtype is not the value "pause", or "scroll", return an error with error code invalid argument.
  301. if (!first_is_one_of(subtype, Pause, Scroll))
  302. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'type' must be one of 'pause' or 'scroll'");
  303. // 3. Let action be an action object constructed with arguments id, "wheel", and subtype.
  304. ActionObject action { move(id), InputSourceType::Wheel, *subtype };
  305. // 4. If subtype is "pause", let result be the result of trying to process a pause action with arguments action
  306. // item and action, and return result.
  307. if (subtype == Pause) {
  308. TRY(process_pause_action(action_item, action));
  309. return action;
  310. }
  311. auto& fields = action.scroll_fields();
  312. // 5. Let duration be the result of getting a property named "duration" from action item.
  313. // 6. If duration is not undefined and duration is not an Integer greater than or equal to 0, return error with error code invalid argument.
  314. // 7. Set the duration property of action to duration.
  315. if (auto duration = TRY(get_optional_property_with_limits<i64>(action_item, "duration"sv, 0, {})); duration.has_value())
  316. fields.duration = AK::Duration::from_milliseconds(*duration);
  317. // 8. Let origin be the result of getting the property origin from action item.
  318. // 9. If origin is undefined let origin equal "viewport".
  319. auto origin = determine_origin(actions_options, action_item.get("origin"sv));
  320. // 10. If origin is not equal to "viewport", or actions options' is element origin steps given origin return false,
  321. // return error with error code invalid argument.
  322. if (!origin.has_value() || origin == ActionObject::OriginType::Pointer)
  323. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'origin' must be 'viewport' or an element origin");
  324. // 11. Set the origin property of action to origin.
  325. fields.origin = origin.release_value();
  326. // 12. Let x be the result of getting the property x from action item.
  327. // 13. If x is not an Integer, return error with error code invalid argument.
  328. // 14. Set the x property of action to x.
  329. fields.x = TRY(get_property<i64>(action_item, "x"sv));
  330. // 15. Let y be the result of getting the property y from action item.
  331. // 16. If y is not an Integer, return error with error code invalid argument.
  332. // 17. Set the y property of action to y.
  333. fields.y = TRY(get_property<i64>(action_item, "y"sv));
  334. // 18. Let deltaX be the result of getting the property deltaX from action item.
  335. // 19. If deltaX is not an Integer, return error with error code invalid argument.
  336. // 20. Set the deltaX property of action to deltaX.
  337. fields.delta_x = TRY(get_property<i64>(action_item, "deltaX"sv));
  338. // 21. Let deltaY be the result of getting the property deltaY from action item.
  339. // 22. If deltaY is not an Integer, return error with error code invalid argument.
  340. // 23. Set the deltaY property of action to deltaY.
  341. fields.delta_y = TRY(get_property<i64>(action_item, "deltaY"sv));
  342. // 24. Return success with data action.
  343. return action;
  344. }
  345. // https://w3c.github.io/webdriver/#dfn-process-an-input-source-action-sequence
  346. static ErrorOr<Vector<ActionObject>, WebDriver::Error> process_input_source_action_sequence(InputState& input_state, JsonValue const& action_sequence, ActionsOptions const& actions_options)
  347. {
  348. // 1. Let type be the result of getting a property named "type" from action sequence.
  349. auto type = input_source_type_from_string(TRY(get_property(action_sequence, "type"sv)));
  350. // 2. If type is not "key", "pointer", "wheel", or "none", return an error with error code invalid argument.
  351. if (!type.has_value())
  352. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'type' must be one of 'key', 'pointer', 'wheel', or 'none'");
  353. // 3. Let id be the result of getting the property "id" from action sequence.
  354. // 4. If id is undefined or is not a String, return error with error code invalid argument.
  355. auto const id = MUST(String::from_byte_string(TRY(get_property(action_sequence, "id"sv))));
  356. // 5. If type is equal to "pointer", let parameters data be the result of getting the property "parameters" from
  357. // action sequence. Then let parameters be the result of trying to process pointer parameters with argument
  358. // parameters data.
  359. Optional<PointerParameters> parameters;
  360. Optional<PointerInputSource::Subtype> subtype;
  361. if (type == InputSourceType::Pointer) {
  362. parameters = TRY(process_pointer_parameters(action_sequence.as_object().get("parameters"sv)));
  363. subtype = parameters->pointer_type;
  364. }
  365. // 6. Let source be the result of trying to get or create an input source given input state, type and id.
  366. auto const& source = *TRY(get_or_create_input_source(input_state, *type, id, subtype));
  367. // 7. If parameters is not undefined, then if its pointerType property is not equal to source's subtype property,
  368. // return an error with error code invalid argument.
  369. if (auto const* pointer_input_source = source.get_pointer<PointerInputSource>(); pointer_input_source && parameters.has_value()) {
  370. if (parameters->pointer_type != pointer_input_source->subtype)
  371. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Invalid 'pointerType' property");
  372. }
  373. // 8. Let action items be the result of getting a property named "actions" from action sequence.
  374. // 9. If action items is not an Array, return error with error code invalid argument.
  375. auto const& action_items = *TRY(get_property<JsonArray const*>(action_sequence, "actions"sv));
  376. // 10. Let actions be a new list.
  377. Vector<ActionObject> actions;
  378. // 11. For each action item in action items:
  379. TRY(action_items.try_for_each([&](auto const& action_item) -> ErrorOr<void, WebDriver::Error> {
  380. // 1. If action item is not an Object return error with error code invalid argument.
  381. if (!action_item.is_object())
  382. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'actions' item is not an Object");
  383. auto action = TRY([&]() {
  384. switch (*type) {
  385. // 2. If type is "none" let action be the result of trying to process a null action with parameters id, and
  386. // action item.
  387. case InputSourceType::None:
  388. return process_null_action(id, action_item.as_object());
  389. // 3. Otherwise, if type is "key" let action be the result of trying to process a key action with parameters
  390. // id, and action item.
  391. case InputSourceType::Key:
  392. return process_key_action(id, action_item.as_object());
  393. // 4. Otherwise, if type is "pointer" let action be the result of trying to process a pointer action with
  394. // parameters id, parameters, action item, and actions options.
  395. case InputSourceType::Pointer:
  396. return process_pointer_action(id, *parameters, action_item.as_object(), actions_options);
  397. // 5. Otherwise, if type is "wheel" let action be the result of trying to process a wheel action with
  398. // parameters id, and action item, and actions options.
  399. case InputSourceType::Wheel:
  400. return process_wheel_action(id, action_item.as_object(), actions_options);
  401. }
  402. VERIFY_NOT_REACHED();
  403. }());
  404. // 6. Append action to actions.
  405. actions.append(move(action));
  406. return {};
  407. }));
  408. // 12. Return success with data actions.
  409. return actions;
  410. }
  411. // https://w3c.github.io/webdriver/#dfn-extract-an-action-sequence
  412. ErrorOr<Vector<Vector<ActionObject>>, WebDriver::Error> extract_an_action_sequence(InputState& input_state, JsonValue const& parameters, ActionsOptions const& actions_options)
  413. {
  414. // 1. Let actions be the result of getting a property named "actions" from parameters.
  415. // 2. If actions is undefined or is not an Array, return error with error code invalid argument.
  416. auto const& actions = *TRY(get_property<JsonArray const*>(parameters, "actions"sv));
  417. // 3. Let actions by tick be an empty List.
  418. Vector<Vector<ActionObject>> actions_by_tick;
  419. // 4. For each value action sequence corresponding to an indexed property in actions:
  420. TRY(actions.try_for_each([&](auto const& action_sequence) -> ErrorOr<void, WebDriver::Error> {
  421. // 1. Let source actions be the result of trying to process an input source action sequence given input state,
  422. // action sequence, and actions options.
  423. auto source_actions = TRY(process_input_source_action_sequence(input_state, action_sequence, actions_options));
  424. // 2. For each action in source actions:
  425. for (auto [i, action] : enumerate(source_actions)) {
  426. // 1. Let i be the zero-based index of action in source actions.
  427. // 2. If the length of actions by tick is less than i + 1, append a new List to actions by tick.
  428. if (actions_by_tick.size() < (i + 1))
  429. actions_by_tick.resize(i + 1);
  430. // 3. Append action to the List at index i in actions by tick.
  431. actions_by_tick[i].append(move(action));
  432. }
  433. return {};
  434. }));
  435. // 5. Return success with data actions by tick.
  436. return actions_by_tick;
  437. }
  438. }