Skip to content

Commit d94d602

Browse files
AtkinsSJZaggy1024
authored andcommitted
LibWebView+WebContent: Propagate unconsumed input events out of OOPWV
Since 9e2bd9d, the OOPWV has been consuming all mouse and keyboard events, preventing action shortcuts from working. So let's fix that. :^) OOPWV now queues up input events, sending them one at a time to the WebContent process and waiting for the new `did_finish_handling_input_event(bool event_was_accepted) =|` IPC call before sending the next one. If the event was not accepted, OOPWV imitates the usual event bubbling: first passing the event to its superclass, then to its parent widget, and finally propagating to any Action shortcuts. With this, shortcuts like Ctrl+I to open Browser's JS console work again, except when a contenteditable field is selected. That's a whole separate stack of yaks. Co-authored-by: Zaggy1024 <zaggy1024@gmail.com>
1 parent 2654bfe commit d94d602

File tree

8 files changed

+168
-14
lines changed

8 files changed

+168
-14
lines changed

Userland/Libraries/LibWebView/OutOfProcessWebView.cpp

Lines changed: 136 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,37 +158,37 @@ void OutOfProcessWebView::handle_resize()
158158

159159
void OutOfProcessWebView::keydown_event(GUI::KeyEvent& event)
160160
{
161-
client().async_key_down(event.key(), event.modifiers(), event.code_point());
161+
enqueue_input_event(event);
162162
}
163163

164164
void OutOfProcessWebView::keyup_event(GUI::KeyEvent& event)
165165
{
166-
client().async_key_up(event.key(), event.modifiers(), event.code_point());
166+
enqueue_input_event(event);
167167
}
168168

169169
void OutOfProcessWebView::mousedown_event(GUI::MouseEvent& event)
170170
{
171-
client().async_mouse_down(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
171+
enqueue_input_event(event);
172172
}
173173

174174
void OutOfProcessWebView::mouseup_event(GUI::MouseEvent& event)
175175
{
176-
client().async_mouse_up(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
176+
enqueue_input_event(event);
177177
}
178178

179179
void OutOfProcessWebView::mousemove_event(GUI::MouseEvent& event)
180180
{
181-
client().async_mouse_move(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
181+
enqueue_input_event(event);
182182
}
183183

184184
void OutOfProcessWebView::mousewheel_event(GUI::MouseEvent& event)
185185
{
186-
client().async_mouse_wheel(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y());
186+
enqueue_input_event(event);
187187
}
188188

189189
void OutOfProcessWebView::doubleclick_event(GUI::MouseEvent& event)
190190
{
191-
client().async_doubleclick(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
191+
enqueue_input_event(event);
192192
}
193193

194194
void OutOfProcessWebView::theme_change_event(GUI::ThemeChangeEvent& event)
@@ -695,4 +695,133 @@ void OutOfProcessWebView::hide_event(GUI::HideEvent&)
695695
set_system_visibility_state(false);
696696
}
697697

698+
void OutOfProcessWebView::enqueue_input_event(InputEvent const& event)
699+
{
700+
m_pending_input_events.enqueue(event);
701+
process_next_input_event();
702+
}
703+
704+
void OutOfProcessWebView::process_next_input_event()
705+
{
706+
if (m_pending_input_events.is_empty())
707+
return;
708+
709+
if (m_is_awaiting_response_for_input_event)
710+
return;
711+
m_is_awaiting_response_for_input_event = true;
712+
713+
// Send the next event over to the web content to be handled by JS.
714+
// We'll later get a message to say whether JS prevented the default event behavior,
715+
// at which point we either discard or handle that event, then try and process the next one.
716+
auto event = m_pending_input_events.head();
717+
event.visit(
718+
[this](GUI::KeyEvent const& event) {
719+
switch (event.type()) {
720+
case GUI::Event::Type::KeyDown:
721+
client().async_key_down(event.key(), event.modifiers(), event.code_point());
722+
break;
723+
case GUI::Event::Type::KeyUp:
724+
client().async_key_up(event.key(), event.modifiers(), event.code_point());
725+
break;
726+
default:
727+
dbgln("Unrecognized key event type in OOPWV input event queue: {}", event.type());
728+
VERIFY_NOT_REACHED();
729+
}
730+
},
731+
[this](GUI::MouseEvent const& event) {
732+
switch (event.type()) {
733+
case GUI::Event::Type::MouseDown:
734+
client().async_mouse_down(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
735+
break;
736+
case GUI::Event::Type::MouseUp:
737+
client().async_mouse_up(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
738+
break;
739+
case GUI::Event::Type::MouseMove:
740+
client().async_mouse_move(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
741+
break;
742+
case GUI::Event::Type::MouseWheel:
743+
client().async_mouse_wheel(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y());
744+
break;
745+
case GUI::Event::Type::MouseDoubleClick:
746+
client().async_doubleclick(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers());
747+
break;
748+
default:
749+
dbgln("Unrecognized mouse event type in OOPWV input event queue: {}", event.type());
750+
VERIFY_NOT_REACHED();
751+
}
752+
});
753+
}
754+
755+
void OutOfProcessWebView::notify_server_did_finish_handling_input_event(bool event_was_accepted)
756+
{
757+
VERIFY(m_is_awaiting_response_for_input_event);
758+
759+
auto event = m_pending_input_events.dequeue();
760+
m_is_awaiting_response_for_input_event = false;
761+
762+
if (!event_was_accepted) {
763+
// Here we handle events that were not consumed or cancelled by web content.
764+
// That is, we manually implement the steps that would have happened if the original
765+
// OutOfProcessWebView::foo_event() had called event.ignore().
766+
//
767+
// The first step is to give our superclass a chance to handle the event.
768+
//
769+
// Then, if it does not, we dispatch the event to our parent widget, but limited so
770+
// that it will never bubble up to the Window. (Otherwise, it would then dispatch the
771+
// event to us since we are the focused widget, and it would go round indefinitely.)
772+
//
773+
// Finally, any unhandled KeyDown events are propagated to trigger any Actions.
774+
event.visit(
775+
[this](GUI::KeyEvent& event) {
776+
switch (event.type()) {
777+
case GUI::Event::Type::KeyDown:
778+
Super::keydown_event(event);
779+
break;
780+
case GUI::Event::Type::KeyUp:
781+
Super::keyup_event(event);
782+
break;
783+
default:
784+
dbgln("Unhandled key event type in OOPWV input event queue: {}", event.type());
785+
VERIFY_NOT_REACHED();
786+
}
787+
788+
if (!event.is_accepted()) {
789+
parent_widget()->dispatch_event(event, window());
790+
791+
// NOTE: If other events can ever trigger shortcuts, propagate those here.
792+
if (!event.is_accepted() && event.type() == GUI::Event::Type::KeyDown)
793+
window()->propagate_shortcuts_up_to_application(event, this);
794+
}
795+
},
796+
[this](GUI::MouseEvent& event) {
797+
switch (event.type()) {
798+
case GUI::Event::Type::MouseDown:
799+
Super::mousedown_event(event);
800+
break;
801+
case GUI::Event::Type::MouseUp:
802+
Super::mouseup_event(event);
803+
break;
804+
case GUI::Event::Type::MouseMove:
805+
Super::mousemove_event(event);
806+
break;
807+
case GUI::Event::Type::MouseWheel:
808+
Super::mousewheel_event(event);
809+
break;
810+
case GUI::Event::Type::MouseDoubleClick:
811+
Super::doubleclick_event(event);
812+
break;
813+
default:
814+
dbgln("Unhandled mouse event type in OOPWV input event queue: {}", event.type());
815+
VERIFY_NOT_REACHED();
816+
}
817+
818+
if (!event.is_accepted())
819+
parent_widget()->dispatch_event(event, window());
820+
// FIXME: Propagate event for mouse-button shortcuts once that is implemented.
821+
});
822+
}
823+
824+
process_next_input_event();
825+
}
826+
698827
}

Userland/Libraries/LibWebView/OutOfProcessWebView.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
/*
22
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
3+
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
34
*
45
* SPDX-License-Identifier: BSD-2-Clause
56
*/
67

78
#pragma once
89

10+
#include <AK/Queue.h>
911
#include <AK/URL.h>
1012
#include <LibGUI/AbstractScrollableWidget.h>
1113
#include <LibGUI/Widget.h>
@@ -182,6 +184,7 @@ class OutOfProcessWebView final
182184
virtual Gfx::IntRect notify_server_did_request_minimize_window() override;
183185
virtual Gfx::IntRect notify_server_did_request_fullscreen_window() override;
184186
virtual void notify_server_did_request_file(Badge<WebContentClient>, String const& path, i32) override;
187+
virtual void notify_server_did_finish_handling_input_event(bool event_was_accepted) override;
185188

186189
void request_repaint();
187190
void handle_resize();
@@ -191,6 +194,10 @@ class OutOfProcessWebView final
191194

192195
void handle_web_content_process_crash();
193196

197+
using InputEvent = Variant<GUI::KeyEvent, GUI::MouseEvent>;
198+
void enqueue_input_event(InputEvent const&);
199+
void process_next_input_event();
200+
194201
AK::URL m_url;
195202

196203
struct SharedBitmap {
@@ -210,6 +217,9 @@ class OutOfProcessWebView final
210217

211218
RefPtr<Gfx::Bitmap> m_backup_bitmap;
212219
RefPtr<GUI::Dialog> m_dialog;
220+
221+
bool m_is_awaiting_response_for_input_event { false };
222+
Queue<InputEvent> m_pending_input_events;
213223
};
214224

215225
}

Userland/Libraries/LibWebView/ViewImplementation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class ViewImplementation {
6666
virtual Gfx::IntRect notify_server_did_request_minimize_window() = 0;
6767
virtual Gfx::IntRect notify_server_did_request_fullscreen_window() = 0;
6868
virtual void notify_server_did_request_file(Badge<WebContentClient>, String const& path, i32) = 0;
69+
virtual void notify_server_did_finish_handling_input_event(bool event_was_accepted) = 0;
6970
};
7071

7172
}

Userland/Libraries/LibWebView/WebContentClient.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,9 @@ void WebContentClient::did_request_file(String const& path, i32 request_id)
280280
m_view.notify_server_did_request_file({}, path, request_id);
281281
}
282282

283+
void WebContentClient::did_finish_handling_input_event(bool event_was_accepted)
284+
{
285+
m_view.notify_server_did_finish_handling_input_event(event_was_accepted);
286+
}
287+
283288
}

Userland/Libraries/LibWebView/WebContentClient.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class WebContentClient final
7676
virtual Messages::WebContentClient::DidRequestMinimizeWindowResponse did_request_minimize_window() override;
7777
virtual Messages::WebContentClient::DidRequestFullscreenWindowResponse did_request_fullscreen_window() override;
7878
virtual void did_request_file(String const& path, i32) override;
79+
virtual void did_finish_handling_input_event(bool event_was_accepted) override;
7980

8081
ViewImplementation& m_view;
8182
};

