Coverage Report

Created: 2026-06-07 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/Dump.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
3
 * Copyright (c) 2021, the SerenityOS developers.
4
 * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 */
8
9
#include <AK/QuickSort.h>
10
#include <AK/StringBuilder.h>
11
#include <AK/Utf8View.h>
12
#include <LibWeb/CSS/CSSFontFaceRule.h>
13
#include <LibWeb/CSS/CSSImportRule.h>
14
#include <LibWeb/CSS/CSSLayerBlockRule.h>
15
#include <LibWeb/CSS/CSSLayerStatementRule.h>
16
#include <LibWeb/CSS/CSSMediaRule.h>
17
#include <LibWeb/CSS/CSSNestedDeclarations.h>
18
#include <LibWeb/CSS/CSSRule.h>
19
#include <LibWeb/CSS/CSSStyleRule.h>
20
#include <LibWeb/CSS/CSSStyleSheet.h>
21
#include <LibWeb/CSS/CSSSupportsRule.h>
22
#include <LibWeb/CSS/PropertyID.h>
23
#include <LibWeb/CSS/PseudoClass.h>
24
#include <LibWeb/DOM/Comment.h>
25
#include <LibWeb/DOM/Document.h>
26
#include <LibWeb/DOM/Element.h>
27
#include <LibWeb/DOM/ShadowRoot.h>
28
#include <LibWeb/DOM/Text.h>
29
#include <LibWeb/Dump.h>
30
#include <LibWeb/HTML/DocumentState.h>
31
#include <LibWeb/HTML/HTMLImageElement.h>
32
#include <LibWeb/HTML/HTMLTemplateElement.h>
33
#include <LibWeb/HTML/ImageRequest.h>
34
#include <LibWeb/HTML/TraversableNavigable.h>
35
#include <LibWeb/Layout/BlockContainer.h>
36
#include <LibWeb/Layout/FormattingContext.h>
37
#include <LibWeb/Layout/FrameBox.h>
38
#include <LibWeb/Layout/InlineNode.h>
39
#include <LibWeb/Layout/Node.h>
40
#include <LibWeb/Layout/SVGBox.h>
41
#include <LibWeb/Layout/TextNode.h>
42
#include <LibWeb/Layout/Viewport.h>
43
#include <LibWeb/Painting/InlinePaintable.h>
44
#include <LibWeb/Painting/PaintableBox.h>
45
#include <LibWeb/Painting/TextPaintable.h>
46
#include <LibWeb/SVG/SVGDecodedImageData.h>
47
#include <stdio.h>
48
49
namespace Web {
50
51
static void indent(StringBuilder& builder, int levels)
52
0
{
53
0
    for (int i = 0; i < levels; i++)
54
0
        builder.append("  "sv);
55
0
}
56
57
static void dump_session_history_entry(StringBuilder& builder, HTML::SessionHistoryEntry const& session_history_entry, int indent_levels)
58
0
{
59
0
    indent(builder, indent_levels);
60
0
    auto const& document = session_history_entry.document();
61
0
    builder.appendff("step=({}) url=({}) is-active=({})\n", session_history_entry.step().get<int>(), session_history_entry.url(), document && document->is_active());
62
0
    for (auto const& nested_history : session_history_entry.document_state()->nested_histories()) {
63
0
        for (auto const& nested_she : nested_history.entries) {
64
0
            dump_session_history_entry(builder, *nested_she, indent_levels + 1);
65
0
        }
66
0
    }
67
0
}
68
69
void dump_tree(HTML::TraversableNavigable& traversable)
70
0
{
71
0
    StringBuilder builder;
72
0
    for (auto const& she : traversable.session_history_entries()) {
73
0
        dump_session_history_entry(builder, *she, 0);
74
0
    }
75
0
    dbgln("{}", builder.string_view());
76
0
}
77
78
void dump_tree(DOM::Node const& node)
79
0
{
80
0
    StringBuilder builder;
81
0
    dump_tree(builder, node);
82
0
    dbgln("{}", builder.string_view());
83
0
}
84
85
void dump_tree(StringBuilder& builder, DOM::Node const& node)
86
0
{
87
0
    static int indent = 0;
88
0
    for (int i = 0; i < indent; ++i)
89
0
        builder.append("  "sv);
90
0
    if (is<DOM::Element>(node)) {
91
0
        builder.appendff("<{}", verify_cast<DOM::Element>(node).local_name());
92
0
        verify_cast<DOM::Element>(node).for_each_attribute([&](auto& name, auto& value) {
93
0
            builder.appendff(" {}={}", name, value);
94
0
        });
95
0
        builder.append(">\n"sv);
96
0
        auto& element = verify_cast<DOM::Element>(node);
97
0
        if (element.use_pseudo_element().has_value()) {
98
0
            for (int i = 0; i < indent; ++i)
99
0
                builder.append("  "sv);
100
0
            builder.appendff("  (pseudo-element: {})\n", CSS::Selector::PseudoElement::name(element.use_pseudo_element().value()));
101
0
        }
102
0
    } else if (is<DOM::Text>(node)) {
103
0
        builder.appendff("\"{}\"\n", verify_cast<DOM::Text>(node).data());
104
0
    } else {
105
0
        builder.appendff("{}\n", node.node_name());
106
0
    }
107
0
    ++indent;
108
0
    if (is<DOM::Element>(node)) {
109
0
        if (auto shadow_root = static_cast<DOM::Element const&>(node).shadow_root()) {
110
0
            dump_tree(builder, *shadow_root);
111
0
        }
112
0
    }
113
0
    if (is<HTML::HTMLImageElement>(node)) {
114
0
        if (auto image_data = static_cast<HTML::HTMLImageElement const&>(node).current_request().image_data()) {
115
0
            if (is<SVG::SVGDecodedImageData>(*image_data)) {
116
0
                ++indent;
117
0
                for (int i = 0; i < indent; ++i)
118
0
                    builder.append("  "sv);
119
0
                builder.append("(SVG-as-image isolated context)\n"sv);
120
0
                auto& svg_data = verify_cast<SVG::SVGDecodedImageData>(*image_data);
121
0
                dump_tree(builder, svg_data.svg_document());
122
0
                --indent;
123
0
            }
124
0
        }
125
0
    }
126
0
    if (is<DOM::ParentNode>(node)) {
127
0
        if (!is<HTML::HTMLTemplateElement>(node)) {
128
0
            static_cast<DOM::ParentNode const&>(node).for_each_child([&](auto& child) {
129
0
                dump_tree(builder, child);
130
0
                return IterationDecision::Continue;
131
0
            });
132
0
        } else {
133
0
            auto& template_element = verify_cast<HTML::HTMLTemplateElement>(node);
134
0
            dump_tree(builder, template_element.content());
135
0
        }
136
0
    }
137
0
    --indent;
138
0
}
139
140
void dump_tree(Layout::Node const& layout_node, bool show_box_model, bool show_specified_style)
141
0
{
142
0
    StringBuilder builder;
143
0
    dump_tree(builder, layout_node, show_box_model, show_specified_style, true);
144
0
    dbgln("{}", builder.string_view());
145
0
}
146
147
void dump_tree(StringBuilder& builder, Layout::Node const& layout_node, bool show_box_model, bool show_specified_style, bool interactive)
148
0
{
149
0
    static size_t indent = 0;
150
0
    for (size_t i = 0; i < indent; ++i)
151
0
        builder.append("  "sv);
152
153
0
    FlyString tag_name;
154
0
    if (layout_node.is_anonymous())
155
0
        tag_name = "(anonymous)"_fly_string;
156
0
    else if (is<DOM::Element>(layout_node.dom_node()))
157
0
        tag_name = verify_cast<DOM::Element>(*layout_node.dom_node()).local_name();
158
0
    else
159
0
        tag_name = layout_node.dom_node()->node_name();
160
161
0
    String identifier;
162
0
    if (layout_node.dom_node() && is<DOM::Element>(*layout_node.dom_node())) {
163
0
        auto& element = verify_cast<DOM::Element>(*layout_node.dom_node());
164
0
        StringBuilder builder;
165
0
        if (element.id().has_value() && !element.id()->is_empty()) {
166
0
            builder.append('#');
167
0
            builder.append(*element.id());
168
0
        }
169
0
        for (auto& class_name : element.class_names()) {
170
0
            builder.append('.');
171
0
            builder.append(class_name);
172
0
        }
173
0
        identifier = MUST(builder.to_string());
174
0
    }
175
176
0
    StringView nonbox_color_on = ""sv;
177
0
    StringView box_color_on = ""sv;
178
0
    StringView svg_box_color_on = ""sv;
179
0
    StringView positioned_color_on = ""sv;
180
0
    StringView floating_color_on = ""sv;
181
0
    StringView inline_color_on = ""sv;
182
0
    StringView line_box_color_on = ""sv;
183
0
    StringView fragment_color_on = ""sv;
184
0
    StringView flex_color_on = ""sv;
185
0
    StringView table_color_on = ""sv;
186
0
    StringView formatting_context_color_on = ""sv;
187
0
    StringView color_off = ""sv;
188
189
0
    if (interactive) {
190
0
        nonbox_color_on = "\033[33m"sv;
191
0
        box_color_on = "\033[34m"sv;
192
0
        svg_box_color_on = "\033[31m"sv;
193
0
        positioned_color_on = "\033[31;1m"sv;
194
0
        floating_color_on = "\033[32;1m"sv;
195
0
        inline_color_on = "\033[36;1m"sv;
196
0
        line_box_color_on = "\033[34;1m"sv;
197
0
        fragment_color_on = "\033[35;1m"sv;
198
0
        flex_color_on = "\033[34;1m"sv;
199
0
        table_color_on = "\033[91;1m"sv;
200
0
        formatting_context_color_on = "\033[37;1m"sv;
201
0
        color_off = "\033[0m"sv;
202
0
    }
203
204
0
    if (!is<Layout::Box>(layout_node)) {
205
0
        builder.appendff("{}{}{} <{}{}{}{}>",
206
0
            nonbox_color_on,
207
0
            layout_node.class_name(),
208
0
            color_off,
209
0
            tag_name,
210
0
            nonbox_color_on,
211
0
            identifier,
212
0
            color_off);
213
0
        builder.append("\n"sv);
214
0
    } else {
215
0
        auto& box = verify_cast<Layout::Box>(layout_node);
216
0
        StringView color_on = is<Layout::SVGBox>(box) ? svg_box_color_on : box_color_on;
217
218
0
        builder.appendff("{}{}{} <{}{}{}{}> ",
219
0
            color_on,
220
0
            box.class_name(),
221
0
            color_off,
222
0
            color_on,
223
0
            tag_name,
224
0
            color_off,
225
0
            identifier);
226
227
0
        if (auto const* paintable_box = box.paintable_box()) {
228
0
            builder.appendff("at ({},{}) content-size {}x{}",
229
0
                paintable_box->absolute_x(),
230
0
                paintable_box->absolute_y(),
231
0
                paintable_box->content_width(),
232
0
                paintable_box->content_height());
233
0
        } else {
234
0
            builder.appendff("(not painted)");
235
0
        }
236
237
0
        if (box.is_positioned())
238
0
            builder.appendff(" {}positioned{}", positioned_color_on, color_off);
239
0
        if (box.is_floating())
240
0
            builder.appendff(" {}floating{}", floating_color_on, color_off);
241
0
        if (box.is_inline_block())
242
0
            builder.appendff(" {}inline-block{}", inline_color_on, color_off);
243
0
        if (box.is_inline_table())
244
0
            builder.appendff(" {}inline-table{}", inline_color_on, color_off);
245
0
        if (box.display().is_flex_inside()) {
246
0
            StringView direction;
247
0
            switch (box.computed_values().flex_direction()) {
248
0
            case CSS::FlexDirection::Column:
249
0
                direction = "column"sv;
250
0
                break;
251
0
            case CSS::FlexDirection::ColumnReverse:
252
0
                direction = "column-reverse"sv;
253
0
                break;
254
0
            case CSS::FlexDirection::Row:
255
0
                direction = "row"sv;
256
0
                break;
257
0
            case CSS::FlexDirection::RowReverse:
258
0
                direction = "row-reverse"sv;
259
0
                break;
260
0
            }
261
0
            builder.appendff(" {}flex-container({}){}", flex_color_on, direction, color_off);
262
0
        }
263
0
        if (box.is_flex_item())
264
0
            builder.appendff(" {}flex-item{}", flex_color_on, color_off);
265
0
        if (box.display().is_table_inside())
266
0
            builder.appendff(" {}table-box{}", table_color_on, color_off);
267
0
        if (box.display().is_table_row_group())
268
0
            builder.appendff(" {}table-row-group{}", table_color_on, color_off);
269
0
        if (box.display().is_table_column_group())
270
0
            builder.appendff(" {}table-column-group{}", table_color_on, color_off);
271
0
        if (box.display().is_table_header_group())
272
0
            builder.appendff(" {}table-header-group{}", table_color_on, color_off);
273
0
        if (box.display().is_table_footer_group())
274
0
            builder.appendff(" {}table-footer-group{}", table_color_on, color_off);
275
0
        if (box.display().is_table_row())
276
0
            builder.appendff(" {}table-row{}", table_color_on, color_off);
277
0
        if (box.display().is_table_cell())
278
0
            builder.appendff(" {}table-cell{}", table_color_on, color_off);
279
280
0
        if (show_box_model) {
281
            // Dump the horizontal box properties
282
0
            builder.appendff(" [{}+{}+{} {} {}+{}+{}]",
283
0
                box.box_model().margin.left,
284
0
                box.box_model().border.left,
285
0
                box.box_model().padding.left,
286
0
                box.paintable_box() ? box.paintable_box()->content_width() : 0,
287
0
                box.box_model().padding.right,
288
0
                box.box_model().border.right,
289
0
                box.box_model().margin.right);
290
291
            // And the vertical box properties
292
0
            builder.appendff(" [{}+{}+{} {} {}+{}+{}]",
293
0
                box.box_model().margin.top,
294
0
                box.box_model().border.top,
295
0
                box.box_model().padding.top,
296
0
                box.paintable_box() ? box.paintable_box()->content_height() : 0,
297
0
                box.box_model().padding.bottom,
298
0
                box.box_model().border.bottom,
299
0
                box.box_model().margin.bottom);
300
0
        }
301
302
0
        if (auto formatting_context_type = Layout::FormattingContext::formatting_context_type_created_by_box(box); formatting_context_type.has_value()) {
303
0
            switch (formatting_context_type.value()) {
304
0
            case Layout::FormattingContext::Type::Block:
305
0
                builder.appendff(" [{}BFC{}]", formatting_context_color_on, color_off);
306
0
                break;
307
0
            case Layout::FormattingContext::Type::Flex:
308
0
                builder.appendff(" [{}FFC{}]", formatting_context_color_on, color_off);
309
0
                break;
310
0
            case Layout::FormattingContext::Type::Grid:
311
0
                builder.appendff(" [{}GFC{}]", formatting_context_color_on, color_off);
312
0
                break;
313
0
            case Layout::FormattingContext::Type::Table:
314
0
                builder.appendff(" [{}TFC{}]", formatting_context_color_on, color_off);
315
0
                break;
316
0
            case Layout::FormattingContext::Type::SVG:
317
0
                builder.appendff(" [{}SVG{}]", formatting_context_color_on, color_off);
318
0
                break;
319
0
            default:
320
0
                break;
321
0
            }
322
0
        }
323
324
0
        builder.appendff(" children: {}", box.children_are_inline() ? "inline" : "not-inline");
325
326
0
        if (is<Layout::FrameBox>(box)) {
327
0
            auto const& frame_box = static_cast<Layout::FrameBox const&>(box);
328
0
            if (auto* nested_browsing_context = frame_box.dom_node().nested_browsing_context()) {
329
0
                if (auto* document = nested_browsing_context->active_document()) {
330
0
                    builder.appendff(" (url: {})", document->url());
331
0
                }
332
0
            }
333
0
        }
334
335
0
        builder.append("\n"sv);
336
0
    }
337
338
0
    if (layout_node.dom_node() && is<HTML::HTMLImageElement>(*layout_node.dom_node())) {
339
0
        if (auto image_data = static_cast<HTML::HTMLImageElement const&>(*layout_node.dom_node()).current_request().image_data()) {
340
0
            if (is<SVG::SVGDecodedImageData>(*image_data)) {
341
0
                auto& svg_data = verify_cast<SVG::SVGDecodedImageData>(*image_data);
342
0
                if (svg_data.svg_document().layout_node()) {
343
0
                    ++indent;
344
0
                    for (size_t i = 0; i < indent; ++i)
345
0
                        builder.append("  "sv);
346
0
                    builder.append("(SVG-as-image isolated context)\n"sv);
347
348
0
                    dump_tree(builder, *svg_data.svg_document().layout_node(), show_box_model, show_specified_style, interactive);
349
0
                    --indent;
350
0
                }
351
0
            }
352
0
        }
353
0
    }
354
355
0
    auto dump_fragment = [&](auto& fragment, size_t fragment_index) {
356
0
        for (size_t i = 0; i < indent; ++i)
357
0
            builder.append("  "sv);
358
0
        builder.appendff("  {}frag {}{} from {} ",
359
0
            fragment_color_on,
360
0
            fragment_index,
361
0
            color_off,
362
0
            fragment.layout_node().class_name());
363
0
        builder.appendff("start: {}, length: {}, rect: {} baseline: {}\n",
364
0
            fragment.start(),
365
0
            fragment.length(),
366
0
            fragment.absolute_rect(),
367
0
            fragment.baseline());
368
0
        if (is<Layout::TextNode>(fragment.layout_node())) {
369
0
            for (size_t i = 0; i < indent; ++i)
370
0
                builder.append("  "sv);
371
0
            auto const& layout_text = static_cast<Layout::TextNode const&>(fragment.layout_node());
372
0
            auto fragment_text = MUST(layout_text.text_for_rendering().substring_from_byte_offset(fragment.start(), fragment.length()));
373
0
            builder.appendff("      \"{}\"\n", fragment_text);
374
0
        }
375
0
    };
376
377
0
    if (is<Layout::BlockContainer>(layout_node) && static_cast<Layout::BlockContainer const&>(layout_node).children_are_inline()) {
378
0
        auto& block = static_cast<Layout::BlockContainer const&>(layout_node);
379
0
        for (size_t fragment_index = 0; block.paintable_with_lines() && fragment_index < block.paintable_with_lines()->fragments().size(); ++fragment_index) {
380
0
            auto const& fragment = block.paintable_with_lines()->fragments()[fragment_index];
381
0
            dump_fragment(fragment, fragment_index);
382
0
        }
383
0
    }
384
385
0
    if (is<Layout::InlineNode>(layout_node) && layout_node.paintable()) {
386
0
        auto const& inline_node = static_cast<Layout::InlineNode const&>(layout_node);
387
0
        auto const& inline_paintable = static_cast<Painting::InlinePaintable const&>(*inline_node.paintable());
388
0
        auto const& fragments = inline_paintable.fragments();
389
0
        for (size_t fragment_index = 0; fragment_index < fragments.size(); ++fragment_index) {
390
0
            auto const& fragment = fragments[fragment_index];
391
0
            dump_fragment(fragment, fragment_index);
392
0
        }
393
0
    }
394
395
0
    if (show_specified_style && layout_node.dom_node() && layout_node.dom_node()->is_element() && verify_cast<DOM::Element>(layout_node.dom_node())->computed_css_values()) {
396
0
        struct NameAndValue {
397
0
            FlyString name;
398
0
            String value;
399
0
        };
400
0
        Vector<NameAndValue> properties;
401
0
        verify_cast<DOM::Element>(*layout_node.dom_node()).computed_css_values()->for_each_property([&](auto property_id, auto& value) {
402
0
            properties.append({ CSS::string_from_property_id(property_id), value.to_string() });
403
0
        });
404
0
        quick_sort(properties, [](auto& a, auto& b) { return a.name < b.name; });
405
406
0
        for (auto& property : properties) {
407
0
            for (size_t i = 0; i < indent; ++i)
408
0
                builder.append("    "sv);
409
0
            builder.appendff("  ({}: {})\n", property.name, property.value);
410
0
        }
411
0
    }
412
413
0
    ++indent;
414
0
    layout_node.for_each_child([&](auto& child) {
415
0
        dump_tree(builder, child, show_box_model, show_specified_style, interactive);
416
0
        return IterationDecision::Continue;
417
0
    });
418
0
    --indent;
419
0
}
420
421
void dump_selector(CSS::Selector const& selector)
422
0
{
423
0
    StringBuilder builder;
424
0
    dump_selector(builder, selector);
425
0
    dbgln("{}", builder.string_view());
426
0
}
427
428
static void dump_qualified_name(StringBuilder& builder, CSS::Selector::SimpleSelector::QualifiedName const& qualified_name)
429
0
{
430
0
    StringView namespace_type;
431
0
    switch (qualified_name.namespace_type) {
432
0
    case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Default:
433
0
        namespace_type = "Default"sv;
434
0
        break;
435
0
    case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::None:
436
0
        namespace_type = "None"sv;
437
0
        break;
438
0
    case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Any:
439
0
        namespace_type = "Any"sv;
440
0
        break;
441
0
    case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Named:
442
0
        namespace_type = "Named"sv;
443
0
        break;
444
0
    }
445
0
    builder.appendff("NamespaceType={}, Namespace='{}', Name='{}'", namespace_type, qualified_name.namespace_, qualified_name.name.name);
446
0
}
447
448
void dump_selector(StringBuilder& builder, CSS::Selector const& selector, int indent_levels)
449
0
{
450
0
    indent(builder, indent_levels);
451
0
    builder.append("CSS::Selector:\n"sv);
452
453
0
    for (auto& relative_selector : selector.compound_selectors()) {
454
0
        indent(builder, indent_levels + 1);
455
456
0
        char const* relation_description = "";
457
0
        switch (relative_selector.combinator) {
458
0
        case CSS::Selector::Combinator::None:
459
0
            relation_description = "None";
460
0
            break;
461
0
        case CSS::Selector::Combinator::ImmediateChild:
462
0
            relation_description = "ImmediateChild";
463
0
            break;
464
0
        case CSS::Selector::Combinator::Descendant:
465
0
            relation_description = "Descendant";
466
0
            break;
467
0
        case CSS::Selector::Combinator::NextSibling:
468
0
            relation_description = "AdjacentSibling";
469
0
            break;
470
0
        case CSS::Selector::Combinator::SubsequentSibling:
471
0
            relation_description = "GeneralSibling";
472
0
            break;
473
0
        case CSS::Selector::Combinator::Column:
474
0
            relation_description = "Column";
475
0
            break;
476
0
        }
477
478
0
        if (*relation_description)
479
0
            builder.appendff("{{{}}} ", relation_description);
480
481
0
        for (size_t i = 0; i < relative_selector.simple_selectors.size(); ++i) {
482
0
            auto& simple_selector = relative_selector.simple_selectors[i];
483
0
            char const* type_description = "Unknown";
484
0
            switch (simple_selector.type) {
485
0
            case CSS::Selector::SimpleSelector::Type::Universal:
486
0
                type_description = "Universal";
487
0
                break;
488
0
            case CSS::Selector::SimpleSelector::Type::Id:
489
0
                type_description = "Id";
490
0
                break;
491
0
            case CSS::Selector::SimpleSelector::Type::Class:
492
0
                type_description = "Class";
493
0
                break;
494
0
            case CSS::Selector::SimpleSelector::Type::TagName:
495
0
                type_description = "TagName";
496
0
                break;
497
0
            case CSS::Selector::SimpleSelector::Type::Attribute:
498
0
                type_description = "Attribute";
499
0
                break;
500
0
            case CSS::Selector::SimpleSelector::Type::PseudoClass:
501
0
                type_description = "PseudoClassSelector";
502
0
                break;
503
0
            case CSS::Selector::SimpleSelector::Type::PseudoElement:
504
0
                type_description = "PseudoElement";
505
0
                break;
506
0
            case CSS::Selector::SimpleSelector::Type::Nesting:
507
0
                type_description = "Nesting";
508
0
                break;
509
0
            }
510
511
0
            builder.appendff("{}:", type_description);
512
513
            // FIXME: This is goofy
514
0
            if (simple_selector.value.has<CSS::Selector::SimpleSelector::Name>()) {
515
0
                builder.append(simple_selector.name());
516
0
            } else if (simple_selector.value.has<CSS::Selector::SimpleSelector::QualifiedName>()) {
517
0
                dump_qualified_name(builder, simple_selector.qualified_name());
518
0
            }
519
520
0
            if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
521
0
                auto const& pseudo_class = simple_selector.pseudo_class();
522
523
0
                builder.appendff(" pseudo_class={}", CSS::pseudo_class_name(pseudo_class.type));
524
0
                auto pseudo_class_metadata = CSS::pseudo_class_metadata(pseudo_class.type);
525
526
0
                switch (pseudo_class_metadata.parameter_type) {
527
0
                case CSS::PseudoClassMetadata::ParameterType::None:
528
0
                    break;
529
0
                case CSS::PseudoClassMetadata::ParameterType::ANPlusB:
530
0
                case CSS::PseudoClassMetadata::ParameterType::ANPlusBOf: {
531
0
                    builder.appendff("(step={}, offset={}", pseudo_class.nth_child_pattern.step_size, pseudo_class.nth_child_pattern.offset);
532
0
                    if (!pseudo_class.argument_selector_list.is_empty()) {
533
0
                        builder.append(", selectors=[\n"sv);
534
0
                        for (auto const& child_selector : pseudo_class.argument_selector_list)
535
0
                            dump_selector(builder, child_selector, indent_levels + 2);
536
0
                        indent(builder, indent_levels + 1);
537
0
                        builder.append("]"sv);
538
0
                    }
539
0
                    builder.append(")"sv);
540
0
                    break;
541
0
                }
542
0
                case CSS::PseudoClassMetadata::ParameterType::CompoundSelector:
543
0
                case CSS::PseudoClassMetadata::ParameterType::ForgivingSelectorList:
544
0
                case CSS::PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList:
545
0
                case CSS::PseudoClassMetadata::ParameterType::SelectorList: {
546
0
                    builder.append("([\n"sv);
547
0
                    for (auto& child_selector : pseudo_class.argument_selector_list)
548
0
                        dump_selector(builder, child_selector, indent_levels + 2);
549
0
                    indent(builder, indent_levels + 1);
550
0
                    builder.append("])"sv);
551
0
                    break;
552
0
                }
553
0
                case CSS::PseudoClassMetadata::ParameterType::Ident:
554
0
                    builder.appendff("(keyword={})", string_from_keyword(pseudo_class.keyword.value()));
555
0
                    break;
556
0
                case CSS::PseudoClassMetadata::ParameterType::LanguageRanges: {
557
0
                    builder.append('(');
558
0
                    builder.join(',', pseudo_class.languages);
559
0
                    builder.append(')');
560
0
                    break;
561
0
                }
562
0
                }
563
0
            }
564
565
0
            if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) {
566
0
                builder.appendff(" pseudo_element={}", simple_selector.pseudo_element().name());
567
0
            }
568
569
0
            if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) {
570
0
                auto const& attribute = simple_selector.attribute();
571
0
                char const* attribute_match_type_description = "";
572
573
0
                switch (attribute.match_type) {
574
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::HasAttribute:
575
0
                    attribute_match_type_description = "HasAttribute";
576
0
                    break;
577
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch:
578
0
                    attribute_match_type_description = "ExactValueMatch";
579
0
                    break;
580
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::ContainsWord:
581
0
                    attribute_match_type_description = "ContainsWord";
582
0
                    break;
583
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::ContainsString:
584
0
                    attribute_match_type_description = "ContainsString";
585
0
                    break;
586
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment:
587
0
                    attribute_match_type_description = "StartsWithSegment";
588
0
                    break;
589
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::StartsWithString:
590
0
                    attribute_match_type_description = "StartsWithString";
591
0
                    break;
592
0
                case CSS::Selector::SimpleSelector::Attribute::MatchType::EndsWithString:
593
0
                    attribute_match_type_description = "EndsWithString";
594
0
                    break;
595
0
                }
596
597
0
                builder.appendff(" [{}, ", attribute_match_type_description);
598
0
                dump_qualified_name(builder, attribute.qualified_name);
599
0
                builder.appendff(", value='{}']", attribute.value);
600
0
            }
601
602
0
            if (i != relative_selector.simple_selectors.size() - 1)
603
0
                builder.append(", "sv);
604
0
        }
