diff --git a/Tests/LibWeb/Ref/iframe-contains-scrollable-box.html b/Tests/LibWeb/Ref/iframe-contains-scrollable-box.html
new file mode 100644
index 00000000000..ba7a74bd830
--- /dev/null
+++ b/Tests/LibWeb/Ref/iframe-contains-scrollable-box.html
@@ -0,0 +1,46 @@
+
+
+
+
diff --git a/Tests/LibWeb/Ref/reference/iframe-contains-scrollable-box-ref.html b/Tests/LibWeb/Ref/reference/iframe-contains-scrollable-box-ref.html
new file mode 100644
index 00000000000..ac2ff5ef34e
--- /dev/null
+++ b/Tests/LibWeb/Ref/reference/iframe-contains-scrollable-box-ref.html
@@ -0,0 +1,36 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index ce4ae6157db..1943ae4dcf1 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -1156,12 +1156,10 @@ void Document::update_layout()
navigable->set_needs_display();
set_needs_to_resolve_paint_only_properties();
- if (navigable->is_traversable()) {
- // NOTE: The assignment of scroll frames only needs to occur for traversables because they take care of all
- // nested navigable documents.
- paintable()->assign_scroll_frames();
- paintable()->assign_clip_frames();
+ paintable()->assign_scroll_frames();
+ paintable()->assign_clip_frames();
+ if (navigable->is_traversable()) {
page().client().page_did_layout();
}
diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
index 3656952810b..b81996d7a63 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
#include
#include
#include
@@ -2082,11 +2083,18 @@ void Navigable::inform_the_navigation_api_about_aborting_navigation()
}));
}
-void Navigable::record_display_list(Painting::DisplayListRecorder& display_list_recorder, PaintConfig config)
+RefPtr Navigable::record_display_list(PaintConfig config)
{
auto document = active_document();
if (!document)
- return;
+ return {};
+
+ auto display_list = Painting::DisplayList::create();
+ Painting::DisplayListRecorder display_list_recorder(display_list);
+
+ if (config.canvas_fill_rect.has_value()) {
+ display_list_recorder.fill_rect(config.canvas_fill_rect.value(), CSS::SystemColor::canvas());
+ }
auto const& page = traversable_navigable()->page();
auto viewport_rect = page.css_to_device_rect(this->viewport_rect());
@@ -2109,27 +2117,22 @@ void Navigable::record_display_list(Painting::DisplayListRecorder& display_list_
auto& viewport_paintable = *document->paintable();
- // NOTE: We only need to refresh the scroll state for traversables because they are responsible
- // for tracking the state of all nested navigables.
- if (is_traversable()) {
- viewport_paintable.refresh_scroll_state();
- viewport_paintable.refresh_clip_state();
- }
+ viewport_paintable.refresh_scroll_state();
+ viewport_paintable.refresh_clip_state();
viewport_paintable.paint_all_phases(context);
- // FIXME: Support scrollable frames inside iframes.
- if (is_traversable()) {
- Vector scroll_offsets_by_frame_id;
- scroll_offsets_by_frame_id.resize(viewport_paintable.scroll_state.size());
- for (auto [_, scrollable_frame] : viewport_paintable.scroll_state) {
- auto scroll_offset = context.rounded_device_point(scrollable_frame->offset).to_type();
- scroll_offsets_by_frame_id[scrollable_frame->id] = scroll_offset;
- }
- display_list_recorder.display_list().apply_scroll_offsets(scroll_offsets_by_frame_id);
+ Vector scroll_offsets_by_frame_id;
+ scroll_offsets_by_frame_id.resize(viewport_paintable.scroll_state.size());
+ for (auto [_, scrollable_frame] : viewport_paintable.scroll_state) {
+ auto scroll_offset = context.rounded_device_point(scrollable_frame->offset).to_type();
+ scroll_offsets_by_frame_id[scrollable_frame->id] = scroll_offset;
}
+ display_list_recorder.display_list().apply_scroll_offsets(scroll_offsets_by_frame_id);
m_needs_repaint = false;
+
+ return display_list;
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#event-uni
diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h
index 066c3d023b0..e58f32830c7 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.h
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.h
@@ -185,8 +185,9 @@ public:
bool paint_overlay { false };
bool should_show_line_box_borders { false };
bool has_focus { false };
+ Optional canvas_fill_rect {};
};
- void record_display_list(Painting::DisplayListRecorder& display_list_recorder, PaintConfig);
+ RefPtr record_display_list(PaintConfig);
Page& page() { return m_page; }
Page const& page() const { return m_page; }
diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp
index e7b07400fde..dbef941553d 100644
--- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp
+++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp
@@ -6,7 +6,6 @@
#include
#include
-#include
#include
#include
#include
@@ -1190,17 +1189,15 @@ JS::GCPtr TraversableNavigable::currently_focused_area()
void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::BackingStore& target, PaintOptions paint_options)
{
- auto display_list = Painting::DisplayList::create();
- Painting::DisplayListRecorder display_list_recorder(display_list);
-
- Gfx::IntRect bitmap_rect { {}, content_rect.size().to_type() };
- display_list_recorder.fill_rect(bitmap_rect, CSS::SystemColor::canvas());
-
HTML::Navigable::PaintConfig paint_config;
paint_config.paint_overlay = paint_options.paint_overlay == PaintOptions::PaintOverlay::Yes;
paint_config.should_show_line_box_borders = paint_options.should_show_line_box_borders;
paint_config.has_focus = paint_options.has_focus;
- record_display_list(display_list_recorder, paint_config);
+ paint_config.canvas_fill_rect = Gfx::IntRect { {}, content_rect.size() };
+ auto display_list = record_display_list(paint_config);
+ if (!display_list) {
+ return;
+ }
switch (page().client().display_list_player_type()) {
case DisplayListPlayerType::SkiaGPUIfAvailable: {
@@ -1209,7 +1206,7 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::
auto& iosurface_backing_store = static_cast(target);
auto texture = m_metal_context->create_texture_from_iosurface(iosurface_backing_store.iosurface_handle());
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, *texture);
- player.execute(display_list);
+ player.execute(*display_list);
return;
}
#endif
@@ -1217,19 +1214,19 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::
#ifdef USE_VULKAN
if (m_skia_backend_context) {
Painting::DisplayListPlayerSkia player(*m_skia_backend_context, target.bitmap());
- player.execute(display_list);
+ player.execute(*display_list);
return;
}
#endif
// Fallback to CPU backend if GPU is not available
Painting::DisplayListPlayerSkia player(target.bitmap());
- player.execute(display_list);
+ player.execute(*display_list);
break;
}
case DisplayListPlayerType::SkiaCPU: {
Painting::DisplayListPlayerSkia player(target.bitmap());
- player.execute(display_list);
+ player.execute(*display_list);
break;
}
default:
diff --git a/Userland/Libraries/LibWeb/Painting/Command.h b/Userland/Libraries/LibWeb/Painting/Command.h
index 57761afbabe..192a296290f 100644
--- a/Userland/Libraries/LibWeb/Painting/Command.h
+++ b/Userland/Libraries/LibWeb/Painting/Command.h
@@ -360,6 +360,18 @@ struct AddMask {
}
};
+struct PaintNestedDisplayList {
+ RefPtr display_list;
+ Gfx::IntRect rect;
+
+ [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
+
+ void translate_by(Gfx::IntPoint const& offset)
+ {
+ rect.translate_by(offset);
+ }
+};
+
using Command = Variant<
DrawGlyphRun,
FillRect,
@@ -389,6 +401,7 @@ using Command = Variant<
DrawRect,
DrawTriangleWave,
AddRoundedRectClip,
- AddMask>;
+ AddMask,
+ PaintNestedDisplayList>;
}
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp
index 81be86eb436..50744a700a4 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp
@@ -86,6 +86,7 @@ void DisplayListPlayer::execute(DisplayList& display_list)
else HANDLE_COMMAND(DrawTriangleWave, draw_triangle_wave)
else HANDLE_COMMAND(AddRoundedRectClip, add_rounded_rect_clip)
else HANDLE_COMMAND(AddMask, add_mask)
+ else HANDLE_COMMAND(PaintNestedDisplayList, paint_nested_display_list)
else VERIFY_NOT_REACHED();
// clang-format on
}
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.h b/Userland/Libraries/LibWeb/Painting/DisplayList.h
index 25b86339b4d..c286b5e00c1 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayList.h
+++ b/Userland/Libraries/LibWeb/Painting/DisplayList.h
@@ -70,6 +70,7 @@ private:
virtual void draw_triangle_wave(DrawTriangleWave const&) = 0;
virtual void add_rounded_rect_clip(AddRoundedRectClip const&) = 0;
virtual void add_mask(AddMask const&) = 0;
+ virtual void paint_nested_display_list(PaintNestedDisplayList const&) = 0;
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
};
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
index 5ab90d6a277..0ba62f001cd 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp
@@ -1299,6 +1299,13 @@ void DisplayListPlayerSkia::add_mask(AddMask const& command)
surface().canvas().clipShader(shader);
}
+void DisplayListPlayerSkia::paint_nested_display_list(PaintNestedDisplayList const& command)
+{
+ auto& canvas = surface().canvas();
+ canvas.translate(command.rect.x(), command.rect.y());
+ execute(*command.display_list);
+}
+
bool DisplayListPlayerSkia::would_be_fully_clipped_by_painter(Gfx::IntRect rect) const
{
return surface().canvas().quickReject(to_skia_rect(rect));
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h
index 4e3c9eecb46..18b0d4ef962 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h
@@ -77,6 +77,7 @@ private:
void draw_triangle_wave(DrawTriangleWave const&) override;
void add_rounded_rect_clip(AddRoundedRectClip const&) override;
void add_mask(AddMask const&) override;
+ void paint_nested_display_list(PaintNestedDisplayList const&) override;
bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp
index cc7f77a284d..170f519b1f2 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp
@@ -22,6 +22,13 @@ void DisplayListRecorder::append(Command&& command)
m_command_list.append(move(command), state().scroll_frame_id);
}
+void DisplayListRecorder::paint_nested_display_list(RefPtr display_list, Gfx::IntRect rect)
+{
+ append(PaintNestedDisplayList {
+ .display_list = move(display_list),
+ .rect = state().translation.map(rect) });
+}
+
void DisplayListRecorder::add_rounded_rect_clip(CornerRadii corner_radii, Gfx::IntRect border_rect, CornerClip corner_clip)
{
append(AddRoundedRectClip {
diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h
index b58de8113b8..8af41429e30 100644
--- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h
+++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h
@@ -121,6 +121,8 @@ public:
void push_stacking_context(PushStackingContextParams params);
void pop_stacking_context();
+ void paint_nested_display_list(RefPtr display_list, Gfx::IntRect rect);
+
void add_rounded_rect_clip(CornerRadii corner_radii, Gfx::IntRect border_rect, CornerClip corner_clip);
void add_mask(RefPtr display_list, Gfx::IntRect rect);
diff --git a/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp b/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp
index 9515ff44216..f54ad3b35f7 100644
--- a/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp
@@ -40,8 +40,7 @@ void NestedBrowsingContextPaintable::paint(PaintContext& context, PaintPhase pha
if (phase == PaintPhase::Foreground) {
auto absolute_rect = this->absolute_rect();
- absolute_rect.translate_by(enclosing_scroll_frame_offset());
- auto clip_rect = context.rounded_device_rect(absolute_rect);
+ auto clip_rect = context.rounded_device_rect(absolute_rect.translated(enclosing_scroll_frame_offset()));
ScopedCornerRadiusClip corner_clip { context, clip_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) };
auto const* hosted_document = layout_box().dom_node().content_document_without_origin_check();
@@ -54,14 +53,13 @@ void NestedBrowsingContextPaintable::paint(PaintContext& context, PaintPhase pha
context.display_list_recorder().save();
context.display_list_recorder().add_clip_rect(clip_rect.to_type());
- auto absolute_device_rect = context.enclosing_device_rect(absolute_rect);
- context.display_list_recorder().translate(absolute_device_rect.x().value(), absolute_device_rect.y().value());
HTML::Navigable::PaintConfig paint_config;
paint_config.paint_overlay = context.should_paint_overlay();
paint_config.should_show_line_box_borders = context.should_show_line_box_borders();
paint_config.has_focus = context.has_focus();
- const_cast(hosted_document)->navigable()->record_display_list(context.display_list_recorder(), paint_config);
+ auto display_list = const_cast(hosted_document)->navigable()->record_display_list(paint_config);
+ context.display_list_recorder().paint_nested_display_list(display_list, context.enclosing_device_rect(absolute_rect).to_type());
context.display_list_recorder().restore();
diff --git a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp
index 06a12a993c1..33007243c1a 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp
+++ b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp
@@ -92,17 +92,16 @@ RefPtr SVGDecodedImageData::render(Gfx::IntSize size) const
m_document->navigable()->set_viewport_size(size.to_type());
m_document->update_layout();
- auto display_list = Painting::DisplayList::create();
- Painting::DisplayListRecorder display_list_recorder(display_list);
-
- m_document->navigable()->record_display_list(display_list_recorder, {});
+ auto display_list = m_document->navigable()->record_display_list({});
+ if (!display_list)
+ return {};
auto painting_command_executor_type = m_page_client->display_list_player_type();
switch (painting_command_executor_type) {
case DisplayListPlayerType::SkiaGPUIfAvailable:
case DisplayListPlayerType::SkiaCPU: {
Painting::DisplayListPlayerSkia display_list_player { *bitmap };
- display_list_player.execute(display_list);
+ display_list_player.execute(*display_list);
break;
}
default: