LibWeb+WebContent: Move scrollbar painting into WebContent

The main intention of this change is to have a consistent look and
behavior across all scrollbars, including elements with
`overflow: scroll` and `overflow: auto`, iframes, and a page.

Before:
- Page's scrollbar is painted by Browser (Qt/AppKit) using the
  corresponding UI framework style,
- Both WebContent and Browser know the scroll position offset.
- WebContent uses did_request_scroll_to() IPC call to send updates.
- Browser uses set_viewport_rect() to send updates.

After:
- Page's scrollbar is painted on WebContent side using the same style as
  currently used for elements with `overflow: scroll` and
  `overflow: auto`. A nice side effects: scrollbars are now painted for
  iframes, and page's scrollbar respects scrollbar-width CSS property.
- Only WebContent knows scroll position offset.
- did_request_scroll_to() is no longer used.
- set_viewport_rect() is changed to set_viewport_size().
This commit is contained in:
Aliaksandr Kalenik 2024-06-03 17:53:55 +03:00 committed by Andreas Kling
parent eb909118bf
commit 5285e22f2a
Notes: sideshowbarker 2024-07-17 01:28:15 +09:00
30 changed files with 147 additions and 186 deletions

View file

@ -351,22 +351,6 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
self.event_being_redispatched = nil;
};
m_web_view_bridge->on_scroll = [self](auto position) {
auto content_rect = [self frame];
auto document_rect = [[self documentView] frame];
auto ns_position = Ladybird::gfx_point_to_ns_point(position);
ns_position.x = max(ns_position.x, document_rect.origin.x);
ns_position.x = min(ns_position.x, document_rect.size.width - content_rect.size.width);
ns_position.y = max(ns_position.y, document_rect.origin.y);
ns_position.y = min(ns_position.y, document_rect.size.height - content_rect.size.height);
[self scrollToPoint:ns_position];
[[self scrollView] reflectScrolledClipView:self];
[self updateViewportRect:Ladybird::WebViewBridge::ForResize::No];
};
m_web_view_bridge->on_cursor_change = [self](auto cursor) {
if (cursor == Gfx::StandardCursor::Hidden) {
if (!m_hidden_cursor.has_value()) {

View file

@ -35,20 +35,6 @@ WebViewBridge::WebViewBridge(Vector<Web::DevicePixelRect> screen_rects, float de
, m_preferred_color_scheme(preferred_color_scheme)
{
m_device_pixel_ratio = device_pixel_ratio;
on_scroll_by_delta = [this](auto x_delta, auto y_delta) {
auto position = m_viewport_rect.location();
position.set_x(position.x() + x_delta);
position.set_y(position.y() + y_delta);
if (on_scroll_to_point)
on_scroll_to_point(position);
};
on_scroll_to_point = [this](auto position) {
if (on_scroll)
on_scroll(to_widget_position(position));
};
}
WebViewBridge::~WebViewBridge() = default;
@ -67,9 +53,9 @@ void WebViewBridge::set_system_visibility_state(bool is_visible)
void WebViewBridge::set_viewport_rect(Gfx::IntRect viewport_rect, ForResize for_resize)
{
viewport_rect.set_size(scale_for_device(viewport_rect.size(), m_device_pixel_ratio));
m_viewport_rect = viewport_rect;
m_viewport_size = viewport_rect.size();
client().async_set_viewport_rect(m_client_state.page_index, m_viewport_rect.to_type<Web::DevicePixels>());
client().async_set_viewport_size(m_client_state.page_index, m_viewport_size.to_type<Web::DevicePixels>());
if (for_resize == ForResize::Yes) {
handle_resize();
@ -126,9 +112,9 @@ void WebViewBridge::update_zoom()
on_zoom_level_changed();
}
Web::DevicePixelRect WebViewBridge::viewport_rect() const
Web::DevicePixelSize WebViewBridge::viewport_size() const
{
return m_viewport_rect.to_type<Web::DevicePixels>();
return m_viewport_size.to_type<Web::DevicePixels>();
}
Gfx::IntPoint WebViewBridge::to_content_position(Gfx::IntPoint widget_position) const

View file

@ -53,18 +53,17 @@ public:
Function<NonnullRefPtr<WebView::WebContentClient>()> on_request_web_content;
Function<void()> on_zoom_level_changed;
Function<void(Gfx::IntPoint)> on_scroll;
private:
WebViewBridge(Vector<Web::DevicePixelRect> screen_rects, float device_pixel_ratio, WebContentOptions const&, Optional<StringView> webdriver_content_ipc_path, Web::CSS::PreferredColorScheme);
virtual void update_zoom() override;
virtual Web::DevicePixelRect viewport_rect() const override;
virtual Web::DevicePixelSize viewport_size() const override;
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const override;
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const override;
Vector<Web::DevicePixelRect> m_screen_rects;
Gfx::IntRect m_viewport_rect;
Gfx::IntSize m_viewport_size;
WebContentOptions m_web_content_options;
Optional<StringView> m_webdriver_content_ipc_path;

View file

@ -91,8 +91,8 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
[self.search_panel setHidden:YES];
auto* scroll_view = [[NSScrollView alloc] init];
[scroll_view setHasVerticalScroller:YES];
[scroll_view setHasHorizontalScroller:YES];
[scroll_view setHasVerticalScroller:NO];
[scroll_view setHasHorizontalScroller:NO];
[scroll_view setLineScroll:24];
[scroll_view setContentView:self.web_view];

View file

@ -55,6 +55,9 @@ WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_con
, m_web_content_options(web_content_options)
, m_webdriver_content_ipc_path(webdriver_content_ipc_path)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_client_state.client = parent_client;
m_client_state.page_index = page_index;
@ -68,13 +71,6 @@ WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_con
verticalScrollBar()->setSingleStep(24);
horizontalScrollBar()->setSingleStep(24);
QObject::connect(verticalScrollBar(), &QScrollBar::valueChanged, [this](int) {
update_viewport_rect();
});
QObject::connect(horizontalScrollBar(), &QScrollBar::valueChanged, [this](int) {
update_viewport_rect();
});
QObject::connect(qGuiApp, &QGuiApplication::screenRemoved, [this](QScreen*) {
update_screen_rects();
});
@ -85,29 +81,10 @@ WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_con
initialize_client((parent_client == nullptr) ? CreateNewClient::Yes : CreateNewClient::No);
on_did_layout = [this](auto content_size) {
verticalScrollBar()->setMinimum(0);
verticalScrollBar()->setMaximum(content_size.height() - m_viewport_rect.height());
verticalScrollBar()->setPageStep(m_viewport_rect.height());
horizontalScrollBar()->setMinimum(0);
horizontalScrollBar()->setMaximum(content_size.width() - m_viewport_rect.width());
horizontalScrollBar()->setPageStep(m_viewport_rect.width());
};
on_ready_to_paint = [this]() {
viewport()->update();
};
on_scroll_by_delta = [this](auto x_delta, auto y_delta) {
horizontalScrollBar()->setValue(max(0, horizontalScrollBar()->value() + x_delta));
verticalScrollBar()->setValue(max(0, verticalScrollBar()->value() + y_delta));
};
on_scroll_to_point = [this](auto position) {
horizontalScrollBar()->setValue(position.x());
verticalScrollBar()->setValue(position.y());
};
on_cursor_change = [this](auto cursor) {
update_cursor(cursor);
};
@ -445,14 +422,14 @@ void WebContentView::paintEvent(QPaintEvent*)
void WebContentView::resizeEvent(QResizeEvent* event)
{
QAbstractScrollArea::resizeEvent(event);
update_viewport_rect();
update_viewport_size();
handle_resize();
}
void WebContentView::set_viewport_rect(Gfx::IntRect rect)
{
m_viewport_rect = rect;
client().async_set_viewport_rect(m_client_state.page_index, rect.to_type<Web::DevicePixels>());
m_viewport_size = rect.size();
client().async_set_viewport_size(m_client_state.page_index, rect.size().to_type<Web::DevicePixels>());
}
void WebContentView::set_window_size(Gfx::IntSize size)
@ -469,15 +446,15 @@ void WebContentView::set_device_pixel_ratio(double device_pixel_ratio)
{
m_device_pixel_ratio = device_pixel_ratio;
client().async_set_device_pixels_per_css_pixel(m_client_state.page_index, m_device_pixel_ratio * m_zoom_level);
update_viewport_rect();
update_viewport_size();
handle_resize();
}
void WebContentView::update_viewport_rect()
void WebContentView::update_viewport_size()
{
auto scaled_width = int(viewport()->width() * m_device_pixel_ratio);
auto scaled_height = int(viewport()->height() * m_device_pixel_ratio);
Gfx::IntRect rect(max(0, horizontalScrollBar()->value()), max(0, verticalScrollBar()->value()), scaled_width, scaled_height);
Gfx::IntRect rect(0, 0, scaled_width, scaled_height);
set_viewport_rect(rect);
}
@ -485,7 +462,7 @@ void WebContentView::update_viewport_rect()
void WebContentView::update_zoom()
{
client().async_set_device_pixels_per_css_pixel(m_client_state.page_index, m_device_pixel_ratio * m_zoom_level);
update_viewport_rect();
update_viewport_size();
}
void WebContentView::showEvent(QShowEvent* event)
@ -661,9 +638,9 @@ void WebContentView::update_cursor(Gfx::StandardCursor cursor)
}
}
Web::DevicePixelRect WebContentView::viewport_rect() const
Web::DevicePixelSize WebContentView::viewport_size() const
{
return m_viewport_rect.to_type<Web::DevicePixels>();
return m_viewport_size.to_type<Web::DevicePixels>();
}
QPoint WebContentView::map_point_to_global_position(Gfx::IntPoint position) const
@ -673,12 +650,12 @@ QPoint WebContentView::map_point_to_global_position(Gfx::IntPoint position) cons
Gfx::IntPoint WebContentView::to_content_position(Gfx::IntPoint widget_position) const
{
return widget_position.translated(max(0, horizontalScrollBar()->value()), max(0, verticalScrollBar()->value()));
return widget_position;
}
Gfx::IntPoint WebContentView::to_widget_position(Gfx::IntPoint content_position) const
{
return content_position.translated(-(max(0, horizontalScrollBar()->value())), -(max(0, verticalScrollBar()->value())));
return content_position;
}
bool WebContentView::event(QEvent* event)
@ -715,7 +692,7 @@ ErrorOr<String> WebContentView::dump_layout_tree()
void WebContentView::enqueue_native_event(Web::MouseEvent::Type type, QSinglePointEvent const& event)
{
auto position = to_content_position({ event.position().x() * m_device_pixel_ratio, event.position().y() * m_device_pixel_ratio });
Web::DevicePixelPoint position = { event.position().x() * m_device_pixel_ratio, event.position().y() * m_device_pixel_ratio };
auto screen_position = Gfx::IntPoint { event.globalPosition().x() * m_device_pixel_ratio, event.globalPosition().y() * m_device_pixel_ratio };
auto button = get_button_from_qt_event(event);
@ -751,7 +728,7 @@ void WebContentView::enqueue_native_event(Web::MouseEvent::Type type, QSinglePoi
}
}
enqueue_input_event(Web::MouseEvent { type, position.to_type<Web::DevicePixels>(), screen_position.to_type<Web::DevicePixels>(), button, buttons, modifiers, wheel_delta_x, wheel_delta_y, nullptr });
enqueue_input_event(Web::MouseEvent { type, position, screen_position.to_type<Web::DevicePixels>(), button, buttons, modifiers, wheel_delta_x, wheel_delta_y, nullptr });
}
struct KeyData : Web::ChromeInputData {

View file

@ -90,11 +90,11 @@ private:
// ^WebView::ViewImplementation
virtual void initialize_client(CreateNewClient) override;
virtual void update_zoom() override;
virtual Web::DevicePixelRect viewport_rect() const override;
virtual Web::DevicePixelSize viewport_size() const override;
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const override;
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const override;
void update_viewport_rect();
void update_viewport_size();
void update_cursor(Gfx::StandardCursor cursor);
void enqueue_native_event(Web::MouseEvent::Type, QSinglePointEvent const& event);
@ -104,7 +104,7 @@ private:
bool m_should_show_line_box_borders { false };
Gfx::IntRect m_viewport_rect;
Gfx::IntSize m_viewport_size;
WebContentOptions m_web_content_options;
StringView m_webdriver_content_ipc_path;

View file

@ -7,6 +7,10 @@
<title>Document</title>
<style>
html {
scrollbar-width: none;
}
p, .container {
border: 0.8em darkviolet;
border-style: dotted double;

View file

@ -5,6 +5,9 @@
body {
background-color: white;
}
html {
scrollbar-width: none;
}
</style>
<!-- To rebase:
1. Open background-clip-text.html in Ladybird

View file

@ -11,7 +11,10 @@
<script>
const iframe = document.createElement("iframe");
iframe.srcdoc = `
<style>body { margin: 0 }</style>
<style>
body { margin: 0 }
html { scrollbar-width: none; }
</style>
<div style="width: 200px; height: 200px; background-color: darkblue"></div>
<div style="width: 200px; height: 200px; background-color: blue"></div>
<div style="width: 200px; height: 200px; background-color: magenta"></div>

View file

@ -1146,6 +1146,11 @@ void Document::update_layout()
paintable()->recompute_selection_states();
m_needs_layout = false;
// Scrolling by zero offset will clamp scroll offset back to valid range if it was out of bounds
// after the viewport size change.
if (auto window = this->window())
window->scroll_by(0, 0);
}
[[nodiscard]] static CSS::RequiredInvalidationAfterStyleChange update_style_recursively(Node& node, CSS::StyleComputer& style_computer)

View file

@ -890,7 +890,7 @@ JS::NonnullGCPtr<Geometry::DOMRectList> Element::get_client_rects() const
if (auto const* paintable_box = this->paintable_box()) {
transform = Gfx::extract_2d_affine_transform(paintable_box->transform());
for (auto const* containing_block = paintable->containing_block(); containing_block; containing_block = containing_block->containing_block()) {
for (auto const* containing_block = paintable->containing_block(); !containing_block->is_viewport(); containing_block = containing_block->containing_block()) {
transform = Gfx::extract_2d_affine_transform(containing_block->transform()).multiply(transform);
scroll_offset.translate_by(containing_block->scroll_offset());
}

View file

@ -1993,60 +1993,36 @@ CSSPixelPoint Navigable::to_top_level_position(CSSPixelPoint a_position)
return position;
}
void Navigable::set_viewport_rect(CSSPixelRect const& rect)
{
bool did_change = false;
if (m_size != rect.size()) {
m_size = rect.size();
if (auto document = active_document()) {
// NOTE: Resizing the viewport changes the reference value for viewport-relative CSS lengths.
document->invalidate_style();
document->set_needs_layout();
}
did_change = true;
m_needs_repaint = true;
}
if (m_viewport_scroll_offset != rect.location()) {
m_viewport_scroll_offset = rect.location();
scroll_offset_did_change();
did_change = true;
m_needs_repaint = true;
}
if (did_change && active_document()) {
active_document()->inform_all_viewport_clients_about_the_current_viewport_rect();
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML::main_thread_event_loop().schedule();
}
void Navigable::perform_scroll_of_viewport(CSSPixelPoint position)
{
auto viewport_rect = this->viewport_rect();
viewport_rect.set_location(position);
set_viewport_rect(viewport_rect);
set_needs_display();
if (is_traversable() && active_browsing_context())
active_browsing_context()->page().client().page_did_request_scroll_to(position);
}
void Navigable::set_size(CSSPixelSize size)
void Navigable::set_viewport_size(CSSPixelSize size)
{
if (m_size == size)
return;
m_size = size;
m_size = size;
if (auto document = active_document()) {
// NOTE: Resizing the viewport changes the reference value for viewport-relative CSS lengths.
document->invalidate_style();
document->set_needs_layout();
}
m_needs_repaint = true;
if (auto document = active_document()) {
document->inform_all_viewport_clients_about_the_current_viewport_rect();
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML::main_thread_event_loop().schedule();
}
}
void Navigable::perform_scroll_of_viewport(CSSPixelPoint new_position)
{
if (m_viewport_scroll_offset != new_position) {
m_viewport_scroll_offset = new_position;
scroll_offset_did_change();
m_needs_repaint = true;
if (auto document = active_document())
document->inform_all_viewport_clients_about_the_current_viewport_rect();
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.

View file

@ -160,11 +160,10 @@ public:
CSSPixelRect to_top_level_rect(CSSPixelRect const&);
CSSPixelSize size() const { return m_size; }
void set_size(CSSPixelSize);
CSSPixelPoint viewport_scroll_offset() const { return m_viewport_scroll_offset; }
CSSPixelRect viewport_rect() const { return { m_viewport_scroll_offset, m_size }; }
void set_viewport_rect(CSSPixelRect const&);
void set_viewport_size(CSSPixelSize);
void perform_scroll_of_viewport(CSSPixelPoint position);
void set_needs_display();

View file

@ -32,7 +32,7 @@ void FrameBox::did_set_content_size()
ReplacedBox::did_set_content_size();
if (dom_node().content_navigable())
dom_node().content_navigable()->set_size(paintable_box()->content_size());
dom_node().content_navigable()->set_viewport_size(paintable_box()->content_size());
}
JS::GCPtr<Painting::Paintable> FrameBox::create_paintable() const

View file

@ -151,13 +151,16 @@ Painting::PaintableBox const* EventHandler::paint_root() const
return m_navigable->active_document()->paintable_box();
}
bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers, int wheel_delta_x, int wheel_delta_y)
bool EventHandler::handle_mousewheel(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers, int wheel_delta_x, int wheel_delta_y)
{
if (!m_navigable->active_document())
return false;
if (!m_navigable->active_document()->is_fully_active())
return false;
auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
auto position = viewport_position.translated(scroll_offset);
m_navigable->active_document()->update_layout();
if (!paint_root())
@ -214,13 +217,16 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint scree
return handled_event;
}
bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
bool EventHandler::handle_mouseup(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
{
if (!m_navigable->active_document())
return false;
if (!m_navigable->active_document()->is_fully_active())
return false;
auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
auto position = viewport_position.translated(scroll_offset);
m_navigable->active_document()->update_layout();
if (!paint_root())
@ -292,8 +298,6 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p
//
// https://html.spec.whatwg.org/multipage/document-sequences.html#the-rules-for-choosing-a-navigable
auto top_level_position = m_navigable->active_document()->navigable()->to_top_level_position(position);
if (JS::GCPtr<HTML::HTMLAnchorElement const> link = node->enclosing_link_element()) {
JS::NonnullGCPtr<DOM::Document> document = *m_navigable->active_document();
auto href = link->href();
@ -302,13 +306,13 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p
if (button == UIEvents::MouseButton::Middle) {
m_navigable->page().client().page_did_middle_click_link(url, link->target().to_byte_string(), modifiers);
} else if (button == UIEvents::MouseButton::Secondary) {
m_navigable->page().client().page_did_request_link_context_menu(top_level_position, url, link->target().to_byte_string(), modifiers);
m_navigable->page().client().page_did_request_link_context_menu(viewport_position, url, link->target().to_byte_string(), modifiers);
}
} else if (button == UIEvents::MouseButton::Secondary) {
if (is<HTML::HTMLImageElement>(*node)) {
auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
auto image_url = image_element.document().parse_url(image_element.src());
m_navigable->page().client().page_did_request_image_context_menu(top_level_position, image_url, "", modifiers, image_element.bitmap());
m_navigable->page().client().page_did_request_image_context_menu(viewport_position, image_url, "", modifiers, image_element.bitmap());
} else if (is<HTML::HTMLMediaElement>(*node)) {
auto& media_element = verify_cast<HTML::HTMLMediaElement>(*node);
@ -321,9 +325,9 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p
.is_looping = media_element.has_attribute(HTML::AttributeNames::loop),
};
m_navigable->page().did_request_media_context_menu(media_element.unique_id(), top_level_position, "", modifiers, move(menu));
m_navigable->page().did_request_media_context_menu(media_element.unique_id(), viewport_position, "", modifiers, move(menu));
} else {
m_navigable->page().client().page_did_request_context_menu(top_level_position);
m_navigable->page().client().page_did_request_context_menu(viewport_position);
}
}
}
@ -336,13 +340,16 @@ after_node_use:
return handled_event;
}
bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
bool EventHandler::handle_mousedown(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
{
if (!m_navigable->active_document())
return false;
if (!m_navigable->active_document()->is_fully_active())
return false;
auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
auto position = viewport_position.translated(scroll_offset);
m_navigable->active_document()->update_layout();
if (!paint_root())
@ -436,13 +443,16 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen
return true;
}
bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen_position, u32 buttons, u32 modifiers)
bool EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 buttons, u32 modifiers)
{
if (!m_navigable->active_document())
return false;
if (!m_navigable->active_document()->is_fully_active())
return false;
auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
auto position = viewport_position.translated(scroll_offset);
m_navigable->active_document()->update_layout();
if (!paint_root())
@ -562,13 +572,16 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen
return true;
}
bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
bool EventHandler::handle_doubleclick(CSSPixelPoint viewport_position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers)
{
if (!m_navigable->active_document())
return false;
if (!m_navigable->active_document()->is_fully_active())
return false;
auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset();
auto position = viewport_position.translated(scroll_offset);
m_navigable->active_document()->update_layout();
if (!paint_root())

View file

@ -147,6 +147,15 @@ CSSPixelRect Page::device_to_css_rect(DevicePixelRect rect) const
};
}
CSSPixelSize Page::device_to_css_size(DevicePixelSize size) const
{
auto scale = client().device_pixels_per_css_pixel();
return {
CSSPixels::floored_value_for(size.width().value() / scale),
CSSPixels::floored_value_for(size.height().value() / scale),
};
}
DevicePixelRect Page::enclosing_device_rect(CSSPixelRect rect) const
{
auto scale = client().device_pixels_per_css_pixel();

View file

@ -82,6 +82,7 @@ public:
DevicePixelPoint css_to_device_point(CSSPixelPoint) const;
DevicePixelRect css_to_device_rect(CSSPixelRect) const;
CSSPixelRect device_to_css_rect(DevicePixelRect) const;
CSSPixelSize device_to_css_size(DevicePixelSize) const;
DevicePixelRect enclosing_device_rect(CSSPixelRect) const;
DevicePixelRect rounded_device_rect(CSSPixelRect) const;

View file

@ -245,7 +245,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
background_positioning_area = get_box(layer.origin).rect;
if (is<Layout::Box>(layout_node)) {
auto* paintable_box = static_cast<Layout::Box const&>(layout_node).paintable_box();
if (paintable_box) {
if (paintable_box && !paintable_box->is_viewport()) {
auto scroll_offset = paintable_box->scroll_offset();
background_positioning_area.translate_by(-scroll_offset.x(), -scroll_offset.y());
}

View file

@ -53,6 +53,12 @@ PaintableWithLines::~PaintableWithLines()
CSSPixelPoint PaintableBox::scroll_offset() const
{
if (is_viewport()) {
auto navigable = document().navigable();
VERIFY(navigable);
return navigable->viewport_scroll_offset();
}
auto const& node = layout_node();
if (node.is_generated_for_before_pseudo_element())
return node.pseudo_element_generator()->scroll_offset(DOM::Element::ScrollOffsetFor::PseudoBefore);
@ -230,7 +236,7 @@ bool PaintableBox::is_scrollable(ScrollDirection direction) const
auto overflow = direction == ScrollDirection::Horizontal ? computed_values().overflow_x() : computed_values().overflow_y();
auto scrollable_overflow_size = direction == ScrollDirection::Horizontal ? scrollable_overflow_rect()->width() : scrollable_overflow_rect()->height();
auto scrollport_size = direction == ScrollDirection::Horizontal ? absolute_padding_box_rect().width() : absolute_padding_box_rect().height();
if (overflow == CSS::Overflow::Auto)
if (is_viewport() || overflow == CSS::Overflow::Auto)
return scrollable_overflow_size > scrollport_size;
return overflow == CSS::Overflow::Scroll;
}
@ -255,20 +261,27 @@ Optional<CSSPixelRect> PaintableBox::scroll_thumb_rect(ScrollDirection direction
if (scroll_overflow_size > scrollport_size)
thumb_position = scroll_offset * (scrollport_size - thumb_size) / (scroll_overflow_size - scrollport_size);
CSSPixelRect thumb_rect;
if (direction == ScrollDirection::Horizontal) {
return CSSPixelRect {
thumb_rect = {
padding_rect.left() + thumb_position,
padding_rect.bottom() - scrollbar_thumb_thickness,
thumb_size,
scrollbar_thumb_thickness
};
} else {
thumb_rect = {
padding_rect.right() - scrollbar_thumb_thickness,
padding_rect.top() + thumb_position,
scrollbar_thumb_thickness,
thumb_size
};
}
return CSSPixelRect {
padding_rect.right() - scrollbar_thumb_thickness,
padding_rect.top() + thumb_position,
scrollbar_thumb_thickness,
thumb_size
};
if (is_viewport())
thumb_rect.translate_by(this->scroll_offset());
return thumb_rect;
}
void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
@ -314,7 +327,7 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
}
auto scrollbar_width = computed_values().scrollbar_width();
if (!layout_box().is_viewport() && phase == PaintPhase::Overlay && scrollbar_width != CSS::ScrollbarWidth::None) {
if (phase == PaintPhase::Overlay && scrollbar_width != CSS::ScrollbarWidth::None) {
auto color = Color(Color::NamedColor::DarkGray).with_alpha(128);
int thumb_corner_radius = static_cast<int>(context.rounded_device_pixels(scrollbar_thumb_thickness / 2));
if (auto thumb_rect = scroll_thumb_rect(ScrollDirection::Horizontal); thumb_rect.has_value()) {

View file

@ -203,6 +203,8 @@ public:
Optional<CSSPixelRect> get_clip_rect() const;
bool is_viewport() const { return layout_box().is_viewport(); }
protected:
explicit PaintableBox(Layout::Box const&);

View file

@ -80,7 +80,7 @@ void ViewportPaintable::assign_scroll_frames()
});
for_each_in_subtree([&](auto const& paintable) {
for (auto block = paintable.containing_block(); block; block = block->containing_block()) {
for (auto block = paintable.containing_block(); !block->is_viewport(); block = block->containing_block()) {
if (auto scroll_frame = scroll_state.get(block); scroll_frame.has_value()) {
if (paintable.is_paintable_box()) {
auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
@ -110,7 +110,7 @@ void ViewportPaintable::assign_clip_frames()
});
for_each_in_subtree([&](auto const& paintable) {
for (auto block = paintable.containing_block(); block; block = block->containing_block()) {
for (auto block = paintable.containing_block(); !block->is_viewport(); block = block->containing_block()) {
if (auto clip_frame = clip_state.get(block); clip_frame.has_value()) {
if (paintable.is_paintable_box()) {
auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
@ -136,7 +136,7 @@ void ViewportPaintable::refresh_scroll_state()
auto const& paintable_box = *it.key;
auto& scroll_frame = *it.value;
CSSPixelPoint offset;
for (auto const* block = &paintable_box.layout_box(); block; block = block->containing_block()) {
for (auto const* block = &paintable_box.layout_box(); !block->is_viewport(); block = block->containing_block()) {
auto const& block_paintable_box = *block->paintable_box();
offset.translate_by(block_paintable_box.scroll_offset());
}

View file

@ -89,7 +89,7 @@ RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
{
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, size).release_value_but_fixme_should_propagate_errors();
VERIFY(m_document->navigable());
m_document->navigable()->set_viewport_rect({ 0, 0, size.width(), size.height() });
m_document->navigable()->set_viewport_size(size.to_type<CSSPixels>());
m_document->update_layout();
Painting::CommandList painting_commands;

View file

@ -392,18 +392,18 @@ void ViewImplementation::resize_backing_stores_if_needed(WindowResizeInProgress
m_client_state.has_usable_bitmap = false;
auto viewport_rect = this->viewport_rect();
if (viewport_rect.is_empty())
auto viewport_size = this->viewport_size();
if (viewport_size.is_empty())
return;
Web::DevicePixelSize minimum_needed_size;
if (window_resize_in_progress == WindowResizeInProgress::Yes) {
// Pad the minimum needed size so that we don't have to keep reallocating backing stores while the window is being resized.
minimum_needed_size = { viewport_rect.width() + 256, viewport_rect.height() + 256 };
minimum_needed_size = { viewport_size.width() + 256, viewport_size.height() + 256 };
} else {
// If we're not in the middle of a resize, we can shrink the backing store size to match the viewport size.
minimum_needed_size = viewport_rect.size();
minimum_needed_size = viewport_size;
m_client_state.front_bitmap = {};
m_client_state.back_bitmap = {};
}
@ -417,7 +417,7 @@ void ViewImplementation::resize_backing_stores_if_needed(WindowResizeInProgress
backing_store.bitmap = new_bitmap_or_error.release_value();
backing_store.id = m_client_state.next_bitmap_id++;
}
backing_store.last_painted_size = viewport_rect.size();
backing_store.last_painted_size = viewport_size;
}
};
@ -430,7 +430,7 @@ void ViewImplementation::resize_backing_stores_if_needed(WindowResizeInProgress
if (front_bitmap.id != old_front_bitmap_id || back_bitmap.id != old_back_bitmap_id) {
client().async_add_backing_store(page_id(), front_bitmap.id, front_bitmap.bitmap->to_shareable_bitmap(), back_bitmap.id,
back_bitmap.bitmap->to_shareable_bitmap());
client().async_set_viewport_rect(page_id(), viewport_rect);
client().async_set_viewport_size(page_id(), viewport_size);
}
}

View file

@ -205,7 +205,7 @@ public:
Function<void(String const&)> on_inspector_executed_console_script;
Function<IPC::File()> on_request_worker_agent;
virtual Web::DevicePixelRect viewport_rect() const = 0;
virtual Web::DevicePixelSize viewport_size() const = 0;
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const = 0;
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const = 0;

View file

@ -161,10 +161,10 @@ void ConnectionFromClient::traverse_the_history_by_delta(u64 page_id, i32 delta)
page->page().traverse_the_history_by_delta(delta);
}
void ConnectionFromClient::set_viewport_rect(u64 page_id, Web::DevicePixelRect const& rect)
void ConnectionFromClient::set_viewport_size(u64 page_id, Web::DevicePixelSize const size)
{
if (auto page = this->page(page_id); page.has_value())
page->set_viewport_rect(rect);
page->set_viewport_size(size);
}
void ConnectionFromClient::add_backing_store(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap)

View file

@ -58,7 +58,7 @@ private:
virtual void load_html(u64 page_id, ByteString const&) override;
virtual void reload(u64 page_id) override;
virtual void traverse_the_history_by_delta(u64 page_id, i32 delta) override;
virtual void set_viewport_rect(u64 page_id, Web::DevicePixelRect const&) override;
virtual void set_viewport_size(u64 page_id, Web::DevicePixelSize const) override;
virtual void key_event(u64 page_id, Web::KeyEvent const&) override;
virtual void mouse_event(u64 page_id, Web::MouseEvent const&) override;
virtual void add_backing_store(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const& front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap const& back_bitmap) override;

View file

@ -233,9 +233,9 @@ void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& ta
}
}
void PageClient::set_viewport_rect(Web::DevicePixelRect const& rect)
void PageClient::set_viewport_size(Web::DevicePixelSize const& size)
{
page().top_level_traversable()->set_viewport_rect(page().device_to_css_rect(rect));
page().top_level_traversable()->set_viewport_size(page().device_to_css_size(size));
}
void PageClient::page_did_request_cursor_change(Gfx::StandardCursor cursor)
@ -321,7 +321,7 @@ void PageClient::page_did_request_scroll_to(Web::CSSPixelPoint scroll_position)
// scroll on browser side happens.
auto viewport = page().top_level_traversable()->viewport_rect();
viewport.set_location(scroll_position);
page().top_level_traversable()->set_viewport_rect(viewport);
page().top_level_traversable()->set_viewport_size(viewport.size());
auto device_scroll_position = page().css_to_device_point(scroll_position);
client().async_did_request_scroll_to(m_id, device_scroll_position.to_type<int>());

View file

@ -44,7 +44,7 @@ public:
virtual void paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap&, Web::PaintOptions = {}) override;
void set_palette_impl(Gfx::PaletteImpl&);
void set_viewport_rect(Web::DevicePixelRect const&);
void set_viewport_size(Web::DevicePixelSize const&);
void set_screen_rects(Vector<Web::DevicePixelRect, 4> const& rects, size_t main_screen_index) { m_screen_rect = rects[main_screen_index]; }
void set_device_pixels_per_css_pixel(float device_pixels_per_css_pixel) { m_device_pixels_per_css_pixel = device_pixels_per_css_pixel; }
void set_preferred_color_scheme(Web::CSS::PreferredColorScheme);

View file

@ -29,7 +29,7 @@ endpoint WebContentServer
add_backing_store(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) =|
ready_to_paint(u64 page_id) =|
set_viewport_rect(u64 page_id, Web::DevicePixelRect rect) =|
set_viewport_size(u64 page_id, Web::DevicePixelSize size) =|
key_event(u64 page_id, Web::KeyEvent event) =|
mouse_event(u64 page_id, Web::MouseEvent event) =|

View file

@ -105,8 +105,8 @@ public:
view->client().async_update_system_theme(0, move(theme));
view->m_viewport_rect = { { 0, 0 }, window_size };
view->client().async_set_viewport_rect(0, view->m_viewport_rect.to_type<Web::DevicePixels>());
view->m_viewport_size = window_size;
view->client().async_set_viewport_size(0, view->m_viewport_size.to_type<Web::DevicePixels>());
view->client().async_set_window_size(0, window_size.to_type<Web::DevicePixels>());
if (!web_driver_ipc_path.is_empty())
@ -168,19 +168,6 @@ private:
, m_cookie_jar(move(cookie_jar))
, m_request_client(move(request_client))
{
on_scroll_to_point = [this](auto position) {
m_viewport_rect.set_location(position);
client().async_set_viewport_rect(0, m_viewport_rect.to_type<Web::DevicePixels>());
};
on_scroll_by_delta = [this](auto x_delta, auto y_delta) {
auto position = m_viewport_rect.location();
position.set_x(position.x() + x_delta);
position.set_y(position.y() + y_delta);
if (on_scroll_to_point)
on_scroll_to_point(position);
};
on_get_cookie = [this](auto const& url, auto source) {
return m_cookie_jar->get_cookie(url, source);
};
@ -203,12 +190,12 @@ private:
void update_zoom() override { }
void initialize_client(CreateNewClient) override { }
virtual Web::DevicePixelRect viewport_rect() const override { return m_viewport_rect.to_type<Web::DevicePixels>(); }
virtual Web::DevicePixelSize viewport_size() const override { return m_viewport_size.to_type<Web::DevicePixels>(); }
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const override { return widget_position; }
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const override { return content_position; }
private:
Gfx::IntRect m_viewport_rect;
Gfx::IntSize m_viewport_size;
RefPtr<Core::Promise<RefPtr<Gfx::Bitmap>>> m_pending_screenshot;
NonnullRefPtr<WebView::Database> m_database;