605
0
        builder.append("\n"sv);
606
0
    }
607
0
}
608
609
void dump_rule(CSS::CSSRule const& rule)
610
0
{
611
0
    StringBuilder builder;
612
0
    dump_rule(builder, rule);
613
0
    dbgln("{}", builder.string_view());
614
0
}
615
616
void dump_rule(StringBuilder& builder, CSS::CSSRule const& rule, int indent_levels)
617
0
{
618
0
    indent(builder, indent_levels);
619
0
    builder.appendff("{}:\n", rule.class_name());
620
621
0
    switch (rule.type()) {
622
0
    case CSS::CSSRule::Type::FontFace:
623
0
        dump_font_face_rule(builder, verify_cast<CSS::CSSFontFaceRule const>(rule), indent_levels);
624
0
        break;
625
0
    case CSS::CSSRule::Type::Import:
626
0
        dump_import_rule(builder, verify_cast<CSS::CSSImportRule const>(rule), indent_levels);
627
0
        break;
628
0
    case CSS::CSSRule::Type::Keyframe:
629
0
    case CSS::CSSRule::Type::Keyframes:
630
        // TODO: Dump them!
631
0
        break;
632
0
    case CSS::CSSRule::Type::LayerBlock:
633
0
        dump_layer_block_rule(builder, verify_cast<CSS::CSSLayerBlockRule const>(rule), indent_levels);
634
0
        break;
635
0
    case CSS::CSSRule::Type::LayerStatement:
636
0
        dump_layer_statement_rule(builder, verify_cast<CSS::CSSLayerStatementRule const>(rule), indent_levels);
637
0
        break;
638
0
    case CSS::CSSRule::Type::Media:
639
0
        dump_media_rule(builder, verify_cast<CSS::CSSMediaRule const>(rule), indent_levels);
640
0
        break;
641
0
    case CSS::CSSRule::Type::Namespace:
642
0
        dump_namespace_rule(builder, verify_cast<CSS::CSSNamespaceRule const>(rule), indent_levels);
643
0
        break;
644
0
    case CSS::CSSRule::Type::NestedDeclarations:
645
0
        dump_nested_declarations(builder, verify_cast<CSS::CSSNestedDeclarations const>(rule), indent_levels);
646
0
        break;
647
0
    case CSS::CSSRule::Type::Style:
648
0
        dump_style_rule(builder, verify_cast<CSS::CSSStyleRule const>(rule), indent_levels);
649
0
        break;
650
0
    case CSS::CSSRule::Type::Supports:
651
0
        dump_supports_rule(builder, verify_cast<CSS::CSSSupportsRule const>(rule), indent_levels);
652
0
        break;
653
0
    }
654
0
}
655
656
void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rule, int indent_levels)
657
0
{
658
0
    auto& font_face = rule.font_face();
659
0
    indent(builder, indent_levels + 1);
660
0
    builder.appendff("font-family: {}\n", font_face.font_family());
661
662
0
    if (font_face.weight().has_value()) {
663
0
        indent(builder, indent_levels + 1);
664
0
        builder.appendff("weight: {}\n", font_face.weight().value());
665
0
    }
666
667
0
    if (font_face.slope().has_value()) {
668
0
        indent(builder, indent_levels + 1);
669
0
        builder.appendff("slope: {}\n", font_face.slope().value());
670
0
    }
671
672
0
    if (font_face.width().has_value()) {
673
0
        indent(builder, indent_levels + 1);
674
0
        builder.appendff("width: {}\n", font_face.width().value());
675
0
    }
676
677
0
    indent(builder, indent_levels + 1);
678
0
    builder.append("sources:\n"sv);
679
0
    for (auto const& source : font_face.sources()) {
680
0
        indent(builder, indent_levels + 2);
681
0
        if (source.local_or_url.has<URL::URL>())
682
0
            builder.appendff("url={}, format={}\n", source.local_or_url.get<URL::URL>(), source.format.value_or("???"_string));
683
0
        else
684
0
            builder.appendff("local={}\n", source.local_or_url.get<AK::String>());
685
0
    }
686
687
0
    indent(builder, indent_levels + 1);
688
0
    builder.append("unicode-ranges:\n"sv);
689
0
    for (auto const& unicode_range : font_face.unicode_ranges()) {
690
0
        indent(builder, indent_levels + 2);
691
0
        builder.appendff("{}\n", unicode_range.to_string());
692
0
    }
693
694
0
    if (font_face.ascent_override().has_value()) {
695
0
        indent(builder, indent_levels + 1);
696
0
        builder.appendff("ascent-override: {}\n", font_face.ascent_override().value());
697
0
    }
698
0
    if (font_face.descent_override().has_value()) {
699
0
        indent(builder, indent_levels + 1);
700
0
        builder.appendff("descent-override: {}\n", font_face.descent_override().value());
701
0
    }
702
0
    if (font_face.line_gap_override().has_value()) {
703
0
        indent(builder, indent_levels + 1);
704
0
        builder.appendff("line-gap-override: {}\n", font_face.line_gap_override().value());
705
0
    }
706
707
0
    indent(builder, indent_levels + 1);
708
0
    builder.appendff("display: {}\n", CSS::to_string(font_face.font_display()));
709
710
0
    if (font_face.font_named_instance().has_value()) {
711
0
        indent(builder, indent_levels + 1);
712
0
        builder.appendff("named-instance: {}\n", font_face.font_named_instance().value());
713
0
    }
714
715
0
    if (font_face.font_language_override().has_value()) {
716
0
        indent(builder, indent_levels + 1);
717
0
        builder.appendff("language-override: {}\n", font_face.font_language_override().value());
718
0
    }
719
720
0
    if (font_face.font_feature_settings().has_value()) {
721
0
        indent(builder, indent_levels + 1);
722
0
        builder.append("feature-settings:"sv);
723
0
        auto const& entries = font_face.font_feature_settings().value();
724
0
        for (auto const& [name, value] : entries) {
725
0
            builder.appendff(" {}={}", name, value);
726
0
        }
727
0
        builder.append("\n"sv);
728
0
    }
729
730
0
    if (font_face.font_variation_settings().has_value()) {
731
0
        indent(builder, indent_levels + 1);
732
0
        builder.append("variation-settings:"sv);
733
0
        auto const& entries = font_face.font_variation_settings().value();
734
0
        for (auto const& [name, value] : entries) {
735
0
            builder.appendff(" {}={}", name, value);
736
0
        }
737
0
        builder.append("\n"sv);
738
0
    }
739
0
}
740
741
void dump_import_rule(StringBuilder& builder, CSS::CSSImportRule const& rule, int indent_levels)
742
0
{
743
0
    indent(builder, indent_levels);
744
0
    builder.appendff("  Document URL: {}\n", rule.url());
745
0
}
746
747
void dump_layer_block_rule(StringBuilder& builder, CSS::CSSLayerBlockRule const& layer_block, int indent_levels)
748
0
{
749
0
    indent(builder, indent_levels);
750
0
    builder.appendff("  Layer Block: `{}`\n", layer_block.internal_name());
751
0
    indent(builder, indent_levels);
752
0
    builder.appendff("  Rules ({}):\n", layer_block.css_rules().length());
753
0
    for (auto& rule : layer_block.css_rules())
754
0
        dump_rule(builder, rule, indent_levels + 2);
755
0
}
756
757
void dump_layer_statement_rule(StringBuilder& builder, CSS::CSSLayerStatementRule const& layer_statement, int indent_levels)
758
0
{
759
0
    indent(builder, indent_levels);
760
0
    builder.append("  Layer Statement: "sv);
761
0
    builder.join(", "sv, layer_statement.name_list());
762
0
}
763
764
void dump_media_rule(StringBuilder& builder, CSS::CSSMediaRule const& media, int indent_levels)
765
0
{
766
0
    indent(builder, indent_levels);
767
0
    builder.appendff("  Media: {}\n", media.condition_text());
768
0
    indent(builder, indent_levels);
769
0
    builder.appendff("  Rules ({}):\n", media.css_rules().length());
770
771
0
    for (auto& rule : media.css_rules())
772
0
        dump_rule(builder, rule, indent_levels + 2);
773
0
}
774
775
void dump_supports_rule(StringBuilder& builder, CSS::CSSSupportsRule const& supports, int indent_levels)
776
0
{
777
0
    indent(builder, indent_levels);
778
0
    builder.appendff("  Supports: {}\n", supports.condition_text());
779
0
    indent(builder, indent_levels);
780
0
    builder.appendff("  Rules ({}):\n", supports.css_rules().length());
781
782
0
    for (auto& rule : supports.css_rules())
783
0
        dump_rule(builder, rule, indent_levels + 2);
784
0
}
785
786
void dump_declaration(StringBuilder& builder, CSS::PropertyOwningCSSStyleDeclaration const& declaration, int indent_levels)
787
0
{
788
0
    indent(builder, indent_levels);
789
0
    builder.appendff("Declarations ({}):\n", declaration.length());
790
0
    for (auto& property : declaration.properties()) {
791
0
        indent(builder, indent_levels);
792
0
        builder.appendff("  {}: '{}'", CSS::string_from_property_id(property.property_id), property.value->to_string());
793
0
        if (property.important == CSS::Important::Yes)
794
0
            builder.append(" \033[31;1m!important\033[0m"sv);
795
0
        builder.append('\n');
796
0
    }
797
0
    for (auto& property : declaration.custom_properties()) {
798
0
        indent(builder, indent_levels);
799
0
        builder.appendff("  {}: '{}'", property.key, property.value.value->to_string());
800
0
        if (property.value.important == CSS::Important::Yes)
801
0
            builder.append(" \033[31;1m!important\033[0m"sv);
802
0
        builder.append('\n');
803
0
    }
804
0
}
805
806
void dump_style_rule(StringBuilder& builder, CSS::CSSStyleRule const& rule, int indent_levels)
807
0
{
808
0
    for (auto& selector : rule.selectors()) {
809
0
        dump_selector(builder, selector, indent_levels + 1);
810
0
    }
811
0
    dump_declaration(builder, rule.declaration(), indent_levels + 1);
812
813
0
    indent(builder, indent_levels);
814
0
    builder.appendff("  Child rules ({}):\n", rule.css_rules().length());
815
0
    for (auto& child_rule : rule.css_rules())
816
0
        dump_rule(builder, child_rule, indent_levels + 2);
817
0
}
818
819
void dump_sheet(CSS::StyleSheet const& sheet)
820
0
{
821
0
    StringBuilder builder;
822
0
    dump_sheet(builder, sheet);
823
0
    dbgln("{}", builder.string_view());
824
0
}
825
826
void dump_sheet(StringBuilder& builder, CSS::StyleSheet const& sheet)
827
0
{
828
0
    auto& css_stylesheet = verify_cast<CSS::CSSStyleSheet>(sheet);
829
830
0
    builder.appendff("CSSStyleSheet{{{}}}: {} rule(s)\n", &sheet, css_stylesheet.rules().length());
831
832
0
    for (auto& rule : css_stylesheet.rules())
833
0
        dump_rule(builder, rule);
834
0
}
835
836
void dump_tree(Painting::Paintable const& paintable)
837
0
{
838
0
    StringBuilder builder;
839
0
    dump_tree(builder, paintable, true);
840
0
    dbgln("{}", builder.string_view());
841
0
}
842
843
void dump_tree(StringBuilder& builder, Painting::Paintable const& paintable, bool colorize, int indent)
844
0
{
845
0
    for (int i = 0; i < indent; ++i)
846
0
        builder.append("  "sv);
847
848
0
    StringView paintable_with_lines_color_on = ""sv;
849
0
    StringView paintable_box_color_on = ""sv;
850
0
    StringView text_paintable_color_on = ""sv;
851
0
    StringView paintable_color_on = ""sv;
852
0
    StringView color_off = ""sv;
853
854
0
    if (colorize) {
855
0
        paintable_with_lines_color_on = "\033[34m"sv;
856
0
        paintable_box_color_on = "\033[33m"sv;
857
0
        text_paintable_color_on = "\033[35m"sv;
858
0
        paintable_color_on = "\033[32m"sv;
859
0
        color_off = "\033[0m"sv;
860
0
    }
861
862
0
    if (is<Painting::PaintableWithLines>(paintable))
863
0
        builder.append(paintable_with_lines_color_on);
864
0
    else if (is<Painting::PaintableBox>(paintable))
865
0
        builder.append(paintable_box_color_on);
866
0
    else if (is<Painting::TextPaintable>(paintable))
867
0
        builder.append(text_paintable_color_on);
868
0
    else
869
0
        builder.append(paintable_color_on);
870
871
0
    builder.appendff("{}{} ({})", paintable.class_name(), color_off, paintable.layout_node().debug_description());
872
873
0
    if (paintable.layout_node().is_box()) {
874
0
        auto const& paintable_box = static_cast<Painting::PaintableBox const&>(paintable);
875
0
        builder.appendff(" {}", paintable_box.absolute_border_box_rect());
876
877
0
        if (paintable_box.has_scrollable_overflow()) {
878
0
            builder.appendff(" overflow: {}", paintable_box.scrollable_overflow_rect());
879
0
        }
880
881
0
        if (!paintable_box.scroll_offset().is_zero()) {
882
0
            builder.appendff(" scroll-offset: {}", paintable_box.scroll_offset());
883
0
        }
884
0
    }
885
0
    builder.append("\n"sv);
886
0
    for (auto const* child = paintable.first_child(); child; child = child->next_sibling()) {
887
0
        dump_tree(builder, *child, colorize, indent + 1);
888
0
    }
889
0
}
890
891
void dump_namespace_rule(StringBuilder& builder, CSS::CSSNamespaceRule const& namespace_, int indent_levels)
892
0
{
893
0
    indent(builder, indent_levels);
894
0
    builder.appendff("  Namespace: {}\n", namespace_.namespace_uri());
895
0
    if (!namespace_.prefix().is_empty())
896
0
        builder.appendff("  Prefix: {}\n", namespace_.prefix());
897
0
}
898
899
void dump_nested_declarations(StringBuilder& builder, CSS::CSSNestedDeclarations const& declarations, int indent_levels)
900
0
{
901
0
    indent(builder, indent_levels);
902
0
    builder.append("  Nested declarations:\n"sv);
903
0
    dump_declaration(builder, declarations.declaration(), indent_levels + 1);
904
0
}
905
906
}