Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Libraries/LibWeb/Compositor/AsyncScrollTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ AsyncStickyArea const* AsyncScrollTree::sticky_area_for_scroll_node_index(Painti

Gfx::FloatPoint AsyncScrollTree::clamp_scroll_offset_to_node(AsyncScrollNode const& node, Gfx::FloatPoint scroll_offset)
{
scroll_offset.set_x(max(0.0f, min(scroll_offset.x(), node.max_scroll_offset.x())));
scroll_offset.set_y(max(0.0f, min(scroll_offset.y(), node.max_scroll_offset.y())));
scroll_offset.set_x(max(node.min_scroll_offset.x(), min(scroll_offset.x(), node.max_scroll_offset.x())));
scroll_offset.set_y(max(node.min_scroll_offset.y(), min(scroll_offset.y(), node.max_scroll_offset.y())));
return scroll_offset;
}

Expand All @@ -80,11 +80,11 @@ Gfx::FloatPoint AsyncScrollTree::scroll_offset_for_node(AsyncScrollNode const& n
bool AsyncScrollTree::can_scroll_node_by_delta(AsyncScrollNode const& node, Painting::ScrollStateSnapshot const& scroll_state_snapshot, Gfx::FloatPoint delta)
{
auto scroll_offset = scroll_offset_for_node(node, scroll_state_snapshot);
if (node.can_be_wheel_scrolled_horizontally && delta.x() < 0 && scroll_offset.x() > 0)
if (node.can_be_wheel_scrolled_horizontally && delta.x() < 0 && scroll_offset.x() > node.min_scroll_offset.x())
return true;
if (node.can_be_wheel_scrolled_horizontally && delta.x() > 0 && scroll_offset.x() < node.max_scroll_offset.x())
return true;
if (node.can_be_wheel_scrolled_vertically && delta.y() < 0 && scroll_offset.y() > 0)
if (node.can_be_wheel_scrolled_vertically && delta.y() < 0 && scroll_offset.y() > node.min_scroll_offset.y())
return true;
if (node.can_be_wheel_scrolled_vertically && delta.y() > 0 && scroll_offset.y() < node.max_scroll_offset.y())
return true;
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Compositor/AsyncScrollingState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ AsyncScrollingState async_scrolling_state_from_display_list(Painting::DisplayLis
.stable_node_id = stable_scroll_node_id_for(command.scrollable_node_id, command.scroll_node_kind, command.pseudo_element_type),
.parent_node_id = {},
.scrollport_rect = command.scrollport_rect,
.min_scroll_offset = command.min_scroll_offset,
.max_scroll_offset = command.max_scroll_offset,
.is_viewport = command.is_viewport,
.can_be_wheel_scrolled_horizontally = command.can_be_wheel_scrolled_horizontally,
Expand Down Expand Up @@ -138,6 +139,7 @@ AsyncScrollingState async_scrolling_state_from_display_list(Painting::DisplayLis
.expanded_thumb_rect = command.expanded_thumb_rect,
.scroll_size = command.scroll_size,
.expanded_scroll_size = command.expanded_scroll_size,
.min_scroll_offset = command.min_scroll_offset,
.max_scroll_offset = command.max_scroll_offset,
.thumb_color = command.thumb_color,
.track_color = command.track_color,
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Compositor/AsyncScrollingState.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct AsyncScrollNode {
AsyncScrollNodeStableID stable_node_id;
Optional<AsyncScrollNodeID> parent_node_id;
Gfx::IntRect scrollport_rect;
Gfx::FloatPoint min_scroll_offset;
Gfx::FloatPoint max_scroll_offset;
bool is_viewport { false };
bool can_be_wheel_scrolled_horizontally { false };
Expand Down Expand Up @@ -111,6 +112,7 @@ struct ViewportScrollbar {
Gfx::IntRect expanded_thumb_rect;
double scroll_size { 0 };
double expanded_scroll_size { 0 };
float min_scroll_offset { 0 };
float max_scroll_offset { 0 };
Color thumb_color;
Color track_color;
Expand Down
45 changes: 45 additions & 0 deletions Libraries/LibWeb/DOM/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,50 @@ static void propagate_scrollbar_width_to_viewport(Element& root_element, Layout:
});
}

// https://drafts.csswg.org/css-writing-modes-4/#principal-flow
static void propagate_principal_writing_mode_to_viewport(Element& root_element, Layout::Viewport& viewport)
{
// The principal writing mode of the document is determined by the used writing-mode, direction, and
// text-orientation values of the root element.
auto const* root_inherited_box_values = root_element.style_group<CSS::ComputedValues::InheritedBoxValues>();
VERIFY(root_inherited_box_values);
auto writing_mode = static_cast<CSS::WritingMode>(root_inherited_box_values->writing_mode);
auto direction = static_cast<CSS::Direction>(root_inherited_box_values->direction);

// As a special case for handling HTML documents, if the root element has a body child element [HTML] whose
// display value is not none, the used value of the of writing-mode and direction properties on root element
// are taken from the computed writing-mode and direction of the first such child element instead of from the
// root element's own values.
// NOTE: Using containment disables this special handling of the HTML body element.
auto* body_element = root_element.first_child_of_type<HTML::HTMLBodyElement>();
auto const* root_box_values = root_element.style_group<CSS::ComputedValues::BoxValues>();
VERIFY(root_box_values);
auto const* body_box_values = body_element ? body_element->style_group<CSS::ComputedValues::BoxValues>() : nullptr;
auto const* body_inherited_box_values = body_element ? body_element->style_group<CSS::ComputedValues::InheritedBoxValues>() : nullptr;
auto has_containment = [](CSS::ComputedValues::BoxValues const& values) {
return values.size_containment || values.inline_size_containment || values.layout_containment || values.style_containment || values.paint_containment;
};
bool propagation_is_disabled_by_containment = has_containment(*root_box_values)
|| (body_box_values && has_containment(*body_box_values));
if (root_element.is_html_html_element() && !propagation_is_disabled_by_containment
&& body_box_values && body_inherited_box_values && !CSS::display_from_ffi_display(body_box_values->display).is_none()) {
writing_mode = static_cast<CSS::WritingMode>(body_inherited_box_values->writing_mode);
direction = static_cast<CSS::Direction>(body_inherited_box_values->direction);
}
root_element.unsafe_layout_node()->modify_computed_values([&](auto& values) {
values.set_writing_mode(writing_mode);
values.set_direction(direction);
});

// https://drafts.csswg.org/css-writing-modes-4/#icb
// The principal writing mode is propagated to the initial containing block and to the viewport, thereby
// affecting the layout of the root element and the scrolling direction of the viewport.
viewport.modify_computed_values([&](auto& values) {
values.set_writing_mode(writing_mode);
values.set_direction(direction);
});
}

// https://drafts.csswg.org/css-overflow-3/#overflow-propagation
static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewport& viewport)
{
Expand Down Expand Up @@ -2161,6 +2205,7 @@ void Document::update_layout(UpdateLayoutReason reason)
}

if (document_element && document_element->unsafe_layout_node()) {
propagate_principal_writing_mode_to_viewport(*document_element, *m_layout_root);
propagate_overflow_to_viewport(*document_element, *m_layout_root);
} else {
m_layout_root->modify_computed_values([](auto& values) {
Expand Down
18 changes: 9 additions & 9 deletions Libraries/LibWeb/HTML/LocalNavigable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3869,14 +3869,13 @@ void LocalNavigable::clamp_viewport_scroll_offset()
return;
if (!document->paintable_box())
return;
auto scrollable_overflow_rect = document->paintable_box()->scrollable_overflow_rect();
if (!scrollable_overflow_rect.has_value())
if (!document->paintable_box()->scrollable_overflow_rect().has_value())
return;
auto max_x = scrollable_overflow_rect->width() - m_viewport_size.width();
auto max_y = scrollable_overflow_rect->height() - m_viewport_size.height();
auto minimum_scroll_offset = document->paintable_box()->minimum_scroll_offset();
auto maximum_scroll_offset = document->paintable_box()->maximum_scroll_offset();
CSSPixelPoint clamped = {
max(CSSPixels(0), min(m_viewport_scroll_offset.x(), max_x)),
max(CSSPixels(0), min(m_viewport_scroll_offset.y(), max_y)),
clamp(m_viewport_scroll_offset.x(), minimum_scroll_offset.x(), maximum_scroll_offset.x()),
clamp(m_viewport_scroll_offset.y(), minimum_scroll_offset.y(), maximum_scroll_offset.y()),
};
if (clamped != m_viewport_scroll_offset)
perform_scroll_of_viewport_scrolling_box(clamped);
Expand Down Expand Up @@ -5133,11 +5132,12 @@ GC::Ref<WebIDL::Promise> LocalNavigable::perform_a_scroll_of_the_viewport(CSSPix
// NB: Must update layout before accessing paintables.
doc->update_layout(DOM::UpdateLayoutReason::NavigableViewportScroll);

auto scrolling_area = doc->paintable_box()->scrollable_overflow_rect()->to_type<float>();
auto minimum_scroll_offset = doc->paintable_box()->minimum_scroll_offset().to_type<double>();
auto maximum_scroll_offset = doc->paintable_box()->maximum_scroll_offset().to_type<double>();
auto new_viewport_scroll_offset = m_viewport_scroll_offset.to_type<double>() + Gfx::Point(layout_dx, layout_dy);
// NOTE: Clamp to the scrolling area.
new_viewport_scroll_offset.set_x(max(0.0, min(new_viewport_scroll_offset.x(), scrolling_area.width() - viewport_size().width().to_double())));
new_viewport_scroll_offset.set_y(max(0.0, min(new_viewport_scroll_offset.y(), scrolling_area.height() - viewport_size().height().to_double())));
new_viewport_scroll_offset.set_x(clamp(new_viewport_scroll_offset.x(), minimum_scroll_offset.x(), maximum_scroll_offset.x()));
new_viewport_scroll_offset.set_y(clamp(new_viewport_scroll_offset.y(), minimum_scroll_offset.y(), maximum_scroll_offset.y()));

auto scroll_promise = perform_a_scroll_of_a_scrolling_box({
.node_id = doc->unique_id(),
Expand Down
34 changes: 21 additions & 13 deletions Libraries/LibWeb/HTML/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
#include <LibWeb/Infra/CharacterTypes.h>
#include <LibWeb/Internals/Internals.h>
#include <LibWeb/Layout/ScrollableOverflow.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/Paintable.h>
Expand Down Expand Up @@ -1723,20 +1724,27 @@ void Window::scroll(ScrollToOptions const& options, GC::Ptr<WebIDL::Promise> pro

VERIFY(document->paintable_box());
auto scrolling_area = document->paintable_box()->scrollable_overflow_rect()->to_type<float>();
auto overflow_directions = Layout::physical_overflow_directions(*document->layout_node());

// 7. -> If the viewport has rightward overflow direction
// Let x be max(0, min(x, viewport scrolling area width - viewport width)).
// -> If the viewport has leftward overflow direction
// Let x be min(0, max(x, viewport width - viewport scrolling area width)).
if (overflow_directions.horizontal_axis_is_positive) {
x = max(0.0f, min(x, scrolling_area.width() - viewport_width));
} else {
x = min(0.0f, max(x, viewport_width - scrolling_area.width()));
}

// 7. FIXME: For now we always assume overflow direction is rightward
// -> If the viewport has rightward overflow direction
// Let x be max(0, min(x, viewport scrolling area width - viewport width)).
x = max(0.0f, min(x, scrolling_area.width() - viewport_width));
// -> If the viewport has leftward overflow direction
// Let x be min(0, max(x, viewport width - viewport scrolling area width)).

// 8. FIXME: For now we always assume overflow direction is downward
// -> If the viewport has downward overflow direction
// Let y be max(0, min(y, viewport scrolling area height - viewport height)).
y = max(0.0f, min(y, scrolling_area.height() - viewport_height));
// -> If the viewport has upward overflow direction
// Let y be min(0, max(y, viewport height - viewport scrolling area height)).
// 8. -> If the viewport has downward overflow direction
// Let y be max(0, min(y, viewport scrolling area height - viewport height)).
// -> If the viewport has upward overflow direction
// Let y be min(0, max(y, viewport height - viewport scrolling area height)).
if (overflow_directions.vertical_axis_is_positive) {
y = max(0.0f, min(y, scrolling_area.height() - viewport_height));
} else {
y = min(0.0f, max(y, viewport_height - scrolling_area.height()));
}
}

// FIXME: 9. Let position be the scroll position the viewport would have by aligning the x-coordinate x of the viewport
Expand Down
30 changes: 11 additions & 19 deletions Libraries/LibWeb/Layout/ScrollableOverflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/Tuple.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/FormAssociatedElement.h>
#include <LibWeb/Layout/Box.h>
Expand All @@ -32,11 +31,6 @@ ContainedBoxesMap collect_scrollable_overflow_contained_boxes(Node const& root,
return contained_boxes_map;
}

struct PhysicalOverflowDirections {
bool horizontal_axis_is_positive { true };
bool vertical_axis_is_positive { true };
};

struct AxisDirection {
bool is_horizontal { false };
bool is_reverse { false };
Expand All @@ -56,8 +50,12 @@ static bool node_is_in_focused_text_control(DOM::Node const& node)
&& shadow_root->host()->is_focused();
}

static PhysicalOverflowDirections physical_overflow_directions(Box const& box)
// https://drafts.csswg.org/cssom-view/#overflow-directions
PhysicalOverflowDirections physical_overflow_directions(Box const& box)
{
// A scrolling box of a viewport or element has two overflow directions, which are the block-end and inline-end
// directions for that viewport or element.

AxisDirection inline_axis {
.is_horizontal = inline_axis_is_horizontal(box.writing_mode()),
.is_reverse = box.inline_axis_is_reverse(),
Expand All @@ -67,30 +65,24 @@ static PhysicalOverflowDirections physical_overflow_directions(Box const& box)
.is_reverse = box.block_axis_is_reverse(),
};

auto horizontal_and_vertical_axes = [&]() {
if (!box.display().is_flex_inside())
return AK::Tuple { inline_axis.is_horizontal ? inline_axis : block_axis, inline_axis.is_horizontal ? block_axis : inline_axis };

if (box.display().is_flex_inside()) {
auto is_row_layout = box.flex_direction() == CSS::FlexDirection::Row
|| box.flex_direction() == CSS::FlexDirection::RowReverse;
auto& main_axis = is_row_layout ? inline_axis : block_axis;
auto& cross_axis = is_row_layout ? block_axis : inline_axis;

auto main_axis = is_row_layout ? inline_axis : block_axis;
if (box.flex_direction() == CSS::FlexDirection::RowReverse
|| box.flex_direction() == CSS::FlexDirection::ColumnReverse) {
main_axis.is_reverse = !main_axis.is_reverse;
}

auto cross_axis = is_row_layout ? block_axis : inline_axis;
// AD-HOC: A legacy webkit box ignores `flex-wrap`, matching other engines.
if (!box.display().is_webkit_box_inside() && box.flex_wrap() == CSS::FlexWrap::WrapReverse)
cross_axis.is_reverse = !cross_axis.is_reverse;
}

return AK::Tuple { main_axis.is_horizontal ? main_axis : cross_axis, main_axis.is_horizontal ? cross_axis : main_axis };
};

auto axes = horizontal_and_vertical_axes();
auto horizontal_axis = axes.get<0>();
auto vertical_axis = axes.get<1>();
auto horizontal_axis = inline_axis.is_horizontal ? inline_axis : block_axis;
auto vertical_axis = inline_axis.is_horizontal ? block_axis : inline_axis;
return {
.horizontal_axis_is_positive = !horizontal_axis.is_reverse,
.vertical_axis_is_positive = !vertical_axis.is_reverse,
Expand Down
7 changes: 7 additions & 0 deletions Libraries/LibWeb/Layout/ScrollableOverflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ namespace Web::Layout {
// Map from each containing block to the boxes it contains.
using ContainedBoxesMap = HashMap<Box const*, Vector<Box const*>>;

struct PhysicalOverflowDirections {
bool horizontal_axis_is_positive { true };
bool vertical_axis_is_positive { true };
};

[[nodiscard]] PhysicalOverflowDirections physical_overflow_directions(Box const&);

[[nodiscard]] ContainedBoxesMap collect_scrollable_overflow_contained_boxes(Node const& root, Function<void(Box const&)> box_visitor = {});

// https://drafts.csswg.org/css-overflow-3/#scrollable-overflow-region
Expand Down
8 changes: 4 additions & 4 deletions Libraries/LibWeb/Painting/DisplayListCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ void PaintNestedDisplayList::dump(StringBuilder& builder) const

void CompositorScrollNode::dump(StringBuilder& builder) const
{
builder.appendff(" scroll_node_index={} parent_scroll_node_index={} scrollport_rect={} max_scroll_offset={} is_viewport={}",
scroll_node_index, parent_scroll_node_index, scrollport_rect, max_scroll_offset, is_viewport);
builder.appendff(" scroll_node_index={} parent_scroll_node_index={} scrollport_rect={} min_scroll_offset={} max_scroll_offset={} is_viewport={}",
scroll_node_index, parent_scroll_node_index, scrollport_rect, min_scroll_offset, max_scroll_offset, is_viewport);
}

static void dump_optional_float(StringBuilder& builder, Optional<float> value)
Expand Down Expand Up @@ -268,8 +268,8 @@ void CompositorMainThreadWheelEventRegion::dump(StringBuilder& builder) const

void CompositorViewportScrollbar::dump(StringBuilder& builder) const
{
builder.appendff(" scroll_node_index={} gutter_rect={} thumb_rect={} expanded_gutter_rect={} expanded_thumb_rect={} scroll_size={} expanded_scroll_size={} max_scroll_offset={} thumb_color={} track_color={} vertical={}",
scroll_node_index, gutter_rect, thumb_rect, expanded_gutter_rect, expanded_thumb_rect, scroll_size, expanded_scroll_size, max_scroll_offset, thumb_color, track_color, vertical);
builder.appendff(" scroll_node_index={} gutter_rect={} thumb_rect={} expanded_gutter_rect={} expanded_thumb_rect={} scroll_size={} expanded_scroll_size={} min_scroll_offset={} max_scroll_offset={} thumb_color={} track_color={} vertical={}",
scroll_node_index, gutter_rect, thumb_rect, expanded_gutter_rect, expanded_thumb_rect, scroll_size, expanded_scroll_size, min_scroll_offset, max_scroll_offset, thumb_color, track_color, vertical);
}

void PaintScrollBar::dump(StringBuilder&) const
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Painting/DisplayListCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ struct CompositorScrollNode {
VisualContextIndex scroll_node_index;
VisualContextIndex parent_scroll_node_index;
Gfx::IntRect scrollport_rect;
Gfx::FloatPoint min_scroll_offset;
Gfx::FloatPoint max_scroll_offset;
CompositorScrollNodeKind scroll_node_kind { CompositorScrollNodeKind::Element };
u8 pseudo_element_type { 0 };
Expand Down Expand Up @@ -683,6 +684,7 @@ struct CompositorViewportScrollbar {
Gfx::IntRect expanded_thumb_rect;
double scroll_size { 0 };
double expanded_scroll_size { 0 };
float min_scroll_offset { 0 };
float max_scroll_offset { 0 };
Color thumb_color;
Color track_color;
Expand Down
Loading
Loading