EventHandler.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Max Wipfli <mail@maxwipfli.ch>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGUI/Event.h>
  8. #include <LibGUI/Window.h>
  9. #include <LibWeb/DOM/Range.h>
  10. #include <LibWeb/DOM/Text.h>
  11. #include <LibWeb/HTML/HTMLAnchorElement.h>
  12. #include <LibWeb/HTML/HTMLIFrameElement.h>
  13. #include <LibWeb/HTML/HTMLImageElement.h>
  14. #include <LibWeb/Layout/InitialContainingBlock.h>
  15. #include <LibWeb/Page/BrowsingContext.h>
  16. #include <LibWeb/Page/EventHandler.h>
  17. #include <LibWeb/Page/Page.h>
  18. #include <LibWeb/UIEvents/EventNames.h>
  19. #include <LibWeb/UIEvents/MouseEvent.h>
  20. namespace Web {
  21. static Gfx::StandardCursor cursor_css_to_gfx(Optional<CSS::Cursor> cursor)
  22. {
  23. if (!cursor.has_value()) {
  24. return Gfx::StandardCursor::None;
  25. }
  26. switch (cursor.value()) {
  27. case CSS::Cursor::Crosshair:
  28. case CSS::Cursor::Cell:
  29. return Gfx::StandardCursor::Crosshair;
  30. case CSS::Cursor::Grab:
  31. case CSS::Cursor::Grabbing:
  32. return Gfx::StandardCursor::Drag;
  33. case CSS::Cursor::Pointer:
  34. return Gfx::StandardCursor::Hand;
  35. case CSS::Cursor::Help:
  36. return Gfx::StandardCursor::Help;
  37. case CSS::Cursor::None:
  38. return Gfx::StandardCursor::Hidden;
  39. case CSS::Cursor::Text:
  40. case CSS::Cursor::VerticalText:
  41. return Gfx::StandardCursor::IBeam;
  42. case CSS::Cursor::Move:
  43. case CSS::Cursor::AllScroll:
  44. return Gfx::StandardCursor::Move;
  45. case CSS::Cursor::Progress:
  46. case CSS::Cursor::Wait:
  47. return Gfx::StandardCursor::Wait;
  48. case CSS::Cursor::ColResize:
  49. return Gfx::StandardCursor::ResizeColumn;
  50. case CSS::Cursor::EResize:
  51. case CSS::Cursor::WResize:
  52. case CSS::Cursor::EwResize:
  53. return Gfx::StandardCursor::ResizeHorizontal;
  54. case CSS::Cursor::RowResize:
  55. return Gfx::StandardCursor::ResizeRow;
  56. case CSS::Cursor::NResize:
  57. case CSS::Cursor::SResize:
  58. case CSS::Cursor::NsResize:
  59. return Gfx::StandardCursor::ResizeVertical;
  60. case CSS::Cursor::NeResize:
  61. case CSS::Cursor::SwResize:
  62. case CSS::Cursor::NeswResize:
  63. return Gfx::StandardCursor::ResizeDiagonalBLTR;
  64. case CSS::Cursor::NwResize:
  65. case CSS::Cursor::SeResize:
  66. case CSS::Cursor::NwseResize:
  67. return Gfx::StandardCursor::ResizeDiagonalTLBR;
  68. default:
  69. return Gfx::StandardCursor::None;
  70. }
  71. }
  72. static Gfx::IntPoint compute_mouse_event_offset(const Gfx::IntPoint& position, const Layout::Node& layout_node)
  73. {
  74. auto top_left_of_layout_node = layout_node.box_type_agnostic_position();
  75. return {
  76. position.x() - static_cast<int>(top_left_of_layout_node.x()),
  77. position.y() - static_cast<int>(top_left_of_layout_node.y())
  78. };
  79. }
  80. EventHandler::EventHandler(Badge<BrowsingContext>, BrowsingContext& frame)
  81. : m_frame(frame)
  82. , m_edit_event_handler(make<EditEventHandler>(frame))
  83. {
  84. }
  85. EventHandler::~EventHandler()
  86. {
  87. }
  88. const Layout::InitialContainingBlock* EventHandler::layout_root() const
  89. {
  90. if (!m_frame.active_document())
  91. return nullptr;
  92. return m_frame.active_document()->layout_node();
  93. }
  94. Layout::InitialContainingBlock* EventHandler::layout_root()
  95. {
  96. if (!m_frame.active_document())
  97. return nullptr;
  98. return m_frame.active_document()->layout_node();
  99. }
  100. bool EventHandler::handle_mousewheel(const Gfx::IntPoint& position, unsigned int buttons, unsigned int modifiers, int wheel_delta)
  101. {
  102. if (!layout_root())
  103. return false;
  104. // FIXME: Support wheel events in subframes.
  105. auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
  106. if (result.layout_node) {
  107. if (result.layout_node->handle_mousewheel({}, position, buttons, modifiers, wheel_delta))
  108. return true;
  109. }
  110. if (auto* page = m_frame.page()) {
  111. page->client().page_did_request_scroll(wheel_delta);
  112. return true;
  113. }
  114. return false;
  115. }
  116. bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button, unsigned modifiers)
  117. {
  118. if (!layout_root())
  119. return false;
  120. if (m_mouse_event_tracking_layout_node) {
  121. m_mouse_event_tracking_layout_node->handle_mouseup({}, position, button, modifiers);
  122. return true;
  123. }
  124. bool handled_event = false;
  125. auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
  126. if (result.layout_node && result.layout_node->wants_mouse_events()) {
  127. result.layout_node->handle_mouseup({}, position, button, modifiers);
  128. // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again.
  129. if (!layout_root())
  130. return true;
  131. result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
  132. }
  133. if (result.layout_node && result.layout_node->dom_node()) {
  134. RefPtr<DOM::Node> node = result.layout_node->dom_node();
  135. if (is<HTML::HTMLIFrameElement>(*node)) {
  136. if (auto* subframe = verify_cast<HTML::HTMLIFrameElement>(*node).nested_browsing_context())
  137. return subframe->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers);
  138. return false;
  139. }
  140. auto offset = compute_mouse_event_offset(position, *result.layout_node);
  141. node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mouseup, offset.x(), offset.y(), position.x(), position.y()));
  142. handled_event = true;
  143. }
  144. if (button == GUI::MouseButton::Left)
  145. m_in_mouse_selection = false;
  146. return handled_event;
  147. }
  148. bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned button, unsigned modifiers)
  149. {
  150. if (!layout_root())
  151. return false;
  152. if (m_mouse_event_tracking_layout_node) {
  153. m_mouse_event_tracking_layout_node->handle_mousedown({}, position, button, modifiers);
  154. return true;
  155. }
  156. NonnullRefPtr document = *m_frame.active_document();
  157. RefPtr<DOM::Node> node;
  158. {
  159. auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
  160. if (!result.layout_node)
  161. return false;
  162. node = result.layout_node->dom_node();
  163. document->set_hovered_node(node);
  164. if (result.layout_node->wants_mouse_events()) {
  165. result.layout_node->handle_mousedown({}, position, button, modifiers);
  166. return true;
  167. }
  168. if (!node)
  169. return false;
  170. if (is<HTML::HTMLIFrameElement>(*node)) {
  171. if (auto* subframe = verify_cast<HTML::HTMLIFrameElement>(*node).nested_browsing_context())
  172. return subframe->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers);
  173. return false;
  174. }
  175. if (auto* page = m_frame.page())
  176. page->set_focused_browsing_context({}, m_frame);
  177. auto offset = compute_mouse_event_offset(position, *result.layout_node);
  178. node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousedown, offset.x(), offset.y(), position.x(), position.y()));
  179. }
  180. // NOTE: Dispatching an event may have disturbed the world.
  181. if (!layout_root() || layout_root() != node->document().layout_node())
  182. return true;
  183. if (button == GUI::MouseButton::Right && is<HTML::HTMLImageElement>(*node)) {
  184. auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
  185. auto image_url = image_element.document().parse_url(image_element.src());
  186. if (auto* page = m_frame.page())
  187. page->client().page_did_request_image_context_menu(m_frame.to_top_level_position(position), image_url, "", modifiers, image_element.bitmap());
  188. return true;
  189. }
  190. if (RefPtr<HTML::HTMLAnchorElement> link = node->enclosing_link_element()) {
  191. auto href = link->href();
  192. auto url = document->parse_url(href);
  193. dbgln("Web::EventHandler: Clicking on a link to {}", url);
  194. if (button == GUI::MouseButton::Left) {
  195. if (href.starts_with("javascript:")) {
  196. document->run_javascript(href.substring_view(11, href.length() - 11));
  197. } else if (href.starts_with('#')) {
  198. auto anchor = href.substring_view(1, href.length() - 1);
  199. m_frame.scroll_to_anchor(anchor);
  200. } else {
  201. document->set_active_element(link);
  202. if (m_frame.is_top_level()) {
  203. if (auto* page = m_frame.page())
  204. page->client().page_did_click_link(url, link->target(), modifiers);
  205. } else {
  206. // FIXME: Handle different targets!
  207. m_frame.loader().load(url, FrameLoader::Type::Navigation);
  208. }
  209. }
  210. } else if (button == GUI::MouseButton::Right) {
  211. if (auto* page = m_frame.page())
  212. page->client().page_did_request_link_context_menu(m_frame.to_top_level_position(position), url, link->target(), modifiers);
  213. } else if (button == GUI::MouseButton::Middle) {
  214. if (auto* page = m_frame.page())
  215. page->client().page_did_middle_click_link(url, link->target(), modifiers);
  216. }
  217. } else {
  218. if (button == GUI::MouseButton::Left) {
  219. auto result = layout_root()->hit_test(position, Layout::HitTestType::TextCursor);
  220. if (result.layout_node && result.layout_node->dom_node()) {
  221. m_frame.set_cursor_position(DOM::Position(*result.layout_node->dom_node(), result.index_in_node));
  222. layout_root()->set_selection({ { result.layout_node, result.index_in_node }, {} });
  223. m_in_mouse_selection = true;
  224. }
  225. } else if (button == GUI::MouseButton::Right) {
  226. if (auto* page = m_frame.page())
  227. page->client().page_did_request_context_menu(m_frame.to_top_level_position(position));
  228. }
  229. }
  230. return true;
  231. }
  232. bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned buttons, unsigned modifiers)
  233. {
  234. if (!layout_root())
  235. return false;
  236. if (m_mouse_event_tracking_layout_node) {
  237. m_mouse_event_tracking_layout_node->handle_mousemove({}, position, buttons, modifiers);
  238. return true;
  239. }
  240. auto& document = *m_frame.active_document();
  241. bool hovered_node_changed = false;
  242. bool is_hovering_link = false;
  243. Gfx::StandardCursor hovered_node_cursor = Gfx::StandardCursor::None;
  244. auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact);
  245. const HTML::HTMLAnchorElement* hovered_link_element = nullptr;
  246. if (result.layout_node) {
  247. if (result.layout_node->wants_mouse_events()) {
  248. document.set_hovered_node(result.layout_node->dom_node());
  249. result.layout_node->handle_mousemove({}, position, buttons, modifiers);
  250. // FIXME: It feels a bit aggressive to always update the cursor like this.
  251. if (auto* page = m_frame.page())
  252. page->client().page_did_request_cursor_change(Gfx::StandardCursor::None);
  253. return true;
  254. }
  255. RefPtr<DOM::Node> node = result.layout_node->dom_node();
  256. if (node && is<HTML::HTMLIFrameElement>(*node)) {
  257. if (auto* subframe = verify_cast<HTML::HTMLIFrameElement>(*node).nested_browsing_context())
  258. return subframe->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, *result.layout_node)), buttons, modifiers);
  259. return false;
  260. }
  261. hovered_node_changed = node != document.hovered_node();
  262. document.set_hovered_node(node);
  263. if (node) {
  264. hovered_link_element = node->enclosing_link_element();
  265. if (hovered_link_element)
  266. is_hovering_link = true;
  267. auto cursor = result.layout_node->computed_values().cursor();
  268. if (node->is_text() && cursor == CSS::Cursor::Auto)
  269. hovered_node_cursor = Gfx::StandardCursor::IBeam;
  270. else
  271. hovered_node_cursor = cursor_css_to_gfx(cursor);
  272. auto offset = compute_mouse_event_offset(position, *result.layout_node);
  273. node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousemove, offset.x(), offset.y(), position.x(), position.y()));
  274. // NOTE: Dispatching an event may have disturbed the world.
  275. if (!layout_root() || layout_root() != node->document().layout_node())
  276. return true;
  277. }
  278. if (m_in_mouse_selection) {
  279. auto hit = layout_root()->hit_test(position, Layout::HitTestType::TextCursor);
  280. if (hit.layout_node && hit.layout_node->dom_node()) {
  281. m_frame.set_cursor_position(DOM::Position(*hit.layout_node->dom_node(), result.index_in_node));
  282. layout_root()->set_selection_end({ hit.layout_node, hit.index_in_node });
  283. }
  284. if (auto* page = m_frame.page())
  285. page->client().page_did_change_selection();
  286. }
  287. }
  288. if (auto* page = m_frame.page()) {
  289. page->client().page_did_request_cursor_change(hovered_node_cursor);
  290. if (hovered_node_changed) {
  291. RefPtr<HTML::HTMLElement> hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element_with_attribute(HTML::AttributeNames::title) : nullptr;
  292. if (hovered_html_element && !hovered_html_element->title().is_null()) {
  293. page->client().page_did_enter_tooltip_area(m_frame.to_top_level_position(position), hovered_html_element->title());
  294. } else {
  295. page->client().page_did_leave_tooltip_area();
  296. }
  297. if (is_hovering_link)
  298. page->client().page_did_hover_link(document.parse_url(hovered_link_element->href()));
  299. else
  300. page->client().page_did_unhover_link();
  301. }
  302. }
  303. return true;
  304. }
  305. bool EventHandler::focus_next_element()
  306. {
  307. if (!m_frame.active_document())
  308. return false;
  309. auto* element = m_frame.active_document()->focused_element();
  310. if (!element) {
  311. element = m_frame.active_document()->first_child_of_type<DOM::Element>();
  312. if (element && element->is_focusable()) {
  313. m_frame.active_document()->set_focused_element(element);
  314. return true;
  315. }
  316. }
  317. for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order())
  318. ;
  319. m_frame.active_document()->set_focused_element(element);
  320. return element;
  321. }
  322. bool EventHandler::focus_previous_element()
  323. {
  324. // FIXME: Implement Shift-Tab cycling backwards through focusable elements!
  325. return false;
  326. }
  327. constexpr bool should_ignore_keydown_event(u32 code_point)
  328. {
  329. // FIXME: There are probably also keys with non-zero code points that should be filtered out.
  330. return code_point == 0;
  331. }
  332. bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point)
  333. {
  334. if (key == KeyCode::Key_Tab) {
  335. if (modifiers & KeyModifier::Mod_Shift)
  336. return focus_previous_element();
  337. else
  338. return focus_next_element();
  339. }
  340. if (layout_root()->selection().is_valid()) {
  341. auto range = layout_root()->selection().to_dom_range()->normalized();
  342. if (range->start_container()->is_editable()) {
  343. m_frame.active_document()->layout_node()->set_selection({});
  344. // FIXME: This doesn't work for some reason?
  345. m_frame.set_cursor_position({ *range->start_container(), range->start_offset() });
  346. if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) {
  347. m_edit_event_handler->handle_delete(range);
  348. return true;
  349. } else if (!should_ignore_keydown_event(code_point)) {
  350. m_edit_event_handler->handle_delete(range);
  351. m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point);
  352. m_frame.increment_cursor_position_offset();
  353. return true;
  354. }
  355. }
  356. }
  357. if (m_frame.cursor_position().is_valid() && m_frame.cursor_position().node()->is_editable()) {
  358. if (key == KeyCode::Key_Backspace) {
  359. if (!m_frame.decrement_cursor_position_offset()) {
  360. // FIXME: Move to the previous node and delete the last character there.
  361. return true;
  362. }
  363. m_edit_event_handler->handle_delete_character_after(m_frame.cursor_position());
  364. return true;
  365. } else if (key == KeyCode::Key_Delete) {
  366. if (m_frame.cursor_position().offset_is_at_end_of_node()) {
  367. // FIXME: Move to the next node and delete the first character there.
  368. return true;
  369. }
  370. m_edit_event_handler->handle_delete_character_after(m_frame.cursor_position());
  371. return true;
  372. } else if (key == KeyCode::Key_Right) {
  373. if (!m_frame.increment_cursor_position_offset()) {
  374. // FIXME: Move to the next node.
  375. }
  376. return true;
  377. } else if (key == KeyCode::Key_Left) {
  378. if (!m_frame.decrement_cursor_position_offset()) {
  379. // FIXME: Move to the previous node.
  380. }
  381. return true;
  382. } else if (!should_ignore_keydown_event(code_point)) {
  383. m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point);
  384. m_frame.increment_cursor_position_offset();
  385. return true;
  386. } else {
  387. // NOTE: Because modifier keys should be ignored, we need to return true.
  388. return true;
  389. }
  390. }
  391. return false;
  392. }
  393. void EventHandler::set_mouse_event_tracking_layout_node(Layout::Node* layout_node)
  394. {
  395. if (layout_node)
  396. m_mouse_event_tracking_layout_node = layout_node->make_weak_ptr();
  397. else
  398. m_mouse_event_tracking_layout_node = nullptr;
  399. }
  400. }