Serenity Operating System
at master 778 lines 27 kB view raw
1/* 2 * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "OutOfProcessWebView.h" 8#include "WebContentClient.h" 9#include <AK/DeprecatedString.h> 10#include <LibFileSystemAccessClient/Client.h> 11#include <LibGUI/Application.h> 12#include <LibGUI/Desktop.h> 13#include <LibGUI/Dialog.h> 14#include <LibGUI/InputBox.h> 15#include <LibGUI/MessageBox.h> 16#include <LibGUI/Painter.h> 17#include <LibGUI/Scrollbar.h> 18#include <LibGUI/Window.h> 19#include <LibGfx/Font/FontDatabase.h> 20#include <LibGfx/Palette.h> 21#include <LibGfx/SystemTheme.h> 22 23REGISTER_WIDGET(WebView, OutOfProcessWebView) 24 25namespace WebView { 26 27OutOfProcessWebView::OutOfProcessWebView() 28{ 29 set_should_hide_unnecessary_scrollbars(true); 30 set_focus_policy(GUI::FocusPolicy::StrongFocus); 31 32 create_client(); 33} 34 35OutOfProcessWebView::~OutOfProcessWebView() = default; 36 37void OutOfProcessWebView::handle_web_content_process_crash() 38{ 39 create_client(); 40 VERIFY(m_client_state.client); 41 42 // Don't keep a stale backup bitmap around. 43 m_backup_bitmap = nullptr; 44 45 handle_resize(); 46 StringBuilder builder; 47 builder.append("<html><head><title>Crashed: "sv); 48 builder.append(escape_html_entities(m_url.to_deprecated_string())); 49 builder.append("</title></head><body>"sv); 50 builder.append("<h1>Web page crashed"sv); 51 if (!m_url.host().is_empty()) { 52 builder.appendff(" on {}", escape_html_entities(m_url.host())); 53 } 54 builder.append("</h1>"sv); 55 auto escaped_url = escape_html_entities(m_url.to_deprecated_string()); 56 builder.appendff("The web page <a href=\"{}\">{}</a> has crashed.<br><br>You can reload the page to try again.", escaped_url, escaped_url); 57 builder.append("</body></html>"sv); 58 load_html(builder.to_deprecated_string(), m_url); 59} 60 61void OutOfProcessWebView::create_client() 62{ 63 m_client_state = {}; 64 65 m_client_state.client = WebContentClient::try_create(*this).release_value_but_fixme_should_propagate_errors(); 66 m_client_state.client->on_web_content_process_crash = [this] { 67 deferred_invoke([this] { 68 handle_web_content_process_crash(); 69 }); 70 }; 71 72 client().async_update_system_theme(Gfx::current_system_theme_buffer()); 73 client().async_update_system_fonts(Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query(), Gfx::FontDatabase::window_title_font_query()); 74 client().async_update_screen_rects(GUI::Desktop::the().rects(), GUI::Desktop::the().main_screen_index()); 75} 76 77void OutOfProcessWebView::paint_event(GUI::PaintEvent& event) 78{ 79 Super::paint_event(event); 80 81 // If the available size is empty, we don't have a front or back bitmap to draw. 82 if (available_size().is_empty()) 83 return; 84 85 GUI::Painter painter(*this); 86 painter.add_clip_rect(event.rect()); 87 88 if (auto* bitmap = m_client_state.has_usable_bitmap ? m_client_state.front_bitmap.bitmap.ptr() : m_backup_bitmap.ptr()) { 89 painter.add_clip_rect(frame_inner_rect()); 90 painter.translate(frame_thickness(), frame_thickness()); 91 if (m_content_scales_to_viewport) 92 painter.draw_scaled_bitmap(rect(), *bitmap, bitmap->rect()); 93 else 94 painter.blit({ 0, 0 }, *bitmap, bitmap->rect()); 95 return; 96 } 97 98 painter.fill_rect(frame_inner_rect(), palette().base()); 99} 100 101void OutOfProcessWebView::resize_event(GUI::ResizeEvent& event) 102{ 103 Super::resize_event(event); 104 handle_resize(); 105} 106 107void OutOfProcessWebView::handle_resize() 108{ 109 client().async_set_viewport_rect(Gfx::IntRect({ horizontal_scrollbar().value(), vertical_scrollbar().value() }, available_size())); 110 111 if (m_client_state.has_usable_bitmap) { 112 // NOTE: We keep the outgoing front bitmap as a backup so we have something to paint until we get a new one. 113 m_backup_bitmap = m_client_state.front_bitmap.bitmap; 114 } 115 116 if (m_client_state.front_bitmap.bitmap) 117 client().async_remove_backing_store(m_client_state.front_bitmap.id); 118 119 if (m_client_state.back_bitmap.bitmap) 120 client().async_remove_backing_store(m_client_state.back_bitmap.id); 121 122 m_client_state.front_bitmap = {}; 123 m_client_state.back_bitmap = {}; 124 m_client_state.has_usable_bitmap = false; 125 126 if (available_size().is_empty()) 127 return; 128 129 if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRx8888, available_size()); !new_bitmap_or_error.is_error()) { 130 m_client_state.front_bitmap.bitmap = new_bitmap_or_error.release_value(); 131 m_client_state.front_bitmap.id = m_client_state.next_bitmap_id++; 132 client().async_add_backing_store(m_client_state.front_bitmap.id, m_client_state.front_bitmap.bitmap->to_shareable_bitmap()); 133 } 134 135 if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRx8888, available_size()); !new_bitmap_or_error.is_error()) { 136 m_client_state.back_bitmap.bitmap = new_bitmap_or_error.release_value(); 137 m_client_state.back_bitmap.id = m_client_state.next_bitmap_id++; 138 client().async_add_backing_store(m_client_state.back_bitmap.id, m_client_state.back_bitmap.bitmap->to_shareable_bitmap()); 139 } 140 141 request_repaint(); 142} 143 144void OutOfProcessWebView::update_zoom() 145{ 146 client().async_set_device_pixels_per_css_pixel(m_device_pixel_ratio * m_zoom_level); 147 // FIXME: Refactor this into separate update_viewport_rect() + request_repaint() like in Ladybird 148 handle_resize(); 149} 150 151void OutOfProcessWebView::keydown_event(GUI::KeyEvent& event) 152{ 153 enqueue_input_event(event); 154} 155 156void OutOfProcessWebView::keyup_event(GUI::KeyEvent& event) 157{ 158 enqueue_input_event(event); 159} 160 161void OutOfProcessWebView::mousedown_event(GUI::MouseEvent& event) 162{ 163 enqueue_input_event(event); 164} 165 166void OutOfProcessWebView::mouseup_event(GUI::MouseEvent& event) 167{ 168 enqueue_input_event(event); 169 170 if (event.button() == GUI::MouseButton::Backward && on_back_button) { 171 on_back_button(); 172 } else if (event.button() == GUI::MouseButton::Forward && on_forward_button) { 173 on_forward_button(); 174 } 175} 176 177void OutOfProcessWebView::mousemove_event(GUI::MouseEvent& event) 178{ 179 enqueue_input_event(event); 180} 181 182void OutOfProcessWebView::mousewheel_event(GUI::MouseEvent& event) 183{ 184 enqueue_input_event(event); 185} 186 187void OutOfProcessWebView::doubleclick_event(GUI::MouseEvent& event) 188{ 189 enqueue_input_event(event); 190} 191 192void OutOfProcessWebView::theme_change_event(GUI::ThemeChangeEvent& event) 193{ 194 Super::theme_change_event(event); 195 client().async_update_system_theme(Gfx::current_system_theme_buffer()); 196 request_repaint(); 197} 198 199void OutOfProcessWebView::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event) 200{ 201 client().async_update_screen_rects(event.rects(), event.main_screen_index()); 202} 203 204void OutOfProcessWebView::notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id) 205{ 206 if (m_client_state.back_bitmap.id == bitmap_id) { 207 m_client_state.has_usable_bitmap = true; 208 m_client_state.back_bitmap.pending_paints--; 209 swap(m_client_state.back_bitmap, m_client_state.front_bitmap); 210 // We don't need the backup bitmap anymore, so drop it. 211 m_backup_bitmap = nullptr; 212 update(); 213 214 if (m_client_state.got_repaint_requests_while_painting) { 215 m_client_state.got_repaint_requests_while_painting = false; 216 request_repaint(); 217 } 218 } 219} 220 221void OutOfProcessWebView::notify_server_did_invalidate_content_rect(Badge<WebContentClient>, [[maybe_unused]] Gfx::IntRect const& content_rect) 222{ 223 request_repaint(); 224} 225 226void OutOfProcessWebView::notify_server_did_change_selection(Badge<WebContentClient>) 227{ 228 request_repaint(); 229} 230 231void OutOfProcessWebView::notify_server_did_request_cursor_change(Badge<WebContentClient>, Gfx::StandardCursor cursor) 232{ 233 set_override_cursor(cursor); 234} 235 236void OutOfProcessWebView::notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) 237{ 238 set_content_size(content_size); 239} 240 241void OutOfProcessWebView::notify_server_did_change_title(Badge<WebContentClient>, DeprecatedString const& title) 242{ 243 if (on_title_change) 244 on_title_change(title); 245} 246 247void OutOfProcessWebView::notify_server_did_request_scroll(Badge<WebContentClient>, i32 x_delta, i32 y_delta) 248{ 249 horizontal_scrollbar().increase_slider_by(x_delta); 250 vertical_scrollbar().increase_slider_by(y_delta); 251} 252 253void OutOfProcessWebView::notify_server_did_request_scroll_to(Badge<WebContentClient>, Gfx::IntPoint scroll_position) 254{ 255 horizontal_scrollbar().set_value(scroll_position.x()); 256 vertical_scrollbar().set_value(scroll_position.y()); 257} 258 259void OutOfProcessWebView::notify_server_did_request_scroll_into_view(Badge<WebContentClient>, Gfx::IntRect const& rect) 260{ 261 scroll_into_view(rect, true, true); 262} 263 264void OutOfProcessWebView::notify_server_did_enter_tooltip_area(Badge<WebContentClient>, Gfx::IntPoint, DeprecatedString const& title) 265{ 266 GUI::Application::the()->show_tooltip(title, nullptr); 267} 268 269void OutOfProcessWebView::notify_server_did_leave_tooltip_area(Badge<WebContentClient>) 270{ 271 GUI::Application::the()->hide_tooltip(); 272} 273 274void OutOfProcessWebView::notify_server_did_hover_link(Badge<WebContentClient>, const AK::URL& url) 275{ 276 if (on_link_hover) 277 on_link_hover(url); 278} 279 280void OutOfProcessWebView::notify_server_did_unhover_link(Badge<WebContentClient>) 281{ 282 set_override_cursor(Gfx::StandardCursor::None); 283 if (on_link_hover) 284 on_link_hover({}); 285} 286 287void OutOfProcessWebView::notify_server_did_click_link(Badge<WebContentClient>, const AK::URL& url, DeprecatedString const& target, unsigned int modifiers) 288{ 289 if (on_link_click) 290 on_link_click(url, target, modifiers); 291} 292 293void OutOfProcessWebView::notify_server_did_middle_click_link(Badge<WebContentClient>, const AK::URL& url, DeprecatedString const& target, unsigned int modifiers) 294{ 295 if (on_link_middle_click) 296 on_link_middle_click(url, target, modifiers); 297} 298 299void OutOfProcessWebView::notify_server_did_start_loading(Badge<WebContentClient>, const AK::URL& url, bool is_redirect) 300{ 301 m_url = url; 302 if (on_load_start) 303 on_load_start(url, is_redirect); 304} 305 306void OutOfProcessWebView::notify_server_did_finish_loading(Badge<WebContentClient>, const AK::URL& url) 307{ 308 m_url = url; 309 if (on_load_finish) 310 on_load_finish(url); 311} 312 313void OutOfProcessWebView::notify_server_did_request_navigate_back(Badge<WebContentClient>) 314{ 315 if (on_navigate_back) 316 on_navigate_back(); 317} 318 319void OutOfProcessWebView::notify_server_did_request_navigate_forward(Badge<WebContentClient>) 320{ 321 if (on_navigate_forward) 322 on_navigate_forward(); 323} 324 325void OutOfProcessWebView::notify_server_did_request_refresh(Badge<WebContentClient>) 326{ 327 if (on_refresh) 328 on_refresh(); 329} 330 331void OutOfProcessWebView::notify_server_did_request_context_menu(Badge<WebContentClient>, Gfx::IntPoint content_position) 332{ 333 if (on_context_menu_request) 334 on_context_menu_request(screen_relative_rect().location().translated(to_widget_position(content_position))); 335} 336 337void OutOfProcessWebView::notify_server_did_request_link_context_menu(Badge<WebContentClient>, Gfx::IntPoint content_position, const AK::URL& url, DeprecatedString const&, unsigned) 338{ 339 if (on_link_context_menu_request) 340 on_link_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position))); 341} 342 343void OutOfProcessWebView::notify_server_did_request_image_context_menu(Badge<WebContentClient>, Gfx::IntPoint content_position, const AK::URL& url, DeprecatedString const&, unsigned, Gfx::ShareableBitmap const& bitmap) 344{ 345 if (on_image_context_menu_request) 346 on_image_context_menu_request(url, screen_relative_rect().location().translated(to_widget_position(content_position)), bitmap); 347} 348 349void OutOfProcessWebView::notify_server_did_request_alert(Badge<WebContentClient>, DeprecatedString const& message) 350{ 351 m_dialog = GUI::MessageBox::construct(window(), message, "Alert"sv, GUI::MessageBox::Type::Information, GUI::MessageBox::InputType::OK); 352 m_dialog->set_icon(window()->icon()); 353 m_dialog->exec(); 354 355 client().async_alert_closed(); 356 m_dialog = nullptr; 357} 358 359void OutOfProcessWebView::notify_server_did_request_confirm(Badge<WebContentClient>, DeprecatedString const& message) 360{ 361 m_dialog = GUI::MessageBox::construct(window(), message, "Confirm"sv, GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::OKCancel); 362 m_dialog->set_icon(window()->icon()); 363 364 client().async_confirm_closed(m_dialog->exec() == GUI::Dialog::ExecResult::OK); 365 m_dialog = nullptr; 366} 367 368void OutOfProcessWebView::notify_server_did_request_prompt(Badge<WebContentClient>, DeprecatedString const& message, DeprecatedString const& default_) 369{ 370 m_dialog = GUI::InputBox::construct(window(), default_, message, "Prompt"sv, GUI::InputType::Text, StringView {}); 371 m_dialog->set_icon(window()->icon()); 372 373 if (m_dialog->exec() == GUI::InputBox::ExecResult::OK) 374 client().async_prompt_closed(static_cast<GUI::InputBox&>(*m_dialog).text_value()); 375 else 376 client().async_prompt_closed({}); 377 378 m_dialog = nullptr; 379} 380 381void OutOfProcessWebView::notify_server_did_request_set_prompt_text(Badge<WebContentClient>, DeprecatedString const& message) 382{ 383 if (m_dialog && is<GUI::InputBox>(*m_dialog)) 384 static_cast<GUI::InputBox&>(*m_dialog).set_text_value(message); 385} 386 387void OutOfProcessWebView::notify_server_did_request_accept_dialog(Badge<WebContentClient>) 388{ 389 if (m_dialog) 390 m_dialog->done(GUI::Dialog::ExecResult::OK); 391} 392 393void OutOfProcessWebView::notify_server_did_request_dismiss_dialog(Badge<WebContentClient>) 394{ 395 if (m_dialog) 396 m_dialog->done(GUI::Dialog::ExecResult::Cancel); 397} 398 399void OutOfProcessWebView::notify_server_did_get_source(const AK::URL& url, DeprecatedString const& source) 400{ 401 if (on_get_source) 402 on_get_source(url, source); 403} 404 405void OutOfProcessWebView::notify_server_did_get_dom_tree(DeprecatedString const& dom_tree) 406{ 407 if (on_get_dom_tree) 408 on_get_dom_tree(dom_tree); 409} 410 411void OutOfProcessWebView::notify_server_did_get_dom_node_properties(i32 node_id, DeprecatedString const& computed_style, DeprecatedString const& resolved_style, DeprecatedString const& custom_properties, DeprecatedString const& node_box_sizing) 412{ 413 if (on_get_dom_node_properties) 414 on_get_dom_node_properties(node_id, computed_style, resolved_style, custom_properties, node_box_sizing); 415} 416 417void OutOfProcessWebView::notify_server_did_output_js_console_message(i32 message_index) 418{ 419 if (on_js_console_new_message) 420 on_js_console_new_message(message_index); 421} 422 423void OutOfProcessWebView::notify_server_did_get_js_console_messages(i32 start_index, Vector<DeprecatedString> const& message_types, Vector<DeprecatedString> const& messages) 424{ 425 if (on_get_js_console_messages) 426 on_get_js_console_messages(start_index, message_types, messages); 427} 428 429void OutOfProcessWebView::notify_server_did_change_favicon(Gfx::Bitmap const& favicon) 430{ 431 if (on_favicon_change) 432 on_favicon_change(favicon); 433} 434 435Vector<Web::Cookie::Cookie> OutOfProcessWebView::notify_server_did_request_all_cookies(Badge<WebContentClient>, AK::URL const& url) 436{ 437 if (on_get_all_cookies) 438 return on_get_all_cookies(url); 439 return {}; 440} 441 442Optional<Web::Cookie::Cookie> OutOfProcessWebView::notify_server_did_request_named_cookie(Badge<WebContentClient>, AK::URL const& url, DeprecatedString const& name) 443{ 444 if (on_get_named_cookie) 445 return on_get_named_cookie(url, name); 446 return {}; 447} 448 449DeprecatedString OutOfProcessWebView::notify_server_did_request_cookie(Badge<WebContentClient>, const AK::URL& url, Web::Cookie::Source source) 450{ 451 if (on_get_cookie) 452 return on_get_cookie(url, source); 453 return {}; 454} 455 456void OutOfProcessWebView::notify_server_did_set_cookie(Badge<WebContentClient>, const AK::URL& url, Web::Cookie::ParsedCookie const& cookie, Web::Cookie::Source source) 457{ 458 if (on_set_cookie) 459 on_set_cookie(url, cookie, source); 460} 461 462void OutOfProcessWebView::notify_server_did_close_browsing_context(Badge<WebContentClient>) 463{ 464 if (on_close) 465 on_close(); 466} 467 468void OutOfProcessWebView::notify_server_did_update_cookie(Badge<WebContentClient>, Web::Cookie::Cookie const& cookie) 469{ 470 if (on_update_cookie) 471 on_update_cookie(cookie); 472} 473 474void OutOfProcessWebView::notify_server_did_update_resource_count(i32 count_waiting) 475{ 476 if (on_resource_status_change) 477 on_resource_status_change(count_waiting); 478} 479 480void OutOfProcessWebView::notify_server_did_request_restore_window() 481{ 482 if (on_restore_window) 483 on_restore_window(); 484} 485 486Gfx::IntPoint OutOfProcessWebView::notify_server_did_request_reposition_window(Gfx::IntPoint position) 487{ 488 if (on_reposition_window) 489 return on_reposition_window(position); 490 return {}; 491} 492 493Gfx::IntSize OutOfProcessWebView::notify_server_did_request_resize_window(Gfx::IntSize size) 494{ 495 if (on_resize_window) 496 return on_resize_window(size); 497 return {}; 498} 499 500Gfx::IntRect OutOfProcessWebView::notify_server_did_request_maximize_window() 501{ 502 if (on_maximize_window) 503 return on_maximize_window(); 504 return {}; 505} 506 507Gfx::IntRect OutOfProcessWebView::notify_server_did_request_minimize_window() 508{ 509 if (on_minimize_window) 510 return on_minimize_window(); 511 return {}; 512} 513 514Gfx::IntRect OutOfProcessWebView::notify_server_did_request_fullscreen_window() 515{ 516 if (on_fullscreen_window) 517 return on_fullscreen_window(); 518 return {}; 519} 520 521void OutOfProcessWebView::notify_server_did_request_file(Badge<WebContentClient>, DeprecatedString const& path, i32 request_id) 522{ 523 auto file = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), path); 524 if (file.is_error()) 525 client().async_handle_file_return(file.error().code(), {}, request_id); 526 else 527 client().async_handle_file_return(0, IPC::File(file.value().stream()), request_id); 528} 529 530void OutOfProcessWebView::did_scroll() 531{ 532 client().async_set_viewport_rect(visible_content_rect()); 533 request_repaint(); 534} 535 536void OutOfProcessWebView::request_repaint() 537{ 538 // If this widget was instantiated but not yet added to a window, 539 // it won't have a back bitmap yet, so we can just skip repaint requests. 540 if (!m_client_state.back_bitmap.bitmap) 541 return; 542 // Don't request a repaint until pending paint requests have finished. 543 if (m_client_state.back_bitmap.pending_paints) { 544 m_client_state.got_repaint_requests_while_painting = true; 545 return; 546 } 547 m_client_state.back_bitmap.pending_paints++; 548 client().async_paint(m_client_state.back_bitmap.bitmap->rect().translated(horizontal_scrollbar().value(), vertical_scrollbar().value()), m_client_state.back_bitmap.id); 549} 550 551void OutOfProcessWebView::js_console_input(DeprecatedString const& js_source) 552{ 553 client().async_js_console_input(js_source); 554} 555 556void OutOfProcessWebView::js_console_request_messages(i32 start_index) 557{ 558 client().async_js_console_request_messages(start_index); 559} 560 561DeprecatedString OutOfProcessWebView::dump_layout_tree() 562{ 563 return client().dump_layout_tree(); 564} 565 566OrderedHashMap<DeprecatedString, DeprecatedString> OutOfProcessWebView::get_local_storage_entries() 567{ 568 return client().get_local_storage_entries(); 569} 570 571OrderedHashMap<DeprecatedString, DeprecatedString> OutOfProcessWebView::get_session_storage_entries() 572{ 573 return client().get_session_storage_entries(); 574} 575 576void OutOfProcessWebView::set_content_filters(Vector<DeprecatedString> filters) 577{ 578 client().async_set_content_filters(filters); 579} 580 581void OutOfProcessWebView::set_proxy_mappings(Vector<DeprecatedString> proxies, HashMap<DeprecatedString, size_t> mappings) 582{ 583 client().async_set_proxy_mappings(move(proxies), move(mappings)); 584} 585 586void OutOfProcessWebView::connect_to_webdriver(DeprecatedString const& webdriver_ipc_path) 587{ 588 client().async_connect_to_webdriver(webdriver_ipc_path); 589} 590 591void OutOfProcessWebView::set_window_position(Gfx::IntPoint position) 592{ 593 client().async_set_window_position(position); 594} 595 596void OutOfProcessWebView::set_window_size(Gfx::IntSize size) 597{ 598 client().async_set_window_size(size); 599} 600 601Gfx::ShareableBitmap OutOfProcessWebView::take_screenshot() const 602{ 603 if (auto* bitmap = m_client_state.has_usable_bitmap ? m_client_state.front_bitmap.bitmap.ptr() : m_backup_bitmap.ptr()) 604 return bitmap->to_shareable_bitmap(); 605 return {}; 606} 607 608Gfx::ShareableBitmap OutOfProcessWebView::take_document_screenshot() 609{ 610 return client().take_document_screenshot(); 611} 612 613void OutOfProcessWebView::focusin_event(GUI::FocusEvent&) 614{ 615 client().async_set_has_focus(true); 616} 617 618void OutOfProcessWebView::focusout_event(GUI::FocusEvent&) 619{ 620 client().async_set_has_focus(false); 621} 622 623void OutOfProcessWebView::set_system_visibility_state(bool visible) 624{ 625 client().async_set_system_visibility_state(visible); 626} 627 628void OutOfProcessWebView::show_event(GUI::ShowEvent&) 629{ 630 set_system_visibility_state(true); 631} 632 633void OutOfProcessWebView::hide_event(GUI::HideEvent&) 634{ 635 set_system_visibility_state(false); 636} 637 638void OutOfProcessWebView::enqueue_input_event(InputEvent const& event) 639{ 640 m_pending_input_events.enqueue(event); 641 process_next_input_event(); 642} 643 644void OutOfProcessWebView::process_next_input_event() 645{ 646 if (m_pending_input_events.is_empty()) 647 return; 648 649 if (m_is_awaiting_response_for_input_event) 650 return; 651 m_is_awaiting_response_for_input_event = true; 652 653 // Send the next event over to the web content to be handled by JS. 654 // We'll later get a message to say whether JS prevented the default event behavior, 655 // at which point we either discard or handle that event, then try and process the next one. 656 auto event = m_pending_input_events.head(); 657 event.visit( 658 [this](GUI::KeyEvent const& event) { 659 switch (event.type()) { 660 case GUI::Event::Type::KeyDown: 661 client().async_key_down(event.key(), event.modifiers(), event.code_point()); 662 break; 663 case GUI::Event::Type::KeyUp: 664 client().async_key_up(event.key(), event.modifiers(), event.code_point()); 665 break; 666 default: 667 dbgln("Unrecognized key event type in OOPWV input event queue: {}", event.type()); 668 VERIFY_NOT_REACHED(); 669 } 670 }, 671 [this](GUI::MouseEvent const& event) { 672 switch (event.type()) { 673 case GUI::Event::Type::MouseDown: 674 client().async_mouse_down(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()); 675 break; 676 case GUI::Event::Type::MouseUp: 677 client().async_mouse_up(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()); 678 break; 679 case GUI::Event::Type::MouseMove: 680 client().async_mouse_move(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()); 681 break; 682 case GUI::Event::Type::MouseWheel: 683 client().async_mouse_wheel(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y()); 684 break; 685 case GUI::Event::Type::MouseDoubleClick: 686 client().async_doubleclick(to_content_position(event.position()), event.button(), event.buttons(), event.modifiers()); 687 break; 688 default: 689 dbgln("Unrecognized mouse event type in OOPWV input event queue: {}", event.type()); 690 VERIFY_NOT_REACHED(); 691 } 692 }); 693} 694 695void OutOfProcessWebView::notify_server_did_finish_handling_input_event(bool event_was_accepted) 696{ 697 VERIFY(m_is_awaiting_response_for_input_event); 698 699 auto event = m_pending_input_events.dequeue(); 700 m_is_awaiting_response_for_input_event = false; 701 702 if (!event_was_accepted) { 703 // Here we handle events that were not consumed or cancelled by web content. 704 // That is, we manually implement the steps that would have happened if the original 705 // OutOfProcessWebView::foo_event() had called event.ignore(). 706 // 707 // The first step is to give our superclass a chance to handle the event. 708 // 709 // Then, if it does not, we dispatch the event to our parent widget, but limited so 710 // that it will never bubble up to the Window. (Otherwise, it would then dispatch the 711 // event to us since we are the focused widget, and it would go round indefinitely.) 712 // 713 // Finally, any unhandled KeyDown events are propagated to trigger any Actions. 714 event.visit( 715 [this](GUI::KeyEvent& event) { 716 switch (event.type()) { 717 case GUI::Event::Type::KeyDown: 718 Super::keydown_event(event); 719 break; 720 case GUI::Event::Type::KeyUp: 721 Super::keyup_event(event); 722 break; 723 default: 724 dbgln("Unhandled key event type in OOPWV input event queue: {}", event.type()); 725 VERIFY_NOT_REACHED(); 726 } 727 728 if (!event.is_accepted()) { 729 parent_widget()->dispatch_event(event, window()); 730 731 // NOTE: If other events can ever trigger shortcuts, propagate those here. 732 if (!event.is_accepted() && event.type() == GUI::Event::Type::KeyDown) 733 window()->propagate_shortcuts_up_to_application(event, this); 734 } 735 }, 736 [this](GUI::MouseEvent& event) { 737 switch (event.type()) { 738 case GUI::Event::Type::MouseDown: 739 Super::mousedown_event(event); 740 break; 741 case GUI::Event::Type::MouseUp: 742 Super::mouseup_event(event); 743 break; 744 case GUI::Event::Type::MouseMove: 745 Super::mousemove_event(event); 746 break; 747 case GUI::Event::Type::MouseWheel: 748 Super::mousewheel_event(event); 749 break; 750 case GUI::Event::Type::MouseDoubleClick: 751 Super::doubleclick_event(event); 752 break; 753 default: 754 dbgln("Unhandled mouse event type in OOPWV input event queue: {}", event.type()); 755 VERIFY_NOT_REACHED(); 756 } 757 758 if (!event.is_accepted()) 759 parent_widget()->dispatch_event(event, window()); 760 // FIXME: Propagate event for mouse-button shortcuts once that is implemented. 761 }); 762 } 763 764 process_next_input_event(); 765} 766 767void OutOfProcessWebView::notify_server_did_get_accessibility_tree(DeprecatedString const& accessibility_tree) 768{ 769 if (on_get_accessibility_tree) 770 on_get_accessibility_tree(accessibility_tree); 771} 772 773void OutOfProcessWebView::set_content_scales_to_viewport(bool b) 774{ 775 m_content_scales_to_viewport = b; 776} 777 778}