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 >
2021-05-18 13:13:58 +02:00
* Copyright ( c ) 2021 , Max Wipfli < mail @ maxwipfli . ch >
2020-06-07 14:40:38 +02:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-06-07 14:40:38 +02:00
*/
# include <LibGUI/Event.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>
2021-11-18 15:01:28 +01:00
# include <LibWeb/HTML/BrowsingContext.h>
2022-11-05 14:34:24 +00:00
# include <LibWeb/HTML/Focus.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>
2023-06-16 10:51:38 -04:00
# include <LibWeb/HTML/HTMLMediaElement.h>
2023-05-15 09:42:56 -04:00
# include <LibWeb/HTML/HTMLVideoElement.h>
2023-02-25 11:04:29 +01:00
# include <LibWeb/Layout/Viewport.h>
2020-07-28 19:27:41 +02:00
# include <LibWeb/Page/EventHandler.h>
2021-08-24 16:28:08 +02:00
# include <LibWeb/Page/Page.h>
2022-03-10 23:13:37 +01:00
# include <LibWeb/Painting/PaintableBox.h>
2020-11-21 19:15:57 +00:00
# include <LibWeb/UIEvents/EventNames.h>
2021-09-27 23:22:21 +02:00
# include <LibWeb/UIEvents/KeyboardEvent.h>
2020-07-28 17:21:23 +02:00
# include <LibWeb/UIEvents/MouseEvent.h>
2022-10-17 20:54:01 +03:00
# include <LibWeb/UIEvents/WheelEvent.h>
2020-06-07 14:40:38 +02:00
namespace Web {
2023-02-25 10:44:51 -07:00
static JS : : GCPtr < DOM : : Node > dom_node_for_event_dispatch ( Painting : : Paintable & paintable )
2022-10-23 17:53:02 +02:00
{
if ( auto node = paintable . mouse_event_target ( ) )
return node ;
if ( auto node = paintable . dom_node ( ) )
return node ;
if ( auto * layout_parent = paintable . layout_node ( ) . parent ( ) )
return layout_parent - > dom_node ( ) ;
return nullptr ;
}
2023-02-25 10:44:51 -07:00
static bool parent_element_for_event_dispatch ( Painting : : Paintable & paintable , JS : : GCPtr < DOM : : Node > & node , Layout : : Node * & layout_node )
2022-10-24 03:24:19 +02:00
{
layout_node = & paintable . layout_node ( ) ;
while ( layout_node & & node & & ! node - > is_element ( ) & & layout_node - > parent ( ) ) {
layout_node = layout_node - > parent ( ) ;
2022-11-06 21:45:30 +01:00
if ( layout_node - > is_anonymous ( ) )
continue ;
2022-10-24 03:24:19 +02:00
node = layout_node - > dom_node ( ) ;
}
return node & & layout_node ;
}
2021-02-21 20:15:00 +00:00
static Gfx : : StandardCursor cursor_css_to_gfx ( Optional < CSS : : Cursor > cursor )
{
if ( ! cursor . has_value ( ) ) {
return Gfx : : StandardCursor : : None ;
}
switch ( cursor . value ( ) ) {
case CSS : : Cursor : : Crosshair :
case CSS : : Cursor : : Cell :
return Gfx : : StandardCursor : : Crosshair ;
case CSS : : Cursor : : Grab :
case CSS : : Cursor : : Grabbing :
return Gfx : : StandardCursor : : Drag ;
case CSS : : Cursor : : Pointer :
return Gfx : : StandardCursor : : Hand ;
case CSS : : Cursor : : Help :
return Gfx : : StandardCursor : : Help ;
case CSS : : Cursor : : None :
return Gfx : : StandardCursor : : Hidden ;
2023-04-13 20:06:21 -04:00
case CSS : : Cursor : : NotAllowed :
return Gfx : : StandardCursor : : Disallowed ;
2021-02-21 20:15:00 +00:00
case CSS : : Cursor : : Text :
case CSS : : Cursor : : VerticalText :
return Gfx : : StandardCursor : : IBeam ;
case CSS : : Cursor : : Move :
case CSS : : Cursor : : AllScroll :
return Gfx : : StandardCursor : : Move ;
case CSS : : Cursor : : Progress :
case CSS : : Cursor : : Wait :
return Gfx : : StandardCursor : : Wait ;
case CSS : : Cursor : : ColResize :
return Gfx : : StandardCursor : : ResizeColumn ;
case CSS : : Cursor : : EResize :
case CSS : : Cursor : : WResize :
case CSS : : Cursor : : EwResize :
return Gfx : : StandardCursor : : ResizeHorizontal ;
case CSS : : Cursor : : RowResize :
return Gfx : : StandardCursor : : ResizeRow ;
case CSS : : Cursor : : NResize :
case CSS : : Cursor : : SResize :
case CSS : : Cursor : : NsResize :
return Gfx : : StandardCursor : : ResizeVertical ;
case CSS : : Cursor : : NeResize :
case CSS : : Cursor : : SwResize :
case CSS : : Cursor : : NeswResize :
return Gfx : : StandardCursor : : ResizeDiagonalBLTR ;
case CSS : : Cursor : : NwResize :
case CSS : : Cursor : : SeResize :
case CSS : : Cursor : : NwseResize :
return Gfx : : StandardCursor : : ResizeDiagonalTLBR ;
2022-09-26 17:33:35 +01:00
case CSS : : Cursor : : ZoomIn :
case CSS : : Cursor : : ZoomOut :
return Gfx : : StandardCursor : : Zoom ;
2023-04-13 20:06:21 -04:00
case CSS : : Cursor : : ContextMenu :
case CSS : : Cursor : : Alias :
case CSS : : Cursor : : Copy :
case CSS : : Cursor : : NoDrop :
// FIXME: No corresponding GFX Standard Cursor, fallthrough to None
case CSS : : Cursor : : Auto :
case CSS : : Cursor : : Default :
2021-02-21 20:15:00 +00:00
default :
return Gfx : : StandardCursor : : None ;
}
}
2022-11-02 17:35:53 +00:00
static CSSPixelPoint compute_mouse_event_offset ( CSSPixelPoint position , Layout : : Node const & layout_node )
2020-06-07 14:40:38 +02:00
{
auto top_left_of_layout_node = layout_node . box_type_agnostic_position ( ) ;
return {
2022-11-02 17:35:53 +00:00
position . x ( ) - top_left_of_layout_node . x ( ) ,
position . y ( ) - top_left_of_layout_node . y ( )
2020-06-07 14:40:38 +02:00
} ;
}
2022-02-06 14:41:29 +01:00
EventHandler : : EventHandler ( Badge < HTML : : BrowsingContext > , HTML : : BrowsingContext & browsing_context )
: m_browsing_context ( browsing_context )
, m_edit_event_handler ( make < EditEventHandler > ( browsing_context ) )
2020-06-07 14:40:38 +02:00
{
}
2022-03-14 13:21:51 -06:00
EventHandler : : ~ EventHandler ( ) = default ;
2020-06-07 14:40:38 +02:00
2022-03-11 00:03:28 +01:00
Painting : : PaintableBox * EventHandler : : paint_root ( )
{
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > active_document ( ) )
2022-03-11 00:03:28 +01:00
return nullptr ;
2023-04-20 16:01:16 +01:00
return const_cast < Painting : : PaintableBox * > ( m_browsing_context - > active_document ( ) - > paintable_box ( ) ) ;
2022-03-11 00:03:28 +01:00
}
Painting : : PaintableBox const * EventHandler : : paint_root ( ) const
{
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > active_document ( ) )
2022-03-11 00:03:28 +01:00
return nullptr ;
2023-04-20 16:01:16 +01:00
return const_cast < Painting : : PaintableBox * > ( m_browsing_context - > active_document ( ) - > paintable_box ( ) ) ;
2022-03-11 00:03:28 +01:00
}
2022-11-02 17:35:53 +00:00
bool EventHandler : : handle_mousewheel ( CSSPixelPoint position , unsigned button , unsigned buttons , unsigned int modifiers , int wheel_delta_x , int wheel_delta_y )
2021-02-22 19:45:41 +01:00
{
2023-08-05 17:24:03 +01:00
constexpr int scroll_step_size = 24 ;
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > active_document ( ) )
m_browsing_context - > active_document ( ) - > update_layout ( ) ;
2022-03-16 18:50:56 +01:00
2022-03-11 00:03:28 +01:00
if ( ! paint_root ( ) )
2021-02-22 19:48:24 +01:00
return false ;
2022-01-20 22:35:01 +01:00
if ( modifiers & KeyModifier : : Mod_Shift )
swap ( wheel_delta_x , wheel_delta_y ) ;
2022-10-17 20:54:01 +03:00
bool handled_event = false ;
2021-02-22 19:48:24 +01:00
2023-01-11 12:51:49 +01:00
JS : : GCPtr < Painting : : Paintable > paintable ;
2023-04-27 09:19:20 -04:00
if ( auto result = target_for_mouse_position ( position ) ; result . has_value ( ) )
paintable = result - > paintable ;
2021-03-02 08:39:07 +11:00
2022-10-17 20:54:01 +03:00
if ( paintable ) {
paintable - > handle_mousewheel ( { } , position , buttons , modifiers , wheel_delta_x , wheel_delta_y ) ;
2022-10-23 17:53:02 +02:00
auto node = dom_node_for_event_dispatch ( * paintable ) ;
2022-10-17 20:54:01 +03:00
if ( node ) {
// FIXME: Support wheel events in nested browsing contexts.
if ( is < HTML : : HTMLIFrameElement > ( * node ) ) {
return false ;
}
// Search for the first parent of the hit target that's an element.
2023-02-25 10:44:51 -07:00
Layout : : Node * layout_node ;
2022-10-24 03:24:19 +02:00
if ( ! parent_element_for_event_dispatch ( * paintable , node , layout_node ) )
2022-10-17 20:54:01 +03:00
return false ;
auto offset = compute_mouse_event_offset ( position , * layout_node ) ;
2023-04-06 16:12:33 +02:00
if ( node - > dispatch_event ( UIEvents : : WheelEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : wheel , offset . x ( ) , offset . y ( ) , position . x ( ) , position . y ( ) , wheel_delta_x , wheel_delta_y , buttons , button ) . release_value_but_fixme_should_propagate_errors ( ) ) ) {
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) ) {
2023-05-27 17:07:08 +02:00
if ( m_browsing_context = = & page - > top_level_browsing_context ( ) )
2023-08-05 17:24:03 +01:00
page - > client ( ) . page_did_request_scroll ( wheel_delta_x * scroll_step_size , wheel_delta_y * scroll_step_size ) ;
2022-10-17 20:54:01 +03:00
}
}
handled_event = true ;
}
2021-02-22 19:48:24 +01:00
}
2022-10-17 20:54:01 +03:00
return handled_event ;
2021-02-22 19:45:41 +01:00
}
2022-11-02 17:35:53 +00:00
bool EventHandler : : handle_mouseup ( CSSPixelPoint position , unsigned button , unsigned buttons , unsigned modifiers )
2020-06-07 14:40:38 +02:00
{
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > active_document ( ) )
m_browsing_context - > active_document ( ) - > update_layout ( ) ;
2022-03-16 18:50:56 +01:00
2022-03-11 00:03:28 +01:00
if ( ! paint_root ( ) )
2020-06-07 14:40:38 +02:00
return false ;
2020-09-11 18:15:47 +02:00
2020-06-07 14:40:38 +02:00
bool handled_event = false ;
2023-01-11 12:51:49 +01:00
JS : : GCPtr < Painting : : Paintable > paintable ;
2023-04-27 09:19:20 -04:00
if ( auto result = target_for_mouse_position ( position ) ; result . has_value ( ) )
paintable = result - > paintable ;
2020-09-11 18:15:47 +02:00
2022-03-14 23:05:55 +00:00
if ( paintable & & paintable - > wants_mouse_events ( ) ) {
if ( paintable - > handle_mouseup ( { } , position , button , modifiers ) = = Painting : : Paintable : : DispatchEventOfSameName : : No )
return false ;
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.
2022-03-11 00:03:28 +01:00
if ( ! paint_root ( ) )
2020-09-12 17:55:19 +02:00
return true ;
2022-11-02 17:35:53 +00:00
if ( auto result = paint_root ( ) - > hit_test ( position , Painting : : HitTestType : : Exact ) ; result . has_value ( ) )
2022-03-21 11:11:05 +01:00
paintable = result - > paintable ;
2020-09-11 18:15:47 +02:00
}
2022-03-14 23:05:55 +00:00
if ( paintable ) {
2022-10-23 17:53:02 +02:00
auto node = dom_node_for_event_dispatch ( * paintable ) ;
2022-02-07 13:27:17 +01:00
2022-03-14 23:05:55 +00:00
if ( node ) {
if ( is < HTML : : HTMLIFrameElement > ( * node ) ) {
if ( auto * nested_browsing_context = static_cast < HTML : : HTMLIFrameElement & > ( * node ) . nested_browsing_context ( ) )
2022-10-20 20:56:38 +03:00
return nested_browsing_context - > event_handler ( ) . handle_mouseup ( position . translated ( compute_mouse_event_offset ( { } , paintable - > layout_node ( ) ) ) , button , buttons , modifiers ) ;
2022-03-14 23:05:55 +00:00
return false ;
}
2022-05-03 16:50:38 +02:00
// Search for the first parent of the hit target that's an element.
// "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)
// "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)
2023-02-25 10:44:51 -07:00
Layout : : Node * layout_node ;
2022-10-24 03:24:19 +02:00
if ( ! parent_element_for_event_dispatch ( * paintable , node , layout_node ) ) {
2022-05-03 16:50:38 +02:00
// FIXME: This is pretty ugly but we need to bail out here.
goto after_node_use ;
}
auto offset = compute_mouse_event_offset ( position , * layout_node ) ;
2022-12-31 16:04:48 +02:00
auto client_offset = compute_mouse_event_client_offset ( position ) ;
2022-12-31 16:40:36 +02:00
auto page_offset = compute_mouse_event_page_offset ( client_offset ) ;
2023-04-06 16:12:33 +02:00
node - > dispatch_event ( UIEvents : : MouseEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : mouseup , offset , client_offset , page_offset , buttons , button ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-03-14 23:05:55 +00:00
handled_event = true ;
2023-07-21 16:42:48 +02:00
bool run_activation_behavior = false ;
2023-05-21 20:29:30 +01:00
if ( node . ptr ( ) = = m_mousedown_target ) {
if ( button = = GUI : : MouseButton : : Primary )
run_activation_behavior = node - > dispatch_event ( UIEvents : : MouseEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : click , offset , client_offset , page_offset , button ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
else if ( button = = GUI : : MouseButton : : Secondary & & ! ( modifiers & Mod_Shift ) ) // Allow the user to bypass custom context menus by holding shift, like Firefox.
run_activation_behavior = node - > dispatch_event ( UIEvents : : MouseEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : contextmenu , offset , client_offset , page_offset , button ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-04-08 21:48:00 -03:00
}
if ( run_activation_behavior ) {
// FIXME: This is ad-hoc and incorrect. The reason this exists is
// because we are missing browsing context navigation:
//
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
//
// Additionally, we currently cannot spawn a new top-level
// browsing context for new tab operations, because the new
// top-level browsing context would be in another process. To
// fix this, there needs to be some way to be able to
// communicate with browsing contexts in remote WebContent
// processes, and then step 8 of this algorithm needs to be
// implemented in BrowsingContext::choose_a_browsing_context:
//
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
2023-02-25 10:44:51 -07:00
if ( JS : : GCPtr < HTML : : HTMLAnchorElement const > link = node - > enclosing_link_element ( ) ) {
2023-02-26 16:09:02 -07:00
JS : : NonnullGCPtr < DOM : : Document > document = * m_browsing_context - > active_document ( ) ;
2022-04-08 21:48:00 -03:00
auto href = link - > href ( ) ;
auto url = document - > parse_url ( href ) ;
dbgln ( " Web::EventHandler: Clicking on a link to {} " , url ) ;
if ( button = = GUI : : MouseButton : : Primary ) {
2022-07-11 17:32:29 +00:00
if ( href . starts_with ( " javascript: " sv ) ) {
2023-06-26 20:04:14 +01:00
document - > navigate_to_javascript_url ( href ) ;
2022-04-08 21:48:00 -03:00
} else if ( ! url . fragment ( ) . is_null ( ) & & url . equals ( document - > url ( ) , AK : : URL : : ExcludeFragment : : Yes ) ) {
2023-02-26 16:09:02 -07:00
m_browsing_context - > scroll_to_anchor ( url . fragment ( ) ) ;
2022-04-08 21:48:00 -03:00
} else {
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > is_top_level ( ) ) {
if ( auto * page = m_browsing_context - > page ( ) )
2022-04-08 21:48:00 -03:00
page - > client ( ) . page_did_click_link ( url , link - > target ( ) , modifiers ) ;
}
2022-03-15 14:37:58 +00:00
}
2022-04-08 21:48:00 -03:00
} else if ( button = = GUI : : MouseButton : : Middle ) {
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) )
2022-04-08 21:48:00 -03:00
page - > client ( ) . page_did_middle_click_link ( url , link - > target ( ) , modifiers ) ;
} else if ( button = = GUI : : MouseButton : : Secondary ) {
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) )
page - > client ( ) . page_did_request_link_context_menu ( m_browsing_context - > to_top_level_position ( position ) , url , link - > target ( ) , modifiers ) ;
2022-03-15 14:37:58 +00:00
}
2022-03-30 18:38:52 +00:00
} else if ( button = = GUI : : MouseButton : : Secondary ) {
2022-04-08 21:48:00 -03:00
if ( is < HTML : : HTMLImageElement > ( * node ) ) {
auto & image_element = verify_cast < HTML : : HTMLImageElement > ( * node ) ;
auto image_url = image_element . document ( ) . parse_url ( image_element . src ( ) ) ;
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) )
page - > client ( ) . page_did_request_image_context_menu ( m_browsing_context - > to_top_level_position ( position ) , image_url , " " , modifiers , image_element . bitmap ( ) ) ;
2023-06-16 10:51:38 -04:00
} else if ( is < HTML : : HTMLMediaElement > ( * node ) ) {
auto & media_element = verify_cast < HTML : : HTMLMediaElement > ( * node ) ;
2023-05-15 09:42:56 -04:00
2023-06-16 10:51:38 -04:00
Page : : MediaContextMenu menu {
. media_url = media_element . document ( ) . parse_url ( media_element . current_src ( ) ) ,
. is_video = is < HTML : : HTMLVideoElement > ( * node ) ,
. is_playing = media_element . potentially_playing ( ) ,
2023-06-16 11:29:54 -04:00
. is_muted = media_element . muted ( ) ,
2023-06-16 10:51:38 -04:00
. has_user_agent_controls = media_element . has_attribute ( HTML : : AttributeNames : : controls ) ,
. is_looping = media_element . has_attribute ( HTML : : AttributeNames : : loop ) ,
} ;
2023-05-15 09:42:56 -04:00
if ( auto * page = m_browsing_context - > page ( ) )
2023-06-16 10:51:38 -04:00
page - > did_request_media_context_menu ( media_element . id ( ) , m_browsing_context - > to_top_level_position ( position ) , " " , modifiers , move ( menu ) ) ;
2023-02-26 16:09:02 -07:00
} else if ( auto * page = m_browsing_context - > page ( ) ) {
page - > client ( ) . page_did_request_context_menu ( m_browsing_context - > to_top_level_position ( position ) ) ;
2022-04-08 21:48:00 -03:00
}
2022-04-07 22:43:30 +01:00
}
2022-03-15 14:37:58 +00:00
}
2022-02-07 13:27:17 +01:00
}
2020-06-07 14:40:38 +02:00
}
2022-05-03 16:50:38 +02:00
after_node_use :
2021-10-27 13:20:27 +02:00
if ( button = = GUI : : MouseButton : : Primary )
2020-06-07 14:40:38 +02:00
m_in_mouse_selection = false ;
return handled_event ;
}
2022-11-02 17:35:53 +00:00
bool EventHandler : : handle_mousedown ( CSSPixelPoint position , unsigned button , unsigned buttons , unsigned modifiers )
2020-06-07 14:40:38 +02:00
{
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > active_document ( ) )
m_browsing_context - > active_document ( ) - > update_layout ( ) ;
2022-03-16 18:50:56 +01:00
2022-03-11 00:03:28 +01:00
if ( ! paint_root ( ) )
2020-06-07 14:40:38 +02:00
return false ;
2020-09-11 18:15:47 +02:00
2023-02-26 16:09:02 -07:00
JS : : NonnullGCPtr < DOM : : Document > document = * m_browsing_context - > active_document ( ) ;
2022-08-28 13:42:07 +02:00
JS : : GCPtr < DOM : : Node > node ;
2020-06-07 14:40:38 +02:00
2020-11-29 16:39:56 +01:00
{
2023-01-11 12:51:49 +01:00
JS : : GCPtr < Painting : : Paintable > paintable ;
2023-04-27 09:19:20 -04:00
if ( auto result = target_for_mouse_position ( position ) ; result . has_value ( ) )
2022-03-21 11:11:05 +01:00
paintable = result - > paintable ;
2023-04-27 09:19:20 -04:00
else
return false ;
2020-06-07 14:40:38 +02:00
2022-03-14 23:05:55 +00:00
auto pointer_events = paintable - > computed_values ( ) . pointer_events ( ) ;
2022-02-07 17:19:33 +00:00
// FIXME: Handle other values for pointer-events.
2022-10-19 13:46:48 +03:00
VERIFY ( pointer_events ! = CSS : : PointerEvents : : None ) ;
2021-10-19 12:53:22 +02:00
2022-10-23 17:53:02 +02:00
node = dom_node_for_event_dispatch ( * paintable ) ;
2020-11-29 16:39:56 +01:00
document - > set_hovered_node ( node ) ;
2020-09-12 17:55:19 +02:00
2022-03-14 23:05:55 +00:00
if ( paintable - > wants_mouse_events ( ) ) {
if ( paintable - > handle_mousedown ( { } , position , button , modifiers ) = = Painting : : Paintable : : DispatchEventOfSameName : : No )
return false ;
2020-11-29 16:39:56 +01:00
}
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 ) ) {
2022-02-06 14:41:29 +01:00
if ( auto * nested_browsing_context = static_cast < HTML : : HTMLIFrameElement & > ( * node ) . nested_browsing_context ( ) )
2022-10-20 20:56:38 +03:00
return nested_browsing_context - > event_handler ( ) . handle_mousedown ( position . translated ( compute_mouse_event_offset ( { } , paintable - > layout_node ( ) ) ) , button , buttons , modifiers ) ;
2020-11-29 16:39:56 +01:00
return false ;
}
2020-06-07 14:40:38 +02:00
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) )
2022-02-06 14:41:29 +01:00
page - > set_focused_browsing_context ( { } , m_browsing_context ) ;
2020-08-14 11:33:20 +02:00
2022-05-03 16:50:38 +02:00
// Search for the first parent of the hit target that's an element.
// "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)
// "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)
2023-02-25 10:44:51 -07:00
Layout : : Node * layout_node ;
2022-10-24 03:24:19 +02:00
if ( ! parent_element_for_event_dispatch ( * paintable , node , layout_node ) )
2022-05-03 16:50:38 +02:00
return false ;
2022-08-28 13:42:07 +02:00
m_mousedown_target = node . ptr ( ) ;
2022-05-03 16:50:38 +02:00
auto offset = compute_mouse_event_offset ( position , * layout_node ) ;
2022-12-31 16:04:48 +02:00
auto client_offset = compute_mouse_event_client_offset ( position ) ;
2022-12-31 16:40:36 +02:00
auto page_offset = compute_mouse_event_page_offset ( client_offset ) ;
2023-04-06 16:12:33 +02:00
node - > dispatch_event ( UIEvents : : MouseEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : mousedown , offset , client_offset , page_offset , buttons , button ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2020-11-29 16:39:56 +01:00
}
// NOTE: Dispatching an event may have disturbed the world.
2023-04-20 16:01:16 +01:00
if ( ! paint_root ( ) | | paint_root ( ) ! = node - > document ( ) . paintable_box ( ) )
2020-07-10 23:43:25 +02:00
return true ;
2022-03-15 14:37:58 +00:00
if ( button = = GUI : : MouseButton : : Primary ) {
2022-11-02 17:35:53 +00:00
if ( auto result = paint_root ( ) - > hit_test ( position , Painting : : HitTestType : : TextCursor ) ; result . has_value ( ) ) {
2022-03-15 14:37:58 +00:00
auto paintable = result - > paintable ;
if ( paintable - > dom_node ( ) ) {
2022-02-06 19:28:09 +01:00
// See if we want to focus something.
bool did_focus_something = false ;
2023-03-18 20:26:56 +01:00
for ( auto candidate = node ; candidate ; candidate = candidate - > parent_or_shadow_host ( ) ) {
2022-02-06 19:28:09 +01:00
if ( candidate - > is_focusable ( ) ) {
2022-11-05 14:34:24 +00:00
// 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".
// 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.
HTML : : run_focusing_steps ( candidate . ptr ( ) , nullptr , " click " sv ) ;
2022-02-06 19:28:09 +01:00
did_focus_something = true ;
break ;
}
}
// If we didn't focus anything, place the document text cursor at the mouse position.
// FIXME: This is all rather strange. Find a better solution.
if ( ! did_focus_something ) {
2023-02-26 16:09:02 -07:00
m_browsing_context - > set_cursor_position ( DOM : : Position ( * paintable - > dom_node ( ) , result - > index_in_node ) ) ;
2023-01-11 19:48:53 +01:00
if ( auto selection = document - > get_selection ( ) ) {
( void ) selection - > set_base_and_extent ( * paintable - > dom_node ( ) , result - > index_in_node , * paintable - > dom_node ( ) , result - > index_in_node ) ;
}
2022-02-06 19:28:09 +01:00
m_in_mouse_selection = true ;
}
2020-08-05 16:55:56 +02:00
}
2020-06-27 14:21:58 -06:00
}
2020-06-07 14:40:38 +02:00
}
return true ;
}
2022-11-02 17:35:53 +00:00
bool EventHandler : : handle_mousemove ( CSSPixelPoint position , unsigned buttons , unsigned modifiers )
2020-06-07 14:40:38 +02:00
{
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > active_document ( ) )
m_browsing_context - > active_document ( ) - > update_layout ( ) ;
2022-03-16 18:50:56 +01:00
2022-03-11 00:03:28 +01:00
if ( ! paint_root ( ) )
2020-06-07 14:40:38 +02:00
return false ;
2020-09-11 18:15:47 +02:00
2023-02-26 16:09:02 -07:00
auto & document = * m_browsing_context - > active_document ( ) ;
2020-06-07 14:40:38 +02:00
bool hovered_node_changed = false ;
bool is_hovering_link = false ;
2021-02-21 20:15:00 +00:00
Gfx : : StandardCursor hovered_node_cursor = Gfx : : StandardCursor : : None ;
2022-03-14 23:05:55 +00:00
2023-01-11 12:51:49 +01:00
JS : : GCPtr < Painting : : Paintable > paintable ;
2022-03-14 23:05:55 +00:00
Optional < int > start_index ;
2023-04-27 09:19:20 -04:00
if ( auto result = target_for_mouse_position ( position ) ; result . has_value ( ) ) {
paintable = result - > paintable ;
start_index = result - > index_in_node ;
2022-03-14 23:05:55 +00:00
}
2020-07-28 18:20:36 +02:00
const HTML : : HTMLAnchorElement * hovered_link_element = nullptr ;
2022-03-14 23:05:55 +00:00
if ( paintable ) {
if ( paintable - > wants_mouse_events ( ) ) {
2022-03-21 11:19:02 +01:00
document . set_hovered_node ( paintable - > dom_node ( ) ) ;
2022-03-14 23:05:55 +00:00
if ( paintable - > handle_mousemove ( { } , position , buttons , modifiers ) = = Painting : : Paintable : : DispatchEventOfSameName : : No )
return false ;
2020-09-11 18:15:47 +02:00
// FIXME: It feels a bit aggressive to always update the cursor like this.
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) )
2020-11-12 18:23:05 +01:00
page - > client ( ) . page_did_request_cursor_change ( Gfx : : StandardCursor : : None ) ;
2020-09-11 18:15:47 +02:00
}
2022-10-23 17:53:02 +02:00
auto node = dom_node_for_event_dispatch ( * paintable ) ;
2020-06-07 14:40:38 +02:00
2020-07-28 18:20:36 +02:00
if ( node & & is < HTML : : HTMLIFrameElement > ( * node ) ) {
2022-02-06 14:41:29 +01:00
if ( auto * nested_browsing_context = static_cast < HTML : : HTMLIFrameElement & > ( * node ) . nested_browsing_context ( ) )
2022-03-14 23:05:55 +00:00
return nested_browsing_context - > event_handler ( ) . handle_mousemove ( position . translated ( compute_mouse_event_offset ( { } , paintable - > layout_node ( ) ) ) , buttons , modifiers ) ;
2020-06-07 14:40:38 +02:00
return false ;
}
2022-03-14 23:05:55 +00:00
auto pointer_events = paintable - > computed_values ( ) . pointer_events ( ) ;
2022-02-07 17:19:33 +00:00
// FIXME: Handle other values for pointer-events.
2022-10-19 13:46:48 +03:00
VERIFY ( pointer_events ! = CSS : : PointerEvents : : None ) ;
2021-10-05 19:47:13 +01:00
2022-11-07 18:22:33 +01:00
// Search for the first parent of the hit target that's an element.
// "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)
// "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)
2023-02-25 10:44:51 -07:00
Layout : : Node * layout_node ;
2022-11-07 18:22:33 +01:00
bool found_parent_element = parent_element_for_event_dispatch ( * paintable , node , layout_node ) ;
2022-08-28 13:42:07 +02:00
hovered_node_changed = node . ptr ( ) ! = document . hovered_node ( ) ;
2020-06-07 14:40:38 +02:00
document . set_hovered_node ( node ) ;
2022-11-07 18:22:33 +01:00
if ( found_parent_element ) {
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 ;
2021-02-21 20:15:00 +00:00
2021-10-05 19:47:13 +01:00
if ( node - > is_text ( ) ) {
2022-03-14 23:05:55 +00:00
auto cursor = paintable - > computed_values ( ) . cursor ( ) ;
2021-10-05 19:47:13 +01:00
if ( cursor = = CSS : : Cursor : : Auto )
hovered_node_cursor = Gfx : : StandardCursor : : IBeam ;
else
2022-02-15 21:48:48 -05:00
hovered_node_cursor = cursor_css_to_gfx ( cursor ) ;
} else if ( node - > is_element ( ) ) {
2022-03-14 23:05:55 +00:00
auto cursor = paintable - > computed_values ( ) . cursor ( ) ;
2022-02-15 21:48:48 -05:00
if ( cursor = = CSS : : Cursor : : Auto )
hovered_node_cursor = Gfx : : StandardCursor : : Arrow ;
else
2021-10-05 19:47:13 +01:00
hovered_node_cursor = cursor_css_to_gfx ( cursor ) ;
}
2021-02-21 20:15:00 +00:00
2022-05-03 16:50:38 +02:00
auto offset = compute_mouse_event_offset ( position , * layout_node ) ;
2022-12-31 16:04:48 +02:00
auto client_offset = compute_mouse_event_client_offset ( position ) ;
2022-12-31 16:40:36 +02:00
auto page_offset = compute_mouse_event_page_offset ( client_offset ) ;
2023-04-06 16:12:33 +02:00
node - > dispatch_event ( UIEvents : : MouseEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : mousemove , offset , client_offset , page_offset , buttons ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2020-11-29 16:39:56 +01:00
// NOTE: Dispatching an event may have disturbed the world.
2023-04-20 16:01:16 +01:00
if ( ! paint_root ( ) | | paint_root ( ) ! = node - > document ( ) . paintable_box ( ) )
2020-07-10 23:43:25 +02:00
return true ;
2020-06-07 14:40:38 +02:00
}
if ( m_in_mouse_selection ) {
2022-11-02 17:35:53 +00:00
auto hit = paint_root ( ) - > hit_test ( position , Painting : : HitTestType : : TextCursor ) ;
2022-03-21 11:16:02 +01:00
if ( start_index . has_value ( ) & & hit . has_value ( ) & & hit - > dom_node ( ) ) {
2023-02-26 16:09:02 -07:00
m_browsing_context - > set_cursor_position ( DOM : : Position ( * hit - > dom_node ( ) , * start_index ) ) ;
2023-01-11 19:48:53 +01:00
if ( auto selection = document . get_selection ( ) ) {
auto anchor_node = selection - > anchor_node ( ) ;
if ( anchor_node )
( void ) selection - > set_base_and_extent ( * anchor_node , selection - > anchor_offset ( ) , * hit - > paintable - > dom_node ( ) , hit - > index_in_node ) ;
else
( void ) selection - > set_base_and_extent ( * hit - > paintable - > dom_node ( ) , hit - > index_in_node , * hit - > paintable - > dom_node ( ) , hit - > index_in_node ) ;
}
2023-02-26 16:09:02 -07:00
m_browsing_context - > set_needs_display ( ) ;
2020-08-05 16:55:56 +02:00
}
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) )
2020-11-12 18:23:05 +01:00
page - > client ( ) . page_did_change_selection ( ) ;
2020-06-07 14:40:38 +02:00
}
}
2020-08-17 12:54:41 +02:00
2023-02-26 16:09:02 -07:00
if ( auto * page = m_browsing_context - > page ( ) ) {
2021-02-21 20:15:00 +00:00
page - > client ( ) . page_did_request_cursor_change ( hovered_node_cursor ) ;
2020-11-12 18:23:05 +01:00
if ( hovered_node_changed ) {
2023-02-25 10:44:51 -07:00
JS : : GCPtr < HTML : : HTMLElement const > hovered_html_element = document . hovered_node ( ) ? document . hovered_node ( ) - > enclosing_html_element_with_attribute ( HTML : : AttributeNames : : title ) : nullptr ;
2020-11-12 18:23:05 +01:00
if ( hovered_html_element & & ! hovered_html_element - > title ( ) . is_null ( ) ) {
2023-02-26 16:09:02 -07:00
page - > client ( ) . page_did_enter_tooltip_area ( m_browsing_context - > to_top_level_position ( position ) , hovered_html_element - > title ( ) ) ;
2020-11-12 18:23:05 +01:00
} else {
page - > client ( ) . page_did_leave_tooltip_area ( ) ;
}
if ( is_hovering_link )
2021-09-09 18:08:56 +02:00
page - > client ( ) . page_did_hover_link ( document . parse_url ( hovered_link_element - > href ( ) ) ) ;
2020-11-12 18:23:05 +01:00
else
page - > client ( ) . page_did_unhover_link ( ) ;
}
2020-06-07 14:40:38 +02:00
}
return true ;
}
2022-11-02 17:35:53 +00:00
bool EventHandler : : handle_doubleclick ( CSSPixelPoint position , unsigned button , unsigned buttons , unsigned modifiers )
2022-06-14 19:38:00 +02:00
{
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > active_document ( ) )
m_browsing_context - > active_document ( ) - > update_layout ( ) ;
2022-06-14 19:38:00 +02:00
if ( ! paint_root ( ) )
return false ;
2023-01-11 12:51:49 +01:00
JS : : GCPtr < Painting : : Paintable > paintable ;
2023-04-27 09:19:20 -04:00
if ( auto result = target_for_mouse_position ( position ) ; result . has_value ( ) )
2022-06-14 19:38:00 +02:00
paintable = result - > paintable ;
2023-04-27 09:19:20 -04:00
else
return false ;
2022-06-14 19:38:00 +02:00
auto pointer_events = paintable - > computed_values ( ) . pointer_events ( ) ;
// FIXME: Handle other values for pointer-events.
if ( pointer_events = = CSS : : PointerEvents : : None )
return false ;
2022-10-23 17:53:02 +02:00
auto node = dom_node_for_event_dispatch ( * paintable ) ;
2022-06-14 19:38:00 +02:00
if ( paintable - > wants_mouse_events ( ) ) {
// FIXME: Handle double clicks.
}
if ( ! node )
return false ;
if ( is < HTML : : HTMLIFrameElement > ( * node ) ) {
if ( auto * nested_browsing_context = static_cast < HTML : : HTMLIFrameElement & > ( * node ) . nested_browsing_context ( ) )
2022-10-20 20:56:38 +03:00
return nested_browsing_context - > event_handler ( ) . handle_doubleclick ( position . translated ( compute_mouse_event_offset ( { } , paintable - > layout_node ( ) ) ) , button , buttons , modifiers ) ;
2022-06-14 19:38:00 +02:00
return false ;
}
// Search for the first parent of the hit target that's an element.
// "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)
2023-02-25 10:44:51 -07:00
Layout : : Node * layout_node ;
2022-10-24 03:24:19 +02:00
if ( ! parent_element_for_event_dispatch ( * paintable , node , layout_node ) )
2022-06-14 19:38:00 +02:00
return false ;
2022-06-14 19:39:33 +02:00
auto offset = compute_mouse_event_offset ( position , * layout_node ) ;
2022-12-31 16:04:48 +02:00
auto client_offset = compute_mouse_event_client_offset ( position ) ;
2022-12-31 16:40:36 +02:00
auto page_offset = compute_mouse_event_page_offset ( client_offset ) ;
2023-04-06 16:12:33 +02:00
node - > dispatch_event ( UIEvents : : MouseEvent : : create_from_platform_event ( node - > realm ( ) , UIEvents : : EventNames : : dblclick , offset , client_offset , page_offset , buttons , button ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-06-14 19:38:00 +02:00
2022-06-14 21:57:29 +02:00
// NOTE: Dispatching an event may have disturbed the world.
2023-04-20 16:01:16 +01:00
if ( ! paint_root ( ) | | paint_root ( ) ! = node - > document ( ) . paintable_box ( ) )
2022-06-14 21:57:29 +02:00
return true ;
if ( button = = GUI : : MouseButton : : Primary ) {
2022-11-02 17:35:53 +00:00
if ( auto result = paint_root ( ) - > hit_test ( position , Painting : : HitTestType : : TextCursor ) ; result . has_value ( ) ) {
2022-10-19 12:56:39 +01:00
auto hit_paintable = result - > paintable ;
if ( ! hit_paintable - > dom_node ( ) )
2022-06-14 21:57:29 +02:00
return true ;
2022-10-19 12:56:39 +01:00
auto const & hit_layout_node = hit_paintable - > layout_node ( ) ;
if ( ! hit_layout_node . is_text_node ( ) )
2022-06-14 21:57:29 +02:00
return true ;
2023-05-23 07:42:01 +02:00
auto & hit_dom_node = verify_cast < DOM : : Text > ( * hit_paintable - > dom_node ( ) ) ;
2022-10-19 12:56:39 +01:00
auto const & text_for_rendering = verify_cast < Layout : : TextNode > ( hit_layout_node ) . text_for_rendering ( ) ;
2022-06-14 21:57:29 +02:00
int first_word_break_before = [ & ] {
// Start from one before the index position to prevent selecting only spaces between words, caused by the addition below.
// This also helps us dealing with cases where index is equal to the string length.
for ( int i = result - > index_in_node - 1 ; i > = 0 ; - - i ) {
if ( is_ascii_space ( text_for_rendering [ i ] ) ) {
// Don't include the space in the selection
return i + 1 ;
}
}
return 0 ;
} ( ) ;
int first_word_break_after = [ & ] {
for ( size_t i = result - > index_in_node ; i < text_for_rendering . length ( ) ; + + i ) {
if ( is_ascii_space ( text_for_rendering [ i ] ) )
return i ;
}
return text_for_rendering . length ( ) ;
} ( ) ;
2023-05-23 07:42:01 +02:00
m_browsing_context - > set_cursor_position ( DOM : : Position ( hit_dom_node , first_word_break_after ) ) ;
2023-01-11 19:48:53 +01:00
if ( auto selection = node - > document ( ) . get_selection ( ) ) {
2023-05-23 07:42:01 +02:00
( void ) selection - > set_base_and_extent ( hit_dom_node , first_word_break_before , hit_dom_node , first_word_break_after ) ;
2023-01-11 19:48:53 +01:00
}
2022-06-14 21:57:29 +02:00
}
}
2022-06-14 19:38:00 +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
{
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > active_document ( ) )
2020-08-14 19:40:37 +02:00
return false ;
2023-02-26 16:09:02 -07:00
auto * element = m_browsing_context - > active_document ( ) - > focused_element ( ) ;
2020-08-14 19:40:37 +02:00
if ( ! element ) {
2023-02-26 16:09:02 -07:00
element = m_browsing_context - > active_document ( ) - > first_child_of_type < DOM : : Element > ( ) ;
2020-08-14 19:40:37 +02:00
if ( element & & element - > is_focusable ( ) ) {
2023-02-26 16:09:02 -07:00
m_browsing_context - > active_document ( ) - > set_focused_element ( element ) ;
2020-08-14 19:40:37 +02:00
return true ;
}
}
for ( element = element - > next_element_in_pre_order ( ) ; element & & ! element - > is_focusable ( ) ; element = element - > next_element_in_pre_order ( ) )
;
2023-02-26 16:09:02 -07:00
m_browsing_context - > active_document ( ) - > set_focused_element ( element ) ;
2020-08-14 19:40:37 +02:00
return element ;
}
bool EventHandler : : focus_previous_element ( )
{
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > active_document ( ) )
2022-02-08 21:23:43 +01:00
return false ;
2023-02-26 16:09:02 -07:00
auto * element = m_browsing_context - > active_document ( ) - > focused_element ( ) ;
2022-02-08 21:23:43 +01:00
if ( ! element ) {
2023-02-26 16:09:02 -07:00
element = m_browsing_context - > active_document ( ) - > last_child_of_type < DOM : : Element > ( ) ;
2022-02-08 21:23:43 +01:00
if ( element & & element - > is_focusable ( ) ) {
2023-02-26 16:09:02 -07:00
m_browsing_context - > active_document ( ) - > set_focused_element ( element ) ;
2022-02-08 21:23:43 +01:00
return true ;
}
}
for ( element = element - > previous_element_in_pre_order ( ) ; element & & ! element - > is_focusable ( ) ; element = element - > previous_element_in_pre_order ( ) )
;
2023-02-26 16:09:02 -07:00
m_browsing_context - > active_document ( ) - > set_focused_element ( element ) ;
2022-02-08 21:23:43 +01:00
return element ;
2020-08-14 19:40:37 +02:00
}
2021-06-01 10:01:11 +02:00
constexpr bool should_ignore_keydown_event ( u32 code_point )
2021-05-18 13:13:58 +02:00
{
2021-06-01 10:01:11 +02:00
// FIXME: There are probably also keys with non-zero code points that should be filtered out.
2022-11-21 15:40:45 +00:00
// FIXME: We should take the modifier keys into consideration somehow. This treats "Ctrl+C" as just "c".
2022-02-19 22:22:45 +01:00
return code_point = = 0 | | code_point = = 27 ;
2021-05-18 13:13:58 +02:00
}
2023-04-09 11:37:42 +02:00
bool EventHandler : : fire_keyboard_event ( FlyString const & event_name , HTML : : BrowsingContext & browsing_context , KeyCode key , unsigned int modifiers , u32 code_point )
2022-11-05 15:36:03 +00:00
{
JS : : NonnullGCPtr < DOM : : Document > document = * browsing_context . active_document ( ) ;
if ( ! document )
return false ;
if ( JS : : GCPtr < DOM : : Element > focused_element = document - > focused_element ( ) ) {
2022-12-12 12:20:02 +01:00
if ( is < HTML : : NavigableContainer > ( * focused_element ) ) {
auto & navigable_container = verify_cast < HTML : : NavigableContainer > ( * focused_element ) ;
if ( navigable_container . nested_browsing_context ( ) )
return fire_keyboard_event ( event_name , * navigable_container . nested_browsing_context ( ) , key , modifiers , code_point ) ;
2022-11-05 15:36:03 +00:00
}
2023-04-09 11:37:42 +02:00
auto event = UIEvents : : KeyboardEvent : : create_from_platform_event ( document - > realm ( ) , event_name , key , modifiers , code_point ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-02-19 10:25:32 +01:00
return ! focused_element - > dispatch_event ( event ) ;
2022-11-05 15:36:03 +00:00
}
2022-11-09 10:56:12 -05:00
// FIXME: De-duplicate this. This is just to prevent wasting a KeyboardEvent allocation when recursing into an (i)frame.
2023-04-09 11:37:42 +02:00
auto event = UIEvents : : KeyboardEvent : : create_from_platform_event ( document - > realm ( ) , event_name , key , modifiers , code_point ) . release_value_but_fixme_should_propagate_errors ( ) ;
2022-11-05 15:36:03 +00:00
if ( JS : : GCPtr < HTML : : HTMLElement > body = document - > body ( ) )
2023-02-19 10:25:32 +01:00
return ! body - > dispatch_event ( event ) ;
2022-11-05 15:36:03 +00:00
2023-02-19 10:25:32 +01:00
return ! document - > root ( ) . dispatch_event ( event ) ;
2022-11-05 15:36:03 +00:00
}
2020-08-14 19:40:37 +02:00
bool EventHandler : : handle_keydown ( KeyCode key , unsigned modifiers , u32 code_point )
{
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > active_document ( ) )
2021-10-12 14:44:00 +02:00
return false ;
2023-02-26 16:09:02 -07:00
JS : : NonnullGCPtr < DOM : : Document > document = * m_browsing_context - > active_document ( ) ;
2021-10-12 14:44:00 +02:00
if ( ! document - > layout_node ( ) )
return false ;
2020-08-14 19:40:37 +02:00
if ( key = = KeyCode : : Key_Tab ) {
if ( modifiers & KeyModifier : : Mod_Shift )
return focus_previous_element ( ) ;
2021-10-12 14:44:52 +02:00
return focus_next_element ( ) ;
2020-08-14 19:40:37 +02:00
}
2023-01-11 19:48:53 +01:00
if ( auto selection = document - > get_selection ( ) ) {
auto range = selection - > range ( ) ;
if ( range & & range - > start_container ( ) - > is_editable ( ) ) {
selection - > remove_all_ranges ( ) ;
2020-12-01 23:36:12 +01:00
2020-12-14 10:58:10 +01:00
// FIXME: This doesn't work for some reason?
2023-02-26 16:09:02 -07:00
m_browsing_context - > 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 ) {
2022-08-09 01:06:47 +02:00
m_edit_event_handler - > handle_delete ( * range ) ;
2020-12-01 23:36:12 +01:00
return true ;
2021-10-12 14:44:52 +02:00
}
if ( ! should_ignore_keydown_event ( code_point ) ) {
2022-08-09 01:06:47 +02:00
m_edit_event_handler - > handle_delete ( * range ) ;
2023-02-26 16:09:02 -07:00
m_edit_event_handler - > handle_insert ( m_browsing_context - > cursor_position ( ) , code_point ) ;
m_browsing_context - > increment_cursor_position_offset ( ) ;
2020-12-14 10:58:10 +01:00
return true ;
}
2020-12-01 23:36:12 +01:00
}
}
2023-07-02 21:40:46 -07:00
if ( auto * element = m_browsing_context - > active_document ( ) - > focused_element ( ) ; is < HTML : : HTMLMediaElement > ( element ) ) {
auto & media_element = static_cast < HTML : : HTMLMediaElement & > ( * element ) ;
media_element . handle_keydown ( { } , key ) . release_value_but_fixme_should_propagate_errors ( ) ;
}
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > cursor_position ( ) . is_valid ( ) & & m_browsing_context - > cursor_position ( ) . node ( ) - > is_editable ( ) ) {
2020-12-01 23:35:47 +01:00
if ( key = = KeyCode : : Key_Backspace ) {
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > decrement_cursor_position_offset ( ) ) {
2021-05-18 13:08:25 +02:00
// FIXME: Move to the previous node and delete the last character there.
return true ;
}
2020-12-02 15:00:55 +01:00
2023-02-26 16:09:02 -07:00
m_edit_event_handler - > handle_delete_character_after ( m_browsing_context - > cursor_position ( ) ) ;
2020-12-02 15:00:55 +01:00
return true ;
2021-10-12 14:44:52 +02:00
}
if ( key = = KeyCode : : Key_Delete ) {
2023-02-26 16:09:02 -07:00
if ( m_browsing_context - > cursor_position ( ) . offset_is_at_end_of_node ( ) ) {
2021-05-18 13:08:25 +02:00
// FIXME: Move to the next node and delete the first character there.
return true ;
}
2023-02-26 16:09:02 -07:00
m_edit_event_handler - > handle_delete_character_after ( m_browsing_context - > cursor_position ( ) ) ;
2020-12-02 15:00:55 +01:00
return true ;
2021-10-12 14:44:52 +02:00
}
if ( key = = KeyCode : : Key_Right ) {
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > increment_cursor_position_offset ( ) ) {
2021-05-18 13:08:25 +02:00
// FIXME: Move to the next node.
}
2020-12-02 15:00:55 +01:00
return true ;
2021-10-12 14:44:52 +02:00
}
if ( key = = KeyCode : : Key_Left ) {
2023-02-26 16:09:02 -07:00
if ( ! m_browsing_context - > decrement_cursor_position_offset ( ) ) {
2021-05-18 13:08:25 +02:00
// FIXME: Move to the previous node.
}
2020-08-02 16:05:59 +02:00
return true ;
2021-10-12 14:44:52 +02:00
}
2022-02-19 18:39:19 +01:00
if ( key = = KeyCode : : Key_Home ) {
2023-02-26 16:09:02 -07:00
auto & node = * static_cast < DOM : : Text * > ( const_cast < DOM : : Node * > ( m_browsing_context - > cursor_position ( ) . node ( ) ) ) ;
m_browsing_context - > set_cursor_position ( DOM : : Position { node , 0 } ) ;
2022-02-19 18:39:19 +01:00
return true ;
}
2022-02-19 18:42:08 +01:00
if ( key = = KeyCode : : Key_End ) {
2023-02-26 16:09:02 -07:00
auto & node = * static_cast < DOM : : Text * > ( const_cast < DOM : : Node * > ( m_browsing_context - > cursor_position ( ) . node ( ) ) ) ;
m_browsing_context - > set_cursor_position ( DOM : : Position { node , ( unsigned ) node . data ( ) . length ( ) } ) ;
2022-02-19 18:42:08 +01:00
return true ;
}
2021-10-12 14:44:52 +02:00
if ( ! should_ignore_keydown_event ( code_point ) ) {
2023-02-26 16:09:02 -07:00
m_edit_event_handler - > handle_insert ( m_browsing_context - > cursor_position ( ) , code_point ) ;
m_browsing_context - > increment_cursor_position_offset ( ) ;
2021-05-18 13:13:58 +02:00
return true ;
2020-08-02 16:05:59 +02:00
}
2021-10-12 14:44:52 +02:00
// NOTE: Because modifier keys should be ignored, we need to return true.
return true ;
2020-08-02 12:10:01 +02:00
}
2020-12-01 23:35:47 +01:00
2023-04-09 11:37:42 +02:00
bool continue_ = fire_keyboard_event ( UIEvents : : EventNames : : keydown , m_browsing_context , key , modifiers , code_point ) ;
2022-11-05 15:37:19 +00:00
if ( ! continue_ )
return false ;
// FIXME: Work out and implement the difference between this and keydown.
2023-04-09 11:37:42 +02:00
return fire_keyboard_event ( UIEvents : : EventNames : : keypress , m_browsing_context , key , modifiers , code_point ) ;
2020-08-02 12:10:01 +02:00
}
2021-09-28 15:39:35 +02:00
bool EventHandler : : handle_keyup ( KeyCode key , unsigned modifiers , u32 code_point )
{
2023-04-09 11:37:42 +02:00
return fire_keyboard_event ( UIEvents : : EventNames : : keyup , m_browsing_context , key , modifiers , code_point ) ;
2021-09-28 15:39:35 +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
{
2022-07-04 00:42:44 +02:00
m_mouse_event_tracking_layout_node = layout_node ;
2020-09-11 18:15:47 +02:00
}
2020-12-03 18:46:56 +01:00
2022-12-31 16:04:48 +02:00
CSSPixelPoint EventHandler : : compute_mouse_event_client_offset ( CSSPixelPoint event_page_position ) const
{
// https://w3c.github.io/csswg-drafts/cssom-view/#dom-mouseevent-clientx
// The clientX attribute must return the x-coordinate of the position where the event occurred relative to the origin of the viewport.
2022-12-31 16:40:36 +02:00
2023-02-26 16:09:02 -07:00
auto scroll_offset = m_browsing_context - > viewport_scroll_offset ( ) ;
2022-11-03 12:49:54 +00:00
return event_page_position . translated ( - scroll_offset ) ;
2022-12-31 16:04:48 +02:00
}
2022-12-31 16:40:36 +02:00
CSSPixelPoint EventHandler : : compute_mouse_event_page_offset ( CSSPixelPoint event_client_offset ) const
{
// https://w3c.github.io/csswg-drafts/cssom-view/#dom-mouseevent-pagex
// 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.
// 2. Let offset be the value of the scrollX attribute of the event’ s associated Window object, if there is one, or zero otherwise.
2023-02-26 16:09:02 -07:00
auto scroll_offset = m_browsing_context - > viewport_scroll_offset ( ) ;
2022-12-31 16:40:36 +02:00
// 3. Return the sum of offset and the value of the event’ s clientX attribute.
2022-11-03 12:49:54 +00:00
return event_client_offset . translated ( scroll_offset ) ;
2022-12-31 16:40:36 +02:00
}
2023-04-27 09:19:20 -04:00
Optional < EventHandler : : Target > EventHandler : : target_for_mouse_position ( CSSPixelPoint position )
{
if ( m_mouse_event_tracking_layout_node ) {
if ( m_mouse_event_tracking_layout_node - > paintable ( ) - > wants_mouse_events ( ) )
return Target { m_mouse_event_tracking_layout_node - > paintable ( ) , { } } ;
m_mouse_event_tracking_layout_node = nullptr ;
}
if ( auto result = paint_root ( ) - > hit_test ( position , Painting : : HitTestType : : Exact ) ; result . has_value ( ) )
return Target { result - > paintable . ptr ( ) , result - > index_in_node } ;
return { } ;
}
2020-06-07 14:40:38 +02:00
}