2023-11-29 13:34:38 -03:00
|
|
|
/*
|
2024-10-04 08:19:50 -03:00
|
|
|
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
|
2023-11-29 13:34:38 -03:00
|
|
|
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
|
|
|
|
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
2024-08-23 07:18:35 -03:00
|
|
|
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
2023-11-29 13:34:38 -03:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2025-03-06 19:32:43 -03:00
|
|
|
#include <AK/JsonObjectSerializer.h>
|
2025-02-24 13:57:33 -03:00
|
|
|
#include <AK/JsonValue.h>
|
2026-04-29 10:57:40 -03:00
|
|
|
#include <AK/Math.h>
|
2026-06-04 05:40:53 -03:00
|
|
|
#include <LibCore/Process.h>
|
2025-03-14 17:22:16 -03:00
|
|
|
#include <LibCore/Timer.h>
|
2026-06-11 12:51:05 -03:00
|
|
|
#include <LibDevTools/IndexedDBSerialization.h>
|
2025-11-23 09:07:35 -03:00
|
|
|
#include <LibGfx/Bitmap.h>
|
2023-11-29 13:34:38 -03:00
|
|
|
#include <LibGfx/ShareableBitmap.h>
|
2026-02-07 13:13:47 -03:00
|
|
|
#include <LibHTTP/Cookie/ParsedCookie.h>
|
2026-03-11 16:10:08 -03:00
|
|
|
#include <LibIPC/TransportHandle.h>
|
2024-02-02 22:00:48 -03:00
|
|
|
#include <LibJS/Console.h>
|
|
|
|
|
#include <LibJS/Runtime/ConsoleObject.h>
|
2023-12-04 05:57:13 -03:00
|
|
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
2024-08-23 06:42:35 -03:00
|
|
|
#include <LibWeb/CSS/CSSImportRule.h>
|
2026-06-03 10:38:44 -03:00
|
|
|
#include <LibWeb/CSS/StyleScope.h>
|
2026-06-04 08:41:20 -03:00
|
|
|
#include <LibWeb/CSS/StyleSheetIdentifier.h>
|
2026-02-11 03:33:58 -03:00
|
|
|
#include <LibWeb/CSS/StyleSheetList.h>
|
2025-03-06 19:32:43 -03:00
|
|
|
#include <LibWeb/DOM/CharacterData.h>
|
2026-02-08 13:19:13 -03:00
|
|
|
#include <LibWeb/DOM/Document.h>
|
2025-03-06 19:32:43 -03:00
|
|
|
#include <LibWeb/DOM/Element.h>
|
|
|
|
|
#include <LibWeb/DOM/MutationType.h>
|
|
|
|
|
#include <LibWeb/DOM/NodeList.h>
|
2026-02-11 03:33:58 -03:00
|
|
|
#include <LibWeb/HTML/BrowsingContext.h>
|
LibWeb+LibWebView+WebContent: Recover after Compositor process crashes
The browser previously treated the out-of-process Compositor as fatal.
Restart the shared Compositor from the browser process, reconnect
process-backed WebContent clients, recreate compositor contexts, restore
viewport state, and ask WebContent to repaint and republish canvas and
media resources. WebContent now marks its compositor connection lost,
returns conservative values for synchronous compositor queries while
reconnecting, and drops outgoing updates until the replacement transport
arrives.
Synchronous input queries through the compositor control connection now
use fallible IPC. If the Compositor exits after the open check or before
the sync reply arrives, scroll and mouse handling report that the
Compositor did not handle the event and let the normal WebContent
fallback run.
Mouse events queued while the Compositor is unavailable now fall back to
direct WebContent dispatch. This keeps input completion in step with the
pending-event queue.
Recovery is capped at three automatic restarts. If the restart limit is
exceeded, if restart, reconnect, or context recreation fails, or if the
replacement Compositor exits during active recovery, the browser crashes
instead of switching process-backed views to a fallback path.
2026-05-23 22:36:24 -03:00
|
|
|
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
2026-06-01 08:27:46 -03:00
|
|
|
#include <LibWeb/HTML/Navigable.h>
|
2024-02-02 22:00:48 -03:00
|
|
|
#include <LibWeb/HTML/Scripting/ClassicScript.h>
|
2026-06-10 12:37:14 -03:00
|
|
|
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
2023-11-29 13:34:38 -03:00
|
|
|
#include <LibWeb/HTML/TraversableNavigable.h>
|
2026-06-13 11:06:49 -03:00
|
|
|
#include <LibWeb/HTML/Window.h>
|
2026-04-29 10:57:40 -03:00
|
|
|
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
|
2026-02-05 14:02:28 -03:00
|
|
|
#include <LibWeb/InvalidateDisplayList.h>
|
2023-11-29 13:34:38 -03:00
|
|
|
#include <LibWeb/Layout/Viewport.h>
|
|
|
|
|
#include <LibWeb/Painting/PaintableBox.h>
|
2026-06-10 12:37:14 -03:00
|
|
|
#include <LibWeb/WebIDL/Promise.h>
|
2025-03-09 12:40:34 -03:00
|
|
|
#include <LibWebView/SiteIsolation.h>
|
2026-02-01 15:27:19 -03:00
|
|
|
#include <LibWebView/ViewImplementation.h>
|
2023-11-29 13:34:38 -03:00
|
|
|
#include <WebContent/ConnectionFromClient.h>
|
2025-02-24 11:48:13 -03:00
|
|
|
#include <WebContent/DevToolsConsoleClient.h>
|
2023-11-29 13:34:38 -03:00
|
|
|
#include <WebContent/PageClient.h>
|
|
|
|
|
#include <WebContent/PageHost.h>
|
|
|
|
|
#include <WebContent/WebContentClientEndpoint.h>
|
|
|
|
|
#include <WebContent/WebDriverConnection.h>
|
2025-03-24 10:27:36 -03:00
|
|
|
#include <WebContent/WebUIConnection.h>
|
2023-11-29 13:34:38 -03:00
|
|
|
|
|
|
|
|
namespace WebContent {
|
|
|
|
|
|
2024-12-09 18:51:19 -03:00
|
|
|
static bool s_is_headless { false };
|
2026-05-11 16:42:02 -03:00
|
|
|
static bool s_async_scrolling_enabled { false };
|
2026-06-13 11:06:49 -03:00
|
|
|
static bool s_should_report_session_history_updates_in_test_mode { false };
|
2023-11-29 13:34:38 -03:00
|
|
|
|
2024-11-14 12:01:23 -03:00
|
|
|
GC_DEFINE_ALLOCATOR(PageClient);
|
2024-04-06 14:16:18 -03:00
|
|
|
|
2026-05-27 08:37:28 -03:00
|
|
|
static String serialize_dom_mutation_target(Web::DOM::Node const& target)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder builder;
|
|
|
|
|
auto serializer = MUST(JsonObjectSerializer<>::try_create(builder));
|
|
|
|
|
target.serialize_tree_as_json(serializer);
|
|
|
|
|
MUST(serializer.finish());
|
|
|
|
|
return MUST(builder.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 18:51:19 -03:00
|
|
|
bool PageClient::is_headless() const
|
|
|
|
|
{
|
|
|
|
|
return s_is_headless;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_is_headless(bool is_headless)
|
|
|
|
|
{
|
|
|
|
|
s_is_headless = is_headless;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 16:42:02 -03:00
|
|
|
void PageClient::set_async_scrolling_enabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
s_async_scrolling_enabled = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
void PageClient::set_should_report_session_history_updates_in_test_mode(bool should_report)
|
|
|
|
|
{
|
|
|
|
|
s_should_report_session_history_updates_in_test_mode = should_report;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-14 12:01:23 -03:00
|
|
|
GC::Ref<PageClient> PageClient::create(JS::VM& vm, PageHost& page_host, u64 id)
|
2023-12-04 05:40:33 -03:00
|
|
|
{
|
2024-11-13 14:13:46 -03:00
|
|
|
return vm.heap().allocate<PageClient>(page_host, id);
|
2023-12-04 05:40:33 -03:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
PageClient::PageClient(PageHost& owner, u64 id)
|
|
|
|
|
: m_owner(owner)
|
2023-12-04 05:57:13 -03:00
|
|
|
, m_page(Web::Page::create(Web::Bindings::main_thread_vm(), *this))
|
2023-11-29 13:34:38 -03:00
|
|
|
, m_id(id)
|
|
|
|
|
{
|
2026-05-11 16:42:02 -03:00
|
|
|
m_page->set_async_scrolling_enabled(s_async_scrolling_enabled);
|
2023-11-29 13:34:38 -03:00
|
|
|
setup_palette();
|
|
|
|
|
|
2026-04-29 10:57:40 -03:00
|
|
|
m_frame_timer = Core::Timer::create_single_shot(0, [this] {
|
|
|
|
|
m_last_frame_dispatch_time = Web::HighResolutionTime::unsafe_shared_current_time();
|
2024-10-03 12:44:57 -03:00
|
|
|
Web::HTML::main_thread_event_loop().queue_task_to_update_the_rendering();
|
|
|
|
|
});
|
2023-12-21 18:58:54 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-03 12:44:57 -03:00
|
|
|
PageClient::~PageClient() = default;
|
|
|
|
|
|
2023-12-04 05:57:13 -03:00
|
|
|
void PageClient::visit_edges(JS::Cell::Visitor& visitor)
|
|
|
|
|
{
|
|
|
|
|
Base::visit_edges(visitor);
|
|
|
|
|
visitor.visit(m_page);
|
2026-01-29 08:50:20 -03:00
|
|
|
visitor.visit(m_top_level_document_console_client);
|
2026-06-10 12:37:14 -03:00
|
|
|
for (auto& promise : m_pending_delete_all_cookies_promises)
|
|
|
|
|
visitor.visit(promise.value);
|
2026-05-27 08:37:28 -03:00
|
|
|
m_pending_dom_mutations.for_each([&](auto& pending_mutation) {
|
|
|
|
|
visitor.visit(pending_mutation.target);
|
|
|
|
|
});
|
2024-10-17 19:50:36 -03:00
|
|
|
|
|
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->visit_edges(visitor);
|
2025-03-24 10:27:36 -03:00
|
|
|
if (m_web_ui)
|
|
|
|
|
m_web_ui->visit_edges(visitor);
|
2023-12-04 05:57:13 -03:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
ConnectionFromClient& PageClient::client() const
|
|
|
|
|
{
|
|
|
|
|
return m_owner.client();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_has_focus(bool has_focus)
|
|
|
|
|
{
|
2026-06-02 19:27:42 -03:00
|
|
|
if (m_has_focus == has_focus)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
m_has_focus = has_focus;
|
2026-06-02 19:27:42 -03:00
|
|
|
|
|
|
|
|
if (auto document = page().top_level_traversable()->active_document()) {
|
|
|
|
|
if (has_focus)
|
|
|
|
|
document->reset_cursor_blink_cycle();
|
|
|
|
|
document->set_cursor_position_needs_repaint();
|
|
|
|
|
}
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
void PageClient::set_window_handle(String window_handle)
|
|
|
|
|
{
|
|
|
|
|
page().top_level_traversable()->set_window_handle(move(window_handle));
|
|
|
|
|
|
|
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_set_window_handle({}, page().top_level_traversable()->window_handle());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::did_start_webdriver_navigation(URL::URL const& url)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_start_webdriver_navigation(m_id, url);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
void PageClient::setup_palette()
|
|
|
|
|
{
|
|
|
|
|
// FIXME: Get the proper palette from our peer somehow
|
|
|
|
|
auto buffer_or_error = Core::AnonymousBuffer::create_with_size(sizeof(Gfx::SystemTheme));
|
|
|
|
|
VERIFY(!buffer_or_error.is_error());
|
|
|
|
|
auto buffer = buffer_or_error.release_value();
|
|
|
|
|
auto* theme = buffer.data<Gfx::SystemTheme>();
|
2024-05-24 00:07:39 -03:00
|
|
|
theme->color[to_underlying(Gfx::ColorRole::Window)] = Color(Color::Magenta).value();
|
|
|
|
|
theme->color[to_underlying(Gfx::ColorRole::WindowText)] = Color(Color::Cyan).value();
|
2023-11-29 13:34:38 -03:00
|
|
|
m_palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PageClient::is_connection_open() const
|
|
|
|
|
{
|
|
|
|
|
return client().is_open();
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-09 12:40:34 -03:00
|
|
|
bool PageClient::is_url_suitable_for_same_process_navigation(URL::URL const& current_url, URL::URL const& target_url) const
|
|
|
|
|
{
|
|
|
|
|
return WebView::is_url_suitable_for_same_process_navigation(current_url, target_url);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
void PageClient::request_new_process_for_navigation(URL::URL const& url, Variant<Empty, String, Web::HTML::POSTResource> document_resource, Web::Bindings::NavigationHistoryBehavior history_handling)
|
2025-03-09 12:40:34 -03:00
|
|
|
{
|
2026-06-13 11:06:49 -03:00
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_start_window_replacement({}, page().top_level_traversable()->window_handle());
|
|
|
|
|
|
|
|
|
|
client().async_did_request_new_process_for_navigation(m_id, url, move(document_resource), history_handling);
|
2025-03-09 12:40:34 -03:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
Gfx::Palette PageClient::palette() const
|
|
|
|
|
{
|
|
|
|
|
return Gfx::Palette(*m_palette_impl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_palette_impl(Gfx::PaletteImpl& impl)
|
|
|
|
|
{
|
|
|
|
|
m_palette_impl = impl;
|
2026-05-25 16:22:50 -03:00
|
|
|
if (auto* document = page().top_level_browsing_context().active_document()) {
|
2024-09-04 05:01:08 -03:00
|
|
|
document->invalidate_style(Web::DOM::StyleInvalidationReason::SettingsChange);
|
2026-05-25 16:22:50 -03:00
|
|
|
document->set_needs_media_query_evaluation();
|
|
|
|
|
}
|
|
|
|
|
request_frame();
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_preferred_color_scheme(Web::CSS::PreferredColorScheme color_scheme)
|
|
|
|
|
{
|
|
|
|
|
m_preferred_color_scheme = color_scheme;
|
2025-08-05 11:56:05 -03:00
|
|
|
if (auto* document = page().top_level_browsing_context().active_document()) {
|
2024-09-04 05:01:08 -03:00
|
|
|
document->invalidate_style(Web::DOM::StyleInvalidationReason::SettingsChange);
|
2025-08-05 11:56:05 -03:00
|
|
|
document->set_needs_media_query_evaluation();
|
|
|
|
|
}
|
2024-06-12 20:03:56 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_preferred_contrast(Web::CSS::PreferredContrast contrast)
|
|
|
|
|
{
|
|
|
|
|
m_preferred_contrast = contrast;
|
2025-08-05 11:56:05 -03:00
|
|
|
if (auto* document = page().top_level_browsing_context().active_document()) {
|
2024-09-04 05:01:08 -03:00
|
|
|
document->invalidate_style(Web::DOM::StyleInvalidationReason::SettingsChange);
|
2025-08-05 11:56:05 -03:00
|
|
|
document->set_needs_media_query_evaluation();
|
|
|
|
|
}
|
2024-06-13 11:15:59 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_preferred_motion(Web::CSS::PreferredMotion motion)
|
|
|
|
|
{
|
|
|
|
|
m_preferred_motion = motion;
|
2025-08-05 11:56:05 -03:00
|
|
|
if (auto* document = page().top_level_browsing_context().active_document()) {
|
2024-09-04 05:01:08 -03:00
|
|
|
document->invalidate_style(Web::DOM::StyleInvalidationReason::SettingsChange);
|
2025-08-05 11:56:05 -03:00
|
|
|
document->set_needs_media_query_evaluation();
|
|
|
|
|
}
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_is_scripting_enabled(bool is_scripting_enabled)
|
|
|
|
|
{
|
|
|
|
|
page().set_is_scripting_enabled(is_scripting_enabled);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_window_position(Web::DevicePixelPoint position)
|
|
|
|
|
{
|
|
|
|
|
page().set_window_position(position);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_window_size(Web::DevicePixelSize size)
|
|
|
|
|
{
|
|
|
|
|
page().set_window_size(size);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 13:53:42 -03:00
|
|
|
void PageClient::compositor_process_lost()
|
|
|
|
|
{
|
|
|
|
|
page().notify_all_webgl_contexts_lost();
|
|
|
|
|
}
|
|
|
|
|
|
LibWeb+LibWebView+WebContent: Recover after Compositor process crashes
The browser previously treated the out-of-process Compositor as fatal.
Restart the shared Compositor from the browser process, reconnect
process-backed WebContent clients, recreate compositor contexts, restore
viewport state, and ask WebContent to repaint and republish canvas and
media resources. WebContent now marks its compositor connection lost,
returns conservative values for synchronous compositor queries while
reconnecting, and drops outgoing updates until the replacement transport
arrives.
Synchronous input queries through the compositor control connection now
use fallible IPC. If the Compositor exits after the open check or before
the sync reply arrives, scroll and mouse handling report that the
Compositor did not handle the event and let the normal WebContent
fallback run.
Mouse events queued while the Compositor is unavailable now fall back to
direct WebContent dispatch. This keeps input completion in step with the
pending-event queue.
Recovery is capped at three automatic restarts. If the restart limit is
exceeded, if restart, reconnect, or context recreation fails, or if the
replacement Compositor exits during active recovery, the browser crashes
instead of switching process-backed views to a fallback path.
2026-05-23 22:36:24 -03:00
|
|
|
void PageClient::compositor_process_reconnected()
|
|
|
|
|
{
|
|
|
|
|
page().top_level_traversable()->repaint_after_compositor_process_reconnect();
|
2026-06-15 13:53:42 -03:00
|
|
|
page().notify_all_canvas_elements_of_lost_backing_storage();
|
2026-06-15 14:37:51 -03:00
|
|
|
page().prepare_canvas_contexts_for_compositing();
|
LibWeb+LibWebView+WebContent: Recover after Compositor process crashes
The browser previously treated the out-of-process Compositor as fatal.
Restart the shared Compositor from the browser process, reconnect
process-backed WebContent clients, recreate compositor contexts, restore
viewport state, and ask WebContent to repaint and republish canvas and
media resources. WebContent now marks its compositor connection lost,
returns conservative values for synchronous compositor queries while
reconnecting, and drops outgoing updates until the replacement transport
arrives.
Synchronous input queries through the compositor control connection now
use fallible IPC. If the Compositor exits after the open check or before
the sync reply arrives, scroll and mouse handling report that the
Compositor did not handle the event and let the normal WebContent
fallback run.
Mouse events queued while the Compositor is unavailable now fall back to
direct WebContent dispatch. This keeps input completion in step with the
pending-event queue.
Recovery is capped at three automatic restarts. If the restart limit is
exceeded, if restart, reconnect, or context recreation fails, or if the
replacement Compositor exits during active recovery, the browser crashes
instead of switching process-backed views to a fallback path.
2026-05-23 22:36:24 -03:00
|
|
|
page().update_all_media_element_video_sinks();
|
|
|
|
|
Web::HTML::main_thread_event_loop().queue_task_to_update_the_rendering();
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-14 19:25:12 -03:00
|
|
|
Queue<Web::QueuedInputEvent>& PageClient::input_event_queue()
|
|
|
|
|
{
|
|
|
|
|
return client().input_event_queue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::report_finished_handling_input_event(u64 page_id, Web::EventResult event_was_handled)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_finish_handling_input_event(page_id, event_was_handled);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 09:15:34 -03:00
|
|
|
Web::Compositor::CompositorContextId PageClient::allocate_compositor_context_id(Web::Compositor::PagePresentationRegistration page_presentation_registration)
|
|
|
|
|
{
|
|
|
|
|
return client().allocate_compositor_context_id(m_id, page_presentation_registration);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 18:09:39 -03:00
|
|
|
void PageClient::set_viewport(Web::DevicePixelSize const& size, double device_pixel_ratio)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2026-02-20 18:09:39 -03:00
|
|
|
auto invalidate = m_device_pixel_ratio != device_pixel_ratio
|
|
|
|
|
? Web::InvalidateDisplayList::Yes
|
|
|
|
|
: Web::InvalidateDisplayList::No;
|
|
|
|
|
|
2026-02-17 21:06:44 -03:00
|
|
|
m_viewport_size = size;
|
2026-02-20 18:09:39 -03:00
|
|
|
m_device_pixel_ratio = device_pixel_ratio;
|
|
|
|
|
|
|
|
|
|
page().top_level_traversable()->set_viewport_size(page().device_to_css_size(size), invalidate);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-20 18:09:39 -03:00
|
|
|
void PageClient::set_zoom_level(double zoom_level)
|
2026-02-05 14:02:28 -03:00
|
|
|
{
|
2026-02-20 18:09:39 -03:00
|
|
|
m_zoom_level = zoom_level;
|
2026-02-25 13:09:59 -03:00
|
|
|
page().top_level_traversable()->set_viewport_size(page().device_to_css_size(m_viewport_size), Web::InvalidateDisplayList::Yes);
|
2026-02-05 14:02:28 -03:00
|
|
|
}
|
|
|
|
|
|
2026-04-29 10:57:40 -03:00
|
|
|
void PageClient::request_frame()
|
2025-07-25 09:10:00 -03:00
|
|
|
{
|
2026-04-29 10:57:40 -03:00
|
|
|
if (m_frame_timer->is_active())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto delay = 0.0;
|
|
|
|
|
if (m_last_frame_dispatch_time.has_value()) {
|
|
|
|
|
auto now = Web::HighResolutionTime::unsafe_shared_current_time();
|
|
|
|
|
auto minimum_frame_interval = 1000.0 / m_maximum_frames_per_second;
|
|
|
|
|
delay = max(0.0, *m_last_frame_dispatch_time + minimum_frame_interval - now);
|
|
|
|
|
}
|
2025-07-25 09:10:00 -03:00
|
|
|
|
2026-04-29 10:57:40 -03:00
|
|
|
m_frame_timer->restart(static_cast<int>(AK::ceil(delay)));
|
|
|
|
|
}
|
2025-07-25 09:10:00 -03:00
|
|
|
|
2026-04-29 10:57:40 -03:00
|
|
|
void PageClient::set_maximum_frames_per_second(double maximum_frames_per_second)
|
|
|
|
|
{
|
|
|
|
|
m_maximum_frames_per_second = maximum_frames_per_second;
|
2025-07-25 09:10:00 -03:00
|
|
|
}
|
|
|
|
|
|
2025-02-20 09:17:29 -03:00
|
|
|
void PageClient::page_did_request_cursor_change(Gfx::Cursor const& cursor)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-02-20 09:17:29 -03:00
|
|
|
client().async_did_request_cursor_change(m_id, cursor);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2025-07-28 17:51:01 -03:00
|
|
|
void PageClient::page_did_change_title(Utf16String const& title)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_change_title(m_id, title);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-04-14 05:27:20 -03:00
|
|
|
void PageClient::page_did_change_url(URL::URL const& url)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_change_url(m_id, url);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
void PageClient::page_did_request_refresh()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_refresh(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 00:37:11 -03:00
|
|
|
void PageClient::page_did_request_resize_window(Gfx::IntSize size)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-10-29 00:37:11 -03:00
|
|
|
client().async_did_request_resize_window(m_id, size);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 00:37:11 -03:00
|
|
|
void PageClient::page_did_request_reposition_window(Gfx::IntPoint position)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-10-29 00:37:11 -03:00
|
|
|
client().async_did_request_reposition_window(m_id, position);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_restore_window()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_restore_window(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 00:45:18 -03:00
|
|
|
void PageClient::page_did_request_maximize_window()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-10-29 00:45:18 -03:00
|
|
|
client().async_did_request_maximize_window(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 00:45:18 -03:00
|
|
|
void PageClient::page_did_request_minimize_window()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-10-29 00:45:18 -03:00
|
|
|
client().async_did_request_minimize_window(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-29 00:45:18 -03:00
|
|
|
void PageClient::page_did_request_fullscreen_window()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-10-29 00:45:18 -03:00
|
|
|
client().async_did_request_fullscreen_window(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2025-03-27 11:59:17 -03:00
|
|
|
void PageClient::page_did_request_exit_fullscreen()
|
|
|
|
|
{
|
|
|
|
|
client().async_did_request_exit_fullscreen(m_id);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-02 11:48:57 -03:00
|
|
|
void PageClient::page_did_request_tooltip_override(Web::CSSPixelPoint position, ByteString const& title)
|
|
|
|
|
{
|
|
|
|
|
auto device_position = page().css_to_device_point(position);
|
|
|
|
|
client().async_did_request_tooltip_override(m_id, { device_position.x(), device_position.y() }, title);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_stop_tooltip_override()
|
|
|
|
|
{
|
|
|
|
|
client().async_did_leave_tooltip_area(m_id);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-02 09:16:24 -03:00
|
|
|
void PageClient::page_did_enter_tooltip_area(ByteString const& title)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-07-02 09:16:24 -03:00
|
|
|
client().async_did_enter_tooltip_area(m_id, title);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_leave_tooltip_area()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_leave_tooltip_area(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 23:02:55 -03:00
|
|
|
void PageClient::page_did_hover_link(URL::URL const& url)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_hover_link(m_id, url);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_unhover_link()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_unhover_link(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-08-01 14:38:21 -03:00
|
|
|
void PageClient::page_did_click_link(URL::URL const& url, ByteString const& target, unsigned modifiers)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_click_link(m_id, url, target, modifiers);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 14:39:57 -03:00
|
|
|
void PageClient::page_did_middle_click_link(URL::URL const& url, ByteString const& target, unsigned modifiers)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_middle_click_link(m_id, url, target, modifiers);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
void PageClient::page_did_start_loading(URL::URL const& url, Variant<Empty, String, Web::HTML::POSTResource> document_resource, bool is_redirect, Web::Bindings::NavigationHistoryBehavior history_handling)
|
|
|
|
|
{
|
|
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_start_loading({}, url);
|
|
|
|
|
|
|
|
|
|
client().async_did_start_loading(m_id, url, move(document_resource), is_redirect, history_handling);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_cancel_loading(URL::URL const& url)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2026-06-13 11:06:49 -03:00
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_cancel_loading({}, url);
|
|
|
|
|
|
|
|
|
|
client().async_did_cancel_loading(m_id, url);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_create_new_document(Web::DOM::Document& document)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
initialize_js_console(document);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-04-11 12:21:09 -03:00
|
|
|
void PageClient::page_did_change_active_document_in_top_level_browsing_context(Web::DOM::Document& document)
|
|
|
|
|
{
|
2024-07-31 13:14:53 -03:00
|
|
|
auto& realm = document.realm();
|
|
|
|
|
|
2026-05-27 08:37:28 -03:00
|
|
|
clear_pending_dom_mutations();
|
2025-03-24 10:27:36 -03:00
|
|
|
m_web_ui.clear();
|
|
|
|
|
|
2024-07-31 14:18:44 -03:00
|
|
|
if (auto console_client = document.console_client()) {
|
2025-01-21 11:12:05 -03:00
|
|
|
auto& web_content_console_client = as<WebContentConsoleClient>(*console_client);
|
2024-07-31 14:18:44 -03:00
|
|
|
m_top_level_document_console_client = web_content_console_client;
|
2024-07-31 13:14:53 -03:00
|
|
|
|
|
|
|
|
auto console_object = realm.intrinsics().console_object();
|
|
|
|
|
console_object->console().set_client(*console_client);
|
2024-07-31 14:18:44 -03:00
|
|
|
}
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-03-28 23:02:55 -03:00
|
|
|
void PageClient::page_did_finish_loading(URL::URL const& url)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_finish_loading(m_id, url);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
void PageClient::wait_for_webdriver_navigation_completion(Optional<u64> page_load_timeout, Function<void(Web::WebDriver::Response)> on_complete)
|
|
|
|
|
{
|
|
|
|
|
auto request_id = m_next_webdriver_navigation_completion_request_id++;
|
|
|
|
|
m_pending_webdriver_navigation_completion_requests.set(request_id, move(on_complete));
|
|
|
|
|
client().async_did_request_webdriver_navigation_completion(m_id, request_id, page_load_timeout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::did_complete_webdriver_navigation_completion(u64 request_id, Web::WebDriver::Response response)
|
|
|
|
|
{
|
|
|
|
|
auto maybe_callback = m_pending_webdriver_navigation_completion_requests.take(request_id);
|
|
|
|
|
if (!maybe_callback.has_value())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
maybe_callback.value()(move(response));
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-18 13:37:32 -03:00
|
|
|
void PageClient::page_did_finish_test(String const& text)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-03-18 13:37:32 -03:00
|
|
|
client().async_did_finish_test(m_id, text);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-12-19 11:16:05 -03:00
|
|
|
void PageClient::page_did_set_test_timeout(double milliseconds)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_set_test_timeout(m_id, milliseconds);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-16 05:24:15 -03:00
|
|
|
void PageClient::page_did_receive_reference_test_metadata(JsonValue metadata)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_receive_reference_test_metadata(m_id, metadata);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-23 18:15:06 -03:00
|
|
|
void PageClient::page_did_set_browser_zoom(double factor)
|
|
|
|
|
{
|
2025-06-26 17:33:58 -03:00
|
|
|
auto traversable = page().top_level_traversable();
|
|
|
|
|
traversable->set_pending_set_browser_zoom_request(true);
|
2024-12-23 18:15:06 -03:00
|
|
|
client().async_did_set_browser_zoom(m_id, factor);
|
|
|
|
|
auto& event_loop = Web::HTML::main_thread_event_loop();
|
2025-06-26 17:33:58 -03:00
|
|
|
event_loop.spin_until(GC::create_function(event_loop.heap(), [this, traversable]() {
|
|
|
|
|
return !traversable->pending_set_browser_zoom_request() || !is_connection_open();
|
2024-12-23 18:15:06 -03:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 14:02:28 -03:00
|
|
|
void PageClient::page_did_set_device_pixel_ratio_for_testing(double ratio)
|
|
|
|
|
{
|
2026-02-20 18:09:39 -03:00
|
|
|
set_viewport(m_viewport_size, ratio);
|
2026-02-05 14:02:28 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-06 13:00:59 -03:00
|
|
|
void PageClient::page_did_request_context_menu(Web::CSSPixelPoint content_position, Web::ContextMenuForInputEventsTarget for_input_events_target)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2026-05-06 13:00:59 -03:00
|
|
|
client().async_did_request_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>(), for_input_events_target);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-03-18 00:22:27 -03:00
|
|
|
void PageClient::page_did_request_link_context_menu(Web::CSSPixelPoint content_position, URL::URL const& url, ByteString const& target, unsigned modifiers)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_link_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>(), url, target, modifiers);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2025-01-10 06:33:27 -03:00
|
|
|
void PageClient::page_did_request_image_context_menu(Web::CSSPixelPoint content_position, URL::URL const& url, ByteString const& target, unsigned modifiers, Optional<Gfx::Bitmap const*> bitmap_pointer)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-01-10 06:33:27 -03:00
|
|
|
Optional<Gfx::ShareableBitmap> bitmap;
|
|
|
|
|
if (bitmap_pointer.has_value() && bitmap_pointer.value())
|
|
|
|
|
bitmap = bitmap_pointer.value()->to_shareable_bitmap();
|
|
|
|
|
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_image_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>(), url, target, modifiers, bitmap);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2025-03-08 12:41:16 -03:00
|
|
|
void PageClient::page_did_request_media_context_menu(Web::CSSPixelPoint content_position, ByteString const& target, unsigned modifiers, Web::Page::MediaContextMenu const& menu)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-03-08 12:41:16 -03:00
|
|
|
client().async_did_request_media_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>(), target, modifiers, menu);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_alert(String const& message)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_alert(m_id, message);
|
2024-10-25 11:28:29 -03:00
|
|
|
|
|
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_open_dialog({});
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::alert_closed()
|
|
|
|
|
{
|
|
|
|
|
page().alert_closed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_confirm(String const& message)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_confirm(m_id, message);
|
2024-10-25 11:28:29 -03:00
|
|
|
|
|
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_open_dialog({});
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::confirm_closed(bool accepted)
|
|
|
|
|
{
|
|
|
|
|
page().confirm_closed(accepted);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_prompt(String const& message, String const& default_)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_prompt(m_id, message, default_);
|
2024-10-25 11:28:29 -03:00
|
|
|
|
|
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_open_dialog({});
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_set_prompt_text(String const& text)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_set_prompt_text(m_id, text);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::prompt_closed(Optional<String> response)
|
|
|
|
|
{
|
|
|
|
|
page().prompt_closed(move(response));
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-11 02:53:10 -03:00
|
|
|
void PageClient::color_picker_update(Optional<Color> picked_color, Web::HTML::ColorPickerUpdateState state)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2023-12-11 02:53:10 -03:00
|
|
|
page().color_picker_update(picked_color, state);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-04-03 14:19:08 -03:00
|
|
|
void PageClient::select_dropdown_closed(Optional<u32> const& selected_item_id)
|
2023-12-07 11:53:49 -03:00
|
|
|
{
|
2024-04-03 14:19:08 -03:00
|
|
|
page().select_dropdown_closed(selected_item_id);
|
2023-12-07 11:53:49 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-31 09:30:47 -03:00
|
|
|
void PageClient::toggle_media_play_state()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-10-31 09:30:47 -03:00
|
|
|
page().toggle_media_play_state();
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::toggle_media_mute_state()
|
|
|
|
|
{
|
|
|
|
|
page().toggle_media_mute_state();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 09:30:47 -03:00
|
|
|
void PageClient::toggle_media_loop_state()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-10-31 09:30:47 -03:00
|
|
|
page().toggle_media_loop_state();
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2025-10-31 09:30:47 -03:00
|
|
|
void PageClient::toggle_media_controls_state()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-10-31 09:30:47 -03:00
|
|
|
page().toggle_media_controls_state();
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::set_user_style(String source)
|
|
|
|
|
{
|
|
|
|
|
page().set_user_style(source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_accept_dialog()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_accept_dialog(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_dismiss_dialog()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_dismiss_dialog(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_change_favicon(Gfx::Bitmap const& favicon)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_change_favicon(m_id, favicon.to_shareable_bitmap());
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-01 15:27:19 -03:00
|
|
|
Optional<Core::SharedVersion> PageClient::page_did_request_document_cookie_version(Core::SharedVersionIndex document_index)
|
|
|
|
|
{
|
|
|
|
|
return Core::get_shared_version(m_document_cookie_version_buffer, document_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_receive_document_cookie_version_buffer(Core::AnonymousBuffer document_cookie_version_buffer)
|
|
|
|
|
{
|
|
|
|
|
m_document_cookie_version_buffer = move(document_cookie_version_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_document_cookie_version_index(Web::UniqueNodeID document_id, String const& domain)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: Support transferring DistinctNumeric over IPC.
|
|
|
|
|
client().async_did_request_document_cookie_version_index(m_id, document_id.value(), domain);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_receive_document_cookie_version_index(Web::UniqueNodeID document_id, Core::SharedVersionIndex document_index)
|
|
|
|
|
{
|
|
|
|
|
if (auto* document = as_if<Web::DOM::Document>(Web::DOM::Node::from_unique_id(document_id)))
|
|
|
|
|
document->set_cookie_version_index(document_index);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 13:13:47 -03:00
|
|
|
Vector<HTTP::Cookie::Cookie> PageClient::page_did_request_all_cookies_webdriver(URL::URL const& url)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-08-05 17:05:45 -03:00
|
|
|
return client().did_request_all_cookies_webdriver(url);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 13:13:47 -03:00
|
|
|
Vector<HTTP::Cookie::Cookie> PageClient::page_did_request_all_cookies_cookiestore(URL::URL const& url)
|
2025-08-05 17:05:45 -03:00
|
|
|
{
|
|
|
|
|
return client().did_request_all_cookies_cookiestore(url);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-07 13:13:47 -03:00
|
|
|
Optional<HTTP::Cookie::Cookie> PageClient::page_did_request_named_cookie(URL::URL const& url, String const& name)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-09-22 14:25:18 -03:00
|
|
|
return client().did_request_named_cookie(url, name);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-07 13:13:47 -03:00
|
|
|
HTTP::Cookie::VersionedCookie PageClient::page_did_request_cookie(URL::URL const& url, HTTP::Cookie::Source source)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2026-02-01 15:27:19 -03:00
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestCookie>(m_id, url, source);
|
2023-11-29 13:34:38 -03:00
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidRequestCookie. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
return response->take_cookie();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 13:13:47 -03:00
|
|
|
void PageClient::page_did_set_cookie(URL::URL const& url, HTTP::Cookie::ParsedCookie const& cookie, HTTP::Cookie::Source source)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-09-22 14:25:18 -03:00
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidSetCookie>(url, cookie, source);
|
2024-01-10 14:23:00 -03:00
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidSetCookie. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2024-01-10 14:23:00 -03:00
|
|
|
}
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-02-07 13:13:47 -03:00
|
|
|
void PageClient::page_did_update_cookie(HTTP::Cookie::Cookie const& cookie)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-03-08 12:41:16 -03:00
|
|
|
client().async_did_update_cookie(cookie);
|
2026-02-01 15:27:19 -03:00
|
|
|
|
|
|
|
|
// Since the above (test-only) IPC is async, we reset the document cookie version now to avoid a stale cache.
|
|
|
|
|
if (auto* document = page().top_level_browsing_context().active_document())
|
|
|
|
|
document->reset_cookie_version();
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-13 09:56:46 -03:00
|
|
|
void PageClient::page_did_expire_cookies_with_time_offset(AK::Duration offset)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_expire_cookies_with_time_offset(offset);
|
2026-02-01 15:27:19 -03:00
|
|
|
|
|
|
|
|
// Since the above (test-only) IPC is async, we reset the document cookie version now to avoid a stale cache.
|
|
|
|
|
if (auto* document = page().top_level_browsing_context().active_document())
|
|
|
|
|
document->reset_cookie_version();
|
2024-10-13 09:56:46 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-10 12:37:14 -03:00
|
|
|
void PageClient::page_did_delete_all_cookies(URL::URL const& url, GC::Ref<Web::WebIDL::Promise> promise)
|
|
|
|
|
{
|
|
|
|
|
auto request_id = m_next_delete_all_cookies_request_id++;
|
|
|
|
|
m_pending_delete_all_cookies_promises.set(request_id, promise);
|
|
|
|
|
client().async_did_request_delete_all_cookies(m_id, request_id, url);
|
|
|
|
|
|
|
|
|
|
if (auto* document = page().top_level_browsing_context().active_document())
|
|
|
|
|
document->reset_cookie_version();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::did_delete_all_cookies(u64 request_id)
|
|
|
|
|
{
|
|
|
|
|
auto maybe_promise = m_pending_delete_all_cookies_promises.take(request_id);
|
|
|
|
|
if (!maybe_promise.has_value())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto promise = maybe_promise.release_value();
|
|
|
|
|
auto& realm = promise->promise()->shape().realm();
|
|
|
|
|
Web::HTML::TemporaryExecutionContext execution_context { realm, Web::HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
|
|
|
|
Web::WebIDL::resolve_promise(realm, promise);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:04:33 -03:00
|
|
|
void PageClient::page_did_store_hsts_policy(String const& domain, HTTP::HSTS::ParsedHSTSPolicy const& policy)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_store_hsts_policy(domain, policy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool PageClient::page_did_is_known_hsts_host(String const& domain)
|
|
|
|
|
{
|
|
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidIsKnownHstsHost>(domain);
|
|
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidIsKnownHstsHost. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2026-03-26 14:04:33 -03:00
|
|
|
}
|
|
|
|
|
return response->result();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-08 18:35:46 -03:00
|
|
|
Optional<String> PageClient::page_did_request_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key)
|
|
|
|
|
{
|
|
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestStorageItem>(storage_endpoint, storage_key, bottle_key);
|
|
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidRequestStorageItem. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2025-06-08 18:35:46 -03:00
|
|
|
}
|
|
|
|
|
return response->take_value();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-26 13:36:55 -03:00
|
|
|
WebView::StorageSetResult PageClient::page_did_set_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key, String const& value)
|
2025-06-08 18:35:46 -03:00
|
|
|
{
|
|
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidSetStorageItem>(storage_endpoint, storage_key, bottle_key, value);
|
|
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidSetStorageItem. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2025-06-08 18:35:46 -03:00
|
|
|
}
|
2025-12-26 13:36:55 -03:00
|
|
|
return response->result();
|
2025-06-08 18:35:46 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_remove_storage_item(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key, String const& bottle_key)
|
|
|
|
|
{
|
|
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRemoveStorageItem>(storage_endpoint, storage_key, bottle_key);
|
|
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidRemoveStorageItem. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2025-06-08 18:35:46 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<String> PageClient::page_did_request_storage_keys(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key)
|
|
|
|
|
{
|
|
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestStorageKeys>(storage_endpoint, storage_key);
|
|
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidRequestStorageKeys. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2025-06-08 18:35:46 -03:00
|
|
|
}
|
|
|
|
|
return response->take_keys();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_clear_storage(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& storage_key)
|
|
|
|
|
{
|
|
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidClearStorage>(storage_endpoint, storage_key);
|
|
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidClearStorage. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2025-06-08 18:35:46 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 10:12:37 -03:00
|
|
|
void PageClient::page_did_broadcast_storage_change(Web::StorageAPI::StorageEndpointType storage_endpoint, String const& url, Optional<String> const& key, Optional<String> const& old_value, Optional<String> const& new_value)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_change_storage_item(m_id, storage_endpoint, url, key, old_value, new_value);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-11 12:51:05 -03:00
|
|
|
void PageClient::page_did_update_indexed_database(String const& url, Web::IndexedDB::TransactionChanges const& changes)
|
|
|
|
|
{
|
|
|
|
|
if (!has_devtools_client())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto update = DevTools::IndexedDB::serialize_update(url, changes);
|
|
|
|
|
if (update.is_empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
client().async_did_update_indexed_database(m_id, update.serialized());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 16:18:00 -03:00
|
|
|
void PageClient::page_did_post_broadcast_channel_message(Web::HTML::BroadcastChannelMessage const& message)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_post_broadcast_channel_message(m_id, message);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
void PageClient::page_did_update_resource_count(i32 count_waiting)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_update_resource_count(m_id, count_waiting);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-01-31 00:55:24 -03:00
|
|
|
PageClient::NewWebViewResult PageClient::page_did_request_new_web_view(Web::HTML::ActivateTab activate_tab, Web::HTML::WebViewHints hints, Web::HTML::TokenizedFeature::NoOpener no_opener)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2024-01-31 00:55:24 -03:00
|
|
|
if (no_opener == Web::HTML::TokenizedFeature::NoOpener::Yes) {
|
|
|
|
|
// FIXME: Create an abstraction to let this WebContent process know about a new process we create?
|
|
|
|
|
// FIXME: For now, just create a new page in the same process anyway
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-30 11:58:13 -03:00
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestNewWebView>(m_id, activate_tab, hints);
|
2024-01-31 00:05:00 -03:00
|
|
|
if (!response) {
|
|
|
|
|
dbgln("WebContent client disconnected during DidRequestNewWebView. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2024-01-31 00:05:00 -03:00
|
|
|
}
|
2024-01-31 00:55:24 -03:00
|
|
|
|
2026-05-30 11:58:13 -03:00
|
|
|
auto& new_client = m_owner.create_page(response->new_page_id());
|
2024-01-31 00:55:24 -03:00
|
|
|
return { &new_client.page(), response->take_handle() };
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_activate_tab()
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_activate_tab(m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2024-02-02 22:00:48 -03:00
|
|
|
void PageClient::page_did_close_top_level_traversable()
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2026-06-18 01:10:37 -03:00
|
|
|
page().top_level_traversable()->compositor_context().stop_presenting_to_client();
|
2026-05-11 16:02:29 -03:00
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
if (m_webdriver)
|
|
|
|
|
m_webdriver->page_did_close_window({}, page().top_level_traversable()->window_handle());
|
|
|
|
|
|
2024-02-02 22:00:48 -03:00
|
|
|
// FIXME: Rename this IPC call
|
|
|
|
|
client().async_did_close_browsing_context(m_id);
|
|
|
|
|
|
|
|
|
|
// NOTE: This only removes the strong reference the PageHost has for this PageClient.
|
|
|
|
|
// It will be GC'd 'later'.
|
|
|
|
|
m_owner.remove_page({}, m_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-26 13:03:07 -03:00
|
|
|
void PageClient::page_did_change_needs_beforeunload_check(bool needs_beforeunload_check)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_change_needs_beforeunload_check(m_id, needs_beforeunload_check);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::send_current_needs_beforeunload_check()
|
|
|
|
|
{
|
|
|
|
|
client().async_did_change_needs_beforeunload_check(m_id, page().needs_beforeunload_check());
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 18:12:55 -03:00
|
|
|
void PageClient::page_did_update_navigation_buttons_state(bool back_enabled, bool forward_enabled)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_update_navigation_buttons_state(m_id, back_enabled, forward_enabled);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
bool PageClient::should_report_session_history_updates() const
|
|
|
|
|
{
|
|
|
|
|
return !Web::HTML::Window::in_test_mode() || s_should_report_session_history_updates_in_test_mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_update_session_history(Vector<Web::HTML::SessionHistoryEntryDescriptor> const& entries, Vector<i32> const& used_steps, size_t current_used_step_index)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_update_session_history(m_id, entries, used_steps, current_used_step_index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String PageClient::page_did_request_ui_process_session_history_for_testing()
|
|
|
|
|
{
|
|
|
|
|
return client().did_request_ui_process_session_history_for_testing(m_id);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 06:27:47 -03:00
|
|
|
String PageClient::page_did_update_session_history_and_request_ui_process_session_history_for_testing(Vector<Web::HTML::SessionHistoryEntryDescriptor> const& entries, Vector<i32> const& used_steps, size_t current_used_step_index)
|
|
|
|
|
{
|
|
|
|
|
return client().did_update_session_history_and_request_ui_process_session_history_for_testing(m_id, entries, used_steps, current_used_step_index);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-13 11:06:49 -03:00
|
|
|
bool PageClient::page_did_request_traverse_the_history_by_delta(int delta, Web::HistoryTraversalPrecheck history_traversal_precheck)
|
|
|
|
|
{
|
|
|
|
|
return client().did_request_traverse_the_history_by_delta(m_id, delta, history_traversal_precheck);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::request_webdriver_history_traversal(int delta, Function<void(WebDriverHistoryTraversalResult)> on_complete)
|
|
|
|
|
{
|
|
|
|
|
auto request_id = m_next_webdriver_history_traversal_request_id++;
|
|
|
|
|
m_pending_webdriver_history_traversal_requests.set(request_id, move(on_complete));
|
|
|
|
|
client().async_did_request_webdriver_history_traversal(m_id, request_id, delta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::did_complete_webdriver_history_traversal(u64 request_id, bool accepted, bool will_replace_web_content_process, bool will_change_top_level_entry)
|
|
|
|
|
{
|
|
|
|
|
auto maybe_callback = m_pending_webdriver_history_traversal_requests.take(request_id);
|
|
|
|
|
if (!maybe_callback.has_value())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
maybe_callback.value()(WebDriverHistoryTraversalResult {
|
|
|
|
|
.accepted = accepted,
|
|
|
|
|
.will_replace_web_content_process = will_replace_web_content_process,
|
|
|
|
|
.will_change_top_level_entry = will_change_top_level_entry,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Web::WebDriver::Response PageClient::request_webdriver_load_url_from_ui(URL::URL const& url)
|
|
|
|
|
{
|
|
|
|
|
return client().did_request_webdriver_load_url_from_ui(m_id, url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Web::WebDriver::Response PageClient::request_webdriver_traverse_history_from_ui(int delta)
|
|
|
|
|
{
|
|
|
|
|
return client().did_request_webdriver_traverse_history_from_ui(m_id, delta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Web::WebDriver::Response PageClient::request_webdriver_mark_web_content_session_history_stale()
|
|
|
|
|
{
|
|
|
|
|
return client().did_request_webdriver_mark_web_content_session_history_stale(m_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Web::WebDriver::Response PageClient::request_webdriver_session_history()
|
|
|
|
|
{
|
|
|
|
|
return client().did_request_webdriver_session_history(m_id);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
void PageClient::request_file(Web::FileRequest file_request)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().request_file(m_id, move(file_request));
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_color_picker(Color current_color)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_color_picker(m_id, current_color);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2025-03-08 12:41:16 -03:00
|
|
|
void PageClient::page_did_request_file_picker(Web::HTML::FileFilter const& accepted_file_types, Web::HTML::AllowMultipleFiles allow_multiple_files)
|
2024-02-25 15:02:47 -03:00
|
|
|
{
|
2025-03-08 12:41:16 -03:00
|
|
|
client().async_did_request_file_picker(m_id, accepted_file_types, allow_multiple_files);
|
2024-02-25 15:02:47 -03:00
|
|
|
}
|
|
|
|
|
|
2023-12-12 18:35:37 -03:00
|
|
|
void PageClient::page_did_request_select_dropdown(Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items)
|
2023-12-07 11:53:49 -03:00
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_request_select_dropdown(m_id, page().css_to_device_point(content_position).to_type<int>(), minimum_width * device_pixels_per_css_pixel(), items);
|
2023-12-07 11:53:49 -03:00
|
|
|
}
|
|
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
void PageClient::page_did_change_theme_color(Gfx::Color color)
|
|
|
|
|
{
|
2024-02-02 22:00:48 -03:00
|
|
|
client().async_did_change_theme_color(m_id, color);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-24 15:44:30 -03:00
|
|
|
void PageClient::page_did_change_background_color(Gfx::Color color)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_change_background_color(m_id, color);
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-01 12:38:31 -03:00
|
|
|
void PageClient::page_did_insert_clipboard_entry(Web::Clipboard::SystemClipboardRepresentation const& entry, StringView presentation_style)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
2025-05-01 12:38:31 -03:00
|
|
|
client().async_did_insert_clipboard_entry(m_id, entry, presentation_style);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_request_clipboard_entries(u64 request_id)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_request_clipboard_entries(m_id, request_id);
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-18 08:18:50 -03:00
|
|
|
void PageClient::page_did_request_primary_paste()
|
2026-05-15 08:02:20 -03:00
|
|
|
{
|
2026-05-18 08:18:50 -03:00
|
|
|
client().async_did_request_primary_paste(m_id);
|
2026-05-15 08:02:20 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-27 12:43:35 -03:00
|
|
|
void PageClient::page_did_update_primary_selection(String const& text)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_update_primary_selection(m_id, text);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-26 20:27:06 -03:00
|
|
|
void PageClient::page_did_change_audio_play_state(Web::HTML::AudioPlayState play_state)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_change_audio_play_state(m_id, play_state);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-21 10:33:23 -03:00
|
|
|
Web::HTML::WorkerAgentId PageClient::start_worker_agent(Web::HTML::WorkerAgentStartRequest&& request)
|
2024-01-06 17:13:59 -03:00
|
|
|
{
|
2026-05-21 10:33:23 -03:00
|
|
|
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::StartWorkerAgent>(m_id, move(request));
|
2024-01-06 17:13:59 -03:00
|
|
|
if (!response) {
|
2026-05-21 10:33:23 -03:00
|
|
|
dbgln("WebContent client disconnected during StartWorkerAgent. Exiting peacefully.");
|
2026-06-04 05:40:53 -03:00
|
|
|
Core::Process::terminate_immediately(0);
|
2024-01-06 17:13:59 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-21 10:33:23 -03:00
|
|
|
return response->agent_id();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::close_worker_agent(Web::HTML::WorkerAgentId agent_id, Web::HTML::WorkerAgentOwnerToken owner_token)
|
|
|
|
|
{
|
|
|
|
|
client().async_close_worker_agent(m_id, agent_id, owner_token);
|
2024-01-06 17:13:59 -03:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 19:32:43 -03:00
|
|
|
void PageClient::page_did_mutate_dom(FlyString const& type, Web::DOM::Node const& target, Web::DOM::NodeList& added_nodes, Web::DOM::NodeList& removed_nodes, GC::Ptr<Web::DOM::Node>, GC::Ptr<Web::DOM::Node>, Optional<String> const& attribute_name)
|
|
|
|
|
{
|
|
|
|
|
Optional<WebView::Mutation::Type> mutation;
|
|
|
|
|
|
|
|
|
|
if (type == Web::DOM::MutationType::attributes) {
|
|
|
|
|
VERIFY(attribute_name.has_value());
|
|
|
|
|
|
|
|
|
|
auto const& element = as<Web::DOM::Element>(target);
|
|
|
|
|
mutation = WebView::AttributeMutation { *attribute_name, element.attribute(*attribute_name) };
|
|
|
|
|
} else if (type == Web::DOM::MutationType::characterData) {
|
|
|
|
|
auto const& character_data = as<Web::DOM::CharacterData>(target);
|
2025-07-24 13:05:52 -03:00
|
|
|
mutation = WebView::CharacterDataMutation { character_data.data().to_utf8_but_should_be_ported_to_utf16() };
|
2025-03-06 19:32:43 -03:00
|
|
|
} else if (type == Web::DOM::MutationType::childList) {
|
|
|
|
|
Vector<Web::UniqueNodeID> added;
|
|
|
|
|
added.ensure_capacity(added_nodes.length());
|
|
|
|
|
|
|
|
|
|
Vector<Web::UniqueNodeID> removed;
|
|
|
|
|
removed.ensure_capacity(removed_nodes.length());
|
|
|
|
|
|
|
|
|
|
for (auto i = 0u; i < added_nodes.length(); ++i)
|
|
|
|
|
added.unchecked_append(added_nodes.item(i)->unique_id());
|
|
|
|
|
for (auto i = 0u; i < removed_nodes.length(); ++i)
|
|
|
|
|
removed.unchecked_append(removed_nodes.item(i)->unique_id());
|
|
|
|
|
|
|
|
|
|
mutation = WebView::ChildListMutation { move(added), move(removed), target.child_count() };
|
|
|
|
|
} else {
|
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 08:37:28 -03:00
|
|
|
auto mutation_message = WebView::Mutation { type.to_string(), target.unique_id(), {}, mutation.release_value() };
|
|
|
|
|
if (m_pending_dom_mutations.is_empty() && target.document().layout_is_up_to_date()) {
|
|
|
|
|
send_dom_mutation(target, move(mutation_message));
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-03-06 19:32:43 -03:00
|
|
|
|
2026-05-27 08:37:28 -03:00
|
|
|
m_pending_dom_mutations.enqueue({ const_cast<Web::DOM::Node&>(target), move(mutation_message) });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::flush_pending_dom_mutations()
|
|
|
|
|
{
|
|
|
|
|
if (!page().listen_for_dom_mutations()) {
|
|
|
|
|
clear_pending_dom_mutations();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!m_pending_dom_mutations.is_empty()) {
|
|
|
|
|
if (!m_pending_dom_mutations.head().target->document().layout_is_up_to_date())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
auto pending_mutation = m_pending_dom_mutations.dequeue();
|
|
|
|
|
send_dom_mutation(*pending_mutation.target, move(pending_mutation.mutation));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::clear_pending_dom_mutations()
|
|
|
|
|
{
|
|
|
|
|
m_pending_dom_mutations.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::send_dom_mutation(Web::DOM::Node const& target, WebView::Mutation mutation)
|
|
|
|
|
{
|
|
|
|
|
mutation.serialized_target = serialize_dom_mutation_target(target);
|
|
|
|
|
client().async_did_mutate_dom(m_id, move(mutation));
|
2025-03-06 19:32:43 -03:00
|
|
|
}
|
|
|
|
|
|
2025-06-26 17:33:58 -03:00
|
|
|
void PageClient::page_did_take_screenshot(Gfx::ShareableBitmap const& screenshot)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_take_screenshot(m_id, screenshot);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 15:59:08 -03:00
|
|
|
ErrorOr<void> PageClient::connect_to_webdriver(ByteString const& webdriver_endpoint)
|
2023-11-29 13:34:38 -03:00
|
|
|
{
|
|
|
|
|
VERIFY(!m_webdriver);
|
2026-03-22 15:59:08 -03:00
|
|
|
m_webdriver = TRY(WebDriverConnection::connect(*this, webdriver_endpoint));
|
2023-11-29 13:34:38 -03:00
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 16:10:08 -03:00
|
|
|
ErrorOr<void> PageClient::connect_to_web_ui(IPC::TransportHandle handle)
|
2025-03-24 10:27:36 -03:00
|
|
|
{
|
|
|
|
|
auto* active_document = page().top_level_browsing_context().active_document();
|
|
|
|
|
if (!active_document || !active_document->window())
|
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
VERIFY(!m_web_ui);
|
2026-03-11 16:10:08 -03:00
|
|
|
m_web_ui = TRY(WebUIConnection::connect(move(handle), *active_document));
|
2025-03-24 10:27:36 -03:00
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::received_message_from_web_ui(String const& name, JS::Value data)
|
|
|
|
|
{
|
|
|
|
|
if (m_web_ui)
|
|
|
|
|
m_web_ui->received_message_from_web_ui(name, data);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 19:24:51 -03:00
|
|
|
void PageClient::page_did_start_network_request(u64 request_id, URL::URL const& url, ByteString const& method, Vector<HTTP::Header> const& request_headers, ReadonlyBytes request_body, Optional<String> initiator_type)
|
2026-01-14 14:43:48 -03:00
|
|
|
{
|
2026-01-14 19:24:51 -03:00
|
|
|
client().async_did_start_network_request(m_id, request_id, url, method, request_headers, request_body, move(initiator_type));
|
2026-01-14 14:43:48 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::page_did_receive_network_response_headers(u64 request_id, u32 status_code, Optional<String> reason_phrase, Vector<HTTP::Header> const& response_headers)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_receive_network_response_headers(m_id, request_id, status_code, move(reason_phrase), response_headers);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:47:04 -03:00
|
|
|
void PageClient::page_did_receive_network_response_body(u64 request_id, ReadonlyBytes data)
|
|
|
|
|
{
|
2026-01-15 10:28:42 -03:00
|
|
|
if (!has_devtools_client())
|
|
|
|
|
return;
|
2026-01-14 16:47:04 -03:00
|
|
|
client().async_did_receive_network_response_body(m_id, request_id, data);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 10:28:42 -03:00
|
|
|
void PageClient::did_connect_devtools_client()
|
|
|
|
|
{
|
2026-06-01 08:27:46 -03:00
|
|
|
auto was_first_devtools_client = !has_devtools_client();
|
2026-01-15 10:28:42 -03:00
|
|
|
++m_devtools_client_count;
|
2026-06-01 08:27:46 -03:00
|
|
|
|
|
|
|
|
if (!was_first_devtools_client)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (auto& navigable : Web::HTML::all_navigables()) {
|
|
|
|
|
if (&navigable->page() != &page())
|
|
|
|
|
continue;
|
|
|
|
|
if (auto active_document = navigable->active_document())
|
|
|
|
|
active_document->update_layout(Web::DOM::UpdateLayoutReason::InspectDevToolsLayoutData);
|
|
|
|
|
}
|
2026-01-15 10:28:42 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::did_disconnect_devtools_client()
|
|
|
|
|
{
|
|
|
|
|
VERIFY(m_devtools_client_count > 0);
|
|
|
|
|
--m_devtools_client_count;
|
2026-06-01 08:27:46 -03:00
|
|
|
|
|
|
|
|
if (has_devtools_client())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (auto& navigable : Web::HTML::all_navigables()) {
|
|
|
|
|
if (&navigable->page() != &page())
|
|
|
|
|
continue;
|
|
|
|
|
if (auto active_document = navigable->active_document())
|
|
|
|
|
active_document->clear_devtools_layout_inspection_data();
|
|
|
|
|
}
|
2026-01-15 10:28:42 -03:00
|
|
|
}
|
|
|
|
|
|
2026-01-14 14:43:48 -03:00
|
|
|
void PageClient::page_did_finish_network_request(u64 request_id, u64 body_size, Requests::RequestTimingInfo const& timing_info, Optional<Requests::NetworkError> const& network_error)
|
|
|
|
|
{
|
|
|
|
|
client().async_did_finish_network_request(m_id, request_id, body_size, timing_info, network_error);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-02 22:00:48 -03:00
|
|
|
void PageClient::initialize_js_console(Web::DOM::Document& document)
|
|
|
|
|
{
|
2024-07-31 13:03:40 -03:00
|
|
|
if (document.is_temporary_document_for_fragment_parsing())
|
|
|
|
|
return;
|
|
|
|
|
|
2024-02-02 22:00:48 -03:00
|
|
|
auto& realm = document.realm();
|
|
|
|
|
auto console_object = realm.intrinsics().console_object();
|
2025-02-24 11:48:13 -03:00
|
|
|
|
2025-03-14 17:22:16 -03:00
|
|
|
auto console_client = DevToolsConsoleClient::create(document.realm(), console_object->console(), *this);
|
2024-07-31 14:18:44 -03:00
|
|
|
document.set_console_client(console_client);
|
2024-02-02 22:00:48 -03:00
|
|
|
}
|
|
|
|
|
|
2025-03-08 12:41:16 -03:00
|
|
|
void PageClient::did_execute_js_console_input(JsonValue const& result)
|
2025-02-24 13:57:33 -03:00
|
|
|
{
|
2025-03-08 12:41:16 -03:00
|
|
|
client().async_did_execute_js_console_input(m_id, result);
|
2025-02-24 13:57:33 -03:00
|
|
|
}
|
|
|
|
|
|
2025-02-23 10:57:40 -03:00
|
|
|
void PageClient::js_console_input(StringView js_source)
|
2024-02-02 22:00:48 -03:00
|
|
|
{
|
|
|
|
|
if (m_top_level_document_console_client)
|
|
|
|
|
m_top_level_document_console_client->handle_input(js_source);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-23 10:57:40 -03:00
|
|
|
void PageClient::run_javascript(StringView js_source)
|
2024-02-02 22:00:48 -03:00
|
|
|
{
|
|
|
|
|
auto* active_document = page().top_level_browsing_context().active_document();
|
|
|
|
|
|
|
|
|
|
if (!active_document)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// This is partially based on "execute a javascript: URL request" https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol
|
|
|
|
|
|
|
|
|
|
// Let settings be browsingContext's active document's relevant settings object.
|
|
|
|
|
auto& settings = active_document->relevant_settings_object();
|
|
|
|
|
|
|
|
|
|
// Let baseURL be settings's API base URL.
|
|
|
|
|
auto base_url = settings.api_base_url();
|
|
|
|
|
|
2026-04-03 13:12:46 -03:00
|
|
|
// Let script be the result of creating a classic script given scriptSource, settings, baseURL, and the default classic script fetch options.
|
2024-02-02 22:00:48 -03:00
|
|
|
// FIXME: This doesn't pass in "default classic script fetch options"
|
|
|
|
|
// FIXME: What should the filename be here?
|
2026-04-03 13:12:46 -03:00
|
|
|
auto script = Web::HTML::ClassicScript::create("(client connection run_javascript)", js_source, settings, move(base_url));
|
2024-02-02 22:00:48 -03:00
|
|
|
|
|
|
|
|
// Let evaluationStatus be the result of running the classic script script.
|
|
|
|
|
auto evaluation_status = script->run();
|
|
|
|
|
|
|
|
|
|
if (evaluation_status.is_error())
|
|
|
|
|
dbgln("Exception :(");
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:13:24 -03:00
|
|
|
void PageClient::did_output_js_console_message(WebView::ConsoleOutput console_output)
|
2024-02-02 22:00:48 -03:00
|
|
|
{
|
2026-01-14 16:13:24 -03:00
|
|
|
client().async_did_output_js_console_message(m_id, move(console_output));
|
2024-02-02 22:00:48 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PageClient::console_peer_did_misbehave(char const* reason)
|
|
|
|
|
{
|
|
|
|
|
client().did_misbehave(reason);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 06:42:35 -03:00
|
|
|
static void gather_style_sheets(Vector<Web::CSS::StyleSheetIdentifier>& results, Web::CSS::CSSStyleSheet& sheet)
|
|
|
|
|
{
|
2026-06-04 08:41:20 -03:00
|
|
|
if (auto identifier = Web::CSS::style_sheet_identifier_for(sheet); identifier.has_value())
|
|
|
|
|
results.append(identifier.release_value());
|
2024-08-23 06:42:35 -03:00
|
|
|
|
|
|
|
|
for (auto& import_rule : sheet.import_rules()) {
|
|
|
|
|
if (import_rule->loaded_style_sheet()) {
|
|
|
|
|
gather_style_sheets(results, *import_rule->loaded_style_sheet());
|
|
|
|
|
} else {
|
|
|
|
|
// We can gather this anyway, and hope it loads later
|
2025-03-12 15:34:23 -03:00
|
|
|
results.append({
|
|
|
|
|
.type = Web::CSS::StyleSheetIdentifier::Type::ImportRule,
|
2025-04-08 14:16:38 -03:00
|
|
|
.url = import_rule->href(),
|
2025-03-12 15:34:23 -03:00
|
|
|
});
|
2024-08-23 06:42:35 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<Web::CSS::StyleSheetIdentifier> PageClient::list_style_sheets() const
|
|
|
|
|
{
|
|
|
|
|
Vector<Web::CSS::StyleSheetIdentifier> results;
|
|
|
|
|
|
|
|
|
|
auto const* document = page().top_level_browsing_context().active_document();
|
|
|
|
|
if (document) {
|
|
|
|
|
for (auto& sheet : document->style_sheets().sheets()) {
|
|
|
|
|
gather_style_sheets(results, sheet);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// User style
|
|
|
|
|
if (page().user_style().has_value()) {
|
|
|
|
|
results.append({
|
|
|
|
|
.type = Web::CSS::StyleSheetIdentifier::Type::UserStyle,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 10:38:44 -03:00
|
|
|
Web::CSS::StyleScope::for_each_user_agent_stylesheet(document && document->in_quirks_mode(), [&](auto&, auto const& identifier) {
|
|
|
|
|
results.append(identifier);
|
2024-08-23 06:42:35 -03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-20 13:14:36 -03:00
|
|
|
void PageClient::ensure_compositor_host()
|
2026-05-18 11:00:28 -03:00
|
|
|
{
|
2026-05-24 17:01:12 -03:00
|
|
|
m_owner.ensure_compositor_host();
|
2026-05-18 11:00:28 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-20 13:14:36 -03:00
|
|
|
Web::Compositor::CompositorHost* PageClient::compositor_host()
|
2026-05-18 11:00:28 -03:00
|
|
|
{
|
2026-05-20 13:14:36 -03:00
|
|
|
return m_owner.compositor_host();
|
2026-05-18 11:00:28 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-20 13:14:36 -03:00
|
|
|
Web::Compositor::CompositorHost const* PageClient::compositor_host() const
|
2026-05-18 11:00:28 -03:00
|
|
|
{
|
2026-05-20 13:14:36 -03:00
|
|
|
return m_owner.compositor_host();
|
2026-05-18 11:00:28 -03:00
|
|
|
}
|
|
|
|
|
|
2024-10-20 05:37:44 -03:00
|
|
|
void PageClient::queue_screenshot_task(Optional<Web::UniqueNodeID> node_id)
|
2024-06-21 13:15:32 -03:00
|
|
|
{
|
2025-06-26 17:33:58 -03:00
|
|
|
page().top_level_traversable()->queue_screenshot_task(node_id);
|
2024-06-21 13:15:32 -03:00
|
|
|
}
|
2025-03-24 10:27:36 -03:00
|
|
|
|
2023-11-29 13:34:38 -03:00
|
|
|
}
|