EventHandler.cpp 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
  4. * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <LibUnicode/CharacterTypes.h>
  9. #include <LibUnicode/Segmenter.h>
  10. #include <LibWeb/DOM/Text.h>
  11. #include <LibWeb/HTML/CloseWatcherManager.h>
  12. #include <LibWeb/HTML/Focus.h>
  13. #include <LibWeb/HTML/HTMLAnchorElement.h>
  14. #include <LibWeb/HTML/HTMLFormElement.h>
  15. #include <LibWeb/HTML/HTMLIFrameElement.h>
  16. #include <LibWeb/HTML/HTMLImageElement.h>
  17. #include <LibWeb/HTML/HTMLMediaElement.h>
  18. #include <LibWeb/HTML/HTMLVideoElement.h>
  19. #include <LibWeb/Layout/Label.h>
  20. #include <LibWeb/Layout/Viewport.h>
  21. #include <LibWeb/Page/DragAndDropEventHandler.h>
  22. #include <LibWeb/Page/EventHandler.h>
  23. #include <LibWeb/Page/Page.h>
  24. #include <LibWeb/Painting/PaintableBox.h>
  25. #include <LibWeb/Painting/TextPaintable.h>
  26. #include <LibWeb/Selection/Selection.h>
  27. #include <LibWeb/UIEvents/EventNames.h>
  28. #include <LibWeb/UIEvents/InputEvent.h>
  29. #include <LibWeb/UIEvents/InputTypes.h>
  30. #include <LibWeb/UIEvents/KeyboardEvent.h>
  31. #include <LibWeb/UIEvents/MouseButton.h>
  32. #include <LibWeb/UIEvents/MouseEvent.h>
  33. #include <LibWeb/UIEvents/PointerEvent.h>
  34. #include <LibWeb/UIEvents/WheelEvent.h>
  35. namespace Web {
  36. #define FIRE(expression) \
  37. if (auto event_result = (expression); event_result == EventResult::Cancelled) \
  38. return event_result;
  39. static GC::Ptr<DOM::Node> dom_node_for_event_dispatch(Painting::Paintable& paintable)
  40. {
  41. if (auto node = paintable.dom_node())
  42. return node;
  43. auto* layout_parent = paintable.layout_node().parent();
  44. while (layout_parent) {
  45. if (auto* node = layout_parent->dom_node())
  46. return node;
  47. layout_parent = layout_parent->parent();
  48. }
  49. return nullptr;
  50. }
  51. static DOM::Node* input_control_associated_with_ancestor_label_element(Painting::Paintable& paintable)
  52. {
  53. if (is<Layout::Label>(paintable.layout_node())) {
  54. auto const& label = verify_cast<Layout::Label>(paintable.layout_node());
  55. return label.dom_node().control().ptr();
  56. }
  57. if (auto const* label = paintable.layout_node().first_ancestor_of_type<Layout::Label>())
  58. return label->dom_node().control().ptr();
  59. return nullptr;
  60. }
  61. static bool parent_element_for_event_dispatch(Painting::Paintable& paintable, GC::Ptr<DOM::Node>& node, Layout::Node*& layout_node)
  62. {
  63. auto* current_ancestor_node = node.ptr();
  64. do {
  65. if (is<HTML::FormAssociatedElement>(current_ancestor_node) && !dynamic_cast<HTML::FormAssociatedElement*>(current_ancestor_node)->enabled()) {
  66. return false;
  67. }
  68. } while ((current_ancestor_node = current_ancestor_node->parent()));
  69. layout_node = &paintable.layout_node();
  70. while (layout_node && node && !node->is_element() && layout_node->parent()) {
  71. layout_node = layout_node->parent();
  72. if (layout_node->is_anonymous())
  73. continue;
  74. node = layout_node->dom_node();
  75. }
  76. return node && layout_node;
  77. }
  78. static Gfx::StandardCursor cursor_css_to_gfx(Optional<CSS::Cursor> cursor)
  79. {
  80. if (!cursor.has_value()) {
  81. return Gfx::StandardCursor::None;
  82. }
  83. switch (cursor.value()) {
  84. case CSS::Cursor::Crosshair:
  85. case CSS::Cursor::Cell:
  86. return Gfx::StandardCursor::Crosshair;
  87. case CSS::Cursor::Grab:
  88. case CSS::Cursor::Grabbing:
  89. return Gfx::StandardCursor::Drag;
  90. case CSS::Cursor::Pointer:
  91. return Gfx::StandardCursor::Hand;
  92. case CSS::Cursor::Help:
  93. return Gfx::StandardCursor::Help;
  94. case CSS::Cursor::None:
  95. return Gfx::StandardCursor::Hidden;
  96. case CSS::Cursor::NotAllowed:
  97. return Gfx::StandardCursor::Disallowed;
  98. case CSS::Cursor::Text:
  99. case CSS::Cursor::VerticalText:
  100. return Gfx::StandardCursor::IBeam;
  101. case CSS::Cursor::Move:
  102. case CSS::Cursor::AllScroll:
  103. return Gfx::StandardCursor::Move;
  104. case CSS::Cursor::Progress:
  105. case CSS::Cursor::Wait:
  106. return Gfx::StandardCursor::Wait;
  107. case CSS::Cursor::ColResize:
  108. return Gfx::StandardCursor::ResizeColumn;
  109. case CSS::Cursor::EResize:
  110. case CSS::Cursor::WResize:
  111. case CSS::Cursor::EwResize:
  112. return Gfx::StandardCursor::ResizeHorizontal;
  113. case CSS::Cursor::RowResize:
  114. return Gfx::StandardCursor::ResizeRow;
  115. case CSS::Cursor::NResize:
  116. case CSS::Cursor::SResize:
  117. case CSS::Cursor::NsResize:
  118. return Gfx::StandardCursor::ResizeVertical;
  119. case CSS::Cursor::NeResize:
  120. case CSS::Cursor::SwResize:
  121. case CSS::Cursor::NeswResize:
  122. return Gfx::StandardCursor::ResizeDiagonalBLTR;
  123. case CSS::Cursor::NwResize:
  124. case CSS::Cursor::SeResize:
  125. case CSS::Cursor::NwseResize:
  126. return Gfx::StandardCursor::ResizeDiagonalTLBR;
  127. case CSS::Cursor::ZoomIn:
  128. case CSS::Cursor::ZoomOut:
  129. return Gfx::StandardCursor::Zoom;
  130. case CSS::Cursor::ContextMenu:
  131. case CSS::Cursor::Alias:
  132. case CSS::Cursor::Copy:
  133. case CSS::Cursor::NoDrop:
  134. // FIXME: No corresponding GFX Standard Cursor, fallthrough to None
  135. case CSS::Cursor::Auto:
  136. case CSS::Cursor::Default:
  137. default:
  138. return Gfx::StandardCursor::None;
  139. }
  140. }
  141. static CSSPixelPoint compute_mouse_event_offset(CSSPixelPoint position, Layout::Node const& layout_node)
  142. {
  143. auto top_left_of_layout_node = layout_node.first_paintable()->box_type_agnostic_position();
  144. return {
  145. position.x() - top_left_of_layout_node.x(),
  146. position.y() - top_left_of_layout_node.y()
  147. };
  148. }
  149. EventHandler::EventHandler(Badge<HTML::Navigable>, HTML::Navigable& navigable)
  150. : m_navigable(navigable)
  151. , m_drag_and_drop_event_handler(make<DragAndDropEventHandler>())
  152. {
  153. }
  154. EventHandler::~EventHandler() = default;
  155. Painting::PaintableBox* EventHandler::paint_root()
  156. {
  157. if (!m_navigable->active_document())
  158. return nullptr;
  159. return m_navigable->active_document()->paintable_box();
  160. }
  161. Painting::PaintableBox const* EventHandler::paint_root() const
  162. {
  163. if (!m_navigable->active_document())
  164. return nullptr;
  165. return m_navigable->active_document()->paintable_box();
  166. }
  167. EventResult EventHandler::handle_mousewheel(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers, int wheel_delta_x, int wheel_delta_y)
  168. {
  169. if (should_ignore_device_input_event())
  170. return EventResult::Dropped;
  171. if (!m_navigable->active_document())
  172. return EventResult::Dropped;
  173. if (!m_navigable->active_document()->is_fully_active())
  174. return EventResult::Dropped;
  175. m_navigable->active_document()->update_layout();
  176. if (!paint_root())
  177. return EventResult::Dropped;
  178. if (modifiers & UIEvents::KeyModifier::Mod_Shift)
  179. swap(wheel_delta_x, wheel_delta_y);
  180. auto handled_event = EventResult::Dropped;
  181. GC::Ptr<Painting::Paintable> paintable;
  182. if (auto result = target_for_mouse_position(viewport_position); result.has_value())
  183. paintable = result->paintable;
  184. if (paintable) {
  185. auto* containing_block = paintable->containing_block();
  186. while (containing_block) {
  187. auto handled_scroll_event = containing_block->handle_mousewheel({}, viewport_position, buttons, modifiers, wheel_delta_x, wheel_delta_y);
  188. if (handled_scroll_event)
  189. return EventResult::Handled;
  190. containing_block = containing_block->containing_block();
  191. }
  192. if (paintable->handle_mousewheel({}, viewport_position, buttons, modifiers, wheel_delta_x, wheel_delta_y))
  193. return EventResult::Handled;
  194. auto node = dom_node_for_event_dispatch(*paintable);
  195. if (node) {
  196. // FIXME: Support wheel events in nested browsing contexts.
  197. if (is<HTML::HTMLIFrameElement>(*node)) {
  198. auto& iframe = static_cast<HTML::HTMLIFrameElement&>(*node);
  199. auto position_in_iframe = viewport_position.translated(compute_mouse_event_offset({}, paintable->layout_node()));
  200. iframe.content_navigable()->event_handler().handle_mousewheel(position_in_iframe, screen_position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y);
  201. return EventResult::Dropped;
  202. }
  203. // Search for the first parent of the hit target that's an element.
  204. Layout::Node* layout_node;
  205. if (!parent_element_for_event_dispatch(*paintable, node, layout_node))
  206. return EventResult::Dropped;
  207. auto page_offset = compute_mouse_event_page_offset(viewport_position);
  208. auto offset = compute_mouse_event_offset(page_offset, *layout_node);
  209. if (node->dispatch_event(UIEvents::WheelEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::wheel, screen_position, page_offset, viewport_position, offset, wheel_delta_x, wheel_delta_y, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors())) {
  210. m_navigable->active_window()->scroll_by(wheel_delta_x, wheel_delta_y);
  211. }
  212. handled_event = EventResult::Handled;
  213. }
  214. }
  215. return handled_event;
  216. }
  217. EventResult EventHandler::handle_mouseup(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
  218. {
  219. if (should_ignore_device_input_event())
  220. return EventResult::Dropped;
  221. if (!m_navigable->active_document())
  222. return EventResult::Dropped;
  223. if (!m_navigable->active_document()->is_fully_active())
  224. return EventResult::Dropped;
  225. m_navigable->active_document()->update_layout();
  226. if (!paint_root())
  227. return EventResult::Dropped;
  228. GC::Ptr<Painting::Paintable> paintable;
  229. if (auto result = target_for_mouse_position(viewport_position); result.has_value())
  230. paintable = result->paintable;
  231. if (paintable && paintable->wants_mouse_events()) {
  232. if (paintable->handle_mouseup({}, viewport_position, button, modifiers) == Painting::Paintable::DispatchEventOfSameName::No)
  233. return EventResult::Cancelled;
  234. // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again.
  235. if (!paint_root())
  236. return EventResult::Handled;
  237. if (auto result = paint_root()->hit_test(viewport_position, Painting::HitTestType::Exact); result.has_value())
  238. paintable = result->paintable;
  239. }
  240. auto handled_event = EventResult::Dropped;
  241. if (paintable) {
  242. auto node = dom_node_for_event_dispatch(*paintable);
  243. if (node) {
  244. if (is<HTML::HTMLIFrameElement>(*node)) {
  245. if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
  246. return content_navigable->event_handler().handle_mouseup(viewport_position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
  247. return EventResult::Dropped;
  248. }
  249. // Search for the first parent of the hit target that's an element.
  250. // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click)
  251. // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
  252. Layout::Node* layout_node;
  253. if (!parent_element_for_event_dispatch(*paintable, node, layout_node)) {
  254. // FIXME: This is pretty ugly but we need to bail out here.
  255. goto after_node_use;
  256. }
  257. auto page_offset = compute_mouse_event_page_offset(viewport_position);
  258. auto offset = compute_mouse_event_offset(page_offset, *layout_node);
  259. node->dispatch_event(UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointerup, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  260. node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mouseup, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  261. handled_event = EventResult::Handled;
  262. bool run_activation_behavior = false;
  263. if (node.ptr() == m_mousedown_target) {
  264. if (button == UIEvents::MouseButton::Primary) {
  265. run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::click, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  266. } else if (button == UIEvents::MouseButton::Middle) {
  267. run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::auxclick, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  268. } else if (button == UIEvents::MouseButton::Secondary) {
  269. // Allow the user to bypass custom context menus by holding shift, like Firefox.
  270. if ((modifiers & UIEvents::Mod_Shift) == 0)
  271. run_activation_behavior = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::contextmenu, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  272. else
  273. run_activation_behavior = true;
  274. }
  275. }
  276. if (run_activation_behavior) {
  277. // FIXME: Currently cannot spawn a new top-level
  278. // browsing context for new tab operations, because the new
  279. // top-level browsing context would be in another process. To
  280. // fix this, there needs to be some way to be able to
  281. // communicate with browsing contexts in remote WebContent
  282. // processes, and then step 8 of this algorithm needs to be
  283. // implemented in Navigable::choose_a_navigable:
  284. //
  285. // https://html.spec.whatwg.org/multipage/document-sequences.html#the-rules-for-choosing-a-navigable
  286. if (GC::Ptr<HTML::HTMLAnchorElement const> link = node->enclosing_link_element()) {
  287. GC::Ref<DOM::Document> document = *m_navigable->active_document();
  288. auto href = link->href();
  289. auto url = document->encoding_parse_url(href);
  290. if (button == UIEvents::MouseButton::Primary && (modifiers & UIEvents::Mod_PlatformCtrl) != 0) {
  291. m_navigable->page().client().page_did_click_link(url, link->target().to_byte_string(), modifiers);
  292. } else if (button == UIEvents::MouseButton::Middle) {
  293. m_navigable->page().client().page_did_middle_click_link(url, link->target().to_byte_string(), modifiers);
  294. } else if (button == UIEvents::MouseButton::Secondary) {
  295. m_navigable->page().client().page_did_request_link_context_menu(viewport_position, url, link->target().to_byte_string(), modifiers);
  296. }
  297. } else if (button == UIEvents::MouseButton::Secondary) {
  298. if (is<HTML::HTMLImageElement>(*node)) {
  299. auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
  300. auto image_url = image_element.document().encoding_parse_url(image_element.src());
  301. m_navigable->page().client().page_did_request_image_context_menu(viewport_position, image_url, "", modifiers, image_element.immutable_bitmap()->bitmap());
  302. } else if (is<HTML::HTMLMediaElement>(*node)) {
  303. auto& media_element = verify_cast<HTML::HTMLMediaElement>(*node);
  304. Page::MediaContextMenu menu {
  305. .media_url = media_element.document().encoding_parse_url(media_element.current_src()),
  306. .is_video = is<HTML::HTMLVideoElement>(*node),
  307. .is_playing = media_element.potentially_playing(),
  308. .is_muted = media_element.muted(),
  309. .has_user_agent_controls = media_element.has_attribute(HTML::AttributeNames::controls),
  310. .is_looping = media_element.has_attribute(HTML::AttributeNames::loop),
  311. };
  312. m_navigable->page().did_request_media_context_menu(media_element.unique_id(), viewport_position, "", modifiers, move(menu));
  313. } else {
  314. m_navigable->page().client().page_did_request_context_menu(viewport_position);
  315. }
  316. }
  317. }
  318. if (auto* input_control = input_control_associated_with_ancestor_label_element(*paintable)) {
  319. if (button == UIEvents::MouseButton::Primary) {
  320. input_control->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::click, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  321. }
  322. }
  323. }
  324. }
  325. after_node_use:
  326. if (button == UIEvents::MouseButton::Primary) {
  327. m_in_mouse_selection = false;
  328. m_mouse_selection_target = nullptr;
  329. }
  330. return handled_event;
  331. }
  332. EventResult EventHandler::handle_mousedown(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
  333. {
  334. if (should_ignore_device_input_event())
  335. return EventResult::Dropped;
  336. if (!m_navigable->active_document())
  337. return EventResult::Dropped;
  338. if (!m_navigable->active_document()->is_fully_active())
  339. return EventResult::Dropped;
  340. m_navigable->active_document()->update_layout();
  341. if (!paint_root())
  342. return EventResult::Dropped;
  343. GC::Ref<DOM::Document> document = *m_navigable->active_document();
  344. GC::Ptr<DOM::Node> node;
  345. {
  346. GC::Ptr<Painting::Paintable> paintable;
  347. if (auto result = target_for_mouse_position(viewport_position); result.has_value())
  348. paintable = result->paintable;
  349. else
  350. return EventResult::Dropped;
  351. auto pointer_events = paintable->computed_values().pointer_events();
  352. // FIXME: Handle other values for pointer-events.
  353. VERIFY(pointer_events != CSS::PointerEvents::None);
  354. node = dom_node_for_event_dispatch(*paintable);
  355. document->set_hovered_node(node);
  356. if (paintable->wants_mouse_events()) {
  357. if (paintable->handle_mousedown({}, viewport_position, button, modifiers) == Painting::Paintable::DispatchEventOfSameName::No)
  358. return EventResult::Cancelled;
  359. }
  360. if (!node)
  361. return EventResult::Dropped;
  362. if (is<HTML::HTMLIFrameElement>(*node)) {
  363. if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
  364. return content_navigable->event_handler().handle_mousedown(viewport_position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
  365. return EventResult::Dropped;
  366. }
  367. m_navigable->page().set_focused_navigable({}, m_navigable);
  368. // Search for the first parent of the hit target that's an element.
  369. // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click)
  370. // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
  371. Layout::Node* layout_node;
  372. if (!parent_element_for_event_dispatch(*paintable, node, layout_node))
  373. return EventResult::Dropped;
  374. m_mousedown_target = node.ptr();
  375. auto page_offset = compute_mouse_event_page_offset(viewport_position);
  376. auto offset = compute_mouse_event_offset(page_offset, *layout_node);
  377. node->dispatch_event(UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointerdown, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  378. node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousedown, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  379. }
  380. // NOTE: Dispatching an event may have disturbed the world.
  381. if (!paint_root() || paint_root() != node->document().paintable_box())
  382. return EventResult::Accepted;
  383. if (button == UIEvents::MouseButton::Primary) {
  384. if (auto result = paint_root()->hit_test(viewport_position, Painting::HitTestType::TextCursor); result.has_value()) {
  385. auto paintable = result->paintable;
  386. auto dom_node = paintable->dom_node();
  387. if (dom_node) {
  388. // See if we want to focus something.
  389. GC::Ptr<DOM::Node> focus_candidate;
  390. if (auto* input_control = input_control_associated_with_ancestor_label_element(*paintable)) {
  391. focus_candidate = input_control;
  392. } else {
  393. for (auto candidate = node; candidate; candidate = candidate->parent_or_shadow_host()) {
  394. if (candidate->is_focusable()) {
  395. focus_candidate = candidate;
  396. break;
  397. }
  398. }
  399. }
  400. // When a user activates a click focusable focusable area, the user agent must run the focusing steps on the focusable area with focus trigger set to "click".
  401. // Spec Note: Note that focusing is not an activation behavior, i.e. calling the click() method on an element or dispatching a synthetic click event on it won't cause the element to get focused.
  402. if (focus_candidate)
  403. HTML::run_focusing_steps(focus_candidate, nullptr, "click"sv);
  404. else if (auto* focused_element = document->focused_element())
  405. HTML::run_unfocusing_steps(focused_element);
  406. auto target = document->active_input_events_target();
  407. if (target) {
  408. m_in_mouse_selection = true;
  409. m_mouse_selection_target = target;
  410. if (modifiers & UIEvents::KeyModifier::Mod_Shift) {
  411. target->set_selection_focus(*dom_node, result->index_in_node);
  412. } else {
  413. target->set_selection_anchor(*dom_node, result->index_in_node);
  414. }
  415. } else if (!focus_candidate) {
  416. m_in_mouse_selection = true;
  417. if (auto selection = document->get_selection()) {
  418. auto anchor_node = selection->anchor_node();
  419. if (anchor_node && modifiers & UIEvents::KeyModifier::Mod_Shift) {
  420. (void)selection->set_base_and_extent(*anchor_node, selection->anchor_offset(), *dom_node, result->index_in_node);
  421. } else {
  422. (void)selection->set_base_and_extent(*dom_node, result->index_in_node, *dom_node, result->index_in_node);
  423. }
  424. }
  425. }
  426. }
  427. }
  428. }
  429. return EventResult::Handled;
  430. }
  431. EventResult EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 buttons, u32 modifiers)
  432. {
  433. if (should_ignore_device_input_event())
  434. return EventResult::Dropped;
  435. if (!m_navigable->active_document())
  436. return EventResult::Dropped;
  437. if (!m_navigable->active_document()->is_fully_active())
  438. return EventResult::Dropped;
  439. m_navigable->active_document()->update_layout();
  440. if (!paint_root())
  441. return EventResult::Dropped;
  442. auto& document = *m_navigable->active_document();
  443. bool hovered_node_changed = false;
  444. bool is_hovering_link = false;
  445. Gfx::StandardCursor hovered_node_cursor = Gfx::StandardCursor::None;
  446. GC::Ptr<Painting::Paintable> paintable;
  447. Optional<int> start_index;
  448. if (auto result = target_for_mouse_position(viewport_position); result.has_value()) {
  449. paintable = result->paintable;
  450. start_index = result->index_in_node;
  451. }
  452. const HTML::HTMLAnchorElement* hovered_link_element = nullptr;
  453. if (paintable) {
  454. if (paintable->wants_mouse_events()) {
  455. document.set_hovered_node(paintable->dom_node());
  456. if (paintable->handle_mousemove({}, viewport_position, buttons, modifiers) == Painting::Paintable::DispatchEventOfSameName::No)
  457. return EventResult::Cancelled;
  458. // FIXME: It feels a bit aggressive to always update the cursor like this.
  459. m_navigable->page().client().page_did_request_cursor_change(Gfx::StandardCursor::None);
  460. }
  461. auto node = dom_node_for_event_dispatch(*paintable);
  462. if (node && is<HTML::HTMLIFrameElement>(*node)) {
  463. if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
  464. return content_navigable->event_handler().handle_mousemove(viewport_position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, buttons, modifiers);
  465. return EventResult::Dropped;
  466. }
  467. auto const cursor = paintable->computed_values().cursor();
  468. auto pointer_events = paintable->computed_values().pointer_events();
  469. // FIXME: Handle other values for pointer-events.
  470. VERIFY(pointer_events != CSS::PointerEvents::None);
  471. // Search for the first parent of the hit target that's an element.
  472. // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click)
  473. // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
  474. Layout::Node* layout_node;
  475. bool found_parent_element = parent_element_for_event_dispatch(*paintable, node, layout_node);
  476. hovered_node_changed = node.ptr() != document.hovered_node();
  477. document.set_hovered_node(node);
  478. if (found_parent_element) {
  479. hovered_link_element = node->enclosing_link_element();
  480. if (hovered_link_element)
  481. is_hovering_link = true;
  482. if (paintable->layout_node().is_text_node()) {
  483. if (cursor == CSS::Cursor::Auto)
  484. hovered_node_cursor = Gfx::StandardCursor::IBeam;
  485. else
  486. hovered_node_cursor = cursor_css_to_gfx(cursor);
  487. } else if (node->is_element()) {
  488. if (cursor == CSS::Cursor::Auto)
  489. hovered_node_cursor = Gfx::StandardCursor::Arrow;
  490. else
  491. hovered_node_cursor = cursor_css_to_gfx(cursor);
  492. }
  493. auto page_offset = compute_mouse_event_page_offset(viewport_position);
  494. auto offset = compute_mouse_event_offset(page_offset, *layout_node);
  495. auto movement = compute_mouse_event_movement(screen_position);
  496. m_mousemove_previous_screen_position = screen_position;
  497. bool continue_ = node->dispatch_event(UIEvents::PointerEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::pointermove, screen_position, page_offset, viewport_position, offset, movement, UIEvents::MouseButton::Primary, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  498. if (!continue_)
  499. return EventResult::Cancelled;
  500. continue_ = node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::mousemove, screen_position, page_offset, viewport_position, offset, movement, UIEvents::MouseButton::Primary, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  501. if (!continue_)
  502. return EventResult::Cancelled;
  503. // NOTE: Dispatching an event may have disturbed the world.
  504. if (!paint_root() || paint_root() != node->document().paintable_box())
  505. return EventResult::Accepted;
  506. }
  507. if (m_in_mouse_selection) {
  508. auto hit = paint_root()->hit_test(viewport_position, Painting::HitTestType::TextCursor);
  509. if (m_mouse_selection_target) {
  510. if (hit.has_value() && hit->paintable->dom_node())
  511. m_mouse_selection_target->set_selection_focus(*hit->paintable->dom_node(), hit->index_in_node);
  512. } else {
  513. if (start_index.has_value() && hit.has_value() && hit->dom_node()) {
  514. if (auto selection = document.get_selection()) {
  515. auto anchor_node = selection->anchor_node();
  516. if (anchor_node) {
  517. if (&anchor_node->root() == &hit->dom_node()->root())
  518. (void)selection->set_base_and_extent(*anchor_node, selection->anchor_offset(), *hit->paintable->dom_node(), hit->index_in_node);
  519. } else {
  520. (void)selection->set_base_and_extent(*hit->paintable->dom_node(), hit->index_in_node, *hit->paintable->dom_node(), hit->index_in_node);
  521. }
  522. }
  523. document.set_needs_display();
  524. }
  525. }
  526. }
  527. }
  528. auto& page = m_navigable->page();
  529. if (page.current_cursor() != hovered_node_cursor) {
  530. page.set_current_cursor(hovered_node_cursor);
  531. page.client().page_did_request_cursor_change(hovered_node_cursor);
  532. }
  533. if (hovered_node_changed) {
  534. GC::Ptr<HTML::HTMLElement const> hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element_with_attribute(HTML::AttributeNames::title) : nullptr;
  535. if (hovered_html_element && hovered_html_element->title().has_value()) {
  536. page.set_is_in_tooltip_area(true);
  537. page.client().page_did_enter_tooltip_area(hovered_html_element->title()->to_byte_string());
  538. } else if (page.is_in_tooltip_area()) {
  539. page.set_is_in_tooltip_area(false);
  540. page.client().page_did_leave_tooltip_area();
  541. }
  542. if (is_hovering_link) {
  543. page.set_is_hovering_link(true);
  544. page.client().page_did_hover_link(document.encoding_parse_url(hovered_link_element->href()));
  545. } else if (page.is_hovering_link()) {
  546. page.set_is_hovering_link(false);
  547. page.client().page_did_unhover_link();
  548. }
  549. }
  550. return EventResult::Handled;
  551. }
  552. EventResult EventHandler::handle_doubleclick(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
  553. {
  554. if (should_ignore_device_input_event())
  555. return EventResult::Dropped;
  556. if (!m_navigable->active_document())
  557. return EventResult::Dropped;
  558. if (!m_navigable->active_document()->is_fully_active())
  559. return EventResult::Dropped;
  560. auto& document = *m_navigable->active_document();
  561. document.update_layout();
  562. if (!paint_root())
  563. return EventResult::Dropped;
  564. GC::Ptr<Painting::Paintable> paintable;
  565. if (auto result = target_for_mouse_position(viewport_position); result.has_value())
  566. paintable = result->paintable;
  567. else
  568. return EventResult::Dropped;
  569. auto pointer_events = paintable->computed_values().pointer_events();
  570. // FIXME: Handle other values for pointer-events.
  571. if (pointer_events == CSS::PointerEvents::None)
  572. return EventResult::Cancelled;
  573. auto node = dom_node_for_event_dispatch(*paintable);
  574. if (paintable->wants_mouse_events()) {
  575. // FIXME: Handle double clicks.
  576. }
  577. if (!node)
  578. return EventResult::Dropped;
  579. if (is<HTML::HTMLIFrameElement>(*node)) {
  580. if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
  581. return content_navigable->event_handler().handle_doubleclick(viewport_position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
  582. return EventResult::Dropped;
  583. }
  584. // Search for the first parent of the hit target that's an element.
  585. // "The topmost event target MUST be the element highest in the rendering order which is capable of being an event target." (https://www.w3.org/TR/uievents/#topmost-event-target)
  586. Layout::Node* layout_node;
  587. if (!parent_element_for_event_dispatch(*paintable, node, layout_node))
  588. return EventResult::Dropped;
  589. auto page_offset = compute_mouse_event_page_offset(viewport_position);
  590. auto offset = compute_mouse_event_offset(page_offset, *layout_node);
  591. node->dispatch_event(UIEvents::MouseEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::dblclick, screen_position, page_offset, viewport_position, offset, {}, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors());
  592. // NOTE: Dispatching an event may have disturbed the world.
  593. if (!paint_root() || paint_root() != node->document().paintable_box())
  594. return EventResult::Accepted;
  595. if (button == UIEvents::MouseButton::Primary) {
  596. if (auto result = paint_root()->hit_test(viewport_position, Painting::HitTestType::TextCursor); result.has_value()) {
  597. if (!result->paintable->dom_node())
  598. return EventResult::Accepted;
  599. if (!is<Painting::TextPaintable>(*result->paintable))
  600. return EventResult::Accepted;
  601. auto& hit_paintable = static_cast<Painting::TextPaintable const&>(*result->paintable);
  602. auto& hit_dom_node = const_cast<DOM::Text&>(verify_cast<DOM::Text>(*hit_paintable.dom_node()));
  603. auto previous_boundary = hit_dom_node.word_segmenter().previous_boundary(result->index_in_node, Unicode::Segmenter::Inclusive::Yes).value_or(0);
  604. auto next_boundary = hit_dom_node.word_segmenter().next_boundary(result->index_in_node).value_or(hit_dom_node.length());
  605. auto target = document.active_input_events_target();
  606. if (target) {
  607. target->set_selection_anchor(hit_dom_node, previous_boundary);
  608. target->set_selection_focus(hit_dom_node, next_boundary);
  609. } else if (auto selection = node->document().get_selection()) {
  610. (void)selection->set_base_and_extent(hit_dom_node, previous_boundary, hit_dom_node, next_boundary);
  611. }
  612. }
  613. }
  614. return EventResult::Handled;
  615. }
  616. EventResult EventHandler::handle_drag_and_drop_event(DragEvent::Type type, CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers, Vector<HTML::SelectedFile> files)
  617. {
  618. if (!m_navigable->active_document())
  619. return EventResult::Dropped;
  620. if (!m_navigable->active_document()->is_fully_active())
  621. return EventResult::Dropped;
  622. auto& document = *m_navigable->active_document();
  623. document.update_layout();
  624. if (!paint_root())
  625. return EventResult::Dropped;
  626. GC::Ptr<Painting::Paintable> paintable;
  627. if (auto result = target_for_mouse_position(viewport_position); result.has_value())
  628. paintable = result->paintable;
  629. else
  630. return EventResult::Dropped;
  631. auto node = dom_node_for_event_dispatch(*paintable);
  632. if (!node)
  633. return EventResult::Dropped;
  634. if (is<HTML::HTMLIFrameElement>(*node)) {
  635. if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
  636. return content_navigable->event_handler().handle_drag_and_drop_event(type, viewport_position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers, move(files));
  637. return EventResult::Dropped;
  638. }
  639. auto page_offset = compute_mouse_event_page_offset(viewport_position);
  640. auto offset = compute_mouse_event_offset(page_offset, paintable->layout_node());
  641. switch (type) {
  642. case DragEvent::Type::DragStart:
  643. return m_drag_and_drop_event_handler->handle_drag_start(document.realm(), screen_position, page_offset, viewport_position, offset, button, buttons, modifiers, move(files));
  644. case DragEvent::Type::DragMove:
  645. return m_drag_and_drop_event_handler->handle_drag_move(document.realm(), document, *node, screen_position, page_offset, viewport_position, offset, button, buttons, modifiers);
  646. case DragEvent::Type::DragEnd:
  647. return m_drag_and_drop_event_handler->handle_drag_leave(document.realm(), screen_position, page_offset, viewport_position, offset, button, buttons, modifiers);
  648. case DragEvent::Type::Drop:
  649. return m_drag_and_drop_event_handler->handle_drop(document.realm(), screen_position, page_offset, viewport_position, offset, button, buttons, modifiers);
  650. }
  651. VERIFY_NOT_REACHED();
  652. }
  653. bool EventHandler::focus_next_element()
  654. {
  655. if (!m_navigable->active_document())
  656. return false;
  657. if (!m_navigable->active_document()->is_fully_active())
  658. return false;
  659. auto set_focus_to_first_focusable_element = [&]() {
  660. auto* element = m_navigable->active_document()->first_child_of_type<DOM::Element>();
  661. for (; element; element = element->next_element_in_pre_order()) {
  662. if (element->is_focusable()) {
  663. m_navigable->active_document()->set_focused_element(element);
  664. return true;
  665. }
  666. }
  667. return false;
  668. };
  669. auto* element = m_navigable->active_document()->focused_element();
  670. if (!element)
  671. return set_focus_to_first_focusable_element();
  672. for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order())
  673. ;
  674. if (!element)
  675. return set_focus_to_first_focusable_element();
  676. m_navigable->active_document()->set_focused_element(element);
  677. return true;
  678. }
  679. bool EventHandler::focus_previous_element()
  680. {
  681. if (!m_navigable->active_document())
  682. return false;
  683. if (!m_navigable->active_document()->is_fully_active())
  684. return false;
  685. auto set_focus_to_last_focusable_element = [&]() {
  686. // FIXME: This often returns the HTML element itself, which has no previous sibling.
  687. auto* element = m_navigable->active_document()->last_child_of_type<DOM::Element>();
  688. for (; element; element = element->previous_element_in_pre_order()) {
  689. if (element->is_focusable()) {
  690. m_navigable->active_document()->set_focused_element(element);
  691. return true;
  692. }
  693. }
  694. return false;
  695. };
  696. auto* element = m_navigable->active_document()->focused_element();
  697. if (!element)
  698. return set_focus_to_last_focusable_element();
  699. for (element = element->previous_element_in_pre_order(); element && !element->is_focusable(); element = element->previous_element_in_pre_order())
  700. ;
  701. if (!element)
  702. return set_focus_to_last_focusable_element();
  703. m_navigable->active_document()->set_focused_element(element);
  704. return true;
  705. }
  706. constexpr bool should_ignore_keydown_event(u32 code_point, u32 modifiers)
  707. {
  708. if (modifiers & (UIEvents::KeyModifier::Mod_Ctrl | UIEvents::KeyModifier::Mod_Alt | UIEvents::KeyModifier::Mod_Super))
  709. return true;
  710. // FIXME: There are probably also keys with non-zero code points that should be filtered out.
  711. return code_point == 0 || code_point == 27;
  712. }
  713. EventResult EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Navigable& navigable, UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat)
  714. {
  715. GC::Ptr<DOM::Document> document = navigable.active_document();
  716. if (!document)
  717. return EventResult::Dropped;
  718. if (!document->is_fully_active())
  719. return EventResult::Dropped;
  720. if (GC::Ptr<DOM::Element> focused_element = document->focused_element()) {
  721. if (is<HTML::NavigableContainer>(*focused_element)) {
  722. auto& navigable_container = verify_cast<HTML::NavigableContainer>(*focused_element);
  723. if (navigable_container.content_navigable())
  724. return fire_keyboard_event(event_name, *navigable_container.content_navigable(), key, modifiers, code_point, repeat);
  725. }
  726. auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point, repeat);
  727. return focused_element->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled;
  728. }
  729. // FIXME: De-duplicate this. This is just to prevent wasting a KeyboardEvent allocation when recursing into an (i)frame.
  730. auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point, repeat);
  731. GC::Ptr target = document->body() ?: &document->root();
  732. return target->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled;
  733. }
  734. // https://w3c.github.io/uievents/#unicode-character-categories
  735. static bool produces_character_value(u32 code_point)
  736. {
  737. // A subset of the General Category values that are defined for each Unicode code point. This subset contains all
  738. // the Letter (Ll, Lm, Lo, Lt, Lu), Number (Nd, Nl, No), Punctuation (Pc, Pd, Pe, Pf, Pi, Po, Ps) and Symbol (Sc,
  739. // Sk, Sm, So) category values.
  740. return Unicode::code_point_has_letter_general_category(code_point)
  741. || Unicode::code_point_has_number_general_category(code_point)
  742. || Unicode::code_point_has_punctuation_general_category(code_point)
  743. || Unicode::code_point_has_symbol_general_category(code_point);
  744. }
  745. EventResult EventHandler::input_event(FlyString const& event_name, FlyString const& input_type, HTML::Navigable& navigable, u32 code_point)
  746. {
  747. auto document = navigable.active_document();
  748. if (!document)
  749. return EventResult::Dropped;
  750. if (!document->is_fully_active())
  751. return EventResult::Dropped;
  752. UIEvents::InputEventInit input_event_init;
  753. if (!is_unicode_control(code_point)) {
  754. input_event_init.data = String::from_code_point(code_point);
  755. }
  756. input_event_init.input_type = input_type;
  757. if (auto* focused_element = document->focused_element()) {
  758. if (is<HTML::NavigableContainer>(*focused_element)) {
  759. auto& navigable_container = verify_cast<HTML::NavigableContainer>(*focused_element);
  760. if (navigable_container.content_navigable())
  761. return input_event(event_name, input_type, *navigable_container.content_navigable(), code_point);
  762. }
  763. auto event = UIEvents::InputEvent::create_from_platform_event(document->realm(), event_name, input_event_init);
  764. return focused_element->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled;
  765. }
  766. auto event = UIEvents::InputEvent::create_from_platform_event(document->realm(), event_name, input_event_init);
  767. if (auto* body = document->body())
  768. return body->dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled;
  769. return document->root().dispatch_event(event) ? EventResult::Accepted : EventResult::Cancelled;
  770. }
  771. EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat)
  772. {
  773. if (!m_navigable->active_document())
  774. return EventResult::Dropped;
  775. if (!m_navigable->active_document()->is_fully_active())
  776. return EventResult::Dropped;
  777. auto dispatch_result = fire_keyboard_event(UIEvents::EventNames::keydown, m_navigable, key, modifiers, code_point, repeat);
  778. if (dispatch_result != EventResult::Accepted)
  779. return dispatch_result;
  780. // https://w3c.github.io/uievents/#event-type-keypress
  781. // If supported by a user agent, this event MUST be dispatched when a key is pressed down, if and only if that key
  782. // normally produces a character value.
  783. if (produces_character_value(code_point)) {
  784. dispatch_result = fire_keyboard_event(UIEvents::EventNames::keypress, m_navigable, key, modifiers, code_point, repeat);
  785. if (dispatch_result != EventResult::Accepted)
  786. return dispatch_result;
  787. }
  788. GC::Ref<DOM::Document> document = *m_navigable->active_document();
  789. if (key == UIEvents::KeyCode::Key_Tab) {
  790. if (modifiers & UIEvents::KeyModifier::Mod_Shift)
  791. return focus_previous_element() ? EventResult::Handled : EventResult::Dropped;
  792. return focus_next_element() ? EventResult::Handled : EventResult::Dropped;
  793. }
  794. // https://html.spec.whatwg.org/multipage/interaction.html#close-requests
  795. if (key == UIEvents::KeyCode::Key_Escape) {
  796. // 7. Let closedSomething be the result of processing close watchers on document's relevant global object.
  797. auto closed_something = document->window()->close_watcher_manager()->process_close_watchers();
  798. // 8. If closedSomething is true, then return.
  799. if (closed_something)
  800. return EventResult::Handled;
  801. // 9. Alternative processing: Otherwise, there was nothing watching for a close request. The user agent may
  802. // instead interpret this interaction as some other action, instead of interpreting it as a close request.
  803. }
  804. if (auto* element = m_navigable->active_document()->focused_element(); is<HTML::HTMLMediaElement>(element)) {
  805. auto& media_element = static_cast<HTML::HTMLMediaElement&>(*element);
  806. if (media_element.handle_keydown({}, key, modifiers).release_value_but_fixme_should_propagate_errors())
  807. return EventResult::Handled;
  808. }
  809. auto* target = document->active_input_events_target();
  810. if (target) {
  811. if (key == UIEvents::KeyCode::Key_Backspace) {
  812. FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::deleteContentBackward, m_navigable, code_point));
  813. target->handle_delete(InputEventsTarget::DeleteDirection::Backward);
  814. FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::deleteContentBackward, m_navigable, code_point));
  815. return EventResult::Handled;
  816. }
  817. if (key == UIEvents::KeyCode::Key_Delete) {
  818. FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::deleteContentForward, m_navigable, code_point));
  819. target->handle_delete(InputEventsTarget::DeleteDirection::Forward);
  820. FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::deleteContentForward, m_navigable, code_point));
  821. return EventResult::Handled;
  822. }
  823. #if defined(AK_OS_MACOS)
  824. if ((modifiers & UIEvents::Mod_Super) != 0) {
  825. if (key == UIEvents::KeyCode::Key_Left) {
  826. key = UIEvents::KeyCode::Key_Home;
  827. modifiers &= ~UIEvents::Mod_Super;
  828. }
  829. if (key == UIEvents::KeyCode::Key_Right) {
  830. key = UIEvents::KeyCode::Key_End;
  831. modifiers &= ~UIEvents::Mod_Super;
  832. }
  833. }
  834. #endif
  835. if (key == UIEvents::KeyCode::Key_Left || key == UIEvents::KeyCode::Key_Right) {
  836. auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
  837. if ((modifiers & UIEvents::Mod_PlatformWordJump) == 0) {
  838. if (key == UIEvents::KeyCode::Key_Left) {
  839. target->decrement_cursor_position_offset(collapse);
  840. } else {
  841. target->increment_cursor_position_offset(collapse);
  842. }
  843. } else {
  844. if (key == UIEvents::KeyCode::Key_Left) {
  845. target->decrement_cursor_position_to_previous_word(collapse);
  846. } else {
  847. target->increment_cursor_position_to_next_word(collapse);
  848. }
  849. }
  850. return EventResult::Handled;
  851. }
  852. if (key == UIEvents::KeyCode::Key_Home) {
  853. auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
  854. target->move_cursor_to_start(collapse);
  855. return EventResult::Handled;
  856. }
  857. if (key == UIEvents::KeyCode::Key_End) {
  858. auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
  859. target->move_cursor_to_end(collapse);
  860. return EventResult::Handled;
  861. }
  862. if (key == UIEvents::KeyCode::Key_Return) {
  863. FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::insertParagraph, m_navigable, code_point));
  864. target->handle_return_key();
  865. FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertParagraph, m_navigable, code_point));
  866. }
  867. // FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
  868. if (!should_ignore_keydown_event(code_point, modifiers)) {
  869. FIRE(input_event(UIEvents::EventNames::beforeinput, UIEvents::InputTypes::insertText, m_navigable, code_point));
  870. target->handle_insert(String::from_code_point(code_point));
  871. FIRE(input_event(UIEvents::EventNames::input, UIEvents::InputTypes::insertText, m_navigable, code_point));
  872. return EventResult::Handled;
  873. }
  874. } else if (auto selection = document->get_selection(); selection && !selection->is_collapsed()) {
  875. if (modifiers & UIEvents::Mod_Shift) {
  876. if (key == UIEvents::KeyCode::Key_Right) {
  877. if (modifiers & UIEvents::Mod_PlatformWordJump)
  878. selection->move_offset_to_next_word(false);
  879. else
  880. selection->move_offset_to_next_character(false);
  881. return EventResult::Handled;
  882. } else if (key == UIEvents::KeyCode::Key_Left) {
  883. if (modifiers & UIEvents::Mod_PlatformWordJump)
  884. selection->move_offset_to_previous_word(false);
  885. else
  886. selection->move_offset_to_previous_character(false);
  887. return EventResult::Handled;
  888. }
  889. }
  890. }
  891. // FIXME: Implement scroll by line and by page instead of approximating the behavior of other browsers.
  892. auto arrow_key_scroll_distance = 100;
  893. auto page_scroll_distance = document->window()->inner_height() - (document->window()->outer_height() - document->window()->inner_height());
  894. switch (key) {
  895. case UIEvents::KeyCode::Key_Up:
  896. case UIEvents::KeyCode::Key_Down:
  897. if (modifiers && modifiers != UIEvents::KeyModifier::Mod_Ctrl)
  898. break;
  899. if (modifiers)
  900. key == UIEvents::KeyCode::Key_Up ? document->scroll_to_the_beginning_of_the_document() : document->window()->scroll_by(0, INT64_MAX);
  901. else
  902. document->window()->scroll_by(0, key == UIEvents::KeyCode::Key_Up ? -arrow_key_scroll_distance : arrow_key_scroll_distance);
  903. return EventResult::Handled;
  904. case UIEvents::KeyCode::Key_Left:
  905. case UIEvents::KeyCode::Key_Right:
  906. if (modifiers && modifiers != UIEvents::KeyModifier::Mod_Alt)
  907. break;
  908. if (modifiers)
  909. document->page().traverse_the_history_by_delta(key == UIEvents::KeyCode::Key_Left ? -1 : 1);
  910. else
  911. document->window()->scroll_by(key == UIEvents::KeyCode::Key_Left ? -arrow_key_scroll_distance : arrow_key_scroll_distance, 0);
  912. return EventResult::Handled;
  913. case UIEvents::KeyCode::Key_PageUp:
  914. case UIEvents::KeyCode::Key_PageDown:
  915. if (modifiers != UIEvents::KeyModifier::Mod_None)
  916. break;
  917. document->window()->scroll_by(0, key == UIEvents::KeyCode::Key_PageUp ? -page_scroll_distance : page_scroll_distance);
  918. return EventResult::Handled;
  919. case UIEvents::KeyCode::Key_Home:
  920. document->scroll_to_the_beginning_of_the_document();
  921. return EventResult::Handled;
  922. case UIEvents::KeyCode::Key_End:
  923. document->window()->scroll_by(0, INT64_MAX);
  924. return EventResult::Handled;
  925. default:
  926. break;
  927. }
  928. return EventResult::Accepted;
  929. }
  930. EventResult EventHandler::handle_keyup(UIEvents::KeyCode key, u32 modifiers, u32 code_point, bool repeat)
  931. {
  932. // Keyup events as a result of auto-repeat are not fired.
  933. // See: https://w3c.github.io/uievents/#events-keyboard-event-order
  934. if (repeat)
  935. return EventResult::Dropped;
  936. return fire_keyboard_event(UIEvents::EventNames::keyup, m_navigable, key, modifiers, code_point, false);
  937. }
  938. void EventHandler::handle_paste(String const& text)
  939. {
  940. auto active_document = m_navigable->active_document();
  941. if (!active_document)
  942. return;
  943. if (!active_document->is_fully_active())
  944. return;
  945. auto* target = active_document->active_input_events_target();
  946. if (!target)
  947. return;
  948. target->handle_insert(text);
  949. }
  950. void EventHandler::set_mouse_event_tracking_paintable(Painting::Paintable* paintable)
  951. {
  952. m_mouse_event_tracking_paintable = paintable;
  953. }
  954. CSSPixelPoint EventHandler::compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const
  955. {
  956. // https://w3c.github.io/csswg-drafts/cssom-view/#dom-mouseevent-pagex
  957. // FIXME: 1. If the event’s dispatch flag is set, return the horizontal coordinate of the position where the event occurred relative to the origin of the initial containing block and terminate these steps.
  958. // 2. Let offset be the value of the scrollX attribute of the event’s associated Window object, if there is one, or zero otherwise.
  959. auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
  960. // 3. Return the sum of offset and the value of the event’s clientX attribute.
  961. return event_client_offset.translated(scroll_offset);
  962. }
  963. CSSPixelPoint EventHandler::compute_mouse_event_movement(CSSPixelPoint screen_position) const
  964. {
  965. // https://w3c.github.io/pointerlock/#dom-mouseevent-movementx
  966. // The attributes movementX movementY must provide the change in position of the pointer,
  967. // as if the values of screenX, screenY, were stored between two subsequent mousemove events eNow and ePrevious and the difference taken movementX = eNow.screenX-ePrevious.screenX.
  968. if (!m_mousemove_previous_screen_position.has_value())
  969. // When unlocked, the system cursor can exit and re-enter the user agent window.
  970. // If it does so and the user agent was not the target of operating system mouse move events
  971. // then the most recent pointer position will be unknown to the user agent and movementX/movementY can not be computed and must be set to zero.
  972. // FIXME: For this to actually work, m_mousemove_previous_client_offset needs to be cleared when the mouse leaves the window
  973. return { 0, 0 };
  974. return { screen_position.x() - m_mousemove_previous_screen_position.value().x(), screen_position.y() - m_mousemove_previous_screen_position.value().y() };
  975. }
  976. Optional<EventHandler::Target> EventHandler::target_for_mouse_position(CSSPixelPoint position)
  977. {
  978. if (m_mouse_event_tracking_paintable) {
  979. if (m_mouse_event_tracking_paintable->wants_mouse_events())
  980. return Target { m_mouse_event_tracking_paintable, {} };
  981. m_mouse_event_tracking_paintable = nullptr;
  982. }
  983. if (auto result = paint_root()->hit_test(position, Painting::HitTestType::Exact); result.has_value())
  984. return Target { result->paintable.ptr(), result->index_in_node };
  985. return {};
  986. }
  987. bool EventHandler::should_ignore_device_input_event() const
  988. {
  989. // From the moment that the user agent is to initiate the drag-and-drop operation, until the end of the drag-and-drop
  990. // operation, device input events (e.g. mouse and keyboard events) must be suppressed.
  991. return m_drag_and_drop_event_handler->has_ongoing_drag_and_drop_operation();
  992. }
  993. void EventHandler::visit_edges(JS::Cell::Visitor& visitor) const
  994. {
  995. m_drag_and_drop_event_handler->visit_edges(visitor);
  996. visitor.visit(m_mouse_event_tracking_paintable);
  997. }
  998. Unicode::Segmenter& EventHandler::word_segmenter()
  999. {
  1000. if (!m_word_segmenter)
  1001. m_word_segmenter = m_navigable->active_document()->word_segmenter().clone();
  1002. return *m_word_segmenter;
  1003. }
  1004. }