diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index e3974b26a8..e30eccb553 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -426,20 +426,20 @@ WebIDL::ExceptionOr> Window::open_impl(StringView u void Window::alert_impl(String const& message) { if (auto* page = this->page()) - page->client().page_did_request_alert(message); + page->did_request_alert(message); } bool Window::confirm_impl(String const& message) { if (auto* page = this->page()) - return page->client().page_did_request_confirm(message); + return page->did_request_confirm(message); return false; } String Window::prompt_impl(String const& message, String const& default_) { if (auto* page = this->page()) - return page->client().page_did_request_prompt(message, default_); + return page->did_request_prompt(message, default_); return {}; } diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp index 07a57f9043..4ab8b34152 100644 --- a/Userland/Libraries/LibWeb/Page/Page.cpp +++ b/Userland/Libraries/LibWeb/Page/Page.cpp @@ -4,9 +4,13 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include +#include +#include #include +#include namespace Web { @@ -105,4 +109,112 @@ HTML::BrowsingContext const& Page::top_level_browsing_context() const return *m_top_level_browsing_context; } +template +static ResponseType spin_event_loop_until_dialog_closed(PageClient& client, Optional& response, SourceLocation location = SourceLocation::current()) +{ + auto& event_loop = Web::HTML::current_settings_object().responsible_event_loop(); + + ScopeGuard guard { [&] { event_loop.set_execution_paused(false); } }; + event_loop.set_execution_paused(true); + + Web::Platform::EventLoopPlugin::the().spin_until([&]() { + return response.has_value() || !client.is_connection_open(); + }); + + if (!client.is_connection_open()) { + dbgln("WebContent client disconnected during {}. Exiting peacefully.", location.function_name()); + exit(0); + } + + return response.release_value(); +} + +void Page::did_request_alert(String const& message) +{ + m_pending_dialog = PendingDialog::Alert; + m_client.page_did_request_alert(message); + + if (!message.is_empty()) + m_pending_dialog_text = message; + + spin_event_loop_until_dialog_closed(m_client, m_pending_alert_response); +} + +void Page::alert_closed() +{ + if (m_pending_dialog == PendingDialog::Alert) { + m_pending_dialog = PendingDialog::None; + m_pending_alert_response = Empty {}; + m_pending_dialog_text.clear(); + } +} + +bool Page::did_request_confirm(String const& message) +{ + m_pending_dialog = PendingDialog::Confirm; + m_client.page_did_request_confirm(message); + + if (!message.is_empty()) + m_pending_dialog_text = message; + + return spin_event_loop_until_dialog_closed(m_client, m_pending_confirm_response); +} + +void Page::confirm_closed(bool accepted) +{ + if (m_pending_dialog == PendingDialog::Confirm) { + m_pending_dialog = PendingDialog::None; + m_pending_confirm_response = accepted; + m_pending_dialog_text.clear(); + } +} + +String Page::did_request_prompt(String const& message, String const& default_) +{ + m_pending_dialog = PendingDialog::Prompt; + m_client.page_did_request_prompt(message, default_); + + if (!message.is_empty()) + m_pending_dialog_text = message; + + return spin_event_loop_until_dialog_closed(m_client, m_pending_prompt_response); +} + +void Page::prompt_closed(String response) +{ + if (m_pending_dialog == PendingDialog::Prompt) { + m_pending_dialog = PendingDialog::None; + m_pending_prompt_response = move(response); + m_pending_dialog_text.clear(); + } +} + +void Page::dismiss_dialog() +{ + switch (m_pending_dialog) { + case PendingDialog::None: + break; + case PendingDialog::Alert: + m_client.page_did_request_accept_dialog(); + break; + case PendingDialog::Confirm: + case PendingDialog::Prompt: + m_client.page_did_request_dismiss_dialog(); + break; + } +} + +void Page::accept_dialog() +{ + switch (m_pending_dialog) { + case PendingDialog::None: + break; + case PendingDialog::Alert: + case PendingDialog::Confirm: + case PendingDialog::Prompt: + m_client.page_did_request_accept_dialog(); + break; + } +} + } diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h index b07541ca04..135281691d 100644 --- a/Userland/Libraries/LibWeb/Page/Page.h +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -83,6 +83,27 @@ public: Gfx::IntSize const& window_size() const { return m_window_size; } void set_window_size(Gfx::IntSize const& size) { m_window_size = size; } + void did_request_alert(String const& message); + void alert_closed(); + + bool did_request_confirm(String const& message); + void confirm_closed(bool accepted); + + String did_request_prompt(String const& message, String const& default_); + void prompt_closed(String response); + + enum class PendingDialog { + None, + Alert, + Confirm, + Prompt, + }; + bool has_pending_dialog() const { return m_pending_dialog != PendingDialog::None; } + PendingDialog pending_dialog() const { return m_pending_dialog; } + Optional const& pending_dialog_text() const { return m_pending_dialog_text; } + void dismiss_dialog(); + void accept_dialog(); + private: PageClient& m_client; @@ -102,10 +123,17 @@ private: Gfx::IntPoint m_window_position {}; Gfx::IntSize m_window_size {}; + + PendingDialog m_pending_dialog { PendingDialog::None }; + Optional m_pending_dialog_text; + Optional m_pending_alert_response; + Optional m_pending_confirm_response; + Optional m_pending_prompt_response; }; class PageClient { public: + virtual bool is_connection_open() const = 0; virtual Gfx::Palette palette() const = 0; virtual Gfx::IntRect screen_rect() const = 0; virtual CSS::PreferredColorScheme preferred_color_scheme() const = 0; @@ -131,8 +159,10 @@ public: virtual void page_did_request_scroll_to(Gfx::IntPoint const&) { } virtual void page_did_request_scroll_into_view(Gfx::IntRect const&) { } virtual void page_did_request_alert(String const&) { } - virtual bool page_did_request_confirm(String const&) { return false; } - virtual String page_did_request_prompt(String const&, String const&) { return {}; } + virtual void page_did_request_confirm(String const&) { } + virtual void page_did_request_prompt(String const&, String const&) { } + virtual void page_did_request_accept_dialog() { } + virtual void page_did_request_dismiss_dialog() { } virtual String page_did_request_cookie(const AK::URL&, Cookie::Source) { return {}; } virtual void page_did_set_cookie(const AK::URL&, Cookie::ParsedCookie const&, Cookie::Source) { } virtual void page_did_update_resource_count(i32) { } diff --git a/Userland/Services/WebContent/PageHost.cpp b/Userland/Services/WebContent/PageHost.cpp index 46837705c1..941d9ff700 100644 --- a/Userland/Services/WebContent/PageHost.cpp +++ b/Userland/Services/WebContent/PageHost.cpp @@ -7,17 +7,13 @@ #include "PageHost.h" #include "ConnectionFromClient.h" -#include #include #include #include #include #include -#include -#include #include #include -#include #include #include #include @@ -54,6 +50,11 @@ void PageHost::setup_palette() m_palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(buffer); } +bool PageHost::is_connection_open() const +{ + return m_client.is_open(); +} + Gfx::Palette PageHost::palette() const { return Gfx::Palette(*m_palette_impl); @@ -237,112 +238,44 @@ void PageHost::page_did_request_link_context_menu(Gfx::IntPoint const& content_p m_client.async_did_request_link_context_menu(content_position, url, target, modifiers); } -template -static ResponseType spin_event_loop_until_dialog_closed(ConnectionFromClient& client, Optional& response, SourceLocation location = SourceLocation::current()) -{ - auto& event_loop = Web::HTML::current_settings_object().responsible_event_loop(); - - ScopeGuard guard { [&] { event_loop.set_execution_paused(false); } }; - event_loop.set_execution_paused(true); - - Web::Platform::EventLoopPlugin::the().spin_until([&]() { - return response.has_value() || !client.is_open(); - }); - - if (!client.is_open()) { - dbgln("WebContent client disconnected during {}. Exiting peacefully.", location.function_name()); - exit(0); - } - - return response.release_value(); -} - void PageHost::page_did_request_alert(String const& message) { - m_pending_dialog = PendingDialog::Alert; m_client.async_did_request_alert(message); - - if (!message.is_empty()) - m_pending_dialog_text = message; - - spin_event_loop_until_dialog_closed(m_client, m_pending_alert_response); } void PageHost::alert_closed() { - if (m_pending_dialog == PendingDialog::Alert) { - m_pending_dialog = PendingDialog::None; - m_pending_alert_response = Empty {}; - m_pending_dialog_text.clear(); - } + page().alert_closed(); } -bool PageHost::page_did_request_confirm(String const& message) +void PageHost::page_did_request_confirm(String const& message) { - m_pending_dialog = PendingDialog::Confirm; m_client.async_did_request_confirm(message); - - if (!message.is_empty()) - m_pending_dialog_text = message; - - return spin_event_loop_until_dialog_closed(m_client, m_pending_confirm_response); } void PageHost::confirm_closed(bool accepted) { - if (m_pending_dialog == PendingDialog::Confirm) { - m_pending_dialog = PendingDialog::None; - m_pending_confirm_response = accepted; - m_pending_dialog_text.clear(); - } + page().confirm_closed(accepted); } -String PageHost::page_did_request_prompt(String const& message, String const& default_) +void PageHost::page_did_request_prompt(String const& message, String const& default_) { - m_pending_dialog = PendingDialog::Prompt; m_client.async_did_request_prompt(message, default_); - - if (!message.is_empty()) - m_pending_dialog_text = message; - - return spin_event_loop_until_dialog_closed(m_client, m_pending_prompt_response); } void PageHost::prompt_closed(String response) { - if (m_pending_dialog == PendingDialog::Prompt) { - m_pending_dialog = PendingDialog::None; - m_pending_prompt_response = move(response); - m_pending_dialog_text.clear(); - } + page().prompt_closed(move(response)); } -void PageHost::dismiss_dialog() +void PageHost::page_did_request_accept_dialog() { - switch (m_pending_dialog) { - case PendingDialog::None: - break; - case PendingDialog::Alert: - m_client.async_did_request_accept_dialog(); - break; - case PendingDialog::Confirm: - case PendingDialog::Prompt: - m_client.async_did_request_dismiss_dialog(); - break; - } + m_client.async_did_request_accept_dialog(); } -void PageHost::accept_dialog() +void PageHost::page_did_request_dismiss_dialog() { - switch (m_pending_dialog) { - case PendingDialog::None: - break; - case PendingDialog::Alert: - case PendingDialog::Confirm: - case PendingDialog::Prompt: - m_client.async_did_request_accept_dialog(); - break; - } + m_client.async_did_request_dismiss_dialog(); } void PageHost::page_did_change_favicon(Gfx::Bitmap const& favicon) diff --git a/Userland/Services/WebContent/PageHost.h b/Userland/Services/WebContent/PageHost.h index a81ad3fe42..ed70439f00 100644 --- a/Userland/Services/WebContent/PageHost.h +++ b/Userland/Services/WebContent/PageHost.h @@ -47,20 +47,9 @@ public: void confirm_closed(bool accepted); void prompt_closed(String response); - enum class PendingDialog { - None, - Alert, - Confirm, - Prompt, - }; - bool has_pending_dialog() const { return m_pending_dialog != PendingDialog::None; } - PendingDialog pending_dialog() const { return m_pending_dialog; } - Optional const& pending_dialog_text() const { return m_pending_dialog_text; } - void dismiss_dialog(); - void accept_dialog(); - private: // ^PageClient + virtual bool is_connection_open() const override; virtual Gfx::Palette palette() const override; virtual Gfx::IntRect screen_rect() const override { return m_screen_rect; } virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override { return m_preferred_color_scheme; } @@ -84,8 +73,10 @@ private: virtual void page_did_create_main_document() override; virtual void page_did_finish_loading(const URL&) override; virtual void page_did_request_alert(String const&) override; - virtual bool page_did_request_confirm(String const&) override; - virtual String page_did_request_prompt(String const&, String const&) override; + virtual void page_did_request_confirm(String const&) override; + virtual void page_did_request_prompt(String const&, String const&) override; + virtual void page_did_request_accept_dialog() override; + virtual void page_did_request_dismiss_dialog() override; virtual void page_did_change_favicon(Gfx::Bitmap const&) override; virtual void page_did_request_image_context_menu(Gfx::IntPoint const&, const URL&, String const& target, unsigned modifiers, Gfx::Bitmap const*) override; virtual String page_did_request_cookie(const URL&, Web::Cookie::Source) override; @@ -111,12 +102,6 @@ private: Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto }; RefPtr m_webdriver; - - PendingDialog m_pending_dialog { PendingDialog::None }; - Optional m_pending_dialog_text; - Optional m_pending_alert_response; - Optional m_pending_confirm_response; - Optional m_pending_prompt_response; }; } diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index 36a8711ac0..cb09130c91 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -1389,11 +1389,11 @@ Messages::WebDriverClient::DismissAlertResponse WebDriverConnection::dismiss_ale TRY(ensure_open_top_level_browsing_context()); // 2. If there is no current user prompt, return error with error code no such alert. - if (!m_page_host.has_pending_dialog()) + if (!m_page_host.page().has_pending_dialog()) return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchAlert, "No user dialog is currently open"sv); // 3. Dismiss the current user prompt. - m_page_host.dismiss_dialog(); + m_page_host.page().dismiss_dialog(); // 4. Return success with data null. return JsonValue {}; @@ -1406,11 +1406,11 @@ Messages::WebDriverClient::AcceptAlertResponse WebDriverConnection::accept_alert TRY(ensure_open_top_level_browsing_context()); // 2. If there is no current user prompt, return error with error code no such alert. - if (!m_page_host.has_pending_dialog()) + if (!m_page_host.page().has_pending_dialog()) return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchAlert, "No user dialog is currently open"sv); // 3. Accept the current user prompt. - m_page_host.accept_dialog(); + m_page_host.page().accept_dialog(); // 4. Return success with data null. return JsonValue {}; @@ -1423,11 +1423,11 @@ Messages::WebDriverClient::GetAlertTextResponse WebDriverConnection::get_alert_t TRY(ensure_open_top_level_browsing_context()); // 2. If there is no current user prompt, return error with error code no such alert. - if (!m_page_host.has_pending_dialog()) + if (!m_page_host.page().has_pending_dialog()) return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchAlert, "No user dialog is currently open"sv); // 3. Let message be the text message associated with the current user prompt, or otherwise be null. - auto const& message = m_page_host.pending_dialog_text(); + auto const& message = m_page_host.page().pending_dialog_text(); // 4. Return success with data message. if (message.has_value()) @@ -1446,20 +1446,20 @@ Messages::WebDriverClient::SendAlertTextResponse WebDriverConnection::send_alert TRY(ensure_open_top_level_browsing_context()); // 4. If there is no current user prompt, return error with error code no such alert. - if (!m_page_host.has_pending_dialog()) + if (!m_page_host.page().has_pending_dialog()) return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchAlert, "No user dialog is currently open"sv); // 5. Run the substeps of the first matching current user prompt: - switch (m_page_host.pending_dialog()) { + switch (m_page_host.page().pending_dialog()) { // -> alert // -> confirm - case PageHost::PendingDialog::Alert: - case PageHost::PendingDialog::Confirm: + case Web::Page::PendingDialog::Alert: + case Web::Page::PendingDialog::Confirm: // Return error with error code element not interactable. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ElementNotInteractable, "Only prompt dialogs may receive text"sv); // -> prompt - case PageHost::PendingDialog::Prompt: + case Web::Page::PendingDialog::Prompt: // Do nothing. break; @@ -1547,7 +1547,7 @@ ErrorOr WebDriverConnection::ensure_open_top_level_ ErrorOr WebDriverConnection::handle_any_user_prompts() { // 1. If there is no current user prompt, abort these steps and return success. - if (!m_page_host.has_pending_dialog()) + if (!m_page_host.page().has_pending_dialog()) return {}; // 2. Perform the following substeps based on the current session’s user prompt handler: @@ -1555,19 +1555,19 @@ ErrorOr WebDriverConnection::handle_any_user_prompt // -> dismiss state case Web::WebDriver::UnhandledPromptBehavior::Dismiss: // Dismiss the current user prompt. - m_page_host.dismiss_dialog(); + m_page_host.page().dismiss_dialog(); break; // -> accept state case Web::WebDriver::UnhandledPromptBehavior::Accept: // Accept the current user prompt. - m_page_host.accept_dialog(); + m_page_host.page().accept_dialog(); break; // -> dismiss and notify state case Web::WebDriver::UnhandledPromptBehavior::DismissAndNotify: // Dismiss the current user prompt. - m_page_host.dismiss_dialog(); + m_page_host.page().dismiss_dialog(); // Return an annotated unexpected alert open error. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnexpectedAlertOpen, "A user dialog is open"sv); @@ -1575,7 +1575,7 @@ ErrorOr WebDriverConnection::handle_any_user_prompt // -> accept and notify state case Web::WebDriver::UnhandledPromptBehavior::AcceptAndNotify: // Accept the current user prompt. - m_page_host.accept_dialog(); + m_page_host.page().accept_dialog(); // Return an annotated unexpected alert open error. return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnexpectedAlertOpen, "A user dialog is open"sv); diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index 986387ebcf..8721a90be0 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -108,6 +108,11 @@ public: } // ^Web::PageClient + virtual bool is_connection_open() const override + { + return true; + } + virtual Gfx::Palette palette() const override { return Gfx::Palette(*m_palette_impl); @@ -199,14 +204,12 @@ public: { } - virtual bool page_did_request_confirm(String const&) override + virtual void page_did_request_confirm(String const&) override { - return false; } - virtual String page_did_request_prompt(String const&, String const&) override + virtual void page_did_request_prompt(String const&, String const&) override { - return String::empty(); } virtual String page_did_request_cookie(AK::URL const&, Web::Cookie::Source) override