mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 08:14:58 +00:00

...and also for hit testing, which is involved in most of them. Much of this is temporary conversions and other awkwardness, which should resolve itself as the rest of LibWeb is converted to these new types. Hopefully. :thousandyakstare:
278 lines
8.2 KiB
C++
278 lines
8.2 KiB
C++
/*
|
|
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
|
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/SourceLocation.h>
|
|
#include <LibWeb/DOM/Document.h>
|
|
#include <LibWeb/HTML/BrowsingContext.h>
|
|
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
|
#include <LibWeb/HTML/Scripting/Environments.h>
|
|
#include <LibWeb/Page/Page.h>
|
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
|
|
|
namespace Web {
|
|
|
|
Page::Page(PageClient& client)
|
|
: m_client(client)
|
|
{
|
|
m_top_level_browsing_context = JS::make_handle(*HTML::BrowsingContext::create_a_new_top_level_browsing_context(*this));
|
|
}
|
|
|
|
Page::~Page() = default;
|
|
|
|
HTML::BrowsingContext& Page::focused_context()
|
|
{
|
|
if (m_focused_context)
|
|
return *m_focused_context;
|
|
return top_level_browsing_context();
|
|
}
|
|
|
|
void Page::set_focused_browsing_context(Badge<EventHandler>, HTML::BrowsingContext& browsing_context)
|
|
{
|
|
m_focused_context = browsing_context.make_weak_ptr();
|
|
}
|
|
|
|
void Page::load(const AK::URL& url)
|
|
{
|
|
top_level_browsing_context().loader().load(url, FrameLoader::Type::Navigation);
|
|
}
|
|
|
|
void Page::load(LoadRequest& request)
|
|
{
|
|
top_level_browsing_context().loader().load(request, FrameLoader::Type::Navigation);
|
|
}
|
|
|
|
void Page::load_html(StringView html, const AK::URL& url)
|
|
{
|
|
top_level_browsing_context().loader().load_html(html, url);
|
|
}
|
|
|
|
Gfx::Palette Page::palette() const
|
|
{
|
|
return m_client.palette();
|
|
}
|
|
|
|
// https://w3c.github.io/csswg-drafts/cssom-view-1/#web-exposed-screen-area
|
|
CSSPixelRect Page::web_exposed_screen_area() const
|
|
{
|
|
auto device_pixel_rect = m_client.screen_rect();
|
|
auto scale = client().device_pixels_per_css_pixel();
|
|
return {
|
|
device_pixel_rect.x().value() / scale,
|
|
device_pixel_rect.y().value() / scale,
|
|
device_pixel_rect.width().value() / scale,
|
|
device_pixel_rect.height().value() / scale
|
|
};
|
|
}
|
|
|
|
CSS::PreferredColorScheme Page::preferred_color_scheme() const
|
|
{
|
|
return m_client.preferred_color_scheme();
|
|
}
|
|
|
|
CSSPixelPoint Page::device_to_css_point(DevicePixelPoint point) const
|
|
{
|
|
return {
|
|
point.x().value() / client().device_pixels_per_css_pixel(),
|
|
point.y().value() / client().device_pixels_per_css_pixel(),
|
|
};
|
|
}
|
|
|
|
DevicePixelPoint Page::css_to_device_point(CSSPixelPoint point) const
|
|
{
|
|
return {
|
|
point.x().value() * client().device_pixels_per_css_pixel(),
|
|
point.y().value() * client().device_pixels_per_css_pixel(),
|
|
};
|
|
}
|
|
|
|
CSSPixelRect Page::device_to_css_rect(DevicePixelRect rect) const
|
|
{
|
|
auto scale = client().device_pixels_per_css_pixel();
|
|
return {
|
|
rect.x().value() / scale,
|
|
rect.y().value() / scale,
|
|
rect.width().value() / scale,
|
|
rect.height().value() / scale
|
|
};
|
|
}
|
|
|
|
DevicePixelRect Page::enclosing_device_rect(CSSPixelRect rect) const
|
|
{
|
|
auto scale = client().device_pixels_per_css_pixel();
|
|
return {
|
|
floorf(rect.x().value() * scale),
|
|
floorf(rect.y().value() * scale),
|
|
ceilf(rect.width().value() * scale),
|
|
ceilf(rect.height().value() * scale)
|
|
};
|
|
}
|
|
|
|
DevicePixelRect Page::rounded_device_rect(CSSPixelRect rect) const
|
|
{
|
|
auto scale = client().device_pixels_per_css_pixel();
|
|
return {
|
|
roundf(rect.x().value() * scale),
|
|
roundf(rect.y().value() * scale),
|
|
roundf(rect.width().value() * scale),
|
|
roundf(rect.height().value() * scale)
|
|
};
|
|
}
|
|
|
|
bool Page::handle_mousewheel(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y)
|
|
{
|
|
return top_level_browsing_context().event_handler().handle_mousewheel(device_to_css_point(position), button, buttons, modifiers, wheel_delta_x, wheel_delta_y);
|
|
}
|
|
|
|
bool Page::handle_mouseup(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
|
{
|
|
return top_level_browsing_context().event_handler().handle_mouseup(device_to_css_point(position), button, buttons, modifiers);
|
|
}
|
|
|
|
bool Page::handle_mousedown(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
|
{
|
|
return top_level_browsing_context().event_handler().handle_mousedown(device_to_css_point(position), button, buttons, modifiers);
|
|
}
|
|
|
|
bool Page::handle_mousemove(DevicePixelPoint position, unsigned buttons, unsigned modifiers)
|
|
{
|
|
return top_level_browsing_context().event_handler().handle_mousemove(device_to_css_point(position), buttons, modifiers);
|
|
}
|
|
|
|
bool Page::handle_doubleclick(DevicePixelPoint position, unsigned button, unsigned buttons, unsigned modifiers)
|
|
{
|
|
return top_level_browsing_context().event_handler().handle_doubleclick(device_to_css_point(position), button, buttons, modifiers);
|
|
}
|
|
|
|
bool Page::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point)
|
|
{
|
|
return focused_context().event_handler().handle_keydown(key, modifiers, code_point);
|
|
}
|
|
|
|
bool Page::handle_keyup(KeyCode key, unsigned modifiers, u32 code_point)
|
|
{
|
|
return focused_context().event_handler().handle_keyup(key, modifiers, code_point);
|
|
}
|
|
|
|
HTML::BrowsingContext& Page::top_level_browsing_context()
|
|
{
|
|
return *m_top_level_browsing_context;
|
|
}
|
|
|
|
HTML::BrowsingContext const& Page::top_level_browsing_context() const
|
|
{
|
|
return *m_top_level_browsing_context;
|
|
}
|
|
|
|
template<typename ResponseType>
|
|
static ResponseType spin_event_loop_until_dialog_closed(PageClient& client, Optional<ResponseType>& 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(DeprecatedString 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(DeprecatedString 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();
|
|
}
|
|
}
|
|
|
|
DeprecatedString Page::did_request_prompt(DeprecatedString const& message, DeprecatedString 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(DeprecatedString 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;
|
|
}
|
|
}
|
|
|
|
}
|