diff --git a/Ladybird/WebContentView.cpp b/Ladybird/WebContentView.cpp index 82e80a452bd..29863317db4 100644 --- a/Ladybird/WebContentView.cpp +++ b/Ladybird/WebContentView.cpp @@ -77,10 +77,6 @@ WebContentView::WebContentView(StringView webdriver_content_ipc_path, WebView::E }); create_client(enable_callgrind_profiling); - - m_backing_store_shrink_timer = Core::Timer::create_single_shot(3000, [this] { - resize_backing_stores_if_needed(WindowResizeInProgress::No); - }).release_value_but_fixme_should_propagate_errors(); } WebContentView::~WebContentView() @@ -446,60 +442,8 @@ void WebContentView::paintEvent(QPaintEvent*) void WebContentView::resizeEvent(QResizeEvent* event) { QAbstractScrollArea::resizeEvent(event); - handle_resize(); - m_backing_store_shrink_timer->restart(); -} - -void WebContentView::handle_resize() -{ update_viewport_rect(); - resize_backing_stores_if_needed(WindowResizeInProgress::Yes); -} - -void WebContentView::resize_backing_stores_if_needed(WindowResizeInProgress window_resize_in_progress) -{ - if (m_client_state.has_usable_bitmap) { - // NOTE: We keep the outgoing front bitmap as a backup so we have something to paint until we get a new one. - m_backup_bitmap = m_client_state.front_bitmap.bitmap; - m_backup_bitmap_size = m_client_state.front_bitmap.last_painted_size; - } - - m_client_state.has_usable_bitmap = false; - - if (m_viewport_rect.is_empty()) - return; - - Gfx::IntSize minimum_needed_size; - - if (window_resize_in_progress == WindowResizeInProgress::Yes) { - // Pad the minimum needed size so that we don't have to keep reallocating backing stores while the window is being resized. - minimum_needed_size = { m_viewport_rect.width() + 256, m_viewport_rect.height() + 256 }; - } else { - // If we're not in the middle of a resize, we can shrink the backing store size to match the viewport size. - minimum_needed_size = m_viewport_rect.size(); - m_client_state.front_bitmap = {}; - m_client_state.back_bitmap = {}; - } - - auto reallocate_backing_store_if_needed = [&](SharedBitmap& backing_store) { - if (!backing_store.bitmap || !backing_store.bitmap->size().contains(minimum_needed_size)) { - if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRx8888, minimum_needed_size); !new_bitmap_or_error.is_error()) { - if (backing_store.bitmap) - client().async_remove_backing_store(backing_store.id); - - backing_store.pending_paints = 0; - backing_store.bitmap = new_bitmap_or_error.release_value(); - backing_store.id = m_client_state.next_bitmap_id++; - client().async_add_backing_store(backing_store.id, backing_store.bitmap->to_shareable_bitmap()); - } - backing_store.last_painted_size = m_viewport_rect.size(); - } - }; - - reallocate_backing_store_if_needed(m_client_state.front_bitmap); - reallocate_backing_store_if_needed(m_client_state.back_bitmap); - - request_repaint(); + handle_resize(); } void WebContentView::set_viewport_rect(Gfx::IntRect rect) @@ -1131,19 +1075,9 @@ void WebContentView::notify_server_did_request_file(Badge, Dep client().async_handle_file_return(0, IPC::File(*file.value()), request_id); } -void WebContentView::request_repaint() +Gfx::IntRect WebContentView::viewport_rect() const { - // If this widget was instantiated but not yet added to a window, - // it won't have a back bitmap yet, so we can just skip repaint requests. - if (!m_client_state.back_bitmap.bitmap) - return; - // Don't request a repaint until pending paint requests have finished. - if (m_client_state.back_bitmap.pending_paints) { - m_client_state.got_repaint_requests_while_painting = true; - return; - } - m_client_state.back_bitmap.pending_paints++; - client().async_paint(m_viewport_rect, m_client_state.back_bitmap.id); + return m_viewport_rect; } bool WebContentView::event(QEvent* event) diff --git a/Ladybird/WebContentView.h b/Ladybird/WebContentView.h index 983134d97bf..961f7354c2a 100644 --- a/Ladybird/WebContentView.h +++ b/Ladybird/WebContentView.h @@ -198,16 +198,9 @@ private: // ^WebView::ViewImplementation virtual void create_client(WebView::EnableCallgrindProfiling = WebView::EnableCallgrindProfiling::No) override; virtual void update_zoom() override; + virtual Gfx::IntRect viewport_rect() const override; - void request_repaint(); void update_viewport_rect(); - void handle_resize(); - - enum class WindowResizeInProgress { - No, - Yes, - }; - void resize_backing_stores_if_needed(WindowResizeInProgress); void ensure_js_console_widget(); void ensure_inspector_widget(); @@ -227,10 +220,5 @@ private: void handle_web_content_process_crash(); - RefPtr m_backup_bitmap; - Gfx::IntSize m_backup_bitmap_size; - StringView m_webdriver_content_ipc_path; - - RefPtr m_backing_store_shrink_timer; }; diff --git a/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp b/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp index d503adf621c..0066e267449 100644 --- a/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp +++ b/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp @@ -105,44 +105,13 @@ void OutOfProcessWebView::paint_event(GUI::PaintEvent& event) void OutOfProcessWebView::resize_event(GUI::ResizeEvent& event) { Super::resize_event(event); + client().async_set_viewport_rect(Gfx::IntRect({ horizontal_scrollbar().value(), vertical_scrollbar().value() }, available_size())); handle_resize(); } -void OutOfProcessWebView::handle_resize() +Gfx::IntRect OutOfProcessWebView::viewport_rect() const { - client().async_set_viewport_rect(Gfx::IntRect({ horizontal_scrollbar().value(), vertical_scrollbar().value() }, available_size())); - - if (m_client_state.has_usable_bitmap) { - // NOTE: We keep the outgoing front bitmap as a backup so we have something to paint until we get a new one. - m_backup_bitmap = m_client_state.front_bitmap.bitmap; - } - - if (m_client_state.front_bitmap.bitmap) - client().async_remove_backing_store(m_client_state.front_bitmap.id); - - if (m_client_state.back_bitmap.bitmap) - client().async_remove_backing_store(m_client_state.back_bitmap.id); - - m_client_state.front_bitmap = {}; - m_client_state.back_bitmap = {}; - m_client_state.has_usable_bitmap = false; - - if (available_size().is_empty()) - return; - - if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRx8888, available_size()); !new_bitmap_or_error.is_error()) { - m_client_state.front_bitmap.bitmap = new_bitmap_or_error.release_value(); - m_client_state.front_bitmap.id = m_client_state.next_bitmap_id++; - client().async_add_backing_store(m_client_state.front_bitmap.id, m_client_state.front_bitmap.bitmap->to_shareable_bitmap()); - } - - if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRx8888, available_size()); !new_bitmap_or_error.is_error()) { - m_client_state.back_bitmap.bitmap = new_bitmap_or_error.release_value(); - m_client_state.back_bitmap.id = m_client_state.next_bitmap_id++; - client().async_add_backing_store(m_client_state.back_bitmap.id, m_client_state.back_bitmap.bitmap->to_shareable_bitmap()); - } - - request_repaint(); + return visible_content_rect(); } void OutOfProcessWebView::update_zoom() @@ -556,21 +525,6 @@ void OutOfProcessWebView::did_scroll() request_repaint(); } -void OutOfProcessWebView::request_repaint() -{ - // If this widget was instantiated but not yet added to a window, - // it won't have a back bitmap yet, so we can just skip repaint requests. - if (!m_client_state.back_bitmap.bitmap) - return; - // Don't request a repaint until pending paint requests have finished. - if (m_client_state.back_bitmap.pending_paints) { - m_client_state.got_repaint_requests_while_painting = true; - return; - } - m_client_state.back_bitmap.pending_paints++; - client().async_paint(m_client_state.back_bitmap.bitmap->rect().translated(horizontal_scrollbar().value(), vertical_scrollbar().value()), m_client_state.back_bitmap.id); -} - void OutOfProcessWebView::js_console_input(DeprecatedString const& js_source) { client().async_js_console_input(js_source); diff --git a/Userland/Libraries/LibWebView/OutOfProcessWebView.h b/Userland/Libraries/LibWebView/OutOfProcessWebView.h index ca4baa89b72..b708d19940d 100644 --- a/Userland/Libraries/LibWebView/OutOfProcessWebView.h +++ b/Userland/Libraries/LibWebView/OutOfProcessWebView.h @@ -179,8 +179,7 @@ private: virtual void notify_server_did_request_file(Badge, DeprecatedString const& path, i32) override; virtual void notify_server_did_finish_handling_input_event(bool event_was_accepted) override; - void request_repaint(); - void handle_resize(); + virtual Gfx::IntRect viewport_rect() const override; void handle_web_content_process_crash(); @@ -188,7 +187,6 @@ private: void enqueue_input_event(InputEvent const&); void process_next_input_event(); - RefPtr m_backup_bitmap; RefPtr m_dialog; bool m_is_awaiting_response_for_input_event { false }; diff --git a/Userland/Libraries/LibWebView/ViewImplementation.cpp b/Userland/Libraries/LibWebView/ViewImplementation.cpp index 7c61b584f51..5d09a8ac682 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.cpp +++ b/Userland/Libraries/LibWebView/ViewImplementation.cpp @@ -10,6 +10,13 @@ namespace WebView { +ViewImplementation::ViewImplementation() +{ + m_backing_store_shrink_timer = Core::Timer::create_single_shot(3000, [this] { + resize_backing_stores_if_needed(WindowResizeInProgress::No); + }).release_value_but_fixme_should_propagate_errors(); +} + WebContentClient& ViewImplementation::client() { VERIFY(m_client_state.client); @@ -124,6 +131,12 @@ void ViewImplementation::run_javascript(StringView js_source) client().async_run_javascript(js_source); } +void ViewImplementation::handle_resize() +{ + resize_backing_stores_if_needed(WindowResizeInProgress::Yes); + m_backing_store_shrink_timer->restart(); +} + #if !defined(AK_OS_SERENITY) ErrorOr> ViewImplementation::launch_web_content_process(ReadonlySpan candidate_web_content_paths, EnableCallgrindProfiling enable_callgrind_profiling, IsLayoutTestMode is_layout_test_mode) @@ -196,4 +209,66 @@ ErrorOr> ViewImplementation::launch_web #endif +void ViewImplementation::resize_backing_stores_if_needed(WindowResizeInProgress window_resize_in_progress) +{ + if (m_client_state.has_usable_bitmap) { + // NOTE: We keep the outgoing front bitmap as a backup so we have something to paint until we get a new one. + m_backup_bitmap = m_client_state.front_bitmap.bitmap; + m_backup_bitmap_size = m_client_state.front_bitmap.last_painted_size; + } + + m_client_state.has_usable_bitmap = false; + + auto viewport_rect = this->viewport_rect(); + if (viewport_rect.is_empty()) + return; + + Gfx::IntSize minimum_needed_size; + + if (window_resize_in_progress == WindowResizeInProgress::Yes) { + // Pad the minimum needed size so that we don't have to keep reallocating backing stores while the window is being resized. + minimum_needed_size = { viewport_rect.width() + 256, viewport_rect.height() + 256 }; + } else { + // If we're not in the middle of a resize, we can shrink the backing store size to match the viewport size. + minimum_needed_size = viewport_rect.size(); + m_client_state.front_bitmap = {}; + m_client_state.back_bitmap = {}; + } + + auto reallocate_backing_store_if_needed = [&](SharedBitmap& backing_store) { + if (!backing_store.bitmap || !backing_store.bitmap->size().contains(minimum_needed_size)) { + if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRx8888, minimum_needed_size); !new_bitmap_or_error.is_error()) { + if (backing_store.bitmap) + client().async_remove_backing_store(backing_store.id); + + backing_store.pending_paints = 0; + backing_store.bitmap = new_bitmap_or_error.release_value(); + backing_store.id = m_client_state.next_bitmap_id++; + client().async_add_backing_store(backing_store.id, backing_store.bitmap->to_shareable_bitmap()); + } + backing_store.last_painted_size = viewport_rect.size(); + } + }; + + reallocate_backing_store_if_needed(m_client_state.front_bitmap); + reallocate_backing_store_if_needed(m_client_state.back_bitmap); + + request_repaint(); +} + +void ViewImplementation::request_repaint() +{ + // If this widget was instantiated but not yet added to a window, + // it won't have a back bitmap yet, so we can just skip repaint requests. + if (!m_client_state.back_bitmap.bitmap) + return; + // Don't request a repaint until pending paint requests have finished. + if (m_client_state.back_bitmap.pending_paints) { + m_client_state.got_repaint_requests_while_painting = true; + return; + } + m_client_state.back_bitmap.pending_paints++; + client().async_paint(viewport_rect(), m_client_state.back_bitmap.id); +} + } diff --git a/Userland/Libraries/LibWebView/ViewImplementation.h b/Userland/Libraries/LibWebView/ViewImplementation.h index 9efe4d1ffb9..e73afc294d1 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.h +++ b/Userland/Libraries/LibWebView/ViewImplementation.h @@ -124,15 +124,28 @@ public: virtual void notify_server_did_request_file(Badge, DeprecatedString const& path, i32) = 0; virtual void notify_server_did_finish_handling_input_event(bool event_was_accepted) = 0; + virtual Gfx::IntRect viewport_rect() const = 0; + protected: static constexpr auto ZOOM_MIN_LEVEL = 0.3f; static constexpr auto ZOOM_MAX_LEVEL = 5.0f; static constexpr auto ZOOM_STEP = 0.1f; + ViewImplementation(); + WebContentClient& client(); WebContentClient const& client() const; virtual void update_zoom() = 0; + enum class WindowResizeInProgress { + No, + Yes, + }; + void resize_backing_stores_if_needed(WindowResizeInProgress); + + void request_repaint(); + void handle_resize(); + virtual void create_client(EnableCallgrindProfiling = EnableCallgrindProfiling::No) {}; #if !defined(AK_OS_SERENITY) @@ -160,6 +173,11 @@ protected: float m_zoom_level { 1.0 }; float m_device_pixel_ratio { 1.0 }; + + RefPtr m_backing_store_shrink_timer; + + RefPtr m_backup_bitmap; + Gfx::IntSize m_backup_bitmap_size; }; } diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index 31c6b138c51..2549a4d19d4 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -59,7 +59,8 @@ public: view->client().async_update_system_theme(move(theme)); view->client().async_update_system_fonts(Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query(), Gfx::FontDatabase::window_title_font_query()); - view->client().async_set_viewport_rect({ { 0, 0 }, window_size }); + view->m_viewport_rect = { { 0, 0 }, window_size }; + view->client().async_set_viewport_rect(view->m_viewport_rect); view->client().async_set_window_size(window_size); if (!web_driver_ipc_path.is_empty()) @@ -154,6 +155,11 @@ private: void notify_server_did_finish_handling_input_event(bool) override { } void update_zoom() override { } void create_client(WebView::EnableCallgrindProfiling) override { } + + virtual Gfx::IntRect viewport_rect() const override { return m_viewport_rect; } + +private: + Gfx::IntRect m_viewport_rect; }; static ErrorOr> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, int screenshot_timeout)