diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index a02badb4e44..091db40eacc 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -6622,6 +6622,15 @@ ElementByIdMap& Document::element_by_id() const return *m_element_by_id; } +String Document::dump_display_list() +{ + update_layout(UpdateLayoutReason::DumpDisplayList); + auto display_list = record_display_list(HTML::PaintConfig {}); + if (!display_list) + return {}; + return display_list->dump(); +} + GC::Ptr ElementByIdMap::get(FlyString const& element_id) const { if (auto elements = m_map.get(element_id); elements.has_value() && !elements->is_empty()) { diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index cb4a93a38fe..c743a24e7d1 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -71,6 +71,7 @@ enum class InvalidateLayoutTreeReason { X(DocumentElementsFromPoint) \ X(DocumentFindMatchingText) \ X(DocumentSetDesignMode) \ + X(DumpDisplayList) \ X(ElementCheckVisibility) \ X(ElementClientHeight) \ X(ElementClientLeft) \ @@ -903,6 +904,8 @@ public: auto& script_blocking_style_sheet_set() { return m_script_blocking_style_sheet_set; } auto const& script_blocking_style_sheet_set() const { return m_script_blocking_style_sheet_set; } + String dump_display_list(); + protected: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; diff --git a/Libraries/LibWeb/Internals/Internals.cpp b/Libraries/LibWeb/Internals/Internals.cpp index 8aa181d2e7c..48fd657e2f0 100644 --- a/Libraries/LibWeb/Internals/Internals.cpp +++ b/Libraries/LibWeb/Internals/Internals.cpp @@ -257,4 +257,9 @@ bool Internals::headless() return page().client().is_headless(); } +String Internals::dump_display_list() +{ + return window().associated_document().dump_display_list(); +} + } diff --git a/Libraries/LibWeb/Internals/Internals.h b/Libraries/LibWeb/Internals/Internals.h index 806d0f23492..4842ef5b520 100644 --- a/Libraries/LibWeb/Internals/Internals.h +++ b/Libraries/LibWeb/Internals/Internals.h @@ -62,6 +62,8 @@ public: bool headless(); + String dump_display_list(); + private: explicit Internals(JS::Realm&); diff --git a/Libraries/LibWeb/Internals/Internals.idl b/Libraries/LibWeb/Internals/Internals.idl index 30e61fc6d16..193c192466c 100644 --- a/Libraries/LibWeb/Internals/Internals.idl +++ b/Libraries/LibWeb/Internals/Internals.idl @@ -50,4 +50,6 @@ interface Internals { undefined setBrowserZoom(double factor); readonly attribute boolean headless; + + DOMString dumpDisplayList(); }; diff --git a/Libraries/LibWeb/Painting/Command.cpp b/Libraries/LibWeb/Painting/Command.cpp index f745bd36860..ab43fcd2f8a 100644 --- a/Libraries/LibWeb/Painting/Command.cpp +++ b/Libraries/LibWeb/Painting/Command.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2024-2025, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ @@ -41,4 +41,194 @@ void PaintInnerBoxShadow::translate_by(Gfx::IntPoint const& offset) box_shadow_params.device_content_rect.translate_by(offset); } +void DrawGlyphRun::dump(StringBuilder& builder) const +{ + builder.appendff("DrawGlyphRun rect={} translation={} color={} scale={}", rect, translation, color, scale); +} + +void FillRect::dump(StringBuilder& builder) const +{ + builder.appendff("FillRect rect={} color={}", rect, color); +} + +void DrawPaintingSurface::dump(StringBuilder& builder) const +{ + builder.appendff("DrawPaintingSurface dst_rect={} src_rect={}", dst_rect, src_rect); +} + +void DrawScaledImmutableBitmap::dump(StringBuilder& builder) const +{ + builder.appendff("DrawScaledImmutableBitmap dst_rect={} clip_rect={}", dst_rect, clip_rect); +} + +void DrawRepeatedImmutableBitmap::dump(StringBuilder& builder) const +{ + builder.appendff("DrawRepeatedImmutableBitmap dst_rect={} clip_rect={}", dst_rect, clip_rect); +} + +void Save::dump(StringBuilder& builder) const +{ + builder.appendff("Save"); +} + +void SaveLayer::dump(StringBuilder& builder) const +{ + builder.appendff("SaveLayer"); +} + +void Restore::dump(StringBuilder& builder) const +{ + builder.appendff("Restore"); +} + +void Translate::dump(StringBuilder& builder) const +{ + builder.appendff("Translate delta={}", delta); +} + +void AddClipRect::dump(StringBuilder& builder) const +{ + builder.appendff("AddClipRect rect={}", rect); +} + +void PushStackingContext::dump(StringBuilder& builder) const +{ + builder.appendff("PushStackingContext"); +} + +void PopStackingContext::dump(StringBuilder& builder) const +{ + builder.appendff("PopStackingContext"); +} + +void PaintLinearGradient::dump(StringBuilder& builder) const +{ + builder.appendff("PaintLinearGradient rect={}", gradient_rect); +} + +void PaintRadialGradient::dump(StringBuilder& builder) const +{ + builder.appendff("PaintRadialGradient rect={} center={} size={}", rect, center, size); +} + +void PaintConicGradient::dump(StringBuilder& builder) const +{ + builder.appendff("PaintConicGradient rect={} position={} angle={}", rect, position, conic_gradient_data.start_angle); +} + +void PaintOuterBoxShadow::dump(StringBuilder& builder) const +{ + builder.appendff("PaintOuterBoxShadow content_rect={} offset=({},{}) blur_radius={} spread_distance={} color={}", box_shadow_params.device_content_rect, box_shadow_params.offset_x, box_shadow_params.offset_y, box_shadow_params.blur_radius, box_shadow_params.spread_distance, box_shadow_params.color); +} + +void PaintInnerBoxShadow::dump(StringBuilder& builder) const +{ + builder.appendff("PaintInnerBoxShadow content_rect={} offset=({},{}) blur_radius={} spread_distance={} color={}", box_shadow_params.device_content_rect, box_shadow_params.offset_x, box_shadow_params.offset_y, box_shadow_params.blur_radius, box_shadow_params.spread_distance, box_shadow_params.color); +} + +void PaintTextShadow::dump(StringBuilder& builder) const +{ + builder.appendff("PaintTextShadow shadow_rect={} text_rect={} draw_location={} blur_radius={} color={} scale={}", shadow_bounding_rect, text_rect, draw_location, blur_radius, color, glyph_run_scale); +} + +void FillRectWithRoundedCorners::dump(StringBuilder& builder) const +{ + builder.appendff("FillRectWithRoundedCorners rect={} color={}", rect, color); +} + +void FillPathUsingColor::dump(StringBuilder& builder) const +{ + builder.appendff("FillPathUsingColor"); +} + +void FillPathUsingPaintStyle::dump(StringBuilder& builder) const +{ + builder.appendff("FillPathUsingPaintStyle"); +} + +void StrokePathUsingColor::dump(StringBuilder& builder) const +{ + builder.appendff("StrokePathUsingColor"); +} + +void StrokePathUsingPaintStyle::dump(StringBuilder& builder) const +{ + builder.appendff("StrokePathUsingPaintStyle"); +} + +void DrawEllipse::dump(StringBuilder& builder) const +{ + builder.appendff("DrawEllipse rect={} color={} thickness={}", rect, color, thickness); +} + +void FillEllipse::dump(StringBuilder& builder) const +{ + builder.appendff("FillEllipse rect={} color={}", rect, color); +} + +void DrawLine::dump(StringBuilder& builder) const +{ + builder.appendff("DrawLine from={} to={} color={} thickness={}", from, to, color, thickness); +} + +void ApplyBackdropFilter::dump(StringBuilder& builder) const +{ + builder.appendff("ApplyBackdropFilter backdrop_region={}", backdrop_region); +} + +void DrawRect::dump(StringBuilder& builder) const +{ + builder.appendff("DrawRect rect={} color={} rough={}", rect, color, rough); +} + +void DrawTriangleWave::dump(StringBuilder& builder) const +{ + builder.appendff("DrawTriangleWave p1={} p2={} color={} amplitude={} thickness={}", p1, p2, color, amplitude, thickness); +} + +void AddRoundedRectClip::dump(StringBuilder& builder) const +{ + builder.appendff("AddRoundedRectClip rect={}", border_rect); +} + +void AddMask::dump(StringBuilder& builder) const +{ + builder.appendff("AddMask rect={} has_display_list={}", rect, display_list != nullptr); +} + +void PaintNestedDisplayList::dump(StringBuilder& builder) const +{ + builder.appendff("PaintNestedDisplayList rect={}", rect); +} + +void PaintScrollBar::dump(StringBuilder& builder) const +{ + builder.appendff("PaintScrollBar"); +} + +void ApplyOpacity::dump(StringBuilder& builder) const +{ + builder.appendff("ApplyOpacity opacity={}", opacity); +} + +void ApplyCompositeAndBlendingOperator::dump(StringBuilder& builder) const +{ + builder.appendff("ApplyCompositeAndBlendingOperator"); +} + +void ApplyFilter::dump(StringBuilder& builder) const +{ + builder.appendff("ApplyFilter"); +} + +void ApplyTransform::dump(StringBuilder& builder) const +{ + builder.appendff("ApplyTransform"); +} + +void ApplyMaskBitmap::dump(StringBuilder& builder) const +{ + builder.appendff("ApplyMaskBitmap"); +} + } diff --git a/Libraries/LibWeb/Painting/Command.h b/Libraries/LibWeb/Painting/Command.h index efe48e1180b..25a7289da72 100644 --- a/Libraries/LibWeb/Painting/Command.h +++ b/Libraries/LibWeb/Painting/Command.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Aliaksandr Kalenik + * Copyright (c) 2024-2025, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -17,16 +16,13 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include -#include #include #include #include @@ -48,6 +44,7 @@ struct DrawGlyphRun { Gfx::Orientation orientation { Gfx::Orientation::Horizontal }; void translate_by(Gfx::IntPoint const& offset); + void dump(StringBuilder&) const; }; struct FillRect { @@ -56,6 +53,7 @@ struct FillRect { [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct DrawPaintingSurface { @@ -66,6 +64,7 @@ struct DrawPaintingSurface { [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; } void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct DrawScaledImmutableBitmap { @@ -80,6 +79,7 @@ struct DrawScaledImmutableBitmap { dst_rect.translate_by(offset); clip_rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct DrawRepeatedImmutableBitmap { @@ -95,16 +95,26 @@ struct DrawRepeatedImmutableBitmap { Repeat repeat; void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } + void dump(StringBuilder&) const; }; -struct Save { }; -struct SaveLayer { }; -struct Restore { }; +struct Save { + void dump(StringBuilder&) const; +}; + +struct SaveLayer { + void dump(StringBuilder&) const; +}; + +struct Restore { + void dump(StringBuilder&) const; +}; struct Translate { Gfx::IntPoint delta; void translate_by(Gfx::IntPoint const& offset) { delta.translate_by(offset); } + void dump(StringBuilder&) const; }; struct AddClipRect { @@ -113,6 +123,7 @@ struct AddClipRect { [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } bool is_clip_or_mask() const { return true; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct PushStackingContext { @@ -133,9 +144,12 @@ struct PushStackingContext { clip_path.value().transform(Gfx::AffineTransform().translate(offset.to_type())); } } + void dump(StringBuilder&) const; }; -struct PopStackingContext { }; +struct PopStackingContext { + void dump(StringBuilder&) const; +}; struct PaintLinearGradient { Gfx::IntRect gradient_rect; @@ -147,6 +161,7 @@ struct PaintLinearGradient { { gradient_rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct PaintOuterBoxShadow { @@ -154,6 +169,7 @@ struct PaintOuterBoxShadow { [[nodiscard]] Gfx::IntRect bounding_rect() const; void translate_by(Gfx::IntPoint const& offset); + void dump(StringBuilder&) const; }; struct PaintInnerBoxShadow { @@ -161,6 +177,7 @@ struct PaintInnerBoxShadow { [[nodiscard]] Gfx::IntRect bounding_rect() const; void translate_by(Gfx::IntPoint const& offset); + void dump(StringBuilder&) const; }; struct PaintTextShadow { @@ -174,6 +191,7 @@ struct PaintTextShadow { [[nodiscard]] Gfx::IntRect bounding_rect() const { return { draw_location.to_type(), shadow_bounding_rect.size() }; } void translate_by(Gfx::IntPoint const& offset) { draw_location.translate_by(offset.to_type()); } + void dump(StringBuilder&) const; }; struct FillRectWithRoundedCorners { @@ -183,6 +201,7 @@ struct FillRectWithRoundedCorners { [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct FillPathUsingColor { @@ -199,6 +218,7 @@ struct FillPathUsingColor { path_bounding_rect.translate_by(offset); aa_translation.translate_by(offset.to_type()); } + void dump(StringBuilder&) const; }; struct FillPathUsingPaintStyle { @@ -216,6 +236,7 @@ struct FillPathUsingPaintStyle { path_bounding_rect.translate_by(offset); aa_translation.translate_by(offset.to_type()); } + void dump(StringBuilder&) const; }; struct StrokePathUsingColor { @@ -237,6 +258,7 @@ struct StrokePathUsingColor { path_bounding_rect.translate_by(offset); aa_translation.translate_by(offset.to_type()); } + void dump(StringBuilder&) const; }; struct StrokePathUsingPaintStyle { @@ -259,6 +281,7 @@ struct StrokePathUsingPaintStyle { path_bounding_rect.translate_by(offset); aa_translation.translate_by(offset.to_type()); } + void dump(StringBuilder&) const; }; struct DrawEllipse { @@ -272,6 +295,7 @@ struct DrawEllipse { { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct FillEllipse { @@ -284,6 +308,7 @@ struct FillEllipse { { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct DrawLine { @@ -299,6 +324,7 @@ struct DrawLine { from.translate_by(offset); to.translate_by(offset); } + void dump(StringBuilder&) const; }; struct ApplyBackdropFilter { @@ -312,6 +338,7 @@ struct ApplyBackdropFilter { { backdrop_region.translate_by(offset); } + void dump(StringBuilder&) const; }; struct DrawRect { @@ -322,6 +349,7 @@ struct DrawRect { [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct PaintRadialGradient { @@ -333,6 +361,7 @@ struct PaintRadialGradient { [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct PaintConicGradient { @@ -343,6 +372,7 @@ struct PaintConicGradient { [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct DrawTriangleWave { @@ -357,6 +387,7 @@ struct DrawTriangleWave { p1.translate_by(offset); p2.translate_by(offset); } + void dump(StringBuilder&) const; }; struct AddRoundedRectClip { @@ -368,6 +399,7 @@ struct AddRoundedRectClip { bool is_clip_or_mask() const { return true; } void translate_by(Gfx::IntPoint const& offset) { border_rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct AddMask { @@ -381,6 +413,8 @@ struct AddMask { { rect.translate_by(offset); } + + void dump(StringBuilder&) const; }; struct PaintNestedDisplayList { @@ -394,6 +428,7 @@ struct PaintNestedDisplayList { { rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct PaintScrollBar { @@ -410,18 +445,22 @@ struct PaintScrollBar { gutter_rect.translate_by(offset); thumb_rect.translate_by(offset); } + void dump(StringBuilder&) const; }; struct ApplyOpacity { float opacity; + void dump(StringBuilder&) const; }; struct ApplyCompositeAndBlendingOperator { Gfx::CompositingAndBlendingOperator compositing_and_blending_operator; + void dump(StringBuilder&) const; }; struct ApplyFilter { Gfx::Filter filter; + void dump(StringBuilder&) const; }; struct ApplyTransform { @@ -432,6 +471,7 @@ struct ApplyTransform { { origin.translate_by(offset.to_type()); } + void dump(StringBuilder&) const; }; struct ApplyMaskBitmap { @@ -443,6 +483,7 @@ struct ApplyMaskBitmap { { origin.translate_by(offset); } + void dump(StringBuilder&) const; }; using Command = Variant< diff --git a/Libraries/LibWeb/Painting/DisplayList.cpp b/Libraries/LibWeb/Painting/DisplayList.cpp index 64f9294e60e..11895b5cdf3 100644 --- a/Libraries/LibWeb/Painting/DisplayList.cpp +++ b/Libraries/LibWeb/Painting/DisplayList.cpp @@ -13,6 +13,16 @@ void DisplayList::append(Command&& command, Optional scroll_frame_id) m_commands.append({ scroll_frame_id, move(command) }); } +String DisplayList::dump() const +{ + StringBuilder builder; + for (auto const& command : m_commands) { + command.command.visit([&builder](auto const& cmd) { cmd.dump(builder); }); + builder.appendff("\n"); + } + return builder.to_string_without_validation(); +} + static Optional command_bounding_rectangle(Command const& command) { return command.visit( diff --git a/Libraries/LibWeb/Painting/DisplayList.h b/Libraries/LibWeb/Painting/DisplayList.h index a85cca088d6..66f219b84e8 100644 --- a/Libraries/LibWeb/Painting/DisplayList.h +++ b/Libraries/LibWeb/Painting/DisplayList.h @@ -96,6 +96,8 @@ public: void set_device_pixels_per_css_pixel(double device_pixels_per_css_pixel) { m_device_pixels_per_css_pixel = device_pixels_per_css_pixel; } double device_pixels_per_css_pixel() const { return m_device_pixels_per_css_pixel; } + String dump() const; + private: DisplayList() = default; diff --git a/Tests/LibWeb/Text/expected/display_list/simple-overflow-hidden.txt b/Tests/LibWeb/Text/expected/display_list/simple-overflow-hidden.txt new file mode 100644 index 00000000000..06526511e7a --- /dev/null +++ b/Tests/LibWeb/Text/expected/display_list/simple-overflow-hidden.txt @@ -0,0 +1,28 @@ +SaveLayer +PushStackingContext +PushStackingContext +Save +AddClipRect rect=[10,10 200x100] +Restore +FillPathUsingColor +Save +AddClipRect rect=[10,10 200x100] +Restore +Save +AddClipRect rect=[10,10 200x100] +FillRect rect=[10,10 300x150] color=rgb(240, 128, 128) +Restore +Save +AddClipRect rect=[10,10 200x100] +Restore +Save +AddClipRect rect=[10,10 200x100] +DrawGlyphRun rect=[10,10 38x18] translation=[10,23.796875] color=rgb(0, 0, 0) scale=1 +Restore +Save +AddClipRect rect=[10,10 200x100] +Restore +PopStackingContext +PopStackingContext +Restore + diff --git a/Tests/LibWeb/Text/input/display_list/simple-overflow-hidden.html b/Tests/LibWeb/Text/input/display_list/simple-overflow-hidden.html new file mode 100644 index 00000000000..284d389b92a --- /dev/null +++ b/Tests/LibWeb/Text/input/display_list/simple-overflow-hidden.html @@ -0,0 +1,30 @@ + + + + +
+
Text
+
+ +