mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 12:32:43 +00:00 
			
		
		
		
	LibWeb: Apply scroll boxes offsets after painting commands recording
With this change, instead of applying scroll offsets during the recording of the painting command list, we do the following: 1. Collect all boxes with scrollable overflow into a PaintContext, each with an id and the total amount of scrolling offset accumulated from ancestor scrollable boxes. 2. During the recording phase assign a corresponding scroll_frame_id to each command that paints content within a scrollable box. 3. Before executing the recorded commands, translate each command that has a scroll_frame_id by the accumulated scroll offset. This approach has following advantages: - Implementing nested scrollables becomes much simpler, as the recording phase only requires the correct assignment of the nearest scrollable's scroll_frame_id, while the accumulated offset from ancestors is applied subsequently. - The recording of painting commands is not tied to a specific offset within scrollable boxes, which means in the future, it will be possible to update the scrolling offset and repaint without the need to re-record painting commands.
This commit is contained in:
		
							parent
							
								
									d3025668a4
								
							
						
					
					
						commit
						ac6b3c989d
					
				
					 12 changed files with 307 additions and 24 deletions
				
			
		|  | @ -0,0 +1,28 @@ | ||||||
|  | <style> | ||||||
|  |     #container { | ||||||
|  |         width: 300px; | ||||||
|  |         height: 500px; | ||||||
|  |         overflow: auto; | ||||||
|  |         position: relative; | ||||||
|  |         border: 2px solid black; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .content { | ||||||
|  |         background-color: lightblue; | ||||||
|  |         height: 100%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .box { | ||||||
|  |         background-color: magenta; | ||||||
|  |         width: 100px; | ||||||
|  |         height: 50px; | ||||||
|  |         position: absolute; | ||||||
|  |         top: 0px; | ||||||
|  |         right: 50px; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | <div id="container"> | ||||||
|  |     <div class="content"> | ||||||
|  |         <div class="box"></div> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | <link rel="match" href="reference/scrollable-box-with-nested-stacking-context-ref.html" /> | ||||||
|  | <style> | ||||||
|  |     #scrollable-box { | ||||||
|  |         width: 300px; | ||||||
|  |         height: 500px; | ||||||
|  |         overflow: auto; | ||||||
|  |         position: relative; | ||||||
|  |         border: 2px solid black; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .content { | ||||||
|  |         height: 1000px; | ||||||
|  |         background-color: lightblue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .abspos { | ||||||
|  |         position: absolute; | ||||||
|  |         bottom: 0px; | ||||||
|  |         right: 0px; | ||||||
|  |         width: 150px; | ||||||
|  |         height: 50px; | ||||||
|  |         background-color: coral; | ||||||
|  |         z-index: 1000; | ||||||
|  |         display: flex; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .box { | ||||||
|  |         background-color: magenta; | ||||||
|  |         width: 100px; | ||||||
|  |         height: 100px; | ||||||
|  |         position: relative; | ||||||
|  |         top: 0px; | ||||||
|  |         left: 0px; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | <div id="scrollable-box"> | ||||||
|  |     <div class="content"> | ||||||
|  |         Lots of scrollable content here! | ||||||
|  |         <div class="box">box</div> | ||||||
|  |     </div> | ||||||
|  |     <div class="abspos"><div class="box">box</div></div> | ||||||
|  | </div> | ||||||
|  | <script> | ||||||
|  |     const scrollContainer = document.getElementById("scrollable-box"); | ||||||
|  |     scrollContainer.scrollTop = 500; | ||||||
|  | </script> | ||||||
|  | @ -80,12 +80,22 @@ struct DrawGlyph { | ||||||
|     FloatPoint position; |     FloatPoint position; | ||||||
|     u32 code_point; |     u32 code_point; | ||||||
|     NonnullRefPtr<Font const> font; |     NonnullRefPtr<Font const> font; | ||||||
|  | 
 | ||||||
|  |     void translate_by(FloatPoint const& delta) | ||||||
|  |     { | ||||||
|  |         position.translate_by(delta); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawEmoji { | struct DrawEmoji { | ||||||
|     FloatPoint position; |     FloatPoint position; | ||||||
|     Gfx::Bitmap const* emoji; |     Gfx::Bitmap const* emoji; | ||||||
|     NonnullRefPtr<Font const> font; |     NonnullRefPtr<Font const> font; | ||||||
|  | 
 | ||||||
|  |     void translate_by(FloatPoint const& delta) | ||||||
|  |     { | ||||||
|  |         position.translate_by(delta); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using DrawGlyphOrEmoji = Variant<DrawGlyph, DrawEmoji>; | using DrawGlyphOrEmoji = Variant<DrawGlyph, DrawEmoji>; | ||||||
|  |  | ||||||
|  | @ -72,11 +72,14 @@ public: | ||||||
| 
 | 
 | ||||||
|     double device_pixels_per_css_pixel() const { return m_device_pixels_per_css_pixel; } |     double device_pixels_per_css_pixel() const { return m_device_pixels_per_css_pixel; } | ||||||
| 
 | 
 | ||||||
|     CSSPixelPoint scroll_offset() const { return m_scroll_offset; } |  | ||||||
|     void translate_scroll_offset_by(CSSPixelPoint offset) { m_scroll_offset.translate_by(offset); } |  | ||||||
| 
 |  | ||||||
|     u32 allocate_corner_clipper_id() { return m_next_corner_clipper_id++; } |     u32 allocate_corner_clipper_id() { return m_next_corner_clipper_id++; } | ||||||
| 
 | 
 | ||||||
|  |     struct ScrollFrame { | ||||||
|  |         i32 id { -1 }; | ||||||
|  |         CSSPixelPoint offset; | ||||||
|  |     }; | ||||||
|  |     HashMap<Painting::PaintableBox const*, ScrollFrame>& scroll_frames() { return m_scroll_frames; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     Painting::RecordingPainter& m_recording_painter; |     Painting::RecordingPainter& m_recording_painter; | ||||||
|     Palette m_palette; |     Palette m_palette; | ||||||
|  | @ -85,9 +88,9 @@ private: | ||||||
|     bool m_should_show_line_box_borders { false }; |     bool m_should_show_line_box_borders { false }; | ||||||
|     bool m_should_paint_overlay { true }; |     bool m_should_paint_overlay { true }; | ||||||
|     bool m_focus { false }; |     bool m_focus { false }; | ||||||
|     CSSPixelPoint m_scroll_offset; |  | ||||||
|     Gfx::AffineTransform m_svg_transform; |     Gfx::AffineTransform m_svg_transform; | ||||||
|     u32 m_next_corner_clipper_id { 0 }; |     u32 m_next_corner_clipper_id { 0 }; | ||||||
|  |     HashMap<Painting::PaintableBox const*, ScrollFrame> m_scroll_frames; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -410,16 +410,17 @@ Optional<CSSPixelRect> PaintableBox::calculate_overflow_clipped_rect() const | ||||||
| 
 | 
 | ||||||
| void PaintableBox::apply_scroll_offset(PaintContext& context, PaintPhase) const | void PaintableBox::apply_scroll_offset(PaintContext& context, PaintPhase) const | ||||||
| { | { | ||||||
|     auto scroll_offset = -this->scroll_offset(); |     if (context.scroll_frames().contains(this)) { | ||||||
|     context.translate_scroll_offset_by(scroll_offset); |         context.recording_painter().save(); | ||||||
|     context.recording_painter().translate({ context.enclosing_device_pixels(scroll_offset.x()), context.enclosing_device_pixels(scroll_offset.y()) }); |         context.recording_painter().set_scroll_frame_id(context.scroll_frames().get(this)->id); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PaintableBox::reset_scroll_offset(PaintContext& context, PaintPhase) const | void PaintableBox::reset_scroll_offset(PaintContext& context, PaintPhase) const | ||||||
| { | { | ||||||
|     auto scroll_offset = this->scroll_offset(); |     if (context.scroll_frames().contains(this)) { | ||||||
|     context.translate_scroll_offset_by(scroll_offset); |         context.recording_painter().restore(); | ||||||
|     context.recording_painter().translate({ context.enclosing_device_pixels(scroll_offset.x()), context.enclosing_device_pixels(scroll_offset.y()) }); |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase phase) const | void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase phase) const | ||||||
|  | @ -445,10 +446,7 @@ void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase ph | ||||||
| 
 | 
 | ||||||
|     if (!m_clipping_overflow) { |     if (!m_clipping_overflow) { | ||||||
|         context.recording_painter().save(); |         context.recording_painter().save(); | ||||||
|         auto scroll_offset = context.scroll_offset(); |  | ||||||
|         context.recording_painter().translate({ -context.enclosing_device_pixels(scroll_offset.x()), -context.enclosing_device_pixels(scroll_offset.y()) }); |  | ||||||
|         context.recording_painter().add_clip_rect(context.enclosing_device_rect(*clip_rect).to_type<int>()); |         context.recording_painter().add_clip_rect(context.enclosing_device_rect(*clip_rect).to_type<int>()); | ||||||
|         context.recording_painter().translate({ context.enclosing_device_pixels(scroll_offset.x()), context.enclosing_device_pixels(scroll_offset.y()) }); |  | ||||||
|         m_clipping_overflow = true; |         m_clipping_overflow = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -826,10 +824,12 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PaintableBox const* PaintableBox::nearest_scrollable_ancestor() const | PaintableBox const* PaintableBox::nearest_scrollable_ancestor_within_stacking_context() const | ||||||
| { | { | ||||||
|     auto* ancestor = parent(); |     auto* ancestor = parent(); | ||||||
|     while (ancestor) { |     while (ancestor) { | ||||||
|  |         if (ancestor->stacking_context_rooted_here()) | ||||||
|  |             return nullptr; | ||||||
|         if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow()) |         if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow()) | ||||||
|             return static_cast<PaintableBox const*>(ancestor); |             return static_cast<PaintableBox const*>(ancestor); | ||||||
|         ancestor = ancestor->parent(); |         ancestor = ancestor->parent(); | ||||||
|  |  | ||||||
|  | @ -194,7 +194,7 @@ public: | ||||||
|     void set_box_shadow_data(Vector<ShadowData> box_shadow_data) { m_box_shadow_data = move(box_shadow_data); } |     void set_box_shadow_data(Vector<ShadowData> box_shadow_data) { m_box_shadow_data = move(box_shadow_data); } | ||||||
|     Vector<ShadowData> const& box_shadow_data() const { return m_box_shadow_data; } |     Vector<ShadowData> const& box_shadow_data() const { return m_box_shadow_data; } | ||||||
| 
 | 
 | ||||||
|     PaintableBox const* nearest_scrollable_ancestor() const; |     PaintableBox const* nearest_scrollable_ancestor_within_stacking_context() const; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     explicit PaintableBox(Layout::Box const&); |     explicit PaintableBox(Layout::Box const&); | ||||||
|  |  | ||||||
|  | @ -9,11 +9,31 @@ | ||||||
| 
 | 
 | ||||||
| namespace Web::Painting { | namespace Web::Painting { | ||||||
| 
 | 
 | ||||||
|  | void DrawGlyphRun::translate_by(Gfx::IntPoint const& offset) | ||||||
|  | { | ||||||
|  |     for (auto& glyph : glyph_run) { | ||||||
|  |         glyph.visit([&](auto& glyph) { | ||||||
|  |             glyph.translate_by(offset.to_type<float>()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     rect.translate_by(offset); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const | Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const | ||||||
| { | { | ||||||
|     return get_outer_box_shadow_bounding_rect(outer_box_shadow_params); |     return get_outer_box_shadow_bounding_rect(outer_box_shadow_params); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void PaintOuterBoxShadow::translate_by(Gfx::IntPoint const& offset) | ||||||
|  | { | ||||||
|  |     outer_box_shadow_params.device_content_rect.translate_by(offset.to_type<DevicePixels>()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PaintInnerBoxShadow::translate_by(Gfx::IntPoint const& offset) | ||||||
|  | { | ||||||
|  |     outer_box_shadow_params.device_content_rect.translate_by(offset.to_type<DevicePixels>()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Gfx::IntRect SampleUnderCorners::bounding_rect() const | Gfx::IntRect SampleUnderCorners::bounding_rect() const | ||||||
| { | { | ||||||
|     return border_rect; |     return border_rect; | ||||||
|  | @ -303,8 +323,8 @@ void RecordingPainter::push_stacking_context(PushStackingContextParams params) | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::pop_stacking_context() | void RecordingPainter::pop_stacking_context() | ||||||
| { | { | ||||||
|     push_command(PopStackingContext {}); |  | ||||||
|     m_state_stack.take_last(); |     m_state_stack.take_last(); | ||||||
|  |     push_command(PopStackingContext {}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RecordingPainter::paint_frame(Gfx::IntRect rect, Palette palette, Gfx::FrameStyle style) | void RecordingPainter::paint_frame(Gfx::IntRect rect, Palette palette, Gfx::FrameStyle style) | ||||||
|  | @ -405,11 +425,27 @@ static Optional<Gfx::IntRect> command_bounding_rectangle(PaintingCommand const& | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void RecordingPainter::apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id) | ||||||
|  | { | ||||||
|  |     for (auto& command_with_scroll_id : m_painting_commands) { | ||||||
|  |         if (command_with_scroll_id.scroll_frame_id.has_value()) { | ||||||
|  |             auto const& scroll_frame_id = command_with_scroll_id.scroll_frame_id.value(); | ||||||
|  |             auto const& scroll_offset = offsets_by_frame_id[scroll_frame_id]; | ||||||
|  |             command_with_scroll_id.command.visit( | ||||||
|  |                 [&](auto& command) { | ||||||
|  |                     if constexpr (requires { command.translate_by(scroll_offset); }) | ||||||
|  |                         command.translate_by(scroll_offset); | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void RecordingPainter::execute(PaintingCommandExecutor& executor) | void RecordingPainter::execute(PaintingCommandExecutor& executor) | ||||||
| { | { | ||||||
|     if (executor.needs_prepare_glyphs_texture()) { |     if (executor.needs_prepare_glyphs_texture()) { | ||||||
|         HashMap<Gfx::Font const*, HashTable<u32>> unique_glyphs; |         HashMap<Gfx::Font const*, HashTable<u32>> unique_glyphs; | ||||||
|         for (auto& command : m_painting_commands) { |         for (auto& command_with_scroll_id : m_painting_commands) { | ||||||
|  |             auto& command = command_with_scroll_id.command; | ||||||
|             if (command.has<DrawGlyphRun>()) { |             if (command.has<DrawGlyphRun>()) { | ||||||
|                 for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run) { |                 for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run) { | ||||||
|                     if (glyph_or_emoji.has<Gfx::DrawGlyph>()) { |                     if (glyph_or_emoji.has<Gfx::DrawGlyph>()) { | ||||||
|  | @ -424,7 +460,8 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor) | ||||||
| 
 | 
 | ||||||
|     if (executor.needs_update_immutable_bitmap_texture_cache()) { |     if (executor.needs_update_immutable_bitmap_texture_cache()) { | ||||||
|         HashMap<u32, Gfx::ImmutableBitmap const*> immutable_bitmaps; |         HashMap<u32, Gfx::ImmutableBitmap const*> immutable_bitmaps; | ||||||
|         for (auto const& command : m_painting_commands) { |         for (auto const& command_with_scroll_id : m_painting_commands) { | ||||||
|  |             auto& command = command_with_scroll_id.command; | ||||||
|             if (command.has<DrawScaledImmutableBitmap>()) { |             if (command.has<DrawScaledImmutableBitmap>()) { | ||||||
|                 auto const& immutable_bitmap = command.get<DrawScaledImmutableBitmap>().bitmap; |                 auto const& immutable_bitmap = command.get<DrawScaledImmutableBitmap>().bitmap; | ||||||
|                 immutable_bitmaps.set(immutable_bitmap->id(), immutable_bitmap.ptr()); |                 immutable_bitmaps.set(immutable_bitmap->id(), immutable_bitmap.ptr()); | ||||||
|  | @ -436,7 +473,8 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor) | ||||||
|     HashTable<u32> skipped_sample_corner_commands; |     HashTable<u32> skipped_sample_corner_commands; | ||||||
|     size_t next_command_index = 0; |     size_t next_command_index = 0; | ||||||
|     while (next_command_index < m_painting_commands.size()) { |     while (next_command_index < m_painting_commands.size()) { | ||||||
|         auto& command = m_painting_commands[next_command_index++]; |         auto& command_with_scroll_id = m_painting_commands[next_command_index++]; | ||||||
|  |         auto& command = command_with_scroll_id.command; | ||||||
|         auto bounding_rect = command_bounding_rectangle(command); |         auto bounding_rect = command_bounding_rectangle(command); | ||||||
|         if (bounding_rect.has_value() && (bounding_rect->is_empty() || executor.would_be_fully_clipped_by_painter(*bounding_rect))) { |         if (bounding_rect.has_value() && (bounding_rect->is_empty() || executor.would_be_fully_clipped_by_painter(*bounding_rect))) { | ||||||
|             if (command.has<SampleUnderCorners>()) { |             if (command.has<SampleUnderCorners>()) { | ||||||
|  | @ -555,9 +593,9 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor) | ||||||
|         if (result == CommandResult::SkipStackingContext) { |         if (result == CommandResult::SkipStackingContext) { | ||||||
|             auto stacking_context_nesting_level = 1; |             auto stacking_context_nesting_level = 1; | ||||||
|             while (next_command_index < m_painting_commands.size()) { |             while (next_command_index < m_painting_commands.size()) { | ||||||
|                 if (m_painting_commands[next_command_index].has<PushStackingContext>()) { |                 if (m_painting_commands[next_command_index].command.has<PushStackingContext>()) { | ||||||
|                     stacking_context_nesting_level++; |                     stacking_context_nesting_level++; | ||||||
|                 } else if (m_painting_commands[next_command_index].has<PopStackingContext>()) { |                 } else if (m_painting_commands[next_command_index].command.has<PopStackingContext>()) { | ||||||
|                     stacking_context_nesting_level--; |                     stacking_context_nesting_level--; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -47,6 +47,8 @@ struct DrawGlyphRun { | ||||||
|     Gfx::IntRect rect; |     Gfx::IntRect rect; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawText { | struct DrawText { | ||||||
|  | @ -59,6 +61,7 @@ struct DrawText { | ||||||
|     Optional<NonnullRefPtr<Gfx::Font>> font {}; |     Optional<NonnullRefPtr<Gfx::Font>> font {}; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct FillRect { | struct FillRect { | ||||||
|  | @ -66,6 +69,7 @@ struct FillRect { | ||||||
|     Color color; |     Color color; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawScaledBitmap { | struct DrawScaledBitmap { | ||||||
|  | @ -75,6 +79,7 @@ struct DrawScaledBitmap { | ||||||
|     Gfx::Painter::ScalingMode scaling_mode; |     Gfx::Painter::ScalingMode scaling_mode; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawScaledImmutableBitmap { | struct DrawScaledImmutableBitmap { | ||||||
|  | @ -84,6 +89,7 @@ struct DrawScaledImmutableBitmap { | ||||||
|     Gfx::Painter::ScalingMode scaling_mode; |     Gfx::Painter::ScalingMode scaling_mode; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct SetClipRect { | struct SetClipRect { | ||||||
|  | @ -116,6 +122,11 @@ struct PushStackingContext { | ||||||
|     CSS::ImageRendering image_rendering; |     CSS::ImageRendering image_rendering; | ||||||
|     StackingContextTransform transform; |     StackingContextTransform transform; | ||||||
|     Optional<StackingContextMask> mask = {}; |     Optional<StackingContextMask> mask = {}; | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         post_transform_translation.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PopStackingContext { }; | struct PopStackingContext { }; | ||||||
|  | @ -125,16 +136,24 @@ struct PaintLinearGradient { | ||||||
|     LinearGradientData linear_gradient_data; |     LinearGradientData linear_gradient_data; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         gradient_rect.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintOuterBoxShadow { | struct PaintOuterBoxShadow { | ||||||
|     PaintOuterBoxShadowParams outer_box_shadow_params; |     PaintOuterBoxShadowParams outer_box_shadow_params; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const; |     [[nodiscard]] Gfx::IntRect bounding_rect() const; | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintInnerBoxShadow { | struct PaintInnerBoxShadow { | ||||||
|     PaintOuterBoxShadowParams outer_box_shadow_params; |     PaintOuterBoxShadowParams outer_box_shadow_params; | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintTextShadow { | struct PaintTextShadow { | ||||||
|  | @ -147,6 +166,7 @@ struct PaintTextShadow { | ||||||
|     Gfx::IntPoint draw_location; |     Gfx::IntPoint draw_location; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return { draw_location, shadow_bounding_rect.size() }; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return { draw_location, shadow_bounding_rect.size() }; } | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { draw_location.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct FillRectWithRoundedCorners { | struct FillRectWithRoundedCorners { | ||||||
|  | @ -158,6 +178,7 @@ struct FillRectWithRoundedCorners { | ||||||
|     Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius; |     Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct FillPathUsingColor { | struct FillPathUsingColor { | ||||||
|  | @ -168,6 +189,12 @@ struct FillPathUsingColor { | ||||||
|     Gfx::FloatPoint aa_translation; |     Gfx::FloatPoint aa_translation; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         path_bounding_rect.translate_by(offset); | ||||||
|  |         aa_translation.translate_by(offset.to_type<float>()); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct FillPathUsingPaintStyle { | struct FillPathUsingPaintStyle { | ||||||
|  | @ -179,6 +206,12 @@ struct FillPathUsingPaintStyle { | ||||||
|     Gfx::FloatPoint aa_translation; |     Gfx::FloatPoint aa_translation; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         path_bounding_rect.translate_by(offset); | ||||||
|  |         aa_translation.translate_by(offset.to_type<float>()); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct StrokePathUsingColor { | struct StrokePathUsingColor { | ||||||
|  | @ -189,6 +222,12 @@ struct StrokePathUsingColor { | ||||||
|     Gfx::FloatPoint aa_translation; |     Gfx::FloatPoint aa_translation; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         path_bounding_rect.translate_by(offset); | ||||||
|  |         aa_translation.translate_by(offset.to_type<float>()); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct StrokePathUsingPaintStyle { | struct StrokePathUsingPaintStyle { | ||||||
|  | @ -200,6 +239,12 @@ struct StrokePathUsingPaintStyle { | ||||||
|     Gfx::FloatPoint aa_translation; |     Gfx::FloatPoint aa_translation; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         path_bounding_rect.translate_by(offset); | ||||||
|  |         aa_translation.translate_by(offset.to_type<float>()); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawEllipse { | struct DrawEllipse { | ||||||
|  | @ -208,6 +253,11 @@ struct DrawEllipse { | ||||||
|     int thickness; |     int thickness; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         rect.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct FillEllipse { | struct FillEllipse { | ||||||
|  | @ -216,6 +266,11 @@ struct FillEllipse { | ||||||
|     Gfx::AntiAliasingPainter::BlendMode blend_mode; |     Gfx::AntiAliasingPainter::BlendMode blend_mode; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         rect.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawLine { | struct DrawLine { | ||||||
|  | @ -225,6 +280,12 @@ struct DrawLine { | ||||||
|     int thickness; |     int thickness; | ||||||
|     Gfx::Painter::LineStyle style; |     Gfx::Painter::LineStyle style; | ||||||
|     Color alternate_color; |     Color alternate_color; | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         from.translate_by(offset); | ||||||
|  |         to.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawSignedDistanceField { | struct DrawSignedDistanceField { | ||||||
|  | @ -234,6 +295,11 @@ struct DrawSignedDistanceField { | ||||||
|     float smoothing; |     float smoothing; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         rect.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintFrame { | struct PaintFrame { | ||||||
|  | @ -242,6 +308,8 @@ struct PaintFrame { | ||||||
|     Gfx::FrameStyle style; |     Gfx::FrameStyle style; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct ApplyBackdropFilter { | struct ApplyBackdropFilter { | ||||||
|  | @ -250,6 +318,11 @@ struct ApplyBackdropFilter { | ||||||
|     CSS::ResolvedBackdropFilter backdrop_filter; |     CSS::ResolvedBackdropFilter backdrop_filter; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         backdrop_region.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawRect { | struct DrawRect { | ||||||
|  | @ -258,6 +331,8 @@ struct DrawRect { | ||||||
|     bool rough; |     bool rough; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintRadialGradient { | struct PaintRadialGradient { | ||||||
|  | @ -267,6 +342,8 @@ struct PaintRadialGradient { | ||||||
|     Gfx::IntSize size; |     Gfx::IntSize size; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintConicGradient { | struct PaintConicGradient { | ||||||
|  | @ -275,6 +352,8 @@ struct PaintConicGradient { | ||||||
|     Gfx::IntPoint position; |     Gfx::IntPoint position; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct DrawTriangleWave { | struct DrawTriangleWave { | ||||||
|  | @ -283,6 +362,12 @@ struct DrawTriangleWave { | ||||||
|     Color color; |     Color color; | ||||||
|     int amplitude; |     int amplitude; | ||||||
|     int thickness; |     int thickness; | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         p1.translate_by(offset); | ||||||
|  |         p2.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct SampleUnderCorners { | struct SampleUnderCorners { | ||||||
|  | @ -292,6 +377,11 @@ struct SampleUnderCorners { | ||||||
|     CornerClip corner_clip; |     CornerClip corner_clip; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const; |     [[nodiscard]] Gfx::IntRect bounding_rect() const; | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         border_rect.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct BlitCornerClipping { | struct BlitCornerClipping { | ||||||
|  | @ -299,6 +389,11 @@ struct BlitCornerClipping { | ||||||
|     Gfx::IntRect border_rect; |     Gfx::IntRect border_rect; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const; |     [[nodiscard]] Gfx::IntRect bounding_rect() const; | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         border_rect.translate_by(offset); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct PaintBorders { | struct PaintBorders { | ||||||
|  | @ -307,6 +402,11 @@ struct PaintBorders { | ||||||
|     BordersDataDevicePixels borders_data; |     BordersDataDevicePixels borders_data; | ||||||
| 
 | 
 | ||||||
|     [[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect.to_type<int>(); } |     [[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect.to_type<int>(); } | ||||||
|  | 
 | ||||||
|  |     void translate_by(Gfx::IntPoint const& offset) | ||||||
|  |     { | ||||||
|  |         border_rect.translate_by(offset.to_type<DevicePixels>()); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using PaintingCommand = Variant< | using PaintingCommand = Variant< | ||||||
|  | @ -457,6 +557,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     void set_font(Gfx::Font const& font); |     void set_font(Gfx::Font const& font); | ||||||
| 
 | 
 | ||||||
|  |     void set_scroll_frame_id(i32 id) | ||||||
|  |     { | ||||||
|  |         state().scroll_frame_id = id; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void save(); |     void save(); | ||||||
|     void restore(); |     void restore(); | ||||||
| 
 | 
 | ||||||
|  | @ -497,20 +602,28 @@ public: | ||||||
|         m_state_stack.append(State()); |         m_state_stack.append(State()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     struct State { |     struct State { | ||||||
|         Gfx::AffineTransform translation; |         Gfx::AffineTransform translation; | ||||||
|         Optional<Gfx::IntRect> clip_rect; |         Optional<Gfx::IntRect> clip_rect; | ||||||
|  |         Optional<i32> scroll_frame_id; | ||||||
|     }; |     }; | ||||||
|     State& state() { return m_state_stack.last(); } |     State& state() { return m_state_stack.last(); } | ||||||
|     State const& state() const { return m_state_stack.last(); } |     State const& state() const { return m_state_stack.last(); } | ||||||
| 
 | 
 | ||||||
|     void push_command(PaintingCommand command) |     void push_command(PaintingCommand command) | ||||||
|     { |     { | ||||||
|         m_painting_commands.append(command); |         m_painting_commands.append({ state().scroll_frame_id, command }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Vector<PaintingCommand> m_painting_commands; |     struct PaintingCommandWithScrollFrame { | ||||||
|  |         Optional<i32> scroll_frame_id; | ||||||
|  |         PaintingCommand command; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Vector<PaintingCommandWithScrollFrame> m_painting_commands; | ||||||
|     Vector<State> m_state_stack; |     Vector<State> m_state_stack; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -184,8 +184,16 @@ void StackingContext::paint_child(PaintContext& context, StackingContext const& | ||||||
|     if (parent_paintable) |     if (parent_paintable) | ||||||
|         parent_paintable->before_children_paint(context, PaintPhase::Foreground); |         parent_paintable->before_children_paint(context, PaintPhase::Foreground); | ||||||
| 
 | 
 | ||||||
|  |     PaintableBox const* nearest_scrollable_ancestor = child.paintable_box().nearest_scrollable_ancestor_within_stacking_context(); | ||||||
|  | 
 | ||||||
|  |     if (nearest_scrollable_ancestor) | ||||||
|  |         nearest_scrollable_ancestor->apply_scroll_offset(context, PaintPhase::Foreground); | ||||||
|  | 
 | ||||||
|     child.paint(context); |     child.paint(context); | ||||||
| 
 | 
 | ||||||
|  |     if (nearest_scrollable_ancestor) | ||||||
|  |         nearest_scrollable_ancestor->reset_scroll_offset(context, PaintPhase::Foreground); | ||||||
|  | 
 | ||||||
|     if (parent_paintable) |     if (parent_paintable) | ||||||
|         parent_paintable->after_children_paint(context, PaintPhase::Foreground); |         parent_paintable->after_children_paint(context, PaintPhase::Foreground); | ||||||
| } | } | ||||||
|  | @ -229,7 +237,7 @@ void StackingContext::paint_internal(PaintContext& context) const | ||||||
|         // Apply scroll offset of nearest scrollable ancestor before painting the positioned descendant.
 |         // Apply scroll offset of nearest scrollable ancestor before painting the positioned descendant.
 | ||||||
|         PaintableBox const* nearest_scrollable_ancestor = nullptr; |         PaintableBox const* nearest_scrollable_ancestor = nullptr; | ||||||
|         if (paintable.is_paintable_box()) |         if (paintable.is_paintable_box()) | ||||||
|             nearest_scrollable_ancestor = static_cast<PaintableBox const&>(paintable).nearest_scrollable_ancestor(); |             nearest_scrollable_ancestor = static_cast<PaintableBox const&>(paintable).nearest_scrollable_ancestor_within_stacking_context(); | ||||||
|         if (nearest_scrollable_ancestor) |         if (nearest_scrollable_ancestor) | ||||||
|             nearest_scrollable_ancestor->apply_scroll_offset(context, PaintPhase::Foreground); |             nearest_scrollable_ancestor->apply_scroll_offset(context, PaintPhase::Foreground); | ||||||
| 
 | 
 | ||||||
|  | @ -265,6 +273,12 @@ void StackingContext::paint_internal(PaintContext& context) const | ||||||
|     for (auto* child : m_children) { |     for (auto* child : m_children) { | ||||||
|         if (!child->paintable_box().is_positioned()) |         if (!child->paintable_box().is_positioned()) | ||||||
|             continue; |             continue; | ||||||
|  | 
 | ||||||
|  |         PaintableBox const* nearest_scrollable_ancestor = child->paintable_box().nearest_scrollable_ancestor_within_stacking_context(); | ||||||
|  | 
 | ||||||
|  |         if (nearest_scrollable_ancestor) | ||||||
|  |             nearest_scrollable_ancestor->apply_scroll_offset(context, PaintPhase::Foreground); | ||||||
|  | 
 | ||||||
|         auto containing_block = child->paintable_box().containing_block(); |         auto containing_block = child->paintable_box().containing_block(); | ||||||
|         auto const* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr; |         auto const* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr; | ||||||
|         if (containing_block_paintable) |         if (containing_block_paintable) | ||||||
|  | @ -273,6 +287,9 @@ void StackingContext::paint_internal(PaintContext& context) const | ||||||
|             paint_child(context, *child); |             paint_child(context, *child); | ||||||
|         if (containing_block_paintable) |         if (containing_block_paintable) | ||||||
|             containing_block_paintable->clear_clip_overflow_rect(context, PaintPhase::Foreground); |             containing_block_paintable->clear_clip_overflow_rect(context, PaintPhase::Foreground); | ||||||
|  | 
 | ||||||
|  |         if (nearest_scrollable_ancestor) | ||||||
|  |             nearest_scrollable_ancestor->reset_scroll_offset(context, PaintPhase::Foreground); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     paint_node(paintable_box(), context, PaintPhase::Outline); |     paint_node(paintable_box(), context, PaintPhase::Outline); | ||||||
|  |  | ||||||
|  | @ -57,4 +57,22 @@ void ViewportPaintable::paint_all_phases(PaintContext& context) | ||||||
|     stacking_context()->paint(context); |     stacking_context()->paint(context); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void ViewportPaintable::collect_scroll_frames(PaintContext& context) const | ||||||
|  | { | ||||||
|  |     i32 next_id = 0; | ||||||
|  |     for_each_in_subtree_of_type<PaintableBox>([&](auto const& paintable_box) { | ||||||
|  |         if (paintable_box.has_scrollable_overflow()) { | ||||||
|  |             auto offset = paintable_box.scroll_offset(); | ||||||
|  |             auto ancestor = paintable_box.parent(); | ||||||
|  |             while (ancestor) { | ||||||
|  |                 if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow()) | ||||||
|  |                     offset.translate_by(static_cast<PaintableBox const*>(ancestor)->scroll_offset()); | ||||||
|  |                 ancestor = ancestor->parent(); | ||||||
|  |             } | ||||||
|  |             context.scroll_frames().set(&paintable_box, { .id = next_id++, .offset = -offset }); | ||||||
|  |         } | ||||||
|  |         return TraversalDecision::Continue; | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -20,6 +20,8 @@ public: | ||||||
|     void paint_all_phases(PaintContext&); |     void paint_all_phases(PaintContext&); | ||||||
|     void build_stacking_context_tree_if_needed(); |     void build_stacking_context_tree_if_needed(); | ||||||
| 
 | 
 | ||||||
|  |     void collect_scroll_frames(PaintContext&) const; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     void build_stacking_context_tree(); |     void build_stacking_context_tree(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -188,8 +188,16 @@ void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& ta | ||||||
|     context.set_should_paint_overlay(paint_options.paint_overlay == Web::PaintOptions::PaintOverlay::Yes); |     context.set_should_paint_overlay(paint_options.paint_overlay == Web::PaintOptions::PaintOverlay::Yes); | ||||||
|     context.set_device_viewport_rect(content_rect); |     context.set_device_viewport_rect(content_rect); | ||||||
|     context.set_has_focus(m_has_focus); |     context.set_has_focus(m_has_focus); | ||||||
|  | 
 | ||||||
|  |     document->paintable()->collect_scroll_frames(context); | ||||||
|     document->paintable()->paint_all_phases(context); |     document->paintable()->paint_all_phases(context); | ||||||
| 
 | 
 | ||||||
|  |     Vector<Gfx::IntPoint> scroll_offsets_by_frame_id; | ||||||
|  |     scroll_offsets_by_frame_id.resize(context.scroll_frames().size()); | ||||||
|  |     for (auto [_, scrollable_frame] : context.scroll_frames()) | ||||||
|  |         scroll_offsets_by_frame_id[scrollable_frame.id] = context.rounded_device_point(scrollable_frame.offset).to_type<int>(); | ||||||
|  |     recording_painter.apply_scroll_offsets(scroll_offsets_by_frame_id); | ||||||
|  | 
 | ||||||
|     if (s_use_gpu_painter) { |     if (s_use_gpu_painter) { | ||||||
| #ifdef HAS_ACCELERATED_GRAPHICS | #ifdef HAS_ACCELERATED_GRAPHICS | ||||||
|         Web::Painting::PaintingCommandExecutorGPU painting_command_executor(*m_accelerated_graphics_context, target); |         Web::Painting::PaintingCommandExecutorGPU painting_command_executor(*m_accelerated_graphics_context, target); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Aliaksandr Kalenik
						Aliaksandr Kalenik