diff --git a/Ladybird/WebContent/CMakeLists.txt b/Ladybird/WebContent/CMakeLists.txt index bf3c07af417..a2606f7148b 100644 --- a/Ladybird/WebContent/CMakeLists.txt +++ b/Ladybird/WebContent/CMakeLists.txt @@ -6,6 +6,7 @@ set(WEBCONTENT_SOURCE_DIR ${LADYBIRD_SOURCE_DIR}/Userland/Services/WebContent/) set(WEBCONTENT_SOURCES ${WEBCONTENT_SOURCE_DIR}/ConnectionFromClient.cpp ${WEBCONTENT_SOURCE_DIR}/ConsoleGlobalEnvironmentExtensions.cpp + ${WEBCONTENT_SOURCE_DIR}/BackingStoreManager.cpp ${WEBCONTENT_SOURCE_DIR}/PageClient.cpp ${WEBCONTENT_SOURCE_DIR}/PageHost.cpp ${WEBCONTENT_SOURCE_DIR}/WebContentConsoleClient.cpp diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h index db23de3a8f1..1a39cc7ba44 100644 --- a/Userland/Libraries/LibWeb/Page/Page.h +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -346,6 +347,7 @@ public: virtual void page_did_request_activate_tab() { } virtual void page_did_close_top_level_traversable() { } virtual void page_did_update_navigation_buttons_state([[maybe_unused]] bool back_enabled, [[maybe_unused]] bool forward_enabled) { } + virtual void page_did_allocate_backing_stores([[maybe_unused]] i32 front_bitmap_id, [[maybe_unused]] Gfx::ShareableBitmap front_bitmap, [[maybe_unused]] i32 back_bitmap_id, [[maybe_unused]] Gfx::ShareableBitmap back_bitmap) { } virtual void request_file(FileRequest) = 0; diff --git a/Userland/Libraries/LibWebView/ViewImplementation.cpp b/Userland/Libraries/LibWebView/ViewImplementation.cpp index 4d477541bc2..2b50476d6d5 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.cpp +++ b/Userland/Libraries/LibWebView/ViewImplementation.cpp @@ -16,10 +16,6 @@ namespace WebView { ViewImplementation::ViewImplementation() { - m_backing_store_shrink_timer = Core::Timer::create_single_shot(3000, [this] { - resize_backing_stores_if_needed(WindowResizeInProgress::No); - }); - m_repeated_crash_timer = Core::Timer::create_single_shot(1000, [this] { // Reset the "crashing a lot" counter after 1 second in case we just // happen to be visiting crashy websites a lot. @@ -386,62 +382,24 @@ void ViewImplementation::did_update_navigation_buttons_state(Badgerestart(); -} - -void ViewImplementation::resize_backing_stores_if_needed(WindowResizeInProgress window_resize_in_progress) +void ViewImplementation::did_allocate_backing_stores(Badge, i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap) { 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_size = this->viewport_size(); - if (viewport_size.is_empty()) - return; + m_client_state.front_bitmap.bitmap = front_bitmap.bitmap(); + m_client_state.front_bitmap.id = front_bitmap_id; + m_client_state.back_bitmap.bitmap = back_bitmap.bitmap(); + m_client_state.back_bitmap.id = back_bitmap_id; +} - Web::DevicePixelSize 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_size.width() + 256, viewport_size.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_size; - m_client_state.front_bitmap = {}; - m_client_state.back_bitmap = {}; - } - - auto old_front_bitmap_id = m_client_state.front_bitmap.id; - auto old_back_bitmap_id = m_client_state.back_bitmap.id; - - auto reallocate_backing_store_if_needed = [&](SharedBitmap& backing_store) { - if (!backing_store.bitmap || !backing_store.bitmap->size().contains(minimum_needed_size.to_type())) { - if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, minimum_needed_size.to_type()); !new_bitmap_or_error.is_error()) { - backing_store.bitmap = new_bitmap_or_error.release_value(); - backing_store.id = m_client_state.next_bitmap_id++; - } - backing_store.last_painted_size = viewport_size; - } - }; - - reallocate_backing_store_if_needed(m_client_state.front_bitmap); - reallocate_backing_store_if_needed(m_client_state.back_bitmap); - - auto& front_bitmap = m_client_state.front_bitmap; - auto& back_bitmap = m_client_state.back_bitmap; - - if (front_bitmap.id != old_front_bitmap_id || back_bitmap.id != old_back_bitmap_id) { - client().async_add_backing_store(page_id(), front_bitmap.id, front_bitmap.bitmap->to_shareable_bitmap(), back_bitmap.id, - back_bitmap.bitmap->to_shareable_bitmap()); - client().async_set_viewport_size(page_id(), viewport_size); - } +void ViewImplementation::handle_resize() +{ + client().async_set_viewport_size(page_id(), this->viewport_size()); } void ViewImplementation::handle_web_content_process_crash() diff --git a/Userland/Libraries/LibWebView/ViewImplementation.h b/Userland/Libraries/LibWebView/ViewImplementation.h index 22d2983a9c0..bd3bdf67cd8 100644 --- a/Userland/Libraries/LibWebView/ViewImplementation.h +++ b/Userland/Libraries/LibWebView/ViewImplementation.h @@ -117,6 +117,8 @@ public: void did_update_navigation_buttons_state(Badge, bool back_enabled, bool forward_enabled) const; + void did_allocate_backing_stores(Badge, i32 front_bitmap_id, Gfx::ShareableBitmap const&, i32 back_bitmap_id, Gfx::ShareableBitmap const&); + enum class ScreenshotType { Visible, Full, @@ -222,12 +224,6 @@ protected: u64 page_id() const; virtual void update_zoom() = 0; - enum class WindowResizeInProgress { - No, - Yes, - }; - void resize_backing_stores_if_needed(WindowResizeInProgress); - void handle_resize(); enum class CreateNewClient { @@ -250,7 +246,6 @@ protected: SharedBitmap front_bitmap; SharedBitmap back_bitmap; u64 page_index { 0 }; - i32 next_bitmap_id { 0 }; bool has_usable_bitmap { false }; } m_client_state; diff --git a/Userland/Libraries/LibWebView/WebContentClient.cpp b/Userland/Libraries/LibWebView/WebContentClient.cpp index c7d2da0a49c..ad3099c3507 100644 --- a/Userland/Libraries/LibWebView/WebContentClient.cpp +++ b/Userland/Libraries/LibWebView/WebContentClient.cpp @@ -593,6 +593,12 @@ void WebContentClient::did_update_navigation_buttons_state(u64 page_id, bool bac view->did_update_navigation_buttons_state({}, back_enabled, forward_enabled); } +void WebContentClient::did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap) +{ + if (auto view = view_for_page_id(page_id); view.has_value()) + view->did_allocate_backing_stores({}, front_bitmap_id, front_bitmap, back_bitmap_id, back_bitmap); +} + void WebContentClient::inspector_did_load(u64 page_id) { if (auto view = view_for_page_id(page_id); view.has_value()) { diff --git a/Userland/Libraries/LibWebView/WebContentClient.h b/Userland/Libraries/LibWebView/WebContentClient.h index bdf172da81b..e771081f510 100644 --- a/Userland/Libraries/LibWebView/WebContentClient.h +++ b/Userland/Libraries/LibWebView/WebContentClient.h @@ -100,6 +100,7 @@ private: virtual void did_insert_clipboard_entry(u64 page_id, String const& data, String const& presentation_style, String const& mime_type) override; virtual void did_change_audio_play_state(u64 page_id, Web::HTML::AudioPlayState) override; virtual void did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) override; + virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const&, i32 back_bitmap_id, Gfx::ShareableBitmap const&) override; virtual void inspector_did_load(u64 page_id) override; virtual void inspector_did_select_dom_node(u64 page_id, i32 node_id, Optional const& pseudo_element) override; virtual void inspector_did_set_dom_node_text(u64 page_id, i32 node_id, String const& text) override; diff --git a/Userland/Services/WebContent/BackingStoreManager.cpp b/Userland/Services/WebContent/BackingStoreManager.cpp new file mode 100644 index 00000000000..71ec7ea0b2e --- /dev/null +++ b/Userland/Services/WebContent/BackingStoreManager.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace WebContent { + +BackingStoreManager::BackingStoreManager(PageClient& page_client) + : m_page_client(page_client) +{ + m_backing_store_shrink_timer = Core::Timer::create_single_shot(3000, [this] { + resize_backing_stores_if_needed(WindowResizingInProgress::No); + }); +} + +void BackingStoreManager::restart_resize_timer() +{ + m_backing_store_shrink_timer->restart(); +} + +void BackingStoreManager::resize_backing_stores_if_needed(WindowResizingInProgress window_resize_in_progress) +{ + auto css_pixels_viewport_rect = m_page_client.page().top_level_traversable()->viewport_rect(); + auto viewport_size = m_page_client.page().css_to_device_rect(css_pixels_viewport_rect).size(); + + if (viewport_size.is_empty()) + return; + + Web::DevicePixelSize minimum_needed_size; + if (window_resize_in_progress == WindowResizingInProgress::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_size.width() + 256, viewport_size.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_size; + m_front_bitmap.clear(); + m_back_bitmap.clear(); + } + + auto old_front_bitmap_id = m_front_bitmap_id; + auto old_back_bitmap_id = m_back_bitmap_id; + + auto reallocate_backing_store_if_needed = [&](RefPtr& bitmap, int& id) { + if (!bitmap || !bitmap->size().contains(minimum_needed_size.to_type())) { + if (auto new_bitmap_or_error = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, minimum_needed_size.to_type()); !new_bitmap_or_error.is_error()) { + bitmap = new_bitmap_or_error.release_value(); + id = m_next_bitmap_id++; + } + } + }; + + reallocate_backing_store_if_needed(m_front_bitmap, m_front_bitmap_id); + reallocate_backing_store_if_needed(m_back_bitmap, m_back_bitmap_id); + + auto& front_bitmap = m_front_bitmap; + auto& back_bitmap = m_back_bitmap; + + if (m_front_bitmap_id != old_front_bitmap_id || m_back_bitmap_id != old_back_bitmap_id) { + m_page_client.page_did_allocate_backing_stores(m_front_bitmap_id, front_bitmap->to_shareable_bitmap(), m_back_bitmap_id, back_bitmap->to_shareable_bitmap()); + } +} + +void BackingStoreManager::swap_back_and_front() +{ + swap(m_front_bitmap, m_back_bitmap); + swap(m_front_bitmap_id, m_back_bitmap_id); +} + +} diff --git a/Userland/Services/WebContent/BackingStoreManager.h b/Userland/Services/WebContent/BackingStoreManager.h new file mode 100644 index 00000000000..ffc2fecc873 --- /dev/null +++ b/Userland/Services/WebContent/BackingStoreManager.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace WebContent { + +class BackingStoreManager { +public: + enum class WindowResizingInProgress { + No, + Yes + }; + void resize_backing_stores_if_needed(WindowResizingInProgress window_resize_in_progress); + void restart_resize_timer(); + + RefPtr back_bitmap() { return m_back_bitmap; } + i32 front_id() const { return m_front_bitmap_id; } + + void swap_back_and_front(); + + BackingStoreManager(PageClient&); + +private: + PageClient& m_page_client; + + i32 m_front_bitmap_id { -1 }; + i32 m_back_bitmap_id { -1 }; + RefPtr m_front_bitmap; + RefPtr m_back_bitmap; + int m_next_bitmap_id { 0 }; + + RefPtr m_backing_store_shrink_timer; +}; + +} diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index f4d1671129b..cf94cca371f 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -155,12 +155,6 @@ void ConnectionFromClient::set_viewport_size(u64 page_id, Web::DevicePixelSize c page->set_viewport_size(size); } -void ConnectionFromClient::add_backing_store(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap) -{ - if (auto page = this->page(page_id); page.has_value()) - page->add_backing_store(front_bitmap_id, front_bitmap, back_bitmap_id, back_bitmap); -} - void ConnectionFromClient::ready_to_paint(u64 page_id) { if (auto page = this->page(page_id); page.has_value()) diff --git a/Userland/Services/WebContent/ConnectionFromClient.h b/Userland/Services/WebContent/ConnectionFromClient.h index 9f62af40325..3f9ac2be6e4 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.h +++ b/Userland/Services/WebContent/ConnectionFromClient.h @@ -63,7 +63,6 @@ private: virtual void set_viewport_size(u64 page_id, Web::DevicePixelSize const) override; virtual void key_event(u64 page_id, Web::KeyEvent const&) override; virtual void mouse_event(u64 page_id, Web::MouseEvent const&) override; - virtual void add_backing_store(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap) override; virtual void ready_to_paint(u64 page_id) override; virtual void debug_request(u64 page_id, ByteString const&, ByteString const&) override; virtual void get_source(u64 page_id) override; diff --git a/Userland/Services/WebContent/PageClient.cpp b/Userland/Services/WebContent/PageClient.cpp index a685f44c59a..a9dcb07141c 100644 --- a/Userland/Services/WebContent/PageClient.cpp +++ b/Userland/Services/WebContent/PageClient.cpp @@ -55,6 +55,7 @@ PageClient::PageClient(PageHost& owner, u64 id) : m_owner(owner) , m_page(Web::Page::create(Web::Bindings::main_thread_vm(), *this)) , m_id(id) + , m_backing_store_manager(*this) { setup_palette(); @@ -96,14 +97,6 @@ void PageClient::ready_to_paint() } } -void PageClient::add_backing_store(i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap) -{ - m_backing_stores.front_bitmap_id = front_bitmap_id; - m_backing_stores.back_bitmap_id = back_bitmap_id; - m_backing_stores.front_bitmap = *const_cast(front_bitmap).bitmap(); - m_backing_stores.back_bitmap = *const_cast(back_bitmap).bitmap(); -} - void PageClient::visit_edges(JS::Cell::Visitor& visitor) { Base::visit_edges(visitor); @@ -216,20 +209,17 @@ void PageClient::paint_next_frame() } } - if (!m_backing_stores.back_bitmap) { + auto back_bitmap = m_backing_store_manager.back_bitmap(); + if (!back_bitmap) return; - } - auto& back_bitmap = *m_backing_stores.back_bitmap; auto viewport_rect = page().css_to_device_rect(page().top_level_traversable()->viewport_rect()); - paint(viewport_rect, back_bitmap); + paint(viewport_rect, *back_bitmap); - auto& backing_stores = m_backing_stores; - swap(backing_stores.front_bitmap, backing_stores.back_bitmap); - swap(backing_stores.front_bitmap_id, backing_stores.back_bitmap_id); + m_backing_store_manager.swap_back_and_front(); m_paint_state = PaintState::WaitingForClient; - client().async_did_paint(m_id, viewport_rect.to_type(), backing_stores.front_bitmap_id); + client().async_did_paint(m_id, viewport_rect.to_type(), m_backing_store_manager.front_id()); } void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& target, Web::PaintOptions paint_options) @@ -245,6 +235,9 @@ void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& ta void PageClient::set_viewport_size(Web::DevicePixelSize const& size) { page().top_level_traversable()->set_viewport_size(page().device_to_css_size(size)); + + m_backing_store_manager.restart_resize_timer(); + m_backing_store_manager.resize_backing_stores_if_needed(BackingStoreManager::WindowResizingInProgress::Yes); } void PageClient::page_did_request_cursor_change(Gfx::StandardCursor cursor) @@ -596,6 +589,11 @@ void PageClient::page_did_change_audio_play_state(Web::HTML::AudioPlayState play client().async_did_change_audio_play_state(m_id, play_state); } +void PageClient::page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) +{ + client().async_did_allocate_backing_stores(m_id, front_bitmap_id, front_bitmap, back_bitmap_id, back_bitmap); +} + IPC::File PageClient::request_worker_agent() { auto response = client().send_sync_but_allow_failure(m_id); @@ -756,5 +754,4 @@ void PageClient::queue_screenshot_task(Optional node_id) m_screenshot_tasks.enqueue({ node_id }); page().top_level_traversable()->set_needs_display(); } - } diff --git a/Userland/Services/WebContent/PageClient.h b/Userland/Services/WebContent/PageClient.h index 063c836c94a..24bdda8b81c 100644 --- a/Userland/Services/WebContent/PageClient.h +++ b/Userland/Services/WebContent/PageClient.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #ifdef HAS_ACCELERATED_GRAPHICS @@ -75,8 +76,6 @@ public: void ready_to_paint(); - void add_backing_store(i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap); - void initialize_js_console(Web::DOM::Document& document); void destroy_js_console(Web::DOM::Document& document); void js_console_input(ByteString const& js_source); @@ -92,6 +91,8 @@ public: void queue_screenshot_task(Optional node_id); + friend class BackingStoreManager; + private: PageClient(PageHost&, u64 id); @@ -156,6 +157,7 @@ private: virtual void page_did_change_theme_color(Gfx::Color color) override; virtual void page_did_insert_clipboard_entry(String data, String presentation_style, String mime_type) override; virtual void page_did_change_audio_play_state(Web::HTML::AudioPlayState) override; + virtual void page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) override; virtual IPC::File request_worker_agent() override; virtual void inspector_did_load() override; virtual void inspector_did_select_dom_node(i32 node_id, Optional const& pseudo_element) override; @@ -203,13 +205,7 @@ private: OwnPtr m_accelerated_graphics_context; #endif - struct BackingStores { - i32 front_bitmap_id { -1 }; - i32 back_bitmap_id { -1 }; - RefPtr front_bitmap; - RefPtr back_bitmap; - }; - BackingStores m_backing_stores; + BackingStoreManager m_backing_store_manager; // NOTE: These documents are not visited, but manually removed from the map on document finalization. HashMap, JS::NonnullGCPtr> m_console_clients; diff --git a/Userland/Services/WebContent/WebContentClient.ipc b/Userland/Services/WebContent/WebContentClient.ipc index 8bc6b078582..404475452ea 100644 --- a/Userland/Services/WebContent/WebContentClient.ipc +++ b/Userland/Services/WebContent/WebContentClient.ipc @@ -80,6 +80,7 @@ endpoint WebContentClient did_change_theme_color(u64 page_id, Gfx::Color color) =| did_insert_clipboard_entry(u64 page_id, String data, String presentation_style, String mime_type) =| did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) =| + did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) =| did_change_audio_play_state(u64 page_id, Web::HTML::AudioPlayState play_state) =| diff --git a/Userland/Services/WebContent/WebContentServer.ipc b/Userland/Services/WebContent/WebContentServer.ipc index e9f3652648e..540e98de7d2 100644 --- a/Userland/Services/WebContent/WebContentServer.ipc +++ b/Userland/Services/WebContent/WebContentServer.ipc @@ -1,8 +1,6 @@ #include #include -#include #include -#include #include #include #include @@ -28,7 +26,6 @@ endpoint WebContentServer reload(u64 page_id) =| traverse_the_history_by_delta(u64 page_id, i32 delta) =| - add_backing_store(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) =| ready_to_paint(u64 page_id) =| set_viewport_size(u64 page_id, Web::DevicePixelSize size) =|