diff --git a/Libraries/LibWeb/Page/ViewportIsFullscreen.h b/Libraries/LibWeb/Page/ViewportIsFullscreen.h new file mode 100644 index 00000000000..24d60b7a0db --- /dev/null +++ b/Libraries/LibWeb/Page/ViewportIsFullscreen.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2026-present, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web { + +enum class ViewportIsFullscreen : u8 { + Yes, + No, +}; + +} diff --git a/Libraries/LibWebView/HeadlessWebView.cpp b/Libraries/LibWebView/HeadlessWebView.cpp index e0b28299a0a..194fc4c7332 100644 --- a/Libraries/LibWebView/HeadlessWebView.cpp +++ b/Libraries/LibWebView/HeadlessWebView.cpp @@ -60,7 +60,7 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi m_viewport_size = size.template to_type(); client().async_set_window_size(m_client_state.page_index, m_viewport_size); - client().async_set_viewport(m_client_state.page_index, m_viewport_size, m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, m_viewport_size, m_device_pixel_ratio, Web::ViewportIsFullscreen::No); client().async_did_update_window_rect(m_client_state.page_index); }; @@ -79,7 +79,7 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi client().async_set_window_position(m_client_state.page_index, screen_rect.location()); client().async_set_window_size(m_client_state.page_index, screen_rect.size()); - client().async_set_viewport(m_client_state.page_index, screen_rect.size(), m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, screen_rect.size(), m_device_pixel_ratio, m_is_fullscreen); client().async_did_update_window_rect(m_client_state.page_index); }; @@ -87,19 +87,22 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi on_fullscreen_window = [this]() { m_previous_dimensions.set_size(m_viewport_size); m_viewport_size = screen_rect.size(); + m_is_fullscreen = Web::ViewportIsFullscreen::Yes; client().async_set_window_position(m_client_state.page_index, screen_rect.location()); client().async_set_window_size(m_client_state.page_index, screen_rect.size()); - client().async_set_viewport(m_client_state.page_index, screen_rect.size(), m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, screen_rect.size(), m_device_pixel_ratio, Web::ViewportIsFullscreen::Yes); client().async_did_update_window_rect(m_client_state.page_index); }; on_exit_fullscreen_window = [this]() { m_viewport_size = m_previous_dimensions.size(); + m_is_fullscreen = Web::ViewportIsFullscreen::No; + client().async_set_window_position(m_client_state.page_index, m_previous_dimensions.location()); client().async_set_window_size(m_client_state.page_index, m_previous_dimensions.size()); - client().async_set_viewport(m_client_state.page_index, m_previous_dimensions.size(), m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, m_previous_dimensions.size(), m_device_pixel_ratio, Web::ViewportIsFullscreen::No); client().async_did_update_window_rect(m_client_state.page_index); }; @@ -168,7 +171,7 @@ void HeadlessWebView::initialize_client(CreateNewClient create_new_client) ViewImplementation::initialize_client(create_new_client); client().async_update_system_theme(m_client_state.page_index, m_theme); - client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio, m_is_fullscreen); client().async_set_window_size(m_client_state.page_index, viewport_size()); client().async_update_screen_rects(m_client_state.page_index, { { screen_rect } }, 0); } @@ -178,7 +181,7 @@ void HeadlessWebView::reset_viewport_size(Web::DevicePixelSize size) m_viewport_size = size; client().async_set_window_size(m_client_state.page_index, m_viewport_size); - client().async_set_viewport(m_client_state.page_index, m_viewport_size, m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, m_viewport_size, m_device_pixel_ratio, m_is_fullscreen); client().async_did_update_window_rect(m_client_state.page_index); } diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp index 1c9da2ad013..a91f401ab76 100644 --- a/Libraries/LibWebView/ViewImplementation.cpp +++ b/Libraries/LibWebView/ViewImplementation.cpp @@ -480,6 +480,15 @@ void ViewImplementation::exit_fullscreen() client().async_exit_fullscreen(page_id()); } +void ViewImplementation::set_is_fullscreen(Web::ViewportIsFullscreen is_fullscreen) +{ + if (m_is_fullscreen == is_fullscreen) + return; + + handle_resize(); + did_update_window_rect(); +} + void ViewImplementation::alert_closed() { client().async_alert_closed(page_id()); @@ -618,7 +627,7 @@ void ViewImplementation::update_zoom() void ViewImplementation::handle_resize() { - client().async_set_viewport(page_id(), this->viewport_size(), m_device_pixel_ratio); + client().async_set_viewport(page_id(), viewport_size(), m_device_pixel_ratio, m_is_fullscreen); } void ViewImplementation::initialize_client(CreateNewClient create_new_client) @@ -635,7 +644,7 @@ void ViewImplementation::initialize_client(CreateNewClient create_new_client) m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid()); client().async_set_window_handle(m_client_state.page_index, m_client_state.client_handle); client().async_set_zoom_level(m_client_state.page_index, m_zoom_level); - client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio); + client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio, m_is_fullscreen); client().async_set_maximum_frames_per_second(m_client_state.page_index, m_maximum_frames_per_second); client().async_set_system_visibility_state(m_client_state.page_index, m_system_visibility_state); client().async_set_document_cookie_version_buffer(m_client_state.page_index, m_document_cookie_version_buffer); diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index 0d84497c101..ef760ca5f74 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -135,6 +136,8 @@ public: void js_console_input(String const&); void exit_fullscreen(); + void set_is_fullscreen(Web::ViewportIsFullscreen is_fullscreen); + void alert_closed(); void confirm_closed(bool accepted); void prompt_closed(Optional const& response); @@ -381,6 +384,8 @@ protected: Web::HTML::MuteState m_mute_state { Web::HTML::MuteState::Unmuted }; + Web::ViewportIsFullscreen m_is_fullscreen { Web::ViewportIsFullscreen::No }; + Core::AnonymousBuffer m_document_cookie_version_buffer; HashMap m_document_cookie_version_indices; diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 85ea371bf78..5da3cfa200e 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -196,7 +196,7 @@ void ConnectionFromClient::traverse_the_history_by_delta(u64 page_id, i32 delta) page->page().traverse_the_history_by_delta(delta); } -void ConnectionFromClient::set_viewport(u64 page_id, Web::DevicePixelSize size, double device_pixel_ratio) +void ConnectionFromClient::set_viewport(u64 page_id, Web::DevicePixelSize size, double device_pixel_ratio, Web::ViewportIsFullscreen) { if (auto page = this->page(page_id); page.has_value()) page->set_viewport(size, device_pixel_ratio); diff --git a/Services/WebContent/ConnectionFromClient.h b/Services/WebContent/ConnectionFromClient.h index be1d6278dd8..59fc2df9789 100644 --- a/Services/WebContent/ConnectionFromClient.h +++ b/Services/WebContent/ConnectionFromClient.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -71,7 +72,7 @@ private: virtual void load_html(u64 page_id, ByteString) override; virtual void reload(u64 page_id) override; virtual void traverse_the_history_by_delta(u64 page_id, i32 delta) override; - virtual void set_viewport(u64 page_id, Web::DevicePixelSize, double device_pixel_ratio) override; + virtual void set_viewport(u64 page_id, Web::DevicePixelSize, double device_pixel_ratio, Web::ViewportIsFullscreen is_fullscreen) override; virtual void key_event(u64 page_id, Web::KeyEvent) override; virtual void mouse_event(u64 page_id, Web::MouseEvent) override; virtual void drag_event(u64 page_id, Web::DragEvent) override; diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc index 8af8d9003f5..cc2d93d7afd 100644 --- a/Services/WebContent/WebContentServer.ipc +++ b/Services/WebContent/WebContentServer.ipc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ endpoint WebContentServer ready_to_paint(u64 page_id) =| - set_viewport(u64 page_id, Web::DevicePixelSize size, double device_pixel_ratio) =| + set_viewport(u64 page_id, Web::DevicePixelSize size, double device_pixel_ratio, Web::ViewportIsFullscreen is_fullscreen) =| key_event(u64 page_id, Web::KeyEvent event) =| mouse_event(u64 page_id, Web::MouseEvent event) =| diff --git a/Tests/LibWeb/test-web/TestWebView.cpp b/Tests/LibWeb/test-web/TestWebView.cpp index 33634015349..4ae1a48984c 100644 --- a/Tests/LibWeb/test-web/TestWebView.cpp +++ b/Tests/LibWeb/test-web/TestWebView.cpp @@ -60,7 +60,8 @@ void TestWebView::on_test_complete(TestCompletion completion) m_pending_screenshot.clear(); m_pending_dialog = Web::Page::PendingDialog::None; m_pending_prompt_text.clear(); - client().async_set_viewport(m_client_state.page_index, viewport_size(), 1.0); + m_is_fullscreen = Web::ViewportIsFullscreen::No; + client().async_set_viewport(m_client_state.page_index, viewport_size(), 1.0, Web::ViewportIsFullscreen::No); m_test_promise->resolve(move(completion)); } diff --git a/UI/Android/src/main/cpp/WebViewImplementationNative.cpp b/UI/Android/src/main/cpp/WebViewImplementationNative.cpp index adb4c07e7f5..f20699092c4 100644 --- a/UI/Android/src/main/cpp/WebViewImplementationNative.cpp +++ b/UI/Android/src/main/cpp/WebViewImplementationNative.cpp @@ -61,7 +61,7 @@ void WebViewImplementationNative::initialize_client(WebView::ViewImplementation: m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid()); client().async_set_window_handle(0, m_client_state.client_handle); - client().async_set_viewport(0, viewport_size(), m_device_pixel_ratio); + client().async_set_viewport(0, viewport_size(), m_device_pixel_ratio, Web::ViewportIsFullscreen::No); client().async_set_zoom_level(0, m_zoom_level); set_system_visibility_state(Web::HTML::VisibilityState::Visible); diff --git a/UI/AppKit/Interface/LadybirdWebView.h b/UI/AppKit/Interface/LadybirdWebView.h index 168297741a6..3ee7504eed2 100644 --- a/UI/AppKit/Interface/LadybirdWebView.h +++ b/UI/AppKit/Interface/LadybirdWebView.h @@ -59,6 +59,8 @@ - (void)handleResize; - (void)handleDevicePixelRatioChange; - (void)handleDisplayRefreshRateChange; +- (void)handleEnteredFullScreen; +- (void)handleExitedFullScreen; - (void)handleExitFullScreen; - (void)handleVisibility:(BOOL)is_visible; diff --git a/UI/AppKit/Interface/LadybirdWebView.mm b/UI/AppKit/Interface/LadybirdWebView.mm index f0ca0524fd9..bf215935f10 100644 --- a/UI/AppKit/Interface/LadybirdWebView.mm +++ b/UI/AppKit/Interface/LadybirdWebView.mm @@ -205,6 +205,16 @@ struct HideCursor { m_web_view_bridge->set_maximum_frames_per_second([[[self window] screen] maximumFramesPerSecond]); } +- (void)handleEnteredFullScreen +{ + m_web_view_bridge->set_is_fullscreen(Web::ViewportIsFullscreen::Yes); +} + +- (void)handleExitedFullScreen +{ + m_web_view_bridge->set_is_fullscreen(Web::ViewportIsFullscreen::No); +} + - (void)handleExitFullScreen { m_web_view_bridge->exit_fullscreen(); @@ -854,7 +864,6 @@ struct HideCursor { } [self.observer onEnterFullscreenWindow]; - m_web_view_bridge->did_update_window_rect(); }; m_web_view_bridge->on_exit_fullscreen_window = [weak_self]() { @@ -864,7 +873,6 @@ struct HideCursor { } [self.observer onExitFullscreenWindow]; - m_web_view_bridge->did_update_window_rect(); }; m_web_view_bridge->on_theme_color_change = [weak_self](auto color) { diff --git a/UI/AppKit/Interface/TabController.mm b/UI/AppKit/Interface/TabController.mm index 9f36f99cd6f..cb999a1ab74 100644 --- a/UI/AppKit/Interface/TabController.mm +++ b/UI/AppKit/Interface/TabController.mm @@ -493,6 +493,18 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde } } +- (void)windowDidEnterFullScreen:(NSNotification*)notification +{ + if (m_fullscreen_requested_for_web_content) + [[[self tab] web_view] handleEnteredFullScreen]; +} + +- (void)windowWillExitFullScreen:(NSNotification*)notification +{ + if (exchange(m_fullscreen_exit_was_ui_initiated, true)) + [[[self tab] web_view] handleExitFullScreen]; +} + - (void)windowDidExitFullScreen:(NSNotification*)notification { if (exchange(m_fullscreen_requested_for_web_content, false)) { @@ -503,8 +515,7 @@ static NSString* const TOOLBAR_TAB_OVERVIEW_IDENTIFIER = @"ToolbarTabOverviewIde } } - if (exchange(m_fullscreen_exit_was_ui_initiated, true)) - [[[self tab] web_view] handleExitFullScreen]; + [[[self tab] web_view] handleExitedFullScreen]; } - (NSApplicationPresentationOptions)window:(NSWindow*)window diff --git a/UI/Qt/BrowserWindow.cpp b/UI/Qt/BrowserWindow.cpp index ef16e37e940..7705054d93c 100644 --- a/UI/Qt/BrowserWindow.cpp +++ b/UI/Qt/BrowserWindow.cpp @@ -728,8 +728,15 @@ void BrowserWindow::changeEvent(QEvent* event) { if (event->type() == QEvent::WindowStateChange) { QWindowStateChangeEvent* stateChangeEvent = static_cast(event); - if (windowState() & Qt::WindowFullScreen && !(stateChangeEvent->oldState() & Qt::WindowFullScreen)) { + bool was_fullscreen = stateChangeEvent->oldState() & Qt::WindowFullScreen; + bool is_fullscreen = windowState() & Qt::WindowFullScreen; + + if (is_fullscreen && !was_fullscreen) { m_fullscreen_mode->entered_fullscreen(); + if (m_fullscreen_mode->is_api_fullscreen()) + view().set_is_fullscreen(Web::ViewportIsFullscreen::Yes); + } else if (!is_fullscreen && was_fullscreen) { + view().set_is_fullscreen(Web::ViewportIsFullscreen::No); } } QWidget::changeEvent(event); diff --git a/UI/Qt/Tab.cpp b/UI/Qt/Tab.cpp index f3518e5b34b..d501c418b08 100644 --- a/UI/Qt/Tab.cpp +++ b/UI/Qt/Tab.cpp @@ -392,13 +392,11 @@ Tab::Tab(BrowserWindow* window, RefPtr parent_client, view().on_fullscreen_window = [this]() { m_toolbar->hide(); m_window->fullscreen_mode().enter(this); - view().did_update_window_rect(); }; view().on_exit_fullscreen_window = [this]() { m_window->fullscreen_mode().exit(FullscreenMode::ExitInitiatedBy::WebContent); m_toolbar->show(); - view().did_update_window_rect(); }; view().on_audio_play_state_changed = [this](auto play_state) {