InputSource.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/WebDriver/Actions.h>
  7. #include <LibWeb/WebDriver/InputSource.h>
  8. #include <LibWeb/WebDriver/InputState.h>
  9. namespace Web::WebDriver {
  10. static InputSourceType input_source_type(InputSource const& input_source)
  11. {
  12. return input_source.visit(
  13. [](NullInputSource const&) { return InputSourceType::None; },
  14. [](KeyInputSource const&) { return InputSourceType::Key; },
  15. [](PointerInputSource const&) { return InputSourceType::Pointer; },
  16. [](WheelInputSource const&) { return InputSourceType::Wheel; });
  17. }
  18. // https://w3c.github.io/webdriver/#dfn-get-a-pointer-id
  19. static u32 get_pointer_id(InputState const& input_state, PointerInputSource::Subtype subtype)
  20. {
  21. // 1. Let minimum id be 0 if subtype is "mouse", or 2 otherwise.
  22. auto minimum_id = subtype == PointerInputSource::Subtype::Mouse ? 0u : 2u;
  23. // 2. Let pointer ids be an empty set.
  24. HashTable<u32> pointer_ids;
  25. // 3. Let sources be the result of getting the values with input state's input state map.
  26. // 4. For each source in sources:
  27. for (auto const& source : input_state.input_state_map) {
  28. // 1. If source is a pointer input source, append source's pointerId to pointer ids.
  29. if (auto const* pointer_input_source = source.value.get_pointer<PointerInputSource>())
  30. pointer_ids.set(pointer_input_source->pointer_id);
  31. }
  32. // 5. Return the smallest integer that is greater than or equal to minimum id and that is not contained in pointer ids.
  33. for (u32 integer = minimum_id; integer < NumericLimits<u32>::max(); ++integer) {
  34. if (!pointer_ids.contains(integer))
  35. return integer;
  36. }
  37. VERIFY_NOT_REACHED();
  38. }
  39. // https://w3c.github.io/webdriver/#dfn-create-a-pointer-input-source
  40. PointerInputSource::PointerInputSource(InputState const& input_state, PointerInputSource::Subtype subtype)
  41. : subtype(subtype)
  42. , pointer_id(get_pointer_id(input_state, subtype))
  43. {
  44. // To create a pointer input source object given input state, and subtype, return a new pointer input source with
  45. // subtype set to subtype, pointerId set to get a pointer id with input state and subtype, and the other items set
  46. // to their default values.
  47. }
  48. UIEvents::KeyModifier GlobalKeyState::modifiers() const
  49. {
  50. auto modifiers = UIEvents::KeyModifier::Mod_None;
  51. if (ctrl_key)
  52. modifiers |= UIEvents::KeyModifier::Mod_Ctrl;
  53. if (shift_key)
  54. modifiers |= UIEvents::KeyModifier::Mod_Shift;
  55. if (alt_key)
  56. modifiers |= UIEvents::KeyModifier::Mod_Alt;
  57. if (meta_key)
  58. modifiers |= UIEvents::KeyModifier::Mod_Super;
  59. return modifiers;
  60. }
  61. Optional<InputSourceType> input_source_type_from_string(StringView input_source_type)
  62. {
  63. if (input_source_type == "none"sv)
  64. return InputSourceType::None;
  65. if (input_source_type == "key"sv)
  66. return InputSourceType::Key;
  67. if (input_source_type == "pointer"sv)
  68. return InputSourceType::Pointer;
  69. if (input_source_type == "wheel"sv)
  70. return InputSourceType::Wheel;
  71. return {};
  72. }
  73. Optional<PointerInputSource::Subtype> pointer_input_source_subtype_from_string(StringView pointer_type)
  74. {
  75. if (pointer_type == "mouse"sv)
  76. return PointerInputSource::Subtype::Mouse;
  77. if (pointer_type == "pen"sv)
  78. return PointerInputSource::Subtype::Pen;
  79. if (pointer_type == "touch"sv)
  80. return PointerInputSource::Subtype::Touch;
  81. return {};
  82. }
  83. // https://w3c.github.io/webdriver/#dfn-create-an-input-source
  84. InputSource create_input_source(InputState const& input_state, InputSourceType type, Optional<PointerInputSource::Subtype> subtype)
  85. {
  86. // Run the substeps matching the first matching value of type:
  87. switch (type) {
  88. // "none"
  89. case InputSourceType::None:
  90. // Let source be the result of create a null input source.
  91. return NullInputSource {};
  92. // "key"
  93. case InputSourceType::Key:
  94. // Let source be the result of create a key input source.
  95. return KeyInputSource {};
  96. // "pointer"
  97. case InputSourceType::Pointer:
  98. // Let source be the result of create a pointer input source with input state and subtype.
  99. return PointerInputSource { input_state, *subtype };
  100. // "wheel"
  101. case InputSourceType::Wheel:
  102. // Let source be the result of create a wheel input source.
  103. return WheelInputSource {};
  104. }
  105. // Otherwise:
  106. // Return error with error code invalid argument.
  107. // NOTE: We know this cannot be reached because the only caller will have already thrown an invalid argument error
  108. // if the `type` parameter was not valid.
  109. VERIFY_NOT_REACHED();
  110. }
  111. // https://w3c.github.io/webdriver/#dfn-remove-an-input-source
  112. void add_input_source(InputState& input_state, String id, InputSource source)
  113. {
  114. // 1. Let input state map be input state's input state map.
  115. // 2. Set input state map[input id] to source.
  116. input_state.input_state_map.set(move(id), move(source));
  117. }
  118. // https://w3c.github.io/webdriver/#dfn-remove-an-input-source
  119. void remove_input_source(InputState& input_state, StringView id)
  120. {
  121. // 1. Assert: None of the items in input state's input cancel list has id equal to input id.
  122. // FIXME: Spec issue: This assertion cannot be correct. For example, when Element Click is executed, the initial
  123. // pointer down action will append a pointer up action to the input cancel list, and the input cancel list
  124. // is never subsequently cleared. So instead of performing this assertion, we remove any action from the
  125. // input cancel list with the provided input ID.
  126. // https://github.com/w3c/webdriver/issues/1809
  127. input_state.input_cancel_list.remove_all_matching([&](ActionObject const& action) {
  128. return action.id == id;
  129. });
  130. // 2. Let input state map be input state's input state map.
  131. // 3. Remove input state map[input id].
  132. input_state.input_state_map.remove(id);
  133. }
  134. // https://w3c.github.io/webdriver/#dfn-get-an-input-source
  135. Optional<InputSource&> get_input_source(InputState& input_state, StringView id)
  136. {
  137. // 1. Let input state map be input state's input state map.
  138. // 2. If input state map[input id] exists, return input state map[input id].
  139. // 3. Return undefined.
  140. return input_state.input_state_map.get(id);
  141. }
  142. // https://w3c.github.io/webdriver/#dfn-get-or-create-an-input-source
  143. ErrorOr<InputSource*, WebDriver::Error> get_or_create_input_source(InputState& input_state, InputSourceType type, StringView id, Optional<PointerInputSource::Subtype> subtype)
  144. {
  145. // 1. Let source be get an input source with input state and input id.
  146. auto source = get_input_source(input_state, id);
  147. // 2. If source is not undefined and source's type is not equal to type, or source is a pointer input source,
  148. // return error with error code invalid argument.
  149. if (source.has_value() && input_source_type(*source) != type) {
  150. // FIXME: Spec issue: It does not make sense to check if "source is a pointer input source". This would errantly
  151. // prevent the ability to perform two pointer actions in a row.
  152. // https://github.com/w3c/webdriver/issues/1810
  153. return WebDriver::Error::from_code(WebDriver::ErrorCode::InvalidArgument, "Property 'type' does not match existing input source type");
  154. }
  155. // 3. If source is undefined, set source to the result of trying to create an input source with input state and type.
  156. if (!source.has_value()) {
  157. // FIXME: Spec issue: The spec doesn't say to add the source to the input state map, but it is explicitly
  158. // expected when we reach the `dispatch tick actions` AO.
  159. // https://github.com/w3c/webdriver/issues/1810
  160. input_state.input_state_map.set(MUST(String::from_utf8(id)), create_input_source(input_state, type, subtype));
  161. source = get_input_source(input_state, id);
  162. }
  163. // 4. Return success with data source.
  164. return &source.value();
  165. }
  166. // https://w3c.github.io/webdriver/#dfn-get-the-global-key-state
  167. GlobalKeyState get_global_key_state(InputState const& input_state)
  168. {
  169. // 1. Let input state map be input state's input state map.
  170. auto const& input_state_map = input_state.input_state_map;
  171. // 2. Let sources be the result of getting the values with input state map.
  172. // 3. Let key state be a new global key state with pressed set to an empty set, altKey, ctrlKey, metaKey, and
  173. // shiftKey set to false.
  174. GlobalKeyState key_state {};
  175. // 4. For each source in sources:
  176. for (auto const& source : input_state_map) {
  177. // 1. If source is not a key input source, continue to the first step of this loop.
  178. auto const* key_input_source = source.value.get_pointer<KeyInputSource>();
  179. if (!key_input_source)
  180. continue;
  181. // 2. Set key state's pressed item to the union of its current value and source's pressed item.
  182. for (auto const& pressed : key_input_source->pressed)
  183. key_state.pressed.set(pressed);
  184. // 3. If source's alt item is true, set key state's altKey item to true.
  185. key_state.alt_key |= key_input_source->alt;
  186. // 4. If source's ctrl item is true, set key state's ctrlKey item to true.
  187. key_state.ctrl_key |= key_input_source->ctrl;
  188. // 5. If source's meta item is true, set key state's metaKey item to true.
  189. key_state.meta_key |= key_input_source->meta;
  190. // 6. If source's shift item is true, set key state's shiftKey item to true.
  191. key_state.shift_key |= key_input_source->shift;
  192. }
  193. // 5. Return key state.
  194. return key_state;
  195. }
  196. }