/* * Copyright (c) 2023, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include namespace Ladybird { template static T scale_for_device(T size, float device_pixel_ratio) { return size.template to_type().scaled(device_pixel_ratio).template to_type(); } ErrorOr> WebViewBridge::create(Vector screen_rects, float device_pixel_ratio, Optional webdriver_content_ipc_path) { return adopt_nonnull_own_or_enomem(new (nothrow) WebViewBridge(move(screen_rects), device_pixel_ratio, move(webdriver_content_ipc_path))); } WebViewBridge::WebViewBridge(Vector screen_rects, float device_pixel_ratio, Optional webdriver_content_ipc_path) : m_screen_rects(move(screen_rects)) , m_webdriver_content_ipc_path(move(webdriver_content_ipc_path)) { m_device_pixel_ratio = device_pixel_ratio; m_inverse_device_pixel_ratio = 1.0 / device_pixel_ratio; create_client(WebView::EnableCallgrindProfiling::No); on_scroll_by_delta = [this](auto x_delta, auto y_delta) { // FIXME: This currently isn't reached because we do not yet propagate mouse wheel events to WebContent. // When that is implemented, make sure our mutations to the viewport position here are correct. auto position = m_viewport_rect.location(); position.set_x(position.x() + x_delta); position.set_y(position.y() + y_delta); if (on_scroll_to_point) on_scroll_to_point(position); }; on_scroll_into_view = [this](auto rect) { if (m_viewport_rect.contains(rect)) return; auto position = m_viewport_rect.location(); if (rect.top() < m_viewport_rect.top()) { position.set_y(rect.top()); } else if (rect.top() > m_viewport_rect.top() && rect.bottom() > m_viewport_rect.bottom()) { position.set_y(rect.bottom() - m_viewport_rect.height()); } else { return; } if (on_scroll_to_point) on_scroll_to_point(position); }; } WebViewBridge::~WebViewBridge() = default; void WebViewBridge::set_system_visibility_state(bool is_visible) { client().async_set_system_visibility_state(is_visible); } void WebViewBridge::set_viewport_rect(Gfx::IntRect viewport_rect, ForResize for_resize) { viewport_rect.set_size(scale_for_device(viewport_rect.size(), m_device_pixel_ratio)); m_viewport_rect = viewport_rect; client().async_set_viewport_rect(m_viewport_rect); request_repaint(); if (for_resize == ForResize::Yes) { handle_resize(); } } void WebViewBridge::mouse_down_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers) { client().async_mouse_down(to_content_position(position), to_underlying(button), to_underlying(button), modifiers); } void WebViewBridge::mouse_up_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers) { client().async_mouse_up(to_content_position(position), to_underlying(button), to_underlying(button), modifiers); } void WebViewBridge::mouse_move_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers) { client().async_mouse_move(to_content_position(position), 0, to_underlying(button), modifiers); } void WebViewBridge::mouse_double_click_event(Gfx::IntPoint position, GUI::MouseButton button, KeyModifier modifiers) { client().async_doubleclick(to_content_position(position), button, to_underlying(button), modifiers); } void WebViewBridge::key_down_event(KeyCode key_code, KeyModifier modifiers, u32 code_point) { client().async_key_down(key_code, modifiers, code_point); } void WebViewBridge::key_up_event(KeyCode key_code, KeyModifier modifiers, u32 code_point) { client().async_key_up(key_code, modifiers, code_point); } Optional WebViewBridge::paintable() { Gfx::Bitmap* bitmap = nullptr; Gfx::IntSize bitmap_size; if (m_client_state.has_usable_bitmap) { bitmap = m_client_state.front_bitmap.bitmap.ptr(); bitmap_size = m_client_state.front_bitmap.last_painted_size; } else { bitmap = m_backup_bitmap.ptr(); bitmap_size = m_backup_bitmap_size; } if (!bitmap) return {}; return Paintable { *bitmap, bitmap_size }; } void WebViewBridge::notify_server_did_enter_tooltip_area(Badge, Gfx::IntPoint, DeprecatedString const& tooltip) { if (on_tooltip_entered) on_tooltip_entered(tooltip); } void WebViewBridge::notify_server_did_leave_tooltip_area(Badge) { if (on_tooltip_left) on_tooltip_left(); } void WebViewBridge::notify_server_did_finish_handling_input_event(bool) { } void WebViewBridge::update_zoom() { } Gfx::IntRect WebViewBridge::viewport_rect() const { return m_viewport_rect; } Gfx::IntPoint WebViewBridge::to_content_position(Gfx::IntPoint widget_position) const { return scale_for_device(widget_position, m_device_pixel_ratio); } Gfx::IntPoint WebViewBridge::to_widget_position(Gfx::IntPoint content_position) const { return scale_for_device(content_position, m_inverse_device_pixel_ratio); } void WebViewBridge::create_client(WebView::EnableCallgrindProfiling enable_callgrind_profiling) { m_client_state = {}; auto candidate_web_content_paths = MUST(get_paths_for_helper_process("WebContent"sv)); auto new_client = MUST(launch_web_content_process(*this, candidate_web_content_paths, enable_callgrind_profiling, WebView::IsLayoutTestMode::No, Ladybird::UseLagomNetworking::Yes)); m_client_state.client = new_client; m_client_state.client->on_web_content_process_crash = [this] { Core::deferred_invoke([this] { handle_web_content_process_crash(); }); }; m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid()); client().async_set_window_handle(m_client_state.client_handle); client().async_set_device_pixels_per_css_pixel(m_device_pixel_ratio); client().async_update_system_fonts(Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query(), Gfx::FontDatabase::window_title_font_query()); update_palette(); if (!m_screen_rects.is_empty()) { // FIXME: Update the screens again if they ever change. client().async_update_screen_rects(m_screen_rects, 0); } if (m_webdriver_content_ipc_path.has_value()) { client().async_connect_to_webdriver(*m_webdriver_content_ipc_path); } } void WebViewBridge::update_palette() { auto theme = MUST(Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/Default.ini", s_serenity_resource_root))); auto palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme); auto palette = Gfx::Palette(move(palette_impl)); client().async_update_system_theme(move(theme)); } }