Userland/Services/WebContent/ConnectionFromClient.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,37 +157,42 @@ void ConnectionFromClient::flush_pending_paint_requests()
157157

158158
void ConnectionFromClient::mouse_down(Gfx::IntPoint const& position, unsigned int button, unsigned int buttons, unsigned int modifiers)
159159
{
160-
page().handle_mousedown(position, button, buttons, modifiers);
160+
report_finished_handling_input_event(page().handle_mousedown(position, button, buttons, modifiers));
161161
}
162162

163163
void ConnectionFromClient::mouse_move(Gfx::IntPoint const& position, [[maybe_unused]] unsigned int button, unsigned int buttons, unsigned int modifiers)
164164
{
165-
page().handle_mousemove(position, buttons, modifiers);
165+
report_finished_handling_input_event(page().handle_mousemove(position, buttons, modifiers));
166166
}
167167

168168
void ConnectionFromClient::mouse_up(Gfx::IntPoint const& position, unsigned int button, unsigned int buttons, unsigned int modifiers)
169169
{
170-
page().handle_mouseup(position, button, buttons, modifiers);
170+
report_finished_handling_input_event(page().handle_mouseup(position, button, buttons, modifiers));
171171
}
172172

173173
void ConnectionFromClient::mouse_wheel(Gfx::IntPoint const& position, unsigned int button, unsigned int buttons, unsigned int modifiers, i32 wheel_delta_x, i32 wheel_delta_y)
174174
{
175-
page().handle_mousewheel(position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y);
175+
report_finished_handling_input_event(page().handle_mousewheel(position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y));
176176
}
177177

178178
void ConnectionFromClient::doubleclick(Gfx::IntPoint const& position, unsigned int button, unsigned int buttons, unsigned int modifiers)
179179
{
180-
page().handle_doubleclick(position, button, buttons, modifiers);
180+
report_finished_handling_input_event(page().handle_doubleclick(position, button, buttons, modifiers));
181181
}
182182

183183
void ConnectionFromClient::key_down(i32 key, unsigned int modifiers, u32 code_point)
184184
{
185-
page().handle_keydown((KeyCode)key, modifiers, code_point);
185+
report_finished_handling_input_event(page().handle_keydown((KeyCode)key, modifiers, code_point));
186186
}
187187

188188
void ConnectionFromClient::key_up(i32 key, unsigned int modifiers, u32 code_point)
189189
{
190-
page().handle_keyup((KeyCode)key, modifiers, code_point);
190+
report_finished_handling_input_event(page().handle_keyup((KeyCode)key, modifiers, code_point));
191+
}
192+
193+
void ConnectionFromClient::report_finished_handling_input_event(bool event_was_handled)
194+
{
195+
async_did_finish_handling_input_event(event_was_handled);
191196
}
192197

193198
void ConnectionFromClient::debug_request(String const& request, String const& argument)

Userland/Services/WebContent/ConnectionFromClient.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class ConnectionFromClient final
9898

9999
void flush_pending_paint_requests();
100100

101+
void report_finished_handling_input_event(bool event_was_handled);
102+
101103
NonnullOwnPtr<PageHost> m_page_host;
102104
struct PaintRequest {
103105
Gfx::IntRect content_rect;

Userland/Services/WebContent/WebContentClient.ipc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ endpoint WebContentClient
5252
did_request_minimize_window() => (Gfx::IntRect window_rect)
5353
did_request_fullscreen_window() => (Gfx::IntRect window_rect)
5454
did_request_file(String path, i32 request_id) =|
55+
did_finish_handling_input_event(bool event_was_accepted) =|
5556

5657
did_output_js_console_message(i32 message_index) =|
5758
did_get_js_console_messages(i32 start_index, Vector<String> message_types, Vector<String> messages) =|

0 commit comments

Comments
 (0)