| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2021-02-09 21:23:50 +01:00
										 |  |  |  * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  |  * modification, are permitted provided that the following conditions are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 1. Redistributions of source code must retain the above copyright notice, this | 
					
						
							|  |  |  |  *    list of conditions and the following disclaimer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | 
					
						
							|  |  |  |  *    this list of conditions and the following disclaimer in the documentation | 
					
						
							|  |  |  |  *    and/or other materials provided with the distribution. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
					
						
							|  |  |  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
					
						
							|  |  |  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
					
						
							|  |  |  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
					
						
							|  |  |  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
					
						
							|  |  |  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
					
						
							|  |  |  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
					
						
							|  |  |  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
					
						
							|  |  |  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <LibGUI/Event.h>
 | 
					
						
							|  |  |  | #include <LibGUI/Window.h>
 | 
					
						
							| 
									
										
										
										
											2020-12-06 19:51:55 +01:00
										 |  |  | #include <LibWeb/DOM/Range.h>
 | 
					
						
							| 
									
										
										
										
											2020-08-02 12:10:01 +02:00
										 |  |  | #include <LibWeb/DOM/Text.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-28 17:21:23 +02:00
										 |  |  | #include <LibWeb/HTML/HTMLAnchorElement.h>
 | 
					
						
							|  |  |  | #include <LibWeb/HTML/HTMLIFrameElement.h>
 | 
					
						
							| 
									
										
										
										
											2020-10-02 19:01:51 +02:00
										 |  |  | #include <LibWeb/HTML/HTMLImageElement.h>
 | 
					
						
							| 
									
										
										
										
											2020-08-21 17:54:44 +02:00
										 |  |  | #include <LibWeb/InProcessWebView.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | #include <LibWeb/Layout/InitialContainingBlockBox.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-28 19:27:41 +02:00
										 |  |  | #include <LibWeb/Page/EventHandler.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Page/Frame.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-21 19:15:57 +00:00
										 |  |  | #include <LibWeb/UIEvents/EventNames.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-28 17:21:23 +02:00
										 |  |  | #include <LibWeb/UIEvents/MouseEvent.h>
 | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Web { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | static Gfx::IntPoint compute_mouse_event_offset(const Gfx::IntPoint& position, const Layout::Node& layout_node) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     auto top_left_of_layout_node = layout_node.box_type_agnostic_position(); | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         position.x() - static_cast<int>(top_left_of_layout_node.x()), | 
					
						
							|  |  |  |         position.y() - static_cast<int>(top_left_of_layout_node.y()) | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EventHandler::EventHandler(Badge<Frame>, Frame& frame) | 
					
						
							|  |  |  |     : m_frame(frame) | 
					
						
							| 
									
										
										
										
											2020-12-01 23:35:47 +01:00
										 |  |  |     , m_edit_event_handler(make<EditEventHandler>(frame)) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EventHandler::~EventHandler() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | const Layout::InitialContainingBlockBox* EventHandler::layout_root() const | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!m_frame.document()) | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							| 
									
										
										
										
											2020-07-10 23:52:06 +02:00
										 |  |  |     return m_frame.document()->layout_node(); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | Layout::InitialContainingBlockBox* EventHandler::layout_root() | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!m_frame.document()) | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							| 
									
										
										
										
											2020-07-10 23:52:06 +02:00
										 |  |  |     return m_frame.document()->layout_node(); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 10:57:59 +02:00
										 |  |  | bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button, unsigned modifiers) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-10 23:43:25 +02:00
										 |  |  |     if (!layout_root()) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_mouse_event_tracking_layout_node) { | 
					
						
							|  |  |  |         m_mouse_event_tracking_layout_node->handle_mouseup({}, position, button, modifiers); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |     bool handled_event = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (result.layout_node && result.layout_node->wants_mouse_events()) { | 
					
						
							|  |  |  |         result.layout_node->handle_mouseup({}, position, button, modifiers); | 
					
						
							| 
									
										
										
										
											2020-09-12 17:55:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |         // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again.
 | 
					
						
							| 
									
										
										
										
											2020-09-12 17:55:19 +02:00
										 |  |  |         if (!layout_root()) | 
					
						
							|  |  |  |             return true; | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |         result = layout_root()->hit_test(position, Layout::HitTestType::Exact); | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 14:46:36 +01:00
										 |  |  |     if (result.layout_node && result.layout_node->dom_node()) { | 
					
						
							|  |  |  |         RefPtr<DOM::Node> node = result.layout_node->dom_node(); | 
					
						
							| 
									
										
										
										
											2020-07-28 18:20:36 +02:00
										 |  |  |         if (is<HTML::HTMLIFrameElement>(*node)) { | 
					
						
							| 
									
										
										
										
											2020-09-22 17:48:04 +02:00
										 |  |  |             if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame()) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |                 return subframe->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         auto offset = compute_mouse_event_offset(position, *result.layout_node); | 
					
						
							| 
									
										
										
										
											2020-11-21 19:15:57 +00:00
										 |  |  |         node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mouseup, offset.x(), offset.y())); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         handled_event = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-09 14:02:45 +01:00
										 |  |  |     if (button == GUI::MouseButton::Left) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         m_in_mouse_selection = false; | 
					
						
							|  |  |  |     return handled_event; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 10:57:59 +02:00
										 |  |  | bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned button, unsigned modifiers) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-10 23:43:25 +02:00
										 |  |  |     if (!layout_root()) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_mouse_event_tracking_layout_node) { | 
					
						
							|  |  |  |         m_mouse_event_tracking_layout_node->handle_mousedown({}, position, button, modifiers); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-06 19:41:10 +02:00
										 |  |  |     NonnullRefPtr document = *m_frame.document(); | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |     RefPtr<DOM::Node> node; | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |         auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); | 
					
						
							|  |  |  |         if (!result.layout_node) | 
					
						
							|  |  |  |             return false; | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |         node = result.layout_node->dom_node(); | 
					
						
							|  |  |  |         document->set_hovered_node(node); | 
					
						
							| 
									
										
										
										
											2020-09-12 17:55:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |         if (result.layout_node->wants_mouse_events()) { | 
					
						
							|  |  |  |             result.layout_node->handle_mousedown({}, position, button, modifiers); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |         if (!node) | 
					
						
							|  |  |  |             return false; | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |         if (is<HTML::HTMLIFrameElement>(*node)) { | 
					
						
							|  |  |  |             if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame()) | 
					
						
							|  |  |  |                 return subframe->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, *result.layout_node)), button, modifiers); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |         if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |             page->set_focused_frame({}, m_frame); | 
					
						
							| 
									
										
										
										
											2020-08-14 11:33:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |         auto offset = compute_mouse_event_offset(position, *result.layout_node); | 
					
						
							|  |  |  |         node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousedown, offset.x(), offset.y())); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // NOTE: Dispatching an event may have disturbed the world.
 | 
					
						
							|  |  |  |     if (!layout_root() || layout_root() != node->document().layout_node()) | 
					
						
							| 
									
										
										
										
											2020-07-10 23:43:25 +02:00
										 |  |  |         return true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 19:01:51 +02:00
										 |  |  |     if (button == GUI::MouseButton::Right && is<HTML::HTMLImageElement>(*node)) { | 
					
						
							|  |  |  |         auto& image_element = downcast<HTML::HTMLImageElement>(*node); | 
					
						
							|  |  |  |         auto image_url = image_element.document().complete_url(image_element.src()); | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |         if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |             page->client().page_did_request_image_context_menu(m_frame.to_main_frame_position(position), image_url, "", modifiers, image_element.bitmap()); | 
					
						
							| 
									
										
										
										
											2020-10-02 19:01:51 +02:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-28 18:20:36 +02:00
										 |  |  |     if (RefPtr<HTML::HTMLAnchorElement> link = node->enclosing_link_element()) { | 
					
						
							| 
									
										
										
										
											2020-07-06 19:41:10 +02:00
										 |  |  |         auto href = link->href(); | 
					
						
							|  |  |  |         auto url = document->complete_url(href); | 
					
						
							| 
									
										
										
										
											2021-01-09 14:02:45 +01:00
										 |  |  |         dbgln("Web::EventHandler: Clicking on a link to {}", url); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         if (button == GUI::MouseButton::Left) { | 
					
						
							|  |  |  |             if (href.starts_with("javascript:")) { | 
					
						
							| 
									
										
										
										
											2020-07-06 19:41:10 +02:00
										 |  |  |                 document->run_javascript(href.substring_view(11, href.length() - 11)); | 
					
						
							| 
									
										
										
										
											2020-07-05 14:50:38 +02:00
										 |  |  |             } else if (href.starts_with('#')) { | 
					
						
							|  |  |  |                 auto anchor = href.substring_view(1, href.length() - 1); | 
					
						
							|  |  |  |                 m_frame.scroll_to_anchor(anchor); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2020-06-07 14:45:59 +02:00
										 |  |  |                 if (m_frame.is_main_frame()) { | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |                     if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |                         page->client().page_did_click_link(url, link->target(), modifiers); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:45:59 +02:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     // FIXME: Handle different targets!
 | 
					
						
							| 
									
										
										
										
											2020-07-07 17:25:33 +02:00
										 |  |  |                     m_frame.loader().load(url, FrameLoader::Type::Navigation); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:45:59 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } else if (button == GUI::MouseButton::Right) { | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |                 page->client().page_did_request_link_context_menu(m_frame.to_main_frame_position(position), url, link->target(), modifiers); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         } else if (button == GUI::MouseButton::Middle) { | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |                 page->client().page_did_middle_click_link(url, link->target(), modifiers); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         if (button == GUI::MouseButton::Left) { | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |             auto result = layout_root()->hit_test(position, Layout::HitTestType::TextCursor); | 
					
						
							| 
									
										
										
										
											2020-11-22 14:46:36 +01:00
										 |  |  |             if (result.layout_node && result.layout_node->dom_node()) { | 
					
						
							| 
									
										
										
										
											2020-08-05 16:55:56 +02:00
										 |  |  |                 m_frame.set_cursor_position(DOM::Position(*node, result.index_in_node)); | 
					
						
							| 
									
										
										
										
											2020-08-21 17:54:44 +02:00
										 |  |  |                 layout_root()->set_selection({ { result.layout_node, result.index_in_node }, {} }); | 
					
						
							| 
									
										
										
										
											2020-08-05 16:55:56 +02:00
										 |  |  |                 m_in_mouse_selection = true; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-07-28 17:21:23 +02:00
										 |  |  |         } else if (button == GUI::MouseButton::Right) { | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |                 page->client().page_did_request_context_menu(m_frame.to_main_frame_position(position)); | 
					
						
							| 
									
										
										
										
											2020-06-27 14:21:58 -06:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-10 10:57:59 +02:00
										 |  |  | bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned buttons, unsigned modifiers) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-10 23:43:25 +02:00
										 |  |  |     if (!layout_root()) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         return false; | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (m_mouse_event_tracking_layout_node) { | 
					
						
							|  |  |  |         m_mouse_event_tracking_layout_node->handle_mousemove({}, position, buttons, modifiers); | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |     auto& document = *m_frame.document(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool hovered_node_changed = false; | 
					
						
							|  |  |  |     bool is_hovering_link = false; | 
					
						
							| 
									
										
										
										
											2020-08-17 12:54:41 +02:00
										 |  |  |     bool is_hovering_text = false; | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |     auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); | 
					
						
							| 
									
										
										
										
											2020-07-28 18:20:36 +02:00
										 |  |  |     const HTML::HTMLAnchorElement* hovered_link_element = nullptr; | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |     if (result.layout_node) { | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (result.layout_node->wants_mouse_events()) { | 
					
						
							| 
									
										
										
										
											2020-11-22 14:46:36 +01:00
										 |  |  |             document.set_hovered_node(result.layout_node->dom_node()); | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  |             result.layout_node->handle_mousemove({}, position, buttons, modifiers); | 
					
						
							|  |  |  |             // FIXME: It feels a bit aggressive to always update the cursor like this.
 | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |                 page->client().page_did_request_cursor_change(Gfx::StandardCursor::None); | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 14:46:36 +01:00
										 |  |  |         RefPtr<DOM::Node> node = result.layout_node->dom_node(); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-28 18:20:36 +02:00
										 |  |  |         if (node && is<HTML::HTMLIFrameElement>(*node)) { | 
					
						
							| 
									
										
										
										
											2020-09-22 17:48:04 +02:00
										 |  |  |             if (auto* subframe = downcast<HTML::HTMLIFrameElement>(*node).content_frame()) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |                 return subframe->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, *result.layout_node)), buttons, modifiers); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         hovered_node_changed = node != document.hovered_node(); | 
					
						
							|  |  |  |         document.set_hovered_node(node); | 
					
						
							|  |  |  |         if (node) { | 
					
						
							| 
									
										
										
										
											2020-08-17 12:54:41 +02:00
										 |  |  |             if (node->is_text()) | 
					
						
							|  |  |  |                 is_hovering_text = true; | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |             hovered_link_element = node->enclosing_link_element(); | 
					
						
							| 
									
										
										
										
											2020-11-19 22:21:16 +01:00
										 |  |  |             if (hovered_link_element) | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |                 is_hovering_link = true; | 
					
						
							|  |  |  |             auto offset = compute_mouse_event_offset(position, *result.layout_node); | 
					
						
							| 
									
										
										
										
											2020-11-21 19:15:57 +00:00
										 |  |  |             node->dispatch_event(UIEvents::MouseEvent::create(UIEvents::EventNames::mousemove, offset.x(), offset.y())); | 
					
						
							| 
									
										
										
										
											2020-11-29 16:39:56 +01:00
										 |  |  |             // NOTE: Dispatching an event may have disturbed the world.
 | 
					
						
							|  |  |  |             if (!layout_root() || layout_root() != node->document().layout_node()) | 
					
						
							| 
									
										
										
										
											2020-07-10 23:43:25 +02:00
										 |  |  |                 return true; | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (m_in_mouse_selection) { | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  |             auto hit = layout_root()->hit_test(position, Layout::HitTestType::TextCursor); | 
					
						
							| 
									
										
										
										
											2020-11-22 14:46:36 +01:00
										 |  |  |             if (hit.layout_node && hit.layout_node->dom_node()) { | 
					
						
							| 
									
										
										
										
											2021-02-15 18:12:14 +01:00
										 |  |  |                 m_frame.set_cursor_position(DOM::Position(*node, result.index_in_node)); | 
					
						
							| 
									
										
										
										
											2020-08-21 17:54:44 +02:00
										 |  |  |                 layout_root()->set_selection_end({ hit.layout_node, hit.index_in_node }); | 
					
						
							| 
									
										
										
										
											2020-08-05 16:55:56 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             if (auto* page = m_frame.page()) | 
					
						
							|  |  |  |                 page->client().page_did_change_selection(); | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-08-17 12:54:41 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |     if (auto* page = m_frame.page()) { | 
					
						
							| 
									
										
										
										
											2020-06-08 20:31:49 +02:00
										 |  |  |         if (is_hovering_link) | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             page->client().page_did_request_cursor_change(Gfx::StandardCursor::Hand); | 
					
						
							|  |  |  |         else if (is_hovering_text) | 
					
						
							|  |  |  |             page->client().page_did_request_cursor_change(Gfx::StandardCursor::IBeam); | 
					
						
							| 
									
										
										
										
											2020-06-08 20:31:49 +02:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2020-11-12 18:23:05 +01:00
										 |  |  |             page->client().page_did_request_cursor_change(Gfx::StandardCursor::None); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (hovered_node_changed) { | 
					
						
							|  |  |  |             RefPtr<HTML::HTMLElement> hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element() : nullptr; | 
					
						
							|  |  |  |             if (hovered_html_element && !hovered_html_element->title().is_null()) { | 
					
						
							|  |  |  |                 page->client().page_did_enter_tooltip_area(m_frame.to_main_frame_position(position), hovered_html_element->title()); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 page->client().page_did_leave_tooltip_area(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (is_hovering_link) | 
					
						
							|  |  |  |                 page->client().page_did_hover_link(document.complete_url(hovered_link_element->href())); | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 page->client().page_did_unhover_link(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-14 19:40:37 +02:00
										 |  |  | bool EventHandler::focus_next_element() | 
					
						
							| 
									
										
										
										
											2020-08-02 12:10:01 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-08-14 19:40:37 +02:00
										 |  |  |     if (!m_frame.document()) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     auto* element = m_frame.document()->focused_element(); | 
					
						
							|  |  |  |     if (!element) { | 
					
						
							|  |  |  |         element = m_frame.document()->first_child_of_type<DOM::Element>(); | 
					
						
							|  |  |  |         if (element && element->is_focusable()) { | 
					
						
							|  |  |  |             m_frame.document()->set_focused_element(element); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order()) | 
					
						
							|  |  |  |         ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     m_frame.document()->set_focused_element(element); | 
					
						
							|  |  |  |     return element; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool EventHandler::focus_previous_element() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // FIXME: Implement Shift-Tab cycling backwards through focusable elements!
 | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (key == KeyCode::Key_Tab) { | 
					
						
							|  |  |  |         if (modifiers & KeyModifier::Mod_Shift) | 
					
						
							|  |  |  |             return focus_previous_element(); | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             return focus_next_element(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-01 23:36:12 +01:00
										 |  |  |     if (layout_root()->selection().is_valid()) { | 
					
						
							| 
									
										
										
										
											2020-12-06 19:51:55 +01:00
										 |  |  |         auto range = layout_root()->selection().to_dom_range()->normalized(); | 
					
						
							| 
									
										
										
										
											2020-12-14 10:58:10 +01:00
										 |  |  |         if (range->start_container()->is_editable()) { | 
					
						
							|  |  |  |             m_frame.document()->layout_node()->set_selection({}); | 
					
						
							| 
									
										
										
										
											2020-12-01 23:36:12 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-14 10:58:10 +01:00
										 |  |  |             // FIXME: This doesn't work for some reason?
 | 
					
						
							|  |  |  |             m_frame.set_cursor_position({ *range->start_container(), range->start_offset() }); | 
					
						
							| 
									
										
										
										
											2020-12-03 18:20:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-14 10:58:10 +01:00
										 |  |  |             if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) { | 
					
						
							| 
									
										
										
										
											2020-12-02 15:00:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-01 23:36:12 +01:00
										 |  |  |                 m_edit_event_handler->handle_delete(range); | 
					
						
							|  |  |  |                 return true; | 
					
						
							| 
									
										
										
										
											2020-12-14 10:58:10 +01:00
										 |  |  |             } else { | 
					
						
							|  |  |  |                 m_edit_event_handler->handle_delete(range); | 
					
						
							|  |  |  |                 m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); | 
					
						
							| 
									
										
										
										
											2021-02-09 21:23:50 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 auto new_position = m_frame.cursor_position(); | 
					
						
							|  |  |  |                 new_position.set_offset(new_position.offset() + 1); | 
					
						
							|  |  |  |                 m_frame.set_cursor_position(move(new_position)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-14 10:58:10 +01:00
										 |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2020-12-01 23:36:12 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-01 23:35:47 +01:00
										 |  |  |     if (m_frame.cursor_position().is_valid() && m_frame.cursor_position().node()->is_editable()) { | 
					
						
							|  |  |  |         if (key == KeyCode::Key_Backspace) { | 
					
						
							| 
									
										
										
										
											2020-12-02 15:00:55 +01:00
										 |  |  |             auto position = m_frame.cursor_position(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (position.offset() == 0) | 
					
						
							|  |  |  |                 TODO(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-09 21:23:50 +01:00
										 |  |  |             auto new_position = m_frame.cursor_position(); | 
					
						
							|  |  |  |             new_position.set_offset(position.offset() - 1); | 
					
						
							|  |  |  |             m_frame.set_cursor_position(move(new_position)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-06 19:51:55 +01:00
										 |  |  |             m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset() - 1, *position.node(), position.offset())); | 
					
						
							| 
									
										
										
										
											2020-12-02 15:00:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } else if (key == KeyCode::Key_Delete) { | 
					
						
							|  |  |  |             auto position = m_frame.cursor_position(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (position.offset() >= downcast<DOM::Text>(position.node())->data().length()) | 
					
						
							|  |  |  |                 TODO(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-06 19:51:55 +01:00
										 |  |  |             m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset(), *position.node(), position.offset() + 1)); | 
					
						
							| 
									
										
										
										
											2020-12-02 15:00:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } else if (key == KeyCode::Key_Right) { | 
					
						
							|  |  |  |             auto position = m_frame.cursor_position(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (position.offset() >= downcast<DOM::Text>(position.node())->data().length()) | 
					
						
							|  |  |  |                 TODO(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-09 21:23:50 +01:00
										 |  |  |             auto new_position = m_frame.cursor_position(); | 
					
						
							|  |  |  |             new_position.set_offset(position.offset() + 1); | 
					
						
							|  |  |  |             m_frame.set_cursor_position(move(new_position)); | 
					
						
							| 
									
										
										
										
											2020-12-02 15:00:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } else if (key == KeyCode::Key_Left) { | 
					
						
							|  |  |  |             auto position = m_frame.cursor_position(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (position.offset() == 0) | 
					
						
							|  |  |  |                 TODO(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-09 21:23:50 +01:00
										 |  |  |             auto new_position = m_frame.cursor_position(); | 
					
						
							|  |  |  |             new_position.set_offset(new_position.offset() - 1); | 
					
						
							|  |  |  |             m_frame.set_cursor_position(move(new_position)); | 
					
						
							| 
									
										
										
										
											2020-12-02 15:00:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 16:05:59 +02:00
										 |  |  |             return true; | 
					
						
							| 
									
										
										
										
											2020-12-02 13:41:58 +01:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); | 
					
						
							| 
									
										
										
										
											2021-02-09 21:23:50 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             auto new_position = m_frame.cursor_position(); | 
					
						
							|  |  |  |             new_position.set_offset(new_position.offset() + 1); | 
					
						
							|  |  |  |             m_frame.set_cursor_position(move(new_position)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:41:58 +01:00
										 |  |  |             return true; | 
					
						
							| 
									
										
										
										
											2020-08-02 16:05:59 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-08-02 12:10:01 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-12-01 23:35:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 15:53:31 +02:00
										 |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2020-08-02 12:10:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | void EventHandler::set_mouse_event_tracking_layout_node(Layout::Node* layout_node) | 
					
						
							| 
									
										
										
										
											2020-09-11 18:15:47 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     if (layout_node) | 
					
						
							|  |  |  |         m_mouse_event_tracking_layout_node = layout_node->make_weak_ptr(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_mouse_event_tracking_layout_node = nullptr; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-12-03 18:46:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-07 14:40:38 +02:00
										 |  |  | } |